Skip to content

Dynamics and Control ​

Dynamics ​

All force and torque acting on the system can be represented as a single vector in the generalized velocity space. This representation is called generalized force τ. Just like in a Cartesian coordinate (i.e., x, y, z axes), the power exerted by an articulated system is computed as a dot product of generalized force and generalized velocity (i.e., u⋅τ).

We can also combine the mass and inertia of the whole articulated system and represent them in a single matrix. This matrix is called mass matrix or inertia matrix and denoted by M. A mass matrix represents how much the articulated system resists change in generalized velocities. Naively speaking, a large mass matrix means that the articulated system experiences a low velocity change for a given generalized force.

The total kinetic energy of the system is computed as 12uTMu. This quantity can be obtained by getKineticEnergy().

The total potential energy due to the gravity is a sum of mgz (mass, gravitational acceleration and height) for all bodies. This quantity can be obtained by getPotentialEnergy(gravity). The gravity vector must be passed because only the world stores it (e.g., world.getGravity()).

The equation of motion of an articulated system is shown below:

τ=M(q)u˙+h(q,u).

Here h is called a non-linear term. There are three sources of force that contribute to the non-linear term: gravity, Coriolis, and centrifugal force. It is rarely useful to compute the gravity contribution to the nonlinear term alone. If it is needed, evaluate the nonlinear term at zero generalized velocity, e.g., on a copy of the robot in another world: then the Coriolis and centrifugal contributions are zero.

The following methods are used to obtain dynamic quantities

  • getMassMatrix() (it includes the rotor inertia on its diagonal)
  • getNonlinearities(gravity)
  • getInverseMassMatrix() (call getMassMatrix() first)

Inverse Dynamics ​

RaiSim can compute inverse dynamics using the recursive Newton-Euler algorithm. It is the only option for computing the force and torque acting at joints. Joint force/torque are the sum of the constraint joint force/torque and actuation force/torque. For example, a revolute joint constrains motions in 5 degrees of freedom, which means that there are 5-dimensional constraint forces/torque and 1-dimensional joint actuation torque acting at a revolute joint.

In minimal coordinate simulation (such as RaiSim), these constraint forces/torques are not computed in the simulation loop. These forces/torques can be computed after a simulation loop using the inverse dynamics pipeline.

To enable inverse dynamics, call raisim::ArticulatedSystem::setComputeInverseDynamics(true). This flag is set automatically if the robot has an IMU sensor. Note that the inverse dynamics pipeline will slow down the simulation by about 10%.

After a simulation loop, you can call raisim::ArticulatedSystem::getForceAtJointInWorldFrame() and raisim::ArticulatedSystem::getTorqueAtJointInWorldFrame() to get forces and torques acting at the specified joint.

Assuming that there are no joint position/velocity limit forces acting at the joint, you can compute the joint actuation as a dot product of the joint axis and the joint torque. The inverse_dynamics example (Server Example: Inverse Dynamics) does this for ANYmal and compares the result with the applied generalized force.

PD Controller ​

When naively implemented, a PD controller can often make a robot unstable. However, this is often less problematic for robotics since this instability is also present in real systems (discrete-time control systems).

For other applications like animation and graphics, it is often desirable to have a PD controller that stays stable with a large time step. Therefore, the built-in PD controller is integrated implicitly and stays stable with much larger time steps and gains than a naive implementation.

The built-in PD torque and the feedforward generalized force are clamped together to the joint actuation limits (URDF effort or setActuationLimits()). Joint damping and actuator torques are added after this clamp and are not clipped by it. Because PD is integrated implicitly, the clamp bounds only its explicit part, and getGeneralizedForce() reports the applied PD torque only approximately (see Joint torque of a simulation step). The built-in PD torque is not an actuator torque, so the motor operating regions of actuators do not clip it (see Actuators below).

To use this PD controller, set the desired control gains first:

cpp
Eigen::VectorXd pGain(robot->getDOF()), dGain(robot->getDOF());
pGain<< ...; // set your proportional gain values here
dGain<< ...; // set your differential gain values here
robot->setPdGains(pGain, dGain);

Note that the dimension of the pGain vector is the same as that of the generalized velocity NOT that of the coordinate. For a floating base, the first six gains are forced to zero. setPdGains() also switches the control mode to ControlMode::PD_PLUS_FEEDFORWARD_TORQUE.

Finally, the target position and the velocity can be set as follows:

cpp
Eigen::VectorXd pTarget(robot->getGeneralizedCoordinateDim()), vTarget(robot->getDOF());
pTarget<< ...; // set your position target
vTarget<< ...; // set your velocity target
robot->setPdTarget(pTarget, vTarget);

Here, the dimension of the pTarget vector is the same as that of the generalized coordinate NOT that of the velocity. This can be confusing and may seem inconsistent. However, this is a valid convention. The only reason the two dimensions differ is the quaternion representation. The quaternion target is represented by a quaternion whereas the virtual spring stiffness between the two orientations can be represented by a 3D vector, which is composed of motions in each angular velocity components.

A feedforward force term can be added by setGeneralizedForce() if desired. This term is set to zero by default. Note that this value is stored in the class instance and does not change unless the user specifies it so. If this feedforward force should be applied for a single time step, set it to zero in the subsequent control loop (after the integrate() call of the world).

The theory of the implemented PD controller can be found in chapter 1.2 of this article. This document is intended for advanced users and is not required to use RaiSim.

Actuators ​

The URDF effort limit is a constant bound per joint on the feedforward generalized force plus the built-in PD torque; joint damping is passive and is not clipped. Actuator torques set with setActuatorTorque() or setActuatorTorques() are clipped separately, to the motor operating regions (MOR) of their motors, mapped to the joints (with their couplings), and added after the effort clamp. Effort limits do not clip actuator torques, and MOR does not clip setGeneralizedForce() or the built-in PD controller. The combined generalized force is not clamped again. To keep a PD controller inside the motor operating regions, compute its torques explicitly and send them with setActuatorTorques(). See Actuators.

Apply External Forces/Torques ​

The following two methods are used to apply external force and torque respectively

  • setExternalForce
  • setExternalTorque

You will find the above methods in the ArticulatedSystem API.

Joint torque of a simulation step ​

This section gives exactly what a simulation step of length Δt computes for an articulated system. Bold symbols are vectors with one entry per generalized velocity (one per revolute or prismatic joint, three per spherical joint and six for a floating base), and a product of two such vectors is taken entry by entry. qt and ut are the generalized coordinate and velocity at the beginning of the step, and ut+1 is the velocity at its end. For a spherical joint, qref−qt stands for the rotation vector of qt−1qref, the shorter rotation (an angle of at most π): a quaternion and its negative are the same orientation, as the joint state or as the target.

1. The command, clamped to the actuation limits.

τPD=kp(qref−qt)+kd(uref−ut)τcmd=clamp(τff+τPD,τlower,τupper)

τff is the force set with setGeneralizedForce(), kp and kd are the gains of setPdGains(), and qref and uref are the targets of setPdTarget(). In the FORCE_AND_TORQUE control mode, kp=kd=0 here and in step 4. The bounds are ∓ the URDF effort or those of setActuationLimits(); a joint without them is not clamped.

2. The actuator torques. Take an actuator with the gear ratio G, the driven joint j, the couplings (c,rc) and the torque τa set with setActuatorTorques() (see Actuators). Its motor has the stall torque τstall=KtVbus/R, the back-EMF damping b=Kt/(RKv) and the peak torque τpeak. With ut,j the entry of ut for joint j:

ωm=Gut,j+∑crcut,cτm,min=clamp(−τstall−bωm,−τpeak,τpeak)τm,max=clamp(−τstall−bωm,−τpeak,τpeak)τm=clamp(τa/G,τm,min,τm,max)

The actuator adds Gτm to joint j and rcτm to each coupled joint c, and τact is the sum over all actuators. With setMotorOperatingRegionEnforced(false), τm=τa/G. The actuation limits do not clip actuator torques.

3. The explicit torque. Everything evaluated at the beginning of the step is summed, and the sum is not clamped:

τexp=τcmd−(bjoint+bout)ut+τact+ks(qs−qt)+JTFext

bjoint is the joint damping (URDF damping or setJointDamping(), see Joint Damping and Friction) and bout the output damping of the joint's actuator. ks and qs are the stiffness and the rest position of the joint springs (URDF <dynamics stiffness spring_mount> or addSpring(), see getSprings()). JTFext are the forces and torques of setExternalForce(), setExternalTorque() and setConstraintForce(), mapped through their Jacobians.

4. The velocity. The PD gains, the damping and the springs are integrated implicitly: the diagonal w is added to the mass matrix M (getMassMatrix(), which includes the rotor inertia). With the nonlinear term h (gravity, Coriolis and centrifugal forces, getNonlinearities()), the articulated-body algorithm computes the velocity without contacts, u∗, and the contact solver adds the impulses λc of the contacts, the joint limits and the joint friction through their Jacobian Jc and the same matrix:

w=Δt2(bjoint+bout+kd)+Δt24(kp+ks)M~=M+diag(w)u∗=ut+ΔtM~−1(τexp−h)ut+1=u∗+M~−1JcTλc

A position-limit impulse acts on a joint outside its range. A velocity-limit impulse acts on a joint whose entry of u∗ exceeds its velocity limit, unless a position-limit impulse acts on that joint. A joint-friction impulse acts on every revolute or prismatic joint with friction (see Joint Damping and Friction).

5. The applied joint torque. With Δu=ut+1−ut, moving the implicit part to the right-hand side gives the equation of motion of the step:

MΔuΔt+h=τexp−wΔuΔt+JcTλcΔt

The torque on the joints over the step is therefore, term by term:

SourceTorque over the step
feedforward and PDτcmd−(12kd+Δt4kp)Δu
damping−12(bjoint+bout)(ut+ut+1) (trapezoidal rule)
actuatorsτact, constant over the step
springsks(qs−qt)−Δt4ksΔu
external forcesJTFext
joint frictionλf/Δt, with |λf|≤(τc,joint+τc,out)Δt
contacts and joint limitsthe rest of JcTλc/Δt

The friction impulse λf, the joints' share of JcTλc, stops a joint if the bound allows it, and otherwise opposes the motion with the bound. τc,joint is the joint friction (see Joint Damping and Friction) and τc,out the output friction of the joint's actuator.

Only the explicit part of the feedforward and PD torque is clamped. The implicit part is not, so the applied PD torque can exceed the actuation limits by (12kd+Δt4kp)|Δu|, e.g., when an impact changes the velocity within one step. Without the clamp, the feedforward and PD torque is

τff+kp(qref−qt−Δt4Δu)+kd(uref−12(ut+ut+1))

6. The position. The position is integrated with the average velocity u¯ of the scheme set with setIntegrationScheme() (a quaternion with the same angular velocity):

qt+1=qt+Δtu¯
Integration schemeu¯
SEMI_IMPLICIT (default)ut+1
TRAPEZOID12(ut+ut+1)
EULERut

The torque is the same for the three. SEMI_IMPLICIT moves the robot with the velocity the contact solver leaves, so a foot that an impact stops stays at the surface. TRAPEZOID follows a body under a constant force exactly (a thrown ball), but under forces that change with the state (gravity on a joint, springs, the PD torque, the gyroscopic force) it gains energy: an undamped pendulum's swing grows by about 9% in 20 s at Δt = 1 ms, where SEMI_IMPLICIT keeps it within 0.1%. On an impact, TRAPEZOID also goes into the ground by up to half the step's travel. RUNGE_KUTTA_4 applies the velocity change of the contact solver at the beginning of the step and then evaluates steps 1-4 without contacts at each of its four stages, at the state of the stage; the actuator bounds use the motor speeds of the stage, and getActuatorStates() describes the last stage. In the PD control mode, SEMI_IMPLICIT replaces EULER and RUNGE_KUTTA_4 once the robot has been in contact (from the step after its first contact), until its state is set again with setState(), setGeneralizedCoordinate() or an applied solveIK() solution. SEMI_IMPLICIT also replaces RUNGE_KUTTA_4 while the robot has loop or mimic constraints, which are eliminated from the velocity of one step (see Closed-Loop Systems).

getGeneralizedForce() returns τcmd+τact at the current state: the PD term with the position error of the last step and the current velocity, and the actuator bounds at the current motor speeds. It does not include the damping, the springs, the external forces, the implicit terms or the solver impulses.

Kinematic loops ​

An articulated system is simulated in the generalized coordinates of a kinematic tree. A loop constraint closes a loop of the tree again: it ties an anchor point on body1 to the coincident point on body2. A pin holds the two points together in all three directions. An equality constraint holds them together only along one or two axes fixed in body1; along the remaining directions the points slide freely. A mimic constraint couples two joint coordinates linearly. The three are handled in the same way. This section derives the dynamics with the equality constraint as the main case; Closed-Loop Systems describes how to model loops and how they behave, and Mimic Joints the mimic constraints. The symbols are those of Joint torque of a simulation step above.

The constraints are not iterated by the contact solver. In every step, each articulated system eliminates them exactly, before the contacts are solved:

  1. it takes the velocity u∗ that the system would reach without them (step 4 above),
  2. applies the one impulse that makes that velocity satisfy them, and
  3. replaces the response of everything else that acts on the system (contacts, joint limits, joint friction, tendons) by its response on the constrained mechanism.

The contact solver then works on a smaller problem whose every solution already satisfies the constraints.

Notation ​

SymbolMeaning
q, ugeneralized coordinate and velocity of the tree (|u| entries; see State and Kinematics)
M, M~the mass matrix, and the mass matrix with the implicit diagonal of step 4
p1, p2world positions of the anchor on body1 and on body2
aiconstrained unit axes of an equality constraint in the world frame. They are fixed in body1's frame B1, where they are B1ai.
vb(x), ωbvelocity of the material point of body b at the world point x, and the angular velocity of body b
Jb(x)Jacobian of that point, Jb(x)u=vb(x) (see State and Kinematics)
J, λall constraint rows stacked (|λ|×|u|), and their impulses
Jc, λcthe rows and impulses of the contacts, the joint limits and the joint friction (step 4)
ethe position errors of the rows
D=JM~−1JTthe coupling of the rows (their inverse effective mass, the Delassus matrix of the constraints), |λ|×|λ|
Δt, βthe time step and the error-correction rate, β=clamp(0.2erp,0,0.3)/Δt (see Closed-Loop Systems)

The constraint and its rate ​

For each of its axes, the equality constraint requires

ei(q)=ai⋅(p1−p2)=0.

A pin uses three fixed world directions as its axes ai. A mimic constraint uses e=qfollower−mqleader−c for the multiplier m and the offset c (see Mimic Joints).

The exact rate of an equality row. The step is solved for velocities, so each row needs the time derivative of its error, linear in the generalized velocity u. Take one axis a, with e=a⋅(p1−p2). Three things move:

  • the axis: it is fixed in body1, so it turns with body1's angular velocity, a˙=ω1×a;
  • body1's anchor: p1 is a material point of body1, so p˙1=v1(p1);
  • body2's anchor: p2 is a material point of body2, so p˙2=v2(p2).

One more fact is needed, the velocity field of a rigid body. The material point of body1 that currently sits at a world point x moves with v1(x)=v1(y)+ω1×(x−y) for any other point y: two points of the same body differ in velocity only by the rotation.

  1. The product rule gives e˙=a˙⋅(p1−p2)+a⋅(p˙1−p˙2).

  2. Inserting the three rates, the first term is the axis turning and the second the anchors moving:

    e˙=(ω1×a)⋅(p1−p2)⏟A: the axis turns+a⋅(v1(p1)−v2(p2))⏟B: the anchors move.
  3. Move body1's velocity from p1 to p2. By the velocity field, v1(p1)=v1(p2)+ω1×(p1−p2), so term B splits into

    B=a⋅(v1(p2)−v2(p2))+a⋅(ω1×(p1−p2)).
  4. The scalar triple product is invariant under a cyclic permutation and changes sign when two factors are swapped, so the last term is −(ω1×a)⋅(p1−p2): term A with the opposite sign.

  5. Term A and the rest of term B cancel:

    e˙=a⋅(v1(p2)−v2(p2))=aT(J1(p2)−J2(p2))⏟a row of Ju.

Nothing was approximated: the rate holds at every instant, however far the anchors have slid apart. An equality constraint with two axes has one such row per axis.

The same result, seen from body1. In body1's frame, B1a and B1p1 are constant, so e changes only through the motion of body2's anchor relative to body1. That relative velocity is body2's velocity at the anchor minus the velocity of the point of body1 that coincides with it, v2(p2)−v1(p2), which gives e˙=a⋅(v1(p2)−v2(p2)) again. An equality constraint says that body2's anchor stays on a plane (one axis) or a line (two axes) fixed in body1; what matters is how that anchor moves relative to body1, measured where the anchor is.

Body1's own anchor would give a wrong row. Evaluating body1 at its own anchor keeps only term B, aT(J1(p1)−J2(p2))u=e˙−(ω1×a)⋅(p1−p2). That row misses the turning of the axis, an error that vanishes only when the anchors coincide or when ω1×a happens to be perpendicular to p1−p2. An equality constraint lets the anchors slide apart along its free directions by design, so the error would grow with the slide and with body1's rotation: a 5 cm slide at 2 rad/s gives 0.1 m/s along the constrained axis. RaiSim uses the exact row above.

A pin. Its axes are fixed in the world, so a˙=0 and there is no term A, and it holds the anchors together in every direction, so p1−p2 stays at zero. Its rows aiT(J1(p1)−J2(p2)) are exact as they are.

Where the force acts. By virtual work, a row aT(J1(p2)−J2(p2)) with the impulse λ applies the generalized impulse

(J1(p2)−J2(p2))Taλ=J1(p2)T(λa)−J2(p2)T(λa):

the point impulse λa on body1 at p2, and −λa on body2 at the same point. The two are equal, opposite and collinear, so they apply no net force or torque to the system. They have no component along the free directions, and their power λe˙ is zero while the constraint holds: the constraint does no work while the points slide.

Equations of motion of a step ​

With the constraint impulses λ added to step 4, one step of the velocity-level integration reads

M~(ut+1−ut)=Δt(τexp−h)+JcTλc+JTλ.

The constraints are imposed on the velocity at the end of the step, with the position error fed back:

(1)Jut+1=−βe.

As one linear system in ut+1 and λ (for given λc), this is the saddle-point (KKT) problem

[M~−JTJ0][ut+1λ]=[M~ut+Δt(τexp−h)+JcTλc−βe].

The impulses λc are unknown as well, and subject to the Coulomb friction cones and to one-sided limits; that is what the contact solver iterates on. The elimination below removes λ from this problem exactly, so the solver iterates only on λc.

Eliminating the constraints ​

1. The free velocity. Without any impulse, the velocity at the end of the step is u∗=ut+ΔtM~−1(τexp−h) (step 4).

2. Solve the first block row for ut+1:

ut+1=u∗+M~−1JcTλc+M~−1JTλ.

3. Substitute it into the constraints (1). This leaves a system for λ alone, whose matrix D is the Schur complement of M~ in the KKT matrix:

Dλ=−βe−Ju∗−JM~−1JcTλc.

Its solution splits into a part that depends only on the free motion and a part induced by the other impulses:

(2)λ=D−1(−βe−Ju∗)⏟λ0−D−1JM~−1JcTλc.

4. Substitute λ back:

ut+1=u∗+M~−1JTλ0⏟u0+(M~−1−M~−1JTD−1JM~−1)⏟Mc−1JcTλc.

These are the reduced dynamics, in two parts:

  • u0 is the free velocity moved onto the constraints; by construction, Ju0=−βe. Each step applies the impulse JTλ0 before the contacts are solved.

  • Every other impulse acts through the inverse mass of the constrained mechanism,

    Mc−1=M~−1−M~−1JTD−1JM~−1,

    whose response to an impulse is the unconstrained response minus its constrained part: Mc−1JcT=M~−1JcT−M~−1JTD−1(JM~−1JcT).

Three properties make this exact:

  • No impulse can open a constraint. JMc−1=JM~−1−DD−1JM~−1=0, so Jut+1=Ju0=−βe for any λc. The constraints hold whatever the contact solver does and however many iterations it runs.
  • The contact problem keeps its form. Mc−1 is symmetric and positive semidefinite. The contacts' apparent inverse inertia becomes JcMc−1JcT: the same kind of quantity as without constraints, only smaller, because the closed mechanism is stiffer.
  • The constraint impulse is still known. Equation (2) gives λ once the solver has found λc; this is the impulse reported for each constraint (see Reading constraint forces).

In short, RaiSim solves the KKT system by block elimination: it eliminates λ through D, solves the contacts on the constrained mechanism with the inverse mass Mc−1, and recovers λ from the solved λc.

If the rows of J are dependent (redundant constraints, or a loop at a singular pose), D is singular. RaiSim then keeps an independent subset of the rows; the motion is the same, and the load goes to the rows that are kept (see Redundant constraints and singular poses).

Why these are the reduced dynamics ​

Constrained dynamics can equivalently be written in coordinates of the allowed motion. Let the columns of N span the null space of J, the velocities that keep the constraints. Restricting the equations of motion to that space (d'Alembert's principle: the constraint forces do no work on the allowed motions) gives the reduced mass NTM~N, and the constrained mechanism responds to a generalized impulse through N(NTM~N)−1NT. For J of full row rank, this is the same matrix:

N(NTM~N)−1NT=M~−1−M~−1JT(JM~−1JT)−1JM~−1=Mc−1.

Both sides map a generalized impulse along the constraints, JTy, to zero, and both map M~Nz to Nz. The two subspaces {JTy} and {M~Nz} together span the whole space: M~Nz=JTy implies NTM~Nz=0, so z=0.

The right-hand form needs only J, M~−1JT and a system with one equation per constraint row. No basis of the null space is built, the generalized coordinates stay those of the tree, and the number of constraint rows |λ| is small compared with |u|.

P=I−M~−1JTD−1J projects onto the allowed velocities, orthogonally in the kinetic-energy metric M~: Pu is the allowed velocity closest to u in ∥⋅∥M~. Moving u∗ to u0 in step 4 is this projection, shifted so that Ju0=−βe instead of zero. Physically, it is the impulse that a perfectly rigid and lossless constraint applies: of all velocities that satisfy the constraints, u0 is the one closest to u∗ in kinetic energy (Gauss's principle of least constraint).

Integration Steps ​

Integration of an articulated system is performed in two stages: integrate1 and integrate2.

The following steps are performed in integrate1

  1. If the time step, the control mode, the PD gains, the damping or the springs changed, update the diagonal w added to the mass matrix (the effective inertia of the implicit springs, dampers and PD gains)
  2. Update positions of the collision bodies
  3. Detect collisions (called by the world instance)
  4. The world assigns contacts on each object and computes the contact normal
  5. Compute the mass matrix, nonlinear term, and inverse inertia matrix
  6. Compute (Sparse) Jacobians of contacts

After this step, all kinematic/dynamic properties are computed. Users can access them if they are necessary for the controller. Next, integrate2 computes the rest of the simulation.

  1. If the controller changed the control mode, the PD gains, the damping or the springs after integrate1, update w as in step 1, then compute contact properties
  2. Compute the PD torque (if used), add the feedforward generalized force, clamp this sum to the joint effort limits, and add the joint damping (never clipped, see Joint Damping and Friction)
  3. Clip the actuator torques to the motor operating regions, map them to the joints and add them; then add the springs and the external forces/torques, and compute the velocity without contacts
  4. Contact solver (called by the world instance). It also solves the joint limits and the joint friction
  5. Integrate the velocity
  6. Integrate the position with the integration scheme

Steps 8-12 are written out exactly in Joint torque of a simulation step.

RaiSim — physics for robotics and AI.