Pre-calibrated Motor + Encoder Issue on ODrive v3.6 (0.5.6) with Hoverboard Motor & AS5047P

Hi
I have an ODrive v3.6 (56V) running firmware 0.5.6, controlling two hoverboard BLDC motor (15 pole pairs) with an AS5047P magnetic encoder in ABI mode.

Problem is

  • FULL_CALIBRATION_SEQUENCE` completes successfully with all errors = 0x0
  • Motor moves correctly in CLOSED_LOOP_CONTROL after calibration
  • After setting motor.pre_calibrated = True and encoder.pre_calibrated = True, then rebooting, the axis faults with:
  • Axis error: 0x40 (ERROR_MOTOR_FAILED)
  • Motor error: 0x4000000
  • Encoder error: 0x0

Is this a known issue with incremental encoders on 0.5.6? Should I use a different pre-calibration strategy, or is calibration-on-every-boot the only reliable option?

Hardware Setup

ODrive: v3.6-56V (firmware v0.5.6)
Motor: Hoverboard BLDC (~15 pole pairs, 5-10Ω)
Encoder: AS5047P (ABI mode, CPR = 4096)
Power supply: 50V, 30A capable
Brake resistor: wired


## Configuration Code (Working - No Pre-Cal)

python

import odrive
from odrive.enums import *
import time

odrv0 = odrive.find_any()
axis = odrv0.axis0

Clear and reset

odrv0.clear_errors()
axis.requested_state = AXIS_STATE_IDLE
time.sleep(0.5)

— MOTOR CONFIG —

axis.motor.config.motor_type = 0 # HIGH CURRENT
axis.motor.config.pole_pairs = 15
axis.motor.config.calibration_current = 10
axis.motor.config.current_lim = 15
axis.motor.config.requested_current_range = 25
axis.motor.config.resistance_calib_max_voltage = 25
axis.motor.config.current_control_bandwidth = 100

— ENCODER CONFIG —

axis.encoder.config.mode = ENCODER_MODE_INCREMENTAL
axis.encoder.config.cpr = 4096 # Measured via shadow_count test: -4107 ≈ 4096
axis.encoder.config.use_index = False
axis.encoder.config.pre_calibrated = False # THIS IS KEY - leaving false
axis.encoder.config.bandwidth = 1000
axis.encoder.config.calib_range = 0.05

— CONTROLLER CONFIG —

axis.controller.config.control_mode = CONTROL_MODE_POSITION_CONTROL
axis.controller.config.input_mode = INPUT_MODE_POS_FILTER
axis.controller.config.pos_gain = 20
axis.controller.config.vel_gain = 0.02
axis.controller.config.vel_integrator_gain = 0.05
axis.controller.config.vel_limit = 10

— BOARD CONFIG —

odrv0.config.enable_brake_resistor = False

print(“Saving base configuration…”)
odrv0.save_configuration()
time.sleep(5)

Reconnect after reboot

odrv0 = odrive.find_any()
axis = odrv0.axis0

— CALIBRATION —

print(“Running FULL_CALIBRATION_SEQUENCE…”)
odrv0.clear_errors()
axis.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE
while axis.current_state != AXIS_STATE_IDLE:
time.sleep(0.1)

print(f"Axis error: {hex(axis.error)}“)
print(f"Motor error: {hex(axis.motor.error)}”)
print(f"Encoder error: {hex(axis.encoder.error)}")

Output: All 0x0 ✓

— ENTER CLOSED LOOP —

axis.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL
time.sleep(1)

Test motion

axis.controller.input_pos = 2
time.sleep(2)
axis.controller.input_pos = 0

Motor moves correctly ✓


## Configuration Code (Fails - With Pre-Cal)

python

After the above calibration succeeds, add this:

axis.motor.config.pre_calibrated = True
axis.encoder.config.pre_calibrated = True

axis.config.startup_motor_calibration = False
axis.config.startup_encoder_offset_calibration = False
axis.config.startup_closed_loop_control = True

print(“Saving pre-calibrated config…”)
odrv0.save_configuration()
time.sleep(5)

Reboot ODrive manually or reconnect

Now on reconnect:

odrv0 = odrive.find_any()
axis = odrv0.axis0

print(f"Axis state: {axis.current_state}“)
print(f"Axis error: {hex(axis.error)}”)
print(f"Motor error: {hex(axis.motor.error)}“)
print(f"Encoder error: {hex(axis.encoder.error)}”)

Output:

Axis state: 1 (IDLE)

Axis error: 0x40 (ERROR_MOTOR_FAILED)

Motor error: 0x4000000

Encoder error: 0x0


## Measurements & Debug Info

Encoder CPR verification (shadow_count test):

* **1 mechanical turn:** -4107 counts
* **5 mechanical turns:** -20,076 counts → -4015.2 counts/rev
* **Conclusion:** Actual CPR ≈ 4096 ✓ (matches configured value)

Bus voltage: 50.47V ✓
Encoder is counting cleanly ✓

## Questions

1. Is the `pre_calibrated` approach fundamentally incompatible with incremental encoders on firmware 0.5.6?
2. Should I stick with "calibrate at every startup" for reliability? This will be technically a problem .
3. Are there any known workarounds (e.g., specific current limits, gain adjustments) to make pre-calibration work?
4. Would switching the AS5047P to **SPI absolute mode** (`ENCODER_MODE_SPI_ABS_AMS`) solve this without needing pre-calibration?
Best regards

Shaker

Because incremental encoders cannot track the position of the motor between reboots, when using an incremental encoder you must either calibrate or do an index search on each startup. Usually index search is recommended, as it’s faster and more robust to load, however it still requires motor movement. You could run the AS5047 in SPI mode instead, however you’d have to change your wiring, and SPI can have some EMI issues with v3.6, so you may need to add ferrite rings to the motor phases or do other EMI mitigation strategies such as routing the SPI lines and motor phases separately, or shielding one/both.