Non-Holonomic Constraints
The nonholonomic module provides tools for systems with velocity-dependent constraints that cannot be integrated to position constraints.
Overview
Non-holonomic constraints involve velocities and cannot be written as \(f(q) = 0\). The module implements:
Pfaffian Constraints: Linear in velocities \(\sum a_i(q)\dot{q}_i = 0\)
Rolling Without Slipping: Wheels, balls, cylinders
Knife-Edge Constraint: Chaplygin sleigh dynamics
Appell’s Equations: Alternative formulation using acceleration energy
Maggi’s Equations: Projection method eliminating multipliers
Theory
Pfaffian Constraints
A non-holonomic constraint has the form:
If this cannot be integrated to \(f(q) = 0\), it is truly non-holonomic.
d’Alembert-Lagrange Equations
With constraints \(A \dot{q} = 0\), equations of motion become:
where \(\lambda_j\) are Lagrange multipliers.
Appell’s Equations
Using the Gibbs-Appell function (acceleration energy):
Appell’s equations are:
This is often simpler for non-holonomic systems.
Usage Examples
Rolling Without Slipping
from mechanics_dsl.domains.classical import NonholonomicSystem, rolling_constraint
import sympy as sp
system = NonholonomicSystem()
# Ball rolling on surface
m = sp.Symbol('m', positive=True)
R = sp.Symbol('R', positive=True)
x = system.get_symbol('x')
theta = system.get_symbol('theta')
x_dot = system.get_symbol('x_dot')
theta_dot = system.get_symbol('theta_dot')
# Kinetic energy
I = sp.Rational(2, 5) * m * R**2 # Solid sphere
T = sp.Rational(1, 2) * m * x_dot**2 + sp.Rational(1, 2) * I * theta_dot**2
system.set_lagrangian(T)
# Rolling constraint: ẋ = R·θ̇
lam = system.add_rolling_constraint('x', 'theta', R)
# Derive equations
eom = system.derive_equations_of_motion(['x', 'theta'])
Chaplygin Sleigh (Knife-Edge)
from mechanics_dsl.domains.classical import NonholonomicSystem, knife_edge_constraint
import sympy as sp
system = NonholonomicSystem()
# Chaplygin sleigh: blade can only move along its direction
m = sp.Symbol('m', positive=True)
I = sp.Symbol('I', positive=True) # Moment of inertia
x = system.get_symbol('x')
y = system.get_symbol('y')
theta = system.get_symbol('theta') # Orientation
x_dot = system.get_symbol('x_dot')
y_dot = system.get_symbol('y_dot')
theta_dot = system.get_symbol('theta_dot')
# Kinetic energy
T = sp.Rational(1, 2) * m * (x_dot**2 + y_dot**2) + \
sp.Rational(1, 2) * I * theta_dot**2
system.set_lagrangian(T)
# Knife-edge constraint: velocity along blade direction
# ẏ·cos(θ) - ẋ·sin(θ) = 0
lam = system.add_knife_edge_constraint('x', 'y', 'theta')
eom = system.derive_equations_of_motion(['x', 'y', 'theta'])
Constraint Matrix Form
from mechanics_dsl.domains.classical import NonholonomicSystem
import sympy as sp
system = NonholonomicSystem()
R = sp.Symbol('R', positive=True)
# Add multiple constraints
system.add_rolling_constraint('x1', 'theta1', R)
system.add_rolling_constraint('y1', 'phi1', R)
# Get constraint matrix A and vector b
# Constraints: A·q̇ + b = 0
A, b = system.get_constraint_matrix(['x1', 'theta1', 'y1', 'phi1'])
print("Constraint matrix A:")
print(A)
Using Appell’s Equations
from mechanics_dsl.domains.classical import AppellEquations
import sympy as sp
appell = AppellEquations()
m = sp.Symbol('m', positive=True)
x_dot = appell.get_symbol('x_dot')
y_dot = appell.get_symbol('y_dot')
# Kinetic energy
T = sp.Rational(1, 2) * m * (x_dot**2 + y_dot**2)
# Compute Gibbs-Appell function (acceleration energy)
S = appell.compute_acceleration_energy(T, ['x', 'y'])
print(f"Gibbs-Appell function: S = {S}")
# S = (1/2)*m*(ẍ² + ÿ²)
# Derive Appell's equations
Q = {'x': sp.Symbol('F_x'), 'y': sp.Symbol('F_y')}
eom = appell.derive_appell_equations(S, Q, ['x', 'y'])
Convenience Functions
from mechanics_dsl.domains.classical import rolling_constraint, knife_edge_constraint
import sympy as sp
R = sp.Symbol('R', positive=True)
# Create rolling constraint object directly
roll = rolling_constraint('x', 'theta', R)
print(f"Constraint: ẋ - R·θ̇ = 0")
print(f"Coefficients: {roll.coefficients}")
# Create knife-edge constraint object
knife = knife_edge_constraint('x', 'y', 'theta')
print(f"Knife-edge: -ẋ·sin(θ) + ẏ·cos(θ) = 0")
API Reference
Classes
- class NonholonomicSystem
System with non-holonomic (velocity) constraints.
- set_lagrangian(L)
Set the system Lagrangian.
- add_constraint(constraint)
Add a NonholonomicConstraint object.
- Returns:
Lagrange multiplier symbol
- add_rolling_constraint(linear, angular, radius)
Add rolling without slipping: v = R·ω.
- add_knife_edge_constraint(x, y, theta)
Add knife-edge constraint: ẏ·cos(θ) - ẋ·sin(θ) = 0.
- derive_equations_of_motion(coordinates)
Derive d’Alembert-Lagrange equations with constraints.
- get_constraint_matrix(coordinates)
Get constraint matrix A and vector b.
- class NonholonomicConstraint
Represents a single non-holonomic constraint.
- Parameters:
coefficients – Dict mapping coordinate to coefficient a_i(q)
inhomogeneous – The b(q,t) term (default 0)
name – Optional constraint name
- as_matrix_form(coordinates)
Convert to matrix form A·q̇ + b = 0.
Functions
- rolling_constraint(linear, angular, radius)
Create rolling without slipping constraint.
- knife_edge_constraint(x, y, theta)
Create knife-edge (Chaplygin sleigh) constraint.
See Also
Constraint Handling - Holonomic constraints with Baumgarte stabilization
Rigid Body Dynamics - Rolling rigid bodies