Control Theory Primer
Why this exists
DART makes it easy to simulate articulated rigid bodies, but it does not pick controller gains, filter noisy measurements, or guarantee that an arbitrary control law is numerically stable for a given timestep. This page gives a short control-theory refresher and maps common concepts onto DART APIs.
Mapping notation to DART
In most robotics/control texts, a multibody system is written in generalized coordinates:
Generalized position:
qGeneralized velocity:
dqGeneralized acceleration:
ddqGeneralized forces/torques:
tau
In DART, these correspond to:
q:Skeleton::getPositions()/skeleton.getPositions()dq:Skeleton::getVelocities()/skeleton.getVelocities()ddq:Skeleton::getAccelerations()/skeleton.getAccelerations()tau:Skeleton::setForces(tau)/skeleton.setForces(tau)
When computing pose errors, prefer DART’s configuration-space helpers over naively subtracting vectors:
Position error on configuration manifolds:
Skeleton::getPositionDifferences(q2, q1)Velocity error:
Skeleton::getVelocityDifferences(dq2, dq1)
These matter for joints whose coordinates live on a manifold (e.g., BallJoint
and the rotational part of FreeJoint).
Actuation in DART: commands vs forces
Every Joint has an ActuatorType that determines what “command” means. Two
common patterns are:
Direct generalized forces (
Joint::FORCE): you computetauand apply it withSkeleton::setForces(...). This is what most tutorial controllers use.Kinematic/servo-style commands (
Joint::VELOCITY,Joint::SERVO, …): you set commands withSkeleton::setCommands(...)/Joint::setCommand(...), and DART converts them into forces internally.
One easy-to-miss detail: World::step() defaults to _resetCommand = true,
which clears commands/forces after each step. A controller should therefore set
its commands (or forces) every timestep.
Proportional-derivative (PD) feedback
PD control is the baseline for stabilizing a pose:
tau = Kp * (q_des - q) + Kd * (dq_des - dq)
For floating-base systems, some DOFs are unactuated (typically the first six).
Set their gains (or the corresponding entries in tau) to zero.
The Atlas Simbicon scene in the gallery shows a floating-base humanoid controlled through a walking controller.
Feedforward + inverse dynamics (“computed torque”)
For better tracking than pure PD, add a feedforward term using the rigid-body dynamics model:
tau = M(q) * ddq_des + C(q, dq) + g(q) + Kp * e_q + Kd * e_dq
Where DART exposes:
M(q):Skeleton::getMassMatrix()C(q, dq):Skeleton::getCoriolisForces()(orgetCoriolisAndGravityForces())g(q):Skeleton::getGravityForces()
This style of controller is sensitive to modeling errors, contact, and time discretization, so it is common to keep the PD terms as a stabilizing “outer loop” even when using inverse dynamics.
Impedance, stiffness, and damping
DART joints support implicit springs and damping. This is often a convenient way to get “soft” behavior without explicitly writing a controller:
Rest pose:
DegreeOfFreedom::setRestPosition(...)Spring stiffness:
DegreeOfFreedom::setSpringStiffness(...)Damping:
DegreeOfFreedom::setDampingCoefficient(...)
Because these are handled with implicit terms, they tend to be more numerically stable than explicit high-gain PD for the same timestep.
See also:
Control Gain Tuning (Discrete-Time) (discrete-time gain tuning)
Gallery (maintained Filament simulation scenes)