Cpk answers one question: given how much a measurement varies and where it sits between its limits, how much room does the process actually have? It is the standard way to say whether a test is likely to keep passing, or whether it is one small drift away from scrapping boards.
The Formula
Cpk is the worse of the two one-sided distances from the mean to each specification limit, measured in units of 3 standard deviations:
CPU = (USL - mean) / (3σ)
CPL = (mean - LSL) / (3σ)
Cpk = min(CPU, CPL)
Taking the minimum is what makes Cpk useful. A process centred perfectly between wide limits and a process hugging one limit can have the same spread, and only Cpk distinguishes them.
Worked Example
A 3.3 V rail with limits of 3.2 V and 3.4 V, from 200 production boards:
| Statistic | Value |
|---|---|
| Mean | 3.31 V |
| Standard deviation | 0.018 V |
| LSL | 3.2 V |
| USL | 3.4 V |
CPU = (3.40 - 3.31) / (3 × 0.018) = 0.09 / 0.054 = 1.67
CPL = (3.31 - 3.20) / (3 × 0.018) = 0.11 / 0.054 = 2.04
Cpk = min(1.67, 2.04) = 1.67
The process is slightly high of centre, so the upper limit is the binding one. Cpk of 1.67 is comfortable.
Cp vs Cpk
Cp ignores where the process sits and measures only whether the spread could fit:
Cp = (USL - LSL) / (6σ)
In the example above, Cp is 0.2 / 0.108 = 1.85. The gap between Cp 1.85 and Cpk 1.67 is the cost of being off-centre. When Cp is much larger than Cpk, the fix is usually recentring rather than reducing variation, which is almost always the cheaper intervention.
See Cp, potential process capability.
Interpreting the Number
| Cpk | Defects (approx, centred) | Meaning |
|---|---|---|
| 0.67 | ~4.5 % | Failing constantly |
| 1.00 | ~0.27 % | Limits at exactly 3σ; marginal |
| 1.33 | ~63 ppm | Common minimum target |
| 1.67 | ~0.6 ppm | Comfortable |
| 2.00 | ~2 ppb | Six-sigma capable |
Most electronics manufacturers target Cpk of at least 1.33 on measurements that matter. Below 1.0, the test fails often enough that operators start treating failures as noise, which is its own problem.
A very high Cpk is worth a question rather than celebration: either the process is genuinely excellent, or the limits are so loose the test cannot fail, in which case it is spending cycle time without protecting anything.
What Invalidates Cpk
Cpk is easy to compute and easy to compute meaninglessly. Four conditions have to hold.
The process must be stable. Capability on an out-of-control process averages two or more distributions into one number describing none of them. Check the control chart first; if there are shifts or trends, fix those before calculating capability.
The data should be roughly normal. The defect-rate column above assumes normality. Heavily skewed or bimodal data makes those ppm figures fiction. Bimodal usually means two populations mixed, such as two fixtures or two component lots, and the fix is to separate them.
The measurement must have resolution. If the instrument resolves to 0.01 V and the real spread is 0.005 V, the computed sigma is an artefact. A sigma of zero is a resolution problem, never perfection.
The sample must be adequate. Thirty units is a rough minimum; fewer makes the sigma estimate unstable enough that the Cpk is noise.
Getting Data That Supports Cpk
Cpk requires the measured values. A pass/fail record cannot produce it at any sample size, which is the practical reason to store numbers rather than verdicts.
procedure.yaml24 lines
name: PCBA Functional Testversion: 1.0.0unit: serial_number: default_value: "PCBA000001" part_number: default_value: "PCBA-100"plugs: - name: dmm python: plugs.dmm:Multimetermain: - name: Power Rails python: phases.power_rails measurements: - name: Rail 3V3 unit: V validators: - operator: ">=" expected_value: 3.2 - operator: "<=" expected_value: 3.4def power_rails(measurements, dmm): measurements.rail_3v3 = dmm.dc_voltage()class Multimeter: def dc_voltage(self) -> float: return 3.31 # Replace with a real instrument readEach run stores the value with its limits, so Cpk per measurement is computed from the data rather than a separate study.
Two habits that keep the number meaningful:
Keep measurement names stable. Renaming Rail 3V3 splits the distribution in two and the capability history with it.
Version the procedure. A limit change alters Cpk with no process change at all. Without the version on each run, that looks like a process event.
Calculating It Yourself
analysis/capability.py22 lines
import statisticsdef cpk(values: list[float], lsl: float, usl: float) -> float: """Process capability index for a two-sided specification.""" if len(values) < 30: raise ValueError("Sample too small for a stable sigma estimate") mean = statistics.mean(values) sigma = statistics.stdev(values) if sigma == 0: raise ValueError("Zero variation; check measurement resolution") cpu = (usl - mean) / (3 * sigma) cpl = (mean - lsl) / (3 * sigma) return min(cpu, cpl)def cp(values: list[float], lsl: float, usl: float) -> float: """Potential capability, ignoring centring.""" sigma = statistics.stdev(values) return (usl - lsl) / (6 * sigma)Acting on a Low Cpk
| Symptom | Likely cause | Action |
|---|---|---|
| Cp good, Cpk poor | Process off-centre | Recentre; usually the cheapest fix |
| Cp and Cpk both poor | Too much variation | Reduce variation, or widen limits if the design allows |
| Cpk fell without a process change | Limits were tightened | Check the procedure version history |
| Bimodal distribution | Two populations mixed | Split by station, fixture or lot and recompute |
| Sigma near zero | Instrument resolution too coarse | Improve the measurement first |
Widening limits is a legitimate response only when the design genuinely tolerates it. Widening them because the process cannot hold them is how marginal product ships.
One-Sided Measurements
Leakage current has a maximum and no minimum; insulation resistance the reverse. Cpk is undefined for those, and the correct indices are CPU and CPL alone. Do not invent a second limit to make a Cpk appear. See CPL and CPU.
Short Term vs Long Term
Cpk conventionally uses short-term, within-subgroup variation. Ppk uses the overall long-term standard deviation. Ppk is usually lower, because it includes the drift between subgroups that Cpk excludes. See Cpk vs Ppk.
If You Already Run OpenHTF
import openhtf as htffrom openhtf.util import unitsfrom tofupilot.openhtf import upload@htf.measures( htf.Measurement("rail_3v3").in_range(3.2, 3.4).with_units(units.VOLT),)def phase_power(test): test.measurements.rail_3v3 = 3.31test = htf.Test(phase_power, part_number="PCBA-100")test.add_output_callbacks(upload())test.execute(test_start=lambda: input("Scan serial: "))Install with pip install "tofupilot[openhtf]", or skip the callback and run it with tofupilot run.
Common Mistakes
| Mistake | Consequence | Fix |
|---|---|---|
| Computing Cpk on an unstable process | A number describing no real distribution | Check the control chart first |
| Recording pass/fail only | Cpk cannot be computed at all | Store the measured values |
| Ignoring bimodal data | Capability is an average of two populations | Split by station, fixture or lot |
| Treating sigma of zero as perfect | Resolution artefact read as excellence | Check instrument resolution |
| Reporting Cpk for a one-sided limit | Requires a fabricated second limit | Use CPU or CPL |
| Widening limits to fix a low Cpk | Marginal product ships | Recentre or reduce variation first |
