Prototyping a Robotic Vacuum Cleaner.

(C) SnowCron.com

In this tutorial we are going to create a platform that can be extended to become a full scale robotic vacuum cleaner. This particular platform as is will not clean anything: it will move, use bump sensors to locate the walls and turn around to avoid them. However, approaches we use are going to help you in case you need to create an industrial prototype of the real thing.

Don't take me wrong: one can build a device with similar functionality using cardboard and thermal glue. Why do we need any advanced prototyping, 3d design software and so on? Because the cardboard robot is a dead end. Here are the reasons:

  1. It is not scalable. Now, even with vacuum cleaner, you might want to make it bigger to fit brushes, bottles with cleaning solution and a mop. What if you are prototyping a car? Can you use cardboard? "Cut it with kitchen knife, then bend it to fit and use tape to hold it in place..." Not impressive. Which means...
  2. It doesn't look that great in your resume.
  3. Also, in this tutorial I am going to build a compact device, fitting parts tight: this is a valuable skill, one you will never master without doing careful design.
  4. Finally, it does a poor job. If you look up "DIY VC" in Youtube, you'll see that they all are not really cleaning anything. Yes, they can pick up small papers or dust from the even floor, but... This is not cleaning. To clean carpet and wood, to be able to pick up dust, dog's hair, occasuonal bolt (one you lost when building a vacuum cleaner) and small rocks or large grain of salt (they use those on the streets in winter), you need a carefully designed robot.

Stating the Objective.

We are going to build a prototype of a mechanical part of robotic vacuum cleaner. Just the part, responcible for navigation: no brushes or turbinas. As the result, our robot is going to be small, about 2 times smaller (which means eight times lighter) than a "real" VC. In addition to the educational reasons, the size (18 cm diameter) is chosen because this is a "comfortable" size for a standard 3d printer.

3d printer is not a mandatory tool: you can cut all parts from plastic or wood. However, this is going to be much longer, not to mention, error-prone. Besides, 3d printed robot looks nicer. A standard size of print bed 3d printers use is 22x22 cm, so 18 cm robot will fit it nicely.

We are going to use cheap electronics: toy (and very imprecise) motors, Arduino Uno and Ni-Mh accumulators, instead of Lithium ones.

Perhaps, the choice of accumulators deserves a separate discussion. Lithium accumulators are state of the art now, their capacity is at least 3 times higher than Ni-Mh.

However, they combust.

As this is a tutorial, I don't think it is a good idea teaching you to build something that can destroy your house. Of course, there are ways of dealing with fire hazard, for example, you can keep accumulators in a fire proof case. But again, this is a tutorial, and it is about design, not about safety. Also, most plastics used in 3d printing burn really well; not quite the napalm, but close enough. And you do not want a napalm bomb driving around your appartments and finally dying under your bed. So - Ni-Mh.

In Simulator of Robotic Vacuum Cleaner you can find a simulator allowing you to build rather complex algorythms of VC navigation. However, here we will use a simplest one: bump - pull back - turn - go. I am going to cover the advanced navigation in a separate tutorial.

After reading this tutorial you will learn:

  1. How to design simple mechanical moving devices.
  2. How to invent devices (we are going to invent a bumper for our robot).
  3. How to program Arduino controller for a simple task (3 sensors, two motors).
  4. How to put together electrical part of a robot, including accumulators (we make sure they can be charged in place), power supply for motors and sensors.

What we design:

What we get:

Design

There are countless 3d designers. For this task we are going to use a simple one: Tinkercad.

This little jewel is very easy to use, does not require installation (works from your browser) and is powerful enough for any beginner level task.

Most robotic vacuum cleaners are disks. The reason is, disc can rotate in place. Anywhere, even under the stool. Angles of rectangular robot, for example, can collide with stool's legs, and rotation will be blocked. Therefore, let's design a disk-shaped robot, 180x180 mm:

Note: we do not care for the height of our robot; it isn't going to perform the actual cleaning, so it doesn't have to fit under the bed. When designing a real VC, height is very important, and the flatter means the better.

Wheels vs Tracks

The next question we have to ask ourselves is the "propulsion system". We know that the robot should rely on its circular shape by rotating around its center: this guarantees that it will not get stuck. So wheels should be positioned symmetric, regarding the center of a device. Still, we have few choices:

First:

Second:

Or even third:

Here the first solution will require an additional small wheel either in front, or in the back, to keep disk balanced (horizontal). The second and third solutions do not require such support. However, they will not work for "real" vacuum cleaner, here is why:

Here, a purple cone symbolizes a VC's side brush: as you can see, wheels will block it. Same logic, just in a less degree (why?) applies to tracks, while the first option (pair of wheels) works with side brushes just fine. The reason is, if we use two wheels, they touch the ground in a single point, far from the side brush.

As an additional consideration against tracks and in favor of wheels, note that even a vell crafted track consumes more energy. And as an additional agrument in favor of two wheels instead of four: we will only need two motors. While if we use four wheels, it is either four motors or some kind of belt or gears, so that one motor can spin two wheels. And no, we can not use "front wheel drive / rear wheel drive" approach here, remember: our robot need to be able to spin around its center, which means, wheels should work together, in synchronized way.

Bumper

Not all robotic vacuum cleaners use bumpers. Some of them rely completely on ultrasound or infra red proximity detectors. Well... Ultrasound can be absorbed by a curtains or a plush toy, infra red can be absorbed by a black object or reflected by the mirror. In both cases the robot will crush into the wall. There are few ways of dealing with it. When robot tries to run through the wall, electric current to motors increases. We can detect it, and conclude there is an obstacle. Unfortunately, when moving on a thick carpet, robot also encounters more resistance, the logic above can decide there is a wall. In the same time, when hitting the wall while being on a polished wooden floor, robot's wheels can keep spinning, slipping, so the system detects no wall.

Another way of dealing with the problem is by using internal inertial sensors: when robot stops, we can detect it, no mater what surface it is on. Yet, it is not nice when the robot hits the wall on the full speed.

A reasonable compromice (one we are not going to use in our "toy" project) is to use ultrasound or infra red sensors to detect an obstacle - if we can - and to reduce speed. It works four times out of five, and robot slows down, before actually touching the wall, which looks much nicer than bumping on it.

Anyway, we are going to use bumper, even though on a tiny model like ours using remote or internal (as oppose to touch) sensors is way easier.


What is a bumper and how does it work? Well, first of all, bumper is nothing but three buttons: front one, responsible for frontal collision, left and right ones, for side collisions:

Note that bumper and a back half of a robot form a circle, so that - yes, it can spin in place, surrounded by obstacles. Now, all we need is a design that allows to have a plastic cemicircle, so that it can be pressed from front, left and right. Oh, one more thing: when pressed near top or bottom part, it should not tilt - it should go BACK:

Correct:

Wrong:

Indeed, let's say we have installed out touch sensor (a button) right behind the bumper, at 1/2 of its height. Then, if the lower part of a bumper hits an obstacle, the "wrong" bumper on the picture above will tilt, rotating around its middle part, and the button will not be pressed! In other words, we need the bumper to move forward and back, while remaining vertical (perpendicular to the floor).

There are few solutions to this problem, the most popular is the following:

As you can see, the bumper is connected to the vertical axes (3 of them) via joint that can only beng in a horizontal plane, while being rigid in vertical. This design will need additional stoppers to prevent bumper from going too far forward or sideways, but it is nearly optimal and used quite often... Let's not use it.

Let's invent an alternative design instead.

What we know: we can use rigid vertical axe, so that our bumper can rotate around it. However, if we attach bumper to the axe without joints, then the closer to the axe, the smaller will be the leverage, so if our VC bumps to the obstacle at the point of a bumper that is close to the axe, it might not be able to generate enough force to push the button:

Now, wouldn't it be nice if we could use the far (from the axe) part of the bumper above, while another (symmetric) bumper handles the "weak" part of our design? Unfortunately, for this to work, those to halves of our bumper should be able to move through each other:

This is a very impotrant rule you should remember: something symmetric in 2d might be asymmetric in 3d! A lot of brilliant engineering solutions is based on this little euristic. Now, let's make two halves of our bumper to move through each other:

Now we have a rigid (vertically) bumper, with few (just two, no joints, unlike in previous design) moving parts, and capable of producing sufficient (to press a button) force, when pushed from any direction. We are going to use it for no particular reason: the first design we mentioned is equally good and let's admit it - looks nicer, too. So maybe we'll use it in future tutorials.

Base

As we have already decided, our robot is going to be shaped as a cylinder. In the middle of this cylinder a pair of wheels will be placed. Wheels should be (directly or indirectly, via some kind of gears) attached to motors. Also, there should be a bumper in front of that device:

As you can see, the base of our vacuum cleaner is not exactly circular: it is cut on the sides to make place for wheels. Also, it has small (about 20mm tall) cylinders that prevent the bumper from rotating too much in the wrong way:

We are going to use simple "toy" motors available from any large supplier (think Ali). They have a tiny motor and a reductor, providing the speed we need. Note that for this particular project I had to use motors that spin about three times faster than they should: I simply didn't have right ones. A proper solution was to order motors with lower RPM (rotations per minute), the second proper solution would be to use an extra reductor. I chose to have higher speed, which was a bad idea: on the video, you can see our robot moving too fast, it makes it turn in an unpredictable way when bumping in the walls. This is not a design flaw: with proper motors the problem will go away. And yes, those same motors are awailable in the same form factor but with different RPMs.

Few more words about motors. For our robot to move where we want it to, we need to make sure motors spin at the same speed. For it to turn at an angle we want it to turn, we should be able to make motors to turn at an exact angle. As this is a toy project, we are not going to address that problem, but at least we need to know how to deal with it in our future projects.

There are three ways of dealing with the task.

First, we can use stepper motors. For stepper motors, we can set an exact angle, and by exact I mean the precision that is WAY better than we need for a vacuum cleaner. However, good stepper motors are expensive.

Second, we can measure the angle the wheel actually turned, and to stop it, when it turned the angle we need. This is an approach used in some commercial robotic vacuum cleaners (just some, as some choose to do what we did: ignoring the problem alltogether). To measure the angle, a beam interruption sensor is used: a light diode emits the beam, a sensor diode registers it. A turbine is attached to the wheel, interrupting the beam as the wheel spins, so the sensor registers impulses, and we can count them:

Note that if you want to use this mechanical approach, you wouldn't have to make the device, as it can be purchased... or extracted from the old VC.

Third, we can create some kind of SLAM system, allowing our robot to "look around" in order to calculate its position. Some robotic vacuum cleaners use lidar, some use video cameras (the most common approach is to look up, measuring shift and rotation relative to the room's cealing). There are many implementations of SLAM, and it is definitely a must for any advanced robot.

In this particular project, we are going to ignore the issue, as it is about mechanical part of the robot, not about its navigation. We will settle for a simple case: robot moves forward until it bumps in the wall. Then it pulls back a little and turns left. Then it moves forward again.

For this simple algorithm, we assume that to "pull back" and to "turn left" all we have to do is to spin wheels back (to pull back) or back/forward (to spin) for some time. So we have replaced angle/distance with time: it is imprecise, but it will work.

To complete this topic, let's mention slippage: when floor is slippery, a robot can slip. Wheels rotate, nothing happens. No mater how precise your control over angle the wheels turn is, if it happens, your robot will move in a pretty much random direction. Well, unles it uses SLAM or inertial sensors.


Back to robot's base. To attach motors to the "base", we are going to use simple box-like cases:

The motor goes in tightly, so no glue, screws or anything like that is required:

As for motor holder boxes, we can glue them to the base. I recommend the "rubber" glue, as it can be easily undone using knife:

When we print wheels, we make sure they have a slot shaped to fit the motor's axe. We can connect it to the motor tightly, with or without glue:

Electronics

On-board computer

First of all, our robot requires a "brain", a controller capable of reading sensors and issuing corresponding commands. We are going to use Arduino Uno: a simple, cheap, yet powerful enough solution.

Sensors

We will need sensors, in our case, three "buttons", central, left and right: when the bumper moves, it should press those buttons. Buttons come in countless shapes, but there is a particular one for the case when it is not a human's finger that does the pressing, but a moving part of a machinery of some kind. These buttons are used in 3d printers, among other things:

Overall schematics

Let's walk through the schematics step by step.

Our robot is very simple, so i am going to draw schematics by hand. For more or less advanced devices, use CAD software available in the net.

First, we have power supply (black). It begins with 14.4V provided by accumulators (not shown), and is converted to 6V by LM-317. 6V is required by motors, also we feed it as input to MT-3608 to get 9V (required by Arduino).

There is a scary structure on the bottom-right, with 1, 2 and 3 in circles: just ignore it. This is how the power button is wired. All you need to know is that you can turn it on and off, that's all it can do.

As you can see, wires from MT-3608 are connected to barrel jack of Arduino Uno. There are more than one way of feeding power to Arduino, but barrel jack is the best: it has its own power stabilizer. Note that when you upload the software to Arduino, power jack should be free. USB port of your computer, in addition to feeding data to Arduino, will also provide it with power. You don't have to disconnect power from barrel jack, just keep in mind that our program will start immediately upon uploading: are you ready for the robot to run away?

The 6V from LM-317 are fed to L293D, which is the "driver" of our two motors. This driver contains two (one per motor) so-called H-bridges. An H-bridge is a set of electronic switches allowing us to run the current through the motor either one way or another. In other words, Arduino controls the H-bridge, and depending on the signals it sends, the motor spins forward or back.

We are done with black wires. The only thing you can notice is the black "-" wire going to resistors (shown in blue). This is a ground wire: we can, and in most cases, should connect all "-" together, calling it "ground".

Second, we have sensors (blue). A sensor in our case is simply a button; there are three of them. To understand how it works, it is enough to focus on one of them, as all three work independently.

On the image above, you can see three resistors, connected on one side to the ground (black wire, the "-"). On the other side of a resistor the wire splits in two: one goes to the Arduino's input (inputs 1, 2 and 3) and another one - to the button and then to the Arduino's "+5V".

The way it works: when button is not pressed, the Arduino's input is connected (through a resistor) to the ground ("-"). When we press the button, it becomes connected to "+5V". So the signal on the input suddenly changes, we (Arduino) can read it and act accordingly.

Third, the motors (red). The L293D driver is symmetric: it contains two identical and independent parts, one per motor. We need it as the output of a microcontroller (Arduino Uno) cannot provide the current a motor (and especially two motors) need: it will either get damaged, or will not work properly, if you try. The L293D can control motors with a voltage between 5V and 36V and a current of up to 600 mA.

We control the driver by setting proper voltage at its inputs: see the computer program discussed below.


Before we continue, there is one more thing to be discussed: the charger for our robot. In this tutorial, I am not going to provide any charger's schematics, as it is a topic for a separate article. However, our robot is "charger-ready". What does it mean?

The most annoying flaw of home-made robots is the fact that to recharge them we need to extract accumulators. This is not nice. So let's leave accumulators where they are. Now, accumulators are going to be connected consequently, it affects the output voltage and the parameters of a charger we need.

Let's say, we want to charge our Ni-Mh accumulator. What should we do? We just send power to it! I am not going into details of slow/normal/fast charging - let's say, we use a slow one. It means that we should send the small current to it, by supplying proper voltage from the charger. That's it. A slow charger sends enough - just enough - of current to charge the accumulator, but NOT enough to make it hot.

So all we need is to provide a pair of wires ("+" and "-") with the jack, so we can plug in the charger. But wait! We already have a power line, from accumulators to power convertor - why don't we split it and insert a connector in the middle? Now we can connect accumulators to the barrel jack of the power convertor, and our robot is ready to operate. Or we can disconnect it from power convertor and connect that same jack to the crharger. Now the robot is off, and accumulators are charging.

Of course this is not the nicest way of doing things. A better way would be to be able to plug in the charger, while the robot is turned off by the power switch (we can do it by connecting the charger's jack in parallel to the power convertor's power line). An even better way would be to make the robot detect charging and to shut down by its electronics (a relay of some sort).

And the best way would be to make it possible for a robot to work while being charged - after all, your cell phone can do it, right?

But this is just a beginners' tutorial, so I am going to use the easiest solution.

Power source

We will need power source. As our robot doesn't do any heavy work (it just moves around), we'll settle for 3 blocks, 4 NiMh accumulators each. NiMh accumulator produces 1.2V, so we'll have a total 4*3*1.2 = 14.4V when accumulators are fully charged. As accumulators discharge, the voltage will drop, which is probably bad.

There is one more problem with voltage: Arduino needs 9V, while motors need 6V. What should we do?

For this kind of problems, we use step-down power convertors. A power convertor takes input voltage in a wide range as an input and produces stable voltage (we can adjust its exact level) as the output. Note the "wide range" part here: as accumulators discharge, the input voltage drops. However, the output voltage will hold; our power converter will suck accumulators dry, and only then will it fail to deliver proper output voltage.

As we need two voltages (6V and 9V), we are going to use two converters. We can use them either parallel (each takes 14.4V as an input) or sequential.

For our power supply, we are going to use two sequential convertors. First, LM-317 (in: 2-24V, out: 3-28V). Second, MT-3608 (in: 2-40V, out: 5-38V). Note that MT-3608 is a step-up convertor, it can increase voltage, but can not decrease it. Therefore, we can not use it to get 9V from 14.4V, but we can use it to get 9V from 6V. As for LM-317, it will convert 14.4V to 6V. This is why convertors are connected sequentially.

Now, convertors are just chips with minimum periphery, so we need to put them somewhere. Let's place them on the same board, as that will look nicer and will be easier to maintain.

To build a board, I am going to use a tiny piece of copper-covered textolite board or copper Clad Board, single-sided (meaning that copper is on one side only). To solder our chips to the board, we need to remove copper, except for the places we want it to be. Here is how.

Board etching

I am going to describe one of the easiest technics, which is the most convenient for home use.

Draw the conductors, as they should appear on the board. Here is an example (just a random picture from the Internet):

The reason I am not using a real picture for our power convertor is because it is embarasingly simple, it doesn't even look like a PSB:

Convert the image to a mirror reflection, using any image editor. We are going to print that image, and then move it to the copper surface of a textolite board. In process, it will be mirror-reflected again.

Now, print the image using a laser printer.

Few words about paper you should use. A standard office paper is no good. A glossy paper they use in a high quality magazine is better. The best is a special paper designed for this purpose, it looks like a vax-covered cooking paper, and printer's toner can be easily removed from it. I had no problems finding it on Amazon using "PCB Circuit Board Thermal Transfer Paper", but I am sure there are cheaper solutions available, too.

Now, clean the textolite surface with alcohol, acetone or something like that, put your print on it, face down, and use hot iron to move the print from paper to copper surface of textolite board.

Now we have a copper surface with printer toner covering (protecting) copper we want to keep. The rest of copper will be removed (just another random picture from the Internet):

To do it, mix 100 ml 3% hydrogen peroxide (source: drug store), 30g citric acid (source: grocery store) and 5g of salt. This amount should be sufficient for 10x10 cm board. The process should take 10-15 minutes, no toxic gases expected, but if you manage to get the solution in your eyes, wash it off with lots of water.

When all excessive copper is disolved, wash the board in the water and remove the toner. You can do it with tooth brush, or with aceton.

Controlling motors

Unfortunately, we can not power our motors directly from Arduino: it just is not powerful enough. So we need a driver, an amplifier, controlled by Arduino, functioninng as some kind of an amplifier. For small (in terms of power consumption) projects, a common approach is to use L293D driver.

The L293D is a 16-pin Motor Driver, which can control a set of two DC motors simultaneously, allowing them to spin in any direction. It can provide up to 600 mA (per channel) at voltages from 4.5 V to 36 V.

On the following picture you can see pins of the chip.

1 - Enable 1-2, when this is HIGH the left part of the IC will work and when it is low the left part won’t work.
2 - INPUT 1, when this pin is HIGH the current will flow though output 1
3 - OUTPUT 1, this pin should be connected to one of the terminal of motor
4,5 - GND, ground pins
6 - OUTPUT 2, this pin should be connected to one of the terminal of motor
7 - INPUT 2, when this pin is HIGH the current will flow though output 2
8 - VCC2, this is the voltage which will be supplied to the motor.
16 - VCC1, this is the power source to the IC. So, this pin should be supplied with 5 V
15 - INPUT 4, when this pin is HIGH the current will flow though output 4
14 - OUTPUT 4, this pin should be connected to one of the terminal of motor
13,12 - GND, ground pins
11 - OUTPUT 3, this pin should be connected to one of the terminal of motor
10 - INPUT 3, when this pin is HIGH the current will flow though output 3
9 - Enable 3-4, when this is HIGH the right part of the IC will work and when it is low the right part won’t work.

The L293D has eight pins on each side (16 total). Per each motor, there are 2 INPUT pins, 2 OUTPUT pins and 1 ENABLE pin. In other wirds, L293D consist of two H-bridges. H-bridge is the simplest circuit for controlling a motor.

As you can see, an H-Bridge circuit contains four switches with the motor at the center forming an H-like arrangement. By switching switches in different combinations, we can send current in opposite directions through the motor, so the motor can spin in different directions.

Now, we are going to use Arduino to send proper signals to L293D's inputs, that works exactly as if there were switches there: the current flows in the different direction. I am going to provide Arduino code example below.

Note the 4 grounds in the IC (integrated circuit). It is done on purpose: in addition to using those pins as ground (minus), we can also use them as a heat sink, soldering them to a large metal wire perhaps. This is done because the chip can get really hot and we want to dispose of that heat.

Putting it all together

Here is the PCB for our power supply: frankly speaking, a simple one:

We simply solder LM-317 and MT-3608 to it, and use bolts on LM-317 to make it hold better (on the image below, bolts are not used yet, so LC-317 is quite loose):

The motor driver was soldered to textolite board, together with some connectors: alltogether, it is now a shield that can be snapped directly in the Arduino (we will discuss the schematics later).

The three blue connectors are for power in (one) and power out (2, one per motor). The black pins go through the board and to the Arduino.

On the next picture, the entire "neural system" of our robot is displayed. Note: you are not supposed to assemble it separately, i have only extracted it for the sake of a presentation.

As you can see, there is an Arduino in the center of a composition. it is using 3 buttons, left, right and center. Power from accumulators is fed to the barrel jack attached to a power switch, and it goes to LM-317. Then it is fed to Arduino via a second barrel jack (at the bottom-left, near the motor), that we insert to barrel jack of Arduino.

Finally, there is a motor driver shield sitting on top of Arduino - have I mentioned that you can purchase a prebuilt one, in case you don't want to DIY?

Squeezing it all in the available space

Now that we have all electronic components listed, we can try to squeeze them into the space we have. If you do it (as you should) in a 3D editor, just draw components and start moving them around until they fit. Soon enough you will discover, that there is more than one way of doing it. For example, should we put batteries at the front of in the back? Remember that a robot with two wheels is unstable, so we will have to add an extra (passive) wheel to support it. Obviously, it should be either at front or at the back. But if we place accumulators (which is the heaviest part of our robot) in front, then the robot's balance will shift, and we'll need more support at front. If we place accumulatots at the back, we'll need support at the back. So some of our choices depend on the other choices.

There is one (two actually) part that we can not move around: motors. We can place them horizontal or vertical, of course, but in any case the axe of a motor should be at the central line of a robot, as the wheel is attached to it. So first thing we place are motors and wheels:

Note that the shape of the base of a robot is shaped so that wheels are inside the 180 mm circle: if our VC will have to spin in place, wheels will not hit any obstacle.

Then we need to find place for sensors: left, center and right buttons.

The left and right buttons, we just have to make sure they are not touching the wheels, and that the bumper, when it hits an obstacle, moves far enough to press those buttons. However, we have a non-obvious trick here. Let's say we place bumper on the robot's base, will it hold steady? The answer is 'no'. It will not move forward too far, as we have those slall cylinders serving as stoppers, but it will still fave some freedom, like an unlocked door. By freedom, I mean backlash, about 5 mm of it. There is nothing wrong with this, except it doesn't look nice.

To solve the problem, we usually would do the same thind we do with doors, if we don't want them to stay half-open: we use a spring. In a robot that small, a strip of plastic, cut from an old credit card will do nicely. However, we are not going to use it.

The reason is simple: the robot is small, the force required to keep its bumper from swinging freely, is small too. And a button we use has just enough of it, so it can work as a spring, pushing the bumper forward (while the stopper prevents it from going too far):

As for the front button, here is another trick: when the robot runs into an obstacle that is in front of it, both left and right parts of a bumper move back. So we can "read" any of it, we don't have to make sure our button is attached to both sides of a bumper:

As our buttons can not hung in the air, we need to provide some support:

Next we find a place for battery holder, with 3 accumylators packs in it. Note that it is raised up on some kind of a leg: we had to do it, as we simply do not have enough space below: it is already taken by the motors. So the battery holder is used to lift accumulators, that's all. A common mistake beginners do is squeezing accumulators in each and evary free spot. It is not a good ides: yes, it will allow you to add one, maybe even two battery packs, but in the same time, taking your robot apart will turn into a nightmare. Remember, we want a prototype, something that can be, in theory, presented for mass production.

Now, let's put batteries where they belong:

As you can see, left and right button (sensors) holders can simply be glued to the sides of a battery holder, while mottors fit under it.


Next, we need to find place for electronics. Note, by the way, two important points here.

First, as electronic components are more fragile and more - let's use this term - "wired", we usually want to install them last. Just to be able to remove them easily, if something goes wrong.

Second, it may look simple, but I tried a lot of different configurations, before it all was arranged in a limited space. You should be ready for this, unless you use proper (and expensive) CAD software that can do most of the work for you.

Anyway, as you can see, the back side of a battery holder compartment is missing:

This is because that side is part of a plastic detail holding both our Arduino (white) and a power convertor (gray):

Here, left to right:

a) Green: a holder itself, that we are going to glue to become a back side of a battery compartment,

b) Magenta: an additional plastic part to be glued to (a). On the other side, a power convertor will be either glued or screwed. The reason we have it as a separate part is to make the overall task easier for 3D printing. It is often easier to print two simple parts and to glue them together, than to design and print a single complex one.

c) Arduino Uno

d) Power convertor

Just to remind you what power convertor looks like:

An overall device looks like this:


As we mentioned above, two wheels are not enough in terms of a balance. As batteries are shifted forward, the robot will tilt forward as well, so we need to add an extra wheel there:

This is a simple device, made of plastic, bolts and nuts. As you remember, there was a hole in the robot's base, now we know what it was for:

Adding the Hood

The last thing we need to do is the "hood", the outer body hiding all robot's internal machinery from customers as well as from agressive outside world. There are countless ways to attach the cover to the base, study candy boxes and you will get tons of ideas. I am going to use quick and dirty solution:

As you can see, the holder of a front touch sensor extends up, providing a simple click-lock that will hold the cover from moving both up and back.

There are sliding locks at the back of the robot, holding the cover in place as long as the front lock is closed:

The last remaining question is about a large opening at the side of a cover. The reason I have it is to make an easy access to Arduino's barrel jack (its power supply input), USB port (in the case we want to reprogram our robot) and to a power cord going from accumulators to power convertor (as you remember, to switch from charging to operational mode, we need to disconnect the charger and to plug the jack into the input of a power convertor).

So we have an additional "back door" that slides in place using the already familiar sliding locks:

Programming our robot

Arduino programming is done on a simplified version of C++. For installation and initial setup instructions, please refer to https://www.arduino.cc/ - an official home of Arduino, and a place where a lot of great tutorials and examples are available.

In this tutorial, I assume you already have the Arduino software installed on your computer, and you know how to compile it and upload it to Arduino Uno. Briefly (for Windows): start arduino.exe, in menu find ports and select a proper port for output. Use good (thick and short) USB cable, long ones tend to fail. Connect port to Arduino, click compile. That's it! Your program will compile and (if no errors found by compiler) uploaded to Arduino.

Note that you don't have to power up the robot to upload a program: Arduino gets sufficient power through USB cable, and you don't have to worry that your robot will try to run away while still being connected to a PC.

Our program is very simple, and it is composed of two files. The main file (let's call it "vc.ino" is a C++ file that has an "ino" extension: arduino.exe, the compiler provided at Arduino official web site, will recognize it as a "project" file, in other words, the central file of your project.

The second is "TrackMotor.h", a file that is included in the main file. The reason it is made separate is that it has functionality for controlling motors. Any motors, not necessarily motors of VC. If at some point we decide to create another program that uses motors, we will simply include this same file in it.

Let's begin with the "TrackMotor.h". The following is a C++ code, so I assume you know C++ basics.

// First, we wrap the entire include file in a #ifndef - #define wrapper. 
// The way it works: if your file does not include the "TrackMotor.h", then 
// TRACK_MOTOR constant is not defined. We go inside #ifndef, define the 
// constant and the rest of code as well. Now, if you include the file 
// twice, you will only go inside #ifndef once, avoiding the "duplicated
// code" error. Of course, we will not explicitly include file twice, 
// this is to protect us from the situation when we include "TrackMotor.h"
// and some other file, that, too, includes "TrackMotor.h"

#ifndef TRACK_MOTOR
#define TRACK_MOTOR

#include "Arduino.h"

// Constants, specifying if the motor should spin forward or back.
enum { MOTOR_FORWARD, MOTOR_BACK };

class TrackMotor
{
    public:
        int m_nEnableMotorPin;
		int m_nRunMotorForwardPin;	
		int m_nRunMotorBackPin;
 
		// Here we set Arduino pins responsible for actions,
		// and specify that they are OUTPUT pins - Arduino
		// writes to them, and motor driver reads from them.
        TrackMotor(int nEnableMotorPin, int nRunMotorForwardPin, 
			int nRunMotorBackPin)
        {
			m_nEnableMotorPin = nEnableMotorPin;
			m_nRunMotorForwardPin = nRunMotorForwardPin;
			m_nRunMotorBackPin = nRunMotorBackPin;
			
			// ---
			
            pinMode(m_nEnableMotorPin, OUTPUT);
			pinMode(m_nRunMotorForwardPin, OUTPUT);
			pinMode(m_nRunMotorBackPin, OUTPUT);  
		}
  
        // ---
  
		// Arduino sets signal level on its pin to HIGH/LOW
		// As pin is connected to the input of a driver, driver
		// turns the motor on/off.
        void enableMotor(bool bActive)
        {
            digitalWrite(m_nEnableMotorPin, 
				bActive == true ? HIGH : LOW);
        }
		
		// ---
		
		// Arduino sets signal level on pins controlling the
		// direction of spinning. Those pins are connected
		// to inputs of H-bridge of L293D.
		void runMotor(int nDirection)
		{
			if(nDirection == MOTOR_FORWARD)
			{
				digitalWrite (m_nRunMotorForwardPin, HIGH);
				digitalWrite (m_nRunMotorBackPin, LOW);
			}
			else if(nDirection == MOTOR_BACK)
			{
				digitalWrite (m_nRunMotorForwardPin, LOW);
				digitalWrite (m_nRunMotorBackPin, HIGH);
			}
            else
				enableMotor(false);
		}
        
    };
	
#endif // TRACK_MOTOR

The main file of our small application is responsible for the logic of a robot. As this is a simple robot, we are not going to do anything elaborate. All our robot does is going forward, until it hits the wall (which means a sensor button is pressed). Then it moves a little bit back to release the sensor, and turns left (always left). Then it goes forward again.

There are many ways this program can be improved. For example, when left sensor signals and obstacle, we should probably turn right. Or not - there are many possible algorithms. We are just using the easiest one.

#include "TrackMotor.h"

// Current state of the motors
enum { MOTORS_OFF, MOTORS_ON, MOTORS_FORWARD, MOTORS_BACK, 
	MOTORS_LEFT, MOTORS_RIGHT };

// Create an object to control the left motor with
// nEnableMotorPin = 5 (Arduino send corresponding signal
// there, nRunMotorForwardPin = 7 and nRunMotorBackPin = 6
// Needless to say, pin numbers should correspond to the 
// actual wiring or the robot.
TrackMotor motorLeft(5, 7, 6);
// Similar, for right motor
TrackMotor motorRight(8, 10, 9);

class Motors
{
    public:
        // Direction of track motors. The idea is to set the 
		// state of a motor (for example, spin forward) and 
		// the NEXT state, that it should switch to after a
		// given timeout (m_nTimeout below).
        int m_nState;
        int m_nNextState;
		
        // Max. time to perform the current operation
        int m_nTimeout;
		
        // Time when the prev. "update" was: we need it to be
		// able to know if the timeout is over.
        unsigned long m_nPreviousMillis;
	
        // Bumper's pushbuttons status
        int m_btnLeftState;
        int m_btnCenterState;
        int m_btnRightState;
		
		// Bumper's pushbuttons pins
        int m_btnLeftPin;
        int m_btnCenterPin;
        int m_btnRightPin;
	
		// We set pin numbers for buttons and instruct 
		// Arduino that those are inputs: we read sensor
		// data from them. Then we start motors.
        Motors()
        {
            m_nPreviousMillis = 0;
			
            m_btnLeftState = 0;
            m_btnRightState = 0;

            m_btnLeftPin = 2;			
            m_btnRightPin = 3;
            m_btnCenterPin = 4;
            
            pinMode(m_btnLeftPin, INPUT);   
            pinMode(m_btnCenterPin, INPUT);
            pinMode(m_btnRightPin, INPUT);
									
            m_nTimeout = 0;
            m_nNextState = MOTORS_LEFT;
            
            setState(MOTORS_ON);
            setState(MOTORS_FORWARD);
        }
  
        // ---
  
		// A "big switch" implementing the final automata
		// for the robot. For example, if the command is 
		// MOTORS_BACK, we set timeout for 0.5 second and 
		// run motors backward (for that duration).
        void setState(int nState)
        {
            m_nState = nState;
            switch(m_nState)
            {
                case MOTORS_ON:
                    Serial.println ("Enabling Motors");
					
                    motorLeft.enableMotor(true);
                    motorRight.enableMotor(true);

                    break;
					
                case MOTORS_FORWARD:
                    Serial.println ("Motors Forward");
					
                    // Go forward for max. 20 seconds
                    m_nTimeout = 20000;	
					
                    motorLeft.runMotor(MOTOR_FORWARD);
                    motorRight.runMotor(MOTOR_FORWARD);
					
                    break;
					
                case MOTORS_BACK:
                    Serial.println ("Motion Backwards");
                    m_nTimeout = 500;

                    motorLeft.runMotor(MOTOR_BACK);
                    motorRight.runMotor(MOTOR_BACK);
					
                    break;
					
                case MOTORS_LEFT:
                    Serial.println ("Turning Left");
                    m_nTimeout = 1000;

                    motorLeft.runMotor(MOTOR_BACK);
                    motorRight.runMotor(MOTOR_FORWARD);

                    break;
					
                case MOTORS_RIGHT:
                    Serial.println ("Turning Right");
                    m_nTimeout = 1000;
					
                    motorLeft.runMotor(MOTOR_FORWARD);
                    motorRight.runMotor(MOTOR_BACK);
					
                    break;
					
                case MOTORS_OFF:
                default:
                    Serial.println ("Disabling Motors");
					
                    motorLeft.enableMotor(false);
                    motorRight.enableMotor(false);
				
                    break;
            }
        }
  
        // ---

		// Depending on the sensor input, we set current and
		// next states. Example: left button got pressed (we
		// hit the wall). if(m_btnLeftState == HIGH) case is
		// processed. 
		// m_nPreviousMillis = currentMillis; which means
		// we set time when the current operation started to
		// current time. Then m_nNextState = MOTORS_RIGHT;
		// which means that after the timeout is over (which
		// is handled by the last "else" below), the command
		// will be issued for the next state (which meand
		// pull back now, and 0.5 seconds later stop pulling back, 
		// then start turning left.
        void update()
        {
            unsigned long currentMillis = millis();
            if(m_nPreviousMillis == 0)
                m_nPreviousMillis = currentMillis;
                
            // If the pushbutton is pressed, buttonState is HIGH.
            // Whatever happens, we begin by moving back.
            if(m_btnLeftState == HIGH)
			{
				Serial.println ("Left button pressed: Moving Back");
				m_nPreviousMillis = currentMillis;
				m_nNextState = MOTORS_RIGHT;
				setState(MOTORS_BACK);
			}
			else if(m_btnCenterState == HIGH)
			{
				Serial.println ("Central button pressed: Moving Back");
				m_nPreviousMillis = currentMillis;
				m_nNextState = MOTORS_LEFT;
				setState(MOTORS_BACK);
			}
			else if(m_btnRightState == HIGH)
			{
				Serial.println ("Right button pressed: Moving Back");
				m_nPreviousMillis = currentMillis;
				m_nNextState = MOTORS_LEFT;
				setState(MOTORS_BACK);
			}            
			else if(m_btnLeftState == LOW && m_btnCenterState == LOW 
				&& m_btnRightState == LOW 
				&& currentMillis - m_nPreviousMillis >= m_nTimeout)
			{
				int nState = m_nNextState;
				
				if(m_nState == MOTORS_LEFT || m_nState == MOTORS_RIGHT)
				{
					Serial.println ("Buttons released");
					m_nNextState = MOTORS_FORWARD;
				}
				else if(m_nState == MOTORS_BACK)
				{
					Serial.println ("Finished going back");
					m_nNextState = MOTORS_FORWARD;
				}
				else if(m_nState == MOTORS_FORWARD)
				{
					Serial.println ("Finished going back");
					m_nNextState = MOTORS_LEFT;
				}
						
				m_nPreviousMillis = currentMillis;
					
				setState(nState);
			}
		}

} motors;

// ---

void setup()
{
    Serial.begin (9600);
}

// ---

// In an infinite loop, read sensor data, and call 
// the motors.update(); above.
void loop() 
{
    motors.m_btnLeftState = digitalRead(motors.m_btnLeftPin);
    motors.m_btnCenterState = digitalRead(motors.m_btnCenterPin);
    motors.m_btnRightState = digitalRead(motors.m_btnRightPin);
  
    motors.update();
}

Proof of work

Below is a link to a short video. Our robot can indeed run around, hit the walls and turn as it does so. There is a single flaw the robot has (I mentioned it when I said that motors I used are too fast). The robot is 3-5 times faster than it should be. As the result, when it hits the wall, it gets turned around at a random angle. Well... I will keep that in mind when I build the next one.

Good luck :)







(C) snowcron.com, all rights reserved

Please read the disclaimer