deke-linear
Constant-TCP-speed Cartesian polyline following for serial manipulators.
Where the deke-topp* retimers are time-optimal (maximise speed under v/a/j
caps), deke-linear holds a constant TCP travel speed — the requirement for
welding and similar process motions — and degrades gracefully near singularities
rather than failing. It is a CNC-style constant-feedrate interpolator in three
stages, the latter two of which implement the deke_types Planner / Retimer
traits so they compose with the rest of the ecosystem.
Pipeline
&[DAffine3] poses + FollowConfig
│
[A] path::condition → Vec<CartesianRun> split at sharp corners; shallow
│ corners smoothed (squiggle Catmull–Rom),
│ arc-length parameterised
[B] CartesianLinearPlanner : Planner dense IK → DP branch track
│ (analytic IK, manipulability-weighted,
│ no Jacobian inversion → singularity-safe)
[C] ConstantSpeedRetimer : Retimer constant feedrate held where the joint
│ v/a/j MVC allows; smooth dips elsewhere
LinearFollower::follow → SRobotTraj per-run trajectories stitched at rest
- Corners (hybrid). A vertex whose turn angle exceeds
PathConditioning::sharp_corner_angleis sharp: the path splits there into runs that start and stop at rest, keeping the vertex exact. Shallower corners are smoothed by the Catmull–Rom spline and traversed without stopping. forbid_interior_dips. By default the speed dips smoothly where the joint v/a/j geometry forces it (a shallow corner, a near-singular patch). Set this flag to require the commanded speed to hold flat through a run's interior — the only sub-commanded speed allowed is the rest ramp at each run's start and end. If a dip would be forced anywhere in the interior, the retime fails withSpeedDipRequired { run, s, feasible_speed, commanded }instead of slowing down.- Singularity tolerance. The planner inverts each pose with the chain's analytic
IK (every branch, limit-filtered, no Jacobian inversion) and routes a continuous
joint track that maximises manipulability
√det(J·Jᵀ). The retimer's feasible-speed ceiling ismin_j v_max,j / |q'_j(s)|, so as a singularity is approached the commanded speed dips smoothly to zero instead of demanding infinite joint speed.
Example
use ;
use DAffine3;
let follower = new; // any ContinuousFKChain + IkSolver
let = follower.follow?; // poses: &[DAffine3], cfg: FollowConfig<N>
Free tool-axis yaw (functional redundancy)
A tool that is rotationally symmetric about one of its axes — a welding torch, spray head — leaves the rotation about that axis free (a 5-DOF task on a 6-DOF arm). Declaring it lets the planner spend that DOF to steer around singularities.
use ;
use Duration;
let cfg = weld // 35 in/min
.with_redundancy;
Yaw is a smooth scalar, so it is gridded coarsely and resolved by a single global
DP over (station) × (yaw × branch) — exact, so it finds the globally optimal yaw
track in one pass. A manipulability node cost steers off singularities, a yaw-rate
edge penalty keeps the spin smooth, and the velocity reconfiguration test rejects
discontinuous edges; the yaw may sweep freely across the window (bounded only by
per-step max_yaw_step). That coarse ψ(s) schedule is then refined at fine
arc-length spacing, with analytic IK placing the arm exactly each step
(predictor–corrector). The free axis is configurable because tool frames differ
(this project is Z-forward; others are X-forward).
Reconfiguration by joint velocity
The planner takes a max_velocity and per-joint velocity ceilings: any edge that
would drive any joint past a configurable fraction (default 90%) of its limit
at that speed is treated as a reconfiguration/discontinuity and rejected. At weld
speeds (20–50 IPM) a joint approaching its velocity limit is the signature of a
singularity or wrist flip, so this cleanly separates "needs to slow down" from
"can't be done continuously." FollowConfig::weld() wires it up; otherwise set
PlannerOptions::{max_velocity, joint_v_max, reconfig_vel_fraction}.
Scope / notes
- Orientation is a full TCP pose per vertex (slerped along the path). For a
symmetric tool, declare the free axis via
with_redundancy(see above) to let the planner resolve the yaw. - Joint velocity is enforced exactly; acceleration/jerk are enforced by the
path-tangent projection plus a jerk-limited integrator. The
q''(s)·ṡ²curvature cross-term is a deliberate first-pass approximation (negligible at process speeds; see the corner tests). - Geometry runs in
f32(squiggle); kinematics and timing inf64.
License
Apache-2.0.