Golfcart one person

We just cleared integrator gain, because the integrator winds up and interferes with steering :slight_smile:
For manual control, I think torque mode is the ‘safest’.

paul: For torque mode to work, it has to have a load - if it is on the test bench then even a very small torque will cause the motor to reach its max speed very quickly. On the ground it will be different.
You can check it by applying a very small torque e.g. 0.01, and keep on doubling it until you see a small movement. Then you can check with your hands to see how much torque it is applying.

This is a quirk of Python: You need False with a capital F. But I’d advise to leave it set to True, and adjust the vel_limit, for safety.
You could even attach vel_limit to a ‘throttle’ on a joystick - this would give you a speed limit that you can adjust as you get more confident with the machine.


I did:

odrv0.axis0.controller.config.control_mode = CONTROL_MODE_TORQUE_CONTROL

odrv0.axis0.requested_state = CONTROL_MODE_TORQUE_CONTROL


odrv0.axis0.controller.input_torque = 0.5

the motor starts spinning to set velocity. But no power. I can easily hold by hand;
put also 10 Nm but the same no power.

SECOND problem how to control with the speed throttle PWM?

sorry to trouble again.
I just don’t find it. And I feel , I am close to the solution.

What have you got for motor.config.current_lim ?

Follow this guide: RC PWM input | ODrive
So to set the mapping for torque, something like this:

odrv0.config.gpio4_pwm_mapping.endpoint = odrv0.axis0.controller._remote_attributes['input_torque']

motor.config.current_lim set to 90
for 800 watt on 36 volt should be enough?
The battery gives the power as I tested the same battery with the motor controller from wheelway, with good power. ( hoover board battery)
I don’t understand the bandwidth thing in the Odrive set up. Is it the min / max frequency that can be supplied to the coils of the motor.( for oscilloscope I understand ) I have the bandwidth set to 100

torque constant is set to 8,27/245

mapping will be ok; thx for the tip

Hi Paul,

I think that hall effect encoders + torque mode might need some special tuning. For your question about bandwidth: this sets a filter cutoff in the firmware, not the frequency sent to the coils.

For example, the motor current control bandwidth sets how fast the torque controller responds (really, the PI gains used for the current controller).

The encoder bandwidth acts like a low-pass filter on the encoder data. Velocity is the change in position over time, so if encoder bandwidth is very high and you have very few encoder counts over time, the ODrive will see velocity spikes instead of a smooth velocity measurement. For encoders with low resolution, a low encoder bandwidth setting will improve the velocity measurement.

For the torque control problem: it will be useful to compare motor current in the velocity control + vel_integrator_gain mode, and then check motor current in torque control.

Can you plot odrv0.axis0.motor.current_control.Iq_measured (torque-producing current) in odrivetool and see what it looks like when you run the motor?

You can plot like this in odrivetool -
start_liveplotter(lambda: [odrv0.axis0.motor.current_control.Iq_measured]).

Note that motor torque = Iq_measured * torque_constant, so this is a measure of how much current is actually being commanded.

Check this plot with the configuration that had good power, and then check it with the configuration that had no power.

I will check and come back.

Hallo P.Johnson,

Thx I understand the bandwidth now.

What I want:

Velocity mode with PWM to control both wheels and be able to make a differential steering.
Good torque;
Not a closed loop ( does not work for the cart as wheel spins back to position.)
When I put Integrator gain up to 1 I have very good power / torque on the wheel
I measured the current Iq.
gain 0.1 current 0.7
gain 0.5 current 2.2 good power!
gain 1 current 2.4 very good power!
In torque control mode current 0.7 and no PWM.

Is the Odrive the right controller for this application ?
So just the good power and velocity PWM and I am ready!
I would like to get it work with the Odrive.
Someone made a cart with differential steering already?
best regards

Hi Paul,

For the sake of ODrive terminology - I understand what you mean by “not closed loop” control, but ODrive is always in “closed loop”, even in torque control. It also makes sense that you would not want much integrator gain!

For your testing, what were your velocity and torque commands? The velocity control works by measuring the error (velocity command - actual velocity) and multiplying that by the gains.

The regular velocity gain acts like a spring - torque = vel_gain * (velocity command - actual velocity). So, for example, you could increase the torque output by commanding an “unrealistic” velocity! You might try setting your vel_limit very high, keep vel_gain the same, and expanding your PWM max and min settings. This should allow more torque, without increasing vel_gain or adding vel_integrator_gain.

The integrator gain acts like an accumulator - it adds up the speed error over time, so it will “grow” the torque output until it hits max current as long as there is speed error (command - actual). This is also why it tries to keep track of position, despite being in velocity control mode.

In torque control mode, the power will depend on what your max and min pwm settings are. Iq_measured should be input_torque / motor.config.torque_constant.

I think the “unrealistic” velocity commands will work for your setup, while also allowing you to have speed control. ODrive can work for this application. I am note sure if someone has already made a differential steering cart already.

1 Like

Hi Paul,

Did you get this golf cart working to your satisfaction?

I am working third donkey FD My Project Donkey and I am having response issues they seem slugish and ramp up slowly.

Below is the output of odrv0.axis0.motor and odrv0.axis0.controller ; axis1 is very similar.
Could you compare with your info and advise any big differences.

Thank you!

In [1]: odrv0.axis0.motor
DC_calib_phA: 0.6329340934753418 (float)
DC_calib_phB: -1.0414408445358276 (float)
DC_calib_phC: 0.4086739420890808 (float)
I_bus: 0.0 (float)
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.0007044867379590869 (float)
phase_resistance: 0.3324545621871948 (float)
pole_pairs: 15 (int32)
pre_calibrated: True (bool)
requested_current_range: 25.0 (float)
resistance_calib_max_voltage: 4.0 (float)
torque_constant: 0.5168750286102295 (float)
torque_lim: inf (float)
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: 33.24545669555664 (float)
p_gain: 0.07044867426156998 (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.6321917772293091 (float)
current_meas_phB: 1.0411418676376343 (float)
current_meas_phC: -0.4093523919582367 (float)
effective_current_lim: 10.0 (float)
error: 0 (uint64)
config: …
temperature: 25.642549514770508 (float)
is_armed: False (bool)
is_calibrated: True (bool)
last_error_time: 0.0 (float)
max_allowed_current: 30.375 (float)
max_dc_calib: 3.0375001430511475 (float)
config: …
temperature: 0.0 (float)
n_evt_current_measurement: 632774 (uint32)
n_evt_pwm_update: 632779 (uint32)
phase_current_rev_gain: 0.012500000186264515 (float)

===========end of odrv0.axis0.motor============

In [5]: odrv0.axis0.controller
anticogging_valid: False (bool)
frequency: 0.0 (float)
pos_amplitude: 0.0 (float)
torque_amplitude: 0.0 (float)
vel_amplitude: 0.0 (float)
autotuning_phase: 0.0 (float)
anticogging: …
axis_to_mirror: 255 (uint8)
circular_setpoint_range: 1.0 (float)
circular_setpoints: False (bool)
control_mode: 2 (uint8)
electrical_power_bandwidth: 20.0 (float)
enable_gain_scheduling: False (bool)
enable_overspeed_error: True (bool)
enable_torque_mode_vel_limit: True (bool)
enable_vel_limit: True (bool)
gain_scheduling_width: 10.0 (float)
homing_speed: 0.25 (float)
inertia: 0.0 (float)
input_filter_bandwidth: 2.0 (float)
input_mode: 1 (uint8)
load_encoder_axis: 0 (uint8)
mechanical_power_bandwidth: 20.0 (float)
mirror_ratio: 1.0 (float)
pos_gain: 1.0 (float)
spinout_electrical_power_threshold: 10.0 (float)
spinout_mechanical_power_threshold: -10.0 (float)
steps_per_circular_range: 1024 (int32)
torque_mirror_ratio: 0.0 (float)
torque_ramp_rate: 0.009999999776482582 (float)
vel_gain: 0.9303750395774841 (float)
vel_integrator_gain: 4.6518754959106445 (float)
vel_integrator_limit: inf (float)
vel_limit: 10.0 (float)
vel_limit_tolerance: 1.2000000476837158 (float)
vel_ramp_rate: 1.0 (float)
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)

==============end of odrv0.axis0.controller===================