diff --git a/.github/workflows/ros2-ci.yml b/.github/workflows/ros2-ci.yml index fb806edbf..1977346db 100644 --- a/.github/workflows/ros2-ci.yml +++ b/.github/workflows/ros2-ci.yml @@ -19,15 +19,6 @@ jobs: - docker-image: "ubuntu:22.04" gz-version: "harmonic" ros-distro: "humble" - - docker-image: "ubuntu:22.04" - gz-version: "fortress" - ros-distro: "rolling" - - docker-image: "ubuntu:22.04" - gz-version: "garden" - ros-distro: "rolling" - - docker-image: "ubuntu:22.04" - gz-version: "harmonic" - ros-distro: "rolling" container: image: ${{ matrix.docker-image }} steps: diff --git a/README.md b/README.md index 53755a5d4..8e1d74d0a 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Galactic | Edifice | [galactic](https://github.com/gazebosim/ros_gz/tree/galacti Galactic | Fortress | [galactic](https://github.com/gazebosim/ros_gz/tree/galactic) | only from source Humble | Fortress | [humble](https://github.com/gazebosim/ros_gz/tree/humble) | https://packages.ros.org Humble | Garden | [humble](https://github.com/gazebosim/ros_gz/tree/humble) | [gazebo packages](https://gazebosim.org/docs/latest/ros_installation#gazebo-garden-with-ros-2-humble-iron-or-rolling-use-with-caution-)[^1] -Humble | Harmonic | [humble](https://github.com/gazebosim/ros_gz/tree/humble) | only from source +Humble | Harmonic | [humble](https://github.com/gazebosim/ros_gz/tree/humble) | [gazebo packages](https://gazebosim.org/docs/harmonic/ros_installation#-gazebo-harmonic-with-ros-2-humble-iron-or-rolling-use-with-caution-)[^1] Rolling | Edifice | [ros2](https://github.com/gazebosim/ros_gz/tree/ros2) | only from source Rolling | Fortress | [ros2](https://github.com/gazebosim/ros_gz/tree/ros2) | https://packages.ros.org Rolling | Garden | [ros2](https://github.com/gazebosim/ros_gz/tree/ros2) | only from source diff --git a/ros_gz/CHANGELOG.rst b/ros_gz/CHANGELOG.rst index b305581d3..fb245cca4 100644 --- a/ros_gz/CHANGELOG.rst +++ b/ros_gz/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package ros_gz ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +0.244.14 (2024-04-08) +--------------------- + 0.244.13 (2024-01-23) --------------------- diff --git a/ros_gz/package.xml b/ros_gz/package.xml index 1d2a50afc..91c75be01 100644 --- a/ros_gz/package.xml +++ b/ros_gz/package.xml @@ -4,7 +4,7 @@ ros_gz - 0.244.13 + 0.244.14 Meta-package containing interfaces for using ROS 2 with Gazebo simulation. Louise Poubel Apache 2.0 diff --git a/ros_gz_bridge/CHANGELOG.rst b/ros_gz_bridge/CHANGELOG.rst index afd5bb04a..412842e43 100644 --- a/ros_gz_bridge/CHANGELOG.rst +++ b/ros_gz_bridge/CHANGELOG.rst @@ -2,6 +2,25 @@ Changelog for package ros_gz_bridge ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +0.244.14 (2024-04-08) +--------------------- +* Added conversion for Detection3D and Detection3DArray (`#523 `_) (`#526 `_) + Co-authored-by: wittenator <9154515+wittenator@users.noreply.github.com> +* Add ROS namespaces to GZ topics (`#512 `_) + Co-authored-by: Alejandro Hernández Cordero +* Correctly export ros_gz_bridge for downstream targets (`#503 `_) (`#506 `_) +* Add a virtual destructor to suppress compiler warning (`#502 `_) (`#505 `_) + Co-authored-by: Michael Carroll +* Add option to change material color from ROS. (`#486 `_) + * Message and bridge for MaterialColor. + This allows bridging MaterialColor from ROS to GZ and is + important for allowing simulation users to create status lights. + --------- + Co-authored-by: Alejandro Hernández Cordero + Co-authored-by: Addisu Z. Taddese + Co-authored-by: Addisu Z. Taddese +* Contributors: Alejandro Hernández Cordero, Benjamin Perseghetti, Krzysztof Wojciechowski, Michael Carroll + 0.244.13 (2024-01-23) --------------------- * backport pr 374 (`#489 `_) diff --git a/ros_gz_bridge/CMakeLists.txt b/ros_gz_bridge/CMakeLists.txt index 4d0e4057f..7df2399bd 100644 --- a/ros_gz_bridge/CMakeLists.txt +++ b/ros_gz_bridge/CMakeLists.txt @@ -119,7 +119,7 @@ add_custom_command( COMMENT "Generating factories for interface types") set(bridge_lib - ros_gz_bridge_lib + ros_gz_bridge ) add_library(${bridge_lib} @@ -129,6 +129,7 @@ add_library(${bridge_lib} src/bridge_handle_ros_to_gz.cpp src/bridge_handle_gz_to_ros.cpp src/factory_interface.cpp + src/service_factory_interface.cpp src/service_factories/ros_gz_interfaces.cpp src/ros_gz_bridge.cpp ${convert_files} @@ -148,8 +149,12 @@ ament_target_dependencies(${bridge_lib} ) target_include_directories(${bridge_lib} - PUBLIC include - PRIVATE src ${generated_path} + PUBLIC + "$" + "$" + PRIVATE + "$" + "$" ) target_link_libraries(${bridge_lib} @@ -162,19 +167,16 @@ rclcpp_components_register_node( PLUGIN ros_gz_bridge::RosGzBridge EXECUTABLE bridge_node) -install(TARGETS ${bridge_lib} +install(TARGETS ${bridge_lib} EXPORT export_${PROJECT_NAME} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin) install( DIRECTORY include/ - DESTINATION include + DESTINATION include/${PROJECT_NAME} ) -ament_export_include_directories(include) -ament_export_libraries(${bridge_lib}) - set(bridge_executables parameter_bridge static_bridge @@ -193,7 +195,7 @@ foreach(bridge ${bridge_executables}) ${BRIDGE_MESSAGE_TYPES} ) install(TARGETS ${bridge} - DESTINATION lib/${PROJECT_NAME} + RUNTIME DESTINATION lib/${PROJECT_NAME} ) endforeach() @@ -348,9 +350,19 @@ if(BUILD_TESTING) endif() -ament_export_dependencies( - rclcpp - ${BRIDGE_MESSAGE_TYPES} -) +# Export old-style CMake variables +ament_export_include_directories("include/${PROJECT_NAME}") +ament_export_libraries(${bridge_lib}) + +# Export modern CMake targets +ament_export_targets(export_${PROJECT_NAME}) + +# specific order: dependents before dependencies +ament_export_dependencies(rclcpp) +ament_export_dependencies(rclcpp_components) +ament_export_dependencies(${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}) +ament_export_dependencies(${GZ_TARGET_PREFIX}-transport${GZ_TRANSPORT_VER}) +ament_export_dependencies(yaml_cpp_vendor) +ament_export_dependencies(${BRIDGE_MESSAGE_TYPES}) ament_package() diff --git a/ros_gz_bridge/README.md b/ros_gz_bridge/README.md index aff3b3797..242d92b2d 100644 --- a/ros_gz_bridge/README.md +++ b/ros_gz_bridge/README.md @@ -5,64 +5,66 @@ between ROS and Gazebo Transport. The following message types can be bridged for topics: -| ROS type | Gazebo type | -|---------------------------------------------|:--------------------------------------:| -| builtin_interfaces/msg/Time | ignition::msgs::Time | -| std_msgs/msg/Bool | ignition::msgs::Boolean | -| std_msgs/msg/ColorRGBA | ignition::msgs::Color | -| std_msgs/msg/Empty | ignition::msgs::Empty | -| std_msgs/msg/Float32 | ignition::msgs::Float | -| std_msgs/msg/Float64 | ignition::msgs::Double | -| std_msgs/msg/Header | ignition::msgs::Header | -| std_msgs/msg/Int32 | ignition::msgs::Int32 | -| std_msgs/msg/UInt32 | ignition::msgs::UInt32 | -| std_msgs/msg/String | ignition::msgs::StringMsg | -| geometry_msgs/msg/Wrench | ignition::msgs::Wrench | -| geometry_msgs/msg/WrenchStamped | ignition::msgs::Wrench | -| geometry_msgs/msg/Quaternion | ignition::msgs::Quaternion | -| geometry_msgs/msg/Vector3 | ignition::msgs::Vector3d | -| geometry_msgs/msg/Point | ignition::msgs::Vector3d | -| geometry_msgs/msg/Pose | ignition::msgs::Pose | -| geometry_msgs/msg/PoseArray | ignition::msgs::Pose_V | -| geometry_msgs/msg/PoseWithCovariance | ignition::msgs::PoseWithCovariance | -| geometry_msgs/msg/PoseStamped | ignition::msgs::Pose | -| geometry_msgs/msg/Transform | ignition::msgs::Pose | -| geometry_msgs/msg/TransformStamped | ignition::msgs::Pose | -| geometry_msgs/msg/Twist | ignition::msgs::Twist | -| geometry_msgs/msg/TwistStamped | ignition::msgs::Twist | -| geometry_msgs/msg/TwistWithCovariance | ignition::msgs::TwistWithCovariance | -| geometry_msgs/msg/TwistWithCovarianceStamped| ignition::msgs::TwistWithCovariance | -| nav_msgs/msg/Odometry | ignition::msgs::Odometry | -| nav_msgs/msg/Odometry | ignition::msgs::OdometryWithCovariance | -| rcl_interfaces/msg/ParameterValue | ignition::msgs::Any | -| ros_gz_interfaces/msg/Altimeter | ignition::msgs::Altimeter | -| ros_gz_interfaces/msg/Contact | ignition::msgs::Contact | -| ros_gz_interfaces/msg/Contacts | ignition::msgs::Contacts | -| ros_gz_interfaces/msg/Dataframe | ignition::msgs::Dataframe | -| ros_gz_interfaces/msg/Entity | ignition::msgs::Entity | -| ros_gz_interfaces/msg/Float32Array | ignition::msgs::Float_V | -| ros_gz_interfaces/msg/GuiCamera | ignition::msgs::GUICamera | -| ros_gz_interfaces/msg/JointWrench | ignition::msgs::JointWrench | -| ros_gz_interfaces/msg/Light | ignition::msgs::Light | -| ros_gz_interfaces/msg/SensorNoise | ignition::msgs::SensorNoise | -| ros_gz_interfaces/msg/StringVec | ignition::msgs::StringMsg_V | -| ros_gz_interfaces/msg/TrackVisual | ignition::msgs::TrackVisual | -| ros_gz_interfaces/msg/VideoRecord | ignition::msgs::VideoRecord | -| ros_gz_interfaces/msg/WorldControl | ignition::msgs::WorldControl | -| rosgraph_msgs/msg/Clock | ignition::msgs::Clock | -| sensor_msgs/msg/BatteryState | ignition::msgs::BatteryState | -| sensor_msgs/msg/CameraInfo | ignition::msgs::CameraInfo | -| sensor_msgs/msg/FluidPressure | ignition::msgs::FluidPressure | -| sensor_msgs/msg/Imu | ignition::msgs::IMU | -| sensor_msgs/msg/Image | ignition::msgs::Image | -| sensor_msgs/msg/JointState | ignition::msgs::Model | -| sensor_msgs/msg/Joy | ignition::msgs::Joy | -| sensor_msgs/msg/LaserScan | ignition::msgs::LaserScan | -| sensor_msgs/msg/MagneticField | ignition::msgs::Magnetometer | -| sensor_msgs/msg/NavSatFix | ignition::msgs::NavSat | -| sensor_msgs/msg/PointCloud2 | ignition::msgs::PointCloudPacked | -| tf2_msgs/msg/TFMessage | ignition::msgs::Pose_V | -| trajectory_msgs/msg/JointTrajectory | ignition::msgs::JointTrajectory | +| ROS type | Gazebo type | +|---------------------------------------------|:-------------------------------------------:| +| builtin_interfaces/msg/Time | ignition::msgs::Time | +| std_msgs/msg/Bool | ignition::msgs::Boolean | +| std_msgs/msg/ColorRGBA | ignition::msgs::Color | +| std_msgs/msg/Empty | ignition::msgs::Empty | +| std_msgs/msg/Float32 | ignition::msgs::Float | +| std_msgs/msg/Float64 | ignition::msgs::Double | +| std_msgs/msg/Header | ignition::msgs::Header | +| std_msgs/msg/Int32 | ignition::msgs::Int32 | +| std_msgs/msg/UInt32 | ignition::msgs::UInt32 | +| std_msgs/msg/String | ignition::msgs::StringMsg | +| geometry_msgs/msg/Wrench | ignition::msgs::Wrench | +| geometry_msgs/msg/WrenchStamped | ignition::msgs::Wrench | +| geometry_msgs/msg/Quaternion | ignition::msgs::Quaternion | +| geometry_msgs/msg/Vector3 | ignition::msgs::Vector3d | +| geometry_msgs/msg/Point | ignition::msgs::Vector3d | +| geometry_msgs/msg/Pose | ignition::msgs::Pose | +| geometry_msgs/msg/PoseArray | ignition::msgs::Pose_V | +| geometry_msgs/msg/PoseWithCovariance | ignition::msgs::PoseWithCovariance | +| geometry_msgs/msg/PoseStamped | ignition::msgs::Pose | +| geometry_msgs/msg/Transform | ignition::msgs::Pose | +| geometry_msgs/msg/TransformStamped | ignition::msgs::Pose | +| geometry_msgs/msg/Twist | ignition::msgs::Twist | +| geometry_msgs/msg/TwistStamped | ignition::msgs::Twist | +| geometry_msgs/msg/TwistWithCovariance | ignition::msgs::TwistWithCovariance | +| geometry_msgs/msg/TwistWithCovarianceStamped| ignition::msgs::TwistWithCovariance | +| nav_msgs/msg/Odometry | ignition::msgs::Odometry | +| nav_msgs/msg/Odometry | ignition::msgs::OdometryWithCovariance | +| rcl_interfaces/msg/ParameterValue | ignition::msgs::Any | +| ros_gz_interfaces/msg/Altimeter | ignition::msgs::Altimeter | +| ros_gz_interfaces/msg/Contact | ignition::msgs::Contact | +| ros_gz_interfaces/msg/Contacts | ignition::msgs::Contacts | +| ros_gz_interfaces/msg/Dataframe | ignition::msgs::Dataframe | +| ros_gz_interfaces/msg/Entity | ignition::msgs::Entity | +| ros_gz_interfaces/msg/Float32Array | ignition::msgs::Float_V | +| ros_gz_interfaces/msg/GuiCamera | ignition::msgs::GUICamera | +| ros_gz_interfaces/msg/JointWrench | ignition::msgs::JointWrench | +| ros_gz_interfaces/msg/Light | ignition::msgs::Light | +| ros_gz_interfaces/msg/SensorNoise | ignition::msgs::SensorNoise | +| ros_gz_interfaces/msg/StringVec | ignition::msgs::StringMsg_V | +| ros_gz_interfaces/msg/TrackVisual | ignition::msgs::TrackVisual | +| ros_gz_interfaces/msg/VideoRecord | ignition::msgs::VideoRecord | +| ros_gz_interfaces/msg/WorldControl | ignition::msgs::WorldControl | +| rosgraph_msgs/msg/Clock | ignition::msgs::Clock | +| sensor_msgs/msg/BatteryState | ignition::msgs::BatteryState | +| sensor_msgs/msg/CameraInfo | ignition::msgs::CameraInfo | +| sensor_msgs/msg/FluidPressure | ignition::msgs::FluidPressure | +| sensor_msgs/msg/Imu | ignition::msgs::IMU | +| sensor_msgs/msg/Image | ignition::msgs::Image | +| sensor_msgs/msg/JointState | ignition::msgs::Model | +| sensor_msgs/msg/Joy | ignition::msgs::Joy | +| sensor_msgs/msg/LaserScan | ignition::msgs::LaserScan | +| sensor_msgs/msg/MagneticField | ignition::msgs::Magnetometer | +| sensor_msgs/msg/NavSatFix | ignition::msgs::NavSat | +| sensor_msgs/msg/PointCloud2 | ignition::msgs::PointCloudPacked | +| tf2_msgs/msg/TFMessage | ignition::msgs::Pose_V | +| trajectory_msgs/msg/JointTrajectory | ignition::msgs::JointTrajectory | +| vision_msgs/msg/Detection3D | ignition::msgs::AnnotatedOriented3DBox | +| vision_msgs/msg/Detection3DArray | ignition::msgs::AnnotatedOriented3DBox_V | And the following for services: @@ -86,7 +88,7 @@ Now we start the ROS listener. ``` # Shell B: -. /opt/ros/galactic/setup.bash +. /opt/ros/humble/setup.bash ros2 topic echo /chatter ``` @@ -118,7 +120,7 @@ Now we start the ROS talker. ``` # Shell C: -. /opt/ros/galactic/setup.bash +. /opt/ros/humble/setup.bash ros2 topic pub /chatter std_msgs/msg/String "data: 'Hi'" --once ``` @@ -156,7 +158,7 @@ Now we start the ROS GUI: ``` # Shell C: -. /opt/ros/galactic/setup.bash +. /opt/ros/humble/setup.bash ros2 run rqt_image_view rqt_image_view /rgbd_camera/image ``` @@ -274,9 +276,43 @@ To run the bridge node with the above configuration: ros2 run ros_gz_bridge parameter_bridge --ros-args -p config_file:=$WORKSPACE/ros_gz/ros_gz_bridge/test/config/full.yaml ``` +## Example 6: Using ROS namespace with the Bridge + +When spawning multiple robots inside the same ROS environment, it is convenient to use namespaces to avoid overlapping topic names. +There are three main types of namespaces: relative, global (`/`) and private (`~/`). For more information, refer to ROS documentation. +Namespaces are applied to Gazebo topic both when specified as `topic_name` as well as `gz_topic_name`. + +By default, the Bridge will not apply ROS namespace on the Gazebo topics. To enable this feature, use parameter `expand_gz_topic_names`. +Let's test our topic with namespace: + +```bash +# Shell A: +. ~/bridge_ws/install/setup.bash +ros2 run ros_gz_bridge parameter_bridge chatter@std_msgs/msg/String@ignition.msgs.StringMsg \ + --ros-args -p expand_gz_topic_names:=true -r __ns:=/demo +``` + +Now we start the Gazebo Transport listener. + +```bash +# Shell B: +ign topic -e -t /demo/chatter +``` + +Now we start the ROS talker. + +```bash +# Shell C: +. /opt/ros/humble/setup.bash +ros2 topic pub /demo/chatter std_msgs/msg/String "data: 'Hi from inside of a namespace'" --once +``` + +By changing `chatter` to `/chatter` or `~/chatter` you can obtain different results. + ## API ROS 2 Parameters: * `subscription_heartbeat` - Period at which the node checks for new subscribers for lazy bridges. * `config_file` - YAML file to be loaded as the bridge configuration + * `expand_gz_topic_names` - Enable or disable ROS namespace applied on GZ topics. diff --git a/ros_gz_bridge/include/ros_gz_bridge/convert/ros_gz_interfaces.hpp b/ros_gz_bridge/include/ros_gz_bridge/convert/ros_gz_interfaces.hpp index c7e671d48..136c8d9b6 100644 --- a/ros_gz_bridge/include/ros_gz_bridge/convert/ros_gz_interfaces.hpp +++ b/ros_gz_bridge/include/ros_gz_bridge/convert/ros_gz_interfaces.hpp @@ -56,6 +56,11 @@ #include #endif // HAVE_DATAFRAME +#if HAVE_MATERIALCOLOR +#include +#include +#endif // HAVE_MATERIALCOLOR + #include namespace ros_gz_bridge @@ -159,6 +164,20 @@ convert_gz_to_ros( const gz::msgs::Light & gz_msg, ros_gz_interfaces::msg::Light & ros_msg); +#if HAVE_MATERIALCOLOR +template<> +void +convert_ros_to_gz( + const ros_gz_interfaces::msg::MaterialColor & ros_msg, + gz::msgs::MaterialColor & gz_msg); + +template<> +void +convert_gz_to_ros( + const gz::msgs::MaterialColor & gz_msg, + ros_gz_interfaces::msg::MaterialColor & ros_msg); +#endif // HAVE_MATERIALCOLOR + template<> void convert_ros_to_gz( diff --git a/ros_gz_bridge/include/ros_gz_bridge/convert/vision_msgs.hpp b/ros_gz_bridge/include/ros_gz_bridge/convert/vision_msgs.hpp index f370144fe..b1556f651 100644 --- a/ros_gz_bridge/include/ros_gz_bridge/convert/vision_msgs.hpp +++ b/ros_gz_bridge/include/ros_gz_bridge/convert/vision_msgs.hpp @@ -17,9 +17,11 @@ // Gazebo Msgs #include +#include // ROS 2 messages #include "vision_msgs/msg/detection2_d_array.hpp" +#include "vision_msgs/msg/detection3_d_array.hpp" #include namespace ros_gz_bridge @@ -47,6 +49,30 @@ void convert_gz_to_ros( const gz::msgs::AnnotatedAxisAligned2DBox_V & gz_msg, vision_msgs::msg::Detection2DArray & ros_msg); + +template<> +void +convert_ros_to_gz( + const vision_msgs::msg::Detection3D & ros_msg, + gz::msgs::AnnotatedOriented3DBox & gz_msg); + +template<> +void +convert_gz_to_ros( + const gz::msgs::AnnotatedOriented3DBox & gz_msg, + vision_msgs::msg::Detection3D & ros_msg); + +template<> +void +convert_ros_to_gz( + const vision_msgs::msg::Detection3DArray & ros_msg, + gz::msgs::AnnotatedOriented3DBox_V & gz_msg); + +template<> +void +convert_gz_to_ros( + const gz::msgs::AnnotatedOriented3DBox_V & gz_msg, + vision_msgs::msg::Detection3DArray & ros_msg); } // namespace ros_gz_bridge #endif // ROS_GZ_BRIDGE__CONVERT__VISION_MSGS_HPP_ diff --git a/ros_gz_bridge/include/ros_gz_bridge/ros_gz_bridge.hpp b/ros_gz_bridge/include/ros_gz_bridge/ros_gz_bridge.hpp index 8d9ff631c..71abf1d7a 100644 --- a/ros_gz_bridge/include/ros_gz_bridge/ros_gz_bridge.hpp +++ b/ros_gz_bridge/include/ros_gz_bridge/ros_gz_bridge.hpp @@ -36,6 +36,13 @@ #define HAVE_DATAFRAME true #endif +// MaterialColor is available from versions 10.1.0 (Harmonic) forward +// This can be removed when the minimum supported version passes 10.1.0 +#if (GZ_MSGS_MAJOR_VERSION > 10) || \ + ((GZ_MSGS_MAJOR_VERSION == 10) && (GZ_MSGS_MINOR_VERSION >= 1)) +#define HAVE_MATERIALCOLOR true +#endif + namespace ros_gz_bridge { /// Forward declarations diff --git a/ros_gz_bridge/package.xml b/ros_gz_bridge/package.xml index 704b7fd4d..811955d22 100644 --- a/ros_gz_bridge/package.xml +++ b/ros_gz_bridge/package.xml @@ -2,7 +2,7 @@ ros_gz_bridge - 0.244.13 + 0.244.14 Bridge communication between ROS and Gazebo Transport Louise Poubel diff --git a/ros_gz_bridge/ros_gz_bridge/__init__.py b/ros_gz_bridge/ros_gz_bridge/__init__.py index b92918405..ef021faf8 100644 --- a/ros_gz_bridge/ros_gz_bridge/__init__.py +++ b/ros_gz_bridge/ros_gz_bridge/__init__.py @@ -16,7 +16,7 @@ import os -from ros_gz_bridge.mappings import MAPPINGS, MAPPINGS_8_4_0 +from ros_gz_bridge.mappings import MAPPINGS, MAPPINGS_10_1_0, MAPPINGS_8_4_0 from rosidl_cmake import expand_template @@ -75,6 +75,14 @@ def mappings(gz_msgs_ver): ros2_message_name=mapping.ros_type, gz_message_name=mapping.gz_type )) + if gz_msgs_ver >= (10, 1, 0): + for (ros2_package_name, mappings) in MAPPINGS_10_1_0.items(): + for mapping in sorted(mappings): + data.append(MessageMapping( + ros2_package_name=ros2_package_name, + ros2_message_name=mapping.ros_type, + gz_message_name=mapping.gz_type + )) return sorted(data, key=lambda mm: mm.ros2_string()) diff --git a/ros_gz_bridge/ros_gz_bridge/mappings.py b/ros_gz_bridge/ros_gz_bridge/mappings.py index 649fd6e34..ddec0f686 100644 --- a/ros_gz_bridge/ros_gz_bridge/mappings.py +++ b/ros_gz_bridge/ros_gz_bridge/mappings.py @@ -106,6 +106,8 @@ 'vision_msgs': [ Mapping('Detection2DArray', 'AnnotatedAxisAligned2DBox_V'), Mapping('Detection2D', 'AnnotatedAxisAligned2DBox'), + Mapping('Detection3DArray', 'AnnotatedOriented3DBox_V'), + Mapping('Detection3D', 'AnnotatedOriented3DBox'), ], } @@ -114,3 +116,9 @@ Mapping('Dataframe', 'Dataframe'), ], } + +MAPPINGS_10_1_0 = { + 'ros_gz_interfaces': [ + Mapping('MaterialColor', 'MaterialColor'), + ], +} diff --git a/ros_gz_bridge/src/convert/ros_gz_interfaces.cpp b/ros_gz_bridge/src/convert/ros_gz_interfaces.cpp index fceb296a8..0d6199551 100644 --- a/ros_gz_bridge/src/convert/ros_gz_interfaces.cpp +++ b/ros_gz_bridge/src/convert/ros_gz_interfaces.cpp @@ -380,6 +380,66 @@ convert_gz_to_ros( ros_msg.intensity = gz_msg.intensity(); } +#if HAVE_MATERIALCOLOR +template<> +void +convert_ros_to_gz( + const ros_gz_interfaces::msg::MaterialColor & ros_msg, + gz::msgs::MaterialColor & gz_msg) +{ + using EntityMatch = gz::msgs::MaterialColor::EntityMatch; + + switch (ros_msg.entity_match) { + case ros_gz_interfaces::msg::MaterialColor::FIRST: + gz_msg.set_entity_match(EntityMatch::MaterialColor_EntityMatch_FIRST); + break; + case ros_gz_interfaces::msg::MaterialColor::ALL: + gz_msg.set_entity_match(EntityMatch::MaterialColor_EntityMatch_ALL); + break; + default: + std::cerr << "Unsupported entity match type [" + << ros_msg.entity_match << "]\n"; + } + + convert_ros_to_gz(ros_msg.header, (*gz_msg.mutable_header())); + convert_ros_to_gz(ros_msg.entity, *gz_msg.mutable_entity()); + convert_ros_to_gz(ros_msg.ambient, *gz_msg.mutable_ambient()); + convert_ros_to_gz(ros_msg.diffuse, *gz_msg.mutable_diffuse()); + convert_ros_to_gz(ros_msg.specular, *gz_msg.mutable_specular()); + convert_ros_to_gz(ros_msg.emissive, *gz_msg.mutable_emissive()); + + gz_msg.set_shininess(ros_msg.shininess); +} + +template<> +void +convert_gz_to_ros( + const gz::msgs::MaterialColor & gz_msg, + ros_gz_interfaces::msg::MaterialColor & ros_msg) +{ + using EntityMatch = gz::msgs::MaterialColor::EntityMatch; + if (gz_msg.entity_match() == EntityMatch::MaterialColor_EntityMatch_FIRST) { + ros_msg.entity_match = ros_gz_interfaces::msg::MaterialColor::FIRST; +/* *INDENT-OFF* */ + } else if (gz_msg.entity_match() == + EntityMatch::MaterialColor_EntityMatch_ALL) { +/* *INDENT-ON* */ + ros_msg.entity_match = ros_gz_interfaces::msg::MaterialColor::ALL; + } else { + std::cerr << "Unsupported EntityMatch [" << + gz_msg.entity_match() << "]" << std::endl; + } + convert_gz_to_ros(gz_msg.header(), ros_msg.header); + convert_gz_to_ros(gz_msg.entity(), ros_msg.entity); + convert_gz_to_ros(gz_msg.ambient(), ros_msg.ambient); + convert_gz_to_ros(gz_msg.diffuse(), ros_msg.diffuse); + convert_gz_to_ros(gz_msg.specular(), ros_msg.specular); + convert_gz_to_ros(gz_msg.emissive(), ros_msg.emissive); + + ros_msg.shininess = gz_msg.shininess(); +} +#endif // HAVE_MATERIALCOLOR + template<> void convert_ros_to_gz( diff --git a/ros_gz_bridge/src/convert/vision_msgs.cpp b/ros_gz_bridge/src/convert/vision_msgs.cpp index 057426c77..61924af57 100644 --- a/ros_gz_bridge/src/convert/vision_msgs.cpp +++ b/ros_gz_bridge/src/convert/vision_msgs.cpp @@ -94,4 +94,89 @@ convert_gz_to_ros( } } +template<> +void +convert_ros_to_gz( + const vision_msgs::msg::Detection3D & ros_msg, + gz::msgs::AnnotatedOriented3DBox & gz_msg) +{ + convert_ros_to_gz(ros_msg.header, (*gz_msg.mutable_header())); + + gz::msgs::Oriented3DBox * box = new gz::msgs::Oriented3DBox(); + gz::msgs::Vector3d * center = new gz::msgs::Vector3d(); + gz::msgs::Vector3d * box_size = new gz::msgs::Vector3d(); + gz::msgs::Quaternion * orientation = new gz::msgs::Quaternion(); + + if (ros_msg.results.size() != 0) { + auto id = ros_msg.results.at(0).hypothesis.class_id; + gz_msg.set_label(std::stoi(id)); + } + + center->set_x(ros_msg.bbox.center.position.x); + center->set_y(ros_msg.bbox.center.position.y); + center->set_z(ros_msg.bbox.center.position.z); + box_size->set_x(ros_msg.bbox.size.x); + box_size->set_y(ros_msg.bbox.size.y); + box_size->set_z(ros_msg.bbox.size.z); + orientation->set_x(ros_msg.bbox.center.orientation.x); + orientation->set_y(ros_msg.bbox.center.orientation.y); + orientation->set_z(ros_msg.bbox.center.orientation.z); + orientation->set_w(ros_msg.bbox.center.orientation.w); + box->set_allocated_center(center); + box->set_allocated_boxsize(box_size); + box->set_allocated_orientation(orientation); + gz_msg.set_allocated_box(box); +} + +template<> +void +convert_gz_to_ros( + const gz::msgs::AnnotatedOriented3DBox & gz_msg, + vision_msgs::msg::Detection3D & ros_msg) +{ + convert_gz_to_ros(gz_msg.header(), ros_msg.header); + + ros_msg.results.resize(1); + ros_msg.results.at(0).hypothesis.class_id = std::to_string(gz_msg.label()); + ros_msg.results.at(0).hypothesis.score = 1.0; + + ros_msg.bbox.center.position.x = gz_msg.box().center().x(); + ros_msg.bbox.center.position.y = gz_msg.box().center().y(); + ros_msg.bbox.center.position.z = gz_msg.box().center().z(); + ros_msg.bbox.size.x = gz_msg.box().boxsize().x(); + ros_msg.bbox.size.y = gz_msg.box().boxsize().y(); + ros_msg.bbox.size.z = gz_msg.box().boxsize().z(); + ros_msg.bbox.center.orientation.x = gz_msg.box().orientation().x(); + ros_msg.bbox.center.orientation.y = gz_msg.box().orientation().y(); + ros_msg.bbox.center.orientation.z = gz_msg.box().orientation().z(); + ros_msg.bbox.center.orientation.w = gz_msg.box().orientation().w(); +} + +template<> +void +convert_ros_to_gz( + const vision_msgs::msg::Detection3DArray & ros_msg, + gz::msgs::AnnotatedOriented3DBox_V & gz_msg) +{ + convert_ros_to_gz(ros_msg.header, (*gz_msg.mutable_header())); + for (const auto & ros_box : ros_msg.detections) { + gz::msgs::AnnotatedOriented3DBox * gz_box = gz_msg.add_annotated_box(); + convert_ros_to_gz(ros_box, *gz_box); + } +} + +template<> +void +convert_gz_to_ros( + const gz::msgs::AnnotatedOriented3DBox_V & gz_msg, + vision_msgs::msg::Detection3DArray & ros_msg) +{ + convert_gz_to_ros(gz_msg.header(), ros_msg.header); + for (const auto & gz_box : gz_msg.annotated_box()) { + vision_msgs::msg::Detection3D ros_box; + convert_gz_to_ros(gz_box, ros_box); + ros_msg.detections.push_back(ros_box); + } +} + } // namespace ros_gz_bridge diff --git a/ros_gz_bridge/src/ros_gz_bridge.cpp b/ros_gz_bridge/src/ros_gz_bridge.cpp index f542ee90c..46a86d987 100644 --- a/ros_gz_bridge/src/ros_gz_bridge.cpp +++ b/ros_gz_bridge/src/ros_gz_bridge.cpp @@ -20,6 +20,8 @@ #include "bridge_handle_ros_to_gz.hpp" #include "bridge_handle_gz_to_ros.hpp" +#include + namespace ros_gz_bridge { @@ -30,6 +32,7 @@ RosGzBridge::RosGzBridge(const rclcpp::NodeOptions & options) this->declare_parameter("subscription_heartbeat", 1000); this->declare_parameter("config_file", ""); + this->declare_parameter("expand_gz_topic_names", false); int heartbeat; this->get_parameter("subscription_heartbeat", heartbeat); @@ -43,14 +46,21 @@ void RosGzBridge::spin() if (handles_.empty()) { std::string config_file; this->get_parameter("config_file", config_file); + bool expand_names; + this->get_parameter("expand_gz_topic_names", expand_names); + const std::string ros_ns = this->get_namespace(); + const std::string ros_node_name = this->get_name(); if (!config_file.empty()) { auto entries = readFromYamlFile(config_file); - for (const auto & entry : entries) { + for (auto & entry : entries) { + if (expand_names) { + entry.gz_topic_name = rclcpp::expand_topic_or_service_name( + entry.gz_topic_name, ros_node_name, ros_ns, false); + } this->add_bridge(entry); } } } - for (auto & bridge : handles_) { bridge->Spin(); } diff --git a/ros_gz_bridge/src/service_factory_interface.cpp b/ros_gz_bridge/src/service_factory_interface.cpp new file mode 100644 index 000000000..4789e0667 --- /dev/null +++ b/ros_gz_bridge/src/service_factory_interface.cpp @@ -0,0 +1,24 @@ +// Copyright 2024 Open Source Robotics Foundation, Inc. +// +// 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 "service_factory_interface.hpp" + +namespace ros_gz_bridge +{ + +ServiceFactoryInterface::~ServiceFactoryInterface() +{ +} + +} // namespace ros_gz_bridge diff --git a/ros_gz_bridge/src/service_factory_interface.hpp b/ros_gz_bridge/src/service_factory_interface.hpp index 9d4f73348..766ce5154 100644 --- a/ros_gz_bridge/src/service_factory_interface.hpp +++ b/ros_gz_bridge/src/service_factory_interface.hpp @@ -29,6 +29,8 @@ namespace ros_gz_bridge class ServiceFactoryInterface { public: + virtual ~ServiceFactoryInterface() = 0; + virtual rclcpp::ServiceBase::SharedPtr create_ros_service( diff --git a/ros_gz_bridge/test/utils/gz_test_msg.cpp b/ros_gz_bridge/test/utils/gz_test_msg.cpp index 0bfdea1db..71a651b3b 100644 --- a/ros_gz_bridge/test/utils/gz_test_msg.cpp +++ b/ros_gz_bridge/test/utils/gz_test_msg.cpp @@ -18,6 +18,7 @@ #include #include +#include #if GZ_MSGS_MAJOR_VERSION >= 10 #define GZ_MSGS_IMU_HAS_COVARIANCE @@ -1346,6 +1347,37 @@ void compareTestMsg(const std::shared_ptr & _msg) EXPECT_FLOAT_EQ(expected_msg.intensity(), _msg->intensity()); } +#if HAVE_MATERIALCOLOR +void createTestMsg(gz::msgs::MaterialColor & _msg) +{ + createTestMsg(*_msg.mutable_header()); + createTestMsg(*_msg.mutable_entity()); + createTestMsg(*_msg.mutable_ambient()); + createTestMsg(*_msg.mutable_diffuse()); + createTestMsg(*_msg.mutable_specular()); + createTestMsg(*_msg.mutable_emissive()); + + _msg.set_shininess(1.0); + _msg.set_entity_match(gz::msgs::MaterialColor::EntityMatch::MaterialColor_EntityMatch_ALL); +} + +void compareTestMsg(const std::shared_ptr & _msg) +{ + gz::msgs::MaterialColor expected_msg; + createTestMsg(expected_msg); + + compareTestMsg(std::make_shared(_msg->header())); + compareTestMsg(std::make_shared(_msg->entity())); + compareTestMsg(std::make_shared(_msg->ambient())); + compareTestMsg(std::make_shared(_msg->diffuse())); + compareTestMsg(std::make_shared(_msg->specular())); + compareTestMsg(std::make_shared(_msg->emissive())); + + EXPECT_EQ(expected_msg.shininess(), _msg->shininess()); + EXPECT_EQ(expected_msg.entity_match(), _msg->entity_match()); +} +#endif // HAVE_MATERIALCOLOR + void createTestMsg(gz::msgs::GUICamera & _msg) { gz::msgs::Header header_msg; @@ -1560,5 +1592,92 @@ void compareTestMsg(const std::shared_ptr } } +void createTestMsg(gz::msgs::AnnotatedOriented3DBox & _msg) +{ + gz::msgs::Header header_msg; + + createTestMsg(header_msg); + + _msg.mutable_header()->CopyFrom(header_msg); + + _msg.set_label(1); + + gz::msgs::Oriented3DBox * box = new gz::msgs::Oriented3DBox(); + gz::msgs::Vector3d * center = new gz::msgs::Vector3d(); + gz::msgs::Vector3d * size = new gz::msgs::Vector3d(); + gz::msgs::Quaternion * orientation = new gz::msgs::Quaternion(); + + center->set_x(2.0); + center->set_y(4.0); + center->set_z(6.0); + size->set_x(3.0); + size->set_y(5.0); + size->set_z(7.0); + orientation->set_x(0.733); + orientation->set_y(0.462); + orientation->set_z(0.191); + orientation->set_w(0.462); + + box->set_allocated_center(center); + box->set_allocated_boxsize(size); + box->set_allocated_orientation(orientation); + _msg.set_allocated_box(box); +} + +void compareTestMsg(const std::shared_ptr & _msg) +{ + gz::msgs::AnnotatedOriented3DBox expected_msg; + createTestMsg(expected_msg); + + compareTestMsg(std::make_shared(_msg->header())); + + EXPECT_EQ(expected_msg.label(), _msg->label()); + + gz::msgs::Oriented3DBox box = _msg->box(); + gz::msgs::Vector3d center = box.center(); + gz::msgs::Vector3d size = box.boxsize(); + gz::msgs::Quaternion orientation = box.orientation(); + + EXPECT_EQ(expected_msg.box().center().x(), center.x()); + EXPECT_EQ(expected_msg.box().center().y(), center.y()); + EXPECT_EQ(expected_msg.box().center().z(), center.z()); + EXPECT_EQ(expected_msg.box().boxsize().x(), size.x()); + EXPECT_EQ(expected_msg.box().boxsize().y(), size.y()); + EXPECT_EQ(expected_msg.box().boxsize().z(), size.z()); + EXPECT_EQ(expected_msg.box().orientation().w(), orientation.w()); + EXPECT_EQ(expected_msg.box().orientation().x(), orientation.x()); + EXPECT_EQ(expected_msg.box().orientation().y(), orientation.y()); + EXPECT_EQ(expected_msg.box().orientation().z(), orientation.z()); +} + +void createTestMsg(gz::msgs::AnnotatedOriented3DBox_V & _msg) +{ + gz::msgs::Header header_msg; + + createTestMsg(header_msg); + + _msg.mutable_header()->CopyFrom(header_msg); + + for (size_t i = 0; i < 4; i++) { + gz::msgs::AnnotatedOriented3DBox * box = _msg.add_annotated_box(); + createTestMsg(*box); + } +} + +void compareTestMsg(const std::shared_ptr & _msg) +{ + gz::msgs::AnnotatedOriented3DBox_V expected_msg; + createTestMsg(expected_msg); + + compareTestMsg(std::make_shared(_msg->header())); + + for (size_t i = 0; i < 4; i++) { + compareTestMsg( + std::make_shared( + _msg->annotated_box( + i))); + } +} + } // namespace testing } // namespace ros_gz_bridge diff --git a/ros_gz_bridge/test/utils/gz_test_msg.hpp b/ros_gz_bridge/test/utils/gz_test_msg.hpp index 7ec6984a0..ad063b628 100644 --- a/ros_gz_bridge/test/utils/gz_test_msg.hpp +++ b/ros_gz_bridge/test/utils/gz_test_msg.hpp @@ -65,6 +65,7 @@ #include #include #include +#include #include @@ -73,6 +74,10 @@ #include #endif // HAVE_DATAFRAME +#if HAVE_MATERIALCOLOR +#include +#endif // HAVE_MATERIALCOLOR + namespace ros_gz_bridge { namespace testing @@ -455,6 +460,16 @@ void createTestMsg(gz::msgs::Light & _msg); /// \param[in] _msg The message to compare. void compareTestMsg(const std::shared_ptr & _msg); +#if HAVE_MATERIALCOLOR +/// \brief Create a message used for testing. +/// \param[out] _msg The message populated. +void createTestMsg(gz::msgs::MaterialColor & _msg); + +/// \brief Compare a message with the populated for testing. +/// \param[in] _msg The message to compare. +void compareTestMsg(const std::shared_ptr & _msg); +#endif // HAVE_MATERIALCOLOR + /// \brief Create a message used for testing. /// \param[out] _msg The message populated. void createTestMsg(gz::msgs::GUICamera & _msg); @@ -511,6 +526,22 @@ void createTestMsg(gz::msgs::AnnotatedAxisAligned2DBox_V & _msg); /// \param[in] _msg The message to compare. void compareTestMsg(const std::shared_ptr & _msg); +/// \brief Create a message used for testing. +/// \param[out] _msg The message populated. +void createTestMsg(gz::msgs::AnnotatedOriented3DBox & _msg); + +/// \brief Compare a message with the populated for testing. +/// \param[in] _msg The message to compare. +void compareTestMsg(const std::shared_ptr & _msg); + +/// \brief Create a message used for testing. +/// \param[out] _msg The message populated. +void createTestMsg(gz::msgs::AnnotatedOriented3DBox_V & _msg); + +/// \brief Compare a message with the populated for testing. +/// \param[in] _msg The message to compare. +void compareTestMsg(const std::shared_ptr & _msg); + } // namespace testing } // namespace ros_gz_bridge diff --git a/ros_gz_bridge/test/utils/ros_test_msg.cpp b/ros_gz_bridge/test/utils/ros_test_msg.cpp index efca25eaa..c6f085e20 100644 --- a/ros_gz_bridge/test/utils/ros_test_msg.cpp +++ b/ros_gz_bridge/test/utils/ros_test_msg.cpp @@ -18,6 +18,7 @@ #include #include +#include #include "gz/msgs/config.hh" @@ -577,6 +578,36 @@ void compareTestMsg(const std::shared_ptr & _msg) EXPECT_FLOAT_EQ(expected_msg.intensity, _msg->intensity); } +#if HAVE_MATERIALCOLOR +void createTestMsg(ros_gz_interfaces::msg::MaterialColor & _msg) +{ + createTestMsg(_msg.header); + createTestMsg(_msg.entity); + createTestMsg(_msg.ambient); + createTestMsg(_msg.diffuse); + createTestMsg(_msg.specular); + createTestMsg(_msg.emissive); + _msg.shininess = 1.0; + _msg.entity_match = ros_gz_interfaces::msg::MaterialColor::ALL; +} + +void compareTestMsg(const std::shared_ptr & _msg) +{ + ros_gz_interfaces::msg::MaterialColor expected_msg; + createTestMsg(expected_msg); + + compareTestMsg(_msg->header); + compareTestMsg(std::make_shared(_msg->entity)); + compareTestMsg(std::make_shared(_msg->ambient)); + compareTestMsg(std::make_shared(_msg->diffuse)); + compareTestMsg(std::make_shared(_msg->specular)); + compareTestMsg(std::make_shared(_msg->emissive)); + + EXPECT_EQ(expected_msg.shininess, _msg->shininess); + EXPECT_EQ(expected_msg.entity_match, _msg->entity_match); +} +#endif // HAVE_MATERIALCOLOR + void createTestMsg(ros_gz_interfaces::msg::GuiCamera & _msg) { createTestMsg(_msg.header); @@ -1446,5 +1477,76 @@ void compareTestMsg(const std::shared_ptr & } } +void createTestMsg(vision_msgs::msg::Detection3D & _msg) +{ + std_msgs::msg::Header header_msg; + createTestMsg(header_msg); + _msg.header = header_msg; + + vision_msgs::msg::ObjectHypothesisWithPose class_prob; + class_prob.hypothesis.class_id = "1"; + class_prob.hypothesis.score = 1.0; + _msg.results.push_back(class_prob); + + _msg.bbox.size.x = 3.0; + _msg.bbox.size.y = 5.0; + _msg.bbox.size.z = 7.0; + _msg.bbox.center.position.x = 2.0; + _msg.bbox.center.position.y = 4.0; + _msg.bbox.center.position.z = 6.0; + _msg.bbox.center.orientation.x = 0.733; + _msg.bbox.center.orientation.y = 0.462; + _msg.bbox.center.orientation.z = 0.191; + _msg.bbox.center.orientation.w = 0.462; +} + +void compareTestMsg(const std::shared_ptr & _msg) +{ + vision_msgs::msg::Detection3D expected_msg; + createTestMsg(expected_msg); + + compareTestMsg(_msg->header); + EXPECT_EQ(expected_msg.results.size(), _msg->results.size()); + for (size_t i = 0; i < _msg->results.size(); i++) { + EXPECT_EQ(expected_msg.results[i].hypothesis.class_id, _msg->results[i].hypothesis.class_id); + EXPECT_EQ(expected_msg.results[i].hypothesis.score, _msg->results[i].hypothesis.score); + } + EXPECT_EQ(expected_msg.bbox.size.x, _msg->bbox.size.x); + EXPECT_EQ(expected_msg.bbox.size.y, _msg->bbox.size.y); + EXPECT_EQ(expected_msg.bbox.size.z, _msg->bbox.size.z); + EXPECT_EQ(expected_msg.bbox.center.position.x, _msg->bbox.center.position.x); + EXPECT_EQ(expected_msg.bbox.center.position.y, _msg->bbox.center.position.y); + EXPECT_EQ(expected_msg.bbox.center.position.z, _msg->bbox.center.position.z); + EXPECT_EQ(expected_msg.bbox.center.orientation.x, _msg->bbox.center.orientation.x); + EXPECT_EQ(expected_msg.bbox.center.orientation.y, _msg->bbox.center.orientation.y); + EXPECT_EQ(expected_msg.bbox.center.orientation.z, _msg->bbox.center.orientation.z); + EXPECT_EQ(expected_msg.bbox.center.orientation.w, _msg->bbox.center.orientation.w); +} + +void createTestMsg(vision_msgs::msg::Detection3DArray & _msg) +{ + std_msgs::msg::Header header_msg; + createTestMsg(header_msg); + _msg.header = header_msg; + + for (size_t i = 0; i < 4; i++) { + vision_msgs::msg::Detection3D detection; + createTestMsg(detection); + _msg.detections.push_back(detection); + } +} + +void compareTestMsg(const std::shared_ptr & _msg) +{ + vision_msgs::msg::Detection3DArray expected_msg; + createTestMsg(expected_msg); + + compareTestMsg(_msg->header); + EXPECT_EQ(expected_msg.detections.size(), _msg->detections.size()); + for (size_t i = 0; i < _msg->detections.size(); i++) { + compareTestMsg(std::make_shared(_msg->detections[i])); + } +} + } // namespace testing } // namespace ros_gz_bridge diff --git a/ros_gz_bridge/test/utils/ros_test_msg.hpp b/ros_gz_bridge/test/utils/ros_test_msg.hpp index 589f4de19..5a39c4ab0 100644 --- a/ros_gz_bridge/test/utils/ros_test_msg.hpp +++ b/ros_gz_bridge/test/utils/ros_test_msg.hpp @@ -57,6 +57,9 @@ #include #endif // HAVE_DATAFRAME #include +#if HAVE_MATERIALCOLOR +#include +#endif // HAVE_MATERIALCOLOR #include #include #include @@ -79,6 +82,7 @@ #include #include #include "vision_msgs/msg/detection2_d_array.hpp" +#include "vision_msgs/msg/detection3_d_array.hpp" namespace ros_gz_bridge { @@ -385,6 +389,16 @@ void createTestMsg(ros_gz_interfaces::msg::Light & _msg); /// \param[in] _msg The message to compare. void compareTestMsg(const std::shared_ptr & _msg); +#if HAVE_MATERIALCOLOR +/// \brief Create a message used for testing. +/// \param[out] _msg The message populated. +void createTestMsg(ros_gz_interfaces::msg::MaterialColor & _msg); + +/// \brief Compare a message with the populated for testing. +/// \param[in] _msg The message to compare. +void compareTestMsg(const std::shared_ptr & _msg); +#endif // HAVE_MATERIALCOLOR + /// \brief Create a message used for testing. /// \param[out] _msg The message populated. void createTestMsg(ros_gz_interfaces::msg::Entity & _msg); @@ -607,6 +621,22 @@ void createTestMsg(vision_msgs::msg::Detection2D & _msg); /// \param[in] _msg The message to compare. void compareTestMsg(const std::shared_ptr & _msg); +/// \brief Create a message used for testing. +/// \param[out] _msg The message populated. +void createTestMsg(vision_msgs::msg::Detection3DArray & _msg); + +/// \brief Compare a message with the populated for testing. +/// \param[in] _msg The message to compare. +void compareTestMsg(const std::shared_ptr & _msg); + +/// \brief Create a message used for testing. +/// \param[out] _msg The message populated. +void createTestMsg(vision_msgs::msg::Detection3D & _msg); + +/// \brief Compare a message with the populated for testing. +/// \param[in] _msg The message to compare. +void compareTestMsg(const std::shared_ptr & _msg); + } // namespace testing } // namespace ros_gz_bridge diff --git a/ros_gz_image/CHANGELOG.rst b/ros_gz_image/CHANGELOG.rst index 328917c9e..1e68461e8 100644 --- a/ros_gz_image/CHANGELOG.rst +++ b/ros_gz_image/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package ros1_ign_image ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +0.244.14 (2024-04-08) +--------------------- + 0.244.13 (2024-01-23) --------------------- diff --git a/ros_gz_image/package.xml b/ros_gz_image/package.xml index e524cafdb..66e761ea8 100644 --- a/ros_gz_image/package.xml +++ b/ros_gz_image/package.xml @@ -1,6 +1,6 @@ ros_gz_image - 0.244.13 + 0.244.14 Image utilities for Gazebo simulation with ROS. Apache 2.0 Louise Poubel diff --git a/ros_gz_interfaces/CHANGELOG.rst b/ros_gz_interfaces/CHANGELOG.rst index abe9c879d..dd1169aa9 100644 --- a/ros_gz_interfaces/CHANGELOG.rst +++ b/ros_gz_interfaces/CHANGELOG.rst @@ -2,6 +2,18 @@ Changelog for package ros_gz_interfaces ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +0.244.14 (2024-04-08) +--------------------- +* Add option to change material color from ROS. (`#486 `_) + * Message and bridge for MaterialColor. + This allows bridging MaterialColor from ROS to GZ and is + important for allowing simulation users to create status lights. + --------- + Co-authored-by: Alejandro Hernández Cordero + Co-authored-by: Addisu Z. Taddese + Co-authored-by: Addisu Z. Taddese +* Contributors: Benjamin Perseghetti + 0.244.13 (2024-01-23) --------------------- diff --git a/ros_gz_interfaces/CMakeLists.txt b/ros_gz_interfaces/CMakeLists.txt index 49c49126f..5f3b312e4 100644 --- a/ros_gz_interfaces/CMakeLists.txt +++ b/ros_gz_interfaces/CMakeLists.txt @@ -27,6 +27,7 @@ set(msg_files "msg/GuiCamera.msg" "msg/JointWrench.msg" "msg/Light.msg" + "msg/MaterialColor.msg" "msg/ParamVec.msg" "msg/SensorNoise.msg" "msg/StringVec.msg" diff --git a/ros_gz_interfaces/msg/MaterialColor.msg b/ros_gz_interfaces/msg/MaterialColor.msg new file mode 100644 index 000000000..cda67d27a --- /dev/null +++ b/ros_gz_interfaces/msg/MaterialColor.msg @@ -0,0 +1,12 @@ +# Entities that match to apply material color: constant definition +uint8 FIRST = 0 +uint8 ALL = 1 + +std_msgs/Header header # Optional header data +ros_gz_interfaces/Entity entity # Entity to change material color +std_msgs/ColorRGBA ambient # Ambient color +std_msgs/ColorRGBA diffuse # Diffuse color +std_msgs/ColorRGBA specular # Specular color +std_msgs/ColorRGBA emissive # Emissive color +float64 shininess # Specular exponent +uint8 entity_match # Entities that match to apply material color diff --git a/ros_gz_interfaces/package.xml b/ros_gz_interfaces/package.xml index c6cdb2a5b..1cfe9c27a 100644 --- a/ros_gz_interfaces/package.xml +++ b/ros_gz_interfaces/package.xml @@ -1,6 +1,6 @@ ros_gz_interfaces - 0.244.13 + 0.244.14 Message and service data structures for interacting with Gazebo from ROS2. Apache 2.0 Louise Poubel diff --git a/ros_gz_sim/CHANGELOG.rst b/ros_gz_sim/CHANGELOG.rst index 4a13427ef..3eb837b3e 100644 --- a/ros_gz_sim/CHANGELOG.rst +++ b/ros_gz_sim/CHANGELOG.rst @@ -2,6 +2,22 @@ Changelog for package ros_gz_sim ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +0.244.14 (2024-04-08) +--------------------- +* Support `` in `package.xml` exports (`#492 `_) + This copies the implementation from `gazebo_ros_paths.py` to provide a + way for packages to set resource paths from `package.xml`. + ``` + e.g. + + + + ``` + The value of `gazebo_model_path` and `gazebo_media_path` is appended to `GZ_SIM_RESOURCE_PATH` + The value of `plugin_path` appended to `GZ_SIM_SYSTEM_PLUGIN_PATH` + --------- +* Contributors: Addisu Z. Taddese + 0.244.13 (2024-01-23) --------------------- diff --git a/ros_gz_sim/README.md b/ros_gz_sim/README.md index c4561a5da..e0b05f624 100644 --- a/ros_gz_sim/README.md +++ b/ros_gz_sim/README.md @@ -37,3 +37,77 @@ See more options with: ``` ros2 run ros_gz_sim create --helpshort ``` + +### Using `` to export model paths in `package.xml` + +The `` tag inside the `` tag of a `package.xml` file can be +used to add paths to `GZ_SIM_RESOURCE_PATH` and `GZ_SIM_SYSTEM_PLUGIN_PATH`, +which are environment variables used to configure Gazebo search paths for +resources (e.g. SDFormat files, meshes, etc) and plugins respectively. + +The values in the attributes `gazebo_model_path` and `gazebo_media_path` are +appended to `GZ_SIM_RESOURCE_PATH`. The value of `plugin_path` is appended to +`GZ_SIM_SYSTEM_PLUGIN_PATH`. See the +[Finding resources](https://gazebosim.org/api/sim/8/resources.html) tutorial to +learn more about these environment variables. + +The keyword `${prefix}` can be used when setting these values and it will be +expanded to the package's share path (i.e., the value of +`ros2 pkg prefix --share `) + +```xml + + + + + + +``` + +Thus the required directory needs to be installed from `CMakeLists.txt` + +```cmake +install(DIRECTORY models + DESTINATION share/${PROJECT_NAME}) +``` + +In order to reference the models in a ROS package unambiguously, it is +recommended to set the value of `gazebo_model_path` to be the parent +of the `prefix`. + +```xml + + + + +``` + +Consider an example where we have a ROS package called `my_awesome_pkg` +and it contains an SDFormat model cool `cool_robot`: + +```bash +my_awesome_pkg +├── models +│   └── cool_robot +│   ├── model.config +│   └── model.sdf +└── package.xml +``` + +With `gazebo_model_path="${prefix}/../` set up, we can +reference the `cool_robot` model in a world file using the package name +in the `uri`: + +```xml + + + + package://my_awesome_pkg/models/cool_robot + + + +``` + +However, if we set `gazebo_model_path=${prefix}/models`, we would +need to reference `cool_robot` as `package://cool_robot`, which +might have a name conflict with other models in the system. diff --git a/ros_gz_sim/launch/gz_sim.launch.py.in b/ros_gz_sim/launch/gz_sim.launch.py.in index 7bed5243e..8f87fb7d1 100644 --- a/ros_gz_sim/launch/gz_sim.launch.py.in +++ b/ros_gz_sim/launch/gz_sim.launch.py.in @@ -14,20 +14,99 @@ """Launch Gazebo Sim with command line arguments.""" +import os from os import environ +from ament_index_python.packages import get_package_share_directory +from catkin_pkg.package import InvalidPackage, PACKAGE_MANIFEST_FILENAME, parse_package +from ros2pkg.api import get_package_names from launch import LaunchDescription from launch.actions import DeclareLaunchArgument, OpaqueFunction from launch.actions import ExecuteProcess, Shutdown from launch.substitutions import LaunchConfiguration +# Copied from https://github.com/ros-simulation/gazebo_ros_pkgs/blob/79fd94c6da76781a91499bc0f54b70560b90a9d2/gazebo_ros/scripts/gazebo_ros_paths.py +""" +Search for model, plugin and media paths exported by packages. + +e.g. + + + +${prefix} is replaced by package's share directory in install. + +Thus the required directory needs to be installed from CMakeLists.txt +e.g. install(DIRECTORY models + DESTINATION share/${PROJECT_NAME}) +""" + +class GazeboRosPaths: + + @staticmethod + def get_paths(): + gazebo_model_path = [] + gazebo_plugin_path = [] + gazebo_media_path = [] + + for package_name in get_package_names(): + package_share_path = get_package_share_directory(package_name) + package_file_path = os.path.join(package_share_path, PACKAGE_MANIFEST_FILENAME) + if os.path.isfile(package_file_path): + try: + package = parse_package(package_file_path) + except InvalidPackage: + continue + for export in package.exports: + if export.tagname == 'gazebo_ros': + if 'gazebo_model_path' in export.attributes: + xml_path = export.attributes['gazebo_model_path'] + xml_path = xml_path.replace('${prefix}', package_share_path) + gazebo_model_path.append(xml_path) + if 'plugin_path' in export.attributes: + xml_path = export.attributes['plugin_path'] + xml_path = xml_path.replace('${prefix}', package_share_path) + gazebo_plugin_path.append(xml_path) + if 'gazebo_media_path' in export.attributes: + xml_path = export.attributes['gazebo_media_path'] + xml_path = xml_path.replace('${prefix}', package_share_path) + gazebo_media_path.append(xml_path) + + gazebo_model_path = os.pathsep.join(gazebo_model_path + gazebo_media_path) + gazebo_plugin_path = os.pathsep.join(gazebo_plugin_path) + + return gazebo_model_path, gazebo_plugin_path + def launch_gz(context, *args, **kwargs): - env = {'GZ_SIM_SYSTEM_PLUGIN_PATH': - ':'.join([environ.get('GZ_SIM_SYSTEM_PLUGIN_PATH', default=''), - environ.get('LD_LIBRARY_PATH', default='')]), - 'IGN_GAZEBO_SYSTEM_PLUGIN_PATH': # TODO(CH3): To support pre-garden. Deprecated. - ':'.join([environ.get('IGN_GAZEBO_SYSTEM_PLUGIN_PATH', default=''), - environ.get('LD_LIBRARY_PATH', default='')])} + model_paths, plugin_paths = GazeboRosPaths.get_paths() + + env = { + "GZ_SIM_SYSTEM_PLUGIN_PATH": os.pathsep.join( + [ + environ.get("GZ_SIM_SYSTEM_PLUGIN_PATH", default=""), + environ.get("LD_LIBRARY_PATH", default=""), + plugin_paths, + ] + ), + "IGN_GAZEBO_SYSTEM_PLUGIN_PATH": os.pathsep.join( # TODO(azeey): To support pre-garden. Deprecated. + [ + environ.get("IGN_GAZEBO_SYSTEM_PLUGIN_PATH", default=""), + environ.get("LD_LIBRARY_PATH", default=""), + plugin_paths, + ] + ), + "GZ_SIM_RESOURCE_PATH": os.pathsep.join( + [ + environ.get("GZ_SIM_RESOURCE_PATH", default=""), + model_paths, + ] + ), + "IGN_GAZEBO_RESOURCE_PATH": os.pathsep.join( # TODO(azeey): To support pre-garden. Deprecated. + [ + environ.get("IGN_GAZEBO_RESOURCE_PATH", default=""), + model_paths, + ] + ), + } gz_args = LaunchConfiguration('gz_args').perform(context) gz_version = LaunchConfiguration('gz_version').perform(context) diff --git a/ros_gz_sim/package.xml b/ros_gz_sim/package.xml index 181c31997..2259a64bf 100644 --- a/ros_gz_sim/package.xml +++ b/ros_gz_sim/package.xml @@ -2,7 +2,7 @@ ros_gz_sim - 0.244.13 + 0.244.14 Tools for using Gazebo Sim simulation with ROS. Alejandro Hernandez diff --git a/ros_gz_sim_demos/CHANGELOG.rst b/ros_gz_sim_demos/CHANGELOG.rst index 377c19d58..47ef9ea99 100644 --- a/ros_gz_sim_demos/CHANGELOG.rst +++ b/ros_gz_sim_demos/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package ros1_gz_sim_demos ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +0.244.14 (2024-04-08) +--------------------- + 0.244.13 (2024-01-23) --------------------- diff --git a/ros_gz_sim_demos/package.xml b/ros_gz_sim_demos/package.xml index 15bf28dc1..d25ec95b4 100644 --- a/ros_gz_sim_demos/package.xml +++ b/ros_gz_sim_demos/package.xml @@ -1,6 +1,6 @@ ros_gz_sim_demos - 0.244.13 + 0.244.14 Demos using Gazebo Sim simulation with ROS. Apache 2.0 Louise Poubel diff --git a/ros_ign/CHANGELOG.rst b/ros_ign/CHANGELOG.rst index ce4c46463..d2bbe37c1 100644 --- a/ros_ign/CHANGELOG.rst +++ b/ros_ign/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package ros_ign ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +0.244.14 (2024-04-08) +--------------------- + 0.244.13 (2024-01-23) --------------------- diff --git a/ros_ign/package.xml b/ros_ign/package.xml index 2e2933896..48ad0ec13 100644 --- a/ros_ign/package.xml +++ b/ros_ign/package.xml @@ -2,7 +2,7 @@ ros_ign - 0.244.13 + 0.244.14 Shim meta-package to redirect to ros_gz. Brandon Ong Apache 2.0 diff --git a/ros_ign_bridge/CHANGELOG.rst b/ros_ign_bridge/CHANGELOG.rst index 17b74fd3b..d685f19fd 100644 --- a/ros_ign_bridge/CHANGELOG.rst +++ b/ros_ign_bridge/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package ros_ign_bridge ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +0.244.14 (2024-04-08) +--------------------- + 0.244.13 (2024-01-23) --------------------- diff --git a/ros_ign_bridge/package.xml b/ros_ign_bridge/package.xml index ccf61c4d6..49db5fd93 100644 --- a/ros_ign_bridge/package.xml +++ b/ros_ign_bridge/package.xml @@ -2,7 +2,7 @@ ros_ign_bridge - 0.244.13 + 0.244.14 Shim package to redirect to ros_gz_bridge. Brandon Ong diff --git a/ros_ign_gazebo/CHANGELOG.rst b/ros_ign_gazebo/CHANGELOG.rst index 2f10998c3..b01effba1 100644 --- a/ros_ign_gazebo/CHANGELOG.rst +++ b/ros_ign_gazebo/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package ros_ign_gazebo ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +0.244.14 (2024-04-08) +--------------------- + 0.244.13 (2024-01-23) --------------------- diff --git a/ros_ign_gazebo/package.xml b/ros_ign_gazebo/package.xml index 9568dec53..ff61e96a9 100644 --- a/ros_ign_gazebo/package.xml +++ b/ros_ign_gazebo/package.xml @@ -2,7 +2,7 @@ ros_ign_gazebo - 0.244.13 + 0.244.14 Shim package to redirect to ros_gz_sim. Brandon Ong diff --git a/ros_ign_gazebo_demos/CHANGELOG.rst b/ros_ign_gazebo_demos/CHANGELOG.rst index 7edbe3359..c2e740eb2 100644 --- a/ros_ign_gazebo_demos/CHANGELOG.rst +++ b/ros_ign_gazebo_demos/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package ros_ign_gazebo_demos ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +0.244.14 (2024-04-08) +--------------------- + 0.244.13 (2024-01-23) --------------------- diff --git a/ros_ign_gazebo_demos/package.xml b/ros_ign_gazebo_demos/package.xml index 77bb5ad71..70951d353 100644 --- a/ros_ign_gazebo_demos/package.xml +++ b/ros_ign_gazebo_demos/package.xml @@ -1,6 +1,6 @@ ros_ign_gazebo_demos - 0.244.13 + 0.244.14 Shim package to redirect to ros_gz_sim_demos. Apache 2.0 Brandon Ong diff --git a/ros_ign_image/CHANGELOG.rst b/ros_ign_image/CHANGELOG.rst index ebc65c75e..2144f0397 100644 --- a/ros_ign_image/CHANGELOG.rst +++ b/ros_ign_image/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package ros_ign_image ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +0.244.14 (2024-04-08) +--------------------- + 0.244.13 (2024-01-23) --------------------- diff --git a/ros_ign_image/package.xml b/ros_ign_image/package.xml index 2b69c8d57..236a2a6a1 100644 --- a/ros_ign_image/package.xml +++ b/ros_ign_image/package.xml @@ -2,7 +2,7 @@ ros_ign_image - 0.244.13 + 0.244.14 Shim package to redirect to ros_gz_image. Brandon Ong diff --git a/ros_ign_interfaces/CHANGELOG.rst b/ros_ign_interfaces/CHANGELOG.rst index 4a008fd4b..281974d50 100644 --- a/ros_ign_interfaces/CHANGELOG.rst +++ b/ros_ign_interfaces/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package ros_ign_interfaces ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +0.244.14 (2024-04-08) +--------------------- + 0.244.13 (2024-01-23) --------------------- diff --git a/ros_ign_interfaces/package.xml b/ros_ign_interfaces/package.xml index 61d040a1d..dadfc9ba9 100644 --- a/ros_ign_interfaces/package.xml +++ b/ros_ign_interfaces/package.xml @@ -1,6 +1,6 @@ ros_ign_interfaces - 0.244.13 + 0.244.14 Shim package to redirect to ros_gz_interfaces. Apache 2.0 Brandon Ong diff --git a/test_ros_gz_bridge/CMakeLists.txt b/test_ros_gz_bridge/CMakeLists.txt new file mode 100644 index 000000000..9a3bb8a83 --- /dev/null +++ b/test_ros_gz_bridge/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.5) + +project(test_ros_gz_bridge) + +# Default to C++14 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +endif() +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic") +endif() + +find_package(ament_cmake REQUIRED) +find_package(ros_gz_bridge REQUIRED) + + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() + + find_package(ament_cmake_gtest REQUIRED) + find_package(launch_testing_ament_cmake REQUIRED) + ament_find_gtest() + + ament_add_gtest(test_ros_gz_bridge src/test_ros_gz_bridge.cpp) + target_link_libraries(test_ros_gz_bridge ros_gz_bridge::ros_gz_bridge) +endif() + +ament_package() diff --git a/test_ros_gz_bridge/package.xml b/test_ros_gz_bridge/package.xml new file mode 100644 index 000000000..3fb8361fb --- /dev/null +++ b/test_ros_gz_bridge/package.xml @@ -0,0 +1,28 @@ + + + + test_ros_gz_bridge + 0.244.14 + Bridge communication between ROS and Gazebo Transport + Aditya Pande + Alejandro Hernandez + + Apache 2.0 + + Michael Carroll + + ament_cmake + + ros_gz_bridge + + ament_cmake_gtest + ament_lint_auto + ament_lint_common + launch_testing_ament_cmake + launch_ros + launch_testing + + + ament_cmake + + diff --git a/test_ros_gz_bridge/src/test_ros_gz_bridge.cpp b/test_ros_gz_bridge/src/test_ros_gz_bridge.cpp new file mode 100644 index 000000000..a32ca8e53 --- /dev/null +++ b/test_ros_gz_bridge/src/test_ros_gz_bridge.cpp @@ -0,0 +1,36 @@ +// Copyright 2024 Open Source Robotics Foundation, Inc. +// +// 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 + +#include + +class test_ros_gz_bridge : public ::testing::Test +{ +public: + static void SetUpTestCase() + { + rclcpp::init(0, nullptr); + } + + static void TearDownTestCase() + { + rclcpp::shutdown(); + } +}; + +TEST_F(test_ros_gz_bridge, SpawnNode) +{ + ros_gz_bridge::RosGzBridge node; +}