Skip to content

Commit

Permalink
Adding non-charging dock support to docking server (for conveyers, pa…
Browse files Browse the repository at this point in the history
…llots, etc) (#4627)

* adding non-charging dock support to docking server

Signed-off-by: Steve Macenski <[email protected]>

* docs and linting

* adding unit tests

Signed-off-by: Steve Macenski <[email protected]>

---------

Signed-off-by: Steve Macenski <[email protected]>
  • Loading branch information
SteveMacenski authored Aug 26, 2024
1 parent c38fefd commit f9e1024
Show file tree
Hide file tree
Showing 14 changed files with 839 additions and 27 deletions.
19 changes: 11 additions & 8 deletions nav2_docking/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Open Navigation's Nav2 Docking Framework

This package contains an automatic robot docking framework & auxiliary tools. It uses plugin `dock` implementations for a particular platform to enable the framework to generalize to robots of many different kinematic models, charging methods, sensor modalities, and so on. It can also handle a database of many different docking locations and dock models to handle a heterogeneous environment. This task server is designed be called by an application BT or autonomy application to dock once completed with tasks or battery is low -- _not_ within the navigate-to-pose action itself (though `undock` may be called from inside navigate actions!).
This package contains an automatic robot docking framework & auxiliary tools. It uses plugin `dock` implementations for a particular platform to enable the framework to generalize to robots of many different kinematic models, charging methods, sensor modalities, non-charging dock request needs, and so on. It can also handle a database of many different docking locations and dock models to handle a heterogeneous environment. This task server is designed be called by an application BT or autonomy application to dock once completed with tasks or battery is low -- _not_ within the navigate-to-pose action itself (though `undock` may be called from inside navigate actions!).

This work is sponsored by [NVIDIA](https://www.nvidia.com/en-us/) and created by [Open Navigation LLC](https://opennav.org).

Expand All @@ -24,17 +24,17 @@ The Docking Framework has 5 main components:
- `Navigator`: A NavigateToPose action client to navigate the robot to the dock's staging pose if not with the prestaging tolerances
- `DockDatabase`: A database of dock instances in an environment and their associated interfaces for transacting with each type. An arbitrary number of types are supported.
- `Controller`: A spiral-based graceful controller to use for the vision-control loop for docking
- `ChargingDock`: Plugins that describe the dock and how to transact with it (check if charging, detection, etc). You can find this plugin header in the `opennav_docking_core` package.
- `ChargingDock` and `NonChargingDock`: Plugins that describe the dock and how to transact with it (check if charging, detection, etc). You can find these plugin headers in the `opennav_docking_core` package.

The `ChargingDock` plugins are the heart of the customizability of the framework to support any type of charging dock for any kind of robot. The `DockDatabase` is how you describe where these docks exist in your environment to interact with and any of them may be used in your docking request.
The `ChargingDock` and `NonChargingDock` plugins are the heart of the customizability of the framework to support any type of charging or non-charging dock for any kind of robot. This means you can both dock with charging stations, as well as non-charging infrastructure such as static locations (ex. conveyers) or dynamic locations (ex. pallets). The `DockDatabase` is how you describe where these docks exist in your environment to interact with and any of them may be used in your docking request. For dynamic locations like docking with movable objects, the action request can include an approximate docking location that uses vision-control loops to refine motion into it.

The docking procedure is as follows:
1. Take action request and obtain the dock's plugin and its pose
2. If the robot is not within the prestaging tolerance of the dock's staging pose, navigate to the staging pose
3. Use the dock's plugin to initially detect the dock and return the docking pose
4. Enter a vision-control loop where the robot attempts to reach the docking pose while its actively being refined by the vision system
5. Exit the vision-control loop once contact has been detected or charging has started
6. Wait until charging starts and return success.
6. Wait until charging starts (if applicable) and return success.

If anywhere this procedure is unsuccessful, `N` retries may be made by driving back to the dock's staging pose and trying again. If still unsuccessful, it will return a failure code to indicate what kind of failure occurred to the client.

Expand Down Expand Up @@ -74,7 +74,7 @@ This service exists to potentially reload the dock server's known dock database

There are two unique elements to consider in specifying docks: dock _instances_ and dock _plugins_. Dock instances are instances of a particular dock in the map, as the database may contain many known docks (and you can specify which by name you'd like to dock at). Dock plugins are the model of dock that each is an instance of. The plugins contain the capabilities to generically detect and connect to a particular dock model. This separation allows us to efficiently enable many individual docking locations of potentially several different revisions with different attributes.

The **dock plugins** are specified in the parameter file as shown below. If you're familiar with plugins in other Nav2 servers, this should look like a familiar design pattern. Note that there is no specific information about the dock's pose or instances. These are generic attributes about the dock revision (such as staging pose, enable charging command, detection method, etc). You can add additional parameters in the dock's namespace as you choose (for example `timeout`).
The **dock plugins** are specified in the parameter file as shown below. If you're familiar with plugins in other Nav2 servers, this should look like a familiar design pattern. Note that there is no specific information about the dock's pose or instances. These are generic attributes about the dock revision (such as staging pose, enable charging command, detection method, etc). You can add additional parameters in the dock's namespace as you choose (for example `timeout`). These can be of both charging and non-charging types.

```
dock_plugins: ["dockv1", "dockv3"]
Expand Down Expand Up @@ -145,23 +145,26 @@ There are two functions used during dock approach:
* `isCharging`: The approach stops when the robot reports `isDocked`, then we wait
for charging to start by calling `isCharging`.
* This may be implemented using the `sensor_msgs/BatteryState` message to check the power status or for charging current.
* Used for charging-typed plugins.

Similarly, there are two functions used during undocking:

* `disableCharging`: This function is called before undocking commences to help
prevent wear on the charge contacts. If the charge dock supports turning off
the charge current, it should be done here.
* Used for charging-typed plugins.
* `hasStoppedCharging`: This function is called while the controller is undocking.
Undocking is successful when charging has stopped and the robot has returned to
the staging pose.
* Used for charging-typed plugins.

Keep in mind that the docking and undocking functions should return quickly as they
will be called inside the control loop. Also make sure that `isDocked` should
return true if `isCharging` returns true.

### Simple Charging Dock Plugin
### Simple (Non-) Charging Dock Plugins

The `SimpleChargingDock` plugin is an example with many common options which may be fully functional for
The `SimpleChargingDock` and `SimpleNonChargingDock` plugins are examples with many common options which may be fully functional for
some robots.

`getStagingPose` applys a parameterized translational and rotational offset to the dock pose to obtain the staging pose.
Expand All @@ -174,7 +177,7 @@ During the docking approach, there are two options for detecting `isDocked`:
1. We can check the joint states of the wheels if the current has spiked above a set threshold to indicate that the robot has made contact with the dock or other physical object.
2. The dock pose is compared with the robot pose and `isDocked` returns true when the distance drops below the specified `docking_threshold`.

The `isCharging` and `hasStoppedCharging` functions have two options:
The `isCharging` and `hasStoppedCharging` functions have two options, when using the charging dock type:
1. Subscribing to a `sensor_msgs/BatteryState` message on topic `battery_state`. The robot is considered charging when the `current` field of the message exceeds the `charging_threshold`.
2. We can return that we are charging is `isDocked() = true`, which is useful for initial testing or low-reliability docking until battery state or similar information is available.

Expand Down
26 changes: 25 additions & 1 deletion nav2_docking/opennav_docking/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,33 @@ target_link_libraries(simple_charging_dock PRIVATE
tf2::tf2
)

add_library(simple_non_charging_dock SHARED
src/simple_non_charging_dock.cpp
)
target_include_directories(simple_non_charging_dock
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>"
)
target_link_libraries(simple_non_charging_dock PUBLIC
${geometry_msgs_TARGETS}
opennav_docking_core::opennav_docking_core
pose_filter
rclcpp::rclcpp
rclcpp_lifecycle::rclcpp_lifecycle
${sensor_msgs_TARGETS}
tf2_geometry_msgs::tf2_geometry_msgs
tf2_ros::tf2_ros
)
target_link_libraries(simple_non_charging_dock PRIVATE
nav2_util::nav2_util_core
pluginlib::pluginlib
tf2::tf2
)

pluginlib_export_plugin_description_file(opennav_docking_core plugins.xml)

install(TARGETS ${library_name} controller pose_filter simple_charging_dock
install(TARGETS ${library_name} controller pose_filter simple_charging_dock simple_non_charging_dock
EXPORT ${PROJECT_NAME}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ class SimpleChargingDock : public opennav_docking_core::ChargingDock
double charging_threshold_;
// If not using an external pose reference, this is the distance threshold
double docking_threshold_;
std::string base_frame_id_;
// Offset for staging pose relative to dock pose
double staging_x_offset_;
double staging_yaw_offset_;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright (c) 2024 Open Navigation LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef OPENNAV_DOCKING__SIMPLE_NON_CHARGING_DOCK_HPP_
#define OPENNAV_DOCKING__SIMPLE_NON_CHARGING_DOCK_HPP_

#include <string>
#include <memory>
#include <vector>

#include "geometry_msgs/msg/pose_stamped.hpp"
#include "sensor_msgs/msg/battery_state.hpp"
#include "sensor_msgs/msg/joint_state.hpp"
#include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
#include "tf2/utils.h"

#include "opennav_docking_core/non_charging_dock.hpp"
#include "opennav_docking/pose_filter.hpp"

namespace opennav_docking
{

class SimpleNonChargingDock : public opennav_docking_core::NonChargingDock
{
public:
/**
* @brief Constructor
*/
SimpleNonChargingDock()
: NonChargingDock()
{}

/**
* @param parent pointer to user's node
* @param name The name of this planner
* @param tf A pointer to a TF buffer
*/
virtual void configure(
const rclcpp_lifecycle::LifecycleNode::WeakPtr & parent,
const std::string & name, std::shared_ptr<tf2_ros::Buffer> tf);

/**
* @brief Method to cleanup resources used on shutdown.
*/
virtual void cleanup() {}

/**
* @brief Method to active Behavior and any threads involved in execution.
*/
virtual void activate() {}

/**
* @brief Method to deactive Behavior and any threads involved in execution.
*/
virtual void deactivate() {}

/**
* @brief Method to obtain the dock's staging pose. This method should likely
* be using TF and the dock's pose information to find the staging pose from
* a static or parameterized staging pose relative to the docking pose
* @param pose Dock with pose
* @param frame Dock's frame of pose
* @return PoseStamped of staging pose in the specified frame
*/
virtual geometry_msgs::msg::PoseStamped getStagingPose(
const geometry_msgs::msg::Pose & pose, const std::string & frame);

/**
* @brief Method to obtain the refined pose of the dock, usually based on sensors
* @param pose The initial estimate of the dock pose.
* @param frame The frame of the initial estimate.
*/
virtual bool getRefinedPose(geometry_msgs::msg::PoseStamped & pose, std::string id);

/**
* @copydoc opennav_docking_core::ChargingDock::isDocked
*/
virtual bool isDocked();

protected:
void jointStateCallback(const sensor_msgs::msg::JointState::SharedPtr state);

// Optionally subscribe to a detected dock pose topic
rclcpp::Subscription<geometry_msgs::msg::PoseStamped>::SharedPtr dock_pose_sub_;
rclcpp::Publisher<geometry_msgs::msg::PoseStamped>::SharedPtr dock_pose_pub_;
rclcpp::Publisher<geometry_msgs::msg::PoseStamped>::SharedPtr filtered_dock_pose_pub_;
rclcpp::Publisher<geometry_msgs::msg::PoseStamped>::SharedPtr staging_pose_pub_;
// If subscribed to a detected pose topic, will contain latest message
geometry_msgs::msg::PoseStamped detected_dock_pose_;
// This is the actual dock pose once it has the specified translation/rotation applied
// If not subscribed to a topic, this is simply the database dock pose
geometry_msgs::msg::PoseStamped dock_pose_;

// Optionally subscribe to joint state message, used to determine if stalled
rclcpp::Subscription<sensor_msgs::msg::JointState>::SharedPtr joint_state_sub_;
std::vector<std::string> stall_joint_names_;
double stall_velocity_threshold_, stall_effort_threshold_;
bool is_stalled_;

// An external reference (such as image_proc::TrackMarkerNode) can be used to detect dock
bool use_external_detection_pose_;
double external_detection_timeout_;
tf2::Quaternion external_detection_rotation_;
double external_detection_translation_x_;
double external_detection_translation_y_;

// Filtering of detected poses
std::shared_ptr<PoseFilter> filter_;

// If not using an external pose reference, this is the distance threshold
double docking_threshold_;
std::string base_frame_id_;
// Offset for staging pose relative to dock pose
double staging_x_offset_;
double staging_yaw_offset_;

rclcpp_lifecycle::LifecycleNode::SharedPtr node_;
std::shared_ptr<tf2_ros::Buffer> tf2_buffer_;
};

} // namespace opennav_docking

#endif // OPENNAV_DOCKING__SIMPLE_NON_CHARGING_DOCK_HPP_
5 changes: 5 additions & 0 deletions nav2_docking/opennav_docking/plugins.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
<description>A simple charging dock plugin.</description>
</class>
</library>
<library path="simple_non_charging_dock">
<class type="opennav_docking::SimpleNonChargingDock" base_class_type="opennav_docking_core::ChargingDock">
<description>A simple non-charging dock plugin.</description>
</class>
</library>
<library path="test_dock">
<class type="opennav_docking::TestFailureDock"
base_class_type="opennav_docking_core::ChargingDock">
Expand Down
Loading

0 comments on commit f9e1024

Please sign in to comment.