Hoveboard motor tuning and position speed

I have the ODrive working with my robot, works great, am controlling it from python on Raspberry Pi. However when using any of the modes, trajectory, raw position, velocity, etc, I can’t seem to get the parameters right to where it will just move to the pos_setpoint and stop… sometimes it won’t budge if the movement is too small (1 count), sometimes it will move and not stop in time.

Is there a video or complete procedure for tuning the motors with the ODrive, as the tuning procedure is very sparse, I have gotten comments that I need to use a ramp signal for velocity, but keep the robot on the ground… if I do that it will be moving all over the place.

Is there any list of specific DETAILED instructions on how to tune the motors?

It should be pretty simple to put a tuning procedure in a python program with some instructions to go with it in the comments. I can do that myself if I knew how to do the tuning

What are you using as feedback? Hall sensors or an encoder? What is the encoder resolution (CPR)?

If you are using hall sensors only on a hoverboard motor, you cannot hope to control the position to within 1 count, that leaves no information left for the velocity. A realistic expectation depends on the load and other factors, but +/-10 counts could be possible I think. If you need precise control you need an encoder. There are some hub motor wheels that have encoders in them (some of my other customers use this for wheeled robots, I could point you to some places).

I think you can tune vel_gain and vel_integrator_gain in velocity control mode by looking at the velocity response while sending velocity step inputs. If you will be using position control, you can then tune the pos_gain in position control mode with position step. Though position control is not very common on wheeled robots (except maybe the incremental position control mode).

Thanks! Yes, it is using 10" hoverboard motors with the hall sensors from the motors; 90 counts per revolution so 31.415" circumference, so 1 count is about .34 inches, 10 counts would be 3.4 inches. The counts seem accurate enough though, it seems like I could just put it in position mode and get it to move, but I am new to this, so I will take your expert word for it.

Yes, if you know of 10" hub wheel motors with encoders built in, please let me know, thanks!

I am using a live HD camera and other sensors that can detect any slight movement so I don’t really even need the Hall encoders or more precise controllers; do I need to bypass your position mode and just control directly with current mode?
UPDATE: I am going to use your velocity mode as that works great, and have it work much like your shopping cart RC video demo; Thanks!

I was able to create a python program that tunes two motors at once; opencv was used just for keyboard keys detection but you can use whatever you like there.
Process:

  1. run program
  2. press ‘u’ to increase vel_gain by 30%; robot moves out and back for 1 second
  3. keep pressing ‘u’, when vibration occurs, press ‘h’ instead
    open up the odrive tool and type:
odrv0.save_configuration()
odrv0.reboot()

Here’s the code:
import RPi.GPIO as GPIO
import odrive
from odrive.enums import *
import time
import cv2

GPIO.setmode(GPIO.BCM)

print("finding an odrive...")
odrv0 = odrive.find_any(timeout=60,serial_number='20773596524B')
#odrv0 = odrive.find_any(timeout=60,path="usb:001:004") #serial_number='20773596524B')
print("found an odrive...")

#turn on headlights
headlight_pin=16
GPIO.setup(headlight_pin,GPIO.OUT)
print("turning on headlights")
GPIO.output(headlight_pin,GPIO.HIGH)

#turn off closed loop control
odrv0.axis0.requested_state = AXIS_STATE_IDLE
odrv0.axis1.requested_state = AXIS_STATE_IDLE

#initial settings for 15 pole-pair 10" hoverboard wheels
vel_gain=.02
vel_integrator_gain=.5*10*vel_gain
print("vel_integrator_gain:",vel_integrator_gain)
odrv0.axis0.controller.config.vel_gain=vel_gain
odrv0.axis0.controller.config.vel_integrator_gain=vel_integrator_gain
odrv0.axis1.controller.config.vel_gain=vel_gain
odrv0.axis1.controller.config.vel_integrator_gain=vel_integrator_gain

#turn on velocity control    
odrv0.axis0.controller.config.control_mode=CTRL_MODE_VELOCITY_CONTROL
odrv0.axis0.controller.config.control_mode=CTRL_MODE_VELOCITY_CONTROL

#turn on closed loop control
odrv0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL
odrv0.axis1.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL

#read in dummy image to use opencv keyboard events (use any image here)
img = cv2.imread('/home/pi/cam2.jpg')

#scale image
scale_percent = 10 # percent of original size
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
dim = (width, height)
# resize image
resized = cv2.resize(img, dim, interpolation = cv2.INTER_AREA)
cv2.imshow('dummy image',resized)

#set vel_setpoint speed for tests
speed=50
doTestMove=False

while True:
    time.sleep(.1)
    key = cv2.waitKey(1) & 0xFF
    # if the `q` key was pressed, break from the loop
    if key == ord("q"):
        break
	# press 'u' to increase gain by 30% and test by moving 
    elif key == ord("u"):
        #increase vel_gain by 30%
        vel_gain=vel_gain*1.3
        odrv0.axis0.controller.config.vel_gain=vel_gain
        odrv0.axis1.controller.config.vel_gain=vel_gain
        print("vel_gain:",vel_gain)
        doTestMove=True
        
	# press 't' to test by moving again with same settings
    elif key == ord("t"):
        #retest current vel_gain
        print("vel_gain:",vel_gain)
        print("vel_integrator_gain:",vel_integrator_gain)
        doTestMove=True
    
    # press 'h' to decrease gain by 50% after getting some vibration from previous increase
	# also sets the vel_integrator_gain at this point based on .5 x 10 (100ms bandwidth) x vel_gain
	# and test by moving 	
    elif key == ord("h"):
        #reduce vel_gain by 50%
        vel_gain=vel_gain*.5
        odrv0.axis0.controller.config.vel_gain=vel_gain
        odrv0.axis1.controller.config.vel_gain=vel_gain
        print("vel_gain:",vel_gain)
        vel_integrator_gain=.5*10*vel_gain
        print("vel_integrator_gain:",vel_integrator_gain)
        odrv0.axis0.controller.config.vel_integrator_gain=vel_integrator_gain
        odrv0.axis1.controller.config.vel_integrator_gain=vel_integrator_gain
        doTestMove=True
        
	# press 'u' to decrease gain by 30% and test by moving 	(not really needed here if you press 'h' the first time you get vibration)
	# if you happened to miss the first vibration, this lets you bring down by 30% before pressing 'h'
    elif key == ord("d"):
        #reduce vel_gain by 30%
        vel_gain=vel_gain*.70
        odrv0.axis0.controller.config.vel_gain=vel_gain
        odrv0.axis1.controller.config.vel_gain=vel_gain
        print("vel_gain:",vel_gain)
        doTestMove=True
        
	# if key was press other than 'q' do a movement test at the set speed for both motors (they are opposite signs as they are mounted opposite each other)	
    if doTestMove:
        doTestMove=False
        #do a test move
        odrv0.axis0.controller.vel_setpoint = -speed
        odrv0.axis1.controller.vel_setpoint = speed
        time.sleep(1)
        #do stop
        odrv0.axis0.controller.vel_setpoint = 0
        odrv0.axis1.controller.vel_setpoint = 0
        time.sleep(1)
        #move back to start
        odrv0.axis0.controller.vel_setpoint = speed
        odrv0.axis1.controller.vel_setpoint = -speed
        time.sleep(1)
        #do stop
        odrv0.axis0.controller.vel_setpoint = 0
        odrv0.axis1.controller.vel_setpoint = 0
        

# cleanup the camera and close any open windows
#camera.close()
cv2.destroyAllWindows()
    
#set to idel    
odrv0.axis0.requested_state = AXIS_STATE_IDLE
odrv0.axis1.requested_state = AXIS_STATE_IDLE

#turn on velocity control    
odrv0.axis0.controller.config.control_mode=CTRL_MODE_VELOCITY_CONTROL  
odrv0.axis1.controller.config.control_mode=CTRL_MODE_VELOCITY_CONTROL

print("turning off headlights")
GPIO.output(headlight_pin,GPIO.LOW)

print("turning off ODrive")
#GPIO.output(odrive_pin,GPIO.LOW)

GPIO.cleanup()

Hi @rengelking

How did you derive the value of the bandwidth, the number 10, in the vel_integrator_gain calculation? or did you just copy it from this webpage?

Best,

Yes, just copied it from that page

Hi @madcowswe ,

I’m using two hub motors for a wheeled robot that has 30 magnets which means it has 90 cpr. I was wondering if it is possible to increment the resolution by mounting external encoders to the wheel and connecting them to the A, B and Z ports in the ODrive controller instead of the hall effect signal wires of the hub motors. Or do I need the hall effect signals of the hub motors to control the hub motors? and if yes, how do I connect the encoders to the ODrive controller?

Another thing, which hub motor wheels have encoders in them?

Best,

You can use an external encoder instead of the hall effect sensors if you prefer. Just set the correct cpr

Thanks @Wetmelon! what encoders do you recommend for a hub motor of circumference of 81.68 inches?

The larger the hub motor the higher the pole count (usually).
The higher the pole count, the more encoder resolution you need.
Just try to find one with high resolution. Your hall sensors give you six counts per pole, but that is not nearly enough.
AS5047p for example has 16384 counts per rev when used in SPI mode. (But this is only supported on Wetmelon’s RazorsEdge branch)
However, maybe this type of encoder cannot be fitted to a hub motor as it is an on-axis sensor, i.e. you need to be able to stick a magnet on the end of the central shaft. If it doesn’t have a shaft, you can’t do that.
There are off-axis alternatives in the form of ring-magnet encoders, discussed in other threads. However they will always require calibration on start-up.