Hello,
I’m new to ODrive, and I was hoping to have it control the drive system of the RC UGV I’m building, which is powered by two 20V cordless drills.
I have the drill’s ESC which obviously runs the motor perfectly fine, but my issue is that I want to run the motor at a slower speed than what the ESC can run it (the slowest speed the ESC allows is ~150rpm before it cuts out to 0; I want at least 20-40rpm as the slowest speed). I was hoping to achieve a ‘creep’ speed function utilizing an ODrive V3.6-56v board.
I have no information on the motor, but I measured the resistance between each phase and it 2.4 ohms exactly between all phases.
I did run some test codes, had my motor successfully ‘beep’ at me, but it doesn’t move at all (Although I have had the motor move a tik (I think it moved the next pole. It moved very little) when running the code, but when I re-ran it, it beeped but didn’t move. I even tried manually spinning the motor with no luck. (I see the amperage go up on my power supply before the motor beeps; The highest being ~1.5A then dropping down to ~0.87A after the motor beeps then drops to 0A)
I’m completely lost and any help on getting my motor running with this board would be greatly appreciated.
Thanks in advance.
Drill Motor:
Wiring Setup:
Output from the code I used (originally I was using 20V but I bumped it to 24v because I heard that’s the operating voltage the ODrive board likes):
=== ODrive Sensorless Motor Setup (FW 0.5.1) ===
Searching for ODrive...
Found ODrive!
Firmware version: 0.5.1
Erasing existing configuration...
Expected disconnection: Device 355434653034 disconnected
Waiting for ODrive to reboot...
Reconnecting to ODrive...
Reconnected successfully!
Configuring for FW 0.5.1...
Configuring motor...
Motor configuration successful
Configuring sensorless control...
Encoder set to NONE mode for sensorless
Controller configuration successful
Saving configuration...
Configuration saved successfully!
Reconnecting after save...
Reconnected after save!
Starting motor calibration...
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 4
Calibrating... State: 1
Motor calibration complete!
Phase resistance: 0.101556 Ohms
Phase inductance: 0.000045 H
Starting sensorless control...
Trying alternative control state...
Sensorless control active! State: 1
=== Starting Motor Demo ===
Current state: IDLE
Velocity: 0.00 RPS
Motor current: 0.00 A
Bus voltage: 24.19 V
AXIS ERROR: 1
Attempting to start motor at 3 RPS (180 RPM)...
Motor not in closed loop! Current state: 1
Starting sensorless control...
Trying alternative control state...
Sensorless control active! State: 1
Velocity set to 3.0 RPS (180.0 RPM)
Current state: 1, Actual vel: 0.00 RPS
Current state: IDLE
Velocity: 0.00 RPS
Motor current: 0.00 A
Bus voltage: 24.19 V
AXIS ERROR: 1
Motor not spinning. Trying higher velocity...
Motor not in closed loop! Current state: 1
Starting sensorless control...
Trying alternative control state...
Sensorless control active! State: 1
Velocity set to 8.0 RPS (480.0 RPM)
Current state: 1, Actual vel: 0.00 RPS
Current state: IDLE
Velocity: 0.00 RPS
Motor current: 0.00 A
Bus voltage: 24.25 V
AXIS ERROR: 1
Motor still not spinning. Trying 15 RPS...
Motor not in closed loop! Current state: 1
Starting sensorless control...
Trying alternative control state...
Motor stopped
Program interrupted by user
One of the codes I used (the output is posted above):
import odrive
from odrive.enums import *
import time
import sys
def setup_odrive_sensorless():
print("Searching for ODrive...")
# Connect to ODrive
try:
odrv0 = odrive.find_any(timeout=10)
print("Found ODrive!")
print(f"Firmware version: {odrv0.fw_version_major}.{odrv0.fw_version_minor}.{odrv0.fw_version_revision}")
except:
print("No ODrive found. Please check USB connection and try again.")
return None
# Erase configuration and handle disconnection
print("Erasing existing configuration...")
try:
odrv0.erase_configuration()
print("Configuration erased. ODrive will reboot...")
except Exception as e:
print(f"Expected disconnection: {e}")
# Wait for reboot and reconnect
print("Waiting for ODrive to reboot...")
time.sleep(5)
print("Reconnecting to ODrive...")
for attempt in range(5):
try:
odrv0 = odrive.find_any(timeout=10)
print("Reconnected successfully!")
break
except:
print(f"Reconnection attempt {attempt + 1}/5 failed, retrying...")
time.sleep(2)
else:
print("Failed to reconnect to ODrive. Please unplug and replug USB, then run script again.")
return None
# For Firmware 0.5.1 - use correct configuration paths
print("Configuring for FW 0.5.1...")
# Configure motor parameters
print("Configuring motor...")
try:
# In FW 0.5.1, motor type might be configured differently
odrv0.axis0.motor.config.pole_pairs = 7 # Common for drill motors
# Current limits
odrv0.axis0.motor.config.current_lim = 25.0 # Increased for drill motor
odrv0.axis0.motor.config.calibration_current = 15.0
odrv0.axis0.motor.config.resistance_calib_max_voltage = 8.0 # Increased for better calibration
print("Motor configuration successful")
except Exception as e:
print(f"Motor configuration warning: {e}")
# For sensorless in FW 0.5.1, we need to use encoderless control
print("Configuring sensorless control...")
try:
# In older firmware, sensorless might be the default when no encoder is configured
odrv0.axis0.encoder.config.mode = 0 # ENCODER_MODE_NONE
print("Encoder set to NONE mode for sensorless")
except Exception as e:
print(f"Encoder configuration warning: {e}")
# Controller configuration
try:
odrv0.axis0.controller.config.control_mode = 2 # VELOCITY_CONTROL = 2
odrv0.axis0.controller.config.vel_limit = 20.0 # Increased limit
odrv0.axis0.controller.config.vel_gain = 0.2
odrv0.axis0.controller.config.vel_integrator_gain = 0.4
print("Controller configuration successful")
except Exception as e:
print(f"Controller configuration warning: {e}")
# Save configuration
print("Saving configuration...")
try:
odrv0.save_configuration()
print("Configuration saved successfully!")
except Exception as e:
print(f"Save configuration warning: {e}")
# Reconnect after save
print("Reconnecting after save...")
time.sleep(2)
try:
odrv0 = odrive.find_any(timeout=10)
print("Reconnected after save!")
except:
print("Reconnecting after save...")
try:
odrv0 = odrive.find_any(timeout=10)
print("Reconnected!")
except:
print("Could not reconnect automatically. Please restart script.")
return None
return odrv0
def calibrate_motor(odrv0):
"""Calibrate the motor"""
if odrv0 is None:
return False
print("Starting motor calibration...")
try:
odrv0.axis0.requested_state = 4 # AXIS_STATE_MOTOR_CALIBRATION = 4
# Wait for calibration to complete
start_time = time.time()
while odrv0.axis0.current_state != 1: # AXIS_STATE_IDLE = 1
if time.time() - start_time > 15: # Increased timeout
print("Calibration timeout!")
# Try to clear errors
try:
odrv0.axis0.error = 0
except:
pass
return False
time.sleep(0.1)
print(f"Calibrating... State: {odrv0.axis0.current_state}")
print("Motor calibration complete!")
try:
print(f"Phase resistance: {odrv0.axis0.motor.config.phase_resistance:.6f} Ohms")
print(f"Phase inductance: {odrv0.axis0.motor.config.phase_inductance:.6f} H")
except:
print("Motor calibration values available")
return True
except Exception as e:
print(f"Calibration failed: {e}")
return False
def start_sensorless_control(odrv0):
"""Start sensorless control mode"""
if odrv0 is None:
return False
print("Starting sensorless control...")
try:
# For FW 0.5.1, sensorless control might be state 5 or 8
# Try both if needed
odrv0.axis0.requested_state = 8 # AXIS_STATE_CLOSED_LOOP_CONTROL = 8
# Check if it worked
time.sleep(0.5)
if odrv0.axis0.current_state != 8:
print("Trying alternative control state...")
odrv0.axis0.requested_state = 5 # AXIS_STATE_SENSORLESS_CONTROL = 5
time.sleep(0.5)
print(f"Sensorless control active! State: {odrv0.axis0.current_state}")
return True
except Exception as e:
print(f"Failed to start sensorless control: {e}")
return False
# Control functions
def set_velocity(odrv0, vel_rps):
"""Set motor velocity in revolutions per second (RPS)"""
if odrv0 is None:
print("No ODrive connected!")
return
try:
# For sensorless, we might need to ensure we're in the right state
if odrv0.axis0.current_state != 8 and odrv0.axis0.current_state != 5:
print(f"Motor not in closed loop! Current state: {odrv0.axis0.current_state}")
start_sensorless_control(odrv0)
time.sleep(0.5)
odrv0.axis0.controller.input_vel = vel_rps
print(f"Velocity set to {vel_rps} RPS ({vel_rps * 60:.1f} RPM)")
print(f"Current state: {odrv0.axis0.current_state}, Actual vel: {odrv0.axis0.encoder.vel_estimate:.2f} RPS")
except Exception as e:
print(f"Error setting velocity: {e}")
def stop_motor(odrv0):
"""Stop the motor"""
if odrv0 is None:
return
try:
odrv0.axis0.controller.input_vel = 0
print("Motor stopped")
except Exception as e:
print(f"Error stopping motor: {e}")
def check_status(odrv0):
"""Check ODrive status"""
if odrv0 is None:
print("No ODrive connected!")
return
try:
state_names = {
0: "UNDEFINED", 1: "IDLE", 2: "STARTUP_SEQUENCE", 3: "FULL_CALIBRATION_SEQUENCE",
4: "MOTOR_CALIBRATION", 5: "SENSORLESS_CONTROL", 6: "ENCODER_INDEX_SEARCH",
7: "ENCODER_OFFSET_CALIBRATION", 8: "CLOSED_LOOP_CONTROL"
}
state = odrv0.axis0.current_state
state_name = state_names.get(state, f"UNKNOWN({state})")
print(f"Current state: {state_name}")
print(f"Velocity: {odrv0.axis0.encoder.vel_estimate:.2f} RPS")
print(f"Motor current: {odrv0.axis0.motor.current_control.Iq_measured:.2f} A")
print(f"Bus voltage: {odrv0.vbus_voltage:.2f} V")
# Check for errors
if odrv0.axis0.error != 0:
print(f"AXIS ERROR: {odrv0.axis0.error}")
if odrv0.axis0.motor.error != 0:
print(f"MOTOR ERROR: {odrv0.axis0.motor.error}")
except Exception as e:
print(f"Error reading status: {e}")
# Main execution
if __name__ == "__main__":
print("=== ODrive Sensorless Motor Setup (FW 0.5.1) ===")
# Setup ODrive
odrv0 = setup_odrive_sensorless()
if odrv0 is None:
print("Setup failed. Exiting.")
sys.exit(1)
# Calibrate motor
if not calibrate_motor(odrv0):
print("Calibration failed. Exiting.")
sys.exit(1)
# Start sensorless control
if not start_sensorless_control(odrv0):
print("Failed to start sensorless control. Exiting.")
sys.exit(1)
# Demo sequence
print("\n=== Starting Motor Demo ===")
try:
# Check status first
check_status(odrv0)
# Start slow - try different speeds
print("Attempting to start motor at 3 RPS (180 RPM)...")
set_velocity(odrv0, 3.0)
time.sleep(2)
# Check if it's actually moving
check_status(odrv0)
if abs(odrv0.axis0.encoder.vel_estimate) < 0.1:
print("Motor not spinning. Trying higher velocity...")
set_velocity(odrv0, 8.0)
time.sleep(2)
check_status(odrv0)
# Try even higher if still not moving
if abs(odrv0.axis0.encoder.vel_estimate) < 0.1:
print("Motor still not spinning. Trying 15 RPS...")
set_velocity(odrv0, 15.0)
time.sleep(2)
check_status(odrv0)
# Stop motor
stop_motor(odrv0)
print("\n=== Demo Complete ===")
print("Use these commands for manual control:")
print(" set_velocity(odrv0, speed_rps)")
print(" stop_motor(odrv0)")
print(" check_status(odrv0)")
# Interactive control
while True:
cmd = input("\nEnter command (v [rps], s, c, q): ").strip().lower()
if cmd.startswith('v'):
try:
speed = float(cmd[1:].strip())
set_velocity(odrv0, speed)
except ValueError:
print("Invalid speed. Use 'v5.0' for 5 RPS")
elif cmd == 's':
stop_motor(odrv0)
elif cmd == 'c':
check_status(odrv0)
elif cmd == 'q':
stop_motor(odrv0)
print("Exiting.")
break
else:
print("Commands: v[speed], s=stop, c=check, q=quit")
except KeyboardInterrupt:
stop_motor(odrv0)
print("\nProgram interrupted by user")