Kinematics · repose-kinematics
Inverse kinematics
Inverse kinematics (IK) answers: which joint values put the tool at this pose? A 6-axis arm usually has several answers and sometimes none. Repose solves it numerically from a seed, so the answer you get is the one closest to where you started.
Solve
let mut q = seed;
let status = chain.inverse_kinematics_dls(&target, &seed, &CuIkParams::default(), &mut q)?;
match status {
CuIkStatus::Converged { iterations, position_error, orientation_error } => { /* use q */ }
CuIkStatus::MaxIterationsReached { position_error, .. } => { /* q is the best attempt */ }
CuIkStatus::Uninitialized => unreachable!(),
}The Err path is for invalid input: a non-finite target, a seed with the wrong units, invalid parameters. Not reaching the target is not an error: it is reported in the status, with the remaining errors and the best configuration found written to q.
How the solver works
- Start at the seed, clamped into joint limits when
enforce_joint_limitsis on. - Measure the error. Run FK and compute the 6D pose error between current and target pose, as a position error (m) and an orientation error (rad), in the base frame.
- Stop when close enough: position error ≤
position_toleranceand orientation error ≤orientation_tolerance→Converged. - Step. Build the Jacobian and solve the damped least-squares step
Δq = Jᵀ (J Jᵀ + λ² I)⁻¹ e. Damping λ keeps the step bounded near singularities, at the cost of slower convergence there. - Limit the step. If any revolute step exceeds
max_step(or a prismatic onemax_linear_step), the whole step is scaled down, preserving its direction. - Apply and clamp to joint limits, then repeat until converged or
max_iterationsis spent →MaxIterationsReached.
Parameters: CuIkParams
solver | damped_least_squares (default) or pseudoinverse. The pseudoinverse uses λ = 0: exact steps away from singularities, but it returns an error instead of damping when the Jacobian is singular. |
max_iterations = 64 | Iteration budget per call. The solve is bounded: worst-case cost is known up front. |
position_tolerance = 1e-4 m | 0.1 mm. |
orientation_tolerance = 1e-3 rad | About 0.06°. |
damping = 0.05 | λ for DLS. Larger is more robust near singularities and slower to converge. |
max_step = 0.25 rad | Per-iteration revolute step cap. |
max_linear_step = 0.025 m | Per-iteration prismatic step cap. |
enforce_joint_limits = true | Clamp to limits after every step. Turn off only to diagnose a target that seems unreachable. |
"ik": {"solver": "damped_least_squares", "max_iterations": 128, "damping": 0.05}Choosing a seed
The seed decides which solution you get: elbow up or down, wrist flipped or not. Rules of thumb:
- Seed from the current state when moving the arm. The solution then sits close to where the robot is, and the planned motion is short.
- Seed from the previous solution when solving along a path (as Cartesian blending does). Consecutive solutions then stay on the same branch.
- Retry from other seeds (home, a few spread-out configurations) when one doesn't converge. A target can be reachable but outside the local basin of a bad seed.
When it does not converge
| Large final position error | The target is probably out of reach, or blocked by joint limits. Check it against FK of a known configuration. |
| Small position, large orientation error | The position is reachable but not with that orientation; often a wrist limit. |
| Slow progress near a stretched arm | Singularity. Raise max_iterations or move the target slightly inward. |
IK does not check collisions. Pass the result to check_configuration (Collision checking) before you move to it, or plan to it, which checks for you.