pylinkage.cam package
Submodules
pylinkage.cam.motion_laws module
Standard cam motion laws.
Motion laws define the dimensionless displacement function s(u) where u in [0, 1] is the normalized angle and s in [0, 1] is the normalized displacement.
- Available motion laws:
HarmonicMotionLaw: Simple harmonic (cosine) motion CycloidalMotionLaw: Cycloidal motion (zero velocity/acceleration at ends) ModifiedTrapezoidalMotionLaw: Modified trapezoidal (low peak acceleration) PolynomialMotionLaw: General polynomial (3-4-5 or custom)
- class pylinkage.cam.motion_laws.CycloidalMotionLaw
Bases:
MotionLawCycloidal motion law.
s(u) = u - sin(2*pi*u) / (2*pi)
- Properties:
Zero velocity at boundaries (smooth start/stop)
Zero acceleration at boundaries (no jerk discontinuity)
Higher peak acceleration than harmonic
Best for high-speed applications requiring smooth motion
Example
>>> law = CycloidalMotionLaw() >>> law.displacement(0.0) # Zero displacement at start 0.0 >>> law.velocity(0.0) # Zero velocity at start 0.0 >>> law.velocity(0.5) # Maximum velocity at midpoint 2.0
- acceleration(u: float) float
Compute cycloidal acceleration.
- displacement(u: float) float
Compute cycloidal displacement.
- property profile_type: int
Return cycloidal profile type code.
- velocity(u: float) float
Compute cycloidal velocity.
- class pylinkage.cam.motion_laws.HarmonicMotionLaw
Bases:
MotionLawSimple harmonic (cosine) motion law.
s(u) = (1 - cos(pi * u)) / 2
- Properties:
Smooth displacement curve
Non-zero acceleration at boundaries (acceleration discontinuity)
Maximum velocity at u = 0.5
Simple and widely used
Example
>>> law = HarmonicMotionLaw() >>> law.displacement(0.0) # Start 0.0 >>> law.displacement(0.5) # Midpoint 0.5 >>> law.displacement(1.0) # End 1.0
- acceleration(u: float) float
Compute harmonic acceleration.
- displacement(u: float) float
Compute harmonic displacement.
- property profile_type: int
Return harmonic profile type code.
- velocity(u: float) float
Compute harmonic velocity.
- class pylinkage.cam.motion_laws.ModifiedTrapezoidalMotionLaw
Bases:
MotionLawModified trapezoidal motion law.
Uses sinusoidal acceleration segments at start/end with constant acceleration in the middle.
- Properties:
Lower peak acceleration than harmonic or cycloidal
Zero velocity at boundaries
Continuous acceleration (no jerk discontinuity)
Good for high-load applications
Example
>>> law = ModifiedTrapezoidalMotionLaw() >>> law.displacement(0.0) 0.0 >>> law.displacement(1.0) 1.0
- acceleration(u: float) float
Compute modified trapezoidal acceleration (numerical).
- displacement(u: float) float
Compute modified trapezoidal displacement.
- property profile_type: int
Return modified trapezoidal profile type code.
- velocity(u: float) float
Compute modified trapezoidal velocity.
- class pylinkage.cam.motion_laws.MotionLaw
Bases:
ABCAbstract base class for cam motion laws.
Motion laws define the dimensionless displacement function s(u) where u in [0, 1] is the normalized angle and s in [0, 1] is the normalized displacement.
Subclasses must implement displacement(), velocity(), and acceleration() methods that operate on normalized coordinates.
- abstractmethod acceleration(u: float) float
Compute dimensionless acceleration d2s/du2.
- Parameters:
u – Normalized angle in [0, 1].
- Returns:
Normalized acceleration.
- abstractmethod displacement(u: float) float
Compute dimensionless displacement s(u).
- Parameters:
u – Normalized angle in [0, 1].
- Returns:
Normalized displacement in [0, 1].
- abstract property profile_type: int
Return the profile type code for numba dispatch.
- to_numba_coefficients() NDArray[np.float64]
Return coefficients for numba evaluation.
Default implementation returns empty array. Override for polynomial/custom motion laws.
- Returns:
Numpy array of coefficients.
- abstractmethod velocity(u: float) float
Compute dimensionless velocity ds/du.
- Parameters:
u – Normalized angle in [0, 1].
- Returns:
Normalized velocity.
- class pylinkage.cam.motion_laws.PolynomialMotionLaw(coefficients: list[float] | None = None)
Bases:
MotionLawGeneral polynomial motion law.
s(u) = sum(coefficients[i] * u^i)
- The polynomial must satisfy boundary conditions:
s(0) = 0, s(1) = 1 (displacement)
ds/du(0) = 0, ds/du(1) = 0 (velocity, typically)
- Common polynomials:
3-4-5 polynomial: [0, 0, 0, 10, -15, 6]
4-5-6-7 polynomial: [0, 0, 0, 0, 35, -84, 70, -20]
Example
>>> # 3-4-5 polynomial (zero velocity and acceleration at ends) >>> law = PolynomialMotionLaw([0, 0, 0, 10, -15, 6]) >>> law.displacement(0.0) 0.0 >>> law.displacement(1.0) 1.0 >>> law.velocity(0.0) 0.0
- acceleration(u: float) float
Compute polynomial acceleration.
- property coefficients: NDArray[np.float64]
Return polynomial coefficients.
- displacement(u: float) float
Compute polynomial displacement.
- property profile_type: int
Return polynomial profile type code.
- to_numba_coefficients() NDArray[np.float64]
Return polynomial coefficients for numba evaluation.
- velocity(u: float) float
Compute polynomial velocity.
- pylinkage.cam.motion_laws.polynomial_345() PolynomialMotionLaw
Create a 3-4-5 polynomial motion law.
s(u) = 10*u^3 - 15*u^4 + 6*u^5
- This polynomial has:
Zero velocity at u=0 and u=1
Zero acceleration at u=0 and u=1
- Returns:
PolynomialMotionLaw instance.
- pylinkage.cam.motion_laws.polynomial_4567() PolynomialMotionLaw
Create a 4-5-6-7 polynomial motion law.
s(u) = 35*u^4 - 84*u^5 + 70*u^6 - 20*u^7
- This polynomial has:
Zero velocity at u=0 and u=1
Zero acceleration at u=0 and u=1
Zero jerk at u=0 and u=1
- Returns:
PolynomialMotionLaw instance.
pylinkage.cam.profiles module
Cam profile definitions.
A cam profile defines the displacement as a function of cam angle. Profiles support evaluation, derivatives, and integration with the optimization constraint system.
- Available profiles:
FunctionProfile: Profile from motion law + timing parameters PointArrayProfile: Profile from discrete points with spline interpolation
- class pylinkage.cam.profiles.CamProfile
Bases:
ABCAbstract base class for cam profiles.
A cam profile defines the follower displacement as a function of cam rotation angle. The profile determines the shape of the cam and the resulting follower motion.
- Variables:
base_radius (float) – Minimum radius (base circle radius).
name (str) – Human-readable identifier.
- base_radius: float
- abstractmethod evaluate(angle: float) float
Evaluate cam radius at given angle.
- Parameters:
angle – Cam rotation angle in radians.
- Returns:
Cam radius at this angle.
- abstractmethod evaluate_derivative(angle: float) float
Evaluate cam radius derivative (dr/dtheta) at given angle.
Required for pressure angle and pitch curve calculations.
- Parameters:
angle – Cam rotation angle in radians.
- Returns:
Derivative dr/dtheta at this angle.
- abstractmethod get_constraints() tuple[float, ...]
Return optimizable constraint values.
- Returns:
Tuple of constraint values that can be optimized.
- name: str
- pitch_radius(angle: float, roller_radius: float) float
Compute pitch curve radius for roller follower.
The pitch curve is the path traced by the roller center.
- Parameters:
angle – Cam rotation angle in radians.
roller_radius – Radius of the roller follower.
- Returns:
Distance from cam center to roller center.
- pressure_angle(angle: float) float
Compute pressure angle at given cam angle.
The pressure angle is the angle between the normal to the cam profile and the direction of follower motion. High pressure angles (> 30 degrees) can cause binding.
- Parameters:
angle – Cam rotation angle in radians.
- Returns:
Pressure angle in radians.
- abstractmethod set_constraints(*args: float | None) None
Set constraint values from optimization.
- Parameters:
*args – Constraint values to set.
- abstractmethod to_numba_data() tuple[int, NDArray[np.float64]]
Convert profile to numba-compatible representation.
- Returns:
Tuple of (profile_type_code, data_array).
- class pylinkage.cam.profiles.FunctionProfile(motion_law: MotionLaw | None = None, base_radius: float = 1.0, total_lift: float = 0.5, rise_start: float = 0.0, rise_end: float | None = None, dwell_high_end: float | None = None, fall_end: float | None = None, name: str | None = None)
Bases:
CamProfileCam profile from motion law and timing parameters.
Defines a rise-dwell-fall-dwell motion using a standard motion law. The motion law determines the shape of the rise and fall segments.
- Variables:
motion_law (pylinkage.cam.motion_laws.MotionLaw) – The motion law defining displacement curve.
base_radius (float) – Base circle radius.
total_lift (float) – Maximum follower displacement.
rise_start (float) – Angle where rise begins (radians).
rise_end (float) – Angle where rise ends / dwell-high begins.
dwell_high_end (float) – Angle where dwell-high ends / fall begins.
fall_end (float) – Angle where fall ends / dwell-low begins.
Example
>>> from pylinkage.cam import FunctionProfile, HarmonicMotionLaw >>> import math >>> profile = FunctionProfile( ... motion_law=HarmonicMotionLaw(), ... base_radius=1.0, ... total_lift=0.5, ... rise_start=0.0, ... rise_end=math.pi/2, ... dwell_high_end=math.pi, ... fall_end=3*math.pi/2, ... ) >>> profile.evaluate(0.0) # At base radius 1.0 >>> profile.evaluate(math.pi) # At max lift 1.5
- base_radius: float
- dwell_high_end: float
- evaluate(angle: float) float
Evaluate cam radius at given angle.
- evaluate_derivative(angle: float) float
Evaluate cam radius derivative at given angle.
- fall_end: float
- get_constraints() tuple[float, ...]
Return optimizable constraints.
Returns base_radius and total_lift as optimizable parameters. Timing parameters are typically fixed.
- name: str
- rise_end: float
- rise_start: float
- set_constraints(base_radius: float | None = None, total_lift: float | None = None, *args: float | None) None
Set constraint values.
- to_numba_data() tuple[int, NDArray[np.float64]]
Convert to numba-compatible representation.
- total_lift: float
- class pylinkage.cam.profiles.PointArrayProfile(angles: list[float] | NDArray[np.float64], radii: list[float] | NDArray[np.float64], periodic: bool = True, name: str | None = None)
Bases:
CamProfileCam profile from discrete (angle, radius) points.
Uses cubic spline interpolation to create a smooth profile through the given points. Supports periodic boundary conditions for closed cam profiles.
The spline coefficients are precomputed at construction for efficient numba evaluation in the simulation loop.
Example
>>> import math >>> angles = [0, math.pi/4, math.pi/2, math.pi, 3*math.pi/2, 2*math.pi] >>> radii = [1.0, 1.2, 1.5, 1.5, 1.2, 1.0] >>> profile = PointArrayProfile(angles=angles, radii=radii) >>> profile.evaluate(math.pi/4) # Approximately 1.2 1.2
- property angles: NDArray[np.float64]
Return the angle values.
- base_radius
- evaluate(angle: float) float
Evaluate cam radius at given angle using spline interpolation.
- evaluate_derivative(angle: float) float
Evaluate cam radius derivative at given angle.
- get_constraints() tuple[float, ...]
Return optimizable constraints (the radius values).
- name
- property radii: NDArray[np.float64]
Return the radius values.
- set_constraints(*radii: float | None) None
Set radius values and recompute spline.
- Parameters:
*radii – New radius values (same length as original).
- to_numba_data() tuple[int, NDArray[np.float64]]
Convert to numba-compatible representation.
Module contents
Cam profile definitions for cam-follower mechanisms.
This module provides classes for defining cam profiles that drive cam-follower mechanisms. Profiles can be defined analytically using motion laws or from discrete points with spline interpolation.
- Profile Types:
FunctionProfile: Profile from motion law + timing parameters PointArrayProfile: Profile from discrete points with spline interpolation
- Motion Laws:
HarmonicMotionLaw: Simple harmonic (cosine) motion CycloidalMotionLaw: Cycloidal motion (zero velocity/acceleration at ends) ModifiedTrapezoidalMotionLaw: Modified trapezoidal (low peak acceleration) PolynomialMotionLaw: General polynomial (3-4-5 or custom)
- Factory Functions:
polynomial_345: Create a 3-4-5 polynomial motion law polynomial_4567: Create a 4-5-6-7 polynomial motion law
Example
Create a cam profile with harmonic motion:
from pylinkage.cam import FunctionProfile, HarmonicMotionLaw
import math
profile = FunctionProfile(
motion_law=HarmonicMotionLaw(),
base_radius=1.0,
total_lift=0.5,
rise_start=0.0,
rise_end=math.pi/2,
dwell_high_end=math.pi,
fall_end=3*math.pi/2,
)
# Evaluate at various angles
r0 = profile.evaluate(0.0) # Base radius
r_max = profile.evaluate(math.pi) # Max radius
Create a cam profile from discrete points:
from pylinkage.cam import PointArrayProfile
import math
angles = [0, math.pi/4, math.pi/2, math.pi, 3*math.pi/2, 2*math.pi]
radii = [1.0, 1.2, 1.5, 1.5, 1.2, 1.0]
profile = PointArrayProfile(angles=angles, radii=radii)
- class pylinkage.cam.CamProfile
Bases:
ABCAbstract base class for cam profiles.
A cam profile defines the follower displacement as a function of cam rotation angle. The profile determines the shape of the cam and the resulting follower motion.
- Variables:
base_radius (float) – Minimum radius (base circle radius).
name (str) – Human-readable identifier.
- base_radius: float
- abstractmethod evaluate(angle: float) float
Evaluate cam radius at given angle.
- Parameters:
angle – Cam rotation angle in radians.
- Returns:
Cam radius at this angle.
- abstractmethod evaluate_derivative(angle: float) float
Evaluate cam radius derivative (dr/dtheta) at given angle.
Required for pressure angle and pitch curve calculations.
- Parameters:
angle – Cam rotation angle in radians.
- Returns:
Derivative dr/dtheta at this angle.
- abstractmethod get_constraints() tuple[float, ...]
Return optimizable constraint values.
- Returns:
Tuple of constraint values that can be optimized.
- name: str
- pitch_radius(angle: float, roller_radius: float) float
Compute pitch curve radius for roller follower.
The pitch curve is the path traced by the roller center.
- Parameters:
angle – Cam rotation angle in radians.
roller_radius – Radius of the roller follower.
- Returns:
Distance from cam center to roller center.
- pressure_angle(angle: float) float
Compute pressure angle at given cam angle.
The pressure angle is the angle between the normal to the cam profile and the direction of follower motion. High pressure angles (> 30 degrees) can cause binding.
- Parameters:
angle – Cam rotation angle in radians.
- Returns:
Pressure angle in radians.
- abstractmethod set_constraints(*args: float | None) None
Set constraint values from optimization.
- Parameters:
*args – Constraint values to set.
- abstractmethod to_numba_data() tuple[int, NDArray[np.float64]]
Convert profile to numba-compatible representation.
- Returns:
Tuple of (profile_type_code, data_array).
- class pylinkage.cam.CycloidalMotionLaw
Bases:
MotionLawCycloidal motion law.
s(u) = u - sin(2*pi*u) / (2*pi)
- Properties:
Zero velocity at boundaries (smooth start/stop)
Zero acceleration at boundaries (no jerk discontinuity)
Higher peak acceleration than harmonic
Best for high-speed applications requiring smooth motion
Example
>>> law = CycloidalMotionLaw() >>> law.displacement(0.0) # Zero displacement at start 0.0 >>> law.velocity(0.0) # Zero velocity at start 0.0 >>> law.velocity(0.5) # Maximum velocity at midpoint 2.0
- acceleration(u: float) float
Compute cycloidal acceleration.
- displacement(u: float) float
Compute cycloidal displacement.
- property profile_type: int
Return cycloidal profile type code.
- velocity(u: float) float
Compute cycloidal velocity.
- class pylinkage.cam.FunctionProfile(motion_law: MotionLaw | None = None, base_radius: float = 1.0, total_lift: float = 0.5, rise_start: float = 0.0, rise_end: float | None = None, dwell_high_end: float | None = None, fall_end: float | None = None, name: str | None = None)
Bases:
CamProfileCam profile from motion law and timing parameters.
Defines a rise-dwell-fall-dwell motion using a standard motion law. The motion law determines the shape of the rise and fall segments.
- Variables:
motion_law (pylinkage.cam.motion_laws.MotionLaw) – The motion law defining displacement curve.
base_radius (float) – Base circle radius.
total_lift (float) – Maximum follower displacement.
rise_start (float) – Angle where rise begins (radians).
rise_end (float) – Angle where rise ends / dwell-high begins.
dwell_high_end (float) – Angle where dwell-high ends / fall begins.
fall_end (float) – Angle where fall ends / dwell-low begins.
Example
>>> from pylinkage.cam import FunctionProfile, HarmonicMotionLaw >>> import math >>> profile = FunctionProfile( ... motion_law=HarmonicMotionLaw(), ... base_radius=1.0, ... total_lift=0.5, ... rise_start=0.0, ... rise_end=math.pi/2, ... dwell_high_end=math.pi, ... fall_end=3*math.pi/2, ... ) >>> profile.evaluate(0.0) # At base radius 1.0 >>> profile.evaluate(math.pi) # At max lift 1.5
- base_radius: float
- dwell_high_end: float
- evaluate(angle: float) float
Evaluate cam radius at given angle.
- evaluate_derivative(angle: float) float
Evaluate cam radius derivative at given angle.
- fall_end: float
- get_constraints() tuple[float, ...]
Return optimizable constraints.
Returns base_radius and total_lift as optimizable parameters. Timing parameters are typically fixed.
- motion_law: MotionLaw
- name: str
- rise_end: float
- rise_start: float
- set_constraints(base_radius: float | None = None, total_lift: float | None = None, *args: float | None) None
Set constraint values.
- to_numba_data() tuple[int, NDArray[np.float64]]
Convert to numba-compatible representation.
- total_lift: float
- class pylinkage.cam.HarmonicMotionLaw
Bases:
MotionLawSimple harmonic (cosine) motion law.
s(u) = (1 - cos(pi * u)) / 2
- Properties:
Smooth displacement curve
Non-zero acceleration at boundaries (acceleration discontinuity)
Maximum velocity at u = 0.5
Simple and widely used
Example
>>> law = HarmonicMotionLaw() >>> law.displacement(0.0) # Start 0.0 >>> law.displacement(0.5) # Midpoint 0.5 >>> law.displacement(1.0) # End 1.0
- acceleration(u: float) float
Compute harmonic acceleration.
- displacement(u: float) float
Compute harmonic displacement.
- property profile_type: int
Return harmonic profile type code.
- velocity(u: float) float
Compute harmonic velocity.
- class pylinkage.cam.ModifiedTrapezoidalMotionLaw
Bases:
MotionLawModified trapezoidal motion law.
Uses sinusoidal acceleration segments at start/end with constant acceleration in the middle.
- Properties:
Lower peak acceleration than harmonic or cycloidal
Zero velocity at boundaries
Continuous acceleration (no jerk discontinuity)
Good for high-load applications
Example
>>> law = ModifiedTrapezoidalMotionLaw() >>> law.displacement(0.0) 0.0 >>> law.displacement(1.0) 1.0
- acceleration(u: float) float
Compute modified trapezoidal acceleration (numerical).
- displacement(u: float) float
Compute modified trapezoidal displacement.
- property profile_type: int
Return modified trapezoidal profile type code.
- velocity(u: float) float
Compute modified trapezoidal velocity.
- class pylinkage.cam.MotionLaw
Bases:
ABCAbstract base class for cam motion laws.
Motion laws define the dimensionless displacement function s(u) where u in [0, 1] is the normalized angle and s in [0, 1] is the normalized displacement.
Subclasses must implement displacement(), velocity(), and acceleration() methods that operate on normalized coordinates.
- abstractmethod acceleration(u: float) float
Compute dimensionless acceleration d2s/du2.
- Parameters:
u – Normalized angle in [0, 1].
- Returns:
Normalized acceleration.
- abstractmethod displacement(u: float) float
Compute dimensionless displacement s(u).
- Parameters:
u – Normalized angle in [0, 1].
- Returns:
Normalized displacement in [0, 1].
- abstract property profile_type: int
Return the profile type code for numba dispatch.
- to_numba_coefficients() NDArray[np.float64]
Return coefficients for numba evaluation.
Default implementation returns empty array. Override for polynomial/custom motion laws.
- Returns:
Numpy array of coefficients.
- abstractmethod velocity(u: float) float
Compute dimensionless velocity ds/du.
- Parameters:
u – Normalized angle in [0, 1].
- Returns:
Normalized velocity.
- class pylinkage.cam.PointArrayProfile(angles: list[float] | NDArray[np.float64], radii: list[float] | NDArray[np.float64], periodic: bool = True, name: str | None = None)
Bases:
CamProfileCam profile from discrete (angle, radius) points.
Uses cubic spline interpolation to create a smooth profile through the given points. Supports periodic boundary conditions for closed cam profiles.
The spline coefficients are precomputed at construction for efficient numba evaluation in the simulation loop.
Example
>>> import math >>> angles = [0, math.pi/4, math.pi/2, math.pi, 3*math.pi/2, 2*math.pi] >>> radii = [1.0, 1.2, 1.5, 1.5, 1.2, 1.0] >>> profile = PointArrayProfile(angles=angles, radii=radii) >>> profile.evaluate(math.pi/4) # Approximately 1.2 1.2
- property angles: NDArray[np.float64]
Return the angle values.
- base_radius
- evaluate(angle: float) float
Evaluate cam radius at given angle using spline interpolation.
- evaluate_derivative(angle: float) float
Evaluate cam radius derivative at given angle.
- get_constraints() tuple[float, ...]
Return optimizable constraints (the radius values).
- name
- property radii: NDArray[np.float64]
Return the radius values.
- set_constraints(*radii: float | None) None
Set radius values and recompute spline.
- Parameters:
*radii – New radius values (same length as original).
- to_numba_data() tuple[int, NDArray[np.float64]]
Convert to numba-compatible representation.
- class pylinkage.cam.PolynomialMotionLaw(coefficients: list[float] | None = None)
Bases:
MotionLawGeneral polynomial motion law.
s(u) = sum(coefficients[i] * u^i)
- The polynomial must satisfy boundary conditions:
s(0) = 0, s(1) = 1 (displacement)
ds/du(0) = 0, ds/du(1) = 0 (velocity, typically)
- Common polynomials:
3-4-5 polynomial: [0, 0, 0, 10, -15, 6]
4-5-6-7 polynomial: [0, 0, 0, 0, 35, -84, 70, -20]
Example
>>> # 3-4-5 polynomial (zero velocity and acceleration at ends) >>> law = PolynomialMotionLaw([0, 0, 0, 10, -15, 6]) >>> law.displacement(0.0) 0.0 >>> law.displacement(1.0) 1.0 >>> law.velocity(0.0) 0.0
- acceleration(u: float) float
Compute polynomial acceleration.
- property coefficients: NDArray[np.float64]
Return polynomial coefficients.
- displacement(u: float) float
Compute polynomial displacement.
- property profile_type: int
Return polynomial profile type code.
- to_numba_coefficients() NDArray[np.float64]
Return polynomial coefficients for numba evaluation.
- velocity(u: float) float
Compute polynomial velocity.
- pylinkage.cam.polynomial_345() PolynomialMotionLaw
Create a 3-4-5 polynomial motion law.
s(u) = 10*u^3 - 15*u^4 + 6*u^5
- This polynomial has:
Zero velocity at u=0 and u=1
Zero acceleration at u=0 and u=1
- Returns:
PolynomialMotionLaw instance.
- pylinkage.cam.polynomial_4567() PolynomialMotionLaw
Create a 4-5-6-7 polynomial motion law.
s(u) = 35*u^4 - 84*u^5 + 70*u^6 - 20*u^7
- This polynomial has:
Zero velocity at u=0 and u=1
Zero acceleration at u=0 and u=1
Zero jerk at u=0 and u=1
- Returns:
PolynomialMotionLaw instance.