Skip to content
Test Types & Methods

Cell Grading and Binning with TofuPilot

Learn how to grade and bin battery cells based on test data using TofuPilot for capacity matching, impedance sorting, and production traceability.

JJulien Buteau
intermediate10 min readMarch 14, 2026

Cell Grading and Binning with TofuPilot

A battery pack is only as good as its weakest cell. Cell grading sorts production cells by measured performance so you can build packs from matched cells. TofuPilot stores every cell's test data and lets you query, filter, and grade cells based on actual measurements.

Why Cell Grading Matters

Cells from the same production line have natural variation. Capacity varies by 2-5%. Impedance varies by 10-20%. If you build a pack from unmatched cells:

  • The weakest cell limits pack capacity
  • Cells age at different rates, causing imbalance
  • BMS has to work harder to keep cells balanced
  • Pack lifetime is shorter than it needs to be

Grading cells before assembly ensures packs perform consistently and last longer.

Grading Criteria

ParameterHow it's measuredWhy it matters
Capacity (Ah)Full charge/discharge cycleDetermines pack energy
Internal impedance (mohm)AC impedance at 1kHzAffects power delivery and heat
Open-circuit voltage (V)Rest voltage after formationIndicates state of charge consistency
Self-discharge rate (mV/day)Voltage drop over 7-14 day restCatches internal micro-shorts
Weight (g)Scale measurementCatches underfilled or overfilled cells

Setting Up Cell Grading in TofuPilot

Step 1: Define the Grading Test Procedure

cell_grading_test.py
from tofupilot import TofuPilotClient

client = TofuPilotClient()

def grade_cell(serial, capacity_ah, impedance_mohm, ocv_v, self_discharge_mv_day, weight_g):
    # Determine grade
    if capacity_ah >= 3.1 and impedance_mohm <= 30 and self_discharge_mv_day <= 0.5:
        grade = "A"
        passed = True
    elif capacity_ah >= 2.9 and impedance_mohm <= 40 and self_discharge_mv_day <= 1.0:
        grade = "B"
        passed = True
    elif capacity_ah >= 2.7 and impedance_mohm <= 50:
        grade = "C"
        passed = True
    else:
        grade = "REJECT"
        passed = False

    client.create_run(
        procedure_id="CELL-GRADING",
        unit_under_test={
            "serial_number": serial,
            "part_number": "CELL-21700-NMC",
        },
        run_passed=passed,
        steps=[{
            "name": "Capacity Test",
            "step_type": "measurement",
            "status": capacity_ah >= 2.7,
            "measurements": [
                {"name": "capacity_ah", "value": capacity_ah, "unit": "Ah", "limit_low": 2.7},
            ],
        }, {
            "name": "Impedance Test",
            "step_type": "measurement",
            "status": impedance_mohm <= 50,
            "measurements": [
                {"name": "impedance_mohm", "value": impedance_mohm, "unit": "mohm", "limit_high": 50},
            ],
        }, {
            "name": "Self-Discharge",
            "step_type": "measurement",
            "status": self_discharge_mv_day <= 1.0,
            "measurements": [
                {"name": "self_discharge_mv_day", "value": self_discharge_mv_day, "unit": "mV/day", "limit_high": 1.0},
                {"name": "ocv_v", "value": ocv_v, "unit": "V", "limit_low": 3.5, "limit_high": 4.2},
            ],
        }, {
            "name": "Physical",
            "step_type": "measurement",
            "status": 68.0 <= weight_g <= 72.0,
            "measurements": [
                {"name": "weight_g", "value": weight_g, "unit": "g", "limit_low": 68.0, "limit_high": 72.0},
                {"name": "grade", "value": grade, "unit": ""},
            ],
        }],
    )
    return grade

Step 2: Analyze Grade Distribution

After grading a production batch, TofuPilot's dashboard shows the distribution:

GradeCountPercentageCapacity rangeImpedance range
A85085%3.10-3.25 Ah22-30 mohm
B12012%2.90-3.09 Ah31-40 mohm
C202%2.70-2.89 Ah41-50 mohm
REJECT101%< 2.70 Ah> 50 mohm

Track this distribution over time. If Grade A yield drops from 85% to 75%, the manufacturing process is drifting.

Step 3: Pack Assembly with Matched Cells

Query TofuPilot for Grade A cells with capacity within a tight window for pack assembly.

pack_matching.py
from tofupilot import TofuPilotClient

client = TofuPilotClient()

# Get all Grade A cells from the latest batch
runs = client.get_runs(
    procedure_id="CELL-GRADING",
    run_passed=True,
    limit=1000,
)

# Filter for Grade A cells within a 50mAh capacity window
grade_a_cells = []
for run in runs:
    for step in run.get("steps", []):
        for m in step.get("measurements", []):
            if m["name"] == "capacity_ah" and m["value"] >= 3.1:
                grade_a_cells.append({
                    "serial": run["unit_under_test"]["serial_number"],
                    "capacity": m["value"],
                })

# Sort by capacity and group into matched sets
grade_a_cells.sort(key=lambda c: c["capacity"])

# Create packs of 12 cells with capacity spread < 50mAh
pack_size = 12
for i in range(0, len(grade_a_cells) - pack_size + 1, pack_size):
    pack = grade_a_cells[i:i + pack_size]
    spread = pack[-1]["capacity"] - pack[0]["capacity"]
    if spread <= 0.05:
        print(f"Pack: {[c['serial'] for c in pack]}, spread: {spread*1000:.0f} mAh")

Self-Discharge Screening

Self-discharge is the most important safety screen. Cells with elevated self-discharge rates may have internal micro-shorts that can lead to thermal events.

The test requires a long rest period (7-14 days), which makes it the bottleneck in cell production. Track self-discharge data in TofuPilot to:

  • Set data-driven pass/fail thresholds based on production distributions
  • Identify cells that need extended screening
  • Correlate self-discharge with other parameters (formation data, impedance)

Traceability from Cell to Pack

When a pack fails in the field, trace back to the individual cells:

  1. Look up the pack serial in TofuPilot
  2. Find which cells were assembled into that pack
  3. Pull each cell's grading data
  4. Check if any cell was marginal at grading time

This traceability is required by most automotive OEMs and is becoming standard across the battery industry.

More Guides

Put this guide into practice