ODrive S1 Current Limit Violation Error

Hey ODrive Community,

I have been having a bit of trouble recently with running into the Current_Limit_Violation error with my setup. This error was much more noticeable before I tuned the PI loop for velocity control. The error now seems to only show up at higher speeds, or when the rover travels over rocky terrain.

The current soft max is set to 40 A continuous. For current hard max, I initially had it at 50 A, which would throw current limit violation errors intermittently. To test, I ended up setting this to 80 A, and the error would still persist intermittently.

I have also majorly reduced the velocity ramp to see if this could fix the issue, but it seems to have not.

I have a feeling that it may be something to do with needing to relax the PI settings a bit, so I will be testing these settings as a next step.

Looking for advice regarding this issue. Thank you in advance!

Current setup:

  • Four wheeled, 50 kg rover, about 1.2 m^3 in scale.
  • Running one Odrive S1 per wheel at 24 V
  • Have a 2 Ohm brake resistor on each ODrive.
  • Using Neo v1.1 motors.
  • Using Neo v1.1 motor built in hall sensor connected to the ODrive.
  • Running the setup in VEL_RAMP mode.
  • Firmware 0.6.8 on all ODrives.

Here are my settings:

Also a neat photo of the design for y’all:

The rover arm also uses ODrive s1, and has not had any issues.

Thanks for the picture and the detailed configuration and description of the issue!

A few questions:

  • Could you share the odrv0.axis0.config.motor.phase_inductance and odrv0.axis0.config.motor.phase_resistance?
  • Could you share the odrv0.axis0.config.motor.current_control_bandwidth?
  • Can you share the exact Current_Limit_Violation you experience? A screenshot or copy-paste of the output of dump_errors(odrv0) when this happens would be useful.

Hey sorry for the late response,

Here are the requested configs:

ODrive 1
odrv0.axis0.config.motor.phase_inductance = 8.781791621004231e-06
odrv0.axis0.config.motor.phase_resistance = 0.02235436998307705
odrv0.axis0.config.motor.current_control_bandwidth = 1000

ODrive 2
odrv0.axis0.config.motor.phase_inductance = 7.948866368678864e-06
odrv0.axis0.config.motor.phase_resistance = 0.022962208837270737
odrv0.axis0.config.motor.current_control_bandwidth = 1000

ODrive 3
odrv0.axis0.config.motor.phase_inductance = 9.48388424149016e-06
odrv0.axis0.config.motor.phase_resistance = 0.0224311463534832
odrv0.axis0.config.motor.current_control_bandwidth = 1000

ODrive 4
odrv0.axis0.config.motor.phase_inductance = 8.594868631917052e-06
odrv0.axis0.config.motor.phase_resistance = 0.026493245735764503
odrv0.axis0.config.motor.current_control_bandwidth = 1000

The current limit violation looks like this:

Since the last time I posted, I’ve tweaked the PID loop a bit. I noticed on the Odrive website GUI graph that current would spike to 15 A from only a bit of torque applied by hand. I tweaked the PID loop and got it to only spike to 2 A with this same torque applied.

At higher speeds, the ODrives do not produce the current limit violation error anymore, however, when climbing rocky terrain it still produces the error.

Thanks for the info! Hmm, since you’re using hall sensors, this may be tricky. I’d frankly try cranking current_hard_max as high as it’ll go (maybe try 120A)? The issue here is that the rapid acceleration/deceleration of the motor combined with the low resolution of the hall sensors is causing the internal current controller to over/undershoot. You could try also increasing the encoder_bandwidth to 250 or so.

Hey solomondg, I would like to re-open this old topic as my team is still encountering the current_limit_violation error. We have tuned the velocity PID to where the motor controller has become decently reliable.

We had attempted setting current_hard_max to infinity, and still encounter the error. Looks like there is a current_hard_max for the inverter that also throws the current_limit_violation error. Pretty sure this is what we are actually hitting. We are unable to change this value without the firmware set to developer mode. We also set the encoder bandwidth to something higher, and still encountered the issue.

Regarding the low resolution hall sensor, we really only need to move forward, backward, and skid-steer with velocity control, and adding a separate external encoder will require a change on the wheel mounting design (Which we can do if it is absolutely necessary).

Additionally, I have greatly reduced the current_slew_rate from default 10,000 to 50 and this has not helped either.

Wondering if we need to change the FOC controller gains to relax the current spikes?
https://docs.odriverobotics.com/v/latest/fibre_types/com_odriverobotics_ODrive.html#ODrive.FieldOrientedController

Also I noticed decently large current spikes while the ODrive is in closed_loop_control mode with velocity and positional gains set to zero, not sure what is causing this.

Can you run odrivetool backup-config config.json and post the resulting config.json here?

Here is the config file. Thank you. Firmware 0.6.8 btw.

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

Just noticed we have inertia set to 0 for velocity control, is this an issue?

Just an update, as we await a response, we are simultaneously upgrading hardware right now to fit new encoders with ~8000 cpr.

A few notes:

Additionally, I have greatly reduced the current_slew_rate from default 10,000 to 50 and this has not helped either.

Would not recommend doing this. Default of 10k is recommended.

Wondering if we need to change the FOC controller gains to relax the current spikes?

Will actually make them worse.

Also I noticed decently large current spikes while the ODrive is in closed_loop_control mode with velocity and positional gains set to zero, not sure what is causing this.

This is just gaussian noise in the S1’s current sense – it looks worse than it is because of the lower sample rate in the GUI. Looks pretty nominal/typical to me.

Just noticed we have inertia set to 0 for velocity control, is this an issue?

I definitely don’t think that’s the cause of the issue – that’s just for a torque feedforward while accelerating/decelerating in VEL_RAMP mode.

So, as you unfortunately know, hall sensors are very very low bandwidth. The ODrive needs the encoder angle to correctly set the current vector in the motor. If there’s a sudden acceleration/deceleration, there will be a transient error in the current vector. Here, since the halls are such low resolution, when there’s a sudden acceleration/deceleration (as I’d imagine there is many of when climbing rocky terrain) there’s a mismatch between the actual current vector and the ODrive’s calculated current vector, causing the overcurrent error.

A few levers you can pull:

  1. You can try increasing the encoder bandwidth – this’ll reduce the smoothness of your low speed control, but may help reduce the current spikes
  2. You can increase current_control_bandwidth to e.g. 2000 – this will cause the ODrive to be able to react faster to those spikes
  3. You can try enabling the three feedforward terms (only two for v0.6.8, I would recommend upgrading to v0.6.11)
  4. You can try re-running the hall phase calibration with axis0.calibration_lockin.current = 40 or so (and ensuring the robot is lifted off the ground during calibration, or motors disconnected from gearbox if possible). This will increase the accuracy of the hall calibration, in case that’s a problem.

One other thing – there was a hall bug that got fixed in 0.6.11, where the velocity estimate would go unstable on rapid deceleration. Usually we only saw that with much lower inertia / high speed motors, but maybe sudden load spikes when doing your offroading is causing that same issue. I’d definitely try upgrading and seeing how it goes.

Thank you for the in-depth response! We will add the new encoders, upgrade firmware, and follow your other recommended steps if the issue still persists.

Very much appreciate your help!

Let me know how it goes! I’d say adding additional encoders is guaranteed to work, but due to the difficulty it’s definitely worth giving the ODrive config-side options (and firmware upgrade, etc) a shot first.