1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//! Pose integration on SE(2) and the odometry process model.
use crateBodyArc;
use crateVector;
use crate;
use crate;
/// Advances `pose` by one odometry increment along the exact constant-twist arc,
/// `pose · exp([Δs, 0, Δθ])`.
///
/// Assumes the body twist is constant across the tick (a zero-order hold on the wheel velocities).
/// Under that assumption the result is exact at any step size — the residual is the hold itself,
/// not integration error.
///
/// Straight-line motion is handled by [`SE2::exp`]'s Taylor branch. Its guard is on the heading
/// *increment*, not the yaw rate, so the tick rate selects the branch: at 1 kHz the branch engages
/// below roughly 1e-3 rad/s. The series is accurate and derivative-continuous there.
///
/// ```
/// use multicalc::kinematics::{BodyArc, integrate};
/// use multicalc::spatial::SE2;
/// // Two ticks of 0.05 rad turn each leave the pose rotated by 0.1 rad.
/// let start = SE2::identity();
/// let step = BodyArc::new(0.1_f64, 0.05); // turn 0.1 rad while moving 0.05 m along the arc
/// let pose = integrate(integrate(start, step), step);
/// assert!((pose.rotation().log() - 0.1).abs() < 1e-12);
/// ```
/// The odometry process model, `[x, y, θ, Δs, Δθ] → [x', y', θ']`.
///
/// Autodiff through this gives both Jacobians a filter needs from one function: columns 0..3 are the
/// state Jacobian, columns 3..5 the control Jacobian.
///
/// The output heading is wrapped to `(−π, π]`, so a filter consuming this must wrap its heading
/// innovation to the same interval.
;