The Ultimate Guide to Jacobian Matrices for Robotics

In this tutorial, we’ll learn about the Jacobian matrix, a useful mathematical tool for robotics.

Real-World Applications

There are some use cases (e.g. robotic painting) where we want to control the velocity of the end effector (i.e. paint sprayer, robotic hand, gripper, etc.) of a robotic arm. One way to do this is to use a library to set the desired speed of each joint on a robotic arm. I did exactly this in this post.

1-Robotic-spray-painting-experiment
A car painting robot. If the robotic arms move too fast, they will do a poor job painting the vehicle. We can use Jacobian matrices to help control the speed of the arm. Image Source: ResearchGate.

Another way we can do this is to use a mathematical tool called the Jacobian matrix. The Jacobian matrix helps you convert angular velocities of the joints (i.e. joint velocities) into the velocity of the end effector of a robotic arm.

For example, if the servo motors of a robotic arm are rotating at some velocity (e.g. in radians per second), we can use the Jacobian matrix to calculate how fast the end effector of a robotic arm is moving (both linear velocity x, y, z and angular velocity roll ωx, pitch ωy, and yaw ωz).

Prerequisites

It is helpful if you know how to do the following:

Overview of the Jacobian Matrix

For a robot that operates in three dimensions, the Jacobian matrix transforms joint velocities into end effector velocities using the following equation:

2-overview-jacobian

q with the dot on top represents the joint velocities (i.e. how fast the joint is rotating for a revolute joint and how fast the joint is extending or contracting for a prismatic joint) . 

  • The size of this vector is n x 1, with n being equal to the number of joints (i.e. servo motors or linear actuators) in your robotic arm. 
  • Note that q can represent either revolute joints (typically represented as θ) or prismatic joints (which I usually represent as d, which means displacement)

J is the Jacobian matrix. It is an m rows x n column matrix (m=3 for two dimensions, and m=6 for a robot that operates in three dimensions). n represents the number of joints.

The matrix on the left represents the velocities of the end effector

x, y, and z with the dots on top represent linear velocities (i.e. how fast the end effector is moving in the x, y, and z directions relative to the base frame of a robotic arm)

ωx, ωy, and ωz represent the angular velocities (i.e. how fast the end effector is rotating around the x, y, and z axis of the base frame of a robotic arm).

Consider this SCARA robotic arm:

11-scara-armJPG

The equation using the Jacobian would be as follows:

3-equation-using-jacobian

Looking closer at the Jacobian, this term can be divided into two parts, where the first three rows are used for linear velocities, and the bottom three rows are used for angular velocities.

4-angular-velocities

Taking a look at a real-world example, you’ll see that the SCARA robot that I’ve been building (see my YouTube video here) can only rotate around the z axis of the base frame (i.e. straight upwards towards the ceiling out of the board). There is no rotation around the x or y axes of the base frame.

How To Find the Jacobian Matrix for a Robotic Arm

To fill in the Jacobian matrix, we use the following tables:

5-revolute-joints

The upper half of the matrix is used to determine the linear components of the velocity, while the bottom part is used to determine the rotational components of the velocity.

6-prismatic-joints

Example 1: Cartesian Robot

15-example-of-cartesian-robot

Consider the following robot that has three prismatic joints.

7-cartesian-robotJPG

Here is the equation with the Jacobian matrix:

8-jacobianJPG

Which is the same as:

9-jacobianJPG

Notice that the q’s were replaced with d’s, which represent “displacement” of the prismatic joint (i.e. linear actuator).

To fill in the Jacobian matrix, we have to come one column at a time from left to right. Each column represents a single joint.

10-jacobianJPG

Let’s start with Joint 1. Making i = 1, we get:

11-jacobianJPG

Now let’s fill in the second column of the matrix which represents Joint 2. Making i = 2, we get:

12-jacobianJPG

Finally, let’s fill in column 3, which represents Joint 3.

13-jacobianJPG

The R in the matrix above stands for “rotation matrix.” For example, R01 stands for the rotation matrix from frame 0 to frame 1.

Now to fill in the rotation matrices, we need to add reference frames to the joints. This tutorial shows you how to do that. I’m going to use that same graphic here.

22-add-yJPG

Let’s start with R00. Since we have no rotation from frame to frame 0,  R00 is the identity matrix. Anything that is multiplied by the identity matrix will remain itself, so our matrix now looks like this:

15-jacobianJPG

What is R01 ? We derived this matrix here.

17-joint-variables-all-0JPG

So now we have:

17-so-now-we-haveJPG-1

Which reduces to:

18-reduces-toJPG

What is R02 ? We know that:

R02  = R0R1

Here is  R12:

19-rot-1-2JPG

Therefore:

20-thereforeJPG

Our Jacobian becomes:

21-jacobian-becomesJPG

Which reduces to:

22-reduces-toJPG-1

Writing this a bit more clearly, we have:

23-more-clearlyJPG

Our original equation then becomes:

24-original-equation-becomesJPG

We can do matrix multiplication on the right side of this equation above to yield six equations:

25-yield-six-equationsJPG

If we look at the kinematic diagram, we can see if the equations above make sense. You can see that the speed of the end effector in the x0 direction is determined by the displacement of joint 2 (i.e. d2).

26-scaraJPG

You can see that the speed of the end effector in the y0 direction is determined by the negative displacement of joint 3 (i.e. d3).

The speed of the end effector in the z0 direction is determined by the positive displacement of joint 1 (i.e. d1).

The last three equations tell us that the end effector will never be able to rotate around the x, y, and z axes of frame 0, the base frame of the robot. This makes sense because all of the joints are prismatic joints. We would need to have revolute joints in order to get rotation.

Example 2: Articulated Robot

Now let’s take a look at another example, an articulated robot that has the following kinematic diagram.

21-articulated-manipulatorJPG

This robotic arm will have three columns since it has three joints (i.e. servo motors). As usual, it will have six rows since we’re working in three dimensions. 

Our equation that relates joint velocity to the velocity of the end effector takes the following form:

28-joint-velocityJPG

The Jacobian matrix J (the 6×3 matrix above with the squares) takes the following form since we have all revolute joints:

29-jacobian-matrixJPG

So using the form above, our Jacobian matrix for the articulated robotic arm is (n is the number of joints):

30-big-jacobian-matrixJPG

You’ll see that each column (i = 1, 2, and 3) uses the Jacobian for revolute joints since each joint is a revolute joint. 

The x in the above matrix stands for the cross product.

Now, let’s fill in this Jacobian matrix, going one column at a time from left to right.

R00 is the identity matrix since there is no rotation from frame 0 to itself.

31-big-jacobianJPG

We now need to find d03. d03 comes from the upper right of the homogeneous transformation matrix from frame 0 to frame 3 (i.e. homgen_0_3). That is, it comes from the T part of the matrix of the following form:

1-homogeneous-n-1-nJPG

To get the homogeneous transformation matrix from frame 0 to frame 3, we first have to fill in the Denavit-Hartenberg table for the articulated robotic arm. I show you how to do this in this tutorial.

Here was the table we got:

31-find-dJPG

Now, we take a look at that table and go one row at a time, starting with the Joint 1 row. To find the homogeneous transformation matrix from frame 0 to frame 1, we use the values from the first row of the table to fill in this…

3-plug-in-values-for-matrixJPG

We then look at the second row of the D-H table and fill in the homogeneous transformation matrix from frame 1 to 2…

4-plug-in-correspondingJPG

We then look at the third row of the D-H parameter table to fill in the homogeneous transformation matrix from frame 2 to 3…

5-look-at-third-rowJPG

Then, to find the homogeneous transformation matrix from the base frame (frame 0) to the end-effector frame (frame 3), we would multiply those three transformation matrices together.

homgen_0_3 = (homgen_0_1)(homgen_1_2)(homgen_2_3) 

d0is a three element vector that is made up of the values of the first three rows of the rightmost column of homgen_0_3. I won’t go through all the matrix multiplication for finding homgen_0_3, but if you follow the method I’ve described above, you will be able to get it.

d00 will always be 0, so our Jacobian will look like this at this stage:

32-jacobianJPG

Going to the second column of the matrix, d01 is a three element vector that is made up of the values of the first three rows of the rightmost column of homgen_0_1 (which you found earlier).

R01 is below (see this tutorial for how to find it):

26-multiply-together-we-getJPG

Going to the third column, d02 is a three element vector that is made up of the values of the first three rows of the rightmost column of homgen_0_2. Since you know homgen_0_1 and homgen_1_2, you can multiply those two matrices together to get homgen_0_2.

homgen_0_2 = (homgen_0_1)(homgen_1_2)

R02 (i.e. rot_mat_0_2) is found by multiplying the rotation matrices from frame 0 to frame 1 and from frame 1 to frame 2.

rot_mat_0_2 = (rot_mat_0_1)(rot_mat_1_2)

We know rot_mat_0_1. Rot_mat_1_2 is the following:

29-rot-1-to-2JPG

Now in the top half your Jacobian matrix you will need to go column by column and take the cross product of two three-element vectors.

33-jacobianJPG

In a real-world setting, you would use a Python library like NumPy to perform this operation. If you do a search for “numpy.cross” you will see how to do this. For example, here is some Python code of how to take the cross product of two three-element vectors:

x = [1, 2, 3]
y = [4, 5, 6]
np.cross(x, y)
Output:
array([-3,  6, -3])

Now going to the bottom half of the matrix, R00 is the identity matrix.

34-jacobianJPG

You can fill in the rest of the bottom half of the Jacobian matrix because we know R01 and R02

Once you have the Jacobian, you will then have the following setup:

35-jacobianJPG

From the setup above, you can find six equations, one for each velocity variable (on the far left). These equations enable us to calculate the velocities of the end effector of the articulated robotic arm given the joint velocities.

36-joint-velocitiesJPG

Inverse Jacobian Matrix

In the previous section, we looked at how to calculate the velocities of the end effector of a robotic arm given the joint velocities. What if we want to do the reverse? We want to calculate the joint velocities given desired velocities of the end effector?

To solve this problem, we must use the inverse of the Jacobian matrix. 

37-inverse-jacobianJPG

A matrix multiplied by its inverse is the identity matrix I. The identity matrix is the matrix version of the number 1.

38-jacobianJPG

You can only take the inverse of a square matrix. A square matrix is a matrix where the number of rows is equal to the number of columns.

So how would we find J-1? Let’s take a look at an example.

Example

Suppose we have the following two degrees of freedom robotic arm.

13-add-y-axesJPG

We have the following equation where the matrix with the 12 squares is J, the Jacobian matrix.

40-jacobian-matrixJPG

We only have two servo motors. These two servo motors control the velocity of the end effector in only the x and y directions (e.g. we have no motion in the z direction). 

Suppose the only thing that matters to us is the linear velocity in the x direction and the linear velocity in the y direction. We can simplify our equation accordingly to this, where the matrix with the squares is J:

41-square-matrixJPG

Let’s replace those squares with some variables.

42-some-variablesJPG
43-matrixJPG

Since…

44-jacobianJPG

To get the J-1, we use the following formula:

45-following-formulaJPG

To get J-1, we first need to find J. J has two revolute joints. Revolute joints take the following form:

46-jacobianJPG

So the equation for our two degree of freedom robotic arm will look like this:

47-will-look-likeJPG

R00 is the identity matrix.

48-after-identity-matrixJPG

To calculate d02 , we need to find the homogeneous transformation matrix from frame 0 to frame 2. We did exactly this on this tutorial.

4-yield-homogeneous-transformJPG
6-two-matrices-would-yieldJPG

homgen_0_2 = (homgen_0_1)(homgen_1_2)

When you do this multiplication, you get the following:

50-get-the-followingJPG

Based on the homogeneous matrix above, here is d02.

51-d-0-2JPG

Plugging that into this expression below and performing the cross product with the vector [0 0 1], we get:

52-full-equationJPG
53-full-equationJPG

And since:

54-homgenJPG

We know that:

  • R01 is the 3×3 matrix in the upper left of homgen_0_1.
  • d01 is the 3×1 vector in the upper right of homgen_0_1.
  • We already know d02. We found that earlier in this tutorial.
  • We can therefore fill in the rest of the upper half of the Jacobian matrix to get this:
55-whole-equationJPG

J is that big matrix above. Since we are only concerned about the linear velocities in the x and y directions, this:

56-matrixJPG

Becomes…

57-matrixJPG

J is that big matrix above. It takes the form:

58-j-matrixJPG

To get the inverse of J (i.e. J-1), we do the following:

59-jJPG

The (J11J22 – J12J21) in the denominator is known as the determinant. I’ll call 1/(J11J22 – J12J21) the reciprocal of the determinant.

Once we know J-1, we can use the following expression to solve the joint velocities given the velocities of the end effector in the x and y directions.

60-joint-velocityJPG

Which is the same as:

61-joint-velocityJPG

Which is:

62-joint-velocityJPG

Programming the Jacobian on a Real Robot

Let’s take a look at all this math we’ve done so far by implementing it on a real-world robot.

To do this section, you need to have assembled the two degrees of freedom SCARA robot here.

We want to calculate the rotation velocity of the two joints (i.e. servo motors) based on desired linear velocities of the end effector.

63-jacobian-real-robotJPG

We will program our robotic arm so that the end effector of the robotic arm moves in a straight line at a constant linear velocity.

This equation will come in handy. That big nasty matrix below is the Jacobian. We’ll use it to calculate the inverse Jacobian matrix in our code.

64-linear-velocityJPG

Open the Arduino IDE and create a new sketch named jacobian_based_path_planning_v2.ino.

Write the following code. It is helpful to write the code line by line rather than copying and pasting so that you understand what is going on.

/*
Program: Jacobian-Based Path Planning
File: jacobian_based_path_planning_v1.ino
Description: This program moves the end effector of a SCARA robotic arm based on your
  desired x and y linear velocities. 
  Motion is calculated using the Jacobian matrix which relates joint velocities
  to end effector velocities.
  Note that Servo 0 = Joint 1 and Servo 1 = Joint 2.
Author: Addison Sears-Collins
Website: https://automaticaddison.com
Date: October 14, 2020
*/
 
#include <VarSpeedServo.h> 

// Define the number of servos
#define SERVOS 2

// Conversion factor from degrees to radians
#define DEG_TO_RAD 0.017453292519943295769236907684886

// Conversion factor from radians to degrees
#define RAD_TO_DEG 57.295779513082320876798154814105

// Create the servo objects.
VarSpeedServo myservo[SERVOS]; 

// Speed of the servo motors
// Speed=1: Slowest
// Speed=255: Fastest.
const int default_speed = 255;

const int std_delay = 10; // Delay in milliseconds

// Attach servos to digital pins on the Arduino
int servo_pins[SERVOS] = {3,5};

// Angle of the first servo
float theta_1 = 0;
float theta_1_increment = 0; 
float theta_1_dot = 0; // rotational velocity of the first servo

// Angle of the second servo
float theta_2 = 0;
float theta_2_increment = 0;
float theta_2_dot = 0; // rotational velocity of the second servo

// Linear velocities of the end effector relative to the base frame
// Units are in centimeters per second
// If x_dot = 0.0, the end effector will move parallel to the y axis
// Play around with these numbers, and observe the motion of the end effector
// relative to the x and y axes of the base frame of the robotic arm.
float x_dot = 0.0;
float y_dot = 1.0;

// Jacobian variables
float reciprocal_of_the_determinant;
float J11;
float J12;
float J21;
float J22;

// Inverse Jacobian variables
float J11_inv;
float J12_inv;
float J21_inv;
float J22_inv;

// Link lengths in centimeters
// You measure these values using a ruler and the kinematic diagram
float a2 = 5.9;
float a4 = 6.0;

void setup() {

  Serial.begin(9600);
  
  // Attach the servos to the servo object 
  // attach(pin, min, max  ) - Attaches to a pin 
  //   setting min and max values in microseconds
  //   default min is 544, max is 2400  
  // Alter these numbers until both servos have a 
  //   180 degree range.
  myservo[0].attach(servo_pins[0], 544, 2475);  
  myservo[1].attach(servo_pins[1], 500, 2475); 

  // Set the angle of the first servo.
  theta_1 = 0.0;

  // Set the angle of the second servo.
  theta_2 = 90.0;
  
  // Set initial servo positions 
  myservo[0].write(theta_1, default_speed, true);  
  myservo[1].write(theta_2, default_speed, true);

  // Let servos get into position
  delay(3000);
  
}
 
void loop() { 

    // Make sure the servos stay within their 180 degree range
    while (theta_1 <= 180.0 && theta_1 >= 0.0 && theta_2 <= 180.0 && theta_2 >= 0.0) { 

      // Convert from degrees to radians
      theta_1 = theta_1 * DEG_TO_RAD;
      theta_2 = theta_2 * DEG_TO_RAD;

      // Calculate the values of the Jacobian matrix
      J11 = -a4 * sin(theta_1) * cos(theta_2) - a4 * cos(theta_1) * sin(theta_2) - a2 * sin(theta_1);
      J12 = -a4 * sin(theta_1) * cos(theta_2) - a4 * cos(theta_1) * sin(theta_2);
      J21 = a4 * cos(theta_1) * cos(theta_2) - a4 * sin(theta_1) * sin(theta_2) + a2 * cos(theta_1);
      J22 = a4 * cos(theta_1) * cos(theta_2) - a4 * sin(theta_1) * sin(theta_2);
       
      reciprocal_of_the_determinant = 1.0/((J11 * J22) - (J12 * J21));

      // Calculate the values of the inverse Jacobian matrix
      J11_inv = reciprocal_of_the_determinant * (J22);
      J12_inv = reciprocal_of_the_determinant * (-J12);
      J21_inv = reciprocal_of_the_determinant * (-J21);      
      J22_inv = reciprocal_of_the_determinant * (J11);
      
      // Set the rotational velocity of the first servo
      theta_1_dot = J11_inv * x_dot + J12_inv * y_dot; 

      // Set the rotational velocity of the second servo
      theta_2_dot = J21_inv * x_dot + J22_inv * y_dot; 

      // Convert rotational velocity in radians per second to X radians in std_delay milliseconds
      // Note that 1 second = 1000 milliseconds and each delay is std_delay milliseconds
      theta_1_increment = (theta_1_dot) * (1/1000.0) * std_delay;

      // Convert rotational velocity in radians per second to X radians in std_delay milliseconds
      // Note that 1 second = 1000 milliseconds and each delay is std_delay milliseconds
      theta_2_increment = (theta_2_dot) * (1/1000.0) * std_delay;

      theta_1 = theta_1 + theta_1_increment; 
      theta_2 = theta_2 + theta_2_increment;

      // Convert the new angles from radians to degrees
      theta_1 = theta_1 * RAD_TO_DEG;
      theta_2 = theta_2 * RAD_TO_DEG;

      Serial.println(theta_1);
      Serial.println(theta_2);
      Serial.println(" ");
     
      myservo[0].write(theta_1, default_speed, true); 
      myservo[1].write(theta_2, default_speed, true);
      
      delay(std_delay); // Delay in milliseconds      
    }    
}     

Load the code to your Arduino.

Wire up your system as shown on this pdf.

2020-10-14-182336

Set the voltage limit on your DC power supply to 6V and the current limit to 2A.

2020-10-14-182317

Run the code. You should see the end effector of your robotic arm move parallel to the y axis in a straight line because we set:

65-end-effector-velocity

After a few seconds, the robotic arm will stop moving in a straight line. The reason for this is that the robotic arm can only move a fixed distance in the positive y direction due to the lengths of the links. The servos, however, continue to rotate until either the first servo or the second servo (or both) reach the limit of their range of rotation (i.e. 0 to 180 degrees).  

That’s it! Keep building!

References

Sodemann, Dr. Angela 2020, RoboGrok, accessed 14 October 2020, <http://robogrok.com/>

Review of Denavit-Hartenberg for Robotic Arms

In this tutorial, we will review the fundamentals of Denavit-Hartenberg parameters, which are a convention for assigning coordinate frames to robotic arms. We assign coordinate frames to robotic arms so that we can get them to do useful work in the real world.

Prerequisites

It is helpful if you’ve already gone through the following tutorials.

Guidelines for Drawing Denavit-Hartenberg Frames

1. If J is the number of joints and F is the number of coordinate frames on a robotic manipulator, the following relation must hold: 

  • F > J

2. One coordinate frame must be on the end effector.

3. There are six ways to draw an axis of a coordinate frame.

5-upJPG
6-downJPG
7-rightJPG
8-leftJPG
9-first-quadrantJPG
10-third-quadrantJPG

Rules for Assigning Denavit-Hartenberg Frames

Here are the four rules that guide the drawing of the D-H coordinate frames:

  1. The z-axis is the axis of rotation for a revolute joint and the direction of motion for a prismatic joint.
  2. The x-axis must be perpendicular to both the current z-axis and the previous z-axis.
  3. The x-axis must intersect the previous z-axis (rule does not apply to frame 0).
  4. The y-axis is determined from the x-axis and z-axis by using the right-hand coordinate system.

For step-by-step examples of assigning Denavit-Hartenberg Frames, check out my tutorial here.

Examples

Here are D-H frames for a Scara robotic arm.

11-scara-armJPG

Here are the D-H frames for a six degree of freedom robot like the Universal Robots UR5.

12-ur5JPG

Here are the D-H frames for a six degree of freedom industrial robot like the FANUC LRMate 200iD. 

13-fanuc-lrmateJPG

Guidelines for Creating the Denavit-Hartenberg Parameter Table

Once you’ve finished drawing the Denavit-Hartenberg frames for a robotic arm, you then create the Denavit-Hartenberg parameter table. This table takes the following form:

  • Number of Rows = Number of Frames – 1
  • Number of Columns = 4: Two columns for rotation and two columns for displacement
7-two-rowsJPG

The Denavit-Hartenberg parameter tables consist of four variables:

  • The two variables used for rotation are θ and α.
  • The two variables used for displacement are r and d.

Consider two coordinate frames: Frame 1 and Frame 2. Let Frame 0 = Frame n-1 and Frame 1 = Frame n.

5-r-and-dJPG
  • θ is the angle from xn-1 to xn around zn-1.
  • α is the angle from zn-1 to zn around xn.
  • r (sometimes you’ll see the letter ‘a’ instead of ‘r’) is the distance between the origin of the n-1 frame and the origin of the n frame along the xn direction.
  • d is the distance from xn-1 to xn along the zn-1 direction.

See step-by-step examples of how to create Denavit-Hartenberg Parameter tables.

Guidelines for Creating Homogeneous Transformation Matrices Using the Denavit-Hartenberg Parameter Table

Once we have filled in the Denavit-Hartenberg (D-H) parameter table for a robotic arm, we find the homogeneous transformation matrices (also known as the Denavit-Hartenberg matrix) by plugging the values into the matrix of the following form, which is the homogeneous transformation matrix for joint n (i.e. the transformation from frame n-1 to frame n).

homgen_n-1_n =

14-homgen-matrixJPG

where R is the 3×3 submatrix in the upper left that represents the rotation from frame n-1 (e.g. frame 0) to frame n (e.g. frame 1), and T is the 3×1 submatrix in the upper right that represents the translation (or displacement) from frame n-1 to frame n.

For example, suppose we have a SCARA robotic arm.

scara-robotic-arm-ces-2020

We draw the kinematic diagram for this arm.

33-add-y-axisJPG

We then use that diagram to find the Denavit-Hartenberg parameters:

15-dh-parameterJPG

We now want to find the homogeneous transformation matrix from frame 0 to frame 1, so we look at the first row of the Denavit-Hartenberg parameter table labeled ‘Joint 1’ (remember that a joint in our case is either a servo motor that produces rotational motion or a linear actuator that produces linear motion). 

In this case, n=1, and we would look at the first row of the D-H table (Joint 1) and plug in the appropriate values into the following matrix:

16-homgen-0-1JPG

For the homogeneous transformation matrix from frame 1 to 2, we would look at the second row of the D-H table (Joint 2) and plug in the corresponding values into the following matrix.

17-homgen-1-2JPG

For the homogeneous transformation matrix from frame 2 to 3, we would look at the third row of the table (Joint 3) and plug in the corresponding values into the following matrix.

18-homgen-2-3JPG

Then, to find the homogeneous transformation matrix from the base frame (frame 0) to the end-effector frame (frame 3), we would multiply all the transformation matrices together.

homgen_0_3 = (homgen_0_1)(homgen_1_2)(homgen_2_3)

See step-by-step examples of how to create Homogeneous Transformation Matrices Using Denavit-Hartenberg.

References

Sodemann, Dr. Angela 2020, RoboGrok, accessed 5 October 2020, <http://robogrok.com/>

How To Perform a Pick and Place Task With a DIY SCARA Robot

In this tutorial, we will program our robot to perform a pick and place task.

Pick and place involves picking up an object in one location and placing it in another.

In this case, we will have our do-it-yourself SCARA robotic arm pick up a hex nut in one location and drop it off in another. Take a look at the video below of what you will build.

Real-World Applications

SCARA robots have a number of real-world applications: 

  • Manufacturing: Picking up items off a conveyor belt and dropping them off on another conveyor belt.
  • Logistics: Picking up an item and placing it in a bin.
  • Factory: Tightening screws.

You can see an image here of a SCARA robot packaging cookies into bins.

scara-robot-packaging-cookies
Source: Gfycat

Prerequisites

  • You completed this tutorial to build a two degree of freedom robotic arm.

You Will Need

Assuming you have gone through the prerequisites and acquired all the components in there, you will also need the following components to complete this project (#ad):

  • 5V or 6V Electric Lifting Magnet (electromagnet to pick up the nut)
  • Actuonix L16 Actuator 100mm 150:1 6V RC Control (linear actuator…check on eBay for this)
  • 22 gauge wire spool (to tie the actuator to the end of the end effector of the robotic arm)
  • Small Hex Nut or a Screw (any size is OK) or Any Small Object Made of Metal
  • 5V Relay (only needed if you’re going to buy the 6V Electric Lifting Magnet)
  • 6 Channel Servo Tester
  • Toy Trash Can
  • 2 x Oblique U-type Robotic Arm Brackets (check eBay and Amazon)
  • M3 Screws and Nuts (3mm in diameter screws)
  • VELCRO Brand Thin Clear Dots with Adhesive 

Connect and Test the Linear Actuator

The first thing you need to do is to replace the upper link of the robot arm with U-type robotic arm brackets.

2020-10-08-140824

Attach the linear actuator to the brackets using VELCRO Brand Thin Clear Dots with Adhesive. My setup looks like this:

2020-10-08-141309
2020-10-08-141327
2020-10-08-141423
2020-10-08-141429

We now have our very own SCARA-style robot.

Let’s test it. The linear actuator has the following specifications:

  • Input voltage: 5-7.4V
  • Stall current: 650mA

Grab the 6 channel digital servo tester. 

Connect the positive red lead to the positive terminal of the power supply.

Connect the negative black lead to the negative terminal of the power supply.

Connect the linear actuator to the digital servo tester so that the:

  • Black lead connects to (-)
  • Red lead connects to (+)
  • White lead connects to (S), which means signal

Now turn on the power supply. Set it to 6.0V with a current limit of 0.50A (ie. 500mA).

2020-10-08-141545

Turn the corresponding knob on the digital servo tester, and watch the linear actuator go back and forth.

2020-10-08-141948
2020-10-08-142006-1

Control the Linear Actuator Using Arduino

Now we want to write code so that we can automatically control the lowering and raising of the linear actuator. We will write code that makes the linear actuator go back and forth repeatedly. 

Open your Arduino IDE, and type the following code. Save the file as control_linear_actuator.ino.

/*
Program: Make a linear actuator go back and forth automatically
File: control_linear_actuator.ino
Description: This program makes the Actuonix L16 Actuator 100mm 150:1 6V RC Control 
  go back and forth.
Author: Addison Sears-Collins
Website: https://automaticaddison.com
Date: October 5, 2020
*/
 
#include <VarSpeedServo.h> 

//////////// Linear Actuator Variables ///////////////
// Create the object.
VarSpeedServo linearActuator; 

// Attach motor to digital pin on the Arduino
const int linearActuatorPin = 12;

// Set the home and extended positions of the linear actuator
const int linearActuatorHomePos = 0;
const int linearActuatorExtPos = 180;

// Stores the current position of the linear actuator
int linearActuatorPos = 0;

// The amount of time in milliseconds alloted to enable the linear actuator to move into position
// The more delay given, the more the linear actuator extends. 100ms, for example, will result
// in full extension of the linear actuator.
const int linearActuatorDelay = 50;
//////////// End Linear Actuator Variables ///////////////

void setup() {  

    // Attach the linear actuator to the pin 
    linearActuator.attach(linearActuatorPin);  
}
 
void loop() {  

  // Linear actuator goes to extended position and then back to home position
  moveLinearActuator();
}

void moveLinearActuator() {
  
  for (linearActuatorPos = linearActuatorHomePos; linearActuatorPos <= linearActuatorExtPos; linearActuatorPos += 1) { // goes from home position to extended position
    linearActuator.write(linearActuatorPos);       
    delay(linearActuatorDelay);  // wait for the actuator to reach the position
  }
  for (linearActuatorPos = linearActuatorExtPos; linearActuatorPos >= linearActuatorHomePos; linearActuatorPos -= 1) { // goes from extended position to home position
    linearActuator.write(linearActuatorPos);          
    delay(linearActuatorDelay);  // wait for the actuator to reach the position
  }  
}

Wire the linear actuator to the Arduino Mega 2560 like what is shown in wiring_linear_actuator_v2.pdf.

Turn on the power supply (6.0V with a current limit of 0.50A), and then plug in the Arduino to the 9V battery. Watch the linear actuator go back and forth.

So that you don’t damage your Arduino Mega, turn off the DC external power supply first before unplugging the Arduino.

Test the Electric Lifting Magnet

Now that we’ve tested the linear actuator, let’s test the electric lifting magnet. 

Grab the electric lifting magnet.

2020-10-08-192254

Attach the positive (typically red) lead of the magnet to the red positive lead of the power supply.

Attach the other lead (typically blue or black) of the magnet to the black lead of the power supply.

Set the voltage to 5V (or 6V depending on what it says on your magnet) and the current limit to 0.50A (the current limit of this component is 0.68A).

Turn on the power supply.

Grab a small metal object like a screw or hex nut, and place it near the magnet.

You should see the hex nut move towards the magnet.

If you are using a 5V electric lifting magnet, you can power the magnet directly with the Arduino by placing the leads in the 5V and GND (ground) pins.

2020-10-08-192237
2020-10-08-192243

Control the Electric Lifting Magnet Using Arduino

Now let’s control the electric lifting magnet using Arduino. 

Using a 6V Electric Lifting Magnet

If you have a 6V electric lifting magnet, follow the instructions in this section. Otherwise, if you’re using a 5V electric lifting magnet, proceed to the next section.

Grab the 5V Relay. This relay acts as a switch for the electric lifting magnet, enabling you to activate and deactivate the magnetism using Arduino.

There are 6 pins on the relay:

  • NO: Normally Open Terminal (peak load of DC28~30V/10A)
  • NC: Normally Closed Terminal
  • COM: Common Terminal
  • DC+: Connects to the 5V pin (power supply) of the Arduino
  • DC-: Connects to the GND (ground) pin of the Arduino
  • IN: Control pin that receives either a HIGH (3-5V) or LOW (0-1.5V) signal from the Arduino (Pin 10 in our case)

If you decide to use the NO terminal instead of the NC terminal, when a HIGH signal is received on the IN pin from the Arduino, the relay switch will turn ON, allowing electric current to flow from C to NO. We’ll use this NO terminal in our work.

If you decide to use the NC terminal instead of the NO terminal, when a LOW signal is received on the IN pin from the Arduino, the relay switch will turn ON allowing electric current to flow from C to NC.

Here is how to wire the relay: control_electric_magnet_arduino_v1.pdf.

Here is the code to test the setup: control_electric_magnet_arduino_v1.ino.

/*
Program: Make an electric lifting magnet turn ON and OFF repeatedly.
File: control_electric_magnet_arduino_v1.ino
Description: This program makes an electric lifting magnet turn ON and OFF repeatedly.
Author: Addison Sears-Collins
Website: https://automaticaddison.com
Date: October 6, 2020
*/

const int magnetSignalPin = 10;    // select the pin that will control the magnet

void setup() {
  pinMode(magnetSignalPin, OUTPUT);    // sets digital pin 10 as output
}

void loop() {
  activateMagnet();
  delay(10000);            // waits for 10 seconds
  deactivateMagnet();
  delay(3000);            // wait for 3 seconds
}

void activateMagnet() {  
  digitalWrite(magnetSignalPin, HIGH); // sets the digital pin ON  
}

void deactivateMagnet() {  
  digitalWrite(magnetSignalPin, LOW); // sets the digital pin OFF  
}

Using a 5V Electric Lifting Magnet

If you’re using the 5V electric lifting magnet, wire your system as follows: control_electric_magnet_arduino_v2.pdf.

For your Arduino, you’ll need to upload the same code from the previous section: control_electric_magnet_arduino_v1.ino.

Your magnet will activate for 10 seconds and then deactivate for 3 seconds.

2020-10-08-192455

Connect the Electric Lifting Magnet to the Linear Actuator

Now that we’ve tested the electric lifting magnet, let’s connect it to the end of the linear actuator. I placed a 3mm diameter screw in one side of the magnet so that it can more easily attach to the end of the linear actuator. 

2020-10-08-213249
2020-10-08-220256

Perform Pick and Place Using Arduino

Before we dive into the pick and place task, let’s see how to perform pick and place using Arduino and our robotic arm. Check out this tutorial for the details on the math.

What we want to do is:

  • Place a hex nut at a predetermined x and y coordinate. This is the pick location.
  • Move the end effector to the pick location.
  • Activate the electric lifting magnet.
  • Lower the linear actuator.
  • Pick up the hex nut.
  • Raise the linear actuator.
  • Move the end effector to the place location.
  • Deactivate the electric lifting magnet.
  • Place the hex nut in the toy trash can by lowering the actuator.
  • Return to the home position (both servos at 0 degrees).

To get started, measure the link lengths, a1, a2, a3, and a4. Use the kinematic diagram below as your guide. You’ll need to use a ruler for this. 

  • a1 is measured vertically from the board’s surface to the top of Link 2. 
  • a2 is measured horizontally from the middle of the first servo (where the central screw is located on Joint 1) to the middle of the second servo (where the central screw on Joint 2 is located).
  • a3 is measured vertically from the top of Link 2 to the top of Link 4.
  • a4 is measured horizontally from the middle of the second servo to the middle of the electric lifting magnet.
33-add-y-axisJPG

Place a metal object (e.g. hex nut) at position x = 4.0, y = 10.0 on the board.

Place the toy trash cash at position x = -4.0, y = 10.0.

2020-10-09-193231

Here is the wiring diagram: pick_and_place_no_vision_v1.pdf.

Set the power supply to 6V with a 0.60A current limit.

2020-10-09-193250
2020-10-09-193710

Here is the code for the Arduino:  pick_and_place_no_vision.ino.

/*
Program: Perform a pick and place task with a SCARA-style robotic 
  arm using inverse kinematics (graphical method)
File: pick_and_place_no_vision_v1.ino
Description: This program performs a pick and place task with a SCARA-style robotic arm. 
  No computer vision is used.
Author: Addison Sears-Collins
Website: https://automaticaddison.com
Date: October 9, 2020
*/
 
#include <VarSpeedServo.h> 

//////////// Servo Motor Variables ///////////////

// Define the number of servos
#define SERVOS 2

// Create the servo objects.
VarSpeedServo myservo[SERVOS]; 

// Speed of the servo motors
// Speed=1: Slowest
// Speed=255: Fastest.
const int servoSpeed = 50;

// Attach servos to digital pins on the Arduino
int servoPins[SERVOS] = {3,5};

//////////// End Servo Motor Variables ///////////////

//////////// Linear Actuator Variables ///////////////

// Create the linear servo object.
VarSpeedServo linearActuator; 

// Attach motor to digital pin on the Arduino
const int linearActuatorPin = 12;

// Set the home position of the linear actuator
const int linearActuatorHomePos = 0;

// Stores the current position of the linear actuator
int linearActuatorPos = 0;

// The amount of time in milliseconds alloted to enable the linear actuator to move into position
// The more delay given, the more the linear actuator extends. 100ms, for example, will result
// in full extension of the linear actuator. Tweak this input here until you get desired results.
const int linearActuatorDelay = 45;

//////////// End Linear Actuator Variables ///////////////

// Link lengths in centimeters
const double a1 = 4.85;
const double a2 = 5.80;
const double a3 = 5.15;
const double a4 = 6.0;

// Select the pin that will control the magnet
const int magnetSignalPin = 10;    

void setup() {

  // Move servos to the home position of 0 degrees
  setupServos(); 

  // Set magnet pin
  pinMode(magnetSignalPin, OUTPUT);    

  // Attach the linear actuator to the digital pin of the Arduino 
  linearActuator.attach(linearActuatorPin);  
  linearActuator.write(0); 
  delay(1000); 
}
 
void loop() {

  // The pick location in centimeters in the 
  // robot's base frame (pick up the object from here)
  double xPosPick = 4.0;
  double yPosPick = 10.0;

  // The place location in centimeters in the 
  // robot's base frame (place the object here)
  double xPosPlace = -4.0;
  double yPosPlace = 10.0;

  // Pick up an object from one location, and place it in another
  pickAndPlace(xPosPick, yPosPick, xPosPlace, yPosPlace);

  while(1) {
    // Return home
    returnHome();
  }
}     

/*  This method converts the desired angle for Servo 1 into a control angle
 *  for Servo 1. It assumes that the 0 degree position on the kinematic
 *  diagram for Servo 1 is actually 90 degrees on the actual servo.
 *  The angle range for Servo 1 on the kinematic diagram is
 *  -90 to 90 degrees, with 0 degrees being the center position. 
 *  The actual servo range for the physical motor
 *  is 0 to 180 degrees. We convert the desired angle 
 *  to a value within that range.
 */
int calcServo1Angle (int inputAngle) {
  
  int result;

  result = map(inputAngle, -90, 90, 0, 180);

  return result;
}  

/* Move servos to the home position */
void setupServos() {
  
  // Attach the servos to the servo object 
  // attach(pin, min, max  ) - Attaches to a pin 
  //   setting min and max values in microseconds
  //   default min is 544, max is 2400  
  // Alter these numbers until both servos have a 
  //   180 degree range.
  myservo[0].attach(servoPins[0], 544, 2475);  
  myservo[1].attach(servoPins[1], 500, 2475); 

  // Set initial servo positions to home position
  myservo[0].write(0, servoSpeed, true);  
  myservo[1].write(calcServo1Angle(0), servoSpeed, true);

  // Wait to let servos get into position
  delay(3000);
}

/* Move arm to the x and y position indicated by the parameters */
void moveArm(double xPos, double yPos) {

  // Define the inverse kinematics variables
  double r1 = 0.0;
  double phi_1 = 0.0;
  double phi_2 = 0.0;
  double phi_3 = 0.0;
  double theta_1 = 0.0;
  double theta_2 = 0.0;
  
  /* Calculate the inverse kinematics*/
  // (1) r1  = square_root((x_0_2)^2 + (y_0_2)^2) 
  // Value is in centimeters
  r1 = sqrt((xPos * xPos) + (yPos * yPos));

  // (2) ϕ1 = arccos((a4^2 - r1^2 - a2^2)/(-2*r1*a2))
  // The returned value is in the range [0, pi] radians. 
  phi_1 = acos(((a4 * a4) - (r1 * r1) - (a2 * a2))/(-2.0 * r1 * a2));

  // (3) ϕ2 = arctan((y_0_2) / (x_0_2)) 
  // The atan2() function computes the principal value of the 
  // arc tangent of y/x, using the signs of both arguments to 
  // determine the quadrant of the return value. 
  // The returned value is in the range [-pi, +pi] radians.
  phi_2 = atan2(yPos, xPos);

  // (4) θ1 =  ϕ1 - ϕ2
  // Value is in radians
  theta_1 = phi_2 - phi_1;

  // (5) ϕ3 = arccos((r1^2 - a2^2 - a4^2)/(-2*a2*a4))
  // Value is in radians
  phi_3 = acos(((r1 * r1) - (a2 * a2) - (a4 * a4))/(-2.0 * a2 * a4));

  //(6) θ2 = 180° - ϕ3 
  theta_2 = PI - phi_3;
    
  /* Convert the joint (servo) angles from radians to degrees*/
  theta_1 = theta_1 * RAD_TO_DEG; // Joint 1
  theta_2 = theta_2 * RAD_TO_DEG; // Joint 2

  /* Move the end effector to the desired x and y position */
  // Set Joint 1 (Servo 0) angle
  myservo[0].write(theta_1, servoSpeed, true); 

  // Set Joint 2 (Servo 1) angle
  myservo[1].write(calcServo1Angle(theta_2), servoSpeed, true); 
 
  // Wait
  delay(1000);
}

/* Activate the electric lifting magnet */
void activateMagnet() {  
  digitalWrite(magnetSignalPin, HIGH); // sets the digital pin ON  
}

/* Deactivate the electric lifting magnet */
void deactivateMagnet() {  
  digitalWrite(magnetSignalPin, LOW); // sets the digital pin OFF 
}

/* Move linear actuator down and then up */
void moveLinearActuator(int extension) {
  
  // goes from home position to extended position
  for (linearActuatorPos = linearActuatorHomePos; linearActuatorPos <= extension; linearActuatorPos += 1) { 
    linearActuator.write(linearActuatorPos); 
    delay(linearActuatorDelay);                       // wait for the actuator to reach the position
  }
  
  // goes from extended position to home position
  for (linearActuatorPos = extension; linearActuatorPos >= linearActuatorHomePos; linearActuatorPos -= 1) { 
    linearActuator.write(linearActuatorPos);          
    delay(linearActuatorDelay * 2);                          // wait for the actuator to reach the position
  }  
}

/* Pick up an object from one location, and place it in another */
void pickAndPlace(double xPosPick, double yPosPick, double xPosPlace, double yPosPlace) {
  
  // Move arm to the pick location
  moveArm(xPosPick, yPosPick);

  activateMagnet();

  // Pick up the object
  moveLinearActuator(180);

  // Move arm to the place location
  moveArm(xPosPlace, yPosPlace);

  deactivateMagnet();

  // Place the object (tweak this input here until you get desired results)
  moveLinearActuator(120);

  delay(1000); 
}

/* Return to the 0 degree home position */
void returnHome() {
  
  // Set servo positions to home position
  myservo[0].write(0, servoSpeed, true);  
  myservo[1].write(calcServo1Angle(0), servoSpeed, true);
}

2020-10-08-215918-1
2020-10-09-194559

Future Challenge Project: Add Computer Vision to Automate Pick and Place

scara-robot-and-vision
Image Source: Borangiu, Theodor & Anton, Florin & Anton, Silvia. (2015). Open Architecture for Vision-Based Robot Motion Planning and Control. Mechanisms and Machine Science. 29. 63-89. 10.1007/978-3-319-14705-5_3.

Up until now, we have placed an object at a predetermined location, and we hardcoded that location in our Arduino code. If you’re feeling really ambitious, what you can now do is integrate the Raspberry Pi camera so you can determine the object’s location automatically. 

What you’ll want to do is:

  • Turn on the Raspberry Pi camera.
  • Place a hex nut on the board, somewhere where the camera can see it, and the robotic arm can reach it.
  • Determine the coordinates of the object using the Raspberry Pi camera. This is the target location.
  • Send the coordinates of that object (in the robot’s base frame) from Raspberry Pi to Arduino.
  • Move the end effector to the target location.
  • Activate the electric lifting magnet.
  • Lower the linear actuator.
  • Pick up the hex nut.
  • Raise the linear actuator.
  • Move the linear actuator to the toy trash can.
  • Lower the linear actuator.
  • Deactivate the electric lifting magnet.
  • Place the hex nut.
  • Raise the linear actuator.
  • Return to the home position
  • Pick and place complete!

You will need to tweak the code in this tutorial to get everything working.

That’s it for today. Keep building!

Disclosure (#ad): As an Amazon Associate, I earn from qualifying purchases.