Golfcart one person

I did but also the servo.h did not work.
But I try again you never know.
Today I did some tests with PWM.h and took the frequency down to 200Hz. But not to 50Hz.
I will test 50 Hz

I tested 50 Hz NO success but I can see on the behaviour of motors that something has changed. Both where running for 2 sec and that was it
There must be something with the firmware
I checked again with osiliscoop and the PWM signal was very nice and at 50Hz.
I will try with UART but difficult to understand (for me)
What is the command for velocity?
That is the only command I want to transfer

Did you check the errors after you tried 50Hz and it failed? It’s always possible that it’s a firmware issue, but we have many other people successfully using 1000-2000us control on two axes with no issue…

Keep in mind, a high time of 1000 microseconds gives you the min command. 1500 microseconds is neutral. 2000 microseconds is max command.

The command for velocity is v <axis> <value>, for example v 0 1.234 will command axis 0 to go 1.234 turns/sec.

Great news
I tried again with Servo.h and it works now. I did something wrong the first time?
Thanks a lot.
I will start with road test soon. This is with a proto proto than start with final model.


When I want to use the Odrive for my cart it is not good that motor is in closed loop. When one of the wheels are more or less blocked it tries to spin to its position. But with a differential steering my cart is going mad.
How can I put it in open loop?
So far I understand I need the Hall sensors in open loop to give the right timing to let motor spin. Like used in an electric bike.

If you are using it in velocity mode, try setting controller.vel_integrator_gain=0
That way the velocity controller will not try to ‘catch up’ after it gets blocked.

Indeed now it thus not try to catch up.
But I have no power on the wheels. They run but no torque
When I play with config. Vel_gain up to 0,1
The Odrive goes out
On 0,08 some more power but nothing compared with the Amazon bike controller I got with the hub wheels

This are the wheels I ordered from Wheelway.
With this I could not do the differential steering.
The wheels are 36volt 800watt

What error does it show? If you connect USB and use the dump_errors command

Do you need to be in velocity mode? Would torque mode (with a velocity limit) work better for your golf cart?

I tried torque mode but no scces but I know that I still not understand the odrive yet.
So far the problem with spinning back is solved that is ok! THX
Today I tried
1-odrv0.axis0.controller.config.control_mode = CONTROL_MODE_TORQUE_CONTROL
2-odrv0.axis0.requested_state = CONTROL_MODE_TORQUE_CONTROL
3- odrv0.axis0.controller.input_torque = ( here i tried different settings up to 10?)
4- tried to enable_current_mode_vel_limit = false but command was not valid
But the power on the motor stays the same.
I feel that I am very close to the solution if I get the power out of the motors I can start with the final development of my cart. Picture is schowing ruff test model to test the software.
I believe I need a lesson to understand the odrive and his commands.

Set vel_integrator_gain

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===================