Sensitivity & Tolerance Analysis
This tutorial covers how to analyze how manufacturing variations affect linkage behavior. Understanding sensitivity helps identify critical dimensions and assess whether a design is robust to manufacturing tolerances.
Overview
Pylinkage provides two complementary analysis tools:
Sensitivity Analysis: Measures how much each constraint dimension affects the output path. Identifies which dimensions are most critical.
Tolerance Analysis: Monte Carlo simulation that shows the statistical variation in output path given manufacturing tolerances.
These tools help answer questions like:
Which link length is most critical to the output path accuracy?
How much output variation should I expect with ±0.1mm tolerances?
Is my design robust enough for the manufacturing process?
Basic Setup
First, create a linkage to analyze:
import pylinkage as pl
# Create a four-bar linkage
crank = pl.Crank(
x=1, y=0,
joint0=(0, 0),
angle=0.1,
distance=1.0,
name="crank"
)
coupler = pl.Revolute(
x=3, y=1,
joint0=crank,
joint1=(4, 0),
distance0=3.0,
distance1=2.0,
name="coupler"
)
linkage = pl.Linkage(
joints=(crank, coupler),
order=(crank, coupler),
)
Sensitivity Analysis
Basic Usage
Sensitivity analysis measures how each constraint dimension affects the output:
# Run sensitivity analysis with 1% perturbation
analysis = linkage.sensitivity_analysis(delta=0.01)
# View the most sensitive constraint
print(f"Most sensitive: {analysis.most_sensitive}")
# View all constraints ranked by sensitivity
for name, sensitivity in analysis.sensitivity_ranking:
print(f" {name}: {sensitivity:.4f}")
Output:
Most sensitive: coupler_dist1
coupler_dist1: 0.0312
coupler_dist2: 0.0287
crank_radius: 0.0156
Understanding Constraint Names
Constraint names are auto-generated based on joint type and name:
crank_radius: Distance from crank pivot to crank endcoupler_dist1: Distance from first anchor to coupler jointcoupler_dist2: Distance from second anchor to coupler joint
The naming follows the pattern {joint_name}_{constraint_type}.
Analyzing Specific Output Joints
By default, the last joint is analyzed. You can specify a different output:
# Analyze sensitivity for the crank output
analysis = linkage.sensitivity_analysis(output_joint=0)
# Or by joint object
analysis = linkage.sensitivity_analysis(output_joint=crank)
Including Transmission Angle
For four-bar linkages, you can also track transmission angle sensitivity:
analysis = linkage.sensitivity_analysis(
delta=0.01,
include_transmission=True
)
print(f"Baseline transmission: {analysis.baseline_transmission:.1f}°")
# Transmission angles for each perturbation
if analysis.perturbed_transmission is not None:
for name, trans in zip(analysis.constraint_names, analysis.perturbed_transmission):
print(f" {name}: {trans:.1f}°")
Exporting to DataFrame
For detailed analysis, export to a pandas DataFrame:
# Requires: pip install pylinkage[analysis]
df = analysis.to_dataframe()
print(df)
Output:
constraint sensitivity perturbed_metric perturbed_transmission
0 crank_radius 0.0156 0.00156 89.5
1 coupler_dist1 0.0312 0.00312 90.2
2 coupler_dist2 0.0287 0.00287 89.8
Tolerance Analysis
Basic Usage
Tolerance analysis uses Monte Carlo simulation to assess manufacturing variability:
# Define tolerances for each constraint
tolerances = {
"crank_radius": 0.1, # +/- 0.1 mm
"coupler_dist1": 0.2, # +/- 0.2 mm
"coupler_dist2": 0.2, # +/- 0.2 mm
}
# Run Monte Carlo analysis
result = linkage.tolerance_analysis(
tolerances=tolerances,
n_samples=1000,
seed=42 # For reproducibility
)
# View statistics
print(f"Mean deviation: {result.mean_deviation:.4f}")
print(f"Max deviation: {result.max_deviation:.4f}")
print(f"Std deviation: {result.std_deviation:.4f}")
Understanding the Results
The ToleranceAnalysis result contains:
nominal_path: Output path at nominal dimensions (n_steps, 2)output_cloud: All Monte Carlo samples (n_samples, n_steps, 2)mean_deviation: Average distance from nominal pathmax_deviation: Worst-case deviationstd_deviation: Standard deviation of deviationsposition_std: Per-position standard deviation (n_steps,)
Visualizing the Tolerance Cloud
Use plot_cloud() to visualize the output variation:
import matplotlib.pyplot as plt
# Create scatter plot of output paths
ax = result.plot_cloud(
show_nominal=True, # Show nominal path as red line
alpha=0.1 # Transparency for sample points
)
plt.title("Output Path Tolerance Cloud")
plt.savefig("tolerance_cloud.png", dpi=150)
plt.show()
This creates a scatter plot showing:
Blue dots: Individual sample output paths
Red line: Nominal (ideal) output path
The spread indicates manufacturing variation
Selective Tolerance Analysis
You can analyze tolerance for specific constraints:
# Only analyze crank radius tolerance
result = linkage.tolerance_analysis(
tolerances={"crank_radius": 0.1},
n_samples=500
)
print(f"Crank-only max deviation: {result.max_deviation:.4f}")
Use in Optimization
Sensitivity as Fitness Penalty
Design linkages that are insensitive to manufacturing variation:
@pl.kinematic_minimization
def robust_linkage(loci, linkage=None, **kwargs):
"""Optimize for path shape while minimizing sensitivity."""
# Path shape objective (e.g., bounding box)
output_path = [step[-1] for step in loci]
bbox = pl.bounding_box(output_path)
path_error = compute_path_error(bbox)
# Sensitivity penalty
analysis = linkage.sensitivity_analysis(delta=0.01)
max_sensitivity = max(analysis.sensitivities.values())
# Combined objective: good path + low sensitivity
return path_error + 10.0 * max_sensitivity
Tolerance-Based Constraints
Reject designs that exceed tolerance requirements:
from pylinkage.exceptions import UnbuildableError
@pl.kinematic_minimization
def tolerance_constrained(loci, linkage=None, **kwargs):
"""Optimize path, rejecting designs with excessive variation."""
# Check tolerance
tolerances = {"crank_radius": 0.1, "coupler_dist1": 0.2, "coupler_dist2": 0.2}
result = linkage.tolerance_analysis(tolerances, n_samples=100)
if result.max_deviation > 0.5: # Reject if max deviation > 0.5mm
raise UnbuildableError("Excessive tolerance variation")
# Path objective
return compute_path_score(loci)
Practical Guidelines
Perturbation Size
The delta parameter controls the relative perturbation size:
delta=0.01: 1% perturbation (recommended default)delta=0.001: 0.1% for fine sensitivity analysisdelta=0.1: 10% for coarse/fast analysis
Smaller perturbations give more accurate local sensitivity but may be affected by numerical noise.
Sample Count
For tolerance analysis, the number of samples affects accuracy:
n_samples=100: Quick estimate (useful in optimization loops)n_samples=1000: Good accuracy for design validationn_samples=10000: High accuracy for final verification
Typical Workflow
Design: Create linkage meeting path requirements
Sensitivity: Run
sensitivity_analysis()to identify critical dimensionsFocus: Tighten tolerances on most sensitive constraints
Validate: Run
tolerance_analysis()to verify acceptable variationIterate: If variation is too high, modify design and repeat
Example Complete Workflow
import pylinkage as pl
import matplotlib.pyplot as plt
# Create linkage
crank = pl.Crank(1, 0, joint0=(0, 0), angle=0.1, distance=1.0, name="crank")
coupler = pl.Revolute(3, 1, joint0=crank, joint1=(4, 0),
distance0=3.0, distance1=2.0, name="coupler")
linkage = pl.Linkage(joints=(crank, coupler), order=(crank, coupler))
# Step 1: Sensitivity analysis
print("=== Sensitivity Analysis ===")
sens = linkage.sensitivity_analysis(delta=0.01)
print(f"Most sensitive: {sens.most_sensitive}")
for name, val in sens.sensitivity_ranking:
print(f" {name}: {val:.4f}")
# Step 2: Tolerance analysis with realistic tolerances
print("\n=== Tolerance Analysis ===")
tolerances = {
"crank_radius": 0.05, # Tight tolerance (sensitive)
"coupler_dist1": 0.1, # Normal tolerance
"coupler_dist2": 0.1, # Normal tolerance
}
tol = linkage.tolerance_analysis(tolerances, n_samples=500, seed=42)
print(f"Mean deviation: {tol.mean_deviation:.4f}")
print(f"Max deviation: {tol.max_deviation:.4f}")
print(f"Std deviation: {tol.std_deviation:.4f}")
# Step 3: Visualize
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
# Plot tolerance cloud
tol.plot_cloud(ax=ax1)
ax1.set_title("Tolerance Cloud")
# Plot per-position std
ax2.plot(tol.position_std)
ax2.set_xlabel("Simulation Step")
ax2.set_ylabel("Position Std Dev")
ax2.set_title("Per-Position Variation")
ax2.grid(True)
plt.tight_layout()
plt.savefig("tolerance_analysis.png", dpi=150)
plt.show()
API Reference
pylinkage.linkage.SensitivityAnalysis- Sensitivity analysis resultspylinkage.linkage.ToleranceAnalysis- Tolerance analysis resultspylinkage.linkage.analyze_sensitivity()- Sensitivity analysis functionpylinkage.linkage.analyze_tolerance()- Tolerance analysis functionpylinkage.linkage.Linkage.sensitivity_analysis()- Convenience methodpylinkage.linkage.Linkage.tolerance_analysis()- Convenience method
Next Steps
See Advanced Optimization Techniques for optimization techniques
See Kinematics-Based Optimization for velocity/acceleration analysis
Explore
pylinkage.linkagefor the complete linkage API