Chapter 4: Rotational Mechanical and Mechatronic Systems

Lesson 3: Coupled Translational–Rotational Systems (Rack-and-Pinion, Crank–Slider)

This lesson develops time-domain dynamic models for mechanisms that couple translation and rotation via geometric constraints. We formalize (i) kinematic mappings \( x = f(\theta) \), (ii) power-consistent force/torque transformations, and (iii) reflected inertia, stiffness, and damping. Two canonical mechanisms are treated in depth: rack-and-pinion and crank–slider. The end goal is a physically consistent ordinary differential equation (ODE) suitable for simulation and control-oriented analysis in later chapters.

1. Conceptual Overview

In Chapter 3, translational systems were modeled using Newton’s law. In Lesson 1 of this chapter, rotational systems were modeled using the rotational analogue. In Lesson 2, we introduced ideal transformers (e.g., gears and levers) to map effort/flow variables between domains.

A coupled translational–rotational mechanism adds an additional ingredient: a kinematic constraint that enforces a deterministic relationship between a translational coordinate \( x(t) \) and a rotational coordinate \( \theta(t) \). In its most general form:

\[ x = f(\theta), \qquad \dot{x} = \frac{d f}{d\theta}\dot{\theta}, \qquad \ddot{x} = \frac{d^2 f}{d\theta^2}\dot{\theta}^2 + \frac{d f}{d\theta}\ddot{\theta}. \]

For ideal, lossless couplings (no slip, no backlash, no compliance inside the mechanism), the correct force–torque mapping is obtained from power invariance: translational power equals rotational power.

\[ P = F \dot{x} = (\tau)\dot{\theta} \quad \Longrightarrow \quad \tau = F \frac{\dot{x}}{\dot{\theta}} = F \frac{d f}{d\theta}. \]

The scalar \( J(\theta) := \frac{d f}{d\theta} \) is the (configuration-dependent) kinematic Jacobian that converts angular motion to linear motion. When \( f(\theta) \) is linear (rack-and-pinion), \( J(\theta) \) is constant. When \( f(\theta) \) is nonlinear (crank–slider), \( J(\theta) \) varies with angle and changes the effective inertia seen by the actuator.

flowchart TD
  A["Choose coordinates (x, theta) and sign conventions"] --> B["Write kinematic constraint: x = f(theta)"]
  B --> C["Differentiate: xdot = (dx/dtheta)*thetadot ; \nxddot = (d2x/dtheta2)*thetadot^2 + (dx/dtheta)*thetaddot"]
  C --> D["Power consistency: tau*thetadot = F*xdot  =>  tau = F*(dx/dtheta)"]
  D --> E["Reflect elements across coupling (mass, spring, damper)"]
  E --> F["Assemble ODE in one coordinate (often theta)"]
  F --> G["Sanity checks: units, energy/power sign, limiting cases"]
  G --> H["Simulate (ODE solver) and validate against intuition"]
        

Assumptions used throughout this lesson: rigid links, ideal joints, no gear backlash, no slip, and (unless stated) viscous damping. Non-ideal gear train effects are deferred to Lesson 4.

2. Rack-and-Pinion: Kinematics, Force/Torque Mapping, and Reflected Parameters

Consider a pinion of pitch radius \( r_p \) driving a rack with displacement \( x \). With no slip at the pitch line:

\[ x = r_p \theta,\qquad \dot{x} = r_p \dot{\theta},\qquad \ddot{x} = r_p \ddot{\theta}. \]

The mapping is linear, so the Jacobian is constant: \( J = \frac{dx}{d\theta} = r_p \). Power invariance gives:

\[ (\tau)\dot{\theta} = F \dot{x} = F (r_p \dot{\theta}) \quad \Longrightarrow \quad \tau = r_p F. \]

This relation is equivalent to a constant-ratio transformer between rotational and translational domains. It also yields a systematic method to “reflect” translational elements into rotational equivalents.

2.1 Reflected mass (equivalent inertia)

Suppose the rack drives a translational mass \( m \). If we write dynamics in the rotational coordinate \( \theta \), the translational kinetic energy is:

\[ T_m = \frac{1}{2} m \dot{x}^2 = \frac{1}{2} m (r_p^2 \dot{\theta}^2) = \frac{1}{2} (m r_p^2)\dot{\theta}^2. \]

This shows the mass behaves like an additional rotational inertia \( J_{\text{ref}} = m r_p^2 \) as seen at the pinion shaft. If the pinion has physical inertia \( J_p \), then the total effective inertia is:

\[ J_{\text{eq}} = J_p + m r_p^2. \]

Proof (power/energy consistency): The generalized momentum associated with the shaft is \( \frac{\partial T}{\partial \dot{\theta}} \). With \( T = \frac{1}{2}J_p \dot{\theta}^2 + \frac{1}{2}m r_p^2 \dot{\theta}^2 \), we get \( \frac{\partial T}{\partial \dot{\theta}} = (J_p + m r_p^2)\dot{\theta} \). For an ideal lossless mechanism, the net shaft torque equals the rate of change of this momentum, hence \( \tau_{\text{net}} = (J_p + m r_p^2)\ddot{\theta} \) when no other effects are present.

2.2 Reflected spring and damper

Suppose the rack is connected to a spring \( k \) and viscous damper \( c \) to ground:

\[ F_s = k x,\qquad F_d = c \dot{x}. \]

Converting each to torque using \( \tau = r_p F \) and \( x = r_p \theta \):

\[ \tau_s = r_p (k x) = r_p k (r_p \theta) = (k r_p^2)\theta, \qquad \tau_d = r_p (c \dot{x}) = r_p c (r_p \dot{\theta}) = (c r_p^2)\dot{\theta}. \]

Hence, the translational spring/damper reflect as torsional spring/damper: \( k_t = k r_p^2 \), \( c_t = c r_p^2 \).

2.3 Example ODE (motor torque driving rack load)

Let input torque be \( \tau_{\text{in}}(t) \), and assume viscous shaft friction \( b_\theta \dot{\theta} \). The rotational-coordinate ODE becomes:

\[ (J_p + m r_p^2)\ddot{\theta} + (b_\theta + c r_p^2)\dot{\theta} + (k r_p^2)\theta = \tau_{\text{in}}(t). \]

The corresponding rack displacement is recovered by \( x(t) = r_p \theta(t) \).

3. Crank–Slider: Geometry, Nonlinear Kinematics, and Mechanical Advantage

Consider a planar crank–slider with crank radius \( r \), connecting rod length \( l \), crank angle \( \theta \), and slider displacement \( x \) along the line of stroke. Using basic geometry (right triangle from the rod projection), the slider position measured from the crank center along the stroke is:

\[ x(\theta) = r \cos\theta + \sqrt{l^2 - r^2 \sin^2\theta}. \]

Differentiating yields the kinematic Jacobian (mechanism “gain”):

\[ \frac{dx}{d\theta} = -r\sin\theta - \frac{r^2 \sin\theta\cos\theta}{\sqrt{l^2 - r^2 \sin^2\theta}}. \]

The velocity mapping is then \( \dot{x} = \left(\frac{dx}{d\theta}\right)\dot{\theta} \). Power invariance provides the instantaneous torque required to balance a slider force \(F\):

\[ (\tau)\dot{\theta} = F \dot{x} = F\left(\frac{dx}{d\theta}\right)\dot{\theta} \quad \Longrightarrow \quad \tau = F \frac{dx}{d\theta}. \]

Interpretation (mechanical advantage): When \( \left|\frac{dx}{d\theta}\right| \) is small (near dead-center positions), a large torque may be required to support a given slider force. This is a purely geometric effect and will appear in dynamic simulations even for simple viscous loads.

flowchart LR
  subgraph R["Rack-and-Pinion (ideal)"]
    R1["Constraint: x = r_p*theta"] --> R2["Jacobian: dx/dtheta = r_p (constant)"]
    R2 --> R3["Power: tau*thetadot = F*xdot"]
    R3 --> R4["Mapping: tau = r_p*F ; xdot = r_p*thetadot"]
    R4 --> R5["Reflections: J_ref = m*r_p^2 ; k_t = k*r_p^2 ; c_t = c*r_p^2"]
  end

  subgraph C["Crank-Slider (ideal)"]
    C1["Constraint: x = r*cos(theta) + sqrt(l^2 - r^2*sin(theta)^2)"] --> C2["Jacobian: dx/dtheta depends on theta"]
    C2 --> C3["Power: tau = F*(dx/dtheta)"]
    C3 --> C4["Effective inertia varies with theta"]
    C4 --> C5["Nonlinear ODE in theta (configuration dependent)"]
  end
        

4. Crank–Slider Dynamics: Configuration-Dependent Effective Inertia

We now derive a compact, actuator-side ODE for a crank–slider with: crank rotational inertia \( J_c \), slider mass \( m \), slider viscous load \( c \dot{x} \), and slider spring load \( k x \). The actuator applies input torque \( \tau_{\text{in}}(t) \).

4.1 Reflected inertia as a function of angle

The total kinetic energy is the sum of crank rotation and slider translation:

\[ T = \frac{1}{2}J_c \dot{\theta}^2 + \frac{1}{2}m \dot{x}^2 = \frac{1}{2}\left(J_c + m\left(\frac{dx}{d\theta}\right)^2\right)\dot{\theta}^2. \]

Define the configuration-dependent equivalent inertia: \( J_{\text{eq}}(\theta) := J_c + m\left(\frac{dx}{d\theta}\right)^2 \).

\[ T = \frac{1}{2}J_{\text{eq}}(\theta)\dot{\theta}^2. \]

4.2 Power-balance proof of the actuator-side equation

For an ideal mechanism, the rate of change of kinetic energy equals the net mechanical power injected:

\[ \frac{dT}{dt} = \tau_{\text{net}}\,\dot{\theta}. \]

Differentiate \( T = \frac{1}{2}J_{\text{eq}}(\theta)\dot{\theta}^2 \) using the chain rule:

\[ \frac{dT}{dt} = \frac{1}{2}\frac{dJ_{\text{eq}}}{d\theta}\dot{\theta}\,\dot{\theta}^2 + J_{\text{eq}}(\theta)\dot{\theta}\ddot{\theta} = \left(J_{\text{eq}}(\theta)\ddot{\theta} + \frac{1}{2}\frac{dJ_{\text{eq}}}{d\theta}\dot{\theta}^2\right)\dot{\theta}. \]

Equating to \( \tau_{\text{net}} \dot{\theta} \) and canceling \( \dot{\theta} \) (with the understanding that the identity holds by continuity at \( \dot{\theta}=0 \)) gives the fundamental actuator-side equation:

\[ J_{\text{eq}}(\theta)\ddot{\theta} + \frac{1}{2}\frac{dJ_{\text{eq}}}{d\theta}\dot{\theta}^2 = \tau_{\text{net}}. \]

Next, compute \( \tau_{\text{net}} \) by reflecting slider forces into torque:

\[ F_{\text{load}} = k x + c \dot{x} \quad \Longrightarrow \quad \tau_{\text{load}} = F_{\text{load}}\frac{dx}{d\theta}. \]

Therefore, with \( \tau_{\text{net}} = \tau_{\text{in}} - \tau_{\text{load}} - b_\theta \dot{\theta} \), we obtain a closed ODE in \( \theta \):

\[ J_{\text{eq}}(\theta)\ddot{\theta} + \frac{1}{2}\frac{dJ_{\text{eq}}}{d\theta}\dot{\theta}^2 + b_\theta \dot{\theta} + \left(k x(\theta) + c \dot{x}\right)\frac{dx}{d\theta} = \tau_{\text{in}}(t), \]

with \( x(\theta) \) and \( \dot{x} = \left(\frac{dx}{d\theta}\right)\dot{\theta} \) given by Section 3. This is a nonlinear, configuration-dependent dynamic model—derived without introducing state-space or transfer functions (reserved for later chapters).

5. Computational Implementations

The goal of these implementations is consistent: define the ODE in a minimal coordinate (here \( \theta \)), then simulate with a numerical integrator. We provide both library-based solvers and at least one from-scratch integrator (RK4) to expose numerical structure.

5.1 Python (NumPy/SciPy + optional SymPy)

Recommended libraries: numpy, scipy.integrate, matplotlib, and optionally sympy for symbolic differentiation of \( x(\theta) \) and \( dx/d\theta \).


import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt

# ---------- Mechanism: rack-and-pinion ----------
def rack_pinion_rhs(t, z, Jp, m, rp, bth, c, k, tau_in_fun):
    # z = [theta, omega]
    theta, omega = z
    Jeq = Jp + m * (rp ** 2)
    tau_in = tau_in_fun(t)
    # torque from reflected spring/damper: (k rp^2)*theta + (c rp^2)*omega
    tau_load = (k * rp ** 2) * theta + (c * rp ** 2) * omega
    domega = (tau_in - bth * omega - tau_load) / Jeq
    return np.array([omega, domega])

# ---------- Mechanism: crank-slider ----------
def x_theta(theta, r, l):
    return r * np.cos(theta) + np.sqrt(l ** 2 - (r ** 2) * (np.sin(theta) ** 2))

def dx_dtheta(theta, r, l):
    denom = np.sqrt(l ** 2 - (r ** 2) * (np.sin(theta) ** 2))
    return -r * np.sin(theta) - (r ** 2) * np.sin(theta) * np.cos(theta) / denom

def dJeq_dtheta(theta, m, r, l):
    # J_eq(theta) = Jc + m*(dx/dtheta)^2  => dJ_eq/dtheta = 2*m*(dx/dtheta)*(d2x/dtheta2)
    # We compute d2x/dtheta2 numerically for simplicity (students can replace with analytic/SymPy).
    h = 1e-6
    d1 = dx_dtheta(theta + h, r, l)
    d0 = dx_dtheta(theta - h, r, l)
    d2x = (d1 - d0) / (2.0 * h)
    return 2.0 * m * dx_dtheta(theta, r, l) * d2x

def crank_slider_rhs(t, z, Jc, m, r, l, bth, c, k, tau_in_fun):
    theta, omega = z
    Jmap = dx_dtheta(theta, r, l)
    x = x_theta(theta, r, l)
    xdot = Jmap * omega

    Jeq = Jc + m * (Jmap ** 2)
    dJeq = dJeq_dtheta(theta, m, r, l)

    tau_in = tau_in_fun(t)
    F_load = k * x + c * xdot
    tau_load = F_load * Jmap

    # J_eq*thetaddot + 0.5*(dJ_eq/dtheta)*omega^2 + bth*omega + tau_load = tau_in
    domega = (tau_in - (0.5 * dJeq * (omega ** 2)) - bth * omega - tau_load) / Jeq
    return np.array([omega, domega])

# ---------- Example simulations ----------
def step_torque(t, tau0=1.0, t0=0.05):
    return tau0 if t >= t0 else 0.0

# Rack-and-pinion parameters
Jp, m, rp = 2e-4, 0.2, 0.01
bth, c, k = 1e-4, 5.0, 200.0

sol1 = solve_ivp(
    fun=lambda t, z: rack_pinion_rhs(t, z, Jp, m, rp, bth, c, k, lambda tt: step_torque(tt, 0.05, 0.02)),
    t_span=(0.0, 0.5),
    y0=np.array([0.0, 0.0]),
    max_step=1e-3
)

theta1 = sol1.y[0]
x1 = rp * theta1

plt.figure()
plt.plot(sol1.t, x1)
plt.xlabel("t [s]")
plt.ylabel("rack displacement x [m]")
plt.grid(True)

# Crank-slider parameters
Jc, m2 = 3e-4, 0.3
r, l = 0.02, 0.08
bth2, c2, k2 = 2e-4, 3.0, 50.0

sol2 = solve_ivp(
    fun=lambda t, z: crank_slider_rhs(t, z, Jc, m2, r, l, bth2, c2, k2, lambda tt: step_torque(tt, 0.02, 0.02)),
    t_span=(0.0, 0.5),
    y0=np.array([0.5, 0.0]),   # start away from singular positions
    max_step=1e-4
)

plt.figure()
plt.plot(sol2.t, sol2.y[0])
plt.xlabel("t [s]")
plt.ylabel("crank angle theta [rad]")
plt.grid(True)
plt.show()
      

5.2 C++ (from-scratch RK4; optional Boost.Odeint / Eigen)

Recommended ecosystem: a minimal RK4 is sufficient for this course stage; for larger projects, consider Boost.Odeint for ODE integration and Eigen for linear algebra (later chapters).


#include <iostream>
#include <vector>
#include <cmath>
#include <functional>

struct State {
  double theta;
  double omega;
};

static inline double x_theta(double theta, double r, double l) {
  return r * std::cos(theta) + std::sqrt(l * l - (r * r) * std::pow(std::sin(theta), 2.0));
}

static inline double dx_dtheta(double theta, double r, double l) {
  double s = std::sin(theta);
  double c = std::cos(theta);
  double denom = std::sqrt(l * l - (r * r) * s * s);
  return -r * s - (r * r) * s * c / denom;
}

// Numerical derivative for d2x/dtheta2 and then dJ_eq/dtheta
static inline double dJeq_dtheta(double theta, double m, double r, double l) {
  double h = 1e-6;
  double d1 = dx_dtheta(theta + h, r, l);
  double d0 = dx_dtheta(theta - h, r, l);
  double d2x = (d1 - d0) / (2.0 * h);
  return 2.0 * m * dx_dtheta(theta, r, l) * d2x;
}

static inline double step_torque(double t, double tau0, double t0) {
  return (t >= t0) ? tau0 : 0.0;
}

// Crank-slider RHS: returns dtheta/dt and domega/dt
State crank_slider_rhs(double t, const State& z,
                       double Jc, double m, double r, double l,
                       double bth, double c, double k,
                       const std::function<double(double)>& tau_in_fun) {
  double theta = z.theta;
  double omega = z.omega;

  double Jmap = dx_dtheta(theta, r, l);
  double x = x_theta(theta, r, l);
  double xdot = Jmap * omega;

  double Jeq = Jc + m * (Jmap * Jmap);
  double dJeq = dJeq_dtheta(theta, m, r, l);

  double tau_in = tau_in_fun(t);
  double Fload = k * x + c * xdot;
  double tau_load = Fload * Jmap;

  double domega = (tau_in - 0.5 * dJeq * omega * omega - bth * omega - tau_load) / Jeq;
  return State{omega, domega};
}

// One RK4 step
State rk4_step(double t, double h, const State& z,
               const std::function<State(double,const State&)>& f) {
  State k1 = f(t, z);

  State z2{ z.theta + 0.5*h*k1.theta, z.omega + 0.5*h*k1.omega };
  State k2 = f(t + 0.5*h, z2);

  State z3{ z.theta + 0.5*h*k2.theta, z.omega + 0.5*h*k2.omega };
  State k3 = f(t + 0.5*h, z3);

  State z4{ z.theta + h*k3.theta, z.omega + h*k3.omega };
  State k4 = f(t + h, z4);

  State out;
  out.theta = z.theta + (h/6.0)*(k1.theta + 2.0*k2.theta + 2.0*k3.theta + k4.theta);
  out.omega = z.omega + (h/6.0)*(k1.omega + 2.0*k2.omega + 2.0*k3.omega + k4.omega);
  return out;
}

int main() {
  // Parameters
  double Jc = 3e-4, m = 0.3;
  double r = 0.02, l = 0.08;
  double bth = 2e-4, c = 3.0, k = 50.0;

  auto tau_in_fun = [&](double t){ return step_torque(t, 0.02, 0.02); };

  auto rhs = [&](double t, const State& z){
    return crank_slider_rhs(t, z, Jc, m, r, l, bth, c, k, tau_in_fun);
  };

  // Simulate
  double t0 = 0.0, tf = 0.5, h = 1e-4;
  State z{0.5, 0.0}; // start away from dead centers
  int N = static_cast<int>((tf - t0)/h);

  for (int i = 0; i <= N; ++i) {
    double t = t0 + i*h;
    if ((i % 1000) == 0) {
      std::cout << t << " " << z.theta << " " << z.omega << "\n";
    }
    z = rk4_step(t, h, z, rhs);
  }
  return 0;
}
      

5.3 Java (from-scratch RK4; optional Apache Commons Math ODE)

Recommended library for production-grade ODE solving: org.apache.commons:commons-math3. Here we provide a transparent RK4 implementation aligned with the C++ version.


import java.util.function.DoubleUnaryOperator;

public class CrankSliderRK4 {

  static class State {
    double theta;
    double omega;
    State(double th, double om) { theta = th; omega = om; }
  }

  static double xTheta(double theta, double r, double l) {
    return r * Math.cos(theta) + Math.sqrt(l*l - (r*r) * Math.pow(Math.sin(theta), 2.0));
  }

  static double dxDtheta(double theta, double r, double l) {
    double s = Math.sin(theta);
    double c = Math.cos(theta);
    double denom = Math.sqrt(l*l - (r*r) * s * s);
    return -r * s - (r*r) * s * c / denom;
  }

  static double dJeqDtheta(double theta, double m, double r, double l) {
    double h = 1e-6;
    double d1 = dxDtheta(theta + h, r, l);
    double d0 = dxDtheta(theta - h, r, l);
    double d2x = (d1 - d0) / (2.0 * h);
    return 2.0 * m * dxDtheta(theta, r, l) * d2x;
  }

  static double stepTorque(double t, double tau0, double t0) {
    return (t >= t0) ? tau0 : 0.0;
  }

  static State rhs(double t, State z,
                   double Jc, double m, double r, double l,
                   double bth, double c, double k,
                   DoubleUnaryOperator tauIn) {

    double th = z.theta;
    double om = z.omega;

    double Jmap = dxDtheta(th, r, l);
    double x = xTheta(th, r, l);
    double xdot = Jmap * om;

    double Jeq = Jc + m * (Jmap * Jmap);
    double dJeq = dJeqDtheta(th, m, r, l);

    double tauInVal = tauIn.applyAsDouble(t);
    double Fload = k * x + c * xdot;
    double tauLoad = Fload * Jmap;

    double dom = (tauInVal - 0.5 * dJeq * om * om - bth * om - tauLoad) / Jeq;
    return new State(om, dom);
  }

  static State rk4Step(double t, double h, State z,
                       java.util.function.BiFunction<Double, State, State> f) {

    State k1 = f.apply(t, z);

    State z2 = new State(z.theta + 0.5*h*k1.theta, z.omega + 0.5*h*k1.omega);
    State k2 = f.apply(t + 0.5*h, z2);

    State z3 = new State(z.theta + 0.5*h*k2.theta, z.omega + 0.5*h*k2.omega);
    State k3 = f.apply(t + 0.5*h, z3);

    State z4 = new State(z.theta + h*k3.theta, z.omega + h*k3.omega);
    State k4 = f.apply(t + h, z4);

    double th = z.theta + (h/6.0)*(k1.theta + 2.0*k2.theta + 2.0*k3.theta + k4.theta);
    double om = z.omega + (h/6.0)*(k1.omega + 2.0*k2.omega + 2.0*k3.omega + k4.omega);
    return new State(th, om);
  }

  public static void main(String[] args) {

    double Jc = 3e-4, m = 0.3;
    double r = 0.02, l = 0.08;
    double bth = 2e-4, c = 3.0, k = 50.0;

    DoubleUnaryOperator tauIn = (t) -> stepTorque(t, 0.02, 0.02);

    java.util.function.BiFunction<Double, State, State> f =
      (t, z) -> rhs(t, z, Jc, m, r, l, bth, c, k, tauIn);

    double t0 = 0.0, tf = 0.5, h = 1e-4;
    int N = (int)((tf - t0) / h);
    State z = new State(0.5, 0.0);

    for (int i = 0; i <= N; i++) {
      double t = t0 + i*h;
      if (i % 1000 == 0) {
        System.out.println(t + " " + z.theta + " " + z.omega);
      }
      z = rk4Step(t, h, z, f);
    }
  }
}
      

5.4 MATLAB (ODE45) and Simulink block structure

MATLAB: use ode45 (nonstiff) to simulate the ODE in \( \theta \). Simulink: implement the same ODE using integrator chains (two integrators for \( \theta \)), plus blocks computing \( x(\theta) \), \( dx/d\theta \), \( J_{\text{eq}}(\theta) \), and the torque balance.


function lesson4_crank_slider_demo
  % Parameters
  Jc = 3e-4; m = 0.3;
  r = 0.02;  l = 0.08;
  bth = 2e-4; c = 3.0; k = 50.0;

  tau0 = 0.02; tStep = 0.02;

  rhs = @(t,z) crank_slider_rhs(t,z,Jc,m,r,l,bth,c,k,tau0,tStep);

  tspan = [0 0.5];
  z0 = [0.5; 0.0]; % [theta; omega]
  opts = odeset('MaxStep',1e-4);

  [t,z] = ode45(rhs,tspan,z0,opts);

  theta = z(:,1);
  figure; plot(t,theta); grid on;
  xlabel('t [s]'); ylabel('theta [rad]');
end

function dz = crank_slider_rhs(t,z,Jc,m,r,l,bth,c,k,tau0,tStep)
  theta = z(1); omega = z(2);

  x = r*cos(theta) + sqrt(l^2 - (r^2)*(sin(theta)^2));
  denom = sqrt(l^2 - (r^2)*(sin(theta)^2));
  Jmap = -r*sin(theta) - (r^2)*sin(theta)*cos(theta)/denom;

  xdot = Jmap*omega;

  Jeq = Jc + m*(Jmap^2);

  % numerical derivative dJeq/dtheta (simple central diff)
  h = 1e-6;
  Jp = -r*sin(theta+h) - (r^2)*sin(theta+h)*cos(theta+h)/sqrt(l^2 - (r^2)*(sin(theta+h)^2));
  Jm = -r*sin(theta-h) - (r^2)*sin(theta-h)*cos(theta-h)/sqrt(l^2 - (r^2)*(sin(theta-h)^2));
  dJmap = (Jp - Jm)/(2*h);
  dJeq = 2*m*Jmap*dJmap;

  tau_in = (t >= tStep)*tau0;

  Fload = k*x + c*xdot;
  tau_load = Fload*Jmap;

  domega = (tau_in - 0.5*dJeq*(omega^2) - bth*omega - tau_load)/Jeq;
  dz = [omega; domega];
end
      

Simulink block outline (continuous-time):

  • Two cascaded Integrator blocks: \( \ddot{\theta} \rightarrow \dot{\theta} \rightarrow \theta \).
  • Compute \( x(\theta) \) and \( J(\theta)=dx/d\theta \) using Trigonometric Function, Product, Sqrt blocks.
  • Compute \( \dot{x} = J(\theta)\dot{\theta} \).
  • Compute \( J_{\text{eq}}(\theta)=J_c + m J(\theta)^2 \).
  • Compute \( \tau_{\text{load}} = (k x + c \dot{x}) J(\theta) \).
  • Compute \( \ddot{\theta} = \big(\tau_{\text{in}} - \tfrac{1}{2}J_{\text{eq}}'(\theta)\dot{\theta}^2 - b_\theta \dot{\theta} - \tau_{\text{load}}\big)/J_{\text{eq}}(\theta) \).

5.5 Wolfram Mathematica (symbolic + NDSolve)

Mathematica is especially useful here for symbolic differentiation of \( x(\theta) \) and automatic generation of \( dx/d\theta \) and \( dJ_{\text{eq}}/d\theta \).


ClearAll["Global`*"];

(* Parameters *)
Jc = 3*10^-4; m = 0.3;
r = 0.02; l = 0.08;
bth = 2*10^-4; c = 3.0; k = 50.0;

x[th_] := r Cos[th] + Sqrt[l^2 - r^2 Sin[th]^2];
Jmap[th_] := D[x[th], th];

Jeq[th_] := Jc + m (Jmap[th]^2);
dJeq[th_] := D[Jeq[th], th];

tauIn[t_] := Piecewise[{ {0.02, t >= 0.02} }, 0];

(* ODE: Jeq(th)*th'' + 1/2*dJeq(th)*(th')^2 + bth*th' + (k*x + c*xdot)*Jmap = tauIn *)
eqn =
  Jeq[th[t]] th''[t] + (1/2) dJeq[th[t]] (th'[t]^2) + bth th'[t] +
   (k x[th[t]] + c (Jmap[th[t]] th'[t])) Jmap[th[t]] == tauIn[t];

sol = NDSolve[
  {eqn, th[0] == 0.5, th'[0] == 0.0},
  th, {t, 0, 0.5},
  MaxStepSize -> 10^-4
];

Plot[Evaluate[th[t] /. sol], {t, 0, 0.5}, GridLines -> Automatic,
 AxesLabel -> {"t [s]", "theta [rad]"}]
      

6. Problems and Solutions

Problem 1 (Rack-and-pinion reflection): A pinion with inertia \( J_p \) and pitch radius \( r_p \) drives a rack with mass \( m \) connected to a spring \( k \) and damper \( c \) to ground. A motor applies torque \( \tau_{\text{in}}(t) \) to the pinion shaft. Derive the ODE in \( \theta \).

Solution: Use the constraint \( x=r_p\theta \), so \( \dot{x}=r_p\dot{\theta} \), \( \ddot{x}=r_p\ddot{\theta} \). Spring/damper forces are \( F_s = kx \), \( F_d = c\dot{x} \). Convert to torque using \( \tau=r_pF \):

\[ \tau_s = r_p(kx) = (k r_p^2)\theta,\qquad \tau_d = r_p(c\dot{x}) = (c r_p^2)\dot{\theta}. \]

The rack mass reflects as inertia \( J_{\text{ref}} = m r_p^2 \), hence \( J_{\text{eq}}=J_p+m r_p^2 \). Including shaft viscous friction \( b_\theta\dot{\theta} \), torque balance yields:

\[ (J_p + m r_p^2)\ddot{\theta} + (b_\theta + c r_p^2)\dot{\theta} + (k r_p^2)\theta = \tau_{\text{in}}(t). \]


Problem 2 (Power mapping for a general constraint): Let an ideal mechanism impose \( x=f(\theta) \). Prove that the force–torque mapping is \( \tau = F \frac{df}{d\theta} \).

Solution: For an ideal (lossless) coupling, instantaneous power is conserved: \( P_{\text{rot}} = (\tau)\dot{\theta} \) and \( P_{\text{trans}} = F\dot{x} \). Since \( x=f(\theta) \), the chain rule gives \( \dot{x} = \frac{df}{d\theta}\dot{\theta} \). Therefore:

\[ (\tau)\dot{\theta} = F\dot{x} = F\left(\frac{df}{d\theta}\dot{\theta}\right) \quad \Longrightarrow \quad \tau = F\frac{df}{d\theta}. \]


Problem 3 (Crank–slider Jacobian): For \( x(\theta) = r\cos\theta + \sqrt{l^2 - r^2\sin^2\theta} \), compute \( \frac{dx}{d\theta} \).

Solution: Differentiate term-by-term. The first term gives \( \frac{d}{d\theta}(r\cos\theta)=-r\sin\theta \). For the square-root term, apply the chain rule:

\[ \frac{d}{d\theta}\sqrt{l^2 - r^2\sin^2\theta} = \frac{1}{2}\left(l^2 - r^2\sin^2\theta\right)^{-1/2}\left(-2r^2\sin\theta\cos\theta\right) = -\frac{r^2\sin\theta\cos\theta}{\sqrt{l^2 - r^2\sin^2\theta}}. \]

Summing yields:

\[ \frac{dx}{d\theta} = -r\sin\theta - \frac{r^2\sin\theta\cos\theta}{\sqrt{l^2 - r^2\sin^2\theta}}. \]


Problem 4 (Effective inertia and actuator-side equation): Assume a crank inertia \(J_c\) and slider mass \(m\) connected by an ideal crank–slider constraint \(x=f(\theta)\). Show that the kinetic energy can be written \( T=\frac{1}{2}J_{\text{eq}}(\theta)\dot{\theta}^2 \) with \( J_{\text{eq}}(\theta)=J_c+m\left(\frac{df}{d\theta}\right)^2 \), and derive \( J_{\text{eq}}(\theta)\ddot{\theta} + \frac{1}{2}\frac{dJ_{\text{eq}}}{d\theta}\dot{\theta}^2 = \tau_{\text{net}} \).

Solution: Using \( \dot{x}=\frac{df}{d\theta}\dot{\theta} \), the kinetic energy is:

\[ T=\frac{1}{2}J_c\dot{\theta}^2 + \frac{1}{2}m\dot{x}^2 = \frac{1}{2}J_c\dot{\theta}^2 + \frac{1}{2}m\left(\frac{df}{d\theta}\right)^2\dot{\theta}^2 = \frac{1}{2}\left(J_c+m\left(\frac{df}{d\theta}\right)^2\right)\dot{\theta}^2. \]

Define \( J_{\text{eq}}(\theta)=J_c+m\left(\frac{df}{d\theta}\right)^2 \) so \( T=\frac{1}{2}J_{\text{eq}}(\theta)\dot{\theta}^2 \). For ideal couplings, power balance gives \( \frac{dT}{dt} = \tau_{\text{net}}\dot{\theta} \). Differentiate \(T\) using the chain rule:

\[ \frac{dT}{dt} = \frac{1}{2}\frac{dJ_{\text{eq}}}{d\theta}\dot{\theta}\,\dot{\theta}^2 + J_{\text{eq}}(\theta)\dot{\theta}\ddot{\theta} = \left(J_{\text{eq}}(\theta)\ddot{\theta} + \frac{1}{2}\frac{dJ_{\text{eq}}}{d\theta}\dot{\theta}^2\right)\dot{\theta}. \]

Equate to \( \tau_{\text{net}}\dot{\theta} \) and cancel \( \dot{\theta} \) to obtain:

\[ J_{\text{eq}}(\theta)\ddot{\theta} + \frac{1}{2}\frac{dJ_{\text{eq}}}{d\theta}\dot{\theta}^2 = \tau_{\text{net}}. \]


Problem 5 (Rack displacement recovery and limiting check): For the rack-and-pinion ODE in Problem 1, show that if \(k=0\), \(c=0\), \(b_\theta=0\), and \( \tau_{\text{in}}(t)=\tau_0 \) constant, then \( x(t) \) is a quadratic function of time.

Solution: With \(k=c=b_\theta=0\), the ODE reduces to:

\[ (J_p+m r_p^2)\ddot{\theta} = \tau_0 \quad \Longrightarrow \quad \ddot{\theta} = \frac{\tau_0}{J_p+m r_p^2} = \text{constant}. \]

Integrating twice gives \( \theta(t)=\theta(0)+\dot{\theta}(0)t+\frac{1}{2}\ddot{\theta}t^2 \). Since \( x=r_p\theta \), we obtain:

\[ x(t)=r_p\theta(0)+r_p\dot{\theta}(0)t+\frac{1}{2}r_p\left(\frac{\tau_0}{J_p+m r_p^2}\right)t^2, \]

which is quadratic in \(t\), consistent with constant acceleration.

7. Summary

We modeled coupled translational–rotational mechanisms by (i) writing kinematic constraints \(x=f(\theta)\), (ii) enforcing power invariance to map force and torque, and (iii) reflecting mass, stiffness, and damping across the coupling. Rack-and-pinion yields constant reflected parameters and a linear ODE, while crank–slider yields configuration-dependent effective inertia and a nonlinear ODE in the actuator coordinate. These time-domain models are the foundation for later chapters on transfer functions, block diagrams, and state-space representations.

8. References

  1. Denavit, J., & Hartenberg, R.S. (1955). A kinematic notation for lower-pair mechanisms based on matrices. Journal of Applied Mechanics, 22(2), 215–221.
  2. Freudenstein, F. (1955). Approximate synthesis of four-bar linkages. Transactions of the ASME, 77, 853–861.
  3. Özgüven, H.N., & Houser, D.R. (1988). Mathematical models used in gear dynamics—A review. Journal of Sound and Vibration, 121(3), 383–411.
  4. Kahraman, A. (1990). Non-linear dynamics of a spur gear pair. Journal of Sound and Vibration, 142(1), 49–75.
  5. Lin, J., & Parker, R.G. (2002). Planetary gear parametric instability caused by mesh stiffness variation. Journal of Sound and Vibration, 249(1), 129–145.
  6. Todorov, T.S. (2002). Synthesis of four-bar mechanisms as function generators by Freudenstein–Chebyshev approximation. Mechanism and Machine Theory, 37(3), 251–261.
  7. del Rincón, A.F., Viadero, F., Iglesias, M., García, P., & de-Juan, A. (2013). A model for the study of meshing stiffness in spur gear transmissions. Mechanism and Machine Theory, 61, 30–58.
Support CaaT Academy

Help keep these engineering tutorials free and growing

If these lessons, examples, and project pages help you, a small donation supports the continued creation and improvement of free control, robotics, software, and engineering education resources.

Created and maintained by Abolfazl Mohammadijoo.