pylinkage.linkage package
Submodules
pylinkage.linkage.analysis module
Analysis tools for linkages.
- pylinkage.linkage.analysis.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.linkage.analysis.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.linkage.analysis.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.linkage.analysis.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.linkage.analysis.movement_bounding_box(loci: Iterable[Iterable[tuple[float, float]]]) tuple[float, float, float, float]
Bounding box for a group of loci.
- Parameters:
loci – Iterable of loci (sequences of coordinates).
- Returns:
Bounding box as (y_min, x_max, y_max, x_min).
pylinkage.linkage.linkage module
pylinkage.linkage.sensitivity module
Sensitivity and tolerance analysis for linkage mechanisms.
This module provides analysis tools for:
Sensitivity analysis: - Measures how each constraint dimension affects the output path - Identifies critical dimensions that most affect mechanism behavior
Tolerance analysis: - Monte Carlo simulation of manufacturing tolerances - Statistical analysis of output variation due to dimensional tolerances
- class pylinkage.linkage.sensitivity.SensitivityAnalysis(sensitivities: dict[str, float], baseline_path_metric: float, baseline_transmission: float | None, perturbed_path_metrics: ndarray[tuple[Any, ...], dtype[float64]], perturbed_transmission: ndarray[tuple[Any, ...], dtype[float64]] | None, constraint_names: tuple[str, ...], perturbation_delta: float)
Bases:
objectResults of sensitivity analysis for a linkage.
Sensitivity measures how much the output path changes when each constraint dimension is perturbed by a small amount.
- Attributes:
- sensitivities: Mapping from constraint name to sensitivity coefficient.
Higher values indicate the output is more sensitive to that constraint.
baseline_path_metric: Mean path deviation at nominal constraints (should be 0). baseline_transmission: Mean transmission angle at nominal (degrees), or None. perturbed_path_metrics: Array of path deviation metrics, one per constraint. perturbed_transmission: Array of transmission angles per constraint, or None. constraint_names: Tuple of constraint names in order. perturbation_delta: Relative perturbation magnitude used (e.g., 0.01 for 1%).
- baseline_path_metric: float
- baseline_transmission: float | None
- constraint_names: tuple[str, ...]
- property most_sensitive: str
Return name of the most sensitive constraint.
- perturbation_delta: float
- perturbed_path_metrics: ndarray[tuple[Any, ...], dtype[float64]]
- perturbed_transmission: ndarray[tuple[Any, ...], dtype[float64]] | None
- sensitivities: dict[str, float]
- property sensitivity_ranking: list[tuple[str, float]]
Return constraints ranked by sensitivity (highest first).
- to_dataframe() object
Export results as pandas DataFrame.
- Returns:
DataFrame with columns: constraint, sensitivity, perturbed_metric
- Raises:
ImportError: If pandas is not installed.
- class pylinkage.linkage.sensitivity.ToleranceAnalysis(nominal_path: ndarray[tuple[Any, ...], dtype[float64]], output_cloud: ndarray[tuple[Any, ...], dtype[float64]], tolerances: dict[str, float], mean_deviation: float, max_deviation: float, std_deviation: float, position_std: ndarray[tuple[Any, ...], dtype[float64]])
Bases:
objectResults of Monte Carlo tolerance analysis.
Tolerance analysis simulates how manufacturing variations in link dimensions affect the output path. Each sample represents a possible manufactured linkage within tolerance bounds.
- Attributes:
nominal_path: Output path at nominal dimensions, shape (n_steps, 2). output_cloud: Monte Carlo results, shape (n_samples, n_steps, 2). tolerances: Dictionary mapping constraint names to their tolerances. mean_deviation: Mean path deviation from nominal across all samples. max_deviation: Maximum path deviation from nominal (worst case). std_deviation: Standard deviation of path deviations. position_std: Per-position standard deviation, shape (n_steps,).
- max_deviation: float
- mean_deviation: float
- nominal_path: ndarray[tuple[Any, ...], dtype[float64]]
- output_cloud: ndarray[tuple[Any, ...], dtype[float64]]
- plot_cloud(ax: Axes | None = None, show_nominal: bool = True, alpha: float = 0.1) Axes
Plot the tolerance cloud as a scatter plot.
- Args:
ax: Matplotlib axes to plot on. Creates new figure if None. show_nominal: Whether to show the nominal path as a solid line. alpha: Transparency for sample points (0-1).
- Returns:
Matplotlib axes with the plot.
- position_std: ndarray[tuple[Any, ...], dtype[float64]]
- std_deviation: float
- to_dataframe() object
Export statistics as pandas DataFrame.
- Returns:
DataFrame with tolerance statistics.
- Raises:
ImportError: If pandas is not installed.
- tolerances: dict[str, float]
- pylinkage.linkage.sensitivity.analyze_sensitivity(linkage: Linkage, output_joint: object | int | None = None, delta: float = 0.01, include_transmission: bool = True, iterations: int | None = None) SensitivityAnalysis
Analyze sensitivity of output path to each constraint dimension.
For each constraint, this function: 1. Perturbs the constraint by delta (relative) 2. Simulates the linkage 3. Measures the path deviation from nominal 4. Computes sensitivity coefficient
- Args:
linkage: The linkage to analyze. output_joint: Joint to measure, index, or None (auto-detect last joint). delta: Relative perturbation magnitude (e.g., 0.01 for 1%). include_transmission: Whether to also measure transmission angle sensitivity. iterations: Number of simulation steps. Defaults to one full rotation.
- Returns:
SensitivityAnalysis with sensitivity coefficients and statistics.
- Example:
>>> analysis = linkage.sensitivity_analysis(delta=0.01) >>> print(analysis.most_sensitive) >>> for name, sens in analysis.sensitivity_ranking: ... print(f"{name}: {sens:.4f}")
- pylinkage.linkage.sensitivity.analyze_tolerance(linkage: Linkage, tolerances: dict[str, float], output_joint: object | int | None = None, iterations: int | None = None, n_samples: int = 1000, seed: int | None = None) ToleranceAnalysis
Analyze manufacturing tolerance effects via Monte Carlo simulation.
For each sample: 1. Randomly perturb constraints within tolerance bounds 2. Simulate the linkage 3. Record the output path
- Args:
linkage: The linkage to analyze. tolerances: Dictionary mapping constraint names to tolerance values.
Each constraint is perturbed by +/- tolerance uniformly.
output_joint: Joint to measure, index, or None (auto-detect last joint). iterations: Number of simulation steps. Defaults to one full rotation. n_samples: Number of Monte Carlo samples to run. seed: Random seed for reproducibility.
- Returns:
ToleranceAnalysis with statistics and the sample cloud.
- Example:
>>> tolerances = { ... "Crank_radius": 0.1, # +/- 0.1 mm ... "Revolute_dist1": 0.2, # +/- 0.2 mm ... } >>> analysis = linkage.tolerance_analysis(tolerances, n_samples=500) >>> print(f"Max deviation: {analysis.max_deviation:.4f}") >>> analysis.plot_cloud()
pylinkage.linkage.transmission module
Kinematic analysis for linkage mechanisms.
This module provides analysis tools for:
Transmission angle analysis (for Revolute/RRR joints): - The angle between coupler and output links at their connecting joint - Ideal range: 40° to 140°, optimal at 90°
Stroke analysis (for Prismatic/RRP joints): - The slide position along the prismatic axis - Tracks min/max/range of travel over a motion cycle
- class pylinkage.linkage.transmission.StrokeAnalysis(min_position: float, max_position: float, mean_position: float, stroke_range: float, positions: ndarray[tuple[Any, ...], dtype[float64]], min_position_step: int, max_position_step: int)
Bases:
objectResults of stroke analysis for a prismatic joint over a motion cycle.
The stroke is the position of the slider along its axis, measured as the signed distance from the first line point (joint1) projected onto the slide axis.
- Attributes:
min_position: Minimum slide position along the axis. max_position: Maximum slide position along the axis. mean_position: Mean slide position. stroke_range: Total travel distance (max - min). positions: Array of slide positions at each step. min_position_step: Step index where minimum position occurs. max_position_step: Step index where maximum position occurs.
- property amplitude: float
Return half the stroke range (useful for oscillating mechanisms).
- property center_position: float
Return the center of the stroke range.
- max_position: float
- max_position_step: int
- mean_position: float
- min_position: float
- min_position_step: int
- positions: ndarray[tuple[Any, ...], dtype[float64]]
- stroke_range: float
- class pylinkage.linkage.transmission.TransmissionAngleAnalysis(min_angle: float, max_angle: float, mean_angle: float, angles: ndarray[tuple[Any, ...], dtype[float64]], is_acceptable: bool, min_deviation: float, max_deviation: float, min_angle_step: int, max_angle_step: int)
Bases:
objectResults of transmission angle analysis over a motion cycle.
- Attributes:
min_angle: Minimum transmission angle in degrees. max_angle: Maximum transmission angle in degrees. mean_angle: Mean transmission angle in degrees. angles: Array of transmission angles at each step (degrees). is_acceptable: True if angle always in acceptable range. min_deviation: Minimum deviation from 90 degrees. max_deviation: Maximum deviation from 90 degrees. min_angle_step: Step index where minimum angle occurs. max_angle_step: Step index where maximum angle occurs.
- property acceptable_range: tuple[float, float]
Return the standard acceptable range [40, 140] degrees.
- angles: ndarray[tuple[Any, ...], dtype[float64]]
- is_acceptable: bool
- max_angle: float
- max_angle_step: int
- max_deviation: float
- mean_angle: float
- min_angle: float
- min_angle_step: int
- min_deviation: float
- plot(ax: Axes | None = None, show_limits: bool = True, show_optimum: bool = True, title: str | None = 'Transmission Angle over Full Cycle') Axes
Plot transmission angle versus crank angle over one full cycle.
Crank angle is plotted in degrees on the x-axis (one revolution = 360°). The acceptable-range limits and the 90° optimum are marked with horizontal reference lines, and the y-axis is fixed to
[0, 180]so plots are comparable across mechanisms.- Parameters:
ax – Axes to draw on. A new figure is created if
None.show_limits – Draw red dashed lines at the acceptable-range bounds (default
[40°, 140°]).show_optimum – Draw a green dotted line at 90° (the transmission-angle optimum).
title – Plot title. Pass
Noneto omit.
- Returns:
The Matplotlib axes the plot was drawn on.
- worst_angle() float
Return the angle with maximum deviation from 90 degrees.
- pylinkage.linkage.transmission.analyze_stroke(linkage: Linkage, prismatic_joint: object | None = None, iterations: int | None = None) StrokeAnalysis
Analyze stroke/slide position over a full motion cycle.
For a linkage with a Prismatic joint, this tracks the slide position along the prismatic axis throughout the motion cycle.
- Args:
linkage: The Linkage to analyze. prismatic_joint: The Prismatic joint to analyze. Auto-detected if None. iterations: Number of simulation steps. Defaults to one full rotation.
- Returns:
StrokeAnalysis with statistics over the motion cycle.
- Raises:
ValueError: If joint cannot be detected or no valid positions found.
- pylinkage.linkage.transmission.analyze_transmission(linkage: Linkage, iterations: int | None = None, acceptable_range: tuple[float, float] = (40.0, 140.0)) TransmissionAngleAnalysis
Analyze transmission angle over a full motion cycle.
For a standard four-bar linkage, joints are auto-detected: - coupler_joint: The Crank joint (B) - output_joint: The Revolute joint (C) - rocker_pivot: The Static joint at Revolute.joint1 (D)
- Args:
linkage: The Linkage to analyze. iterations: Number of simulation steps. Defaults to one full rotation. acceptable_range: (min, max) acceptable angles in degrees.
- Returns:
TransmissionAngleAnalysis with statistics over the motion cycle.
- Raises:
ValueError: If joints cannot be detected or no valid positions found.
- pylinkage.linkage.transmission.compute_slide_position(slider_pos: tuple[float, float], line_point1: tuple[float, float], line_point2: tuple[float, float]) float
Compute the slide position along a prismatic axis.
The position is the signed distance from line_point1 to the projection of slider_pos onto the line, measured in the direction of line_point2.
- Args:
slider_pos: Current position of the slider joint. line_point1: First point defining the slide axis (origin). line_point2: Second point defining the slide axis (direction).
- Returns:
Signed distance along the axis from line_point1.
- pylinkage.linkage.transmission.compute_transmission_angle(coupler_joint: tuple[float, float], output_joint: tuple[float, float], rocker_pivot: tuple[float, float]) float
Compute transmission angle in degrees.
The transmission angle is the angle between: - Coupler link: from coupler_joint (B) to output_joint (C) - Rocker link: from rocker_pivot (D) to output_joint (C)
- Args:
coupler_joint: Position of the coupler input joint (B). output_joint: Position where angle is measured (C). rocker_pivot: Position of the rocker ground pivot (D).
- Returns:
Transmission angle in degrees (0-180).
- pylinkage.linkage.transmission.stroke_at_position(linkage: Linkage, prismatic_joint: object | None = None) float
Compute the slide position of a prismatic joint at current position.
- Args:
linkage: The linkage to analyze. prismatic_joint: The Prismatic joint to analyze. Auto-detected if None.
- Returns:
Slide position along the prismatic axis.
- Raises:
ValueError: If joint cannot be determined or positions are invalid.
- pylinkage.linkage.transmission.transmission_angle_at_position(linkage: Linkage, coupler_joint: object | None = None, output_joint: object | None = None, rocker_pivot: object | None = None) float
Compute transmission angle at the current linkage position.
- Args:
linkage: The linkage to analyze. coupler_joint: Joint at coupler input (B). Auto-detected if None. output_joint: Joint where angle is measured (C). Auto-detected if None. rocker_pivot: Ground pivot of rocker (D). Auto-detected if None.
- Returns:
Transmission angle in degrees.
- Raises:
ValueError: If joints cannot be determined or positions are invalid.
Module contents
Linkage analysis utilities (trajectory extraction, transmission, sensitivity, tolerance).
The legacy Linkage class that previously lived here has been
removed. Use pylinkage.simulation.Linkage for the modern
component/actuator/dyad API or pylinkage.mechanism.Mechanism
for the links-and-joints model. The analysis helpers exported here
work with both containers via pylinkage._compat.
- class pylinkage.linkage.SensitivityAnalysis(sensitivities: dict[str, float], baseline_path_metric: float, baseline_transmission: float | None, perturbed_path_metrics: ndarray[tuple[Any, ...], dtype[float64]], perturbed_transmission: ndarray[tuple[Any, ...], dtype[float64]] | None, constraint_names: tuple[str, ...], perturbation_delta: float)
Bases:
objectResults of sensitivity analysis for a linkage.
Sensitivity measures how much the output path changes when each constraint dimension is perturbed by a small amount.
- Attributes:
- sensitivities: Mapping from constraint name to sensitivity coefficient.
Higher values indicate the output is more sensitive to that constraint.
baseline_path_metric: Mean path deviation at nominal constraints (should be 0). baseline_transmission: Mean transmission angle at nominal (degrees), or None. perturbed_path_metrics: Array of path deviation metrics, one per constraint. perturbed_transmission: Array of transmission angles per constraint, or None. constraint_names: Tuple of constraint names in order. perturbation_delta: Relative perturbation magnitude used (e.g., 0.01 for 1%).
- baseline_path_metric: float
- baseline_transmission: float | None
- constraint_names: tuple[str, ...]
- property most_sensitive: str
Return name of the most sensitive constraint.
- perturbation_delta: float
- perturbed_path_metrics: ndarray[tuple[Any, ...], dtype[float64]]
- perturbed_transmission: ndarray[tuple[Any, ...], dtype[float64]] | None
- sensitivities: dict[str, float]
- property sensitivity_ranking: list[tuple[str, float]]
Return constraints ranked by sensitivity (highest first).
- to_dataframe() object
Export results as pandas DataFrame.
- Returns:
DataFrame with columns: constraint, sensitivity, perturbed_metric
- Raises:
ImportError: If pandas is not installed.
- class pylinkage.linkage.StrokeAnalysis(min_position: float, max_position: float, mean_position: float, stroke_range: float, positions: ndarray[tuple[Any, ...], dtype[float64]], min_position_step: int, max_position_step: int)
Bases:
objectResults of stroke analysis for a prismatic joint over a motion cycle.
The stroke is the position of the slider along its axis, measured as the signed distance from the first line point (joint1) projected onto the slide axis.
- Attributes:
min_position: Minimum slide position along the axis. max_position: Maximum slide position along the axis. mean_position: Mean slide position. stroke_range: Total travel distance (max - min). positions: Array of slide positions at each step. min_position_step: Step index where minimum position occurs. max_position_step: Step index where maximum position occurs.
- property amplitude: float
Return half the stroke range (useful for oscillating mechanisms).
- property center_position: float
Return the center of the stroke range.
- max_position: float
- max_position_step: int
- mean_position: float
- min_position: float
- min_position_step: int
- positions: ndarray[tuple[Any, ...], dtype[float64]]
- stroke_range: float
- class pylinkage.linkage.ToleranceAnalysis(nominal_path: ndarray[tuple[Any, ...], dtype[float64]], output_cloud: ndarray[tuple[Any, ...], dtype[float64]], tolerances: dict[str, float], mean_deviation: float, max_deviation: float, std_deviation: float, position_std: ndarray[tuple[Any, ...], dtype[float64]])
Bases:
objectResults of Monte Carlo tolerance analysis.
Tolerance analysis simulates how manufacturing variations in link dimensions affect the output path. Each sample represents a possible manufactured linkage within tolerance bounds.
- Attributes:
nominal_path: Output path at nominal dimensions, shape (n_steps, 2). output_cloud: Monte Carlo results, shape (n_samples, n_steps, 2). tolerances: Dictionary mapping constraint names to their tolerances. mean_deviation: Mean path deviation from nominal across all samples. max_deviation: Maximum path deviation from nominal (worst case). std_deviation: Standard deviation of path deviations. position_std: Per-position standard deviation, shape (n_steps,).
- max_deviation: float
- mean_deviation: float
- nominal_path: ndarray[tuple[Any, ...], dtype[float64]]
- output_cloud: ndarray[tuple[Any, ...], dtype[float64]]
- plot_cloud(ax: Axes | None = None, show_nominal: bool = True, alpha: float = 0.1) Axes
Plot the tolerance cloud as a scatter plot.
- Args:
ax: Matplotlib axes to plot on. Creates new figure if None. show_nominal: Whether to show the nominal path as a solid line. alpha: Transparency for sample points (0-1).
- Returns:
Matplotlib axes with the plot.
- position_std: ndarray[tuple[Any, ...], dtype[float64]]
- std_deviation: float
- to_dataframe() object
Export statistics as pandas DataFrame.
- Returns:
DataFrame with tolerance statistics.
- Raises:
ImportError: If pandas is not installed.
- tolerances: dict[str, float]
- class pylinkage.linkage.TransmissionAngleAnalysis(min_angle: float, max_angle: float, mean_angle: float, angles: ndarray[tuple[Any, ...], dtype[float64]], is_acceptable: bool, min_deviation: float, max_deviation: float, min_angle_step: int, max_angle_step: int)
Bases:
objectResults of transmission angle analysis over a motion cycle.
- Attributes:
min_angle: Minimum transmission angle in degrees. max_angle: Maximum transmission angle in degrees. mean_angle: Mean transmission angle in degrees. angles: Array of transmission angles at each step (degrees). is_acceptable: True if angle always in acceptable range. min_deviation: Minimum deviation from 90 degrees. max_deviation: Maximum deviation from 90 degrees. min_angle_step: Step index where minimum angle occurs. max_angle_step: Step index where maximum angle occurs.
- property acceptable_range: tuple[float, float]
Return the standard acceptable range [40, 140] degrees.
- angles: ndarray[tuple[Any, ...], dtype[float64]]
- is_acceptable: bool
- max_angle: float
- max_angle_step: int
- max_deviation: float
- mean_angle: float
- min_angle: float
- min_angle_step: int
- min_deviation: float
- plot(ax: Axes | None = None, show_limits: bool = True, show_optimum: bool = True, title: str | None = 'Transmission Angle over Full Cycle') Axes
Plot transmission angle versus crank angle over one full cycle.
Crank angle is plotted in degrees on the x-axis (one revolution = 360°). The acceptable-range limits and the 90° optimum are marked with horizontal reference lines, and the y-axis is fixed to
[0, 180]so plots are comparable across mechanisms.- Parameters:
ax – Axes to draw on. A new figure is created if
None.show_limits – Draw red dashed lines at the acceptable-range bounds (default
[40°, 140°]).show_optimum – Draw a green dotted line at 90° (the transmission-angle optimum).
title – Plot title. Pass
Noneto omit.
- Returns:
The Matplotlib axes the plot was drawn on.
- worst_angle() float
Return the angle with maximum deviation from 90 degrees.
- pylinkage.linkage.analyze_sensitivity(linkage: Linkage, output_joint: object | int | None = None, delta: float = 0.01, include_transmission: bool = True, iterations: int | None = None) SensitivityAnalysis
Analyze sensitivity of output path to each constraint dimension.
For each constraint, this function: 1. Perturbs the constraint by delta (relative) 2. Simulates the linkage 3. Measures the path deviation from nominal 4. Computes sensitivity coefficient
- Args:
linkage: The linkage to analyze. output_joint: Joint to measure, index, or None (auto-detect last joint). delta: Relative perturbation magnitude (e.g., 0.01 for 1%). include_transmission: Whether to also measure transmission angle sensitivity. iterations: Number of simulation steps. Defaults to one full rotation.
- Returns:
SensitivityAnalysis with sensitivity coefficients and statistics.
- Example:
>>> analysis = linkage.sensitivity_analysis(delta=0.01) >>> print(analysis.most_sensitive) >>> for name, sens in analysis.sensitivity_ranking: ... print(f"{name}: {sens:.4f}")
- pylinkage.linkage.analyze_stroke(linkage: Linkage, prismatic_joint: object | None = None, iterations: int | None = None) StrokeAnalysis
Analyze stroke/slide position over a full motion cycle.
For a linkage with a Prismatic joint, this tracks the slide position along the prismatic axis throughout the motion cycle.
- Args:
linkage: The Linkage to analyze. prismatic_joint: The Prismatic joint to analyze. Auto-detected if None. iterations: Number of simulation steps. Defaults to one full rotation.
- Returns:
StrokeAnalysis with statistics over the motion cycle.
- Raises:
ValueError: If joint cannot be detected or no valid positions found.
- pylinkage.linkage.analyze_tolerance(linkage: Linkage, tolerances: dict[str, float], output_joint: object | int | None = None, iterations: int | None = None, n_samples: int = 1000, seed: int | None = None) ToleranceAnalysis
Analyze manufacturing tolerance effects via Monte Carlo simulation.
For each sample: 1. Randomly perturb constraints within tolerance bounds 2. Simulate the linkage 3. Record the output path
- Args:
linkage: The linkage to analyze. tolerances: Dictionary mapping constraint names to tolerance values.
Each constraint is perturbed by +/- tolerance uniformly.
output_joint: Joint to measure, index, or None (auto-detect last joint). iterations: Number of simulation steps. Defaults to one full rotation. n_samples: Number of Monte Carlo samples to run. seed: Random seed for reproducibility.
- Returns:
ToleranceAnalysis with statistics and the sample cloud.
- Example:
>>> tolerances = { ... "Crank_radius": 0.1, # +/- 0.1 mm ... "Revolute_dist1": 0.2, # +/- 0.2 mm ... } >>> analysis = linkage.tolerance_analysis(tolerances, n_samples=500) >>> print(f"Max deviation: {analysis.max_deviation:.4f}") >>> analysis.plot_cloud()
- pylinkage.linkage.analyze_transmission(linkage: Linkage, iterations: int | None = None, acceptable_range: tuple[float, float] = (40.0, 140.0)) TransmissionAngleAnalysis
Analyze transmission angle over a full motion cycle.
For a standard four-bar linkage, joints are auto-detected: - coupler_joint: The Crank joint (B) - output_joint: The Revolute joint (C) - rocker_pivot: The Static joint at Revolute.joint1 (D)
- Args:
linkage: The Linkage to analyze. iterations: Number of simulation steps. Defaults to one full rotation. acceptable_range: (min, max) acceptable angles in degrees.
- Returns:
TransmissionAngleAnalysis with statistics over the motion cycle.
- Raises:
ValueError: If joints cannot be detected or no valid positions found.
- pylinkage.linkage.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.linkage.compute_slide_position(slider_pos: tuple[float, float], line_point1: tuple[float, float], line_point2: tuple[float, float]) float
Compute the slide position along a prismatic axis.
The position is the signed distance from line_point1 to the projection of slider_pos onto the line, measured in the direction of line_point2.
- Args:
slider_pos: Current position of the slider joint. line_point1: First point defining the slide axis (origin). line_point2: Second point defining the slide axis (direction).
- Returns:
Signed distance along the axis from line_point1.
- pylinkage.linkage.compute_transmission_angle(coupler_joint: tuple[float, float], output_joint: tuple[float, float], rocker_pivot: tuple[float, float]) float
Compute transmission angle in degrees.
The transmission angle is the angle between: - Coupler link: from coupler_joint (B) to output_joint (C) - Rocker link: from rocker_pivot (D) to output_joint (C)
- Args:
coupler_joint: Position of the coupler input joint (B). output_joint: Position where angle is measured (C). rocker_pivot: Position of the rocker ground pivot (D).
- Returns:
Transmission angle in degrees (0-180).
- pylinkage.linkage.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.linkage.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.linkage.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.