Back to Engineering

Robotics & Automation Series Part 5: Dynamics & Robot Modeling

February 13, 2026 Wasil Zafar 35 min read

Understand the forces and torques that govern robot motion — Newton-Euler formulation, Lagrangian dynamics, mass and inertia matrices, friction modeling, and energy-based approaches.

Table of Contents

  1. Newton-Euler Formulation
  2. Lagrangian Dynamics
  3. Friction & Contact Modeling
  4. Advanced Dynamics
  5. Dynamics Parameter Calculator
  6. Exercises & Challenges
  7. Conclusion & Next Steps

Newton-Euler Formulation

Series Overview: This is Part 5 of our 18-part Robotics & Automation Series. Here we explore the dynamics that describe how forces and torques produce robot motion — essential for trajectory planning and control design.

Robotics & Automation Mastery

Your 18-step learning path • Currently on Step 5
Introduction to Robotics
History, types, DOF, architectures, mechatronics, ethics
Sensors & Perception Systems
Encoders, IMUs, LiDAR, cameras, sensor fusion, Kalman filters, SLAM
Actuators & Motion Control
DC/servo/stepper motors, hydraulics, drivers, gear systems
Kinematics (Forward & Inverse)
DH parameters, transformations, Jacobians, workspace analysis
5
Dynamics & Robot Modeling
Newton-Euler, Lagrangian, inertia, friction, contact modeling
You Are Here
6
Control Systems & PID
PID tuning, state-space, LQR, MPC, adaptive & robust control
7
Embedded Systems & Microcontrollers
Arduino, STM32, RTOS, PWM, serial protocols, FPGA
8
Robot Operating Systems (ROS)
ROS2, nodes, topics, Gazebo, URDF, navigation stacks
9
Computer Vision for Robotics
Calibration, stereo vision, object recognition, visual SLAM
10
AI Integration & Autonomous Systems
ML, reinforcement learning, path planning, swarm robotics
11
Human-Robot Interaction (HRI)
Cobots, gesture/voice control, safety standards, social robotics
12
Industrial Robotics & Automation
PLC, SCADA, Industry 4.0, smart factories, digital twins
13
Mobile Robotics
Wheeled/legged robots, autonomous vehicles, drones, marine robotics
14
Safety, Reliability & Compliance
Functional safety, redundancy, ISO standards, cybersecurity
15
Advanced & Emerging Robotics
Soft robotics, bio-inspired, surgical, space, nano-robotics
16
Systems Integration & Deployment
HW/SW co-design, testing, field deployment, lifecycle
17
Robotics Business & Strategy
Startups, product-market fit, manufacturing, go-to-market
18
Complete Robotics System Project
Autonomous rover, pick-and-place arm, delivery robot, swarm sim

While kinematics describes where a robot moves, dynamics describes how it moves — accounting for masses, inertias, gravity, and forces. The Newton-Euler formulation approaches dynamics from first principles: Newton's second law (F = ma) for linear motion and Euler's equation (Ï„ = Iα) for rotational motion.

Everyday Analogy: Kinematics is like knowing the route on a map. Dynamics is knowing what engine power, fuel, and road friction you need to actually drive along that route at a given speed. Without dynamics, your robot might plan beautiful trajectories but have no idea what motor torques to command.

Forward vs. Inverse Dynamics

ProblemGivenFindUse Case
Forward DynamicsJoint torques Ï„Joint accelerations q̈Simulation — "apply these torques, how does the robot move?"
Inverse DynamicsDesired trajectory (q, q̇, q̈)Required torques Ï„Control — "what torques produce this motion?"

Free Body Diagrams for Robot Links

For each link in a robot chain, we draw a free body diagram showing all forces and torques acting on it. The Newton-Euler approach propagates these forces and torques along the chain in two passes:

Recursive Newton-Euler Algorithm (RNEA):
Forward pass (base → tip): Propagate velocities and accelerations from base to end-effector.
Backward pass (tip → base): Propagate forces and torques from end-effector back to base.
import numpy as np

class Link:
    """Represents a single robot link for dynamics computation."""
    def __init__(self, mass, length, inertia, com_offset=0.5):
        self.mass = mass          # kg
        self.length = length      # m
        self.inertia = inertia    # kg·m² (about center of mass)
        self.com = com_offset * length  # center of mass distance from joint

def inverse_dynamics_2link(links, theta, theta_dot, theta_ddot, gravity=9.81):
    """Inverse dynamics for a 2-link planar arm using Newton-Euler.
    
    Args:
        links: List of Link objects [link1, link2]
        theta: Joint angles [theta1, theta2] in radians
        theta_dot: Joint velocities [w1, w2]
        theta_ddot: Joint accelerations [a1, a2]
        gravity: Gravitational acceleration
    Returns:
        Required joint torques [tau1, tau2]
    """
    m1, m2 = links[0].mass, links[1].mass
    L1, L2 = links[0].length, links[1].length
    lc1, lc2 = links[0].com, links[1].com
    I1, I2 = links[0].inertia, links[1].inertia
    g = gravity
    
    t1, t2 = theta
    w1, w2 = theta_dot
    a1, a2 = theta_ddot
    
    # Inertia matrix M(q) — 2x2 for 2-link arm
    M11 = (m1*lc1**2 + I1 + m2*(L1**2 + lc2**2 + 2*L1*lc2*np.cos(t2)) + I2)
    M12 = m2*(lc2**2 + L1*lc2*np.cos(t2)) + I2
    M21 = M12
    M22 = m2*lc2**2 + I2
    
    # Coriolis/centrifugal terms C(q, q_dot)
    h = -m2 * L1 * lc2 * np.sin(t2)
    C1 = h * (2*w1*w2 + w2**2)  # Coriolis + centrifugal
    C2 = -h * w1**2              # Back-reaction
    
    # Gravity terms G(q)
    G1 = (m1*lc1 + m2*L1)*g*np.cos(t1) + m2*lc2*g*np.cos(t1 + t2)
    G2 = m2*lc2*g*np.cos(t1 + t2)
    
    # τ = M(q)·q̈ + C(q,q̇) + G(q)
    tau1 = M11*a1 + M12*a2 + C1 + G1
    tau2 = M21*a1 + M22*a2 + C2 + G2
    
    return np.array([tau1, tau2])

# Define robot links
link1 = Link(mass=5.0, length=0.5, inertia=0.1)
link2 = Link(mass=3.0, length=0.4, inertia=0.05)

# Scenario: Holding still at 45°, 30° (gravity compensation)
theta = np.radians([45, 30])
tau_static = inverse_dynamics_2link(
    [link1, link2], theta, 
    theta_dot=[0, 0], theta_ddot=[0, 0]
)
print("Static Gravity Compensation:")
print(f"  θ = (45°, 30°)")
print(f"  τ1 = {tau_static[0]:.3f} Nm, τ2 = {tau_static[1]:.3f} Nm")

# Scenario: Moving with constant velocity (Coriolis/centrifugal)
tau_moving = inverse_dynamics_2link(
    [link1, link2], theta,
    theta_dot=[1.0, 0.5], theta_ddot=[0, 0]
)
print(f"\nConstant Velocity (ω1=1, ω2=0.5 rad/s):")
print(f"  τ1 = {tau_moving[0]:.3f} Nm, τ2 = {tau_moving[1]:.3f} Nm")

# Scenario: Accelerating (full dynamics)
tau_accel = inverse_dynamics_2link(
    [link1, link2], theta,
    theta_dot=[1.0, 0.5], theta_ddot=[2.0, 1.5]
)
print(f"\nAccelerating (α1=2, α2=1.5 rad/s²):")
print(f"  τ1 = {tau_accel[0]:.3f} Nm, τ2 = {tau_accel[1]:.3f} Nm")

Practical Examples

Case Study: ABB IRB 6700 — Payload Capacity from Dynamics

Industrial Robot 150 kg Payload

The ABB IRB 6700 can handle 150 kg with a 3.2m reach. Its datasheet specifies maximum payload, but how is this number determined? Through inverse dynamics analysis.

Process: Engineers model each of the 6 links with mass, inertia tensor, and center of mass. For the worst-case trajectory (maximum acceleration at full extension), they compute the required joint torques via inverse dynamics. The payload limit is where any motor's required torque reaches its peak continuous rating.

Key Insight: Payload capacity depends on the trajectory! A robot can handle more weight when moving slowly near its base than when making fast, extended motions. Dynamics makes this trade-off quantifiable.

Lagrangian Dynamics

The Lagrangian formulation offers an alternative to Newton-Euler that's more elegant for complex systems. Instead of tracking forces on each link, it works with energy — the difference between kinetic and potential energy of the entire system.

Why Two Formulations?
• Newton-Euler is computationally efficient (O(n) for n joints) — ideal for real-time control
• Lagrangian is mathematically elegant and gives the equations of motion in closed form — ideal for analysis and design
Lagrangian Equation:
L = T − V (Lagrangian = Kinetic Energy − Potential Energy)
d/dt(∂L/∂q̇ᵢ) − ∂L/∂qáµ¢ = τᵢ (Euler-Lagrange equation for joint i)
import numpy as np

def lagrangian_1link(theta, theta_dot, theta_ddot, 
                     mass=2.0, length=0.5, gravity=9.81):
    """Lagrangian dynamics for a single pendulum (1-link arm).
    
    The simplest possible example to illustrate the method.
    Kinetic energy: T = 0.5 * I * theta_dot^2
    Potential energy: V = m * g * lc * cos(theta)
    """
    lc = length / 2  # Center of mass at link midpoint
    I = mass * lc**2  # Simplified inertia (point mass approximation)
    
    # Energy components
    T = 0.5 * I * theta_dot**2
    V = mass * gravity * lc * np.cos(theta)
    L = T - V
    
    # Euler-Lagrange equation: I*theta_ddot + m*g*lc*sin(theta) = tau
    tau = I * theta_ddot + mass * gravity * lc * np.sin(theta)
    
    return tau, T, V

# Example: gravity torque at different angles
print("1-Link Arm (Pendulum) — Lagrangian Dynamics")
print(f"Mass=2.0kg, Length=0.5m")
print("=" * 55)

angles = [0, 30, 45, 90, 180]
for deg in angles:
    rad = np.radians(deg)
    tau, T, V = lagrangian_1link(rad, 0, 0)
    print(f"θ={deg:4d}° → Ï„_gravity={tau:7.3f} Nm, "
          f"T={T:.3f} J, V={V:7.3f} J")

# With motion: accelerating at 5 rad/s² from 45°
tau_accel, T, V = lagrangian_1link(np.radians(45), 2.0, 5.0)
print(f"\nθ=45°, ω=2 rad/s, α=5 rad/s²:")
print(f"  Required torque: {tau_accel:.3f} Nm")
print(f"  (gravity component: {2.0*9.81*0.25*np.sin(np.radians(45)):.3f} Nm)")
print(f"  (inertial component: {2.0*0.25**2 * 5.0:.3f} Nm)")

Mass & Inertia Matrices

For multi-link robots, the dynamics equation takes the elegant matrix form:

Robot Equation of Motion:
τ = M(q)·q̈ + C(q, q̇)·q̇ + G(q)

Where:
• M(q) — Mass/inertia matrix (n×n, symmetric, positive definite)
• C(q, q̇) — Coriolis and centrifugal matrix
• G(q) — Gravity vector
• q, q̇, q̈ — Joint positions, velocities, accelerations
• Ï„ — Joint torques
import numpy as np

def compute_dynamics_matrices(theta, m1=5.0, m2=3.0,
                               L1=0.5, L2=0.4, g=9.81):
    """Compute M, C, G matrices for a 2-link planar arm.
    
    Returns the three fundamental dynamics matrices.
    """
    lc1, lc2 = L1/2, L2/2
    I1 = m1 * L1**2 / 12  # Rod inertia about center
    I2 = m2 * L2**2 / 12
    
    t2 = theta[1]
    
    # Mass matrix M(q)
    M = np.array([
        [m1*lc1**2 + I1 + m2*(L1**2 + lc2**2 + 2*L1*lc2*np.cos(t2)) + I2,
         m2*(lc2**2 + L1*lc2*np.cos(t2)) + I2],
        [m2*(lc2**2 + L1*lc2*np.cos(t2)) + I2,
         m2*lc2**2 + I2]
    ])
    
    # Coriolis matrix C(q, q_dot) using Christoffel symbols
    h = m2 * L1 * lc2 * np.sin(t2)
    C = np.array([
        [-h, -h],
        [ h,  0]
    ])
    
    # Gravity vector G(q)
    G = np.array([
        (m1*lc1 + m2*L1)*g*np.cos(theta[0]) + m2*lc2*g*np.cos(theta[0]+t2),
        m2*lc2*g*np.cos(theta[0] + t2)
    ])
    
    return M, C, G

# Analyze dynamics at different configurations
configs = [
    (np.radians([0, 0]), "Horizontal, extended"),
    (np.radians([90, 0]), "Vertical, extended"),
    (np.radians([0, 90]), "Horizontal, elbow bent"),
]

for theta, desc in configs:
    M, C, G = compute_dynamics_matrices(theta)
    print(f"\nConfiguration: {desc}")
    print(f"  M (mass matrix):\n    {np.round(M, 4)}")
    print(f"  det(M) = {np.linalg.det(M):.6f} (always > 0)")
    print(f"  G (gravity): [{G[0]:.3f}, {G[1]:.3f}] Nm")
    
    # Eigenvalues of M show principal inertias
    eigvals = np.linalg.eigvals(M)
    print(f"  Principal inertias: {np.round(eigvals, 4)}")
    print(f"  Condition number: {max(eigvals)/min(eigvals):.2f}")
Properties of M(q):
1. Symmetric: M = MT (always)
2. Positive Definite: xTMx > 0 for all x ≠ 0 (kinetic energy is always positive)
3. Configuration-Dependent: M changes as the robot moves
4. Bounded: λmin(M) ≤ ||M|| ≤ λmax(M) — important for control stability

Friction & Contact Modeling

Real robots aren't frictionless. Friction in joints, gears, and bearings significantly affects performance. Accurate friction models are critical for precise control, especially at low speeds.

Friction Models

ModelEquationCapturesComplexity
Coulombτ_f = μ·sign(q̇)Constant kinetic frictionSimple
Viscousτ_f = b·q̇Velocity-dependent dampingSimple
Coulomb + Viscousτ_f = μ·sign(q̇) + b·q̇Both effects combinedModerate
StribeckÏ„_f includes velocity-dependent static→kinetic transitionStick-slip at low speedsComplex
LuGreState-dependent bristle modelPre-sliding, hysteresisAdvanced
import numpy as np

def coulomb_friction(velocity, mu=0.5):
    """Coulomb (dry) friction — constant magnitude, opposes motion."""
    return mu * np.sign(velocity)

def viscous_friction(velocity, b=0.1):
    """Viscous (fluid) friction — proportional to velocity."""
    return b * velocity

def combined_friction(velocity, mu=0.5, b=0.1):
    """Coulomb + viscous friction model."""
    return mu * np.sign(velocity) + b * velocity

def stribeck_friction(velocity, mu_s=0.8, mu_k=0.5, b=0.1, v_s=0.1):
    """Stribeck friction model — captures stick-slip behavior.
    
    Args:
        mu_s: Static friction coefficient
        mu_k: Kinetic friction coefficient
        b: Viscous coefficient
        v_s: Stribeck velocity (transition speed)
    """
    # Stribeck curve: smooth transition from static to kinetic
    stribeck = mu_k + (mu_s - mu_k) * np.exp(-(velocity / v_s)**2)
    return stribeck * np.sign(velocity) + b * velocity

# Compare friction models
velocities = np.linspace(-2, 2, 201)

print("Friction Model Comparison at v = 0.5 rad/s")
print("=" * 50)
v_test = 0.5
print(f"Coulomb:   Ï„ = {coulomb_friction(v_test):.3f} Nm")
print(f"Viscous:   Ï„ = {viscous_friction(v_test):.3f} Nm")
print(f"Combined:  Ï„ = {combined_friction(v_test):.3f} Nm")
print(f"Stribeck:  Ï„ = {stribeck_friction(v_test):.3f} Nm")

# Show Stribeck effect at low speeds
print(f"\nStribeck Effect at Low Speeds:")
for v in [0.01, 0.05, 0.1, 0.5, 1.0]:
    f_strib = stribeck_friction(v)
    f_comb = combined_friction(v)
    print(f"  v={v:.2f}: Stribeck={f_strib:.3f}, Combined={f_comb:.3f}")

Contact Dynamics

When robots interact with the environment — grasping objects, walking on ground, or doing assembly — contact dynamics become essential. Contacts introduce discontinuities (impact) and constraints (non-penetration).

Case Study: Boston Dynamics Spot — Contact-Rich Locomotion

Legged Robot Dynamic Walking

Boston Dynamics' Spot robot navigates rough terrain by continuously making and breaking contact with the ground through its 12 actuated joints (3 per leg). Each footfall involves:

Impact dynamics: When a foot strikes the ground, the robot experiences an impulsive force transfer. Spot's controller must predict and compensate for these impacts in real time.

Friction cone constraints: Each foot contact is stable only if the ground reaction force stays within the friction cone (no slipping). Spot continuously monitors this to determine safe footholds.

Contact scheduling: The gait planner decides which feet are in contact and when — balancing stability, speed, and energy efficiency. This is a hybrid dynamics problem (continuous motion + discrete contact events).

import numpy as np

def spring_damper_contact(penetration, velocity, 
                          k=10000, b=500, mu=0.5):
    """Simple spring-damper contact model.
    
    Models ground contact as a spring + damper when penetration > 0.
    Friction modeled with Coulomb model.
    
    Args:
        penetration: Depth below ground (positive = contact)
        velocity: [vx, vz] velocity at contact point
        k: Contact stiffness (N/m)
        b: Contact damping (N·s/m)
        mu: Friction coefficient
    Returns:
        Contact force [Fx, Fz]
    """
    if penetration <= 0:
        return np.array([0.0, 0.0])  # No contact
    
    # Normal force (spring-damper, only compressive)
    Fn = k * penetration - b * velocity[1]
    Fn = max(Fn, 0)  # Can only push, not pull
    
    # Tangential friction force
    Ft = -mu * Fn * np.sign(velocity[0]) if abs(velocity[0]) > 1e-6 else 0
    
    return np.array([Ft, Fn])

# Simulate a ball dropping onto ground
print("Spring-Damper Contact Model Simulation")
print("=" * 50)

# Ball parameters
mass = 1.0  # kg
z = 0.5     # initial height (m)
vz = 0.0    # initial vertical velocity
vx = 0.2    # horizontal velocity
dt = 0.001  # time step
g = 9.81

print(f"Ball: mass={mass}kg, initial height={z}m")
print(f"Contact: k=10000 N/m, b=500 N·s/m, μ=0.5")
print(f"\nTime    Height   Velocity  Contact Force")
print("-" * 50)

for step in range(500):
    t = step * dt
    penetration = -z  # Positive when below ground (z=0)
    F = spring_damper_contact(penetration, np.array([vx, vz]))
    
    # Update dynamics: a = F/m - g
    az = F[1]/mass - g
    ax = F[0]/mass
    
    vz += az * dt
    vx += ax * dt
    z += vz * dt
    
    if step % 50 == 0:
        print(f"{t:.3f}s  {z:8.4f}m  {vz:8.4f} m/s  Fz={F[1]:8.1f} N")

Advanced Dynamics Topics

Manipulator Dynamics

Industrial manipulators present unique dynamics challenges due to their serial structure, high gear ratios, and need for precision. Key considerations include:

Gravity Compensation

Many industrial robots dedicate significant motor torque just to fighting gravity. Gravity compensation pre-computes the gravity torque G(q) and feeds it forward, freeing the controller to focus on motion control.

import numpy as np

def gravity_compensation(theta, m1=5.0, m2=3.0, 
                          L1=0.5, L2=0.4, g=9.81):
    """Compute gravity compensation torques for a 2-link arm.
    
    These torques exactly cancel gravity at the given configuration.
    """
    lc1, lc2 = L1/2, L2/2
    
    G1 = (m1*lc1 + m2*L1)*g*np.cos(theta[0]) + m2*lc2*g*np.cos(theta[0]+theta[1])
    G2 = m2*lc2*g*np.cos(theta[0] + theta[1])
    
    return np.array([G1, G2])

# Show gravity torques across workspace
print("Gravity Compensation Torques Across Configurations")
print("=" * 55)
print("2-Link arm: m1=5kg, m2=3kg, L1=0.5m, L2=0.4m")
print(f"{'θ1':>6} {'θ2':>6} {'τ1_grav':>10} {'τ2_grav':>10} {'Total':>10}")
print("-" * 55)

for t1 in [0, 30, 45, 60, 90]:
    for t2 in [0, 45, 90]:
        theta = np.radians([t1, t2])
        G = gravity_compensation(theta)
        print(f"{t1:6d}° {t2:6d}° {G[0]:10.3f} {G[1]:10.3f} "
              f"{np.sum(np.abs(G)):10.3f} Nm")

# Key insight: maximum gravity torque at horizontal
theta_max = np.radians([0, 0])
G_max = gravity_compensation(theta_max)
print(f"\nMax gravity load (arm horizontal): τ1={G_max[0]:.3f} Nm")
print(f"This requires {G_max[0]:.1f} Nm continuous torque just to hold position!")

Mobile Robot Dynamics

Mobile robots have fundamentally different dynamics from manipulators. They move through space under nonholonomic constraints — restrictions on velocity that cannot be integrated into position constraints.

Nonholonomic Constraint: A car can't move sideways. This velocity constraint (v_lateral = 0) cannot be converted to a position constraint — a car can reach any sideways position through a sequence of forward/reverse motions with steering. This is what makes parallel parking challenging!
import numpy as np

def differential_drive_dynamics(state, v_left, v_right, 
                                  wheel_radius=0.05, wheel_base=0.3, dt=0.01):
    """Simulate differential drive robot dynamics.
    
    State: [x, y, theta] — position and heading
    Inputs: left and right wheel velocities (rad/s)
    """
    x, y, theta = state
    R = wheel_radius
    L = wheel_base
    
    # Convert wheel velocities to robot velocities
    v = R * (v_right + v_left) / 2       # Linear velocity
    omega = R * (v_right - v_left) / L    # Angular velocity
    
    # Update state (Euler integration)
    x_new = x + v * np.cos(theta) * dt
    y_new = y + v * np.sin(theta) * dt
    theta_new = theta + omega * dt
    
    return np.array([x_new, y_new, theta_new]), v, omega

# Simulate different motions
state = np.array([0.0, 0.0, 0.0])  # Start at origin facing +X
dt = 0.01

print("Differential Drive Robot Dynamics")
print("=" * 55)

# Motion 1: Straight line
print("\n1. Straight Line (both wheels same speed):")
s = np.array([0.0, 0.0, 0.0])
for _ in range(100):
    s, v, w = differential_drive_dynamics(s, 10.0, 10.0, dt=dt)
print(f"   After 1s: x={s[0]:.3f}, y={s[1]:.3f}, θ={np.degrees(s[2]):.1f}°")

# Motion 2: Pure rotation (spin in place)
print("\n2. Pure Rotation (wheels opposite speeds):")
s = np.array([0.0, 0.0, 0.0])
for _ in range(100):
    s, v, w = differential_drive_dynamics(s, -10.0, 10.0, dt=dt)
print(f"   After 1s: x={s[0]:.4f}, y={s[1]:.4f}, θ={np.degrees(s[2]):.1f}°")

# Motion 3: Arc turn
print("\n3. Arc Turn (different wheel speeds):")
s = np.array([0.0, 0.0, 0.0])
for _ in range(300):
    s, v, w = differential_drive_dynamics(s, 8.0, 12.0, dt=dt)
print(f"   After 3s: x={s[0]:.3f}, y={s[1]:.3f}, θ={np.degrees(s[2]):.1f}°")
print(f"   Turn radius: {0.05*(12+8)/(2*(12-8)/0.3):.3f} m")

Energy-Based Modeling

Energy methods provide powerful tools for analyzing robot dynamics without solving differential equations directly.

Energy Concepts in Robotics:
• Kinetic Energy: T = ½ q̇T M(q) q̇ — always positive, depends on velocities and configuration
• Potential Energy: V = Σ máµ¢ g háµ¢ — gravitational potential of each link
• Power: P = Ï„T q̇ — rate of energy input from motors
• Passivity: The decrease in stored energy ≤ power input (fundamental for stable control)
import numpy as np

def compute_energy(theta, theta_dot, m1=5.0, m2=3.0,
                   L1=0.5, L2=0.4, g=9.81):
    """Compute kinetic and potential energy for a 2-link arm."""
    lc1, lc2 = L1/2, L2/2
    I1, I2 = m1*L1**2/12, m2*L2**2/12
    t1, t2 = theta
    w1, w2 = theta_dot
    
    # Heights of centers of mass
    h1 = lc1 * np.sin(t1)
    h2 = L1 * np.sin(t1) + lc2 * np.sin(t1 + t2)
    
    # Potential energy
    V = m1*g*h1 + m2*g*h2
    
    # Kinetic energy (from mass matrix)
    M11 = m1*lc1**2 + I1 + m2*(L1**2 + lc2**2 + 2*L1*lc2*np.cos(t2)) + I2
    M12 = m2*(lc2**2 + L1*lc2*np.cos(t2)) + I2
    M22 = m2*lc2**2 + I2
    
    T = 0.5 * (M11*w1**2 + 2*M12*w1*w2 + M22*w2**2)
    
    return T, V, T + V

# Energy analysis along a trajectory
print("Energy Analysis — 2-Link Arm Swinging Under Gravity")
print("=" * 60)
print(f"{'Angle':>8} {'Kinetic':>10} {'Potential':>10} {'Total':>10}")
print("-" * 60)

# Simulate pendulum-like swing (no motors, gravity only)
t1, t2 = np.radians(60), np.radians(0)
w1, w2 = 0.0, 0.0
dt = 0.01

for step in range(500):
    T, V, E = compute_energy([t1, t2], [w1, w2])
    
    if step % 50 == 0:
        print(f"{np.degrees(t1):8.1f}° {T:10.3f} J {V:10.3f} J {E:10.3f} J")
    
    # Simple gravity-driven update (no friction)
    lc1 = 0.25
    alpha1 = -(5.0*lc1 + 3.0*0.5)*9.81*np.cos(t1) / (5.0*lc1**2 + 3.0*0.5**2 + 5.0*0.5**2/12)
    w1 += alpha1 * dt
    t1 += w1 * dt

print(f"\nNote: Total energy should remain approximately constant")
print(f"(conservation of energy — no friction or external forces)")

Dynamics Parameter Calculator

Use this tool to document your robot's dynamic parameters and compute key quantities. Enter link masses, lengths, and inertias, then download as Word, Excel, or PDF.

Dynamics Parameter Calculator

Enter link physical properties. Format: mass(kg), length(m), inertia(kg·m²) per link. Download as Word, Excel, or PDF.

Draft auto-saved

All data stays in your browser. Nothing is sent to or stored on any server.

Exercises & Challenges

Exercise 1: Gravity Torque Computation

Compute the gravity compensation torques for a 2-link arm at θ = (0°, 0°) — the worst case. Then compare with θ = (90°, 0°) — the arm pointing straight up.

import numpy as np

def gravity_torques(t1_deg, t2_deg, m1=5.0, m2=3.0, L1=0.5, L2=0.4, g=9.81):
    t1, t2 = np.radians(t1_deg), np.radians(t2_deg)
    lc1, lc2 = L1/2, L2/2
    G1 = (m1*lc1 + m2*L1)*g*np.cos(t1) + m2*lc2*g*np.cos(t1+t2)
    G2 = m2*lc2*g*np.cos(t1 + t2)
    return G1, G2

# Worst case: horizontal
G1, G2 = gravity_torques(0, 0)
print(f"Horizontal (0°, 0°): Ï„1={G1:.3f} Nm, Ï„2={G2:.3f} Nm")

# Best case: vertical
G1, G2 = gravity_torques(90, 0)
print(f"Vertical (90°, 0°): Ï„1={G1:.3f} Nm, Ï„2={G2:.3f} Nm")

print(f"\n→ Gravity torque drops to near zero when arm points up!")

Exercise 2: Inverse Dynamics for a Trajectory

Compute the required torques for a 1-link arm to track a sinusoidal trajectory: θ(t) = 45° · sin(2Ï€t). What are the peak torques?

import numpy as np

mass, length, g = 3.0, 0.4, 9.81
lc = length / 2
I = mass * length**2 / 12

t = np.linspace(0, 1, 101)
amplitude = np.radians(45)
omega = 2 * np.pi  # 1 Hz

theta = amplitude * np.sin(omega * t)
theta_dot = amplitude * omega * np.cos(omega * t)
theta_ddot = -amplitude * omega**2 * np.sin(omega * t)

# Inverse dynamics: tau = I*alpha + m*g*lc*sin(theta)
tau = (mass*lc**2 + I) * theta_ddot + mass*g*lc*np.sin(theta)

print("Sinusoidal Trajectory Inverse Dynamics")
print(f"θ(t) = 45°Â·sin(2Ï€t), mass={mass}kg, L={length}m")
print(f"Peak θ_ddot = {np.max(np.abs(theta_ddot)):.2f} rad/s²")
print(f"Peak torque = {np.max(np.abs(tau)):.3f} Nm")
print(f"Inertial peak = {np.max(np.abs((mass*lc**2+I)*theta_ddot)):.3f} Nm")
print(f"Gravity peak  = {mass*g*lc:.3f} Nm")

Exercise 3: Friction Identification

Given velocity-torque measurements from a motor, identify the Coulomb and viscous friction parameters using curve fitting.

import numpy as np

# Simulated measurements (with noise)
np.random.seed(42)
true_mu, true_b = 0.4, 0.08
velocities = np.array([0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0])
torques = true_mu + true_b * velocities + np.random.normal(0, 0.02, len(velocities))

# Linear regression: tau = mu + b*v → [1, v] @ [mu, b]^T = tau
A = np.column_stack([np.ones_like(velocities), velocities])
params, _, _, _ = np.linalg.lstsq(A, torques, rcond=None)
mu_est, b_est = params

print("Friction Parameter Identification")
print(f"True:      μ={true_mu:.3f} Nm, b={true_b:.4f} Nm·s/rad")
print(f"Estimated: μ={mu_est:.3f} Nm, b={b_est:.4f} Nm·s/rad")
print(f"Error:     μ_err={abs(mu_est-true_mu):.4f}, b_err={abs(b_est-true_b):.5f}")

Conclusion & Next Steps

In this article, we've explored the physics that governs robot motion:

  • Newton-Euler formulation provides an efficient recursive algorithm (O(n)) for computing joint torques
  • Lagrangian dynamics derives elegant closed-form equations of motion using energy methods
  • The robot equation Ï„ = M(q)q̈ + C(q,q̇)q̇ + G(q) captures all dynamic effects in matrix form
  • Friction models (Coulomb, viscous, Stribeck) account for real-world joint resistance
  • Contact dynamics handle robot-environment interaction essential for manipulation and locomotion
  • Energy methods provide powerful analysis tools and the foundation for stability-guaranteed control
What's Next: Now that we understand both the geometry (kinematics) and physics (dynamics) of robots, Part 6 (Control Systems & PID) will show how to design controllers that make robots follow desired trajectories — from simple PID to advanced model predictive control (MPC).

Next in the Series

In Part 6: Control Systems & PID, we'll learn how to design controllers that make robots follow desired trajectories — PID, state-space, LQR, MPC, and advanced control strategies.