1use std::collections::{hash_map::Entry, HashMap, HashSet, VecDeque};
2
3use r2r::{
4 builtin_interfaces::msg::{Duration, Time},
5 geometry_msgs::msg::{Transform, TransformStamped},
6 std_msgs::msg::Header,
7 tf2_msgs::msg::TFMessage,
8};
9
10use crate::{
11 tf_error::TfError,
12 tf_graph_node::TfGraphNode,
13 tf_individual_transform_chain::TfIndividualTransformChain,
14 transforms::{chain_transforms, get_inverse, to_transform_stamped},
15};
16
17#[derive(Clone, Debug)]
18pub struct TfBuffer {
19 child_transform_index: HashMap<String, HashSet<String>>,
20 transform_data: HashMap<TfGraphNode, TfIndividualTransformChain>,
21 cache_duration: Duration,
22}
23
24const DEFAULT_CACHE_DURATION_SECONDS: i32 = 10;
25
26impl TfBuffer {
27 pub(crate) fn new() -> Self {
28 Self::new_with_duration(Duration {
29 sec: DEFAULT_CACHE_DURATION_SECONDS,
30 nanosec: 0,
31 })
32 }
33
34 pub fn new_with_duration(cache_duration: Duration) -> Self {
35 TfBuffer {
36 child_transform_index: HashMap::new(),
37 transform_data: HashMap::new(),
38 cache_duration,
39 }
40 }
41
42 pub(crate) fn handle_incoming_transforms(&mut self, transforms: TFMessage, static_tf: bool) {
43 for transform in transforms.transforms {
44 self.add_transform(&transform, static_tf);
45 self.add_transform(&get_inverse(&transform), static_tf);
46 }
47 }
48
49 fn add_transform(&mut self, transform: &TransformStamped, static_tf: bool) {
50 self.child_transform_index
52 .entry(transform.header.frame_id.clone())
53 .or_default()
54 .insert(transform.child_frame_id.clone());
55
56 let key = TfGraphNode {
57 child: transform.child_frame_id.clone(),
58 parent: transform.header.frame_id.clone(),
59 };
60
61 match self.transform_data.entry(key) {
62 Entry::Occupied(e) => e.into_mut(),
63 Entry::Vacant(e) => e.insert(TfIndividualTransformChain::new(
64 static_tf,
65 self.cache_duration.clone(),
66 )),
67 }
68 .add_to_buffer(transform.clone());
69 }
70
71 fn retrieve_transform_path(
73 &self,
74 from: String,
75 to: String,
76 time: &Time,
77 ) -> Result<Vec<String>, TfError> {
78 let mut res = vec![];
79 let mut frontier: VecDeque<String> = VecDeque::new();
80 let mut visited: HashSet<String> = HashSet::new();
81 let mut parents: HashMap<String, String> = HashMap::new();
82 visited.insert(from.clone());
83 frontier.push_front(from.clone());
84
85 while !frontier.is_empty() {
86 let current_node = frontier.pop_front().unwrap();
87 if current_node == to {
88 break;
89 }
90 if let Some(children) = self.child_transform_index.get(¤t_node) {
91 for v in children {
92 if visited.contains(v) {
93 continue;
94 }
95
96 if self
97 .transform_data
98 .get(&TfGraphNode {
99 child: v.clone(),
100 parent: current_node.clone(),
101 })
102 .map_or(false, |chain| chain.has_valid_transform(time))
103 {
104 parents.insert(v.to_string(), current_node.clone());
105 frontier.push_front(v.to_string());
106 visited.insert(v.to_string());
107 }
108 }
109 }
110 }
111 let mut r = to.clone();
112 while r != from {
113 res.push(r.clone());
114 let parent = parents.get(&r);
115
116 match parent {
117 Some(x) => r = x.to_string(),
118 None => {
119 return Err(TfError::CouldNotFindTransform(
120 from,
121 to,
122 self.child_transform_index.clone(),
123 ))
124 }
125 }
126 }
127 res.reverse();
128 Ok(res)
129 }
130
131 pub fn lookup_transform(
133 &self,
134 from: &str,
135 to: &str,
136 time: &Time,
137 ) -> Result<TransformStamped, TfError> {
138 let from = from.to_string();
139 let to = to.to_string();
140 let path = self.retrieve_transform_path(from.clone(), to.clone(), time);
141
142 match path {
143 Ok(path) => {
144 let mut tf_list: Vec<Transform> = Vec::new();
145 let mut first = from.clone();
146 for intermediate in path {
147 let node = TfGraphNode {
148 child: intermediate.clone(),
149 parent: first.clone(),
150 };
151 let time_cache = self.transform_data.get(&node).unwrap();
152 let transform = time_cache.get_closest_transform(time);
153 match transform {
154 Err(e) => return Err(e),
155 Ok(x) => {
156 tf_list.push(x.transform);
157 }
158 }
159 first.clone_from(&intermediate);
160 }
161 let final_tf = chain_transforms(&tf_list);
162 let msg = TransformStamped {
163 child_frame_id: to,
164 header: Header {
165 frame_id: from,
166 stamp: time.clone(),
167 },
168 transform: final_tf,
169 };
170 Ok(msg)
171 }
172 Err(x) => Err(x),
173 }
174 }
175
176 pub(crate) fn lookup_transform_with_time_travel(
177 &self,
178 to: &str,
179 time2: Time,
180 from: &str,
181 time1: Time,
182 fixed_frame: &str,
183 ) -> Result<TransformStamped, TfError> {
184 let tf1 = self.lookup_transform(from, fixed_frame, &time1)?;
185 let tf2 = self.lookup_transform(to, fixed_frame, &time2)?;
186 let transforms = get_inverse(&tf1);
187 let result = chain_transforms(&[tf2.transform, transforms.transform]);
188 Ok(to_transform_stamped(
189 result,
190 from.to_string(),
191 to.to_string(),
192 &time1,
193 ))
194 }
195}
196
197#[cfg(test)]
198mod test {
199 use r2r::{
200 builtin_interfaces::msg::Time,
201 geometry_msgs::msg::{Quaternion, Vector3},
202 };
203
204 use super::*;
205 use crate::utils::time_from_nanosec;
206
207 const PARENT: &str = "parent";
208 const CHILD0: &str = "child0";
209 const CHILD1: &str = "child1";
210
211 fn build_test_tree(buffer: &mut TfBuffer, time: f64) {
217 let nsecs = ((time - ((time.floor() as i64) as f64)) * 1E9) as u32;
218
219 let world_to_item = TransformStamped {
220 child_frame_id: "item".to_string(),
221 header: Header {
222 frame_id: "world".to_string(),
223 stamp: Time {
224 sec: time.floor() as i32,
225 nanosec: nsecs,
226 },
227 },
228 transform: Transform {
229 rotation: Quaternion {
230 x: 0f64,
231 y: 0f64,
232 z: 0f64,
233 w: 1f64,
234 },
235 translation: Vector3 {
236 x: 1f64,
237 y: 0f64,
238 z: 0f64,
239 },
240 },
241 };
242 buffer.add_transform(&world_to_item, true);
243 buffer.add_transform(&get_inverse(&world_to_item), true);
244
245 let world_to_base_link = TransformStamped {
246 child_frame_id: "base_link".to_string(),
247 header: Header {
248 frame_id: "world".to_string(),
249 stamp: Time {
250 sec: time.floor() as i32,
251 nanosec: nsecs,
252 },
253 },
254 transform: Transform {
255 rotation: Quaternion {
256 x: 0f64,
257 y: 0f64,
258 z: 0f64,
259 w: 1f64,
260 },
261 translation: Vector3 {
262 x: 0f64,
263 y: time,
264 z: 0f64,
265 },
266 },
267 };
268 buffer.add_transform(&world_to_base_link, false);
269 buffer.add_transform(&get_inverse(&world_to_base_link), false);
270
271 let base_link_to_camera = TransformStamped {
272 child_frame_id: "camera".to_string(),
273 header: Header {
274 frame_id: "base_link".to_string(),
275 stamp: Time {
276 sec: time.floor() as i32,
277 nanosec: nsecs,
278 },
279 },
280 transform: Transform {
281 rotation: Quaternion {
282 x: 0f64,
283 y: 0f64,
284 z: 0f64,
285 w: 1f64,
286 },
287 translation: Vector3 {
288 x: 0.5f64,
289 y: 0f64,
290 z: 0f64,
291 },
292 },
293 };
294 buffer.add_transform(&base_link_to_camera, true);
295 buffer.add_transform(&get_inverse(&base_link_to_camera), true);
296 }
297
298 #[test]
300 fn test_basic_tf_lookup() {
301 let mut tf_buffer = TfBuffer::new();
302 build_test_tree(&mut tf_buffer, 0f64);
303 let res = tf_buffer.lookup_transform("camera", "item", &time_from_nanosec(0));
304 let expected = TransformStamped {
305 child_frame_id: "item".to_string(),
306 header: Header {
307 frame_id: "camera".to_string(),
308 stamp: time_from_nanosec(0),
309 },
310 transform: Transform {
311 rotation: Quaternion {
312 x: 0f64,
313 y: 0f64,
314 z: 0f64,
315 w: 1f64,
316 },
317 translation: Vector3 {
318 x: 0.5f64,
319 y: 0f64,
320 z: 0f64,
321 },
322 },
323 };
324 assert_eq!(res.unwrap(), expected);
325 }
326
327 #[test]
329 fn test_basic_tf_interpolation() {
330 let mut tf_buffer = TfBuffer::new();
331 build_test_tree(&mut tf_buffer, 0f64);
332 build_test_tree(&mut tf_buffer, 1f64);
333 let res = tf_buffer.lookup_transform("camera", "item", &time_from_nanosec(700_000_000));
334 let expected = TransformStamped {
335 child_frame_id: "item".to_string(),
336 header: Header {
337 frame_id: "camera".to_string(),
338 stamp: time_from_nanosec(700_000_000),
339 },
340 transform: Transform {
341 rotation: Quaternion {
342 x: 0f64,
343 y: 0f64,
344 z: 0f64,
345 w: 1f64,
346 },
347 translation: Vector3 {
348 x: 0.5f64,
349 y: -0.7f64,
350 z: 0f64,
351 },
352 },
353 };
354 assert_eq!(res.unwrap(), expected);
355 }
356
357 #[test]
359 fn test_basic_tf_time_travel() {
360 let mut tf_buffer = TfBuffer::new();
361 build_test_tree(&mut tf_buffer, 0f64);
362 build_test_tree(&mut tf_buffer, 1f64);
363 let res = tf_buffer.lookup_transform_with_time_travel(
364 "camera",
365 time_from_nanosec(400_000_000),
366 "camera",
367 time_from_nanosec(700_000_000),
368 "item",
369 );
370 let expected = TransformStamped {
371 child_frame_id: "camera".to_string(),
372 header: Header {
373 frame_id: "camera".to_string(),
374 stamp: time_from_nanosec(700_000_000),
375 },
376 transform: Transform {
377 rotation: Quaternion {
378 x: 0f64,
379 y: 0f64,
380 z: 0f64,
381 w: 1f64,
382 },
383 translation: Vector3 {
384 x: 0f64,
385 y: 0.3f64,
386 z: 0f64,
387 },
388 },
389 };
390 assert_approx_eq(res.unwrap(), expected);
391 }
392
393 #[test]
394 fn test_add_transform() {
395 let mut tf_buffer = TfBuffer::new();
396 let transform00 = TransformStamped {
397 header: Header {
398 frame_id: PARENT.to_string(),
399 stamp: time_from_nanosec(0),
400 },
401 child_frame_id: CHILD0.to_string(),
402 ..Default::default()
403 };
404 let transform01 = TransformStamped {
405 header: Header {
406 frame_id: PARENT.to_string(),
407 stamp: Time { sec: 1, nanosec: 0 },
408 },
409 child_frame_id: CHILD0.to_string(),
410 ..Default::default()
411 };
412 let transform1 = TransformStamped {
413 header: Header {
414 frame_id: PARENT.to_string(),
415 ..Default::default()
416 },
417 child_frame_id: CHILD1.to_string(),
418 ..Default::default()
419 };
420 let transform0_key = TfGraphNode {
421 child: CHILD0.to_owned(),
422 parent: PARENT.to_owned(),
423 };
424 let transform1_key = TfGraphNode {
425 child: CHILD1.to_owned(),
426 parent: PARENT.to_owned(),
427 };
428 let static_tf = true;
429 tf_buffer.add_transform(&transform00, static_tf);
430 assert_eq!(tf_buffer.child_transform_index.len(), 1);
431 assert!(tf_buffer.child_transform_index.contains_key(PARENT));
432 let children = tf_buffer.child_transform_index.get(PARENT).unwrap();
433 assert_eq!(children.len(), 1);
434 assert!(children.contains(CHILD0));
435 assert_eq!(tf_buffer.transform_data.len(), 1);
436 assert!(tf_buffer.transform_data.contains_key(&transform0_key));
437 let data = tf_buffer.transform_data.get(&transform0_key);
438 assert!(data.is_some());
439 assert_eq!(data.unwrap().transform_chain.len(), 1);
440
441 tf_buffer.add_transform(&transform01, static_tf);
442 assert_eq!(tf_buffer.child_transform_index.len(), 1);
443 assert!(tf_buffer.child_transform_index.contains_key(PARENT));
444 let children = tf_buffer.child_transform_index.get(PARENT).unwrap();
445 assert_eq!(children.len(), 1);
446 assert!(children.contains(CHILD0));
447 assert_eq!(tf_buffer.transform_data.len(), 1);
448 assert!(tf_buffer.transform_data.contains_key(&transform0_key));
449 let data = tf_buffer.transform_data.get(&transform0_key);
450 assert!(data.is_some());
451 assert_eq!(data.unwrap().transform_chain.len(), 2);
452
453 tf_buffer.add_transform(&transform1, static_tf);
454 assert_eq!(tf_buffer.child_transform_index.len(), 1);
455 assert!(tf_buffer.child_transform_index.contains_key(PARENT));
456 let children = tf_buffer.child_transform_index.get(PARENT).unwrap();
457 assert_eq!(children.len(), 2);
458 assert!(children.contains(CHILD0));
459 assert!(children.contains(CHILD1));
460 assert_eq!(tf_buffer.transform_data.len(), 2);
461 assert!(tf_buffer.transform_data.contains_key(&transform0_key));
462 assert!(tf_buffer.transform_data.contains_key(&transform1_key));
463 let data = tf_buffer.transform_data.get(&transform0_key);
464 assert!(data.is_some());
465 assert_eq!(data.unwrap().transform_chain.len(), 2);
466 let data = tf_buffer.transform_data.get(&transform1_key);
467 assert!(data.is_some());
468 assert_eq!(data.unwrap().transform_chain.len(), 1);
469 }
470
471 #[test]
472 fn test_cache_duration() {
473 let mut tf_buffer = TfBuffer::new_with_duration(Duration { sec: 1, nanosec: 0 });
474 let transform00 = TransformStamped {
475 header: Header {
476 frame_id: PARENT.to_string(),
477 stamp: time_from_nanosec(0),
478 },
479 child_frame_id: CHILD0.to_string(),
480 ..Default::default()
481 };
482 let transform01 = TransformStamped {
483 header: Header {
484 frame_id: PARENT.to_string(),
485 stamp: Time { sec: 1, nanosec: 0 },
486 },
487 child_frame_id: CHILD0.to_string(),
488 ..Default::default()
489 };
490 let transform02 = TransformStamped {
491 header: Header {
492 frame_id: PARENT.to_string(),
493 stamp: Time { sec: 2, nanosec: 0 },
494 },
495 child_frame_id: CHILD0.to_string(),
496 ..Default::default()
497 };
498 let transform0_key = TfGraphNode {
499 child: CHILD0.to_owned(),
500 parent: PARENT.to_owned(),
501 };
502
503 let static_tf = true;
504 tf_buffer.add_transform(&transform00, static_tf);
505 assert_eq!(tf_buffer.child_transform_index.len(), 1);
506 assert_eq!(tf_buffer.transform_data.len(), 1);
507 assert!(tf_buffer.transform_data.contains_key(&transform0_key));
508 let data = tf_buffer.transform_data.get(&transform0_key);
509 assert!(data.is_some());
510 assert_eq!(data.unwrap().transform_chain.len(), 1);
511 assert_eq!(
512 data.unwrap().transform_chain[0].header.stamp,
513 time_from_nanosec(0)
514 );
515
516 tf_buffer.add_transform(&transform01, static_tf);
517 assert_eq!(tf_buffer.child_transform_index.len(), 1);
518 assert_eq!(tf_buffer.transform_data.len(), 1);
519 assert!(tf_buffer.transform_data.contains_key(&transform0_key));
520 let data = tf_buffer.transform_data.get(&transform0_key);
521 assert!(data.is_some());
522 assert_eq!(data.unwrap().transform_chain.len(), 2);
523 assert_eq!(
524 data.unwrap().transform_chain[0].header.stamp,
525 time_from_nanosec(0)
526 );
527 assert_eq!(
528 data.unwrap().transform_chain[1].header.stamp,
529 time_from_nanosec(1_000_000_000)
530 );
531
532 tf_buffer.add_transform(&transform02, static_tf);
533 assert_eq!(tf_buffer.child_transform_index.len(), 1);
534 assert_eq!(tf_buffer.transform_data.len(), 1);
535 assert!(tf_buffer.transform_data.contains_key(&transform0_key));
536 let data = tf_buffer.transform_data.get(&transform0_key);
537 assert!(data.is_some());
538 assert_eq!(data.unwrap().transform_chain.len(), 2);
539 assert_eq!(
540 data.unwrap().transform_chain[0].header.stamp,
541 time_from_nanosec(1_000_000_000)
542 );
543 assert_eq!(
544 data.unwrap().transform_chain[1].header.stamp,
545 time_from_nanosec(2_000_000_000)
546 );
547 }
548
549 fn assert_approx_eq(msg1: TransformStamped, msg2: TransformStamped) {
550 assert_eq!(msg1.header, msg2.header);
551 assert_eq!(msg1.child_frame_id, msg2.child_frame_id);
552
553 assert!((msg1.transform.rotation.x - msg2.transform.rotation.x).abs() < 1e-9);
554 assert!((msg1.transform.rotation.y - msg2.transform.rotation.y).abs() < 1e-9);
555 assert!((msg1.transform.rotation.z - msg2.transform.rotation.z).abs() < 1e-9);
556 assert!((msg1.transform.rotation.w - msg2.transform.rotation.w).abs() < 1e-9);
557
558 assert!((msg1.transform.translation.x - msg2.transform.translation.x).abs() < 1e-9);
559 assert!((msg1.transform.translation.y - msg2.transform.translation.y).abs() < 1e-9);
560 assert!((msg1.transform.translation.z - msg2.transform.translation.z).abs() < 1e-9);
561 }
562
563 #[test]
568 fn test_dynamic_tree() {
569 let mut tf_buffer = TfBuffer::new();
570
571 let base_to_camera1 = TransformStamped {
572 child_frame_id: "camera1".to_string(),
573 header: Header {
574 frame_id: "base".to_string(),
575 stamp: Time { sec: 1, nanosec: 0 },
576 },
577 transform: Transform {
578 rotation: Quaternion {
579 x: 0.0,
580 y: 0.0,
581 z: 0.0,
582 w: 1.0,
583 },
584 translation: Vector3 {
585 x: 1.0,
586 y: 0.0,
587 z: 0.0,
588 },
589 },
590 };
591 tf_buffer.add_transform(&base_to_camera1, true);
592 tf_buffer.add_transform(&get_inverse(&base_to_camera1), true);
593
594 let base_to_camera2 = TransformStamped {
595 child_frame_id: "camera2".to_string(),
596 header: Header {
597 frame_id: "base".to_string(),
598 stamp: Time { sec: 1, nanosec: 0 },
599 },
600 transform: Transform {
601 rotation: Quaternion {
602 x: 0.0,
603 y: 0.0,
604 z: 1.0,
605 w: 0.0,
606 },
607 translation: Vector3 {
608 x: -1.0,
609 y: 0.0,
610 z: 0.0,
611 },
612 },
613 };
614 tf_buffer.add_transform(&base_to_camera2, true);
615 tf_buffer.add_transform(&get_inverse(&base_to_camera2), true);
616
617 let marker_to_target = TransformStamped {
618 child_frame_id: "target".to_string(),
619 header: Header {
620 frame_id: "marker".to_string(),
621 stamp: Time { sec: 1, nanosec: 0 },
622 },
623 transform: Transform {
624 rotation: Quaternion {
625 x: 0.0,
626 y: 0.0,
627 z: 0.0,
628 w: 1.0,
629 },
630 translation: Vector3 {
631 x: -0.5,
632 y: 0.0,
633 z: 0.0,
634 },
635 },
636 };
637 tf_buffer.add_transform(&marker_to_target, true);
638 tf_buffer.add_transform(&get_inverse(&marker_to_target), true);
639
640 let mut camera1_to_marker = TransformStamped {
641 child_frame_id: "marker".to_string(),
642 header: Header {
643 frame_id: "camera1".to_string(),
644 stamp: Time { sec: 1, nanosec: 0 },
645 },
646 transform: Transform {
647 rotation: Quaternion {
648 x: 0.0,
649 y: 0.0,
650 z: 0.0,
651 w: 1.0,
652 },
653 translation: Vector3 {
654 x: 1.0,
655 y: 1.0,
656 z: 0.0,
657 },
658 },
659 };
660 tf_buffer.add_transform(&camera1_to_marker, false);
661 tf_buffer.add_transform(&get_inverse(&camera1_to_marker), false);
662
663 camera1_to_marker.header.stamp.sec = 2;
664 camera1_to_marker.transform.translation.y = -1.0;
665 tf_buffer.add_transform(&camera1_to_marker, false);
666 tf_buffer.add_transform(&get_inverse(&camera1_to_marker), false);
667
668 let mut camera2_to_marker = TransformStamped {
669 child_frame_id: "marker".to_string(),
670 header: Header {
671 frame_id: "camera2".to_string(),
672 stamp: Time { sec: 3, nanosec: 0 },
673 },
674 transform: Transform {
675 rotation: Quaternion {
676 x: 0.0,
677 y: 0.0,
678 z: 0.0,
679 w: 1.0,
680 },
681 translation: Vector3 {
682 x: 1.0,
683 y: 1.0,
684 z: 0.0,
685 },
686 },
687 };
688 tf_buffer.add_transform(&camera2_to_marker, false);
689 tf_buffer.add_transform(&get_inverse(&camera2_to_marker), false);
690
691 camera2_to_marker.header.stamp.sec = 4;
692 camera2_to_marker.transform.translation.y = -1.0;
693 tf_buffer.add_transform(&camera2_to_marker, false);
694 tf_buffer.add_transform(&get_inverse(&camera2_to_marker), false);
695
696 let result = tf_buffer.lookup_transform("base", "target", &Time { sec: 1, nanosec: 0 });
697 assert_eq!(
698 result.unwrap().transform.translation,
699 Vector3 {
700 x: 1.5,
701 y: 1.0,
702 z: 0.0
703 }
704 );
705
706 let result = tf_buffer.lookup_transform(
707 "base",
708 "target",
709 &Time {
710 sec: 1,
711 nanosec: 500_000_000,
712 },
713 );
714 assert_eq!(
715 result.unwrap().transform.translation,
716 Vector3 {
717 x: 1.5,
718 y: 0.0,
719 z: 0.0
720 }
721 );
722
723 let result = tf_buffer.lookup_transform("base", "target", &Time { sec: 2, nanosec: 0 });
724 assert_eq!(
725 result.unwrap().transform.translation,
726 Vector3 {
727 x: 1.5,
728 y: -1.0,
729 z: 0.0
730 }
731 );
732
733 let result = tf_buffer.lookup_transform(
734 "base",
735 "target",
736 &Time {
737 sec: 2,
738 nanosec: 500_000_000,
739 },
740 );
741 assert!(result.is_err());
742
743 let result = tf_buffer.lookup_transform("base", "target", &Time { sec: 3, nanosec: 0 });
744 assert_eq!(
745 result.unwrap().transform.translation,
746 Vector3 {
747 x: -1.5,
748 y: -1.0,
749 z: 0.0
750 }
751 );
752
753 let result = tf_buffer.lookup_transform(
754 "base",
755 "target",
756 &Time {
757 sec: 3,
758 nanosec: 500_000_000,
759 },
760 );
761 assert_eq!(
762 result.unwrap().transform.translation,
763 Vector3 {
764 x: -1.5,
765 y: -0.0,
766 z: 0.0
767 }
768 );
769
770 let result = tf_buffer.lookup_transform("base", "target", &Time { sec: 4, nanosec: 0 });
771 assert_eq!(
772 result.unwrap().transform.translation,
773 Vector3 {
774 x: -1.5,
775 y: 1.0,
776 z: 0.0
777 }
778 );
779
780 let result = tf_buffer.lookup_transform(
781 "base",
782 "target",
783 &Time {
784 sec: 4,
785 nanosec: 500_000_000,
786 },
787 );
788 assert!(result.is_err());
789
790 camera1_to_marker.header.stamp.sec = 5;
791 camera1_to_marker.transform.translation.x = 0.5;
792 camera1_to_marker.transform.translation.y = 1.0;
793 tf_buffer.add_transform(&camera1_to_marker, false);
794 tf_buffer.add_transform(&get_inverse(&camera1_to_marker), false);
795
796 camera1_to_marker.header.stamp.sec = 6;
797 camera1_to_marker.transform.translation.y = -1.0;
798 tf_buffer.add_transform(&camera1_to_marker, false);
799 tf_buffer.add_transform(&get_inverse(&camera1_to_marker), false);
800
801 let result = tf_buffer.lookup_transform("base", "target", &Time { sec: 5, nanosec: 0 });
802 assert_eq!(
803 result.unwrap().transform.translation,
804 Vector3 {
805 x: 1.0,
806 y: 1.0,
807 z: 0.0
808 }
809 );
810
811 let result = tf_buffer.lookup_transform(
812 "base",
813 "target",
814 &Time {
815 sec: 5,
816 nanosec: 500_000_000,
817 },
818 );
819 assert_eq!(
820 result.unwrap().transform.translation,
821 Vector3 {
822 x: 1.0,
823 y: 0.0,
824 z: 0.0
825 }
826 );
827
828 let result = tf_buffer.lookup_transform("base", "target", &Time { sec: 6, nanosec: 0 });
829 assert_eq!(
830 result.unwrap().transform.translation,
831 Vector3 {
832 x: 1.0,
833 y: -1.0,
834 z: 0.0
835 }
836 );
837 }
838}