38 #include "nav2_costmap_2d/costmap_filters/speed_filter.hpp"
46 #include "nav2_costmap_2d/costmap_filters/filter_values.hpp"
47 #include "nav2_util/occ_grid_utils.hpp"
53 : filter_info_sub_(nullptr), mask_sub_(nullptr),
54 speed_limit_pub_(nullptr), filter_mask_(nullptr), global_frame_(
""),
55 speed_limit_(NO_SPEED_LIMIT), speed_limit_prev_(NO_SPEED_LIMIT)
60 const std::string & filter_info_topic)
62 std::lock_guard<CostmapFilter::mutex_t> guard(*
getMutex());
64 nav2::LifecycleNode::SharedPtr node = node_.lock();
66 throw std::runtime_error{
"Failed to lock node"};
70 std::string speed_limit_topic = node->declare_or_get_parameter(name_ +
"." +
"speed_limit_topic",
71 std::string(
"speed_limit"));
75 enable_path_lookahead_ = node->declare_or_get_parameter(
76 name_ +
"." +
"enable_path_lookahead",
false);
77 max_decel_ = node->declare_or_get_parameter(
78 name_ +
"." +
"max_decel", -0.5);
79 min_lookahead_ = node->declare_or_get_parameter(
80 name_ +
"." +
"min_lookahead", 0.3);
81 max_lookahead_ = node->declare_or_get_parameter(
82 name_ +
"." +
"max_lookahead", 5.0);
83 std::string path_topic = node->declare_or_get_parameter(
84 name_ +
"." +
"path_topic", std::string(
"plan"));
85 std::string odom_topic = node->declare_or_get_parameter(
86 name_ +
"." +
"odom_topic", std::string(
"odom"));
89 if (enable_path_lookahead_) {
90 if (max_decel_ >= 0.0) {
93 "SpeedFilter: max_decel should be negative,"
94 "lookahead distance will be set to max_lookahead instead");
96 if (min_lookahead_ < 0.0) {
99 "SpeedFilter: min_lookahead = %f is negative,"
100 "clamping to 0.0m", min_lookahead_);
101 min_lookahead_ = 0.0;
103 if (max_lookahead_ < min_lookahead_) {
106 "SpeedFilter: max_lookahead = %f is less than min_lookahead = %f,"
107 "clamping to min_lookahead.",
108 max_lookahead_, min_lookahead_);
109 max_lookahead_ = min_lookahead_;
117 "SpeedFilter: Subscribing to \"%s\" topic for filter info...",
119 filter_info_sub_ = node->create_subscription<nav2_msgs::msg::CostmapFilterInfo>(
125 global_frame_ = layered_costmap_->getGlobalFrameID();
128 speed_limit_pub_ = node->create_publisher<nav2_msgs::msg::SpeedLimit>(
130 speed_limit_pub_->on_activate();
133 if (enable_path_lookahead_) {
137 "SpeedFilter: Path lookahead enabled. Subscribing to \"%s\" topic for path...",
138 resolved_path_topic.c_str());
139 path_sub_ = node->create_subscription<nav_msgs::msg::Path>(
143 odom_smoother_ = std::make_shared<nav2_util::OdomSmoother>(
144 node, 0.3, odom_topic);
146 lookahead_pub_ = node->create_publisher<geometry_msgs::msg::PointStamped>(
147 name_ +
"/lookahead_point");
148 lookahead_pub_->on_activate();
152 base_ = BASE_DEFAULT;
153 multiplier_ = MULTIPLIER_DEFAULT;
157 held_lookahead_dist_ = 0.0;
158 lookahead_start_idx_ = 0;
162 const nav2_msgs::msg::CostmapFilterInfo::ConstSharedPtr & msg)
164 std::lock_guard<CostmapFilter::mutex_t> guard(*
getMutex());
166 nav2::LifecycleNode::SharedPtr node = node_.lock();
168 throw std::runtime_error{
"Failed to lock node"};
178 "SpeedFilter: New costmap filter info arrived from %s topic. Updating old filter info.",
186 multiplier_ = msg->multiplier;
187 if (msg->type == SPEED_FILTER_PERCENT) {
192 "SpeedFilter: Using expressed in a percent from maximum speed"
193 "speed_limit = %f + filter_mask_data * %f",
195 }
else if (msg->type == SPEED_FILTER_ABSOLUTE) {
200 "SpeedFilter: Using absolute speed_limit = %f + filter_mask_data * %f",
203 RCLCPP_ERROR(logger_,
"SpeedFilter: Mode is not supported");
212 "SpeedFilter: Subscribing to \"%s\" topic for filter mask...",
214 mask_sub_ = node->create_subscription<nav_msgs::msg::OccupancyGrid>(
221 const nav_msgs::msg::OccupancyGrid::ConstSharedPtr & msg)
223 std::lock_guard<CostmapFilter::mutex_t> guard(*
getMutex());
228 "SpeedFilter: Received filter mask from %s topic.",
mask_topic_.c_str());
232 "SpeedFilter: New filter mask arrived from %s topic. Updating old filter mask.",
234 filter_mask_.reset();
241 const nav_msgs::msg::Path::ConstSharedPtr & msg)
243 std::lock_guard<CostmapFilter::mutex_t> guard(*
getMutex());
248 lookahead_start_idx_ = 0;
252 const geometry_msgs::msg::Pose & pose,
253 double & speed_limit)
255 geometry_msgs::msg::Pose mask_pose;
258 if (!
transformPose(global_frame_, pose, filter_mask_->header.frame_id, mask_pose)) {
263 unsigned int mask_robot_i, mask_robot_j;
264 if (!nav2_util::worldToMap(
265 filter_mask_, mask_pose.position.x, mask_pose.position.y,
266 mask_robot_i, mask_robot_j))
273 int8_t speed_mask_data =
getMaskData(filter_mask_, mask_robot_i, mask_robot_j);
274 if (speed_mask_data == SPEED_MASK_NO_LIMIT) {
277 speed_limit = NO_SPEED_LIMIT;
278 }
else if (speed_mask_data == SPEED_MASK_UNKNOWN) {
283 "SpeedFilter: Found unknown cell in filter_mask[%i, %i], "
284 "which is invalid for this kind of filter",
285 mask_robot_i, mask_robot_j);
289 speed_limit = speed_mask_data * multiplier_ + base_;
291 if (speed_limit < 0.0 || speed_limit > 100.0) {
294 "SpeedFilter: Speed limit in filter_mask[%i, %i] is %f%%, "
295 "out of bounds of [0, 100]. Setting it to no-limit value.",
296 mask_robot_i, mask_robot_j, speed_limit);
297 speed_limit = NO_SPEED_LIMIT;
300 if (speed_limit < 0.0) {
303 "SpeedFilter: Speed limit in filter_mask[%i, %i] is less than 0 m/s, "
304 "which can not be true. Setting it to no-limit value.",
305 mask_robot_i, mask_robot_j);
306 speed_limit = NO_SPEED_LIMIT;
314 const geometry_msgs::msg::Pose & robot_pose,
315 double lookahead_dist,
316 double & speed_limit)
318 double min_speed_limit = std::numeric_limits<double>::max();
321 held_lookahead_dist_ = 0.0;
324 auto lookahead_point_msg = std::make_unique<geometry_msgs::msg::PointStamped>();
325 lookahead_point_msg->header.frame_id = global_frame_;
326 lookahead_point_msg->header.stamp = clock_->now();
327 lookahead_point_msg->point = robot_pose.position;
330 nav_msgs::msg::Path transformed_path;
331 if(current_path_->header.frame_id != global_frame_) {
332 if(!nav2_util::transformPathInTargetFrame(*current_path_, transformed_path, *tf_,
335 RCLCPP_ERROR_THROTTLE(logger_, *(clock_), 5000,
336 "SpeedFilter: Failed to transform path to global frame, "
337 "no speed limit will be published");
341 transformed_path = *current_path_;
344 const auto & poses = transformed_path.poses;
345 const size_t pose_search_start =
346 (lookahead_start_idx_ < poses.size()) ? lookahead_start_idx_ : 0;
349 lookahead_start_idx_ = nav2_util::distance_from_path(
350 transformed_path, robot_pose, pose_search_start).closest_segment_index;
353 double limit_at_robot_pose = NO_SPEED_LIMIT;
356 RCLCPP_ERROR_THROTTLE(logger_, *(clock_), 5000,
357 "SpeedFilter: Failed to get speed limit at robot pose");
361 if (limit_at_robot_pose != NO_SPEED_LIMIT) {
362 min_speed_limit = limit_at_robot_pose;
366 double dist_along_path = 0.0;
367 for (
size_t i = lookahead_start_idx_; i < poses.size(); ++i) {
368 if (dist_along_path > lookahead_dist) {
373 lookahead_point_msg->point = poses[i].pose.position;
375 double sampled_speed_limit = NO_SPEED_LIMIT;
377 sampled_speed_limit != NO_SPEED_LIMIT)
379 min_speed_limit = std::min(min_speed_limit, sampled_speed_limit);
383 if (i + 1 < poses.size()) {
384 dist_along_path += nav2_util::geometry_utils::euclidean_distance(
385 poses[i].pose.position, poses[i + 1].pose.position);
389 if (lookahead_pub_ && lookahead_pub_->get_subscription_count() > 0) {
390 lookahead_pub_->publish(std::move(lookahead_point_msg));
394 if (min_speed_limit == std::numeric_limits<double>::max()) {
395 min_speed_limit = NO_SPEED_LIMIT;
399 if (limit_at_robot_pose != min_speed_limit) {
400 held_lookahead_dist_ = lookahead_dist;
403 speed_limit = min_speed_limit;
409 int ,
int ,
int ,
int ,
410 const geometry_msgs::msg::Pose & pose)
412 std::lock_guard<CostmapFilter::mutex_t> guard(*
getMutex());
416 RCLCPP_WARN_THROTTLE(
417 logger_, *(clock_), 2000,
418 "SpeedFilter: Filter mask was not received");
424 const bool use_path_lookahead =
425 enable_path_lookahead_ &&
426 current_path_ && !current_path_->poses.empty();
428 if (use_path_lookahead) {
429 const auto twist = odom_smoother_->getTwist();
430 const double linear_vel = std::abs(twist.linear.x);
433 double d_lookahead = max_lookahead_;
434 if (max_decel_ < 0.0) {
435 d_lookahead = (linear_vel * linear_vel) / (2.0 * std::abs(max_decel_));
436 d_lookahead = std::clamp(d_lookahead, min_lookahead_, max_lookahead_);
439 d_lookahead = std::max(d_lookahead, held_lookahead_dist_);
443 RCLCPP_ERROR(logger_,
"SpeedFilter: Failed to get speed limit from lookahead");
448 RCLCPP_ERROR(logger_,
"SpeedFilter: Failed to get speed limit at pose");
453 if (speed_limit_ != speed_limit_prev_) {
454 if (speed_limit_ != NO_SPEED_LIMIT) {
455 RCLCPP_DEBUG(logger_,
"SpeedFilter: Speed limit is set to %f", speed_limit_);
457 RCLCPP_DEBUG(logger_,
"SpeedFilter: Speed limit is set to its default value");
461 std::unique_ptr<nav2_msgs::msg::SpeedLimit> msg =
462 std::make_unique<nav2_msgs::msg::SpeedLimit>();
463 msg->header.frame_id = global_frame_;
464 msg->header.stamp = clock_->now();
465 msg->percentage = percentage_;
466 msg->speed_limit = speed_limit_;
467 speed_limit_pub_->publish(std::move(msg));
469 speed_limit_prev_ = speed_limit_;
475 std::lock_guard<CostmapFilter::mutex_t> guard(*
getMutex());
477 filter_info_sub_.reset();
479 if (speed_limit_pub_) {
480 speed_limit_pub_->on_deactivate();
481 speed_limit_pub_.reset();
483 if (lookahead_pub_) {
484 lookahead_pub_->on_deactivate();
485 lookahead_pub_.reset();
491 std::lock_guard<CostmapFilter::mutex_t> guard(*
getMutex());
501 #include "pluginlib/class_list_macros.hpp"
A QoS profile for latched, reliable topics with a history of 10 messages.
A 2D costmap provides a mapping between points in the world and their associated "costs".
int8_t getMaskData(nav_msgs::msg::OccupancyGrid::ConstSharedPtr filter_mask, const unsigned int mx, const unsigned int my) const
Get the data of a cell in the filter mask.
bool transformPose(const std::string global_frame, const geometry_msgs::msg::Pose &global_pose, const std::string mask_frame, geometry_msgs::msg::Pose &mask_pose) const
: Transforms robot pose from current layer frame to mask frame
std::string filter_info_topic_
: Name of costmap filter info topic
std::string mask_topic_
: Name of filter mask topic
mutex_t * getMutex()
: returns pointer to a mutex
Abstract class for layered costmap plugin implementations.
std::string joinWithParentNamespace(const std::string &topic)
Reads in a speed restriction mask and enables a robot to dynamically adjust speed based on pose in ma...
void resetFilter() override
Reset the costmap filter / topic / info.
bool getSpeedLimitFromLookahead(const geometry_msgs::msg::Pose &robot_pose, double lookahead_dist, double &speed_limit)
Get the speed limit from the path lookahead.
void filterInfoCallback(const nav2_msgs::msg::CostmapFilterInfo::ConstSharedPtr &msg)
Callback for the filter information.
bool getSpeedLimitAtPose(const geometry_msgs::msg::Pose &pose, double &speed_limit)
Look up the speed limit at a single pose in the costmap's global frame.
SpeedFilter()
A constructor.
void process(nav2_costmap_2d::Costmap2D &master_grid, int min_i, int min_j, int max_i, int max_j, const geometry_msgs::msg::Pose &pose) override
Process the keepout layer at the current pose / bounds / grid.
void pathCallback(const nav_msgs::msg::Path::ConstSharedPtr &msg)
Callback for the planned path used by path lookahead.
void maskCallback(const nav_msgs::msg::OccupancyGrid::ConstSharedPtr &msg)
Callback for the filter mask.
void initializeFilter(const std::string &filter_info_topic) override
Initialize the filter and subscribe to the info topic.
bool isActive()
If this filter is active.