Skip to main content
Erschienen in:
Buchtitelbild

Open Access 2014 | OriginalPaper | Buchkapitel

11. Assembling and Controlling a Robotic Arm

verfasst von : Manoel Carlos Ramon

Erschienen in: Intel® Galileo and Intel® Galileo Gen 2

Verlag: Apress

Aktivieren Sie unsere intelligente Suche, um passende Fachinhalte oder Patente zu finden.

search-config
loading …

Abstract

In 1979, the Robot Institute of America (RIA) classified the robot as a reprogrammable, multifunctional manipulator designed to move material, parts, tools, and specialized devices through variable programmed motions for the performance of a variety of tasks.
In 1979, the Robot Institute of America (RIA) classified the robot as a reprogrammable, multifunctional manipulator designed to move material, parts, tools, and specialized devices through variable programmed motions for the performance of a variety of tasks.
Not including the word “reprogrammable” mentioned by RIA, the first introduction of the robot was in 1495 by Leonardo da Vinci, with his medieval robotic knight. This robot could wave, sit, and move its arms and neck. Of course it was totally mechanic. A representation of this robot is shown in Figure 11-1 (part of Wikimedia Commons under “Creative Commons Attribution/Share-Alike License”).
In 1920, the Czech novelist Karel Capek created a science fiction play called “Rosumovi Univerzální Roboti” (“Rossum’s Universal Robots”), where humanoid robots were created to work for humans but then the robots decide to exterminate and extinguish the humans. The 1984 Arnold Schwarzenegger movie, Terminator, involves a similar story.
You will not build a full humanoid robot in this chapter but you will learn about robotic arm principles and design engineering. In the process, you’ll learn how to build a simple robotic arm.
You will also build a six-channel controller that can be designed using inexpensive components. You can integrate the arm into the Intel Galileo boards or an Edison Arduino kit.

An introduction to Robotic Arm Theory

A robot arm has a mechanical structure that alters its form using a group of electric motors that behave like servo motors, pneumatic, or hydraulic actuators. They attempt to reproduce movement similar to a human arm.
A common term that’s used when a robot arm is designed is the DOF (degrees of freedom ); it is related to roll, yaw, and pitch. Figure 11-2 shows a representation of these movements in 3D space.
For example, using you own arm and following Figure 11-2, try to reproduce the following movements:
  • Shoulder pitch
  • Shoulder roll
  • Arm yaw
  • Elbow pitch
  • Wrist pitch
  • Wrist yaw
  • Wrist roll
Your arm, as it turns out, has seven DOF; the wrist alone has an amazing three DOF.
There are several factors you must consider when constructing a robot arm, including the maximum load weight, the stall torques of each one of the servos, how much weight each servo must support related to its position in the arm, and the weight each frame that constitutes the arm.
In Chapter 4, you learned about servo motors, but not about stall torque, which is an important concept that you must understand. Stall torque helps determine which servos you need to use in your robotic arm.
When you order a servo motor, it comes with several items on its specification, including stall torque. Stall torque is usually measured in ounces per inch or kilograms per centimeter. To understand what this means, consider Figure 11-3.
In Figure 11-3, the length is represented by L and it’s the measurement of center of the servo’s shaft to the end of the arm. The letter M represents the quantity of mass (weight) attached to the end of the arm.
So, when you read that a servo supports a specific amount of Kg/cm or oz/inch, it means the servo hangs if you apply the weight mentioned in a 1-cm or 1-inch arm.
For example, suppose you have a servo with a stall torque of 100 oz/inch. That means when you use a 1-inch long arm, the servo will hang if you apply a weight equal to 100oz (M). In other words, the servo will not be able to rotate or it might rotate backwards.
Torque is force times length, but if you search for different servos on the Internet, you will realize that vendors only mention the mass, excluding the gravitational acceleration. A second point to be considered when the vendors provide such numbers is that the measurement is the worst-case scenario when the arm is holding weight in the horizontal position. This is explain further in a bit.
By definition, torque must consider theperpendicular length, as shown in Figure 11-4, and not the arm length as you see in the servo specification. This sometimes causes confusion.
The formula that defines torque can be expressed as follows:
T = F × L
T = m × g
So you can conclude:
T = F × L = (m × g) × L
Where T means torque, L is the perpendicular length, m is the mass, and g is the gravitational acceleration. Of course, the force (F) in this case never changes since the mass is constant and subjected to the same gravitational acceleration.
With this torque formula in mind, note in Figure 11-5 that the worst-case scenario is when the arm is stretched out horizontally because the perpendicular length is bigger.
Thus you can say the worst-case condition is:
T = F × L3
Now imagine the arm with this perpendicular length and a load, as shown in Figure 11-6.
With a weight identified as A and located L1 from the servo, you can consider (approximately) that arm weight occupies the center of the mass of L2. The torque must take into account the two weights (A and F) and the two distances (L1 and L2). The torque in this scenario is as follows:
T = A × L1 + F × L2
T = A × L1 + F × (L1/2)
However, a robotic arm is a set of servos joined together to create one arm. Consider the robotic arm in Figure 11-7.
Suppose you need to evaluate the torque in servo A1. You will have:
If the weights F1 and F2 are located in the center of the mass, then you can conclude:
Replacing the L4 and L2, you have:
It means, if you know the weight of each servo and the length of each part of your arm, it’s possible to evaluate the torque and determine which servos to use. In this example, you simply need to know L1, L3, and the weight of the servos and arm parts.
Note that each servo will have a different torque due to its location along the arm.
Another important point that you need to include in your calculation is the maximum load. For example, suppose the mechanical gripper of your arm is actuator A3 and you know its weight. You simply need to add the load weight to A3’s weight and make the calculations.

Using an Online Tool to Calculate Your Servos

The web site robotshop.com offers a very nice online tool that helps you calculate the servos that must be used in your arm. To use this tool, visit http://www.robotshop.com/blog/en/robot-arm-torque-calculator-9712 and enter the arm and servo data. They use a different notation than presented on this chapter, where L is the arm’s length, M is the weight in kg of each part of your arm, and A is the weight of your servos. Don’t forget to include the load max in the gripper servo, as explained previously.
This tool is very simple to use and saves a lot of time and possible mistakes during your evaluation. Figure 11-8 shows a screenshot of this tool.

Considerations About This Project

This project is recommended for Intel Galileo Gen 2. There are some issues when it’s applied using Intel Galileo or Intel Edison Arduino Kit.
As explained in Chapter 4, Intel Galileo does not offer a sufficient PWM signal resolution in that the PWM signal does not offer a good resolution. That means your arm’s movements might seem a little choppy due to the lack of precision in the PWM.
A similar issue occurs with the Intel Edison Arduino Kit. It offers only two PWM channels and this project requires six channels to run the robotic arm. However, if you own an Intel Edison with the Intel Edison Arduino toolkit, you can use the same sketches used in this chapter. Instead of using the Servo API you can use the SoftwareServo API. This API runs in the user space context and brings an emulation of PWM signal in the digital ports. The good thing is, you can use any digital port to connect your servos; however, considering it runs in the userspace context, small glitches in the servo might be observed because processes with higher priority run in the kernel context, which can impact the userspace performance.
If you decide to use Intel Edison on this project, you need to replace the objects called “Servo” with “SoftwareServo”. All methods are exactly the same.
This project runs smoothly without any issues in Intel Galileo Gen 2.

The Robotic Arm

Internet sites offer thousands of robot arm kits, instructions for building your own robot out of wood, 3D printing, scrap materials, and more.
This book uses a low-cost arm kit that has a good quality aluminum form. This kit is sold by elabpeers, which is located in California.
The arm offers five DOF and comes with a gripper that offers two DOF, totaling seven DOF. You can order only the mechanical parts or you can include the servos as well.
The whole kit, with servos in place, is shown in Figure 11-9; the equivalent parts of a human arm are marked.
The kit includes an arm with:
  • Four U-shaped long brackets
  • Three U-shaped brackets used for the base
  • One mechanical gripper
  • Six metallic servo wheels
  • Four cup bearings
  • Several screws and nuts (more than you need)
If you decide to get the whole kit, it includes:
  • Four servos MG996R that provide a stall torque of 9.4kg/cm @ 4.8V and 12kg/cm @ 6V
  • Two servos 5521MG with a stall torque of 17,25Kg/cm @ 4.8V and 20.32 Kg/cm @ 6V
  • Three servo extension wire cables
  • Servo accessories packages with rubber rings, horns, and fittings
Figure 11-10 shows the whole kit and all its components.
This configuration is enough to keep your servos running smoothly; however, if you have better servos you can order only the arm body with gripper for $68.
If your budget permits, I recommend replacing the MG996R servo with the HEXFLY servo.
The MG996R or MG995R servo are considered the worst available in the market. There are many clones of such servos in the market don’t match the promised specification. Using these servos to work the base, wrists, and gripper is fine, but the elbow and shoulder need something better. These servos can’t even handle the weight of the arm without a load in the gripper.
The other required components for building this arm are listed in Table 11-1.
Table 11-1.
Materials List for the Robotic Arm
Quantity
Description
1
Robotic arm kit with servos (only if you do not have the servos recommended; otherwise, order the armor kit).
4
Servo extension wire cables.
1
Piece of wood around 7 x 12 x 0.5 inches (at least).
1
C-clamp if you are using the arm body in a table.
4
M6-30mm hex-head cap screw with nuts and flat washers or equivalent.
3
Dupont jumper cables, male-to-male, or pieces of 1/4 watt wires.
1
Five-inch nose plier.
1
Screwdrivers for M3 and M4 screws.
The piece of wood is used to create a base for the arm body. If you’ll be using this arm over a table (mostly), it’s best to use a c-clamp in order to hold the arm to the table. You can find c-clamps for $2.26.
The 1/4 x 2 inch screws with nuts are used to hold the arm body to the piece of wood and the flat washers are used to help the horizontal equalization of the arm body in relation to the wood.
Note
The servos recommended in this chapter work in conjunction with the arm body. They are fast and strong enough to damage or scratch your table or any other surface, or even damage your Intel Galileo boards, your hand, or any other element in the reachable area of action. Make sure you and your Intel Galileo board are far from the robotic arm using the servo extensions. Use the c-clamp to hold the arm to the surface that sets the arm. If possible, protect your surface with cardboard, a rubber matt, or any other material.
The next section explains how to assemble the arm and put the servos in place.

Assembling the Robotic Arm

The assembly procedure of this arm takes around two hours when done without error. The kit has many screws divided in M3 8mm, M3 8mm flat, M3 10mm, and M4 10mm and it is very important to understand where each screw goes in order to guarantee the functionality of your arm body.
The usage of the screws is summarized as follows:
  • The M3 10mm screws are used in the bearings to connect the servo that will manipulate the gripper and the wrist.
  • The M4 10mm screws connect the servos with servo brackets, but if you are using the 5521MG servo then you need to use the M3 10mm screws. If you are using any other servo and M4 10mm is being difficult, try the M3 10mm screws.
  • The M3 8mm flat screws connect the servo wheel to the servo only.
  • The M3 8mm rounded screws connect the rest of arm parts—like the short U-shapes—to each other and the servo to the long U-shapes.
Figure 11-11 illustrates where the screws are placed. This is explained in more detail in the following pages.
Usually elabpeers sends more screws than necessary, so do not worry about the quantity of screws.
The following sections describe how to assemble the robotic arm.

Step 1: Preparing the Servos

You must first make sure all the servos are in place exactly 90 degrees. All you need to do is connect the servos according to Figure 4-2 (Chapter 4). Then run the program in Listing 11-1.
Listing 11-1. prepare_servos.ino
#include <Servo.h>
#define PIN 9
Servo prepareServo;
void setup() {
   // Attaching the servo to PIN
   prepareServo.attach(PIN);
}
void loop() {
  // moves the servo to 90 degrees
  prepareServo.write(90);
  delay(250);
}
The sketch is very simple and it defines an object called prepareServo as an instance of the Servo class and attaches to the pin 9 defined by PIN. In the loop, the servo then moves to 90 degrees. For more details regarding the Servo API, consult Chapter 4.
Using each of the six servos, connect to Intel Galileo and wait for all the servos to be moved to 90 degrees. This is the reference angle for assembling this arm body.
Connect the aluminum servo wheel to all the servos except the servo that will be used in the gripper. Try to make the servo wheel align to 90 degrees, as shown in Figure 11-12.
The next step is to assemble the base.

Step 2: Assembling the Base

Join the two U-shape bases using four M3 8mm screws and the respective nuts, as shown in Figure 11-13.
Fix a third u-shape perpendicular to the other two U-shapes, as shown in Figure 11-14. Use the M3 screws and nuts to do so.
Locate a bearing and a M3 10mm screw. Then insert the screw in the bearing, as shown in Figure 11-15.
Using a servo bracket shape, connect the bearing supported by the screw and fix it using the proper nut, as shown in Figure 11-16.
Next, connect the servo bracket with the bearing to the base assembled using the M3 8mm screws and nuts. See Figure 11-17
The next step is to set the servo motor and the long U-shape. The long U-shape will be connected to the bearing and to the servo wheel. You need the parts shown in Figure 11-18 (left) to complete this step. The way the long U-shape must be connected is shown in Figure 11-18 (right). This figure is only illustrative so you can see how the connection is made; the servo must be fixed first.
Connect the servo using the M4 8mm screws and connect the U-shape by first placing it in the bearing. Push it to the top of servo wheel, as shown in Figure 11-19.
Attach the servo wheel to the servo using a screen M3 8mm flat screw in the center of the wheel. Your base should be ready.
Assemble another servo bracket by connecting a new bearing with the corresponding screws, as shown in Figure 11-16. Figure 11-20 shows the elements being connected. Make sure the screws are not loose.
The next step is to assemble the shoulder.

Step 3: Assembling the Shoulder

To assemble the shoulder, you need two long U-shapes opposing each other, as shown in Figure 1-21 (left). Place both shapes as shown in Figure 11-21 (right) using M3 8mm screws and nuts.
Connect the servo you chose to be the shoulder to the servo bracket shown in Figure 11-20. Use M4 screws and place the two long shapes using M3 8mm screws and nuts, as shown in Figure 11-22.
The shoulder servo and the arm will look similar to Figure 11-23 if you’ve placed them correctly.
The next step is to assemble the elbow.

Step 4: Assembling the Elbow

To start, you need a servo bracket, a long U-shape, an L-shape, a bearing, one M3 10mm and four M3 8mm screws and nuts, as shown in Figure 11-24.
Place the bearing using M3 10mm screws and the right nut and then connect the L-shape using the M3 8mm screws and nuts, as shown in Figure 11-25.
Then connect the long U-shape to the L-shape, as shown in Figure 11-26.
Next, connect the servo designated to be the elbow using M4 screws and nuts, as shown in Figure 11-27.
Finally, connect the elbow to the arm you assembled in Step 3. Fix the servo with M3 8mm screws, as shown in Figure 11-28.
The next step is to assemble the wrist.

Step 5: Assembling the Wrist

The wrist has two movements—pitch and rotate—and they require two servos and consequently two servo brackets, as shown in Figure 11-29 (left). To assemble the wrist, you also need a bearing connected using M3 10mm screws and nuts. Place both servo brackets using four M3 8mm nuts, as shown in Figure 11-29 (right).
Then connect one of the servos in the inferior bracket using the M4 10mm screws and nuts. Fix the servo wheel to the elbow using M3 8mm screws, as shown in Figure 11-30 (left). Finally, add the second servo, fixing it with M4 10mm screws and nuts in the superior bracket, as shown in Figure 11-30 (right).
Now let’s assemble the gripper.

Step 6: Assembling the Mechanical Gripper

The gripper contains a very simple movement—it opens and closes—so only one servo is necessary. However, the assemble process is very laborious. First, you must connect the gripper to the wrist using two simple M3 8mm screws, as shown in Figure 11-31.
Then, using the servo accessories pack, pick four rubber rings as shown in Figure 11-32 (left) and place them in the holes of the servos, as shown in Figure 11-32 (right). Note that you don’t connect the servo wheel to the servo this time.
Add the servo using the M4 screws to the bottom of the gripper, as shown in Figure 11-33.
Fix a servo wheel to the bottom of the gripper gear, as shown in Figure 11-34 (left), using two M3 8mm screws, as shown in Figure 11-34 (right).
Finally, adjust the gripper gears by connecting them to each other. Keep the gripper opened around 0.8 inches (2 cm) and use the M3 8mm flat screw to place it in the center of the servo. Figure 11-35 shows the final gripper.
The robotic arm is ready at this point. Now you need to create a base to sustain the arm. That process is explained in Step 7.

Step 7: Assembling a Base

You need to set the robot arm in a base made of wood, acrylic, polycarbonate, metal, or any material you have available. This project used a small piece of wood. Using a pen or pencil, mark the center of the external holes in the aluminum base of the robot arm and use a 1/4 drill bit for the holes. It is also best to use a spade that’s a bit bigger than the drill bit; for example, a ¾ one, as shown in Figure 11-36 (left). The function of the spade bit is to avoid any bounce caused by bumps that will fix the robotic arm, as shown in Figure 11-36 (right).
There is no requirement regarding the type of screws, nuts, or washers you have to use, so if you have spare screws that can sustain the robotic arm, it’s fine to use what you have available. This project used M6-30mm hex-head cap screws with corresponding nuts and washers.
Insert the screws and connect the arm base to the wooden base, as shown in Figure 11-37. Make sure the screw is not bouncing the base; if it is, you need to use a smaller screw, add some washers, or cut your screws.
Figure 11-38 shows the robotic arm in the wooden base.
If your base is small, the robotic arm will likely fall during its movement or when the gripper is holding something. To solve this problem, I fixed the base to my desk using a c-clamp, as shown in Figure 11-38 (right). Note that I had to use the spade bit to remove a little bit of the wood. This created a perfect socket for the c-clamp that basically penetrated the wood and formed a perfect join.
You now just have to control the arm, which is explained in the next section.

Controlling the Robotic Arm

At this point you robot arm is ready to be controlled. To make this possible, you must be able to control the six servos. The board Intel Galileo Gen 2 contains exactly six PWM ports in the pins identified with a tilde (∼), as explained in Chapter 3, Figure 3-10. Thus, there are enough PWM ports to control the robotic arm. It is possible to write simple software to make the robotic arm execute pre-programmed movements, but the project would not be so interesting.
Consider these relevant aspects:
  • Power supply : During my tests I realized the servos can drain up to 2.2A for a load of 100 grams at 5V. That means Intel Galileo boards and Edison Arduino toolkit boards do not provide a current (source) that guarantees the functionality of all servos since the limit is 80mA.
  • Servos control : The servos must be controlled somehow. You should have at least five potentiometers and a simple button to open and close the gripper. If you use joysticks or thumbsticks, each axis (x and y) could control two servo channels, so three of them would be enough to control the whole arm.
  • Quantity of wires : Each servo requires three wires. including ground, power, and pulse. Considering there are six servos in the arm the quantity of wires goes to 18. Besides this, it is necessary to add wire expanders to guarantee the robotic arm will be far enough from Intel Galileo or Intel Edison and to avoid damaging your board. Considering you cannot use an Intel Galileo or Edison board to power the servos, as explained previously, you also need to consider more cables related to an external power supply. If you have a potentiometer or joysticks or thumbsticks to control the robotic arm, you need at least 15 wires. All these wires can lead to a considerable mess.
I decided to build a six-channel servo controller set on an universal soldering board to organize all my cables. I placed the thumbsticks and connector to receive the external power supply and 5V from Intel Galileo or Intel Edison boards designated to the thumbsticks. All for around $30.
And a specific API was created in order to use the thumbsticks in two different modes. This is explained further in subsequent sections.

Building a Servo Control Board

In order to build the servo’s control boards, you can create a very simple board control and then create a new API, as explained in the next sections.

The Hardware

The servo board control is a very simple design, yet it can control six servos using three thumbsticks.
Table 11-2 lists the materials necessary to build this board.
Table 11-2.
Materials List for the Servo Board Controller
Number
Description
3
Thumbsticks SKU 121340 from dealxtream.com or equivalent
3
1K Ohm 1/4W resistors
1
Single-sided prototyping board, 6x3 inches (universal board)
1
Three-pole terminal block
1
470uF electrolytic capacitor
1
Diode 5404 (or three 1N4007 or 1N4004 diodes)
2
40 header pins 2.54mm single row
6
Servo extension cables
1
Power supply 5 or 6 VDC with 3A (minimum)
12
M3 0.5 x 10mm screws with nuts
37
Dupont male-to-female cables
5
Hex or round M3 nylon spacers (standoff) with screws
1
Soldering iron with lead
Some
AWG23 0.5mm2 or breadboard cables with at least two different colors (30 inches is enough)
Some
18 AWG 1.5mm2 cables with at least two different colors (11 inches is enough)
It is important to understand the components that make up this board, and they are explained in the next sections.

The Thumbsticks

The main component in the material list that composes the servos control board is the thumbstick.
A thumbstick is an analogic stick that offers an analog variation through two internal potentiometers related to the central position of the stick. Each potentiometer refers to an axis, so two potentiometers give you x and y references. This kind of control stick offers much more precision in terms of movement when compared to the old joysticks from 80s, like the ones used on the Atari console system with simple contacts to right, left, up, and down only. Thumbsticks are used in stick controls like the ones in PlayStation and Xbox systems. They offer analogic readings that can be transformed into intensity and movement direction. The thumbstick chosen for this project has a button functionality if you press the thumbstick tower to down.
Interfacing with Intel Galileo or Edison is very simple and uses the analog headers to read the respective movements and a digital port to detect if the button was pressed. When you move the thumbstick, the corresponding potentiometer’s value changes, the ADC on Intel Galileo or Edison reads the voltage of each potentiometer and converts it to digital. It is possible to identify in which direction and with what intensity the thumbstick is being moved.
There are different thumbsticks available in the market, including SainSmart, Arrela, Paralax, Xima, and many others with different prices.
The thumbstick chosen for this project is robust and very affordable, costing only $2.78 each when you order three on dealxtream (see http://www.dx.com/s/121340 ).
Figure 11-39 (left) shows a picture of the thumbstick and its terminals, and Figure 11-39 (right) shows its schematics.
There are five terminals on this thumbstick—5V, GND, SW (a button click when you push the tower down), VRx (a variation on the x-axis), and VRy (a variation on the y-axis).
Then only problem with this thumbstick is a pull-up resistor of 10KOhms is required to enable the button functionality. It must be soldered manually, as shown in Figure 11-40, because this resistor is not part of the original stick. Some other vendors like SainSmart do not require this work, but are more expensive.
The project works with any thumbstick and you are free to choose your favorite.
The thumbsticks are powered by 5V provided by 5V and GND headers provided by Intel boards. This separates them from any source of noise that could come from servos, even when it’s filtered by the diode reducing wrong readings in the ADC.
When there is one thumbstick, the system is very easy and the APIs available on the Internet can handle it. However, when there are more than one, it is a little bit more complicated because each time you move one stick you also might affect the reading in the other stick. This is because the sticks share the same 5V, so the impedances of each thumbstick is impacted even if a single stick is moved. Figure 11-41 shows the circuit with three thumbsticks sharing the same power line and ground; imagine you move the first thumbstick to the left, thereby changing the reading VRx1. All the other VRxs readings will be affected because the equivalent impedance also changes for each VRxs and VRys and might cause “ghost” movements in the robot.
In order to solve this problem, you need to separate the thumbsticks and not power them using a single 5V source as proposed in Figure 11-42. In this case, the idea is to power the thumbstick multiplexing the entries TS1, TS2, and TS3 with digital pin on Intel Galileo or Edison headers. Thus the software changes TS1 to HIGH state by powering on the thumbstick briefly with 5V, reads the VRx1, VRy1, and SW pins then changes TS1 to LOW state again by disabling the thumbstick. This process repeats with thumbstick 2, with thumbstick 3, and the returns to thumbstick 1 again. This process is very fast and you will be able to use all thumbsticks at same time without any interference or “ghost” movements in the arm. It is the implementation the simple idea of mux using digital headers.
The next discussion explains issues related to the external power supply.

External Power Supply

The servos used in this project can drain a good amount of current compared to the 80mA offered by the regular headers of Intel Galileo or Intel Edison boards, since they can drain up to 2.2A.
Thus an external power supply of 5V or 6V and 3A must be included.
Note the thumbsticks also use 5V but at a very low current and are read through analog ports. Thus to avoid interference from external power supply to the readings of these thumbsticks, the design selects the 5V provided by Intel Galileo or Intel Edison boards.
Also make sure the external power lines are filtered for any noises that might come from servo motors. 470uF was enough during the tests.

Circuit Protection

Considering a 5 VDC with a capacity of 3A is used, it is a good idea to protect the circuit to avoid catastrophic results if you were to invert the polarity.
For this purpose, a simple diode protects the external power line is best. The 1N5404 supports up to 3A. If you do not have a 5404 diode, but you have an 1N4004 or 1N4007 available, you can replace 1N5404 with three diodes in parallel, because each one supports up to 1A, as shown in Figure 11-43.

Assembling the Board

Based on the considerations discussed here, a simple idea for the board diagram is shown in Figure 11-44.
The OUTPUT STICK terminals must be connected to analog headers (A0 to A5) and the button of each stick must be connected to a digital header.
The THUMBSTICK SELECTION (TS) must be connected to Intel Galileo or Edison digital headers that will multiplex the thumbsticks to avoid the problem with the impedances mentioned in previous sections.
The INPUT PWM signal is the PWM signal that will be driven by the PWM header on Intel Galileo or Edison while the INPUT SERVO terminals connect the servos directly.
The OUTPUT ALTERNATIVE 5V can be used to connect an external peripheral. This chapter uses this terminal when the you learn about the ground coffee gripper later.
And finally, there is a three-pole terminal block that receives the external power supply, ground, and 5V provided by Intel Galileo or Edison.
To understand how the servos board controller works, it is also necessary to understand how this board connects to the external peripherals, in other words, how it connects with the servos and with Intel Galileo or Edison. Figure 11-45 shows such connections.
You can arrange the components in the prototyping board as shown in Figure 11-46 (top view) and Figure 11-47 (bottom view).
Before you start to build the board, consider these minor mechanical recommendations:
  • The prototyping board is specified in the material list as 6x3 inches. If you use a small board (even if the thumbsticks fits), make sure the controllers will have space and the thumbsticks will not collide with each other when you move their towers. That’s why there should be a gap of at least 1.2 inches between each thumbstick.
  • Note in Figure 11-47 that five standoffs were used, four in the edges and one in the center. The reason for the central one is that, since you are using this board as a controller, you might rest your hands on the board. The central standoff will keep the board from warping given the weight of your hands. digikey.com sells different types of standoffs; the nylon ones are only around $0.23 each.
  • Be wise when you arrange the terminals and avoid wires passing over the board. This can impact the thumbstick’s usability.
Now that your hardware is ready to be tested, you must create the software and integrate it into a sketch, as explained in the next section.

The Software

Some APIs already provide thumbsticks that are available on the Internet. However, there are some challengeswith these kinds of thumbsticks:
  • There is more than one simple thumbstick to be controlled.
  • The thumbsticks must be calibrated.
  • The code should control the servo limits of each component in the robotic arm.
  • Considering there are six servos to be controlled, the control must be easy for the user.
The first challenge was explained in the section entitled, “The Hardware,” which discussed the digital header being used as a mux to select each thumbstick at a time. It’s easy to implement in terms of software because every time a function is called to read the thumbstick state, you simply need to supply 5V to the respective stick, read the state, and then disable the thumbstick.
The second challenge is calibrating the thumbsticks. There is no guarantee that all the thumbsticks received will have the same impedance and the same mechanical adjustments to the center position. Therefore, it’s necessary to make the software understand each thumbstick by identifying the value of the center position and the boundaries. This ensures proper conversion.
The third challenge is related to how the software programs each thumbstick so that it respects the mechanical limits that each servo must support. For example, it doesn’t make sense for the servo that controls the gripper to rotate between 0 to 180 degrees because the gripper operates with an opening that limits the servo from 70 to 130 degrees. On the other hand, the servo in the base can work between 0 to 180 degrees without problems.
The final challenge is that the servo must be easily controlled and this is addressed further on this section.
The next step is to present the API and test it.

An API for Servos Board Control

The API provides a simple interface that allows you to read the state of each thumbstick by retrieving values that respect the angle limits of the servos controlled by the thumbstick.
Each thumbstick can control two servos, one in the x-axis and the other in the y-axis.
So, the API must convert the analog reading on the x- and y-axis to the limits that you specify.
In addition to the limit control, it is also necessary to determine how the analog headers (VRx and VRy) and button pin connect to Intel Galileo or Edison, and determine which digital header will select the thumbstick (TS).
It is also interesting to operate the thumbstick in two different modes:
  • Absolute position: The servos will follow the thumbstick movement. So if you release the thumbstick, the servo will move to its initial position because the thumbstick automatically moves to the central position. You need to keep holding the sticks to move the servos that will follow the same movements.
  • Step in touch: The servos do not follow the stick, but move in the direction you point the thumbsticks.
Listing 11-2 shows the header file of the API with the methods necessary to implement such functionalities.
Listing 11-2. Thumbstick.h
/*
    This file is part of Thumstick API.
    Thumstick API is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    Thumstick API is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    You should have received a copy of the GNU General Public License
    along with Foobar.  If not, see < http://www.gnu.org/licenses/ >.
    by Manoel Carlos Ramon (manoel.ramon@gm ail.com)
    Nov 2014
    version 1.0
*/
#ifndef _THUMBSTICK_API
#define _THUMBSTICK_API
#include <Arduino.h>
#define NOT_CALIBRATED    22
#define MAX_FLOATING      10
#define STEP_X            5
#define STEP_Y            5
#define NUMBER_OF_SAMPLES 25
#define DEBUG 1
class Thumbstick {
  private:
  int stick_step = 100;
  int lastButtonState = HIGH;
  long lastDebounceTime = 0;   // the last time the output pin was toggled
  long debounceDelay = 400;    // adjust this value if necessary to avoid flickering
  int X_max = NOT_CALIBRATED;  // max X retrieved during calibration
  int Y_max = NOT_CALIBRATED;  // max Y retrieved during calibration
  int X_min = NOT_CALIBRATED;  // min X retrieved during calibration
  int Y_min = NOT_CALIBRATED;  // min Y retrieved during calibration
  int centerX = 0;
  int centerY = 0;
  int pinAnalogX;
  int pinAnalogY;
  int pinButton;
  int pin_select;
  bool centerCalibrated = false; // informs if the center was calibrated
  // 0 and 180 are the default angles values
  int servoX_MinAngle = 0;    // the minimum angle supported by servo attached to axis X
  int servoX_MaxAngle = 180;  // the maximum angle supported by servo attached to axis X
  int servoY_MinAngle = 0;    // the minimum angle supported by servo attached to axis Y
  int servoY_MaxAngle = 180;  // the maximum angle supported by servo attached to axis Y
  // number of samples to estabelish the center during the calibration
  int samplesOfrCenterXCounter = NUMBER_OF_SAMPLES, samplesOfrCenterYCounter = NUMBER_OF_SAMPLES;
  int lastAngleX = -1, lastAngleY = -1;
  // checks the button state considering the debounce
  int checkButtonState();
  public:
  enum {
     ANALOG_RAW,          // Only brings the raw values... for testing only
     ABSOLUTE_POSITION,   // The servos will move according joystick position
     STEPS_IN_TOUCH,       // The servos moves increases and decreases its angles
     INVALID_MOVE         // Just to mark the invalid boundaries
  } typedef MOVE_T;
  MOVE_T move_type = ANALOG_RAW;  // initial mode
  struct {
    int X;                // the current X angle
    int Y;                // the current Y angle
    int buttonPressed;    // True if thumbstick button is pressed
  } typedef JOYSTICK_XY_T;
  int  Xvalue;            // current X value read from analog port
  int  Yvalue;            // current Y value read from analog port
  int  Zvalue;            // current button state
  // Contructor
  //  _pin_select : which digital pin that selects the thumbstick during the multiplexation
  //  pinX        : which analog port reads the variation of X axis
  //  pinY        : which analog port reads the variation of Y axis
  //  _pinButton  : which digital pin reads the button state
  //  minX        : the minimum servo angle on axis X (default is 0)
  //  maxX        : the maximum servo angle on axis X (default is 180)
  //  minY        : the minimum servo angle on axis Y (default is 0)
  //  maxY        : the maximum servo angle on axis Y (default is 180)
  Thumbstick(int _pin_select, int pinX, int pinY, int _pinButton, int minX = 0, int maxX = 180, int minY = 0, int maxY = 180);
  // Destructor
  ∼Thumbstick();
  // init the pin mode (must be in setup() function)
  void initPins();
  // Performs the calibration
  void calibrate();
  // checks if the thumbstick was calibrated
  // Returns: TRUE if calibrated or FALSE if it is not.
  bool isCalibrated();
  // only for debug purposes
  void dumpCalibration();
  // informs if center was calibrated
  bool isCenterCalibrated() { return centerCalibrated; };
  // reads the current status of thumbstick
  // Parameter:
  //      MOVE_T format - the mode of reading
  // Returns:
  //      JOYSTICK_XY_T - the coordinates and button state
  JOYSTICK_XY_T read(MOVE_T format);
};
#endif
The implementation of each method is shown in Listing 11-3.
Listing 11-3. Thumbstick.cpp
/*
    This file is part of Thumstick API.
    Thumstick API is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    Thumstick API is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    You should have received a copy of the GNU General Public License
    along with Foobar.  If not, see < http://www.gnu.org/licenses/ >.
    by Manoel Carlos Ramon (manoel.ramon@gmail.com)
    Nov 2014
    version 1.0
*/
#include "Thumbstick.h"
  // Contructor
  //  _pin_select : which digital pin that selects the thumbstick during the multiplexation
  //  pinX        : which analog port reads the variation of X axis
  //  pinY        : which analog port reads the variation of Y axis
  //  _pinButton  : which digital pin reads the button state
  //  minX        : the minimum servo angle on axis X (default is 0)
  //  maxX        : the maximum servo angle on axis X (default is 180)
  //  minY        : the minimum servo angle on axis Y (default is 0)
  //  maxY        : the maximum servo angle on axis Y (default is 180)
  Thumbstick::Thumbstick(int _pin_select, int pinX, int pinY, int _pinButton, int minX, int maxX, int minY, int maxY)
  {
      pinAnalogX = pinX;
      pinAnalogY = pinY;
      pinButton  = _pinButton;
      pin_select = _pin_select;
      servoX_MinAngle = minX;
      servoX_MaxAngle = maxX;
      servoY_MinAngle = minY;
      servoY_MaxAngle = maxY;
  }
  // Destructor
  Thumbstick::∼Thumbstick()
  {
  }
  // init the pin mode (must be in setup() function)
  void Thumbstick::initPins()
  {
      pinMode(pinButton, INPUT);
      pinMode(pin_select, OUTPUT);
  }
  // Performs the calibration
  void Thumbstick::calibrate()
  {
       JOYSTICK_XY_T res = this->read(ANALOG_RAW);
       // the following logic tries to find the maximum and minimum
       // values for all axis. Such values will be used to identify
       // the limits of your thumbstick.
       if (res.X < X_min)
       {
            X_min = res.X;
       }
       if (res.X > X_max)
       {
            X_max = res.X;
       }
       if (res.Y < Y_min)
       {
            Y_min = res.Y;
       }
       if (res.Y > Y_max)
       {
            Y_max = res.Y;
       }
       // the center of each thumbstick must be found
       // considering the center is the first thing that calibration process search for
       // then some samples must be aquire until ADC turn stable.
       if ((centerX == 0) && (--samplesOfrCenterXCounter ==0))
       {
           centerX = res.X;
           Serial.print("centerX:");
           Serial.println(centerX);
       }
       if ((centerY == 0) && (--samplesOfrCenterYCounter == 0))
       {
           centerY = res.Y;
           Serial.print("centerY:");
           Serial.println(centerY);
       }
       // just to inform the centers were got
       if ((centerX !=0) && (centerY !=0))
       {
                centerCalibrated = true;
       }
       if ((lastAngleX == -1) && (centerX != 0) && (X_min != NOT_CALIBRATED) && (X_max != NOT_CALIBRATED) && (Y_max != NOT_CALIBRATED) && (Y_min != NOT_CALIBRATED))
       {
            lastAngleX =  constrain(map(centerX , X_min , X_max , servoX_MinAngle, servoX_MaxAngle), servoX_MinAngle, servoX_MaxAngle);
            lastAngleY =  constrain(map(centerY , Y_min , Y_max , servoY_MinAngle, servoY_MaxAngle), servoY_MinAngle, servoY_MaxAngle);
       }
  }
  // checks if the thumbstick was calibrated
  // Returns: TRUE if calibrated or FALSE if it is not.
  bool Thumbstick::isCalibrated()
  {
      bool result = false;
      if ((X_min != NOT_CALIBRATED) && (X_max != NOT_CALIBRATED) &&
          (Y_min != NOT_CALIBRATED) && (Y_max != NOT_CALIBRATED)) {
             result = true;
      }
     return result;
  }
  // only for debug purposes
  void Thumbstick::dumpCalibration()
  {
    if (DEBUG)
    {
        Serial.print("Xmax:");
        Serial.print(X_max);
        Serial.print("  Xmin:");
        Serial.print(X_min);
        Serial.print("  Ymax:");
        Serial.print(Y_max);
        Serial.print("  Ymin:");
        Serial.print(Y_min);
        Serial.print("  centerX:");
        Serial.print(centerX);
        Serial.print("  Angle centerX:");
        Serial.print(lastAngleX);
        Serial.print("  centerY:");
        Serial.print(centerY);
        Serial.print("  Angle centerY:");
        Serial.println(lastAngleY);
    }
  }
  // checks the button state considering the debounce
  int Thumbstick::checkButtonState()
  {
    // read the state of the switch into a local variable:
    int reading = digitalRead(this->pinButton);
     // check to see if you just pressed the button
     // (i.e. the input went from LOW to HIGH),  and you've waited
     // long enough since the last press to ignore any noise:
     // If the switch changed, due to noise or pressing:
     if (reading != lastButtonState) {
       // reset the debouncing timer
       lastDebounceTime = millis();
     }
     if ((millis() - lastDebounceTime) > debounceDelay) {
       // whatever the reading is at, it's been there for longer
        // if the button state has changed:
       if (reading != Zvalue) {
          Zvalue = reading;
       }
     }
     lastButtonState = reading;
     return lastButtonState;
  }
  // reads the current status of thumbstick
  // Parameter:
  //      MOVE_T format - the mode of reading
  // Returns:
  //      JOYSTICK_XY_T - the coordinates and button state
  Thumbstick::JOYSTICK_XY_T Thumbstick::read(MOVE_T format)
  {
       int stepX = 0, absoluteX, absoluteY, stepY = 0;
       Thumbstick::JOYSTICK_XY_T result = {0,0, false};
           // selecting the right thumbstick to provide 5V
       digitalWrite(pin_select, HIGH);
       delay(5);
       result.buttonPressed = false;
       if (checkButtonState() == LOW)
       {
          result.buttonPressed = true;
       }
       // reading the raw analog values
       Xvalue = analogRead(this->pinAnalogX);
       Yvalue = analogRead(this->pinAnalogY);
       switch (format)
       {
         case ANALOG_RAW:         // Only brings the raw values of the joystick potentiometers
            result.X = Xvalue;
            result.Y = Yvalue;
         break;
         case ABSOLUTE_POSITION:  // The servo will move according joystick position
            result.X = constrain(map(Xvalue , X_min , X_max , servoX_MinAngle, servoX_MaxAngle), servoX_MinAngle, servoX_MaxAngle);
            result.Y = constrain(map(Yvalue , Y_min , Y_max , servoY_MinAngle, servoY_MaxAngle), servoY_MinAngle, servoY_MaxAngle);
         break;
         case STEPS_IN_TOUCH:     // The joystick moves increases and decreases servos coordinates
            int x;
            int centerx;
            x = map(Xvalue , X_min , X_max , 0, 255);
            centerx = map(centerX , X_min , X_max , 0, 255);
            // checking if the reading is floating
            if (abs(x - centerx) > MAX_FLOATING)
            {
                 if (x > centerx) stepX = STEP_X;
                 if (x < centerx) stepX = -STEP_X;
            }
            // reading the last angle and adding result.X
            int y;
            int centery;
            y = map(Yvalue , Y_min , Y_max , 0, 255);
            centery = map(centerY , Y_min , Y_max , 0, 255);
            // checking if the reading is floating
            if (abs(y - centery) > MAX_FLOATING)
            {
                 if (y > centery) stepY = STEP_Y;
                 if (y < centery) stepY = -STEP_Y;
            }
            if (lastAngleX >= 0)
            {
                 lastAngleX += stepX;
                 lastAngleX = constrain(lastAngleX, servoX_MinAngle, servoX_MaxAngle);
                 result.X = lastAngleX;
            }
lastAngleY >=0)
            {
                 lastAngleY += stepY;
                 lastAngleY = constrain(lastAngleY, servoY_MinAngle+1, servoY_MaxAngle);
                 result.Y = lastAngleY;
            }
         break;
         default:
            if (DEBUG) Serial.println("Wrong mode chosen!");
         break;
       }
       // disable the thumbstick selection
       digitalWrite(pin_select, LOW);
       return result;
  }

Reviewing the Thumbstick API

The API includes a class called Thumbstick that determines how the thumbstick is connected to Intel Galileo or Edison and the limits of each servo. In other words, how are the thumbstick’s VRx and VRy terminals connected to the analog headers, how the button is connected to the digital ports, which pin on Intel Galileo or Edison will select the thumbstick (TS), and what the limits of the servos controlled by the x- and y-axis are.
A new type called MOVE_T defines the mode the thumbstick must work in; you can choose between ABSOLUTE_POSITION and STEPS_IN_TOUCH.
Another type called JOYSTICK_XY_T determines the position related to the X and Y axes and whether the button was pressed (buttonPressed). This type is specifically returned by the read() method, which receives MOVE_T as an input parameter and internally makes all the conversions to returns the angles in the correct mode.
Two other important public methods are calibrate(), which performs the calibration, and isCalibrate(), which checks if the thumbstick was calibrated properly.
The initPins() method must be added to the setup() function of the sketches to guarantee the digital and analog pins will be configured correctly.
Analyzing the read() method a little bit more, you will realize that the method selects the thumbstick in the beginning and disables it in the end of the method calling the digitalWrite() functions. Then it checks the button state by calling a private method called checkButtonState(), which checks the current button state using a debounce. It uses the same technique used in the project called “Moisture Sensor” described in Chapter 8. The rest of the code reads the analog values of x and y using analogRead() and, according to the mode passed through the variable format, makes the proper conversion. Remember that analogRead() converts the reading in a value between 0 to 23 (read Chapter 3 for more details), and the ABSOLUTE_POSITION or STEP_IN_TOUCH mode converts these values to the proper angles on each mode. Of course, the ADC is not perfect. Even with 12 bits and considering you built a decent prototyping board, a small amount of floating is expected; that tolerance is controlled by MAX_FLOATING.
Thumbstick::JOYSTICK_XY_T Thumbstick::read(MOVE_T format)
  {
    Thumbstick::JOYSTICK_XY_T result = {0,0, false};
...
...
...
       digitalWrite(pin_select, HIGH);
...
       if (checkButtonState() == LOW)
       {
          result.buttonPressed = true;
       }
...
       // reading the raw analog values
       Xvalue = analogRead(this->pinAnalogX);
       Yvalue = analogRead(this->pinAnalogY);
       switch (format)
       {
...
...
...
         case ABSOLUTE_POSITION:  // The servo will move according joystick position
            result.X = constrain(map(Xvalue , X_min , X_max , servoX_MinAngle, servoX_MaxAngle), servoX_MinAngle, servoX_MaxAngle);
            result.Y = constrain(map(Yvalue , Y_min , Y_max , servoY_MinAngle, servoY_MaxAngle), servoY_MinAngle, servoY_MaxAngle);
         break;
         case STEPS_IN_TOUCH:     // The joystick moves increases and decreases servos coordinates
            int x;
            int centerx;
            x = map(Xvalue , X_min , X_max , 0, 255);
            centerx = map(centerX , X_min , X_max , 0, 255);
            // checking if the reading is floating
            if (abs(x - centerx) > MAX_FLOATING)
            {
                 if (x > centerx) stepX = STEP_X;
                 if (x < centerx) stepX = -STEP_X;
            }
            // reading the last angle and adding result.X
            int y;
            int centery;
            y = map(Yvalue , Y_min , Y_max , 0, 255);
            centery = map(centerY , Y_min , Y_max , 0, 255);
            // checking if the reading is floating
            if (abs(y - centery) > MAX_FLOATING)
            {
                 if (y > centery) stepY = STEP_Y;
                 if (y < centery) stepY = -STEP_Y;
            }
...
...
...
                 lastAngleX += stepX;
                 lastAngleX = constrain(lastAngleX, servoX_MinAngle, servoX_MaxAngle);
                 result.X = lastAngleX;
...
...
...
                 lastAngleY += stepY;
                 lastAngleY = constrain(lastAngleY, servoY_MinAngle+1, servoY_MaxAngle);
                 result.Y = lastAngleY;
...
...
...
       }
       // disable the thumbstick selection
       digitalWrite(pin_select, LOW);
       return result;
  }
The calibration implemented in the calibrate() method reads the analog values when each thumbstick is on the central position during some samples controlled by the samplesOfrCenterXCounter and samplesOfrCenterYCounter variables. The user usually spins the thumbstick to check the maximum x and y values of each thumbstick. Such calibration is critical for getting the right result when the read() method is called. Note all values are initiated with the value defined by NOT_CALIBRATED, which is 22. The thumbsticks usually start in the center position and the analogic conversion varies between 0 and 1023. 22 is then too far from the center (average point) of 512 and mathematically you can conclude that the thumbsticks were not calibrated.

Installing the API

This library code is located in the code/Thumbstick folder and it must be installed in your IDE. The installation process is simple:
1.
Close any running IDE instances.
 
2.
Move the Thumbstick folder to the arduino-1.5.3/libraries directory of your IDE installation.
 
3.
Open the IDE again and select Sketch ➤ Import Library from the menu. Check if it is listed as Thumbstick . If it is, open the RoboticArm.ino sketch in the examples folder of the library or select Files ➤ Examples ➤ Thumbstick ➤ RoboticArm.ino using the menu. If the library is not present, check if the library is properly installed. You can also learn more about libraries at http://arduino.cc/en/Guide/Libraries .
 

A Sketch to Control the Robotic Arm

With the API installed and the board built, it’s time to create a sketch to control the arm.
Listing 11-4 shows the sketch for the Intel Galileo boards.
Listing 11-4. RoboticArm.ino
#include <Thumbstick.h>
#include <Servo.h>
#define PIN_SERVO_BASE      3
#define PIN_SERVO_SHOULDER  5
#define PIN_SERVO_ELBOW     6
#define PIN_SERVO_WRISTX    9
#define PIN_SERVO_WRISTY    11
#define PIN_SERVO_GRIPPER   10
// Defining the gripper angle limitations
#define GRIPPER_OPENED 90
#define GRIPPER_CLOSED 125
// Declaring the servos in the arm
Servo baseServo;
Servo shoulderServo;
Servo elbowServo;
Servo wristYServo;
Servo wristXServo;
Servo gripper;
// Declaring the sticks with
Thumbstick joystick1(8,  A0, A1, 0, 0, 180, 90, 180);
Thumbstick joystick2(12, A2, A3, 1, 0, 180, 0, 180);
Thumbstick joystick3(13, A4, A5, 2);
// Variables used to store the results
Thumbstick::JOYSTICK_XY_T res1;
Thumbstick::JOYSTICK_XY_T res2;
Thumbstick::JOYSTICK_XY_T res3;
void setup()
{
   // for debug purposes let's use the serial terminal
   Serial.begin(115200);
   // init the joystick pins
   joystick1.initPins();
   joystick2.initPins();
   joystick3.initPins();
   baseServo.attach(PIN_SERVO_BASE);          // joystick 1 - axis -> X
   shoulderServo.attach(PIN_SERVO_SHOULDER);  // joystick 1 - axis -> y
   elbowServo.attach(PIN_SERVO_ELBOW);        // jostick 2 - axis -> Y
   wristYServo.attach(PIN_SERVO_WRISTX);      // joystick 3 - axis -> X
   wristXServo.attach(PIN_SERVO_WRISTY);      // joystick 3 - axis -> Y
   gripper.attach(PIN_SERVO_GRIPPER);          // joystick 1, 2 OR 3 - button
   // initial position - all servos in 90 degrees and gripper opened
   baseServo.write(90);
   shoulderServo.write(90);
   elbowServo.write(90);
   wristYServo.write(90);
   wristXServo.write(90);
   gripper.write(GRIPPER_OPENED);
   // a small delay to allow the user to open the serial terminal
   delay(3000);
   Serial.println("*** Calibrating Joysticks **** ");
   Serial.println();
   Serial.println("Reading the CENTERS of each control stick");
   // let's run the calibration process for 15 seconds (15000 ms)
   boolean msgShown = false;
   long t = millis();
   while (millis()-t < 15000) {
      joystick1.calibrate();
      joystick2.calibrate();
      joystick3.calibrate();
      if (joystick1.isCenterCalibrated() &&
          joystick2.isCenterCalibrated() &&
          joystick3.isCenterCalibrated() &&
          msgShown == false)
          {
              msgShown = true; // only to show the message one single time
              Serial.println("Please, start spinning the thumbsticks !!!!");
          }
   }
   if (DEBUG)
   {
      joystick1.dumpCalibration();
      joystick2.dumpCalibration();
      joystick3.dumpCalibration();
   }
   Serial.println("*** Calibration process completed.");
}
void loop()
{
   static boolean isGripperOpened = true;
   // checking the joysticks calibration
   if (joystick1.isCalibrated() == false)
   {
       Serial.println("ERROR: Joystick 1 not calibrated");
   }
   if (joystick2.isCalibrated() == false)
   {
       Serial.println("ERROR: Joystick 2 not calibrated");
   }
   if (joystick3.isCalibrated() == false)
   {
       Serial.println("ERROR: Joystick 3 not calibrated");
   }
   // In case some tumbstick is not calibrated the user must be informed
   if ((joystick1.isCalibrated()  == false) ||
       (joystick2.isCalibrated()  == false) ||
       (joystick3.isCalibrated()  == false))
   {
        // aborting
        Serial.println("You need to calibrate the joysticks !!! RESTART the sketch pressing RESET button!! ");
        delay(1000);
        return;
   }
    Thumbstick::MOVE_T move_type = Thumbstick::STEPS_IN_TOUCH;                 //let's use this first
   //Thumbstick::MOVE_T move_type = Thumbstick::ABSOLUTE_POSITION;
   //Thumbstick::MOVE_T move_type = Thumbstick::ANALOG_RAW;
   // making the reading of all 3 thumbsticks
   res1 = joystick1.read(move_type);
   res2 = joystick2.read(move_type);
   res3 = joystick3.read(move_type);
   if (DEBUG)
   {
       Serial.print("Joystick1 -> X:");
       Serial.print(res1.X);
       Serial.print(" Y:");
       Serial.print(res1.Y);
       Serial.print(" Z:");
       Serial.print(res1.buttonPressed);
       Serial.print("   Joystick2 -> X:");
       Serial.print(res2.X);
       Serial.print(" Y:");
       Serial.print(res2.Y);
       Serial.print(" Z:");
       Serial.print(res2.buttonPressed);
       Serial.print("   Joystick3 -> X:");
       Serial.print(res3.X);
       Serial.print(" Y:");
       Serial.print(res3.Y);
       Serial.print(" Z:");
       Serial.print(res3.buttonPressed);
       Serial.print(" gripper:");
       Serial.println(gripper.read());
   }
   // moving the servos  according the responses of each thumbstick
   baseServo.write(res1.X);
   shoulderServo.write(res1.Y);
   elbowServo.write(res2.Y);
   wristYServo.write(res3.Y);
   wristXServo.write(res3.X);
   // in our example the button functionality of all button might be used
   // to manipulate the gripper
   if ((res1.buttonPressed) || (res2.buttonPressed) || (res3.buttonPressed))
   {
        if (isGripperOpened)
        {
             gripper.write(GRIPPER_CLOSED);
             isGripperOpened = false;
        }
        else
        {
             isGripperOpened = true;
             gripper.write(GRIPPER_OPENED);
        }
        // this small delay is to allow the gripper complete
        // it is task before a new movement
        delay(600);
   }
}
Before you run the code and try to control the robotic arm, it is essential that you read the code review in order to understand how the arm must be controlled.

Reviewing RoboticArm.ino

The code starts by defining the pins that each servo will be connected to, defining the mechanical limits of the gripper (90 to 125 degrees), and suggesting names for the servo objects according to their position in the arm.
#define PIN_SERVO_BASE      3
#define PIN_SERVO_SHOULDER  5
#define PIN_SERVO_ELBOW     6
#define PIN_SERVO_WRISTX    9
#define PIN_SERVO_WRISTY    11
#define PIN_SERVO_GRIPPER   10
// Defining the gripper angle limitations
#define GRIPPER_OPENED 90
#define GRIPPER_CLOSED 125
// Declaring the servos in the arm
Servo baseServo;
Servo shoulderServo;
Servo elbowServo;
Servo wristYServo;
Servo wristXServo;
Servo gripper;
Then the thumbsticks objects are created:
// Declaring the sticks with
Thumbstick joystick1(8,  A0, A1, 0, 0, 180, 90, 180);
Thumbstick joystick2(12, A2, A3, 1, 0, 180, 0, 180);
Thumbstick joystick3(13, A4, A5, 2);
Note that each object defines the pin that will select it, the analog ports the thumbstick is connected to, the pin that will receive the button press signals, and the minimum and maximum angles of servos on the X and Y axes, respectively. If the angles are not passed in as joystick3 the default values 0 and 180 degrees are used.
In the setup() function, the thumbsticks pins are initiated, the servos are attached to their respective pins, and the arm is placed at 90 degrees with the gripper opened. No calibration takes place.
// init the joystick pins
joystick1.initPins();
joystick2.initPins();
joystick3.initPins();
baseServo.attach(PIN_SERVO_BASE);          // joystick 1 - axis -> X
shoulderServo.attach(PIN_SERVO_SHOULDER);  // joystick 1 - axis -> y
elbowServo.attach(PIN_SERVO_ELBOW);        // jostick 2 - axis -> Y
wristYServo.attach(PIN_SERVO_WRISTX);      // joystick 3 - axis -> X
wristXServo.attach(PIN_SERVO_WRISTY);      // joystick 3 - axis -> Y
gripper.attach(PIN_SERVO_GRIPPER);          // joystick 1, 2 OR 3 - button
// initial position - all servos in 90 degrees and gripper opened
baseServo.write(90);
shoulderServo.write(90);
elbowServo.write(90);
wristYServo.write(90);
wristXServo.write(90);
gripper.write(GRIPPER_OPENED);
Still in the setup() function, the calibration is invoked in 15 seconds. It means you have 15 seconds to spin your thumbstick to calibrate them after the center of each thumbstick is detected.
while (millis()-t < 15000 ) {
   joystick1.calibrate();
   joystick2.calibrate();
   joystick3.calibrate();
   if (joystick1.isCenterCalibrated() &&
       joystick2.isCenterCalibrated() &&
       joystick3.isCenterCalibrated() &&
       msgShown == false)
       {
           msgShown = true; // only to show the message one single time
           Serial.println("Please, start spinning the thumbsticks !!!!");
       }
}
The rest of the code is concentrated in the loop() function and it is very simple. If the calibration was not made for any of thumbsticks, the program aborts and asks you to reset the sketch. Otherwise, the thumbsticks are read using “step in touch” mode, as explained previously.
   Thumbstick::MOVE_T move_type = Thumbstick::STEPS_IN_TOUCH;         //let's use this first
   // making the reading of all 3 thumbsticks
   res1 = joystick1.read(move_type);
   res2 = joystick2.read(move_type);
   res3 = joystick3.read(move_type);
The only thing to do is to “ask” the servos to move to the position returned by the thumbstick readings and check if the button was pressed to open or close the gripper.
// moving the servos  according the responses of each thumbstick
   baseServo.write(res1.X);
   shoulderServo.write(res1.Y);
   elbowServo.write(res2.Y);
   wristYServo.write(res3.Y);
   wristXServo.write(res3.X);
   // in our example the button functionality of all button might be used
   // to manipulate the gripper
   if ((res1.buttonPressed) || (res2.buttonPressed) || (res3.buttonPressed))
   {
        if (isGripperOpened)
        {
             gripper.write(GRIPPER_CLOSED);
             isGripperOpened = false;
        }
        else
        {
             isGripperOpened = true;
             gripper.write(GRIPPER_OPENED);
        }
        // this small delay is to allow the gripper complete
        // it is task before a new movement
        delay(600);
   }

Running RoboticArm.ino

Dont power the robotic arm. Instead, keep the external power supply disconnected. You can then test if your thumbsticks are properly connected to Intel Galileo or Edison, test the calibration, and check the debug messages on the serial monitor.
As soon as you download the sketch, open the serial console by choosing Tools ➤ Serial Monitor or pressing Ctrl+Shift+M.
You will see some messages regarding the calibration of the thumbsticks.
*** Calibrating Joysticks ****
Reading the CENTERS of each control stick
517
509
495
491
478
489
Please, start spinning the thumbsticks !!!!
At this point, spin each of the thumbsticks in all directions. Try to reach the maximum possible. Recall that you have 15 seconds to spin all the thumbsticks.
If your calibration attempt did not calibrate any of the thumbsticks, you will see error messages like these:
*** Calibration process completed.
ERROR: Joystick 1 not calibrated
ERROR: Joystick 2 not calibrated
ERROR: Joystick 3 not calibrated
You need to calibrate the joysticks !!! RESTART the sketch pressing RESET button!!  
In this case, download the sketch again or press Reset on Intel Galileo or Edison Arduino kit to restart the sketch.
If you calibrate successfully, information regarding each thumbstick is displayed in the serial console. It’s time to check if the angle limits of each servo match the limits you programmed in when the thumbstick objects were created.
Joystick1 -> X:90 Y:177 Z:0   Joystick2 -> X:88 Y:88 Z:0   Joystick3 -> X:85 Y:88 Z:0 gripper:90
Joystick1 -> X:90 Y:177 Z:0   Joystick2 -> X:88 Y:88 Z:0   Joystick3 -> X:85 Y:88 Z:0 gripper:90
Joystick1 -> X:90 Y:177 Z:0   Joystick2 -> X:88 Y:88 Z:0   Joystick3 -> X:85 Y:88 Z:0 gripper:90
Joystick1 -> X:90 Y:177 Z:0   Joystick2 -> X:88 Y:88 Z:0   Joystick3 -> X:85 Y:88 Z:0 gripper:90
Joystick1 -> X:90 Y:177 Z:0   Joystick2 -> X:88 Y:88 Z:0   Joystick3 -> X:85 Y:88 Z:0 gripper:90
Joystick1 -> X:90 Y:177 Z:0   Joystick2 -> X:88 Y:88 Z:0   Joystick3 -> X:85 Y:88 Z:0 gripper:90
Joystick1 -> X:90 Y:177 Z:0   Joystick2 -> X:88 Y:88 Z:0   Joystick3 -> X:85 Y:88 Z:0 gripper:90
Joystick1 -> X:90 Y:177 Z:0   Joystick2 -> X:88 Y:88 Z:0   Joystick3 -> X:85 Y:88 Z:0 gripper:90
Play a little bit with the thumbsticks and check how far the angles change and if the buttons work. If everything is okay, you can power your robotic arm and play with it.
Note if you kept the robotic arm powered while you are calibrating, the arm stands in 90 degrees and then changes to another position when the calibration is successful.
In order to understand this process better, watch the video named calibrating_the_arm.mp4 in the video folder of this chapter. It shows the calibration process and the working arm.

A Gripper Based on Coffee and a Balloon

If you play with your arm, you will notice that some small objects, such as coins, small screwdrivers, and screws, are very difficult to catch using the mechanical gripper that comes with the kit.
Fortunately for us, in 2010, a publication entitled “Universal Robotic Gripper Based on the Jamming of Granular Material” by Brown, E., Rodenberg, N., Amend, J., Mozeika, A., Steltz, E., Zakin, M., Lipson, H., and Jaeger, H., published in Proceedings of the National Academy of Sciences (PNAS), Vol. 107, no. 44, pp.18809-18814, described a brilliant solution to this issue. It explains how to create a universal gripper using ground coffee, a balloon, and a vacuum pump that can crab small objects. If you are interested in reading the full publication, visit http://creativemachines.cornell.edu/sites/default/files/PNAS10_Amend.pdf .
Figure 11-48 shows how the process works. First the gripper approaches the object (1) and then forms over the object (2). The air pump then removes the air, forcing the ground coffee to hold the object (3). finally, the gripper holds the object (4).
In this chapter, you’ll create a very simple version of this gripper using the materials in Table 11-3.
Table 11-3.
Materials List for Ground Coffee Gripper
Number
Description
3
Vacuum pump, 12V 0-16'' Hg
3
12V power supply 2A with switching power supply adapter
1
Relay module shield (one relay is enough)
1
Party balloon at least 24 inches wide
1
Funnel with 10mm or 8mm orifice
1
4 feet 3/8'' (10mm) OD and 1/4'' ID (8mm) clear vinyl house
1
Small piece (2x2 inch) of filter fabric socket (also called drain sleeve)
Some
Around 10 inches of 1/4w wires
Some
Around 10 inches of 1/2w wires of at least two different colors
Regarding the vacuum pump, it’s best to order the ROB-10398 from sparkfun.com. Check out the product at https://www.sparkfun.com/products/10398 .
The 12V power supply used in this project was ordered from Amazon (ASIN B006NTNGN0). You can check out the product at http://www.amazon.com/JACKYLED-Switching-Power-Supply-Adapter/dp/B006NTNGN0 . It comes with a switching power supply adapter that makes the connection to the air pump easier.
Make sure the filter can hold the balloon properly; if it’s too big or too small it won’t provide a good join.
The idea is illustrated in Figure 11-49.
The relay shield closes its contact, thereby allowing the 12V power supply to turn on the air pump. The air pump removes the air inside the gripper.
The relay shield needs 5V and you can use the terminal headers tagged as OUTPUT ALTERNATIVE 5V.
Figure 11-50 shows the air pump connected to the vinyl housing. The relay shield is already receiving 12V in one of its N.A. terminals. The vinyl house is connected to the right barb because it is the one that vacuums the air.
The relay shield used in this project has two relays, but you can use a shield with a single relay.
The next step is to create the gripper using the funnel, balloon, and coffee.

Preparing the Coffee Gripper

To prepare the gripper, you need to fill about 70% of the balloon with coffee. Use the funnel to insert the ground coffee, as shown in Figure 11-51.
Then insert the balloon in the funnel and pass its ribbon through the funnel, as shown in Figure 11-52 (left). You can use a screwdriver or some other object to help to pass the ribbon.
Put the little piece of filter fabric on the top of the ribbon and insert the vinyl housing, as shown in Figure 11-52 (right). This filter fabric socket will prevent the ground coffee from being sucked up by the pump.
If the vinyl housing is not tight enough, you can use a piece of tape to hold it in place.

A Sketch for the Coffee Gripper

Let’s reuseRoboticArm.ino but make a small change to it. You’ll use the first thumbstick declared as joystick1 to control the coffee gripper. The digital header pin 4 will command the relay. If you make these changes to Listing 11-3, you’ll get the following:
void setup()
{
...
...
...
   pinMode(4, OUTPUT);
}
void loop()
{
   static boolean isGripperOpened = true, isCoffeeGripperActive = false ;
...
...
...
  // in our example the button functionality of all button might be used
   // to manipulate the gripper
   if ((res2.buttonPressed) || (res3.buttonPressed))
   {
        if (isGripperOpened)
        {
             gripper.write(GRIPPER_CLOSED);
             isGripperOpened = false;
        }
        else
        {
             isGripperOpened = true;
             gripper.write(GRIPPER_OPENED);
        }
        // this small delay is to allow the gripper complete
        // it is task before a new movement
        delay(600);
   }
   if (res1.buttonPressed)
   {
        if (isCoffeeGripperActive == true)
        {
           isCoffeeGripperActive = false;
           digitalWrite(4, LOW);
        }
        else
        {
          isCoffeeGripperActive = true;
          digitalWrite(4, HIGH);
        }
   }
}
In the setup() function, the digital pin 4 was set to OUTPUT and it is used to command the relay shield. In the loop() function, a second static variable called isCoffeeGripperActive determines whether the air pump must be turned on. The second and third thumbsticks can control the mechanical gripper. On other hand, if the first thumbstick’s button is pressed, the air pump is turned on due to the relay activation on pin 4. This means the coffee gripper is working.
In the code directory of this chapter, there is a sketch named RoboticArmWithCoffeeGripper.ino based on Listing 11-3 with these changes already made.

RunningRoboticArmWithCoffeeGripper.ino

You might wonder why the code uses two grippers. If you created your “coffee gripper” based on the same material mentioned, it will weigh around 100g (0.22 pounds).
Considering the robotic arm was projected to raise objects around 200g (0.44 pounds) or less, it can hold the coffee gripper and catch small objects.
In other words, it is a gripper holding another gripper, which enables you to avoid new mechanical changes to the system.
Another important point—when you rest the coffee gripper over the object and turn on the air pump, wait at least four seconds before you move the arm because the air takes some time to leave the balloon and hold the object.
Figure 11-53 shows the mechanical gripper holding the coffee gripper. It’s holding a mini screw driver.

Ideas for Improving this Project

There are many things you can do with your arm kit and one of them is create different manners to control the robotic arm. This chapter introduced a servo board controller, but you could use a different approach. For example, you could build an exoskeleton using wood and potentiometers in your joins (shoulder, elbow, and wrists). You could make the robotic arm reproduce your exact movements. A simple button in you palm could open and close the gripper.
You also can build your own robotic arm instead of ordering the kit, especially if you have a 3D printer or a laser cutter. There are several open projects on the Internet that will allow you to download the project and build the arm. Nothing prevents you from using the servo board controller to calculate which servos to use.

Summary

This chapter showed you how to create a robotic arm using several Arduino APIs.
You also learned a little bit about the robotic arm theory. You learned how to build your own robotic arm, evaluate which servos to use, determine the maximum load supported, and build a board that controls several servos. All this in order to create a simple human-machine interface.
Open Access This chapter is licensed under the terms of the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (http://​creativecommons.​org/​licenses/​by-nc-nd/​4.​0/​), which permits any noncommercial use, sharing, distribution and reproduction in any medium or format, as long as you give appropriate credit to the original author(s) and the source, provide a link to the Creative Commons licence and indicate if you modified the licensed material. You do not have permission under this licence to share adapted material derived from this chapter or parts of it.
The images or other third party material in this chapter are included in the chapter’s Creative Commons licence, unless indicated otherwise in a credit line to the material. If material is not included in the chapter’s Creative Commons licence and your intended use is not permitted by statutory regulation or exceeds the permitted use, you will need to obtain permission directly from the copyright holder.
Metadaten
Titel
Assembling and Controlling a Robotic Arm
verfasst von
Manoel Carlos Ramon
Copyright-Jahr
2014
Verlag
Apress
DOI
https://doi.org/10.1007/978-1-4302-6838-3_11

Premium Partner