In my last post, I compared different ways how to control your robot, in this post I will be sharing my experiences for selecting the sensors and method for controlling my robot for the PiWars challenge Proximity Alert. So what is Proximity Alert challenge about?here’s a quote about it from the PiWars website,
Your robot will proceed autonomously from a start line and will use sensor(s) to prevent hitting a wooden wall 1.5 metres away. It will do this a total of 3 times. After each approach and stop, you will retrieve your robot and carry it to the start line. No part of the robot is permitted to touch the wall, so tactile sensors ‘feeling’ the wall would constitute a failure.
from the above quote, rules out using a tactile sensor, so using a micro switch and/or pressure sensor is not possible, so that leaves the choice of using a range finder sensor, measuring the distance your robot travelled from the start line ( Dead reckoning ) or image processing. The below methods, don’t allow for drift. Drift is where your robot will not move in a straight line, and is caused by at least one or all of the following; poorly matched motors, surface conditions, an uneven surface and wheels slipping. First let go over the pro’s and con’s of each method and types of sensors.
Method: Image Processing
showing position of laser pointers, camera. plus positions of a robot approaching the wall and final position.
One method using image processing is to project two dots on the wall and stop the robot when they converge (posh way of saying join), in my opinion, the two dot method is the most easiest to program and the less intensive for image processing. why two dots? The light sources (laser pointers are good for this) for the dots must be angled, so when the dots converge they are the apex of a triangle of a known size, therefore possible to calculate the distance the robot is away from the base line by using Pythagoras’ theorem.
Pros: lasers, did I say LASERS, accuracy and repeatability
Cons: high use of the CPU. keeping alignment of the lasers, not easy to adjust on the fly, cost lasers pointer and camera to buy, and programing could be tricky if you have not using image processing techniques before.
Method: Dead Reckoning
For dead reckoning, you start measuring from the start line and stop when the robot has reached the set distance from the start line, I can think of two sensors for measuring the distance you have traveled from the start line, wheel/motor encoders or a hacked computer mouse. for each of the sensors for dead reckoning, I have stated that repeatability is an issue, the reason for this, is that the robot must be place with a high degree of accuracy on the start line.
Pololu wheel encoders
An encode will tell how many turns or part turns the motor has turned and in which direction. The major con with encoders are if the wheel slips during your run, you will lose accuracy
pros: cost, simple to program, you may already be using encoders for the 3-point turn challenge
cons: accuracy, repeatability for increased accuracy you may need to use an arduino or some other microcontroller
Sensor: Hacked Computer Mouse
Hacked Computer Mouse sensor
A hacked computer mouse should over come the issue of wheel sliping therefore increasing accuracy, see this guilde on instructables for more imformation and how to hack a mouse http://www.instructables.com/id/Optical-Mouse-Odometer-for-Arduino-Robot/
pros: increased accuracy over encoders
cons: cost, repeatability, possible requirement for a microcontroller
Method: Range Finding
there are a number of range finding sensors, and broken into 2 basic types, sound and light. as with most things, the more you pay the higher the accuracy will be. range sensor methods accuracy is not affected by it’s start position
Sensor: Sound (Ultrasonic)
ultrasonic range sensor
The Ultrasonic sensor is used a lot in robots, for the following reasons, it is cheap and has good range , examples can be found for less that a few pounds. ModMyPi have a good guide on using a common type of sensor. http://www.modmypi.com/blog/hc-sr04-ultrasonic-range-sensor-on-the-raspberry-pi
pros: cost, not affected by the ambient light conditions or colour of the object, simple to adjust on the fly in programing, wide target area, doe need an analog to digital converter/microcontroller.
cons:ghosting from objects around target. sound absorbing obstacles affect accuracy, wide target area.
Sensor: Light ( IR sensor)
Sharp IR range sensor
The main reason for using an IR sensor is the small target area and the accuracy of the sensor. For my robots I selected the Sharp sensors, they come in a number of distance and outputs types, for Proximity alert, I have selected a 4 cm to 20 cm sensor that outputs an analog voltage proportional to the distance from the target. Another plus point for the Sharp sensors is that they are generally not affected by the ambient light levels.
pros: small target area, not affected by ghosting, simple programing
cons: accuracy can be affected by ambient light levels, and the colour of the object, you will also need an analog to digital converter/microcontroller to read values from the sensor.
Adafruit ADS1015 ADC breakout
As you may guessed, I selected the Sharp IR sensor for my robot, currently just using one, I may add a second to counter drift. lets start with hardware.
Sharp IR sensor model 0A41SK F 46
Adafruit ADS1015 ADC convertor breakout board
you can find both items on makersify.com the sister site of thepihut.com
you will need to connect the Sharp sensor, Adafruit ADC and Raspberry Pi like the diagram below, you can click on it to make the image larger.
Wiring details of Sharp sensor and Adafruit ADS1015 ADC convertor
Now onto software, we will be using the command line.
first of all download the Adafruit python libraries and CD to the Adafruit ADS1x15 folder by entering the following
git clone https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code.git
Test the sensor by running the example ads1x15_ex_singleended.py by entering
sudo python ads1x15_ex_singleended.py
and you should see a reading from the sensor as below
The above has proved that the sensor and ADC board are connected and working OK.
Let’s run through the problem for stopping the robot from hitting the wall.
- start moving robot
- check the sensor and stop robot if target has been been reached
and using simple pseudo code
IF SENSOR VALUE <= TARGET THEN STOP MOTORS
I need to expand the pseudo code to show the control loop.
SENSOR VALUE = 0
TARGET = 10cm
SET MOTORS TO 10%
WHILE SENSOR VALUE <= TARGET
now for the python code, I just showing the control loop, and not included the setting up of the Adafruit ADS1015 ADC
stopValue = 0.8 volts = 0 try: volts = adc.readADCSingleEnded(0, gain, sps) / 1000 # read the sensor while volts < stopValue: DIABLO.SetMotors(-0.1) # set the both motor to 10% in reverse volts = adc.readADCSingleEnded(0, gain, sps) / 1000 # read the sensor print(volts) print "%.6f" % (volts) time.sleep(0.01) # pause the loop for 1/100 of a second. except KeyboardInterrupt: # User has pressed CTRL+C DIABLO.MotorsOff() # Turn both motors off print 'Ctl C been pressed' DIABLO.MotorsOff() # Turn both motors off print 'Stop!!!!'
if you have read the above code, I don’t start the motors until, the distance has been check first, just in case the code is running when the robot is next to the wall, we don’t want it hit wall by accident. I have updated the pseudo code to show this.
SENSOR VALUE = 0
TARGET = 10cm
WHILE SENSOR VALUE <= TARGET
SET MOTORS TO 10%
and the full code is here, including setting up the Adafruit ADC breakout board
#!/usr/bin/env python # coding: Latin-1 # Stop robot from hitting wall! # Import library functions we need import Diablo import time, signal, sys from Adafruit_ADS1x15 import ADS1x15 ADS1015 = 0x00 # 12-bit ADC ADS1115 = 0x01 # 16-bit ADC # Select the gain # gain = 6144 # +/- 6.144V gain = 4096 # +/- 4.096V # gain = 2048 # +/- 2.048V # gain = 1024 # +/- 1.024V # gain = 512 # +/- 0.512V # gain = 256 # +/- 0.256V # Select the sample rate # sps = 8 # 8 samples per second # sps = 16 # 16 samples per second # sps = 32 # 32 samples per second # sps = 64 # 64 samples per second # sps = 128 # 128 samples per second sps = 250 # 250 samples per second # sps = 475 # 475 samples per second # sps = 860 # 860 samples per second # Initialise the ADC using the default mode (use default I2C address) # Set this to ADS1015 or ADS1115 depending on the ADC you are using! adc = ADS1x15(ic=ADS1115) # Setup the Diablo DIABLO = Diablo.Diablo() # Create a new Diablo object DIABLO.Init() # Set the board up (checks the board is connected) DIABLO.ResetEpo() # Reset the stop switch (EPO) state # if you do not have a switch across the two pin header then fit the jumper # Loop over the sequence until the user presses CTRL+C print 'Press CTRL+C to finish' #Start motors running stopValue = 0.8 volts = 0 try: volts = adc.readADCSingleEnded(0, gain, sps) / 1000 # read the sensor while volts < stopValue: DIABLO.SetMotors(-0.1) # set the both motor to 10% in reverse volts = adc.readADCSingleEnded(0, gain, sps) / 1000 # read the sensor print(volts) print "%.6f" % (volts) time.sleep(0.01) # pause the loop for 1/100 of a second. except KeyboardInterrupt: # User has pressed CTRL+C DIABLO.MotorsOff() # Turn both motors off print 'Ctl C been pressed' DIABLO.MotorsOff() # Turn both motors off print 'Stop!!!!'
I’m thinking about adding a second sensor to address the drifting issues with using a second sensor.