Microcontroller-Driven Robotic Arm with Software Integration

Timeline: January 2024 Context: Matura thesis in computer science, MNG Rämibühl Zürich Supervision: Christoph Vogel Grade: 6/6 Alejo Restrepo

For my Matura thesis I designed and built a robot arm from consumer electronics and affordable materials, and programmed it to emulate the positioning capabilities of an industrial robot arm.

The arm is driven by an Arduino UNO and six servo motors and is piloted with joysticks. On the software side I derived two mathematical control methods for positioning the end-effector, implemented them as a C++ library, and ran a series of user tests to compare them.

The hardware

The arm uses six servos with torques matched to their position in the chain: 40 kg/cm at the base and shoulder, 10 kg/cm at the elbow joints and 2.2 kg/cm at the hand. The structure is built from 2 mm plywood sheets, cut and glued in layers with instant glue, which soaks through the fibers and hardens the wood completely. The shoulder link is reinforced with an aluminum skeleton, with plywood layers molded to its shape to keep the weight down.

Sketch of the prototype design with the six joints and their ranges
Sketch of the prototype design with the six joints.
The full robot arm structure with part nomenclature
The full structure with part nomenclature, true to scale.

A wide, flat base plate keeps the whole structure from tipping over and houses the strongest servo. The most important design decision sits at the elbow: the fourth motor is displaced 6.7 cm above the third servo's axis and rotated by 90°. The absolute range of the joint stays the same, but the elbow no longer runs into the shoulder, so the arm can fold completely into itself without colliding with its own structure, and it gains 6.7 cm of forward reach at full stretch.

Work range plots of the arm in both elbow configurations
Work range of the arm in both elbow configurations, plotted in GeoGebra.
Base plate upper view with the Arduino

Base plate, upper view, with the Arduino.

Base plate front view with the shoulder servo mounted

Base plate with the shoulder servo mounted.

Shoulder servo front view

Shoulder servo, front view.

Shoulder servo side view

Shoulder servo, side view.

Metal body of the shoulder link, front view

Metal body of the shoulder link.

Metal body connected with the elbow servo

Metal body connected with the elbow servo.

Forearm joint

Forearm joint.

Hand joint

Hand joint.

The base, shoulder and elbow servos draw too much current to run off the Arduino, so they get an external supply: two sets of four AA batteries connected in parallel, 6 volts with double the amps. The three smaller servos run off the Arduino's 5 V pin. Input comes from two joystick modules, a 2-axis one that maps to x and y movement and a 1-axis one for pivoting the hand.

Circuit diagram drawn in Tinkercad with the Arduino, six servos and battery packs
The full circuit, drawn in Tinkercad: Arduino UNO, six servos and the external battery packs.

Deriving the kinematics

The control problem lives on the vertical plane: the shoulder, elbow and hand joints position the end-effector in 2D. Adding up the horizontal and vertical components of each link with sines and cosines gives the forward kinematic equations, the position of the end-effector as a function of the joint angles.

Vector graphic of the arm with the joint angles
Vector graphic of the arm with the joint angles θ1, θ2 and θ3.

Going the other way is the interesting part. The first method is an exact trigonometric inversion. A 3-jointed system has infinitely many solutions for a given point, so the orientation of the end-effector is fixed parallel to the ground. Drawing a vertical line through the target point closes the arm into a polygon that divides into four triangles, and their internal angles give θ3 in terms of θ1 and θ2, which reduces everything to a two-joint problem. Bisecting the two remaining angles with the segments S and R lets the law of cosines solve the rest.

The arm closed into a polygon divided into four triangles
Closing the arm into a polygon of four triangles to define θ3.
Bisecting the joint angles with the segments S and R
Bisecting θ1 and θ2 with the segments S and R for the law of cosines.
The inverse kinematic equations for the three joint angles
The resulting inverse kinematic equations: target coordinates in, joint angles out.

The second method is an approximate local inversion. Instead of solving for an absolute coordinate, it takes a small displacement (Δx, Δy) and computes the joint angle changes that produce it. The forward kinematic functions are differentiable, so a small change in the angles maps to a small change in position through their Jacobian. As long as that Jacobian is not singular, the inverse function theorem lets me turn the relationship around and solve for the angle changes from the displacement.

The change in joint angles written as the inverse Jacobian of the forward kinematics times the change in position
Inverting the Jacobian of the forward kinematics turns a small displacement (Δx, Δy) into joint angle changes.

The entries of the Jacobian are just the partial derivatives of the forward kinematic functions, which I already had. The catch is singular configurations, where the Jacobian cannot be inverted. In two dimensions the simplest test is its determinant: where it goes to zero the method breaks down, so the software has to keep the arm away from those regions.

The determinant of the Jacobian expanded from the partial derivatives of the forward kinematic functions
The determinant of the Jacobian, which flags the singular configurations the arm has to stay away from.

Solving the inverted system with Cramer's rule gives a closed form for the angle changes in terms of the displacement.

The derivative inverse kinematic equations for the joint angle changes, solved with Cramer's rule
The derivative equations: a small displacement (Δx, Δy) in, the joint angle changes out.

It is less exact than the trigonometric method, but it scales: the same idea extends to 3D and to arms with more joints, where exact trigonometric solutions stop existing.

Both methods keep the hand parallel to the ground, which means the arm cannot approach objects from the top or the sides. The fix for this is the pivoting solution: rotate the hand joint while moving the wrist to new coordinates that keep the end-effector pinned in place. Both methods got their own version of this correction.

The software

Everything runs as C++ on the Arduino, structured as a small library. A ServoArm class wraps each motor and maps arm angles to servo instructions through a calibration range, since the cheap servos are imprecise and some are mounted mirrored. A Robot class coordinates the joints, exposes methods like moveBy and rotateHandBy, and can switch between the exact and the derivative method at runtime. Every projected move is checked against the servo limits and a stability threshold before it executes, and rejected moves are logged over serial.

The joysticks get their own classes that turn the analog readings into displacement deltas on every cycle of the control loop. The whole library is public on GitHub.

Testing it on people

To compare the two methods I ran tests with five participants. In the first protocol they piloted the end-effector along horizontal, vertical and diagonal paths with both methods, then answered yes-or-no questions about correctness, smoothness and input delay. In the second protocol they had to drive the arm to a marker on the table and tip it over within ten seconds, twice per method.

The experiment environment with the arm and the target marker
The test environment: pilot the end-effector to the marker and tip it over.
Overlaid arm positions during the horizontal path test

Horizontal path.

Overlaid arm positions during the vertical path test

Vertical path.

Overlaid arm positions during the 45 degree diagonal path test

Diagonal path, 45°.

Overlaid arm positions during the 135 degree diagonal path test

Diagonal path, 135°.

The robot printed its coordinates over serial every 100 ms, so every run could be plotted afterwards in GeoGebra. Of the 20 runs towards the object, 18 succeeded. The exact method took people to the target on straighter paths with fewer corrections, while the derivative method took longer on average, and the only two failed runs were both with it.

All 20 test trajectories towards the target object plotted on a graph
All 20 runs towards the target object, plotted from the serial output.
Chart of run times per participant and method
Time per run for each participant and method.

The pivoting test measured how far the end-effector drifts from its position while the hand rotates from 0° to 180° and back. The exact method stayed within 1.5 cm of the starting point. The derivative method accumulated error along the rotation and drifted 4.1 cm, which makes it unusable for pivoting in practice.

Multiple exposures of the pivoting test measured against a ruler
Multiple exposures of the pivoting test, measured against a ruler.

The takeaway was a tradeoff: the exact method wins on precision and smoothness but does not extend cleanly to 3D, while the derivative method is approximate but generalizes to higher dimensions and more joints. Most of the remaining error was hardware: cheap potentiometer servos with around 1° of resolution, hand-cut parts and link lengths that are hard to measure precisely. A 3D printer and better actuators would remove most of it. This project is also where the later arms started, and the idea of extending the kinematics to three dimensions eventually became the 6-axis arm I built at ETH.

This post is a condensed version of my Matura thesis, "Microcontroller-Driven Robotic Arm with Software Integration" (MNG Rämibühl, January 2024). The full paper, with the complete derivations, the code walkthrough and all test tables, is available as a PDF: read the full thesis.

The C++ library is on GitHub: github.com/alerest285/robotic_arm.