Kinematic Chain
Skeleton of a serial robot arm with six revolute joints. This is one example of a kinematic structure, reducing the motion modelling and specification to a geometric problem of relative motion of reference frames. The Kinematics and Dynamics Library (KDL) develops an application independent framework for modelling and computation of kinematic chains, such as robots, biomechanical human models, computer-animated figures, machine tools, etc. It provides class libraries for geometrical objects (point, frame, line,... ), kinematic chains of various families (serial, humanoid, parallel, mobile,... ), and their motion specification and interpolation.
This document is not ready yet, but it's a wiki page so feel free to contribute
There are different ways for getting the software.
They will be available soon on the fmtc debian repos.
svn checkout http://svn.mech.kuleuven.be/repos/orocos/trunk/kdl
mkdir <kdl-dir>/build ; cd <kdl-dir>/build
ccmake ..
make;make check;make install
\TODO explain why KDL has a reason to exist and is most likely the best in it's kind
|
Table of Contents
|
A Vector represents the 3D position of a point/object wrt the reference frame. It can be seen as a 3-element vector containing X-Y-Z values:
![]() |
(1) |
Vector v1; //The default constructor, X-Y-Z are initialized to zero Vector v2(x,y,z); //X-Y-Z are initialized with the given values Vector v3(v2); //The copy constructor Vector v4 = Vector::Zero(); //All values are set to zero
The operators [ ] and ( ) use indeces from 0..2, index checking is enabled/disabled by the DEBUG/NDEBUG definitions:
v1[0]=v2[1];//copy y value of v2 to x value of v1 v2(1)=v3(3);//copy z value of v3 to y value of v2 v3.x( v4.y() );//copy y value of v4 to x value of v3
You can multiply or divide a Vector with a double using the operator * and /:
v2=2*v1; v3=v1/2;
v2+=v1; v3-=v1; v4=v1+v2; v5=v2-v3;
v3=v1*v2; //Cross product double a=dot(v1,v2)//Scalar product
You can reset the values of a vector to zero:
SetToZero(v1);
With or without giving an accuracy:
v1==v2; v2!=v3; Equal(v3,v4,eps);//with accuracy eps
Represents the 3D rotation of an object wrt the reference frame. Internally it is represented by a 3x3 matrix which is a non-minimal representation:
![]() |
(2) |
The following result always in consistent Rotations. This means the rows/columns are always normalized and orthogonal:
Rotation r1; //The default constructor, initializes to an 3x3 identity matrix Rotation r1 = Rotation::Identity();//Identity Rotation = zero rotation Rotation r2 = Rotation::RPY(roll,pitch,yaw); //Rotation build out off Roll-Pitch-Yaw angles Rotation r3 = Rotation::EulerZYZ(alpha,beta,gamma); //Rotation build out off Euler Z-Y-Z angles Rotation r4 = Rotation::EulerZYX(alpha,beta,gamma); //Rotation build out off Euler Z-Y-X angles Rotation r5 = Rotation::Rot(vector,angle); //Rotation build out off an equivalent axis(vector) and an angle.
The following should be used with care, they can result in inconsistent rotation matrices, since there is no checking if columns/rows are normalized or orthogonal
Rotation r6( Xx,Yx,Zx,Xy,Yy,Zy,Xz,Yz,Zz);//Give each individual element (Column-Major) Rotation r7(vectorX,vectorY,vectorZ);//Give each individual column
Individual values, the indices go from 0..2:
double Zx = r1(0,2);
Getting EulerZYZ, Euler ZYX, Roll-Pitch-Yaw angles , equivalent rotation axis with angle:
r1.GetEulerZYZ(alpha,beta,gamma); r1.GetEulerZYX(alpha,beta,gamma); r1.GetRPY(roll,pitch,yaw); axis = r1.GetRot();//gives only rotation axis angle = r1.GetRotAngle(axis);//gives both angle and rotation axis
Getting the Unit vectors:
vecX=r1.UnitX();//or r1.UnitX(vecX); vecY=r1.UnitY();//or r1.UnitY(vecY); vecZ=r1.UnitZ();//or r1.UnitZ(vecZ);
Inverting the rotation:
r1.SetInverse();//r1 is inverted
Getting the inverted rotation:
r2=r1.Inverse();//r2 is the inverse rotation of r1
You can compose two rotations to a new rotation, the order of the rotations is important:
r3=r1*r2;
You can add a rotation around X-Y-Z directly to an existing rotation
r1.DoRotX(angle); r2.DoRotY(angle); r3.DorotZ(angle);
The rotation around X-Y-Z is applied to the rotation (r1) such that
r1 = r1*Rotation::RotX(angle)
You can rotate a Vector using a Rotation and the operator *.
v2=r1*v1;
A different way to see this: You can calculate the new (v2) X-Y-Z values of a vector (v1) if the reference frame is rotated with a Rotation r1.
This can be done in the same way as vectors are compared, with operators == and != or with the function Equal:
r1==r2; r1!=r2; Equal(r1,r2,eps);
A Frame can represents the pose of an object/frame wrt a reference frame (4x4 matrix)
![]() |
(3) |
Since a Frame contains a Rotation and a Vector it can only be build by supplying their values:
Frame f1;//Creates Identity frame Frame f1=Frame::Identity();//Creates an identity frame: Rotation::Identity() and Vector::Zero() Frame f2(your_rotation);//Create a frame with your_rotation and a zero vector Frame f3(your_vector);//Create a frame with your_vector and a identity rotation Frame f4(your_rotation,your_vector);//Create a frame with your_rotation Frame f5(your_vector,your_rotation);//and your_vector Frame f5(f6);//the copy constructor
You can get an individual element of a frame using it's 4x4 matrix representation:
double x = f1(0,3); double Yy = f1(1,1);
A better way is to go through the underlying Rotation and vector:
Vector p = f1.p; Rotation M = f1.M;
You can use the operator * to compose pose transformations. If you have a Frame F_A_B that specifies the pose transformation from a frame A to a frame B and a Frame F_B_C that specifies the pose transformation from a frame B to a frame C you can calculate the Frame F_A_C as follows:
Frame F_A_C = F_A_B * F_B_C;
The Frame F_A_C now specifies the pose transformation from frame A to frame C, or said otherwise, F_A_C.p is the location of the origin of frame C expressed in frame A, and F_A_C.M is the rotation of frame C expressed in frame A.
Again the operators == and != or Equal can be used:
f1==f2; f1!=f2; Equal(f1,f2,eps);
A Twist represents a 6D velocity with a 3D translational and a 3D rotational part (6x1 matrix). Internally it is represented with by two Vectors, one containing the translational velocitity, vel, and one containing the rotational velocity, rot:
![]() |
(4) |
Twist t1; //Default constructor, initializes to Zero through Vector Twist t2(vel,rot);//Vector vel, and Vector rot Twist t3 = Twist::Zero();//Zero twist
Using the operators [ ] and ( ), the indeces from 0..2 return the elements of vel, the indeces from 3..5 return the elements of rot:
double vx = t1(0); double omega_y = t1[4]; t1(1) = vy; t1[5] = omega_z;
Because some robotics literature put the rotation part on top it is safer to use the vel, rot members to access the individual elements:
double vx = t1.vel.x();//or vx = t1.vel(0); double omega_y = t1.rot.y();//or omega_y = t1.rot(1); t1.vel.y(v_y);//or t1.vel(1)=v_y;//etc
The same operators as for Vector are available:
t2=2*t1; t2=t1*2; t2=t1/2;
The same operators as for Vector are available:
t1+=t2; t1-=t2; t3=t1+t2; t3=t1-t2;
Again the operators == and != or Equal can be used:
t1==t2; t1!=t2; Equal(t1,t2,eps);
A Wrench represents a 6D force with a 3D force and a 3D torque part (6x1 matrix). Internally it is represented with by two Vectors, one containing the forces, force, and one containing the torques, torque:
![]() |
(5) |
Wrench w1; //Default constructor, initializes to Zero through Vector Wrench w2(force,torque);//Vector force, and Vector torque Wrench w3 = Wrench::Zero();//Zero wrench
Using the operators [ ] and ( ), the indeces from 0..2 return the elements of force, the indeces from 3..5 return the elements of torque:
double fx = w1(0); double ty = w1[4]; w1(1) = fy; w1[5] = tz;
Because some robotics literature put the torque part on top it is safer to use the torque, force members to access the individual elements:
double fx = w1.force.x();//or fx = w1.force(0); double ty = w1.torque.y();//or ty = w1.torque(1); w1.force.y(fy);//or w1.force(1)=fy;//etc
The same operators as for Vector are available:
w2=2*w1; w2=w1*2; w2=w1/2;
The same operators as for Twist are available:
w1+=w2; w1-=w2; w3=w1+w2; w3=w1-w2;
Again the operators == and != or Equal can be used:
w1==w2; w1!=w2; Equal(w1,w2,eps);
A Wrench or Twist is expressed in a certain reference frame and reference point, most of the times the reference point coincides with the base point of the reference frame, but this does not have to be the case.
The values of a Wrench or Twist changes if the reference frame or reference point is changed.
If you want to change the reference point you need the vector p from the old reference point to the new reference point expressed in the reference frame of the Wrench or Twist:
t2 = t1.RefPoint(v_old_new); w2 = w1.RefPoint(v_old_new);
If you want to change the reference frame but want to keep the reference point intact you can use a Rotation matrix which expresses the rotation of the current reference frame B wrt to the new reference frame A:
ta = R_AB*tb; wa = R_AB*wb;
Use a Frame that contains both the rotation and the pose change, changing the reference frame and reference point from B to A:
ta = F_AB*tb; wa = F_AB*wb;
t_ab = diff(F_b_A,F_b_B,timestep) F_b_B = F_b_A.addDelta(t_ab,timestep)
t_ab is the twist to move from frame A to frame B in timestep seconds, with reference frame b and reference point the origin of A.
A KDL::Chain is
A Segment contains
To start from the bottom:
When constructing a Joint, you have to define the type of the joint, you can choose from the following:
Joint rx = Joint(Joint::RotX); Joint ry = Joint(Joint::RotY); Joint rz = Joint(Joint::RotZ); Joint tx = Joint(Joint::TransX); Joint ty = Joint(Joint::TransY); Joint tz = Joint(Joint::TransZ); Joint fixed = Joint(Joint::None);
A joint is 1D.
You can get the pose and twist of the joint:
double q = M_PI/4; Frame f = rx.pose(q); double qdot = 0.1; Twist t = rx.twist(qdot);
When constructing a Segment you have to give the Joint at the root and the Frame describing the pose to the tip, the constructor takes copies of the arguments, you cannot change the frame or joint afterwards!!!:
Segment s = Segment(Joint(Joint::RotX), Frame(Rotation::RPY(0.0,M_PI/4,0.0), Vector(0.1,0.2,0.3) ));
A segment is still 1D, you can get the pose and the twist of the tip of the segment wrt the root:
double q=M_PI/2; Frame f = s.pose(q); double qdot=0.1; Twist t = s.twist(q,qdot);
When asking the twist of the tip you have to give the position value of the joint because the twist is expressed in the root frame of the segment but the reference point of the Twist is at the tip of the segment.
You can get the pose of the link of the segment, this does not include the pose of the joint!!!
Frame F_link = segment1.getFrameToTip();
A Chain has a default constructor, creating an empty chain without any segments and a copy-constructor, creating a copy of an existing chain:
Chain chain1(); Chain chain2(chain3);
Chain are constructed by adding Segments or adding existing chains, to the end of the chain, these functions add copies of the arguments, not the arguments themselves!!!:
chain1.addSegment(segment1); chain1.addChain(chain2);
You can get the number of joints and number of segments (this is not always the same since a segment can have a Joint::None, which is not included in the number of joints):
unsigned int nj = chain1.getNrOfJoints(); unsigned int js = chain1.getNrOfSegments();
You can iterate over the segments of a chain by getting a reference to each successive segment:
Segment& segment3 = chain1.getSegment(3);
KDL contains for the moment only generic solvers for kinematic chains. They can be used (with care) for every KDL::Chain.
The idea behind the generic solvers is to have a uniform API. We do this by inheriting from the abstract classes for each type of solver:
A seperate solver has to be created for each chain. At construction time, it will allocate all necessary resources.
A specific type of solver can add some solver-specific functions/parameters to the interface, but still has to use the generic interface for it's main solving purpose.
The forward kinematics use the function JntToCart(...) to calculate the Cartesian space values from the Joint space values. The inverse kinematics use the function CartToJnt(...) to calculate the Joint space values from the Cartesian space values.
For now we only have one generic forward and velocity position kinematics solver.
It recursively adds the poses/velocity of the successive segments, going from the first to the last segment, you can also get intermediate results by giving a Segment number:
ChainFkSolverPos_recursive fksolver(chain1); JntArray q(chain1.getNrOfJoints); q=... Frame F_result; fksolver.JntToCart(q,F_result,segment_nr);
Some small examples for usage.
Do not hesitate to add your own small examples.