18 #include "rclcpp/rclcpp.hpp"
19 #include "opennav_docking/controller.hpp"
20 #include "nav2_util/geometry_utils.hpp"
21 #include "nav2_ros_common/node_utils.hpp"
22 #include "tf2/utils.hpp"
23 #include "nav2_ros_common/tf2_factories.hpp"
25 using rcl_interfaces::msg::ParameterType;
27 namespace opennav_docking
31 const nav2::LifecycleNode::SharedPtr & node, nav2::TransformBuffer::SharedPtr tf,
32 std::string fixed_frame, std::string base_frame)
33 : tf2_buffer_(tf), fixed_frame_(fixed_frame), base_frame_(base_frame)
35 logger_ = node->get_logger();
36 clock_ = node->get_clock();
38 std::string costmap_topic, footprint_topic;
39 k_phi_ = node->declare_or_get_parameter(
"controller.k_phi", 3.0);
40 k_delta_ = node->declare_or_get_parameter(
"controller.k_delta", 2.0);
41 beta_ = node->declare_or_get_parameter(
"controller.beta", 0.4);
42 lambda_ = node->declare_or_get_parameter(
"controller.lambda", 2.0);
43 v_linear_min_ = node->declare_or_get_parameter(
"controller.v_linear_min", 0.1);
44 v_linear_max_ = node->declare_or_get_parameter(
"controller.v_linear_max", 0.25);
45 v_angular_max_ = node->declare_or_get_parameter(
"controller.v_angular_max", 0.75);
46 slowdown_radius_ = node->declare_or_get_parameter(
"controller.slowdown_radius", 0.25);
47 deceleration_max_ = node->declare_or_get_parameter(
"controller.deceleration_max", 2.5);
48 rotate_to_heading_angular_vel_ = node->declare_or_get_parameter(
49 "controller.rotate_to_heading_angular_vel", 1.0);
50 rotate_to_heading_max_angular_accel_ = node->declare_or_get_parameter(
51 "controller.rotate_to_heading_max_angular_accel", 3.2);
52 use_collision_detection_ = node->declare_or_get_parameter(
53 "controller.use_collision_detection",
true);
54 costmap_topic = node->declare_or_get_parameter(
"controller.costmap_topic",
55 std::string(
"local_costmap/costmap_raw"));
56 footprint_topic = node->declare_or_get_parameter(
"controller.footprint_topic",
57 std::string(
"local_costmap/published_footprint"));
58 transform_tolerance_ = node->declare_or_get_parameter(
59 "controller.transform_tolerance", 0.1);
60 projection_time_ = node->declare_or_get_parameter(
61 "controller.projection_time", 5.0);
62 simulation_time_step_ = node->declare_or_get_parameter(
63 "controller.simulation_time_step", 0.1);
64 dock_collision_threshold_ = node->declare_or_get_parameter(
65 "controller.dock_collision_threshold", 0.3);
67 control_law_ = std::make_unique<nav2_graceful_controller::SmoothControlLaw>(
68 k_phi_, k_delta_, beta_, lambda_, slowdown_radius_, deceleration_max_,
69 v_linear_min_, v_linear_max_, v_angular_max_);
72 post_set_params_handler_ = node->add_post_set_parameters_callback(
75 this, std::placeholders::_1));
76 on_set_params_handler_ = node->add_on_set_parameters_callback(
79 this, std::placeholders::_1));
81 if (use_collision_detection_) {
86 node->create_publisher<nav_msgs::msg::Path>(
"docking_trajectory");
92 trajectory_pub_.reset();
93 collision_checker_.reset();
95 footprint_sub_.reset();
99 const geometry_msgs::msg::Pose & pose, geometry_msgs::msg::Twist & cmd,
bool is_docking,
102 std::lock_guard<std::mutex> lock(dynamic_params_lock_);
103 cmd = control_law_->calculateRegularVelocity(pose, backward);
108 const double & angular_distance_to_heading,
109 const geometry_msgs::msg::Twist & current_velocity,
112 geometry_msgs::msg::Twist cmd_vel;
113 const double sign = angular_distance_to_heading > 0.0 ? 1.0 : -1.0;
114 const double angular_vel = sign * rotate_to_heading_angular_vel_;
115 const double min_feasible_angular_speed =
116 current_velocity.angular.z - rotate_to_heading_max_angular_accel_ * dt;
117 const double max_feasible_angular_speed =
118 current_velocity.angular.z + rotate_to_heading_max_angular_accel_ * dt;
120 std::clamp(angular_vel, min_feasible_angular_speed, max_feasible_angular_speed);
123 double max_vel_to_stop =
124 std::sqrt(2 * rotate_to_heading_max_angular_accel_ * fabs(angular_distance_to_heading));
125 if (fabs(cmd_vel.angular.z) > max_vel_to_stop) {
126 cmd_vel.angular.z = sign * max_vel_to_stop;
133 const geometry_msgs::msg::Pose & target_pose,
bool is_docking,
bool backward)
136 auto trajectory = std::make_unique<nav_msgs::msg::Path>();
137 trajectory->header.frame_id = base_frame_;
138 trajectory->header.stamp = clock_->now();
141 geometry_msgs::msg::PoseStamped next_pose;
142 next_pose.header.frame_id = base_frame_;
143 trajectory->poses.push_back(next_pose);
146 geometry_msgs::msg::TransformStamped base_to_fixed_transform;
148 base_to_fixed_transform = tf2_buffer_->lookupTransform(
149 fixed_frame_, base_frame_, trajectory->header.stamp,
150 tf2::durationFromSec(transform_tolerance_));
151 }
catch (tf2::TransformException & ex) {
153 logger_,
"Could not get transform from %s to %s: %s",
154 base_frame_.c_str(), fixed_frame_.c_str(), ex.what());
159 double distance = std::numeric_limits<double>::max();
160 unsigned int max_iter =
static_cast<unsigned int>(ceil(projection_time_ / simulation_time_step_));
164 next_pose.pose = control_law_->calculateNextPose(
165 simulation_time_step_, target_pose, next_pose.pose, backward);
168 trajectory->poses.push_back(next_pose);
171 geometry_msgs::msg::PoseStamped local_pose = next_pose;
172 local_pose.header.stamp = trajectory->header.stamp;
173 tf2::doTransform(local_pose, local_pose, base_to_fixed_transform);
179 double dock_collision_distance = is_docking ?
180 nav2_util::geometry_utils::euclidean_distance(target_pose, next_pose.pose) :
181 std::hypot(next_pose.pose.position.x, next_pose.pose.position.y);
184 if (use_collision_detection_ &&
185 dock_collision_distance > dock_collision_threshold_ &&
186 !collision_checker_->isCollisionFree(local_pose.pose))
189 logger_,
"Collision detected at pose: (%.2f, %.2f, %.2f) in frame %s",
190 local_pose.pose.position.x, local_pose.pose.position.y, local_pose.pose.position.z,
191 local_pose.header.frame_id.c_str());
192 trajectory_pub_->publish(std::move(trajectory));
197 distance = nav2_util::geometry_utils::euclidean_distance(target_pose, next_pose.pose);
198 }
while(distance > 1e-2 && trajectory->poses.size() < max_iter);
200 trajectory_pub_->publish(std::move(trajectory));
206 const nav2::LifecycleNode::SharedPtr & node,
207 std::string costmap_topic, std::string footprint_topic,
double transform_tolerance)
209 costmap_sub_ = std::make_unique<nav2_costmap_2d::CostmapSubscriber>(node, costmap_topic);
210 footprint_sub_ = std::make_unique<nav2_costmap_2d::FootprintSubscriber>(
211 node, footprint_topic, *tf2_buffer_, base_frame_, transform_tolerance);
212 collision_checker_ = std::make_shared<nav2_costmap_2d::CostmapTopicCollisionChecker>(
213 *costmap_sub_, *footprint_sub_, node->get_name());
217 const std::vector<rclcpp::Parameter> & parameters)
219 rcl_interfaces::msg::SetParametersResult result;
220 result.successful =
true;
221 for (
const auto & parameter : parameters) {
222 const auto & param_type = parameter.get_type();
223 const auto & param_name = parameter.get_name();
224 if (param_name.find(
"controller.") != 0) {
227 if (param_type == ParameterType::PARAMETER_DOUBLE) {
228 if (parameter.as_double() < 0.0) {
230 logger_,
"The value of parameter '%s' is incorrectly set to %f, "
231 "it should be >=0. Ignoring parameter update.",
232 param_name.c_str(), parameter.as_double());
233 result.successful =
false;
243 std::lock_guard<std::mutex> lock(dynamic_params_lock_);
245 for (
auto parameter : parameters) {
246 const auto & param_type = parameter.get_type();
247 const auto & param_name = parameter.get_name();
248 if (param_name.find(
"controller.") != 0) {
251 if (param_type == rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE) {
252 if (param_name ==
"controller.k_phi") {
253 k_phi_ = parameter.as_double();
254 }
else if (param_name ==
"controller.k_delta") {
255 k_delta_ = parameter.as_double();
256 }
else if (param_name ==
"controller.beta") {
257 beta_ = parameter.as_double();
258 }
else if (param_name ==
"controller.lambda") {
259 lambda_ = parameter.as_double();
260 }
else if (param_name ==
"controller.v_linear_min") {
261 v_linear_min_ = parameter.as_double();
262 }
else if (param_name ==
"controller.v_linear_max") {
263 v_linear_max_ = parameter.as_double();
264 }
else if (param_name ==
"controller.v_angular_max") {
265 v_angular_max_ = parameter.as_double();
266 }
else if (param_name ==
"controller.slowdown_radius") {
267 slowdown_radius_ = parameter.as_double();
268 }
else if (param_name ==
"controller.deceleration_max") {
269 deceleration_max_ = parameter.as_double();
270 }
else if (param_name ==
"controller.rotate_to_heading_angular_vel") {
271 rotate_to_heading_angular_vel_ = parameter.as_double();
272 }
else if (param_name ==
"controller.rotate_to_heading_max_angular_accel") {
273 rotate_to_heading_max_angular_accel_ = parameter.as_double();
274 }
else if (param_name ==
"controller.projection_time") {
275 projection_time_ = parameter.as_double();
276 }
else if (param_name ==
"controller.simulation_time_step") {
277 simulation_time_step_ = parameter.as_double();
278 }
else if (param_name ==
"controller.dock_collision_threshold") {
279 dock_collision_threshold_ = parameter.as_double();
283 control_law_->setCurvatureConstants(k_phi_, k_delta_, beta_, lambda_);
284 control_law_->setSlowdownRadius(slowdown_radius_);
285 control_law_->setMaxDeceleration(deceleration_max_);
286 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...