No torque in torque mode

I carefully soldered them to the back of the ODrive PCB itself… I twisted the three ground legs together and soldered them to the GND pin at the back of the encoder header, and then soldered each remaining leg to the A,B,Z pins.

Yes, you should see a small increase every time you increase the torque. It won’t be as much as the motor current itself, but it should be very noticeable.

What values did the ODrive measure for motor.config.phase_resistance and motor.config.phase_inductance? Do they match your motor’s spec?
You could try increasing motor.config.calibration_current if you have a very low resistance motor.

I will try to add the capacitors, but i have to order them first, unfortunately…

I can’t see any increase in current during closed loop control in torque control mode. The power supply tells me, that 68mA are drawn, regardless of if i command 0, 1, 3, 10 Nm of input_torque. Interestingly, during calibration run, i see around 400mA on the power supply. The liveplotter also tells me, that no current is drawn at all, see the following scope during a time input_torque was set to 3:

phase_resistance = 0.08205939084291458
phase_inductance = 3.98140364268329e-05

My problem is, that i do not exactly know which motor i have here at the moment, because i am using a motor of my colleague. I will tomorrow try to get a better datasheet, all i know at the moment, that it’s one of the motors listed here.

My motor.config.calibration_current is set to 10A at the moment, as said before, i can see 400mA been drawn during calibration.

Yes, it’s normal to see 400mA being drawn from a 30V supply, when pushing 10A through a resistance of only 0.08 Ohms, since i^2 * R = 100*0.08 = 8 Watts.
400mA from 30V is… 12 Watts.

So after erasing config, you are still unable to drive the motor in torque mode? Very odd…
What config items did you change to get to this point? Just polepairs and cpr?

Just to check, you do have encoder.config.type = ENCODER_TYPE_HALL, right?

With a current_lim of 10A and torque_constant of 0.04, the maximum torque you can command is 10 * 0.04 = 0.4Nm. Everything above that value will be limited internally to 0.4Nm (the limit is the lesser of torque_lim and current_lim * torque_constant). It is strange that the power supply current would not increase to a value over 68mA when you command a non-zero torque.

1 Like

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?