1use crate::kinematic_traits::{Joints, Kinematics, Pose, Solutions};
4use crate::kinematics_with_shape::KinematicsWithShape;
5use crate::rrt::RRTPlanner;
6use crate::utils;
7use crate::utils::{dump_joints, transition_costs};
8use bitflags::bitflags;
9use nalgebra::Translation3;
10use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
11use std::fmt;
12use std::sync::Arc;
13use std::sync::atomic::{AtomicBool, Ordering};
14use std::time::Instant;
15
16pub const DEFAULT_TRANSITION_COSTS: [f64; 6] = [1.2, 1.1, 1.1, 0.9, 0.9, 0.8];
19
20pub struct Cartesian<'a> {
22 pub robot: &'a KinematicsWithShape,
23
24 pub check_step_m: f64,
27
28 pub check_step_rad: f64,
31
32 pub max_transition_cost: f64,
34
35 pub transition_coefficients: Joints,
37
38 pub linear_recursion_depth: usize,
42
43 pub rrt: RRTPlanner,
47
48 pub include_linear_interpolation: bool,
52
53 pub debug: bool,
55}
56
57bitflags! {
58 #[derive(Clone, Copy)]
60 pub struct PathFlags: u32 {
61 const NONE = 0b0000_0000;
62
63 const ONBOARDING = 1 << 1;
67
68 const TRACE = 1 << 2;
70
71 const LIN_INTERP = 1 << 3;
76
77 const LAND = 1 << 4;
80
81 const LANDING = 1 << 5;
84
85 const PARK = 1 << 6;
89
90 const PARKING = 1 << 7;
93
94 const FORWARDS = 1 << 8;
96
97 const BACKWARDS = 1 << 9;
99
100 const ALTERED = 1 << 10;
102
103 const ORIGINAL = Self::TRACE.bits() | Self::LAND.bits() | Self::PARK.bits();
106
107 const DEBUG = 1 << 31;
110
111 const CARTESIAN = Self::LIN_INTERP.bits() | Self::LAND.bits() | Self::PARK.bits();
113 }
114}
115
116#[derive(Clone, Copy)]
117pub(crate) struct AnnotatedPose {
118 pub (crate) pose: Pose,
119 pub (crate) flags: PathFlags,
120}
121
122#[derive(Clone, Copy)]
124pub struct AnnotatedJoints {
125 pub joints: Joints,
126 pub flags: PathFlags,
127}
128
129impl AnnotatedPose {
130 pub(crate) fn interpolate(&self, other: &AnnotatedPose, p: f64) -> AnnotatedPose {
131 assert!((0.0..=1.0).contains(&p));
132
133 let self_translation = &self.pose.translation.vector;
135 let other_translation = &other.pose.translation.vector;
136
137 let translation = self_translation.lerp(&other_translation, p);
138 let rotation = self.pose.rotation.slerp(&other.pose.rotation, p);
139
140 AnnotatedPose {
141 pose: Pose::from_parts(Translation3::from(translation), rotation),
142 flags: PathFlags::LIN_INTERP,
143 }
144 }
145}
146
147fn flag_representation(flags: &PathFlags) -> String {
148 const FLAG_MAP: &[(PathFlags, &str)] = &[
149 (PathFlags::LIN_INTERP, "LIN_INTERP"),
150 (PathFlags::LAND, "LAND"),
151 (PathFlags::PARK, "PARK"),
152 (PathFlags::TRACE, "TRACE"),
153 (PathFlags::CARTESIAN, "CARTESIAN"),
154 (PathFlags::ONBOARDING, "ONBOARDING"),
155 ];
156
157 FLAG_MAP
158 .iter()
159 .filter(|(flag, _)| flags.contains(*flag))
160 .map(|(_, name)| *name)
161 .collect::<Vec<_>>()
162 .join(" | ")
163}
164
165impl fmt::Debug for AnnotatedPose {
166 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
167 let translation = self.pose.translation.vector;
168 let rotation = self.pose.rotation;
169 write!(
170 formatter,
171 "{}: [{:.3}, {:.3}, {:.3}], quat {{ w: {:.3}, i: {:.3}, j: {:.3}, k: {:.3} }}",
172 flag_representation(&self.flags),
173 translation.x,
174 translation.y,
175 translation.z,
176 rotation.w,
177 rotation.i,
178 rotation.j,
179 rotation.k
180 )
181 }
182}
183
184impl fmt::Debug for AnnotatedJoints {
185 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
186 write!(
187 formatter,
188 "{}: {:.2}, {:.2}, {:.2}, {:.2}, {:.2}, {:.2} ",
189 flag_representation(&self.flags),
190 self.joints[0].to_degrees(),
191 self.joints[1].to_degrees(),
192 self.joints[2].to_degrees(),
193 self.joints[3].to_degrees(),
194 self.joints[4].to_degrees(),
195 self.joints[5].to_degrees(),
196 )
197 }
198}
199
200struct Transition {
201 from: AnnotatedPose,
202 to: AnnotatedPose,
203 previous: Joints,
204 solutions: Solutions,
205}
206
207impl Cartesian<'_> {
208
209 pub fn plan(
212 &self,
213 from: &Joints,
214 land: &Pose,
215 steps: Vec<Pose>,
216 park: &Pose,
217 ) -> Result<Vec<AnnotatedJoints>, String> {
218 if self.robot.collides(from) {
219 return Err("Onboarding point collides".into());
220 }
221 let strategies = self.robot.inverse_continuing(land, from);
222 if strategies.is_empty() {
223 return Err("Unable to start from onboarding point".into());
224 }
225 let poses = self.with_intermediate_poses(land, &steps, park);
226 println!("Probing {} strategies", strategies.len());
227
228 let stop = Arc::new(AtomicBool::new(false));
229
230 strategies
231 .par_iter()
232 .find_map_any(|strategy| {
233 match self.probe_strategy(
234 strategy, &poses, &stop
235 ) {
236 Ok(outcome) => {
237 println!("Strategy worked out: {:?}", strategy);
238 stop.store(true, Ordering::Relaxed);
239 Some(Ok(outcome))
240 }
241 Err(msg) => {
242 if self.debug {
243 println!("Strategy failed: {:?}, {}", strategy, msg);
244 }
245 None }
247 }
248 })
249 .unwrap_or_else(|| {
250 Err(format!(
251 "No strategy worked out of {} tried",
252 strategies.len()
253 ))
254 })
255 }
256
257 fn probe_strategy(
259 &self,
260 work_path_start: &Joints,
261 poses: &Vec<AnnotatedPose>,
262 stop: &AtomicBool,
263 ) -> Result<Vec<AnnotatedJoints>, String> {
264 println!("Cartesian planning started, computing strategy {work_path_start:?}");
265
266 let started = Instant::now();
267 let mut trace = Vec::with_capacity(100 + poses.len() + 10);
268 trace.push(AnnotatedJoints {
270 joints: *work_path_start,
271 flags: PathFlags::LAND,
272 }.clone());
273
274 let mut step = 1;
275
276 let mut pairs_iterator = poses.windows(2);
277
278 while let Some([from, to]) = pairs_iterator.next() {
279 let prev = trace
280 .last()
281 .expect("Should have start and strategy points")
282 .clone();
283
284 let transition = self.step_adaptive_linear_transition(&prev.joints, from, to, 0);
285 match transition {
286 Ok(extension) => {
287 for (p, step) in extension.iter().enumerate() {
288 let flags = if p < extension.len() - 1 {
289 (to.flags | PathFlags::LIN_INTERP)
290 & !(PathFlags::TRACE | PathFlags::PARK)
291 } else {
292 to.flags
293 };
294 trace.push(AnnotatedJoints {
295 joints: step.clone(),
296 flags: flags,
297 });
298 }
299 }
300
301 Err(failed_transition) => {
302 let mut success = false;
303 println!(
305 "Closing step {:?} with RRT", step
306 );
307 let solutions = self.robot.inverse_continuing(&to.pose, &prev.joints);
308 for next in solutions {
309 let path = self.rrt.plan_rrt(&prev.joints, &next, self.robot, stop);
310 if let Ok(path) = path {
311 println!(" ... closed with RRT {} steps", path.len());
312 for step in path {
313 trace.push(AnnotatedJoints {
314 joints: step,
315 flags: to.flags & !PathFlags::LIN_INTERP,
316 });
317 }
318 success = true;
319 break;
320 }
321 }
322
323 if !success {
324 self.log_failed_transition(&failed_transition, step);
325 return Err(format!("Failed to transition at step {}", step));
326 }
327 }
328 }
329 if to.flags.contains(PathFlags::TRACE) {
330 step += 1;
331 }
332 }
333
334 if self.debug {
335 println!(
336 "Cartesian planning till collision check took {:?}",
337 started.elapsed()
338 );
339 }
340 if stop.load(Ordering::Relaxed) {
341 return Err("Stopped".into());
342 }
343
344 Ok(trace)
345 }
346
347 fn step_adaptive_linear_transition(
351 &self,
352 starting: &Joints,
353 from: &AnnotatedPose, to: &AnnotatedPose,
355 depth: usize,
356 ) -> Result<Vec<Joints>, Transition> {
357 pub const DIV_RATIO: f64 = 0.5;
358
359 let solutions = self
361 .robot
362 .kinematics
363 .inverse_continuing(&to.pose, &starting);
364
365 for next in &solutions {
367 let cost = transition_costs(starting, next, &self.transition_coefficients);
370 if cost <= self.max_transition_cost {
371 return Ok(vec![next.clone()]); }
373 }
374
375 if depth < self.linear_recursion_depth {
379 let midpose = from.interpolate(to, DIV_RATIO);
382 let first_track =
383 self.step_adaptive_linear_transition(starting, from, &midpose, depth + 1)?;
384 let mid_step = first_track.last().unwrap().clone();
385
386 let second_track =
389 self.step_adaptive_linear_transition(&mid_step, &midpose, to, depth + 1)?;
390
391 Ok(first_track
392 .into_iter()
393 .chain(second_track.into_iter())
394 .collect())
395 } else {
396 Err(Transition {
397 from: from.clone(),
398 to: to.clone(),
399 previous: starting.clone(),
400 solutions: solutions,
401 })
402 }
403 }
404
405 fn log_failed_transition(&self, transition: &Transition, step: i32) {
406 if !self.debug {
407 return;
408 }
409 println!("Step {} from [1..n] failed", step);
410 println!(
411 "No transition with cost below {}:",
412 self.max_transition_cost.to_degrees()
413 );
414 println!(
415 " from: {:?} collides {}",
416 transition.from,
417 self.robot.collides(&transition.previous)
418 );
419 dump_joints(&transition.previous);
420 println!(" to: {:?}", transition.to);
421 println!(" Possible transitions:");
422 for s in &transition.solutions {
423 dump_joints(s);
424 println!(
425 " transition {} collides: {}",
426 utils::transition_costs(&transition.previous, s, &DEFAULT_TRANSITION_COSTS)
427 .to_degrees(),
428 self.robot.collides(s)
429 );
430 }
431 }
432
433 fn with_intermediate_poses(
434 &self,
435 land: &Pose,
436 steps: &Vec<Pose>,
437 park: &Pose,
438 ) -> Vec<AnnotatedPose> {
439 let mut poses = Vec::with_capacity(10 * steps.len() + 2);
440
441 poses.push(AnnotatedPose {
443 pose: *land,
444 flags: PathFlags::LAND,
445 });
446
447 if !steps.is_empty() {
448 self.add_intermediate_poses(land, &steps[0], &mut poses);
450
451 for i in 0..steps.len() - 1 {
453 poses.push(AnnotatedPose {
454 pose: steps[i].clone(),
455 flags: PathFlags::TRACE,
456 });
457
458 self.add_intermediate_poses(&steps[i], &steps[i + 1], &mut poses);
459 }
460
461 let last = *steps.last().unwrap();
463 poses.push(AnnotatedPose {
464 pose: last.clone(),
465 flags: PathFlags::TRACE,
466 });
467
468 self.add_intermediate_poses(&last, park, &mut poses);
470 } else {
471 self.add_intermediate_poses(land, park, &mut poses);
473 }
474
475 poses.push(AnnotatedPose {
477 pose: park.clone(),
478 flags: PathFlags::PARK,
479 });
480
481 poses
482 }
483
484 fn add_intermediate_poses(&self, start: &Pose, end: &Pose, poses: &mut Vec<AnnotatedPose>) {
486 let translation_diff = end.translation.vector - start.translation.vector;
488 let translation_distance = translation_diff.norm();
489
490 let rotation_diff = end.rotation * start.rotation.inverse();
492 let rotation_angle = rotation_diff.angle();
493
494 let translation_steps = (translation_distance / self.check_step_m).ceil() as usize;
496 let rotation_steps = (rotation_angle / self.check_step_rad).ceil() as usize;
497
498 let steps = translation_steps.max(rotation_steps).max(1);
500
501 let translation_step = translation_diff / steps as f64;
503
504 for i in 1..steps {
506 let fraction = i as f64 / steps as f64;
507
508 let intermediate_translation = start.translation.vector + translation_step * i as f64;
510 let intermediate_rotation = start.rotation.slerp(&end.rotation, fraction);
511
512 let intermediate_pose =
514 Pose::from_parts(intermediate_translation.into(), intermediate_rotation);
515
516 poses.push(AnnotatedPose {
517 pose: intermediate_pose,
518 flags: PathFlags::LIN_INTERP,
519 });
520 }
521 }
522}