Webcam robot


So I was working on trying to get wheels on the pan/tilt webcam and realized I should post how I did the first part of it before I post the second. Youtube of it in action here.

The parts you need are:
two mounts to hold two servos
some kind of heavy base to attach to the servos to prevent it from shaking and tipping over
arduino , wiring and a joystick
python 2.6 , pyserial, and pygame module.

BIG BIG thanks to the folks over at principal labs for posting great tutorials and the code. They did 90% of the work and I slightly altered the python script to make it work for two servos moving in different directions.

So the setup is pretty simple, a joystick > computer running python script > send data serially to arduino > servos

first hook up the two servos to pins 2,3 ground and +5V on the arduino.

I used a script that principal labs made to send data serially. Make sure you find out what com port your arduino is connected to and replace that part of the code.

#!/usr/bin/env python
 
################################################
# Module:   servo.py
# Created:  2 April 2008
# Author:   Brian D. Wendt
#   http://principialabs.com/
# Version:  0.2
# License:  GPLv3
#   http://www.fsf.org/licensing/
'''
Provides a serial connection abstraction layer
for use with Arduino "MultipleServos" sketch.
'''
################################################
 
import serial
 
usbport = 'COM9'
ser = serial.Serial(usbport, 9600, timeout=1)
#print ser
 
def move(servo, angle):
    '''Moves the specified servo to the supplied angle.
 
    Arguments:
        servo
          the servo number to command, an integer from 1-4
        angle
          the desired servo angle, an integer from 0 to 180
 
    (e.g.) >>> servo.move(2, 90)
           ... # "move servo #2 to 90 degrees"'''
 
    if (0 <= angle <= 180):
        ser.write(chr(255))
        ser.write(chr(servo))
        ser.write(chr(angle))
        print servo
        print angle
    else:
        print "Servo angle must be an integer between 0 and 180.\n"

Now the python script uses the previous one to interpret the joystick data.

#!/usr/bin/env python
import servo
import pygame
 
 
# allow multiple joysticks
joy = []
 
# handle joystick event
def handleJoyEvent(e):
    if e.type == pygame.JOYAXISMOTION:
        axis = "unknown"
        if (e.dict['axis'] == 0):
            axis = "X"
 
        if (e.dict['axis'] == 1):
            axis = "Y"
 
        if (e.dict['axis'] == 2):
            axis = "Throttle"
 
        if (e.dict['axis'] == 3):
            axis = "Z"
 
        if (axis != "unknown"):
            str = "Axis: %s; Value: %f" % (axis, e.dict['value'])
            # uncomment to debug
            output(str, e.dict['joy'])
 
            # Arduino joystick-servo hack
            if (axis == "X"):
                pos = e.dict['value']
                # convert joystick position to servo increment, 0-180
                move = round(pos * 90, 0)
                if (move < 0):
                    serv = int(90 - abs(move))
                else:
                    serv = int(move + 90)
                # convert position to ASCII character
                servoPosition = serv
                # and send to Arduino over serial connection
                servo.move(1, servoPosition)
 
            # Arduino joystick-servo hack
            if (axis == "Y"):
                pos = e.dict['value']
                # convert joystick position to servo increment, 0-180
                move = round(pos * 90, 0)
                if (move < 0):
                    serv = int(90 - abs(move))
                else:
                    serv = int(move + 90)
                # convert position to ASCII character
                servoPosition = serv
                # and send to Arduino over serial connection
                servo.move(2, servoPosition)
 
    elif e.type == pygame.JOYBUTTONDOWN:
        str = "Button: %d" % (e.dict['button'])
        # uncomment to debug
        output(str, e.dict['joy'])
        # Button 0 (trigger) to quit
        if (e.dict['button'] == 0):
            print "Pew Pew You're DEAD!!\n"
        if (e.dict['button'] == 8):
            print "Bye!\n"
            quit()
    else:
        pass
 
# print the joystick position
def output(line, stick):
    print "Joystick: %d; %s" % (stick, line)
 
# wait for joystick input
def joystickControl():
    while True:
        e = pygame.event.wait()
        if (e.type == pygame.JOYAXISMOTION or e.type == pygame.JOYBUTTONDOWN):
            handleJoyEvent(e)
 
# main method
def main():
    # initialize pygame
    pygame.joystick.init()
    pygame.display.init()
    if not pygame.joystick.get_count():
        print "\nPlease connect a joystick and run again.\n"
        quit()
    print "\n%d joystick(s) detected." % pygame.joystick.get_count()
    for i in range(pygame.joystick.get_count()):
        myjoy = pygame.joystick.Joystick(i)
        myjoy.init()
        joy.append(myjoy)
        print "Joystick %d: " % (i) + joy[i].get_name()
    print "Depress trigger (button 0) to quit.\n"
 
    # run joystick listener loop
    joystickControl()
 
# allow use as a module or standalone script
if __name__ == "__main__":
    main()
Make sure your arduino to your computer and if all goes well you should get my debug info for the joystick showing up. And the final part is the micro controller sketch:
#!/usr/bin/env python
/*
 * MultipleServos
 * --------------
 * Arduino servo control from a PC
 *
 * Created: 2 April 2008
 * Author:  Brian D. Wendt
 *   http://principialabs.com/
 * License: GPLv3, copyleft 2008
 *   http://www.fsf.org/licensing/
 *
 * Adapted from code by Tom Igoe
 *   http://itp.nyu.edu/physcomp/Labs/Servo
 */
 
 
/** Adjust these values for your servo and setup, if necessary **/
int pinArray[4] = {2, 3, 4, 5}; // digital pins for the servos
int minPulse = 600;             // minimum servo position
int maxPulse = 2400;            // maximum servo position
int refreshTime =  20;          // time (ms) between pulses (50Hz)
 
/** The Arduino will calculate these values for you **/
int i;              // iterator
int servoPin;       // control pin for current servo
int userInput[3];   // raw input from serial buffer, 3 bytes
int pulseWidth;     // servo pulse width
int servoPosition;  // commanded servo position, 0-180 degrees
int pulseRange;     // maxPulse - minPulse
int centerServo;    // servo starting point
long lastPulse = 0; // recorded time (ms) of the last pulse
int servo;          // which servo to pulse? 1-4
int servo1[2];      // servo #1 array{pin, pulsewidth}
int servo2[2];      // servo #2 array{pin, pulsewidth}
int servo3[2];      // servo #3 array{pin, pulsewidth}
int servo4[2];      // servo #4 array{pin, pulsewidth}
int pin;            // digital pin for pulse() function
int puls;           // pulsewidth for pulse() function
int startbyte;      // start byte, begin reading input
 
void setup() {
  // loop through all 4 servo pins
  // and set them as OUTPUT
  for (i=0;i<4;i++) {
    pinMode(pinArray[i], OUTPUT);
  }
  // servo starting point (center)
  pulseRange  = maxPulse - minPulse;
  centerServo = maxPulse - ((pulseRange)/2);
  pulseWidth  = centerServo;
  // map pins to servos
  servo1[0] = pinArray[0];  // servo #1 is pin 2
  servo2[0] = pinArray[1];  // servo #2 is pin 3
  servo3[0] = pinArray[2];  // servo #3 is pin 4
  servo4[0] = pinArray[3];  // servo #4 is pin 5
  // center all servos
  servo1[1] = pulseWidth;
  servo2[1] = pulseWidth;
  servo3[1] = pulseWidth;
  servo4[1] = pulseWidth;
  // open serial connection
  Serial.begin(9600);
}
 
void loop() {
  // wait for serial input (min 3 bytes in buffer)
  if (Serial.available() > 2) {
    //read the first byte
    startbyte = Serial.read();
    // if it's really the startbyte (255)
    if (startbyte == 255) {
      // then get the next two bytes
      for (i=0;i<2;i++) {
        userInput[i] = Serial.read();
      }
      // first byte = servo to move?
      servo = userInput[0];
      // second byte = which position?
      servoPosition = userInput[1];
      // packet check
      if (servoPosition == 255) { servo = 255; }
      // compute pulseWidth from servoPosition
      pulseWidth = minPulse + (servoPosition * (pulseRange/180));
      // stop servo pulse at min and max
      if (pulseWidth > maxPulse) { pulseWidth = maxPulse; }
      if (pulseWidth < minPulse) { pulseWidth = minPulse; }
      // assign new pulsewidth to appropriate servo
      switch (servo) {
        case 1:
          servo1[1] = pulseWidth;
          break;
        case 2:
          servo2[1] = pulseWidth;
          break;
        case 3:
          servo3[1] = pulseWidth;
          break;
        case 4:
          servo4[1] = pulseWidth;
          break;
      }
    }
  }
  // pulse each servo
  if (millis() - lastPulse >= refreshTime) {
    pulse(servo1[0], servo1[1]);
    pulse(servo2[0], servo2[1]);
    pulse(servo3[0], servo3[1]);
    pulse(servo4[0], servo4[1]);
    // save the time of the last pulse
    lastPulse = millis();
  }
}
 
void pulse(int pin, int puls) {
    digitalWrite(pin, HIGH); // start the pulse
    delayMicroseconds(puls); // pulse width
    digitalWrite(pin, LOW);  // stop the pulse
}