Skip to content

Commit 461a7ba

Browse files
authored
Add a timeout to the wait for transforms step of the costmap activation. (#3866)
* Add a timeout to the wait for transforms step of the costmap activation. Signed-off-by: Fabian König <fabiankoenig@gmail.com> * Rename wait_for_transforms_timeout to initial_transform_timeout * Activate costmap publishers only after transforms are checked * Check if controller server activation was succesful in planner_server * Add unittest for costmap activation Signed-off-by: Fabian König <fabiankoenig@gmail.com> --------- Signed-off-by: Fabian König <fabiankoenig@gmail.com>
1 parent 63fc6a1 commit 461a7ba

File tree

6 files changed

+94
-13
lines changed

6 files changed

+94
-13
lines changed

‎nav2_controller/src/controller_server.cpp‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,10 @@ ControllerServer::on_activate(const rclcpp_lifecycle::State & /*state*/)
242242
{
243243
RCLCPP_INFO(get_logger(), "Activating");
244244

245-
costmap_ros_->activate();
245+
const auto costmap_ros_state = costmap_ros_->activate();
246+
if (costmap_ros_state.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE) {
247+
return nav2_util::CallbackReturn::FAILURE;
248+
}
246249
ControllerMap::iterator it;
247250
for (it = controllers_.begin(); it != controllers_.end(); ++it) {
248251
it->second->activate();

‎nav2_costmap_2d/include/nav2_costmap_2d/costmap_2d_ros.hpp‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ class Costmap2DROS : public nav2_util::LifecycleNode
360360
bool always_send_full_costmap_{false};
361361
std::string footprint_;
362362
float footprint_padding_{0};
363-
std::string global_frame_; ///< The global frame for the costmap
363+
std::string global_frame_; ///< The global frame for the costmap
364364
int map_height_meters_{0};
365365
double map_publish_frequency_{0};
366366
double map_update_frequency_{0};
@@ -374,11 +374,12 @@ class Costmap2DROS : public nav2_util::LifecycleNode
374374
std::vector<std::string> filter_names_;
375375
std::vector<std::string> filter_types_;
376376
double resolution_{0};
377-
std::string robot_base_frame_; ///< The frame_id of the robot base
377+
std::string robot_base_frame_; ///< The frame_id of the robot base
378378
double robot_radius_;
379-
bool rolling_window_{false}; ///< Whether to use a rolling window version of the costmap
379+
bool rolling_window_{false}; ///< Whether to use a rolling window version of the costmap
380380
bool track_unknown_space_{false};
381-
double transform_tolerance_{0}; ///< The timeout before transform errors
381+
double transform_tolerance_{0}; ///< The timeout before transform errors
382+
double initial_transform_timeout_{0}; ///< The timeout before activation of the node errors
382383

383384
// Derived parameters
384385
bool use_radius_{false};

‎nav2_costmap_2d/src/costmap_2d_ros.cpp‎

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ void Costmap2DROS::init()
129129
declare_parameter("rolling_window", rclcpp::ParameterValue(false));
130130
declare_parameter("track_unknown_space", rclcpp::ParameterValue(false));
131131
declare_parameter("transform_tolerance", rclcpp::ParameterValue(0.3));
132+
declare_parameter("initial_transform_timeout", rclcpp::ParameterValue(60.0));
132133
declare_parameter("trinary_costmap", rclcpp::ParameterValue(true));
133134
declare_parameter("unknown_cost_value", rclcpp::ParameterValue(static_cast<unsigned char>(0xff)));
134135
declare_parameter("update_frequency", rclcpp::ParameterValue(5.0));
@@ -265,20 +266,16 @@ Costmap2DROS::on_activate(const rclcpp_lifecycle::State & /*state*/)
265266
{
266267
RCLCPP_INFO(get_logger(), "Activating");
267268

268-
footprint_pub_->on_activate();
269-
costmap_publisher_->on_activate();
270-
271-
for (auto & layer_pub : layer_publishers_) {
272-
layer_pub->on_activate();
273-
}
274-
275269
// First, make sure that the transform between the robot base frame
276270
// and the global frame is available
277271

278272
std::string tf_error;
279273

280274
RCLCPP_INFO(get_logger(), "Checking transform");
281275
rclcpp::Rate r(2);
276+
const auto initial_transform_timeout = rclcpp::Duration::from_seconds(
277+
initial_transform_timeout_);
278+
const auto initial_transform_timeout_point = now() + initial_transform_timeout;
282279
while (rclcpp::ok() &&
283280
!tf_buffer_->canTransform(
284281
global_frame_, robot_base_frame_, tf2::TimePointZero, &tf_error))
@@ -288,12 +285,29 @@ Costmap2DROS::on_activate(const rclcpp_lifecycle::State & /*state*/)
288285
" to become available, tf error: %s",
289286
robot_base_frame_.c_str(), global_frame_.c_str(), tf_error.c_str());
290287

288+
// Check timeout
289+
if (now() > initial_transform_timeout_point) {
290+
RCLCPP_ERROR(
291+
get_logger(), "Failed to activate %s because transform from %s to %s did not become available before timeout",
292+
get_name(), robot_base_frame_.c_str(), global_frame_.c_str());
293+
294+
return nav2_util::CallbackReturn::FAILURE;
295+
}
296+
291297
// The error string will accumulate and errors will typically be the same, so the last
292298
// will do for the warning above. Reset the string here to avoid accumulation
293299
tf_error.clear();
294300
r.sleep();
295301
}
296302

303+
// Activate publishers
304+
footprint_pub_->on_activate();
305+
costmap_publisher_->on_activate();
306+
307+
for (auto & layer_pub : layer_publishers_) {
308+
layer_pub->on_activate();
309+
}
310+
297311
// Create a thread to handle updating the map
298312
stopped_ = true; // to active plugins
299313
stop_updates_ = false;
@@ -386,6 +400,7 @@ Costmap2DROS::getParameters()
386400
get_parameter("rolling_window", rolling_window_);
387401
get_parameter("track_unknown_space", track_unknown_space_);
388402
get_parameter("transform_tolerance", transform_tolerance_);
403+
get_parameter("initial_transform_timeout", initial_transform_timeout_);
389404
get_parameter("update_frequency", map_update_frequency_);
390405
get_parameter("width", map_width_meters_);
391406
get_parameter("plugins", plugin_names_);

‎nav2_costmap_2d/test/unit/CMakeLists.txt‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,8 @@ ament_add_gtest(denoise_layer_test denoise_layer_test.cpp image_test.cpp image_p
5555
target_link_libraries(denoise_layer_test
5656
nav2_costmap_2d_core layers
5757
)
58+
59+
ament_add_gtest(lifecycle_test lifecycle_test.cpp)
60+
target_link_libraries(lifecycle_test
61+
nav2_costmap_2d_core
62+
)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Licensed under the Apache License, Version 2.0 (the "License");
2+
// you may not use this file except in compliance with the License.
3+
// You may obtain a copy of the License at
4+
//
5+
// http://www.apache.org/licenses/LICENSE-2.0
6+
//
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
13+
#include <memory>
14+
15+
#include "gtest/gtest.h"
16+
17+
#include "nav2_costmap_2d/costmap_2d_ros.hpp"
18+
#include "rclcpp/rclcpp.hpp"
19+
#include "lifecycle_msgs/msg/state.hpp"
20+
21+
22+
TEST(LifecylceTest, CheckInitialTfTimeout) {
23+
rclcpp::init(0, nullptr);
24+
25+
auto costmap = std::make_shared<nav2_costmap_2d::Costmap2DROS>("test_costmap");
26+
costmap->set_parameter({"initial_transform_timeout", 0.0});
27+
28+
std::thread spin_thread{[costmap]() {rclcpp::spin(costmap->get_node_base_interface());}};
29+
30+
{
31+
const auto state_after_configure = costmap->configure();
32+
ASSERT_EQ(state_after_configure.id(), lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE);
33+
// Without providing the transform from global to robot base the activation should fail
34+
// and the costmap should transition into the inactive state.
35+
const auto state_after_activate = costmap->activate();
36+
ASSERT_EQ(state_after_activate.id(), lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE);
37+
}
38+
39+
// Set a dummy transform from global to robot base
40+
geometry_msgs::msg::TransformStamped transform_global_to_robot{};
41+
transform_global_to_robot.header.frame_id = costmap->getGlobalFrameID();
42+
transform_global_to_robot.child_frame_id = costmap->getBaseFrameID();
43+
costmap->getTfBuffer()->setTransform(transform_global_to_robot, "test", true);
44+
// Now the costmap should successful transition into the active state
45+
{
46+
const auto state_after_activate = costmap->activate();
47+
ASSERT_EQ(state_after_activate.id(), lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE);
48+
}
49+
50+
rclcpp::shutdown();
51+
if (spin_thread.joinable()) {
52+
spin_thread.join();
53+
}
54+
}

‎nav2_planner/src/planner_server.cpp‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,10 @@ PlannerServer::on_activate(const rclcpp_lifecycle::State & /*state*/)
174174
plan_publisher_->on_activate();
175175
action_server_pose_->activate();
176176
action_server_poses_->activate();
177-
costmap_ros_->activate();
177+
const auto costmap_ros_state = costmap_ros_->activate();
178+
if (costmap_ros_state.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE) {
179+
return nav2_util::CallbackReturn::FAILURE;
180+
}
178181

179182
PlannerMap::iterator it;
180183
for (it = planners_.begin(); it != planners_.end(); ++it) {

0 commit comments

Comments
 (0)