Python to interface with the Pololu 8-channel servo controller

19 January, 2009

I now have a Python script which is general enough to move any of the 8 servos to any angle. At the bash shell I can type:

python controlsscii.py pos 0 90

and the ‘position’ routine will run – moving servo 0 to 90 degrees using the ‘Mini SSC II’ protocol. Along side this I produced a ‘Pololu’ protocol version running a ‘speed’ and ‘position’ routine which can be updated with further routines. I concentrated on the former for ease of debugging when things got frustrating!

The code is as follows:

import serial
import sys
#set up the serial port for action (0==COM1==ttyS0)
ser=serial.Serial(0)
ser.baudrate=2400

def setpos(n,angle):
  #Quick check that things are in range
  if angle > 180 or angle <0:
    angle=90
    print "WARNING: Angle range should be between 0 and 180. Setting angle to 90 degrees to be safe..."
    print "moving servo "+str(n)+" to "+str(angle)+" degrees."

  byteone=int(254*angle/180)
  #move to an absolute position in 8-bit mode (0x04 for the mode, 0 for the servo, 0-255 for the position (spread over two bytes))
  bud=chr(0xFF)+chr(n)+chr(byteone)
  ser.write(bud)

mode=sys.argv[1]
n=int(sys.argv[2])
m=int(sys.argv[3])

if mode=='pos':
  setpos(n,m)
else:
  print "No commands given.\nUsage: controlsscii.py pos <servo> <angle>"

Simple when you know how! And here’s the ‘Pololu’ protocol version (so far):

import serial
import sys
#set up the serial port for action
ser=serial.Serial(0)
ser.baudrate=2400
def setspeed(n,speed):
  #Quick check that things are in range
  if speed > 127 or speed <0:
    speed=1
    print "WARNING: Speed should be between 0 and 127. Setting speed to 1..."
    print "Setting servo "+str(n)+" speed to "+str(speed)+" out of 127."
  speed=int(speed)
  #set speed (needs 0x80 as first byte, 0x01 as the second, 0x01 is for speed, 0 for servo 0, and 127 for max speed)
  bud=chr(0x80)+chr(0x01)+chr(0x01)+chr(n)+chr(speed)
  ser.write(bud)

def setpos(n,angle):
  #Check that things are in range
  if angle > 180 or angle <0:
    angle=90
    print "WARNING: Angle range should be between 0 and 180. Setting angle to 90 degrees to be safe..."
    print "moving servo "+str(n)+" to "+str(angle)+" degrees."

  #Valid range is 500-5500
  offyougo=int(5000*angle/180)+500
  #Get the lowest 7 bits
  byteone=offyougo&127
  #Get the highest 7 bits
  bytetwo=(offyougo-(offyougo&127))/128
  #move to an absolute position in 8-bit mode (0x04 for the mode, 0 for the servo, 0-255 for the position (spread over two bytes))
  bud=chr(0x80)+chr(0x01)+chr(0x04)+chr(n)+chr(bytetwo)+chr(byteone)
  ser.write(bud)

mode=sys.argv[1]
n=int(sys.argv[2])
m=int(sys.argv[3])
if mode=='speed':
  setspeed(n,m)
elif mode=='pos':
  setpos(n,m)
else:
  print "No commands given.\nUsage: controlfunction.py speed <servo> <speed>, or\n controlfunction.py pos <servo> <angle>"

Hope this has helped someone. Don’t forget to set the jumpers to set the protocol.

Updated: Code had all the indentation removed on upload (really not a good thing for Python!). It’s still not great as it runs off the right hand side but I’ll work out showing code at some point!

Advertisements

Controlling servos using Ubuntu (linux server) and Python

17 January, 2009

As I mentioned previously, I bought one of these servo controllers from Pololu. It’s capable of controlling up to 8 servos with instructions from either a microcontroller using TTL (via one of the headers) or over RS232 from a computer. I want to build a robot arm which is controlled over the web. Therefore I want my Ubuntu server to interface with this controller.

I’m not very good at the low level programming stuff and the documentation isn’t all that great for hobbyists (in my opinion) so I had some fun trying to get it all to work.

So here’s the first part of my story/tutorial based on my experiences using python on linux to control a servo…

Protocol info

OK, so this device comes with a manual describing two types of protocol it supports; SSCII and Pololu. SSCII looks the most straight forward but it’s limited in a few ways. The worst part is that you can only use 7-bit precision when placing the servos and you can’t change speeds. Saying that if it suits your needs then go ahead. It needs three bytes of information: A constant (tells the thing you’re talking to it and not something else!), a servo number, and a position (0-127 representing 180 degrees). The following will move servo 0 to position 84 (0xFF being hex representation of the constant byte):

0xFF, 0, 84
11111111|00000000|01010100

A handy tip is that if you need to convert, Google is able to go between decimal, hex, and binary for you. Simply type “0xff in binary” into the search box!

The Pololu protocol allows different speeds for different servos, 8-bit precision, and setting neutral positions too. For that reason it’s too much for me to outline fully here. Check the manual (pdf) out for further information.

I will describe the two commands which I use below. The first is to set the speed of a servo before moving it. It consists of 5 bytes; a control, a board identifier (if you’re only using one board this never changes either), a label for the command (0x01 is for speed), a servo identifier (0-8), and a speed value (1 is the slowest, 127 is the fastest, 0 is uncontrolled – ie as fast as it will go):

0x80, 0x01, 0x01, 0, 127
10000000|00000001|00000001|00000000|01111111

The second is for 7-bit precision positioning (I said there is higher precision than this but that uses a different mode to this). It too consists of 5 bytes; a control, a board identifier, a label for the command (0x02 is for 7-bit positioning), a servo identifier, and a position (0-127):

0x80, 0x01, 0x02, 0, 127
10000000|00000001|00000010|00000000|01111111

Programming info

In linux serial ports are labelled as ttyS0 to ttySx, where x is the highest available. These are equivalent to the COM1, COM2, etc you see labelled in Windows. I was instantly pleased that to find that a command from the shell such as:

echo “this info will go down the serial line” > \dev\ttyS0

sends ASCII data, character for character, down the serial line. I thought things were going to be simple as PHP can issue the command “exec(some shell command)” – instant web controlled hardware!

I was lucky enough to have with me a cheap oscilloscope (USB DAQ from Measurement Computing) to monitor the output from the relevant pin of the serial cable (pin 2 – rx, although I think it should have been pin 3 – best to check!).  I quickly discovered that I couldn’t send the correct bit sequence down the serial line required by the Pololu controller. I could have found the ASCII equivalents for the header digits (the 0x80 and 0x01, etc) but when it came to sending data values for positions this would be difficult. Who knows, this may be good enough for someone out there. It was good for testing purposes if nothing else.

I then weighed up my options and decided to use Python to program in. This was because it is high level, has some nice serial commands via pyserial, and I’ve heard loads about it. It is supposed to be easy to do complicated things and there’s plenty on the web about it. It’s open source and much of the linux community use it. If you’re running linux you probably have it. Go to a command line and type “python”. This should then give you a shell type environment to enter python commands. To quit type “quit()”.

So after downloading and installing Pyserial (see the instructions on the pyserial web site) I started testing the commands.

At the start you have to tell Python to use Pyserial:

import serial

And then

ser=serial.Serial(0)

sets the serial port to use (ttyS0, or COM1 in this case) and assigns it a handle. Now I can write things to it using:

ser.write(“things in here”)

which is equivalent to the Bash, shell way of doing things. But you can assign values to variables and say

ser.write(variable)

This is really handy because you can assign ‘variable’ the correct value based on what you’re trying to do, and even concatenate (string together) multiple values. Also by using the command ‘chr()’ one can input decimal and hexidecimal values in and they will be converted to ASCII for transmission. So my simple program is shown below. It sets the speed of a single servo (servo 0) to maximum, moves it to a position, and then to another after a small wait (5 seconds).

import serial
import time

#set up the serial port for action
ser=serial.Serial(0)
ser.baudrate=2400

#output some data (qualifiers first)

#set speed (needs 0x80 as first byte, 0x01 as the second, 0x01 is for speed, 0 for servo 0, and 127 for max speed)
bud=chr(0x80)+chr(0x01)+chr(0x01)+chr(0)+chr(127)
ser.write(bud)
time.sleep(1)

#move to one position in 7-bit mode (0x02 for the mode, 0 for the servo, 127 for the position)
bud=chr(0x80)+chr(0x01)+chr(0x02)+chr(0)+chr(127)
ser.write(bud)
time.sleep(5)

#move to the next
bud=chr(0x80)+chr(0x01)+chr(0x02)+chr(0)+chr(0)
ser.write(bud)

This file is saved as control.py and then run from the command line using the following:

python control.py

The only confusion I have is that the servo seems to ‘bounce’. It goes to the first position fine, waits, then seems to bounce off one position to get to the second. Not sure I understand this – it could be that the centre hasn’t been set yet. I will iron this out soon, I’m sure. (Update: The servos didn’t have enough power. I have the jumper js=jcc on which meant that the servos were pulling current away from the electronics, making it fail. I fixed this by sourcing my power off a molex connector from the server. Works well!!)

Concluding

Obviously this is just setting up the basics and I have a long way to go before it becomes useful but it does show that my server can control servos! What’s also cool is that I’ve been using three PCs to do it! One is the server, the second is a work laptop with a USB data acquisition board for debugging, and the third is a windows PC (my HP mini-note 2113) for writing code and running commands over SSH. This is handy because my server doesn’t have a monitor and it allows me to develop in a user friendly environment (ie not all on the command line!). So I use Notepad++ (which is great for a lot of things – HTML, CSS, PHP, etc) to edit the Python code and Putty (which is a windows SSH client) to control the server and run the file.

Next step is to make lots of functions and have them called from the command line (eg “python control.py -move 0 123” or similar). I can then write a PHP web page which can call them with some (filtered!) user input.

I hope this has been useful. I had a hard time starting out with serial comms so hopefully I can save someone else that initial pain!