No torque in torque mode

0.4Nm is still quite a lot of torque for an unloaded motor not to be turning. It’s about the weight of a pint of beer on the end of a 15cm ruler. :stuck_out_tongue:

It seems to me that @moaeX29’s ODrive is not sending any current to the motor in torque mode. As I say, if it really is driving 10A in any direction, he should see the same 400mA on his power supply that he sees during the calibration process. Something fishy is going on.

Thank you for your help, i really appreciate that!
@towen: these are the commands i set after erasing configuration:

odrv0.axis0.encoder.config.mode = 1
odrv0.axis0.encoder.config.cpr = 42
odrv0.axis0.motor.config.pole_pairs = 7
odrv0.save_configuration()
odrv0.reboot()

So yes, encoder type is set to Hall. No other changes than that. Complete calibration run works without errors.

@PJohnson: thanks for that hint. At least now i know, that it can’t make any difference if i command 1, 2, 3, or 100 Nm, because it’s limited to 0.4, if i understood correctly?

Generally speaking: could it be a reason for all these problems if i mixed up some wires to the motor / encoder? I didn’t have any document for my motor, so i just mixed the phases until the calibration run was successfull. I felt save with the successfull calibration run, but maybe there are some cases where calibration run succeeds but it still isn’t the right configuration?

P.S. when exiting odrivetool, i almost always get error logs like this:

Exception ignored in: <function Image.__del__ at 0x03F28F10>
Traceback (most recent call last):
  File "c:\users\maxdh\appdata\local\programs\python\python38-32\lib\tkinter\__init__.py", line 4014, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop

not that could be reason for e.g. parameters not being stored or something? If not, forget it :sweat_smile:

whoa, i found something, that might point out that i’m kind of stupid :sweat_smile:
as you described above, i set all gains to zero for tuning. Now i erased configuration, only set encoder.mode, encoder.cpr and motor.pole_pairs and i actually can feel a pull and see about 300mA been drawn on my PSU!! :partying_face: But it only works as long is firmly hold the motor, as soon as i let it rotate by 1°, the errors (see above) appear, but that is normal i guess?

So how should i then tune torque mode? If i set all gains to zero, no torque is generated…

Thank you, i am really happy for that little advancement :smiley:

I now tried to get something running in velocity control mode, set

controller.config.control_mode = CONTROL_MODE_VELOCITY_CONTROL
controller.config.input_mode= INPUT_MODE_VEL_RAMP
controller.config.vel_ramp_rate = 0.5

When i start closed loop control with input_vel = 0 it is stable, nothing is moving, no current ist drawn. As soon as i set target velocity to e.g. 5 turns/s it makes a short jerk and the following errors appear:

axis0
  axis: Error(s):
    AXIS_ERROR_CONTROLLER_FAILED
  motor: Error(s):
    MOTOR_ERROR_CONTROL_DEADLINE_MISSED
  fet_thermistor: no error
  motor_thermistor: no error
  encoder: no error
  controller: Error(s):
    CONTROLLER_ERROR_OVERSPEED

The scope looks like this (blue is current_meas_phC, orange is vel_estimate):

EDIT: Found my mistake, vel_limit was set to 2.0. I increased it to 10.0, now no error appears, but no useful rotation begins, see the following scope:

EDIT2: Now i set vel_integrator_gain = 0and vel_gain = 0.01, now it is rotating, a little bit noisy and the scope looks like this:


The velocity signal looks very strange, is it only because of the low hall sensor resolution? It looks like it only has plain values of 3, 6, 9 and 12 (when i increase velocity). So only multiples of three, no “float” values, that is strange isn’t it? Scope was recorded with ìnput_vel = 5`

EDIT3: now when i brake the motor with my hand, somehow there was the following situation:


the vel_estimate is negative, although i am 100% percentage shure the motor was still rotating in positive direction… so clearly something is wrong with encoder signal

Not exactly, no… If you let the motor move in torque mode, it should not produce any error at all. It should carry on producing that torque however you move it.
If you let go of it and it spins out of control, then you can expect to see the ERROR_OVERSPEED.
But other than that, there should be no reason for it to produce errors in torque mode, and the fact that it does, means you still have a pretty fundamental problem, most likely with your Hall sensor.

I would ignore the issues in velocity mode for now, because torque mode still isn’t behaving properly. :frowning:

hmm ok thanks… in the meantime i added the capacitors to the hall Signals, i only had 100nF but i think for the lower speeds that should work? I also received another Motor including Hall Sensor today, i will try that on monday.

It’s not to do with the speed if the motor, but the speed of the signal edges with respect to the sampling speed of the encoder input. If the capacitance is too high, it could cause ERROR_ILLEGAL_HALL_STATE. But 100nF is not too far away from 22, it should be ok.

Good luck :slight_smile:

Hi together,

today i am trying to get another motor running, this one. When i start full calibration, i get the ERROR_PHASE_RESISTANCE_OUT_OF_RANGE error. When i look it up, i see the following values:

calibration_current = 10.0 (float)
resistance_calib_max_voltage = 2.0 (float)
phase_inductance = 4.0044971683528274e-05 (float)
phase_resistance = 0.08224697411060333 (float)
torque_constant = 0.10337500274181366 (float)

When i measure resitance with my (cheap) multimeter, i measure 0.5 Ohm between all three phases…

What am i doing wrong again? :sob: Thank you very much for your help!!

Try increasing that calibration_current to 20

same behaviour… Resistance out of range… Also tested it with 30A.

Try adjusting the values here https://docs.odriverobotics.com/api/odrive.motor.error

Yeah, thank you for that hint! Calibration completed successfully. I set resistance_calib_max_voltage = 6 because calibration_current is 10A and i rougly measured 0.5 ohm resistance. The result of the motor calibration:

phase_inductance = 7.907329563749954e-05 (float)
phase_resistance = 0.1924062967300415 (float)

Are these values in a plausible range? When i measured the resistance between each of the three phases, i measured 0.5 ohm with my cheap multimeter.

Unfortunately, now i have the torque mode problems again. When activating closed loop control with torque mode and input_torque = 0 everything is stable and nothing happens. When i now slightliy rotate the motor manually, immediately the following errors appear:

axis: Error(s):
    AXIS_ERROR_CONTROLLER_FAILED
  motor: Error(s):
    MOTOR_ERROR_CONTROL_DEADLINE_MISSED
  fet_thermistor: no error
  motor_thermistor: no error
  encoder: no error
  controller: Error(s):
    CONTROLLER_ERROR_OVERSPEED

The same happens as soo as i set input_torque = 1.

Increasing the vel_limit helped, but i am still wondering why the encoder measures such i high velocity, although the wheel is barely turning. I am assuming the reason could be the low resolution of the hall sensor?

As so can see here, when i turn the motor manually, i only get vel_estimates in multiples of around 3 (vel_estimate_counts is around 120). My CPR is 42, so ~120 / 42 = ~3. Does that mean my best resolution i can get of a velocity is 3 U/s?

Maybe phase_resistance is talking about the resistance of one phase, not two.
When you measure with your multimeter, you are measuring two phases in series (for a star-wound motor, which are the vast majority of motors)
In that case, you should expect to see 0.25 Ohm, and 0.198 is not so far off.

As for your velocity issue… Check if encoder.config.enable_phase_interpolation is set.

That is normal with hall effect sensors. The encoder estimator acts like a filter, so you can change odrv0.axis0.encoder.config.bandwidth to get better low speed behavior. The stock value is 1000.0f (radians/s I think? might be Hz), a lower value will give better low speed behavior.

The problem is - the ODrive will have executed many, many, many, control loop cycles between one hall effect count and the next one. It’s basically like smacking a train car with a hammer to get it to move, but the train car is so light that it starts and stops between hammer-smacks. Bandwidth here is like train car weight.

1 Like

Has this issue been solved? I can not find the solution above. I am experiencing the same problem: torque control mode is not working.

I have an odrive v3.6 56V and a 25V LiPo battery as power source. Fully charged, odrv0.vbus_voltage = 26V.
The green LED is on, and the 3.3V and 5V signals from the board are OK.
22nF capacitors connected to the Hall sensor inputs of the board.

I am first following the Getting Started guide.
By accident I do have a hoverboard BLDC motor with internal Hall sensors, with the exact connections like in the guide. I am using the following commands to set axis1 into velocity control, which works fine:

odrv0.erase_configuration()

odrv0.config.enable_brake_resistor = False
odrv0.config.dc_max_negative_current = -1
odrv0.config.max_regen_current = 40

odrv0.axis1.motor.config.pole_pairs = 15
odrv0.axis1.motor.config.resistance_calib_max_voltage = 4
odrv0.axis1.motor.config.requested_current_range = 50
odrv0.axis1.motor.config.current_control_bandwidth = 100
odrv0.axis1.motor.config.torque_constant = 16 

odrv0.axis1.encoder.config.mode = ENCODER_MODE_HALL
odrv0.axis1.encoder.config.cpr = 90
odrv0.axis1.encoder.config.calib_scan_distance = 150

odrv0.axis1.encoder.config.bandwidth = 100
odrv0.axis1.controller.config.pos_gain = 1
odrv0.axis1.controller.config.vel_limit = 10  # 1000


odrv0.axis1.controller.config.vel_gain = 0.02 * odrv0.axis1.motor.config.torque_constant * odrv0.axis1.encoder.config.cpr
odrv0.axis1.controller.config.vel_integrator_gain = 0.1 * odrv0.axis1.motor.config.torque_constant * odrv0.axis1.encoder.config.cpr

odrv0.save_configuration()  # includes a reboot

odrv0.axis1.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE
odrv0.axis1.motor.config.pre_calibrated = True
odrv0.axis1.encoder.config.pre_calibrated = True

odrv0.save_configuration()

During calibration the motor makes several noises and rotations in both directions, while doing the three calibrations (motor, Hall_polarity, and Encoder_offset)

Afterwards, odrv0.axis1.motor gives:

DC_calib_phA: 0.17508473992347717 (float)
DC_calib_phB: 0.08516408503055573 (float)
DC_calib_phC: -0.260256826877594 (float)
I_bus: 0.0 (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: 10.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.0003803673607762903 (float)
  phase_resistance: 0.18802085518836975 (float)
  pole_pairs: 15 (int32)
  pre_calibrated: True (bool)
  requested_current_range: 50.0 (float)
  resistance_calib_max_voltage: 4.0 (float)
  torque_constant: 16.0 (float)
  torque_lim: inf (float)
current_control:
  I_measured_report_filter_k: 1.0 (float)
  Ialpha_measured: 0.0 (float)
  Ibeta_measured: 0.0 (float)
  Id_measured: 0.0 (float)
  Id_setpoint: 0.0 (float)
  Iq_measured: 0.0 (float)
  Iq_setpoint: 0.0 (float)
  Vd_setpoint: 0.0 (float)
  Vq_setpoint: 0.0 (float)
  final_v_alpha: 0.0 (float)
  final_v_beta: 0.0 (float)
  i_gain: 18.802085876464844 (float)
  p_gain: 0.038036737591028214 (float)
  phase: 0.0 (float)
  phase_vel: 0.0 (float)
  power: 0.0 (float)
  v_current_control_integral_d: 0.0 (float)
  v_current_control_integral_q: 0.0 (float)
current_meas_phA: -0.17588825523853302 (float)
current_meas_phB: -0.08383689075708389 (float)
current_meas_phC: 0.2596939504146576 (float)
effective_current_lim: 10.0 (float)
error: 0 (uint64)
fet_thermistor:
  config: ...
  temperature: 22.07196044921875 (float)
is_armed: False (bool)
is_calibrated: True (bool)
last_error_time: 0.0 (float)
max_allowed_current: 60.75 (float)
max_dc_calib: 6.075000286102295 (float)
motor_thermistor:
  config: ...
  temperature: 0.0 (float)
n_evt_current_measurement: 4043377 (uint32)
n_evt_pwm_update: 4043381 (uint32)
phase_current_rev_gain: 0.02500000037252903 (float)

odrv0.axis1.encoder gives:

calib_scan_response: 0.0 (float)
config:
  abs_spi_cs_gpio_pin: 1 (uint16)
  bandwidth: 100.0 (float)
  calib_range: 0.019999999552965164 (float)
  calib_scan_distance: 150.0 (float)
  calib_scan_omega: 12.566370964050293 (float)
  cpr: 90 (int32)
  direction: -1 (int32)
  enable_phase_interpolation: True (bool)
  find_idx_on_lockin_only: False (bool)
  hall_polarity: 0 (uint8)
  hall_polarity_calibrated: True (bool)
  ignore_illegal_hall_state: False (bool)
  index_offset: 0.0 (float)
  mode: 1 (uint16)
  phase_offset: -69 (int32)
  phase_offset_float: -0.47774702310562134 (float)
  pre_calibrated: True (bool)
  sincos_gpio_pin_cos: 4 (uint16)
  sincos_gpio_pin_sin: 3 (uint16)
  use_index: False (bool)
  use_index_offset: True (bool)
count_in_cpr: 2 (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: 0.023302078247070312 (float)
pos_abs: 0 (int32)
pos_circular: 0.033057015389204025 (float)
pos_cpr_counts: 2.975128173828125 (float)
pos_estimate: 0.03305697813630104 (float)
pos_estimate_counts: 2.975128173828125 (float)
set_linear_count(obj: object_ref, count: int32)
shadow_count: 2 (int32)
spi_error_rate: 0.0 (float)
vel_estimate: 0.0 (float)
vel_estimate_counts: 0.0 (float)

odrv0.axis1 gives:

acim_estimator:
  config: ...
  phase_offset: 0.0 (float)
  rotor_flux: 0.0 (float)
  slip_vel: 0.0 (float)
  stator_phase: 0.0 (float)
  stator_phase_vel: 0.0 (float)
config:
  calibration_lockin: ...
  can: ...
  dir_gpio_pin: 8 (uint16)
  enable_sensorless_mode: False (bool)
  enable_step_dir: False (bool)
  enable_watchdog: False (bool)
  general_lockin: ...
  sensorless_ramp: ...
  startup_closed_loop_control: False (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: 7 (uint16)
  watchdog_timeout: 0.0 (float)
controller:
  anticogging_valid: False (bool)
  autotuning: ...
  autotuning_phase: 0.0 (float)
  config: ...
  electrical_power: 0.0 (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)
current_state: 1 (uint8)
encoder:
  calib_scan_response: 0.0 (float)
  config: ...
  count_in_cpr: 2 (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: 0.023302078247070312 (float)
  pos_abs: 0 (int32)
  pos_circular: 0.033057015389204025 (float)
  pos_cpr_counts: 2.975128173828125 (float)
  pos_estimate: 0.03305697813630104 (float)
  pos_estimate_counts: 2.975128173828125 (float)
  set_linear_count(obj: object_ref, count: int32)
  shadow_count: 2 (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.16147330403327942 (float)
  DC_calib_phB: 0.0936511978507042 (float)
  DC_calib_phC: -0.25501498579978943 (float)
  I_bus: 0.0 (float)
  config: ...
  current_control: ...
  current_meas_phA: -0.1618221402168274 (float)
  current_meas_phB: -0.09373145550489426 (float)
  current_meas_phC: 0.255417138338089 (float)
  effective_current_lim: 10.0 (float)
  error: 0 (uint64)
  fet_thermistor: ...
  is_armed: False (bool)
  is_calibrated: True (bool)
  last_error_time: 0.0 (float)
  max_allowed_current: 60.75 (float)
  max_dc_calib: 6.075000286102295 (float)
  motor_thermistor: ...
  n_evt_current_measurement: 1350525 (uint32)
  n_evt_pwm_update: 1350529 (uint32)
  phase_current_rev_gain: 0.02500000037252903 (float)
requested_state: 0 (uint8)
sensorless_estimator:
  config: ...
  error: 0 (uint8)
  phase: 0.0 (float)
  phase_vel: 0.0 (float)
  pll_pos: 0.0 (float)
  vel_estimate: 0.0 (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)

Then I turn the volocity control on, this way:

odrv0.axis1.controller.config.control_mode = CONTROL_MODE_VELOCITY_CONTROL
odrv0.axis1.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL
odrv0.axis1.controller.input_vel = 2

And then the motor spins fine at 2 turns/sec continuously. I can hold the motor by hand to feel the torque increasing, upto a maximum current (odrv0.axis1.current_state) of 10A.

And then I try to put it in torque mode with the following commands:

odrv0.axis1.controller.input_vel = 0
odrv0.axis1.requested_state = AXIS_STATE_IDLE

odrv0.axis1.controller.config.pos_gain = 0
odrv0.axis1.controller.config.vel_gain = 0
odrv0.axis1.controller.config.vel_integrator_gain = 0
odrv0.axis1.controller.config.control_mode = CONTROL_MODE_TORQUE_CONTROL
odrv0.axis1.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL
odrv0.axis1.current_state  # It actually is in closed_loop (8)
odrv0.axis1.controller.input_torque = 0.1

Result: nothing happens, no sound, no movements, no spinning, no torque, no errors, I can freely rotate the motor.
I have repeated everything with axis0 and that gives the same result; no torque control (and velocity control OK).

Can anybody tell what I am missing please?

Ok right off the bat, this part is wrong:

odrv0.config.enable_brake_resistor = False
odrv0.config.dc_max_negative_current = -1
odrv0.config.max_regen_current = 40

That should be dc_max_negative_current = -41 (-40 - 1)

Check controller.config.enable_torque_mode_vel_limit = False, or increase the vel_limit. Also, double check your torque_constant (and 0.1Nm is not a lot of torque)

vel_limit = 10 was already included above, and I already tried enable_torque_mode_vel_limit=False, and now I also increased the dc_max_negative_current, which by the way I only increased because in velocity control I got an over-current error (even without any external load, just while accelerating and decelerating the motor).
And even at input_torque=1 nothing happens. No torque, rotation, or sound, whatsoever on either axis0 or axis1.
How to check the torque_constant? I just used what was written in the getting started and hoverboard guides.

I re-read the getting started guide. I decided to set odrv0.axis0.motor.config.torque_constant = 1 to enable setting torque in amps. that did the trick. It now starts turning at input_torque = 0.4. And if I increase it, then the torque (or revs) increase. Great, thank you.

1 Like

Googling, I found a nice website: Tutorial: How to measure the KV of a brushless motor – fishpepper.de. Other sites say that KV=16 seems to be an average value for these motors, so I still don’t understand why I didn’t get any torque with that.
Oh, correction. If I increase the input from a minimum of 0.4A (to get spinning) to 6Nm, then it is also working with torque_constant=KV=16.

1 Like