18 #include "rclcpp/logging.hpp"
19 #include "rclcpp/parameter.hpp"
20 #include "opennav_docking/controller.hpp"
21 #include "nav2_util/geometry_utils.hpp"
22 #include "nav2_ros_common/node_utils.hpp"
23 #include "tf2/utils.hpp"
24 #include "nav2_ros_common/tf2_factories.hpp"
26 using rcl_interfaces::msg::ParameterType;
28 namespace opennav_docking
32 const nav2::LifecycleNode::SharedPtr & node, nav2::TransformBuffer::SharedPtr tf,
33 std::string fixed_frame, std::string base_frame)
34 : tf2_buffer_(tf), fixed_frame_(fixed_frame), base_frame_(base_frame)
36 logger_ = node->get_logger();
37 clock_ = node->get_clock();
39 std::string costmap_topic, footprint_topic;
40 k_phi_ = node->declare_or_get_parameter(
"controller.k_phi", 3.0);
41 k_delta_ = node->declare_or_get_parameter(
"controller.k_delta", 2.0);
42 beta_ = node->declare_or_get_parameter(
"controller.beta", 0.4);
43 lambda_ = node->declare_or_get_parameter(
"controller.lambda", 2.0);
44 v_linear_min_ = node->declare_or_get_parameter(
"controller.v_linear_min", 0.1);
45 v_linear_max_ = node->declare_or_get_parameter(
"controller.v_linear_max", 0.25);
46 v_angular_max_ = node->declare_or_get_parameter(
"controller.v_angular_max", 0.75);
47 slowdown_radius_ = node->declare_or_get_parameter(
"controller.slowdown_radius", 0.25);
48 deceleration_max_ = node->declare_or_get_parameter(
"controller.deceleration_max", 2.5);
49 rotate_to_heading_angular_vel_ = node->declare_or_get_parameter(
50 "controller.rotate_to_heading_angular_vel", 1.0);
51 rotate_to_heading_max_angular_accel_ = node->declare_or_get_parameter(
52 "controller.rotate_to_heading_max_angular_accel", 3.2);
53 use_collision_detection_ = node->declare_or_get_parameter(
54 "controller.use_collision_detection",
true);
55 costmap_topic = node->declare_or_get_parameter(
"controller.costmap_topic",
56 std::string(
"local_costmap/costmap_raw"));
57 footprint_topic = node->declare_or_get_parameter(
"controller.footprint_topic",
58 std::string(
"local_costmap/published_footprint"));
59 transform_tolerance_ = node->declare_or_get_parameter(
60 "controller.transform_tolerance", 0.1);
61 projection_time_ = node->declare_or_get_parameter(
62 "controller.projection_time", 5.0);
63 simulation_time_step_ = node->declare_or_get_parameter(
64 "controller.simulation_time_step", 0.1);
65 dock_collision_threshold_ = node->declare_or_get_parameter(
66 "controller.dock_collision_threshold", 0.3);
68 control_law_ = std::make_unique<nav2_graceful_controller::SmoothControlLaw>(
69 k_phi_, k_delta_, beta_, lambda_, slowdown_radius_, deceleration_max_,
70 v_linear_min_, v_linear_max_, v_angular_max_);
73 post_set_params_handler_ = node->add_post_set_parameters_callback(
76 this, std::placeholders::_1));
77 on_set_params_handler_ = node->add_on_set_parameters_callback(
80 this, std::placeholders::_1));
82 if (use_collision_detection_) {
87 node->create_publisher<nav_msgs::msg::Path>(
"docking_trajectory");
93 trajectory_pub_.reset();
94 collision_checker_.reset();
96 footprint_sub_.reset();
100 const geometry_msgs::msg::Pose & pose, geometry_msgs::msg::Twist & cmd,
bool is_docking,
103 std::lock_guard<std::mutex> lock(dynamic_params_lock_);
104 cmd = control_law_->calculateRegularVelocity(pose, backward);
109 const double & angular_distance_to_heading,
110 const geometry_msgs::msg::Twist & current_velocity,
113 geometry_msgs::msg::Twist cmd_vel;
114 const double sign = angular_distance_to_heading > 0.0 ? 1.0 : -1.0;
115 const double angular_vel = sign * rotate_to_heading_angular_vel_;
116 const double min_feasible_angular_speed =
117 current_velocity.angular.z - rotate_to_heading_max_angular_accel_ * dt;
118 const double max_feasible_angular_speed =
119 current_velocity.angular.z + rotate_to_heading_max_angular_accel_ * dt;
121 std::clamp(angular_vel, min_feasible_angular_speed, max_feasible_angular_speed);
124 double max_vel_to_stop =
125 std::sqrt(2 * rotate_to_heading_max_angular_accel_ * fabs(angular_distance_to_heading));
126 if (fabs(cmd_vel.angular.z) > max_vel_to_stop) {
127 cmd_vel.angular.z = sign * max_vel_to_stop;
134 const geometry_msgs::msg::Pose & target_pose,
bool is_docking,
bool backward)
137 auto trajectory = std::make_unique<nav_msgs::msg::Path>();
138 trajectory->header.frame_id = base_frame_;
139 trajectory->header.stamp = clock_->now();
142 geometry_msgs::msg::PoseStamped next_pose;
143 next_pose.header.frame_id = base_frame_;
144 trajectory->poses.push_back(next_pose);
147 geometry_msgs::msg::TransformStamped base_to_fixed_transform;
149 base_to_fixed_transform = tf2_buffer_->lookupTransform(
150 fixed_frame_, base_frame_, trajectory->header.stamp,
151 tf2::durationFromSec(transform_tolerance_));
152 }
catch (tf2::TransformException & ex) {
154 logger_,
"Could not get transform from %s to %s: %s",
155 base_frame_.c_str(), fixed_frame_.c_str(), ex.what());
160 double distance = std::numeric_limits<double>::max();
161 unsigned int max_iter =
static_cast<unsigned int>(ceil(projection_time_ / simulation_time_step_));
165 next_pose.pose = control_law_->calculateNextPose(
166 simulation_time_step_, target_pose, next_pose.pose, backward);
169 trajectory->poses.push_back(next_pose);
172 geometry_msgs::msg::PoseStamped local_pose = next_pose;
173 local_pose.header.stamp = trajectory->header.stamp;
174 tf2::doTransform(local_pose, local_pose, base_to_fixed_transform);
180 double dock_collision_distance = is_docking ?
181 nav2_util::geometry_utils::euclidean_distance(target_pose, next_pose.pose) :
182 std::hypot(next_pose.pose.position.x, next_pose.pose.position.y);
185 if (use_collision_detection_ &&
186 dock_collision_distance > dock_collision_threshold_ &&
187 !collision_checker_->isCollisionFree(local_pose.pose))
190 logger_,
"Collision detected at pose: (%.2f, %.2f, %.2f) in frame %s",
191 local_pose.pose.position.x, local_pose.pose.position.y, local_pose.pose.position.z,
192 local_pose.header.frame_id.c_str());
193 trajectory_pub_->publish(std::move(trajectory));
198 distance = nav2_util::geometry_utils::euclidean_distance(target_pose, next_pose.pose);
199 }
while(distance > 1e-2 && trajectory->poses.size() < max_iter);
201 trajectory_pub_->publish(std::move(trajectory));
207 const nav2::LifecycleNode::SharedPtr & node,
208 std::string costmap_topic, std::string footprint_topic,
double transform_tolerance)
210 costmap_sub_ = std::make_unique<nav2_costmap_2d::CostmapSubscriber>(node, costmap_topic);
211 footprint_sub_ = std::make_unique<nav2_costmap_2d::FootprintSubscriber>(
212 node, footprint_topic, *tf2_buffer_, base_frame_, transform_tolerance);
213 collision_checker_ = std::make_shared<nav2_costmap_2d::CostmapTopicCollisionChecker>(
214 *costmap_sub_, *footprint_sub_, node->get_name());
218 const std::vector<rclcpp::Parameter> & parameters)
220 rcl_interfaces::msg::SetParametersResult result;
221 result.successful =
true;
222 for (
const auto & parameter : parameters) {
223 const auto & param_type = parameter.get_type();
224 const auto & param_name = parameter.get_name();
225 if (param_name.find(
"controller.") != 0) {
228 if (param_type == ParameterType::PARAMETER_DOUBLE) {
229 if (parameter.as_double() < 0.0) {
231 logger_,
"The value of parameter '%s' is incorrectly set to %f, "
232 "it should be >=0. Ignoring parameter update.",
233 param_name.c_str(), parameter.as_double());
234 result.successful =
false;
244 std::lock_guard<std::mutex> lock(dynamic_params_lock_);
246 for (
auto parameter : parameters) {
247 const auto & param_type = parameter.get_type();
248 const auto & param_name = parameter.get_name();
249 if (param_name.find(
"controller.") != 0) {
252 if (param_type == rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE) {
253 if (param_name ==
"controller.k_phi") {
254 k_phi_ = parameter.as_double();
255 }
else if (param_name ==
"controller.k_delta") {
256 k_delta_ = parameter.as_double();
257 }
else if (param_name ==
"controller.beta") {
258 beta_ = parameter.as_double();
259 }
else if (param_name ==
"controller.lambda") {
260 lambda_ = parameter.as_double();
261 }
else if (param_name ==
"controller.v_linear_min") {
262 v_linear_min_ = parameter.as_double();
263 }
else if (param_name ==
"controller.v_linear_max") {
264 v_linear_max_ = parameter.as_double();
265 }
else if (param_name ==
"controller.v_angular_max") {
266 v_angular_max_ = parameter.as_double();
267 }
else if (param_name ==
"controller.slowdown_radius") {
268 slowdown_radius_ = parameter.as_double();
269 }
else if (param_name ==
"controller.deceleration_max") {
270 deceleration_max_ = parameter.as_double();
271 }
else if (param_name ==
"controller.rotate_to_heading_angular_vel") {
272 rotate_to_heading_angular_vel_ = parameter.as_double();
273 }
else if (param_name ==
"controller.rotate_to_heading_max_angular_accel") {
274 rotate_to_heading_max_angular_accel_ = parameter.as_double();
275 }
else if (param_name ==
"controller.projection_time") {
276 projection_time_ = parameter.as_double();
277 }
else if (param_name ==
"controller.simulation_time_step") {
278 simulation_time_step_ = parameter.as_double();
279 }
else if (param_name ==
"controller.dock_collision_threshold") {
280 dock_collision_threshold_ = parameter.as_double();
284 control_law_->setCurvatureConstants(k_phi_, k_delta_, beta_, lambda_);
285 control_law_->setSlowdownRadius(slowdown_radius_);
286 control_law_->setMaxDeceleration(deceleration_max_);
287 control_law_->setSpeedLimit(v_linear_min_, v_linear_max_, v_angular_max_);
void updateParametersCallback(const std::vector< rclcpp::Parameter > ¶meters)
Apply parameter updates after validation This callback is executed when parameters have been successf...
bool isTrajectoryCollisionFree(const geometry_msgs::msg::Pose &target_pose, bool is_docking, bool backward=false)
Check if a trajectory is collision free.
Controller(const nav2::LifecycleNode::SharedPtr &node, nav2::TransformBuffer::SharedPtr tf, std::string fixed_frame, std::string base_frame)
Create a controller instance. Configure ROS 2 parameters.
bool computeVelocityCommand(const geometry_msgs::msg::Pose &pose, geometry_msgs::msg::Twist &cmd, bool is_docking, bool backward=false)
Compute a velocity command using control law.
~Controller()
A destructor for opennav_docking::Controller.
geometry_msgs::msg::Twist computeRotateToHeadingCommand(const double &angular_distance_to_heading, const geometry_msgs::msg::Twist ¤t_velocity, const double &dt)
Perform a command for in-place rotation.
void configureCollisionChecker(const nav2::LifecycleNode::SharedPtr &node, std::string costmap_topic, std::string footprint_topic, double transform_tolerance)
Configure the collision checker.
rcl_interfaces::msg::SetParametersResult validateParameterUpdatesCallback(const std::vector< rclcpp::Parameter > ¶meters)
Validate incoming parameter updates before applying them. This callback is triggered when one or more...