New Wheels

Found some used wheels on eBay in the correct size a few days ago & they arrived today. The problem with the old ones was they were very wide and tough for the servos to move; where-as, the new ones are slightly taller and only 2/3 of the width. Stood on the desk the servo’s are struggling much less, this may also be helped by the new tyres having a more rounded profile.

IMG_1617Comparison of old tires to the new ones.

 

A Little Maths

Yep it had to happen! I promise I keep it simple(ish).

Now I have the PS3 controller values as inputs and the servo PWM module as outputs and I need a way of linking the two together. For now, the link input -> output is going to be simple to allow manual control but later this will be altered to add automation.

The joystick values are from -1.0 .. +1.0 and so the first thing is to add +1 to the value to make positive 0.0 .. +2.0. The PWM is an integer value as opposed to floating-point so we’ll correct that later. The output values we need for the PWM are 150 (min value) to 600 (max value); so we are scaling 0.0 .. +2.0 into 150 .. 600. OK so far?

I promise I keep it simple

Tough bit now!

  1. CorrectedInput = input +1
  2. Span = (max – min) / 2
  3. Output = (CorrectedInput * Span) + min
  4. IntegerOutput = int(Output)
  • Line 1, is input from the PS3 controller of 0.0 .. +2.0.
  • Line 2, calculates the required span of the output (max – min, e.g. 450) and divided by 2 (e.g. 225) to correct for the input being 0.0 .. +2.0.
  • Line 3, CorrectedInput multiplied by the Span gives the required value (e.g. 0.0 .. +450.0) and adding min gives the offset (+150.0 .. +600.0).
  • Line 4, converts the floating point number into integer for the PMW module to use.

Phew!

In practice I found a couple of issues:

  1. The servos don’t travel full range and so I reduced min & max to suit.
  2. The joystick inputs were backwards to what was required for the direction of the vehicle and so, I subtracted the CorrectedInput from 2.0 to swap around the value (e.g. +2.0 .. 0.0 as opposed to 0.0 .. +2.0).

A snippet of code to do this might be :

# Axis_IPs [0] is the input from axis 0 on the joystick
# PWM_OPs [0] is the output to channel 0 on the PWM module

max = 600
min = 150

span = (max - min) / 2
CorrectedInput = 2 - (Axis_IPs [0] + 1)
Output = (CorrectedInput * span ) + min
PWM_OPs [0] = int(Output)

LED Display

There are a number of different modes… those I have thought of so far as listed and some I’ve not devised yet! So I will use the up & down buttons on the PS3 controller to increment & decrement a value for selecting the modes and a 7 segment LED digit to display. The modes are:

0: all off
1: normal front steer
2: crab steer
3: opposite steer
4: free front & rear steer
5: rear steer
6: servo test with no drive
7 & 8: not used

The dot on the display is used to show at a glance when the drive is active & so is on for modes 1..5.

As I already have Adafruit’s 16-channel 12-bit PWM module & its stated as also being able to drive LEDs, I’ll use this to drive the display. The display is connected to channels 4..11. When a segment of the display is required it will be set to 100% using a value of 4096 (or off with a value of 0).

add LED wiring details and photo

Testing Servos

The servos were just the cheapest heavy-duty ones from the local model shop, but even so I may need larger to move the huge wheels. As this a rock-crawler model, both the axles are identical with a separate motor and steering servo. The advantage for this setup is that we have 4 wheel drive and 4 wheel steering. The modes for the steering maybe:

  • Normal front wheel steering
  • Rear wheel steer like a forklift truck
  • Crab steer where the vehicle moves sideways with both axles turned in the same direction
  • Opposite steer for a quick turn with axles in different directions
  • Random steer of both axles

Both axles are the same with the execution of the motor turns in the opposite direction on the rear.

IMG_1621

Rear axle showing servo and steering.

IMG_1622

Rear axle showing motor location.

add pics of modes

We have 4 wheel drive and 4 wheel steering.

To control the servos (and ultimately the speed controller) the Adafruit’s 16-channel 12-bit PWM/Servo Driver was selected with the idea of using as many standard RC bits as possible, hence using a standard speed controller not a RPi specific one; whether this is a good idea remains to be seen! setting up the driver board went pretty smoothly using the directions on Adafruit’s site; front servo was connected to channel 0 and rear to channel 2.

The code for testing the servo was copied (and slightly modified) and did move the servos back & forth as expected like a wiper blade.

# Simple demo of of the PCA9685 PWM servo/LED controller library.
# This will move channel 0 from min to max position repeatedly.
# Author: Tony DiCola
# License: Public Domain
from __future__ import division
import time

# Import the PCA9685 module.
import Adafruit_PCA9685


# Uncomment to enable debug output.
#import logging
#logging.basicConfig(level=logging.DEBUG)

# Initialise the PCA9685 using the default address (0x40).
pwm = Adafruit_PCA9685.PCA9685()

# Alternatively specify a different address and/or bus:
#pwm = Adafruit_PCA9685.PCA9685(address=0x41, busnum=2)

# Configure min and max servo pulse lengths
servo_min = 150 # Min pulse length out of 4096
servo_max = 600 # Max pulse length out of 4096

# Helper function to make setting a servo pulse width simpler.
def set_servo_pulse(channel, pulse):
 pulse_length = 1000000 # 1,000,000 us per second
 pulse_length //= 60 # 60 Hz
 print('{0}us per period'.format(pulse_length))
 pulse_length //= 4096 # 12 bits of resolution
 print('{0}us per bit'.format(pulse_length))
 pulse *= 1000
 pulse //= pulse_length
 pwm.set_pwm(channel, 0, pulse)

# Set frequency to 60hz, good for servos.
pwm.set_pwm_freq(60)

print('Moving servo on channel 0, press Ctrl-C to quit...')
while True:
 # Move servo on channel O between extremes.
 pwm.set_pwm(0, 0, servo_min)
 time.sleep(1)
 pwm.set_pwm(0, 0, servo_max)
 time.sleep(1)

There is a good explanation of PWM & Servos here. The important lines of code to note (taken from the Adafruit instructions) are:

  • pwm.set_pwm_freq(60)

    Where the argument is (freq) between 40 and 1000.

    pwm.set_pwm(0, 0, servo_min)

    The arguments are (channel, on, off). The module is 12 bit and so pulse is divided into 4096 “ticks”. For our purposes we need pulse widths from 1mS (minimum) to 2mS (maximum), about 150..600 (centre is about 375) & all starting at 0, the start of the pulse.

Raspberry Pi + PS3 Controller = Major Headache

Getting the PS3 controller working was (so far) the worst part of the project! There seem to be LOADS of examples on t’internet of how to do this, but with a couple of problems:

  • They are quite old
  • They are based on the same example
  • They DON’T WORK!!! At least not with the current OS distro.

Getting the PS3 controller working was the worst part of the project!

The best example I found (that works) is here:  https://pythonhosted.org/triangula/sixaxis.html I have taken a pdf save and it’s here should it ever be deleted: Configuring Playstation 3 Controllers — Triangula 0.3 So, Top Bloke and many thanks!

The most important (and satisfying) point is the instruction:

pi@raspberrypi~$ls/dev/input

2016-08-21-102200_1400x1050_scrot

And then seeing “jso” in the response. This shows the bluetooth receiver is setup with the driver and the controller has connected.

coffee-png

I followed the example as far as the section about accessing the values from within python but it appears to be more complicated than necessary. So, other examples were found of the python code and the following was cobbled together (sorry I can’t remember where the code came from… please let me know if you know):

import pygame

# Define some colors
BLACK = ( 0, 0, 0)
WHITE = ( 255, 255, 255)

# This is a simple class that will help us print to the screen
# It has nothing to do with the joysticks, just outputing the
# information.
class TextPrint:
 def __init__(self):
 self.reset()
 self.font = pygame.font.Font(None, 20)

def prt(self, screen, textString):
 textBitmap = self.font.render(textString, True, BLACK)
 screen.blit(textBitmap, [self.x, self.y])
 self.y += self.line_height
 
 def reset(self):
 self.x = 10
 self.y = 10
 self.line_height = 15
 
 def indent(self):
 self.x += 10
 
 def unindent(self):
 self.x -= 10


pygame.init()

size = [500, 800]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My Game")

#Loop until the user clicks the close button.
done = False

# Used to manage how fast the screen updates
clock = pygame.time.Clock()

# Initialize the joysticks
pygame.joystick.init()
 
# Get ready to print
textPrint = TextPrint()

# -------- Main Program Loop -----------
while done==False:
 # EVENT PROCESSING STEP
 for event in pygame.event.get(): # User did something
 if event.type == pygame.QUIT: # If user clicked close
 done=True # Flag that we are done so we exit this loop

screen.fill(WHITE)
 textPrint.reset()

joystick = pygame.joystick.Joystick(0)
 joystick.init()
 
 axes = 4 
 for i in range( axes ):
 axis = joystick.get_axis( i )
 aaa = axis + 1
 textPrint.prt(screen, "Axis {} value: {:>6.4f}".format(i, aaa) )
 textPrint.unindent()
 
 buttons = joystick.get_numbuttons()
 for i in range( buttons ):
 button = joystick.get_button( i )
 textPrint.prt(screen, "Button {:>2} value: {}".format(i,button) )
 textPrint.unindent()
 
 # Go ahead and update the screen with what we've drawn.
 pygame.display.flip()

# Limit to 20 frames per second
 clock.tick(20)
 
pygame.quit ()


When this runs (if the PS3 controller is correctly connected) the window will display the 4 values for the 2 joysticks and state for each of the buttons. I did initially use all the analogue values but with the accelerometers constantly moving it just made things too confusing and so I limited to just the 4 axis for the joysticks.

2016-08-21-102551_1400x1050_scrot