Day 6
Control II: operational-space, impedance, contact
LECTURE & READING
Glossary primer (12 min)
- Operational-space Control & PlanningControlThe method used to make the robot move the way you want. (OSC) — Compute desired task-space (Cartesian) Movement, Mechanics & Robot BodyAccelerationHow quickly velocity changes., then map to Movement, Mechanics & Robot BodyJointA movable connection between robot parts. torques:
τ = J.T · F + (M − J.T · Λ · J · M⁻¹).proj(τ_null). Generalizes to redundant arms. - Impedance Control & PlanningControlThe method used to make the robot move the way you want. — Make the Core ConceptsRobotA physical system with sensors and actuators that can observe the world and take actions. behave like a spring-damper in Core ConceptsTaskThe job the robot is supposed to complete, such as pick-and-place, navigation, or drawer opening. space:
F_task = K_p · (x_des − x) − K_d · ẋ. The Core ConceptsRobotA physical system with sensors and actuators that can observe the world and take actions. can be soft (low Kp) or stiff (high Kp). - Movement, Mechanics & Robot BodyComplianceThe robot’s ability to yield a little during contact instead of staying rigid. — How much the Core ConceptsRobotA physical system with sensors and actuators that can observe the world and take actions. yields under external force. Inverse of Movement, Mechanics & Robot BodyStiffnessHow strongly a robot resists motion or deformation..
- Movement, Mechanics & Robot BodyContactPhysical interaction between the robot and an object or surface. — Control & PlanningConstraintA rule the robot must obey, such as avoiding collisions or staying within joint limits. forces when bodies touch. MuJoCo simulates them via soft-constraint solver (
solref,solimp). - Force/Movement, Mechanics & Robot BodyTorqueA rotational force around a joint or axis. Perception & SensingSensorA device that provides information about the robot or its environment. — Measures wrench (3 force + 3 Movement, Mechanics & Robot BodyTorqueA rotational force around a joint or axis.) at a frame. MuJoCo:
<sensor type="force"/>. - Hybrid position/force Control & PlanningControlThe method used to make the robot move the way you want. — Position-control some Cartesian directions, force-control others. Useful for surface Movement, Mechanics & Robot BodyContactPhysical interaction between the robot and an object or surface. tasks.
Real-world analogy
Position Control & PlanningControlThe method used to make the robot move the way you want. is "go to coordinates X." Impedance Control & PlanningControlThe method used to make the robot move the way you want. is "behave like an invisible rubber band attached to point X." Stiff = rigid glass, compliant = wet noodle. A peg-in-hole Core ConceptsTaskThe job the robot is supposed to complete, such as pick-and-place, navigation, or drawer opening. with stiff position Control & PlanningControlThe method used to make the robot move the way you want. will smash if even slightly misaligned; with low-stiffness impedance Control & PlanningControlThe method used to make the robot move the way you want., it gently slides into place under Movement, Mechanics & Robot BodyContactPhysical interaction between the robot and an object or surface. Movement, Mechanics & Robot BodyFrictionResistance between contacting surfaces that affects sliding and grasping..
Hour 1 — Lectures
- Khatib's foundational OSC lecture (CS223A Lectures 13–14):
- Lecture 13
- Lecture 14
- Khatib's original 1987 paper "A Unified Approach for Motion and Force Control & PlanningControlThe method used to make the robot move the way you want." — skim
Reading
Khatib's original 1987 paper "A Unified Approach for Motion and Force Control & PlanningControlThe method used to make the robot move the way you want." — skim
Open PDF
Hour 2 — Lectures
- Russ Tedrake Robotic Manipulation & TasksManipulationUsing a robot arm or hand to move or interact with objects. Ch. 3 "Differential Movement, Mechanics & Robot BodyInverse kinematics (IK)Calculating the joint values needed to reach a desired pose.", focusing on Differential Movement, Mechanics & Robot BodyInverse kinematics (IK)Calculating the joint values needed to reach a desired pose. with constraints: https://manipulation.csail.mit.edu/pick.html
- Modern Robotics Ch. 11 §11.6 "Hybrid Motion-Force Control & PlanningControlThe method used to make the robot move the way you want.".
LAB
Hour 3 — Lab: impedance circle-tracing on Panda with simulated push (75 min)
What you're building. A task-space impedance Control & PlanningControllerThe algorithm or system that turns desired behavior into motor commands. for the Panda. The EE traces a circle of radius 0.10 m at z=0.7 m at 0.25 Hz. While it traces, an external force perturbs the EE for 1 s. You compare stiff (Kp=1500) vs compliant (Kp=200) tuning and observe how the EE deviates and recovers.
What success looks like at the end. You have:
1. src/day6_impedance.py runnable.
2. Plot figures/day6_circle_tracking.png showing two side-by-side scatter plots (stiff vs compliant). Stiff: tight circle with small deformation under push. Compliant: visibly distorted circle, larger deformation.
3. Plot figures/day6_force_vs_disp.png showing peak EE displacement and peak interaction force during the push, for both regimes.
4. Console: "Stiff: peak disp 0.018 m, peak force 24.3 N. Compliant: peak disp 0.071 m, peak force 14.2 N."
Step 1 — Add an impedance controller (50 min)
Create src/day6_impedance.py:
"""Day 6: Task-space impedance control on the Panda EE, with a 1s push perturbation."""
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 = 8.0
ARM_DOF = 7
Q_HOME = np.array([0, -0.785, 0, -2.356, 0, 1.571, 0.785])
# Trajectory: circle in xy-plane at z=0.7
CIRCLE_RADIUS = 0.10
CIRCLE_FREQ_HZ = 0.25
CIRCLE_CENTER = np.array([0.4, 0.0, 0.5])
# Joint-space damping for residual modes
KQ_DAMP = np.array([5, 5, 5, 5, 2, 2, 2], dtype=float)
def task_space_target(t: float):
omega = 2 * np.pi * CIRCLE_FREQ_HZ
x = CIRCLE_CENTER + np.array([
CIRCLE_RADIUS * np.cos(omega * t),
CIRCLE_RADIUS * np.sin(omega * t),
0.0,
])
xd = np.array([
-CIRCLE_RADIUS * omega * np.sin(omega * t),
CIRCLE_RADIUS * omega * np.cos(omega * t),
0.0,
])
return x, xd
def run(kp_lin: float, push_force: float = 30.0, push_window=(3.0, 4.0)):
"""One run with given task-space stiffness Kp_lin (N/m).
Damping uses critical damping rule: Kd = 2*sqrt(Kp * m_eff). We use a fixed m_eff~1.
"""
model = mujoco.MjModel.from_xml_path(MODEL_PATH)
data = mujoco.MjData(model)
data.qpos[:ARM_DOF] = Q_HOME.copy()
data.qvel[:] = 0
mujoco.mj_forward(model, data)
ee_site_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_SITE, "attachment_site")
ee_body_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_BODY, "hand")
Kp = kp_lin * np.eye(3)
Kd = 2 * np.sqrt(kp_lin) * np.eye(3)
n_steps = int(SIM_TIME_S / model.opt.timestep)
history_pos = np.zeros((n_steps, 3))
history_target = np.zeros((n_steps, 3))
history_force = np.zeros(n_steps) # commanded EE force magnitude
t_arr = np.zeros(n_steps)
for k in range(n_steps):
t = k * model.opt.timestep
# --- Task-space target
x_des, xd_des = task_space_target(t)
# --- Current EE pose and velocity
x_now = data.site_xpos[ee_site_id].copy()
jacp = np.zeros((3, model.nv))
jacr = np.zeros((3, model.nv))
mujoco.mj_jacSite(model, data, jacp, jacr, ee_site_id)
xd_now = jacp[:, :ARM_DOF] @ data.qvel[:ARM_DOF]
# --- Impedance: F = Kp*(x_des - x) + Kd*(xd_des - xd)
F = Kp @ (x_des - x_now) + Kd @ (xd_des - xd_now)
history_force[k] = np.linalg.norm(F)
# --- Map F to joint torques: τ = J.T · F (translational only)
tau_task = jacp[:, :ARM_DOF].T @ F
# --- Joint-space damping for residual / null-space (small)
tau_null = -KQ_DAMP * data.qvel[:ARM_DOF]
# --- Gravity comp
tau_gc = data.qfrc_bias[:ARM_DOF].copy()
# --- Apply
data.qfrc_applied[:] = 0
data.qfrc_applied[:ARM_DOF] = tau_task + tau_null + tau_gc
# --- External push
if push_window[0] <= t < push_window[1]:
data.xfrc_applied[ee_body_id, :3] = [push_force, 0, 0]
else:
data.xfrc_applied[ee_body_id, :] = 0
mujoco.mj_step(model, data)
history_pos[k] = data.site_xpos[ee_site_id].copy()
history_target[k] = x_des
t_arr[k] = t
return t_arr, history_pos, history_target, history_force
def main():
print("Run 1: STIFF (Kp = 1500 N/m)")
t_s, pos_s, tgt_s, force_s = run(kp_lin=1500.0)
print("Run 2: COMPLIANT (Kp = 200 N/m)")
t_c, pos_c, tgt_c, force_c = run(kp_lin=200.0)
# Tracking error during push window (t in 3–4s)
push_mask_s = (t_s >= 3.0) & (t_s < 4.0)
push_mask_c = (t_c >= 3.0) & (t_c < 4.0)
err_s = np.linalg.norm(pos_s - tgt_s, axis=1)
err_c = np.linalg.norm(pos_c - tgt_c, axis=1)
peak_disp_s = err_s[push_mask_s].max()
peak_disp_c = err_c[push_mask_c].max()
peak_force_s = force_s[push_mask_s].max()
peak_force_c = force_c[push_mask_c].max()
print(f"\nStiff: peak disp {peak_disp_s:.3f} m, peak |F| {peak_force_s:.1f} N")
print(f"Compliant: peak disp {peak_disp_c:.3f} m, peak |F| {peak_force_c:.1f} N")
# --- Plot circles in xy
fig, axes = plt.subplots(1, 2, figsize=(11, 5))
for ax, (pos, tgt, label) in zip(axes,
[(pos_s, tgt_s, f"Stiff (Kp=1500), peak disp {peak_disp_s*1000:.0f} mm"),
(pos_c, tgt_c, f"Compliant (Kp=200), peak disp {peak_disp_c*1000:.0f} mm")]):
ax.plot(tgt[:, 0], tgt[:, 1], 'k--', alpha=0.4, label='target')
ax.plot(pos[:, 0], pos[:, 1], 'b-', alpha=0.7, label='actual')
# Highlight push window
push_mask = (t_s >= 3.0) & (t_s < 4.0)
ax.plot(pos[push_mask, 0], pos[push_mask, 1], 'r-', linewidth=2, label='during push')
ax.set_xlabel("X (m)")
ax.set_ylabel("Y (m)")
ax.set_title(label)
ax.legend(fontsize=9)
ax.set_aspect('equal')
ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig("../figures/day6_circle_tracking.png", dpi=120)
print("Wrote figures/day6_circle_tracking.png")
# --- Bar plot of peak disp vs peak force
fig2, ax2 = plt.subplots(figsize=(7, 4))
x = np.arange(2)
width = 0.35
ax2.bar(x - width/2, [peak_disp_s * 1000, peak_disp_c * 1000], width, label='peak disp (mm)', color='C0')
ax2.bar(x + width/2, [peak_force_s, peak_force_c], width, label='peak |F| (N)', color='C3')
ax2.set_xticks(x)
ax2.set_xticklabels(['Stiff Kp=1500', 'Compliant Kp=200'])
ax2.set_ylabel("Magnitude")
ax2.set_title("Stiffness trade-off under 1s, 30N push")
ax2.legend()
ax2.grid(alpha=0.3, axis='y')
plt.tight_layout()
plt.savefig("../figures/day6_force_vs_disp.png", dpi=120)
print("Wrote figures/day6_force_vs_disp.png")
return peak_disp_s, peak_disp_c, peak_force_s, peak_force_c
if __name__ == "__main__":
main()Run:
cd ~/robo47/w1-foundations
python src/day6_impedance.pyExpected output (numbers within ±30%):
Run 1: STIFF (Kp = 1500 N/m)
Run 2: COMPLIANT (Kp = 200 N/m)
Stiff: peak disp 0.018 m, peak |F| 24.3 N
Compliant: peak disp 0.071 m, peak |F| 14.2 N
Wrote figures/day6_circle_tracking.png
Wrote figures/day6_force_vs_disp.png- Expected figures:
day6_circle_tracking.png: Two side-by-side circles. Both show a target dashed black circle (radius 0.10 m). The stiff blue trace stays nearly on top of it, with a small red bump (peak ~2 cm) during the push. The compliant blue trace is visibly less round overall and during the push the red bump is much larger (~7 cm), showing the EE getting shoved out by ~7 cm.day6_force_vs_disp.png: Bar chart with stiff peak disp ~18 mm and stiff peak force ~24 N versus compliant peak disp ~71 mm and compliant peak force ~14 N. The trade-off is visible: stiff = small motion, large force; compliant = large motion, small force.
Step 2 — Common failures and fixes (15 min)
- EE shoots away during push (compliant case) —
Kp_lin=200is fine; if you set it to 50 the EE genuinely flies. Keep ≥ 100. - Stiff circle is also wobbly — Movement, Mechanics & Robot BodyJointA movable connection between robot parts. damping
KQ_DAMPtoo low. Raise to[10, 10, 10, 10, 4, 4, 4]. - Both runs identical — You forgot to pass
kp_linthroughrun(). PrintKpinside the loop on first step to verify. - `mj_jacSite` returns garbage — Confirm the site name "attachment_site" exists.
Step 3 — Log and commit (10 min)
echo "day6_imped_stiff,$(date -I),6,impedance_stiff,$(cd ~/robo47/w1-foundations && git rev-parse --short HEAD),0,Panda,impedance,4000,1,peak_disp_m,0.018,kp1500" >> ~/robo47/metrics/metrics.csv
echo "day6_imped_comp,$(date -I),6,impedance_compliant,$(cd ~/robo47/w1-foundations && git rev-parse --short HEAD),0,Panda,impedance,4000,1,peak_disp_m,0.071,kp200" >> ~/robo47/metrics/metrics.csv
cd ~/robo47/w1-foundations
git add src/day6_impedance.py figures/day6_circle_tracking.png figures/day6_force_vs_disp.png
git commit -m "Day 6: task-space impedance, stiff-vs-compliant trade-off"Deliverable checklist
Why this lab matters
Every contact-rich Manipulation & TasksManipulationUsing a robot arm or hand to move or interact with objects. Core ConceptsTaskThe job the robot is supposed to complete, such as pick-and-place, navigation, or drawer opening. — peg Manipulation & TasksInsertionPlacing one object into another, like plugging in a connector., wiping a surface, opening a drawer — relies on the operator picking the right Movement, Mechanics & Robot BodyStiffnessHow strongly a robot resists motion or deformation. in each Cartesian direction. VLAs that output Movement, Mechanics & Robot BodyJointA movable connection between robot parts. targets implicitly delegate this to the low-level Control & PlanningControllerThe algorithm or system that turns desired behavior into motor commands.; ones that output EE deltas (most modern ones) need the underlying Control & PlanningControllerThe algorithm or system that turns desired behavior into motor commands. to be impedance-style or they'll crash on Movement, Mechanics & Robot BodyContactPhysical interaction between the robot and an object or surface.. When BeyondMimic's authors talk about "Movement, Mechanics & Robot BodyComplianceThe robot’s ability to yield a little during contact instead of staying rigid." they mean exactly the Kp dial you tuned today.
Side quest (optional, 45 min)
Implement full OSC: build the task-space inertia matrix Λ = (J · M⁻¹ · J.T)⁻¹ and the dynamically-consistent Movement, Mechanics & Robot BodyTorqueA rotational force around a joint or axis. map τ = J.T · Λ · (ẍ_des - J̇ q̇) + null-space term. Compare tracking accuracy against today's simpler J.T · F Navigation & LocomotionMappingBuilding a representation of the environment.. The full version is what Khatib advocates and what high-end controllers (Cartesio, Drake's IIWA stack) actually run.
---