Asymmetrical ILLEGAL_HALL_STATE on Axis 0 only - Pinpointed to DC_calib_phC offset (v3.5 24V)

Summary

On an ODrive v3.5 24V, ENCODER_HALL_POLARITY_CALIBRATION fails with EncoderError.ILLEGAL_HALL_STATE (error 16) on axis 0 only. Axis 1 calibrates and runs perfectly. Swapping the two motors between channels does not move the problem — it stubbornly stays on axis 0.

Hall signals are provably clean at standstill. The failure only occurs while the motor is driven. I’ve run a systematic set of tests and narrowed it down to what looks like a localized hardware/grounding anomaly on Axis 0.

Setup

Item Value
Board ODrive v3.5, hw_version_variant = 24
Firmware reports 0.0.0, fw_version_unreleased = 1
Host tool odrive Python package 0.6.11.post1, Python 3.14.7, Windows
Motors two identical 24V hoverboard hub motors (Model: WT25HB24V20180124)
Pole pairs 15 (measured: 91 hall transitions per mechanical revolution)
Supply 7S Li-ion → XL4016 buck converter → 22.0–22.1 V
Brake resistor none, enable_brake_resistor = False

Relevant config, identical on both axes:

encoder.config.mode                = 1 (HALL)
encoder.config.cpr                 = 90
motor.config.pole_pairs            = 15
encoder.config.calib_scan_distance = 150
encoder.config.bandwidth           = 100
gpio9/10/11 and gpio12/13/14 mode  = 0 (DIGITAL)
config.dc_bus_overvoltage_trip_level = 24.5
config.dc_max_negative_current       = -1.0

Symptom

Axis 1 — full sequence succeeds:

MOTOR_CALIBRATION                 OK   R = 0.1745 Ω   L = 304 µH
ENCODER_HALL_POLARITY_CALIBRATION OK   hall_polarity = 0
ENCODER_OFFSET_CALIBRATION        OK   phase_offset = 75, direction = 1

Closed loop velocity control verified at 1.0 and 1.5 turn/s, tracking within 1%.

Axis 0 — motor calibration fine, polarity calibration always fails:

MOTOR_CALIBRATION                 OK   R = 0.1784 Ω   L = 310 µH
ENCODER_HALL_POLARITY_CALIBRATION FAIL after 4.0 s
                                  encoder.error = 16 (ILLEGAL_HALL_STATE)
                                  ibus peak 1.17 A — the motor does spin

Key Troubleshooting Tests & Findings

  1. Hall signal quality at standstill: Hand-turning the wheel gives a perfect 120° Gray code (187 transitions sampled via get_gpio_states(), zero illegal states). Pull-ups are working fine on all 6 pins.
  2. Motor swap: Moving phases and halls together to the other channel proved that the issue stays on Axis 0. Both motors work flawlessly on Axis 1.
  3. Hardware Errata check: Verified that axis0.encoder.hall_state strictly maps to GPIO 9/10/11. The known silkscreen label swap errata does not apply here.
  4. Hardware Filters: Added 22 nF ceramic capacitors between all 6 hall lines and GND close to J4. It made absolutely no difference for Axis 0.
  5. The Cross-Axis Smoking Gun: When driving Axis 0, Axis 1 (stationary) immediately gets pulled into an ILLEGAL_HALL_STATE (111). Conversely, driving Axis 1 leaves Axis 0 perfectly stable at error 0. Because Hall sensors are open-collector, reading 111 means they lost their ability to pull low, suggesting a major localized ground bounce or 5V rail shift near Axis 0 during PWM cycles.
  6. The Current Loop Clue: Looking at current loop noise during lockin commands, I noticed a massive asymmetry in the current sensing calibration offsets:
    • axis1.DC_calib_phC = -0.148
    • axis0.DC_calib_phC = -1.107 ← Massive 1.1V negative offset discrepancy on Phase C.

Current Hypotheses & Questions

The massive -1.107 offset on Axis 0 Phase C suggests a damaged current sense shunt amplifier (e.g., OPA365) or a micro-crack/cold solder joint on Phase C’s shunt resistor. This likely forces the SVPWM algorithm to aggressively distort voltage vectors to compensate for a ghost current, creating massive localized EMI or localized ground lifting that triggers the 111 illegal state.

Since I am planning to upgrade this project to a 4-Wheel Drive (4WD) architecture using a second ODrive v3.5 board over CAN bus, I need to get this axis sorted out.

  1. Has anyone seen such a large DC_calib offset anomaly before, and is it a known failure mode for the shunt op-amps on legacy v3.5 boards?
  2. Since pre_calibrated = True is rejected by the firmware unless a successful calibration routine finishes, would the cleanest workaround be to comment out the ILLEGAL_HALL_STATE safety check in encoder.cpp and flash a custom firmware?
  3. Any layout advice when wiring two ODrive v3.5 boards together to a master MCU via CAN to prevent cross-board ground loops from making this worse?

Thanks for your insights!

Yeah, the v3.5 had really bad EMI issues. Where’d you get this board from?