Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Dynamics in Python

Gravity compensation, inverse dynamics, forward dynamics, and mass matrix computation via the featherstone articulated-body algorithm bridge.

Setup

import kinetic
import numpy as np

robot = kinetic.Robot("franka_panda")
dyn = kinetic.Dynamics(robot)

Gravity compensation

Torques needed to hold the robot stationary at a given configuration (countering gravity):

q = np.array([0.0, -0.3, 0.0, -1.5, 0.0, 1.2, 0.0])
tau_gravity = dyn.gravity_compensation(q)

print("Gravity torques (Nm):")
for name, t in zip(robot.joint_names, tau_gravity):
    print(f"  {name}: {t:+.3f}")

Shoulder joints carry more weight – you’ll see larger torques there.

Inverse dynamics

Given position, velocity, and acceleration, compute the required torques:

q   = np.array([0.0, -0.3, 0.0, -1.5, 0.0, 1.2, 0.0])
qd  = np.zeros(7)          # zero velocity (static)
qdd = np.zeros(7)          # zero acceleration

tau = dyn.inverse_dynamics(q, qd, qdd)
# With zero velocity and acceleration, this equals gravity compensation
print(f"ID torques: {np.round(tau, 3)}")

With non-zero acceleration (e.g., a step response):

qdd = np.array([0, 0, 0, 1.0, 0, 0, 0])  # accelerate joint 4
tau = dyn.inverse_dynamics(q, qd, qdd)
print(f"Torques with acceleration: {np.round(tau, 3)}")

Forward dynamics

Given position, velocity, and applied torques, compute the resulting accelerations:

tau_applied = dyn.gravity_compensation(q)  # exactly counteract gravity
qdd = dyn.forward_dynamics(q, qd, tau_applied)
print(f"Accelerations: {np.round(qdd, 6)}")
# Should be near-zero (gravity compensated, no external forces)

Apply extra torque to see the robot accelerate:

tau_extra = dyn.gravity_compensation(q)
tau_extra[3] += 5.0  # 5 Nm extra on joint 4

qdd = dyn.forward_dynamics(q, qd, tau_extra)
print(f"Joint 4 acceleration: {qdd[3]:.3f} rad/s^2")

Mass matrix

The joint-space inertia matrix M(q) – symmetric positive-definite:

M = dyn.mass_matrix(q)
print(f"Mass matrix shape: {M.shape}")   # (7, 7) for Panda
print(f"Diagonal (inertias): {np.diag(M).round(3)}")
print(f"Symmetric: {np.allclose(M, M.T)}")
print(f"Positive definite: {np.all(np.linalg.eigvalsh(M) > 0)}")

Trajectory feasibility check

Before executing on real hardware, verify that the required torques stay within the robot’s effort limits:

# Plan and time-parameterize
planner = kinetic.Planner(robot)
start = np.zeros(7)
goal = kinetic.Goal.joints(np.array([0.5, -0.5, 0.3, -1.0, 0.2, 1.0, 0.3]))
traj = planner.plan(start, goal)

# Check torques at each waypoint
times, positions, velocities = traj.to_numpy()
max_torques = np.zeros(robot.dof)

for i in range(len(times)):
    q = positions[i]
    qd = velocities[i]
    # Approximate acceleration from finite differences
    if i == 0 or i == len(times) - 1:
        qdd = np.zeros(robot.dof)
    else:
        dt = times[i+1] - times[i-1]
        qdd = (velocities[i+1] - velocities[i-1]) / dt if dt > 0 else np.zeros(robot.dof)

    tau = dyn.inverse_dynamics(q, qd, qdd)
    max_torques = np.maximum(max_torques, np.abs(tau))

print("Max torques per joint (Nm):")
for name, t in zip(robot.joint_names, max_torques):
    print(f"  {name}: {t:.2f}")

Next