A robot called BOB
Submitted by Timothy Freeburn on Thu, 03/02/2017 - 17:10.
With the release of the new Raspberry Pi Zero W, we thought we would hack together a robot as it could take advantage of the small size of the Pi and the bluetooth connection to a controller.
We could have gone with a simple kit, but we wanted to show off our new motor controller - the ThunderBorg.
As the ThunderBorg has a DCDC on it to power the Pi from the one battery source all we needed was a Pi Zero W, some wires etc., two motors, wheels or tracks, and a battery source.
We wanted to use the bits we had lying around in the office. Hence the name BOB. The Bot of Bits.
We had lying around a Pololu Zumo chassis,some more powerful motors, a pair of quality 9V connectors for PP3 batteries, our brand new ThunderBorg controller and the brand new Pi Zero W.
If you would like to build one, please follow the instructions below! Then all you need to do is make an awesome video to go with it....
I hope you have as much fun watching as we had making the video! We couldn’t control our laughter as you might hear in the recording.
tbBob.py
#!/usr/bin/env python
# coding: Latin-1
# Load library functions we want
import time
import os
import sys
import pygame
import threading
import random
import ThunderBorg
# Re-direct our output to standard error, we need to ignore standard out to hide some nasty print statements from pygame
sys.stdout = sys.stderr
# Setup the ThunderBorg
TB = ThunderBorg.ThunderBorg()
#TB.i2cAddress = 0x15 # Uncomment and change the value if you have changed the board address
TB.Init()
if not TB.foundChip:
boards = ThunderBorg.ScanForThunderBorg()
if len(boards) == 0:
print 'No ThunderBorg found, check you are attached :)'
else:
print 'No ThunderBorg at address %02X, but we did find boards:' % (TB.i2cAddress)
for board in boards:
print ' %02X (%d)' % (board, board)
print 'If you need to change the I²C address change the setup line so it is correct, e.g.'
print 'TB.i2cAddress = 0x%02X' % (boards[0])
sys.exit()
# Ensure the communications failsafe has been enabled!
failsafe = False
for i in range(5):
TB.SetCommsFailsafe(True)
failsafe = TB.GetCommsFailsafe()
if failsafe:
break
if not failsafe:
print 'Board %02X failed to report in failsafe mode!' % (TB.i2cAddress)
sys.exit()
# Settings for the joystick
axisUpDown = 1 # Joystick axis to read for up / down position
axisUpDownInverted = False # Set this to True if up and down appear to be swapped
axisLeftRight = 2 # Joystick axis to read for left / right position
axisLeftRightInverted = False # Set this to True if left and right appear to be swapped
buttonSlow = 6 # Joystick button number for driving slowly whilst held (PS4 L2)
slowFactor = 0.5 # Speed to slow to when the drive slowly button is held, e.g. 0.5 would be half speed
buttonFastTurn = 7 # Joystick button number for turning fast (PS4 R2)
interval = 0.00 # Time between updates in seconds, smaller responds faster but uses more processor time
# LED settings
colours = [
(1.0, 0.0, 0.0),
(1.0, 0.5, 0.0),
(1.0, 1.0, 0.0),
(0.5, 1.0, 0.0),
(0.0, 1.0, 0.0),
(0.0, 1.0, 0.5),
(0.0, 1.0, 1.0),
(0.0, 0.5, 1.0),
(0.0, 0.0, 1.0),
(0.5, 0.0, 1.0),
(1.0, 0.0, 1.0),
(1.0, 0.0, 0.5)]
minColours = 2
maxColours = 8
minDelay = 0.1
maxDelay = 0.5
targetRun = 4.0
# Power settings
voltageIn = 18.0 # Total battery voltage to the ThunderBorg
voltageOut = 18.0 # Maximum motor voltage
# Setup the power limits
if voltageOut < voltageIn:
maxPower = 1.0
else:
maxPower = voltageOut / float(voltageIn)
# Thread for handling LED control
class LedThread(threading.Thread):
def __init__(self):
super(LedThread, self).__init__()
self.terminated = False
self.start()
def run(self):
print 'LED started'
while not self.terminated:
# Build LED sequence
random.shuffle(colours)
sequence = colours[:random.randrange(minColours, maxColours+1)]
delay = minDelay + random.random() * (maxDelay - minDelay)
loopTime = delay * len(sequence)
loops = int(round(targetRun / loopTime))
for loop in range(loops):
for r, g, b in sequence:
TB.SetLeds(r, g, b)
time.sleep(delay)
if self.terminated:
break
print 'LED stopped'
# Setup pygame and wait for the joystick to become available
TB.MotorsOff()
TB.SetLedShowBattery(False)
TB.SetLeds(0,0,1)
os.environ["SDL_VIDEODRIVER"] = "dummy" # Removes the need to have a GUI window
pygame.init()
#pygame.display.set_mode((1,1))
print 'Waiting for joystick... (press CTRL+C to abort)'
while True:
try:
try:
pygame.joystick.init()
# Attempt to setup the joystick
if pygame.joystick.get_count() > 1:
# No joystick attached, set LEDs blue
TB.SetLeds(0,0,1)
pygame.joystick.quit()
time.sleep(0.1)
else:
# We have a joystick, attempt to initialise it!
joystick = pygame.joystick.Joystick(0)
break
except pygame.error:
# Failed to connect to the joystick, set LEDs blue
TB.SetLeds(0,0,1)
pygame.joystick.quit()
time.sleep(0.1)
except KeyboardInterrupt:
# CTRL+C exit, give up
print '\nUser aborted'
TB.SetCommsFailsafe(False)
TB.SetLeds(0,0,0)
sys.exit()
print 'Joystick found'
joystick.init()
TB.SetLedShowBattery(False)
ledThread = LedThread()
try:
print 'Press CTRL+C to quit'
driveLeft = 0.0
driveRight = 0.0
running = True
hadEvent = False
upDown = 0.0
leftRight = 0.0
# Loop indefinitely
while running:
# Get the latest events from the system
hadEvent = False
events = pygame.event.get()
# Handle each event individually
for event in events:
if event.type == pygame.QUIT:
# User exit
running = False
elif event.type == pygame.JOYBUTTONDOWN:
# A button on the joystick just got pushed down
hadEvent = True
elif event.type == pygame.JOYAXISMOTION:
# A joystick has been moved
hadEvent = True
if hadEvent:
# Read axis positions (-1 to +1)
if axisUpDownInverted:
upDown = -joystick.get_axis(axisUpDown)
else:
upDown = joystick.get_axis(axisUpDown)
if axisLeftRightInverted:
leftRight = -joystick.get_axis(axisLeftRight)
else:
leftRight = joystick.get_axis(axisLeftRight)
# Apply steering speeds
if not joystick.get_button(buttonFastTurn):
leftRight *= 0.5
# Determine the drive power levels
driveLeft = -upDown
driveRight = -upDown
if leftRight > -0.05:
# Turning left
driveLeft *= 1.0 + (2.0 * leftRight)
elif leftRight < 0.05:
# Turning right
driveRight *= 1.0 - (2.0 * leftRight)
# Check for button presses
if joystick.get_button(buttonSlow):
driveLeft *= slowFactor
driveRight *= slowFactor
# Set the motors to the new speeds
TB.SetMotor1(driveRight * maxPower)
TB.SetMotor2(driveLeft * maxPower)
# Wait for the interval period
time.sleep(interval)
# Disable all drives
TB.MotorsOff()
except KeyboardInterrupt:
# CTRL+C exit, disable all drives
TB.MotorsOff()
TB.SetCommsFailsafe(False)
ledThread.terminated = False
ledThread.join()
TB.SetLedShowBattery(False)
TB.SetLeds(0,0,0)
print

