pylinkage package
Subpackages
- pylinkage.collections package
- pylinkage.geometry package
- pylinkage.joints package
- pylinkage.linkage package
- Submodules
- pylinkage.linkage.analysis module
- pylinkage.linkage.linkage module
- pylinkage.linkage.sensitivity module
SensitivityAnalysisSensitivityAnalysis.baseline_path_metricSensitivityAnalysis.baseline_transmissionSensitivityAnalysis.constraint_namesSensitivityAnalysis.most_sensitiveSensitivityAnalysis.perturbation_deltaSensitivityAnalysis.perturbed_path_metricsSensitivityAnalysis.perturbed_transmissionSensitivityAnalysis.sensitivitiesSensitivityAnalysis.sensitivity_rankingSensitivityAnalysis.to_dataframe()
ToleranceAnalysisanalyze_sensitivity()analyze_tolerance()
- pylinkage.linkage.transmission module
StrokeAnalysisTransmissionAngleAnalysisTransmissionAngleAnalysis.acceptable_rangeTransmissionAngleAnalysis.anglesTransmissionAngleAnalysis.is_acceptableTransmissionAngleAnalysis.max_angleTransmissionAngleAnalysis.max_angle_stepTransmissionAngleAnalysis.max_deviationTransmissionAngleAnalysis.mean_angleTransmissionAngleAnalysis.min_angleTransmissionAngleAnalysis.min_angle_stepTransmissionAngleAnalysis.min_deviationTransmissionAngleAnalysis.plot()TransmissionAngleAnalysis.worst_angle()
analyze_stroke()analyze_transmission()compute_slide_position()compute_transmission_angle()stroke_at_position()transmission_angle_at_position()
- Module contents
SensitivityAnalysisSensitivityAnalysis.baseline_path_metricSensitivityAnalysis.baseline_transmissionSensitivityAnalysis.constraint_namesSensitivityAnalysis.most_sensitiveSensitivityAnalysis.perturbation_deltaSensitivityAnalysis.perturbed_path_metricsSensitivityAnalysis.perturbed_transmissionSensitivityAnalysis.sensitivitiesSensitivityAnalysis.sensitivity_rankingSensitivityAnalysis.to_dataframe()
StrokeAnalysisToleranceAnalysisTransmissionAngleAnalysisTransmissionAngleAnalysis.acceptable_rangeTransmissionAngleAnalysis.anglesTransmissionAngleAnalysis.is_acceptableTransmissionAngleAnalysis.max_angleTransmissionAngleAnalysis.max_angle_stepTransmissionAngleAnalysis.max_deviationTransmissionAngleAnalysis.mean_angleTransmissionAngleAnalysis.min_angleTransmissionAngleAnalysis.min_angle_stepTransmissionAngleAnalysis.min_deviationTransmissionAngleAnalysis.plot()TransmissionAngleAnalysis.worst_angle()
analyze_sensitivity()analyze_stroke()analyze_tolerance()analyze_transmission()bounding_box()compute_slide_position()compute_transmission_angle()extract_trajectories()extract_trajectory()kinematic_default_test()
- pylinkage.optimization package
- pylinkage.visualizer package
Submodules
pylinkage.exceptions module
The exceptions module is a simple quick way to access the built-in exceptions.
Created on Wed Jun 16, 15:20:06 2021.
@author: HugoFara
- exception pylinkage.exceptions.NotCompletelyDefinedError(joint: Any, message: str = 'The joint is not completely defined!')
Bases:
ExceptionThe linkage definition is incomplete.
- exception pylinkage.exceptions.OptimizationError(message: str = 'Optimization failed')
Bases:
ExceptionShould be raised when the optimization process fails.
- exception pylinkage.exceptions.UnbuildableError(joint: Any, message: str = 'Unable to solve constraints')
Bases:
ExceptionShould be raised when the constraints cannot be solved.
Module contents
PyLinkage is a module to create, optimize and visualize linkages.
Please see the documentation at https://hugofara.github.io/pylinkage/. A copy of the documentation should have been distributed on your system in the docs/ folder.
Created on Thu Jun 10 21:30:52 2021
@author: HugoFara
- pylinkage.ComponentId
alias of
str
- pylinkage.EdgeId
alias of
str
- pylinkage.HyperedgeId
alias of
str
- class pylinkage.JointType(*values)
Bases:
IntEnumKinematic joint type.
This enum classifies joints by their allowed degrees of freedom. Values are explicit integers for serialization stability.
- Attributes:
REVOLUTE: Pin joint allowing rotation (R). 1 DOF rotation. PRISMATIC: Slider joint allowing translation (P). 1 DOF translation. GROUND: Fixed revolute joint on frame. Special case for mechanism module. TRACKER: Observer joint (T). 0 DOF, just tracks a position.
- GROUND = 3
- PRISMATIC = 2
- REVOLUTE = 1
- TRACKER = 4
- class pylinkage.Linkage(components: Iterable[Component], order: Iterable[Component] | None = None, name: str | None = None)
Bases:
objectA planar linkage mechanism built from components.
The Linkage class orchestrates a collection of components (Ground points, actuators, and dyads) to simulate a planar mechanism. It handles solve order computation, stepping, and constraint management.
- Example:
>>> from pylinkage.components import Ground >>> from pylinkage.actuators import Crank >>> from pylinkage.dyads import RRRDyad >>> from pylinkage.simulation import Linkage >>> >>> O1 = Ground(0.0, 0.0, name="O1") >>> O2 = Ground(2.0, 0.0, name="O2") >>> crank = Crank(anchor=O1, radius=1.0, angular_velocity=0.1) >>> rocker = RRRDyad(crank.output, O2, distance1=2.0, distance2=1.5) >>> linkage = Linkage([O1, O2, crank, rocker], name="Four-Bar") >>> for positions in linkage.step(): ... print(positions)
- analyze_sensitivity(output_joint: object | int | None = None, delta: float = 0.01, include_transmission: bool = True, iterations: int | None = None) SensitivityAnalysis
Compute sensitivity of an output path to constraint perturbations.
- analyze_stroke(prismatic_joint: object | None = None, iterations: int | None = None) StrokeAnalysis
Analyze stroke/slide position over a full motion cycle.
- analyze_tolerance(tolerances: dict[str, float], output_joint: object | int | None = None, iterations: int | None = None, n_samples: int = 1000, seed: int | None = None) ToleranceAnalysis
Monte-Carlo tolerance analysis over the output path.
- analyze_transmission(iterations: int | None = None, acceptable_range: tuple[float, float] = (40.0, 140.0)) TransmissionAngleAnalysis
Analyze transmission angle over a full motion cycle.
See
pylinkage.linkage.analyze_transmission()for details.
- compile() None
Pre-compile the numba solver state for
step_fast().Cached on
self._solver_dataand reused until invalidated by a call tocompile()again.
- components: tuple[Component, ...]
- property dyads: tuple[Component, ...]
Return components (backwards compatibility alias).
- get_accelerations() list[tuple[float, float] | None]
Return accelerations for all components.
- Returns:
List of (ax, ay) tuples, one per component. Returns None for components whose acceleration has not been computed.
- get_constraints() list[float]
Return all geometric constraints as a flat list.
- Returns:
Flat list of all constraint values, used by optimizers.
- get_coords() list[tuple[float | None, float | None]]
Return positions of all components.
- Returns:
List of (x, y) positions.
- get_rotation_period() int
Return number of steps for one full cycle.
Computes the LCM of all actuator periods (cranks, arc cranks, and linear actuators). For cranks, period is 2*pi / angular_velocity. For arc cranks, period is 2 * (arc_end - arc_start) / angular_velocity. For linear actuators, period is 2 * stroke / velocity.
- Returns:
Number of iterations with dt=1.
- get_velocities() list[tuple[float, float] | None]
Return velocities for all components.
- Returns:
List of (vx, vy) tuples, one per component. Returns None for components whose velocity has not been computed.
- indeterminacy() int
Mobility (DOF) of the linkage — planar Gruebler-Kutzbach.
DOF = 3·(n − 1) − 2·R − Pwhere each non-ground component contributes its share of bodies and kinematic pairs:Groundanchors are points on the frame (no new body, no new pair on their own);Crank/LinearActuatoradd 1 body + 1 R/P pair;binary dyads (
RRRDyad,FixedDyad) add 2 bodies + 3 R-pairs;RRPDyadadds 2 bodies + 2 R-pairs + 1 P-pair.
A standard Grashof four-bar (Crank + RRRDyad) returns
1.
- name: str
- rebuild(positions: list[tuple[float, float]] | None = None) None
Rebuild the linkage, optionally setting initial positions.
- Args:
- positions: Initial positions for each component. If None, uses
current positions.
- set_completely(constraints: list[float], positions: list[tuple[float, float]]) None
Apply both constraints and initial positions in one call.
- Args:
constraints: Flat list (as accepted by
set_constraints()). positions: Per-component(x, y)positions(as accepted by
set_coords()).
- set_constraints(values: list[float]) None
Set constraints from a flat list.
Used to apply optimization results. Invalidates any cached SolverData so the next
step_fast()recompiles.- Args:
values: Flat list of constraint values.
- set_coords(coords: list[tuple[float, float]]) None
Set positions for all components.
- Args:
coords: List of (x, y) positions.
- set_input_velocity(actuator: Crank, omega: float, alpha: float = 0.0) None
Set angular velocity and acceleration for a crank actuator.
This is used for kinematics computation (velocity/acceleration analysis). The omega value will be used to compute linear velocities at each joint.
- Args:
actuator: The crank actuator to set velocity for. omega: Angular velocity in rad/s (physical units for analysis). alpha: Angular acceleration in rad/s² (default 0).
- Raises:
ValueError: If the actuator is not part of this linkage.
- Example:
>>> linkage.set_input_velocity(crank, omega=10.0) # 10 rad/s >>> for pos, vel, acc in linkage.step_with_derivatives(): ... print(f"Position: {pos}, Velocity: {vel}")
- simulation(iterations: int | None = None, dt: float = 1.0) _SimulationContext
Return a context manager that simulates this linkage.
The context restores the initial joint positions on exit, so repeated invocations return to the same starting state.
- step(iterations: int | None = None, dt: float = 1) Generator[tuple[tuple[float | None, float | None], ...], None, None]
Simulate the linkage.
Yields positions for all components at each step.
- Args:
iterations: Number of steps. If None, uses get_rotation_period(). dt: Time step multiplier for actuators (cranks and linear actuators).
- Yields:
Tuple of (x, y) positions for each component.
- step_fast(iterations: int | None = None, dt: float = 1) ndarray[tuple[Any, ...], dtype[float64]]
Run the simulation through the numba-compiled solver.
Significantly faster than
step()for large iteration counts because it avoids per-step Python dispatch.- Args:
iterations: Number of steps. Defaults to
get_rotation_period(). dt: Time step multiplier (default 1.0).- Returns:
Trajectory array of shape
(iterations, n_components, 2). Unbuildable configurations appear as NaN — check withnp.isnan(trajectory).any().
- step_fast_with_kinematics(iterations: int | None = None, dt: float = 1.0) tuple[ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[float64]]]
Run the numba-compiled simulation, returning velocities and accelerations.
Per-crank
omega/alphainputs must be set viaset_input_velocity()(cranks without an explicit input default to zero).- Args:
iterations: Number of steps. Defaults to
get_rotation_period(). dt: Time step multiplier (default 1.0).- Returns:
(positions, velocities, accelerations)— each a numpy array of shape(iterations, n_components, 2).
- step_with_derivatives(iterations: int | None = None, dt: float = 1) Generator[tuple[tuple[tuple[float | None, float | None], ...], tuple[tuple[float, float] | None, ...], tuple[tuple[float, float] | None, ...]], None, None]
Simulate the linkage with velocity and acceleration computation.
Yields positions, velocities, and accelerations for all components at each step. Requires that omega (and optionally alpha) is set on crank actuators via set_input_velocity().
- Args:
iterations: Number of steps. If None, uses get_rotation_period(). dt: Time step multiplier for actuators (cranks and linear actuators).
- Yields:
Tuple of (positions, velocities, accelerations) where: - positions: Tuple of (x, y) for each component - velocities: Tuple of (vx, vy) or None for each component - accelerations: Tuple of (ax, ay) or None for each component
- Example:
>>> linkage.set_input_velocity(crank, omega=10.0) >>> for pos, vel, acc in linkage.step_with_derivatives(): ... print(f"Crank velocity: {vel[2]}")
- stroke_position() float
Slide position of a prismatic joint at the current pose.
See
pylinkage.linkage.stroke_at_position().
- to_hypergraph() tuple[HypergraphLinkage, Dimensions]
Return a hypergraph view of this linkage.
Delegates to
pylinkage.hypergraph.from_sim_linkage(). The return is a tuple(HypergraphLinkage, Dimensions).
- transmission_angle() float
Transmission angle at the current pose, in degrees.
See
pylinkage.linkage.transmission_angle_at_position().
- pylinkage.NodeId
alias of
str
- class pylinkage.NodeRole(*values)
Bases:
IntEnumRole of a node/joint in the mechanism.
Classifies joints by their kinematic role in the linkage structure.
- Attributes:
GROUND: Fixed frame point that does not move. DRIVER: Input/motor joint that provides motion (actuated). DRIVEN: Position computed from constraints (passive, part of Assur groups).
- DRIVEN = 2
- DRIVER = 1
- GROUND = 0
- exception pylinkage.NotCompletelyDefinedError(joint: Any, message: str = 'The joint is not completely defined!')
Bases:
ExceptionThe linkage definition is incomplete.
- exception pylinkage.OptimizationError(message: str = 'Optimization failed')
Bases:
ExceptionShould be raised when the optimization process fails.
- pylinkage.PortId
alias of
str
- class pylinkage.Simulation(linkage: Any, iterations: int | None = None, dt: float = 1.0)
Bases:
objectContext-managed wrapper around a linkage’s
step()generator.Works with any container that exposes
get_coords(),set_coords(positions), andstep(iterations=..., dt=...).- Example:
>>> with linkage.simulation(iterations=100) as sim: ... for step, coords in sim: ... print(step, coords)
- property iterations: int
Number of iterations for this simulation.
- property linkage: Any
The linkage being simulated.
- exception pylinkage.UnbuildableError(joint: Any, message: str = 'Unable to solve constraints')
Bases:
ExceptionShould be raised when the constraints cannot be solved.
- exception pylinkage.UnderconstrainedError(linkage: Linkage | str, message: str = 'The linkage is under-constrained!')
Bases:
ExceptionThe linkage is under-constrained and multiple solutions may exist.
- pylinkage.bounding_box(locus: Iterable[tuple[float, float]]) tuple[float, float, float, float]
Compute the bounding box of a locus.
- Parameters:
locus – A list of points or any iterable with the same structure.
- Returns:
Bounding box as (y_min, x_max, y_max, x_min).
- pylinkage.circle_intersect(x1: float, y1: float, r1: float, x2: float, y2: float, r2: float, tol: float = 0.0) tuple[int, float, float, float, float]
Get the intersections of two circles.
Transcription of a Matt Woodhead program, method provided by Paul Bourke, 1997. http://paulbourke.net/geometry/circlesphere/.
- Parameters:
x1 – X coordinate of first circle center.
y1 – Y coordinate of first circle center.
r1 – Radius of first circle.
x2 – X coordinate of second circle center.
y2 – Y coordinate of second circle center.
r2 – Radius of second circle.
tol – Distance under which two points are considered equal.
- Returns:
Tuple of (n_intersections, x1, y1, x2, y2) where: - n=0: No intersection (other values undefined) - n=1: One intersection at (x1, y1) - n=2: Two intersections at (x1, y1) and (x2, y2) - n=3: Same circle (x1, y1, x2 are center and radius)
- pylinkage.cyl_to_cart(radius: float, theta: float, ori_x: float = 0.0, ori_y: float = 0.0) tuple[float, float]
Convert polar coordinates into cartesian.
- Parameters:
radius – Distance from origin.
theta – Angle starting from abscissa axis.
ori_x – Origin X coordinate (Default value = 0.0).
ori_y – Origin Y coordinate (Default value = 0.0).
- Returns:
Cartesian coordinates (x, y).
- pylinkage.extract_trajectories(loci: Sequence[Sequence[tuple[float, float] | tuple[Any, Any]]], linkage: Any | None = None) dict[Any, tuple[ndarray, ndarray]]
Extract the (x, y) path of every joint from simulation loci.
Frames where a given joint’s position is
None(unbuildable configuration) are skipped per joint — each joint’s arrays only contain frames where that joint was successfully solved.- Parameters:
loci – Sequence of frames as produced by
Linkage.step()orMechanism.step(). Each frame is a sequence of(x, y)tuples, one per joint/component in the linkage’s iteration order.linkage – Optional
LinkageorMechanism. If provided, the returned dict is keyed by joint name; otherwise it is keyed by integer index.
- Returns:
Mapping
{joint_name_or_index: (xs, ys)}. Each(xs, ys)pair is a tuple ofnumpy.ndarraywith matching length. Empty arrays indicate a joint was never buildable.
- pylinkage.extract_trajectory(loci: Sequence[Sequence[tuple[float, float] | tuple[Any, Any]]], joint: int | str | Any = -1, linkage: Any | None = None) tuple[ndarray, ndarray]
Extract the (x, y) path of a single joint from simulation loci.
Frames where the joint position is
None(unbuildable configuration) are silently skipped.- Parameters:
loci – Sequence of frames as produced by
Linkage.step()orMechanism.step(). Each frame is a sequence of(x, y)tuples, one per joint/component in the linkage’s iteration order.joint –
Which joint’s trajectory to extract. Can be:
an integer index into each frame (default
-1= last joint),a joint/component name (requires
linkage),a joint/component instance (requires
linkage).
linkage – The
LinkageorMechanismthe loci come from. Required whenjointis a name or instance.
- Returns:
Pair
(xs, ys)ofnumpy.ndarraywith the same length. Empty arrays if every frame is unbuildable.
- pylinkage.intersection(obj_1: tuple[float, float] | tuple[float, float, float], obj_2: tuple[float, float] | tuple[float, float, float], tol: float = 0.0) tuple[float, float] | tuple[tuple[float, float], ...] | tuple[float, float, float] | None
Intersection of two arbitrary objects.
The input objects should be points or circles.
- Parameters:
obj_1 – First point or circle (as tuple).
obj_2 – Second point or circle (as tuple).
tol – Absolute tolerance to use if provided.
- Returns:
The intersection found, if any.
- pylinkage.kinematic_default_test(func: Callable[[...], float], error_penalty: float) Callable[[Linkage, Iterable[float], JointPositions | None], float]
Standard run for any linkage before a complete fitness evaluation.
This decorator makes a kinematic simulation, before passing the loci to the decorated function.
- Parameters:
func – Fitness function to be decorated.
error_penalty – Penalty value for unbuildable linkage. Common values include float(‘inf’) and 0.
- pylinkage.norm(x: float, y: float) float
Return the norm of a 2-dimensional vector.
- Parameters:
x – X component.
y – Y component.
- Returns:
Vector magnitude.
- pylinkage.sqr_dist(x1: float, y1: float, x2: float, y2: float) float
Square of the distance between two points.
Faster than dist when only comparing distances.
- Parameters:
x1 – X coordinate of first point.
y1 – Y coordinate of first point.
x2 – X coordinate of second point.
y2 – Y coordinate of second point.
- Returns:
Squared distance.