How to Track Battery Cycling Test Data with TofuPilot
Battery cycling tests generate thousands of data points per cell: voltage curves, current profiles, temperature readings, capacity measurements across hundreds of cycles. TofuPilot stores all of it in a structured, queryable format so you can track capacity fade, compare cells, and catch quality issues before they reach the pack.
What Battery Cycling Data Looks Like
A single cell cycling test produces:
| Data point | Per cycle | Over 500 cycles |
|---|---|---|
| Voltage vs. time | ~1000 samples | 500,000 samples |
| Current vs. time | ~1000 samples | 500,000 samples |
| Temperature | ~100 samples | 50,000 samples |
| Charge capacity (Ah) | 1 value | 500 values |
| Discharge capacity (Ah) | 1 value | 500 values |
| Coulombic efficiency | 1 value | 500 values |
Multiply that by hundreds or thousands of cells in production, and you're looking at a data management problem that spreadsheets can't handle.
Logging Cycling Data to TofuPilot
Per-Cycle Upload
Upload results after each charge/discharge cycle completes. This gives you real-time visibility into cell performance.
from tofupilot import TofuPilotClient
client = TofuPilotClient()
def log_cycle(cell_serial, cycle_number, charge_ah, discharge_ah, temp_max, voltage_curve):
coulombic_eff = discharge_ah / charge_ah * 100 if charge_ah > 0 else 0
capacity_retention = discharge_ah / nominal_capacity * 100
client.create_run(
procedure_id=f"CELL-CYCLING-C{cycle_number}",
unit_under_test={"serial_number": cell_serial},
run_passed=capacity_retention > 80 and temp_max < 45,
steps=[{
"name": f"Cycle {cycle_number}",
"step_type": "measurement",
"status": capacity_retention > 80,
"measurements": [
{"name": "charge_capacity_ah", "value": charge_ah, "unit": "Ah", "limit_low": 2.8},
{"name": "discharge_capacity_ah", "value": discharge_ah, "unit": "Ah", "limit_low": 2.8},
{"name": "coulombic_efficiency_pct", "value": coulombic_eff, "unit": "%", "limit_low": 99.0},
{"name": "capacity_retention_pct", "value": capacity_retention, "unit": "%", "limit_low": 80.0},
{"name": "max_temperature_c", "value": temp_max, "unit": "°C", "limit_high": 45.0},
{"name": "voltage_curve_v", "value": voltage_curve, "unit": "V"},
],
}],
)End-of-Life Summary Upload
After cycling completes, upload a summary with key aging metrics.
client.create_run(
procedure_id="CELL-CYCLING-SUMMARY",
unit_under_test={"serial_number": cell_serial},
run_passed=final_capacity_retention > 80,
steps=[{
"name": "Cycling Summary",
"step_type": "measurement",
"status": final_capacity_retention > 80,
"measurements": [
{"name": "total_cycles", "value": 500, "unit": "cycles"},
{"name": "initial_capacity_ah", "value": 3.2, "unit": "Ah"},
{"name": "final_capacity_ah", "value": 2.72, "unit": "Ah"},
{"name": "capacity_retention_pct", "value": 85.0, "unit": "%", "limit_low": 80.0},
{"name": "avg_coulombic_efficiency", "value": 99.7, "unit": "%", "limit_low": 99.0},
{"name": "max_temperature_observed", "value": 42.3, "unit": "°C", "limit_high": 45.0},
],
}],
)Tracking Capacity Fade Across Production
The real value of centralized cycling data is comparing cells across production batches. TofuPilot's measurement trending shows:
- Capacity retention distribution: Are all cells aging at the same rate?
- Batch-to-batch variation: Does the new electrolyte formulation change the fade curve?
- Outlier detection: Which cells are degrading faster than expected?
If batch 47 cells show 90% retention at 300 cycles while batch 46 showed 94%, something changed in the manufacturing process. The data in TofuPilot tells you immediately.
Cell Grading from Cycling Data
Not all cells are created equal. Cycling data helps grade cells into bins for different applications.
| Grade | Criteria | Application |
|---|---|---|
| A | Capacity > 3.1 Ah, retention > 95% at 200 cycles | EV packs |
| B | Capacity > 2.9 Ah, retention > 90% at 200 cycles | Energy storage |
| C | Below Grade B | Second-life applications |
TofuPilot's measurement filters let you query cells by any combination of cycling metrics to assign grades automatically.
Integration with Battery Cyclers
Most battery cyclers (Arbin, Maccor, Neware, BioLogic) export data in CSV or proprietary formats. Parse the cycler output and upload to TofuPilot.
import csv
from tofupilot import TofuPilotClient
client = TofuPilotClient()
def import_arbin_data(csv_path, cell_serial):
with open(csv_path) as f:
reader = csv.DictReader(f)
for row in reader:
if row["Step_Type"] == "Discharge":
client.create_run(
procedure_id="CELL-CYCLING",
unit_under_test={"serial_number": cell_serial},
run_passed=float(row["Discharge_Capacity(Ah)"]) > 2.8,
steps=[{
"name": f"Cycle {row['Cycle_Index']}",
"step_type": "measurement",
"status": True,
"measurements": [
{"name": "discharge_capacity_ah", "value": float(row["Discharge_Capacity(Ah)"]), "unit": "Ah"},
{"name": "charge_capacity_ah", "value": float(row["Charge_Capacity(Ah)"]), "unit": "Ah"},
{"name": "max_voltage_v", "value": float(row["Voltage(V)"]), "unit": "V"},
],
}],
)Safety Monitoring
Battery testing has unique safety requirements. TofuPilot helps track safety-critical measurements:
- Maximum temperature: Cells approaching thermal runaway thresholds
- Voltage anomalies: Cells that don't reach full charge voltage or drop too fast
- Capacity jumps: Sudden capacity changes that indicate internal shorts
- Impedance growth: Rising internal resistance indicating degradation
Set tight limits on these safety measurements. A cell that passes capacity specs but shows abnormal temperature behavior needs investigation before it goes into a pack.