Chapter 9. RoboBoat

This chapter will focus on the construction of a model catamaran (RoboBoat) that is guided by a GPS autopilot. The RoboBoat will follow a pre-programmed path that is selected by you, and created using Google Earth.

My motivation for this project came from a totally different direction: scuba diving. I have been addicted to this hobby for nearly 30 years now, and during this time I have always tried to combine sports and technical issues. This sometimes led to some weird underwater electronics, like LED lamps, waterproof laser pointers, homebrew diving computers, electronically controlled tank-filling equipment, and so on...

The idea for a robot boat was born on an expedition to a lake in the north Italian Alps that was 7500 ft (2300m) above sea level. The diving site was totally unknown to us, and I assume that we were the first people to carry 100-pound diving equipment on our backs to that altitude and go diving. We had no data about the depth of that lake, and because I am a very curious person, I wanted to know in advance what the dive had in store for us. So I took a sonar with me, mounted it on a body board, and swam crisscross around the lake to see what depths were below the surface. In principle, this method worked fine, but swimming in a lake with a water temperature of 4°C is no fun, even with a ¼″ (7mm) neoprene diving suit.

When I went home, the idea of automating this surveying process continued in my head, and I tried several methods to remotely control a model ship that carried a sonar on it. To make a long story short, none of these methods worked to my satisfaction.

To create a digital depth-map of a lake, you need a platform that is able to follow a straight line for at least some hundred meters. Doing this with an RC-controlled model ship is a real challenge for various reasons:

  • To get depth data that can be processed to a 3D map, you need depth values that have been sampled along parallel straight lines.

  • Holding a straight line is difficult when the boat is more than 300ft (or about 100m) away, because you are not able to see it anymore.

  • Following parallel paths is also difficult if you have no reference path.

Finally, after many trial and error approaches, I ended up with a configuration that worked fine:

  • A catamaran with benign, straight-line behavior that gracefully follows a straight line

  • A windmill-type assembly with an aircraft-propeller and a brushless DC motor for the propulsion

  • A GPS-controlled autopilot hardware/software system, based on an Arduino platform called ArduPilot

Some Words in Advance

When I was a student at the university (which was quite a long time ago), I was taking a course for control-loop design. One of the tasks was to optimize the control-loop parameters for an assembly that balanced a wooden stick. The stick was mounted on a roller skate, which was fixed on either side to a rope. The rope was connected to a motor that moved the roller skate with the stick on it back and forth. The motor was controlled by an analog PID controller. It worked quite well; the skate oscillated a little bit and held the stick in a perfect upright position. The whole assembly looked very crude, and the wooden stick was indeed a branch from a tree. I asked my professor why they did not make a more perfect assembly with a metal stick instead of a branch, and with a better push-pull assembly. His answer was short:

"If your assembly is mechanically perfect, nobody would believe that the system is balanced by electronics."

With this in mind, I have designed the RoboBoat. The mechanics are very simple and sometimes crude. But, if you see your catamaran on a lake, following a straight line and returning to the launch point like a boomerang, you'll see how right my professor was.

Parts List for the RoboBoat

Let's first start with the parts list for all the materials that are needed to construct the boat (Table 9-1), the propulsion assembly, and the electronics. I have tried to use materials that are easily available in do-it-yourself stores and model shops.

Table 9.1. RoboBoat Parts List

Part

Description

Price

- Propulsion Assembly -

PVC tube, 7″ (170mm) long

1″ (25mm) outer diameter PVC tubing from Home Depot Store SKU $2.00 # 193755

$2.00

PVC rod, 2″ (50mm) long

¾″ (20mm) outer diameter, trimmed on a lathe so that it exactly fits the inner diameter of the tube. If not available, use a ¾″ PVC tubing instead. E.g. Home Depot SKU # 193712

$2.00

Baseplate

Aluminum or PVC plate 2″L (50mm) × 6″W (160mm), and ¼″ thick (5mm) from Home Depot

$1.00

(2) Rudder horns

Can be found in RC stores or Hobbyking.com, part #OR7-601GRx10 or similar

$1.04 (10-pc set)

(2) Pushrods

Can be found in RC stores, steel wire, 1 or 1.5mm diameter, 4″ (100m) length each

$1.00

(2) or (4) Pushrod connectors

Are used to connect the pushrods to the rudder horns; either RC store or from Hobbyking.com, part #GWPHD001 or similar

$2.20 (5-pc set)

RC servo

Any "standard" size RC servo will do. e.g. HobbyKing.com, part # BMS-410STD.

$5.00–$12.00

Motor

ROBBE ROXXY3 ROXXY BL-Outrunner 2827-26 part #477926, comes complete with motor mounting kit; or similar BLDC motor, Kv between 800 Rpm/v and 1200 Rpm/v, 100-200W

$30.00

Propeller

APC 10×5″ or 11×5″ Hobbyking.com, part #APC10x5-E or similar

$2.00

Screw

M5*20mm counter sunk screw, to fix the pivot to the baseplate from Home Depot

$0.10

- Electronics -

ArduPilot PCB

ArduPilot board, available from SparkFun: www.sparkfun.com/products/8785. Do not forget to modify as described.

$24.95

Programming adapter

FTDI BASIC Breakout −5V, available from SparkFun: www.sparkfun.com/products/9716

$14.95

USB cable

You will also need USB type A to Mini-B cable; if you don't have already in your shack, get this one: http://www.sparkfun.com/products/598cable.

$3.95

(2) Header 3pin

Breakaway headers 0.1″ (2.54mm) through-hole, http://www.sparkfun.com/products/116 or similar

$2.50

(2) Header 6pin

Make from foregoing product

--

ESC

Brushless DC motor controller, 20A Type, available in RC shops or from Hobbyking.com, part #TR_P25A

$12.00–$30.00

GPS module

EM406 from SparkFun or DIYDrones.com, http://www.sparkfun.com/products/465 or http://store.diydrones.com/EM_406_GPS_1Hz_p/em-406a.htm, comes ready with cable

$60.00

Battery pack

Any 3S LiPo pack with at least 3000maH will do, e.g., HobbyKing.com, part #Z50003S15C

$30.00–$40.00

Power connectors

Look in your RC shop for adequate ones; TAMYIA type or similar

$3.00

- Boat -

EPS or XPS foam board

Base material for the hulls, Home Depot, 40″L (1000mm) × 20″W (500mm) × 4″D (100mm)

$10.00–$20.00

XPS board

Base material for the deck, Home Depot, 24″W (600mm) × 15″D (400mm) × 1″D (25mm)

$5.00–$10.00

Cardboard

Use to make the templates for the hulls, about 1mm thickness

--

Double-sided sticky tape

Used to glue the templates to the Styrofoam block from Home Depot

$4.00

EPS/XPS glue

Contact glue to join the segments together, e.g. from http://www.diysupermarket.com/uhu-por-polystyrene-foam-glue.html

$5.00

(4) Foam anchors

Used to connect the hulls to the deck, Home Depot

$10.00

Fiberglass fabric

Use fine fabric, e.g. Home Depot SKU # 846759

$10.00

Epoxy resin

Use one with a pot life of about 20 to 30 minutes. From RC stores or marine suppliers.

$20.00–$40.00

Fin

Use PVC foam sheets with 3mm thickness or painted plywood or similar. e.g. from Home Depot

$5.00

(4) M5 bolts with eyes

Used to connect the deck to the hulls, 1.5″ (40mm) long from Home Depot

$5.00

Plywood spacers

Make from painted plywood, 1.5″ (40mm) × 1.5″ (40mm), drill a 5mm hole in the middle from Home Depot

$1.00

Additional Tools/Materials (available at Home Depot or other DIY stores)

Disposable rubber or plastic gloves

Hot wire Styrofoam cutter (see below for contruction)

Razor knife (I used one made by Stanley)

Medium grade sandpaper

Disposable paintbrush

Now let's review some of the more important materials and tools you'll need to complete the catamaran structure. The following sections provide more details on each item (later in the chapter, we'll walk through propulsion assembly, electronics, and software).

Polystyrene Foam

This material is one of the most popular plastics in the world. It is made of polystyrene or PS.

As shown in Figure 9-1, there are two general types available: expanded Polystyrene (EPS) and extruded Polystyrene (XPS).

EPS is more lightweight and less rigid than XPS. Both types can usually be found in DIY ("do it yourself") stores in the form of rigid panels with various thicknesses.

EPS on the left, XPS on the right

Figure 9.1. EPS on the left, XPS on the right

EPS is usually white and has visible pores the size of a few millimeters. When you break this material, you can see some of the pores fly around like small bubbles. This material is very lightweight, soft, and not so easy for sanding.

XPS is usually colored and is usually used for the insulation of basements. It is more expensive, heavier, and harder than the low-density version, but it is easier to treat with sanding paper.

Either of the two materials will work for the construction of the hulls. I personally prefer XPS, because the finish will be better afterward. For the hulls, a material thickness of 4″ (100mm) is adequate. If you don't find this thickness in your local store, simply take smaller ones and glue them together until you have the appropriate size. More about the glue later.

Both of these materials share one drawback: they can be easily melted away by most organic solvents. If you want to start any treatment on this material with paint, resin, or putty, please try in advance on a piece of unused material and allow at least one hour to observe what happens. Using the wrong material on polystyrene can ruin your day in seconds!

Epoxy Resin

We will use this product in combination with fiberglass fabric as a coating for the EPS/XPS to add more strength to the hull. Here is some advice in advance.

Please do not use material for the coating other than this one. There are other two component resins available on the market; most of them can be found in DIY stores as resins or putty. Most of them are based on polyester, and that material is definitely not compatible with polystyrene. Here is one test: if it smells extremely like plastic, you can be sure that you have chosen the wrong product.

Back to epoxy. This is a two-component resin, and it is available in most hobby or modeling shops. Usually it is compatible with most materials, even polystyrene. There are various brand names of that product available. Please ask your local dealer or search the Internet for more information.

After mixing the two components in the right ratio (the resin and the hardener), you will get a honey-like liquid, which can be processed for about 15–30 minutes until it starts to thicken. The total time to cure is about 12 to 24 hours, depending on the product. So it is good practice to start the work in the evening, let it cure overnight, and wake up with a (hopefully) ruggedized hull in your workshop.

There are two methods of mixing you can use: by weight or by volume. Please read the instructions that come with the product; usually both methods are described there. The ratio has to be kept within a certain accuracy (about +−5% is OK). Please be careful about that, otherwise you will end up with a hull that will be sticky for years.

After the mixing of the two components, thoroughly stir it for at least one minute and allow another minute of silence to eliminate the air bubbles.

Gloves

Please wear disposable rubber or plastic gloves for the whole time you are in contact with the material for these reasons:

  1. The product is not poisonous but may irritate your skin.

  2. You will surely get in contact with that extreme sticky liquid when applying it to the hull, and it is no fun working with sticky fingers.

  3. The material can be easily wiped off from the gloves with paper towels.

Fiberglass Fabric

Fiberglass fabric is made of very thin glass filaments. These filaments are woven like "ordinary" fabric (Figure 9-2).

There are lots of fiberglass products available. For the coating of the hull, a fine fabric should be used to give a smooth surface and for ease of work.

Thick fiberglass fabric or fiberglass mats are not suitable because these materials are too stiff to handle. Fiberglass is usually categorized on a "weight per surface" basis. Values around 0.3oz per sqft (80 grams per m2) are perfect.

Woven fiberglass fabric with 80g/m2

Figure 9.2. Woven fiberglass fabric with 80g/m2

Fiberglass fabric can easily be cut in appropriate pieces with a pair of scissors. You will need to buy a pair of scissors especially for your work, because the wear of the blades is much higher compared to cutting tissues or paper, and the scissors may (they surely will) get contaminated by epoxy resin. Hardened epoxy resin on fiberglass can be cut with a sharp razor knife. This is helpful for cutting away excess material prior to sanding.

Glue

To glue the segments together, you must use Styrofoam glue. This should be available at a hobby shop or most DIY stores. The glue is a contact adhesive, like others, but it has a solvent that is compatible with polystyrene.

With the materials out of the way, let's discuss the tools.

Styrofoam Cutter and Razor Knife

A hot wire Styrofoam cutter is absolutely mandatory to make a smooth cut of the segments. If you don't want to buy a cutter, you can easily make your own. There are lots of plans on the Internet; please have a look at this site: www.instructables.com/id/Hot-wire-foam-cutter/. Or simply Google "Styrofoam cutter" and you will find lots of links for DIY plans to make your own.

You do not need one with a table, because the cardboard template will be your guide. The wire length should at least be 12″ (300mm) to have enough room to operate. Please test in advance on some unused Styrofoam pieces.

A sharp razor knife is also useful for trimming and cutting the foam.

Miscellaneous

Sanding paper is useful for smoothing the surface. After the thermal cutting of the segments, the surface of the Styrofoam is a little bit roughened through the melted material. I would suggest firstly gluing all segments together and then doing a little bit of sanding. Be careful not to use sanding paper that is too coarse and not to apply too much force on the material.

For applying the epoxy resin on the Styrofoam and on the fabric, a disposable paintbrush is perfect. After using it, the paintbrush is useless. I do not know any solvents to wash the resin off. Buy some cheap ones and throw them away after use.

The RoboBoat Design

Before I arrived at the catamaran solution, I tried lots of mono-hull assemblies, but they all had the same problem: they didn't follow a straight line. This problem can be overcome with a keel that goes deep below the waterline, but I wanted a platform that would only touch the surface, to avoid contacts with water plants (this is also the reason I have an air propeller and not a water propeller).

A catamaran is a very stable construction that can be thought of as a "boat running on rails," and, in addition, there is lot of room between the two keels for any type of payload. Model catamarans of the size I wanted are very rare in RC modeling, so I decided to build one of my own. I first started with a fairly easy construction with a triangular cross-section that did its job very well, but was not optimized for drag, because the wetted surface of a triangular shape is nearly the worst that can be thought of (see Figure 9-3).

A simple catamaran with triangular hulls

Figure 9.3. A simple catamaran with triangular hulls

The final construction I settled on is very easy. As you read this overview, keep in mind that there are many materials that can be used instead of the PVC foam sheets, such as balsa wood, water-resistant plywood, or the like.

I used PVC foam sheets with a thickness of about 1/10″ (2.54mm). This material is very lightweight, can be easily cut with a Stanley razor knife and can be glued with hot glue. The side walls are made of rectangular sheets in the size of 40″ (1000mm) × 6″ (150mm). The front and rear tips are identical and are made of triangular sheets. In the inside of the hull, additional triangular sheets give the hull some stability. The top is also made of a rectangular sheet of the size 40″ (1000mm) × 4.5″ (110mm). It is helpful to make some templates out of cardboard, especially for the triangular sheets that form the front and rear tips. Glue it all together with hot glue and that's it!

The connection between the two hulls (the deck) is created using a sheet of hard XPS Styrofoam, measuring 15″L × 15″W × 1″D. The foam board is fixed to the hulls by 4″-long (100mm) screws, with the help of 90° angles that are glued on the top of the hulls.

That construction can be easily disassembled, simply by removing the screws.

The overall size of this catamaran is 55″ (1400mm) in length, 15″ (400mm) in width, and 6″ (150mm) in height at a net weight of about 7.7lbs (3500g). The energy consumption to keep up a velocity of about 3.5mph (or 5.5km/h or about 3 knots) is some 110 watts, when the boat has to carry an extra payload of about 4.5lbs (2000g).

This boat design has worked well on many occasions as a proof of concept for the catamaran approach. It also showed that the adjustment of the PID controller parameters (more on that later) is much less critical than on a monohull design. The V-shape of the hulls gives an excellent straight-line stability, and there is no need for additional fins, as on other keel shapes.

If you have enough battery capacity or you only want to make short trips, this design is absolutely sufficient, whereas if you want a more efficient design, a more streamlined keel is mandatory.

After some research on the Internet, I found some interesting designs for model catamaran hulls on a French web site. The one that fit most of my needs was called WR-C 21, and there was also a downloadable .dxf file available. A friend of mine who is specialized on 3D-CAD imported this file into his CAD system and created a three-dimensional model out of the two-dimensional .dxf file. With this model, it was possible to cut the hull in sections 4″ (100mm) long.

Now, on to building the boat.

Assembling the Boat

In this section, I walk you through the process of building the boat hull.

The Templates

The paper templates with the cross-sections of the hulls can be found as a downloadable .pdf file at http://code.google.com/p/roboboat/downloads/list and Apress.com. Print twice all the segment templates from "Segment 1" to "Segment 14." Each segment template consists of two outlines: the inner and the outer one. This is the reason you shall print it out twice. Glue the printout on cardboard with a thickness of about 0.04″ (1mm). Then cut out an inner and an outer template of each segment with a pair of scissors, as I did for Segment 6, shown in Figures 9-4 and 9-5.

The printed templates

Figure 9.4. The printed templates

The templates, glued on cardboard and cut

Figure 9.5. The templates, glued on cardboard and cut

If there are any burrs, remove them with sanding paper. Apply dual-sided sticky tape on the backs of the templates (Figure 9-6). There is no need to cover the whole template with the tape. A piece ¾″ (20mm) × ¾″ (20mm) should be OK. Do not peel off the protective paper yet.

The templates from the back, with sticky tape

Figure 9.6. The templates from the back, with sticky tape

The first and the last segments (1 and 14) are very delicate to handle because the ends are very thin. I have shortened these segments to half of their original length, which was 2″ (50mm). This will shorten the overall length of the hull to 50″ (1300mm), but this will have no effect on the performance of the boat.

Gluing the Templates on the EPS/XPS Board

Now, the most critical part of the work begins. You have to adjust the front and back template to each other and then glue it to the Styrofoam board. The boards come pre-cut with very exact edges, and this helps a lot for the adjustment (Figure 9-7). First paint an exact rectangular line on the front side and the adjacent sides of the board.

Preparation of the EPS board

Figure 9.7. Preparation of the EPS board

Then take the outer template, peel off the protective paper from the sticky tape, and adjust the line to the vertical crosshair line that is printed on the template. Align this template to the top edge of the board (Figure 9-8).

Alignment of the template on the EPS board

Figure 9.8. Alignment of the template on the EPS board

Next, measure the vertical distance (d) between the outer and inner template and adjust the inner template on the back of the board accordingly, e.g., move it (d) down from the top edge. Note that the vertical distances in the middle segments are close to zero.

Cutting Out the Segments

You have to use a hot-wire foam cutter for this purpose. Figure 9-9 shows a picture of my DIY version.

The DIY Styrofoam cutter from the author

Figure 9.9. The DIY Styrofoam cutter from the author

First separate the segment from the panel by making a rough cutout of the segment, allowing about ½″ of extra Styrofoam around the template, as shown in Figure 9-10. This piece will now be much easier to make perfect with one more pass through the wire cutter.

Coarse cutout of one segment

Figure 9.10. Coarse cutout of one segment

Then cut away the excess material by using the cardboard template as a guideline, as shown in Figure 9-11. Adjust the temperature of the wire so that it gives a smooth cut. Temperatures that are too high will melt away the material under the template.

Experiment a little until the results are satisfying.

Fine cutting of one segment

Figure 9.11. Fine cutting of one segment

The ready cut segment should now look as shown in Figure 9-12. It is important to label each side of the cut segment with a felt-tip pen after you have peeled off the templates. You will need this labeling afterward, when gluing the segments together.

One segment after cutting

Figure 9.12. One segment after cutting

Gluing the Segments Together

This step is simple. Apply a layer of the glue to one of the surfaces, put the other surface together, press firmly, and then separate the surfaces. With this method, you will have a thin layer of the glue on either side. Allow some minutes to dry until the surface has a rubber-like consistency that is still a little bit sticky. Then put the surfaces together and press firmly. Attention! Adjust the two surfaces exactly, prior to putting them together. There will be no second chance. The glue acts immediately! In Figure 9-13, you can see two segments prior to gluing.

Two adjacent segments with EPS/XPS glue

Figure 9.13. Two adjacent segments with EPS/XPS glue

After having glued all segments together, the result should look like that shown in Figure 9-14.

All segments glued together

Figure 9.14. All segments glued together

To smooth out the surface and to remove excess material, use sanding paper. This will take some time. It's best to do the sanding outside and use old clothing because there will be quite a bit of polystyrene dust created during this process.

Inserting the Foam Anchors

To connect the deck with the hulls, foam anchors are very useful. Normally, these anchors are used to hold screws in walls that have been insulated with foam panels or for use in sheetrock walls. Look in your DIY store for this item. The one I have used looks like that in Figure 9-15.

A foam anchor from the DIY store

Figure 9.15. A foam anchor from the DIY store

Normally, these anchors are made to hold wood screws. To hold machine screws, I tapped an M5 thread into the anchor (Figure 9-16).

The foam anchor screwed into the hull

Figure 9.16. The foam anchor screwed into the hull

To give more strength, you can apply a little bit of epoxy resin to the anchor prior to inserting it into the foam.

The placement of the anchors is in the middle of segments 7 and 11, as you can see in Figure 9-17.

The placement of the anchors

Figure 9.17. The placement of the anchors

The Coating

This is delicate work, so it is best to be prepared. Put a lot of old newspaper around the area of work before you begin. To hold the hull in place, make a stand from some unused foam, and insert two foam anchors with two M5 threaded bolts, as shown in Figure 9-18.

The hull, prior to coating

Figure 9.18. The hull, prior to coating

This will hold the hull in place for the coating with the fiberglass fabric.

I did the coating in two steps: first the bottom of the hull and then the top. I also made overlapping areas on the top. To secure the fiberglass to the foam, use some pins that can be removed after the hardening of the epoxy.

First cut out a rectangular piece of fiberglass in the size of 53″ × 12″ (1350mm × 300mm). This will fit the length and will give an overlap to the top of about 0.8″ (20mm). Lay it over the hull that sits bottom-up on the stand. Adjust the material. Make a cut in the wrinkles and overlay the material. If the fiberglass is positioned, mix the epoxy according to the instructions of the product you are using. Wear rubber gloves. Then apply the epoxy over the fiberglass by speckling with the paintbrush. The fiberglass will soak up the epoxy and leak through onto the foam. Try to squeeze out air that is trapped between the foam and the fabric.

When you have finished, it should look like Figure 9-19.

The hull, coated with fiberglass fabric and epoxy resin

Figure 9.19. The hull, coated with fiberglass fabric and epoxy resin

If you don't have a fiberglass sheet this big, you can also do a "patchwork" with smaller pieces. This will ease the work, especially if you are not familiar with fiberglass work. Simply overlap the patches and speckle them with the paintbrush.

After the epoxy hardens (usually after 12 hours), you can take the hull from its stand and look at your work. Excess material can be cut away with a sharp razor knife cutter. To coat the top side, first put some sticky tape over the holes of the foam anchors to avoid epoxy resin going into it. Then fix the hull (e.g., between two bricks) and apply the fiberglass and the epoxy as described earlier.

Applying the Finish

After coating both sides of both hulls, there are still some things to do. The tips of the last segments (1 and 14) must also be coated. You can do this by cutting out fiberglass sheets that match the cross-sections and epoxying them to the foam. An alternative way to get the end-points finished is to make a "putty" by mixing epoxy resin and a filler material like cotton flakes or glass bubbles (available in RC model shops). You can then apply this material to the end-points.

After the epoxy hardens, cut away excess material with a razor knife and do some sanding to smooth the surface and the edges.

The Fins

To give good straight-line stability, you must add small fins to ends of the hulls. The fins are simply triangular sheets that can be made of various materials (Figure 9-20). I used PVC foam sheets with a thickness of about ⅞″ (2mm). Any other waterproof material of this thickness will work. You can even use plywood, if painted or treated with urethane. Cut out a triangle the size of 4″ (100mm) × 1.5″ (40mm), and glue it to the end of the hull. I used hot glue, which works fine if you have roughened the surface with sanding paper prior to applying the glue.

The fins

Figure 9.20. The fins

Painting

There is no need for painting, but for the sake of visibility, I suggest applying some fluorescent paint onto it. You can use any sort of paint, because the epoxy acts as an isolation layer for the foam. It is good practice to use a white primer layer and then apply the fluorescent layer by either a paintbrush or spray. The white primer layer will increase the visibility of the fluorescent paint.

The Deck

This is a fairly easy task, compared to the fabrication of the hulls. Simply cut out a 23″ (600mm) × 15″ (400mm) sheet of an XPS foam panel with a thickness of at least ¾″ (20mm). To add strength, you can coat this board with epoxy, but it is not mandatory, because the XPS is very solid. If you don't have XPS, use EPS instead and coat it with epoxy. Figure 9-21 shows the dimensions and the position of the four mounting holes for the hulls. The holes are symmetrical, so I put the dimensions of only one for reference. The holes in the middle part of the board are for fixing other superstructures and are not relevant at this point of the assembly.

The deck with mounting holes

Figure 9.21. The deck with mounting holes

Completing the Assembly

Now, the great moment has come! For the assembly, I have used M5 bolts with "eyes" and some rectangular plywood spacers to hold all pieces together (Figure 9-22). The eyes are useful to tie some of the superstructures to the deck.

Mounting material for the connection of deck and hulls

Figure 9.22. Mounting material for the connection of deck and hulls

Simply screw the deck panel to the hulls and tighten until the stability is sufficient (Figure 9-23). Do not over-tighten the screws, to avoid damage to the foam.

Deck and hull mounted together

Figure 9.23. Deck and hull mounted together

If you have done your work right, you should have something that looks like Figure 9-24.

The finished catamaran platform

Figure 9.24. The finished catamaran platform

With the hull assembly completed, we will move on to building the propulsion system. Catamarans are typically propelled by the wind and sails. The RoboBoat has a quite different kind of propulsion: a wind generator that will be described in the next section.

The Propulsion Assembly

It was a long and troublesome path to arrive at the assembly that I am currently using. I tried several approaches and ended up with something I call the "windmill assembly," shown in Figure 9-25. So, what is it?

The propulsion assembly

Figure 9.25. The propulsion assembly

It is simply a motor with a propeller that is mounted on a tube that sits on a pivot. The tube is turned with an RC servo. That's all.

Compared to the typical water-propelled boat, there are some advantages to an air-powered setup:

  • A water propeller is more complicated to install because you have to penetrate the hull, thus introducing the problem of sealing out unwanted water from the hull.

  • An air-powered propulsion assembly has no problems with waterplants, which can wrap into the propeller of a water-propelled system.

  • R/C airplane motor/propeller kits are very common and widely available.

  • The whole assembly is conceived as a "module," which can be easily mounted on different swimming platforms.

  • The module is a combination of propulsion and steering and can be changed as a whole.

  • The thrust-vector steering that is achieved by turning the propeller is very efficient.

There are lots of ways to build such an assembly. The one I am describing here is not mandatory; give your creativity a chance to find your own solutions. I am in the happy situation of owning a lathe, and so it was fairly easy to make a pivot that exactly fits.

For the construction, I basically have used a PVC tube with an outer diameter of 1″ (25mm), which sits on a pivot.

In the next section, we will discuss the propulsion assembly and the steps required to build it.

The Baseplate

To easily mount and dismount the whole assembly to different hulls, I have put all the components on an intermediate baseplate (Figure 9-26). A 0.15″ (4mm)-thick aluminum plate is the best choice, if you want a rock-solid construction. In the pictures in this book, I have used two PVC foam sheets that are glued together. Other materials that are water-resistant and rigid enough will also be suitable (e.g., Plexiglas, painted plywood, etc.). The baseplate is fixed to the deck of the boat with four screws. For that purpose, drill four holes ½″ away from each edge.

The pivot is screwed to the baseplate with an M5 countersunk screw. Drill a hole of ½″ (6mm) with a countersink on one side. The baseplate will also hold the servo. The servo has to be adjusted in height to match the rudder horn of the turn assembly.

Drawing of the baseplate

Figure 9.26. Drawing of the baseplate

The Pivot

The pivot is made of a PVC rod with a diameter of ¾″ (20mm). On the bottom is an M5 thread, which is used to screw it to the baseplate (Figure 9-27). This is the only component that you may need a lathe to manufacture. The pivot should match closely to the inner diameter of the tube. A PVC fitting that matches the PVC tube (from outside) may also be suitable, but I have never tried.

The pivot mounted on the baseplate

Figure 9.27. The pivot mounted on the baseplate

The Tube

The tube shown in Figure 9-28 holds the motor and sits on the pivot. It is made of a 1″ (25mm) PVC tube. To use propellers with diameters up to 11″, the tube should have a length of 7″ (170mm).

The PVC tube

Figure 9.28. The PVC tube

To hold the motor, simply drill two holes with a diameter of ⅛″ (3mm) in the upper part of the tube, as shown in Figure 9-29. If your motor has a shaft on both sides, drill a hole of about 5/16″ (8mm) into the tube to have room for the shaft. For ease of mounting, drill two holes of 5/16″ (8mm) on the opposite side of the ⅛″ (3mm) holes. Then fix the motor to the tube using M3 screws.

Mounting holes for the motor

Figure 9.29. Mounting holes for the motor

The Rudder Horns

The rudder horns connect the tube to the servo. There is one on each side of the tube, which will give a push-pull connection to the servo, which is more stable than a single pushrod. I used general-purpose rudder horns for RC models, which are available in hobby stores. The lower part of the tube must be flattened with a file to give space for fixing the rudder horns. The horns can be glued to the tube with contact glue or with two-component epoxy glue (Figure 9-30). Be sure that the holes in the horn are aligned to the center of the PVC tube.

A rudder horn, glued to the tube

Figure 9.30. A rudder horn, glued to the tube

The Motor

Basically, you can use any motor that can turn a propeller and that produces enough thrust to push the boat forward. I used the brushless DC "outrunner" (BLDC) type shown in Figure 9-31 for several reasons. Brushed motors are also possible, but if you plan long-term trips with the boat, a BLDC is the better choice because there are no brushes that can wear out. BLDC motors that are suited for the boat are widely available, and the price is affordable. I will describe my configuration in depth, but if you want to use other types, here are basic guidelines for the selection:

  • Voltage: from 6 to 14V (two or three LiPo batteries in series)

  • Kv: between 600 and 1200 rpm/V

  • Power: between 100 and 200 watts

With a matched propeller, you should achieve a thrust of at least 1 pound (450 grams). The diameter of the propeller should be in the range of 9″ to 11″.

Outrunner BLDC motors carry the magnets in a "cage" that runs around the stator coils. The shaft is fixed to the cage. For that reason, most of the "outside" part of the motor is turning. This makes the mounting of the motor different to the mounting of brushed motors. If possible, use one that has a shaft mounting on the cage and that is sold with a mounting kit, which consists of the motor, the propeller, and the propeller driver.

The propulsion-thrust should be in the range of about 1.1 to 1.8 pounds (500 to 800 grams). I have taken several measurements with different types of motors and propellers.

The following combination is the one that I am actually using:

  • Motor: ROBBE ROXXY BL-Outrunner 2827-26

  • Propeller: APC 11″ × 5.5″

The motor is usually sold in a kit with the motor mount and the propeller driver.

A BLDC outrunner motor

Figure 9.31. A BLDC outrunner motor

The Servo

Use a standard RC servo that can be found in RC model shops. The rudder horn of the servo should have a diameter of 1.75″ (45mm). The servo can be either glued or screwed to the baseplate. For better performance, use screws to mount the servo.

The Pushrods

Steel wires with a diameter of 1–1.5mm are best suited for the connection between the rudder horns of the servo and the tube. If you can find pushrod connectors, the mounting and the adjustment are very easy. If not, you can bend the ends of the wires by 90° and insert them into the holes of the rudder horns.

Electronics

I must give credit to the ArduPilot (Arduino) community that made it possible to complete this project: the DIYDrones.com home page is a real treasure when you have to deal with unmanned robotics. Special thanks to Chris Anderson and Jordi Muñoz, who are the founders of that great open hardware and open source project. The key element of the electronics I am using here was derived from an Arduino-based autopilot that was originally conceived for model airplanes, called the ArduPilot (AP).

Before we go into depth, let's first look at an overview of the whole system. As you can see from Figure 9-32, the whole electronic system is fairly simple. It consists of the following components:

  • The ArduPilot PCB

  • A GPS module

  • An ESC for brushless DC motors

  • A motor

  • An RC servo

  • A battery pack

Block diagram of the electronics

Figure 9.32. Block diagram of the electronics

Let's look at these components in more detail, so you can see how they interact with each other.

The Heart of the System: The ArduPilot PCB

The small printed circuit board shown in Figure 9-33 has all key elements on board that are needed to control a vehicle by GPS. It is based on the same ATmega328 microprocessor used on the Arduino, though the ArduPilot utilizes a very small surface mount version of the chip.

The ArduPilot PCB

Figure 9.33. The ArduPilot PCB

The board is compatible with the Arduino platform, so it is very easy for the user to modify and upload new software to the board. The board consists mainly of the following components:

  • An 8-bit AVR microcontroller, the ATmega328

  • An ATtiny AVR microcontroller for switching between R/C and autonomous control (not used in this project)

  • A GPS interface connector

  • Interfaces to control RC servos and electronic speed controllers (ESC)

  • A multiplexer that can switch the control between RC control and the microcontroller

  • Several other features used for airplane models, which will not be used in this project

The GPS Module

The GPS module shown in Figure 9-34 is the "sensor" of the autopilot system, similar to what you might find inside a commercial automotive or outdoor GPS unit. It measures the actual position, the velocity, and the heading of the boat. It is connected to the ArduPilot board via a small, SMD-type connector. The cable for the connection between the GPS module and the ArduPilot is usually shipped with the module.

The EM406 GPS module

Figure 9.34. The EM406 GPS module

The ArduPilot board "talks" to the GPS module over a UART (serial) connection. There is one important thing to consider: the ATmega328 microcontroller has only one UART interface on-chip, which is also used for the programming of the chip. To avoid a conflict, the GPS module must be unplugged from the ArduPilot board while programming! More on programming in the section "The Software".

As of my writing this chapter, the EM406 GPS module remains my favorite unit to work with. There are many other GPS modules on the market that may also be suitable, but the EM406 has proved to be very robust and reliable.

The Electronic Speed Controller (ESC)

The device shown in Figure 9-35 serves two functions: it supplies the ArduPilot Board with a regulated voltage of 5V and it controls the speed of the BLDC motor. The ArduPilot board is powered with 5V that is derived from an ESC with a so-called battery eliminator circuit (BEC). This is simply a linear regulator that sits inside the ESC electronics and provides a regulated 5V supply out of the voltage from the battery pack. The connection to the ESC is done via a standard RC servo connector with three poles. There are lots of ESCs out on the market, and it is impossible to suggest one that is best. It is advisable to buy one that is matched to the motor that you will use.

The electronic speed controller (ESC)

Figure 9.35. The electronic speed controller (ESC)

The Motor

The motor is a so-called "brushless" DC motor (BLDC). It produces the thrust that drives the boat forward.

BLDC motors usually have three cables that are fixed to the motor. The connectors are directly soldered to the cables so that they can be easily connected to the ESC. If your motor turns in the wrong direction, simply swap two cables and the motor turns in the opposite direction. Care has to be taken that the cables do not interfere with the propeller. Use tie-wraps to fix it to the PVC rod, which was already described in the "Propulsion Assembly" section of this chapter.

The Rudder Servo

The rudder servo turns the "windmill assembly" to steer the boat. Here you can use a "standard" RC servo. Servos of this type are widely available. There are also so-called "high-torque" servos available; they are more powerful, but slower. Any of the two versions are suited to do the job. Please do not use mini or micro servos; they are useful for model airplanes, but for a boat, they may be too small.

The Battery Pack

The battery pack provides the electrical energy for the whole electronics and—mostly—for the motor.

There are many possible solutions when it comes to selecting a battery pack. Actually, I would prefer to buy a 3S LiPo battery pack with a capacity of about 4000mAh. These types of batteries are now very common in RC modeling, and can be found online for around the same price as older battery types (like NiMH or NiCad). If you want to use other batteries, use the following data as a guideline:

  • Voltage: Between 10 and 12 volts

  • Capacity: 4000mAh or above

  • Sustained current: 6 to 8 amperes (10c discharge rating or better)

Because the weight of a boat is not as critical as on an airplane, you may also find that using lead-acid type batteries will work.

To charge the batteries, use an appropriate charger. Special care has to be taken when using batteries that contain lithium, like LiIon and LiPo types, that they are not over-discharged. The lithium that is in the batteries may start to burn when the battery is overcharged or shorted. A lithium fire cannot be extinguished with water. I strongly recommend charging lithium batteries outdoors, placed on inert materials like stone tiles.

Some words about power connectors—there are some "standard type" connectors available on the market. Usually, the ESC and the battery packs come with connectors. If they are incompatible, you have to cut one side (either the ESC or the battery pack) and solder the appropriate connector to it. I have used TAMYIA-type connectors for all of my projects, because they are widely available and very robust.

Assembling the Electronics

To get the most out of this section, you should be familiar with the basics of soldering. If you are unsure, ask somebody who has the know-how and who can help you on that topic. I also recommend reading the tutorials at the SparkFun.com web site, where there are lots of articles and "how-to"s that can help newcomers to embedded electronics.

We start the assembly with the ArduPilot board that is usually shipped without connectors for the servos and the programming connector. All connectors that have to be soldered onto the board are breakaway headers with a pitch of 0.1″ (2.54mm) that can be found in nearly every DIY electronics shop or at http://www.sparkfun.com/products/116.

They can be easily cut or simply broken to the appropriate size. For the ArduPilot board, we need two 3-way headers and one 6-way header—that's all. Figure 9-36 shows the locations. The servo connectors are called OUT1 and OUT2 in the schematics. The programming connector is named JP2 or USB/SERIAL in the schematics.

The connectors on the ArduPilot board

Figure 9.36. The connectors on the ArduPilot board

After soldering the connectors, we have to make one modification on the board (Figure 9-37). We need to do this because the board provides an RC override functionality that is indispensable when you use it as an autopilot hardware for flying platforms.

For that, a multiplexer IC is integrated on the board that is used to switch the control of the servos either to the ATmega328 microcontroller or to an RC receiver. The control of the multiplexer is done by the ATtiny microcontroller (located to the right of the ATmega328 chip), which by default switches the control to the RC receiver. For the control of the boat, we do not need this functionality. To make sure that the ATtiny does not switch the multiplexer to the wrong position, we have to give control over the multiplexer to the ATmega328. Actually there is no way to do this function with software, so we have to modify the hardware. This is simply done by lifting pin 6 of the ATtiny microprocessor that controls the multiplexer. You can do this by de-soldering the pin or by cutting it with a sharp knife. If you are familiar with SMD soldering, you may also de-solder the whole chip, because it will not be needed.

The MUX modification

Figure 9.37. The MUX modification

The Programming Adapter

You will need the adapter shown in Figure 9-38 to upload new software/waypoints to the ArduPilot board. It contains a USB to serial converter chip, so that you can connect this adapter to an USB port of your computer that runs the Arduino environment. Prior to using this adapter, you have to install the drivers for this device on your computer. Follow the instructions on the Arduino home page for the setup of the environment. The adapter that I am using comes from SparkFun electronics. You will find the product and all the documentation needed at http://www.sparkfun.com/products/9716.

The programming adapter

Figure 9.38. The programming adapter

Software and Mission Planning

As you may expect, software is a major part of any autopilot system. Because the main task of the autopilot is to hold a vehicle on a predefined course, two of its key technologies are the Global Positioning System (GPS) and microcontrollers. So before we delve into the software and mission planning, let's examine more closely one of these key technologies, the GPS.

GPS Receivers

GPS receivers are widely available now as handheld devices the size of mobile phones or even watches. For our project, we use only the receiver hardware of such devices. They are available as OEM modules that are the size of a dollar coin and smaller. All these modules have a serial interface that transmits a data stream in the form of the National Marine Electronics Association (NMEA) protocol. NMEA has established a communication standard for the connection of marine electronic devices like logs, digital compasses, sonar, and GPS receivers. The NMEA protocol consists of ASCII "sentences" that are output from the device periodically. Each of these sentences contains various navigation information (e.g., current position, speed over ground, heading, UTC time, etc.). For our autopilot system, we need only two pieces of information from the receiver: the actual position of the boat and the direction the boat is heading in.

Some words about the behavior of GPS receivers—a GPS receiver outputs valid heading information only when it is moved with a minimum speed of about 1.5mph (3km/h). When the boat is at a standstill, or moving very slowly, the heading information of the GPS receiver is not reliable and the boat may move in the wrong direction. The behavior of different GPS receiver modules at a standstill is different, and usually the way each module exactly behaves has to be reverse-engineered. The EM406, which I use, performs well at low speeds (above 3km/h) and outputs random heading information when at a standstill. For that purpose, I let the boat go off after launch for some five seconds without any GPS control. After that time, the GPS receiver will output reliable heading data that can be fed into the autopilot software. Another good thing to know is that GPS receivers do not immediately output valid navigation data. There are several modes the receivers perform when powered up. Mainly, two modes are important to know:

  • Cold start: This normally happens when you switch on your device after long periods without power. In this mode, modern receivers (like the EM406) need some minutes to get their first satellite fix. So don't worry if the autopilot will not start immediately after power-on.

  • Warm start: This happens when the receiver is already powered up and had a valid fix some hours ago. In this case, it takes some seconds to some minutes to get a valid fix. The EM406 has a capacitor on board, which holds up the internal clock oscillator and the so-called ephemeris information of the satellites for about one week.

The time for a warm start cannot be predicted, but usually one simple rule applies: the longer the time the module was powered off, the longer it takes to get the first fix.

Consider also that whenever you are testing the receiver, you should have a clear view of the sky. Indoors it will usually not work.

Some receivers have an LED on board that indicates the state of the receiver. On the EM406, the LED is on when the receiver is waiting for a valid fix, and it starts blinking when valid navigation data is available.

Now on to the software part of our autopilot system.

The Software

The software was developed using the Arduino IDE. You should be familiar with the basics of the C programming language and with Arduino if you want to make your own modifications to the software.

In short, the software on the ArduPilot microprocessor is designed to perform the following tasks:

  1. Initialize the hardware; wait until the GPS module has a valid fix, and then start the motor

  2. Get the actual position and heading information from the GPS

  3. Calculate the distance and bearing to the next waypoint

  4. Compare the actual heading with the bearing and feed the difference to the rudder servo (this is a very simplified view of the PID algorithm, but mainly, that is it)

  5. When the actual position is close to (within range of) the first waypoint, switch to the next waypoint

  6. If/when the last waypoint is reached, stop the motor; if not, proceed with step 2

You can download the software from http://code.google.com/p/roboboat/downloads/list or Apress.com.

There you will find a file called AP_RoboBoat.zip. If you unzip this file, you will get all the source code files for the autopilot that runs the RoboBoat. The software is separated into six modules (Arduino tabs) and two header files. According to Arduino conventions, these files must be placed into a folder that has the same name as the main program. In this case, copy it into a folder called AP_RoboBoat. Double-click the file ending with .pde. The Arduino IDE will open and you can proceed.

In the following sections, I will briefly describe the function of the modules. These modules make up the entire autopilot software system. If you have built your boat and propulsion assembly as described in the previous sections, there should be no further modifications necessary.

AP_RoboBoat Module

This tab contains the declaration of global variables and the two Arduino functions setup() and loop() (Listing 9-1). The function setup() does all the initialization of the hardware peripherals and waits until a first fix is available from the GPS. The function loop() contains the main program code and does all the navigation.

Example 9.1. AP_RoboBoat.pde

/* By Chris Anderson, Jordi Munoz, modified by Harald Molle for use on model boats */
/* Nov/27/2010
/* Version 1.1 */
/* Released under an Apache 2.0 open source license*/
/* Project home page is at DIYdrones.com (and ArduPilot.com)
/* We hope you improve the code and share it with us at DIY Drones!*/


#include "defines.h"
#include "waypoints.h"

// Global variables definition
int waypoints;  // waypoint counter

unsigned int integrator_reset_counter = 0;  // counter variable (in seconds) for the
Integrator holdoff-time after a waypoint switch

byte current_wp = 0; //This variable stores the actual waypoint we are trying to reach.

int wp_bearing = 0; //Bearing to the current waypoint (in degrees)
unsigned int wp_distance = 0; // Distance to the current waypoint (in meters)

//GPS obtained information
float lat = 0; //Current Latitude
float lon = 0; //Current Longitude
unsigned long time; // curent UTC time
float ground_speed = 0;   // Speed over ground
int  course = 0;          // Course over ground
int alt = 0;              //Altitude above sea
// Flag variables
byte jumplock_wp = 0; // When switching waypoints this lock will allow only one transition.
byte gps_new_data_flag = 0; // A simple flag to know when we've got new gps data.

// rudder setpoint variable, holds the calculated value for the rudder servo
int rudder_setpoint = 0;

byte fix_position = 0; // Flag variable for valid gps position

// Arduino Startup, entry point after power-on
void setup()
{

  init_ardupilot(); // Initialize the hardware specific peripherals

  waypoints = sizeof(wps) / sizeof(LONLAT); // calculate the number of waypoints

  Init_servo(); //Initalize the servos, see "Servo_Control" tab.

  test_rudder(); //Just move the servo to see that there is something living
  bldc_arm_throttle();  // Initialize the BLDC controller

  print_header(); //print the header line on the debug channel

  delay(500);  // wait until UART Tx Buffer is surely purged

  init_startup_parameters();  // Wait for first GPS Fix

  test_rudder();   // Move rudder-servo to see that the launch-time is close

  bldc_start_throttle(); // start the motor

  delay (5000); // go the first five seconds without GPS control to get the direction vector
stabilized

  init_startup_parameters(); // re-synchronize GPS

}

// Program main loop starts here

// Arduino main loop
void loop()
{


  gps_parse_nmea(); // parse incoming NMEA Messages from GPS Module and store relevant data in
global variables

  if((gps_new_data_flag & 0x01) == 0x01)    //Checking new GPS "GPRMC" data flag in position
  {
digitalWrite(YELLOW_LED, HIGH); // pulse the yellow LED to indicate a received GPS
sentence
    gps_new_data_flag &= (∼0x01); //Clearing new data flag...
    rudder_control(); // Control function for steering the course to next waypoint
    if (integrator_reset_counter++ < WP_TIMEOUT)    // Force I and D part to zero for
WP_TIMEOUT seconds after each waypoint switch
     reset_PIDs();

    send_to_ground();   /*Print values on datalogger, if attached, just for debugging*/
  } // end if gps_new_data...


  // Ensure that the autopilot will jump ONLY ONE waypoint

    if((wp_distance < WP_RADIUS) && (jumplock_wp == 0x00)) //Checking if the waypoint distance
is less than WP_RADIUS m, and check if the lock is open
    {
      current_wp++; //Switch the waypoint
      jumplock_wp = 0x01; //Lock the waypoint switcher.
      integrator_reset_counter = 0;

      if(current_wp >= waypoints)    // Check if we've passed all the waypoints, if yes stop
motor
       finish_mission();
     } // end if wp_distance...

  digitalWrite(YELLOW_LED,LOW);  //Turning off the status LED
} // end loop ()

Debug Module

This tab contains some functions for system test and integration (Listing 9-2). If you have a serial datalogger, you can use it to record the data of the PID control loop that is output every second.

Example 9.2. Debug.pde

// PID Debug Variables
float pid_p;
float pid_i;
float pid_d;
float pid_dt;
int dbg_pid_error;

// Debugging output, sends the value of internal variables to the datalogger every second
// Floating point values are multiplied and converted to integers to get it through the
Serial.print function
void send_to_ground(void)
{
Serial.print(course);
    Serial.print("	");

    Serial.print((int)wp_bearing);
    Serial.print("	");

    Serial.print(dbg_pid_error);
    Serial.print("	");

    Serial.print(wp_distance);
    Serial.print("	");

    Serial.print(time);
    Serial.print("	");

    Serial.print((int)rudder_setpoint);
    Serial.print("	");

    Serial.print((int)current_wp);
    Serial.print("	");

    Serial.print((int)pid_p);
    Serial.print("	");

    Serial.print((int)pid_i);
    Serial.print("	");

    Serial.print((int)pid_d);
    Serial.print("	");

    ground_speed *= 18.0; // Scale miles/h to km/h * 10
    Serial.print((int)ground_speed);
    Serial.print("	");

    Serial.print(alt);

    Serial.println();

}
// Debugging output, sends the value of internal variables to the datalogger once on startup
// Floating point values are multiplied and converted to integers to get it through the
Serial.print function
void  print_header(void)
{
 // Header for the System constants
  Serial.println("KP_HEADING		 KI_HEADING		 KD_HEADING		 INTEGRATOR_MAX		 RAM");
  delay(250);
  Serial.print ((int)(KP_HEADING * 100));
  Serial.print("		");
  Serial.print ((int)(KI_HEADING * 100));
  Serial.print("		");
Serial.print ((int)(KD_HEADING * 100));
  Serial.print("		");
  Serial.print ((int)(INTEGRATOR_LIMIT));
  Serial.print("		");
  Serial.println( ram_info() );
  delay(250);

  // header for the debugging variables
  Serial.println("Act	 Setp	 err	 Dist	 Time	 Rudd	 WP	 pid_p	 pid_i	 pid_d	 speed	
alt");
  delay (250);
}

// function to calculate the remaining amount of RAM in Bytes
// Check always, if you have changed the Waypoint array (see the Header of the debug-output)
int ram_info()
{
  uint8_t *heapptr;
  uint8_t *stackptr;

  stackptr = (uint8_t *)malloc(4);   // use stackptr temporarily
  heapptr = stackptr;               // save value of heap pointer
  free(stackptr);                 // free up the memory again (sets stackptr to 0)
  stackptr =  (uint8_t *)(SP);   // save value of stack pointer

  return ((int) stackptr - (int) heapptr);
}

Init Module

This tab contains all initialization functions for the hardware (Listing 9-3).

Example 9.3. Init.pde

void init_ardupilot(void)
{
  gps_init_baudrate();
  Serial.begin(9600);

  //Declaring pins

  pinMode(5,INPUT); // Mode pin (not used)
  pinMode(11,OUTPUT); // Simulator Output pin (not used)
  pinMode(MUX_PIN,OUTPUT);  //MUX pin, applies only to modified Hardware !
  pinMode(BLUE_LED,OUTPUT); // LOCK LED pin in ardupilot board, indicates valid GPS data
  pinMode(YELLOW_LED,OUTPUT);// Status LED, blinks, when valid satellite fix data is received
  pinMode(SERVO1_IN_PIN,INPUT); // Throttle input from RC Rx (only used for RC control)
pinMode(SERVO2_IN_PIN,INPUT); // Rudder input from RC Rx  (only used for RC control)

#ifdef RADIO_CONTROL
  init_RC_control();         // Initialize Radio control
#endif

  switch_to_ardupilot();     // default servo control by Ardupilot
}

void init_startup_parameters(void)
{
  //yeah a do-while loop, checks over and over again until we have valid GPS position and lat
is diferent from zero.
  //I re-verify the Lat because sometimes fails and sets home lat as zero. This way never goes
wrong..
  do
  {
    gps_parse_nmea(); //Reading and parsing GPS data
  }
  while(((fix_position < 0x01) || (lat == 0)));

  //Another verification
  gps_new_data_flag=0;

  do
  {
    gps_parse_nmea(); //Reading and parsing GPS data
  }
  while((gps_new_data_flag&0x01 != 0x01) & (gps_new_data_flag&0x02 != 0x02));
  rudder_control(); //I've put this here because i need to calculate the distance to the next
waypoint, otherwise it will start at waypoint 2.
}

Navigation Module

This tab is one of the most important of the autopilot (Listing 9-4). It contains the so called "NMEA-parser," which decodes the data from the GPS module and stores it in global variables. The tab also contains the functions that calculate the bearing angle and the distance to the next waypoint.

Example 9.4. Navigation.pde

// Variables used by the NMEA Parser
char buffer[90]; //Serial buffer to catch GPS data
/*GPS Pointers*/
char *token;
char *search = ",";
char *brkb, *pEnd;
/*************************************************************************
 * This functions parses the NMEA strings...
 * Pretty complex but never fails and works well with all GPS modules and baud speeds.. :-)
 * Just change the Serial.begin() value in the first tab for higher baud speeds
 *************************************************************************/

void gps_parse_nmea(void)
{
  const char head_rmc[]="GPRMC"; //GPS NMEA header to look for
  const char head_gga[]="GPGGA"; //GPS NMEA header to look for

  static byte unlock=1; //some kind of event flag
  static byte checksum=0; //the checksum generated
  static byte checksum_received=0; //Checksum received
  static byte counter=0; //general counter

  //Temporary variables for some tasks, specially used in the GPS parsing part
  unsigned long temp=0;
  unsigned long temp2=0;
  unsigned long temp3=0;


  while(Serial.available() > 0)
  {
    if(unlock==0)
    {
      buffer[0]=Serial.read();//puts a byte in the buffer

      if(buffer[0]=='$')//Verify if is the preamble $
      {
        unlock=1;
      }
    }
    /*************************************************/
    else
    {
      buffer[counter]=Serial.read();


      if(buffer[counter]==0x0A)//Looks for F
      {

        unlock=0;

        if (strncmp (buffer, head_rmc, 5) == 0)   // $GPRMC parsing starts here
        {

          /*Generating and parsing received checksum, */
          for(int x=0; x<100; x++)
          {
            if(buffer[x]=='*')
{
              checksum_received=strtol(&buffer[x+1],NULL,16);//Parsing received checksum...
              break;
            }
            else
            {
              checksum ^= buffer[x]; //XOR the received data...
            }
          }

          if(checksum_received == checksum)//Checking checksum
          {
            /* Token will point to the data between comma "'", returns the data in the order
received */
            /*THE GPRMC order is: UTC, UTC status, Lat, N/S indicator, Lon, E/W indicator,
speed, course, date, mode, checksum*/
            token = strtok_r(buffer, search, &brkb); //Contains the header GPRMC, not used

            token = strtok_r(NULL, search, &brkb); //UTC Time, not used
            time = atol (token);
            token = strtok_r(NULL, search, &brkb); //Valid UTC data? maybe not used...


            //Longitude in degrees, decimal minutes. (ej. 4750.1234 degrees decimal minutes =
47.835390 decimal degrees)
            //Where 47 are degrees and 50 the minutes and .1234 the decimals of the minutes.
            //To convert to decimal degrees, divide the minutes by 60 (including decimals),
            //Example: "50.1234/60=.835390", then add the degrees, ex: "47+.835390=47.835390"
decimal degrees
            token = strtok_r(NULL, search, &brkb); //Contains Latitude in degrees decimal
minutes...

            // Serial.println(token);

            //taking only degrees, and minutes without decimals,
            //strtol stop parsing till reach the decimal point "."  result example 4750,
eliminates .1234
            temp = strtol (token, &pEnd, 10);

            //takes only the decimals of the minutes
            //result example 1234.
            temp2 = strtol (pEnd + 1, NULL, 10);

            //joining degrees, minutes, and the decimals of minute, now without the point...
            //Before was 4750.1234, now the result example is 47501234...
            temp3 = (temp * 10000) + (temp2);


            //modulo to leave only the decimal minutes, eliminating only the degrees..
            //Before was 47501234, the result example is 501234.
            temp3 = temp3 % 1000000;
//Dividing to obtain only the degrees, before was 4750

            //The result example is 47 (4750/100=47)
            temp /= 100;

            //Joining everything and converting to float variable...
            //First i convert the decimal minutes to degrees decimals stored in "temp3",
example: 501234/600000= .835390
            //Then i add the degrees stored in "temp" and add the result from the first step,
example 47+.835390=47.835390
            //The result is stored in "lat" variable...
            lat=temp + ( (float)temp3 / 600000 );


            token = strtok_r(NULL, search, &brkb); //lat, north or south?
            //If the char is equal to S (south), multiply the result by −1..
            if(*token == 'S')
            {
              lat = lat * −1;
            }

            //This the same procedure use in lat, but now for Lon....
            token = strtok_r(NULL, search, &brkb);

            // Serial.println(token);

            temp = strtol (token, &pEnd, 10);
            temp2 = strtol (pEnd + 1, NULL, 10);
            temp3 = (temp * 10000) + (temp2);
            temp3 = temp3 % 1000000;
            temp /= 100;
            lon=temp + ((float)temp3 / 600000);

            token = strtok_r(NULL, search, &brkb); //lon, east or west?
            if(*token == 'W')
            {
              lon=lon * −1;
            }

            token = strtok_r(NULL, search, &brkb); //Speed overground?
            ground_speed = atof(token);

            token = strtok_r(NULL, search, &brkb); //Course?
            course= atoi(token);

            gps_new_data_flag |= 0x01; //Update the flag to indicate the new data has arrived.

            jumplock_wp=0x00;//clearing waypoint lock..

          }
          checksum=0;
        } //End of the GPRMC parsing
if (strncmp (buffer,head_gga,5) == 0)  // $GPGGA parsing starts here
        {
          /*Generating and parsing received checksum, */
          for(int x=0; x<100; x++)
          {
            if(buffer[x] == '*')
            {
              checksum_received = strtol(&buffer[x+1], NULL, 16);//Parsing received
checksum...
              break;
            }
            else
            {
              checksum ^= buffer[x]; //XOR the received data...
            }
          }

          if(checksum_received == checksum)//Checking checksum
          {
            token = strtok_r(buffer, search, &brkb);//GPGGA header, not used anymore
            token = strtok_r(NULL, search, &brkb);//UTC, not used!!
            token = strtok_r(NULL, search, &brkb);//lat, not used!!
            token = strtok_r(NULL, search, &brkb);//north/south, nope...
            token = strtok_r(NULL, search, &brkb);//lon, not used!!
            token = strtok_r(NULL, search, &brkb);//wets/east, nope
            token = strtok_r(NULL, search, &brkb);//Position fix, used!!
            fix_position = atoi(token);
            token = strtok_r(NULL, search, &brkb); //sats in use!! Nein...
            token = strtok_r(NULL, search, &brkb);//HDOP, not needed
            token = strtok_r(NULL, search, &brkb);//ALTITUDE, is the only meaning of this
string.. in meters of course.
            alt = atoi(token);
            if(alt < 0)
            {
              alt = 0;
            }

            if(fix_position >= 0x01)
              digitalWrite(BLUE_LED,HIGH); //Status LED...
            else
             digitalWrite(BLUE_LED,LOW);

            gps_new_data_flag |= 0x02; //Update the flag to indicate the new data has arrived.
          }
          checksum=0; //Restarting the checksum
        } // end of $GPGGA parsing

        for(int a=0; a<=counter; a++)//restarting the buffer
        {
          buffer[a]=0;
        }
counter=0; //Restarting the counter
      }
      else
      {
        counter++; //Incrementing counter
      }
    }
  }

}
/*************************************************************************
 * //Function to calculate the course between two waypoints
 * //I'm using the real formulas--no lookup table fakes!
 *************************************************************************/
int get_gps_course(float flat1, float flon1, float flat2, float flon2)
{
  float calc;
  float bear_calc;

  float x = 69.1 * (flat2 - flat1);
  float y = 69.1 * (flon2 - flon1) * cos(flat1/57.3);

  calc=atan2(y,x);

  bear_calc= degrees(calc);

  if(bear_calc<=1){
    bear_calc=360+bear_calc;
  }
  return bear_calc;
}


/*************************************************************************
 * //Function to calculate the distance between two waypoints
 * //I'm using the real formulas
 *************************************************************************/
unsigned int get_gps_dist(float flat1, float flon1, float flat2, float flon2)
{

    float x = 69.1 * (flat2 - flat1);
    float y = 69.1 * (flon2 - flon1) * cos(flat1/57.3);

    return (float)sqrt((float)(x*x) + (float)(y*y))*1609.344;
}

/***************************************************************************/
//Computes heading the error, and choose the shortest way to reach the desired heading
/***************************************************************************/
int compass_error(int PID_set_Point, int PID_current_Point)
{
   float PID_error=0;//Temporary variable
if(fabs(PID_set_Point-PID_current_Point) > 180)
    {
               if(PID_set_Point-PID_current_Point < −180)
               {
                 PID_error=(PID_set_Point+360)-PID_current_Point;
               }
               else
               {
                 PID_error=(PID_set_Point-360)-PID_current_Point;
               }
    }
    else
    {
      PID_error=PID_set_Point-PID_current_Point;
    }

    return PID_error;
}

// This function stops all activity and will never return
// This is the end...
void finish_mission(void)
{
  bldc_stop_throttle();

#ifdef RADIO_CONTROL
   switch_to_radio(); // Give control back to Radio
#endif

  while (1)     // loop forever, if timeout reached (and start to swim and recover the boat)
  {
    digitalWrite(YELLOW_LED,LOW); // Fast flashing Yellow LED to indicate arrival
    delay(100);
    digitalWrite(YELLOW_LED,HIGH);
    delay(100);
   }
}


/*************************************************************************
 * rudder Control, reads gps info, calculates navigation, executes PID and sends values to the
servo..
 *************************************************************************/
void rudder_control(void)
{

  wp_bearing=get_gps_course(lat, lon, wps[current_wp].lat, wps[current_wp].lon);//Calculating
Bearing, this function is located in the GPS_Navigation tab..

  wp_distance = get_gps_dist(lat, lon, wps[current_wp].lat, wps[current_wp].lon);
//Calculating Distance, this function is located in the GPS_Navigation tab..
rudder_setpoint = MIDDLE_RUDDER+PID_heading(compass_error(wp_bearing, course)); //Central
Position + PID(compass_error(desired course, current course)).

  pulse_servo_rudder((long)rudder_setpoint);  //Sending values to servo, 90 degrees is central
position.

}



// This function switches the EM406 into 9600 Baud
// Normally, the EM406 defaults to NMEA and 4800 Baud after long power-OFF times

void gps_init_baudrate(void)
{
    Serial.begin(4800); // Always try in 4800 Baud first.
    delay(100);
    Serial.println("$PSRF100,1,9600,8,1,0*0D"); //  command to switch SIRFIII to NMEA, 9600,
8, N, 1
    delay(100);
    Serial.begin(9600);  // switch finally back to 9600 Baud
}

PID_control Module

This tab contains the function that does the control loop for the straight line navigation (Listing 9-5). This is one of the most important modules, because it uses a PID algorithm that is implemented in a very simple manner. The behavior of the PID loop is controlled by constants that are described in the section "The PID Constants."

Example 9.5. PID_Control.pde

//PID loop variables
int heading_previous_error;
float heading_I = 0.0;             //Stores the result of the integrator

/****************************************************************************************
 * PID= P+I+D This function only works, when GPS with one second update is used.
 ***************************************************************/
int PID_heading(int PID_error)
{
  static float heading_D; //Stores the result of the derivator
  static float heading_output; //Stores the result of the PID loop
dbg_pid_error = PID_error; // deBug

  heading_I += (float)PID_error;

  heading_I = constrain(heading_I, -INTEGRATOR_LIMIT, INTEGRATOR_LIMIT); //Limit the PID
integrator...

  //Derivation part
  heading_D = ((float)PID_error - (float)heading_previous_error);

  heading_output = 0.0;//Clearing the variable.

  heading_output =  (KP_HEADING * (float)PID_error);  //Proportional part, is just the KP
constant * error.. and adding to the output
  pid_p =  (KP_HEADING * (float)PID_error);

  heading_output += (KI_HEADING * heading_I);  //Adding integrator result...
  pid_i = (KI_HEADING * heading_I);

  heading_output += (KD_HEADING * heading_D);// /Adding derivator result....
  pid_d = (KD_HEADING * heading_D);


  //Adds all the PID results and limit the output...
  heading_output = constrain(heading_output, (float)HEADING_MIN,
(float)HEADING_MAX);//limiting the output....

  heading_previous_error = PID_error;//Saving the actual error to use it later (in derivating
part)...

  //Now checking if the user have selected normal or reverse mode (servo)...
  if(REVERSE_RUDDER == 1)
  {
    return (int)(−1 * heading_output);
  }
  else
  {
    return (int)(heading_output);
  }
}

/*************************************************************************
 * Reset all the PIDs
 *************************************************************************/
void reset_PIDs(void)
{
  heading_previous_error = 0.0;
  heading_I = 0.0;
}

Servo_control Module

This tab contains the functions that output the pulses to the rudder servo and to the ESC for the motor (Listing 9-6). This module uses the pulse width modulation (PWM) unit of the AVR microcontroller that is used on all Arduino platforms.

Example 9.6. Servo_control.pde

/**************************************************************
* Configuring the PWM hadware... If you want to understand this,
*  you must read the Data Sheet of atmega168..
* The following functionsare optimized for speed. The Arduino Servo library
* may not work, because it consumes more processing time than this ones
***************************************************************/

void Init_servo(void)//This part will configure the PWM to control the servo 100% by hardware,
and not waste CPU time..
{
  digitalWrite(RUDDER,LOW);//Defining servo output pins
  pinMode(RUDDER,OUTPUT);
  digitalWrite(THROTTLE,LOW);
  pinMode(THROTTLE,OUTPUT);

  /*Timer 1 settings for fast PWM*/
  //Note: these strange strings that follow, like OCRI1A, are actually predefined Atmega168
registers.
  // We load the registers and the chip does the rest.

    //Remember the registers not declared here remain zero by default...
  TCCR1A =((1<<WGM11)|(1<<COM1B1)|(1<<COM1A1)); //Please read page 131 of DataSheet, we are
changing the registers settings of WGM11,COM1B1,COM1A1 to 1 thats all...
  TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS11); //Prescaler set to 8, that give us a resolution of
2us, read page 134 of data sheet
  OCR1A = 2000; //the period of servo 1, remember 2us resolution, 2000/2 = 1000us the pulse
period of the servo...
  OCR1B = 3000; //the period of servo 2, 3000/2=1500 us, more or less is the central
position...
  ICR1 = 40000; //50hz freq...Datasheet says  (system_freq/prescaler)/target frequency. So
(16000000hz/8)/50hz=40000,
  //must be 50hz because is the servo standard (every 20 ms, and 1hz = 1sec) 1000ms/20ms=50hz,
elementary school stuff...
}

/**************************************************************
 * Function to pulse the throttle servo
 ***************************************************************/
void pulse_servo_throttle(long angle)//Will convert the angle to the equivalent servo
position...
{
  //angle=constrain(angle,180,0);
OCR1A = ((angle * (MAX16_THROTTLE - MIN16_THROTTLE)) / 180L + MIN16_THROTTLE) * 2L;
}

/**************************************************************
 * Function to pulse the yaw/rudder servo...
 ***************************************************************/
void pulse_servo_rudder(long angle) // converts the angle to the equivalent servo position...
{
OCR1B = ((angle  *(MAX16_RUDDER - MIN16_RUDDER)) / 180L + MIN16_RUDDER) * 2L;
}


void bldc_arm_throttle(void) // "arm" the BLDC controller for the throttle
{
  delay(2000);
  bldc_stop_throttle();  // then switch to approx. zero, Servo controller armed
  delay(4000);
}


void bldc_start_throttle(void)  // brushless Motor (Multiplex controller)
{
  pulse_servo_throttle(MOTOR_SPEED); // set Motor speed
}

// function to stop the Motor   // brushless Motor (Multiplex controller)
void bldc_stop_throttle(void)
{
  pulse_servo_throttle(MOTOR_OFF);  // switch to approx. zero
}

void test_rudder(void)
{
  pulse_servo_rudder(MIDDLE_RUDDER + HEADING_MIN);
  delay(1500);
  pulse_servo_rudder(MIDDLE_RUDDER + HEADING_MAX);
  delay(1500);
  pulse_servo_rudder(MIDDLE_RUDDER);
  delay(1500);
}


// Module to control the ArduPilot via Radio Control (RC)
// You have to use an RC equipment, that supports a failsafe functionality
// e.g. if the Transmitter is switched OFF, on the receiver channel there should be
// "silence" (either HIGH or LOW level)
// I actually have tested this with a 2.4GHZ SPEKTRUM System.
// Analog Systems may always output some pulses due to erroneous received signals
// My cheap 27MHz Radio control did not work
// Please check thoroughly, before you make you first start!
// Function to Check, if there are pulses on the Rx rudder Input

// I took the rudder channel, because on the SPEKTRUM, the failsafe function
// outputs pulses on the throttle channel (default speed), when the Transmitter is OFF.
// This function checks for "silence" on the rudder channel.
// If there is silence, the Transmitter is switched OFF and the control should be given to
// the ArduPilot




// Return 0 if no pulse available (timeout > 25ms)
int check_radio(void)
{
  return (int) pulseIn(SERVO2_IN_PIN, HIGH, 25000); // Check, if there are pulses on the Rx
rudder Input;
}


// Function to switch the Multiplexer to the ArduPilot
void switch_to_ardupilot (void)
{
  digitalWrite(MUX_PIN,HIGH);  //  servos controlled by Ardupilot
}

// Function to switch the Multiplexer to the RC Receiver
void switch_to_radio (void)
{
  digitalWrite(MUX_PIN,LOW);  //  servos controlled by Radio control
}

Header Files

To adapt the software to your special needs, there are two additional header files that are called defines.h and waypoints.h. The defines.h file contains all constants that can be modified to your needs. I will describe them in depth next. The waypoints.h file contains a predefined constant array that holds the latitude and longitude coordinates of the path that the boat shall follow (this file is further discussed in the section "Mission Planning").

Note

Please refer to http://code.google.com/p/roboboat/downloads/list or Apress.com for the downloadable files.

Installing the Software

To load the software to the ArduPilot board, you will need the following:

  • A PC or Mac computer that runs the Arduino IDE; you can download the most recent version of this development environment from the site www.arduino.cc.

  • An FTDI breakout board from SparkFun with the USB cable

  • A power supply for the ArduPilot board, a battery pack, and an ESC without a motor

  • A folder that contains the autopilot software; remember that the folder must have the same name as the main program.

After you have all the prerequisites ready, you can perform the following procedures:

  • Install the Arduino IDE.

  • Configure the Arduino IDE.

  • Compile and upload the code.

  • Customize the code (optional.

Installing the Arduino IDE

Prior to starting the Arduino IDE, you have to install the drivers for the USB to serial converter and then you have to tell Arduino which serial port it shall use to communicate with the board. The procedure for that is described in depth for the various operating systems on the "getting started" part of the Arduino site: http://arduino.cc/en/Guide/HomePage.

After double-clicking the AP_RoboBoat.pde file, the Arduino IDE should start up and you should see a screen that looks like Figure 9-39.

Screenshot of the Arduino IDE

Figure 9.39. Screenshot of the Arduino IDE

Configuring the Arduino IDE

After having successfully installed the Arduino IDE, you have to tell Arduino which type of board you are using. The ArduPilot uses a ATmega328 microprocessor on it, and the board comes close to the "Arduino Duemillanove Board." Please select Arduino Duemilanove w/ATmega328 in the Arduino IDE Tools menu, as shown in Figure 9-40.

Selecting the hardware

Figure 9.40. Selecting the hardware

Compiling and Uploading the Code

Now you should be ready to upload the code to the board. If you have already loaded the AP_RoboBoat into the Arduino IDE, you simply have to press the upload button and the IDE will begin to compile the source code and start the upload automatically (Figure 9-41). You can see this on the fast flashing red and green LEDs on the USB to serial converter (Figure 9-42).

The upload button in the Arduino IDE

Figure 9.41. The upload button in the Arduino IDE

Connecting the ArduPilot with the programming adapter

Figure 9.42. Connecting the ArduPilot with the programming adapter

If the upload was OK, you are ready to do the first tests with the boat, which are described in the "Integrating the System" section.

Customizing the Code

Normally, you do not have to make changes in the code itself, if you have done all steps according to the description in this chapter. However, if you want to customize the software to your needs, you have to modify the defines.h header file. I have put all relevant system constants into this file, and I will give a short description of some of the most important settings. In the following six sections, I'll describe all the relevant settings in this file.

Customizing the Rudder Control

There is one constant that controls the "polarity" of the rudder movements. If you have built the propulsion assembly as described in this book, there is no need to change this directive (the default value is 1):

#define REVERSE_RUDDER 1 // normal = 0 and reverse = 1

Another constant controls the middle position of the rudder servo:

#define MIDDLE_RUDDER 90  // central position of rudder servo in degrees, adjust for trim•
 corrections if necessary

With this constant, you can fine-adjust the middle position of the servo. If you are using fixed pushrods that cannot be adjusted, you can use this directive to adjust the middle position of your assembly. Keep in mind that this works only for small changes in the range of +− 10°. As a part of the initialization process, the software first moves the rudder to its maximum extents and then re-positions it to the middle position. So you can see where the middle position really is.

Customizing the PID Constants

The following constants are the most critical and control the stability of the boat:

#define KP_HEADING 2.0    // proportional part of PID control
#define KI_HEADING 0.07   // integrator part of PID control
#define KD_HEADING 0.00001 // derivator part of PID control (not used)

The software uses a proportional-integrator-derivator (PID) control-loop algorithm for the stabilization of the course. I will not go into depth about control-loop theory, but there are some things you should know: the KP_HEADING controls the "gain" of the difference between the setpoint and the actual course. If you increase this value, the boat will react more directly to changes in direction and is likely to oscillate. If you decrease this value, the curves will get longer and the boat will react more slowly to changes in direction. A value between 1.0 and 2.0 should be OK if you have constructed the boat as described earlier. The KI_HEADING controls the integrator part of the algorithm, and it will add long-term stability, offset correction, and precision to the course. Be very careful when modifying this parameter; values that are too high may result in recovery swimming!

The derivator part is normally used to react to fast changes, but this will not work for the type of boat we are using. What we are using for the boat is only the PI part; therefore the KD_HEADING value is set close to zero.

Customizing the Motor Speed

The following constant sets the speed for the propulsion motor. It heavily depends on the ESC/motor/propeller/battery combination that you are using. The value is in virtual "degrees" because we are using the same functions that control an RC servo that lies in the range of 0° to +180°. With the combination that I am actually using, the setting of 80 yields a thrust of about 500g.

#define MOTOR_SPEED 80  // around 5A with Roxxy Outrunner

Customizing the Waypoint Timeout

The following constant has to do with the optimization of the straight-line behavior of the ship. As mentioned earlier, we are using a PI control loop. This is not the whole truth. The integrator part can drift away when the boat has to perform a turn (usually when switching from one waypoint to another). And so it can take a while for the boat to recover a straight line. To optimize for that, the integrator part of the algorithm is forced to zero for some seconds after a waypoint switch, which in turn makes the control loop a simple proportional type for some seconds. With the actual setting, the integral part of the control loop (which gives the precision) starts about 50m after the waypoint. Note that if you have set your waypoints too close to each other, you will end up with a P-type controller.

#define WP_TIMEOUT 15 // Waypoint Timeout counter value in seconds

Customizing the Waypoint Radius

The following constant is necessary, because the measurement accuracy of the GPS and the overall precision of the boat is not absolutely exact. To avoid the boat circling endlessly around a waypoint, a switch to the next waypoint is actuated when the boat is in a perimeter of WP_RADIUS of the actual waypoint. I have used a value in the range of 10 to 20 meters, which will give good results.

#define WP_RADIUS 15  // Radius for waypoint-hit in m

Customizing the Rudder Extents

The following two constants define the maximum extend in degrees that the servo can travel around the MIDDLE_RUDDER setting. This setting also defines the minimum radius the boat can achieve on a turn.

#define   HEADING_MAX 60  // Servo max position in degrees
#define   HEADING_MIN −60 // Servo min position in degrees

Note

Normally there is no need to change these constants. You should first be familiar with the code if you want to change the settings for the optimization and customization of your system because any changes may lead to strange behavior of the boat!

The next big step is "mission planning," which is the part where you direct your boat where it should go and (hopefully) where it has to come back to.

Mission Planning

So far so good, but how do we tell the autopilot where to go?

The mission of the boat can be seen as a list of waypoints the boat tries to reach. A waypoint is represented in a WGS84 latitude/longitude format, which is common to most GPS receivers and geographical information systems like Google Earth. The latitude and longitude values are expressed in degrees. The latitude starts at the equator, goes up north to +90° at the north pole, and goes down south to the south pole to −90°. The longitude starts with 0° at Greenwich (which is a small town in the vicinity of London, England, where the royal observatory is placed), goes east with positive values up to +180°, and goes west with negative values to −180°. Usually, for navigation, the coordinates are expressed in degrees, minutes, and seconds. What we will use in the software is a floating point format, where the coordinates are represented in decimal degrees, which makes it easier to calculate with.

The waypoint coordinates are stored in a constant array that can be found in the waypoints.h file, which is part of the ArduPilot code. To plan a new mission, you have to simply copy the new waypoint coordinates to that array, recompile the code, and download it to the board.

Here is an example (found in the file waypoints.h) with three waypoints that represent a triangular course in a lake in southern Germany.

LONLAT wps[] =
{
10.021409,  48.350234,
10.020944,  48.350475,
10.021905,  48.350598  /* Home*/
};

In this example, the first value (10.021409) is the longitude of the first waypoint, the second value (48.350234) is the latitude of the first waypoint, the third value (10.020944) is the longitude of the second waypoint, and so on.

The last waypoint should be chosen very close to the shoreline of the lake. After reaching this waypoint, the software of the autopilot switches the motor off, and the boat will (hopefully) glide the last meters to the shoreline without propulsion.

After the startup of the code, the waypoints are copied into the RAM of the microcontroller. The ATmega328 has only 2KB of RAM, so the amount of waypoints that can be stored is limited. The two float values of one waypoint consume 8 bytes of RAM. I suggest not using more than 100 waypoints for a single mission (which is a lot).

Employing Google Earth for the Coordinates

There are many ways to get the coordinates of the mission. The one that I actually prefer is Google Earth. The use of this program is free; it runs on PC, Mac, and LINUX platforms, and the accuracy of the satellite images is usually in the range of a few meters. The program can be downloaded from www.google.com/earth.

To plan a mission with Google Earth, you simply have to create a path. This is done by clicking the path icon on the command bar (Figure 9-43). You can then add waypoints to the path by simply clicking the places you want to go (Figure 9-44).

Create path button in Google Earth

Figure 9.43. Create path button in Google Earth

A sample mission

Figure 9.44. A sample mission

If you have finished, give the path a name and store it as a .kml file. This is done by right-clicking the name of the created path on the side navigation bar of Google Earth and selecting "save location as". It is important to choose .kml as the file format, because this format is based on the XML language and can be easily edited with a text editor.

Next, you have to open the .kml file with a text editor of your choice. A typical .kml file will look like Figure 9-45.

Inside a .kml file

Figure 9.45. Inside a .kml file

The only thing that we need is the coordinates that are placed between the <coordinates> and the </coordinates> tags. Copy those coordinates and paste them into a new file. The coordinates are in the format longitude, latitude, altitude. The altitude is usually 0 and that value must be deleted. Format the values so that the lon/lat pairs are each in one line.

If you have finished your editing, the file should look like this:

10.02109518987307,48.35076100306986,
10.02003617743083,48.35030483570462,
10.02033731414647,48.34998321856332,
10.02158407280307,48.35026252663885

Google Earth creates a lot of positions after the decimal point. Arduino uses only five to six, but this is of no concern, because the compiler will format it to the appropriate floating point representation.

After you have finished the editing, you have to copy and paste it into the array that is defined in the waypoints.h tab.

Your result should then look like this:

LONLAT wps[] =
{
10.02109518987307,48.35076100306986,
10.02003617743083,48.35030483570462,
10.02033731414647,48.34998321856332,
10.02158407280307,48.35026252663885
};

After that step, you must recompile the code and upload it to the board—you only have to press the upload button, as already described earlier.

If all of the foregoing steps have been done, you can start the integration of all components, which will be described in the next chapter.

Putting It All Together

After having fabricated the hulls, the deck, and the propulsion assembly, and integrated the electronics with the software, the time has come to put all pieces of the whole system together. The professionals call this step the "system integration." Some advice in advance: What we are doing is a hobby project with RC model parts. Some of these parts are not toys and can harm people. Please be extremely careful when following these guidelines.

  • The battery pack should be handled with care. Do not shortcut the leads; this can happen when you have to solder the power connector to the cables that come out of the battery pack. LiPo batteries can deliver very high currents when shorted, and this may cause a fire or severe burns. When soldering, always isolate the copper leads with sticky tape. As already mentioned, the charging of the battery pack should always be done outdoors or in a controlled area. The charger must support cell balancing. Please carefully read the instructions that come with the charger.

  • The next source of trouble can be the motor. A BLDC motor is very powerful. Before connecting it to the ArduPilot board, be sure that you have secured all screws that hold the motor. If the motor starts vibrating, e.g., when one of the screws gets loose, the other screws will follow in fractions of a second. A drop of glue on the threads will be sufficient to avoid that problem. Also, for your first experience with the motor, make it without a propeller. After you have mounted the propeller, keep away from it! The blades are very sharp and can injure you very quickly.

  • When operating the whole propulsion assembly, be sure to have it fixed to something rigid like a table or the boat's deck.

  • The whole assembly will turn arbitrarily when you operate the electronics at a standstill. Keep things away from the turning point of the motor assembly. Secure the cables with a tie-wrap to the PVC tube. If the cables get into the propeller, this may ruin the motor (and the rest of your day).

  • When you operate the boat in the water, be sure that no one is in the water. According to Murphy's law, you can be sure that the boat will hit the one swimmer that is in the water. Believe me—I know what I am talking about! An assembly with around 3kg of weight has a very high impulse when running at 10km/h and crashing into a person. On land, this may only be painful; in the water, it can be lethal!

  • Ask the owner of the water whether you can let an unguided boat run there.

  • Add a main power switch between the battery back, and make it easily accessible. This may help to switch the whole system off very fast when something unexpected happens (and it surely will).

Note

I did not describe the housing of the electronics. It is up to you to find your own way to encapsulate the electronics and to make it splash-proof. I have used a lunchbox that can be found in most kitchens (be sure to inform the head of your household prior to drilling some holes in one of her (his) beloved Tupperware boxes). The ESC may get hot; put it in a vented place. I have coated mine with silicone rubber to make it waterproof and put it in "free air" outside of the lunchbox.

Integrating the System

Now let's walk through the integration step by step:

  1. Connect the hulls to the deck with four M5 screws, as already described in the section "Hull Assembly."

  2. Mount the propulsion assembly to the aft of the deck by four M5 screws. Use butterfly nuts for convenience. Do not forget to put the rectangular plywood washers under the butterfly nuts. Secure the cables from the motor so that they cannot interfere with the propeller. I have fixed the cables to the PVC tube with a tie-wrap.

  3. Connect the ESC and the servo cable to the ArduPilot board (Figure 9-46). The ground lead is usually black or brown and points to the edge of the PCB.

    Connection of the ESC to the ArduPilot board

    Figure 9.46. Connection of the ESC to the ArduPilot board

  4. Connect the GPS module to the ArduPilot board. Be sure to have the programming adapter disconnected, because the GPS shares the same port and if both are connected, it will not work.

  5. Connect the three motor cables to the ESC. If the motor turns in the wrong direction, simply swap two cables.

  6. Triple-verify all of the connections. If you have put everything together correctly, the wiring should look like Figure 9-47.

    The whole system together

    Figure 9.47. The whole system together

  7. Go outdoors and connect the battery pack. If it is connected correctly, the following should happen:

    • On the ArduPilot board, two red LEDs should be on.

    • The red LED on the GPS module should be on.

    • The servo should turn to the maximum extent and then return to the middle position.

    • After some minutes, the LED on the GPS module should start to blink, and at the same time the blue LED on the ArduPilot board should be on.

    • The motor should start some seconds later.

    • The servo should perform arbitrary movements.

  8. If all of the foregoing checks out, the system is now fully operative and you can safely turn off the power.

Ships Ahoy!

Now, it's time to put the boat into the water. There is always a thrill when doing this for the first time. To avoid unwanted side effects, here are some hints that may be helpful:

  • Have your swimsuit ready. If you own a pair of fins, take them with you. If you have a boat (I mean a real one, not an RC model) or you know somebody who owns one, the better. A neoprene suit may be helpful in cold water.

  • For the first start, I suggest attaching a fishing line with a length of at least 100m to the aft of the hulls and placing the waypoints some 50m away from the launchpoint. The fishing line may make the boat slower, but you can test if the boat finds its waypoints, and if something unexpected happens, you can pull the boat back without the need to swim. Let the spool of the fishing line flow free, and don't forget to attach the end of the line to the spool.

  • If you have a laptop computer, take it with you; you may need it to adjust the values of the PID controller. If you have planned your mission with Google Earth, be sure to have the region of your mission in the cache. With that, Google Earth also works without a connection to the Internet and you can do a new plan even on very remote sites.

Troubleshooting

If you have trouble getting it all to work, the following are a couple of basic troubleshooting guidelines. Additionally, there are some helpful hints on this project's web site: http://code.google.com/p/roboboat/.

Please have a look at this site to get updated with the last news and software revisions.

The Thrust of the Motor/Propeller Is Not Sufficient

This can have more than one reason. First check if the propeller is mounted the right way. If you are using a normal (non-reversed) one, the side with the markings of the propeller should point toward the motor's body (to the front of the boat). The motor should turn clockwise if viewed from behind, as shown in Figure 9-48.

The turning direction of the propeller

Figure 9.48. The turning direction of the propeller

Maybe the setting for the motorspeed is too low for your ESC/motor combination. Increase the value of the #define MOTOR_SPEED directive in the file defines.h.

The Motor Does Not Start

Each ESC is different. And each of them has a microcontroller with software on it. Most of them are programmable to adapt them to the needs of the user. Most of them have a failsafe function to avoid an inadvertent start of the motor.

I have added some code on the web site to check what is wrong. Usually, it has to do with the failsafe behavior of your ESC. First read the manual to see how it is implemented. Usually it works this way: put the throttle to a middle value and wait some seconds. Then put the throttle to a minimum value for some seconds—this will "arm" the ESC. You may hear some beeping code coming out of the motor windings. Then put the throttle to the desired value. If you have a remote control with a transmitter and receiver, connect the receiver to the ESC and test the settings with the transmitter. The functions that control the throttle can be found in the Servo_Control.pde tab of the software. Have a look at them and adjust the values to your needs.

Note that on the actual software, the motor starts only after the GPS has a valid fix.

Summary

I hope that all the information given here is sufficient for you to start your own boating project.

Remember to use this chapter as a guideline for your own ideas. There is no need to follow the descriptions verbatim; there are many roads that lead to Rome. If the materials that I used are not available, feel free to try other ones, make derivations if you like, experiment, and have fun.

For more information on this project, go to http://code.google.com/p/roboboat/.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset