So now it's time to carry on building my robotic poker table! Before that though - more on my tools :) Up in that photo is:
A drill
An impact driver (kind of like a drill but vibrates as it turns - good for use as a power screw driver)
A jig saw
A power planer
A circular saw
A torch (came with the wireless tools above))
A router
And finally, a massive mitre saw
Here's the mitre saw in all its glory:
The big daddy of saws - a Mitre Saw
It's job is to take slightly wonky planks of wood and cutting them until they're straighter than Chuck Norris.
Anyhoo, enough on the power tool front. Time to cut up some wood until we have a table. Todays job was to cut wood for each leg and the cross frame on which the top of the table rests. The legs are made up of 3 slices of ply wood which will eventually be glued together and then planed off so they're perfectly flat.
After a few hours with the circular saw, me Dave and Angela finished chopping the legs and the cross frame. You can see the finished bits here, along with the lower shelf and table surface I made earlier.
All the bits of wood for the table cut roughly to size
Crucially, all the bits we cut today are larger than they need to be by at least 5mm. This gives us plenty of room to trim them down using the mitre saw, power planer, or even a bit of sand paper for the fine tuning, and get a really nice smooth finish.
Once assembled, the corners of the table will be 3 layered legs, reinforced with the cross frame:
Table corners without showing joints
Unfortunately that'd fall to bits pretty quickly, so we need to do some joinery. Thankfully having 3 layered legs makes this relatively easy for a novice such as myself. I'm going to cut sections out of 2 layers for the side and cross beams to sit in, along with a small join at the top to hold the surface in place:
3 layers of the leg after joint sections are cut
Unfortunately this isn't a job for the meaty circular saw, so I cut back to the more controllable jig saw and after some careful measuring I make the first joint:
A table corner with a finished joint
Pretty nifty even if I do say so myself (and I do). Once the first joint is done, I use it as a template to build another 5 (I'm creating 2 spares in case I mess any up), rather than measuring everything out again. This goes pretty well and by the end of the day I have a frame that almost stands up:
When you consider there's nothing holding that frame up other than a couple of clamps to keep 2 of the 4 legs together, you get a picture of how powerful some simple joinery is.
Well, that's all I got to today (I won't bother blogging about the hour I spent tidying up). Next up I'll get those planks home and cut them all to beautifully precise widths using the mitre saw, then glue the legs together and plane them down as 1 unit to get a perfect finish. Won't be long before I can start work on the electo-mechanical parts :)
I had a dream about theremins the other day (those things that make noises in response to you moving your hands) and pondered how to build one, or something similar, or just something that makes noise. Having pondered how to build one, I thought of some stuff and had absolutely no choice but to try it out :)
My initial plan was to use 1 or more IR range finders to measure the distance of an object from a sensor, then use that distance (or some function of it) to choose a pitch. An Arduino then generates a wave and pumps it out through a speaker, so the very simple circuit would be along the lines of this:
Simple circuit for Arduino to drive a speaker using IR sensor readings
In case you're wondering, that little speaker circuit is genuinely all you need to generate noise from an Arduino - a little 80ohm speaker plus a small resistor and you can send it a wave (potentially using the handy 'tone' command) to generate a noise. However this has some major floors:
It's fairly quiet, as you're powering the speaker off the Arduino
You can only really generate square waves, as the GPIO pins are either on or off
Again, due to the binary nature of the GPIO pins you can't digitally control volume
Anyhoo, it quickly became apparent that this wouldn't do the job. Initially I had a blast at building an R2R ladder digital-to-analog converter (excellent web page on them here), which would allow the Arduino to output a 10 bit binary value, which is then converted to a voltage and pumped into the speaker. Unfortunately once this was working the signal was so weak that it couldn't drive the speaker. Fortunately, I then remembered my Speak Jet chip from this earlier post.
The Speak Jet is a speech synthesis chip, which internally uses a 5 channel synthesizer to produce phoneoms. However you can take direct control over the synth if you prefer, which is exactly what I decided to do. So first step is to expand the circuit to this one:
I haven't shown the whole speak jet setup (which you can peruse at your leisure from earlier posts), but it's basically the speak jet chip plus a *20 gain audio amplifier built with an LM386 op amp. A bit of wiring later and....
That bread board is the speak jet and op amp squeezed into a neat little circuit that I'll probably never dismantle again. Although it looks a little complex, it's mostly just the right organisation of capacitors and resistors to build a simple audio amplifier from an LM386 as specified in their manual. In addition to the circuitry, you can see the oscilliscope attached to the speaker output, with the wave being generated on it.
With a little bit of code to read values from the sensor, convert them into a frequency range and then send over serial to the speak jet (which I might post at some point), I end up with this ear bleeding monstrosity:
If you had headphones on when you played that then I am sorry.
Anyhoo, it makes noise but isn't particularly useful. A real instrument does more than make noise - it is controllable, so the user can choose when it should make noise. I decide the key to this instrument is to add a push button to 'strum' the note. So with a small tweak to the circuitry:
I now have a push switch in there, and update the code so it sends volume commands to the speak jet in addition to pitch commands. When the user is holding down the push switch the note is played at full volume, and after releasing it the note decays for half a second. In addition, the pitch can only change when the button is down, allowing the user to bend the note as they play it. Unfortunately my musical abilities are limited to the listening side of things, so I grab Mark.A from his work and hand him a plank of wood and a push button:
And there you have it! An Infra Red Synthesizer :)
Not done too much more on the table, but I've put together a few photos and a video to show it.
First up, here's the video, build in OpenSCad of the components falling into place and then rotating the table central section:
It gets the idea across pretty well. Once the key switch is activated and the push button is hit, that central section will spin round 180 degrees to flip from poker to coffee mode, or visa versa.
Now these are the actual components I have so far:
Note the plastic connector that attaches the motor to the steel axle. After wondering how to do this for ages, it suddenly occurred to me I could just design them in a CAD package and print them off on my 3D printer. The connectors have correctly sized holes in at each end, so all that's left is to sand off the axle a bit (so it has a rough surface), then glue it in with high torque super glue or epoxy resin.
All I'm missing now are some springs to fit into the solenoid, a battery and the key switch.
So as I said, we've actually done a bit of work as well - one table lid coming up:
That central section is the bit that'll have glass on one side, and felt on the other.
And finally, a sneak preview of my design for telescopic legs (which won't be in version 1):
Ultimately this is a home made rotary linear actuator, which uses a threaded rod attached to a motor to push a bolt forwards or backwards, translating rotary movement into linear movement. Whether or not I end up using this for the table, it's a nice design for a powerful and accurate home made actuator, which typically cost quite a bit to buy pre-made.
For a long while I've wanted to create a poker table (cos I play poker), however my small flat doesn't have room for one. As a result, I decided to create a coffee table that could transform into a poker table... electronically of course. Originally I was going to get cooler and add an entirely pointless auto-levelling telescopic leg setup as well, but I'm gonna leave that till later or it'll take ages!
I started from this basic design, which is based around the idea of a rotating midsection controlled by 2 motors at each end:
On John's advice we decide to build the table out of marine ply wood - a very high quality and strong material, ideal for projects like this. It turns out you can only by 2.4mx1.2m sheets, so I grab one that is 18mm thick and another that is 12mm thick, which comes to quite a lot of money, but what the hell - this is a piece of furniture so needs to be beautiful as well as functional!
Anyhoo, once the basic idea is down on paper, the actual mechanism needs designing. After some pondering I came down to:
2 miniature high torque geared DC motors to turn the central section
4 solenoids placed along the sides of the table that retract when the lid turns, then pop back out to lock it in place
4 microswitches placed along the sides of the table to detect when it is in the correct position
An Arduino Nano to drive it all
A rechargeable battery stashed in one of the legs
A pair of switches (one of which is a key switch) to trigger a revolution.
The pair of switches is quite crucial, as the last thing we want is accidental table turning. Hence I'll have 1 switch that you must turn on with a key, before hitting a push button to actually trigger the turn. Once this has happened, the key switch must be de-activated before another turn can occur. I'm also tempted to set it up so the push button must be pressed within 30s of the key switch being activated. Super careful basically. I don't want my dinner suddenly flipped upside down accidentally!
So, with this in mind, I switch to a CAD program to model it. First up, a view of the actual table:
As you can see, it looks like a fairly classic 2 shelve design, the real difference being that the glass central section can spin round to reveal a felt lined surface, ideal for poker.
These next 2 images show some of the intended components:
Components embedded in table
Components raised out of the table so you can see them.
You can see the key parts here - a solenoid and microswitch to make up the detect/lock mechanism, and a motor with a long axle to turn the central section.
The few remaining bits to work out are:
Where to put the battery? I'm thinking a rechargable battery embedded in one of the legs would be ideal. I'd rather not have a mains cable trailing to my table the whole time :)
Should the table talk? It only seems logical for the table to say 'table taking form of poker' when you press the button....
Will it actually work?
We've started actually building now, so next post I'll add some pictures of the components and the wood work itself. I'm also planning to blow a load of money on some nice new power tools, so I'll probably post pictures of them too.
I have a 3d printer! To be more precise, it's a UP! Plus 3D printer, and I got it a few weeks ago but didn't get around to writing about it. Some specs are here on the cool components web site (where I bought it), although the developers are at http://pp3dp.com/. Here's a nice picture I found:
UP Plus 3D Printer
It was incredibly easy to setup. You basically assemble a few bits with a screw driver (provided), install software and plug it in! First time you use it you'll need to calibrate which would be a fairly simple process, except the software has changed since the manual was written which added a small amount of confusion. Either way, it took less than an hour to set up so I was happy.
Now, the software has it's up sides and down sides. A few good points:
It takes the extremely simple stl file format, which comes as standard in most 3d packages, and is easy to write code for as well.
Very robust - I've thrown millions of polygons at it and it's never crashed
Relatively fast. Obviously give it a few million polygons and it runs at 1fps, but its generally good enough just for positioning your model
It automatically adds scaffolding to support your model as it's built, allowing you to create models with overhangs
So in general it does what it says on the tin. However it's not exactly a miracle of UI design. Getting the settings right for a given model does involve a little bit of guesswork, but after a few attempts you can generally get it right first time.
Here's a few models I printed in my experiments to see what it could do:
Statue Of David
Some bits and bobs I printed
These images show a few fun tests:
A wine glass with an incredibly thin stork, to see how skinny it could print
The oval has text on (which says Chris)
The plastic square on the motor is modelled to perfectly fit to the axle. I did this on the basis that if I can print a perfect fitting square, I can print a perfect fitting wheel :)
A statue of david (top) because my friend broke his
The best of all though is a chain! This nifty model was printed as a single object. Mark modelled it in 3D Studio with the small gaps needed between each chain link. The software automatically places scaffolding to allow it to print, you wait a few hours and... well best shown with a video:
Going forwards I'm going to create MmBot 2's chassis and framework using the printer. First though, I needed to find a decent bit of CAD software, and found it very frustrating. Too many massively complex uber tools or simpler but ropey applications. So I decided to create my own small CSG tool designed for creating pieces of robot, which I'll probably write about next time.
It currently contains most of my Arduino code so far, and the beginnings of the design for the new chassis using OpenSCad. Feel free to grab what you need, along with the general statement of 'I take no responsibility for what you do with the code even if it blows up a computer or nuclear power station or whatever' :).
I've been slacking a little bit on keeping this blog up to date, but still progressing robot wise. My latest report is that I finally wired up the final components to MmBot - the Sabre Tooth 2x5 motor controller, and 2 quadrature encoders I have been building. Together they allow me to control MmBot's speed and direction quite accurately, and as result I will be able to reliably issue commands such as 'turn 90 degrees' or 'go forwards 3.5 metres'. First up though, here's MmBot in all her completeness:
And another one from above:
All parts are now attached and wired up. That's:
2 Infra red range finders (front left/right)
2 LinkSprite JPEG colour cameras (the eyes)
1 Infra red motion detector (the nose)
1 Ultra sound range finder (on top of head)
2 quadrature encoders, each containing 2 infra red reflectivity sensors (next to the wheels)
1 Blue smirf blue tooth modem
1 Sabre tooth 2x5 motor controller
2 18V DC geared motors
1 Arduino Mega
The only bits missing are the interactive bits, which I may or may not add to this version of MmBot. These would be:
1 speak jet voice/sound synthesizer (tested but not connected to MmBot)
1 voice recognition chip
2 led matrices
Speaker
Any extra leds I want to stick on!
While it'd be nice to get the interactive bits on as well, the circuitry is becoming a bit messy and I don't need it to achieve my initial goal of wandering around the office, identifying people or points of interest and looking cute. Plus my Raspberry Pi finally has a delivery date (3 weeks), and the cooler interactive stuff will be much easier and more powerful once it's hooked up.
The main thing I got working today though was the quadrature encoder and motor controller. This first fairly boring video shows the motor controller in action, gradually swinging the motors between full reverse and full forwards:
Next, things get a little more interesting. I start by asking both motors to go at 75% power and plonk MmBot down on the floor. Now you might hope that she would go in a straight line - after all I'msending the same power to each motor. Unfortunately even in the best of scenarios motors aren't perfectly matched, and if you then introduce things like friction or wobbly wheels resulting from my supreme workmanship MmBot drives around in circles.
Not to worry though - that's why I built my encoders in the first place. These devices allow me to measure the actual speed the wheels are turning at. For MmBot to travel in a straight line both wheels need to turn at the same rate, so all I need to do is write some code which:
Supplies a fixed amount of power to left wheel (say 75%)
Initially supply the same amount of power to the right wheel
If the right wheel is going slower than the left wheel, supply it with more power
If the right wheel is going faster than the left wheel, supply it with less power
Sounds very simple, and it is in fact fairly simple. The only problem with this feedback type code is that the results aren't instantaneous - supplying more power to a wheel will allow it to reach a higher speed, but it takes time to have an effect. You have to be careful that your code accounts for this, or you'll find yourself constantly over compensating, and will end up driving all over the place!
You've seen my earlier code to read from the quadrature encoders, and other code to trigger sabre tooth motor controllers. this tiny bit of new code in the loop achieves speed control:
//check where left encoder has got to relative to right encoder
if(left_encoder_pos < right_encoder_pos)
{
//left is behind right, so we need to increase right motor speed
motor_right_speed = min(255,motor_right_speed+1);
MotorSerial.write(20);
MotorSerial.write(motor_right_speed);
//and reset encoder positions (to avoid constant over compensation)
left_encoder_pos = 0;
right_encoder_pos = 0;
}
else if(left_encoder_pos > right_encoder_pos)
{
//left is ahead of right, so need to decrease right motor speed
motor_right_speed = max(128,motor_right_speed-1);
MotorSerial.write(20);
MotorSerial.write(motor_right_speed);
//and reset encoder positions (to avoid constant over compensation)
left_encoder_pos = 0;
right_encoder_pos = 0;
}
I wire up some switches on MmBot to turn on/off motors, and enable/disable speed control. This video shows the difference. Disclaimer: this is one of the worst filmed videos in the world, and my video editing abilities range from 0 to 0. I clearly need a tripod, a good camera and a lot of practice with AfterFx. Check out the start bit and the end bit, and pretend you never saw the middle bit.
And that's that! MmBot V1 is pretty much hardware complete. I'm in 2 minds now - I could go on and try to get some better code in there - take some steps towards autonomy. On the other hand, I now have a raspberry Pi in the post. Needs a bit of thought :)
I've got quadrature encoders working to measure speed and they're now attached to MmBot (although not wired to the arduino yet). Next I need to replace my very basic home made motor controller with the nice Sabre Tooth one I have. In a much earlier post I had a first blast at this but it turned out I had the Sabre Tooth RC. Fortunately, my standard Sabre Tooth 2x5 has now arrived which supports serial communication, and here it is:
Sabre Tooth 2x5 Motor Controller
It's a neat little piece of kit. On the left you can see the motor power connection, with the motor connectors top and bottom. On the right is GND and Vcc coming from Arduino, plus a white signal cable. This controller can run in lots of different modes which are configured with the switches at the bottom.
After a bit of experimentation I find the right setup for a simple serial connection at 9600 baud rate. In this mode you send the motor a value from 1 to 127 to control motor A (1=full reverse, 64=stop,127=full forwards), and 128 to 255 to control motor B in a similar fashion. In addition, sending a 0 instantly stops both motors.
To get things going, here's the first basic circuit I build:
Circuit diagram of Arduino connected to Sabre Tooth
It's a pretty simple setup. On the left I have the Sabre Tooth connected to a 12V battery, a 12V DC motor , and (instead of another motor) the oscilliscope. On the right you can see the Arduino wired up, with GPIO3 going to the signal 1 connector. Signal 2 is not needed for serial communication. With this setup I can control the actual motor by sending values from 1 to 127, and I can monitor the signals that get sent to a motor by sending values from 128 to 255.
So that's the circuit, here it is built:
Arduino connected to Sabre Tooth, controlling a 12V DC motor.
All pretty simple so far, and now for some equally simple code:
#include <SoftwareSerial.h>
SoftwareSerial MotorSerial(3,2);
void setup()
{
MotorSerial.begin(9600);
Serial.begin(9600);
Serial.println("Hello");
}
void loop()
{
//gradually take motors from full stop to full reverse
for(int i = 64; i >= 1; i--)
{
MotorSerial.write(i);
MotorSerial.write(i+128);
delay(100);
}
//take motors from full reverse, back to stop and then to full forwards
for(int i = 1; i <= 127; i++)
{
MotorSerial.write(i);
MotorSerial.write(i+128);
delay(100);
}
//take motors back down to full stop
for(int i = 127; i >= 64; i--)
{
MotorSerial.write(i);
MotorSerial.write(i+128);
delay(100);
}
}
In the setup function I simply initialize a software serial connection, which is transmitting via GPIO 3 at 9600 baud. The main loop simply sends different values to the motors to slowly get to full speed in one direction, then gradually go to full speed in the other direction, and eventually come back to a stop.
And finally, a video of it in action:
All good. Massive thumbs up to Dimension Engineering - this piece of kit isn't just really powerful - it's really easy to use. Not the cheapest of controlellers, but I'd highly recommend it if you're willing to spend a few pounds.
Over the past few days I've been experimenting with ways to improve my new home made quadrature encoder. By the end of my previous post I was successfully taking readings and converting them into rpm, which were then printed out to the serial monitor and finally graphed in excel. However my initial results were fairly noisy as you can see from this graph:
Initial spikey encoder results - rpm plotted against time
In theory that's the rpm of the wheel over time, however the wheel is turning at a roughly constant velocity so in a perfect world the graph would be a nice straight line. Now that's not necessarily going to be achievable (especially with my fairly rugged home made solution), however it's worth having a go at improving my results. Be forewarned: this post is gonna be very graphy and probably close to none-understandable, especially if you didn't catch the last one - read on if you dare!
The first thing I do is sit down and think of what might be causing these inaccuracies. I'm not convinced it's a sensor issue, and the Arduino is easily fast enough to keep up. After some pondering, the first few potential culprits I come up with are:
If the sensors are not out of phase by exactly 1/4 of a wave length then I'd expect readings to alternate between slightly too fast, then slightly too slow every interrupt. They should however average out, making every 2nd
interrupt (half a wave) or 4th interrupt (a whole wave) right on the money. Note: not worked out which one yet, but the key is, it'd be very very regular :).
The wheel is a little wobbly, which means it's getting closer to and farther from the sensors each revolution. If the sensors are perfectly aligned this wouldn't be a problem, however if they were at a slight angle it could turn the square wave into a slightly sin-waveish thing. I'd expect to see fairly regular inaccuracies in a pattern that repeats once per revolution (every 32 interrupts) if this was the issue.
It could be the time difference between when my interrupt triggers to tick the encoder counter, and when I actually do my RPM calculations (every x ms) in the main loop. If this were the issue I'd expect to see fairly random inaccuracies within a threshold of roughly 5ms (the time it takes for a single encoder tick at 360rpm)
To track down the culprit, I print out rpm calculations every 50ms, then every 150ms, then every 250ms, then every 1000ms and plot them against encoder position:
Encoder results at different intervals - still very spikey
Longer gaps will inevitably give flatter lines as they allow more interrupts to pass in between readings, so the data will naturally be smoothed. However the difference in patterns between the graphs should still indicate which of the above problems (if any) are the issue. As you can see, regardless of interval time, we get extremely spikey results with no discernable pattern. Before getting clever it occurs to me that I'm measuring times in milliseconds, but a single encoder tick can take around 5ms - this gives me at best a 20% error margin. I make a very simple change so the code does calculations at a microsecond level of accuracy instead, and look what happens:
Encoder results using microsecond accuracy instead of millsecond
Wow! What a difference. I'm still getting spikey results, but there's clearly a median line for each interval time, and the errors that bounce either side of it are extremely regular.
Now I'm convinced I'm not losing data due to accuracy (as even the errors are predictable!) I switch to running at constant 150ms intervals, printing out the rpm using 4 different calculations:
No error compensation
Compensation for time difference between interrupt and calculation time
Compensation for out of phase sensors
Combined both settings above
I make 2 changes to achieve this. First, my interrupt function now records the time at which it took it's last reading:
void updateEncoder()
{
encoder_time = micros(); //record time in microseconds of reading
byte new_encoder = readEncoder();
int dir = QEM[current_encoder*4+new_encoder];
encoder_pos += dir;
current_encoder = new_encoder;
}
Then I update the loop function as follows:
//record current time, the current encoder position, and the time the last encoder reading occured
long new_time = micros();
long new_encoder_pos = encoder_pos;
long new_encoder_time = encoder_time;
//calculate rpm with no compensation
current_rpm_nocomp = calcrpm(last_time, new_time, last_encoder_pos, new_encoder_pos);
//calculate rpm using time compensation (i.e. we use the last encoder time rather than current time in calculations)
current_rpm_timecomp = calcrpm(last_encoder_time, new_encoder_time, last_encoder_pos, new_encoder_pos);
//calculate rpm, only updating if it's an even numbered reading
current_rpm_evencomp = (new_encoder_pos & 1) ? current_rpm_evencomp : calcrpm(last_time, new_time, last_encoder_pos, new_encoder_pos);
//calculate rpm if even numbered reading, using time compensation
current_rpm_allcomp = (new_encoder_pos & 1) ? current_rpm_allcomp : calcrpm(last_encoder_time, new_encoder_time, last_encoder_pos, new_encoder_pos);
//record last readings to use in next calculations
last_time = new_time;
last_encoder_pos = new_encoder_pos;
last_encoder_time = new_encoder_time;
Printing out the results and plotting against time, I now get the following graph:
Different approaches to error compensation for the encoder
The purple line contains the fully compensated data and look how flat it is! I decide that given this is a prototype (in MmBot V2 I'll use motors with built in encoders) this is good enough for now. So, as one final step, I attach the encoder to the inside of MmBot as you can see here:
Black/white wheel disk on inside of wheel, and 2 IR reflectivity sensors facing it to make a quadrature encoder
And with it attached, I plot encoder position against time, with the motor turning on/off at regular intervals:
Encoder position plotted against time (in microseconds) with motor turning on/off at regular intervals
Pretty slick!
That's it for now - next up I'll properly attach both encoders, connect them to the Arduino Mega inside MmBot, wire up my Sabre Tooth motor controller and get some real navigation going!
In the last post I began adding some more sensors, the last of which was a rotary encoder. This device is used to measure the speed of a spinning wheel, using a black+white striped disk and an infra red reflectivity sensor:
Wheel with disk
Sensor pointing at disk
Connected to oscilloscope
On the left you can see the coloured disk, centre is a cross section to show the sensor and wheel, and right shows the actual square wave signal coming out as I spin the wheel. For more info on the basic setup, see my last post (More Sensors).
Now it's time for something more interesting. My next goal is to be able to identify whether the motor is going forwards or backwards. With just 1 sensor this isn't possible, as I just get a binary value back. However it occurred to me that using 2 out-of-phase waves I'll be able to work out which way the wheel is turning. My original idea was to have a disk with 2 waves on, and have 2 sensors - one for each wave. You can see this disk on the right hand side below:
3 potential wheel disks - single wave low res (left), single wave high res (centre), dual wave (right)
However after talking to Alex about it, he pointed out that you can achieve the same result with a single wave disk, provided you have 2 IR sensors that are positioned out of phase with the square wave printed on the disk. I put together 2 extra disks as you can see above - a hi res one (middle), and in case the sensor can't handle it, a lower resolution one (left).
This diagram shows how I'll mount the sensors to be 'out of phase' with the wave represented by the black/white stripes on the disk:
2 sensors for quadrature encoder mounted out of phase with the pattern on the disk
You can see when the disk is lined up so the left sensor is in the centre of the black section, the right sensor is exactly on the boundary between the black section and the next white section. So how does this help? Well, hopefully this wave shows things a bit more clearly:
Wave's generated from each sensor by the turning wheel
Here you can see the waves generated by both sensors as the wheel rotates. Note how they are the same wave, just out of phase by 1/4 of a wave length. The red line shows where the wheel is right now, and you can see from the reading that Wave 1 is low (over black), and Wave 2 is on the boundary between black and white - just like in the earlier cross section. Now, if the wheel is turning, one of 2 things can happen:
The wheel can turn left (red line moves towards green dashed line). When the red line hits the green line, a change in wave 1 is detected (goes from low to high), and wave 2 is high
The wheel can turn right (red line moves towards blue dashed line). When the red line hits the blue line, a change in wave 1 is detected (goes from low to high), and wave 2 is low
So, we were in the centre of a low point in wave 1. We waited until a change was detected in wave 1, and then the reading from wave 2 tells us which direction we went in! This principle can be applied at any point in time - we wait for a change in a wave, and then the reading from the other wave gives you direction. On top of that, the frequency of either wave tells you speed. So... speed and direction! Hurray.
Next step, build something. To start, I solder cables to 4 sensors (2 for each wheel):
2 sensors mounted on wood (right), my awesome soldering (left)
The sensors on the right are mounted on some wood ready for attaching. The ones on the left are just there so you can admire my awesome soldering :)
Sensors connected to oscilloscope
Close up of sensors pointing at disk
I now build a simple construction (mostly out of blu-tac, cardboard and tape) to mount a motor on the side of my desk, with the 2 sensors held directly in front of it. The motor has virtually no resistance and a full 12V going through, so is running at around 360rpm. This video shows the system at work, with one of the infra red sensors connected to an oscilloscope:
That's step 1 done - I can take readings from 2 sensors in front of a wheel. Next up, I connect it to an Arduino - initially just into analogue inputs 1 and 2:
Sensors connected to analog inputs on Arduino
This simple program is the first test I do (I left out setup as it doesn't do anything except init the serial connection):
int left_buff[256];
int buff_pos = 0;
void loop()
{
left_buff[buff_pos] = analogRead(1);
buff_pos++;
if(buff_pos >= 256)
{
for(int i = 0; i < buff_pos; i++)
Serial.println(left_buff[i]);
buff_pos = 0;
}
delay(5);
}
This is basically reading values from the left sensor every 5ms (significantly less than 1/4 of a wave length at 360rpm) into a buffer. When the buffer fills, it gets dumped to the serial monitor and the process begins again. The resulting output isn't very pretty:
But what happens if we plonk it into excel and draw a graph?
Output from Arduino when reading a sensor as the wheel slows down
This shows the output as the motor slows down - we're still getting that wave. It's not square as we aren't sampling often enough, so I decide to try another approach:
Now I'm reading the wave constantly, with no delays whatsoever. I couldn't output to serial fast enough to display these readings, however what I can do is output digital results to the digital pins, and monitor the output with an oscilloscope:
Square wave coming out of digital pin, generated by Arduino reading sensors
As you can see, I now get a perfectly regular square wave. If you can read the settings you'll see the wave alternates about once every 10ms. With 16 coloured strips on the card this means it takes 160ms for the wheel to turn once, thus the wheel turns 6.25 times per second. Multiply by 60 and you're at 375 rpm - just 15 off what I expected!
The Arduino is reading sensor data now, and I've proved it works with the short program above. However this code (and anything that used it) needs to be updating constantly so it can detect changes in sensor output as soon as they occur. This won't fit into any useful program, so I need to switch the code to use interrupts. This is an event triggered by the hardware that interupts whatever is happening and runs some code.
Unfortunately interupts on the Arduino are only available for digital IO, so the first problem is how to convert the analog output from the sensors into a digital input for the Arduino. After worrying about this for a while, I just plug the sensors directly into the digital IO. Fortunately enough the range coming from the analog sensor is wide enough to cross the digital IO reference voltage so I get a nice solid square wave coming out straight away. Now some modifications to code to use interupts instead of constant polling.
As you can see here, there's no actual need for an loop function at all. I attach the interrupt 0 (which is on pin 2) to leftChange, and interrupt 1 (which is on pin 3) to rightChange. When an interupt is triggered I read the corresponding pin and output it's value just as with the earlier example. Once again, the oscilllosope shows me a square wave - things are working!
Now to get some useful data out (with the help of this tutorial by OddBot). The main thing he points out is how to convert the encoder signal into a direction. I end up with this code which is based off his examples:
byte current_encoder = 0;
int QEM [16] = {0,-1,1,2,1,0,2,-1,-1,2,0,1,2,1,-1,0};
long encoder_pos = 0;
byte readEncoder()
{
byte left = digitalRead(PIN_IRLEFT);
byte right = digitalRead(PIN_IRRIGHT);
digitalWrite(PIN_OUTLEFT,left);
digitalWrite(PIN_OUTRIGHT,right);
return left | (right << 1);
}
void updateEncoder()
{
byte new_encoder = readEncoder();
int dir = QEM[current_encoder*4+new_encoder];
encoder_pos += dir;
current_encoder = new_encoder;
}
I'm doing 3 things here:
Reading a number from 0 to 3 to indicate the 2 bit encoder value (1 bit per sensor)
Using the QEM (quadrature encoder matrix) lookup table to convert a previous+current encoder value into a direction
Incrementing or decrementing the current encoder position based on direction
In other words, each time the interupt is triggered, I either increment or decrement the current encoder position.
Next up, I add my loop function as follows:
void loop()
{
long new_time = millis();
long new_encoder_pos = encoder_pos;
long pos_delta = new_encoder_pos - last_encoder_pos;
long time_delta = new_time-last_time;
last_time = new_time;
last_encoder_pos = new_encoder_pos;
long rpm = ((pos_delta * 60000) / time_delta)/32;
Serial.println(rpm);
delay(100);
}
This is basically taking a change in encoder position and the time passed to work out the speed the wheel is turning. Just pos_delta / time_delta would give me stripes-per-millisecond which isn't too useful, but multiply it by 60000 (milliseconds -> minutes), and divide by 32 (number of interrupts per revolution) and you get rpm! And surprise surprise, if I print out the rpm and graph it in excel:
RPM graph coming from Arduino
It's spikier than I'd like (although that's probably due to badly positioned sensors and a wobbly blu-tac attached wheel), but it's clearly giving a solid reading of just around 400rpm - probably what I'd expect from this motor given all that's currently attached is a small wooden disk!
Well, that's it for now. I have a quadrature encoder built. Next up - fit the disk and sensors to MmBot. Once that's done I can take the rpm of each wheel and use it to work out the the actual velocity of the robot at the point the wheel is touching the ground, eventually giving me the ability to reliably say 'go forwards 5m', or 'turn 90 degrees'.
I've been pondering how to push forwards with the AI side of this project, and come to the conclusion that I really need a good set of sensors in order to get much success. If I had my Raspberry Pi and a couple of web cams it might be possible to do everything using just vision (although I doubt it), but realistically the more sensors the better. So, with that in mind I set about adding more sensors to MmBot - hurray!
You already heard about the PING ultrasound sensor in earlier posts, but as a recap it's a little device that sends an ultra sound pulse and waits for it to come back (aka sonar) which is one way of measuring distance. Another approach is to use infra red range finders. These devices shine an infra red LED in a fairly tight beam, and measure the strength of the beam that comes back. They each have their own faults and benefits, so I'm gonna use both.
First up, here's my newly soldered and ready to mount set of sensors:
New sensors ready to attach
The ultra sound and motion sensors will be mounted on the head (partly as it's a good place - high up and moveable, but mostly because they're cute and the motion sensor makes a good nose). I'll attach the infra red sensors on the front of the robot on each side so they can detect oncoming obstacles.
After a bit of fiddling and a load of tape, I get the sensors temporarilly attached to MmBot in a layout I'm happy with:
New sensors taped to MmBot
I'm not convinced the ultra sound looks great on top of the head - something I'll need to fix in the final design. However the IR sensors on the sides look fine, and as I said earlier - the motion sensor still makes a good nose. It even glows red when it detects motion!
Once things are wired up I'm not in the mood to start programming much (partly cos I have a cold), so I get my new oscilloscope and attach it to read the output from one of the IR range finders:
Wave being generated by moving my hand in front of the IR range finder
This video shows the whole thing in motion:
After testing all the sensors it's time to properly attach them. For the IR sensors I simply drill a hole and bolt them to the chassis. Similarly, the motion sensor goes on the front of the head so a bit of wood and a couple of bolts later it's easily attached. However the ultra sound sensor needs to sit on a frame as you can see here:
Ultrasound sensor in a frame on top of the head
There's nothing to bolt it to, and I'm not convinced I can simply glue it to the top of the head. There'll be too much torque when the head turns and the frame will quickly break off. To solve this, I cut 2 grooves into the head frame that the sensor frame can slot into:
Ultra sound sensor frame slotting into the head
Now I can glue it, and with a bit of the old saw dust + super glue = instant hardening mega solid resin trick the bond will be stronger than the wood!
That's the first set of sensors going, but while doing so it occured to me that a key part of knowing where you are is the ability to take a guess at your current location based on your movement since your last known location. For example, if I know there's a bar 10 miles away at 45 degrees north, then I turn to 45 degrees north and walk for 10 miles, I can place a good bet that I'm somewhere near the bar.
The predictive approach detailed above gets less and less accurate over time, however when combined with other senses it is extremely useful and can massively reduce computation time. You can see why by considering a scenario we all know too well - being lost on a long car journey. When this happens I generally try and find a street sign, then try to find that sign on the map. However, as I know roughly where I am, I only have to scan a small section of map (hopefully) to find the street. Without this predictive ability I'd need to scan the entire UK road atlas every time I needed to find a street.
All of MmBot's motion is driven from 2 wheels. If I can measure the speed each wheel is turning, I can derive the speed each side of the robot is moving at, and thus it's path across the floor. So how to measure wheel speed? Well it turns out this is a solved problem and uses a device called an encoder. This device uses infra red reflectivity sensors at it's heart. They're just like the range finders mentioned earlier, but much less accurate and only operate within around 1.5cm. This video shows one attached to an oscilloscope (sorry for rotatedness):
Despite the odd angle, you can hopefully see the signal changing as I move my finger over the sensor.
To make an encoder you take advantage of the fact that black reflects less IR than white. If you attach a disk like this to the face of a wheel or cog:
Disk attached to inside of wheel
Then attach an IR reflectivity sensor opposite it, as you can see in this cross section of MmBot:
Cross section of MmBot, showing IR encoder and wheel disk
As the wheel turns, the amount of IR reflected back will alternate between low (black strip) and high (white strip). By measuring the speed at which the signal alternates, I can derive the speed at which the wheel is rotating and thus (assuming there's no slip), the speed at which the robot is moving! The alternating signal generates a square wave as this diagram illustrates:
Wave (top) being generated from alternating colour (bottom) as the wheel slows down
And here it is in real life!
Oscilloscope showing signal from rotating wheel
As the wheel turns it generates a signal which you can see displayed on my oscilloscope. This video show it in action, with the wheel turning alternately on/off:
That's where I got to today. Next up I'm going to refine my wheel encoder design and try to get it bidirectional (i.e. it can tell the difference between forwards/backwards).