17 #include "nav2_ros_common/node_utils.hpp"
18 #include "opennav_docking/simple_charging_dock.hpp"
19 #include "opennav_docking/utils.hpp"
21 namespace opennav_docking
25 const nav2::LifecycleNode::WeakPtr & parent,
26 const std::string & name, std::shared_ptr<tf2_ros::Buffer> tf)
31 node_ = parent.lock();
33 throw std::runtime_error{
"Failed to lock node"};
37 nav2::declare_parameter_if_not_declared(
38 node_, name +
".use_battery_status", rclcpp::ParameterValue(
true));
41 nav2::declare_parameter_if_not_declared(
42 node_, name +
".use_external_detection_pose", rclcpp::ParameterValue(
false));
43 nav2::declare_parameter_if_not_declared(
44 node_, name +
".external_detection_timeout", rclcpp::ParameterValue(1.0));
45 nav2::declare_parameter_if_not_declared(
46 node_, name +
".external_detection_translation_x", rclcpp::ParameterValue(-0.20));
47 nav2::declare_parameter_if_not_declared(
48 node_, name +
".external_detection_translation_y", rclcpp::ParameterValue(0.0));
49 nav2::declare_parameter_if_not_declared(
50 node_, name +
".external_detection_rotation_yaw", rclcpp::ParameterValue(0.0));
51 nav2::declare_parameter_if_not_declared(
52 node_, name +
".external_detection_rotation_pitch", rclcpp::ParameterValue(1.57));
53 nav2::declare_parameter_if_not_declared(
54 node_, name +
".external_detection_rotation_roll", rclcpp::ParameterValue(-1.57));
55 nav2::declare_parameter_if_not_declared(
56 node_, name +
".filter_coef", rclcpp::ParameterValue(0.1));
59 nav2::declare_parameter_if_not_declared(
60 node_, name +
".charging_threshold", rclcpp::ParameterValue(0.5));
63 nav2::declare_parameter_if_not_declared(
64 node_, name +
".use_stall_detection", rclcpp::ParameterValue(
false));
65 nav2::declare_parameter_if_not_declared(
66 node_, name +
".stall_joint_names", rclcpp::PARAMETER_STRING_ARRAY);
67 nav2::declare_parameter_if_not_declared(
68 node_, name +
".stall_velocity_threshold", rclcpp::ParameterValue(1.0));
69 nav2::declare_parameter_if_not_declared(
70 node_, name +
".stall_effort_threshold", rclcpp::ParameterValue(1.0));
73 nav2::declare_parameter_if_not_declared(
74 node_, name +
".docking_threshold", rclcpp::ParameterValue(0.05));
77 nav2::declare_parameter_if_not_declared(
78 node_, name +
".staging_x_offset", rclcpp::ParameterValue(-0.7));
79 nav2::declare_parameter_if_not_declared(
80 node_, name +
".staging_yaw_offset", rclcpp::ParameterValue(0.0));
83 nav2::declare_parameter_if_not_declared(
84 node_, name +
".dock_direction", rclcpp::ParameterValue(std::string(
"forward")));
85 nav2::declare_parameter_if_not_declared(
86 node_, name +
".rotate_to_dock", rclcpp::ParameterValue(
false));
88 node_->get_parameter(name +
".use_battery_status", use_battery_status_);
89 node_->get_parameter(name +
".use_external_detection_pose", use_external_detection_pose_);
90 node_->get_parameter(name +
".external_detection_timeout", external_detection_timeout_);
92 name +
".external_detection_translation_x", external_detection_translation_x_);
94 name +
".external_detection_translation_y", external_detection_translation_y_);
95 double yaw, pitch, roll;
96 node_->get_parameter(name +
".external_detection_rotation_yaw", yaw);
97 node_->get_parameter(name +
".external_detection_rotation_pitch", pitch);
98 node_->get_parameter(name +
".external_detection_rotation_roll", roll);
99 external_detection_rotation_.setEuler(pitch, roll, yaw);
100 node_->get_parameter(name +
".charging_threshold", charging_threshold_);
101 node_->get_parameter(name +
".stall_velocity_threshold", stall_velocity_threshold_);
102 node_->get_parameter(name +
".stall_effort_threshold", stall_effort_threshold_);
103 node_->get_parameter(name +
".docking_threshold", docking_threshold_);
104 node_->get_parameter(
"base_frame", base_frame_id_);
105 node_->get_parameter(name +
".staging_x_offset", staging_x_offset_);
106 node_->get_parameter(name +
".staging_yaw_offset", staging_yaw_offset_);
108 std::string dock_direction;
109 node_->get_parameter(name +
".dock_direction", dock_direction);
110 dock_direction_ = utils::getDockDirectionFromString(dock_direction);
111 if (dock_direction_ == opennav_docking_core::DockDirection::UNKNOWN) {
112 throw std::runtime_error{
"Dock direction is not valid. Valid options are: forward or backward"};
115 node_->get_parameter(name +
".rotate_to_dock", rotate_to_dock_);
116 if (rotate_to_dock_ && dock_direction_ != opennav_docking_core::DockDirection::BACKWARD) {
117 throw std::runtime_error{
"Parameter rotate_to_dock is enabled but dock direction is not "
118 "backward. Please set dock direction to backward."};
123 node_->get_parameter(name +
".filter_coef", filter_coef);
124 filter_ = std::make_unique<PoseFilter>(filter_coef, external_detection_timeout_);
126 if (use_battery_status_) {
127 battery_sub_ = node_->create_subscription<sensor_msgs::msg::BatteryState>(
129 [
this](
const sensor_msgs::msg::BatteryState::SharedPtr state) {
130 is_charging_ = state->current > charging_threshold_;
134 if (use_external_detection_pose_) {
135 dock_pose_.header.stamp = rclcpp::Time(0);
136 dock_pose_sub_ = node_->create_subscription<geometry_msgs::msg::PoseStamped>(
137 "detected_dock_pose",
138 [
this](
const geometry_msgs::msg::PoseStamped::SharedPtr pose) {
139 detected_dock_pose_ = *pose;
143 bool use_stall_detection;
144 node_->get_parameter(name +
".use_stall_detection", use_stall_detection);
145 if (use_stall_detection) {
147 node_->get_parameter(name +
".stall_joint_names", stall_joint_names_);
148 if (stall_joint_names_.size() < 1) {
149 RCLCPP_ERROR(node_->get_logger(),
"stall_joint_names cannot be empty!");
151 joint_state_sub_ = node_->create_subscription<sensor_msgs::msg::JointState>(
153 std::bind(&SimpleChargingDock::jointStateCallback,
this, std::placeholders::_1),
157 dock_pose_pub_ = node_->create_publisher<geometry_msgs::msg::PoseStamped>(
"dock_pose");
158 filtered_dock_pose_pub_ = node_->create_publisher<geometry_msgs::msg::PoseStamped>(
159 "filtered_dock_pose");
160 staging_pose_pub_ = node_->create_publisher<geometry_msgs::msg::PoseStamped>(
"staging_pose");
164 const geometry_msgs::msg::Pose & pose,
const std::string & frame)
167 if (!use_external_detection_pose_) {
170 dock_pose_.header.frame_id = frame;
171 dock_pose_.pose = pose;
175 const double yaw = tf2::getYaw(pose.orientation);
176 geometry_msgs::msg::PoseStamped staging_pose;
177 staging_pose.header.frame_id = frame;
178 staging_pose.header.stamp = node_->now();
179 staging_pose.pose = pose;
180 staging_pose.pose.position.x += cos(yaw) * staging_x_offset_;
181 staging_pose.pose.position.y += sin(yaw) * staging_x_offset_;
182 tf2::Quaternion orientation;
183 orientation.setEuler(0.0, 0.0, yaw + staging_yaw_offset_);
184 staging_pose.pose.orientation = tf2::toMsg(orientation);
187 staging_pose_pub_->publish(staging_pose);
194 if (!use_external_detection_pose_) {
195 dock_pose_pub_->publish(pose);
201 geometry_msgs::msg::PoseStamped detected = detected_dock_pose_;
204 auto timeout = rclcpp::Duration::from_seconds(external_detection_timeout_);
205 if (node_->now() - detected.header.stamp > timeout) {
206 RCLCPP_WARN(node_->get_logger(),
"Lost detection or did not detect: timeout exceeded");
213 if (detected.header.frame_id != pose.header.frame_id) {
215 if (!tf2_buffer_->canTransform(
216 pose.header.frame_id, detected.header.frame_id,
217 detected.header.stamp, rclcpp::Duration::from_seconds(0.2)))
219 RCLCPP_WARN(node_->get_logger(),
"Failed to transform detected dock pose");
222 tf2_buffer_->transform(detected, detected, pose.header.frame_id);
223 }
catch (
const tf2::TransformException & ex) {
224 RCLCPP_WARN(node_->get_logger(),
"Failed to transform detected dock pose");
230 detected = filter_->update(detected);
231 filtered_dock_pose_pub_->publish(detected);
234 geometry_msgs::msg::PoseStamped just_orientation;
235 just_orientation.pose.orientation = tf2::toMsg(external_detection_rotation_);
236 geometry_msgs::msg::TransformStamped transform;
237 transform.transform.rotation = detected.pose.orientation;
238 tf2::doTransform(just_orientation, just_orientation, transform);
240 tf2::Quaternion orientation;
241 orientation.setEuler(0.0, 0.0, tf2::getYaw(just_orientation.pose.orientation));
242 dock_pose_.pose.orientation = tf2::toMsg(orientation);
245 dock_pose_.header = detected.header;
246 dock_pose_.pose.position = detected.pose.position;
247 const double yaw = tf2::getYaw(dock_pose_.pose.orientation);
248 dock_pose_.pose.position.x += cos(yaw) * external_detection_translation_x_ -
249 sin(yaw) * external_detection_translation_y_;
250 dock_pose_.pose.position.y += sin(yaw) * external_detection_translation_x_ +
251 cos(yaw) * external_detection_translation_y_;
252 dock_pose_.pose.position.z = 0.0;
255 dock_pose_pub_->publish(dock_pose_);
262 if (joint_state_sub_) {
267 if (dock_pose_.header.frame_id.empty()) {
273 geometry_msgs::msg::PoseStamped base_pose;
274 base_pose.header.stamp = rclcpp::Time(0);
275 base_pose.header.frame_id = base_frame_id_;
276 base_pose.pose.orientation.w = 1.0;
278 tf2_buffer_->transform(base_pose, base_pose, dock_pose_.header.frame_id);
279 }
catch (
const tf2::TransformException & ex) {
284 double d = std::hypot(
285 base_pose.pose.position.x - dock_pose_.pose.position.x,
286 base_pose.pose.position.y - dock_pose_.pose.position.y);
287 return d < docking_threshold_;
292 return use_battery_status_ ? is_charging_ :
isDocked();
305 void SimpleChargingDock::jointStateCallback(
const sensor_msgs::msg::JointState::SharedPtr state)
307 double velocity = 0.0;
309 for (
size_t i = 0; i < state->name.size(); ++i) {
310 for (
auto & name : stall_joint_names_) {
311 if (state->name[i] == name) {
313 velocity += abs(state->velocity[i]);
314 effort += abs(state->effort[i]);
320 effort /= stall_joint_names_.size();
321 velocity /= stall_joint_names_.size();
323 is_stalled_ = (velocity < stall_velocity_threshold_) && (effort > stall_effort_threshold_);
328 #include "pluginlib/class_list_macros.hpp"
A QoS profile for standard reliable topics with a history of 10 messages.
virtual geometry_msgs::msg::PoseStamped getStagingPose(const geometry_msgs::msg::Pose &pose, const std::string &frame)
Method to obtain the dock's staging pose. This method should likely be using TF and the dock's pose i...
virtual bool disableCharging()
Undocking while current is still flowing can damage a charge dock so some charge docks provide the ab...
virtual void configure(const nav2::LifecycleNode::WeakPtr &parent, const std::string &name, std::shared_ptr< tf2_ros::Buffer > tf)
virtual bool isCharging()
Are we charging? If a charge dock requires any sort of negotiation to begin charging,...
virtual bool isDocked()
Have we made contact with dock? This can be implemented in a variety of ways: by establishing communi...
virtual bool hasStoppedCharging()
Similar to isCharging() but called when undocking.
virtual bool getRefinedPose(geometry_msgs::msg::PoseStamped &pose, std::string id)
Method to obtain the refined pose of the dock, usually based on sensors.
Abstract interface for a charging dock for the docking framework.