Skip to content

Commit 017e6dd

Browse files
committed
First working version of the rust path planning
1 parent 3800327 commit 017e6dd

4 files changed

Lines changed: 83 additions & 90 deletions

File tree

src/bitbots_navigation/bitbots_path_planning/config/path_planning.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ bitbots_path_planning:
55
rate: 20.0
66

77
map:
8-
planning_frame: map
8+
frame: map
99
robot_update_topic: robots_relative_filtered
1010
ball_diameter: 0.13
1111
inflation:

src/bitbots_navigation/bitbots_path_planning/src/controller.rs

Lines changed: 26 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ pub enum ControllerStepError {
3737
LocalizationError(#[from] TfError),
3838
}
3939

40-
pub struct Controller<'a> {
41-
tf: &'a TfListener,
40+
pub struct Controller {
4241
params: Arc<StdMutex<Params>>,
4342
}
4443

@@ -53,9 +52,9 @@ fn get_yaw_from_quaternion(quaternion: &Quaternion) -> f64 {
5352
.0 as f64
5453
}
5554

56-
impl<'a> Controller<'a> {
57-
pub fn new(tf: &'a TfListener, params: Arc<StdMutex<Params>>) -> Self {
58-
Self { tf, params }
55+
impl Controller {
56+
pub fn new(params: Arc<StdMutex<Params>>) -> Self {
57+
Self { params }
5958
}
6059

6160
// Calculates a command velocity based on a given path
@@ -64,15 +63,12 @@ impl<'a> Controller<'a> {
6463

6564
let params = self.params.lock().unwrap().clone();
6665

67-
let my_position = self
68-
.tf
69-
.lookup_transform(
70-
&params.map.frame,
71-
&params.base_footprint_frame,
72-
Time::default(),
73-
)
74-
.map_err(|e| ControllerStepError::LocalizationError(e))?
75-
.transform;
66+
let my_position = path
67+
.poses
68+
.first()
69+
.expect("Path must contain at least one pose")
70+
.pose
71+
.clone();
7672

7773
let end_pose = &path
7874
.poses
@@ -88,59 +84,43 @@ impl<'a> Controller<'a> {
8884
}
8985
};
9086

91-
let walk_angle = (goal_pose.position.y - my_position.translation.y)
92-
.atan2(goal_pose.position.x - my_position.translation.x);
87+
let walk_angle = (goal_pose.position.y - my_position.position.y)
88+
.atan2(goal_pose.position.x - my_position.position.x);
9389

9490
let distance = if path.poses.len() < 3 {
95-
(end_pose.position.x - my_position.translation.x)
96-
.hypot(end_pose.position.y - my_position.translation.y)
91+
(end_pose.position.x - my_position.position.x)
92+
.hypot(end_pose.position.y - my_position.position.y)
9793
} else {
9894
path.poses
9995
.iter()
10096
.fold(
101-
(0.0, my_position.translation),
97+
(0.0, my_position.position),
10298
|(distance, last_pose), pose| {
10399
let dx = pose.pose.position.x - last_pose.x;
104100
let dy = pose.pose.position.y - last_pose.y;
105-
(
106-
distance + dx.hypot(dy),
107-
Vector3 {
108-
x: pose.pose.position.x,
109-
y: pose.pose.position.y,
110-
z: 0.0,
111-
},
112-
)
101+
(distance + dx.hypot(dy), pose.pose.position.clone())
113102
},
114103
)
115104
.0
116105
};
117106

118-
let walk_vel = distance
119-
* params.controller.translation_slow_down_factor;
107+
let walk_vel = distance * params.controller.translation_slow_down_factor;
120108

121-
let diff = if distance
122-
> params
123-
.controller
124-
.orient_to_goal_distance
125-
{
126-
walk_angle - get_yaw_from_quaternion(&my_position.rotation)
109+
let diff = if distance > params.controller.orient_to_goal_distance {
110+
walk_angle - get_yaw_from_quaternion(&my_position.orientation)
127111
} else {
128112
get_yaw_from_quaternion(&end_pose.orientation)
129-
- get_yaw_from_quaternion(&my_position.rotation)
113+
- get_yaw_from_quaternion(&my_position.orientation)
130114
};
131115

132-
let min_angle = diff % std::f64::consts::TAU; // TODO check math / remainder implementation
116+
let min_angle = diff.sin().atan2(diff.cos());
133117

134-
cmd_vel.angular.z = (min_angle
135-
* params
136-
.controller
137-
.rotation_slow_down_factor)
138-
.clamp(
139-
-params.controller.max_rotation_vel,
140-
params.controller.max_rotation_vel,
141-
);
118+
cmd_vel.angular.z = (min_angle * params.controller.rotation_slow_down_factor).clamp(
119+
-params.controller.max_rotation_vel,
120+
params.controller.max_rotation_vel,
121+
);
142122

143-
let local_heading = walk_angle - get_yaw_from_quaternion(&my_position.rotation);
123+
let local_heading = walk_angle - get_yaw_from_quaternion(&my_position.orientation);
144124
let local_heading_vector_x = local_heading.cos();
145125
let local_heading_vector_y = local_heading.sin();
146126

src/bitbots_navigation/bitbots_path_planning/src/main.rs

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
6060
let mut avoid_ball_sub = node
6161
.subscribe::<Bool>("ball_obstacle_active", r2r::QosProfile::default())?
6262
.fuse();
63+
let mut pose_sub = node
64+
.subscribe::<r2r::geometry_msgs::msg::PoseWithCovarianceStamped>(
65+
"/pose_with_covariance",
66+
r2r::QosProfile::default(),
67+
)?
68+
.fuse();
6369

6470
let cmd_vel_pub = node.create_publisher::<Twist>("cmd_vel", r2r::QosProfile::default())?;
6571
let path_pub = node.create_publisher::<Path>("path", r2r::QosProfile::default())?;
@@ -71,45 +77,46 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
7177
1. / params.lock().unwrap().rate,
7278
))?;
7379

74-
let tf_listener = tf_r2r::TfListener::new(&mut node);
75-
7680
// Share the node between tasks / threads
7781
let node = Arc::new(Mutex::new(node));
7882
let spin_node = node.clone();
7983

8084
tokio::task::spawn(async move {
8185
// Define a thread-local state
82-
let mut planner = Planner::new(&tf_listener, params.clone());
83-
let controller = Controller::new(&tf_listener, params.clone());
86+
let mut planner = Planner::new(params.clone());
87+
let controller = Controller::new(params.clone());
8488

8589
// Handle events
8690
loop {
8791
tokio::select! {
92+
Some(msg) = pose_sub.next() => {
93+
planner.update_own_pose(msg);
94+
},
8895
_ = timer.tick() => {
89-
r2r::log_warn!(&nl, "Planner step");
9096
if planner.active() {
91-
r2r::log_warn!(&nl, "Planner active");
9297
// Calculate the path to the goal pose considering the current map
93-
match planner.step() {
98+
let path = match planner.step() {
9499
Err(e) => {
95100
r2r::log_warn!(&nl, "Planner step failed: {:}", e);
101+
continue;
96102
},
97103
Ok(path) => {
98-
r2r::log_warn!(&nl, "Got path");
99104
// Publish the path for visualization
100105
path_pub.publish(&path).expect("Publishing the path failed");
101-
// Calculate the command velocity to follow the given path
102-
match controller.step(&path) {
103-
Err(e) => {
104-
r2r::log_warn!(&nl, "Controller step failed: {:}", e);
105-
},
106-
Ok((cmd_vel, carrot_point)) => {
107-
// Publish the walk command to control the robot
108-
cmd_vel_pub.publish(&cmd_vel).expect("Publishing command velocity failed");
109-
// Publish the carrot point for visualization
110-
carrot_pub.publish(&carrot_point).expect("Publishing carrot failed");
111-
}
112-
}
106+
path
107+
}
108+
};
109+
// Calculate the command velocity to follow the given path
110+
match controller.step(&path) {
111+
Err(e) => {
112+
r2r::log_warn!(&nl, "Controller step failed: {:}", e);
113+
continue;
114+
},
115+
Ok((cmd_vel, carrot_point)) => {
116+
// Publish the walk command to control the robot
117+
cmd_vel_pub.publish(&cmd_vel).expect("Publishing command velocity failed");
118+
// Publish the carrot point for visualization
119+
carrot_pub.publish(&carrot_point).expect("Publishing carrot failed");
113120
}
114121
}
115122
}

src/bitbots_navigation/bitbots_path_planning/src/planner.rs

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use bitbots_rust_nav::map::ObstacleMapConfig;
77
use bitbots_rust_nav::obstacle::RoundObstacle;
88
use r2r::builtin_interfaces::msg::Time;
99
use r2r::geometry_msgs::msg::PoseStamped;
10+
use r2r::geometry_msgs::msg::PoseWithCovarianceStamped;
1011
use r2r::nav_msgs::msg::Path;
1112
use r2r::std_msgs::msg::Header;
1213
use r2r::RosParams;
@@ -32,25 +33,25 @@ pub struct InflationParams {
3233
pub obstacle_margin: f64,
3334
}
3435

35-
pub struct Planner<'a> {
36-
tf: &'a TfListener,
36+
pub struct Planner {
37+
current_pose: Option<PoseStamped>,
3738
goal: Option<PoseStamped>,
3839
ball_obstacle_active: bool,
3940
params: Arc<StdMutex<Params>>,
4041
}
4142

4243
#[derive(Error, Debug)]
4344
pub enum PlannerStepError {
44-
#[error("Could not determine the robot location. Tf error: {0}")]
45-
LocalizationError(#[from] TfError),
45+
#[error("Could not determine the robot location.")]
46+
LocalizationError(),
4647
#[error("No goal was given when step was called")]
4748
NoGoalError(),
4849
}
4950

50-
impl<'a> Planner<'a> {
51-
pub fn new(tf: &'a TfListener, params: Arc<StdMutex<Params>>) -> Self {
51+
impl Planner {
52+
pub fn new(params: Arc<StdMutex<Params>>) -> Self {
5253
Self {
53-
tf,
54+
current_pose: None,
5455
goal: None,
5556
ball_obstacle_active: false,
5657
params,
@@ -79,50 +80,55 @@ impl<'a> Planner<'a> {
7980
self.ball_obstacle_active = state;
8081
}
8182

83+
// Updates the current robot pose
84+
pub fn update_own_pose(&mut self, pose: PoseWithCovarianceStamped) {
85+
self.current_pose = Some(PoseStamped {
86+
header: pose.header,
87+
pose: pose.pose.pose,
88+
});
89+
}
90+
8291
// Compute the next path to the goal
8392
pub fn step(&self) -> Result<Path, PlannerStepError> {
8493
// Check if we have a goal
8594
let goal = self.goal.as_ref().ok_or(PlannerStepError::NoGoalError())?;
8695

8796
let params = self.params.lock().unwrap().clone();
8897

89-
r2r::log_warn!("bitbots_path_planning", "Fetched goal {:?}", params.map.inflation.robot_radius);
90-
9198
// Get our current position
92-
let my_position = self
93-
.tf
94-
.lookup_transform(&params.map.frame, &params.base_footprint_frame, Time::default())
95-
.map_err(|e| PlannerStepError::LocalizationError(e))?
96-
.transform
97-
.translation;
98-
99-
r2r::log_warn!("bitbots_path_planning", "Fetched our position");
99+
let my_pose = self
100+
.current_pose
101+
.as_ref()
102+
.ok_or(PlannerStepError::LocalizationError())?
103+
.clone(); // Todo handle timeout
100104

101105
let map_config = ObstacleMapConfig::new(
102106
params.map.inflation.robot_radius,
103107
params.map.inflation.obstacle_margin,
104108
12,
105109
);
106110

107-
r2r::log_warn!("bitbots_path_planning", "Made obstacle map config");
108-
109111
let obstacles = vec![];
110112
// TODO add robot to map
111113

112114
// TODO add ball to map
113115

114-
let path = ObstacleMap::new(map_config, obstacles)
116+
let path: Vec<_> = ObstacleMap::new(map_config, obstacles)
115117
.shortest_path(
116-
(my_position.x, my_position.y),
117-
(goal.pose.position.x, goal.pose.position.x),
118+
(my_pose.pose.position.x, my_pose.pose.position.y),
119+
(goal.pose.position.x, goal.pose.position.y),
118120
)
119121
.iter()
120122
.map(|position| {
121123
let mut pose = PoseStamped::default();
122124
pose.pose.position.x = position.0;
123125
pose.pose.position.y = position.1;
124-
pose
126+
pose.clone()
125127
})
128+
.collect();
129+
130+
let path: Vec<PoseStamped> = std::iter::once(my_pose)
131+
.chain(path)
126132
.chain([goal.clone()])
127133
.collect();
128134

0 commit comments

Comments
 (0)