openrr_teleop/
move_base.rs

1use arci::{
2    gamepad::{Axis, Button, GamepadEvent},
3    BaseVelocity, MoveBase,
4};
5use async_trait::async_trait;
6use parking_lot::Mutex;
7
8use super::control_mode::ControlMode;
9
10const BASE_LINEAR_VEL_AXIS_GAIN: f64 = 0.5;
11const BASE_ANGULAR_VEL_AXIS_GAIN: f64 = 1.5;
12const BASE_TURBO_GAIN: f64 = 2.0;
13
14struct MoveBaseModeInner {
15    vel: BaseVelocity,
16    is_enabled: bool,
17    is_turbo: bool,
18}
19
20impl MoveBaseModeInner {
21    fn new() -> Self {
22        Self {
23            vel: BaseVelocity::default(),
24            is_enabled: false,
25            is_turbo: false,
26        }
27    }
28
29    fn handle_event(&mut self, ev: GamepadEvent) -> bool {
30        let mut should_stop = false;
31        match ev {
32            GamepadEvent::AxisChanged(Axis::LeftStickX, v) => {
33                self.vel.y = v * BASE_LINEAR_VEL_AXIS_GAIN
34            }
35            GamepadEvent::AxisChanged(Axis::LeftStickY, v) => {
36                self.vel.x = v * BASE_LINEAR_VEL_AXIS_GAIN
37            }
38            GamepadEvent::AxisChanged(Axis::RightStickX, v) => {
39                self.vel.theta = v * BASE_ANGULAR_VEL_AXIS_GAIN
40            }
41            GamepadEvent::ButtonPressed(Button::RightTrigger2) => {
42                self.is_enabled = true;
43            }
44            GamepadEvent::ButtonReleased(Button::RightTrigger2) => {
45                self.is_enabled = false;
46                self.vel = BaseVelocity::default();
47                should_stop = true;
48            }
49            GamepadEvent::ButtonPressed(Button::LeftTrigger2) => {
50                self.is_turbo = true;
51            }
52            GamepadEvent::ButtonReleased(Button::LeftTrigger2) => {
53                self.is_turbo = false;
54            }
55            GamepadEvent::Disconnected => {
56                self.is_enabled = false;
57                self.is_turbo = false;
58                self.vel = BaseVelocity::default();
59                should_stop = true;
60            }
61            _ => {}
62        }
63        should_stop
64    }
65
66    fn get_target_velocity(&self) -> Option<BaseVelocity> {
67        if self.is_enabled {
68            if self.is_turbo {
69                let turbo_vel = self.vel * BASE_TURBO_GAIN;
70                Some(turbo_vel)
71            } else {
72                Some(self.vel)
73            }
74        } else {
75            None
76        }
77    }
78}
79
80pub struct MoveBaseMode<T: MoveBase> {
81    move_base: T,
82    mode: String,
83    submode: String,
84    inner: Mutex<MoveBaseModeInner>,
85}
86
87impl<T> MoveBaseMode<T>
88where
89    T: MoveBase,
90{
91    pub fn new(mode: String, move_base: T) -> Self {
92        Self {
93            move_base,
94            mode,
95            submode: "".to_string(),
96            inner: Mutex::new(MoveBaseModeInner::new()),
97        }
98    }
99}
100
101#[async_trait]
102impl<T> ControlMode for MoveBaseMode<T>
103where
104    T: MoveBase,
105{
106    fn handle_event(&self, ev: GamepadEvent) {
107        if self.inner.lock().handle_event(ev) {
108            // stop immediately
109            self.move_base
110                .send_velocity(&BaseVelocity::default())
111                .unwrap();
112        }
113    }
114
115    async fn proc(&self) {
116        if let Some(v) = self.inner.lock().get_target_velocity() {
117            self.move_base.send_velocity(&v).unwrap();
118        }
119    }
120
121    fn mode(&self) -> &str {
122        &self.mode
123    }
124
125    fn submode(&self) -> String {
126        self.submode.to_owned()
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use arci::DummyMoveBase;
133    use assert_approx_eq::*;
134
135    use super::*;
136
137    #[test]
138    fn test_move_mode_new() {
139        let mode_name = String::from("tested");
140        let base = DummyMoveBase::new();
141        let mode = MoveBaseMode::new(mode_name.clone(), base);
142
143        assert_eq!(
144            format!("{:?}", mode.move_base),
145            format!("{:?}", DummyMoveBase::new())
146        );
147        assert_eq!(mode.mode, mode_name);
148        assert_eq!(mode.submode, String::from(""));
149        assert_eq!(
150            format!("{:?}", mode.inner.lock().vel),
151            format!("{:?}", BaseVelocity::default())
152        );
153        assert!(!mode.inner.lock().is_enabled);
154        assert!(!mode.inner.lock().is_turbo);
155    }
156
157    #[test]
158    fn test_move_mode_get() {
159        let mode_name = String::from("tested");
160        let base = DummyMoveBase::new();
161        let mode = MoveBaseMode::new(mode_name.clone(), base);
162
163        let base_mode = mode.mode();
164        assert_eq!(base_mode, mode_name);
165        let base_sub_mode = mode.submode();
166        assert_eq!(base_sub_mode, String::from(""));
167    }
168
169    #[tokio::test]
170    async fn test_move_mode_proc() {
171        let mode_name = String::from("tested");
172        const X: f64 = 1.2;
173        const Y: f64 = 3.5;
174        const THETA: f64 = 1.8;
175        let mode = MoveBaseMode {
176            move_base: DummyMoveBase::new(),
177            mode: mode_name.clone(),
178            submode: "".to_string(),
179            inner: Mutex::new(MoveBaseModeInner {
180                vel: BaseVelocity {
181                    x: X,
182                    y: Y,
183                    theta: THETA,
184                },
185                is_enabled: false,
186                is_turbo: false,
187            }),
188        };
189        mode.proc().await;
190        let current = mode.move_base.current_velocity().unwrap();
191        assert_approx_eq!(current.x, 0.0);
192        assert_approx_eq!(current.y, 0.0);
193        assert_approx_eq!(current.theta, 0.0);
194        println!("{:?} {current:?}", mode.inner.lock().vel);
195
196        let mode = MoveBaseMode {
197            move_base: DummyMoveBase::new(),
198            mode: mode_name.clone(),
199            submode: "".to_string(),
200            inner: Mutex::new(MoveBaseModeInner {
201                vel: BaseVelocity {
202                    x: 1.2,
203                    y: 3.5,
204                    theta: 1.8,
205                },
206                is_enabled: false,
207                is_turbo: true,
208            }),
209        };
210        mode.proc().await;
211        let current = mode.move_base.current_velocity().unwrap();
212        assert_approx_eq!(current.x, 0.0);
213        assert_approx_eq!(current.y, 0.0);
214        assert_approx_eq!(current.theta, 0.0);
215        println!("{:?} {current:?}", mode.inner.lock().vel);
216
217        let mode = MoveBaseMode {
218            move_base: DummyMoveBase::new(),
219            mode: mode_name.clone(),
220            submode: "".to_string(),
221            inner: Mutex::new(MoveBaseModeInner {
222                vel: BaseVelocity {
223                    x: 1.2,
224                    y: 3.5,
225                    theta: 1.8,
226                },
227                is_enabled: true,
228                is_turbo: false,
229            }),
230        };
231        mode.proc().await;
232        let current = mode.move_base.current_velocity().unwrap();
233        assert_approx_eq!(current.x, X);
234        assert_approx_eq!(current.y, Y);
235        assert_approx_eq!(current.theta, THETA);
236        println!("{:?} {current:?}", mode.inner.lock().vel);
237
238        let mode = MoveBaseMode {
239            move_base: DummyMoveBase::new(),
240            mode: mode_name.clone(),
241            submode: "".to_string(),
242            inner: Mutex::new(MoveBaseModeInner {
243                vel: BaseVelocity {
244                    x: 1.2_f64,
245                    y: 3.5,
246                    theta: 1.8,
247                },
248                is_enabled: true,
249                is_turbo: true,
250            }),
251        };
252        mode.proc().await;
253        let current = mode.move_base.current_velocity().unwrap();
254        assert_approx_eq!(current.x, X * 2.0);
255        assert_approx_eq!(current.y, Y * 2.0);
256        assert_approx_eq!(current.theta, THETA * 2.0);
257        println!("{:?} {current:?}", mode.inner.lock().vel);
258    }
259}