Brick
Loading...
Searching...
No Matches
trajectory.h
1#pragma once
2
3#include <algorithm>
4#include <string>
5
6#include "core/math/geometry/pose2d.h"
7#include "core/math/kinematics/chassis_accelerations.h"
8#include "core/math/kinematics/chassis_velocities.h"
9#include "core/math/kinematics/tank_kinematics.h"
10#include "core/math/kinematics/tank_wheel_velocities.h"
11#include "core/math/units.h"
12
13using namespace units::literals;
14
15/**
16 * @brief Pair representing 2D Pose and scalar path units::Curvature.
17 */
19 Pose2d pose;
20 units::Curvature curvature;
21};
22
23/**
24 * @brief Represents an action event triggered at a specific time along a trajectory.
25 */
27 units::Time time;
28 std::string name;
29};
30
31/**
32 * @brief Represents a time-parameterized 2D motion trajectory.
33 *
34 * Stores discrete State samples (time, velocity, acceleration, pose, curvature)
35 * along a parameterized path.
36 */
38 public:
39 /**
40 * @brief Represents a single discrete state sample along a trajectory.
41 */
42 struct State {
43 units::Time t = 0_s; ///< Elapsed time from trajectory start
44 units::Length s = 0_in; ///< Distance along trajectory
45 units::Velocity velocity = 0_inps; ///< Linear chassis velocity
46 units::Acceleration acceleration = 0_inps2; ///< Linear chassis acceleration
47 Pose2d pose{}; ///< 2D robot pose (x, y, theta)
48 units::Curvature curvature = 0_radpm; ///< Path curvature at pose
49
50 /** @brief Default constructor. */
51 State() = default;
52
53 /**
54 * @brief Constructs a Trajectory State with explicit values.
55 * @param t Sample time.
56 * @param velocity Linear velocity.
57 * @param acceleration Linear acceleration.
58 * @param pose 2D pose.
59 * @param curvature Path curvature.
60 * @param s Distance along trajectory.
61 */
63 units::Time t, units::Velocity velocity, units::Acceleration acceleration, Pose2d pose,
64 units::Curvature curvature, units::Length s = 0_in
65 )
66 : t(t),
67 s(s),
70 pose(pose),
72
73 /** @brief Checks equality between two States. */
74 bool operator==(const State& other) const {
75 return t == other.t && velocity == other.velocity && acceleration == other.acceleration &&
76 pose == other.pose && curvature == other.curvature;
77 }
78
79 /**
80 * @brief Linearly interpolates between this state and end_value at fraction i in [0, 1].
81 * @param end_value Target state for interpolation.
82 * @param i Interpolation fraction ratio [0, 1].
83 * @return Interpolated State.
84 */
85 State interpolate(State end_value, double i) const {
86 const units::Time new_t = t + (end_value.t - t) * i;
87 const units::Time delta_t = new_t - t;
88
89 if (delta_t < 0_s) {
90 return end_value.interpolate(*this, 1.0 - i);
91 }
92
93 const bool reversing =
94 velocity < 0_inps || (units::abs(velocity) < 1E-9_inps && acceleration < 0_inps2);
95 const units::Velocity new_v = velocity + (acceleration * delta_t);
96 const units::Length new_s =
97 (velocity * delta_t + 0.5 * acceleration * delta_t * delta_t) * (reversing ? -1.0 : 1.0);
98
99 const units::Length distance = end_value.pose.translation().distance(pose.translation());
100 const double interpolation_frac = distance > 1E-9_in ? double(new_s / distance) : i;
101
102 Pose2d new_pose = pose.interpolate(end_value.pose, interpolation_frac);
103
104 const units::Curvature new_curvature =
105 curvature + (end_value.curvature - curvature) * interpolation_frac;
106
107 return State(new_t, new_v, acceleration, new_pose, new_curvature, s + new_s);
108 }
109
110 ChassisVelocities chassis_velocities() const {
111 return ChassisVelocities(velocity, 0_inps, velocity * curvature);
112 }
113
114 ChassisAccelerations chassis_accelerations() const {
116 }
117
118 TankWheelVelocities to_wheel_velocities(const TankKinematics& kin) const {
119 return kin.to_wheel_velocities(chassis_velocities());
120 }
121
122 TankWheelAccelerations to_wheel_accelerations(const TankKinematics& kin) const {
123 return kin.to_wheel_accelerations(chassis_accelerations());
124 }
125 };
126
127 /** @brief Default constructor. */
128 Trajectory() = default;
129
130 /**
131 * @brief Constructs a Trajectory from a vector of State samples.
132 * @param states Vector of time-parameterized states.
133 */
134 explicit Trajectory(std::vector<State> states) : states_(std::move(states)) {
135 if (!states_.empty()) {
136 total_time_ = states_.back().t;
137 }
138 }
139
140 /** @return True if the trajectory contains no state samples. */
141 bool empty() const { return states_.empty(); }
142
143 /** @return Total duration of the trajectory. */
144 units::Time total_time() const { return total_time_; }
145
146 /** @return Reference to the internal vector of State samples. */
147 const std::vector<State>& states() const { return states_; }
148
149 /** @return Vector of events attached to this trajectory. */
150 const std::vector<TrajectoryEvent>& events() const { return events_; }
151
152 /** @brief Attaches a vector of time-parameterized events to the trajectory. */
153 void set_events(std::vector<TrajectoryEvent> events) { events_ = std::move(events); }
154
155 /**
156 * @brief Interpolates the time at which a given arc distance is reached.
157 * @param s Arc distance along trajectory.
158 * @return Interpolated time.
159 */
160 units::Time time_from_distance(units::Length s) const {
161 if (states_.empty() || s <= 0_in) return 0_s;
162 if (s >= states_.back().s) return total_time_;
163
164 std::vector<State>::const_iterator it = std::lower_bound(
165 states_.begin() + 1, states_.end(), s,
166 [](const State& st, units::Length val) { return st.s < val; }
167 );
168
169 const State& s0 = *(it - 1);
170 const State& s1 = *it;
171
172 if (s1.s == s0.s) return s1.t;
173 double alpha = (s - s0.s) / (s1.s - s0.s);
174 return s0.t + (s1.t - s0.t) * alpha;
175 }
176
177 /**
178 * @brief Samples trajectory state at timestamp t using binary search interpolation.
179 * @param t Target timestamp.
180 * @return Interpolated State struct.
181 */
182 State sample(units::Time t) const {
183 if (states_.empty()) {
184 return State{};
185 }
186
187 if (t <= states_.front().t) {
188 return states_.front();
189 }
190 if (t >= total_time_) {
191 return states_.back();
192 }
193
194 std::vector<State>::const_iterator sample = std::lower_bound(
195 states_.cbegin() + 1, states_.cend(), t,
196 [](const State& a, const units::Time& b) { return a.t < b; }
197 );
198
199 std::vector<State>::const_iterator prev_sample = sample - 1;
200
201 if (units::abs(sample->t - prev_sample->t) < 1E-9_s) {
202 return *sample;
203 }
204
205 return prev_sample->interpolate(*sample, ((t - prev_sample->t) / (sample->t - prev_sample->t)));
206 }
207
208 /**
209 * @brief Expresses all poses in the trajectory relative to a reference pose.
210 * @param pose Reference origin pose.
211 * @return Relative Trajectory.
212 */
213 Trajectory relative_to(const Pose2d& pose) const {
214 std::vector<State> new_states = states_;
215 for (State& state : new_states) {
216 state.pose = state.pose.relative_to(pose);
217 }
218 return Trajectory(new_states);
219 }
220
221 /**
222 * @brief Mirrors the trajectory across the line represented by a pose.
223 *
224 * The pose translation is any point on the mirror line, and the pose rotation
225 * is the direction of the line.
226 *
227 * @param line_pose A point and direction defining the mirror line.
228 * @return Mirrored Trajectory.
229 */
230 Trajectory mirror(const Pose2d& line_pose) const {
231 std::vector<State> new_states;
232 new_states.reserve(states_.size());
233 for (const State& st : states_) {
234 State new_st = st;
235 const Pose2d local_pose = st.pose.relative_to(line_pose);
236 const Pose2d reflected_local_pose(local_pose.x(), -local_pose.y(), -local_pose.rotation());
237 new_st.pose = line_pose.transform_by(reflected_local_pose - Pose2d{});
238 new_st.curvature = -st.curvature;
239 new_states.push_back(new_st);
240 }
241 return Trajectory(new_states);
242 }
243
244 /**
245 * @brief Rotates the trajectory 180 degrees around a center point (Point reflection).
246 * @param center The center point (default 3 tiles from each field edge).
247 * @return Rotated Trajectory.
248 */
249 Trajectory rotate_center(Translation2d center = Translation2d(3_tiles, 3_tiles)) const {
250 std::vector<State> new_states;
251 new_states.reserve(states_.size());
252 for (const State& st : states_) {
253 State new_st = st;
254 const Translation2d new_translation = 2.0 * center - st.pose.translation();
255 new_st.pose = Pose2d(new_translation, st.pose.rotation().opposite());
256 // Curvature remains unchanged for 180 degree rotation
257 new_states.push_back(new_st);
258 }
259 return Trajectory(new_states);
260 }
261
262 /**
263 * @brief Returns a time-reversed version of this trajectory (traces the path from end to
264 * start).
265 * @return Reversed Trajectory.
266 */
268 std::vector<State> new_states;
269 new_states.reserve(states_.size());
270 for (std::vector<State>::const_reverse_iterator it = states_.rbegin(); it != states_.rend();
271 ++it) {
272 State st = *it;
273 st.t = total_time_ - st.t;
274 st.velocity = -st.velocity;
275 st.curvature = -st.curvature;
276 st.s = states_.back().s - st.s;
277 new_states.push_back(st);
278 }
279 return Trajectory(new_states);
280 }
281
282 /**
283 * @brief Concatenates two trajectories end-to-end.
284 * @param other Secondary trajectory to append.
285 * @return Combined Trajectory.
286 */
287 Trajectory operator+(const Trajectory& other) const {
288 if (states_.empty()) return other;
289 if (other.empty()) return *this;
290
291 std::vector<State> states = states_;
292 std::vector<State> other_states = other.states();
293 const units::Length total_distance = states_.back().s;
294 for (State& other_state : other_states) {
295 other_state.t += total_time_;
296 other_state.s += total_distance;
297 }
298
299 states.insert(states.end(), other_states.begin() + 1, other_states.end());
300 Trajectory combined(states);
301
302 std::vector<TrajectoryEvent> combined_events = events_;
303 for (const TrajectoryEvent& ev : other.events()) {
304 combined_events.push_back({ev.time + total_time_, ev.name});
305 }
306 combined.set_events(std::move(combined_events));
307
308 return combined;
309 }
310
311 /** @return Initial pose at t = 0s. */
312 Pose2d initial_pose() const { return sample(0_s).pose; }
313
314 /** @brief Checks equality between two trajectories. */
315 bool operator==(const Trajectory& other) const {
316 return total_time_ == other.total_time_ && states_ == other.states_;
317 }
318
319 private:
320 std::vector<State> states_;
321 std::vector<TrajectoryEvent> events_;
322 units::Time total_time_ = 0_s;
323};
324
325/**
326 * @brief High-efficiency streaming sampler for real-time control loops.
327 *
328 * Caches the search index during sequential calls to sample(units::Time t),
329 * achieving O(1) step lookup in 100Hz loop cycles.
330 */
332 public:
333 /**
334 * @brief Constructs a TrajectorySampler bound to a target Trajectory.
335 *
336 * @warning The TrajectorySampler holds a non-owning pointer to the trajectory.
337 * The provided Trajectory object MUST outlive the sampler. Do not pass temporary objects.
338 *
339 * @param trajectory Reference to trajectory to sample.
340 */
341 explicit TrajectorySampler(const Trajectory& trajectory)
342 : trajectory_(&trajectory), cached_index_(0) {}
343
344 /** @brief Resets cached index back to start. */
345 void reset() { cached_index_ = 0; }
346
347 /**
348 * @brief Samples state at timestamp t using cached sequential lookup (O(1)).
349 * @param t Target timestamp.
350 * @return Interpolated State.
351 */
352 Trajectory::State sample(units::Time t) {
353 if (!trajectory_ || trajectory_->empty()) {
354 return Trajectory::State{};
355 }
356 const std::vector<Trajectory::State>& states = trajectory_->states();
357 if (t <= states.front().t) {
358 cached_index_ = 0;
359 return states.front();
360 }
361 if (t >= trajectory_->total_time()) {
362 cached_index_ = states.size() - 1;
363 return states.back();
364 }
365
366 while (cached_index_ + 1 < states.size() && states[cached_index_ + 1].t <= t) {
367 cached_index_++;
368 }
369 while (cached_index_ > 0 && states[cached_index_].t > t) {
370 cached_index_--;
371 }
372
373 if (cached_index_ + 1 >= states.size()) {
374 return states.back();
375 }
376
377 const Trajectory::State& prev = states[cached_index_];
378 const Trajectory::State& next = states[cached_index_ + 1];
379 if (units::abs(next.t - prev.t) < 1E-9_s) {
380 return next;
381 }
382 return prev.interpolate(next, ((t - prev.t) / (next.t - prev.t)));
383 }
384
385 private:
386 const Trajectory* trajectory_ = nullptr;
387 size_t cached_index_ = 0;
388};
constexpr Q distance(LinearVector2d other) const
Gets the distance between two vectors.
Definition translation2d.h:107
Definition pose2d.h:21
constexpr Rotation2d rotation() const
Gets the rotation.
Definition pose2d.h:101
constexpr Pose2d transform_by(const Transform2d &transform) const
Definition pose2d.h:176
constexpr units::Length y() const
Gets the y value of the translation.
Definition pose2d.h:86
constexpr Pose2d interpolate(const Pose2d &end, double fraction) const
Definition pose2d.h:189
constexpr Translation2d translation() const
Gets the translation.
Definition pose2d.h:95
constexpr units::Length x() const
Gets the x value of the translation.
Definition pose2d.h:77
constexpr Pose2d relative_to(const Pose2d &other) const
Definition pose2d.h:163
constexpr Pose2d()
Default Constructor for Pose2d.
Definition pose2d.h:28
TankWheelAccelerations to_wheel_accelerations(const ChassisAccelerations &chassis_accels) const
Returns left and right component accelerations from a chassis acceleration using inverse kinematics.
Definition tank_kinematics.h:92
TankWheelVelocities to_wheel_velocities(const ChassisVelocities &chassis_velocities) const
Returns left and right component velocities from a chassis velocity using inverse kinematics.
Definition tank_kinematics.h:55
void reset()
Resets cached index back to start.
Definition trajectory.h:345
Trajectory::State sample(units::Time t)
Samples state at timestamp t using cached sequential lookup (O(1)).
Definition trajectory.h:352
TrajectorySampler(const Trajectory &trajectory)
Constructs a TrajectorySampler bound to a target Trajectory.
Definition trajectory.h:341
Represents a time-parameterized 2D motion trajectory.
Definition trajectory.h:37
units::Time total_time() const
Definition trajectory.h:144
Trajectory reverse() const
Returns a time-reversed version of this trajectory (traces the path from end to start).
Definition trajectory.h:267
Trajectory mirror(const Pose2d &line_pose) const
Mirrors the trajectory across the line represented by a pose.
Definition trajectory.h:230
Trajectory rotate_center(Translation2d center=Translation2d(3_tiles, 3_tiles)) const
Rotates the trajectory 180 degrees around a center point (Point reflection).
Definition trajectory.h:249
Trajectory relative_to(const Pose2d &pose) const
Expresses all poses in the trajectory relative to a reference pose.
Definition trajectory.h:213
void set_events(std::vector< TrajectoryEvent > events)
Attaches a vector of time-parameterized events to the trajectory.
Definition trajectory.h:153
units::Time time_from_distance(units::Length s) const
Interpolates the time at which a given arc distance is reached.
Definition trajectory.h:160
Trajectory(std::vector< State > states)
Constructs a Trajectory from a vector of State samples.
Definition trajectory.h:134
Trajectory operator+(const Trajectory &other) const
Concatenates two trajectories end-to-end.
Definition trajectory.h:287
Pose2d initial_pose() const
Definition trajectory.h:312
bool operator==(const Trajectory &other) const
Checks equality between two trajectories.
Definition trajectory.h:315
State sample(units::Time t) const
Samples trajectory state at timestamp t using binary search interpolation.
Definition trajectory.h:182
bool empty() const
Definition trajectory.h:141
const std::vector< State > & states() const
Definition trajectory.h:147
Trajectory()=default
Default constructor.
const std::vector< TrajectoryEvent > & events() const
Definition trajectory.h:150
Definition units.h:691
Represents robot chassis accelerations.
Definition chassis_accelerations.h:12
Represents robot chassis velocities.
Definition chassis_velocities.h:14
Pair representing 2D Pose and scalar path units::Curvature.
Definition trajectory.h:18
Represents an action event triggered at a specific time along a trajectory.
Definition trajectory.h:26
Represents a single discrete state sample along a trajectory.
Definition trajectory.h:42
bool operator==(const State &other) const
Checks equality between two States.
Definition trajectory.h:74
State interpolate(State end_value, double i) const
Linearly interpolates between this state and end_value at fraction i in [0, 1].
Definition trajectory.h:85
State(units::Time t, units::Velocity velocity, units::Acceleration acceleration, Pose2d pose, units::Curvature curvature, units::Length s=0_in)
Constructs a Trajectory State with explicit values.
Definition trajectory.h:62
Pose2d pose
2D robot pose (x, y, theta)
Definition trajectory.h:47
units::Length s
Distance along trajectory.
Definition trajectory.h:44
units::Curvature curvature
Path curvature at pose.
Definition trajectory.h:48
units::Acceleration acceleration
Linear chassis acceleration.
Definition trajectory.h:46
units::Time t
Elapsed time from trajectory start.
Definition trajectory.h:43
State()=default
Default constructor.
units::Velocity velocity
Linear chassis velocity.
Definition trajectory.h:45