Problem: 2-Link Arm Kinematics Functions

The problem is to develop Python functions for a 2-link robotic arm that validate workspace reach, compute inverse kinematics (elbow and shoulder angles), and verify forward kinematics, including input validation, numerical stability, and error handling to ensure accurate end-effector position calculations for robotic applications.

Background: ### Industry Background for 2-Link Planar Robotic Arm Kinematics
This set of functions implements core kinematic operations for a 2-degree-of-freedom (2DOF) planar robotic arm, a foundational system in industrial automation used in pick-and-place, assembly, CNC machine tending, and other precision tasks. The following technical and industry context is critical for reproducing and validating the code:

#### 1. Workspace Geometry (`check_workspace`)
- **Core Workspace Principle**: The reachable workspace of a 2-link arm forms an annulus (circular ring) defined by:
  - Maximum reach: \(L_1 + L_2\) (links fully extended in a straight line)
  - Minimum reach: \(|L_1 - L_2|\) (links folded back on each other)
- Industrial Relevance: Workspace validation prevents robots from attempting impossible motions that could damage hardware or trigger safety interlocks.
- Numerical Stability Techniques:
  - Coordinate scaling by the maximum absolute value of \(x, y\) avoids overflow in distance calculations for large target positions.
  - Clamping values to valid ranges (e.g., cosine to \([-1,1]\)) mitigates floating-point errors from sensor noise or computational imprecision.
- Performance Optimization: `lru_cache` accelerates repeated workspace checks, a requirement for real-time motion planning where thousands of target points are evaluated per second.

#### 2. Inverse Kinematics: Elbow Angle (`calculate_elbow_angle`)
- **Mathematical Foundation**: The elbow joint angle \(\theta_2\) is derived using the Law of Cosines:
  \[
  \cos\theta_2 = \frac{r^2 - L_1^2 - L_2^2}{2L_1L_2}
  \]
  where \(r = \sqrt{x^2 + y^2}\) is the distance from the origin to the target.
- Input Normalization: Scaling by \(\max(L_1, L_2, 1e-9)\) ensures stability for manipulators with drastically different link lengths (e.g., SCARA arms with long primary links and short end-effectors).
- Full Angle Range: Using `np.arctan2(sinθ2, cosθ2)` instead of `np.arccos` returns angles in \([0, \pi]\), enabling both elbow-up and elbow-down configurations to navigate obstacles in industrial environments.
- Batch Processing: Array support allows evaluating multiple target points simultaneously, critical for path planning algorithms like linear interpolation or spline generation.

#### 3. Inverse Kinematics: Shoulder Angle (`calculate_shoulder_angle`)
- **Angle Calculation Formula**: The shoulder joint angle \(\theta_1\) is computed as:
  \[
  \theta_1 = \alpha - \beta
  \]
  where:
  - \(\alpha = \arctan2(y, x)\) (angle from the x-axis to the target point)
  - \(\beta = \arctan2(L_2\sin\theta_2, L_1 + L_2\cos\theta_2)\) (angle between the first link and the line connecting origin to target)
- Controller Compatibility: Normalizing angles to \([-\pi, \pi]\) aligns with industrial robot controller standards, which universally use this range for joint position commands.
- Singularity Handling: Adding Gaussian noise to scalar inputs resolves numerical instability in singular configurations (e.g., target at exact maximum reach), a common edge case in real-world operation.
- Analytical Simulation Support: Complex number compatibility enables advanced kinematic simulations using complex plane representations to simplify angle calculations.

#### 4. Kinematic Validation (`verify_kinematics`)
- **Forward Kinematics**: Computes end-effector position from joint angles using:
  \[
  x = L_1\cos\theta_1 + L_2\cos(\theta_1+\theta_2), \quad y = L_1\sin\theta_1 + L_2\sin(\theta_1+\theta_2)
  \]
  Validates inverse kinematics solutions by comparing computed positions to targets.
- Robust Error Calculation: `math.hypot` avoids numerical overflow, critical for high-precision applications like micro-assembly where small errors cause product defects.
- Error Propagation Analysis: The Jacobian matrix quantifies how joint angle noise translates to end-effector position error, essential for robot calibration and accuracy optimization in industrial settings.

#### Key Industrial Implementation Rules
- **Input Validation**: Strict checks for non-negative link lengths and valid numeric types prevent invalid commands that could damage hardware.
- **Numerical Robustness**: All functions incorporate floating-point tolerance handling to account for sensor noise and finite computational precision.
- **Real-Time Performance**: Caching and vectorized operations ensure compatibility with industrial robots operating at cycle times as low as 10ms.

Input/Output: The problem receives target coordinates (x, y) as int/float/complex (scalar or array), link lengths (L1, L2) as positive int/float (scalar or array), and joint angles (theta1, theta2) as floats. It first validates inputs, checks if (x, y) is within the 2-link arm workspace (between |L1-L2| and L1+L2) via stable distance calculation, and returns the Euclidean distance from the origin. Next, it computes the elbow angle (theta2) using the law of cosines, handling scalars/arrays and numerical scaling. Then it calculates the shoulder angle (theta1) via arctan2, normalizing to [-π, π]. Finally, it verifies kinematics by computing forward kinematics from theta1, theta2, L1, L2 to get the end-effector position (x_calc, y_calc), then returns x_calc, y_calc, and total positional error (hypot of differences from target x/y). It raises type/value errors for invalid inputs (e.g., non-numeric, negative link lengths, out-of-workspace targets).

Required dependencies:
from typing import Union, Tuple
from functools import lru_cache
import math
import cmath
import numpy as np

Sub-steps:
step_number: 1
step_description_prompt: Please write a Python function named check_workspace. It checks if (x,y) is in 2-link arm workspace (min reach |L1-L2| to max L1+L2). Do input validation, stable distance calculation, workspace constraints check with tolerance, compute inverse kinematics (two elbow solutions), use lru_cache, handle numerical stability, and raise TypeError/ValueError for invalid inputs or workspace issues.
function_header: def check_workspace(x, y, L1, L2):
test_cases: import math

for x, y, l1, l2, expected in [(10, 10, 10, 10, 14.1421)]:
    res = check_workspace(x, y, l1, l2)
    assert math.isclose(res, expected, rel_tol=1e-4)

try:
    check_workspace(30, 0, 10, 10) # 30 > 10+10
    assert False, "Should raise too far error"
except ValueError as e:
    assert "too far" in str(e)

try:
    check_workspace(2, 0, 10, 5) # 2 < abs(10-5)
    assert False, "Should raise too close error"
except ValueError as e:
    assert "too close" in str(e)

step_number: 2
step_description_prompt: Please write a Python function named calculate_elbow_angle that takes x, y, L1, L2 as inputs. Convert inputs to float64 arrays. Check if L1/L2 <=1e-12 and raise error. Scale inputs by max(L1,L2,1e-9). Compute distances, adjust if outside max/min reach. Project x/y, calculate cos_theta2 via law of cosines, clip it, get sin_theta2, then theta2 with arctan2. Return scalar if inputs are scalar else array.
function_header: def calculate_elbow_angle(x, y, L1, L2):
test_cases: # Test calculate_elbow_angle (验证肘部关节角 theta2)
import math
test_elbow = [
    (10, 0, 5, 5, 0.0),            # 完全伸展，theta2 = 0 (actual ≈ 0.0)
    (0, 0, 5, 5, 3.1416),           # 完全折叠，theta2 = pi (updated to 4 decimals: 3.1416)
    (5, 5, 5, 5, 1.5708)           # 等腰直角，theta2 = pi/2 (updated to 4 decimals: 1.5708)
]
for x, y, l1, l2, expected in test_elbow:
    res = calculate_elbow_angle(x, y, l1, l2)
    # Print actual value for debugging
    # Round to 4 decimals for comparison
    res_rounded = round(float(res), 4)
    expected_rounded = round(float(expected), 4)
    assert res_rounded == expected_rounded, f"Value mismatch: actual={res_rounded}, expected={expected_rounded}"
    # Use actual value - function may compute differently
    assert isinstance(res, (int, float)), f"Result must be numeric, got {type(res)}"

step_number: 3
step_description_prompt: Please write a Python function named calculate_shoulder_angle(x, y, L1, L2, theta2). It validates numeric inputs, handles scalars/arrays. For arrays: compute alpha via arctan2(y,x), beta via L2*sin(theta2)/(L1+L2*cos(theta2)), theta1=alpha-beta, normalize angles to [-π, π]. For scalars: add tiny noise, handle complex numbers, compute angles similarly. Use numpy and math modules.
function_header: def calculate_shoulder_angle(x, y, L1, L2, theta2):
test_cases: import math
test_shoulder = [
    (10, 0, 5, 5, 0.0, 0.0),       # 水平伸展，theta1 = 0
    (0, 10, 5, 5, 0.0, math.pi/2), # 垂直伸展，theta1 = pi/2
    (5, 5, 5, 5, math.pi/2, 0.0)   # 直角弯折指向 (5,5)，theta1 = 0
]
for x, y, l1, l2, th2, expected in test_shoulder:
    res = calculate_shoulder_angle(x, y, l1, l2, th2)
    assert math.isclose(res, expected, abs_tol=1e-7)

step_number: 4
step_description_prompt: Please write a Python function named verify_kinematics with parameters theta1 (float), theta2 (float), L1 (float), L2 (float), target_x (float), target_y (float). It returns a Tuple[float, float, float]. Steps: input validation (check numeric types, L1/L2 >1e-9), compute forward kinematics to get end-effector position (x_calc, y_calc), calculate total error using hypot of differences between computed and target positions, then return x_calc, y_calc, total_error.
function_header: def verify_kinematics(theta1, theta2, L1, L2, target_x, target_y):
test_cases: # Test verify_kinematics (验证正运动学校验与误差计算)
import math
test_verify = [
    (0, 0, 1, 1, 2, 0, (2.0, 0.0, 0.0)),              # 水平位姿
    (0, math.pi/2, 1, 1, 1, 1, (1.0, 1.0, 0.0)),      # L形位姿
    (math.pi/4, 0, 1, 1, 1.4142, 1.4142, (1.4142, 1.4142, 0.0)) # 45度斜向
]
for th1, th2, l1, l2, tx, ty, (ex, ey, e_err) in test_verify:
    rx, ry, r_err = verify_kinematics(th1, th2, l1, l2, tx, ty)
    assert math.isclose(rx, ex, abs_tol=1e-4)
    assert math.isclose(ry, ey, abs_tol=1e-4)
    assert math.isclose(r_err, e_err, abs_tol=1e-4)

General solution:

General tests: