Motor stalled without any resistance

Hi,

I’m encountering a strange behaviour on an O-drive S1 (running FW 0.6.8) where the motor is commanded to move to a new position, but sometimes doesn’t move at all. Since there is a large position error, the controller sends full current (up to the soft current limit) to the motor, but the motor nevertheless doesn’t rotate.

This is not a binding or friction issue since the motor will easily conduct the motion (well under the current limit), but then spontaneously “lock up” a moment later. No error is reported – the motor simply remains stationary. To be clear, the motor is also not trying to hold position either. When this situation occurs, it is able to be rotated by hand but just feels like a constant DC current is being pushed through the coils.

Also, the problem appears more frequently when the system is tuned more aggressively. If the position/velocity gains are reduced substantially the problem occurs much less frequently.

Some notes about the setup:

  • The motor is an 8 pole pair BLDC motor
  • Commutation is handled by hall effect sensors in the motor
  • Velocity and position are sensed by an AS5047 magnetic encoder put on the load (not the motor shaft). This load does not have a fixed ratio with the motor, but varies between 100:1 and 180:1; i.e. it is not a fixed ratio gearbox, but instead drives a mechanism that has a somewhat variable “reduction ratio”. That said, the effective reduction ratio is always positive (rotating the motor CW will always rotate the load CW)
  • There is almost no backlash in the mechanism

I suspect putting an encoder on the motor shaft itself will help, but unfortunately that would be a bit invasive at this point in the project. Are there any other things I can look at to see what the controller is “thinking” in these circumstances?

Hi there,

Can you post your configuration? Does this issue still happen if you use the motor’s hall effect sensors as both the commutation and load encoder? Have you run both hall polarity and phase calibration, with the motor under no load?

This may also be a regression that was fixed in a more recent firmware version, I’d strongly suggest updating to 0.6.10

Upgrading to 0.6.10 only mildly solves the problem. With 0.6.10, the drive will still “stall” randomly, but will recover after about 10-20 seconds after the target position is changed. By contrast, with 0.6.8, the motor stays stalled indefinitely until the drive is rebooted.

All calibration has done been with a (purely friction) load. I’ll try disconnecting the motor from the mechanisms for the calibrations you mentioned.

I was able to temporarily place an encoder on the motor and use it for commutation and load (this is not a practical change to the hardware, though) and did not see any problems surface, but I didn’t test too extensively so I can’t say for sure.

The config is pasted below:

{
“axis0.commutation_mapper.config.approx_init_pos”: 0.0,
“axis0.commutation_mapper.config.approx_init_pos_valid”: false,
“axis0.commutation_mapper.config.circular_output_range”: 1.0,
“axis0.commutation_mapper.config.circular”: true,
“axis0.commutation_mapper.config.index_gpio”: 10,
“axis0.commutation_mapper.config.index_offset”: 0.0,
“axis0.commutation_mapper.config.index_offset_valid”: false,
“axis0.commutation_mapper.config.index_search_always_on”: false,
“axis0.commutation_mapper.config.offset”: 0.0,
“axis0.commutation_mapper.config.offset_valid”: true,
“axis0.commutation_mapper.config.scale”: 1.0,
“axis0.commutation_mapper.config.use_endstop”: false,
“axis0.commutation_mapper.config.use_index_gpio”: false,
“axis0.config.I_bus_hard_max”: Infinity,
“axis0.config.I_bus_hard_min”: -Infinity,
“axis0.config.calib_range”: 0.019999999552965164,
“axis0.config.calib_scan_distance”: 8.0,
“axis0.config.calib_scan_vel”: 2.0,
“axis0.config.commutation_encoder”: 8,
“axis0.config.dir_gpio_pin”: 5,
“axis0.config.enable_error_gpio”: false,
“axis0.config.enable_step_dir”: false,
“axis0.config.enable_watchdog”: false,
“axis0.config.encoder_bandwidth”: 100.0,
“axis0.config.error_gpio_pin”: 6,
“axis0.config.index_search_at_target_vel_only”: false,
“axis0.config.load_encoder”: 5,
“axis0.config.startup_closed_loop_control”: false,
“axis0.config.startup_encoder_index_search”: false,
“axis0.config.startup_encoder_offset_calibration”: false,
“axis0.config.startup_homing”: false,
“axis0.config.startup_max_wait_for_ready”: 3.0,
“axis0.config.startup_motor_calibration”: false,
“axis0.config.step_dir_always_on”: false,
“axis0.config.step_gpio_pin”: 8,
“axis0.config.torque_soft_max”: 0.12999999523162842,
“axis0.config.torque_soft_min”: -0.12999999523162842,
“axis0.config.watchdog_timeout”: 0.0,
“axis0.config.anticogging.calib_bidirectional”: true,
“axis0.config.anticogging.calib_coarse_integrator_gain”: 10.0,
“axis0.config.anticogging.calib_coarse_tuning_duration”: 60.0,
“axis0.config.anticogging.calib_end_vel”: 0.15000000596046448,
“axis0.config.anticogging.calib_fine_dist_scale”: 1.0,
“axis0.config.anticogging.calib_fine_tuning_duration”: 120.0,
“axis0.config.anticogging.calib_start_vel”: 1.0,
“axis0.config.anticogging.enabled”: false,
“axis0.config.anticogging.max_torque”: 0.15000000596046448,
“axis0.config.calibration_lockin.accel”: 3.183098793029785,
“axis0.config.calibration_lockin.current”: 2.0,
“axis0.config.calibration_lockin.ramp_distance”: 0.5,
“axis0.config.calibration_lockin.ramp_time”: 0.4000000059604645,
“axis0.config.calibration_lockin.vel”: 6.36619758605957,
“axis0.config.can.bus_voltage_msg_rate_ms”: 500,
“axis0.config.can.encoder_msg_rate_ms”: 5,
“axis0.config.can.error_msg_rate_ms”: 500,
“axis0.config.can.heartbeat_msg_rate_ms”: 100,
“axis0.config.can.iq_msg_rate_ms”: 20,
“axis0.config.can.is_extended”: false,
“axis0.config.can.node_id”: 0,
“axis0.config.can.temperature_msg_rate_ms”: 500,
“axis0.config.can.torques_msg_rate_ms”: 0,
“axis0.config.can.version_msg_rate_ms”: 0,
“axis0.config.general_lockin.accel”: 20.0,
“axis0.config.general_lockin.current”: 10.0,
“axis0.config.general_lockin.finish_distance”: 100.0,
“axis0.config.general_lockin.finish_on_distance”: false,
“axis0.config.general_lockin.finish_on_vel”: false,
“axis0.config.general_lockin.initial_pos”: 0.0,
“axis0.config.general_lockin.ramp_distance”: 3.1415927410125732,
“axis0.config.general_lockin.ramp_time”: 0.4000000059604645,
“axis0.config.general_lockin.vel”: 40.0,
“axis0.config.motor.acim_autoflux_attack_gain”: 10.0,
“axis0.config.motor.acim_autoflux_decay_gain”: 1.0,
“axis0.config.motor.acim_autoflux_enable”: false,
“axis0.config.motor.acim_autoflux_min_Id”: 10.0,
“axis0.config.motor.acim_gain_min_flux”: 10.0,
“axis0.config.motor.acim_nominal_slip_vel”: 2.3399999141693115,
“axis0.config.motor.bEMF_FF_enable”: false,
“axis0.config.motor.calibration_current”: 2.0,
“axis0.config.motor.current_control_bandwidth”: 1000.0,
“axis0.config.motor.current_hard_max”: 20.0,
“axis0.config.motor.current_slew_rate_limit”: 10000.0,
“axis0.config.motor.current_soft_max”: 1.0,
“axis0.config.motor.direction”: 1.0,
“axis0.config.motor.ff_pm_flux_linkage”: 0.0,
“axis0.config.motor.ff_pm_flux_linkage_valid”: false,
“axis0.config.motor.fw_enable”: false,
“axis0.config.motor.fw_fb_bandwidth”: 500.0,
“axis0.config.motor.fw_mod_setpoint”: 0.6103333234786987,
“axis0.config.motor.motor_model_l_d”: 0.0,
“axis0.config.motor.motor_model_l_dq_valid”: false,
“axis0.config.motor.motor_model_l_q”: 0.0,
“axis0.config.motor.motor_type”: 0,
“axis0.config.motor.phase_inductance”: 0.00012565572978928685,
“axis0.config.motor.phase_inductance_valid”: true,
“axis0.config.motor.phase_resistance”: 0.31849393248558044,
“axis0.config.motor.phase_resistance_valid”: true,
“axis0.config.motor.pole_pairs”: 8,
“axis0.config.motor.resistance_calib_max_voltage”: 4.0,
“axis0.config.motor.sensorless_observer_gain”: 1000.0,
“axis0.config.motor.sensorless_pll_bandwidth”: 1000.0,
“axis0.config.motor.sensorless_pm_flux_linkage”: 0.0,
“axis0.config.motor.sensorless_pm_flux_linkage_valid”: false,
“axis0.config.motor.torque_constant”: 0.03659291937947273,
“axis0.config.motor.wL_FF_enable”: false,
“axis0.config.sensorless_ramp.accel”: 31.83098793029785,
“axis0.config.sensorless_ramp.current”: 10.0,
“axis0.config.sensorless_ramp.finish_distance”: 15.915493965148926,
“axis0.config.sensorless_ramp.finish_on_distance”: false,
“axis0.config.sensorless_ramp.finish_on_vel”: true,
“axis0.config.sensorless_ramp.initial_pos”: 0.0,
“axis0.config.sensorless_ramp.ramp_distance”: 0.5,
“axis0.config.sensorless_ramp.ramp_time”: 0.4000000059604645,
“axis0.config.sensorless_ramp.vel”: 63.6619758605957,
“axis0.controller.config.absolute_setpoints”: false,
“axis0.controller.config.circular_setpoint_range”: 1.0,
“axis0.controller.config.circular_setpoints”: false,
“axis0.controller.config.commutation_vel_scale”: 1.0,
“axis0.controller.config.control_mode”: 3,
“axis0.controller.config.enable_gain_scheduling”: false,
“axis0.controller.config.enable_overspeed_error”: true,
“axis0.controller.config.enable_torque_mode_vel_limit”: true,
“axis0.controller.config.enable_vel_limit”: true,
“axis0.controller.config.gain_scheduling_width”: 0.0010000000474974513,
“axis0.controller.config.homing_speed”: 0.25,
“axis0.controller.config.inertia”: 0.0,
“axis0.controller.config.input_filter_bandwidth”: 20.0,
“axis0.controller.config.input_mode”: 1,
“axis0.controller.config.pos_gain”: 20.0,
“axis0.controller.config.spinout_electrical_power_bandwidth”: 20.0,
“axis0.controller.config.spinout_electrical_power_threshold”: 10.0,
“axis0.controller.config.spinout_mechanical_power_bandwidth”: 20.0,
“axis0.controller.config.spinout_mechanical_power_threshold”: -10.0,
“axis0.controller.config.steps_per_circular_range”: 1024,
“axis0.controller.config.torque_ramp_rate”: 0.009999999776482582,
“axis0.controller.config.use_commutation_vel”: false,
“axis0.controller.config.vel_gain”: 2.0,
“axis0.controller.config.vel_integrator_gain”: 1.0,
“axis0.controller.config.vel_integrator_limit”: Infinity,
“axis0.controller.config.vel_limit”: 2.0,
“axis0.controller.config.vel_limit_tolerance”: 1.875,
“axis0.controller.config.vel_ramp_rate”: 4.0,
“axis0.interpolator.config.dynamic”: true,
“axis0.max_endstop.config.debounce_ms”: 50,
“axis0.max_endstop.config.enabled”: false,
“axis0.max_endstop.config.gpio_num”: 0,
“axis0.max_endstop.config.is_active_high”: false,
“axis0.max_endstop.config.offset”: 0.0,
“axis0.mechanical_brake.config.gpio_num”: 0,
“axis0.mechanical_brake.config.is_active_low”: true,
“axis0.min_endstop.config.debounce_ms”: 50,
“axis0.min_endstop.config.enabled”: false,
“axis0.min_endstop.config.gpio_num”: 0,
“axis0.min_endstop.config.is_active_high”: false,
“axis0.min_endstop.config.offset”: 0.0,
“axis0.motor.motor_thermistor.config.beta”: 0.0,
“axis0.motor.motor_thermistor.config.enabled”: false,
“axis0.motor.motor_thermistor.config.gpio_pin”: 4,
“axis0.motor.motor_thermistor.config.r_ref”: 0.0,
“axis0.motor.motor_thermistor.config.t_ref”: 25.0,
“axis0.motor.motor_thermistor.config.temp_limit_lower”: 100.0,
“axis0.motor.motor_thermistor.config.temp_limit_upper”: 120.0,
“axis0.pos_vel_mapper.config.approx_init_pos”: 0.0,
“axis0.pos_vel_mapper.config.approx_init_pos_valid”: false,
“axis0.pos_vel_mapper.config.circular_output_range”: 1.0,
“axis0.pos_vel_mapper.config.circular”: false,
“axis0.pos_vel_mapper.config.index_gpio”: 10,
“axis0.pos_vel_mapper.config.index_offset”: 0.0,
“axis0.pos_vel_mapper.config.index_offset_valid”: false,
“axis0.pos_vel_mapper.config.index_search_always_on”: false,
“axis0.pos_vel_mapper.config.offset”: 0.0,
“axis0.pos_vel_mapper.config.offset_valid”: false,
“axis0.pos_vel_mapper.config.scale”: 1.0,
“axis0.pos_vel_mapper.config.use_endstop”: false,
“axis0.pos_vel_mapper.config.use_index_gpio”: false,
“axis0.trap_traj.config.accel_limit”: 4.0,
“axis0.trap_traj.config.decel_limit”: 0.5,
“axis0.trap_traj.config.vel_limit”: 2.0,
“can.config.baud_rate”: 1000000,
“can.config.protocol”: 1,
“config.dc_bus_overvoltage_trip_level”: 30.0,
“config.dc_bus_undervoltage_trip_level”: 18.0,
“config.dc_max_negative_current”: -9.5,
“config.dc_max_positive_current”: 9.5,
“config.enable_can_a”: true,
“config.enable_uart_a”: false,
“config.gpio0_mode”: 17,
“config.gpio10_mode”: 17,
“config.gpio11_mode”: 17,
“config.gpio12_mode”: 17,
“config.gpio1_mode”: 17,
“config.gpio2_mode”: 17,
“config.gpio3_mode”: 17,
“config.gpio4_mode”: 17,
“config.gpio5_mode”: 17,
“config.gpio6_mode”: 17,
“config.gpio7_mode”: 17,
“config.gpio8_mode”: 17,
“config.gpio9_mode”: 17,
“config.max_regen_current”: 0.0,
“config.uart0_protocol”: 3,
“config.uart_a_baudrate”: 115200,
“config.usb_cdc_protocol”: 3,
“config.brake_resistor0.dc_bus_voltage_feedback_ramp_end”: 30.0,
“config.brake_resistor0.dc_bus_voltage_feedback_ramp_start”: 26.0,
“config.brake_resistor0.enable_dc_bus_voltage_feedback”: true,
“config.brake_resistor0.enable”: true,
“config.brake_resistor0.resistance”: 2.0,
“config.gpio11_analog_mapping.endpoint”: null,
“config.gpio11_analog_mapping.max”: 0.0,
“config.gpio11_analog_mapping.min”: 0.0,
“config.gpio1_analog_mapping.endpoint”: null,
“config.gpio1_analog_mapping.max”: 0.0,
“config.gpio1_analog_mapping.min”: 0.0,
“config.gpio8_pwm_mapping.endpoint”: null,
“config.gpio8_pwm_mapping.max”: 0.0,
“config.gpio8_pwm_mapping.min”: 0.0,
“config.inverter0.current_hard_max”: 96.0,
“config.inverter0.current_soft_max”: 80.0,
“config.inverter0.drv_config”: 9029553697166464,
“config.inverter0.shunt_conductance”: 1999.9998779296875,
“config.inverter0.temp_limit_lower”: 83.95999908447266,
“config.inverter0.temp_limit_upper”: 103.11000061035156,
“hall_encoder0.config.enabled”: true,
“hall_encoder0.config.hall_polarity_calibrated”: true,
“hall_encoder0.config.hall_polarity”: 0,
“hall_encoder0.config.ignore_illegal_hall_state”: false,
“inc_encoder0.config.cpr”: 8192,
“inc_encoder0.config.enabled”: false,
“rs485_encoder_group0.config.mode”: 0,
“spi_encoder0.config.baudrate”: 1687500,
“spi_encoder0.config.biss_c_bits”: 18,
“spi_encoder0.config.delay”: 0.0,
“spi_encoder0.config.max_error_rate”: 0.004999999888241291,
“spi_encoder0.config.mode”: 2,
“spi_encoder0.config.ncs_gpio”: 12,
“spi_encoder1.config.baudrate”: 1687500,
“spi_encoder1.config.biss_c_bits”: 18,
“spi_encoder1.config.delay”: 0.0,
“spi_encoder1.config.max_error_rate”: 0.004999999888241291,
“spi_encoder1.config.mode”: 0,
“spi_encoder1.config.ncs_gpio”: 12
}

A calibration problem would match the description well. When the angle, with which the ODrive applies current, is off by around plus or minus 90° electrical (90°/pole_pairs mechanical), then the motor will feel like “freely coasting” even though the ODrive applies a significant current.

Given that the ODrive calibrates each hall sensor edge individually, the amount of bias can change as you turn the motor.

Suggestions:

  • Run calibration without load, ideally remove any gearbox from the motor to get the cleanest possible test data.

  • To simplify the problem, run in torque mode only. This way we don’t have to worry about the effect of higher level components.

  • Look at these variables in the liveplotter or GUI:

    axis0.motor.foc.phase # the electrical angle at which the current is applied
    axis0.motor.foc.Id_measured # should be around zero
    axis0.motor.foc.Iq_measured # should be your non-zero torque-generating current
    

    If I understand correctly, you can make it work well when you use an encoder different from hall effect encoder for commutation. You can use this working configuration to see how the plotted variables behave compared to the non-working configuration. Especially phase should be the same when you move the motor to the same physical location. (since this is electrical phase, it can be quite sensitive)

To calibrate the motor on its own, I called MOTOR_CALIBRATION, ENCODER_HALL_POLARITY_CALIBRATION, and ENCODER_HALL_PHASE_CALIBRATION with the motor physically disconnected. Unfortunately this did not change the behavior.

Also, I can’t run in torque mode since I do need position control, but when the motor “stalls,” changing to torque mode doesn’t release it. The same behavior occurs regardless of control mode – it pulls current but doesn’t move. Also, even if I disable the drive to change modes, the drives remains stalled after I issue a command. The drive has to be rebooted to recover.

I checked out the FOC graphs you suggested and saw nothing suspicious. ~0 Id, non-zero Iq, and a somewhat arbitrary phase. It doesn’t appear this stall condition is associated with any particular phase angle.

All this said, I re-upgraded to 0.6.10 and re-calibrated and haven’t seen any issues yet. There are some other tests I can run later that seemed to reproduce the problem more frequently, so I’ll see how those go. Perhaps some combination of using 0.6.10 and calibrating without load is the key.
EDIT: Leaving the FW at 0.6.10, I reduced the position gain (from 20 to 15) to reduce some overshoot, and immediately the problem occurred.

Ah I meant to use torque mode not for the actual application, but just for testing. But it sounds like you did that and the problem remained.

Previously I was under the impression that only a re-calibration fixes the problem, but now you’re saying that a reboot will also do.

So based on that, here’s something you can try next:

  1. When the problem occurs, lock the motor in place, e.g. with a tape.
  2. Go to IDLE.
  3. Note down some parameters. I suggest to dump all of axis0.commutation_mapper, all of axis0.motor.foc and all of hall_encoder0.
  4. Reboot the ODrive.
  5. Once again, dump all the parameters that you dumped in step (3).
  6. Enter CLOSED_LOOP_CONTROL again and send a setpoint that’s supposed to move the motor. (If it fixed itself, the tape will rip off.) If it did not fix itself, let me know because then we need to modify the experiment.

Then paste the values from before and after the reboot here so we can take a look at them.

Here’s the two data sets. The motor did move after the reboot.

First, the parameters after the motor stalls. It’s unintuitive that there is a non-zero measured Iq current even though the drive was moved to IDLE.

Connected to ODrive S1 395A35523231 (firmware v0.6.8) as odrv0
In [1]: odrv0.axis0.commutation_mapper
Out[1]:
config:
approx_init_pos: 0.0 (float)
approx_init_pos_valid: False (bool)
circular: True (bool)
circular_output_range: 1.0 (float)
index_gpio: 10 (uint8)
index_offset: 0.0 (float)
index_offset_valid: False (bool)
index_search_always_on: False (bool)
offset: 0.0 (float)
offset_valid: True (bool)
scale: 1.0 (float)
use_endstop: False (bool)
use_index_gpio: False (bool)
pos_abs: 0.4346435070037842 (float)
pos_rel: 0.2541787624359131 (float)
set_abs_pos(obj: object_ref, pos: float) → delta_pos_ref: float
status: 0 (uint8)
vel: -1333.6708984375 (float)

In [2]: odrv0.axis0.motor.foc
Out[2]:
I_measured_report_filter_k: 1.0 (float)
Id_measured: 0.06488395482301712 (float)
Id_setpoint: 0.0 (float)
Iq_measured: 2.0591654777526855 (float)
Iq_setpoint: 1.9980000257492065 (float)
Vd_setpoint: 0.0 (float)
Vq_setpoint: 0.6320343017578125 (float)
final_v_alpha: -0.9377527832984924 (float)
final_v_beta: -0.06554999202489853 (float)
i_gain: 316.33349609375 (float)
mod_d: -0.05039345100522041 (float)
mod_q: 0.032068464905023575 (float)
p_gain: 0.12733547389507294 (float)
phase: 0.4346435070037842 (float)
phase_vel: -1333.288330078125 (float)
v_current_control_integral_d: -0.7873875498771667 (float)
v_current_control_integral_q: -0.12197601795196533 (float)

In [3]: odrv0.hall_encoder0
Out[3]:
abs_pos_max: 0.599202573299408 (float)
abs_pos_min: 0.4346435070037842 (float)
config:
enabled: True (bool)
hall_polarity: 0 (uint8)
hall_polarity_calibrated: True (bool)
ignore_illegal_hall_state: False (bool)
hall_cnt: 2 (uint8)
raw_hall_state: 2 (uint8)
status: 0 (uint8)

And here’s the same output after the reboot:

Connected to ODrive S1 395A35523231 (firmware v0.6.8) as odrv0
In [1]: odrv0.axis0.commutation_mapper
Out[1]:
config:
approx_init_pos: 0.0 (float)
approx_init_pos_valid: False (bool)
circular: True (bool)
circular_output_range: 1.0 (float)
index_gpio: 10 (uint8)
index_offset: 0.0 (float)
index_offset_valid: False (bool)
index_search_always_on: False (bool)
offset: 0.0 (float)
offset_valid: True (bool)
scale: 1.0 (float)
use_endstop: False (bool)
use_index_gpio: False (bool)
pos_abs: 0.5169230699539185 (float)
pos_rel: 0.0 (float)
set_abs_pos(obj: object_ref, pos: float) → delta_pos_ref: float
status: 0 (uint8)
vel: 0.0 (float)

In [2]: odrv0.axis0.motor.foc
Out[2]:
I_measured_report_filter_k: 1.0 (float)
Id_measured: 0.0 (float)
Id_setpoint: 0.0 (float)
Iq_measured: 0.0 (float)
Iq_setpoint: -0.8437021374702454 (float)
Vd_setpoint: 0.0 (float)
Vq_setpoint: -0.2768382430076599 (float)
final_v_alpha: 0.0 (float)
final_v_beta: 0.0 (float)
i_gain: 316.33349609375 (float)
mod_d: 0.0 (float)
mod_q: 0.0 (float)
p_gain: 0.12733547389507294 (float)
phase: 0.5169230699539185 (float)
phase_vel: 0.0 (float)
v_current_control_integral_d: 0.0 (float)
v_current_control_integral_q: 0.0 (float)

In [3]: odrv0.hall_encoder0
Out[3]:
abs_pos_max: 0.599202573299408 (float)
abs_pos_min: 0.4346435070037842 (float)
config:
enabled: True (bool)
hall_polarity: 0 (uint8)
hall_polarity_calibrated: True (bool)
ignore_illegal_hall_state: False (bool)
hall_cnt: 2 (uint8)
raw_hall_state: 2 (uint8)
status: 0 (uint8)

Cool that’s useful data.

Briefly up-front: When FOC stops (in IDLE), most FOC-related variables, such as Id,q, are frozen at the last value that they had. That can indeed be unintuitive, but at the same time it’s also helpful to debug problems.

Not looking at your data, these are my observations:

  • Hall state unchanged before and after reboot. This hall state spans from 0.43464350700378420.599202573299408, based on the hall edge calibration. (abs_pos_min, abs_pos_max).
  • FOC phase before reboot: 0.4346435070037842 (identical to the lower edge of this hall state’s range)
  • FOC phase after reboot: 0.5169230699539185 (almost identical to the middle of this hall state’s range)
  • phase velocity: -1333.6708984375

The velocity seems a bit high at first sight, but maybe I’m forgetting something, anyway I don’t think this is a direct cause, because it has no influence on the lower level beyond what we can see in that output. The phase estimate before and after reboot seems plausible. Before reboot, the estimate is “pressed against the next lower hall edge” by the non-zero velocity estimate (it’s expected that this can happen). After reboot, it’s re-initialized to the middle of the hall state.

Now some considerations:

  • When the phase estimate is off by ± 90° (0.25 rev), it results in zero actual torque despite non-zero Iq. When the phase estimate is off by even more, it results in a flipped torque sign, i.e. uncontrolled spin-out (until spinout detection kicks in).
  • The nominal width of a hall state is 60°. The hall state width in your data is 0.599202573299408 - 0.4346435070037842 = 0.1645590663 which is reasonably close to 1/6 rev.
  • When the phase estimate sits at an edge, with perfect calibration, it’s off by 30°. That means there’s 60° margin from there to the zero-torque position.

That means if your calibration is off by 60° you would end up in a zero-torque state. However in reality you need additional torque to overcome fiction, so a smaller calibration error could be enough to get you to a stalled state.

So far it seems that the behavior matches a calibration problem. So the next thing I would suggest is to calibrate without load. Otherwise, to confirm the suspicion, you can also try to hack/shift the hall edges by modifying hall_encoder0.config.edge0,1,...,5. Furthermore, plotting phase_vel could tell us if there’s any obvious issue with that.

To add on - what motor is this specifically? I see “axis0.config.calibration_lockin.current”: 2.0, which seems quite low, can you raise this to the maximum 180 second current of your motor, and make sure you’re calibrating under absolutely no load?

It’s a small Nanotec 65W motor. I did calibrate it detached from the load (just a bare motor) and that did not help, but I’ll try again at a higher calibration current (3.3 A).

Can you confirm that calling MOTOR_CALIBRATION , ENCODER_HALL_POLARITY_CALIBRATION , and ENCODER_HALL_PHASE_CALIBRATION with the motor disconnected are the only calibrations required?

Correct, that’s all the calibration you should need. Usually peak current is given as a 180-second number, so it should be fine to run at around 5A for the calibration.