A unit that passes every production test and then fails at the customer means the test did not exercise the condition that broke it, or measured it and let it through. Learn the four usual causes (coverage gaps, limits too wide, intermittent faults, measuring the wrong thing), how to tell them apart, and how to feed each field return back into the test with TofuPilot.
Symptoms and Likely Causes
| Symptom | Likely cause | Section |
|---|---|---|
| Returned unit passes the production test again on the bench | Test conditions differ from use: load, vibration, temperature, duration | Cause 1 |
| Returned unit's original results were inside limits but far from the population | Limits too wide | Cause 2 |
| Failure cannot be reproduced, or appears and disappears | Intermittent fault, measured only at rest | Cause 3 |
| Test reports pass on a function that does not work in use | The measurement is a proxy that does not represent the function | Cause 4 |
| Several returns share a lot, a date range or a station | Process change that the test does not cover | Cause 1 or 2, then the line |
The first check on any return is the same: find the unit's original test results by serial number. Without that history, every cause above is a guess.
Cause 1: The Test Runs Under Conditions the Unit Never Sees in Use
A bench test is usually static and short: nominal supply, room temperature, no load or a light load, a few seconds per measurement. Use is dynamic and long. A defect that only appears under one of the use conditions passes the bench.
| Condition | Bench test | Use | Defect that only shows in use |
|---|---|---|---|
| Current | No load or light load | Full load, peaks, regenerative transients | Resistive joints and crimps, undersized traces, MOSFETs with poor attach |
| Vibration | None | Continuous, broadband | Cracked joints on heavy parts and wires, connectors, loose hardware |
| Temperature | Ambient | Cold starts, hot enclosures, cycles | Marginal components, solder fatigue, timing margins |
| Duration | Seconds | Hours | Thermal runaway, slow drift, wear |
| Transitions | Steady setpoints | Steps, starts, stops | Inrush, overshoot, bus overvoltage on deceleration |
An example from motor and ESC testing shows the size of the gap. On a measured bench, a 3115-size brushless motor without a propeller drew 3.5 to 4.5 A at full throttle on a 6S pack. With a typical propeller, the same motor is estimated to draw 60 to 80 A at full throttle. A no-prop test therefore exercises the power path at about 4 / 70 ≈ 6% of its use current. A joint that is resistive enough to overheat at 70 A dissipates I²R, so (4 / 70)² ≈ 0.3% of that power at 4 A, and passes. See motor and actuator testing for loaded test setups.
What to Do
- List the use conditions for the product and mark which ones the production test covers.
- For each uncovered condition, decide between adding it to the test (a load, a vibration step, a temperature point) and covering it with a screen such as random vibration or thermal cycling.
- Do not try to cover use conditions by overstressing. A current far above use, applied to "burn out" weak joints, is destructive and degrades the good joints too.
Cause 2: Limits Too Wide
Limits copied from a datasheet, or widened to stop false failures, can pass units that are clearly abnormal compared to the population. A unit at the edge of a wide limit is often the one with a defect.
Compare the returned unit's original measurements to the distribution of all units of the same part:
| Returned unit position | Interpretation | Action |
|---|---|---|
| Near the center of the distribution | Test did not see the defect | Coverage gap: Cause 1, 3 or 4 |
| In the tail, inside limits | Test saw an abnormal value and passed it | Tighten the limit toward the population |
| Outside limits | Unit should have failed | Check for retest after failure, overrides, or a limit changed after the run |
In TofuPilot, every measurement has a control chart and a Cpk across runs. Placing the returned unit's value on that chart answers the question in one look. For the method to set limits from the distribution rather than the datasheet, see how to set measurement limits using production data, and for monitoring drift over time, what SPC is.
Cause 3: Intermittent Faults Measured at Rest
A cold or cracked solder joint can read a normal resistance at rest, because the cracked surfaces still touch. It opens under vibration, flexing or thermal expansion. A single reading at rest passes it every time.
Measure the same quantity while the unit is disturbed, and record the variation, not one value:
- Vibrate, tap or flex the assembly during the measurement.
- Sample continuously and record the maximum deviation from the mean, not only the mean.
- Use a 4-wire (Kelvin) connection for low resistances: separate force and sense contacts, otherwise the probe contact resistance dominates and its own variation hides the joint.
The phase below samples a joint resistance while a shaker runs, and records the mean and the peak deviation:
procedure.yaml28 lines
name: Power Lead Joint Checkversion: 1.1.0plugs: - name: Shaker Controller key: shaker python: plugs.shaker:ShakerController - name: Micro-ohmmeter key: ohmmeter python: plugs.ohmmeter:MicroOhmmetermain: - name: Battery Lead Resistance Under Vibration python: phases.joint_vibration:joint_vibration timeout: 5m with: duration_s: 60 measurements: - name: Joint Resistance Mean unit: mΩ validators: - operator: "<=" expected_value: 0.8 - name: Joint Resistance Max Deviation unit: mΩ validators: - operator: "<=" expected_value: 0.1phases/joint_vibration.py20 lines
import statisticsimport timedef joint_vibration(shaker, ohmmeter, measurements, log, duration_s): samples = [] shaker.start() try: deadline = time.monotonic() + duration_s while time.monotonic() < deadline: samples.append(ohmmeter.read_milliohm()) time.sleep(0.05) finally: shaker.stop() mean = statistics.fmean(samples) deviation = max(abs(s - mean) for s in samples) log.info(f"{len(samples)} samples, mean {mean:.3f} mOhm, max deviation {deviation:.3f} mOhm") measurements.joint_resistance_mean = mean measurements.joint_resistance_max_deviation = deviationThe limits and duration are example values. A good joint gives a stable reading under vibration; a cracked one shows spikes, which the deviation measurement catches even when the mean stays normal.
Cause 4: Measuring the Wrong Thing
A test can measure something easy that correlates with the function on good units and does not on defective ones.
| Proxy measurement | What it misses | Measurement that represents the function |
|---|---|---|
| Motor spins (boolean) | Excess current, rough commutation, a weak phase | Current and speed at defined throttle points, per phase |
| Voltage present on a rail at no load | Rail collapse under load | Rail voltage at rated load current |
| Communication link responds once | Error rate over time | Packet count and error count over a fixed duration |
| Visual check of a joint | Internal crack, insufficient wetting | Joint voltage drop at a known current, compared to a golden unit |
Record values, not booleans. A boolean pass hides how close the unit was to failing, and gives nothing to compare a return against.
Feed Field Returns Back Into the Test
Each return is a test case the production test failed. The loop below turns it into a change in the test.
- Identify the unit by serial number and open its test history. In TofuPilot, every run is stored against the unit serial number, with each measurement, its limits, the station and the procedure version.
- Record the return analysis as a run on the same serial number. Run the production procedure, or a dedicated failure-analysis procedure, on the returned unit, and mark it with run metadata so these runs can be filtered:
def mark_return(run): run.metadata["context"] = "field_return" run.metadata["rma"] = "RMA-0001" # your return reference- Classify the cause with the tables above: coverage gap, limit too wide, intermittent, wrong measurement.
- Change the test: add the measurement or the condition, or tighten the limit. Bump the procedure
version, which is recorded with every run, so results before and after the change can be compared. - Verify on the returned unit. The changed test must fail the returned unit. If it does not, the change does not address the defect.
- Watch the production data. A new measurement or a tighter limit lowers first-pass yield at first. Check the failure Pareto to confirm that the new failures are the defect, not the test. If yield falls in an unexpected way, see why yield drops.
Common Pitfalls
Closing a return as "no fault found" after one bench test. The bench test is the test that already passed the unit. Reproduce the use conditions before concluding.
Widening limits to reduce false failures without looking at the distribution. It also lets through the units in the tail, which are the likely field failures.
Recording booleans instead of values. A return cannot be compared to a population that was never measured.
Overstressing to compensate for missing coverage. High current or extreme stress applied to every unit damages good units. Use screens with levels set not to consume good-unit life.
Changing the test without verifying it on the returned unit. Without that check, nothing shows the new test would have caught the failure.
Key Points
- Start every return investigation from the unit's original results, found by serial number.
- Bench tests are usually static and short; failures in use come from load, vibration, temperature, duration and transitions.
- A returned unit in the tail of the distribution points to limits that are too wide; one near the center points to a coverage gap.
- Intermittent faults need measurement during vibration or flexing, recorded as variation, with Kelvin connections for low resistances.
- Measure the function, record values, and feed each return back as a test change verified on the returned unit.
