Spinout on hoverboard motors

I have been tweaking the tuning and parameters for a couple days, but keep getting a Spinout error. Running 2 8" hoverboard motors on Odrive 3.6, firmware and odrivetools updated to 0.5.2. Power is from 2 Milwaukee LiPo batteries in series for about 40V. This is on a lawnmower frame with casters in front and motors on rear.
Have completed initial motor and encoder calibration, and subsequent tuning.

My tuning values:
odrv0.axis0.controller.config.pos_gain = 1.5
odrv0.axis0.controller.vel_gain = 5.0
odrv0.axis0.controller.config.vel_integrator_gain = 50

I see in the release notes for 0.5.2 that there are these new parameters, but I don’t know where to begin changing them:
mechanical_power_bandwidth, electrical_power_bandwidth, spinout_electrical_power_threshold, spinout_mechanical_power_threshold added to controller.config for spinout detection.

The problem arises when driving on bumpy lawn and one of the motors loses traction and spins.

This is the error I’m getting:

dump_errors(odrv0)
system: no error
axis0
  axis: no error
  motor: Error(s):
    MOTOR_ERROR_UNKNOWN_TORQUE
    MOTOR_ERROR_UNKNOWN_VOLTAGE_COMMAND
  sensorless_estimator: no error
  encoder: no error
  controller: Error(s):
    CONTROLLER_ERROR_SPINOUT_DETECTED
axis1
  axis: no error
  motor: Error(s):
    MOTOR_ERROR_UNKNOWN_TORQUE
    MOTOR_ERROR_UNKNOWN_VOLTAGE_COMMAND
  sensorless_estimator: no error
  encoder: no error
  controller: Error(s):
    CONTROLLER_ERROR_SPINOUT_DETECTED

and here is my axis0 config:

odrv0.axis0
Out[8]: 
acim_estimator:
  config: ...
  phase_offset: 0.0 (float)
  rotor_flux: 0.0 (float)
  slip_vel: 0.0 (float)
  stator_phase: 2.0783329010009766 (float)
  stator_phase_vel: 0.0 (float)
config:
  calibration_lockin: ...
  can: ...
  dir_gpio_pin: 2 (uint16)
  enable_sensorless_mode: False (bool)
  enable_step_dir: False (bool)
  enable_watchdog: False (bool)
  general_lockin: ...
  sensorless_ramp: ...
  startup_closed_loop_control: True (bool)
  startup_encoder_index_search: False (bool)
  startup_encoder_offset_calibration: False (bool)
  startup_homing: False (bool)
  startup_motor_calibration: False (bool)
  step_dir_always_on: False (bool)
  step_gpio_pin: 1 (uint16)
  watchdog_timeout: 0.0 (float)
controller:
  anticogging_valid: False (bool)
  autotuning: ...
  config: ...
  electrical_power: -12.752242088317871 (float)
  error: 128 (uint8)
  input_pos: 0.0 (float)
  input_torque: 0.0 (float)
  input_vel: 0.01676177978515625 (float)
  last_error_time: 144.9705047607422 (float)
  mechanical_power: -2.788583944006386e-43 (float)
  move_incremental(obj: object_ref, displacement: float, from_input_pos: bool)
  pos_setpoint: 0.0 (float)
  start_anticogging_calibration(obj: object_ref)
  torque_setpoint: 0.0 (float)
  trajectory_done: True (bool)
  vel_integrator_torque: 12.80757999420166 (float)
  vel_setpoint: 0.01676177978515625 (float)
current_state: 1 (uint8)
encoder:
  calib_scan_response: 0.0 (float)
  config: ...
  count_in_cpr: 56 (int32)
  delta_pos_cpr_counts: -5.605193857299268e-45 (float)
  error: 0 (uint16)
  hall_state: 2 (uint8)
  index_found: False (bool)
  interpolation: 0.5 (float)
  is_ready: True (bool)
  phase: 2.0783329010009766 (float)
  pos_abs: 0 (int32)
  pos_circular: 0.6330385804176331 (float)
  pos_cpr_counts: 56.975059509277344 (float)
  pos_estimate: -3.3669183254241943 (float)
  pos_estimate_counts: -303.02264404296875 (float)
  set_linear_count(obj: object_ref, count: int32)
  shadow_count: -304 (int32)
  spi_error_rate: 0.0 (float)
  vel_estimate: 0.0 (float)
  vel_estimate_counts: 0.0 (float)
error: 0 (uint32)
is_homed: False (bool)
last_drv_fault: 0 (uint32)
max_endstop:
  config: ...
  endstop_state: False (bool)
mechanical_brake:
  config: ...
  engage(obj: object_ref)
  release(obj: object_ref)
min_endstop:
  config: ...
  endstop_state: False (bool)
motor:
  DC_calib_phA: -0.9716607928276062 (float)
  DC_calib_phB: 0.6928533911705017 (float)
  DC_calib_phC: 0.27865874767303467 (float)
  I_bus: 0.0 (float)
  config: ...
  current_control: ...
  current_meas_phA: 0.97132807970047 (float)
  current_meas_phB: -0.6932101845741272 (float)
  current_meas_phC: -0.2786383628845215 (float)
  effective_current_lim: 25.0 (float)
  error: 4563402752 (uint64)
  fet_thermistor: ...
  is_armed: False (bool)
  is_calibrated: True (bool)
  last_error_time: 144.91799926757812 (float)
  max_allowed_current: 30.375 (float)
  max_dc_calib: 3.0375001430511475 (float)
  motor_thermistor: ...
  n_evt_current_measurement: 1931844 (uint32)
  n_evt_pwm_update: 1931852 (uint32)
  phase_current_rev_gain: 0.012500000186264515 (float)
requested_state: 0 (uint8)
sensorless_estimator:
  config: ...
  error: 0 (uint8)
  phase: -1.6794039011001587 (float)
  phase_vel: -8.6188294517342e-05 (float)
  pll_pos: -1.6794037818908691 (float)
  vel_estimate: -3.286079845565837e-06 (float)
step_dir_active: False (bool)
steps: 0 (int64)
task_times:
  acim_estimator_update: ...
  can_heartbeat: ...
  controller_update: ...
  current_controller_update: ...
  current_sense: ...
  dc_calib: ...
  encoder_update: ...
  endstop_update: ...
  motor_update: ...
  open_loop_controller_update: ...
  pwm_update: ...
  sensorless_estimator_update: ...
  thermistor_update: ...
trap_traj:
  config: ...
watchdog_feed(obj: object_ref)

Starting to dig into this, here is my controller config:

odrv0.axis0.controller
Out[5]: 
anticogging_valid: False (bool)
autotuning:
  frequency: 0.0 (float)
  pos_amplitude: 0.0 (float)
  pos_phase: 0.0 (float)
  torque_amplitude: 0.0 (float)
  torque_phase: 0.0 (float)
  vel_amplitude: 0.0 (float)
  vel_phase: 0.0 (float)
config:
  anticogging: ...
  axis_to_mirror: 255 (uint8)
  circular_setpoint_range: 1.0 (float)
  circular_setpoints: False (bool)
  control_mode: 2 (uint8)
  electrical_power_bandwidth: 20.0 (float)
  enable_current_mode_vel_limit: True (bool)
  enable_gain_scheduling: False (bool)
  enable_overspeed_error: True (bool)
  enable_vel_limit: True (bool)
  gain_scheduling_width: 10.0 (float)
  homing_speed: 0.25 (float)
  inertia: 0.0 (float)
  input_filter_bandwidth: 2.0 (float)
  input_mode: 1 (uint8)
  load_encoder_axis: 0 (uint8)
  mechanical_power_bandwidth: 20.0 (float)
  mirror_ratio: 1.0 (float)
  pos_gain: 1.5 (float)
  spinout_electrical_power_threshold: 10.0 (float)
  spinout_mechanical_power_threshold: -10.0 (float)
  steps_per_circular_range: 1024 (int32)
  torque_mirror_ratio: 0.0 (float)
  torque_ramp_rate: 0.009999999776482582 (float)
  vel_gain: 5.0 (float)
  vel_integrator_gain: 50.0 (float)
  vel_limit: 10.0 (float)
  vel_limit_tolerance: 1.2000000476837158 (float)
  vel_ramp_rate: 1.0 (float)
electrical_power: -0.03492157906293869 (float)
error: 0 (uint8)
input_pos: 0.0 (float)
input_torque: 0.0 (float)
input_vel: 0.0 (float)
last_error_time: 0.0 (float)
mechanical_power: 0.0 (float)
move_incremental(obj: object_ref, displacement: float, from_input_pos: bool)
pos_setpoint: 0.0 (float)
start_anticogging_calibration(obj: object_ref)
torque_setpoint: 0.0 (float)
trajectory_done: True (bool)
vel_integrator_torque: 0.0 (float)
vel_setpoint: 0.0 (float)

I found this from issue #462 during implementation of Spinout detection:

Incorrect encoder offset can lead to an uncontrolled spinout situation due to inadvertent flipped sign of torque production. Incorrect encoder offset can occur due to missed pulses in an incremental encoder, or general incorrect calibration.

I don’t believe I am missing encoder pulses (am using the built in Hall sensors), as both motors will produce the same error, under the same conditions.

I found this post with same errors:
https://discourse.odriverobotics.com/t/multiple-errors-when-during-trap-moves-with-high-accelerations/7512

He was advised:

You’re accelerating harder than the current controller can keep up. Try increasing motor.config.current_control_bandwidth to 1500 or 2000.

I will try changing my odrv0.axis0.motor.config.current_control_bandwidth and see how that goes, currently = 100.

odrv0.axis0.motor
Out[2]: 
DC_calib_phA: -1.0117568969726562 (float)
DC_calib_phB: 0.7242457866668701 (float)
DC_calib_phC: 0.2878829836845398 (float)
I_bus: 0.0004915696335956454 (float)
config:
  I_bus_hard_max: inf (float)
  I_bus_hard_min: -inf (float)
  I_leak_max: 0.10000000149011612 (float)
  R_wL_FF_enable: False (bool)
  acim_autoflux_attack_gain: 10.0 (float)
  acim_autoflux_decay_gain: 1.0 (float)
  acim_autoflux_enable: False (bool)
  acim_autoflux_min_Id: 10.0 (float)
  acim_gain_min_flux: 10.0 (float)
  bEMF_FF_enable: False (bool)
  calibration_current: 10.0 (float)
  current_control_bandwidth: 100.0 (float)
  current_lim: 25.0 (float)
  current_lim_margin: 8.0 (float)
  dc_calib_tau: 0.20000000298023224 (float)
  inverter_temp_limit_lower: 100.0 (float)
  inverter_temp_limit_upper: 120.0 (float)
  motor_type: 0 (uint8)
  phase_inductance: 0.0003976746229454875 (float)
  phase_resistance: 0.228278249502182 (float)
  pole_pairs: 15 (int32)
  pre_calibrated: True (bool)
  requested_current_range: 25.0 (float)
  resistance_calib_max_voltage: 4.0 (float)
  torque_constant: 0.5168750286102295 (float)
  torque_lim: inf (float)
current_control:
  I_measured_report_filter_k: 1.0 (float)
  Ialpha_measured: -0.4351482391357422 (float)
  Ibeta_measured: 0.050543807446956635 (float)
  Id_measured: 0.5140417814254761 (float)
  Id_setpoint: 0.0 (float)
  Iq_measured: 0.0461774542927742 (float)
  Iq_setpoint: 0.0 (float)
  Vd_setpoint: 0.0 (float)
  Vq_setpoint: 0.0 (float)
  final_v_alpha: -0.06178823113441467 (float)
  final_v_beta: 0.017945382744073868 (float)
  i_gain: 22.82782554626465 (float)
  p_gain: 0.03976746276021004 (float)
  phase: -0.016060352325439453 (float)
  phase_vel: 0.0 (float)
  power: 0.01660437136888504 (float)
  v_current_control_integral_d: -0.07354786247015 (float)
  v_current_control_integral_q: 0.01986502856016159 (float)
current_meas_phA: -0.07055532932281494 (float)
current_meas_phB: -0.14246290922164917 (float)
current_meas_phC: 0.19290730357170105 (float)
effective_current_lim: 25.0 (float)
error: 0 (uint64)
fet_thermistor:
  config: ...
  temperature: 34.1611213684082 (float)
is_armed: True (bool)
is_calibrated: True (bool)
last_error_time: 0.0 (float)
max_allowed_current: 30.375 (float)
max_dc_calib: 3.0375001430511475 (float)
motor_thermistor:
  config: ...
  temperature: 0.0 (float)
n_evt_current_measurement: 663201 (uint32)
n_evt_pwm_update: 663206 (uint32)
phase_current_rev_gain: 0.012500000186264515 (float)
1 Like

I set

odrv0.axis0.motor.config.current_control_bandwidth = 1000

This gave an improvement in torque and acceleration before spinout. Tried going all the way up to 2000 with no real gains.

Still getting these same errors:

dump_errors(odrv0)
system: no error
axis0
  axis: no error
  motor: Error(s):
    MOTOR_ERROR_UNKNOWN_TORQUE
    MOTOR_ERROR_UNKNOWN_VOLTAGE_COMMAND
  sensorless_estimator: no error
  encoder: no error
  controller: Error(s):
    CONTROLLER_ERROR_SPINOUT_DETECTED
axis1
  axis: no error
  motor: Error(s):
    MOTOR_ERROR_UNKNOWN_TORQUE
    MOTOR_ERROR_UNKNOWN_VOLTAGE_COMMAND
  sensorless_estimator: no error
  encoder: no error
  controller: Error(s):
    CONTROLLER_ERROR_SPINOUT_DETECTED

Ah yeah, try tweaking the encoder bandwidth. What’s it set to now?

encoder.config.bandwidth = 100. That must bee the default as I have not changed it.

100 is the setting we recommend for hall effect sensors but the default is 1000