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.

Hello,

I am commissioning one hoverboard motor for a 6DOF simulator actuator. I would appreciate help understanding an overspeed fault before doing further powered tests.

My hardware:

  • ODrive v3.6 56V, firmware 0.5.6
  • Approximately 38.5V battery
  • Hoverboard motor, 15 pole pairs
  • AS5047P in SPI absolute mode
  • Encoder CPR: 16384
  • Encoder direction: +1
  • Encoder SPI error rate: always zero
  • 2Ω brake resistor installed
  • Motor mechanically unloaded except for a 1:1 belt connecting it to the encoder
  • Second ODrive axis disconnected and insulated

Motor calibration completes successfully:

  • Phase resistance: approximately 0.221Ω
  • Phase inductance: approximately 325µH
  • Torque constant: 0.516875 Nm/A
  • Current-control bandwidth: 100 rad/s
  • Motor and encoder pre-calibration survive reboot correctly

Closed-loop position holding works without errors. A low-energy torque command also produces movement. Positive movement is physically anticlockwise on my installation.

For the most recent velocity test, I used temporary RAM settings only:

Control mode: velocity control
Input mode: velocity ramp
Velocity command: +0.020 turns/s
Velocity ramp rate: 0.020 turns/s²
Velocity gain: 15
Velocity integrator gain: 15
Velocity integrator torque limit: 0.15 Nm
Torque limit: 0.45 Nm
Current limit: 1.0 A
Encoder bandwidth: 100 rad/s
Velocity limit: 0.030 turns/s
Velocity limit tolerance: 1.2
Overspeed threshold: 0.036 turns/s

The motor entered closed-loop control and accepted the command, but approximately 1.45 seconds later it stopped with:

Axis error:       0xA40
Motor error:      0x110000000
Encoder error:    0x0
Controller error: 0x1

My understanding of these errors is:

Controller 0x1 = OVERSPEED
Axis 0xA40 = CONTROLLER_FAILED + MOTOR_FAILED + WATCHDOG_TIMER_EXPIRED
Motor 0x110000000 = UNKNOWN_TORQUE + UNKNOWN_VOLTAGE_COMMAND

I believe the controller overspeed was the first fault. The motor errors appeared afterward because the controller stopped producing a valid torque command. The watchdog error appeared later as part of my independent safety cutoff.

An independent CAN logger sampled the encoder every approximately 50ms:

  • First count: -4920
  • Last count: -4811
  • Net movement: 109 counts
  • Movement: 0.00665 turns, approximately 2.4°
  • Maximum count-derived interval velocity: approximately 0.0244 turns/s
  • Peak Iq setpoint: 0.871A
  • Peak measured Iq: 0.462A
  • Bus voltage remained between 38.38V and 38.45V
  • No encoder SPI or CAN errors

Therefore, the slower 20Hz logger never measured more than 0.0244 turns/s, but the ODrive’s internal higher-rate velocity estimate apparently exceeded the 0.036 turns/s trip threshold between samples.

After a complete power cycle, both axes returned to IDLE with all errors zero. Calibration and saved configuration remained correct.

My questions are:

  1. Is this likely a real short acceleration caused by stick-slip/breakaway, or a spike in the encoder velocity estimator?
  2. Are velocity gains of 15/15 too aggressive for this low-speed hoverboard motor application?
  3. Is an encoder bandwidth of 100 rad/s appropriate for an AS5047P SPI encoder at approximately 0.02 turns/s?
  4. Is a 0.036 turns/s overspeed threshold simply too close to the commanded speed for initial commissioning?
  5. What conservative velocity gains, encoder bandwidth, and velocity-limit margin would you recommend for the next test?
  6. Is there anything specific to SPI absolute encoders and firmware 0.5.6 that I should check?

I have the complete 50ms CAN trace available if it would help.

Thank you.