Email me
← Writing

Learning note · Robotics maths, part 1 ·

2D and 3D rotation matrices

Building rotation matrices from the ground up, with every multiplication worked by hand, ending at the orientation matrix the balancing robot gives its IMU.

I write these while learning the topic, to check I understand it. If something here is wrong, I'd like to know.

This note builds rotation matrices from the ground up and shows the arithmetic in detail.

Convention used in this note

  • right-handed coordinate system
  • column vectors
  • active rotations: the vector rotates while the coordinate frame stays fixed
  • positive rotations follow the right-hand rule

Other conventions exist, so always check the one used by a robotics library or lecture.

What a rotation matrix is trying to do

Suppose a robot has a vector pointing in some direction:

v=[xy]or in 3Dv=[xyz]v= \begin{bmatrix} x\\ y \end{bmatrix} \qquad\text{or in 3D}\qquad v= \begin{bmatrix} x\\ y\\ z \end{bmatrix}

A rotation matrix changes the direction of that vector without changing its length. The basic operation is:

v′=Rv\boxed{v'=Rv}

where vv is the original vector, RR is the rotation matrix and v′v' is the rotated vector.

The matrix does not mean “multiply matching-looking numbers together”. Matrix multiplication follows a specific row-by-column rule.

Matrix multiplication refresher

Suppose:

A=[abcd],v=[xy]A= \begin{bmatrix} a&b\\ c&d \end{bmatrix}, \qquad v= \begin{bmatrix} x\\ y \end{bmatrix}

The first answer value comes from the first row of the matrix dotted with the vector, ax+byax+by. The second comes from the second row, cx+dycx+dy. So:

Av=[ax+bycx+dy]\boxed{ Av= \begin{bmatrix} ax+by\\ cx+dy \end{bmatrix} }

Worked mini-example

A=[2345],v=[1020]A= \begin{bmatrix} 2&3\\ 4&5 \end{bmatrix}, \qquad v= \begin{bmatrix} 10\\ 20 \end{bmatrix}

First row times the column:

(2)(10)+(3)(20)=20+60=80(2)(10)+(3)(20)=20+60=80

Second row times the column:

(4)(10)+(5)(20)=40+100=140(4)(10)+(5)(20)=40+100=140

Therefore:

Av=[80140]Av= \begin{bmatrix} 80\\ 140 \end{bmatrix}

You always use addition between the products. For a rotation matrix, the negative signs are already built into the matrix entries.

The standard 2D rotation matrix

The standard counterclockwise 2D rotation matrix is:

R(θ)=[cos⁡θ−sin⁡θsin⁡θcos⁡θ]\boxed{ R(\theta)= \begin{bmatrix} \cos\theta & -\sin\theta\\ \sin\theta & \cos\theta \end{bmatrix} }

where θ\theta is the rotation angle. Multiplying it out row-by-column:

[x′y′]=[cos⁡θ−sin⁡θsin⁡θcos⁡θ][xy]\begin{bmatrix} x'\\ y' \end{bmatrix} = \begin{bmatrix} \cos\theta & -\sin\theta\\ \sin\theta & \cos\theta \end{bmatrix} \begin{bmatrix} x\\ y \end{bmatrix}

The first row gives:

x′=xcos⁡θ−ysin⁡θ\boxed{x'=x\cos\theta-y\sin\theta}

and the second:

y′=xsin⁡θ+ycos⁡θ\boxed{y'=x\sin\theta+y\cos\theta}

Worked 2D examples

Rotate (1, 0) by 90°

We expect a vector pointing along positive xx to rotate counterclockwise until it points along positive yy. At 90∘90^\circ, cos⁡90∘=0\cos90^\circ=0 and sin⁡90∘=1\sin90^\circ=1, so:

R(90∘)=[0−110]R(90^\circ)= \begin{bmatrix} 0&-1\\ 1&0 \end{bmatrix}

Multiply:

v′=[0−110][10]v'= \begin{bmatrix} 0&-1\\ 1&0 \end{bmatrix} \begin{bmatrix} 1\\ 0 \end{bmatrix} x′=(0)(1)+(−1)(0)=0y′=(1)(1)+(0)(0)=1x'=(0)(1)+(-1)(0)=0 \qquad y'=(1)(1)+(0)(0)=1 v′=[01]\boxed{ v'= \begin{bmatrix} 0\\ 1 \end{bmatrix} }

That is exactly the (1,0)→(0,1)(1,0)\rightarrow(0,1) we expected before calculating. This kind of geometric sanity check is extremely useful in robotics.

Rotate (2, 1) by 90°

Using the same R(90∘)R(90^\circ):

x′=(0)(2)+(−1)(1)=−1y′=(1)(2)+(0)(1)=2x'=(0)(2)+(-1)(1)=-1 \qquad y'=(1)(2)+(0)(1)=2 v′=[−12]\boxed{ v'= \begin{bmatrix} -1\\ 2 \end{bmatrix} }

Check the length before and after:

∥v∥=22+12=5∥v′∥=(−1)2+22=5\|v\|=\sqrt{2^2+1^2}=\sqrt5 \qquad \|v'\|=\sqrt{(-1)^2+2^2}=\sqrt5

The direction changed and the length did not, which is exactly what a pure rotation should do.

Rotate (1, 1) by 45°

Recall cos⁡45∘=sin⁡45∘=22≈0.7071\cos45^\circ=\sin45^\circ=\frac{\sqrt2}{2}\approx0.7071, so:

R(45∘)=[0.7071−0.70710.70710.7071]R(45^\circ)= \begin{bmatrix} 0.7071&-0.7071\\ 0.7071&0.7071 \end{bmatrix} x′=(0.7071)(1)+(−0.7071)(1)=0y′=(0.7071)(1)+(0.7071)(1)=1.4142x'=(0.7071)(1)+(-0.7071)(1)=0 \qquad y'=(0.7071)(1)+(0.7071)(1)=1.4142 v′≈[01.4142]\boxed{ v'\approx \begin{bmatrix} 0\\ 1.4142 \end{bmatrix} }

(1,1)(1,1) already points at 45∘45^\circ. Rotating it another 45∘45^\circ should make it point straight up, and it does.

Direction convention

With this convention a positive angle is counterclockwise and a negative angle is clockwise. A clockwise rotation by θ\theta is R(−θ)R(-\theta), and using cos⁡(−θ)=cos⁡θ\cos(-\theta)=\cos\theta and sin⁡(−θ)=−sin⁡θ\sin(-\theta)=-\sin\theta:

R(−θ)=[cos⁡θsin⁡θ−sin⁡θcos⁡θ]\boxed{ R(-\theta)= \begin{bmatrix} \cos\theta&\sin\theta\\ -\sin\theta&\cos\theta \end{bmatrix} }

Example: clockwise 90°

R(−90∘)=[01−10]R(-90^\circ)= \begin{bmatrix} 0&1\\ -1&0 \end{bmatrix}

Applied to (1,0)(1, 0):

x′=(0)(1)+(1)(0)=0y′=(−1)(1)+(0)(0)=−1x'=(0)(1)+(1)(0)=0 \qquad y'=(-1)(1)+(0)(0)=-1

which is exactly a 90∘90^\circ clockwise turn from positive xx.

Moving from 2D to 3D

In 3D the rotation matrix is 3×33\times3. The key idea:

When rotating around one axis, the coordinate along that axis stays unchanged.

Rotating around the zz-axis changes xx and yy and leaves zz alone. That is why the 2D rotation pattern appears inside each 3D matrix.

Rotation about the x-axis

Rx(ϕ)=[1000cos⁡ϕ−sin⁡ϕ0sin⁡ϕcos⁡ϕ]\boxed{ R_x(\phi)= \begin{bmatrix} 1&0&0\\ 0&\cos\phi&-\sin\phi\\ 0&\sin\phi&\cos\phi \end{bmatrix} }

ϕ\phi is commonly used for roll. The first row, [100]\begin{bmatrix}1&0&0\end{bmatrix}, guarantees x′=xx'=x while the yy-zz plane rotates.

Example: 90° about x

Rx(90∘)=[10000−1010]R_x(90^\circ)= \begin{bmatrix} 1&0&0\\ 0&0&-1\\ 0&1&0 \end{bmatrix}

Rotate the vector along positive yy, v=(0,1,0)v=(0,1,0):

x′=(1)(0)+(0)(1)+(0)(0)=0x'=(1)(0)+(0)(1)+(0)(0)=0 y′=(0)(0)+(0)(1)+(−1)(0)=0y'=(0)(0)+(0)(1)+(-1)(0)=0 z′=(0)(0)+(1)(1)+(0)(0)=1z'=(0)(0)+(1)(1)+(0)(0)=1

So a positive 90∘90^\circ rotation about xx sends positive yy to positive zz, following the right-hand rule.

Rotation about the y-axis

Ry(θ)=[cos⁡θ0sin⁡θ010−sin⁡θ0cos⁡θ]\boxed{ R_y(\theta)= \begin{bmatrix} \cos\theta&0&\sin\theta\\ 0&1&0\\ -\sin\theta&0&\cos\theta \end{bmatrix} }

The middle row guarantees y′=yy'=y.

Example: 90° about y

Ry(90∘)=[001010−100]R_y(90^\circ)= \begin{bmatrix} 0&0&1\\ 0&1&0\\ -1&0&0 \end{bmatrix}

Rotate positive xx, v=(1,0,0)v=(1,0,0):

x′=0y′=0z′=(−1)(1)+(0)(0)+(0)(0)=−1x'=0 \qquad y'=0 \qquad z'=(-1)(1)+(0)(0)+(0)(0)=-1

So positive xx rotates toward negative zz. The sign placement is the reverse of RxR_x and RzR_z, which is why RyR_y often feels less intuitive at first.

Rotation about the z-axis

Rz(ψ)=[cos⁡ψ−sin⁡ψ0sin⁡ψcos⁡ψ0001]\boxed{ R_z(\psi)= \begin{bmatrix} \cos\psi&-\sin\psi&0\\ \sin\psi&\cos\psi&0\\ 0&0&1 \end{bmatrix} }

ψ\psi is commonly used for yaw. This is the 2D rotation matrix placed in the xx-yy corner of a 3×33\times3 matrix, and zz stays unchanged.

A 3D example

Rotate v=(2,1,3)v=(2,1,3) by 90∘90^\circ about zz:

[0−10100001][213]=[(0)(2)+(−1)(1)+(0)(3)(1)(2)+(0)(1)+(0)(3)(0)(2)+(0)(1)+(1)(3)]=[−123]\begin{bmatrix} 0&-1&0\\ 1&0&0\\ 0&0&1 \end{bmatrix} \begin{bmatrix} 2\\ 1\\ 3 \end{bmatrix} = \begin{bmatrix} (0)(2)+(-1)(1)+(0)(3)\\ (1)(2)+(0)(1)+(0)(3)\\ (0)(2)+(0)(1)+(1)(3) \end{bmatrix} = \begin{bmatrix} -1\\ 2\\ 3 \end{bmatrix}

z=3z=3 did not change, because zz was the rotation axis. This is a useful way to reconstruct the three standard matrices rather than memorising them: the axis row and column are zeros with a 11 where they cross, and the other four entries are the 2D pattern.

Properties of rotation matrices

A valid pure rotation matrix satisfies:

RTR=IR^TR=I

which means its rows and columns are mutually perpendicular unit vectors. It follows that:

R−1=RTdet⁡(R)=1\boxed{R^{-1}=R^T} \qquad \boxed{\det(R)=1}

A determinant of −1-1 would indicate a reflection rather than a proper rotation.

Checking it for 90° about z

RTR=[010−100001][0−10100001]R^TR= \begin{bmatrix} 0&1&0\\ -1&0&0\\ 0&0&1 \end{bmatrix} \begin{bmatrix} 0&-1&0\\ 1&0&0\\ 0&0&1 \end{bmatrix}

The top-left entry is row 1 of RTR^T dotted with column 1 of RR: (0)(0)+(1)(1)+(0)(0)=1(0)(0)+(1)(1)+(0)(0)=1. The entry beside it is (0)(−1)+(1)(0)+(0)(0)=0(0)(-1)+(1)(0)+(0)(0)=0. Continuing gives the identity, confirming the matrix preserves lengths and angles.

Undoing a rotation

If v′=Rvv'=Rv, then v=R−1v′v=R^{-1}v', and because R−1=RTR^{-1}=R^T:

v=RTv′\boxed{v=R^Tv'}

For example, Rz(90∘)R_z(90^\circ) took (1,0,0)(1,0,0) to (0,1,0)(0,1,0), and its transpose takes (0,1,0)(0,1,0) straight back to (1,0,0)(1,0,0).

Robotics interpretation

Rotation matrices mostly appear when moving a vector between coordinate frames. Suppose a camera sees a point as pC=(1,0,2)p_C=(1,0,2), where the subscript CC means “expressed in the camera frame”. A matrix BRC{}^BR_C describes how the camera axes are oriented relative to the robot body frame BB, and:

pB=BRC pCp_B={}^BR_C\,p_C

expresses the same physical vector in the body frame. This is where careful frame notation becomes important.

Project connection: the balancing robot’s IMU

The balancing-car firmware gives the MPU6050 DMP this orientation matrix:

S=[−1000−10001]S= \begin{bmatrix} -1&0&0\\ 0&-1&0\\ 0&0&1 \end{bmatrix}

For a sensor-frame vector vs=(xs,ys,zs)v_s=(x_s,y_s,z_s), the body-frame components are vb=Svsv_b=Sv_s:

xb=(−1)xs+(0)ys+(0)zs=−xsx_b=(-1)x_s+(0)y_s+(0)z_s=-x_s yb=(0)xs+(−1)ys+(0)zs=−ysy_b=(0)x_s+(-1)y_s+(0)z_s=-y_s zb=(0)xs+(0)ys+(1)zs=zsz_b=(0)x_s+(0)y_s+(1)z_s=z_s vb=[−xs−yszs]\boxed{ v_b= \begin{bmatrix} -x_s\\ -y_s\\ z_s \end{bmatrix} }

The sensor’s xx and yy are reversed while zz stays unchanged, and that is exactly Rz(180∘)R_z(180^\circ), because cos⁡180∘=−1\cos180^\circ=-1 and sin⁡180∘=0\sin180^\circ=0. The sensor is mounted rotated half a turn about the vertical axis.

Common mistakes

Multiplying element-by-element. RvRv is row-by-column multiplication, not matching entries.

Changing the plus signs. A row [0−10]\begin{bmatrix}0&-1&0\end{bmatrix} times (0,1,0)(0,1,0) is (0)(0)+(−1)(1)+(0)(0)(0)(0)+(-1)(1)+(0)(0). The negative comes from the matrix entry, not from switching to subtraction.

Mixing degrees and radians. Hand calculations use degrees; robotics software almost always uses radians, 90∘=π290^\circ=\frac{\pi}{2}. In NumPy:

theta = np.deg2rad(90)

Mixing active and passive rotations. An active rotation rotates the vector. A passive rotation changes the frame used to describe the same vector. They are related by the transpose, so a sign or transpose difference can appear between textbooks.

Summary

  • The main operation is v′=Rvv'=Rv, and one output number is one matrix row dotted with the vector column.
  • Rotation preserves length.
  • The rotation-axis coordinate stays unchanged; the other two behave like a 2D rotation.
  • The transpose reverses a proper rotation.
  • Multiplication order matters when combining rotations, which is where Euler angles come in next.