18 #include "rclcpp/rclcpp.hpp"
19 #include "opennav_docking/controller.hpp"
20 #include "nav2_util/geometry_utils.hpp"
21 #include "nav2_util/node_utils.hpp"
22 #include "nav_2d_utils/conversions.hpp"
23 #include "tf2/utils.hpp"
25 namespace opennav_docking
29 const rclcpp_lifecycle::LifecycleNode::SharedPtr & node, std::shared_ptr<tf2_ros::Buffer> tf,
30 std::string fixed_frame, std::string base_frame)
31 : tf2_buffer_(tf), fixed_frame_(fixed_frame), base_frame_(base_frame)
33 logger_ = node->get_logger();
34 clock_ = node->get_clock();
36 nav2_util::declare_parameter_if_not_declared(
37 node,
"controller.k_phi", rclcpp::ParameterValue(3.0));
38 nav2_util::declare_parameter_if_not_declared(
39 node,
"controller.k_delta", rclcpp::ParameterValue(2.0));
40 nav2_util::declare_parameter_if_not_declared(
41 node,
"controller.beta", rclcpp::ParameterValue(0.4));
42 nav2_util::declare_parameter_if_not_declared(
43 node,
"controller.lambda", rclcpp::ParameterValue(2.0));
44 nav2_util::declare_parameter_if_not_declared(
45 node,
"controller.v_linear_min", rclcpp::ParameterValue(0.1));
46 nav2_util::declare_parameter_if_not_declared(
47 node,
"controller.v_linear_max", rclcpp::ParameterValue(0.25));
48 nav2_util::declare_parameter_if_not_declared(
49 node,
"controller.v_angular_max", rclcpp::ParameterValue(0.75));
50 nav2_util::declare_parameter_if_not_declared(
51 node,
"controller.slowdown_radius", rclcpp::ParameterValue(0.25));
52 nav2_util::declare_parameter_if_not_declared(
53 node,
"controller.rotate_to_heading_angular_vel", rclcpp::ParameterValue(1.0));
54 nav2_util::declare_parameter_if_not_declared(
55 node,
"controller.rotate_to_heading_max_angular_accel", rclcpp::ParameterValue(3.2));
56 nav2_util::declare_parameter_if_not_declared(
57 node,
"controller.use_collision_detection", rclcpp::ParameterValue(
true));
58 nav2_util::declare_parameter_if_not_declared(
59 node,
"controller.costmap_topic",
60 rclcpp::ParameterValue(std::string(
"local_costmap/costmap_raw")));
61 nav2_util::declare_parameter_if_not_declared(
62 node,
"controller.footprint_topic", rclcpp::ParameterValue(
63 std::string(
"local_costmap/published_footprint")));
64 nav2_util::declare_parameter_if_not_declared(
65 node,
"controller.transform_tolerance", rclcpp::ParameterValue(0.1));
66 nav2_util::declare_parameter_if_not_declared(
67 node,
"controller.projection_time", rclcpp::ParameterValue(5.0));
68 nav2_util::declare_parameter_if_not_declared(
69 node,
"controller.simulation_time_step", rclcpp::ParameterValue(0.1));
70 nav2_util::declare_parameter_if_not_declared(
71 node,
"controller.dock_collision_threshold", rclcpp::ParameterValue(0.3));
73 node->get_parameter(
"controller.k_phi", k_phi_);
74 node->get_parameter(
"controller.k_delta", k_delta_);
75 node->get_parameter(
"controller.beta", beta_);
76 node->get_parameter(
"controller.lambda", lambda_);
77 node->get_parameter(
"controller.v_linear_min", v_linear_min_);
78 node->get_parameter(
"controller.v_linear_max", v_linear_max_);
79 node->get_parameter(
"controller.v_angular_max", v_angular_max_);
80 node->get_parameter(
"controller.slowdown_radius", slowdown_radius_);
81 control_law_ = std::make_unique<nav2_graceful_controller::SmoothControlLaw>(
82 k_phi_, k_delta_, beta_, lambda_, slowdown_radius_, v_linear_min_, v_linear_max_,
86 dyn_params_handler_ = node->add_on_set_parameters_callback(
89 node->get_parameter(
"controller.use_collision_detection", use_collision_detection_);
90 node->get_parameter(
"controller.projection_time", projection_time_);
91 node->get_parameter(
"controller.simulation_time_step", simulation_time_step_);
92 node->get_parameter(
"controller.transform_tolerance", transform_tolerance_);
94 if (use_collision_detection_) {
95 std::string costmap_topic, footprint_topic;
96 node->get_parameter(
"controller.costmap_topic", costmap_topic);
97 node->get_parameter(
"controller.footprint_topic", footprint_topic);
98 node->get_parameter(
"controller.dock_collision_threshold", dock_collision_threshold_);
102 node->get_parameter(
"controller.rotate_to_heading_angular_vel", rotate_to_heading_angular_vel_);
103 node->get_parameter(
"controller.rotate_to_heading_max_angular_accel",
104 rotate_to_heading_max_angular_accel_);
107 node->create_publisher<nav_msgs::msg::Path>(
"docking_trajectory", 1);
112 control_law_.reset();
113 trajectory_pub_.reset();
114 collision_checker_.reset();
115 costmap_sub_.reset();
116 footprint_sub_.reset();
120 const geometry_msgs::msg::Pose & pose, geometry_msgs::msg::Twist & cmd,
bool is_docking,
123 std::lock_guard<std::mutex> lock(dynamic_params_lock_);
124 cmd = control_law_->calculateRegularVelocity(pose, backward);
129 const double & angular_distance_to_heading,
130 const geometry_msgs::msg::Twist & current_velocity,
133 geometry_msgs::msg::Twist cmd_vel;
134 const double sign = angular_distance_to_heading > 0.0 ? 1.0 : -1.0;
135 const double angular_vel = sign * rotate_to_heading_angular_vel_;
136 const double min_feasible_angular_speed =
137 current_velocity.angular.z - rotate_to_heading_max_angular_accel_ * dt;
138 const double max_feasible_angular_speed =
139 current_velocity.angular.z + rotate_to_heading_max_angular_accel_ * dt;
141 std::clamp(angular_vel, min_feasible_angular_speed, max_feasible_angular_speed);
144 double max_vel_to_stop =
145 std::sqrt(2 * rotate_to_heading_max_angular_accel_ * fabs(angular_distance_to_heading));
146 if (fabs(cmd_vel.angular.z) > max_vel_to_stop) {
147 cmd_vel.angular.z = sign * max_vel_to_stop;
154 const geometry_msgs::msg::Pose & target_pose,
bool is_docking,
bool backward)
157 nav_msgs::msg::Path trajectory;
158 trajectory.header.frame_id = base_frame_;
159 trajectory.header.stamp = clock_->now();
162 geometry_msgs::msg::PoseStamped next_pose;
163 next_pose.header.frame_id = base_frame_;
164 trajectory.poses.push_back(next_pose);
167 geometry_msgs::msg::TransformStamped base_to_fixed_transform;
169 base_to_fixed_transform = tf2_buffer_->lookupTransform(
170 fixed_frame_, base_frame_, trajectory.header.stamp,
171 tf2::durationFromSec(transform_tolerance_));
172 }
catch (tf2::TransformException & ex) {
174 logger_,
"Could not get transform from %s to %s: %s",
175 base_frame_.c_str(), fixed_frame_.c_str(), ex.what());
180 double distance = std::numeric_limits<double>::max();
181 unsigned int max_iter =
static_cast<unsigned int>(ceil(projection_time_ / simulation_time_step_));
185 next_pose.pose = control_law_->calculateNextPose(
186 simulation_time_step_, target_pose, next_pose.pose, backward);
189 trajectory.poses.push_back(next_pose);
192 geometry_msgs::msg::PoseStamped local_pose = next_pose;
193 local_pose.header.stamp = trajectory.header.stamp;
194 tf2::doTransform(local_pose, local_pose, base_to_fixed_transform);
200 double dock_collision_distance = is_docking ?
201 nav2_util::geometry_utils::euclidean_distance(target_pose, next_pose.pose) :
202 std::hypot(next_pose.pose.position.x, next_pose.pose.position.y);
205 if (use_collision_detection_ &&
206 dock_collision_distance > dock_collision_threshold_ &&
207 !collision_checker_->isCollisionFree(nav_2d_utils::poseToPose2D(local_pose.pose)))
210 logger_,
"Collision detected at pose: (%.2f, %.2f, %.2f) in frame %s",
211 local_pose.pose.position.x, local_pose.pose.position.y, local_pose.pose.position.z,
212 local_pose.header.frame_id.c_str());
213 trajectory_pub_->publish(trajectory);
218 distance = nav2_util::geometry_utils::euclidean_distance(target_pose, next_pose.pose);
219 }
while(distance > 1e-2 && trajectory.poses.size() < max_iter);
221 trajectory_pub_->publish(trajectory);
227 const rclcpp_lifecycle::LifecycleNode::SharedPtr & node,
228 std::string costmap_topic, std::string footprint_topic,
double transform_tolerance)
230 costmap_sub_ = std::make_unique<nav2_costmap_2d::CostmapSubscriber>(node, costmap_topic);
231 footprint_sub_ = std::make_unique<nav2_costmap_2d::FootprintSubscriber>(
232 node, footprint_topic, *tf2_buffer_, base_frame_, transform_tolerance);
233 collision_checker_ = std::make_shared<nav2_costmap_2d::CostmapTopicCollisionChecker>(
234 *costmap_sub_, *footprint_sub_, node->get_name());
237 rcl_interfaces::msg::SetParametersResult
240 std::lock_guard<std::mutex> lock(dynamic_params_lock_);
242 rcl_interfaces::msg::SetParametersResult result;
243 for (
auto parameter : parameters) {
244 const auto & param_type = parameter.get_type();
245 const auto & param_name = parameter.get_name();
246 if (param_name.find(
"controller.") != 0) {
249 if (param_type == rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE) {
250 if (param_name ==
"controller.k_phi") {
251 k_phi_ = parameter.as_double();
252 }
else if (param_name ==
"controller.k_delta") {
253 k_delta_ = parameter.as_double();
254 }
else if (param_name ==
"controller.beta") {
255 beta_ = parameter.as_double();
256 }
else if (param_name ==
"controller.lambda") {
257 lambda_ = parameter.as_double();
258 }
else if (param_name ==
"controller.v_linear_min") {
259 v_linear_min_ = parameter.as_double();
260 }
else if (param_name ==
"controller.v_linear_max") {
261 v_linear_max_ = parameter.as_double();
262 }
else if (param_name ==
"controller.v_angular_max") {
263 v_angular_max_ = parameter.as_double();
264 }
else if (param_name ==
"controller.slowdown_radius") {
265 slowdown_radius_ = parameter.as_double();
266 }
else if (param_name ==
"controller.rotate_to_heading_angular_vel") {
267 rotate_to_heading_angular_vel_ = parameter.as_double();
268 }
else if (param_name ==
"controller.rotate_to_heading_max_angular_accel") {
269 rotate_to_heading_max_angular_accel_ = parameter.as_double();
270 }
else if (param_name ==
"controller.projection_time") {
271 projection_time_ = parameter.as_double();
272 }
else if (param_name ==
"controller.simulation_time_step") {
273 simulation_time_step_ = parameter.as_double();
274 }
else if (param_name ==
"controller.dock_collision_threshold") {
275 dock_collision_threshold_ = parameter.as_double();
279 control_law_->setCurvatureConstants(k_phi_, k_delta_, beta_, lambda_);
280 control_law_->setSlowdownRadius(slowdown_radius_);
281 control_law_->setSpeedLimit(v_linear_min_, v_linear_max_, v_angular_max_);
285 result.successful =
true;
bool isTrajectoryCollisionFree(const geometry_msgs::msg::Pose &target_pose, bool is_docking, bool backward=false)
Check if a trajectory is collision free.
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 rclcpp_lifecycle::LifecycleNode::SharedPtr &node, std::string costmap_topic, std::string footprint_topic, double transform_tolerance)
Configure the collision checker.
Controller(const rclcpp_lifecycle::LifecycleNode::SharedPtr &node, std::shared_ptr< tf2_ros::Buffer > tf, std::string fixed_frame, std::string base_frame)
Create a controller instance. Configure ROS 2 parameters.
rcl_interfaces::msg::SetParametersResult dynamicParametersCallback(std::vector< rclcpp::Parameter > parameters)
Callback executed when a parameter change is detected.