Quaternions and animations

I like using FreeCAD, and the fact you can use python to making it do things. In this post I will show how to setup an animation.

I needed to 3D print a thing so I prepared this model:

Animations in FreeCAD are managed through quaternions: let’s look into it.

Quaternions?!?

When I was a math student, I first met quaternions in the first year. Quaternions are an extension of complex numbers, introducing new “imaginary” units. A quaternion is a sequence of four real numbers, that can be written as q=(a,b,c,d)=a + b  \mathbf{i} + c  \mathbf{j} + d  \mathbf{k} .

Quaternions are a clever way to represent rotations. What do you need to represent a rotation in a three dimensional space? The axis of rotation (three numbers) and the angle of rotation (fourth number). Mind that we could do with two numbers for the axis of rotation, since we need to define just a direction, so the square sum of the three numbers defining the axis needs to be one.

So, we want to represent a rotation of \theta radians around an axis with vecto (\hat x,\hat y,\hat z) with \hat x^2+\hat y^2 +\hat z^2=1. The right quaternion is:

 q_\theta =\cos(\theta/2)+\sin(\theta/2) (\hat x  \mathbf{i} + \hat y \mathbf{j} + \hat z \mathbf{k} )

Quaternions are an extension of compex numbers, so we can sum them and also multiply them. An interest thing is that multiplication is cyclic and anti-commutative:

  \mathbf{ij}= \mathbf{k}; \mathbf{jk}= \mathbf{i};  \mathbf{ki}= \mathbf{j} and  \mathbf{ji}= \mathbf{-k}; \mathbf{kj}= \mathbf{-i};  \mathbf{ik}= \mathbf{-j}

Such multiplication rule is quite useful because if we define two quaternion rotation q(\vec a,\theta) and  q(\vec b,\psi) the composition of the two rotation is still a rotation described by a quaternion, by multiplication:

 q(\vec a,\theta)  \times  q(\vec a,\psi)  =  q(\vec c,\gamma)

and  q(\vec c,\gamma) will be the rotation resulting in applying first the \psi rotation and then the $\theta$ rotation. Enough of math, for now, let’s start coding.

FreeCAD and quaternions

FreeCAD is my favourite CAD, for two reasons:

  • is open source (so non need for autocad or so…)
  • you can tweak in some code in Python (and I love Python)

Rotations of objects in FreeCAD are rapresented by quaternions. For example, if you have an object called “Body1”, you can put in the console:

App.ActiveDocument.Body1.Placement.Rotation.Q

and FreeCAD will happily answer with a tuple of four number that (surprise!) is the quaternion representing the rotation of the object. In order to rotate an object, we simply have to put in the Rotation.Q property the desired quaternion.

The animation code

And here we are, let’s see how we can animate our CAD drawing using python and quaternions. We are interested in the macro bar:

so we create a new macro and start coding in the editor. First thing to do (after loading libraries) is to write some function to implement quaternion algebra:

from PySide import QtCore
from math import sin,cos

def QuatMult(q1,q2):
# Calculates quaternion product
	a=q1[0]*q2[0]-q1[1]*q2[1]-q1[2]*q2[2]-q1[3]*q2[3]
	b=q1[0]*q2[1]+q1[1]*q2[0]+q1[2]*q2[3]-q1[3]*q2[2]
	c=q1[0]*q2[2]-q1[1]*q2[3]+q1[2]*q2[0]+q1[3]*q2[1]
	d=q1[0]*q2[3]+q1[1]*q2[2]-q1[2]*q2[1]+q1[3]*q2[0]
	return (a,b,c,d)

and then some useful rotation:

def ZRotation(theta):
	theta=theta/180.0*3.14159
	a=cos(theta/2)
	b=0
	c=0
	d=sin(theta/2)
	return (a,b,c,d)
def YRotation(theta):
	theta=theta/180.0*3.14159
	a=cos(theta/2)
	b=0
	c=sin(theta/2)
	d=0
	return (a,b,c,d)
def XRotation(theta):
	theta=theta/180.0*3.14159
	a=cos(theta/2)
	b=sin(theta/2)
	c=0
	d=0
	return (a,b,c,d)

We can now define the animation core, the function that will retrieve the quaternion for the three objects that need to be moved: the gear, the geared sled and the arm of the servo that moves the gear:

def Animate(theta):
	arm=App.ActiveDocument.getObjectsByLabel("Arm")[0]
	gear=App.ActiveDocument.getObjectsByLabel("Gear")[0]
	sled=App.ActiveDocument.getObjectsByLabel("GearedSled")[0]
	myRotL=XRotation(-theta*2.03125)
	myRotR=YRotation(-theta)
	qRotArm=QuatMult(myRotL,arm.Placement.Rotation.Q)
	qRotGear=QuatMult(myRotL,gear.Placement.Rotation.Q)
	qRotSled=QuatMult(myRotR,sled.Placement.Rotation.Q)
	arm.Placement.Rotation.Q=qRotArm
	gear.Placement.Rotation.Q=qRotGear
	sled.Placement.Rotation.Q=qRotSled
	App.ActiveDocument.recompute()

This function simply rotates the sled of theta and the gear and the servo arm of 2.03125 times theta (the number is calculate from the number of teeths ratio).

The animation we want to create will turn the servo arm in one direction then stop and get back: in order to do that we have to implement same global state and a function to manage the forward and backward turns:

my_index=0
my_status=1
timer=QtCore.QTimer()
# Updating function
def UpDate():
	global my_index
	global my_status
	global timer
	if (my_status>0):
		my_index+=1
		Animate(1)
	if (my_status<0):
		my_index-=1
		Animate(-1)
	if (my_index>=30):
		my_status=-1
	if ((my_index<0)&(my_status<0)):
		timer.stop()

To start the animation we need some last lines of code:

# Timer initialization (50ms)
timer.timeout.connect(UpDate)
timer.start(50)
# DEBUG if we are here everything is ok...
FreeCAD.Console.PrintMessage("OK")

that will also print a reassuring “OK” in the status bar if everything has gone right. Is time to see the result!