18 #include "nav2_ros_common/node_utils.hpp"
19 #include "opennav_docking/simple_non_charging_dock.hpp"
20 #include "opennav_docking/utils.hpp"
21 #include "nav2_ros_common/tf2_factories.hpp"
23 using namespace std::chrono_literals;
25 namespace opennav_docking
28 void SimpleNonChargingDock::configure(
29 const nav2::LifecycleNode::WeakPtr & parent,
30 const std::string & name, nav2::TransformBuffer::SharedPtr tf)
34 node_ = parent.lock();
36 throw std::runtime_error{
"Failed to lock node"};
40 use_external_detection_pose_ = node_->declare_or_get_parameter(
41 name +
".use_external_detection_pose",
false);
42 external_detection_timeout_ = node_->declare_or_get_parameter(
43 name +
".external_detection_timeout", 1.0);
44 external_detection_translation_x_ = node_->declare_or_get_parameter(
45 name +
".external_detection_translation_x", -0.20);
46 external_detection_translation_y_ = node_->declare_or_get_parameter(
47 name +
".external_detection_translation_y", 0.0);
48 double yaw = node_->declare_or_get_parameter(
49 name +
".external_detection_rotation_yaw", 0.0);
50 double pitch = node_->declare_or_get_parameter(
51 name +
".external_detection_rotation_pitch", 1.57);
52 double roll = node_->declare_or_get_parameter(
53 name +
".external_detection_rotation_roll", -1.57);
54 double filter_coef = node_->declare_or_get_parameter(
55 name +
".filter_coef", 0.1);
58 detector_service_name_ = node_->declare_or_get_parameter(
59 name +
".detector_service_name", std::string(
""));
60 detector_service_timeout_ = node_->declare_or_get_parameter(
61 name +
".detector_service_timeout", 5.0);
62 subscribe_toggle_ = node_->declare_or_get_parameter(
63 name +
".subscribe_toggle",
false);
66 bool use_stall_detection = node_->declare_or_get_parameter(
67 name +
".use_stall_detection",
false);
68 stall_joint_names_ = node_->declare_or_get_parameter(
69 name +
".stall_joint_names", std::vector<std::string>());
70 stall_velocity_threshold_ = node_->declare_or_get_parameter(
71 name +
".stall_velocity_threshold", 1.0);
72 stall_effort_threshold_ = node_->declare_or_get_parameter(
73 name +
".stall_effort_threshold", 1.0);
76 docking_threshold_ = node_->declare_or_get_parameter(
77 name +
".docking_threshold", 0.05);
80 staging_x_offset_ = node_->declare_or_get_parameter(
81 name +
".staging_x_offset", -0.7);
82 staging_yaw_offset_ = node_->declare_or_get_parameter(
83 name +
".staging_yaw_offset", 0.0);
86 std::string dock_direction = node_->declare_or_get_parameter(
87 name +
".dock_direction", std::string(
"forward"));
88 rotate_to_dock_ = node_->declare_or_get_parameter(
89 name +
".rotate_to_dock",
false);
91 node_->get_parameter(
"base_frame", base_frame_id_);
94 detection_active_ =
false;
95 initial_pose_received_ =
false;
98 if (use_external_detection_pose_ && !subscribe_toggle_) {
99 dock_pose_.header.stamp = rclcpp::Time(0);
100 dock_pose_sub_ = node_->create_subscription<geometry_msgs::msg::PoseStamped>(
101 "detected_dock_pose",
102 [
this](
const geometry_msgs::msg::PoseStamped::ConstSharedPtr & pose) {
103 detected_dock_pose_ = *pose;
104 initial_pose_received_ =
true;
109 dock_direction_ = utils::getDockDirectionFromString(dock_direction);
110 if (dock_direction_ == opennav_docking_core::DockDirection::UNKNOWN) {
111 throw std::runtime_error{
"Dock direction is not valid. Valid options are: forward or backward"};
114 if (rotate_to_dock_ && dock_direction_ != opennav_docking_core::DockDirection::BACKWARD) {
115 throw std::runtime_error{
"Parameter rotate_to_dock is enabled but dock direction is not "
116 "backward. Please set dock direction to backward."};
120 external_detection_rotation_.setRPY(roll, pitch, yaw);
121 filter_ = std::make_unique<PoseFilter>(filter_coef, external_detection_timeout_);
123 if (!detector_service_name_.empty()) {
124 detector_client_ = node_->create_client<std_srvs::srv::Trigger>(
125 detector_service_name_,
false);
128 if (use_stall_detection) {
130 if (stall_joint_names_.size() < 1) {
131 RCLCPP_ERROR(node_->get_logger(),
"stall_joint_names cannot be empty!");
133 joint_state_sub_ = node_->create_subscription<sensor_msgs::msg::JointState>(
135 std::bind(&SimpleNonChargingDock::jointStateCallback,
this, std::placeholders::_1),
139 dock_pose_pub_ = node_->create_publisher<geometry_msgs::msg::PoseStamped>(
141 filtered_dock_pose_pub_ = node_->create_publisher<geometry_msgs::msg::PoseStamped>(
143 staging_pose_pub_ = node_->create_publisher<geometry_msgs::msg::PoseStamped>(
147 geometry_msgs::msg::PoseStamped SimpleNonChargingDock::getStagingPose(
148 const geometry_msgs::msg::Pose & pose,
const std::string & frame)
151 if (!use_external_detection_pose_) {
154 dock_pose_.header.frame_id = frame;
155 dock_pose_.pose = pose;
159 const double yaw = tf2::getYaw(pose.orientation);
160 geometry_msgs::msg::PoseStamped staging_pose;
161 staging_pose.header.frame_id = frame;
162 staging_pose.header.stamp = node_->now();
163 staging_pose.pose = pose;
164 staging_pose.pose.position.x += cos(yaw) * staging_x_offset_;
165 staging_pose.pose.position.y += sin(yaw) * staging_x_offset_;
166 tf2::Quaternion orientation;
167 orientation.setRPY(0.0, 0.0, yaw + staging_yaw_offset_);
168 staging_pose.pose.orientation = tf2::toMsg(orientation);
171 staging_pose_pub_->publish(std::make_unique<geometry_msgs::msg::PoseStamped>(staging_pose));
175 bool SimpleNonChargingDock::getRefinedPose(geometry_msgs::msg::PoseStamped & pose, std::string)
178 if (!use_external_detection_pose_) {
179 dock_pose_pub_->publish(std::make_unique<geometry_msgs::msg::PoseStamped>(pose));
185 if (!initial_pose_received_) {
186 RCLCPP_WARN(node_->get_logger(),
"Waiting for first detected_dock_pose; none received yet");
191 geometry_msgs::msg::PoseStamped detected = detected_dock_pose_;
194 auto timeout = rclcpp::Duration::from_seconds(external_detection_timeout_);
195 if (node_->now() - detected.header.stamp > timeout) {
196 RCLCPP_WARN(node_->get_logger(),
"Lost detection or did not detect: timeout exceeded");
203 if (detected.header.frame_id != pose.header.frame_id) {
205 if (!tf2_buffer_->canTransform(
206 pose.header.frame_id, detected.header.frame_id,
207 detected.header.stamp, rclcpp::Duration::from_seconds(0.2)))
209 RCLCPP_WARN(node_->get_logger(),
"Failed to transform detected dock pose");
212 tf2_buffer_->transform(detected, detected, pose.header.frame_id);
213 }
catch (
const tf2::TransformException & ex) {
214 RCLCPP_WARN(node_->get_logger(),
"Failed to transform detected dock pose");
220 detected = filter_->update(detected);
221 filtered_dock_pose_pub_->publish(std::make_unique<geometry_msgs::msg::PoseStamped>(detected));
224 geometry_msgs::msg::PoseStamped just_orientation;
225 just_orientation.pose.orientation = tf2::toMsg(external_detection_rotation_);
226 geometry_msgs::msg::TransformStamped transform;
227 transform.transform.rotation = detected.pose.orientation;
228 tf2::doTransform(just_orientation, just_orientation, transform);
230 tf2::Quaternion orientation;
231 orientation.setRPY(0.0, 0.0, tf2::getYaw(just_orientation.pose.orientation));
232 dock_pose_.pose.orientation = tf2::toMsg(orientation);
235 dock_pose_.header = detected.header;
236 dock_pose_.pose.position = detected.pose.position;
237 const double yaw = tf2::getYaw(dock_pose_.pose.orientation);
238 dock_pose_.pose.position.x += cos(yaw) * external_detection_translation_x_ -
239 sin(yaw) * external_detection_translation_y_;
240 dock_pose_.pose.position.y += sin(yaw) * external_detection_translation_x_ +
241 cos(yaw) * external_detection_translation_y_;
242 dock_pose_.pose.position.z = 0.0;
245 dock_pose_pub_->publish(std::make_unique<geometry_msgs::msg::PoseStamped>(dock_pose_));
250 bool SimpleNonChargingDock::isDocked()
252 if (joint_state_sub_) {
257 if (dock_pose_.header.frame_id.empty()) {
263 geometry_msgs::msg::PoseStamped base_pose;
264 base_pose.header.stamp = rclcpp::Time(0);
265 base_pose.header.frame_id = base_frame_id_;
266 base_pose.pose.orientation.w = 1.0;
268 tf2_buffer_->transform(base_pose, base_pose, dock_pose_.header.frame_id);
269 }
catch (
const tf2::TransformException & ex) {
274 double d = std::hypot(
275 base_pose.pose.position.x - dock_pose_.pose.position.x,
276 base_pose.pose.position.y - dock_pose_.pose.position.y);
277 return d < docking_threshold_;
280 void SimpleNonChargingDock::jointStateCallback(
281 const sensor_msgs::msg::JointState::ConstSharedPtr & state)
283 double velocity = 0.0;
285 for (
size_t i = 0; i < state->name.size(); ++i) {
286 for (
auto & name : stall_joint_names_) {
287 if (state->name[i] == name) {
289 velocity += abs(state->velocity[i]);
290 effort += abs(state->effort[i]);
296 effort /= stall_joint_names_.size();
297 velocity /= stall_joint_names_.size();
299 is_stalled_ = (velocity < stall_velocity_threshold_) && (effort > stall_effort_threshold_);
302 bool SimpleNonChargingDock::startDetectionProcess()
305 if (detection_active_) {
310 if (detector_client_) {
311 auto req = std::make_shared<std_srvs::srv::Trigger::Request>();
313 auto future = detector_client_->invoke(
315 std::chrono::duration_cast<std::chrono::nanoseconds>(
316 std::chrono::duration<double>(detector_service_timeout_)));
318 if (!future || !future->success) {
320 node_->get_logger(),
"Detector service '%s' failed to start.",
321 detector_service_name_.c_str());
324 }
catch (
const std::exception & e) {
326 node_->get_logger(),
"Calling detector service '%s' failed: %s",
327 detector_service_name_.c_str(), e.what());
334 if (subscribe_toggle_ && !dock_pose_sub_) {
335 dock_pose_sub_ = node_->create_subscription<geometry_msgs::msg::PoseStamped>(
336 "detected_dock_pose",
337 [
this](
const geometry_msgs::msg::PoseStamped::ConstSharedPtr & pose) {
338 detected_dock_pose_ = *pose;
339 initial_pose_received_ =
true;
344 detection_active_ =
true;
345 RCLCPP_INFO(node_->get_logger(),
"External detector activation requested.");
349 bool SimpleNonChargingDock::stopDetectionProcess()
352 if (!detection_active_) {
357 if (detector_client_) {
358 auto req = std::make_shared<std_srvs::srv::Trigger::Request>();
360 auto future = detector_client_->invoke(
362 std::chrono::duration_cast<std::chrono::nanoseconds>(
363 std::chrono::duration<double>(detector_service_timeout_)));
365 if (!future || !future->success) {
367 node_->get_logger(),
"Detector service '%s' failed to stop.",
368 detector_service_name_.c_str());
371 }
catch (
const std::exception & e) {
373 node_->get_logger(),
"Calling detector service '%s' failed: %s",
374 detector_service_name_.c_str(), e.what());
381 if (subscribe_toggle_ && dock_pose_sub_) {
382 dock_pose_sub_.reset();
385 detection_active_ =
false;
386 initial_pose_received_ =
false;
387 RCLCPP_INFO(node_->get_logger(),
"External detector deactivation requested.");
391 void SimpleNonChargingDock::activate()
393 dock_pose_pub_->on_activate();
394 filtered_dock_pose_pub_->on_activate();
395 staging_pose_pub_->on_activate();
398 void SimpleNonChargingDock::deactivate()
400 stopDetectionProcess();
401 dock_pose_pub_->on_deactivate();
402 filtered_dock_pose_pub_->on_deactivate();
403 staging_pose_pub_->on_deactivate();
404 RCLCPP_DEBUG(node_->get_logger(),
"SimpleNonChargingDock deactivated");
407 void SimpleNonChargingDock::cleanup()
409 detector_client_.reset();
410 dock_pose_sub_.reset();
411 detection_active_ =
false;
412 initial_pose_received_ =
false;
413 RCLCPP_DEBUG(node_->get_logger(),
"SimpleNonChargingDock cleaned up");
418 #include "pluginlib/class_list_macros.hpp"
A QoS profile for latched, reliable topics with a history of 1 messages.
A QoS profile for standard reliable topics with a history of 10 messages.
Abstract interface for a charging dock for the docking framework.