18 #include "nav2_ros_common/node_utils.hpp"
19 #include "opennav_docking/simple_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 SimpleChargingDock::configure(
29 const nav2::LifecycleNode::WeakPtr & parent,
30 const std::string & name, nav2::TransformBuffer::SharedPtr tf)
35 node_ = parent.lock();
37 throw std::runtime_error{
"Failed to lock node"};
41 use_battery_status_ = node_->declare_or_get_parameter(
42 name +
".use_battery_status",
true);
45 detector_service_name_ = node_->declare_or_get_parameter(
46 name +
".detector_service_name", std::string(
""));
47 detector_service_timeout_ = node_->declare_or_get_parameter(
48 name +
".detector_service_timeout", 5.0);
49 subscribe_toggle_ = node_->declare_or_get_parameter(
50 name +
".subscribe_toggle",
false);
53 use_external_detection_pose_ = node_->declare_or_get_parameter(
54 name +
".use_external_detection_pose",
false);
55 external_detection_timeout_ = node_->declare_or_get_parameter(
56 name +
".external_detection_timeout", 1.0);
57 external_detection_translation_x_ = node_->declare_or_get_parameter(
58 name +
".external_detection_translation_x", -0.20);
59 external_detection_translation_y_ = node_->declare_or_get_parameter(
60 name +
".external_detection_translation_y", 0.0);
61 double yaw = node_->declare_or_get_parameter(
62 name +
".external_detection_rotation_yaw", 0.0);
63 double pitch = node_->declare_or_get_parameter(
64 name +
".external_detection_rotation_pitch", 1.57);
65 double roll = node_->declare_or_get_parameter(
66 name +
".external_detection_rotation_roll", -1.57);
67 double filter_coef = node_->declare_or_get_parameter(
68 name +
".filter_coef", 0.1);
71 charging_threshold_ = node_->declare_or_get_parameter(
72 name +
".charging_threshold", 0.5);
75 bool use_stall_detection = node_->declare_or_get_parameter(
76 name +
".use_stall_detection",
false);
77 stall_joint_names_ = node_->declare_or_get_parameter(
78 name +
".stall_joint_names", std::vector<std::string>());
79 stall_velocity_threshold_ = node_->declare_or_get_parameter(
80 name +
".stall_velocity_threshold", 1.0);
81 stall_effort_threshold_ = node_->declare_or_get_parameter(
82 name +
".stall_effort_threshold", 1.0);
85 docking_threshold_ = node_->declare_or_get_parameter(
86 name +
".docking_threshold", 0.05);
89 staging_x_offset_ = node_->declare_or_get_parameter(
90 name +
".staging_x_offset", -0.7);
91 staging_yaw_offset_ = node_->declare_or_get_parameter(
92 name +
".staging_yaw_offset", 0.0);
95 std::string dock_direction = node_->declare_or_get_parameter(
96 name +
".dock_direction", std::string(
"forward"));
97 rotate_to_dock_ = node_->declare_or_get_parameter(
98 name +
".rotate_to_dock",
false);
100 node_->get_parameter(
"base_frame", base_frame_id_);
103 detection_active_ =
false;
104 initial_pose_received_ =
false;
107 if (use_external_detection_pose_ && !subscribe_toggle_) {
108 dock_pose_.header.stamp = rclcpp::Time(0);
109 dock_pose_sub_ = node_->create_subscription<geometry_msgs::msg::PoseStamped>(
110 "detected_dock_pose",
111 [
this](
const geometry_msgs::msg::PoseStamped::ConstSharedPtr & pose) {
112 detected_dock_pose_ = *pose;
113 initial_pose_received_ =
true;
118 dock_direction_ = utils::getDockDirectionFromString(dock_direction);
119 if (dock_direction_ == opennav_docking_core::DockDirection::UNKNOWN) {
120 throw std::runtime_error{
"Dock direction is not valid. Valid options are: forward or backward"};
123 if (rotate_to_dock_ && dock_direction_ != opennav_docking_core::DockDirection::BACKWARD) {
124 throw std::runtime_error{
"Parameter rotate_to_dock is enabled but dock direction is not "
125 "backward. Please set dock direction to backward."};
129 external_detection_rotation_.setRPY(roll, pitch, yaw);
130 filter_ = std::make_unique<PoseFilter>(filter_coef, external_detection_timeout_);
132 if (!detector_service_name_.empty()) {
133 detector_client_ = node_->create_client<std_srvs::srv::Trigger>(
134 detector_service_name_,
false);
137 if (use_battery_status_) {
138 battery_sub_ = node_->create_subscription<sensor_msgs::msg::BatteryState>(
140 [
this](
const sensor_msgs::msg::BatteryState::ConstSharedPtr & state) {
141 is_charging_ = state->current > charging_threshold_;
145 if (use_stall_detection) {
147 if (stall_joint_names_.size() < 1) {
148 RCLCPP_ERROR(node_->get_logger(),
"stall_joint_names cannot be empty!");
150 joint_state_sub_ = node_->create_subscription<sensor_msgs::msg::JointState>(
152 std::bind(&SimpleChargingDock::jointStateCallback,
this, std::placeholders::_1),
156 dock_pose_pub_ = node_->create_publisher<geometry_msgs::msg::PoseStamped>(
158 filtered_dock_pose_pub_ = node_->create_publisher<geometry_msgs::msg::PoseStamped>(
160 staging_pose_pub_ = node_->create_publisher<geometry_msgs::msg::PoseStamped>(
164 geometry_msgs::msg::PoseStamped SimpleChargingDock::getStagingPose(
165 const geometry_msgs::msg::Pose & pose,
const std::string & frame)
168 if (!use_external_detection_pose_) {
171 dock_pose_.header.frame_id = frame;
172 dock_pose_.pose = pose;
176 const double yaw = tf2::getYaw(pose.orientation);
177 geometry_msgs::msg::PoseStamped staging_pose;
178 staging_pose.header.frame_id = frame;
179 staging_pose.header.stamp = node_->now();
180 staging_pose.pose = pose;
181 staging_pose.pose.position.x += cos(yaw) * staging_x_offset_;
182 staging_pose.pose.position.y += sin(yaw) * staging_x_offset_;
183 tf2::Quaternion orientation;
184 orientation.setRPY(0.0, 0.0, yaw + staging_yaw_offset_);
185 staging_pose.pose.orientation = tf2::toMsg(orientation);
188 staging_pose_pub_->publish(std::make_unique<geometry_msgs::msg::PoseStamped>(staging_pose));
192 bool SimpleChargingDock::getRefinedPose(geometry_msgs::msg::PoseStamped & pose, std::string )
195 if (!use_external_detection_pose_) {
196 dock_pose_pub_->publish(std::make_unique<geometry_msgs::msg::PoseStamped>(pose));
202 if (!initial_pose_received_) {
203 RCLCPP_WARN(node_->get_logger(),
"Waiting for first detected_dock_pose; none received yet");
208 geometry_msgs::msg::PoseStamped detected = detected_dock_pose_;
211 auto timeout = rclcpp::Duration::from_seconds(external_detection_timeout_);
212 if (node_->now() - detected.header.stamp > timeout) {
214 node_->get_logger(),
"Lost detection or did not detect: "
215 "timeout exceeded (is %2.2f seconds old)",
216 static_cast<float>((node_->now() - detected.header.stamp).seconds()));
223 if (detected.header.frame_id != pose.header.frame_id) {
225 if (!tf2_buffer_->canTransform(
226 pose.header.frame_id, detected.header.frame_id,
227 detected.header.stamp, rclcpp::Duration::from_seconds(0.2)))
230 node_->get_logger(),
"Failed to transform detected dock pose: "
231 "cannot transform %s to %s (at time %2.2f s)",
232 detected.header.frame_id.c_str(),
233 pose.header.frame_id.c_str(),
235 detected.header.stamp.sec + detected.header.stamp.nanosec * 1e-9
239 tf2_buffer_->transform(detected, detected, pose.header.frame_id);
240 }
catch (
const tf2::TransformException & ex) {
241 RCLCPP_WARN(node_->get_logger(),
"Failed to transform detected dock pose: %s", ex.what());
247 detected = filter_->update(detected);
248 filtered_dock_pose_pub_->publish(std::make_unique<geometry_msgs::msg::PoseStamped>(detected));
251 geometry_msgs::msg::PoseStamped just_orientation;
252 just_orientation.pose.orientation = tf2::toMsg(external_detection_rotation_);
253 geometry_msgs::msg::TransformStamped transform;
254 transform.transform.rotation = detected.pose.orientation;
255 tf2::doTransform(just_orientation, just_orientation, transform);
257 tf2::Quaternion orientation;
258 orientation.setRPY(0.0, 0.0, tf2::getYaw(just_orientation.pose.orientation));
259 dock_pose_.pose.orientation = tf2::toMsg(orientation);
262 dock_pose_.header = detected.header;
263 dock_pose_.pose.position = detected.pose.position;
264 const double yaw = tf2::getYaw(dock_pose_.pose.orientation);
265 dock_pose_.pose.position.x += cos(yaw) * external_detection_translation_x_ -
266 sin(yaw) * external_detection_translation_y_;
267 dock_pose_.pose.position.y += sin(yaw) * external_detection_translation_x_ +
268 cos(yaw) * external_detection_translation_y_;
269 dock_pose_.pose.position.z = 0.0;
272 dock_pose_pub_->publish(std::make_unique<geometry_msgs::msg::PoseStamped>(dock_pose_));
277 bool SimpleChargingDock::isDocked()
279 if (joint_state_sub_) {
284 if (dock_pose_.header.frame_id.empty()) {
290 geometry_msgs::msg::PoseStamped base_pose;
291 base_pose.header.stamp = rclcpp::Time(0);
292 base_pose.header.frame_id = base_frame_id_;
293 base_pose.pose.orientation.w = 1.0;
295 tf2_buffer_->transform(base_pose, base_pose, dock_pose_.header.frame_id);
296 }
catch (
const tf2::TransformException & ex) {
301 double d = std::hypot(
302 base_pose.pose.position.x - dock_pose_.pose.position.x,
303 base_pose.pose.position.y - dock_pose_.pose.position.y);
304 return d < docking_threshold_;
307 bool SimpleChargingDock::isCharging()
309 return use_battery_status_ ? is_charging_ : isDocked();
312 bool SimpleChargingDock::disableCharging()
317 bool SimpleChargingDock::hasStoppedCharging()
319 return !isCharging();
322 void SimpleChargingDock::jointStateCallback(
323 const sensor_msgs::msg::JointState::ConstSharedPtr & state)
325 double velocity = 0.0;
327 for (
size_t i = 0; i < state->name.size(); ++i) {
328 for (
auto & name : stall_joint_names_) {
329 if (state->name[i] == name) {
331 velocity += abs(state->velocity[i]);
332 effort += abs(state->effort[i]);
338 effort /= stall_joint_names_.size();
339 velocity /= stall_joint_names_.size();
341 is_stalled_ = (velocity < stall_velocity_threshold_) && (effort > stall_effort_threshold_);
344 bool SimpleChargingDock::startDetectionProcess()
347 if (detection_active_) {
352 if (detector_client_) {
353 auto req = std::make_shared<std_srvs::srv::Trigger::Request>();
355 auto future = detector_client_->invoke(
357 std::chrono::duration_cast<std::chrono::nanoseconds>(
358 std::chrono::duration<double>(detector_service_timeout_)));
360 if (!future || !future->success) {
362 node_->get_logger(),
"Detector service '%s' failed to start.",
363 detector_service_name_.c_str());
366 }
catch (
const std::exception & e) {
368 node_->get_logger(),
"Calling detector service '%s' failed: %s",
369 detector_service_name_.c_str(), e.what());
376 if (subscribe_toggle_ && !dock_pose_sub_) {
377 dock_pose_sub_ = node_->create_subscription<geometry_msgs::msg::PoseStamped>(
378 "detected_dock_pose",
379 [
this](
const geometry_msgs::msg::PoseStamped::ConstSharedPtr & pose) {
380 detected_dock_pose_ = *pose;
381 initial_pose_received_ =
true;
386 detection_active_ =
true;
387 RCLCPP_INFO(node_->get_logger(),
"External detector activation requested.");
391 bool SimpleChargingDock::stopDetectionProcess()
394 if (!detection_active_) {
399 if (detector_client_) {
400 auto req = std::make_shared<std_srvs::srv::Trigger::Request>();
402 auto future = detector_client_->invoke(
404 std::chrono::duration_cast<std::chrono::nanoseconds>(
405 std::chrono::duration<double>(detector_service_timeout_)));
407 if (!future || !future->success) {
409 node_->get_logger(),
"Detector service '%s' failed to stop.",
410 detector_service_name_.c_str());
413 }
catch (
const std::exception & e) {
415 node_->get_logger(),
"Calling detector service '%s' failed: %s",
416 detector_service_name_.c_str(), e.what());
423 if (subscribe_toggle_ && dock_pose_sub_) {
424 dock_pose_sub_.reset();
427 detection_active_ =
false;
428 initial_pose_received_ =
false;
429 RCLCPP_INFO(node_->get_logger(),
"External detector deactivation requested.");
433 void SimpleChargingDock::activate()
435 dock_pose_pub_->on_activate();
436 filtered_dock_pose_pub_->on_activate();
437 staging_pose_pub_->on_activate();
440 void SimpleChargingDock::deactivate()
442 stopDetectionProcess();
443 dock_pose_pub_->on_deactivate();
444 filtered_dock_pose_pub_->on_deactivate();
445 staging_pose_pub_->on_deactivate();
446 RCLCPP_DEBUG(node_->get_logger(),
"SimpleChargingDock deactivated");
449 void SimpleChargingDock::cleanup()
451 detector_client_.reset();
452 dock_pose_sub_.reset();
453 detection_active_ =
false;
454 initial_pose_received_ =
false;
455 RCLCPP_DEBUG(node_->get_logger(),
"SimpleChargingDock cleaned up");
460 #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.