Skip to content

Commit d755910

Browse files
AGVFM-537 AGV Naviation: create logic to calculate the length for w2mo path using SMAC (#82)
* Ability to get a path from the planner from any 2 poses (start_pose and goal): add use_start_pose and start_pose to ComputePathToPose action (ros-navigation#2179) * add use_start_pose to ComputePathToPose.action * add use_start_pose to ComputePathToPose bt * start instead of start_pose + else if * [ComputePathToPose Action API break] change 'pose' for 'goal' +use_start * test wip * add compute path to pose BT test for use_start * last start_pose renaming * Transform start and goal in costmap frame * Revert "Transform start and goal in costmap frame" This reverts commit 4ed8eb0. * lint * AGVFM-537 AGV Naviation: create logic to calculate the length for w2mo path using SMAC -> fixed incorrect cherry pick, fixed compute_path_to_pose_python_action.cpp; * Fix ros-navigation#2186 (ros-navigation#2222) * Fix ros-navigation#2186 * goal_pose instead of goal->goal * avoid redundant code * change name and switch to input + output param pattern * code style * Fix ros-navigation#2186 (ros-navigation#2222) * Fix ros-navigation#2186 * goal_pose instead of goal->goal * avoid redundant code * change name and switch to input + output param pattern * code style * fixed comments; * Revert "fixed comments;" This reverts commit 412464b1 Co-authored-by: G.Doisy <doisyg@users.noreply.github.com>
1 parent 53b49b7 commit d755910

File tree

12 files changed

+172
-31
lines changed

12 files changed

+172
-31
lines changed

nav2_behavior_tree/include/nav2_behavior_tree/plugins/action/compute_path_to_pose_action.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ class ComputePathToPoseAction : public BtActionNode<nav2_msgs::action::ComputePa
4242
{
4343
BT::OutputPort<nav_msgs::msg::Path>("path", "Path created by ComputePathToPose node"),
4444
BT::InputPort<geometry_msgs::msg::PoseStamped>("goal", "Destination to plan to"),
45+
BT::InputPort<geometry_msgs::msg::PoseStamped>(
46+
"start", "Start pose of the path if overriding current robot pose"),
4547
BT::InputPort<std::string>("planner_id", ""),
4648
});
4749
}

nav2_behavior_tree/nav2_tree_nodes.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
<Action ID="ComputePathToPose">
2020
<input_port name="goal">Destination to plan to</input_port>
21+
<input_port name="start">Start pose of the path if overriding current robot pose</input_port>
2122
<output_port name="path">Path created by ComputePathToPose node</output_port>
2223
<input_port name="planner_id"/>
2324
</Action>

nav2_behavior_tree/plugins/action/compute_path_to_pose_action.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@ ComputePathToPoseAction::ComputePathToPoseAction(
3030

3131
void ComputePathToPoseAction::on_tick()
3232
{
33-
getInput("goal", goal_.pose);
33+
getInput("goal", goal_.goal);
3434
getInput("planner_id", goal_.planner_id);
35+
if (getInput("start", goal_.start)) {
36+
goal_.use_start = true;
37+
}
3538
}
3639

3740
BT::NodeStatus ComputePathToPoseAction::on_success()

nav2_behavior_tree/plugins/action/compute_path_to_pose_python_action.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ namespace nav2_behavior_tree
3131

3232
void ComputePathToPoseActionPython::on_tick()
3333
{
34-
getInput("goal", goal_.pose);
34+
getInput("goal", goal_.goal);
3535
getInput("planner_id", goal_.planner_id);
3636
}
3737

nav2_behavior_tree/test/plugins/action/test_compute_path_to_pose_action.cpp

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,13 @@ class ComputePathToPoseActionServer : public TestActionServer<nav2_msgs::action:
4141
{
4242
const auto goal = goal_handle->get_goal();
4343
auto result = std::make_shared<nav2_msgs::action::ComputePathToPose::Result>();
44-
result->path.poses.resize(1);
45-
result->path.poses[0].pose.position.x = goal->pose.pose.position.x;
44+
result->path.poses.resize(2);
45+
result->path.poses[1].pose.position.x = goal->goal.pose.position.x;
46+
if (goal->use_start) {
47+
result->path.poses[0].pose.position.x = goal->start.pose.position.x;
48+
} else {
49+
result->path.poses[0].pose.position.x = 0.0;
50+
}
4651
goal_handle->succeed(result);
4752
}
4853
};
@@ -133,7 +138,8 @@ TEST_F(ComputePathToPoseActionTestFixture, test_tick)
133138
EXPECT_EQ(config_->blackboard->get<bool>("path_updated"), false);
134139
EXPECT_EQ(tree_->rootNode()->executeTick(), BT::NodeStatus::RUNNING);
135140
EXPECT_EQ(tree_->rootNode()->getInput<std::string>("planner_id"), std::string("GridBased"));
136-
EXPECT_EQ(action_server_->getCurrentGoal()->pose.pose.position.x, 1.0);
141+
EXPECT_EQ(action_server_->getCurrentGoal()->goal.pose.position.x, 1.0);
142+
EXPECT_FALSE(action_server_->getCurrentGoal()->use_start);
137143
EXPECT_EQ(action_server_->getCurrentGoal()->planner_id, std::string("GridBased"));
138144

139145
// tick until node succeeds
@@ -147,8 +153,9 @@ TEST_F(ComputePathToPoseActionTestFixture, test_tick)
147153
// check if returned path is correct
148154
nav_msgs::msg::Path path;
149155
config_->blackboard->get<nav_msgs::msg::Path>("path", path);
150-
EXPECT_EQ(path.poses.size(), 1u);
151-
EXPECT_EQ(path.poses[0].pose.position.x, 1.0);
156+
EXPECT_EQ(path.poses.size(), 2u);
157+
EXPECT_EQ(path.poses[0].pose.position.x, 0.0);
158+
EXPECT_EQ(path.poses[1].pose.position.x, 1.0);
152159

153160
// halt node so another goal can be sent
154161
tree_->rootNode()->halt();
@@ -159,18 +166,89 @@ TEST_F(ComputePathToPoseActionTestFixture, test_tick)
159166
config_->blackboard->set("goal", goal);
160167

161168
EXPECT_EQ(tree_->rootNode()->executeTick(), BT::NodeStatus::RUNNING);
162-
EXPECT_EQ(action_server_->getCurrentGoal()->pose.pose.position.x, -2.5);
169+
EXPECT_EQ(action_server_->getCurrentGoal()->goal.pose.position.x, -2.5);
163170

164171
while (tree_->rootNode()->status() != BT::NodeStatus::SUCCESS) {
165172
tree_->rootNode()->executeTick();
166173
}
167174

175+
EXPECT_EQ(tree_->rootNode()->status(), BT::NodeStatus::SUCCESS);
176+
EXPECT_EQ(action_server_->getCurrentGoal()->goal.pose.position.x, -2.5);
168177
// path is updated on new goal
169178
EXPECT_EQ(config_->blackboard->get<bool>("path_updated"), true);
170179

171180
config_->blackboard->get<nav_msgs::msg::Path>("path", path);
172-
EXPECT_EQ(path.poses.size(), 1u);
173-
EXPECT_EQ(path.poses[0].pose.position.x, -2.5);
181+
EXPECT_EQ(path.poses.size(), 2u);
182+
EXPECT_EQ(path.poses[0].pose.position.x, 0.0);
183+
EXPECT_EQ(path.poses[1].pose.position.x, -2.5);
184+
}
185+
186+
TEST_F(ComputePathToPoseActionTestFixture, test_tick_use_start)
187+
{
188+
// create tree
189+
std::string xml_txt =
190+
R"(
191+
<root main_tree_to_execute = "MainTree" >
192+
<BehaviorTree ID="MainTree">
193+
<ComputePathToPose goal="{goal}" start="{start}" path="{path}" planner_id="GridBased"/>
194+
</BehaviorTree>
195+
</root>)";
196+
197+
tree_ = std::make_shared<BT::Tree>(factory_->createTreeFromText(xml_txt, config_->blackboard));
198+
199+
// create new start and set it on blackboard
200+
geometry_msgs::msg::PoseStamped start;
201+
start.header.stamp = node_->now();
202+
start.pose.position.x = 2.0;
203+
config_->blackboard->set("start", start);
204+
205+
// create new goal and set it on blackboard
206+
geometry_msgs::msg::PoseStamped goal;
207+
goal.header.stamp = node_->now();
208+
goal.pose.position.x = 1.0;
209+
config_->blackboard->set("goal", goal);
210+
211+
// tick until node succeeds
212+
while (tree_->rootNode()->status() != BT::NodeStatus::SUCCESS) {
213+
tree_->rootNode()->executeTick();
214+
}
215+
216+
// the goal should have reached our server
217+
EXPECT_EQ(tree_->rootNode()->status(), BT::NodeStatus::SUCCESS);
218+
EXPECT_EQ(tree_->rootNode()->getInput<std::string>("planner_id"), std::string("GridBased"));
219+
EXPECT_EQ(action_server_->getCurrentGoal()->goal.pose.position.x, 1.0);
220+
EXPECT_EQ(action_server_->getCurrentGoal()->start.pose.position.x, 2.0);
221+
EXPECT_TRUE(action_server_->getCurrentGoal()->use_start);
222+
EXPECT_EQ(action_server_->getCurrentGoal()->planner_id, std::string("GridBased"));
223+
224+
// check if returned path is correct
225+
nav_msgs::msg::Path path;
226+
config_->blackboard->get<nav_msgs::msg::Path>("path", path);
227+
EXPECT_EQ(path.poses.size(), 2u);
228+
EXPECT_EQ(path.poses[0].pose.position.x, 2.0);
229+
EXPECT_EQ(path.poses[1].pose.position.x, 1.0);
230+
231+
// halt node so another goal can be sent
232+
tree_->rootNode()->halt();
233+
EXPECT_EQ(tree_->rootNode()->status(), BT::NodeStatus::IDLE);
234+
235+
// set new goal and new start
236+
goal.pose.position.x = -2.5;
237+
start.pose.position.x = -1.5;
238+
config_->blackboard->set("goal", goal);
239+
config_->blackboard->set("start", start);
240+
241+
while (tree_->rootNode()->status() != BT::NodeStatus::SUCCESS) {
242+
tree_->rootNode()->executeTick();
243+
}
244+
245+
EXPECT_EQ(tree_->rootNode()->status(), BT::NodeStatus::SUCCESS);
246+
EXPECT_EQ(action_server_->getCurrentGoal()->goal.pose.position.x, -2.5);
247+
248+
config_->blackboard->get<nav_msgs::msg::Path>("path", path);
249+
EXPECT_EQ(path.poses.size(), 2u);
250+
EXPECT_EQ(path.poses[0].pose.position.x, -1.5);
251+
EXPECT_EQ(path.poses[1].pose.position.x, -2.5);
174252
}
175253

176254
int main(int argc, char ** argv)

nav2_costmap_2d/include/nav2_costmap_2d/costmap_2d_ros.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,16 @@ class Costmap2DROS : public nav2_util::LifecycleNode
140140
*/
141141
bool getRobotPose(geometry_msgs::msg::PoseStamped & global_pose);
142142

143+
/**
144+
* @brief Transform the input_pose in the global frame of the costmap
145+
* @param input_pose pose to be transformed
146+
* @param transformed_pose pose transformed
147+
* @return True if the pose was transformed successfully, false otherwise
148+
*/
149+
bool transformPoseToGlobalFrame(
150+
const geometry_msgs::msg::PoseStamped & input_pose,
151+
geometry_msgs::msg::PoseStamped & transformed_pose);
152+
143153
/** @brief Returns costmap name */
144154
std::string getName() const
145155
{

nav2_costmap_2d/src/costmap_2d_ros.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,21 @@ Costmap2DROS::getRobotPose(geometry_msgs::msg::PoseStamped & global_pose)
548548
}
549549
}
550550

551+
bool
552+
Costmap2DROS::transformPoseToGlobalFrame(
553+
const geometry_msgs::msg::PoseStamped & input_pose,
554+
geometry_msgs::msg::PoseStamped & transformed_pose)
555+
{
556+
if (input_pose.header.frame_id == global_frame_) {
557+
transformed_pose = input_pose;
558+
return true;
559+
} else {
560+
return nav2_util::transformPoseInTargetFrame(
561+
input_pose, transformed_pose, *tf_buffer_,
562+
global_frame_, transform_tolerance_);
563+
}
564+
}
565+
551566
bool
552567
Costmap2DROS::is_static_layer_costmap_initialized()
553568
{

nav2_msgs/action/ComputePathToPose.action

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#goal definition
2-
geometry_msgs/PoseStamped pose
2+
geometry_msgs/PoseStamped goal
3+
geometry_msgs/PoseStamped start
34
string planner_id
5+
bool use_start # If true, use current robot pose as path start, if false, use start above instead
46
---
57
#result definition
68
nav_msgs/Path path

nav2_planner/src/planner_server.cpp

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -222,32 +222,46 @@ PlannerServer::computePlan()
222222
}
223223

224224
geometry_msgs::msg::PoseStamped start;
225-
if (!costmap_ros_->getRobotPose(start)) {
225+
if (goal->use_start) {
226+
start = goal->start;
227+
} else if (!costmap_ros_->getRobotPose(start)) {
226228
error_publisher_.publish("Couldn't get robot current position", 27);
227229
action_server_->terminate_current();
228230
return;
229231
}
230232

233+
// Changing the start and goal pose frame to the global_frame_ of costmap_ros_ if needed
234+
geometry_msgs::msg::PoseStamped goal_pose = goal->goal;
235+
geometry_msgs::msg::PoseStamped start_pose = start;
236+
if (!costmap_ros_->transformPoseToGlobalFrame(start, start_pose) ||
237+
!costmap_ros_->transformPoseToGlobalFrame(goal->goal, goal_pose))
238+
{
239+
RCLCPP_WARN(
240+
get_logger(), "Could not transform the start or goal pose in the costmap frame");
241+
action_server_->terminate_current();
242+
return;
243+
}
244+
231245
if (action_server_->is_preempt_requested()) {
232246
goal = action_server_->accept_pending_goal();
233247
}
234248

235-
result->path = getPlan(start, goal->pose, goal->planner_id);
249+
result->path = getPlan(start_pose, goal_pose, goal->planner_id);
236250

237251
if (result->path.poses.size() == 0) {
238252
RCLCPP_WARN(
239253
get_logger(), "Planning algorithm %s failed to generate a valid"
240254
" path from (%.2f, %.2f) to (%.2f, %.2f)", goal->planner_id.c_str(),
241-
start.pose.position.x, start.pose.position.y, goal->pose.pose.position.x, goal->pose.pose.position.y);
255+
start_pose.pose.position.x, start_pose.pose.position.y, goal_pose.pose.position.x, goal_pose.pose.position.y);
242256
action_server_->terminate_current();
243257
return;
244258
}
245259

246260
RCLCPP_DEBUG(
247261
get_logger(),
248-
"Found valid path of size %u to (%.2f, %.2f)",
249-
result->path.poses.size(), goal->pose.pose.position.x,
250-
goal->pose.pose.position.y);
262+
"Found valid path of size %lu to (%.2f, %.2f)",
263+
result->path.poses.size(), goal_pose.pose.position.x,
264+
goal_pose.pose.position.y);
251265

252266
// Publish the plan for visualization purposes
253267
publishPlan(result->path);
@@ -266,8 +280,8 @@ PlannerServer::computePlan()
266280
} catch (std::exception & ex) {
267281
RCLCPP_WARN(
268282
get_logger(), "%s plugin failed to plan calculation to (%.2f, %.2f): \"%s\"",
269-
goal->planner_id.c_str(), goal->pose.pose.position.x,
270-
goal->pose.pose.position.y, ex.what());
283+
goal->planner_id.c_str(), goal->goal.pose.position.x,
284+
goal->goal.pose.position.y, ex.what());
271285
error_publisher_.publish(goal->planner_id + " plugin failed to plan calculation: " + ex.what(), 25);
272286
// TODO(orduno): provide information about fail error to parent task,
273287
// for example: couldn't get costmap update

nav2_util/include/nav2_util/robot_utils.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ bool getCurrentPose(
3636
tf2_ros::Buffer & tf_buffer, const std::string global_frame = "map",
3737
const std::string robot_frame = "base_link", const double transform_timeout = 0.1);
3838

39+
bool transformPoseInTargetFrame(
40+
const geometry_msgs::msg::PoseStamped & input_pose,
41+
geometry_msgs::msg::PoseStamped & transformed_pose,
42+
tf2_ros::Buffer & tf_buffer, const std::string target_frame,
43+
const double transform_timeout = 0.1);
44+
3945
} // end namespace nav2_util
4046

4147
#endif // NAV2_UTIL__ROBOT_UTILS_HPP_

0 commit comments

Comments
 (0)