Unexplainable CONTROL_DEADLINE_MISSED

Hi guys,

We’ve been reading the community posts a lot for tips and tricks, but we seem to be stuck.

We’ve got two Aerostar 800kv motors driving spindles, connected to an arm to move a wing.
Motor:

Firmware version: 0.5.1
Encoder: AMT102-V

Calibration at startup is no problem, and the motors are controllable via PWM. We’re using Input mode TRAP_TRAJ. But after a couple commands (sometimes even just 1) it goes back to Idle and drops the error:
MOTOR_ERROR_CONTROL_DEADLINE_MISSED

Config is identical for both motors:

odrv0.axis0.motor.config

pre_calibrated = True (bool)
pole_pairs = 7 (int)
calibration_current = 15.0 (float)
resistance_calib_max_voltage = 2.0 (float)
phase_inductance = 6.232279702089727e-06 (float)
phase_resistance = 0.03844253718852997 (float)
torque_constant = 0.03999999910593033 (float)
direction = 1 (int)
motor_type = 0 (int)
current_lim = 20.0 (float)
current_lim_margin = 8.0 (float)
torque_lim = inf (float)
inverter_temp_limit_lower = 100.0 (float)
inverter_temp_limit_upper = 120.0 (float)
requested_current_range = 60.0 (float)
current_control_bandwidth = 100.0 (float)
acim_slip_velocity = 14.706000328063965 (float)
acim_gain_min_flux = 10.0 (float)
acim_autoflux_min_Id = 10.0 (float)
acim_autoflux_enable = False (bool)
acim_autoflux_attack_gain = 10.0 (float)
acim_autoflux_decay_gain = 1.0 (float)

odrv0.axis0.encoder.config

mode = 0 (int)
use_index = False (bool)
find_idx_on_lockin_only = False (bool)
abs_spi_cs_gpio_pin = 1 (int)
zero_count_on_find_idx = True (bool)
cpr = 8192 (int)
offset = 4339 (int)
pre_calibrated = False (bool)
offset_float = 1.085343837738037 (float)
enable_phase_interpolation = True (bool)
bandwidth = 1000.0 (float)
calib_range = 0.10000000149011612 (float)
calib_scan_distance = 50.26548385620117 (float)
calib_scan_omega = 12.566370964050293 (float)
idx_search_unidirectional = False (bool)
ignore_illegal_hall_state = False (bool)
sincos_gpio_pin_sin = 3 (int)
sincos_gpio_pin_cos = 4 (int)

odrv0.axis0.controller.config

gain_scheduling_width = 10.0 (float)
enable_vel_limit = True (bool)
enable_current_mode_vel_limit = True (bool)
enable_gain_scheduling = False (bool)
enable_overspeed_error = True (bool)
control_mode = 3 (int)
input_mode = 5 (int)
pos_gain = 50.0 (float)
vel_gain = 0.05000000074505806 (float)
vel_integrator_gain = 1.0 (float)
vel_limit = 20.0 (float)
vel_limit_tolerance = 5.0 (float)
vel_ramp_rate = 5.0 (float)
torque_ramp_rate = 0.009999999776482582 (float)
circular_setpoints = False (bool)
circular_setpoint_range = 1.0 (float)
homing_speed = -2.0 (float)
inertia = 0.0 (float)
axis_to_mirror = 255 (int)
mirror_ratio = 1.0 (float)
load_encoder_axis = 0 (int)
input_filter_bandwidth = 2.0 (float)
anticogging:
index = 0 (int)
pre_calibrated = False (bool)
calib_anticogging = False (bool)
calib_pos_threshold = 1.0 (float)
calib_vel_threshold = 1.0 (float)
cogging_ratio = 1.0 (float)
anticogging_enabled = True (bool)

Please help,
Any tips or tricks will do

Okay, something’s working…

We’ve swapped the wiring to the motors. Which swaps the problem as well.
We’ve changed PWM input channels from GPIO3/4 to GPIO1/2, same problem.

Then we downgraded to Firmware version 0.4.12 aaand… bingo bango, no problem.
(but without TRAP_TRAJ)

Next question is:
What is the risk of commenting out the lines that generate the unexplained error (CONTROL_DEADLINE_MISSED)?

Please let us know, before we change the code and burn down everything.

Did you pull that error directly from axisX.motor.error or with dump_errors(odrv0)? dump_errors() will show all errors.

The deadline missed error is triggered if the motor PWM values are not updated in time - a likely cause is another error that causes motor PWM to not update (like controller overspeed). If it shows up alone, it means that there isn’t enough CPU time.

What PWM frequency are you using and what is the source?

1 Like

Hi @PJohnson thank you for the quick response!

Did you pull that error directly from axisX.motor.error or with dump_errors(odrv0) ?

Yes, dump_errors(odrv0).
The other error it gave was MOTOR DISARMED (caused by DEADLINE MISSED, right?)

What PWM frequency are you using and what is the source?

The source is a Embention Veronte autopilot - PWM update frequency: 50Hz
We can boost this to be 250Hz?

If it shows up alone, it means that there isn’t enough CPU time.

Do you mean on the Odrive or Veronte side?

(caused by DEADLINE MISSED, right?)

Right

We can boost this to be 250Hz?

I recommend keeping it at 50Hz if that update rate is fast enough for your application

Do you mean on the Odrive or Veronte side?

ODrive side

Looking at the code a bit more, I’m pretty sure that trapezoidal trajectory mode will not work with RC PWM input. I’m not sure why it would cause DEADLINE MISSED, but even if that error did not happen, the “goal point” for the trajectory planner is not updated by PWM inputs (or analog inputs). Would a different input mode like INPUT_MODE_POS_FILTER work for you application?

Hey @PJohnson thanks again for the response,

I’m pretty sure that trapezoidal trajectory mode will not work with RC PWM input

The strange thing is, we’ve seen it working with TRAP_TRAJ.
But after playing with it and moving it around, it would randomly throw the error.

We will try lowering the PWM update frequency.
And try the other INPUT_MODES.

Testing with an Arduino sending PWM inputs every 1000ms, making steps of 10 deg.
Works fine, but as soon as there is too much resistance it stops and throws:

  axis1
  axis: Error(s):
    ERROR_CONTROLLER_FAILED
  motor: Error(s):
    ERROR_CONTROL_DEADLINE_MISSED
  encoder: no error
  controller: Error(s):
    ERROR_OVERSPEED

Maybe the maximum time for the motor to reach its setpoint can be longer?
Is there a way to extend the DEADLINE time?

ERROR_OVERSPEED

Aha! Did you get this error before or is it new? This is caused by the velocity exceeding odrv0.axisX.controller.config.vel_limit. This error causes a deadline missed error. The wording is sort of confusing - a missed deadline is not about how long it takes a motor to reach it’s setpoint, it’s about missing a control loop cycle update (125 microseconds).

You can handle this by raising the velocity limit or disable the error by setting odrv0.axisX.controller.config.enable_overspeed_error = False

TrapTraj will work with the RC PWM input - it updates the trajectory every timestep if the input_pos has changed.

Man, after settings the odrv0.axisX.controller.config.enable_overspeed_error = False I had such good hopes, seeing it move like crazy without throwing that error.

Controlling it with an Arduino proves no problem.
But as soon as its connected to the Veronte PWM, it won’t even start with the startup_encoder_offset_calibration

We tried turning the update frequency of the Veronte down; 10Hz, 1Hz, 50Hz one axis works(for a short period), the other stops before startup.

What else can we try?

RE: Wow, realisation: Veronte PWM is 3.3V, Arduino 5V, could that cause the problem?

Not sure if this has anything to do with your problem, but are you sure your torque constant is set correctly. It currently seems to be set to the default value. Try replacing it by 8.27/(your motor’s KV)

Hi,
I think I have a very similar issue with ODrive. I also use firmware 0.5.1 (but with some minor modifications) and I get this error:
axis0
axis: Error(s):
AXIS_ERROR_MOTOR_DISARMED
motor: Error(s):
MOTOR_ERROR_CONTROL_DEADLINE_MISSED
fet_thermistor: no error
motor_thermistor: no error
encoder: no error
controller: no error
axis1
axis: no error
motor: no error
fet_thermistor: no error
motor_thermistor: no error
encoder: no error
controller: no error

and I also use INPUT_MODE_TRAP_TRAJ and I think that might be the issue here. Because when I switch to INPUT_MODE_PASSTHROUGH, the problem disappears. But I assume INPUT_MODE_TRAP_TRAJ works for others, so the issue cannot merely be that this mode is too slow, can it?

Something that I noticed too is the following: When I do not set the ack bit when I set variables over the USB Native Protocol, this issue starts happening within one second. When I set that bit, it can work for minutes.
As I understand it, the ack bit means that I expect a response. And I assumed that when I do not set it, it reduces the workload of the communication thread, but it seems to have the opposite effect.
I wrote the communication client mostly myself and there may be bugs there, but how do I find them? I changed the protocol.cpp file in fibre to log errors when they happen, but they dont when my client communicates with odrive. (by default, the communication thread just ignores errors).
It also always happens with axis0, which is strange because I give both axes always basically the same commands.
Can someone make sense of this? I am trying to fix this for days now and I still do not understand the cause.

thanks and best regards!

Thanks @Jorijs , changed that!

It’s good to see that we’re not the only one with this problem.

Changing to INPUT_MODE_PASSTHROUGH makes it work for a little while.
(now it doesnt throw the error as soon as we turn it on)

But after a few commands (sometimes even 1) it throws the error for axis0.
Axis1 seems to work perfect.

#============ MOTOR CONFIG ============    
#---- axis0 main wing ----
odrv0.axis0.motor.config.pre_calibrated = True
odrv0.axis0.motor.config.motor_type = 0                     #standard
odrv0.axis0.motor.config.pole_pairs = 7                     #standard
odrv0.axis0.motor.config.current_lim = 30                   #[standard = 10]      
odrv0.axis0.motor.config.current_lim_margin = 3             #standard
odrv0.axis0.motor.config.calibration_current = 15           #[standard = 10] 
odrv0.axis0.motor.config.current_control_bandwidth = 100    #[standard = 1000]
odrv0.axis0.motor.config.torque_constant = 0.0103375
#---- axis1 canard ----
odrv0.axis1.motor.config.pre_calibrated = True
odrv0.axis1.motor.config.motor_type = 0                     #standard
odrv0.axis1.motor.config.pole_pairs = 7                     #standard
odrv0.axis1.motor.config.current_lim = 30                   #[standard = 10]       
odrv0.axis1.motor.config.current_lim_margin = 3             #standard
odrv0.axis1.motor.config.calibration_current = 15           #[standard = 10] 
odrv0.axis1.motor.config.current_control_bandwidth = 100    #[standard = 1000]
odrv0.axis1.motor.config.torque_constant = 0.0103375

#============ ENCODER CONFIG ============
#---- axis0 main wing ----
odrv0.axis0.encoder.config.pre_calibrated = True    #[standard = False]
odrv0.axis0.encoder.config.mode = 0                 #standard
odrv0.axis0.encoder.config.cpr = 8192               #standard
odrv0.axis0.encoder.config.use_index = False        #standard
odrv0.axis0.encoder.config.calib_range = 0.1       #[standard = 0.02]
odrv0.axis0.config.step_gpio_pin = 6                #[standard = 1]
odrv0.axis0.config.dir_gpio_pin = 6                 #[standard = 2]
#---- axis1 canard ----
odrv0.axis1.encoder.config.pre_calibrated = True    #[standard = False]
odrv0.axis1.encoder.config.mode = 0                 #standard
odrv0.axis1.encoder.config.cpr = 8192               #standard
odrv0.axis1.encoder.config.use_index = False        #standard
odrv0.axis1.config.step_gpio_pin = 6                #[standard = 7]
odrv0.axis1.config.dir_gpio_pin = 6                 #[standard = 8]
odrv0.axis1.encoder.config.calib_range = 0.1       #[standard = 0.02]

#============ CONTROLLER CONFIG ============
odrv0.axis0.controller.config.enable_overspeed_error = False
odrv0.axis1.controller.config.enable_overspeed_error = False
#---- axis0 main wing ----
odrv0.axis0.controller.config.input_mode = 1            #INPUT_MODE_PASSTHROUGH   #5 INPUT_MODE_TRAP_TRAJ
odrv0.axis0.controller.config.control_mode = 3           #standard
odrv0.axis0.controller.config.pos_gain = 5               #[standard = 20]
odrv0.axis0.controller.config.vel_gain = 0.05            #[standard = 0.166]
odrv0.axis0.controller.config.vel_integrator_gain = 0.2  #[standard = 0.333]
odrv0.axis0.controller.config.vel_limit = 20             #[standard = 2]
odrv0.axis0.controller.config.vel_limit_tolerance = 5    #0 disable [standard = 1.2]
odrv0.axis0.controller.config.vel_ramp_rate = 30
#---- axis1 canard ----
odrv0.axis1.controller.config.input_mode = 1            #INPUT_MODE_PASSTHROUGH   #5 INPUT_MODE_TRAP_TRAJ
odrv0.axis1.controller.config.control_mode = 3           #standard
odrv0.axis1.controller.config.pos_gain = 5               #[standard = 20]
odrv0.axis1.controller.config.vel_gain = 0.05            #[standard = 0.166]
odrv0.axis1.controller.config.vel_integrator_gain = 0.2  #[standard = 0.333]
odrv0.axis1.controller.config.vel_limit = 20             #[standard = 2]
odrv0.axis1.controller.config.vel_limit_tolerance = 5    #0 disable [standard = 1.2]
odrv0.axis1.controller.config.vel_ramp_rate = 30         #[standard = 1.0]
#--
odrv0.axis0.trap_traj.config.vel_limit = 25
odrv0.axis0.trap_traj.config.accel_limit = 30
odrv0.axis0.trap_traj.config.decel_limit = 30
#<odrv>.<axis>.controller.config.inertia = <Float>
#--
odrv0.axis1.trap_traj.config.vel_limit = 25
odrv0.axis1.trap_traj.config.accel_limit = 30
odrv0.axis1.trap_traj.config.decel_limit = 30
#<odrv>.<axis>.controller.config.inertia = <Float>

@Wetmelon / @PJohnson anything else we can try?