Calibrating the Lynxmotion AL5D Robot Arm
![]() |
A Brief Survey of Existing Methods
I reviewed other’s work before devising my own arm calibration technique. After all there’s little gain in reinventing the wheel, right? Here are the ones I looked at:
- Lynxmotion offers a free Arm Control and Calibration software. I have not used it but from reading the Lynxmotion manual, their software allows you to train the robot arm to perform complex actions. To do so, it takes a list of target positions as input then tells the arm to faithfully trace through the trajectory much like connecting-the-dots. (Of course it can do other actions such as open/close the gripper, rotate wrist, etc.) Before you can program the arm though, you must go through a calibration procedure. This involves matching the arm’s position to a set of poses as shown:
After the matching step, the software learns the calibration parameters. Unfortunately the software does not allow you to do advanced things like call an external program (eg, a chess server) or wait for user input, so I decided not to use it.
- Several people have posted the Inverse Kinematics equations for the Lynxmotion arm. An IK formula computes the angle that each joint needs to bend to so the gripper can reach a target position. I used Mike Keesling’s formula in my code, but see Hun Hong and Laurent Gay for alternatives. What’s cool about the spreadsheets is that you can see a graphical simulation of the arm’s pose as you try different target positions. However, having the IK formula is not enough. We need to know is the exact pulse width to send each servo to rotate to the desired angle.
- Thankfully the Tic-Tac-Toe Playing Robotic Arm project by Dr. Rainer Hessmer takes us further. Besides giving a great tutorial on Inverse Kinematics, Dr. Hessmer also explains his servo calibration method in detail and even provides source code. The basic approach is as follows:
- For the base rotate and wrist up/down servos, take several angle vs. pulseWidth observations and compute the slope-intercept parameters using linear regression
- For the gripper, take several opening distance vs. pulseWidth observations and compute the slope-intercept
- For the shoulder and elbow servos, move the gripper to several (x,y) locations on the surface of a paper grid. Use an Inverse Kinematics equation to solve for the shoulder and elbow joint angles. Take the derived angles and their associated servo pulse widths then compute the slope-intercepts.
Here is Dr. Hessmer’s calibration spreadsheet and demo of his Tic-Tac-Toe robot. To be clear, this is not my work, but I drew inspiration from it.
Calibration, Take One
My first approach is to model each joint with a linear equation:
Angle : joint angle, in degrees
PulseWidth : servo pulse width, in milliseconds
This is similar to Dr. Hessmer’s method except I measured all joint angles directly as opposed to deriving the shoulder and elbow angles using IK. To make measuring easier, I posed the arm in an inverted “L” stance: shoulder link (humerus) points up, elbow bent at -90 degrees so that the forearm (ulna) points forward. Then I worked on each joint, sending different pulse widths to achieve the desired angles. After completing the measurements for one joint, reverted the arm to the inverted “L” pose before repeating the process for the next. These are the numbers I got:
When plotted, the pattern stands out. Notice that except for the shoulder joint, the lines are pretty straight. This is good news because a linear model matches the data well.
Computing the slope and zero point (aka “intercept”) parameters is straighforward using Python and NumPy.
angles = calib_data[joint]['angles'] pulseWidths = calib_data[joint]['pulses'] A1 = np.column_stack([pulseWidths, np.ones_like(pulseWidths)]) eqn,residuals,rank,s = np.linalg.lstsq(A1, angles) slope,intercept = eqn
Lines 1-2 just grabs all the (angle, pulse_width) value pairs that were measured for a given servo. For example, the values for the base rotation servo are:
“base”: {
“angles” : [-90, 0, 90],
“pulses” : [600, 1500, 2400]
}
Testing the Model
Armed with the new calibration parameters, it’s time for action. To test the calibration accuracy, I made the robot arm touch the center of the tiles of a wooden chessboard while I measured the position offsets. The chessboard I used is a non-folding type with 1-3/4″ tiles and wood thickness of 5/8″. To simplify measuring, I made the hand touch the board at a right angle such that the wrist is directly above the gripper tip. This virtually eliminates any error contribution from the hand. Because of this requirement, a few tiles in the first and last rows (where the hand must slant to reach the tile) were excluded.
For each tile I measured the X (depth) and Y (horizontal) position offsets from its center as well as the Z (height) offset from the surface. To check for Z-axis overshoot, I instructed the arm to hover 1/2″ above the tile and measured the gap. A total of 48 tiles were reachable with the gripper touching the board at right angle. I measured the horizontal and height position error on these 48 tiles, yielding the statistics below.
| Average | Stdev | Max | |
|---|---|---|---|
| Horizontal Error | 0.35 | 0.15 | 0.71 |
| Height Error | 0.74 | 0.40 | 1.38 |
This level of accuracy isn’t enough. Let’s look at the error profile.. note the warping effect:
I was surprised by the overall inaccuracy given how good the linear model fit was for each servo. What could be causing these position errors? I’m tempted to explore this further, but my practical side convinced me to leave this stone unturned and focus on completing the project. That said, I suspect:
- the tension of two springs supporting the shoulder arm is non linear, causing rotational distortion in the shoulder joint
- small errors in calibration and measurement of link lengths get magnified at the tip of the gripper
- gravity effect especially when reaching far
- deadband in the servos
In Part 4, I’ll explain how I made the arm movement more precise.





