Trajectory Tracking Control
This chapter collects the control techniques needed to track planned robot-arm
motion in skelarm. Unlike the following reaching chapter, it does not assume a
specific goal such as moving the hand to a point. The reference may come from any
task: drawing a curve, tracking a sampled demonstration, following a joint-space
test trajectory, or reaching a target through a planned path.
The usual pipeline is:
- Plan a desired task-space or joint-space trajectory.
- Convert task-space references into joint-space references if needed.
- Track the joint-space reference with a torque controller.
The endpoint trajectory notation is
where \(p_r(t)\) is the desired task-space reference and \(p(q)\) is the endpoint position from forward kinematics. A reaching task is one important application: choose \(p_r(t)\) to move from the initial endpoint \(p_0\) to the target \(p^\ast\), then track the resulting reference with the methods below.
1. Trajectory planning
A trajectory planner creates references that are smooth enough for the controller and dynamics. For a task-space path from \(p_0\) to \(p_1\), a common form is
The scalar schedule \(s(t)\) controls endpoint velocity and acceleration:
- Linear interpolation uses \(s=t/T\). It is easy, but has velocity jumps at the start and end.
- Cubic smoothing uses \(s=3u^2-2u^3\) with \(u=t/T\). It gives zero endpoint velocity.
- Quintic smoothing uses \(s=10u^3-15u^4+6u^5\). It gives zero endpoint velocity and acceleration.
- Minimum-jerk profiles minimize the integral of squared jerk. For the rest-to-rest point-to-point problem above, the solution is mathematically the same quintic time law as quintic smoothing:
subject to
With these boundary conditions,
In the current skelarm trajectory-planning problem, therefore, a quintic
schedule and a rest-to-rest minimum_jerk schedule can share the same
implementation. They become distinct when the problem statement changes: for
example, if endpoint velocity or acceleration is non-zero, if multiple via-points
or multiple movement segments are optimized together, if segment durations are
also optimized, or if the objective includes task constraints such as obstacle
avoidance or effort penalties. In those cases, quintic interpolation is a local
polynomial construction, while minimum jerk is an optimization problem whose
solution may require solving for polynomial coefficients or a larger trajectory
optimization.
The planned reference should provide at least position and velocity. Acceleration is needed for inverse-dynamics feedforward and computed torque control:
Joint-space trajectories can be planned in the same way by replacing \(p_r\) with \(q_r\). That avoids inverse kinematics during tracking, but it does not directly shape the endpoint path.
2. Task-space to joint-space conversion
skelarm actuates joints. A task-space trajectory must therefore become a joint
reference before a joint torque controller can track it.
IK-based position conversion
At each sample time, solve
with compute_inverse_kinematics. The previous solution is the best seed for the
next sample:
This gives a feasible position reference and naturally respects the joint limits
enforced by the IK solver. skelarm implements this conversion as
ik_joint_reference, which warm-starts each solve from the previous solution and
fills \(\dot{q}_r\) and \(\ddot{q}_r\) by finite differences, returning a
SampledJointReference that a tracking controller reads by linear interpolation.
Warm-starting also preserves joint-reference continuity. Redundant arms can reach the same endpoint with multiple postures; abrupt posture switching would give a valid endpoint trajectory but a poor joint trajectory, and seeding each solve from the previous solution keeps the posture from jumping.
Resolved motion rate conversion
Resolved motion rate control converts task velocity into joint velocity:
where \(J^\#\) is a pseudoinverse or damped pseudoinverse. For the endpoint task, a stable default is
Then integrate \(\dot{q}_r\) forward in time. This method can produce smoother
joint references than independent per-sample IK when the time step is small, but
it can drift from the desired task path. skelarm's resolved_rate_joint_reference
adds task-space feedback to correct that drift,
exposing the damping \(\mu\) as damping and \(K_{\mathrm{task}}\) as k_task.
This is velocity-level control, not torque control. In a dynamic simulation it is used to generate \(q_r(t)\) and \(\dot{q}_r(t)\) for a lower-level torque controller.
3. Joint-space trajectory tracking
After conversion, the tracking controller receives \(q_r(t)\) and often
\(\dot{q}_r(t)\), \(\ddot{q}_r(t)\). The output is the joint torque vector \(\tau\)
passed to compute_forward_dynamics or simulate_robot.
Direct joint-space PD control
The simplest dynamic controller is
\(K_p\) and \(K_d\) are positive diagonal gain matrices. skelarm provides it as the
JointPD controller, which is callable as a simulate_robot torque callback and
also runs through the fixed-step simulate_controlled loop. It does not compensate
for the arm's inertia or Coriolis terms, so the same gains can behave differently
across configurations.
Inverse-dynamics feedforward plus feedback
If a full joint reference is available, inverse dynamics can compute the torque that would realize the reference motion in the nominal model:
Then add feedback:
skelarm provides this as InverseDynamicsFeedforwardPD, which holds a separate
clone of the skeleton, sets its q, dq, and ddq to the reference state, and
runs compute_inverse_dynamics to read the feedforward tau. This is useful when
the planned trajectory is trusted and the feedback term only needs to correct
tracking error.
Computed torque control
Computed torque control uses the current model to choose a desired joint acceleration
then commands
With an exact model and no torque saturation, the tracking error follows the linear second-order system
skelarm provides this as the ComputedTorque controller, which forms \(H(q)\) with
compute_mass_matrix and \(b(q,\dot{q})\) with compute_coriolis_gravity_vector.
Model predictive control
Model predictive control (MPC) is another model-based tracking method, but it is not the same as computed torque control. Computed torque chooses an instantaneous model-cancelling torque. MPC repeatedly solves a finite-horizon optimization problem, applies only the first control input, then replans from the next measured state.
A simple skelarm formulation can start in joint space. Define the discrete
state and input as
Using a fixed control interval \(\Delta t\), the prediction model can use
compute_forward_dynamics:
with a simple semi-implicit Euler update
For a horizon of \(N\) steps, a basic tracking objective is
where
skelarm's JointSpaceMPC uses symmetric torque bounds (tau_max) and a soft
joint-limit penalty (limit_weight). Hard state constraints,
are not yet enforced: the solver is scipy.optimize.minimize with L-BFGS-B,
whose only hard constraints are box bounds on the torque variables. JointSpaceMPC
optimizes a flattened torque sequence over a small horizon and warm-starts from the
previous solution. Its controller loop is:
- Read the current state \((q,\dot{q})\).
- Slice the next \(N\) samples from the reference trajectory.
- Optimize \(\tau_0,\dots,\tau_{N-1}\) by rolling out
compute_forward_dynamics. - Apply only \(\tau_0\).
- Shift the optimized sequence by one step to warm-start the next solve.
Only joint-space MPC is implemented. Task-space MPC could be added by including an endpoint term,
but the joint-space form is kept deliberately simple: it avoids mixing IK, trajectory conversion, and constrained optimal control in the same controller.
Fixed-step simulation
MPC is naturally discrete and stateful. The simulate_robot helper uses
adaptive solve_ivp, which may call a torque callback multiple times per
output interval, so it is unsuitable for a stateful controller. JointSpaceMPC
is therefore run with the fixed-step simulate_controlled loop, which calls the
controller once per control interval with the same dt as the MPC rollout.
Gravity convention
The default skelarm arm moves in a horizontal plane, so gravity is zero
unless a non-zero grav_vec is explicitly supplied. Control formulas should
be written against the same convention as compute_forward_dynamics.
4. Implementation in skelarm
The trajectory-tracking layer described above is implemented across
skelarm.trajectory, skelarm.control, and skelarm.mpc:
Trajectory(inskelarm.trajectory) produces sampled \(p_r\), \(\dot{p}_r\), and \(\ddot{p}_r\) for thelinear,cubic,quintic, andminimum_jerkschedules (the last two share one polynomial; see ยง1).ik_joint_referenceconverts a task trajectory to a joint reference by samplewisecompute_inverse_kinematicswith warm starts.resolved_rate_joint_referenceconverts by damped-pseudoinverse velocity integration with optional task-space feedback.JointPD,InverseDynamicsFeedforwardPD, andComputedTorque(inskelarm.control) implement the three torque controllers.simulate_controlledis the fixed-step (semi-implicit Euler) loop for stateful controllers; it returns aStateLogfor replay and analysis.JointSpaceMPC(inskelarm.mpc) is the joint-space MPC tracker with torque bounds, soft joint-limit penalties, and warm starts.- The behaviors are tested in
tests/test_trajectory.py,tests/test_control.py, andtests/test_mpc.py(endpoint path tracking, joint-reference continuity, resolved-rate drift correction, computed-torque tracking quality, and MPC torque-bound handling).
A Controller is callable as f(t, skeleton) -> tau, so the stateless PD,
feedforward, and computed-torque controllers also work directly as a
simulate_robot torque callback. Controllers with their own dynamic state, such
as MPC and the online reference shaping of the next chapter, instead use the
reset/update hooks and the fixed control interval of simulate_controlled.
These controllers can also be configured from a single TOML scenario file and run
without writing code; see the Control Configuration
guide, the skelarm.scenario loader, and the tools/reaching_simulator.py runner.