Introduction: Make OWI Robotic Arm Self Propelled on Tracks Controlled Wirelessly by the Wii Controller and Raspberry Pi

There are at least 4 or 5 other projects on Instructables.com around modifying or controlling OWI Robotic Arm. The objective of this project is summarized in these points.

1. Mobility - this project mounts the arm on a tracked vehicle for mobility to be able to expand the arm's usability.

2. Wireless - the arm is controlled wirelessly with bluetooth.

3. Control - with the combination of the Wii controller and nunchuck we have motion control inputs, joystick, directional buttons and numerous push buttons to work with. The nunchuck implementation in this project allows the operator to operate all the joints on the arm by gripping the nunchuck with one hand and moving it in an intuitive manner to easily place the arm in the desired position. Some of the joints have proportional control for more precision.

We will use the Raspberry Pi to control this robot. We will program it with Python programming language using the CWiiD library and bluetooth library for the Wii controller.

Here are a couple Youtube videos that shows the robot in operation. My objective for this project was to be able to control the robotic arm with precision and use movements of the hand that feel natural and are closely related to the motion of the robotic arm.


Step 1: Parts

Parts needed for this project

1. OWI Robotic Arm Edge - this is the robotic arm in kit form.

2. Tamiya Tracked Vehicle Chassis Kit

3. Tamiya 70168 Double Gearbox L/R Independ 4-Speed

4. Motor Drive Controller Board Module L298N Dual H Bridge -- Quantity 4 --

5. Raspberry Pi computer

6. 5 volt 1 amp power bank designed to charge cell phone -- Quantity 2 --

7. Wii remote and Nunchuck controller

I bought all of this from Amazon except the Wii controller which I already had

Step 2: Let's Get Started

I started by putting the track kit together. This is a picture of the track kit totally put together. Assembled like this the track will only go straight forward or backward. We want to be able to turn right and left also. In order to do this you need to buy the Tamiya 70168 Double Gearbox.

Step 3: Put Together the Double Gearbox

You need to assemble the double gearbox from a kit. It can be assembled to go one of four speeds. I made it to run at the slowest speed which also has the most torque.

Step 4: Mount Dual Gearbox on the Track Kit

The dual gearbox mounting holes are too wide for the standard base so I glued a block that is just wide enough for the screws but not wide enough to interfere with the tracks. Now you can go ahead and mount the gearbox.

Step 5: Add Mounts and Base

The track vehicle is designed to run with the shorter tracks on the ground. For stability purposes I flipped it over to have the long section of track on the ground. I glued on two mounting supports then I made a base that the arm can sit in and also have room for the four L298N Dual H Bridge Motor Drive Controllers.

Step 6: Base Design

The base allows the robotic arm to sit in the middle with space for a battery and motor drivers on both sides.

Step 7: Build the Robotic Arm

Now we need to assemble the robotic arm from the kit. There are many instructables already on building the arm. I suggest that you search out one of them if you want more help. I found the kit to be of high quality and everything fit together nicely.

Important Note : Go ahead and build the kit except for the wired control unit and the battery box. We won't use either of those in this project.

Step 8: Make Room for the Main Battery

Next I used my Dremel tool to remove some plastic in the battery box so my battery bank would fit in it. I ended up cutting the end of it totally off to make room for the USB plug (not shown in this picture.

Step 9: Wire Up the Motor Drivers

There are 5 motors in the robotic arm and 2 motors in the track drive. The L298N Dual H Bridge can handle 2 motors each, so we need 4 of them with one spot to spare. Each Dual H Bridge is packaged with 2 motor drivers on the same board. They work totally independent and are packaged on the same board strictly for convenience.

Here is how to wire up the motor driver

Lets use the base rotate and the base motion (vertical) motors as an example

Cut the connector off the base rotate motor 2 wire group. One wire goes to Out1 and the other to Out2.

Cut the connector off the base motion motor 2 wire group. One wire goes to Out3 and the other to Out4.

Look at the picture and find "Connect motor here" (2 places)

Connect the motor battery where it says Ground and +5V

One note here: All of the motors in this project were originally designed to run at 3 volts. I am running them at 5 volts. With most of these small DC motors this isn't a problem unless you raise the voltage too much.

We will cover the control input on the next step

Step 10: Controlling the Motor Drivers

The control of the base rotate motor will be done by In1 and In2

The control of the base motion motor will be done by In3 and In4

So in this example pins 10,11,12 and 13 would be wired as follows

Also each set (In1 and In2) -- (In3 and In4) have an enable pin. The driver comes with the enable pin jumpered to +5 volts which enables it all time. In this project we want to control some of the motors with pulse modulation which allows controlling the speed of the motor by turning the enable pin on and off real fast. Note how pins 31 and 33 are wired to the respective enable pins.

List of all the GPIO pins from the python code

LeftMotorTrackA = 3

LeftMotorTrackB = 5

RightMotorTrackA = 7

RightMotorTrackB = 8

MotorBaseRotateA = 10 ----- In1

MotorBaseRotateB = 11 ----- In2

MotorBaseMotionA = 12 ----- In3

MotorBaseMotionB = 13 ----- In4

MotorElbowA = 15

MotorElbowB = 16

MotorWristA = 18

MotorWristB = 19

MotorGripperA = 21

MotorGripperB = 22

BaseMotionEnable=31 ----- Enable pin for In3-In4

BaseRotateEnable=33 ----- Enable pin for In1-in2

GripperEnable=35

Step 11: Wire Up the Rest of the Motor Drivers

Based on the last two steps, you should be able to repeat the process on the remaining 3 motor drivers. I placed the drivers in the case based on the length of the wires coming from the arm. I didn't need to extend any of the wires beyond their original length. You will need to connect the battery power for all the motor drivers together. You also will have to connect the Raspberry Pi ground to the ground pin on one of the drivers. Their grounds are all tied together through the power connections so they all will work. The picture 2 steps back shows the ground connection to the Raspberry Pi.

Step 12: Setting Up the Raspberry Pi

We will use the Raspberry Pi to control this robot. We will program it with Python programming language using the CWiiD library and bluetooth library for the Wii controller.

First we need to install bluetooth

Start by plugging your Bluetooth USB dongle into your Pi and reboot it.

You can check the dongle by typing lsusb

pi@raspberrypi ~ $ lsusb
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.

Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp.

Bus 001 Device 004: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode)

To make sure you have bluetooth installed run the following

sudo apt-get update

sudo apt-get install bluetooth

install a Python library called CWiiD

In order to get informations from the Wiimote through Bluetooth, we have to install a Python library called CWiiD, it is like a "driver" if you want. To install it, it is very simple, just type :

sudo apt-get install python-cwiid

Step 13: About the Code

Ok about the code:

1. First we will assign all the GPIO pins that will control the motor drivers

2. Next create functions for all possible motions of the arm and track

3. Look for the Wii controller and connect to it.

4. Setup pulse width modulation (PWM) for the base rotate, base motion and the gripper. This allows us to slow down the motors for a precise feel by changing the duty cycle of the pulse.

Here is a very good article on PWM

http://raspi.tv/2013/rpi-gpio-0-5-2a-now-has-software-pwm-how-to-use-it

5. The rest of the code is a big loop looking for input from the Wii controller and Nunchuck

For more on Wii programming, here is an instructable that shows the use of every button on the Wii that I used for reference.

https://www.instructables.com/id/Raspberry-Pi-and-Wiimote-controlled-Robot-Arm/step5/The-Code/

import cwiid <br>
import time
import os
import RPi.GPIO as GPIO
from time import sleep
GPIO.cleanup()
#assign GPIO pins 
LeftMotorTrackA = 3
LeftMotorTrackB = 5
RightMotorTrackA = 7
RightMotorTrackB = 8
MotorBaseRotateA = 10
MotorBaseRotateB = 11
MotorBaseMotionA = 12
MotorBaseMotionB = 13
MotorElbowA = 15
MotorElbowB = 16
MotorWristA = 18
MotorWristB = 19
MotorGripperA = 21
MotorGripperB = 22
BaseMotionEnable=31
BaseRotateEnable=33
GripperEnable=35
GripperLED=40
#set GPIO pins to output
GPIO.setmode(GPIO.BOARD)
GPIO.setup(LeftMotorTrackA,GPIO.OUT)
GPIO.setup(LeftMotorTrackB,GPIO.OUT)
GPIO.setup(RightMotorTrackA,GPIO.OUT)
GPIO.setup(RightMotorTrackB,GPIO.OUT)
GPIO.setup(MotorBaseRotateA,GPIO.OUT)
GPIO.setup(MotorBaseRotateB,GPIO.OUT)
GPIO.setup(MotorBaseMotionA,GPIO.OUT)
GPIO.setup(MotorBaseMotionB,GPIO.OUT)
GPIO.setup(MotorElbowA,GPIO.OUT)
GPIO.setup(MotorElbowB,GPIO.OUT)
GPIO.setup(MotorWristA,GPIO.OUT)
GPIO.setup(MotorWristB,GPIO.OUT)
GPIO.setup(MotorGripperA,GPIO.OUT)
GPIO.setup(MotorGripperB,GPIO.OUT)
GPIO.setup(BaseMotionEnable,GPIO.OUT)
GPIO.setup(BaseRotateEnable,GPIO.OUT)
GPIO.setup(GripperEnable,GPIO.OUT)
GPIO.setup(GripperLED,GPIO.OUT)
def TrackDriveForward():
    GPIO.output(LeftMotorTrackA,GPIO.HIGH)
    GPIO.output(LeftMotorTrackB,GPIO.LOW)
    GPIO.output(RightMotorTrackA,GPIO.HIGH)
    GPIO.output(RightMotorTrackB,GPIO.LOW)
def TrackDriveReverse():
    GPIO.output(LeftMotorTrackA,GPIO.LOW)
    GPIO.output(LeftMotorTrackB,GPIO.HIGH)
    GPIO.output(RightMotorTrackA,GPIO.LOW)
    GPIO.output(RightMotorTrackB,GPIO.HIGH)
def TrackDriveLeft():
    GPIO.output(LeftMotorTrackA,GPIO.LOW)
    GPIO.output(LeftMotorTrackB,GPIO.HIGH)
    GPIO.output(RightMotorTrackA,GPIO.HIGH)
    GPIO.output(RightMotorTrackB,GPIO.LOW)
def TrackDriveRight():
    GPIO.output(LeftMotorTrackA,GPIO.HIGH)
    GPIO.output(LeftMotorTrackB,GPIO.LOW)
    GPIO.output(RightMotorTrackA,GPIO.LOW)
    GPIO.output(RightMotorTrackB,GPIO.HIGH)
def TrackDriveStop():
    GPIO.output(LeftMotorTrackA,GPIO.LOW)
    GPIO.output(LeftMotorTrackB,GPIO.LOW)
    GPIO.output(RightMotorTrackA,GPIO.LOW)
    GPIO.output(RightMotorTrackB,GPIO.LOW)
def BaseRotateRight():
    GPIO.output(MotorBaseRotateA,GPIO.HIGH)
    GPIO.output(MotorBaseRotateB,GPIO.LOW)
def BaseRotateLeft():
    GPIO.output(MotorBaseRotateA,GPIO.LOW)
    GPIO.output(MotorBaseRotateB,GPIO.HIGH)
def BaseMotionUp():
    GPIO.output(MotorBaseMotionA,GPIO.HIGH)
    GPIO.output(MotorBaseMotionB,GPIO.LOW)
def BaseMotionDown():
    GPIO.output(MotorBaseMotionA,GPIO.LOW)
    GPIO.output(MotorBaseMotionB,GPIO.HIGH)
def BaseXStop():
    GPIO.output(MotorBaseRotateA,GPIO.LOW)
    GPIO.output(MotorBaseRotateB,GPIO.LOW)
def BaseYStop():
    GPIO.output(MotorBaseMotionA,GPIO.LOW)
    GPIO.output(MotorBaseMotionB,GPIO.LOW)
def ElbowUp():
    GPIO.output(MotorElbowA,GPIO.LOW)
    GPIO.output(MotorElbowB,GPIO.HIGH)
def ElbowDown():
    GPIO.output(MotorElbowA,GPIO.HIGH)
    GPIO.output(MotorElbowB,GPIO.LOW)    
def ElbowStop():
    GPIO.output(MotorElbowA,GPIO.LOW)
    GPIO.output(MotorElbowB,GPIO.LOW)
def WristDown():
    GPIO.output(MotorWristA,GPIO.HIGH)
    GPIO.output(MotorWristB,GPIO.LOW)
def WristUp():
    GPIO.output(MotorWristA,GPIO.LOW)
    GPIO.output(MotorWristB,GPIO.HIGH)
def WristStop():
    GPIO.output(MotorWristA,GPIO.LOW)
    GPIO.output(MotorWristB,GPIO.LOW)
def GripperOpen():
    GPIO.output(MotorGripperA,GPIO.HIGH)
    GPIO.output(MotorGripperB,GPIO.LOW)
def GripperClose():
    GPIO.output(MotorGripperA,GPIO.LOW)
    GPIO.output(MotorGripperB,GPIO.HIGH)
def GripperStop():
    GPIO.output(MotorGripperA,GPIO.LOW)
    GPIO.output(MotorGripperB,GPIO.LOW)
#set pulse width modulation to 50 hz
BMpw = GPIO.PWM(BaseMotionEnable, 50)
BRpw = GPIO.PWM(BaseRotateEnable, 50)
GRpw = GPIO.PWM(GripperEnable, 50)
#start base pwm at 50%
BMpw.start(50)
BRpw.start(50)
#set gripper at a steady 40% to make it work slower
GRpw.start(40)
#Make the Bluetooth dongle discoverable
os.system("sudo hciconfig hci0 piscan")
#connecting to the Wiimote. This allows several attempts 
# as first few often fail. 
print 'Press 1+2 on your Wiimote now...' 
wm = None 
i=2 
while not wm: 
  try: 
    wm=cwiid.Wiimote() 
  except RuntimeError: 
    if (i>10): 
      print "Giving up connecting"
      quit() 
      break 
    print "Error opening wiimote connection" 
    print "attempt " + str(i) 
    i +=1 
    #Pause for a bit
    time.sleep(0.2)
#Got here, tell the user
print "Success - we have connected!"
#set up the inputs we are looking for
wm.rpt_mode = cwiid.RPT_BTN | cwiid.RPT_ACC | cwiid.RPT_EXT
#Wait a bit
time.sleep(0.5)
#Do a rumble
wm.rumble = True
time.sleep(0.5)
wm.rumble = False
#Now start checking for button presses
print "Ready to receive button presses and accelerometer input"
#Loop for ever detecting
try:
  while True:
    #Set up a button object to check
    buttons = wm.state['buttons']
    if buttons & cwiid.BTN_UP:
      print "button UP"
      TrackDriveForward()
    elif buttons & cwiid.BTN_DOWN:
      print "button DOWN"
      TrackDriveReverse()
    elif buttons & cwiid.BTN_LEFT:
      print "button LEFT"
      TrackDriveLeft()
    elif buttons & cwiid.BTN_RIGHT:
      print "button RIGHT"
      TrackDriveRight()
    else:
      print "track stop"
      TrackDriveStop()
    if (buttons & cwiid.BTN_MINUS):
      print 'Minus pressed'
      GripperClose()
    elif (buttons & cwiid.BTN_PLUS):
      print 'Plus pressed'
      GripperOpen()
    elif (buttons & cwiid.BTN_1):
      print '1 pressed'
    elif (buttons & cwiid.BTN_2):
      print '2 pressed'
    else:
      GripperStop()  
      print "else stop"
#the LED is operated directly off the GPIO voltage. All the motors use a driver
    if (buttons & cwiid.BTN_A):
       GPIO.output(GripperLED,GPIO.HIGH)
       print 'a pressed'
    else:
       GPIO.output(GripperLED,GPIO.LOW)
#stop everthing by pressing button B -- panic stop
    if (buttons & cwiid.BTN_B):
      print 'b pressed'
      GPIO.cleanup()
#Here we handle the nunchuk, along with the joystick and the buttons
    while(1):
        if wm.state.has_key('nunchuk'):
            try:
                #Here is the data for the nunchuk stick:
                #X axis:LeftMax = 25, Middle = 125, RightMax = 225
                NunchukStickX = (wm.state['nunchuk']['stick'][cwiid.X])
                #Y axis:DownMax = 30, Middle = 125, UpMax = 225
                NunchukStickY = (wm.state['nunchuk']['stick'][cwiid.Y])
                #The 'NunchukStickX' and the 'NunchukStickY' variables now store the stick values
                print NunchukStickX
                print NunchukStickY
                if (NunchukStickY >160):
                    print "elbow down"
                    #fine tune the arm motions by changing duty cycle
                    BMpw.ChangeDutyCycle(20)
                    ElbowDown()
                elif (NunchukStickY < 110):
                    print "elbow up"
                    BMpw.ChangeDutyCycle(40)
                    ElbowUp()
                else:
                    ElbowStop()
                    print "ystop"                
                #Here we take care of all of our data for the accelerometer
                #The nunchuk has an accelerometer that records in a similar manner to the wiimote, but the number range is different
                #The X range is: 70 if tilted 90 degrees to the left and 175 if tilted 90 degrees to the right
                #The Y range is: 70 if tilted 90 degrees down (the buttons pointing down), and 175 if tilted 90 degrees up (buttons $
                numchkX = wm.state['nunchuk']['acc'][cwiid.X]
                numchkY = wm.state['nunchuk']['acc'][cwiid.Y]
                #make the arm movement proportional to the nunchuck by changing PWM
                if (numchkY >160):
                   if (numchkY < 170):
                       BMpw.ChangeDutyCycle(20)
                       BaseMotionDown()
                   else:
                       BMpw.ChangeDutyCycle(40)
                       BaseMotionDown()
                elif (numchkY < 120):
                   if (numchkY > 110):
                       BMpw.ChangeDutyCycle(60)
                       BaseMotionUp()
                   else:
                       BMpw.ChangeDutyCycle(95)
                       BaseMotionUp()
                else:
                   BaseYStop()
                if (numchkX <100):
                   if (numchkX > 80):
                       BRpw.ChangeDutyCycle(20)
                       BaseRotateLeft()
                   else:
                       BRpw.ChangeDutyCycle(40)
                       BaseRotateLeft()
                elif (numchkX > 140):
                   if (numchkX < 160):
                       BRpw.ChangeDutyCycle(20)
                       BaseRotateRight()
                   else:
                       BRpw.ChangeDutyCycle(45)
                       BaseRotateRight()
                else:
                   BaseXStop()
                print str(numchkX)+' '+str(numchkY)
                ChukBtn = wm.state['nunchuk']['buttons']
                if (ChukBtn == 1):
                    WristDown()
                elif (ChukBtn == 2):
                    WristUp()
                else:
                    WristStop()
                break
            except KeyError:
                print 'No nunchuk detected.'
        else:
            if (Counter == 10000):
                print 'No nunchuk detected.'
                Counter = Counter/10000
                break
            Counter = Counter + 1
            break
    #Chill for a bit
    time.sleep(0.3)
except KeyboardInterrupt:
    pass
print "Goodby"

Step 14: Auto Start the Python Program

When I want to use the robot, I would like to turn on the motor battery and turn on the computer battery and be ready to hook up to the Wii. You just push the 1 and 2 button on the Wii and wait for it to rumble, then you know it is connected. Here is how to set it up like that:

First open the /etc/rc.local file

sudo nano /etc/rc.local

Now that is open I insert the following line right ahead of #Print the IP address

python /home/pi/pythoncode/robottrackarm.py &

This will run Python and my program on startup

#!/bin/sh -e<br>
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
python /home/pi/pythoncode/robottrackarm.py &
# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi
exit 0

Step 15: Here Is the Final Package

That finishes this project. Here is the final look with everything plugged in and the top covers on. I hope I have inspired someone to give this a try. Let me know what you think about the project.

Raspberry Pi Contest

Participated in the
Raspberry Pi Contest