Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ROS2 docking utils #462

Merged
merged 62 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
05eb483
added half tested panther_docking package
delihus Aug 15, 2024
c8cfb37
Added tests
delihus Aug 19, 2024
4dab7b1
Added redme
delihus Aug 20, 2024
a582884
run precommit
delihus Aug 20, 2024
0584719
Fix typo
delihus Aug 20, 2024
ea582cf
After coderabbit review
delihus Aug 20, 2024
cc093ce
fixed parameters | fixed exporting_package
delihus Aug 20, 2024
5634f15
Adjust cmake
delihus Aug 22, 2024
7fdd090
Added getParameters and declareParameters | review suggestions
delihus Aug 22, 2024
0b8c46e
added offsetPose | moved transformPose to ros_utils
delihus Aug 22, 2024
bfb4811
removed hardware deps
delihus Aug 22, 2024
a19a85f
Moved TransformPose to tf2_utils
delihus Aug 22, 2024
60f460f
added filter description
delihus Aug 23, 2024
2213878
removed realtime_tools
delihus Aug 23, 2024
3a87e3d
Modef OffsetFunction utils
delihus Aug 23, 2024
c5bb087
added and applied ArePosesNear()
delihus Aug 24, 2024
12a084d
fix tf utils header guard
delihus Aug 24, 2024
f0b0396
Added comment
delihus Aug 26, 2024
c00010d
Added locking node
delihus Aug 26, 2024
e77e0d5
Update docking to develop (#399)
delihus Aug 28, 2024
8dcbd36
Merge remote-tracking branch 'origin/ros2-devel' into ros2-docking
delihus Aug 28, 2024
3bc8f34
ROS2 docking manager plugins (#394)
delihus Aug 28, 2024
b3b4fbb
JoySubscription BT node (#397)
delihus Aug 29, 2024
b993bf3
Merge remote-tracking branch 'origin/ros2-devel' into ros2-docking
delihus Aug 29, 2024
97d85c2
Fixed model
delihus Sep 9, 2024
09a6410
Revert "Fixed model"
delihus Sep 9, 2024
4a77928
Fixed model
delihus Sep 9, 2024
758ec7f
Readme update
delihus Sep 11, 2024
638d22c
Update panther_docking/README.md
delihus Sep 13, 2024
2bc81af
Added exec depend and pip install to cmake
delihus Sep 13, 2024
165cedb
add python as interpreter
delihus Sep 13, 2024
c790c53
Merge pull request #409 from husarion/ros2-docking-components
pawelirh Sep 13, 2024
863ef77
Fixed namespaces and updated ros_component_description (#424)
delihus Oct 10, 2024
4189b20
Ros2 docking manager no lights (#430)
rafal-gorecki Nov 8, 2024
5fc7560
ROS2 docking fix joy (#439)
delihus Nov 8, 2024
4ee2f68
Charging Dock Plugin based on dock pose subscriber (#431)
delihus Nov 8, 2024
0327ac4
Ros2 docking charging dock wibotic msgs (#436)
delihus Nov 19, 2024
b9e11a5
ROS2 docking nav2 (#443)
delihus Nov 29, 2024
0e70171
Ros2 docking tf compare (#451)
delihus Dec 6, 2024
469e1e6
Ros2 docking lifecycle node (#453)
delihus Dec 6, 2024
31ae7ce
Changed names in package
delihus Dec 6, 2024
7c29b8c
Revert "Changed names in package"
delihus Dec 6, 2024
14b3297
ROS2 Docking - panther_docking to husarion_ugv_docking (#459)
delihus Dec 11, 2024
66d943f
Removed docking
delihus Dec 11, 2024
177de7c
Merge remote-tracking branch 'origin/ros2-devel' into ros2-docking-utils
delihus Dec 11, 2024
9788164
Removed docking manager
delihus Dec 11, 2024
624d8ad
Removed docking manager
delihus Dec 11, 2024
863c835
Fixed after merge
delihus Dec 12, 2024
39a7f12
Merge remote-tracking branch 'origin/ros2-devel' into ros2-docking-utils
delihus Dec 12, 2024
9345045
Merge remote-tracking branch 'origin/ros2-devel' into ros2-docking-utils
delihus Dec 12, 2024
645f833
Code rabbit ai suggestions
delihus Dec 17, 2024
6eda511
Merge remote-tracking branch 'origin/ros2-devel' into ros2-docking-utils
delihus Dec 17, 2024
eda1ea1
Remove accident panther_manager
delihus Dec 20, 2024
1c09d65
Removed dock plugins
delihus Dec 20, 2024
2bebce6
Fixed path
delihus Dec 20, 2024
c3a8732
Fixed path
delihus Dec 20, 2024
afe42db
Fixed path
delihus Dec 20, 2024
5f4d2ec
Merge remote-tracking branch 'origin/ros2-devel' into ros2-docking-utils
delihus Dec 20, 2024
0f151e3
Removed docking files
delihus Dec 20, 2024
f2ec111
Removed tf2_utils
delihus Dec 20, 2024
dbb95d0
removed station launch
delihus Dec 20, 2024
47df893
Fixed HEADER GUARD
delihus Dec 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion husarion_ugv/hardware_deps.repos
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ repositories:
ros_components_description:
type: git
url: https://github.com/husarion/ros_components_description.git
version: b29f5637a243b0008ac197032575c8df47883b2c
version: 517350ac672b7383c3377e63244353a104096c39
ros2_controllers: # Caused by two error: 1. https://github.com/ros-controls/ros2_controllers/pull/1104 2. There is no nice way to change `sensor_name` imu_bradcaster param when spawning multiple robots -> ros2_control refer only to single imu entity
type: git
url: https://github.com/husarion/ros2_controllers/
Expand Down
2 changes: 1 addition & 1 deletion husarion_ugv/simulation_deps.repos
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ repositories:
ros_components_description:
type: git
url: https://github.com/husarion/ros_components_description.git
version: b29f5637a243b0008ac197032575c8df47883b2c
version: 517350ac672b7383c3377e63244353a104096c39
ros2_controllers: # Caused by two error: 1. https://github.com/ros-controls/ros2_controllers/pull/1104 2. There is no nice way to change `sensor_name` imu_bradcaster param when spawning multiple robots -> ros2_control refer only to single imu entity
type: git
url: https://github.com/husarion/ros2_controllers/
Expand Down
9 changes: 9 additions & 0 deletions husarion_ugv_gazebo/launch/simulation.launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ def generate_launch_description():
),
)

spawn_charging_docks = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
PathJoinSubstitution(
[FindPackageShare("panther_gazebo"), "launch", "spawn_charging_docks.launch.py"]
)
),
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Incorrect package reference in launch file path

The launch file spawn_charging_docks.launch.py exists but is located in the husarion_ugv_gazebo package, not in panther_gazebo. The launch file path in simulation.launch.py needs to be updated to reference the correct package.

  • Update line 99 in simulation.launch.py:
[FindPackageShare("panther_gazebo"), "launch", "spawn_charging_docks.launch.py"]

should be:

[FindPackageShare("husarion_ugv_gazebo"), "launch", "spawn_charging_docks.launch.py"]
🔗 Analysis chain

Verify charging dock launch file availability

The launch action is correctly implemented, but we should verify the existence of the referenced launch file.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify the existence of the charging docks launch file
fd -t f "spawn_charging_docks.launch.py" 

Length of output: 98


Script:

#!/bin/bash
# Check if the launch file is in the correct package (panther_gazebo)
fd -t f -p "panther_gazebo.*spawn_charging_docks.launch.py"

# Let's also check the content of the found launch file to understand the discrepancy
cat husarion_ugv_gazebo/launch/spawn_charging_docks.launch.py

Length of output: 4332


actions = [
declare_gz_gui,
declare_namespace_arg,
Expand All @@ -102,6 +110,7 @@ def generate_launch_description():
gz_sim,
rviz_launch,
simulate_robots,
spawn_charging_docks,
]

return LaunchDescription(actions)
125 changes: 125 additions & 0 deletions husarion_ugv_gazebo/launch/spawn_charging_docks.launch.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, how we should spawn station. This solutions require duplicating config from docking_parameters.
Other option can be

  1. Moving it into husarion_ugv_docking and run it only if use_sim=true.
  2. In other hand we can add some yaml with station position in husarion_gz_worlds.

Copy link
Contributor Author

@delihus delihus Dec 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Okay!
  2. I think it will make a dependency between panther-navigation repo and husarion_gz_worlds

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved spawn to husarion_ugv_docking

Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/usr/bin/env python3

# 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.

import os
from tempfile import NamedTemporaryFile

import imageio
import yaml
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, OpaqueFunction
from launch.conditions import IfCondition
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare


def generate_apriltag_and_get_path(tag_id):
from moms_apriltag import TagGenerator2

tag_generator = TagGenerator2("tag36h11")
tag_image = tag_generator.generate(tag_id, scale=1000)
temp_file = NamedTemporaryFile(suffix=f"_tag_{tag_id}.png", delete=False)

imageio.imwrite(temp_file.name, tag_image)
return temp_file.name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use a context manager when working with temporary files

To ensure proper resource management and avoid potential file leaks, use a context manager (with statement) when creating and using temporary files.

Apply this diff to use a context manager:

 def generate_apriltag_and_get_path(tag_id):
     from moms_apriltag import TagGenerator2
 
     tag_generator = TagGenerator2("tag36h11")
     tag_image = tag_generator.generate(tag_id, scale=1000)
-    temp_file = NamedTemporaryFile(suffix=f"_tag_{tag_id}.png", delete=False)
-
-    imageio.imwrite(temp_file.name, tag_image)
-    return temp_file.name
+    with NamedTemporaryFile(suffix=f"_tag_{tag_id}.png", delete=False) as temp_file:
+        imageio.imwrite(temp_file.name, tag_image)
+        return temp_file.name
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
temp_file = NamedTemporaryFile(suffix=f"_tag_{tag_id}.png", delete=False)
imageio.imwrite(temp_file.name, tag_image)
return temp_file.name
with NamedTemporaryFile(suffix=f"_tag_{tag_id}.png", delete=False) as temp_file:
imageio.imwrite(temp_file.name, tag_image)
return temp_file.name
🧰 Tools
🪛 Ruff (0.8.0)

35-35: Use a context manager for opening files

(SIM115)



def spawn_stations(context, *args, **kwargs):
docking_server_config_path = LaunchConfiguration("docking_server_config_path").perform(context)
use_docking = LaunchConfiguration("use_docking").perform(context)
docking_server_config = None

try:
with open(os.path.join(docking_server_config_path)) as file:
docking_server_config = yaml.safe_load(file)
if not isinstance(docking_server_config, dict) or "/**" not in docking_server_config:
raise ValueError("Invalid configuration structure")
except Exception as e:
print(f"Error loading docking server config: {str(e)}")
return []
rafal-gorecki marked this conversation as resolved.
Show resolved Hide resolved

actions = []

ros_parameters = docking_server_config["/**"]["ros__parameters"]
docks_names = ros_parameters["docks"]
for dock_name in docks_names:
pose = ros_parameters[dock_name]["pose"]

spawn_station = Node(
package="ros_gz_sim",
executable="create",
arguments=[
"-name",
[dock_name, "_station"],
"-topic",
[dock_name, "_station_description"],
"-x",
str(pose[0]),
"-y",
str(pose[1] - 2.0), # -2.0 is the offset between world and map
rafal-gorecki marked this conversation as resolved.
Show resolved Hide resolved
"-z",
"0.5", # station z is not in 0.0
"-R",
"1.57",
"-P",
"0.0",
"-Y",
str(pose[2] - 1.57),
],
emulate_tty=True,
condition=IfCondition(use_docking),
)

actions.append(spawn_station)

return actions


def generate_launch_description():
declare_device_namespace = DeclareLaunchArgument(
"device_namespace",
default_value="",
description="Device namespace that will appear before all non absolute topics and TF frames, used for distinguishing multiple cameras on the same robot.",
)

declare_docking_server_config_path_arg = DeclareLaunchArgument(
"docking_server_config_path",
default_value=PathJoinSubstitution(
[
FindPackageShare("husarion_ugv_docking"),
"config",
"docking_server.yaml",
]
),
description=("Path to docking server configuration file."),
)

declare_use_docking_arg = DeclareLaunchArgument(
"use_docking",
default_value="True",
description="Enable docking server and spawn docking stations in a simulation.",
choices=["True", "False", "true", "false"],
)

return LaunchDescription(
[
declare_docking_server_config_path_arg,
declare_device_namespace,
declare_use_docking_arg,
OpaqueFunction(function=spawn_stations),
]
)
43 changes: 43 additions & 0 deletions husarion_ugv_manager/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ set(PACKAGE_DEPENDENCIES
behaviortree_ros2
generate_parameter_library
libssh
geometry_msgs
opennav_docking_msgs
panther_msgs
husarion_ugv_utils
rclcpp
rclcpp_action
sensor_msgs
std_msgs
std_srvs
tf2_geometry_msgs
yaml-cpp)

foreach(PACKAGE IN ITEMS ${PACKAGE_DEPENDENCIES})
Expand Down Expand Up @@ -52,6 +56,21 @@ add_library(shutdown_hosts_from_file_bt_node SHARED
target_link_libraries(shutdown_hosts_from_file_bt_node ssh yaml-cpp)
list(APPEND plugin_libs shutdown_hosts_from_file_bt_node)

add_library(dock_robot_bt_node SHARED src/plugins/action/dock_robot_node.cpp)
list(APPEND plugin_libs dock_robot_bt_node)

add_library(undock_robot_bt_node SHARED
src/plugins/action/undock_robot_node.cpp)
list(APPEND plugin_libs undock_robot_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)
list(APPEND plugin_libs tick_after_timeout_bt_node)
Expand Down Expand Up @@ -137,6 +156,30 @@ if(BUILD_TESTING)
src/plugins/action/shutdown_hosts_from_file_node.cpp)
list(APPEND plugin_tests ${PROJECT_NAME}_test_shutdown_hosts_from_file_node)

ament_add_gtest(
${PROJECT_NAME}_test_dock_robot_node
test/plugins/action/test_dock_robot_node.cpp
src/plugins/action/dock_robot_node.cpp)
list(APPEND plugin_tests ${PROJECT_NAME}_test_dock_robot_node)

ament_add_gtest(
${PROJECT_NAME}_test_undock_robot_node
test/plugins/action/test_undock_robot_node.cpp
src/plugins/action/undock_robot_node.cpp)
list(APPEND plugin_tests ${PROJECT_NAME}_test_undock_robot_node)

ament_add_gtest(
${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)
rafal-gorecki marked this conversation as resolved.
Show resolved Hide resolved

ament_add_gtest(
${PROJECT_NAME}_test_tick_after_timeout_node
test/plugins/decorator/test_tick_after_timeout_node.cpp
Expand Down
1 change: 1 addition & 0 deletions husarion_ugv_manager/config/lights_manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
lights_manager:
ros__parameters:
timer_frequency: 10.0
bt_server_port: 5555
battery:
percent:
window_len: 6
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
#include "behaviortree_cpp/utils/shared_library.h"
#include "behaviortree_ros2/plugins.hpp"

#include <geometry_msgs/msg/pose_stamped.hpp>
#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>

namespace husarion_ugv_manager::behavior_tree_utils
{

Expand Down Expand Up @@ -90,4 +93,78 @@ inline std::string GetLoggerPrefix(const std::string & bt_node_name)
}
} // namespace husarion_ugv_manager

namespace BT
{
/**
* @brief Converts a string to a vector of float.
*
* @param str The string to convert.
* @return std::vector<float> The vector of float.
*
* @throw BT::RuntimeError Throws when there is no input or cannot parse float.
*/
template <>
inline std::vector<float> convertFromString<std::vector<float>>(StringView str)
{
auto parts = splitString(str, ';');
std::vector<float> output;
output.reserve(parts.size());
for (const StringView & part : parts) {
output.push_back(convertFromString<float>(part));
}
return output;
}

/**
* @brief Converts a string to a PoseStamped message.
*
* The string format should be "x;y;z;roll;pitch;yaw;frame_id" where:
* - x, y, z: Position coordinates.
* - roll, pitch, yaw: Rotation around axes XYZ.
* - 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<geometry_msgs::msg::PoseStamped>(
StringView str)
{
constexpr std::size_t expected_parts_size = 7;

auto parts = splitString(str, ';');
if (parts.size() != expected_parts_size) {
throw BT::RuntimeError(
"Invalid input for PoseStamped. Expected " + std::to_string(expected_parts_size) +
" values: x;y;z;roll;pitch;yaw;frame_id");
}

geometry_msgs::msg::PoseStamped pose_stamped;

try {
pose_stamped.pose.position.x = convertFromString<double>(parts[0]);
pose_stamped.pose.position.y = convertFromString<double>(parts[1]);
pose_stamped.pose.position.z = convertFromString<double>(parts[2]);

const auto roll = convertFromString<double>(parts[3]);
const auto pitch = convertFromString<double>(parts[4]);
const auto yaw = convertFromString<double>(parts[5]);
tf2::Quaternion quaternion;
quaternion.setRPY(roll, pitch, yaw);
pose_stamped.pose.orientation = tf2::toMsg(quaternion);

pose_stamped.header.frame_id = convertFromString<std::string>(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 // HUSARION_UGV_MANAGER_BEHAVIOR_TREE_UTILS_HPP_
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ class CallSetBoolService : public BT::RosServiceNode<std_srvs::srv::SetBool>

static BT::PortsList providedPorts()
{
return providedBasicPorts({BT::InputPort<bool>("data", "true / false value")});
return providedBasicPorts(
{BT::InputPort<bool>("data", "Boolean value to send with the service request.")});
}

virtual bool setRequest(typename Request::SharedPtr & request) override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ class CallSetLedAnimationService : public BT::RosServiceNode<panther_msgs::srv::

static BT::PortsList providedPorts()
{
return providedBasicPorts({
BT::InputPort<unsigned>("id", "animation ID"),
BT::InputPort<std::string>("param", "optional parameter"),
BT::InputPort<bool>("repeating", "indicates if animation should repeat"),
});
return providedBasicPorts(
{BT::InputPort<unsigned>("id", "Animation ID to trigger."),
delihus marked this conversation as resolved.
Show resolved Hide resolved
BT::InputPort<std::string>("param", "Optional animation parameter."),
BT::InputPort<bool>(
"repeating", "Specifies whether the animation should repeated continuously.")});
}

virtual bool setRequest(typename Request::SharedPtr & request) override;
Expand Down
Loading
Loading