openrr_base/
odometry.rs
1use std::sync::Mutex;
2
3use arci::{BaseVelocity, Isometry2, Vector2};
4
5use crate::Error;
6
7const DEFAULT_TIMEOUT_MILLIS: u128 = 5000;
8
9#[derive(Debug)]
10pub struct Odometry {
11 position: Mutex<Option<Isometry2<f64>>>,
13 last_update_timestamp: Mutex<Option<std::time::Instant>>,
14 timeout_millis: Mutex<u128>,
15}
16
17impl Odometry {
18 pub fn new_from_pose(position: Isometry2<f64>) -> Self {
19 Self {
20 position: Mutex::new(Some(position)),
21 last_update_timestamp: Mutex::new(None),
22 timeout_millis: Mutex::new(DEFAULT_TIMEOUT_MILLIS),
24 }
25 }
26
27 pub fn update_by_velocity(&self, velocity: &BaseVelocity) -> Result<(), Error> {
28 let mut position = self.position.lock().unwrap();
29 let mut locked_update_time = self.last_update_timestamp.lock().unwrap();
30 let timeout_millis = *self.timeout_millis.lock().unwrap();
31
32 let last_update_time = match *locked_update_time {
33 Some(t) => t,
34 None => std::time::Instant::now(),
35 };
36
37 match *position {
38 Some(p) => {
39 if last_update_time.elapsed().as_millis() > timeout_millis && timeout_millis != 0 {
40 *position = None;
41 *locked_update_time = Some(std::time::Instant::now());
42 Err(Error::CurrentPositionUnknown)
43 } else {
44 let dt = last_update_time.elapsed().as_secs_f64();
45 let dx = Isometry2::new(
46 Vector2::new(velocity.x * dt, velocity.y * dt),
47 velocity.theta * dt,
48 );
49
50 *position = Some(p * dx);
51 *locked_update_time = Some(std::time::Instant::now());
52
53 Ok(())
54 }
55 }
56 None => Err(Error::CurrentPositionUnknown),
57 }
58 }
59
60 pub fn resolve_lost(&self, current_pose: Isometry2<f64>) {
62 let mut pose = self.position.lock().unwrap();
63 let mut update_time = self.last_update_timestamp.lock().unwrap();
64
65 *pose = Some(current_pose);
66 *update_time = Some(std::time::Instant::now());
67 }
68
69 pub fn set_timeout_millis(&self, millis: u128) {
70 let mut timeout_millis = self.timeout_millis.lock().unwrap();
71 *timeout_millis = millis;
72 }
73
74 pub fn current_pose(&self) -> Result<Isometry2<f64>, Error> {
75 match self.position.lock().unwrap().to_owned() {
76 Some(pose) => Ok(pose),
77 None => Err(Error::CurrentPositionUnknown),
78 }
79 }
80}
81
82impl Default for Odometry {
83 fn default() -> Self {
84 Self {
85 position: Mutex::new(None),
86 last_update_timestamp: Mutex::new(None),
87 timeout_millis: Mutex::new(DEFAULT_TIMEOUT_MILLIS),
88 }
89 }
90}
91
92#[cfg(test)]
93mod test {
94 use super::*;
95
96 #[test]
97 fn test_base_odometry() {
98 let vel = BaseVelocity::new(100.0, 200.0, 100.0 * std::f64::consts::FRAC_PI_2);
99 let odom = Odometry::new_from_pose(Isometry2::default());
100
101 std::thread::sleep(std::time::Duration::from_millis(10));
102
103 odom.update_by_velocity(&vel).unwrap();
104 let pose = odom.current_pose();
105
106 assert!(
107 pose.as_ref().unwrap().translation.x > 1.00
108 || pose.as_ref().unwrap().translation.x < 1.05
109 );
110 assert!(
111 pose.as_ref().unwrap().translation.y > 2.00
112 || pose.as_ref().unwrap().translation.y < 2.10
113 );
114 }
115}