All projects

3-DOF Spherical Wrist

Forward-kinematics demonstration showing the 3-DOF wrist and its coordinate frames.
Forward kinematics

Joint-angle sliders update the DH frames and resulting wrist pose.

Kinematics derivation

The mechanism is a 3-DOF spherical wrist. Now, in order to define the kinematics, we need to define the joint frames. We’ll use the Denavit-Hartenberg convention to define the frames, mainly because that’s just what I learned in school. Typically, a conventional spherical wrist has all frame origins located at the wrist center, with the exception of the end-effector. This removes a lot of complexity, especially as we tackle the Jacobian later on. Something like this:

Conventional 3-DOF spherical wrist with intersecting joint axes and coincident frame origins.
Figure 1. Conventional spherical-wrist framing.

But you know what? That’s too easy. Since the formulas are already known and sitting in my old course textbook, let’s define a new wrist with some impractical frames. It’ll serve as a good test case for our derivations:

Custom 3-DOF wrist showing frame zero at the base, coincident frames one and two at the upper joint center, and frame three displaced at the output.
Figure 2. Custom wrist framing.

Here we have the base frame at the bottom joint, frames 1 and 2 offset to the ‘center’, and then frame 3 at the end effector. The first thing we need to do is define the DH parameters for this wrist. The table below shows the parameters for each joint:

  • αi\alpha_i: angle from k‾i−1\vect{k}_{i-1} to k‾i\vect{k}_i, about i‾i\vect{i}_i
  • did_i: distance between origins o∼i−1\underset{\sim}{o}_{i-1} and o∼i\underset{\sim}{o}_i, along k‾i−1\vect{k}_{i-1}
  • aia_i: distance between k‾i−1\vect{k}_{i-1} and k‾i\vect{k}_i, along i‾i\vect{i}_i
  • θi\theta_i: angle from i‾i−1\vect{i}_{i-1} to i‾i\vect{i}_i, about k‾i−1\vect{k}_{i-1}

Link 0 is just the base frame.

DHθi\theta_idid_iaia_iαi\alpha_i
Link 1q1q_1l1l_100−90∘-90^\circ
Link 2q2−90∘q_2-90^\circ0000−90∘-90^\circ
Link 3q3q_3l2l_2000∘0^\circ

l1l_1 is the distance from the base to the center of the wrist (o0o_0 to o1o_1), and l2l_2 is the distance from the center of the wrist to the end effector (o1o_1 to o3o_3). Now, we can use these parameters to define the transformation matrices for each joint.

For a revolute joint, we have the frame given by the following equations. Here, k×k\mathord{\times} is the skew-matrix of the kk vector:

Ci=Ci−1eθik×Ri,o∼i=o∼i−1+Ci−1eθik×δ‾i\begin{aligned} C_i &= C_{i-1} e^{\theta_i k\mathord{\times}} R_i, \\ \underset{\sim}{o}_i &= \underset{\sim}{o}_{i-1} + C_{i-1} e^{\theta_i k\mathord{\times}}\vect{\delta}_i \end{aligned}

And for a prismatic joint, we have the frame given by:

Ci=Ci−1Ri,o∼i=o∼i−1+Ci−1(δ‾i+dik)\begin{aligned} C_i &= C_{i-1} R_i, \\ \underset{\sim}{o}_i &= \underset{\sim}{o}_{i-1} + C_{i-1}\left(\vect{\delta}_i+d_i k\right) \end{aligned}

Using the parameters above, we can convert the DH parameters into homogeneous transformation matrices. We will then use these matrices to determine the overall transformation from the base frame to the end-effector frame, giving us the forward kinematics of the wrist.

[Cio∼i0‾ T1]=[Ci−1o∼i−10‾ T1][eθik×Rieθik×δ‾i0‾ T1]⏟i−1Ti\begin{bmatrix} C_i & \underset{\sim}{o}_i \\ \vect{0}^{\,T} & 1 \end{bmatrix} = \begin{bmatrix} C_{i-1} & \underset{\sim}{o}_{i-1} \\ \vect{0}^{\,T} & 1 \end{bmatrix} \underbrace{ \begin{bmatrix} e^{\theta_i k\mathord{\times}}R_i & e^{\theta_i k\mathord{\times}}\vect{\delta}_i \\ \vect{0}^{\,T} & 1 \end{bmatrix}}_{{}^{i-1}T_i}

Or, for prismatic joints:

[Cio∼i0‾ T1]=[Ci−1o∼i−10‾ T1][Riδ‾i+dik0‾ T1]⏟i−1Ti\begin{bmatrix} C_i & \underset{\sim}{o}_i \\ \vect{0}^{\,T} & 1 \end{bmatrix} = \begin{bmatrix} C_{i-1} & \underset{\sim}{o}_{i-1} \\ \vect{0}^{\,T} & 1 \end{bmatrix} \underbrace{ \begin{bmatrix} R_i & \vect{\delta}_i+d_i k \\ \vect{0}^{\,T} & 1 \end{bmatrix}}_{{}^{i-1}T_i}

And so, to combine these in a general formula for the homogeneous transformations, we can write:

i−1Ti=[eθik×Rieθik×(δ‾i+dik)0‾ T1]{}^{i-1}T_i = \begin{bmatrix} e^{\theta_i k\mathord{\times}}R_i & e^{\theta_i k\mathord{\times}}\left(\vect{\delta}_i+d_i k\right) \\ \vect{0}^{\,T} & 1 \end{bmatrix}

Now, using our DH parameters, we can write out the transformation matrices for each joint.

0T1=[eq1k×e−π2i×l1k0‾ T1]{}^{0}T_1 = \begin{bmatrix} e^{q_1 k\mathord{\times}}e^{-\frac{\pi}{2}i\mathord{\times}} & l_1 k \\ \vect{0}^{\,T} & 1 \end{bmatrix} 1T2=[e(q2−π2)k×e−π2i×00‾ T1]{}^{1}T_2 = \begin{bmatrix} e^{\left(q_2-\frac{\pi}{2}\right)k\mathord{\times}} e^{-\frac{\pi}{2}i\mathord{\times}} & 0 \\ \vect{0}^{\,T} & 1 \end{bmatrix} 2T3=[eq3k×l2k0‾ T1]{}^{2}T_3 = \begin{bmatrix} e^{q_3 k\mathord{\times}} & l_2 k \\ \vect{0}^{\,T} & 1 \end{bmatrix}

And there we go. We can now determine the forward kinematics of the wrist by multiplying these transformation matrices with one another.

And as a bit of an unnecessary but cleaner intermediate step, we can calculate all the transformations in base frame 0:

0T2=0T1 1T2,0T3=0T1 1T2 2T3=0T2 2T3\begin{aligned} {}^{0}T_2 &= {}^{0}T_1\,{}^{1}T_2, \\ {}^{0}T_3 &= {}^{0}T_1\,{}^{1}T_2\,{}^{2}T_3 = {}^{0}T_2\,{}^{2}T_3 \end{aligned}

And there we have it: the forward kinematics of the wrist. Now we can write the overall transformations as:

[C1o∼10‾ T1]=[C0o∼00‾ T1]0T1\begin{aligned} \begin{bmatrix} C_{1} & \underset{\sim}{o}_{1} \\ \vect{0}^{\,T} & 1 \end{bmatrix} &= \begin{bmatrix} C_{0} & \underset{\sim}{o}_{0} \\ \vect{0}^{\,T} & 1 \end{bmatrix} {}^{0}T_1 \end{aligned} [C2o∼20‾ T1]=[C0o∼00‾ T1]0T2=[C1o∼10‾ T1]1T2\begin{aligned} \begin{bmatrix} C_{2} & \underset{\sim}{o}_{2} \\ \vect{0}^{\,T} & 1 \end{bmatrix} &= \begin{bmatrix} C_{0} & \underset{\sim}{o}_{0} \\ \vect{0}^{\,T} & 1 \end{bmatrix} {}^{0}T_2 \\ &= \begin{bmatrix} C_{1} & \underset{\sim}{o}_{1} \\ \vect{0}^{\,T} & 1 \end{bmatrix} {}^{1}T_2 \end{aligned} [C3o∼30‾ T1]=[C0o∼00‾ T1]0T3=[C2o∼20‾ T1]2T3\begin{aligned} \begin{bmatrix} C_{3} & \underset{\sim}{o}_{3} \\ \vect{0}^{\,T} & 1 \end{bmatrix} &= \begin{bmatrix} C_{0} & \underset{\sim}{o}_{0} \\ \vect{0}^{\,T} & 1 \end{bmatrix} {}^{0}T_3 \\ &= \begin{bmatrix} C_{2} & \underset{\sim}{o}_{2} \\ \vect{0}^{\,T} & 1 \end{bmatrix} {}^{2}T_3 \end{aligned}

Velocity kinematics

I’ll be honest because this section was tough to revisit. I actually skipped it and jumped straight to inverse kinematics first. But, it makes the most sense to cover it right after forward kinematics, so here we go.

Angular Velocities

Back to the kinematics. Rather than looking at the whole transformation, including the frame and origin, let’s just look at the frame rotation matrices. These represent the pure orientation changes between frames.

C1=C0 0C1=C0Q,Q=0C1C_{1}=C_{0}\,{}^0C_1=C_{0}Q, \qquad Q={}^0C_1

Since QQ is a rotation matrix, it is orthogonal, so we can write:

QTQ=QQT=IQ^{T}Q = QQ^{T} = I

With a little bit of algebra, we can manipulate this to get:

Q˙=Q˙I=Q˙QTQ\dot{Q}=\dot{Q}I=\dot{Q}Q^TQ

Let’s define Ω=Q˙QT\Omega=\dot{Q}Q^T:

Q˙=ΩQ\dot{Q}=\Omega Q

To verify this property, let’s go back to the orthogonal identity and differentiate it:

QQT=I,Q˙QT+QQ˙T=0\begin{aligned} QQ^T&=I, \\ \dot{Q}Q^T+Q\dot{Q}^T&=0 \end{aligned}

Substituting Ω=Q˙QT\Omega=\dot{Q}Q^T yields:

Ω+ΩT=0\Omega+\Omega^T=0

or

ΩT=−Ω\Omega^T=-\Omega

Thus, Ω\Omega is skew-symmetric. This matrix allows us to extract the physical angular velocity vector. In robotics, the cross-product operator \mathord{\times} converts a 3-element velocity vector into this skew-symmetric matrix form.

Relating this back to those initial formulas, we can express the angular velocity matrix as

0w1,0×=Ω=Q˙QT=0C˙1(0C1)T{}^0w_{1,0}\mathord{\times} =\Omega =\dot{Q}Q^T ={}^0\dot{C}_1\left({}^0C_1\right)^T

Which, in this example, is the angular velocity of frame 1 with respect to frame 0, expressed in frame 0.

To retrieve the actual angular velocity vector, w‾1,0\vect{w}_{1,0}, we just multiply the angular velocity matrix by the base frame:

w‾1,0=C‾0 0w1,0\vect{w}_{1,0} = \vect{C}_0\,{}^0w_{1,0}

Now that we have that, and assuming we’ve calculated the homogeneous transformations, we can move on to calculating the angular velocities of each frame.First, we grab the angular velocities with respect to the previous frame only, expressed in that local frame:

0w1,0=θ˙1k,1w2,1=θ˙2k,2w3,2=θ˙3k\begin{aligned} {}^0w_{1,0}&=\dot{\theta}_1 k, \\ {}^1w_{2,1}&=\dot{\theta}_2 k, \\ {}^2w_{3,2}&=\dot{\theta}_3 k \end{aligned}

Remember that each kk is the unit z-axis of that respective joint frame about which the rotation occurs.

It is important to remember that angular velocities simply add together, but we need to make sure they are all expressed in the exact same coordinate frame. To make them all relative to and expressed in the base frame 0, we rotate the local vectors forward:

0w2,0=0w1,0+0C1 1w2,10w3,0=0w2,0+0C2 2w3,2\begin{aligned} {}^0w_{2,0}&={}^0w_{1,0}+{}^0C_1\,{}^1w_{2,1} \\ {}^0w_{3,0}&={}^0w_{2,0}+{}^0C_2\,{}^2w_{3,2} \end{aligned}

Finally, we determine the global vectors:

w‾2,0=w‾1,0+C‾1 1w2,1w‾3,0=w‾2,0+C‾2 2w3,2\begin{aligned} \vect{w}_{2,0} = \vect{w}_{1,0} + \vect{C}_1\,{}^1w_{2,1} \\ \vect{w}_{3,0} = \vect{w}_{2,0} + \vect{C}_2\,{}^2w_{3,2} \end{aligned}

And there we have it, the final angular velocity vectors for our spherical wrist.

Translational velocities

Let’s move on to the translational velocities, or o˙∼n\underset{\sim}{\dot{o}}_n.

These represent the linear velocities of the origins of each frame with respect to the base frame. We can use the angular velocities we just calculated to find them. While a more direct formula is typically used for the Jacobian, deriving this from first principles helps show exactly what is happening under the hood.

We start by extracting the position vectors of the frame origins from the fourth column of our homogeneous transformation matrices:

o∼1=(0T1)0:3, 3o∼2=(0T2)0:3, 3o∼3=(0T3)0:3, 3\begin{aligned} \underset{\sim}{o}_1&=\left({}^0T_1\right)_{0:3,\,3} \\ \underset{\sim}{o}_2&=\left({}^0T_2\right)_{0:3,\,3} \\ \underset{\sim}{o}_3&=\left({}^0T_3\right)_{0:3,\,3} \end{aligned}

The base origin is simply defined as o∼0=0‾\underset{\sim}{o}_0=\vect{0}.

Calculating the velocity of each individual frame origin step-by-step down the chain is the first method I learned as it provides a very intuitive approach, so we’ll do the same here. For this, we essentailly track the physical motion outwards, building the velocity profile link by link.

To find how each joint’s rotation we need the radius vectors. Here, we will define each radius as the displacement between the consecutive origins:

r‾1=o∼1−o∼0r‾2=o∼2−o∼1r‾3=o∼3−o∼2\begin{aligned} \vect{r}_1&=\underset{\sim}{o}_1-\underset{\sim}{o}_0 \\ \vect{r}_2&=\underset{\sim}{o}_2-\underset{\sim}{o}_1 \\ \vect{r}_3&=\underset{\sim}{o}_3-\underset{\sim}{o}_2 \end{aligned}

We then propagate the velocity outwards from the base. Each link inherits the linear velocity of the previous origin and adds its own rotational cross-product contribution:

o˙∼1=o˙∼0+w‾1,0×r‾1o˙∼2=o˙∼1+w‾2,0×r‾2o˙∼3=o˙∼2+w‾3,0×r‾3\begin{aligned} \underset{\sim}{\dot{o}}_1&=\underset{\sim}{\dot{o}}_0 +\vect{w}_{1,0}\times\vect{r}_{1} \\ \underset{\sim}{\dot{o}}_2&=\underset{\sim}{\dot{o}}_1 +\vect{w}_{2,0}\times\vect{r}_{2} \\ \underset{\sim}{\dot{o}}_3&=\underset{\sim}{\dot{o}}_2 +\vect{w}_{3,0}\times\vect{r}_{3} \end{aligned}

As you can imagine, this link-by-link approach, calculating every intermediate step, can get a bit tedious if you only care about the end-effector.

Direct Jacobian Method: each joint’s contribution to the end-effector velocity

Rather than looking at step-by-step segments, let’s instead look directly at how each joint’s rotation impacts the end-effector origin o∼3\underset{\sim}{o}_{3} directly.

To do this, we change our perspective on the radius vectors. The radius vector becomes the total lever arm extending from the joint origin in question all the way to that final endpoint:

r‾1=o∼3−o∼0r‾2=o∼3−o∼1r‾3=o∼3−o∼2\begin{aligned} \vect{r}_1&=\underset{\sim}{o}_3-\underset{\sim}{o}_0 \\ \vect{r}_2&=\underset{\sim}{o}_3-\underset{\sim}{o}_1 \\ \vect{r}_3&=\underset{\sim}{o}_3-\underset{\sim}{o}_2 \end{aligned}

Using these lever arms, the cross product w‾i,i−1×0r‾i\vect{w}_{i,i-1}\times{}^0\vect{r}_i gives the linear velocity contribution of each individual joint to the final tip. Summing up the base velocity and the individual joint contributions yields the same total linear velocity of our end-effector origin:

o˙∼3=o˙∼0+(w‾1,0×r‾1)+(w‾2,1×r‾2)+(w‾3,2×r‾3)\begin{aligned} \underset{\sim}{\dot{o}}_3&=\underset{\sim}{\dot{o}}_0 + \left(\vect{w}_{1,0}\times \vect{r}_1\right) + \left(\vect{w}_{2,1}\times\vect{r}_2\right) + \left(\vect{w}_{3,2}\times \vect{r}_3\right) \end{aligned}

This structural relationship is exactly what lets us build the geometric manipulator Jacobian. The Jacobian maps joint velocities directly to end-effector velocities:

[o˙∼nw‾n,0]=[J‾1⋯J‾n]q˙‾=J(q‾)q˙‾\begin{aligned} \begin{bmatrix} \underset{\sim}{\dot{o}}_n \\ \vect{w}_{n,0} \end{bmatrix} &= \begin{bmatrix} \vect{J}_1 & \cdots & \vect{J}_n \end{bmatrix} \vect{\dot{q}} \\ &=J(\vect{q})\vect{\dot{q}} \end{aligned}

By separating out the individual (q˙i\dot{q}_{i}) terms from our velocity equations, we reveal the standard column formula for a revolute joint:

J‾i=[k‾i−1×(o∼n−o∼i−1)k‾i−1]\vect{J}_i = \begin{bmatrix} \vect{k}_{i-1}\times \left(\underset{\sim}{o}_n-\underset{\sim}{o}_{i-1}\right) \\ \vect{k}_{i-1} \end{bmatrix}

I know we didn’t really cover it, but for a prismatic joint, there is no angular velocity, only linear velocity along the direction of the joint. So, the Jacobian column for a prismatic joint is simply:

J‾i=[k‾i−10‾]\vect{J}_i = \begin{bmatrix} \vect{k}_{i-1} \\ \vect{0} \end{bmatrix}

Inverse kinematics

Since we went with our own custom wrist frames, the inverse kinematics will need to be derived manually. For a more conventional spherical wrist with all the frames meeting perfectly at the center, the inverse kinematics is well known and can be found in many textbooks. Our inverse kinematics formulas are still quite similar, but let’s go ahead and derive them from scratch.

Our inverse kinematics are still similar, but again, let’s go ahead and derive them.

To start, suppose we know C3C_{3} and o∼3\underset{\sim }{o}{}_{3}, which represent our target end-effector frame orientation and position. We want to find the corresponding joint angles θ1\theta _{1}, θ2\theta _{2}, and θ3\theta _{3} to achieve this target. We can begin by looking at our forward-kinematics equations and doing some algebra to isolate the joint angles, keeping in mind the custom frames we defined and tracking which vectors line up.

C3=eθ1k‾0×eθ2k‾1×eθ3k‾2×C‾0=eθ1k‾0×eθ2j‾0×eθ3i‾0×C‾0=C‾0eθ1k×eθ2j×eθ3i×=eθ1k‾0×C‾0eθ2j×eθ3i×\begin{aligned} C_3 &=e^{\theta_1\vect{k}_0\mathord{\times}} e^{\theta_2\vect{k}_1\mathord{\times}} e^{\theta_3\vect{k}_2\mathord{\times}} \vect{C}_0 \\ &=e^{\theta_1\vect{k}_0\mathord{\times}} e^{\theta_2\vect{j}_0\mathord{\times}} e^{\theta_3\vect{i}_0\mathord{\times}} \vect{C}_0 \\ &= \vect{C}_0 e^{\theta_1 k\mathord{\times}} e^{\theta_2 j\mathord{\times}} e^{\theta_3 i\mathord{\times}} \\ &=e^{\theta_1\vect{k}_0\mathord{\times}} \vect{C}_0 e^{\theta_2 j\mathord{\times}} e^{\theta_3 i\mathord{\times}} \end{aligned}

we post-multiply both sides (multiply on the right) by e−θ3i×e^{-\theta_3 i\mathord{\times}}. This corresponds to a rotation of −θ3-\theta _{3} about the i‾3\vect{i}_3 axis and cleanly cancels out that last rotation term.

C‾3e−θ3i×=eθ1k‾3×C‾3=eθ1k‾0×C‾0eθ2j×\begin{aligned} \vect{C}_3e^{-\theta_3 i\mathord{\times}} &=e^{\theta_1\vect{k}_3\mathord{\times}}\vect{C}_3 \\ &=e^{\theta_1\vect{k}_0\mathord{\times}}\vect{C}_0 e^{\theta_2 j\mathord{\times}} \end{aligned}

Now we’re on to something, but we still have too many variables. We need to remove at least one of them. For θ2\theta _{2}, the rotation happens entirely about the jj axis. If we isolate only the jj component of our matrix equation, the θ2\theta _{2} term drops out because a frame rotating around its own jj axis leaves that axis unchanged.

e−θ3k‾3×C3j=eθ1k‾0×C0j,eθ2j×j=Ie^{-\theta_3\vect{k}_3\mathord{\times}}C_3j =e^{\theta_1\vect{k}_0\mathord{\times}}C_0j, \qquad e^{\theta_2 j\mathord{\times}}j=I

By looking at this shared relationship from both the base side and the end-effector side, we can break the main equation into two separate vector conditions.

eθ1k‾0×j‾0=k‾1,e−θ3k‾3×j‾3=−k‾1.\begin{aligned} e^{\theta_1\vect{k}_0\mathord{\times}}\vect{j}_0 &=\vect{k}_1, \\ e^{-\theta_3\vect{k}_3\mathord{\times}}\vect{j}_3 &=-\vect{k}_1. \end{aligned}

These equations do not bring both vectors onto the exact same target vector. In a conventional wrist they would, but our custom layout changes things. The first equation rotates j‾0\vect{j}_0 onto k‾1\vect{k}_1, while the second rotates j‾3\vect{j}_3 onto −k‾1-\vect{k}_1. This opposite sign in the second equation comes directly from our DH frame definitions where at θ3=0\theta_3=0 we have j‾3=j‾2=−k‾1\vect{j}_3=\vect{j}_2=-\vect{k}_1.

Even though the two target vectors end up with opposite signs, both equations still depend on that exact same intermediate joint-axis direction, k‾1\vect{k}_1. From our frame geometry, k‾1\vect{k}_1 is perpendicular to both k‾0\vect{k}_0 and k‾3\vect{k}_3. Therefore, as long as we are in a nonsingular pose, we can calculate k‾1\vect{k}_1 directly.

k‾1=±k‾0×k‾3∥k‾0×k‾3∥.\vect{k}_1 = \pm \frac{\vect{k}_0\times\vect{k}_3} {\left\lVert\vect{k}_0\times\vect{k}_3\right\rVert}.

The two possible signs give us the two standard Euler-angle branches of the inverse-kinematics solution. We will just carry this ±\pm choice through the math for now and return to what it physically means after we finish solving for the three joint angles.

k‾1=±k‾0×k‾3∥k‾0×k‾3∥\vect{k}_1 =\pm\frac{\vect{k}_0\times\vect{k}_3} {\left\lVert\vect{k}_0\times\vect{k}_3\right\rVert}

Now let’s look closely at how those three frames relate geometrically.

For clarity, we can draw these direction vectors with a common tail even though their coordinate-frame origins do not actually sit at the exact same point in space. This visual shift does not alter our vector math. Since ini_{n}, jnj_{n}, and knk_{n} are free unit direction vectors expressed in frame 0, their dot products, cross products, sums, and differences remain completely independent of where we choose to sketch them. The physical joint axes themselves still intersect at the wrist center where O1=O2O_1=O_2.

eθ1k‾0×j‾0=k‾1e^{\theta_1\vect{k}_0\mathord{\times}}\vect{j}_0=\vect{k}_1

This equation shows that rotating the direction vector j‾0\vect{j}_0 about k‾0\vect{k}_0 by θ1\theta_1 produces k‾1\vect{k}_1. It does not mean that the physical base frame is being rotated. Rather, the rotation operator is being applied to a copy of the known direction vector j‾0\vect{j}_0.

Two-part theta-one vector construction showing \(j_0\) rotating toward \(k_1\) about \(k_0\), followed by the sum-and-difference triangle used for the half-angle relationship.
Figure 3. Vector construction for θ₁.

Now we have our constructed triangle with our three vectors. The trick is to translate the vector k‾1−j‾0\vect{k}_1-\vect{j}_0 to form two right triangles:

∣θ1∣2=arctan⁡(∥12(k‾1−j‾0)∥∥12(k‾1+j‾0)∥)∣θ1∣=2arctan⁡(∥k‾1−j‾0∥∥k‾1+j‾0∥)\begin{aligned} \frac{|\theta_1|}{2} &=\arctan\left( \frac{\left\lVert\frac{1}{2}(\vect{k}_1-\vect{j}_0)\right\rVert} {\left\lVert\frac{1}{2}(\vect{k}_1+\vect{j}_0)\right\rVert} \right) \\ |\theta_1| &= 2\arctan\left( \frac{\left\lVert\vect{k}_1-\vect{j}_0\right\rVert} {\left\lVert\vect{k}_1+\vect{j}_0\right\rVert} \right) \end{aligned}

j‾0\vect{j}_0 is known from the base frame, k‾3\vect{k}_3 is known from the end-effector frame, and k‾1\vect{k}_1 \vect{k}_1 comes directly from our cross-product formula.

To determine the sign of θ1\theta_1, we need to check whether k‾1\vect{k}_1 lies on the positive or negative side of our rotation axis. To do this, recall from figure 3 that positive right-handed rotation about +k0+k_0 carries j0j_0 toward −i0-i_0. Therefore, a positive θ1\theta_1 gives k1k_1 a negative i0i_0 component.

sign⁡(θ1)=−sign⁡(k‾1Ti‾0)\begin{aligned} \operatorname{sign}(\theta_1) % &=-\operatorname{sign}\left( % \vect{k}_1\cdot(\vect{j}_0\times\vect{k}_0) % \right) \\\ &=-\operatorname{sign}\left(\vect{k}_1^T\vect{i}_0\right) \end{aligned}

We can use this exact same triangle trick to solve for θ3\theta _{3}, but we have to keep our custom negative sign in mind. At θ3=0\theta_3=0, our DH definitions state that j3=j2=−k1j_3=j_2=-k_1. Since frame 3 is built by rotating frame 2 about their common axis k2=k3k_2=k_3, we can set up the rotation tracking.

j3=eθ3k3×(−k1),j_3=e^{\theta_3 k_{3} \times}(-k_1),

Or, as we found earlier:

e−θ3k‾3×j‾3=−k‾1e^{-\theta_3\vect{k}_3\mathord{\times}}\vect{j}_3=-\vect{k}_1

k‾1\vect{k}_1, j‾3\vect{j}_3, and k‾3\vect{k}_3 are all known.

Since k‾1\vect{k}_1, j‾3\vect{j}_3, and k‾3\vect{k}_3 are all known quantities, we can solve the triangle directly.

e−θ3k‾3×j‾3=−k‾1,∣θ3∣=2arctan⁡(∥j‾3−(−k‾1)∥∥j‾3+(−k‾1)∥)=2arctan⁡(∥j‾3+k‾1∥∥j‾3−k‾1∥),sign⁡(θ3)=sign⁡((−k‾1)Ti‾3)\begin{aligned} e^{-\theta_3\vect{k}_3\mathord{\times}}\vect{j}_3&=-\vect{k}_1, \\ \left|\theta_3\right| &=2\arctan\left( \frac{\left\lVert\vect{j}_3-(-\vect{k}_1)\right\rVert} {\left\lVert\vect{j}_3+(-\vect{k}_1)\right\rVert} \right) \\ &=2\arctan\left( \frac{\left\lVert\vect{j}_3+\vect{k}_1\right\rVert} {\left\lVert\vect{j}_3-\vect{k}_1\right\rVert} \right), \\ \operatorname{sign}(\theta_3) &=\operatorname{sign}\left((-\vect{k}_1)^T\vect{i}_3\right) \end{aligned}

For the sign, again we are essentially checking whether −k‾1-\vect{k}_1 lies on the positive or negative side of the rotation axis k‾3\vect{k}_3. A positive θ3\theta_3 rotates −k‾1-\vect{k}_1 toward i‾3\vect{i}_3. Therefore, to determine the sign we check the projection of −k‾1-\vect{k}_1 onto i‾3\vect{i}_3.

Revisiting θ2\theta_2, we can use a similar approach and the same tricks. But instead of deriving the full the algebraic formula and equivalent vectors, at this point we can just look at it:

j‾1=−k‾0i‾1=j‾1×k‾1eθ2k‾1×i‾1=k‾2=k‾3\begin{aligned} \vect{j}_1 = -\vect{k}_0 \\ \vect{i}_1 = \vect{j}_1 \times \vect{k}_1 \\ e^{\theta_2\vect{k}_1\mathord{\times}}\vect{i}_1 =\vect{k}_2 =\vect{k}_3 \end{aligned}

And again, we have:

∣θ2∣=2arctan⁡(∥k‾3−i‾1∥∥k‾3+i‾1∥),sign⁡(θ2)=−sign⁡(k‾3Tk‾0)\begin{aligned} \left|\theta_2\right| &=2\arctan\left( \frac{\left\lVert\vect{k}_3-\vect{i}_1\right\rVert} {\left\lVert\vect{k}_3+\vect{i}_1\right\rVert} \right), \\ \operatorname{sign}(\theta_2) &=-\operatorname{sign}\left(\vect{k}_3^T\vect{k}_0\right) \end{aligned}

And there we have it: θ1\theta_1, θ2\theta_2, and θ3\theta_3 solved for one nonsingular IK branch. There is still one important detail hiding in our first equation for k‾1\vect{k}_1: the ±\pm means that there are two possible angle solutions for the same end-effector pose.

We can see where those two solutions come from by going back to the cross product:

k‾0×k‾3=cos⁡(θ2)k‾1\vect{k}_0\times\vect{k}_3 = \cos(\theta_2)\vect{k}_1

If we take the positive normalized cross product, we are choosing the branch where cos⁡(θ2)>0\cos(\theta_2)>0, or equivalently

−π2<θ2<π2.-\frac{\pi}{2}<\theta_2<\frac{\pi}{2}.

This is the branch returned by our Python implementation. Taking the negative direction for k‾1\vect{k}_1 gives the other Euler-angle solution, which uses different joint angles but still produces the same end-effector pose. In a more complete solver, we could choose between the two using joint limits, collision checks, or trajectory optimization. That is a bit beyond what we need here though, so we simply use the positive normalized cross product.

Finally, if k‾0×k‾3=0\vect{k}_0\times\vect{k}_3=0, we cannot normalize the cross product at all. This is the wrist’s singular configuration: k‾1\vect{k}_1 is no longer uniquely determined, which also means that θ1\theta_1 and θ3\theta_3 cannot be recovered individually.

Wrist singularities

Certain configurations create singularities in this wrist, which we can see directly using the Jacobian matrix. A singularity does not mean that the wrist is completely stuck. It means that two Jacobian columns have become linearly dependent, meaning two separate joints are producing the same instantaneous direction of motion. As a result, the wrist temporarily loses one independent degree of freedom (the Jacobian loses rank).

For the angular part of our Jacobian, each column represents one joint axis expressed in the base frame 0.

Jω(q‾)=[0k‾00k‾10k‾2],0w3,0=Jω(q‾)q˙‾.J_\omega(\vect{q}) = \begin{bmatrix} {}^0\vect{k}_0 & {}^0\vect{k}_1 & {}^0\vect{k}_2 \end{bmatrix}, \qquad {}^0w_{3,0}=J_\omega(\vect{q})\vect{\dot{q}}.

To see where the determinant comes from, we can write each of those axes in frame 0 coordinates.

0k‾0=[001],0k‾1=[−sin⁡θ1cos⁡θ10],0k‾2=[cos⁡θ1cos⁡θ2sin⁡θ1cos⁡θ2−sin⁡θ2].\begin{aligned} {}^0\vect{k}_0 &=\begin{bmatrix}0\\0\\1\end{bmatrix}, \\ {}^0\vect{k}_1 &=\begin{bmatrix}-\sin\theta_1\\\cos\theta_1\\0\end{bmatrix}, \\ {}^0\vect{k}_2 &=\begin{bmatrix} \cos\theta_1\cos\theta_2\\ \sin\theta_1\cos\theta_2\\ -\sin\theta_2 \end{bmatrix}. \end{aligned}

Placing those three vectors into the columns of our angular Jacobian, JωJ_\omega, gives:

Jω=[0−sin⁡θ1cos⁡θ1cos⁡θ20cos⁡θ1sin⁡θ1cos⁡θ210−sin⁡θ2].J_\omega = \begin{bmatrix} 0 & -\sin\theta_1 & \cos\theta_1\cos\theta_2 \\ 0 & \cos\theta_1 & \sin\theta_1\cos\theta_2 \\ 1 & 0 & -\sin\theta_2 \end{bmatrix}.

Because the first column contains only one nonzero entry, expanding the determinant along that column keeps the algebra fairly simple.

det⁡(Jω)=∣−sin⁡θ1cos⁡θ1cos⁡θ2cos⁡θ1sin⁡θ1cos⁡θ2∣=−sin⁡2θ1cos⁡θ2−cos⁡2θ1cos⁡θ2=−(sin⁡2θ1+cos⁡2θ1)cos⁡θ2=−cos⁡θ2.\begin{aligned} \det\left(J_\omega\right) &= \begin{vmatrix} -\sin\theta_1 & \cos\theta_1\cos\theta_2 \\ \cos\theta_1 & \sin\theta_1\cos\theta_2 \end{vmatrix} \\ &=-\sin^2\theta_1\cos\theta_2 -\cos^2\theta_1\cos\theta_2 \\ &=-\left(\sin^2\theta_1+\cos^2\theta_1\right)\cos\theta_2 \\ &=-\cos\theta_2. \end{aligned}

For our custom wrist definitions, the final determinant expression works out to:

det⁡(Jω)=−cos⁡θ2.\det\left(J_\omega\right)=-\cos\theta_2.

Therefore, the wrist hits a singularity for the condition:

θ2=±90∘+n180∘,n∈Z.\theta_2=\pm90^\circ + n180^\circ, \quad n\in\mathbb{Z}.

At these specific configurations, k‾2\vect{k}_2 aligns parallel or antiparallel to k‾0\vect{k}_0. In other words, joints 1 and 3 are rotating about the exact same physical line in space, meaning their velocity contributions are no longer independent. The Jacobian drops from rank 3 to rank 2. The wrist can still rotate in two independent directions, but it cannot instantaneously produce an angular velocity in every possible direction.

This also explains the failure case in the inverse kinematics derivation. We calculated k‾1\vect{k}_1 using k‾0×k‾3\vect{k}_0\times\vect{k}_3, but at the singularity, k‾0\vect{k}_0 and k‾3\vect{k}_3 become parallel, causing that cross product to collapse to zero. The individual values of θ1\theta _{1} and θ3\theta _{3} are no longer unique because only their combined, cumulative effect is defined.

One last interesting note is that near the singularity, although the Jacobian has not quite lost rank, it becomes poorly conditioned. This means that a small requested change in end-effector velocity might demand very large or conflicting joint speeds. In a real-life implementation, these speed spikes may stress the motors and mechanical joints, causing premature wear or failure. And so, these poorly conditioned zones need to be actively avoided just as much as the singularity itself.