The problem
Most low-cost BMS controllers cannot run the battery models found in research papers. Compression methods such as quantisation and pruning are widely studied for vision and speech, but their effect on regression models for battery SOC and SOH on very small MCUs is less well characterised.
Objectives
- Train reference float models for SOC (and optionally SOH) on public datasets.
- Apply post-training and quantisation-aware int8 and lower-bit quantisation, and magnitude and structured pruning.
- Implement inference kernels for each format on the Cortex-M3 and measure cycles, flash and RAM.
- Build Pareto fronts of accuracy against resource use and validate selected points on the kit cell.
What you need
From the EV Embedded Development Kit
- Second STM32F103C8T6 as the Smart BMS unit
- 1S battery with connector and T-sense input
- Charge/discharge control relay
- Load section: three switchable resistive loads with LEDs
- Battery charging unit with its own switch
- Serial UART interface
Software
- STM32CubeIDE
- Embedded C with HAL drivers
- CMSIS-NN (optional)
- Python with TensorFlow (Model Optimization toolkit) or PyTorch, NumPy, pandas
- NASA PCoE and CALCE datasets
Extra parts
- INA219 I2C current/power monitor module — Current is an input to the models and needed for reference SOC during live validation on the kit cell.
This project uses the kit's lithium cell. Respect polarity, never short the terminals, and don't leave the cell charging unattended.
Key concepts
- Post-training vs quantisation-aware training
- Post-training quantisation converts a trained float model; quantisation-aware training simulates integer rounding during training so the model learns to tolerate it, usually recovering accuracy at low bit widths.
- Unstructured vs structured pruning
- Unstructured pruning zeroes individual weights, which saves memory only with sparse storage; structured pruning removes whole neurons or channels, which directly reduces computation on a simple MCU.
- Pareto front
- The set of models for which no other model is better in both accuracy and resource use; it shows the real trade-off a designer can choose from.
- Sub-byte packing
- Storing 4-bit or 2-bit weights two or four to a byte saves flash but costs unpacking instructions at inference time on the Cortex-M3.
Step-by-step guide
- 1
Define models, metrics and budget
Pick two or three model families: a feed-forward network on windowed features, a small 1D temporal convolution and optionally a tiny GRU. Define metrics: SOC RMSE and maximum error, flash bytes, peak RAM, cycles per inference. Set the budget as what is left after the BMS firmware, measured from its map file, not the full 64 KB and 20 KB.
Checkpoint: A protocol document with model families, metrics and a measured memory budget.
- 2
Train float baselines
Prepare NASA PCoE and CALCE data with cell-level splits and consistent reference SOC. Train each family at several sizes with fixed seeds and record float accuracy. These are the upper bounds for compression. Use at least three seeds for each configuration, because small models vary noticeably between runs. Store each trained model with its training settings, data version and validation scores so every compressed variant can be traced back to the exact float model it came from.
Checkpoint: Float accuracy for each family and size with seed variation.
- 3
Apply quantisation variants
For each model, apply post-training int8 with a representative calibration set, quantisation-aware int8, and quantisation-aware 4-bit weights with 8-bit activations. Compare per-tensor and per-channel scales. Build NumPy integer references that mirror exactly what the firmware will compute. Use the same calibration set for every model so differences come from the method rather than the data. Note where accuracy drops sharply, such as at 4-bit weights without quantisation-aware training, since these cliffs are among the most useful findings for designers.
Checkpoint: Accuracy for every variant from the integer reference, not from simulated float.
- 4
Apply pruning variants
Apply magnitude-based unstructured pruning at several sparsity levels with fine-tuning, and structured neuron pruning that shrinks layer widths. Combine the best pruning with quantisation. Record exact parameter counts and the storage format each needs. Fine-tune with the same budget after each pruning step so the comparison is fair. For unstructured pruning, also calculate the storage needed for indices, because a sparse format that stores many indices can end up larger than the dense model it replaces.
Checkpoint: Accuracy for pruned and pruned-plus-quantised models at each sparsity.
- 5
Implement inference kernels
Write C kernels for dense int8, packed int4 weights and sparse (CSR-style) int8 layers, and use CMSIS-NN where it applies. Run them on the Smart BMS STM32, verifying bit-exact output against the NumPy references using vectors sent over UART. Measure cycles with the DWT counter, compile with fixed flags and report flash and stack-plus-buffer RAM. Keep the kernel interfaces identical so the benchmark harness can swap formats without other changes.
Checkpoint: A verified kernel library with a cycle and memory table for each format.
- 6
Profile on the target
Benchmark every model variant on the hardware, not just estimate. Note effects such as flash wait states, unpacking overhead of int4 and indexing overhead of sparse formats, which can make a smaller model slower. Repeat measurements to show they are stable. Run each benchmark with the BMS firmware active, as well as alone, to see how interrupts and shared flash access affect timing. Report median and worst-case cycles, not only averages.
Checkpoint: Measured cycles, flash and RAM for all variants, repeatable within a small spread.
- 7
Build Pareto fronts
Plot accuracy against flash, RAM and cycles and identify Pareto-optimal models. Analyse which technique dominates in which region: for example structured pruning for speed, int4 for flash. Report findings that would change a designer's choice. Mark the memory and time budgets left by the BMS firmware on each plot, so readers can see which models are actually deployable. Discuss surprising results, such as a 4-bit model that is smaller but slower than its int8 version because of unpacking.
Checkpoint: Pareto plots and a written analysis of the dominant techniques.
- 8
Validate on the kit cell
Fit the INA219 with the cell disconnected, run new load profiles on the kit and evaluate selected Pareto models live alongside the BMS firmware. Follow lithium safety: polarity, no shorts, voltage limits and supervised charging. Check whether the ranking from public data holds on the kit cell. Use the same test profiles for every selected model, and report the gap between dataset and kit accuracy as a measure of how well the conclusions transfer.
Checkpoint: Live accuracy for selected models on the kit, compared with their dataset accuracy.
- 9
Package and write up
Release models, kernels, benchmark scripts and data so the fronts can be regenerated. Write the thesis chapter around clear design guidance, with limits such as a single MCU family and compiler. Include a script that rebuilds the firmware for each variant, runs the benchmarks on a connected kit and regenerates the plots automatically. Write the design guidance as a short decision table that tells an engineer which technique to try first for a given flash, RAM and latency limit.
Checkpoint: A reproducible benchmark and a draft chapter.
Testing & validation
- All accuracy figures computed from integer reference models identical to firmware.
- Bit-exact kernel verification on the target.
- Measured, repeated cycle counts on hardware.
- Live validation of selected models on the kit cell.
Expected results
Measured accuracy-versus-resource Pareto fronts for compressed battery models on a Cortex-M3, a verified library of integer kernels and practical guidance on which compression to use under which constraint.
Research angle for your PhD
Research questions: for battery state regression on an FPU-less MCU with under 64 KB flash, which combinations of quantisation and pruning are Pareto-optimal, and do memory savings translate into speed given wait states and unpacking costs? Metrics: SOC RMSE and maximum error, flash, peak RAM and measured cycles. Baselines: float models, plain post-training int8. Datasets: NASA PCoE and CALCE, plus kit validation. Publishable contributions would be measured (not estimated) Pareto fronts on real hardware, open kernels, and evidence where common compression assumptions fail on small MCUs.
Take it further
- Add mixed-precision search per layer.
- Study knowledge distillation from a large model to small students.
- Measure energy per inference with an external power monitor.
- Repeat for SOH models with ageing data.
Viva and review questions
Why might a pruned model be slower than a dense one?
Unstructured sparsity needs index lookups and irregular memory access, which on a Cortex-M3 can cost more than the multiplications saved unless sparsity is very high.
Why measure on hardware instead of counting operations?
Operation counts ignore flash wait states, unpacking and loop overheads. Our measurements showed cases where the model with fewer operations was not the fastest.
When does quantisation-aware training help?
Mostly at low bit widths such as 4-bit weights, where post-training rounding loses too much accuracy. At int8 the difference was small for our models.