Skip to content

Commit 3800327

Browse files
committed
Current state of path planning migration
1 parent ac6e7ab commit 3800327

5 files changed

Lines changed: 117 additions & 59 deletions

File tree

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
bitbots_path_planning:
2+
ros__parameters:
3+
base_footprint_frame: base_footprint
4+
tf_buffer_duration: 5.0
5+
rate: 20.0
6+
7+
map:
8+
planning_frame: map
9+
robot_update_topic: robots_relative_filtered
10+
ball_diameter: 0.13
11+
inflation:
12+
robot_radius: 0.3
13+
obstacle_margin: 0.1
14+
15+
controller:
16+
carrot_distance: 1
17+
max_rotation_vel: 0.4
18+
max_vel_x: 0.12
19+
min_vel_x: -0.06
20+
max_vel_y: 0.07
21+
smoothing_tau: 0.1
22+
rotation_i_factor: 0.0
23+
rotation_slow_down_factor: 0.6
24+
translation_slow_down_factor: 0.6
25+
orient_to_goal_distance: 0.5
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<launch>
22
<arg name="sim" default="false" description="true: activates simulation time" />
33

4-
<node pkg="bitbots_path_planning" exec="path_planning" name="bitbots_path_planning" output="screen">
4+
<node pkg="bitbots_path_planning" exec="bitbots_path_planning" name="bitbots_path_planning" output="screen">
55
<param name="use_sim_time" value="$(var sim)"/>
6+
<param from="$(find-pkg-share bitbots_path_planning)/config/path_planning.yaml"/>
67
</node>
78
</launch>

src/bitbots_navigation/bitbots_path_planning/src/controller.rs

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ use crate::Params;
1111

1212
use thiserror::Error;
1313

14-
#[derive(RosParams, Default, Debug)]
14+
#[derive(RosParams, Default, Debug, Clone)]
1515
pub struct ControllerParams {
1616
// The distance to the carrot that we want to reach on the path
17-
carrot_distance: f32,
17+
carrot_distance: i32,
1818
// The maximum rotation velocity of the robot in rad/s around the z-axis
1919
max_rotation_vel: f64,
2020
// Clamped p gain of the rotation controller
@@ -23,6 +23,12 @@ pub struct ControllerParams {
2323
translation_slow_down_factor: f64,
2424
// Distance at which we switch from orienting towards the path to orienting towards the goal poses orientation (in meters)
2525
orient_to_goal_distance: f64,
26+
// Maximum velocity we want to reach in different directions (base_footprint coordinate system)
27+
max_vel_x: f64,
28+
// Minimum velocity we want to reach in different directions (base_footprint coordinate system)
29+
min_vel_x: f64,
30+
// Maximum velocity we want to reach in different directions (base_footprint coordinate system)
31+
max_vel_y: f64,
2632
}
2733

2834
#[derive(Error, Debug)]
@@ -31,8 +37,8 @@ pub enum ControllerStepError {
3137
LocalizationError(#[from] TfError),
3238
}
3339

34-
pub struct Controller {
35-
tf: Arc<Mutex<TfListener>>,
40+
pub struct Controller<'a> {
41+
tf: &'a TfListener,
3642
params: Arc<StdMutex<Params>>,
3743
}
3844

@@ -47,22 +53,22 @@ fn get_yaw_from_quaternion(quaternion: &Quaternion) -> f64 {
4753
.0 as f64
4854
}
4955

50-
impl Controller {
51-
pub fn new(tf: Arc<Mutex<TfListener>>, params: Arc<StdMutex<Params>>) -> Self {
56+
impl<'a> Controller<'a> {
57+
pub fn new(tf: &'a TfListener, params: Arc<StdMutex<Params>>) -> Self {
5258
Self { tf, params }
5359
}
5460

5561
// Calculates a command velocity based on a given path
56-
pub async fn step(&self, path: &Path) -> Result<(Twist, PointStamped), ControllerStepError> {
62+
pub fn step(&self, path: &Path) -> Result<(Twist, PointStamped), ControllerStepError> {
5763
let mut cmd_vel = Twist::default();
5864

65+
let params = self.params.lock().unwrap().clone();
66+
5967
let my_position = self
6068
.tf
61-
.lock()
62-
.await
6369
.lookup_transform(
64-
&self.params.lock().unwrap().map.frame,
65-
&self.params.lock().unwrap().base_footprint_frame,
70+
&params.map.frame,
71+
&params.base_footprint_frame,
6672
Time::default(),
6773
)
6874
.map_err(|e| ControllerStepError::LocalizationError(e))?
@@ -74,7 +80,7 @@ impl Controller {
7480
.expect("Expect path to contain at least one pose")
7581
.pose;
7682
let goal_pose = {
77-
let carrot_distance = self.params.lock().unwrap().controller.carrot_distance as usize;
83+
let carrot_distance = params.controller.carrot_distance as usize;
7884
if path.poses.len() > carrot_distance {
7985
&path.poses[path.poses.len() - carrot_distance].pose
8086
} else {
@@ -110,18 +116,10 @@ impl Controller {
110116
};
111117

112118
let walk_vel = distance
113-
* self
114-
.params
115-
.lock()
116-
.unwrap()
117-
.controller
118-
.translation_slow_down_factor;
119+
* params.controller.translation_slow_down_factor;
119120

120121
let diff = if distance
121-
> self
122-
.params
123-
.lock()
124-
.unwrap()
122+
> params
125123
.controller
126124
.orient_to_goal_distance
127125
{
@@ -134,21 +132,41 @@ impl Controller {
134132
let min_angle = diff % std::f64::consts::TAU; // TODO check math / remainder implementation
135133

136134
cmd_vel.angular.z = (min_angle
137-
* self
138-
.params
139-
.lock()
140-
.unwrap()
135+
* params
141136
.controller
142137
.rotation_slow_down_factor)
143138
.clamp(
144-
-self.params.lock().unwrap().controller.max_rotation_vel,
145-
self.params.lock().unwrap().controller.max_rotation_vel,
139+
-params.controller.max_rotation_vel,
140+
params.controller.max_rotation_vel,
146141
);
147142

148143
let local_heading = walk_angle - get_yaw_from_quaternion(&my_position.rotation);
149144
let local_heading_vector_x = local_heading.cos();
150145
let local_heading_vector_y = local_heading.sin();
151146

147+
// Returns interpolates the x and y maximum velocity linearly based on our heading
148+
// See https://github.com/bit-bots/bitbots_main/issues/366#issuecomment-2161460917
149+
fn interpolate_max_vel(x_limit: f64, y_limit: f64, target_x: f64, target_y: f64) -> f64 {
150+
let n = (x_limit * target_y.abs()) + (y_limit * target_x);
151+
let intersection_x = (x_limit * y_limit * target_x) / n;
152+
let intersection_y = (x_limit * y_limit * target_y.abs()) / n;
153+
intersection_x.hypot(intersection_y)
154+
}
155+
156+
let walk_vel = walk_vel.min(interpolate_max_vel(
157+
if local_heading_vector_x > 0. {
158+
params.controller.max_vel_x
159+
} else {
160+
params.controller.min_vel_x
161+
},
162+
params.controller.max_vel_y,
163+
local_heading_vector_x,
164+
local_heading_vector_y,
165+
));
166+
167+
cmd_vel.linear.x = walk_vel * local_heading_vector_x;
168+
cmd_vel.linear.y = walk_vel * local_heading_vector_y;
169+
152170
let carrot_point = PointStamped {
153171
header: path.header.clone(),
154172
point: goal_pose.position.clone(),

src/bitbots_navigation/bitbots_path_planning/src/main.rs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use r2r::RosParams;
1414
mod controller;
1515
mod planner;
1616

17-
#[derive(RosParams, Default, Debug)]
17+
#[derive(RosParams, Default, Debug, Clone)]
1818
pub struct Params {
1919
// The rate at which the path planning is executed
2020
pub rate: f32,
@@ -71,32 +71,35 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
7171
1. / params.lock().unwrap().rate,
7272
))?;
7373

74-
let tf_listener = Arc::new(Mutex::new(tf_r2r::TfListener::new(&mut node)));
74+
let tf_listener = tf_r2r::TfListener::new(&mut node);
7575

7676
// Share the node between tasks / threads
7777
let node = Arc::new(Mutex::new(node));
7878
let spin_node = node.clone();
7979

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

8585
// Handle events
8686
loop {
8787
tokio::select! {
8888
_ = timer.tick() => {
89+
r2r::log_warn!(&nl, "Planner step");
8990
if planner.active() {
91+
r2r::log_warn!(&nl, "Planner active");
9092
// Calculate the path to the goal pose considering the current map
91-
match planner.step().await {
93+
match planner.step() {
9294
Err(e) => {
9395
r2r::log_warn!(&nl, "Planner step failed: {:}", e);
9496
},
9597
Ok(path) => {
98+
r2r::log_warn!(&nl, "Got path");
9699
// Publish the path for visualization
97100
path_pub.publish(&path).expect("Publishing the path failed");
98101
// Calculate the command velocity to follow the given path
99-
match controller.step(&path).await {
102+
match controller.step(&path) {
100103
Err(e) => {
101104
r2r::log_warn!(&nl, "Controller step failed: {:}", e);
102105
},
@@ -112,6 +115,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
112115
}
113116
},
114117
Some(msg) = goal_sub.next() => {
118+
r2r::log_warn!(&nl, "Got goal");
115119
planner.set_goal(msg);
116120
},
117121
_ = cancel_sub.next() => {
@@ -124,11 +128,19 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
124128
}
125129
});
126130

127-
// Spin the underlying rcl node object
128-
loop {
129-
spin_node
130-
.lock()
131-
.await
132-
.spin_once(std::time::Duration::from_millis(100));
133-
}
131+
// Spin the node
132+
tokio::task::spawn(async move {
133+
loop {
134+
spin_node
135+
.lock()
136+
.await
137+
.spin_once(std::time::Duration::from_millis(100));
138+
tokio::task::yield_now().await; // Let the other tasks run too
139+
}
140+
});
141+
142+
// Await shutdown signal
143+
tokio::signal::ctrl_c().await?;
144+
145+
Ok(())
134146
}

src/bitbots_navigation/bitbots_path_planning/src/planner.rs

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use tf_r2r::{TfError, TfListener};
1414
use thiserror::Error;
1515
use tokio::sync::Mutex;
1616

17-
#[derive(RosParams, Default, Debug)]
17+
#[derive(RosParams, Default, Debug, Clone)]
1818
pub struct MapParams {
1919
// The frame in which the path planning is done
2020
pub frame: String,
@@ -24,31 +24,31 @@ pub struct MapParams {
2424
pub inflation: InflationParams,
2525
}
2626

27-
#[derive(RosParams, Default, Debug)]
27+
#[derive(RosParams, Default, Debug, Clone)]
2828
pub struct InflationParams {
2929
// Radius of a circle on the ground that represents the space occupied by our robot. Instead of planning with both a robot polygon/circle and an obstacle polygon, we just inflate the obstacles and assume the robot is a point. This is faster and simpler
3030
pub robot_radius: f64,
3131
// Distance we want to keep to obstacles when planning a path around them. No immediate action is required if the robot is closer than this distance to an obstacle, but we don't consider paths this close during the visibility graph generation.
3232
pub obstacle_margin: f64,
3333
}
3434

35-
pub struct Planner {
36-
tf: Arc<Mutex<TfListener>>,
35+
pub struct Planner<'a> {
36+
tf: &'a TfListener,
3737
goal: Option<PoseStamped>,
3838
ball_obstacle_active: bool,
3939
params: Arc<StdMutex<Params>>,
4040
}
4141

4242
#[derive(Error, Debug)]
4343
pub enum PlannerStepError {
44-
#[error("Could not determine the robot location")]
44+
#[error("Could not determine the robot location. Tf error: {0}")]
4545
LocalizationError(#[from] TfError),
4646
#[error("No goal was given when step was called")]
4747
NoGoalError(),
4848
}
4949

50-
impl Planner {
51-
pub fn new(tf: Arc<Mutex<TfListener>>, params: Arc<StdMutex<Params>>) -> Self {
50+
impl<'a> Planner<'a> {
51+
pub fn new(tf: &'a TfListener, params: Arc<StdMutex<Params>>) -> Self {
5252
Self {
5353
tf,
5454
goal: None,
@@ -80,30 +80,32 @@ impl Planner {
8080
}
8181

8282
// Compute the next path to the goal
83-
pub async fn step(&self) -> Result<Path, PlannerStepError> {
83+
pub fn step(&self) -> Result<Path, PlannerStepError> {
8484
// Check if we have a goal
8585
let goal = self.goal.as_ref().ok_or(PlannerStepError::NoGoalError())?;
8686

87+
let params = self.params.lock().unwrap().clone();
88+
89+
r2r::log_warn!("bitbots_path_planning", "Fetched goal {:?}", params.map.inflation.robot_radius);
90+
8791
// Get our current position
8892
let my_position = self
8993
.tf
90-
.lock()
91-
.await
92-
.lookup_transform(
93-
&self.params.lock().unwrap().map.frame,
94-
&self.params.lock().unwrap().base_footprint_frame,
95-
Time::default(),
96-
)
94+
.lookup_transform(&params.map.frame, &params.base_footprint_frame, Time::default())
9795
.map_err(|e| PlannerStepError::LocalizationError(e))?
9896
.transform
9997
.translation;
10098

99+
r2r::log_warn!("bitbots_path_planning", "Fetched our position");
100+
101101
let map_config = ObstacleMapConfig::new(
102-
self.params.lock().unwrap().map.inflation.robot_radius,
103-
self.params.lock().unwrap().map.inflation.obstacle_margin,
102+
params.map.inflation.robot_radius,
103+
params.map.inflation.obstacle_margin,
104104
12,
105105
);
106106

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

0 commit comments

Comments
 (0)