In [1]:
import modern_robotics as mr
import numpy as np

Modern Robotics Course 3: Robot Dynamics¶

Aug 12 2025

Goal:¶

Implement a function to simulate a trajectory for a UDF5 robot given initial conditions

Given robot specifications are:

  • home matrix
  • spatial inertia matrix
  • screw axes
In [2]:
M01 = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0.089159], [0, 0, 0, 1]]
M12 = [[0, 0, 1, 0.28], [0, 1, 0, 0.13585], [-1, 0, 0, 0], [0, 0, 0, 1]]
M23 = [[1, 0, 0, 0], [0, 1, 0, -0.1197], [0, 0, 1, 0.395], [0, 0, 0, 1]]
M34 = [[0, 0, 1, 0], [0, 1, 0, 0], [-1, 0, 0, 0.14225], [0, 0, 0, 1]]
M45 = [[1, 0, 0, 0], [0, 1, 0, 0.093], [0, 0, 1, 0], [0, 0, 0, 1]]
M56 = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0.09465], [0, 0, 0, 1]]
M67 = [[1, 0, 0, 0], [0, 0, 1, 0.0823], [0, -1, 0, 0], [0, 0, 0, 1]]
G1 = np.diag([0.010267495893, 0.010267495893,  0.00666, 3.7, 3.7, 3.7])
G2 = np.diag([0.22689067591, 0.22689067591, 0.0151074, 8.393, 8.393, 8.393])
G3 = np.diag([0.049443313556, 0.049443313556, 0.004095, 2.275, 2.275, 2.275])
G4 = np.diag([0.111172755531, 0.111172755531, 0.21942, 1.219, 1.219, 1.219])
G5 = np.diag([0.111172755531, 0.111172755531, 0.21942, 1.219, 1.219, 1.219])
G6 = np.diag([0.0171364731454, 0.0171364731454, 0.033822, 0.1879, 0.1879, 0.1879])
Glist = [G1, G2, G3, G4, G5, G6]
Mlist = [M01, M12, M23, M34, M45, M56, M67] 
Slist = [[0,         0,         0,         0,        0,        0],
         [0,         1,         1,         1,        0,        1],
         [1,         0,         0,         0,       -1,        0],
         [0, -0.089159, -0.089159, -0.089159, -0.10915, 0.005491],
         [0,         0,         0,         0,  0.81725,        0],
         [0,         0,     0.425,   0.81725,        0,  0.81725]]

Initial conditions (for simulation 1):

  • in home position
  • not moving
  • no initial joint torques
  • no external wrench (acting on end-effector)

Essentially, robot is only affected by gravity

In [40]:
theta_0 = np.array([0,0,0,0,0,0])
dtheta_0 = np.zeros(6)
tau_0 = np.zeros(6)
F_tip = np.zeros(6)
theta_0, dtheta_0, F_tip, tau_0
Out[40]:
(array([0, 0, 0, 0, 0, 0]),
 array([0., 0., 0., 0., 0., 0.]),
 array([0., 0., 0., 0., 0., 0.]),
 array([0., 0., 0., 0., 0., 0.]))

Function needs to

  • accept initial conditions
  • perform forward dynamics to find acceleration
  • perform an integration step knowing position, velocity, and acceleration to find the next position and velocity
In [37]:
def write_trajectory(theta_0, dtheta_0, tau_0, F_tip, sim_time, N, file_name):
    """Provided initial conditions, simulate trajectory for sim_time with N steps and write to csv
    

    Input:

    theta_0: initial joint configuration
    dtheta_0: initial joint velocity
    tau_0: initial joint torque (if any)
    F_tip: wrench applied to end effector (if any)
    sim_time is the total time we'd like the trajectory to simulate
    N is number of integration steps we'd like to do over the simulation time
    file_name: where to save the joint configuration trajectory
    """

    g = np.array([0,0,-9.81])

    # Setup traces for forward and inverse calculations
    theta_trace = np.array(theta_0, ndmin=2)
    dtheta_trace = np.array(dtheta_0, ndmin=2)

    # timestep
    dt = sim_time/N

    for i in range(0,N):
    
        ddtheta = mr.ForwardDynamics(theta_trace[i],dtheta_trace[i],tau_0,g,F_tip,Mlist,Glist,Slist)    
        theta_next, dtheta_next = mr.EulerStep(theta_trace[i], dtheta_trace[i], ddtheta, dt)    
    
        theta_trace = np.vstack((theta_trace, theta_next))
        dtheta_trace = np.vstack((dtheta_trace, dtheta_next))

    np.savetxt(file_name, theta_trace, delimiter=",")
In [38]:
write_trajectory(theta_0, dtheta_0, tau_0, F_tip, 3, 3000, "simulation1.csv")

Simulation 1:

In [42]:
from IPython.display import YouTubeVideo

# Replace with the YouTube video ID (the part after `v=` in the URL)
YouTubeVideo("bqfhuXzYpPI", width=640, height=360)
Out[42]:

Simulation 2:

In [43]:
from IPython.display import YouTubeVideo

# Replace with the YouTube video ID (the part after `v=` in the URL)
YouTubeVideo("U6cf2WuVPiM", width=640, height=360)
Out[43]:

Worth noting in both videos is the slight increase in energy that is visible. Unless enough integration steps are used (small enough dt), the error or drift introduced creates energy. A more sophisticated integration formula is required to remove this kind of error

In [ ]:
 
In [ ]: