"motor error unknown phase estimate" on restart, but motor can run fine before being reset

This is an Odrive 3.6, being used with an RW170 wheel, which has an AB encoder with no index.

Pasted below is the output for the latest issue. I can run full calibration, and run encoder offset calibration, and get no errors. Then I can switch to closed loop control and enter speed commands and the motor runs.

Then if I put it into idle and save, it comes back not working. If I put it into closed loop control mode I get an error and cannot command it to move.

In [361]: odrv0.axis0.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE

In [362]: dump_errors(odrv0, True)
system: no error
(gives no error, removed to shorten)

In [363]: odrv0.axis0.motor.config.pre_calibrated
Out[363]: True

In [364]: odrv0.axis0.encoder.config.pre_calibrated
Out[364]: False

In [365]: odrv0.axis0.requested_state = AXIS_STATE_ENCODER_OFFSET_CALIBRATION
     ...: 

In [366]: dump_errors(odrv0, True)
system: no error
(gives no error, removed to shorten)

In [367]: odrv0.axis0.encoder.config.pre_calibrated
Out[367]: False

In [368]: odrv0.axis0.encoder.config.pre_calibrated = True

In [369]: odrv0.axis0.encoder.config.pre_calibrated
Out[369]: False

I cannot set pre-calibrated to true on the encoder

after a reboot, it won’t work, I get this error

axis0
  axis: no error
  motor: Error(s):
    MOTOR_ERROR_UNKNOWN_PHASE_ESTIMATE

I can run encoder calibration again and then the motor works

 odrv0.axis0.requested_state = AXIS_STATE_ENCODER_OFFSET_CALIBRATION
     ...: 
     ...: 

In [388]: dump_errors(odrv0, True)
(gives no error, removed to shorten)

In [389]: odrv0.axis0.controller.input_vel = 0.5

In [390]: odrv0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL

In [391]: odrv0.axis0.controller.input_vel = 0.5

Is there some way to not need to run encoder calibration at startup? Or does it always need to do that if there is no index?

What does this MOTOR_ERROR_UNKNOWN_PHASE_ESTIMATE mean? I’ve checked the motor on another driver and it can run fine. The wiring is good, if I measure the resistance across phases it’s just the motor coil resistance, and besides it runs as expected once the encoder calibration has been run again.

I am going to be running this on a robot that can’t be manually calibrated every time it starts up. Do I need changes in the hardware or is there a setting to avoid needing to do this?

Alternatively, If it doesn’t have any way to automatically go into encoder driven FOC on startup, is there some way it could start sensorless, or on Hall sensors and automatically drop into encoder mode once it’s moved far enough?

If you left it in IDLE mode how much current would the Odrive draw? Maybe there is a way to make the current draw on the STM chip to where it just looks at the encoder info. One might have to look into the
STM spec sheet and then the firmware to see if it is visible.

Always needs to do it if there’s no index. CNC machines have used battery-backup encoders since the 80s or 90s because of this issue (until high resolution absolute encoders got good enough).

You can do it with some small tweaks to the firmware. It’s not supported out of the box though.

This is really driving me crazy.

I put an index on there, and it works right after I calibrate, it finds the index, but upon reset I still get the same MOTOR_ERROR_UNKNOWN_PHASE_ESTIMATE when I try to run.

It’s so persistent no matter what settings I try. I’d think the motor was bad but it runs fine before reset, it calibrates, the software finds the index and reads the encoder. The motor phases read 1.7 ohms like they should. And I can run the motor on another type of driver. It just doesn’t make sense.

Here is a dump of axis0 settings
axis0:
acim_estimator: …
config: …
controller: …
current_state: 1 (uint8)
encoder: …
error: 0 (uint32)
is_homed: False (bool)
last_drv_fault: 0 (uint32)
max_endstop: …
mechanical_brake: …
min_endstop: …
motor: …
requested_state: 0 (uint8)
sensorless_estimator: …
step_dir_active: False (bool)
steps: 0 (int64)
task_times: …
trap_traj: …
watchdog_feed(obj: object_ref)
axis1:
acim_estimator: …
config: …
controller: …
current_state: 1 (uint8)
encoder: …
error: 0 (uint32)
is_homed: False (bool)
last_drv_fault: 0 (uint32)
max_endstop: …
mechanical_brake: …
min_endstop: …
motor: …
requested_state: 0 (uint8)
sensorless_estimator: …
step_dir_active: False (bool)
steps: 0 (int64)
task_times: …
trap_traj: …
watchdog_feed(obj: object_ref)
brake_resistor_armed: True (bool)
brake_resistor_current: 0.0 (float)
brake_resistor_saturated: False (bool)
can:
config: …
error: 0 (uint8)
clear_errors(obj: object_ref)
config:
brake_resistance: 2.0 (float)
dc_bus_overvoltage_ramp_end: 59.92000198364258 (float)
dc_bus_overvoltage_ramp_start: 59.92000198364258 (float)
dc_bus_overvoltage_trip_level: 59.92000198364258 (float)
dc_bus_undervoltage_trip_level: 8.0 (float)
dc_max_negative_current: -25.0 (float)
dc_max_positive_current: 20.0 (float)
enable_brake_resistor: True (bool)
enable_can_a: True (bool)
enable_dc_bus_overvoltage_ramp: False (bool)
enable_i2c_a: False (bool)
enable_uart_a: True (bool)
enable_uart_b: False (bool)
enable_uart_c: False (bool)
error_gpio_pin: 0 (uint32)
gpio10_mode: 11 (uint8)
gpio11_mode: 2 (uint8)
gpio12_mode: 12 (uint8)
gpio13_mode: 12 (uint8)
gpio14_mode: 2 (uint8)
gpio15_mode: 7 (uint8)
gpio16_mode: 7 (uint8)
gpio1_mode: 4 (uint8)
gpio1_pwm_mapping: …
gpio2_mode: 4 (uint8)
gpio2_pwm_mapping: …
gpio3_analog_mapping: …
gpio3_mode: 3 (uint8)
gpio3_pwm_mapping: …
gpio4_analog_mapping: …
gpio4_mode: 3 (uint8)
gpio4_pwm_mapping: …
gpio5_mode: 3 (uint8)
gpio6_mode: 0 (uint8)
gpio7_mode: 0 (uint8)
gpio8_mode: 0 (uint8)
gpio9_mode: 11 (uint8)
max_regen_current: 25.0 (float)
uart0_protocol: 3 (uint8)
uart1_protocol: 3 (uint8)
uart2_protocol: 3 (uint8)
uart_a_baudrate: 115200 (uint32)
uart_b_baudrate: 115200 (uint32)
uart_c_baudrate: 115200 (uint32)
usb_cdc_protocol: 3 (uint8)
enter_dfu_mode(obj: object_ref)
erase_configuration(obj: object_ref)
error: 0 (uint8)
fw_version_major: 0 (uint8)
fw_version_minor: 5 (uint8)
fw_version_revision: 4 (uint8)
fw_version_unreleased: 0 (uint8)
get_adc_voltage(obj: object_ref, gpio: uint32) → voltage: float
get_dma_status(obj: object_ref, stream_num: uint8) → status: uint32
get_drv_fault(obj: object_ref) → drv_fault: uint64
get_gpio_states(obj: object_ref) → status: uint32
get_interrupt_status(obj: object_ref, irqn: int32) → status: uint32
hw_version_major: 3 (uint8)
hw_version_minor: 6 (uint8)
hw_version_variant: 56 (uint8)
ibus: 0.0 (float)
ibus_report_filter_k: 1.0 (float)
misconfigured: False (bool)
n_evt_control_loop: 970331 (uint32)
n_evt_sampling: 970337 (uint32)
oscilloscope:
get_val(obj: object_ref, index: uint32) → val: float
size: 4096 (uint32)
otp_valid: True (bool)
reboot(obj: object_ref)
save_configuration(obj: object_ref) → success: bool
serial_number: 59872788820273 (uint64)
system_stats:
i2c: …
max_stack_usage_analog: 308 (uint32)
max_stack_usage_axis: 540 (uint32)
max_stack_usage_can: 216 (uint32)
max_stack_usage_startup: 524 (uint32)
max_stack_usage_uart: 284 (uint32)
max_stack_usage_usb: 412 (uint32)
min_heap_space: 47432 (uint32)
prio_analog: -2 (int32)
prio_axis: 3 (int32)
prio_can: 0 (int32)
prio_startup: 0 (int32)
prio_uart: 0 (int32)
prio_usb: 0 (int32)
stack_size_analog: 1024 (uint32)
stack_size_axis: 2048 (uint32)
stack_size_can: 1024 (uint32)
stack_size_startup: 2048 (uint32)
stack_size_uart: 4096 (uint32)
stack_size_usb: 4096 (uint32)
uptime: 121418 (uint32)
usb: …
task_timers_armed: False (bool)
task_times:
control_loop_checks: …
control_loop_misc: …
dc_calib_wait: …
sampling: …
test_function(obj: object_ref, delta: int32) → cnt: int32
test_property: 0 (uint32)
user_config_loaded: 30262 (uint32)
vbus_voltage: 24.599340438842773 (float)

Are you doing everything in this order?

use_index = true
pre_calibrated = false
encoder index search
encoder offset calibration
set pre_calibrated = True
save_configuration (forces reboot)

encoder index search
closed loop