libstp
Robotics library for Botball
Loading...
Searching...
No Matches
spline_motion.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <memory>
4#include <optional>
5#include <vector>
6#include <utility>
7
8#include "motion/motion.hpp"
12#include "foundation/types.hpp"
13
14#include <Eigen/Core>
15
16namespace libstp::motion
17{
29 {
30 std::vector<std::pair<double, double>> waypoints_m;
31 std::vector<double> headings_rad; // empty = tangent-following; one per waypoint = explicit
32 // Fraction of max speed AND travel direction via its sign:
33 // - speed_scale > 0 → drive the path forwards (nose leads).
34 // - speed_scale < 0 → drive the path in reverse. On a holonomic
35 // (mecanum/omni) drivetrain the robot HOLDS its start orientation and
36 // strafes the path backwards (nose stays put — genuine reverse). On a
37 // differential drivetrain, which cannot hold heading through a curve,
38 // it falls back to rear-first (nose points opposite the travel).
39 // Magnitude is clamped to [0.01, 1.0].
40 double speed_scale{1.0};
41
49 std::optional<double> final_heading_rad{};
50 };
51
54 {
55 double time_s{0.0};
56 double dt{0.0};
57 // Arc-length progress
58 double arc_length_m{0.0}; // measured progress along spline
59 double arc_target_m{0.0}; // total spline length
60 // Path tracking
61 double cross_track_m{0.0}; // signed perpendicular distance to path
62 // Heading
63 double heading_rad{0.0}; // current heading
64 double target_heading_rad{0.0}; // desired heading (tangent or interpolated)
65 double heading_error_rad{0.0};
66 // Velocity
68 // Commands
69 double cmd_vx_mps{0.0};
70 double cmd_vy_mps{0.0};
71 double cmd_wz_radps{0.0};
72 // Profile state
75 bool saturated{false};
76 // Path tracking health
77 bool off_path{false}; // cross-track error exceeded kOffPathThreshold
78 };
79
88 class SplineMotion final : public Motion
89 {
90 public:
92
93 void start() override;
94 void update(double dt) override;
95 [[nodiscard]] bool isFinished() const override;
96
97 [[nodiscard]] const std::vector<SplineMotionTelemetry>& getTelemetry() const { return telemetry_; }
98
99 private:
100 void complete();
101
103 [[nodiscard]] double targetHeadingAt(double s) const;
104
105 SplineMotionConfig cfg_{};
106 std::unique_ptr<CatmullRomSpline> spline_;
107 double max_velocity_{0.0};
108 ProfiledPIDController profiled_pid_;
109 std::unique_ptr<foundation::PidController> heading_pid_;
110 bool finished_{false};
111
112 // Initial pose snapshot — replaces odometry().reset() at start.
113 Eigen::Vector2d initial_position_m_{Eigen::Vector2d::Zero()};
114 double initial_absolute_heading_rad_{0.0}; // getAbsoluteHeading() captured at start
115
116 // Heading interpolation LUT: arc-length → heading (built if explicit headings)
117 std::vector<double> heading_arc_lengths_; // arc-length at each point (origin + waypoints)
118 std::vector<double> all_headings_; // heading at each point (origin=0 + user headings)
119 bool use_explicit_headings_{false};
120
121 // Reverse traversal (speed_scale < 0), resolved in start() once the
122 // drivetrain's lateral capability is known.
123 bool reverse_{false};
124 bool hold_heading_{false}; // holonomic reverse: keep the start orientation
125 bool rear_first_{false}; // differential reverse: nose opposite travel
126
127 // Projection tracking
128 double s_measured_{0.0}; // last projected arc-length (hint for next cycle)
129
130 // Velocity tracking for settling detection
131 double prev_s_measured_{0.0};
132 double filtered_velocity_{0.0};
133 static constexpr double kVelocityFilterAlpha{0.3};
134 static constexpr double kSettlingVelocity{0.02}; // m/s
135
136 // Profile-at-goal counter: complete after N cycles at goal with near-zero setpoint velocity
137 int at_goal_cycles_{0};
138 static constexpr int kAtGoalSettleCycles{30}; // 30 cycles @ 100Hz = 300ms
139
140 // Saturation feedback
141 double speed_scale_{1.0};
142 double heading_scale_{1.0};
143 int unsaturated_cycles_{0};
144
145 // Off-path detection: warn once per motion instance when cross-track
146 // error exceeds this threshold to avoid log spam.
147 bool off_path_warned_{false};
148 static constexpr double kOffPathThreshold{0.15}; // 15 cm
149
150 // Telemetry
151 double elapsed_time_{0.0};
152 std::vector<SplineMotionTelemetry> telemetry_;
153 };
154}
Motion(MotionContext ctx)
Definition motion.hpp:35
void update(double dt) override
bool isFinished() const override
const std::vector< SplineMotionTelemetry > & getTelemetry() const
SplineMotion(MotionContext ctx, SplineMotionConfig config)
std::vector< std::pair< double, double > > waypoints_m
std::optional< double > final_heading_rad