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

rust
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

  1. Start at the seed, clamped into joint limits when enforce_joint_limits is on.
  2. 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.
  3. Stop when close enough: position error ≤ position_tolerance and orientation error ≤ orientation_tolerance → Converged.
  4. 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.
  5. Limit the step. If any revolute step exceeds max_step (or a prismatic one max_linear_step), the whole step is scaled down, preserving its direction.
  6. Apply and clamp to joint limits, then repeat until converged or max_iterations is spent → MaxIterationsReached.

Parameters: CuIkParams

solverdamped_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 = 64Iteration budget per call. The solve is bounded: worst-case cost is known up front.
position_tolerance = 1e-4 m0.1 mm.
orientation_tolerance = 1e-3 radAbout 0.06°.
damping = 0.05λ for DLS. Larger is more robust near singularities and slower to converge.
max_step = 0.25 radPer-iteration revolute step cap.
max_linear_step = 0.025 mPer-iteration prismatic step cap.
enforce_joint_limits = trueClamp to limits after every step. Turn off only to diagnose a target that seems unreachable.
every field optional
"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 errorThe target is probably out of reach, or blocked by joint limits. Check it against FK of a known configuration.
Small position, large orientation errorThe position is reachable but not with that orientation; often a wrist limit.
Slow progress near a stretched armSingularity. 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.

API reference