Skip to main content

frcrs/
trajectory.rs

1#![allow(unused_imports)]
2
3use ordered_float::NotNan;
4use serde::{Deserialize, Serialize};
5use std::ops::{Add, Mul, Sub};
6use uom::si::{
7    angle::{degree, radian},
8    angular_velocity::radian_per_second,
9    f64::{Angle, AngularVelocity, Length, Time, Velocity},
10    length::{meter, millimeter},
11    time::second,
12    velocity::meter_per_second,
13};
14
15#[derive(Serialize, Deserialize)]
16pub struct ChoreoTrajectory {
17    pub name: String,
18    pub version: u32,
19    pub snapshot: Snapshot,
20    pub params: Params,
21    pub trajectory: TrajectoryData,
22    pub events: Vec<Event>,
23}
24
25#[derive(Serialize, Deserialize)]
26pub struct Snapshot {
27    pub waypoints: Vec<SnapshotWaypoint>,
28    pub constraints: Vec<Constraint>,
29    #[serde(rename = "targetDt")]
30    pub target_dt: f64,
31}
32
33#[derive(Serialize, Deserialize)]
34pub struct SnapshotWaypoint {
35    pub x: f64,
36    pub y: f64,
37    pub heading: f64,
38    pub intervals: u32,
39    pub split: bool,
40    #[serde(rename = "fixTranslation")]
41    pub fix_translation: bool,
42    #[serde(rename = "fixHeading")]
43    pub fix_heading: bool,
44    #[serde(rename = "overrideIntervals")]
45    pub override_intervals: bool,
46}
47
48#[derive(Serialize, Deserialize)]
49pub struct Constraint {
50    // TODO: implement
51}
52
53#[derive(Serialize, Deserialize)]
54pub struct Params {
55    // TODO: implement
56}
57
58#[derive(Serialize, Deserialize)]
59pub struct Event {
60    // TODO: implement
61}
62
63#[derive(Serialize, Deserialize)]
64pub struct TrajectoryData {
65    pub samples: Vec<Sample>,
66    pub waypoints: Vec<f64>,
67}
68
69#[derive(Serialize, Deserialize, Debug)]
70pub struct Sample {
71    pub t: f64,
72    pub x: f64,
73    pub y: f64,
74    pub heading: f64,
75    #[serde(rename = "vx")]
76    pub velocity_x: f64,
77    #[serde(rename = "vy")]
78    pub velocity_y: f64,
79    #[serde(rename = "omega")]
80    pub angular_velocity: f64,
81}
82
83pub struct Path {
84    samples: Vec<PoseSample>,
85    waypoints: Vec<f64>,
86}
87
88#[derive(Clone, Debug)]
89struct PoseSample {
90    time: f64,
91    pose: Pose,
92}
93
94impl Path {
95    pub fn from_trajectory(trajectory: &str) -> Result<Self, serde_json::Error> {
96        let choreo = serde_json::from_str::<ChoreoTrajectory>(trajectory)?;
97
98        let valid_waypoints = choreo
99            .snapshot
100            .waypoints
101            .iter()
102            .enumerate()
103            .filter(|(_, wp)| wp.split)
104            .map(|(i, _)| choreo.trajectory.waypoints[i])
105            .collect();
106
107        let trajectory_data = TrajectoryData {
108            samples: choreo.trajectory.samples,
109            waypoints: valid_waypoints,
110        };
111
112        Ok(Self::from_trajectory_data(trajectory_data))
113    }
114
115    fn from_trajectory_data(data: TrajectoryData) -> Self {
116        let mut samples = Vec::with_capacity(data.samples.len());
117        for sample in data.samples {
118            samples.push(PoseSample {
119                time: sample.t,
120                pose: sample.into(),
121            });
122        }
123
124        // Sort by time just in case
125        samples.sort_by(|a, b| a.time.partial_cmp(&b.time).unwrap());
126
127        Self {
128            samples,
129            waypoints: data.waypoints,
130        }
131    }
132
133    pub fn get(&self, elapsed: Time) -> Pose {
134        let t = elapsed.get::<second>();
135
136        match self
137            .samples
138            .binary_search_by(|probe| probe.time.partial_cmp(&t).unwrap())
139        {
140            Ok(idx) => self.samples[idx].pose.clone(), // exact match
141            Err(0) => self.samples[0].pose.clone(),    // before first sample
142            Err(idx) if idx >= self.samples.len() => self.samples.last().unwrap().pose.clone(), // after last sample
143            Err(idx) => {
144                let before = &self.samples[idx - 1];
145                let after = &self.samples[idx];
146                let progress = (t - before.time) / (after.time - before.time);
147                before.pose.lerp(&after.pose, progress)
148            }
149        }
150    }
151
152    pub fn length(&self) -> Time {
153        Time::new::<second>(self.samples.last().unwrap().time)
154    }
155
156    pub fn waypoints(&self) -> &Vec<f64> {
157        &self.waypoints
158    }
159}
160
161#[derive(Clone, Debug)]
162pub struct Pose {
163    pub x: Length,
164    pub y: Length,
165    pub heading: Angle,
166    pub angular_velocity: AngularVelocity,
167    pub velocity_x: Velocity,
168    pub velocity_y: Velocity,
169}
170
171impl Pose {
172    fn lerp(&self, other: &Pose, l: f64) -> Pose {
173        Pose {
174            x: lerp(self.x, other.x, l),
175            y: lerp(self.y, other.y, l),
176            heading: lerp(self.heading, other.heading, l),
177            angular_velocity: lerp(self.angular_velocity, other.angular_velocity, l),
178            velocity_x: lerp(self.velocity_x, other.velocity_x, l),
179            velocity_y: lerp(self.velocity_y, other.velocity_y, l),
180        }
181    }
182
183    /// X and Y are half of the field length and width
184    /// velocity might be wrong
185    pub fn mirror(&self, x: Length, y: Length) -> Pose {
186        Pose {
187            x: x - self.x + x,
188            y: y - self.y + y,
189            heading: self.heading + Angle::new::<radian>(std::f64::consts::PI),
190            angular_velocity: -self.angular_velocity,
191            velocity_x: self.velocity_x,
192            velocity_y: self.velocity_y,
193        }
194    }
195}
196
197fn lerp<A>(a: A, b: A, l: f64) -> A
198where
199    A: Sub<A, Output = A> + Add<A, Output = A> + Mul<f64, Output = A> + Clone,
200{
201    a.clone() + (b - a) * l
202}
203
204impl From<Sample> for Pose {
205    fn from(value: Sample) -> Self {
206        Self {
207            x: Length::new::<meter>(value.x),
208            y: Length::new::<meter>(value.y),
209            heading: Angle::new::<radian>(value.heading),
210            angular_velocity: AngularVelocity::new::<radian_per_second>(value.angular_velocity),
211            velocity_x: Velocity::new::<meter_per_second>(value.velocity_x),
212            velocity_y: Velocity::new::<meter_per_second>(value.velocity_y),
213        }
214    }
215}
216
217// #[test]
218// fn parse() {
219//     let data = include_str!("../../RobotCode2025/auto/Blue2.traj");
220//     let path = Path::from_trajectory(data).unwrap();
221//     for i in path.waypoints() {
222//         println!("{}", i);
223//     }
224// }
225//
226// #[test]
227// fn mirror_test() {
228//     let path = Path::from_trajectory(include_str!("../../RobotCode2025/auto/Blue2.traj")).unwrap();
229//     let setpoint = path.get(Time::new::<second>(0.0));
230//
231//     println!("{:?}", setpoint);
232//
233//     let setpoint = setpoint.mirror(
234//         Length::new::<meter>(17.55 / 2.),
235//         Length::new::<meter>(8.05 / 2.),
236//     );
237//
238//     assert!((setpoint.x.get::<meter>() - 9.53808).abs() < 1e-5);
239//     assert!((setpoint.y.get::<meter>() - 0.44384).abs() < 1e-5);
240//     assert!((setpoint.heading.get::<degree>() - 180.).abs() < 1e-5);
241// }