openrr_client/clients/
local_move.rs
1use arci::{BaseVelocity, MoveBase, Navigation};
2use k::nalgebra as na;
3use na::Isometry2;
4use serde::{Deserialize, Serialize};
5
6use crate::Error;
7
8#[derive(Debug, Serialize, Deserialize, Clone)]
9#[serde(deny_unknown_fields)]
10pub struct LocalMoveConfig {
11 pub reach_distance_threshold: f64,
12 pub reach_angle_threshold: f64,
13 pub control_frequency: f64,
14 pub linear_gain: f64,
15 pub angular_gain: f64,
16 pub max_linear_vel: f64,
17 pub max_angular_vel: f64,
18}
19
20impl LocalMoveConfig {
21 pub fn new<P: AsRef<std::path::Path>>(path: P) -> Result<Self, Error> {
22 let config: LocalMoveConfig = toml::from_str(
23 &std::fs::read_to_string(&path)
24 .map_err(|e| Error::NoFile(path.as_ref().to_owned(), e))?,
25 )
26 .map_err(|e| Error::TomlParseFailure(path.as_ref().to_owned(), e))?;
27 Ok(config)
28 }
29}
30
31#[derive(Debug)]
32pub struct LocalMove<N, M>
33where
34 N: Navigation,
35 M: MoveBase,
36{
37 pub nav_client: N,
38 pub vel_client: M,
39 pub config: LocalMoveConfig,
40}
41
42impl<N, M> LocalMove<N, M>
43where
44 N: Navigation,
45 M: MoveBase,
46{
47 pub fn new(nav_client: N, vel_client: M, config: LocalMoveConfig) -> Self {
48 Self {
49 nav_client,
50 vel_client,
51 config,
52 }
53 }
54
55 pub fn is_reached(&self, pose_error: Isometry2<f64>) -> bool {
56 pose_error.translation.vector.norm() < self.config.reach_distance_threshold
57 && pose_error.rotation.angle().abs() < self.config.reach_angle_threshold
58 }
59
60 pub fn send_zero_velocity(&self) -> Result<(), arci::Error> {
61 self.vel_client
62 .send_velocity(&BaseVelocity::new(0.0, 0.0, 0.0))
63 }
64
65 pub fn send_control_velocity_from_pose_error(
66 &self,
67 pose_error: Isometry2<f64>,
68 ) -> Result<(), arci::Error> {
69 let vel_x = na::clamp(
72 self.config.linear_gain * pose_error.translation.vector[0],
73 -self.config.max_linear_vel,
74 self.config.max_linear_vel,
75 );
76 let vel_y = na::clamp(
77 self.config.linear_gain * pose_error.translation.vector[1],
78 -self.config.max_linear_vel,
79 self.config.max_linear_vel,
80 );
81 let vel_theta = na::clamp(
82 self.config.angular_gain * pose_error.rotation.angle(),
83 -self.config.max_angular_vel,
84 self.config.max_angular_vel,
85 );
86
87 self.vel_client
88 .send_velocity(&BaseVelocity::new(vel_x, vel_y, vel_theta))
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use arci::{DummyMoveBase, DummyNavigation};
95 use assert_approx_eq::assert_approx_eq;
96 use na::Vector2;
97
98 use super::*;
99
100 #[test]
101 fn test_config() {
102 let path = std::path::Path::new("tests/local_move_sample.toml");
103 let config = LocalMoveConfig::new(path).unwrap();
104 assert_approx_eq!(config.reach_distance_threshold, 0.1);
105 assert_approx_eq!(config.control_frequency, 10.0);
106 assert_approx_eq!(config.linear_gain, 1.0);
107 assert_approx_eq!(config.max_linear_vel, 1.0);
108 }
109
110 #[test]
111 fn test() {
112 let path = std::path::Path::new("tests/local_move_sample.toml");
113 let config = LocalMoveConfig::new(path).unwrap();
114 assert_approx_eq!(config.linear_gain, 1.0);
115 let move_base = DummyMoveBase::new();
116 let nav = DummyNavigation::new();
117 let local_move = LocalMove::new(Box::new(nav), Box::new(move_base), config);
118
119 assert!(local_move.is_reached(Isometry2::new(Vector2::new(0.0, 0.0), 0.0)));
121 assert!(local_move.is_reached(Isometry2::new(Vector2::new(0.05, 0.05), 0.05)));
122 assert!(!local_move.is_reached(Isometry2::new(Vector2::new(0.11, 0.0), 0.0))); assert!(!local_move.is_reached(Isometry2::new(Vector2::new(0.0, 0.0), 0.11))); local_move
127 .send_control_velocity_from_pose_error(Isometry2::new(Vector2::new(1.0, -1.0), 1.0))
128 .unwrap();
129 let vel0 = local_move.vel_client.current_velocity().unwrap();
130 assert_approx_eq!(vel0.x, 1.0); assert_approx_eq!(vel0.y, -1.0); assert_approx_eq!(vel0.theta, 1.0); local_move
135 .send_control_velocity_from_pose_error(Isometry2::new(Vector2::new(2.0, -2.0), 2.0))
136 .unwrap(); let vel1 = local_move.vel_client.current_velocity().unwrap();
138 assert_approx_eq!(vel1.x, 1.0); assert_approx_eq!(vel1.y, -1.0); assert_approx_eq!(vel1.theta, 1.0); local_move.send_zero_velocity().unwrap();
144 let vel2 = local_move.vel_client.current_velocity().unwrap();
145 assert_approx_eq!(vel2.x, 0.0);
146 assert_approx_eq!(vel2.y, 0.0);
147 assert_approx_eq!(vel2.theta, 0.0);
148 }
149}