Skip to main content

Module otg

Module otg 

Source
Expand description

Online trajectory generation: a smooth 1 kHz command from a stream of stepped targets.

Otg turns a target position that may change at any time – every cycle, in bursts, or not for seconds – into a per-cycle position command whose velocity, acceleration and jerk never exceed the limits it was built with, whose acceleration is continuous (the trajectory is C2), and which reaches the target and then stays there exactly. It is the causal alternative to a spline, which needs future knots a low-rate commander cannot supply, and to a low-pass filter, whose peak speed grows with the size of the step. MultiOtg runs one per axis, optionally synchronised; CartesianOtg is the three-axis one. No allocation.

§Algorithm

Every call to Otg::step re-plans from the current state (p, v, a) to (target, 0, 0) and then follows that plan for one cycle. The plan is the classical seven-segment, jerk-limited profile: a time-optimal velocity transfer from (v, a) to a peak velocity v_p with zero acceleration (jerk +j_max up to a peak acceleration, hold it, jerk -j_max back to zero – three segments with closed-form durations), a cruise at v_p, and the mirror transfer from v_p to rest. The only free parameter is v_p: it is ±v_max with a cruise when the target is far enough, and otherwise a root of f(v_p) = target - p, where f is the displacement of the two transfers. f is increasing in v_p except for one hump next to v_rd = v + a|a| / (2 j_max), the velocity reached by ramping the acceleration straight to zero (an unsaturated reversal there costs 2 sqrt(d / j_max) of extra time at about v_rd for a change d of peak velocity), so the domain is split at v_rd and v_rd ± a_max² / j_max, every piece whose ends bracket the target is bisected, and the shortest plan found wins. A target closer than the braking distance gives a root of the opposite sign: the profile passes the target, stops and comes back, all within the limits and without a jerk spike. This is the profile structure of Haschke, Weitnauer and Ritter, On-line planning of time-optimal, jerk-limited trajectories (IROS 2008), evaluated one cycle at a time the way Ruckig (Berscheid and Kröger, RSS 2021) does, whose four jerk patterns are the pieces above. The acceleration along the plan is piecewise linear with slope ±j_max or 0, so the trajectory is C2 and a finite difference of the acceleration over any cycle length, including a 2 ms cycle after a lost packet, stays within j_max; the velocity transfer keeps v_rd within ±v_max, which is what makes the velocity bound hold through the transient of every re-plan.

The target velocity is always zero: the generator is for positional targets from a planner, a vision loop or a hand, not for velocity tracking. The synchronisation in MultiOtg stretches the faster axes to the slowest one’s duration by lowering their peak velocity (a second bisection, on the scale of v_p); an axis whose profile has no peak to lower (one that is exactly braking to its target) keeps its minimum duration.

§In a control loop

Three rules, learnt from a run on a real FER in which the first version of the bridge in examples/nonrealtime_commander.rs was clamped by the rate limiter behind it and then orbited at the velocity cap for twenty seconds. The limits are per axis: two axes at full acceleration have a vector norm sqrt 2 above it, so a budget that is a norm (which is what limit_rate_cartesian_pose bounds) needs OtgLimits::per_axis_for_norm. Step one nominal cycle per command (DELTA_T), not the measured period: the robot and the rate limiter check every packet against a 1 ms budget, so a 2 ms step after a lost packet is a doubled velocity to them. And re-anchor on the robot’s echo of the position every cycle with Otg::set_position (O_T_EE_c; not its twist, see Otg::set_state), so that if anything behind the generator does alter a command, the next plan starts from what was actually sent; a rate limiter that tracks a pose it has fallen behind has no braking logic and never catches up.

use franka::otg::{Otg, OtgLimits};
let limits = OtgLimits { max_velocity: 0.3, max_acceleration: 0.5, max_jerk: 20.0 };
let mut otg = Otg::new(0.0, limits).unwrap();
otg.set_target(0.05).unwrap();
let mut t = 0.0f64;
while otg.position() != 0.05 {
    otg.step(0.001);
    t += 0.001;
}
assert!((t - 0.658).abs() < 0.01, "a 5 cm S-curve under these limits takes 0.658 s");

Structs§

MultiOtg
N Otgs with the same limits, stepped together, optionally synchronised so that all axes arrive at their targets at the same time (the faster axes are slowed down).
Otg
A single-axis online trajectory generator; see the module documentation.
OtgLimits
The per-axis limits of an Otg: all three must be finite and positive.

Type Aliases§

CartesianOtg
A three-axis MultiOtg for a Cartesian position.