diff --git a/README.md b/README.md
index 633e44f3e..6f628a764 100644
--- a/README.md
+++ b/README.md
@@ -102,8 +102,10 @@ Launch arguments are largely common to both simulation and physical robot. Howev
| 🤖🖥️ | `namespace` | Add namespace to all launched nodes.
***string:*** `env(ROBOT_NAMESPACE)` |
| 🤖🖥️ | `publish_robot_state` | Whether to publish the default Panther robot description.
***bool:*** `True` |
| 🖥️ | `robots` | The list of the robots spawned in the simulation e.g. `robots:='robot1={x: 1.0, y: -2.0}; robot2={x: 1.0, y: -4.0}'`
***string:*** `''` |
+| 🤖🖥️ | `robot_states_bt_project_path` | Path to BehaviorTree project file, responsible for robot states management.
***string:*** [`RobotStatesBT.btproj`](./panther_manager/behavior_trees/RobotStatesBT.btproj) |
| 🤖🖥️ | `safety_bt_project_path` | Path to BehaviorTree project file, responsible for safety and shutdown management.
***string:*** [`PantherSafetyBT.btproj`](./panther_manager/behavior_trees/PantherSafetyBT.btproj) |
| 🤖🖥️ | `shutdown_hosts_config_path` | Path to file with list of hosts to request shutdown.
***string:*** [`shutdown_hosts_config.yaml`](./panther_manager/config/shutdown_hosts_config.yaml) |
+| 🤖🖥️ | `use_docking` | Enable docking server.
***bool:*** `True` |
| 🤖🖥️ | `use_ekf` | Enable or disable EKF.
***bool:*** `True` |
| 🤖🖥️ | `use_sim` | Whether simulation is used.
***bool:*** `False` |
| 🤖🖥️ | `user_led_animations_file` | Path to a YAML file with a description of the user-defined animations.
***string:*** `''` |
diff --git a/panther/panther_hardware.repos b/panther/panther_hardware.repos
index c6df86e10..b9e9b0c57 100644
--- a/panther/panther_hardware.repos
+++ b/panther/panther_hardware.repos
@@ -10,7 +10,7 @@ repositories:
panther_msgs:
type: git
url: https://github.com/husarion/panther_msgs.git
- version: fcee4d9f249a62adc113eb80be4885b08024ee9c
+ version: 6eb04e3a8d30e9700b4600d0ad30415bc70fd6f9
ros_components_description:
type: git
url: https://github.com/husarion/ros_components_description.git
diff --git a/panther/panther_simulation.repos b/panther/panther_simulation.repos
index d6c6932fc..b9631fd71 100644
--- a/panther/panther_simulation.repos
+++ b/panther/panther_simulation.repos
@@ -10,7 +10,7 @@ repositories:
panther_msgs:
type: git
url: https://github.com/husarion/panther_msgs.git
- version: fcee4d9f249a62adc113eb80be4885b08024ee9c
+ version: 6eb04e3a8d30e9700b4600d0ad30415bc70fd6f9
ros_components_description:
type: git
url: https://github.com/husarion/ros_components_description.git
diff --git a/panther_docking/launch/docking.launch.py b/panther_docking/launch/docking.launch.py
index d386bd31e..84fa4b440 100644
--- a/panther_docking/launch/docking.launch.py
+++ b/panther_docking/launch/docking.launch.py
@@ -13,8 +13,8 @@
# limitations under the License.
from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
-from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch.actions import DeclareLaunchArgument
+from launch.conditions import IfCondition
from launch.substitutions import (
EnvironmentVariable,
LaunchConfiguration,
@@ -26,12 +26,13 @@
def generate_launch_description():
- use_sim = LaunchConfiguration("use_sim")
- declare_use_sim_arg = DeclareLaunchArgument(
- "use_sim",
- default_value="False",
- description="Whether simulation is used",
- choices=[True, False, "True", "False", "true", "false", "1", "0"],
+ docking_server_config_path = LaunchConfiguration("docking_server_config_path")
+ declare_docking_server_config_path_arg = DeclareLaunchArgument(
+ "docking_server_config_path",
+ default_value=PathJoinSubstitution(
+ [FindPackageShare("panther_docking"), "config", "panther_docking_server.yaml"]
+ ),
+ description=("Path to docking server configuration file."),
)
namespace = LaunchConfiguration("namespace")
@@ -41,13 +42,20 @@ def generate_launch_description():
description="Add namespace to all launched nodes.",
)
- docking_server_config_path = LaunchConfiguration("docking_server_config_path")
- declare_docking_server_config_path_arg = DeclareLaunchArgument(
- "docking_server_config_path",
- default_value=PathJoinSubstitution(
- [FindPackageShare("panther_docking"), "config", "panther_docking_server.yaml"]
- ),
- description=("Path to docking server configuration file."),
+ use_docking = LaunchConfiguration("use_docking")
+ declare_use_docking_arg = DeclareLaunchArgument(
+ "use_docking",
+ default_value="True",
+ description="Enable docking server.",
+ choices=["True", "False", "true", "false"],
+ )
+
+ use_sim = LaunchConfiguration("use_sim")
+ declare_use_sim_arg = DeclareLaunchArgument(
+ "use_sim",
+ default_value="False",
+ description="Whether simulation is used.",
+ choices=["True", "False", "true", "false"],
)
namespaced_docking_server_config = ReplaceString(
@@ -58,18 +66,21 @@ def generate_launch_description():
docking_server_node = Node(
package="opennav_docking",
executable="opennav_docking",
+ namespace=namespace,
parameters=[
namespaced_docking_server_config,
{"use_sim_time": use_sim},
],
- namespace=namespace,
+ remappings=[("~/transition_event", "~/_transition_event")],
emulate_tty=True,
+ condition=IfCondition(use_docking),
)
docking_server_activate_node = Node(
package="nav2_lifecycle_manager",
executable="lifecycle_manager",
name="nav2_docking_lifecycle_manager",
+ namespace=namespace,
parameters=[
{
"autostart": True,
@@ -79,28 +90,15 @@ def generate_launch_description():
"use_sim_time": use_sim,
},
],
- namespace=namespace,
- )
-
- station_launch = IncludeLaunchDescription(
- PythonLaunchDescriptionSource(
- PathJoinSubstitution(
- [
- FindPackageShare("panther_docking"),
- "launch",
- "station.launch.py",
- ]
- ),
- ),
- launch_arguments={"namespace": namespace}.items(),
+ condition=IfCondition(use_docking),
)
return LaunchDescription(
[
- declare_use_sim_arg,
- declare_namespace_arg,
declare_docking_server_config_path_arg,
- station_launch,
+ declare_namespace_arg,
+ declare_use_docking_arg,
+ declare_use_sim_arg,
docking_server_node,
docking_server_activate_node,
]
diff --git a/panther_gazebo/launch/simulate_robot.launch.py b/panther_gazebo/launch/simulate_robot.launch.py
index ad7252e7a..aa55478a1 100644
--- a/panther_gazebo/launch/simulate_robot.launch.py
+++ b/panther_gazebo/launch/simulate_robot.launch.py
@@ -185,6 +185,19 @@ def generate_launch_description():
emulate_tty=True,
)
+ docking_launch = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ PathJoinSubstitution(
+ [
+ FindPackageShare("panther_docking"),
+ "launch",
+ "docking.launch.py",
+ ]
+ ),
+ ),
+ launch_arguments={"namespace": namespace, "use_sim": "True"}.items(),
+ )
+
return LaunchDescription(
[
declare_battery_config_path_arg,
@@ -200,5 +213,6 @@ def generate_launch_description():
ekf_launch,
simulate_components,
gz_bridge,
+ docking_launch,
]
)
diff --git a/panther_gazebo/package.xml b/panther_gazebo/package.xml
index 89e7baaac..184e07c1c 100644
--- a/panther_gazebo/package.xml
+++ b/panther_gazebo/package.xml
@@ -34,6 +34,7 @@
nav2_common
panther_controller
panther_description
+ panther_docking
panther_lights
panther_localization
panther_manager
diff --git a/panther_manager/CMakeLists.txt b/panther_manager/CMakeLists.txt
index 1f7bcfb9e..9a9970339 100644
--- a/panther_manager/CMakeLists.txt
+++ b/panther_manager/CMakeLists.txt
@@ -11,14 +11,17 @@ set(PACKAGE_DEPENDENCIES
behaviortree_cpp
behaviortree_ros2
libssh
+ geometry_msgs
+ opennav_docking_msgs
panther_msgs
panther_utils
rclcpp
rclcpp_action
sensor_msgs
+ std_msgs
std_srvs
- yaml-cpp
- opennav_docking_msgs)
+ tf2_geometry_msgs
+ yaml-cpp)
foreach(PACKAGE IN ITEMS ${PACKAGE_DEPENDENCIES})
find_package(${PACKAGE} REQUIRED)
@@ -59,9 +62,13 @@ add_library(undock_robot_bt_node SHARED
src/plugins/action/undock_robot_node.cpp)
list(APPEND plugin_libs undock_robot_bt_node)
-add_library(are_buttons_pressed_bt_node SHARED
- src/plugins/condition/are_buttons_pressed.cpp)
-list(APPEND plugin_libs are_buttons_pressed_bt_node)
+add_library(check_bool_msg_bt_node SHARED
+ src/plugins/condition/check_bool_msg.cpp)
+list(APPEND plugin_libs check_bool_msg_bt_node)
+
+add_library(check_joy_msg_bt_node SHARED
+ src/plugins/condition/check_joy_msg.cpp)
+list(APPEND plugin_libs check_joy_msg_bt_node)
add_library(tick_after_timeout_bt_node SHARED
src/plugins/decorator/tick_after_timeout_node.cpp)
@@ -73,16 +80,19 @@ foreach(bt_plugin ${plugin_libs})
endforeach()
add_executable(
- safety_manager_node src/safety_manager_node_main.cpp
- src/safety_manager_node.cpp src/behavior_tree_manager.cpp)
+ safety_manager_node
+ src/safety_manager_node_main.cpp src/safety_manager_node.cpp
+ src/behavior_tree_manager.cpp src/behavior_tree_manager.cpp)
ament_target_dependencies(
safety_manager_node
behaviortree_ros2
+ geometry_msgs
panther_msgs
panther_utils
rclcpp
sensor_msgs
- std_msgs)
+ std_msgs
+ tf2_geometry_msgs)
target_link_libraries(safety_manager_node ${plugin_libs})
add_executable(
@@ -91,17 +101,35 @@ add_executable(
ament_target_dependencies(
lights_manager_node
behaviortree_ros2
+ geometry_msgs
panther_msgs
panther_utils
rclcpp
sensor_msgs
- std_msgs)
+ std_msgs
+ tf2_geometry_msgs)
target_link_libraries(lights_manager_node ${plugin_libs})
+add_executable(
+ robot_states_manager_node
+ src/robot_states_manager_node_main.cpp src/robot_states_manager_node.cpp
+ src/behavior_tree_manager.cpp)
+ament_target_dependencies(
+ robot_states_manager_node
+ behaviortree_ros2
+ geometry_msgs
+ panther_msgs
+ panther_utils
+ rclcpp
+ sensor_msgs
+ std_msgs
+ tf2_geometry_msgs)
+target_link_libraries(robot_states_manager_node ${plugin_libs})
+
install(TARGETS ${plugin_libs} DESTINATION lib)
install(TARGETS safety_manager_node lights_manager_node
- DESTINATION lib/${PROJECT_NAME})
+ robot_states_manager_node DESTINATION lib/${PROJECT_NAME})
install(DIRECTORY behavior_trees config launch
DESTINATION share/${PROJECT_NAME})
@@ -161,10 +189,16 @@ if(BUILD_TESTING)
list(APPEND plugin_tests ${PROJECT_NAME}_test_undock_robot_node)
ament_add_gtest(
- ${PROJECT_NAME}_test_are_buttons_pressed
- test/plugins/condition/test_are_buttons_pressed.cpp
- src/plugins/condition/are_buttons_pressed.cpp)
- list(APPEND plugin_tests ${PROJECT_NAME}_test_are_buttons_pressed)
+ ${PROJECT_NAME}_test_check_bool_msg
+ test/plugins/condition/test_check_bool_msg.cpp
+ src/plugins/condition/check_bool_msg.cpp)
+ list(APPEND plugin_tests ${PROJECT_NAME}_test_check_bool_msg)
+
+ ament_add_gtest(
+ ${PROJECT_NAME}_test_check_joy_msg
+ test/plugins/condition/test_check_joy_msg.cpp
+ src/plugins/condition/check_joy_msg.cpp)
+ list(APPEND plugin_tests ${PROJECT_NAME}_test_check_joy_msg)
ament_add_gtest(
${PROJECT_NAME}_test_tick_after_timeout_node
@@ -197,8 +231,9 @@ if(BUILD_TESTING)
${PROJECT_NAME}_test_behavior_tree_utils
PUBLIC $
$)
- ament_target_dependencies(${PROJECT_NAME}_test_behavior_tree_utils
- behaviortree_cpp behaviortree_ros2 panther_utils)
+ ament_target_dependencies(
+ ${PROJECT_NAME}_test_behavior_tree_utils behaviortree_cpp behaviortree_ros2
+ geometry_msgs panther_utils tf2_geometry_msgs)
ament_add_gtest(
${PROJECT_NAME}_test_behavior_tree_manager
@@ -221,10 +256,12 @@ if(BUILD_TESTING)
${PROJECT_NAME}_test_lights_manager_node
behaviortree_cpp
behaviortree_ros2
+ geometry_msgs
panther_msgs
panther_utils
rclcpp
sensor_msgs
+ tf2_geometry_msgs
std_msgs)
ament_add_gtest(
@@ -239,11 +276,13 @@ if(BUILD_TESTING)
${PROJECT_NAME}_test_lights_behavior_tree
behaviortree_cpp
behaviortree_ros2
+ geometry_msgs
panther_msgs
panther_utils
rclcpp
sensor_msgs
- std_msgs)
+ std_msgs
+ tf2_geometry_msgs)
ament_add_gtest(
${PROJECT_NAME}_test_safety_manager_node test/test_safety_manager_node.cpp
@@ -256,11 +295,13 @@ if(BUILD_TESTING)
${PROJECT_NAME}_test_safety_manager_node
behaviortree_cpp
behaviortree_ros2
+ geometry_msgs
panther_msgs
panther_utils
rclcpp
sensor_msgs
- std_msgs)
+ std_msgs
+ tf2_geometry_msgs)
ament_add_gtest(
${PROJECT_NAME}_test_safety_behavior_tree
@@ -274,12 +315,14 @@ if(BUILD_TESTING)
${PROJECT_NAME}_test_safety_behavior_tree
behaviortree_cpp
behaviortree_ros2
+ geometry_msgs
panther_msgs
panther_utils
rclcpp
sensor_msgs
std_msgs
- std_srvs)
+ std_srvs
+ tf2_geometry_msgs)
endif()
ament_package()
diff --git a/panther_manager/CONFIGURATION.md b/panther_manager/CONFIGURATION.md
index 3317e43a2..0ab52d2b3 100644
--- a/panther_manager/CONFIGURATION.md
+++ b/panther_manager/CONFIGURATION.md
@@ -206,6 +206,7 @@ To use your customized project, you can modify the `bt_project_file` ROS paramet
Groot2 also provides a real-time visualization tool that allows you to see and debug actively running trees. To use this tool with trees launched with the `panther_manager` package, you need to specify the port associated with the tree you want to visualize. The ports for each tree are listed below:
-- Lights tree: `10.15.20.2:5555`
-- Safety tree: `10.15.20.2:6666`
-- Shutdown tree: `10.15.20.2:7777`
+- Lights tree: `10.15.20.2:5550`
+- RobotState tree: `10.15.20.2:5555`
+- Safety tree: `10.15.20.2:5560`
+- Shutdown tree: `10.15.20.2:5565`
diff --git a/panther_manager/behavior_trees/RobotStatesBT.btproj b/panther_manager/behavior_trees/RobotStatesBT.btproj
new file mode 100644
index 000000000..964181491
--- /dev/null
+++ b/panther_manager/behavior_trees/RobotStatesBT.btproj
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+ animation ID
+ optional parameter
+ indicates if animation should repeat
+ ROS service name
+
+
+ ROS service name
+
+
+
+Topic name for sensor_msgs/Joy message type
+ Vector of provided axis inputs
+
+
+
+Topic name for sensor_msgs/Joy message type
+ Max timeout to accept the msg
+ Vector of provided axis inputs
+ Vector of provided button inputs
+
+
+ bool value indicating whether to use dock ID
+ dock ID
+ type of the dock
+ maximum staging time
+ bool value indicating whether to navigate to
+ staging pose
+ action name to call
+
+
+ time in s to wait before ticking child again
+
+
+ type of the dock
+ maximum time to get back to the staging pose
+ action name to call
+
+
+
diff --git a/panther_manager/behavior_trees/lights.xml b/panther_manager/behavior_trees/lights.xml
index 5af3c4865..c632cdc75 100644
--- a/panther_manager/behavior_trees/lights.xml
+++ b/panther_manager/behavior_trees/lights.xml
@@ -3,15 +3,13 @@
+ _skipIf="battery_status != POWER_SUPPLY_STATUS_CHARGING \
&& battery_status != POWER_SUPPLY_STATUS_FULL">
@@ -20,10 +18,8 @@
param="{battery_percent_round}"
repeating="true"
service_name="lights/set_animation"
- _skipIf="battery_percent_round == charging_anim_percent \
-&& current_anim_id == CHARGING_BATTERY_ANIM_ID"
- _onSuccess="charging_anim_percent = battery_percent_round; \
-current_anim_id = CHARGING_BATTERY_ANIM_ID"/>
+ _skipIf="battery_percent_round == charging_anim_percent \
&& current_anim_id == CHARGING_BATTERY_ANIM_ID"
+ _onSuccess="charging_anim_percent = battery_percent_round; \
current_anim_id = CHARGING_BATTERY_ANIM_ID"/>
-
-
+ _skipIf="battery_status != POWER_SUPPLY_STATUS_DISCHARGING \
&& battery_status != POWER_SUPPLY_STATUS_NOT_CHARGING">
+
+
+
+
+
+
+
+ _skipIf="battery_percent < CRITICAL_BATTERY_THRESHOLD_PERCENT \
|| battery_percent >= LOW_BATTERY_THRESHOLD_PERCENT">
param=""
repeating="true"
service_name="lights/set_animation"
- _skipIf="battery_status != POWER_SUPPLY_STATUS_UNKNOWN \
-|| current_anim_id == ERROR_ANIM_ID"
+ _skipIf="battery_status != POWER_SUPPLY_STATUS_UNKNOWN \
|| current_anim_id == ERROR_ANIM_ID"
_onSuccess="current_anim_id = ERROR_ANIM_ID"/>
diff --git a/panther_manager/behavior_trees/robot_states.xml b/panther_manager/behavior_trees/robot_states.xml
new file mode 100644
index 000000000..dd0dd3f6d
--- /dev/null
+++ b/panther_manager/behavior_trees/robot_states.xml
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ROS service name
+
+
+
Topic name for sensor_msgs/Joy message type
+ Max timeout to accept the msg
+ Vector of provided axis inputs
+ Vector of provided button inputs
+
+
+ bool value indicating whether to use dock ID
+ dock ID
+ type of the dock
+ maximum staging time
+ bool value indicating whether to navigate to
staging pose
+ action name to call
+
+
+ type of the dock
+ maximum time to get back to the staging pose
+ action name to call
+
+
+
+
diff --git a/panther_manager/config/robot_states_manager.yaml b/panther_manager/config/robot_states_manager.yaml
new file mode 100644
index 000000000..e65b274d3
--- /dev/null
+++ b/panther_manager/config/robot_states_manager.yaml
@@ -0,0 +1,12 @@
+/**:
+ robot_states_manager:
+ ros__parameters:
+ timer_frequency: 50.0
+ ros_communication_timeout:
+ availability: 2.0
+ response: 1.0
+ ros_plugin_libs:
+ - call_trigger_service_bt_node
+ - check_joy_msg_bt_node
+ - dock_robot_bt_node
+ - undock_robot_bt_node
diff --git a/panther_manager/include/panther_manager/behavior_tree_utils.hpp b/panther_manager/include/panther_manager/behavior_tree_utils.hpp
index 5ee2331e3..c73bc71f7 100644
--- a/panther_manager/include/panther_manager/behavior_tree_utils.hpp
+++ b/panther_manager/include/panther_manager/behavior_tree_utils.hpp
@@ -26,6 +26,9 @@
#include "behaviortree_cpp/utils/shared_library.h"
#include "behaviortree_ros2/plugins.hpp"
+#include
+#include
+
namespace panther_manager::behavior_tree_utils
{
@@ -93,28 +96,76 @@ inline std::string GetLoggerPrefix(const std::string & bt_node_name)
namespace BT
{
/**
- * @brief Converts a string to a vector of integers.
+ * @brief Converts a string to a vector of float.
*
* @param str The string to convert.
- * @return std::vector The vector of integers.
+ * @return std::vector The vector of float.
*
- * @throw BT::RuntimeError Throws when there is no input or cannot parse int.
+ * @throw BT::RuntimeError Throws when there is no input or cannot parse float.
*/
template <>
-inline std::vector convertFromString>(StringView str)
+inline std::vector convertFromString>(StringView str)
{
- auto parts = BT::splitString(str, ';');
- if (!parts.size()) {
- throw BT::RuntimeError("invalid input");
- } else {
- std::vector result;
- for (auto & part : parts) {
- result.push_back(convertFromString(part));
- }
-
- return result;
+ auto parts = splitString(str, ';');
+ std::vector output;
+ output.reserve(parts.size());
+ for (const StringView & part : parts) {
+ output.push_back(convertFromString(part));
}
+ return output;
}
+
+/**
+ * @brief Converts a string to a PoseStamped message.
+ *
+ * The string format should be "roll,pitch,yaw,x,y,z,frame_id" where:
+ * - roll, pitch, yaw: Euler angles in radians.
+ * - x, y, z: Position coordinates.
+ * - frame_id: Coordinate frame ID (string).
+ *
+ * @param str The string to convert.
+ * @return geometry_msgs::msg::PoseStamped The converted PoseStamped message.
+ *
+ * @throw BT::RuntimeError Throws if the input is invalid or cannot be parsed.
+ */
+template <>
+inline geometry_msgs::msg::PoseStamped convertFromString(
+ StringView str)
+{
+ auto parts = splitString(str, ';');
+
+ if (parts.size() != 7) {
+ throw BT::RuntimeError(
+ "Invalid input for PoseStamped. Expected 7 values: x;y;z;roll;pitch;yaw;frame_id");
+ }
+
+ geometry_msgs::msg::PoseStamped pose_stamped;
+
+ try {
+ // Position (x, y, z)
+ pose_stamped.pose.position.x = convertFromString(parts[0]);
+ pose_stamped.pose.position.y = convertFromString(parts[1]);
+ pose_stamped.pose.position.z = convertFromString(parts[2]);
+
+ // Orientation (R,P,Y -> Quaternion)
+ double roll = convertFromString(parts[3]);
+ double pitch = convertFromString(parts[4]);
+ double yaw = convertFromString(parts[5]);
+ tf2::Quaternion quaternion;
+ quaternion.setRPY(roll, pitch, yaw);
+ pose_stamped.pose.orientation = tf2::toMsg(quaternion);
+
+ // Frame ID and current time
+ pose_stamped.header.frame_id = convertFromString(parts[6]);
+ pose_stamped.header.stamp = rclcpp::Clock().now();
+
+ } catch (const std::exception & e) {
+ throw BT::RuntimeError("Failed to convert string to PoseStamped: " + std::string(e.what()));
+ }
+
+ return pose_stamped;
+}
+
} // namespace BT
#endif // PANTHER_MANAGER_BEHAVIOR_TREE_UTILS_HPP_
diff --git a/panther_manager/include/panther_manager/lights_manager_node.hpp b/panther_manager/include/panther_manager/lights_manager_node.hpp
index 21937cd86..a2e92ca2c 100644
--- a/panther_manager/include/panther_manager/lights_manager_node.hpp
+++ b/panther_manager/include/panther_manager/lights_manager_node.hpp
@@ -25,6 +25,7 @@
#include "std_msgs/msg/bool.hpp"
#include "panther_msgs/msg/led_animation.hpp"
+#include "panther_msgs/msg/robot_state.hpp"
#include "panther_utils/moving_average.hpp"
@@ -36,6 +37,7 @@ namespace panther_manager
using BatteryStateMsg = sensor_msgs::msg::BatteryState;
using BoolMsg = std_msgs::msg::Bool;
using LEDAnimationMsg = panther_msgs::msg::LEDAnimation;
+using RobotStateMsg = panther_msgs::msg::RobotState;
/**
* @brief This class is responsible for creating a BehaviorTree responsible for lights management,
@@ -69,12 +71,14 @@ class LightsManagerNode : public rclcpp::Node
private:
void BatteryCB(const BatteryStateMsg::SharedPtr battery);
void EStopCB(const BoolMsg::SharedPtr e_stop);
+ void RobotStateCB(const RobotStateMsg::SharedPtr robot_state);
void LightsTreeTimerCB();
float update_charging_anim_step_;
rclcpp::Subscription::SharedPtr battery_sub_;
rclcpp::Subscription::SharedPtr e_stop_sub_;
+ rclcpp::Subscription::SharedPtr robot_state_sub_;
rclcpp::TimerBase::SharedPtr lights_tree_timer_;
std::unique_ptr> battery_percent_ma_;
diff --git a/panther_manager/include/panther_manager/plugins/action/call_set_bool_service_node.hpp b/panther_manager/include/panther_manager/plugins/action/call_set_bool_service_node.hpp
index adafe1d46..96f40482b 100644
--- a/panther_manager/include/panther_manager/plugins/action/call_set_bool_service_node.hpp
+++ b/panther_manager/include/panther_manager/plugins/action/call_set_bool_service_node.hpp
@@ -36,7 +36,8 @@ class CallSetBoolService : public BT::RosServiceNode
static BT::PortsList providedPorts()
{
- return providedBasicPorts({BT::InputPort("data", "true / false value")});
+ return providedBasicPorts(
+ {BT::InputPort("data", "Boolean value to send with the service request.")});
}
virtual bool setRequest(typename Request::SharedPtr & request) override;
diff --git a/panther_manager/include/panther_manager/plugins/action/call_set_led_animation_service_node.hpp b/panther_manager/include/panther_manager/plugins/action/call_set_led_animation_service_node.hpp
index 38a915a5a..a0dcff1e1 100644
--- a/panther_manager/include/panther_manager/plugins/action/call_set_led_animation_service_node.hpp
+++ b/panther_manager/include/panther_manager/plugins/action/call_set_led_animation_service_node.hpp
@@ -36,11 +36,11 @@ class CallSetLedAnimationService : public BT::RosServiceNode("id", "animation ID"),
- BT::InputPort("param", "optional parameter"),
- BT::InputPort("repeating", "indicates if animation should repeat"),
- });
+ return providedBasicPorts(
+ {BT::InputPort("id", "Animation ID to trigger."),
+ BT::InputPort("param", "Optional animation parameter."),
+ BT::InputPort(
+ "repeating", "Specifies whether the animation should repeated continuously.")});
}
virtual bool setRequest(typename Request::SharedPtr & request) override;
diff --git a/panther_manager/include/panther_manager/plugins/action/dock_robot_node.hpp b/panther_manager/include/panther_manager/plugins/action/dock_robot_node.hpp
index 2e4a5c03f..7793bd216 100644
--- a/panther_manager/include/panther_manager/plugins/action/dock_robot_node.hpp
+++ b/panther_manager/include/panther_manager/plugins/action/dock_robot_node.hpp
@@ -48,22 +48,31 @@ class DockRobot : public BT::RosActionNode("use_dock_id", true, "Whether to use the dock's ID or dock pose fields"),
- BT::InputPort("dock_id", "Dock ID or name to use"),
- BT::InputPort("dock_type", "The dock plugin type, if using dock pose"),
- BT::InputPort(
- "max_staging_time", 1000.0, "Maximum time to navigate to the staging pose"),
- BT::InputPort(
- "navigate_to_staging_pose", true, "Whether to autonomously navigate to staging pose"),
+ return providedBasicPorts(
+ {BT::InputPort(
+ "use_dock_id", true, "Determines whether to use the dock's ID or dock pose fields."),
+ BT::InputPort(
+ "dock_id",
+ "Specifies the dock's ID or name from the dock database (used if 'use_dock_id' is true)."),
+ BT::InputPort(
+ "dock_pose",
+ "Specifies the dock's pose (used if 'use_dock_id' is false). Format: "
+ "\"x;y;z;roll;pitch;yaw;frame_id\""),
+ BT::InputPort(
+ "dock_type",
+ "Defines the type of dock being used when docking via pose. Not needed if only one dock "
+ "type is available."),
+ BT::InputPort(
+ "max_staging_time", 120.0,
+ "Maximum time allowed (in seconds) for navigating to the dock's staging pose."),
+ BT::InputPort(
+ "navigate_to_staging_pose", true,
+ "Specifies whether the robot should autonomously navigate to the staging pose."),
- BT::OutputPort(
- "success", "If the action was successful"),
- BT::OutputPort(
- "error_code", "Contextual error code, if any"),
- BT::OutputPort(
- "num_retries", "Number of retries attempted"),
- });
+ BT::OutputPort(
+ "error_code", "Returns an error code indicating the reason for failure, if any."),
+ BT::OutputPort(
+ "num_retries", "Reports the number of retry attempts made during the docking process.")});
}
};
diff --git a/panther_manager/include/panther_manager/plugins/action/shutdown_hosts_from_file_node.hpp b/panther_manager/include/panther_manager/plugins/action/shutdown_hosts_from_file_node.hpp
index 949565f61..c9eaa79e0 100644
--- a/panther_manager/include/panther_manager/plugins/action/shutdown_hosts_from_file_node.hpp
+++ b/panther_manager/include/panther_manager/plugins/action/shutdown_hosts_from_file_node.hpp
@@ -39,10 +39,8 @@ class ShutdownHostsFromFile : public ShutdownHosts
static BT::PortsList providedPorts()
{
- return {
- BT::InputPort(
- "shutdown_hosts_file", "global path to YAML file with hosts to shutdown"),
- };
+ return {BT::InputPort(
+ "shutdown_hosts_file", "Absolute path to a YAML file listing the hosts to shut down.")};
}
private:
diff --git a/panther_manager/include/panther_manager/plugins/action/shutdown_single_host_node.hpp b/panther_manager/include/panther_manager/plugins/action/shutdown_single_host_node.hpp
index 630db6505..680e849d2 100644
--- a/panther_manager/include/panther_manager/plugins/action/shutdown_single_host_node.hpp
+++ b/panther_manager/include/panther_manager/plugins/action/shutdown_single_host_node.hpp
@@ -38,14 +38,17 @@ class ShutdownSingleHost : public ShutdownHosts
static BT::PortsList providedPorts()
{
return {
- BT::InputPort("ip", "ip of the host to shutdown"),
- BT::InputPort("username", "user to log into while executing shutdown command"),
- BT::InputPort("port", "SSH communication port"),
- BT::InputPort("command", "command to execute on shutdown"),
- BT::InputPort("timeout", "time in seconds to wait for host to shutdown"),
+ BT::InputPort("ip", "IP address of the host to shut down."),
+ BT::InputPort(
+ "username", "Username to use for logging in and executing the shutdown command."),
+ BT::InputPort("port", "SSH port used for communication (default is usually 22)."),
+ BT::InputPort("command", "Shutdown command to execute on the remote host."),
+ BT::InputPort(
+ "timeout", "Maximum time (in seconds) to wait for the host to shut down."),
BT::InputPort(
- "ping_for_success", "ping host until it is not available or timeout is reached"),
- };
+ "ping_for_success",
+ "Whether to continuously ping the host until it becomes unreachable or the timeout is "
+ "reached.")};
}
private:
diff --git a/panther_manager/include/panther_manager/plugins/action/signal_shutdown_node.hpp b/panther_manager/include/panther_manager/plugins/action/signal_shutdown_node.hpp
index 7bc9709db..abee0f743 100644
--- a/panther_manager/include/panther_manager/plugins/action/signal_shutdown_node.hpp
+++ b/panther_manager/include/panther_manager/plugins/action/signal_shutdown_node.hpp
@@ -35,7 +35,7 @@ class SignalShutdown : public BT::SyncActionNode
static BT::PortsList providedPorts()
{
return {
- BT::InputPort("reason", "", "reason to shutdown robot"),
+ BT::InputPort("reason", "", "Reason to shutdown robot."),
};
}
diff --git a/panther_manager/include/panther_manager/plugins/action/undock_robot_node.hpp b/panther_manager/include/panther_manager/plugins/action/undock_robot_node.hpp
index ade7380ff..820ad3c20 100644
--- a/panther_manager/include/panther_manager/plugins/action/undock_robot_node.hpp
+++ b/panther_manager/include/panther_manager/plugins/action/undock_robot_node.hpp
@@ -49,16 +49,17 @@ class UndockRobot : public BT::RosActionNode(
- "dock_type", "The dock plugin type, if not previous instance used for docking"),
- BT::InputPort(
- "max_undocking_time", 30.0, "Maximum time to get back to the staging pose"),
+ return providedBasicPorts(
+ {BT::InputPort(
+ "dock_type",
+ "Specifies the dock plugin type to use for undocking. If empty, the previously used dock "
+ "type is assumed."),
+ BT::InputPort(
+ "max_undocking_time", 30.0,
+ "Maximum allowable time (in seconds) to undock and return to the staging pose."),
- BT::OutputPort(
- "success", "If the action was successful"),
- BT::OutputPort("error_code", "Error code"),
- });
+ BT::OutputPort(
+ "error_code", "Returns an error code if the undocking process fails.")});
}
};
diff --git a/panther_manager/include/panther_manager/plugins/condition/are_buttons_pressed.hpp b/panther_manager/include/panther_manager/plugins/condition/check_bool_msg.hpp
similarity index 61%
rename from panther_manager/include/panther_manager/plugins/condition/are_buttons_pressed.hpp
rename to panther_manager/include/panther_manager/plugins/condition/check_bool_msg.hpp
index 8b0534b5e..fbe52a88d 100644
--- a/panther_manager/include/panther_manager/plugins/condition/are_buttons_pressed.hpp
+++ b/panther_manager/include/panther_manager/plugins/condition/check_bool_msg.hpp
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef PANTHER_MANAGER_PLUGINS_CONDITION_ARE_BUTTONS_PRESSED_HPP_
-#define PANTHER_MANAGER_PLUGINS_CONDITION_ARE_BUTTONS_PRESSED_HPP_
+#ifndef PANTHER_MANAGER_PLUGINS_CONDITION_CHECK_BOOL_MSG_HPP_
+#define PANTHER_MANAGER_PLUGINS_CONDITION_CHECK_BOOL_MSG_HPP_
#include
#include
@@ -22,34 +22,34 @@
#include
#include
-#include
+#include
#include "panther_manager/behavior_tree_utils.hpp"
namespace panther_manager
{
-class AreButtonsPressed : public BT::RosTopicSubNode
+// FIXME: There is no possibility to set QoS profile. Add it in the future to subscribe e_stop.
+class CheckBoolMsg : public BT::RosTopicSubNode
{
+ using BoolMsg = std_msgs::msg::Bool;
+
public:
- AreButtonsPressed(
+ CheckBoolMsg(
const std::string & name, const BT::NodeConfig & conf, const BT::RosNodeParams & params)
- : BT::RosTopicSubNode(name, conf, params)
+ : BT::RosTopicSubNode(name, conf, params)
{
}
- BT::NodeStatus onTick(const std::shared_ptr & last_msg);
+ BT::NodeStatus onTick(const BoolMsg::SharedPtr & last_msg);
static BT::PortsList providedPorts()
{
return providedBasicPorts(
- {BT::InputPort>("buttons", "state of buttons to accept a condition")});
+ {BT::InputPort("data", "Specifies the expected state of the data field.")});
}
-
-private:
- std::vector buttons_;
};
} // namespace panther_manager
-#endif // PANTHER_MANAGER_PLUGINS_CONDITION_ARE_BUTTONS_PRESSED_HPP_
+#endif // PANTHER_MANAGER_PLUGINS_CONDITION_CHECK_BOOL_MSG_HPP_
diff --git a/panther_manager/include/panther_manager/plugins/condition/check_joy_msg.hpp b/panther_manager/include/panther_manager/plugins/condition/check_joy_msg.hpp
new file mode 100644
index 000000000..f50aafce0
--- /dev/null
+++ b/panther_manager/include/panther_manager/plugins/condition/check_joy_msg.hpp
@@ -0,0 +1,68 @@
+// Copyright 2024 Husarion sp. z o.o.
+//
+// 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 PANTHER_MANAGER_PLUGINS_CONDITION_CHECK_JOY_MSG_HPP_
+#define PANTHER_MANAGER_PLUGINS_CONDITION_CHECK_JOY_MSG_HPP_
+
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+
+#include "panther_manager/behavior_tree_utils.hpp"
+
+namespace panther_manager
+{
+
+class CheckJoyMsg : public BT::RosTopicSubNode
+{
+ using JoyMsg = sensor_msgs::msg::Joy;
+
+public:
+ CheckJoyMsg(
+ const std::string & name, const BT::NodeConfig & conf, const BT::RosNodeParams & params)
+ : BT::RosTopicSubNode(name, conf, params)
+ {
+ }
+
+ BT::NodeStatus onTick(const JoyMsg::SharedPtr & last_msg);
+
+ static BT::PortsList providedPorts()
+ {
+ return providedBasicPorts(
+ {BT::InputPort>(
+ "axes", "",
+ "Specifies the expected state of the axes field. An empty string (\"\") means the value "
+ "is ignored."),
+ BT::InputPort>(
+ "buttons", "",
+ "Specifies the expected state of the buttons field. An empty string (\"\") means the "
+ "value is ignored."),
+ BT::InputPort(
+ "timeout", 0.0, "Maximum allowable time delay to accept the condition.")});
+ }
+
+private:
+ bool checkAxes(const JoyMsg::SharedPtr & last_msg);
+ bool checkButtons(const JoyMsg::SharedPtr & last_msg);
+ bool checkTimeout(const JoyMsg::SharedPtr & last_msg);
+};
+
+} // namespace panther_manager
+
+#endif // PANTHER_MANAGER_PLUGINS_CONDITION_CHECK_JOY_MSG_HPP_
diff --git a/panther_manager/include/panther_manager/plugins/decorator/tick_after_timeout_node.hpp b/panther_manager/include/panther_manager/plugins/decorator/tick_after_timeout_node.hpp
index 20a273e5f..7661abf95 100644
--- a/panther_manager/include/panther_manager/plugins/decorator/tick_after_timeout_node.hpp
+++ b/panther_manager/include/panther_manager/plugins/decorator/tick_after_timeout_node.hpp
@@ -31,7 +31,8 @@ class TickAfterTimeout : public BT::DecoratorNode
static BT::PortsList providedPorts()
{
- return {BT::InputPort("timeout", "time in s to wait before ticking child again")};
+ return {BT::InputPort(
+ "timeout", "Time in seconds to wait before ticking the child node again")};
}
private:
diff --git a/panther_manager/include/panther_manager/robot_states_manager_node.hpp b/panther_manager/include/panther_manager/robot_states_manager_node.hpp
new file mode 100644
index 000000000..f971e1971
--- /dev/null
+++ b/panther_manager/include/panther_manager/robot_states_manager_node.hpp
@@ -0,0 +1,82 @@
+// Copyright 2024 Husarion sp. z o.o.
+//
+// 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 PANTHER_MANAGER_ROBOT_STATES_MANAGER_NODE_HPP_
+#define PANTHER_MANAGER_ROBOT_STATES_MANAGER_NODE_HPP_
+
+#include
+#include
+
+#include "behaviortree_cpp/bt_factory.h"
+#include "rclcpp/rclcpp.hpp"
+
+#include "std_msgs/msg/bool.hpp"
+
+#include "panther_msgs/msg/robot_state.hpp"
+#include "panther_utils/moving_average.hpp"
+
+#include
+
+namespace panther_manager
+{
+
+using BoolMsg = std_msgs::msg::Bool;
+using RobotStateMsg = panther_msgs::msg::RobotState;
+
+/**
+ * @brief This class is responsible for creating a BehaviorTree responsible for docking management,
+ * spinning it, and updating blackboard entries based on subscribed topics.
+ */
+class RobotStatesManagerNode : public rclcpp::Node
+{
+public:
+ RobotStatesManagerNode(
+ const std::string & node_name, const rclcpp::NodeOptions & options = rclcpp::NodeOptions());
+ ~RobotStatesManagerNode() {}
+
+ void Initialize();
+
+protected:
+ void DeclareParameters();
+ void RegisterBehaviorTree();
+ std::map CreateBlackboard();
+
+ /**
+ * @brief Checks whether the required blackboard entries for the docking tree are present. These
+ * entries are usually updated when the first ROS message containing required information is
+ * received.
+ *
+ * @return True if all required blackboard entries are present.
+ */
+ bool SystemReady();
+
+ std::unique_ptr docking_tree_manager_;
+
+private:
+ void EStopCB(const BoolMsg::SharedPtr e_stop);
+ void RobotStatesTimerCB();
+
+ void PublishRobotStateMsg();
+ RobotStateMsg CreateRobotStateMsg(int8_t state_id);
+
+ rclcpp::Subscription::SharedPtr e_stop_sub_;
+ rclcpp::Publisher::SharedPtr robot_state_pub_;
+ rclcpp::TimerBase::SharedPtr docking_tree_timer_;
+
+ BT::BehaviorTreeFactory factory_;
+};
+
+} // namespace panther_manager
+
+#endif // PANTHER_MANAGER_ROBOT_STATES_MANAGER_NODE_HPP_
diff --git a/panther_manager/include/panther_manager/types.hpp b/panther_manager/include/panther_manager/types.hpp
new file mode 100644
index 000000000..4fa55c1e3
--- /dev/null
+++ b/panther_manager/include/panther_manager/types.hpp
@@ -0,0 +1,29 @@
+// Copyright 2024 Husarion sp. z o.o.
+//
+// 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 PANTHER_MANAGER_TYPES_HPP_
+#define PANTHER_MANAGER_TYPES_HPP_
+
+namespace panther_manager
+{
+
+enum DockingCmd : unsigned {
+ DOCKING_CMD_NONE = 0U,
+ DOCKING_CMD_DOCK = 1U,
+ DOCKING_CMD_UNDOCK = 2U,
+};
+
+} // namespace panther_manager
+
+#endif // PANTHER_MANAGER_TYPES_HPP_
diff --git a/panther_manager/launch/manager.launch.py b/panther_manager/launch/manager.launch.py
index 14f4c8c75..2d78d058b 100644
--- a/panther_manager/launch/manager.launch.py
+++ b/panther_manager/launch/manager.launch.py
@@ -32,6 +32,15 @@ def generate_launch_description():
panther_version = EnvironmentVariable(name="PANTHER_ROBOT_VERSION", default_value="1.0")
panther_manager_dir = FindPackageShare("panther_manager")
+ docking_bt_project_path = LaunchConfiguration("docking_bt_project_path")
+ declare_docking_bt_project_path_arg = DeclareLaunchArgument(
+ "docking_bt_project_path",
+ default_value=PathJoinSubstitution(
+ [panther_manager_dir, "behavior_trees", "RobotStatesBT.btproj"]
+ ),
+ description="Path to BehaviorTree project file, responsible for robot states management.",
+ )
+
lights_bt_project_path = LaunchConfiguration("lights_bt_project_path")
declare_lights_bt_project_path_arg = DeclareLaunchArgument(
"lights_bt_project_path",
@@ -78,6 +87,18 @@ def generate_launch_description():
description="Whether simulation is used",
)
+ robot_states_manager_node = Node(
+ package="panther_manager",
+ executable="robot_states_manager_node",
+ name="robot_states_manager",
+ parameters=[
+ PathJoinSubstitution([panther_manager_dir, "config", "robot_states_manager.yaml"]),
+ {"bt_project_path": docking_bt_project_path},
+ ],
+ namespace=namespace,
+ emulate_tty=True,
+ )
+
lights_manager_node = Node(
package="panther_manager",
executable="lights_manager_node",
@@ -107,11 +128,13 @@ def generate_launch_description():
)
actions = [
+ declare_docking_bt_project_path_arg,
declare_lights_bt_project_path_arg,
declare_safety_bt_project_path_arg,
declare_namespace_arg,
declare_shutdown_hosts_config_path_arg,
declare_use_sim_arg,
+ robot_states_manager_node,
lights_manager_node,
safety_manager_node,
]
diff --git a/panther_manager/package.xml b/panther_manager/package.xml
index 6da23d973..a9cd5f765 100644
--- a/panther_manager/package.xml
+++ b/panther_manager/package.xml
@@ -19,15 +19,18 @@
behaviortree_cpp
behaviortree_ros2
+ geometry_msgs
iputils-ping
libboost-dev
libssh-dev
+ opennav_docking_msgs
panther_msgs
panther_utils
rclcpp
rclcpp_action
sensor_msgs
std_srvs
+ tf2_geometry_msgs
yaml-cpp
ament_lint_auto
diff --git a/panther_manager/src/lights_manager_node.cpp b/panther_manager/src/lights_manager_node.cpp
index 53ff612bd..c1c479995 100644
--- a/panther_manager/src/lights_manager_node.cpp
+++ b/panther_manager/src/lights_manager_node.cpp
@@ -49,7 +49,7 @@ LightsManagerNode::LightsManagerNode(
battery_percent_window_len, 1.0);
const auto initial_blackboard = CreateLightsInitialBlackboard();
- lights_tree_manager_ = std::make_unique("Lights", initial_blackboard, 5555);
+ lights_tree_manager_ = std::make_unique("Lights", initial_blackboard, 5550);
RCLCPP_INFO(this->get_logger(), "Node constructed successfully.");
}
@@ -68,6 +68,9 @@ void LightsManagerNode::Initialize()
e_stop_sub_ = this->create_subscription(
"hardware/e_stop", rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable(),
std::bind(&LightsManagerNode::EStopCB, this, _1));
+ robot_state_sub_ = this->create_subscription(
+ "robot_state", rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable(),
+ std::bind(&LightsManagerNode::RobotStateCB, this, _1));
const float timer_freq = this->get_parameter("timer_frequency").as_double();
const auto timer_period_ms =
@@ -150,6 +153,13 @@ std::map LightsManagerNode::CreateLightsInitialBlackboard
{"CRITICAL_BATTERY_THRESHOLD_PERCENT", critical_battery_threshold_percent},
{"LOW_BATTERY_ANIM_PERIOD", low_battery_anim_period},
{"LOW_BATTERY_THRESHOLD_PERCENT", low_battery_threshold_percent},
+ // robot states
+ {"robot_state", int(RobotStateMsg::E_STOP)},
+ {"ROBOT_STATE_ERROR", int(RobotStateMsg::ERROR)},
+ {"ROBOT_STATE_ESTOP", int(RobotStateMsg::E_STOP)},
+ {"ROBOT_STATE_STANDBY", int(RobotStateMsg::STANDBY)},
+ {"ROBOT_STATE_DOCKING", int(RobotStateMsg::DOCKING)},
+ {"ROBOT_STATE_SUCCESS", int(RobotStateMsg::SUCCESS)},
// anim constants
{"E_STOP_ANIM_ID", unsigned(LEDAnimationMsg::E_STOP)},
{"READY_ANIM_ID", unsigned(LEDAnimationMsg::READY)},
@@ -170,7 +180,6 @@ std::map LightsManagerNode::CreateLightsInitialBlackboard
{"POWER_SUPPLY_STATUS_FULL", unsigned(BatteryStateMsg::POWER_SUPPLY_STATUS_FULL)},
// battery health constants
{"POWER_SUPPLY_HEALTH_OVERHEAT", unsigned(BatteryStateMsg::POWER_SUPPLY_HEALTH_OVERHEAT)},
-
};
RCLCPP_INFO(this->get_logger(), "Blackboard created.");
@@ -218,6 +227,11 @@ void LightsManagerNode::LightsTreeTimerCB()
}
}
+void LightsManagerNode::RobotStateCB(const RobotStateMsg::SharedPtr robot_state)
+{
+ lights_tree_manager_->GetBlackboard()->set("robot_state", robot_state->state_id);
+}
+
bool LightsManagerNode::SystemReady()
{
if (
diff --git a/panther_manager/src/plugins/action/dock_robot_node.cpp b/panther_manager/src/plugins/action/dock_robot_node.cpp
index 065476be6..8f5a60cf0 100644
--- a/panther_manager/src/plugins/action/dock_robot_node.cpp
+++ b/panther_manager/src/plugins/action/dock_robot_node.cpp
@@ -27,7 +27,6 @@ bool DockRobot::setGoal(Goal & goal)
}
if (!this->getInput("use_dock_id", goal.use_dock_id)) {
- goal.use_dock_id = false;
RCLCPP_WARN_STREAM(
this->logger(), GetLoggerPrefix(name())
<< "use_dock_id not set, using default value: " << goal.use_dock_id);
@@ -41,7 +40,6 @@ bool DockRobot::setGoal(Goal & goal)
}
if (!this->getInput("navigate_to_staging_pose", goal.navigate_to_staging_pose)) {
- goal.navigate_to_staging_pose = false;
RCLCPP_WARN_STREAM(
this->logger(), GetLoggerPrefix(name()) << "navigate_to_staging_pose not set, using default "
"value: "
@@ -63,7 +61,6 @@ BT::NodeStatus DockRobot::onResultReceived(const WrappedResult & wr)
{
const auto & result = wr.result;
- this->setOutput("success", result->success);
this->setOutput("error_code", result->error_code);
this->setOutput("num_retries", result->num_retries);
diff --git a/panther_manager/src/plugins/action/undock_robot_node.cpp b/panther_manager/src/plugins/action/undock_robot_node.cpp
index 89c8cc101..e47c000a9 100644
--- a/panther_manager/src/plugins/action/undock_robot_node.cpp
+++ b/panther_manager/src/plugins/action/undock_robot_node.cpp
@@ -39,7 +39,6 @@ BT::NodeStatus UndockRobot::onResultReceived(const WrappedResult & wr)
{
const auto & result = wr.result;
- this->setOutput("success", result->success);
this->setOutput("error_code", result->error_code);
if (result->success) {
diff --git a/panther_manager/src/plugins/condition/are_buttons_pressed.cpp b/panther_manager/src/plugins/condition/are_buttons_pressed.cpp
deleted file mode 100644
index 1aad81af7..000000000
--- a/panther_manager/src/plugins/condition/are_buttons_pressed.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2024 Husarion sp. z o.o.
-//
-// 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.
-
-#include "panther_manager/plugins/condition/are_buttons_pressed.hpp"
-
-#include "panther_manager/behavior_tree_utils.hpp"
-
-namespace panther_manager
-{
-
-BT::NodeStatus AreButtonsPressed::onTick(const std::shared_ptr & last_msg)
-{
- getInput>("buttons", buttons_);
-
- if (!last_msg) {
- RCLCPP_WARN_STREAM(this->logger(), GetLoggerPrefix(name()) << "There is no joy messages!");
- return BT::NodeStatus::FAILURE;
- }
-
- if (last_msg->buttons.size() < buttons_.size()) {
- RCLCPP_ERROR_STREAM(
- this->logger(), GetLoggerPrefix(name()) << "Joy message has " << last_msg->buttons.size()
- << " buttons, expected at least " << buttons_.size());
- return BT::NodeStatus::FAILURE;
- }
-
- if (std::equal(buttons_.begin(), buttons_.end(), last_msg->buttons.begin())) {
- return BT::NodeStatus::SUCCESS;
- }
-
- return BT::NodeStatus::FAILURE;
-}
-
-} // namespace panther_manager
-
-#include "behaviortree_ros2/plugins.hpp"
-CreateRosNodePlugin(panther_manager::AreButtonsPressed, "AreButtonsPressed");
diff --git a/panther_manager/src/plugins/condition/check_bool_msg.cpp b/panther_manager/src/plugins/condition/check_bool_msg.cpp
new file mode 100644
index 000000000..a6de3c376
--- /dev/null
+++ b/panther_manager/src/plugins/condition/check_bool_msg.cpp
@@ -0,0 +1,34 @@
+// Copyright 2024 Husarion sp. z o.o.
+//
+// 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.
+
+#include "panther_manager/plugins/condition/check_bool_msg.hpp"
+
+#include "panther_manager/behavior_tree_utils.hpp"
+
+namespace panther_manager
+{
+
+BT::NodeStatus CheckBoolMsg::onTick(const BoolMsg::SharedPtr & last_msg)
+{
+ bool expected_data;
+ getInput("data", expected_data);
+
+ return (last_msg && expected_data == last_msg->data) ? BT::NodeStatus::SUCCESS
+ : BT::NodeStatus::FAILURE;
+}
+
+} // namespace panther_manager
+
+#include "behaviortree_ros2/plugins.hpp"
+CreateRosNodePlugin(panther_manager::CheckBoolMsg, "CheckBoolMsg");
diff --git a/panther_manager/src/plugins/condition/check_joy_msg.cpp b/panther_manager/src/plugins/condition/check_joy_msg.cpp
new file mode 100644
index 000000000..f99f476a3
--- /dev/null
+++ b/panther_manager/src/plugins/condition/check_joy_msg.cpp
@@ -0,0 +1,81 @@
+// Copyright 2024 Husarion sp. z o.o.
+//
+// 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.
+
+#include "panther_manager/plugins/condition/check_joy_msg.hpp"
+
+#include "panther_manager/behavior_tree_utils.hpp"
+
+namespace panther_manager
+{
+
+BT::NodeStatus CheckJoyMsg::onTick(const JoyMsg::SharedPtr & last_msg)
+{
+ return (last_msg && checkAxes(last_msg) && checkButtons(last_msg) && checkTimeout(last_msg))
+ ? BT::NodeStatus::SUCCESS
+ : BT::NodeStatus::FAILURE;
+}
+
+bool CheckJoyMsg::checkAxes(const JoyMsg::SharedPtr & last_msg)
+{
+ std::vector expected_axes;
+ getInput>("axes", expected_axes);
+
+ if (expected_axes.empty()) {
+ return true;
+ }
+
+ if (last_msg->axes.size() != expected_axes.size()) {
+ RCLCPP_WARN_STREAM(
+ this->logger(), GetLoggerPrefix(name())
+ << "Joy message has " << last_msg->axes.size()
+ << " axes, expected at least " << expected_axes.size());
+ return false;
+ }
+
+ return std::equal(expected_axes.begin(), expected_axes.end(), last_msg->axes.begin());
+}
+
+bool CheckJoyMsg::checkButtons(const JoyMsg::SharedPtr & last_msg)
+{
+ std::vector expected_buttons;
+ getInput>("buttons", expected_buttons);
+
+ if (expected_buttons.empty()) {
+ return true;
+ }
+
+ if (last_msg->buttons.size() != expected_buttons.size()) {
+ RCLCPP_WARN_STREAM(
+ this->logger(), GetLoggerPrefix(name())
+ << "Joy message has " << last_msg->axes.size()
+ << " axes, expected at least " << expected_buttons.size());
+ return false;
+ }
+
+ return std::equal(expected_buttons.begin(), expected_buttons.end(), last_msg->buttons.begin());
+}
+
+bool CheckJoyMsg::checkTimeout(const JoyMsg::SharedPtr & last_msg)
+{
+ double max_timeout;
+ getInput("timeout", max_timeout);
+
+ return (max_timeout <= 0.0) ||
+ (rclcpp::Clock().now().seconds() < last_msg->header.stamp.sec + max_timeout);
+}
+
+} // namespace panther_manager
+
+#include "behaviortree_ros2/plugins.hpp"
+CreateRosNodePlugin(panther_manager::CheckJoyMsg, "CheckJoyMsg");
diff --git a/panther_manager/src/robot_states_manager_node.cpp b/panther_manager/src/robot_states_manager_node.cpp
new file mode 100644
index 000000000..b9e4417c3
--- /dev/null
+++ b/panther_manager/src/robot_states_manager_node.cpp
@@ -0,0 +1,213 @@
+// Copyright 2024 Husarion sp. z o.o.
+//
+// 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.
+
+#include "panther_manager/robot_states_manager_node.hpp"
+
+#include
+#include
+#include
+#include