Can't find the limit preventing higher power

I’m using two ODrive Pros. They mechanically push against each other, one with torque commands, the other with speed commands. This works at small torques.

I was trying to scale up to higher torque, and I’ve hit some sort of limit, well before I expected to. The limit is around 10A, which seems like the kind of number that I (or someone else) would have used as a limit. But I’ve been through the configs and tried to change all the current limits to be 14A or higher, and none of them change the result. I don’t see a torque limit that isn’t infinity. And the speed the other motor has them running at is below the speed limits of both motors.

I believe a limit in the ODrive is the problem because of the difference between “set point” and “effective set point” for torque. The set point rises above 10A-worth of torque as I ask for it. The effective set point hits a cap, somewhere around 10A of torque, and deviates from the setpoint. So the ODrive seems to be deciding not to output more current.

I’ve tried this using the GUI to control, and I’ve tried this using analog voltage inputs (which our application is going to use). Both have the same limit.

Here is my odrivetool backup-config output. I’ve navigated this, the Python properties, and the GUI as best I can, looking for a limit I’ve missed.

Can someone point me at the problem?

{
  "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": 7,
  "axis0.commutation_mapper.config.index_offset": 0.0,
  "axis0.commutation_mapper.config.index_offset_valid": false,
  "axis0.commutation_mapper.config.offset": 0.0,
  "axis0.commutation_mapper.config.offset_valid": true,
  "axis0.commutation_mapper.config.passive_index_search": false,
  "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.I_bus_soft_max": Infinity,
  "axis0.config.I_bus_soft_min": -Infinity,
  "axis0.config.P_bus_soft_max": Infinity,
  "axis0.config.P_bus_soft_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_bandwidth": NaN,
  "axis0.config.commutation_encoder": 8,
  "axis0.config.dir_gpio_pin": 9,
  "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": 10,
  "axis0.config.index_search_at_target_vel_only": false,
  "axis0.config.load_encoder": 8,
  "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": Infinity,
  "axis0.config.torque_soft_min": -Infinity,
  "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": 14.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": 0,
  "axis0.config.can.encoder_msg_rate_ms": 10,
  "axis0.config.can.error_msg_rate_ms": 0,
  "axis0.config.can.heartbeat_msg_rate_ms": 100,
  "axis0.config.can.iq_msg_rate_ms": 0,
  "axis0.config.can.node_id": 63,
  "axis0.config.can.powers_msg_rate_ms": 0,
  "axis0.config.can.temperature_msg_rate_ms": 0,
  "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": 14.0,
  "axis0.config.motor.current_control_bandwidth": 1000.0,
  "axis0.config.motor.current_hard_max": 60.0,
  "axis0.config.motor.current_slew_rate_limit": 10000.0,
  "axis0.config.motor.current_soft_max": 40.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.7786434292793274,
  "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.00024816382210701704,
  "axis0.config.motor.phase_inductance_valid": true,
  "axis0.config.motor.phase_resistance": 0.06482724845409393,
  "axis0.config.motor.phase_resistance_valid": true,
  "axis0.config.motor.pole_pairs": 10,
  "axis0.config.motor.power_torque_report_filter_bandwidth": 8000.0,
  "axis0.config.motor.resistance_calib_max_voltage": 16.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.1969047635793686,
  "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": 1,
  "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.use_load_encoder_for_commutation_vel": false,
  "axis0.controller.config.vel_gain": 0.1666666716337204,
  "axis0.controller.config.vel_integrator_gain": 0.3333333432674408,
  "axis0.controller.config.vel_integrator_limit": Infinity,
  "axis0.controller.config.vel_limit": 13.0,
  "axis0.controller.config.vel_limit_tolerance": 1.384615421295166,
  "axis0.controller.config.vel_ramp_rate": 10.0,
  "axis0.enable_pin.config.debounce_ms": 50,
  "axis0.enable_pin.config.enabled": false,
  "axis0.enable_pin.config.gpio_num": 11,
  "axis0.enable_pin.config.is_active_high": false,
  "axis0.enable_pin.config.offset": 0.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": 3,
  "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": 7,
  "axis0.pos_vel_mapper.config.index_offset": 0.0,
  "axis0.pos_vel_mapper.config.index_offset_valid": false,
  "axis0.pos_vel_mapper.config.offset": 0.0,
  "axis0.pos_vel_mapper.config.offset_valid": false,
  "axis0.pos_vel_mapper.config.passive_index_search": false,
  "axis0.pos_vel_mapper.config.scale": 0.10000000149011612,
  "axis0.pos_vel_mapper.config.use_endstop": false,
  "axis0.pos_vel_mapper.config.use_index_gpio": false,
  "axis0.trap_traj.config.accel_limit": 0.5,
  "axis0.trap_traj.config.decel_limit": 0.5,
  "axis0.trap_traj.config.vel_limit": 2.0,
  "can.config.baud_rate": 250000,
  "can.config.protocol": 0,
  "config.dc_bus_overvoltage_trip_level": 55.0,
  "config.dc_bus_undervoltage_trip_level": 30.0,
  "config.dc_max_negative_current": -30.0,
  "config.dc_max_positive_current": 30.0,
  "config.enable_uart_a": false,
  "config.gpio0_mode": 17,
  "config.gpio10_mode": 17,
  "config.gpio11_mode": 17,
  "config.gpio12_mode": 17,
  "config.gpio13_mode": 17,
  "config.gpio14_mode": 17,
  "config.gpio15_mode": 17,
  "config.gpio16_mode": 17,
  "config.gpio17_mode": 17,
  "config.gpio18_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.user_config_0": 0,
  "config.user_config_1": 0,
  "config.user_config_2": 0,
  "config.user_config_3": 0,
  "config.user_config_4": 0,
  "config.user_config_5": 0,
  "config.user_config_6": 0,
  "config.user_config_7": 0,
  "config.gpio15_analog_mapping.endpoint": null,
  "config.gpio15_analog_mapping.max": 0.0,
  "config.gpio15_analog_mapping.min": 0.0,
  "config.gpio16_analog_mapping.endpoint": null,
  "config.gpio16_analog_mapping.max": 0.0,
  "config.gpio16_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.gpio9_pwm_mapping.endpoint": null,
  "config.gpio9_pwm_mapping.max": 0.0,
  "config.gpio9_pwm_mapping.min": 0.0,
  "config.inverter0.current_hard_max": 150.0,
  "config.inverter0.current_soft_max": 100.0,
  "config.inverter0.drv_config": 9029553772700800,
  "config.inverter0.mod_magn_max": 0.8651593923568726,
  "config.inverter0.shunt_conductance": 1999.9998779296875,
  "config.inverter0.temp_limit_lower": 83.95999908447266,
  "config.inverter0.temp_limit_upper": 103.11000061035156,
  "config.motor_fan.enabled": false,
  "config.motor_fan.lower": 70.0,
  "config.motor_fan.upper": 80.0,
  "config.odrv_fan.enabled": false,
  "config.odrv_fan.lower": 70.0,
  "config.odrv_fan.upper": 80.0,
  "hall_encoder0.config.edge0": 0.7532078623771667,
  "hall_encoder0.config.edge1": 0.5818825960159302,
  "hall_encoder0.config.edge2": 0.41558584570884705,
  "hall_encoder0.config.edge3": 0.25480058789253235,
  "hall_encoder0.config.edge4": 0.08079959452152252,
  "hall_encoder0.config.edge5": 0.9166572690010071,
  "hall_encoder0.config.edges_calibrated": true,
  "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,
  "hall_encoder1.config.edge0": 0.56243896484375,
  "hall_encoder1.config.edge1": 1.401298464324817e-45,
  "hall_encoder1.config.edge2": 0.0,
  "hall_encoder1.config.edge3": 0.0,
  "hall_encoder1.config.edge4": 0.0,
  "hall_encoder1.config.edge5": 1.1479437019748901e-41,
  "hall_encoder1.config.edges_calibrated": false,
  "hall_encoder1.config.enabled": true,
  "hall_encoder1.config.hall_polarity_calibrated": false,
  "hall_encoder1.config.hall_polarity": 0,
  "hall_encoder1.config.ignore_illegal_hall_state": false,
  "inc_encoder0.config.cpr": 8192,
  "inc_encoder0.config.enabled": false,
  "inc_encoder1.config.cpr": 8192,
  "inc_encoder1.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": 0,
  "spi_encoder0.config.ncs_gpio": 17,
  "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": 17
}

I figured out the solution before I posted my question (as is often the case when I write out a question), but I’ve decided to post it, and a solution, StackOverflow-style, in case it helps others.

The answer is from this forum post from 2015 [Odrive pro limited to 20 amps in GUI while in torque control mode].

While in torque control mode, the velocity limit is enabled by default with enable_torque_mode_vel_limit (axis0.controller.config.enable_torque_mode_vel_limit). This includes not only an actual limit (which I was well below), but also the vel_gain (axis0.controller.config.vel_gain) that goes with velocity control. This is used to “derate the torque vs speed”, and this link to the docs is given [Controller — ODrive Documentation 0.6.11 documentation].

My solution was to change my vel_gain from 0.167 to 0.900. This let me 6x the torque and current before it hits this derating.

I also had to raise the spinout detection thresholds to not panic all the time. I imagine that the motor’s response to current is confusing to the ODrive, since it is also being pushed by the other motor. I didn’t hit these detection thresholds until my current got higher.

So thanks for the 2015 question and answer!

Great to hear you figured it out!

I think one of the reasons you’re also getting spinout is because you’re using a hall encoder – those are really low resolution, so they introduce some lag to the encoder estimator, leading to false spinouts especially under high acceleration.