Day 4
Dynamics: Lagrangian, mass matrix, gravity compensation
LECTURE & READING
Glossary primer (10 min)
- Equation of motion (EOM) —
M(q) q̈ + C(q, q̇) q̇ + g(q) = τ. Newton's law for an articulated body. - Mass matrix M(q) — Symmetric positive-definite n×n matrix; the generalized inertia of the body at config
q. - Coriolis / centrifugal C(q, q̇) q̇ — Velocity-coupling terms; they vanish when
q̇ = 0. - Gravity vector g(q) — Movement, Mechanics & Robot BodyJointA movable connection between robot parts. torques required to hold the configuration
qagainst gravity. Pure function of pose. - Inverse Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia. — Given desired
q̈, q̇, q, solve for the Movement, Mechanics & Robot BodyTorqueA rotational force around a joint or axis.τthat achieves it. MuJoCo:mj_rne(Recursive Newton-Euler). - Forward Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia. — Given
τ, solve forq̈. MuJoCo: included in everymj_step. - Gravity compensation — Apply
τ = g(q)so the Core ConceptsRobotA physical system with sensors and actuators that can observe the world and take actions. just floats — gravity no longer fights your other commands. - Computed Movement, Mechanics & Robot BodyTorqueA rotational force around a joint or axis. Control & PlanningControlThe method used to make the robot move the way you want. —
τ = M(q) q̈_des + C(q, q̇) q̇ + g(q) + K_p (q_des − q) + K_d (q̇_des − q̇).
Real-world analogy
The mass matrix M is "how heavy does each Movement, Mechanics & Robot BodyJointA movable connection between robot parts. feel when you wiggle it from this pose?" An arm fully outstretched has a much higher effective inertia at the shoulder than the same arm folded. The gravity vector g is "what muscle Movement, Mechanics & Robot BodyTorqueA rotational force around a joint or axis. do I need just to not fall?" A weight lifter with arms overhead is fighting g; the same weight at chest height is much lower g.
Hour 1 — Robot Academy primer (optional, ~15 min)
- Rigid Body Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia. masterclass, just the lessons "Gravity load" and "Inertia and coupled motion": https://robotacademy.net.au/masterclass/rigid-body-dynamics/
Skip if comfortable; spend the time on Hour 2 instead.
Hour 2 — Math
- Modern Robotics Ch. 8 exact sequence, ~50 min:
- 8.1 Lagrangian Formulation of Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia., Part 1
Video
8.1 Lagrangian Formulation of Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia., Part 1
Open source - 8.1.3 Understanding the Mass Matrix
- 8.2 Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia. of a Single Rigid Body, Part 1
Video
8.2 Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia. of a Single Rigid Body, Part 1
Open source - 8.2 Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia. of a Single Rigid Body, Part 2
Video
8.2 Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia. of a Single Rigid Body, Part 2
Open source - 8.3 Newton-Euler Inverse Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia.
Video
8.3 Newton-Euler Inverse Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia.
Open source - 8.5 Forward Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia. of Open Chains
Video
8.5 Forward Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia. of Open Chains
Open source - 8.6 Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia. in the Core ConceptsTaskThe job the robot is supposed to complete, such as pick-and-place, navigation, or drawer opening. Space
Video
8.6 Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia. in the Core ConceptsTaskThe job the robot is supposed to complete, such as pick-and-place, navigation, or drawer opening. Space
Open source - 8.7 Constrained Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia.
Video
8.7 Constrained Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia.
Open source - 8.9 Actuation, Gearing, and Movement, Mechanics & Robot BodyFrictionResistance between contacting surfaces that affects sliding and grasping.
Video
8.9 Actuation, Gearing, and Movement, Mechanics & Robot BodyFrictionResistance between contacting surfaces that affects sliding and grasping.
Open source - Stanford CS223A Lectures 10–12 (Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia.), Khatib:
- Lecture 10
- Lecture 11
- Lecture 12
- Free PDF: Lynch & Park Ch. 8 §8.1–8.3.
LAB
Hour 3 — Lab: gravity-compensation + joint-space PD on the Panda (75 min)
What you're building. A Movement, Mechanics & Robot BodyTorqueA rotational force around a joint or axis. Control & PlanningControllerThe algorithm or system that turns desired behavior into motor commands. for the Panda that holds it static at any configuration against gravity using mujoco.mj_rne, then adds a joint-space PD on top to track a desired pose despite a simulated 1 N external push at the Movement, Mechanics & Robot BodyGripperA common end-effector used to grasp objects..
What success looks like at the end. You have:
1. src/day4_grav_comp.py runnable.
2. Plot figures/day4_tracking_error.png showing four lines: (a) joint-1 error without gravity comp, (b) joint-1 error with gravity comp + PD, (c) the same for Movement, Mechanics & Robot BodyJointA movable connection between robot parts. 4. With gravity comp, the steady-state error is ≤ 0.01 rad; without, it drifts to ≥ 0.2 rad.
3. Console reports the difference: "Mean tracking error reduced from 0.143 rad to 0.008 rad (17.9× improvement)".
Step 1 — Understand `mj_rne` (15 min)
mj_rne(m, d, flg_acc, qfrc)writes Movement, Mechanics & Robot BodyJointA movable connection between robot parts. torques intoqfrcsuch that, given currentqposandqvel:- If
flg_acc == 0:qfrc = C(q, q̇) q̇ + g(q)(just bias). - If
flg_acc == 1:qfrc = M(q) q̈ + C(q, q̇) q̇ + g(q)(full inverse Movement, Mechanics & Robot BodyDynamicsThe study of motion including forces, torques, mass, and inertia.).
There's also a built-in data.qfrc_bias that is computed automatically every step and equals the flg_acc==0 case. We'll use that directly.
Quick smoke test:
python -c "
import mujoco, numpy as np
m = mujoco.MjModel.from_xml_path('mujoco_menagerie/franka_emika_panda/scene.xml')
d = mujoco.MjData(m)
d.qpos[:7] = [0, -0.785, 0, -2.356, 0, 1.571, 0.785]
mujoco.mj_forward(m, d)
print('qfrc_bias[:7]:', np.round(d.qfrc_bias[:7], 3))
"Expected output: Non-zero values, mostly negative for joints fighting gravity.
qfrc_bias[:7]: [ 0. -19.59 0. 19.96 0. 1.36 0. ](Numbers within ~10% of these.)
Step 2 — Write the controller (35 min)
Create src/day4_grav_comp.py:
"""Day 4: Gravity compensation + joint-space PD on the Panda. Compare with and without."""
from __future__ import annotations
import numpy as np
import matplotlib.pyplot as plt
import mujoco
MODEL_PATH = "../mujoco_menagerie/franka_emika_panda/scene.xml"
SIM_TIME_S = 5.0
# Joints we'll control (the 7 arm joints). MuJoCo Panda has 7 arm joints + 2 gripper.
ARM_DOF = 7
# Home configuration
Q_HOME = np.array([0, -0.785, 0, -2.356, 0, 1.571, 0.785])
# PD gains (tuned roughly: ~2 Hz natural freq, critical damping)
KP = np.array([200, 200, 150, 150, 80, 80, 40], dtype=float)
KD = np.array([20, 20, 15, 15, 8, 8, 4], dtype=float)
def run_controller(use_grav_comp: bool, push_force: float = 1.0):
"""Run a 5-second sim. Optionally apply gravity comp. Push the EE for 1s starting at t=2s."""
model = mujoco.MjModel.from_xml_path(MODEL_PATH)
data = mujoco.MjData(model)
# Disable any default control (we compute torque ourselves)
data.qpos[:ARM_DOF] = Q_HOME.copy()
data.qvel[:] = 0
mujoco.mj_forward(model, data)
# We'll apply torques via data.qfrc_applied (added to each step's force balance)
n_steps = int(SIM_TIME_S / model.opt.timestep)
err = np.zeros((n_steps, ARM_DOF))
t_arr = np.zeros(n_steps)
ee_body_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_BODY, "hand")
for k in range(n_steps):
t = k * model.opt.timestep
# PD around home
q = data.qpos[:ARM_DOF].copy()
qd = data.qvel[:ARM_DOF].copy()
tau_pd = KP * (Q_HOME - q) - KD * qd
# Gravity comp
if use_grav_comp:
tau_gc = data.qfrc_bias[:ARM_DOF].copy()
else:
tau_gc = np.zeros(ARM_DOF)
# Apply torque
data.qfrc_applied[:] = 0
data.qfrc_applied[:ARM_DOF] = tau_pd + tau_gc
# External push on EE for 1 second starting at t=2
if 2.0 <= t < 3.0:
data.xfrc_applied[ee_body_id, :3] = [push_force, 0, 0]
else:
data.xfrc_applied[ee_body_id, :] = 0
mujoco.mj_step(model, data)
err[k] = data.qpos[:ARM_DOF] - Q_HOME
t_arr[k] = t
return t_arr, err
def main():
print("Run 1: PD only (no gravity comp)...")
t1, err_no_gc = run_controller(use_grav_comp=False)
print("Run 2: PD + gravity compensation...")
t2, err_with_gc = run_controller(use_grav_comp=True)
# Steady-state error: mean over last 1 s
n = len(t1)
mask_ss = (t1 > 4.0)
mean_err_no_gc = np.mean(np.abs(err_no_gc[mask_ss]))
mean_err_with_gc = np.mean(np.abs(err_with_gc[mask_ss]))
improvement = mean_err_no_gc / max(mean_err_with_gc, 1e-9)
print(f"\nMean steady-state |error| no gravity comp: {mean_err_no_gc:.4f} rad")
print(f"Mean steady-state |error| with gravity comp: {mean_err_with_gc:.4f} rad")
print(f"Improvement: {improvement:.1f}x")
# Plot joints 1 and 4 (most affected by gravity)
fig, axes = plt.subplots(2, 1, figsize=(10, 6), sharex=True)
axes[0].plot(t1, err_no_gc[:, 1], label='No grav comp', color='C3')
axes[0].plot(t2, err_with_gc[:, 1], label='With grav comp', color='C0')
axes[0].axvspan(2, 3, alpha=0.15, color='gray', label='1N push')
axes[0].set_ylabel("Joint 2 error (rad)")
axes[0].legend()
axes[0].grid(alpha=0.3)
axes[1].plot(t1, err_no_gc[:, 3], label='No grav comp', color='C3')
axes[1].plot(t2, err_with_gc[:, 3], label='With grav comp', color='C0')
axes[1].axvspan(2, 3, alpha=0.15, color='gray')
axes[1].set_xlabel("Time (s)")
axes[1].set_ylabel("Joint 4 error (rad)")
axes[1].grid(alpha=0.3)
fig.suptitle(f"Joint-tracking error: PD only vs PD + gravity comp ({improvement:.1f}× better)")
plt.tight_layout()
plt.savefig("../figures/day4_tracking_error.png", dpi=120)
print("Wrote figures/day4_tracking_error.png")
return mean_err_no_gc, mean_err_with_gc
if __name__ == "__main__":
main()Run:
cd ~/robo47/w1-foundations
python src/day4_grav_comp.pyExpected output:
Run 1: PD only (no gravity comp)...
Run 2: PD + gravity compensation...
Mean steady-state |error| no gravity comp: 0.1423 rad
Mean steady-state |error| with gravity comp: 0.0079 rad
Improvement: 18.0x
Wrote figures/day4_tracking_error.png(Your numbers should be within ±20% of these.)
Expected figure: Two stacked plots, Movement, Mechanics & Robot BodyJointA movable connection between robot parts. 2 on top, Movement, Mechanics & Robot BodyJointA movable connection between robot parts. 4 on bottom. Red curves (no grav comp) settle at non-zero offsets (~0.1–0.2 rad steady-state error). Blue curves (with grav comp) settle near zero. Both curves have a brief 1 s perturbation visible during the gray-shaded push interval at t=2–3 s, but the blue one recovers cleanly while the red one shifts permanently.
Step 3 — Common failures and fixes (10 min)
- "Improvement: 1.0x" — You're applying gravity comp in both runs. Check that
tau_gcis zeros whenuse_grav_comp=False. - Both curves explode (NaN) —
KPtoo high ormj_stepnot stepping at defaultdt. Printmodel.opt.timestep(should be 0.002 s). - No effect from the push —
xfrc_appliedindexing wrong. Check thatee_body_idis positive (not -1). The body is named "hand" in the standard menagerie Panda; if it changed, run[mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_BODY, i) for i in range(model.nbody)].
Step 4 — Log and commit (5 min)
echo "day4_gc_pd,$(date -I),4,grav_comp_vs_no,$(cd ~/robo47/w1-foundations && git rev-parse --short HEAD),0,Panda,pd_grav,2500,1,improvement_x,18.0,kp200_kd20" >> ~/robo47/metrics/metrics.csv
cd ~/robo47/w1-foundations
git add src/day4_grav_comp.py figures/day4_tracking_error.png
git commit -m "Day 4: gravity compensation + PD, 18x tracking improvement"Deliverable checklist
Why this lab matters
Real torque-controlled robots (Franka, Universal Robots, KUKA iiwa) all run a gravity-compensation loop at 1 kHz under whatever higher-level Control & PlanningControllerThe algorithm or system that turns desired behavior into motor commands. you stack on top — including learned policies. Many "Imitation & Reinforcement LearningImitation Learning (IL)Teaching a robot by showing it examples of how to do a task. fails on hardware" stories trace back to bad gravity comp. Knowing this is the difference between debugging a problem in 30 minutes versus 3 days.
Side quest (optional, 30 min)
Implement full computed-torque Control & PlanningControlThe method used to make the robot move the way you want.: τ = M q̈_des + C q̇ + g + K_p e + K_d ė. You'll need mj_fullM to build M. Compare against PD+grav for tracking a sinusoidal Movement, Mechanics & Robot BodyJointA movable connection between robot parts. Core ConceptsTrajectoryA sequence of states or actions over time. q_des(t) = Q_HOME + 0.3 sin(2π · 0.5 · t).
---