Hi everyone,
I’m using an ODrive v3.6 with firmware version 0.5.4, and I have successfully calibrated two hoverboard motors with hall encoders. I’m trying to control these motors using PWM signals:
GPIO4 for throttle (forward/reverse).
GPIO3 for steering (left/right).
I’ve written a Python script (below) to map the PWM signals on GPIO4 and GPIO3 to control motor velocities (axis0.controller.input_vel and axis1.controller.input_vel). The goal is to combine the throttle and steering inputs so the left and right motors adjust speeds accordingly (e.g., for turning).
However, it doesn’t seem to work as expected:
- The motors don’t respond correctly to the PWM signals.
- I’m unsure if my approach to normalizing the PWM signals and combining them into left/right velocities is correct.
Setup Details:
ODrive v3.6, firmware 0.5.4.
Hoverboard motors with hall encoders.
Motors calibrated successfully.
GPIO4 and GPIO3 configured as PWM inputs with a range of -2 to 2.
Here’s what I’ve tried so far:
- Calibrated the motors with hall encoders (using
AXIS_STATE_MOTOR_CALIBRATIONandAXIS_STATE_ENCODER_OFFSET_CALIBRATIONand 'AXIS_STATE_HALL_POLARITY_CALIBRATION). - Configured GPIO3 and GPIO4 as PWM inputs.
- Tested the script to read PWM signals using
get_adc_voltage.
Questions:
- Is it possible to directly map two independent PWM channels (GPIO4 for throttle, GPIO3 for steering) to control the velocities of two axes as I’m trying to do?
- Am I correctly using the ODrive configuration (e.g.,
gpio3_pwm_mapping,gpio4_pwm_mapping)? - Are there additional steps I need to take in the firmware or Python API to achieve this setup?
Here’s the Python script I’ve been testing:
python
import odrive
from odrive.enums import GPIO_MODE_PWM, AXIS_STATE_CLOSED_LOOP_CONTROL
import time
Connect to the ODrive
print(“Connecting to ODrive…”)
odrv0 = odrive.find_any()
print(“ODrive connected!”)
Configure GPIO4 for throttle (forward/reverse)
print(“Configuring GPIO4 for throttle (forward/reverse)…”)
odrv0.config.gpio4_mode = GPIO_MODE_PWM
odrv0.config.gpio4_pwm_mapping.min = -2 # Minimum PWM range
odrv0.config.gpio4_pwm_mapping.max = 2 # Maximum PWM range
Configure GPIO3 for steering (left/right)
print(“Configuring GPIO3 for steering (left/right)…”)
odrv0.config.gpio3_mode = GPIO_MODE_PWM
odrv0.config.gpio3_pwm_mapping.min = -2 # Minimum PWM range
odrv0.config.gpio3_pwm_mapping.max = 2 # Maximum PWM range
Save and reboot the ODrive configuration
print(“Saving configuration…”)
try:
odrv0.save_configuration()
except Exception as e:
print(f"Save error: {e}“)
print(“Rebooting ODrive…”)
try:
odrv0.reboot()
except Exception as e:
print(f"Reboot error: {e}”)
Wait for the ODrive to reboot
time.sleep(5)
print(“Reconnecting to ODrive…”)
odrv0 = odrive.find_any()
Activate CLOSED_LOOP_CONTROL for both axes
print(“Activating CLOSED_LOOP_CONTROL…”)
odrv0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL
odrv0.axis1.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL
print(“Configuration complete. Test PWM channels on GPIO3 and GPIO4.”)
Main loop with debugging
try:
while True:
# Read voltages from GPIO4 (throttle) and GPIO3 (steering)
throttle = odrv0.get_adc_voltage(4) # Voltage from GPIO4
steering = odrv0.get_adc_voltage(3) # Voltage from GPIO3
# Debug: print the read voltages
print(f"Read voltage: Throttle = {throttle:.2f}, Steering = {steering:.2f}")
# Normalize voltages (-2 to 2 mapped to -1.0 to 1.0)
throttle = throttle / 2 # Divide by 2 to normalize
steering = steering / 2 # Divide by 2 to normalize
# Calculate motor velocities
motor_left = throttle + steering
motor_right = throttle - steering
# Limit velocities to the range [-1, 1]
motor_left = max(-1, min(1, motor_left))
motor_right = max(-1, min(1, motor_right))
# Debug: print calculated velocities
print(f"Motor commands: Left = {motor_left:.2f}, Right = {motor_right:.2f}")
# Send velocity commands to motors
odrv0.axis0.controller.input_vel = motor_left
odrv0.axis1.controller.input_vel = motor_right
# Pause to avoid CPU overload
time.sleep(0.01)
except KeyboardInterrupt:
print(“Script stopped by user.”)
# Stop the motors
odrv0.axis0.controller.input_vel = 0
odrv0.axis1.controller.input_vel = 0