Motor Controllers: How do they Work?
Here’s an example of a task that should have been simple, but isn’t. Abby’s drivetrain is powered by a Dimension Engineering Sabertooth 2x25 dual motor driver. These motor controllers are used in every robot in the lab (except Otto, who is still an actual wheelchair), using the same wiring and software (and DIP switch settings) as Abby. When I tried out Abby for the first time last Monday, hers just didn’t work.
Background
- The Sabertooth can be commanded in 4 possible ways:
- 0-5v analog on S1 and S2 inputs to control motor output
- Servo PWM on S1 and S2 inputs to control motor output
- Simple serial on S1 input to control both motors
- Packetized serial on S1 input to control both motors
- The sabertooth DIP switches are set to use packetized serial on address 130.
- The baud rate that the Sabertooth receives serial commands is itself settable by a serial command. (Good design, guys.)
- Chad’s FPGA code used 9600 baud serial.
Knowns
- The ROS stack on the PC is publishing commands to the /cmd_vel topic, and they change appropriately when the joystick is moved.
- There is a signal that looks like a serial signal coming out of the appropriate in on the cRIO, which is connected to the correct pin on the sabertooth (checked with an oscilloscope)
- The signal is, in fact, 9600 baud. (confirmed with an oscilloscope)
- The power to the motors is enabled (confirmed with a multimeter)
Things I’ve Tried
- Connecting the motor controller on Abby to Jinx’s cRIO. Result: Abby’s wheels did not move, but this may have been due to the incredibly strange setup. I did not check or confirm the serial commands Jinx was sending.
- Connecting the motor controller on Jinx to Abby’s cRIO. Result: Jinx’s wheels did not move, but again, I did not check the serial commands. I know the FPGA code includes a serial hold when the motors are disabled, and Abby’s were probably disabled during this test.
-
Connecting a spare 2x50HV motor driver to Abby’s cRIO with no load on the output and checking the output with an oscilloscope.
Result: Nothing on the output. The cause of this was later determined to be a baudrate mismatch. (See below.)
-
Setting Abby’s motor controller to use analog input and testing it with a potentiometer and 24v bench power supply.
Result: Both motors appear to drive properly, although the bench power supply tended to cut out.
-
Setting Abby’s motor controller to use servo PWM and testing it with an Arduino generating a servo sweep.
Result: Both motors appear to drive properly, although the bench power supply tended to cut out.
-
Setting the 2x50HV to servo PWM and driving each channel with a ~300ohm power resistor as load.
Result: Both channels performed as expected.
-
Setting the 2x50HV to use packetized serial and setting each channel in turn to assorted test values using a Python script, PySerial, and the FTDI USB->serial chip on an Arduino (instructions/code below).
Result: Initially, failure. Then realized that the 2x50HV defaults to 2400 baud. I tried again in 2400 baud (I had been using 9600). At 2400 baud, both channels responded to commands.
-
Setting the 2x25 to use packetized serial and setting each channel in turn to assorted test values using the same code/setup as above.
Result: Success on both channels after changing the baud rate from 38400 to 9600.
-
Testing the 2x25 (with the baud rate now presumably at 9600) with the cRIO on Abby.
Result: Failure. Confirmed that Abby was sending serial commands with an oscilloscope, but did not attempt to intercept them. Proceeded to retest with the Python script/FTDI adapter and could not get a response on any of the 4 baud rates the Sabertooth accepts.
Next Steps
- If the 2x50HV will respond to commands from Abby’s cRIO, I’m just going to drop it in instead of the 2x25. I don’t have time to keep trying to figure out what’s wrong here. HARLIE has a 2x50HV, and given how heavy Abby is, the 2x50HV is probably better suited anyway.
- If the 2x50HV won’t respond to commands from Abby’s cRIO, I’m going to attempt to intercept those commands using my FTDI Adapter and see if the commands are what they should be (and that they match commands from Jinx/HARLIE).
Using an Arduino (Duemilanove) as an FTDI Adapter
- Carefully remove the ATMega microcontroller from the Arduino board.
- Connect Arduino pin 0 (RX) to the serial input of the device you wish to communicate with. This channel comes FROM the PC.
- Connect Arduino pin 1 (TX) to the serial output of the device you wish to communicate with. This channel goes TO the PC.
- Connect the Arduino board to the PC. In windows, you’ll need FTDI drivers (which come with the
Arduino software, but are also available from FTDI and elsewhere). The adapter should show up as
a COM port. In Linux (tested in Ubuntu) the adapter should work out of the box and show up as
/dev/ttyUSB0
(orttyUSB1
etc if you already have a USB/serial adapter).
Python Code to Control a Sabertooth Motor Controller (Very Hacky)
Here’s a very hacky Python script to control the Sabertooth in packetized serial mode. You can download it here.
import serial
import struct
commands = {
'motor1fwd': 0,
'motor1bwd': 1,
'vmin': 2,
'vmax': 3,
'motor2fwd': 4,
'motor2bwd': 5,
'motor1drive': 6,
'motor2drive': 7,
'timeout': 14,
'baud': 15
}
baudcodes = {
2400: 1,
9600: 2,
19200: 3,
38400: 4
}
baud = 2400
ser = serial.Serial('/dev/ttyUSB0', baud)
def set_baud(address, baudrate):
packet = make_packet(address, commands['baud'], baudcodes[baudrate])
ser.write(packet)
def drive(address, motor, speed):
if motor==1:
command = 0
elif motor==2:
command = 4
else:
return false
if speed 127:
speed = 127
packet = make_packet(address, command, speed)
ser.write(packet)
def make_packet(address, command, data):
checksum = 127 & (address + command + data)
return struct.pack('BBBB', address, command, data, checksum)
addr = 130
drive(addr, 1, 10)