From 1954499c9eccf1d5af00ff8151915cde0418a407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Hern=C3=A1ndez=20Cordero?= Date: Fri, 29 Nov 2024 09:51:24 +0100 Subject: [PATCH] Imported upstream version '0.244.16' of 'upstream' --- .github/CODEOWNERS | 3 +- .github/workflows/build-and-test.sh | 24 +- .github/workflows/ros2-ci.yml | 16 +- .github/workflows/triage.yml | 8 +- README.md | 42 +-- README_RENAME.md | 2 +- img/video_img.png | Bin 221780 -> 0 bytes ros_gz/CHANGELOG.rst | 62 +---- ros_gz/package.xml | 7 +- ros_gz_bridge/CHANGELOG.rst | 242 ++++-------------- ros_gz_bridge/CMakeLists.txt | 96 ++++--- ros_gz_bridge/README.md | 195 +++++++------- .../bin/ros_gz_bridge_markdown_table | 6 +- .../ros_gz_bridge/convert/geometry_msgs.hpp | 13 - .../ros_gz_bridge/convert/rcl_interfaces.hpp | 2 +- .../convert/ros_gz_interfaces.hpp | 19 +- .../ros_gz_bridge/convert/rosgraph_msgs.hpp | 2 +- .../include/ros_gz_bridge/ros_gz_bridge.hpp | 21 +- ros_gz_bridge/launch/ros_gz_bridge.launch | 20 -- ros_gz_bridge/launch/ros_gz_bridge.launch.py | 147 ----------- ros_gz_bridge/package.xml | 27 +- ros_gz_bridge/resource/pkg_factories.cpp.em | 10 +- ros_gz_bridge/resource/ros_gz_bridge | 0 ros_gz_bridge/ros_gz_bridge/__init__.py | 27 +- .../ros_gz_bridge/actions/__init__.py | 22 -- .../ros_gz_bridge/actions/ros_gz_bridge.py | 160 ------------ ros_gz_bridge/ros_gz_bridge/mappings.py | 15 +- ros_gz_bridge/setup.cfg | 3 - ros_gz_bridge/src/bridge_config.cpp | 116 ++++----- ros_gz_bridge/src/bridge_handle.cpp | 2 - ros_gz_bridge/src/bridge_handle.hpp | 3 - ros_gz_bridge/src/bridge_handle_gz_to_ros.cpp | 3 +- ros_gz_bridge/src/convert/actuator_msgs.cpp | 1 + ros_gz_bridge/src/convert/geometry_msgs.cpp | 21 -- ros_gz_bridge/src/convert/gps_msgs.cpp | 2 +- .../src/convert/ros_gz_interfaces.cpp | 8 +- ros_gz_bridge/src/convert/sensor_msgs.cpp | 12 +- ros_gz_bridge/src/factory.hpp | 46 +--- ros_gz_bridge/src/factory_interface.hpp | 3 +- ros_gz_bridge/src/parameter_bridge.cpp | 8 +- ros_gz_bridge/src/ros_gz_bridge.cpp | 1 - .../service_factories/ros_gz_interfaces.cpp | 6 +- ros_gz_bridge/src/service_factory.hpp | 8 +- ros_gz_bridge/src/static_bridge.cpp | 2 +- ros_gz_bridge/test/bridge_config.cpp | 169 ++++++------ ros_gz_bridge/test/config/empty.yaml | 0 ros_gz_bridge/test/config/full_ign.yaml | 18 ++ ros_gz_bridge/test/config/invalid.yaml | 8 - ros_gz_bridge/test/config/minimum_ign.yaml | 20 ++ .../test/resource/gz_publisher.cpp.em | 4 +- .../test/resource/gz_subscriber.cpp.em | 2 +- ros_gz_bridge/test/utils/gz_test_msg.cpp | 5 +- ros_gz_bridge/test/utils/gz_test_msg.hpp | 22 +- ros_gz_bridge/test/utils/ros_test_msg.cpp | 32 +-- ros_gz_bridge/test/utils/ros_test_msg.hpp | 21 +- ros_gz_image/CHANGELOG.rst | 104 +------- ros_gz_image/CMakeLists.txt | 64 ++++- ros_gz_image/README.md | 13 +- ros_gz_image/package.xml | 23 +- ros_gz_image/src/image_bridge.cpp | 32 +-- ros_gz_interfaces/CHANGELOG.rst | 96 +++---- ros_gz_interfaces/package.xml | 6 +- ros_gz_point_cloud/examples/depth_camera.sdf | 2 +- ros_gz_point_cloud/examples/gpu_lidar.sdf | 2 +- ros_gz_point_cloud/examples/rgbd_camera.sdf | 2 +- ros_gz_point_cloud/package.xml | 5 +- ros_gz_sim/CHANGELOG.rst | 175 ++----------- ros_gz_sim/CMakeLists.txt | 147 ++++++----- ros_gz_sim/README.md | 2 +- ros_gz_sim/launch/gz_server.launch | 14 - ros_gz_sim/launch/gz_server.launch.py | 104 -------- ros_gz_sim/launch/gz_sim.launch.py.in | 16 +- ros_gz_sim/launch/gz_spawn_model.launch | 28 -- ros_gz_sim/launch/gz_spawn_model.launch.py | 94 ------- ros_gz_sim/launch/ros_gz_sim.launch | 29 --- ros_gz_sim/launch/ros_gz_sim.launch.py | 129 ---------- ros_gz_sim/launch/ros_gz_spawn_model.launch | 48 ---- .../launch/ros_gz_spawn_model.launch.py | 169 ------------ ros_gz_sim/package.xml | 32 +-- ros_gz_sim/resource/ros_gz_sim | 0 ros_gz_sim/ros_gz_sim/__init__.py | 22 -- ros_gz_sim/ros_gz_sim/actions/__init__.py | 24 -- .../ros_gz_sim/actions/gz_spawn_model.py | 207 --------------- ros_gz_sim/ros_gz_sim/actions/gzserver.py | 124 --------- ros_gz_sim/setup.cfg | 3 - ros_gz_sim/src/create.cpp | 223 +++++----------- ros_gz_sim/src/gzserver.cpp | 80 ------ ros_gz_sim/test/test_create.cpp | 56 ---- ros_gz_sim/test/test_create_node.launch.py | 47 ---- ros_gz_sim_demos/CHANGELOG.rst | 123 ++------- .../launch/joint_states.launch.py | 6 +- .../robot_description_publisher.launch.py | 12 +- ros_gz_sim_demos/launch/tf_bridge.launch.py | 2 +- ros_gz_sim_demos/package.xml | 13 +- ros_ign/CHANGELOG.rst | 44 ++++ ros_ign/CMakeLists.txt | 10 + ros_ign/README.md | 2 + ros_ign/package.xml | 24 ++ ros_ign_bridge/CHANGELOG.rst | 82 ++++++ ros_ign_bridge/CMakeLists.txt | 29 +++ ros_ign_bridge/README.md | 2 + ros_ign_bridge/package.xml | 19 ++ ros_ign_bridge/src/parameter_bridge_shim.cpp | 43 ++++ ros_ign_bridge/src/static_bridge_shim.cpp | 43 ++++ ros_ign_gazebo/CHANGELOG.rst | 62 +++++ ros_ign_gazebo/CMakeLists.txt | 58 +++++ ros_ign_gazebo/README.md | 2 + ros_ign_gazebo/launch/ign_gazebo.launch.py | 33 +++ ros_ign_gazebo/package.xml | 19 ++ ros_ign_gazebo/src/create_shim.cpp | 43 ++++ ros_ign_gazebo_demos/CHANGELOG.rst | 50 ++++ ros_ign_gazebo_demos/CMakeLists.txt | 17 ++ ros_ign_gazebo_demos/README.md | 2 + .../launch/air_pressure.launch.py | 34 +++ ros_ign_gazebo_demos/launch/battery.launch.py | 34 +++ ros_ign_gazebo_demos/launch/camera.launch.py | 34 +++ .../launch/depth_camera.launch.py | 34 +++ .../launch/diff_drive.launch.py | 34 +++ .../launch/gpu_lidar.launch.py | 34 +++ .../launch/gpu_lidar_bridge.launch.py | 34 +++ .../launch/image_bridge.launch.py | 34 +++ ros_ign_gazebo_demos/launch/imu.launch.py | 34 +++ .../launch/joint_states.launch.py | 34 +++ .../launch/magnetometer.launch.py | 34 +++ .../launch/rgbd_camera.launch.py | 34 +++ .../launch/rgbd_camera_bridge.launch.py | 34 +++ .../robot_description_publisher.launch.py | 37 +++ .../launch/tf_bridge.launch.py | 34 +++ .../launch/triggered_camera.launch.py | 34 +++ ros_ign_gazebo_demos/package.xml | 17 ++ ros_ign_image/CHANGELOG.rst | 46 ++++ ros_ign_image/CMakeLists.txt | 26 ++ ros_ign_image/README.md | 2 + ros_ign_image/package.xml | 19 ++ ros_ign_image/src/image_bridge_shim.cpp | 43 ++++ ros_ign_interfaces/CHANGELOG.rst | 61 +++++ ros_ign_interfaces/CMakeLists.txt | 49 ++++ ros_ign_interfaces/README.md | 2 + ros_ign_interfaces/msg/Contact.msg | 6 + ros_ign_interfaces/msg/Contacts.msg | 2 + ros_ign_interfaces/msg/Entity.msg | 13 + ros_ign_interfaces/msg/EntityFactory.msg | 11 + ros_ign_interfaces/msg/GuiCamera.msg | 12 + ros_ign_interfaces/msg/JointWrench.msg | 8 + ros_ign_interfaces/msg/Light.msg | 29 +++ ros_ign_interfaces/msg/StringVec.msg | 7 + ros_ign_interfaces/msg/TrackVisual.msg | 33 +++ ros_ign_interfaces/msg/VideoRecord.msg | 16 ++ ros_ign_interfaces/msg/WorldControl.msg | 7 + ros_ign_interfaces/msg/WorldReset.msg | 3 + ros_ign_interfaces/package.xml | 27 ++ ros_ign_interfaces/srv/ControlWorld.srv | 3 + ros_ign_interfaces/srv/DeleteEntity.srv | 3 + ros_ign_interfaces/srv/SetEntityPose.srv | 4 + ros_ign_interfaces/srv/SpawnEntity.srv | 3 + test_ros_gz_bridge/CHANGELOG.rst | 32 --- test_ros_gz_bridge/package.xml | 2 +- 157 files changed, 2577 insertions(+), 3052 deletions(-) delete mode 100644 img/video_img.png delete mode 100644 ros_gz_bridge/launch/ros_gz_bridge.launch delete mode 100644 ros_gz_bridge/launch/ros_gz_bridge.launch.py delete mode 100644 ros_gz_bridge/resource/ros_gz_bridge delete mode 100644 ros_gz_bridge/ros_gz_bridge/actions/__init__.py delete mode 100644 ros_gz_bridge/ros_gz_bridge/actions/ros_gz_bridge.py delete mode 100644 ros_gz_bridge/setup.cfg delete mode 100644 ros_gz_bridge/test/config/empty.yaml create mode 100644 ros_gz_bridge/test/config/full_ign.yaml delete mode 100644 ros_gz_bridge/test/config/invalid.yaml create mode 100644 ros_gz_bridge/test/config/minimum_ign.yaml delete mode 100644 ros_gz_sim/launch/gz_server.launch delete mode 100644 ros_gz_sim/launch/gz_server.launch.py delete mode 100644 ros_gz_sim/launch/gz_spawn_model.launch delete mode 100644 ros_gz_sim/launch/gz_spawn_model.launch.py delete mode 100644 ros_gz_sim/launch/ros_gz_sim.launch delete mode 100644 ros_gz_sim/launch/ros_gz_sim.launch.py delete mode 100644 ros_gz_sim/launch/ros_gz_spawn_model.launch delete mode 100644 ros_gz_sim/launch/ros_gz_spawn_model.launch.py delete mode 100644 ros_gz_sim/resource/ros_gz_sim delete mode 100644 ros_gz_sim/ros_gz_sim/__init__.py delete mode 100644 ros_gz_sim/ros_gz_sim/actions/__init__.py delete mode 100644 ros_gz_sim/ros_gz_sim/actions/gz_spawn_model.py delete mode 100644 ros_gz_sim/ros_gz_sim/actions/gzserver.py delete mode 100644 ros_gz_sim/setup.cfg delete mode 100644 ros_gz_sim/src/gzserver.cpp delete mode 100644 ros_gz_sim/test/test_create.cpp delete mode 100644 ros_gz_sim/test/test_create_node.launch.py create mode 100644 ros_ign/CHANGELOG.rst create mode 100644 ros_ign/CMakeLists.txt create mode 100644 ros_ign/README.md create mode 100644 ros_ign/package.xml create mode 100644 ros_ign_bridge/CHANGELOG.rst create mode 100644 ros_ign_bridge/CMakeLists.txt create mode 100644 ros_ign_bridge/README.md create mode 100644 ros_ign_bridge/package.xml create mode 100644 ros_ign_bridge/src/parameter_bridge_shim.cpp create mode 100644 ros_ign_bridge/src/static_bridge_shim.cpp create mode 100644 ros_ign_gazebo/CHANGELOG.rst create mode 100644 ros_ign_gazebo/CMakeLists.txt create mode 100644 ros_ign_gazebo/README.md create mode 100644 ros_ign_gazebo/launch/ign_gazebo.launch.py create mode 100644 ros_ign_gazebo/package.xml create mode 100644 ros_ign_gazebo/src/create_shim.cpp create mode 100644 ros_ign_gazebo_demos/CHANGELOG.rst create mode 100644 ros_ign_gazebo_demos/CMakeLists.txt create mode 100644 ros_ign_gazebo_demos/README.md create mode 100644 ros_ign_gazebo_demos/launch/air_pressure.launch.py create mode 100644 ros_ign_gazebo_demos/launch/battery.launch.py create mode 100644 ros_ign_gazebo_demos/launch/camera.launch.py create mode 100644 ros_ign_gazebo_demos/launch/depth_camera.launch.py create mode 100644 ros_ign_gazebo_demos/launch/diff_drive.launch.py create mode 100644 ros_ign_gazebo_demos/launch/gpu_lidar.launch.py create mode 100644 ros_ign_gazebo_demos/launch/gpu_lidar_bridge.launch.py create mode 100644 ros_ign_gazebo_demos/launch/image_bridge.launch.py create mode 100644 ros_ign_gazebo_demos/launch/imu.launch.py create mode 100644 ros_ign_gazebo_demos/launch/joint_states.launch.py create mode 100644 ros_ign_gazebo_demos/launch/magnetometer.launch.py create mode 100644 ros_ign_gazebo_demos/launch/rgbd_camera.launch.py create mode 100644 ros_ign_gazebo_demos/launch/rgbd_camera_bridge.launch.py create mode 100755 ros_ign_gazebo_demos/launch/robot_description_publisher.launch.py create mode 100644 ros_ign_gazebo_demos/launch/tf_bridge.launch.py create mode 100644 ros_ign_gazebo_demos/launch/triggered_camera.launch.py create mode 100644 ros_ign_gazebo_demos/package.xml create mode 100644 ros_ign_image/CHANGELOG.rst create mode 100644 ros_ign_image/CMakeLists.txt create mode 100644 ros_ign_image/README.md create mode 100644 ros_ign_image/package.xml create mode 100644 ros_ign_image/src/image_bridge_shim.cpp create mode 100644 ros_ign_interfaces/CHANGELOG.rst create mode 100644 ros_ign_interfaces/CMakeLists.txt create mode 100644 ros_ign_interfaces/README.md create mode 100644 ros_ign_interfaces/msg/Contact.msg create mode 100644 ros_ign_interfaces/msg/Contacts.msg create mode 100644 ros_ign_interfaces/msg/Entity.msg create mode 100644 ros_ign_interfaces/msg/EntityFactory.msg create mode 100644 ros_ign_interfaces/msg/GuiCamera.msg create mode 100644 ros_ign_interfaces/msg/JointWrench.msg create mode 100644 ros_ign_interfaces/msg/Light.msg create mode 100644 ros_ign_interfaces/msg/StringVec.msg create mode 100644 ros_ign_interfaces/msg/TrackVisual.msg create mode 100644 ros_ign_interfaces/msg/VideoRecord.msg create mode 100644 ros_ign_interfaces/msg/WorldControl.msg create mode 100644 ros_ign_interfaces/msg/WorldReset.msg create mode 100644 ros_ign_interfaces/package.xml create mode 100644 ros_ign_interfaces/srv/ControlWorld.srv create mode 100644 ros_ign_interfaces/srv/DeleteEntity.srv create mode 100644 ros_ign_interfaces/srv/SetEntityPose.srv create mode 100644 ros_ign_interfaces/srv/SpawnEntity.srv delete mode 100644 test_ros_gz_bridge/CHANGELOG.rst diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 627c951b7..9847eb2f5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,5 +1,4 @@ # More info: # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners -* @adityapande-1995 -* @ahcorde +* @chapulina diff --git a/.github/workflows/build-and-test.sh b/.github/workflows/build-and-test.sh index 8909db553..86b2fd8ba 100755 --- a/.github/workflows/build-and-test.sh +++ b/.github/workflows/build-and-test.sh @@ -10,15 +10,31 @@ export ROS_PYTHON_VERSION=3 apt update -qq apt install -qq -y lsb-release wget curl build-essential -echo "deb http://packages.osrfoundation.org/gazebo/ubuntu-stable `lsb_release -cs` main" > /etc/apt/sources.list.d/gazebo-stable.list -wget https://packages.osrfoundation.org/gazebo.key -O - | apt-key add - +if [ "$GZ_VERSION" == "garden" ]; then + echo "deb http://packages.osrfoundation.org/gazebo/ubuntu-stable `lsb_release -cs` main" > /etc/apt/sources.list.d/gazebo-stable.list + wget https://packages.osrfoundation.org/gazebo.key -O - | apt-key add - + + GZ_DEPS="libgz-sim7-dev" + + ROSDEP_ARGS="--skip-keys='sdformat-urdf'" +elif [ "$GZ_VERSION" == "harmonic" ]; then + echo "deb http://packages.osrfoundation.org/gazebo/ubuntu-stable `lsb_release -cs` main" > /etc/apt/sources.list.d/gazebo-stable.list + wget https://packages.osrfoundation.org/gazebo.key -O - | apt-key add - + + GZ_DEPS="libgz-sim8-dev" + + ROSDEP_ARGS="--skip-keys='sdformat-urdf'" +fi + +# Fortress comes through rosdep for Focal and Jammy # Dependencies. echo "deb http://packages.ros.org/ros2-testing/ubuntu `lsb_release -cs` main" > /etc/apt/sources.list.d/ros2-testing.list curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | apt-key add - apt-get update -qq -apt-get install -y python3-colcon-common-extensions \ - python3-rosdep +apt-get install -y $GZ_DEPS \ + python3-colcon-common-extensions \ + python3-rosdep rosdep init rosdep update diff --git a/.github/workflows/ros2-ci.yml b/.github/workflows/ros2-ci.yml index 448194704..f5040c0be 100644 --- a/.github/workflows/ros2-ci.yml +++ b/.github/workflows/ros2-ci.yml @@ -10,9 +10,15 @@ jobs: fail-fast: false matrix: include: - - docker-image: "ubuntu:24.04" + - docker-image: "ubuntu:22.04" + gz-version: "fortress" + ros-distro: "humble" + - docker-image: "ubuntu:22.04" + gz-version: "garden" + ros-distro: "humble" + - docker-image: "ubuntu:22.04" gz-version: "harmonic" - ros-distro: "jazzy" + ros-distro: "humble" container: image: ${{ matrix.docker-image }} steps: @@ -24,3 +30,9 @@ jobs: DOCKER_IMAGE: ${{ matrix.docker-image }} GZ_VERSION: ${{ matrix.gz-version }} ROS_DISTRO: ${{ matrix.ros-distro }} + - name: Build sdformat_urdf from source + uses: actions/checkout@v4 + if: ${{ matrix.gz-version }} == "garden" + with: + repository: ros/sdformat_urdf + ref: humble diff --git a/.github/workflows/triage.yml b/.github/workflows/triage.yml index 2c94852da..736670e0e 100644 --- a/.github/workflows/triage.yml +++ b/.github/workflows/triage.yml @@ -10,8 +10,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Add ticket to inbox - uses: actions/add-to-project@v0.5.0 + uses: technote-space/create-project-card-action@v1 with: - project-url: https://github.com/orgs/gazebosim/projects/7 - github-token: ${{ secrets.TRIAGE_TOKEN }} + PROJECT: Core development + COLUMN: Inbox + GITHUB_TOKEN: ${{ secrets.TRIAGE_TOKEN }} + CHECK_ORG_PROJECT: true diff --git a/README.md b/README.md index 48fb930a7..8e1d74d0a 100644 --- a/README.md +++ b/README.md @@ -9,18 +9,14 @@ Galactic | Fortress | [galactic](https://github.com/gazebosim/ros_gz/tree/galact 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) | [gazebo packages](https://gazebosim.org/docs/harmonic/ros_installation#-gazebo-harmonic-with-ros-2-humble-iron-or-rolling-use-with-caution-)[^1] -Iron | Fortress | [humble](https://github.com/gazebosim/ros_gz/tree/iron) | https://packages.ros.org -Iron | Garden | [humble](https://github.com/gazebosim/ros_gz/tree/iron) | only from source -Iron | Harmonic | [humble](https://github.com/gazebosim/ros_gz/tree/iron) | only from source -Jazzy | Garden | [ros2](https://github.com/gazebosim/ros_gz/tree/ros2) | only from source -Jazzy | Harmonic | [jazzy](https://github.com/gazebosim/ros_gz/tree/jazzy) | https://packages.ros.org -Rolling | Fortress | [humble](https://github.com/gazebosim/ros_gz/tree/humble) | https://packages.ros.org +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 Rolling | Harmonic | [ros2](https://github.com/gazebosim/ros_gz/tree/ros2) | only from source [^1]: Binaries for these pairings are provided from a the packages.osrfoundation.org repository. Refer to https://gazebosim.org/docs/latest/ros_installation for installation instructions. -For information on ROS(1) and Gazebo compatibility, refer to the [noetic branch README](https://github.com/gazebosim/ros_gz/tree/noetic) +For information on ROS 2 and Gazebo compatibility, refer to the [melodic branch README](https://github.com/gazebosim/ros_gz/tree/melodic) > Please [ticket an issue](https://github.com/gazebosim/ros_gz/issues/) if you'd like support to be added for some combination. @@ -59,11 +55,11 @@ This repository holds packages that provide integration between ## Install -This branch supports ROS Jazzy. See above for other ROS versions. +This branch supports ROS Humble. See above for other ROS versions. ### Binaries -Rolling binaries are available for Fortress. +Humble binaries are available for Fortress. They are hosted at https://packages.ros.org. 1. Add https://packages.ros.org @@ -74,24 +70,24 @@ They are hosted at https://packages.ros.org. 1. Install `ros_gz` - sudo apt install ros-jazzy-ros-gz + sudo apt install ros-humble-ros-gz ### From source #### ROS Be sure you've installed -[ROS Jazzy](https://docs.ros.org/en/jazzy/Installation.html) +[ROS Humble](https://docs.ros.org/en/humble/Installation.html) (at least ROS-Base). More ROS dependencies will be installed below. #### Gazebo -Install either [Garden or Harmonic](https://gazebosim.org/docs). +Install either [Edifice, Fortress, or Garden](https://gazebosim.org/docs). Set the `GZ_VERSION` environment variable to the Gazebo version you'd like to compile against. For example: - export GZ_VERSION=harmonic # IMPORTANT: Replace with correct version + export GZ_VERSION=edifice # IMPORTANT: Replace with correct version > You only need to set this variable when compiling, not when running. @@ -107,14 +103,14 @@ The following steps are for Linux and OSX. cd ~/ws/src # Download needed software - git clone https://github.com/gazebosim/ros_gz.git -b jazzy + git clone https://github.com/gazebosim/ros_gz.git -b humble ``` 1. Install dependencies (this may also install Gazebo): ``` cd ~/ws - rosdep install -r --from-paths src -i -y --rosdistro jazzy + rosdep install -r --from-paths src -i -y --rosdistro humble ``` > If `rosdep` fails to install Gazebo libraries and you have not installed them before, please follow [Gazebo installation instructions](https://gazebosim.org/docs/latest/install). @@ -134,6 +130,22 @@ The following steps are for Linux and OSX. > try building with `colcon build --parallel-workers=1 --executor sequential`. You might also have to set `export MAKEFLAGS="-j 1"` before running `colcon build` to limit > the number of processors used to build a single package. + If `colcon build` fails with [this issue](https://github.com/gazebosim/ros_gz/issues/401) + + ``` + CMake Error at CMakeLists.txt:81 (find_package): + By not providing "Findactuator_msgs.cmake" in CMAKE_MODULE_PATH this + project has asked CMake to find a package configuration file provided by + "actuator_msgs", but CMake did not find one. + ``` + + ```bash + cd src + git clone git@github.com:rudislabs/actuator_msgs.git + cd ../ + colcon build + ``` + ## ROSCon 2022 [![](img/video_img.png)](https://vimeo.com/showcase/9954564/video/767127300) diff --git a/README_RENAME.md b/README_RENAME.md index 92ba754f2..17007f4e2 100644 --- a/README_RENAME.md +++ b/README_RENAME.md @@ -7,7 +7,7 @@ This allows users to do either of these and get equivalent behavior: ```bash ros2 run ros_gz parameter_bridge [...] -ros2 run ros_gz parameter_bridge [...] # Will emit deprecation warning +ros2 run ros_ign parameter_bridge [...] # Will emit deprecation warning ``` Additionally, installed files like launch files, message interfaces etc. are **duplicated** versions of the ones in `ros_gz` (but renamed as appropriate), and point to `ros_gz` dependencies as well (e.g. launch files pointing to `ros_gz` nodes.) diff --git a/img/video_img.png b/img/video_img.png deleted file mode 100644 index 626c73539ef721f72bf8226d3a413cfd8d544f2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 221780 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�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∨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 LNkvXXu0mjf@~H7@ diff --git a/ros_gz/CHANGELOG.rst b/ros_gz/CHANGELOG.rst index cc02dbeb0..4ccbf16a5 100644 --- a/ros_gz/CHANGELOG.rst +++ b/ros_gz/CHANGELOG.rst @@ -2,61 +2,23 @@ Changelog for package ros_gz ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -1.0.7 (2024-11-08) ------------------- - -1.0.6 (2024-10-31) ------------------- - -1.0.5 (2024-10-14) ------------------- +0.244.16 (2024-07-22) +--------------------- -1.0.4 (2024-08-29) ------------------- +0.244.15 (2024-07-03) +--------------------- -1.0.3 (2024-07-22) ------------------- +0.244.14 (2024-04-08) +--------------------- -1.0.2 (2024-07-03) ------------------- -* Prepare for 1.0.0 Release (`#495 `_) -* 0.244.14 -* Changelog -* 0.244.13 -* Changelog -* 0.244.12 -* Changelog -* 0.246.0 -* Update changelogs -* Port: humble to ros2 (`#386 `_) -* Merge branch 'humble' into mjcarroll/humble_to_ros2 -* Update maintainers (`#376 `_) -* Humble ➡️ ROS2 (`#323 `_) - Humble ➡️ ROS2 -* Merge branch 'humble' into ports/humble_to_ros2 -* 0.245.0 -* Changelog -* humble to ros2 (`#311 `_) - Co-authored-by: Michael Carroll -* Merge remote-tracking branch 'origin/humble' into ahcorde/humble_to_ros2 -* Contributors: Addisu Z. Taddese, Aditya Pande, Alejandro Hernández Cordero, Jose Luis Rivero, Michael Carroll, ahcorde - -1.0.0 (2024-04-24) ------------------- +0.244.13 (2024-01-23) +--------------------- -0.246.0 (2023-08-31) --------------------- -* Port: humble to ros2 (`#386 `_) -* Update maintainers (`#376 `_) -* Humble ➡️ ROS2 (`#323 `_) -* Contributors: Aditya Pande, Alejandro Hernández Cordero, Michael Carroll, ahcorde +0.244.12 (2023-12-13) +--------------------- -0.245.0 (2022-10-12) --------------------- -* humble to ros2 (`#311 `_) - Co-authored-by: Michael Carroll -* Merge remote-tracking branch 'origin/humble' into ahcorde/humble_to_ros2 -* Contributors: Alejandro Hernández Cordero, ahcorde +0.244.11 (2023-05-23) +--------------------- 0.244.10 (2023-05-03) --------------------- diff --git a/ros_gz/package.xml b/ros_gz/package.xml index ce9b5ff50..da3e888ca 100644 --- a/ros_gz/package.xml +++ b/ros_gz/package.xml @@ -4,14 +4,11 @@ ros_gz - 1.0.7 + 0.244.16 Meta-package containing interfaces for using ROS 2 with Gazebo simulation. - Aditya Pande - Alejandro Hernandez + Louise Poubel Apache 2.0 - Louise Poubel - ament_cmake ros_gz_bridge diff --git a/ros_gz_bridge/CHANGELOG.rst b/ros_gz_bridge/CHANGELOG.rst index b47e530bb..e1b6fef2c 100644 --- a/ros_gz_bridge/CHANGELOG.rst +++ b/ros_gz_bridge/CHANGELOG.rst @@ -2,91 +2,39 @@ Changelog for package ros_gz_bridge ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -1.0.7 (2024-11-08) ------------------- - -1.0.6 (2024-10-31) ------------------- -* Extra parameter to start a container (`#616 `_) (`#618 `_) - (cherry picked from commit 8115ccaaedea718841367eb64e500e13df392fd7) - Co-authored-by: Carlos Agüero -* Contributors: mergify[bot] - -1.0.5 (2024-10-14) ------------------- -* Merge pull request `#607 `_ from Amronos/ros2-jazzy-backport -* Fix changelogs and versions -* adds deadline and liveliness QoSPolicyKinds to qos_overriding_options (`#609 `_) (`#613 `_) -* Remove default_value for required arguments (`#602 `_) -* Fix errors with name of bridge not being given (`#600 `_) -* Use optional parameters in actions (`#601 `_) -* Making use_composition true by default (`#578 `_) -* Use `ignoreLocalMessages` in the bridge (`#559 `_) -* Update launch files with name parameter (`#556 `_) -* Ensure the same container is used for the bridge and gz_server (`#553 `_) -* Launch ros_gz_bridge from xml (`#550 `_) -* Launch gzserver and the bridge as composable nodes (`#528 `_) -* adds deadline and liveliness QoSPolicyKinds to qos_overriding_options (`#609 `_) (`#613 `_) -* Contributors: Aarav Gupta, Addisu Z. Taddese, Alejandro Hernández Cordero, Amronos, Carlos Agüero, mergify[bot] - -1.0.4 (2024-08-29) ------------------- -* feat: `override_timestamps_with_wall_time` parameter (backport `#562 `_) (`#584 `_) - Co-authored-by: Rein Appeldoorn -* Use memcpy instead of std::copy when bridging images (`#565 `_) (`#585 `_) - While testing ros <-> gz communication using the bridge I noticed that the bridge was talking quite a bit of time copying images from Gazebo to ROS. I found that the std::copy operation that we're doing is substantially slower than the memcpy alternative. I think that in principle this shouldn't happen but the numbers are quite clear. Perhaps std::copy is doing something that doesn't use cache effectively - --------- - Co-authored-by: Jose Luis Rivero - (cherry picked from commit a781b78852112246245c05481db6335388d4f736) - Co-authored-by: Carlos Agüero -* Contributors: mergify[bot] - -1.0.3 (2024-07-22) ------------------- -* Add support for gz.msgs.EntityWrench (base branch: ros2) (`#573 `_) (`#574 `_) +0.244.16 (2024-07-22) +--------------------- +* Add support for gz.msgs.EntityWrench (base branch: ros2) (backport `#573 `_) (`#575 `_) + * Add support for gz.msgs.EntityWrench (base branch: ros2) (`#573 `_) (cherry picked from commit f9afb69d1163633dd978024bb7271a28cf7b551a) + # Conflicts: + # ros_gz_bridge/README.md + # ros_gz_bridge/test/utils/gz_test_msg.hpp + * Fixed merge + * Update ros_gz_bridge/README.md + Co-authored-by: Addisu Z. Taddese + --------- Co-authored-by: Victor T. Noppeney + Co-authored-by: Alejandro Hernández Cordero + Co-authored-by: Addisu Z. Taddese * Contributors: mergify[bot] -1.0.2 (2024-07-03) ------------------- -* Merge pull request `#569 `_ from azeey/iron_to_jazzy - Merge iron ➡️ jazzy -* Merge iron into jazzy -* Add option to change material color from ROS. (`#521 `_) - Forward port of `#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. - (cherry picked from commit 78dc4823121f085594e6028a93f1e571eb04f58b) -* Merge pull request `#564 `_ from azeey/humble_to_iron - Humble ➡️ Iron -* Merge humble -> iron -* populate imu covariances when converting (`#375 `_) (`#540 `_) - Co-authored-by: El Jawad Alaa -* Prepare for 1.0.0 Release (`#495 `_) -* Use gz_vendor packages (`#531 `_) +0.244.15 (2024-07-03) +--------------------- * [backport Humble] Create bridge for GPSFix msg (`#316 `_) (`#538 `_) Co-authored-by: Rousseau Vincent -* [backport Iron] Create bridge for GPSFix msg (`#316 `_) (`#537 `_) - Co-authored-by: Rousseau Vincent -* 0.244.14 -* Changelog -* Added conversion for Detection3D and Detection3DArray (`#523 `_) (`#526 `_) - Co-authored-by: wittenator <9154515+wittenator@users.noreply.github.com> -* Added conversion for Detection3D and Detection3DArray (`#523 `_) (`#525 `_) +* Contributors: Alejandro Hernández Cordero + +0.244.14 (2024-04-08) +--------------------- +* Added conversion for Detection3D and Detection3DArray (`#523 `_) (`#526 `_) Co-authored-by: wittenator <9154515+wittenator@users.noreply.github.com> -* [Backport rolling] Add ROS namespaces to GZ topics (`#517 `_) - Co-authored-by: Krzysztof Wojciechowski <49921081+Kotochleb@users.noreply.github.com> -* ign to gz (`#519 `_) -* Add ROS namespaces to GZ topics (`#512 `_) +* 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 `_) +* 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 -* Correctly export ros_gz_bridge for downstream targets (`#503 `_) -* Add a virtual destructor to suppress compiler warning (`#502 `_) -* Add option to change material color from ROS. (`#486 `_) +* 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. @@ -94,126 +42,30 @@ Changelog for package ros_gz_bridge Co-authored-by: Alejandro Hernández Cordero Co-authored-by: Addisu Z. Taddese Co-authored-by: Addisu Z. Taddese -* 0.244.13 -* Changelog -* backport pr 374 (`#489 `_) -* populate imu covariances when converting (`#488 `_) -* 0.244.12 -* Changelog -* Backport: Add conversion for geometry_msgs/msg/TwistStamped <-> gz.msgs.Twist (`#468 `_) (`#470 `_) -* Add conversion for geometry_msgs/msg/TwistStamped <-> gz.msgs.Twist (`#468 `_) -* Added messages for 2D Bounding Boxes to ros_gz_bridge (`#458 `_) (`#466 `_) - Co-authored-by: Alejandro Hernandez Cordero -* populate imu covariances when converting (`#375 `_) -* 0.246.0 -* Update changelogs -* Add harmonic CI (`#447 `_) - * Add harmonic CI - * Include garden options - * Add harmonic stanza - * Additional message headers - --------- -* SensorNoise msg bridging (`#417 `_) -* Added Altimeter msg bridging (`#413 `_) -* Update README.md (`#411 `_) - The ROS type for gz.msgs.NavSat messages should be **sensor_msgs/msg/NavSatFix** instead of **sensor_msgs/msg/NavSatFixed** -* Add missing rosidl_cmake dep to ros_gz_bridge (`#391 `_) - Co-authored-by: Chris Lalancette -* allow converting from/to TwistWithCovarianceStamped (`#374 `_) - * allow converting from/to TwistWithCovarianceStamped - -------- - Co-authored-by: Alejandro Hernández Cordero -* Added doc (`#393 `_) -* Port: humble to ros2 (`#386 `_) -* Merge branch 'humble' into mjcarroll/humble_to_ros2 -* allow converting from/to PoseWithCovarianceStamped (`#381 `_) - * allow converting from/to PoseWithCovarianceStamped -* Add actuator_msgs to bridge. (`#378 `_) -* Update maintainers (`#376 `_) -* Fix warning message (`#371 `_) -* Improve error messages around config loading (`#356 `_) -* Bringing the Joy to gazebo. (`#350 `_) - Enable using the gazebo bridge with Joy. -* Fix double wait in ros_gz_bridge (`#347 `_) -* Create bridge for GPSFix msg (`#316 `_) -* Humble ➡️ ROS2 (`#323 `_) - Humble ➡️ ROS2 -* Merge branch 'humble' into ports/humble_to_ros2 -* 0.245.0 -* Changelog -* humble to ros2 (`#311 `_) - Co-authored-by: Michael Carroll -* Remove Humble+ deprecations (`#312 `_) - * Remove Humble+ deprecations -* Merge remote-tracking branch 'origin/humble' into ahcorde/humble_to_ros2 -* Remove all ignition references on ROS 2 branch (`#302 `_) - * Remove all shims - * Update CMakeLists and package.xml for garden - * Complete garden gz renaming - * Drop fortress CI -* Contributors: Addisu Z. Taddese, Aditya Pande, Alejandro Hernández Cordero, Arjun K Haridas, Benjamin Perseghetti, El Jawad Alaa, Jose Luis Rivero, Krzysztof Wojciechowski, Michael Carroll, Rousseau Vincent, Yadu, ahcorde, wittenator, ymd-stella - -1.0.0 (2024-04-24) ------------------- -* Use gz_vendor packages (`#531 `_) -* Added conversion for Detection3D and Detection3DArray (`#523 `_) (`#525 `_) - Co-authored-by: wittenator <9154515+wittenator@users.noreply.github.com> -* [Backport rolling] Add ROS namespaces to GZ topics (`#517 `_) - Co-authored-by: Krzysztof Wojciechowski <49921081+Kotochleb@users.noreply.github.com> -* ign to gz (`#519 `_) -* Correctly export ros_gz_bridge for downstream targets (`#503 `_) -* Add a virtual destructor to suppress compiler warning (`#502 `_) -* Add conversion for geometry_msgs/msg/TwistStamped <-> gz.msgs.Twist (`#468 `_) -* Added messages for 2D Bounding Boxes to ros_gz_bridge (`#458 `_) (`#466 `_) - Co-authored-by: Alejandro Hernandez Cordero -* populate imu covariances when converting (`#375 `_) -* Contributors: Addisu Z. Taddese, Alejandro Hernández Cordero, El Jawad Alaa, Michael Carroll - -0.246.0 (2023-08-31) --------------------- -* Add harmonic CI (`#447 `_) - * Add harmonic CI - * Include garden options - * Add harmonic stanza - * Additional message headers - --------- -* SensorNoise msg bridging (`#417 `_) -* Added Altimeter msg bridging (`#413 `_) -* Update README.md (`#411 `_) +* Contributors: Alejandro Hernández Cordero, Benjamin Perseghetti, Krzysztof Wojciechowski, Michael Carroll + +0.244.13 (2024-01-23) +--------------------- +* backport pr 374 (`#489 `_) +* populate imu covariances when converting (`#488 `_) +* Contributors: El Jawad Alaa + +0.244.12 (2023-12-13) +--------------------- +* Backport: Add conversion for geometry_msgs/msg/TwistStamped <-> gz.msgs.Twist (`#468 `_) (`#470 `_) +* Add support for Harmonic/Humble pairing (`#462 `_) +* Added messages for 2D Bounding Boxes to ros_gz_bridge (`#458 `_) +* Fix double wait in ros_gz_bridge (`#347 `_) (`#450 `_) +* [backport humble] SensorNoise msg bridging (`#417 `_) +* [backport humble] Added Altimeter msg bridging (`#413 `_) (`#414 `_) (`#426 `_) +* [backport humble] Update README.md (`#411 `_) The ROS type for gz.msgs.NavSat messages should be **sensor_msgs/msg/NavSatFix** instead of **sensor_msgs/msg/NavSatFixed** -* Add missing rosidl_cmake dep to ros_gz_bridge (`#391 `_) - Co-authored-by: Chris Lalancette -* allow converting from/to TwistWithCovarianceStamped (`#374 `_) - Co-authored-by: Alejandro Hernández Cordero -* Added doc (`#393 `_) -* Port: humble to ros2 (`#386 `_) -* Merge branch 'humble' into mjcarroll/humble_to_ros2 -* allow converting from/to PoseWithCovarianceStamped (`#381 `_) - * allow converting from/to PoseWithCovarianceStamped -* Add actuator_msgs to bridge. (`#378 `_) -* Update maintainers (`#376 `_) -* Fix warning message (`#371 `_) -* Improve error messages around config loading (`#356 `_) -* Bringing the Joy to gazebo. (`#350 `_) - Enable using the gazebo bridge with Joy. -* Fix double wait in ros_gz_bridge (`#347 `_) -* Create bridge for GPSFix msg (`#316 `_) -* Humble ➡️ ROS2 (`#323 `_) -* Contributors: Aditya Pande, Alejandro Hernández Cordero, Arjun K Haridas, Benjamin Perseghetti, El Jawad Alaa, Michael Carroll, Rousseau Vincent, Yadu, ahcorde, ymd-stella - -0.245.0 (2022-10-12) --------------------- -* humble to ros2 (`#311 `_) - Co-authored-by: Michael Carroll -* Remove Humble+ deprecations (`#312 `_) - * Remove Humble+ deprecations -* Merge remote-tracking branch 'origin/humble' into ahcorde/humble_to_ros2 -* Remove all ignition references on ROS 2 branch (`#302 `_) - * Remove all shims - * Update CMakeLists and package.xml for garden - * Complete garden gz renaming - * Drop fortress CI -* Contributors: Alejandro Hernández Cordero, Michael Carroll, ahcorde +* Contributors: Addisu Z. Taddese, Alejandro Hernández Cordero, Arjun K Haridas, wittenator + +0.244.11 (2023-05-23) +--------------------- +* Add actuator_msgs to humble bridge. (`#394 `_) +* Contributors: Benjamin Perseghetti 0.244.10 (2023-05-03) --------------------- diff --git a/ros_gz_bridge/CMakeLists.txt b/ros_gz_bridge/CMakeLists.txt index 44911577c..8f4cc27d1 100644 --- a/ros_gz_bridge/CMakeLists.txt +++ b/ros_gz_bridge/CMakeLists.txt @@ -14,18 +14,58 @@ find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) find_package(rclcpp_components REQUIRED) find_package(yaml_cpp_vendor REQUIRED) -find_package(yaml-cpp REQUIRED) -find_package(gz_transport_vendor REQUIRED) -find_package(gz-transport REQUIRED) - -find_package(gz_msgs_vendor REQUIRED) -find_package(gz-msgs REQUIRED) +# TODO(CH3): Deprecated. Remove on tock. +if("$ENV{GZ_VERSION}" STREQUAL "" AND NOT "$ENV{IGNITION_VERSION}" STREQUAL "") + message(DEPRECATION "Environment variable [IGNITION_VERSION] is deprecated. Use [GZ_VERSION] instead.") + set(ENV{GZ_VERSION} $ENV{IGNITION_VERSION}) +endif() -# Install the python module for this package -ament_python_install_package(${PROJECT_NAME}) +# Edifice +if("$ENV{GZ_VERSION}" STREQUAL "edifice") + find_package(ignition-transport10 REQUIRED) + find_package(ignition-msgs7 REQUIRED) + + set(GZ_TARGET_PREFIX ignition) + set(GZ_MSGS_VER ${ignition-msgs7_VERSION_MAJOR}) + set(GZ_TRANSPORT_VER ${ignition-transport10_VERSION_MAJOR}) + + message(STATUS "Compiling against Ignition Edifice") +# Garden +elseif("$ENV{GZ_VERSION}" STREQUAL "garden") + find_package(gz-transport12 REQUIRED) + find_package(gz-msgs9 REQUIRED) + + set(GZ_TARGET_PREFIX gz) + set(GZ_MSGS_VER ${gz-msgs9_VERSION_MAJOR}) + set(GZ_TRANSPORT_VER ${gz-transport12_VERSION_MAJOR}) + + message(STATUS "Compiling against Gazebo Garden") +elseif("$ENV{GZ_VERSION}" STREQUAL "harmonic") + find_package(gz-transport13 REQUIRED) + find_package(gz-msgs10 REQUIRED) + + set(GZ_TARGET_PREFIX gz) + set(GZ_MSGS_VER ${gz-msgs10_VERSION_MAJOR}) + set(GZ_TRANSPORT_VER ${gz-transport13_VERSION_MAJOR}) + + message(STATUS "Compiling against Gazebo Harmonic") +# Default to Fortress +else() + find_package(ignition-transport11 REQUIRED) + find_package(ignition-msgs8 REQUIRED) + + set(GZ_TARGET_PREFIX ignition) + set(GZ_MSGS_VER ${ignition-msgs8_VERSION_MAJOR}) + set(GZ_TRANSPORT_VER ${ignition-transport11_VERSION_MAJOR}) + + message(STATUS "Compiling against Ignition Fortress") +endif() -set(GZ_MSGS_VERSION_FULL ${gz-msgs_VERSION}) +set(GZ_MSGS_VERSION_MAJOR ${${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}_VERSION_MAJOR}) +set(GZ_MSGS_VERSION_MINOR ${${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}_VERSION_MINOR}) +set(GZ_MSGS_VERSION_PATCH ${${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}_VERSION_PATCH}) +set(GZ_MSGS_VERSION_FULL ${GZ_MSGS_VERSION_MAJOR}.${GZ_MSGS_VERSION_MINOR}.${GZ_MSGS_VERSION_PATCH}) set(BRIDGE_MESSAGE_TYPES builtin_interfaces @@ -98,18 +138,15 @@ add_library(${bridge_lib} ) target_link_libraries(${bridge_lib} - PUBLIC - gz-msgs::core - gz-transport::core - PRIVATE - yaml-cpp::yaml-cpp + ${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}::core + ${GZ_TARGET_PREFIX}-transport${GZ_TRANSPORT_VER}::core ) ament_target_dependencies(${bridge_lib} - PUBLIC - rclcpp - rclcpp_components - ${BRIDGE_MESSAGE_TYPES} + rclcpp + rclcpp_components + yaml_cpp_vendor + ${BRIDGE_MESSAGE_TYPES} ) target_include_directories(${bridge_lib} @@ -121,6 +158,11 @@ target_include_directories(${bridge_lib} "$" ) +target_link_libraries(${bridge_lib} + ${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}::core + ${GZ_TARGET_PREFIX}-transport${GZ_TRANSPORT_VER}::core +) + rclcpp_components_register_node( ${bridge_lib} PLUGIN ros_gz_bridge::RosGzBridge @@ -136,11 +178,6 @@ install( DESTINATION include/${PROJECT_NAME} ) -install( - DIRECTORY launch/ - DESTINATION share/${PROJECT_NAME}/launch -) - set(bridge_executables parameter_bridge static_bridge @@ -176,7 +213,7 @@ if(BUILD_TESTING) ${PROJECT_SOURCE_DIR}/src/convert/rcl_interfaces_TEST.cpp ) target_link_libraries(test_rcl_interfaces - gz-msgs::core + ${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}::core ${rcl_interfaces_TARGETS} gtest gtest_main @@ -211,8 +248,8 @@ if(BUILD_TESTING) ) target_link_libraries(test_utils ${GTEST_LIBRARIES} - gz-msgs::core - gz-transport::core + ${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}::core + ${GZ_TARGET_PREFIX}-transport${GZ_TRANSPORT_VER}::core ) ament_target_dependencies(test_utils rclcpp @@ -324,10 +361,9 @@ ament_export_targets(export_${PROJECT_NAME}) # specific order: dependents before dependencies ament_export_dependencies(rclcpp) ament_export_dependencies(rclcpp_components) -ament_export_dependencies(gz_msgs_vendor) -ament_export_dependencies(gz-msgs) -ament_export_dependencies(gz_transport_vendor) -ament_export_dependencies(gz-transport) +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 2a53c08cd..c2f7ce3da 100644 --- a/ros_gz_bridge/README.md +++ b/ros_gz_bridge/README.md @@ -5,94 +5,77 @@ between ROS and Gazebo Transport. The following message types can be bridged for topics: -| ROS type | Gazebo Transport Type | -| ---------------------------------------------- | :------------------------------: | -| actuator_msgs/msg/Actuators | gz.msgs.Actuators | -| builtin_interfaces/msg/Time | gz.msgs.Time | -| geometry_msgs/msg/Point | gz.msgs.Vector3d | -| geometry_msgs/msg/Pose | gz.msgs.Pose | -| geometry_msgs/msg/PoseArray | gz.msgs.Pose_V | -| geometry_msgs/msg/PoseStamped | gz.msgs.Pose | -| geometry_msgs/msg/PoseWithCovariance | gz.msgs.PoseWithCovariance | -| geometry_msgs/msg/PoseWithCovarianceStamped | gz.msgs.PoseWithCovariance | -| geometry_msgs/msg/Quaternion | gz.msgs.Quaternion | -| geometry_msgs/msg/Transform | gz.msgs.Pose | -| geometry_msgs/msg/TransformStamped | gz.msgs.Pose | -| geometry_msgs/msg/Twist | gz.msgs.Twist | -| geometry_msgs/msg/TwistStamped | gz.msgs.Twist | -| geometry_msgs/msg/TwistWithCovariance | gz.msgs.TwistWithCovariance | -| geometry_msgs/msg/TwistWithCovarianceStamped | gz.msgs.TwistWithCovariance | -| geometry_msgs/msg/Vector3 | gz.msgs.Vector3d | -| geometry_msgs/msg/Wrench | gz.msgs.Wrench | -| geometry_msgs/msg/WrenchStamped | gz.msgs.Wrench | -| gps_msgs/msg/GPSFix | gz.msgs.NavSat | -| nav_msgs/msg/Odometry | gz.msgs.Odometry | -| nav_msgs/msg/Odometry | gz.msgs.OdometryWithCovariance | -| rcl_interfaces/msg/ParameterValue | gz.msgs.Any | -| ros_gz_interfaces/msg/Altimeter | gz.msgs.Altimeter | -| ros_gz_interfaces/msg/Contact | gz.msgs.Contact | -| ros_gz_interfaces/msg/Contacts | gz.msgs.Contacts | -| ros_gz_interfaces/msg/Dataframe | gz.msgs.Dataframe | -| ros_gz_interfaces/msg/Entity | gz.msgs.Entity | -| ros_gz_interfaces/msg/EntityWrench | gz.msgs.EntityWrench | -| ros_gz_interfaces/msg/Float32Array | gz.msgs.Float_V | -| ros_gz_interfaces/msg/GuiCamera | gz.msgs.GUICamera | -| ros_gz_interfaces/msg/JointWrench | gz.msgs.JointWrench | -| ros_gz_interfaces/msg/Light | gz.msgs.Light | -| ros_gz_interfaces/msg/ParamVec | gz.msgs.Param | -| ros_gz_interfaces/msg/ParamVec | gz.msgs.Param_V | -| ros_gz_interfaces/msg/SensorNoise | gz.msgs.SensorNoise | -| ros_gz_interfaces/msg/StringVec | gz.msgs.StringMsg_V | -| ros_gz_interfaces/msg/TrackVisual | gz.msgs.TrackVisual | -| ros_gz_interfaces/msg/VideoRecord | gz.msgs.VideoRecord | -| rosgraph_msgs/msg/Clock | gz.msgs.Clock | -| sensor_msgs/msg/BatteryState | gz.msgs.BatteryState | -| sensor_msgs/msg/CameraInfo | gz.msgs.CameraInfo | -| sensor_msgs/msg/FluidPressure | gz.msgs.FluidPressure | -| sensor_msgs/msg/Image | gz.msgs.Image | -| sensor_msgs/msg/Imu | gz.msgs.IMU | -| sensor_msgs/msg/JointState | gz.msgs.Model | -| sensor_msgs/msg/Joy | gz.msgs.Joy | -| sensor_msgs/msg/LaserScan | gz.msgs.LaserScan | -| sensor_msgs/msg/MagneticField | gz.msgs.Magnetometer | -| sensor_msgs/msg/NavSatFix | gz.msgs.NavSat | -| sensor_msgs/msg/PointCloud2 | gz.msgs.PointCloudPacked | -| std_msgs/msg/Bool | gz.msgs.Boolean | -| std_msgs/msg/ColorRGBA | gz.msgs.Color | -| std_msgs/msg/Empty | gz.msgs.Empty | -| std_msgs/msg/Float32 | gz.msgs.Float | -| std_msgs/msg/Float64 | gz.msgs.Double | -| std_msgs/msg/Header | gz.msgs.Header | -| std_msgs/msg/Int32 | gz.msgs.Int32 | -| std_msgs/msg/String | gz.msgs.StringMsg | -| std_msgs/msg/UInt32 | gz.msgs.UInt32 | -| tf2_msgs/msg/TFMessage | gz.msgs.Pose_V | -| trajectory_msgs/msg/JointTrajectory | gz.msgs.JointTrajectory | -| vision_msgs/msg/Detection2D | gz.msgs.AnnotatedAxisAligned2DBox | -| vision_msgs/msg/Detection2DArray | gz.msgs.AnnotatedAxisAligned2DBox_V | -| vision_msgs/msg/Detection3D | gz::msgs::AnnotatedOriented3DBox | -| vision_msgs/msg/Detection3DArray | gz::msgs::AnnotatedOriented3DBox_V | +| 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 | +| gps_msgs/GPSFix | ignition::msgs::NavSat | +| 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/EntityWrench | ignition::msgs::EntityWrench | +| 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: | ROS type | Gazebo request | Gazebo response | |--------------------------------------|:--------------------------:| --------------------- | -| ros_gz_interfaces/srv/ControlWorld | gz.msgs.WorldControl | gz.msgs.Boolean | +| ros_gz_interfaces/srv/ControlWorld | ignition.msgs.WorldControl | ignition.msgs.Boolean | Run `ros2 run ros_gz_bridge parameter_bridge -h` for instructions. -**NOTE**: If during startup, gazebo detects that there is another publisher on `/clock`, it will only create the fully qualified `/world//clock topic`. -Gazebo would be the only `/clock` publisher, the sole source of clock information. - -You should create an unidirectional `/clock` bridge: - -```bash -ros2 run ros_gz_bridge parameter_bridge /clock@rosgraph_msgs/msg/Clock[gz.msgs.Clock -``` - -An alternative set-up can be using the bridge with the `override_timestamps_with_wall_time` ros parameter set to `true` (default=`false`). In this set-up, -all header timestamps of the outgoing messages will be stamped with the wall time. This can be useful when the simulator has to communicate with an external system that requires wall times. - ## Example 1a: Gazebo Transport talker and ROS 2 listener Start the parameter bridge which will watch the specified topics. @@ -100,14 +83,14 @@ Start the parameter bridge which will watch the specified topics. ``` # Shell A: . ~/bridge_ws/install/setup.bash -ros2 run ros_gz_bridge parameter_bridge /chatter@std_msgs/msg/String@gz.msgs.StringMsg +ros2 run ros_gz_bridge parameter_bridge /chatter@std_msgs/msg/String@ignition.msgs.StringMsg ``` Now we start the ROS listener. ``` # Shell B: -. /opt/ros/rolling/setup.bash +. /opt/ros/humble/setup.bash ros2 topic echo /chatter ``` @@ -115,7 +98,7 @@ Now we start the Gazebo Transport talker. ``` # Shell C: -gz topic -t /chatter -m gz.msgs.StringMsg -p 'data:"Hello"' +ign topic -t /chatter -m ignition.msgs.StringMsg -p 'data:"Hello"' ``` ## Example 1b: ROS 2 talker and Gazebo Transport listener @@ -125,21 +108,21 @@ Start the parameter bridge which will watch the specified topics. ``` # Shell A: . ~/bridge_ws/install/setup.bash -ros2 run ros_gz_bridge parameter_bridge /chatter@std_msgs/msg/String@gz.msgs.StringMsg +ros2 run ros_gz_bridge parameter_bridge /chatter@std_msgs/msg/String@ignition.msgs.StringMsg ``` Now we start the Gazebo Transport listener. ``` # Shell B: -gz topic -e -t /chatter +ign topic -e -t /chatter ``` Now we start the ROS talker. ``` # Shell C: -. /opt/ros/rolling/setup.bash +. /opt/ros/humble/setup.bash ros2 topic pub /chatter std_msgs/msg/String "data: 'Hi'" --once ``` @@ -153,14 +136,14 @@ First we start Gazebo Sim (don't forget to hit play, or Gazebo Sim won't generat ``` # Shell A: -gz sim sensors_demo.sdf +ign gazebo sensors_demo.sdf ``` Let's see the topic where camera images are published. ``` # Shell B: -gz topic -l | grep image +ign topic -l | grep image /rgbd_camera/depth_image /rgbd_camera/image ``` @@ -170,14 +153,14 @@ Then we start the parameter bridge with the previous topic. ``` # Shell B: . ~/bridge_ws/install/setup.bash -ros2 run ros_gz_bridge parameter_bridge /rgbd_camera/image@sensor_msgs/msg/Image@gz.msgs.Image +ros2 run ros_gz_bridge parameter_bridge /rgbd_camera/image@sensor_msgs/msg/Image@ignition.msgs.Image ``` Now we start the ROS GUI: ``` # Shell C: -. /opt/ros/rolling/setup.bash +. /opt/ros/humble/setup.bash ros2 run rqt_image_view rqt_image_view /rgbd_camera/image ``` @@ -211,15 +194,15 @@ On terminal B, we start a ROS 2 listener: And terminal C, publish an Gazebo message: -`gz topic -t /chatter -m gz.msgs.StringMsg -p 'data:"Hello"'` +`ign topic -t /chatter -m ignition.msgs.StringMsg -p 'data:"Hello"'` At this point, you should see the ROS 2 listener echoing the message. Now let's try the other way around, ROS 2 -> Gazebo. -On terminal D, start an Gazebo listener: +On terminal D, start an Igntion listener: -`gz topic -e -t /chatter` +`ign topic -e -t /chatter` And on terminal E, publish a ROS 2 message: @@ -237,7 +220,7 @@ On terminal A, start the service bridge: On terminal B, start Gazebo, it will be paused by default: -`gz sim shapes.sdf` +`ign gazebo shapes.sdf` On terminal C, make a ROS request to unpause simulation: @@ -259,35 +242,35 @@ bridge may be specified: # Set just topic name, applies to both - topic_name: "chatter" ros_type_name: "std_msgs/msg/String" - gz_type_name: "gz.msgs.StringMsg" + gz_type_name: "ignition.msgs.StringMsg" # Set just ROS topic name, applies to both - ros_topic_name: "chatter_ros" ros_type_name: "std_msgs/msg/String" - gz_type_name: "gz.msgs.StringMsg" + gz_type_name: "ignition.msgs.StringMsg" # Set just GZ topic name, applies to both -- gz_topic_name: "chatter_gz" +- gz_topic_name: "chatter_ign" ros_type_name: "std_msgs/msg/String" - gz_type_name: "gz.msgs.StringMsg" + gz_type_name: "ignition.msgs.StringMsg" # Set each topic name explicitly - ros_topic_name: "chatter_both_ros" - gz_topic_name: "chatter_both_gz" + gz_topic_name: "chatter_both_ign" ros_type_name: "std_msgs/msg/String" - gz_type_name: "gz.msgs.StringMsg" + gz_type_name: "ignition.msgs.StringMsg" # Full set of configurations - ros_topic_name: "ros_chatter" - gz_topic_name: "gz_chatter" + gz_topic_name: "ign_chatter" ros_type_name: "std_msgs/msg/String" - gz_type_name: "gz.msgs.StringMsg" + gz_type_name: "ignition.msgs.StringMsg" subscriber_queue: 5 # Default 10 publisher_queue: 6 # Default 10 lazy: true # Default "false" direction: BIDIRECTIONAL # Default "BIDIRECTIONAL" - Bridge both directions - # "GZ_TO_ROS" - Bridge Gz topic to ROS - # "ROS_TO_GZ" - Bridge ROS topic to Gz + # "GZ_TO_ROS" - Bridge Ignition topic to ROS + # "ROS_TO_GZ" - Bridge ROS topic to Ignition ``` To run the bridge node with the above configuration: @@ -315,14 +298,14 @@ Now we start the Gazebo Transport listener. ```bash # Shell B: -gz topic -e -t /demo/chatter +ign topic -e -t /demo/chatter ``` Now we start the ROS talker. ```bash # Shell C: -. /opt/ros/rolling/setup.bash +. /opt/ros/humble/setup.bash ros2 topic pub /demo/chatter std_msgs/msg/String "data: 'Hi from inside of a namespace'" --once ``` diff --git a/ros_gz_bridge/bin/ros_gz_bridge_markdown_table b/ros_gz_bridge/bin/ros_gz_bridge_markdown_table index 6e5d12e47..05c11b726 100755 --- a/ros_gz_bridge/bin/ros_gz_bridge_markdown_table +++ b/ros_gz_bridge/bin/ros_gz_bridge_markdown_table @@ -37,11 +37,11 @@ def main(argv=sys.argv[1:]): msgs_ver = tuple(map(int, args.gz_msgs_ver.split('.'))) rows = [] - rows.append(f'| {"ROS type":35}| {"Gazebo Transport Type":35}|') - rows.append(f'|{"-":-<36}|:{"-":-<34}:|') + rows.append(f'| {"ROS type":32}| {"Gazebo Transport Type":32}|') + rows.append(f'|{"-":-<33}|:{"-":-<31}:|') for mapping in mappings(msgs_ver): - rows.append('| {:35}| {:35}|'.format( + rows.append('| {:32}| {:32}|'.format( mapping.ros2_package_name + '/msg/' + mapping.ros2_message_name, mapping.gz_string())) print('\n'.join(rows)) diff --git a/ros_gz_bridge/include/ros_gz_bridge/convert/geometry_msgs.hpp b/ros_gz_bridge/include/ros_gz_bridge/convert/geometry_msgs.hpp index 6416fa357..70537def2 100644 --- a/ros_gz_bridge/include/ros_gz_bridge/convert/geometry_msgs.hpp +++ b/ros_gz_bridge/include/ros_gz_bridge/convert/geometry_msgs.hpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -113,18 +112,6 @@ convert_ros_to_gz( const geometry_msgs::msg::PoseWithCovariance & ros_msg, gz::msgs::PoseWithCovariance & gz_msg); -template<> -void -convert_gz_to_ros( - const gz::msgs::PoseWithCovariance & gz_msg, - geometry_msgs::msg::PoseWithCovarianceStamped & ros_msg); - -template<> -void -convert_ros_to_gz( - const geometry_msgs::msg::PoseWithCovarianceStamped & ros_msg, - gz::msgs::PoseWithCovariance & gz_msg); - template<> void convert_gz_to_ros( diff --git a/ros_gz_bridge/include/ros_gz_bridge/convert/rcl_interfaces.hpp b/ros_gz_bridge/include/ros_gz_bridge/convert/rcl_interfaces.hpp index a81e35a7d..261849c5f 100644 --- a/ros_gz_bridge/include/ros_gz_bridge/convert/rcl_interfaces.hpp +++ b/ros_gz_bridge/include/ros_gz_bridge/convert/rcl_interfaces.hpp @@ -15,7 +15,7 @@ #ifndef ROS_GZ_BRIDGE__CONVERT__RCL_INTERFACES_HPP_ #define ROS_GZ_BRIDGE__CONVERT__RCL_INTERFACES_HPP_ -// GZ messages +// Ignition messages #include // ROS 2 messages 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 b49f02d7f..756ff2a88 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 @@ -22,11 +22,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include @@ -42,11 +40,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include @@ -54,8 +50,19 @@ #include #include +// Required for HAVE_DATAFRAME definition #include +#if HAVE_DATAFRAME +#include +#include +#endif // HAVE_DATAFRAME + +#if HAVE_MATERIALCOLOR +#include +#include +#endif // HAVE_MATERIALCOLOR + #include namespace ros_gz_bridge @@ -133,6 +140,7 @@ convert_gz_to_ros( const gz::msgs::Contacts & gz_msg, ros_gz_interfaces::msg::Contacts & ros_msg); +#if HAVE_DATAFRAME template<> void convert_ros_to_gz( @@ -144,6 +152,7 @@ void convert_gz_to_ros( const gz::msgs::Dataframe & ign_msg, ros_gz_interfaces::msg::Dataframe & ros_msg); +#endif // HAVE_DATAFRAME template<> void @@ -169,6 +178,7 @@ 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( @@ -180,6 +190,7 @@ void convert_gz_to_ros( const gz::msgs::MaterialColor & gz_msg, ros_gz_interfaces::msg::MaterialColor & ros_msg); +#endif // HAVE_MATERIALCOLOR template<> void diff --git a/ros_gz_bridge/include/ros_gz_bridge/convert/rosgraph_msgs.hpp b/ros_gz_bridge/include/ros_gz_bridge/convert/rosgraph_msgs.hpp index d4d7d5100..7916aaa73 100644 --- a/ros_gz_bridge/include/ros_gz_bridge/convert/rosgraph_msgs.hpp +++ b/ros_gz_bridge/include/ros_gz_bridge/convert/rosgraph_msgs.hpp @@ -16,7 +16,7 @@ #define ROS_GZ_BRIDGE__CONVERT__ROSGRAPH_MSGS_HPP_ // Gazebo Msgs -#include +#include // ROS 2 messages #include 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 735cdecce..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 @@ -24,6 +24,25 @@ #include #include "ros_gz_bridge/bridge_config.hpp" +// Dataframe is available from versions 8.4.0 (fortress) forward +// This can be removed when the minimum supported version passes 8.4.0 +#if (IGNITION_MSGS_MAJOR_VERSION > 8) || \ + ((IGNITION_MSGS_MAJOR_VERSION == 8) && (IGNITION_MSGS_MINOR_VERSION >= 4)) +#define HAVE_DATAFRAME true +#endif + +#if (GZ_MSGS_MAJOR_VERSION > 8) || \ + ((GZ_MSGS_MAJOR_VERSION == 8) && (GZ_MSGS_MINOR_VERSION >= 4)) +#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 @@ -37,7 +56,7 @@ class RosGzBridge : public rclcpp::Node /// \param[in] options options control creation of the ROS 2 node explicit RosGzBridge(const rclcpp::NodeOptions & options = rclcpp::NodeOptions()); - /// \brief Add a new ROS-GZ bridge to the node + /// \brief Add a new ROS-IGN bridge to the node /// \param[in] config Parameters to control creation of a new bridge void add_bridge(const BridgeConfig & config); diff --git a/ros_gz_bridge/launch/ros_gz_bridge.launch b/ros_gz_bridge/launch/ros_gz_bridge.launch deleted file mode 100644 index fa76221e8..000000000 --- a/ros_gz_bridge/launch/ros_gz_bridge.launch +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - diff --git a/ros_gz_bridge/launch/ros_gz_bridge.launch.py b/ros_gz_bridge/launch/ros_gz_bridge.launch.py deleted file mode 100644 index 3e52dee67..000000000 --- a/ros_gz_bridge/launch/ros_gz_bridge.launch.py +++ /dev/null @@ -1,147 +0,0 @@ -# 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. - -"""Launch ros_gz bridge in a component container.""" - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, GroupAction -from launch.conditions import IfCondition -from launch.substitutions import LaunchConfiguration, PythonExpression -from launch_ros.actions import ComposableNodeContainer, LoadComposableNodes, Node -from launch_ros.descriptions import ComposableNode - - -def generate_launch_description(): - - bridge_name = LaunchConfiguration('bridge_name') - config_file = LaunchConfiguration('config_file') - container_name = LaunchConfiguration('container_name') - create_own_container = LaunchConfiguration('create_own_container') - namespace = LaunchConfiguration('namespace') - use_composition = LaunchConfiguration('use_composition') - use_respawn = LaunchConfiguration('use_respawn') - log_level = LaunchConfiguration('log_level') - - declare_bridge_name_cmd = DeclareLaunchArgument( - 'bridge_name', description='Name of ros_gz_bridge node' - ) - - declare_config_file_cmd = DeclareLaunchArgument( - 'config_file', description='YAML config file' - ) - - declare_container_name_cmd = DeclareLaunchArgument( - 'container_name', - default_value='ros_gz_container', - description='Name of container that nodes will load in if use composition', - ) - - declare_create_own_container_cmd = DeclareLaunchArgument( - 'create_own_container', - default_value='False', - description='Whether the bridge should start its own ROS container when using composition \ - (not recommended). This option should only be set to true if you plan to put your ROS \ - node in the container created by the bridge. This is not needed if you want Gazebo and \ - the bridge to be in the same ROS container.', - ) - - declare_namespace_cmd = DeclareLaunchArgument( - 'namespace', default_value='', description='Top-level namespace' - ) - - declare_use_composition_cmd = DeclareLaunchArgument( - 'use_composition', default_value='False', description='Use composed bringup if True' - ) - - declare_use_respawn_cmd = DeclareLaunchArgument( - 'use_respawn', - default_value='False', - description='Whether to respawn if a node crashes. Applied when composition is disabled.', - ) - - declare_log_level_cmd = DeclareLaunchArgument( - 'log_level', default_value='info', description='log level' - ) - - load_nodes = GroupAction( - condition=IfCondition(PythonExpression(['not ', use_composition])), - actions=[ - Node( - package='ros_gz_bridge', - executable='bridge_node', - name=bridge_name, - namespace=namespace, - output='screen', - respawn=use_respawn, - respawn_delay=2.0, - parameters=[{'config_file': config_file}], - arguments=['--ros-args', '--log-level', log_level], - ), - ], - ) - - load_composable_nodes_with_container = ComposableNodeContainer( - condition=IfCondition( - PythonExpression([use_composition, ' and ', create_own_container])), - name=LaunchConfiguration('container_name'), - namespace='', - package='rclcpp_components', - executable='component_container', - composable_node_descriptions=[ - ComposableNode( - package='ros_gz_bridge', - plugin='ros_gz_bridge::RosGzBridge', - name=bridge_name, - namespace=namespace, - parameters=[{'config_file': config_file}], - extra_arguments=[{'use_intra_process_comms': True}], - ), - ], - output='screen', - ) - - load_composable_nodes_without_container = LoadComposableNodes( - condition=IfCondition( - PythonExpression([use_composition, ' and not ', create_own_container])), - target_container=container_name, - composable_node_descriptions=[ - ComposableNode( - package='ros_gz_bridge', - plugin='ros_gz_bridge::RosGzBridge', - name=bridge_name, - namespace=namespace, - parameters=[{'config_file': config_file}], - extra_arguments=[{'use_intra_process_comms': True}], - ), - ], - ) - - # Create the launch description and populate - ld = LaunchDescription() - - # Declare the launch options - ld.add_action(declare_bridge_name_cmd) - ld.add_action(declare_config_file_cmd) - ld.add_action(declare_container_name_cmd) - ld.add_action(declare_create_own_container_cmd) - ld.add_action(declare_namespace_cmd) - ld.add_action(declare_use_composition_cmd) - ld.add_action(declare_use_respawn_cmd) - ld.add_action(declare_log_level_cmd) - # Add the actions to launch all of the bridge nodes - ld.add_action(load_nodes) - ld.add_action(load_composable_nodes_with_container) - ld.add_action(load_composable_nodes_without_container) - - return ld diff --git a/ros_gz_bridge/package.xml b/ros_gz_bridge/package.xml index b04e59bc6..d7b85c512 100644 --- a/ros_gz_bridge/package.xml +++ b/ros_gz_bridge/package.xml @@ -2,27 +2,20 @@ ros_gz_bridge - 1.0.7 + 0.244.16 Bridge communication between ROS and Gazebo Transport - Aditya Pande - Alejandro Hernandez + Louise Poubel Apache 2.0 Shivesh Khaitan - Louise Poubel - Carlos Agüero ament_cmake - ament_cmake_python pkg-config - rosidl_pycommon actuator_msgs geometry_msgs gps_msgs - launch - launch_ros nav_msgs rclcpp rclcpp_components @@ -35,8 +28,20 @@ yaml_cpp_vendor vision_msgs - gz_msgs_vendor - gz_transport_vendor + + gz-msgs10 + gz-transport13 + + gz-msgs9 + gz-transport12 + + ignition-msgs8 + ignition-transport11 + ignition-msgs8 + ignition-transport11 + + ignition-msgs7 + ignition-transport10 ament_cmake_gtest ament_lint_auto diff --git a/ros_gz_bridge/resource/pkg_factories.cpp.em b/ros_gz_bridge/resource/pkg_factories.cpp.em index 2d9bcfaa0..ef81673e9 100644 --- a/ros_gz_bridge/resource/pkg_factories.cpp.em +++ b/ros_gz_bridge/resource/pkg_factories.cpp.em @@ -39,7 +39,7 @@ get_factory__@(ros2_package_name)( return std::make_shared< Factory< @(m.ros2_type()), - @(m.gz_type()) + @(m.ign_type()) > >("@(m.ros2_string())", "@(m.gz_string())"); } @@ -52,10 +52,10 @@ template<> void Factory< @(m.ros2_type()), - @(m.gz_type()) + @(m.ign_type()) >::convert_ros_to_gz( const @(m.ros2_type()) & ros_msg, - @(m.gz_type()) & gz_msg) + @(m.ign_type()) & gz_msg) { ros_gz_bridge::convert_ros_to_gz(ros_msg, gz_msg); } @@ -64,9 +64,9 @@ template<> void Factory< @(m.ros2_type()), - @(m.gz_type()) + @(m.ign_type()) >::convert_gz_to_ros( - const @(m.gz_type()) & gz_msg, + const @(m.ign_type()) & gz_msg, @(m.ros2_type()) & ros_msg) { ros_gz_bridge::convert_gz_to_ros(gz_msg, ros_msg); diff --git a/ros_gz_bridge/resource/ros_gz_bridge b/ros_gz_bridge/resource/ros_gz_bridge deleted file mode 100644 index e69de29bb..000000000 diff --git a/ros_gz_bridge/ros_gz_bridge/__init__.py b/ros_gz_bridge/ros_gz_bridge/__init__.py index f6ea05eed..ef021faf8 100644 --- a/ros_gz_bridge/ros_gz_bridge/__init__.py +++ b/ros_gz_bridge/ros_gz_bridge/__init__.py @@ -16,15 +16,9 @@ import os -from ros_gz_bridge.mappings import MAPPINGS +from ros_gz_bridge.mappings import MAPPINGS, MAPPINGS_10_1_0, MAPPINGS_8_4_0 -from rosidl_pycommon import expand_template - -from . import actions - -__all__ = [ - 'actions', -] +from rosidl_cmake import expand_template @dataclass @@ -72,6 +66,23 @@ def mappings(gz_msgs_ver): ros2_message_name=mapping.ros_type, gz_message_name=mapping.gz_type )) + + if gz_msgs_ver >= (8, 4, 0): + for (ros2_package_name, mappings) in MAPPINGS_8_4_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 + )) + 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/actions/__init__.py b/ros_gz_bridge/ros_gz_bridge/actions/__init__.py deleted file mode 100644 index 0a2c26657..000000000 --- a/ros_gz_bridge/ros_gz_bridge/actions/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# 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. - -"""Actions module.""" - -from .ros_gz_bridge import RosGzBridge - - -__all__ = [ - 'RosGzBridge', -] diff --git a/ros_gz_bridge/ros_gz_bridge/actions/ros_gz_bridge.py b/ros_gz_bridge/ros_gz_bridge/actions/ros_gz_bridge.py deleted file mode 100644 index 8459de4f9..000000000 --- a/ros_gz_bridge/ros_gz_bridge/actions/ros_gz_bridge.py +++ /dev/null @@ -1,160 +0,0 @@ -# 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. - -"""Module for the ros_gz bridge action.""" - -from typing import List -from typing import Optional - -from launch.action import Action -from launch.actions import IncludeLaunchDescription -from launch.frontend import Entity, expose_action, Parser -from launch.launch_context import LaunchContext -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.some_substitutions_type import SomeSubstitutionsType -from launch.substitutions import PathJoinSubstitution -from launch_ros.substitutions import FindPackageShare - - -@expose_action('ros_gz_bridge') -class RosGzBridge(Action): - """Action that executes a ros_gz bridge ROS [composable] node.""" - - def __init__( - self, - *, - bridge_name: SomeSubstitutionsType, - config_file: SomeSubstitutionsType, - container_name: Optional[SomeSubstitutionsType] = 'ros_gz_container', - create_own_container: Optional[SomeSubstitutionsType] = 'False', - namespace: Optional[SomeSubstitutionsType] = '', - use_composition: Optional[SomeSubstitutionsType] = 'False', - use_respawn: Optional[SomeSubstitutionsType] = 'False', - log_level: Optional[SomeSubstitutionsType] = 'info', - **kwargs - ) -> None: - """ - Construct a ros_gz bridge action. - - All arguments are forwarded to `ros_gz_bridge.launch.ros_gz_bridge.launch.py`, - so see the documentation of that class for further details. - - :param: bridge_name Name of ros_gz_bridge node - :param: config_file YAML config file. - :param: container_name Name of container that nodes will load in if use composition. - :param: create_own_container Whether to start a ROS container when using composition. - :param: namespace Top-level namespace. - :param: use_composition Use composed bringup if True. - :param: use_respawn Whether to respawn if a node crashes (when composition is disabled). - :param: log_level Log level. - """ - super().__init__(**kwargs) - self.__bridge_name = bridge_name - self.__config_file = config_file - self.__container_name = container_name - self.__create_own_container = create_own_container - self.__namespace = namespace - self.__use_composition = use_composition - self.__use_respawn = use_respawn - self.__log_level = log_level - - @classmethod - def parse(cls, entity: Entity, parser: Parser): - """Parse ros_gz_bridge.""" - _, kwargs = super().parse(entity, parser) - - bridge_name = entity.get_attr( - 'bridge_name', data_type=str, - optional=False) - - config_file = entity.get_attr( - 'config_file', data_type=str, - optional=False) - - container_name = entity.get_attr( - 'container_name', data_type=str, - optional=True) - - create_own_container = entity.get_attr( - 'create_own_container', data_type=str, - optional=True) - - namespace = entity.get_attr( - 'namespace', data_type=str, - optional=True) - - use_composition = entity.get_attr( - 'use_composition', data_type=str, - optional=True) - - use_respawn = entity.get_attr( - 'use_respawn', data_type=str, - optional=True) - - log_level = entity.get_attr( - 'log_level', data_type=str, - optional=True) - - if isinstance(bridge_name, str): - bridge_name = parser.parse_substitution(bridge_name) - kwargs['bridge_name'] = bridge_name - - if isinstance(config_file, str): - config_file = parser.parse_substitution(config_file) - kwargs['config_file'] = config_file - - if isinstance(container_name, str): - container_name = parser.parse_substitution(container_name) - kwargs['container_name'] = container_name - - if isinstance(create_own_container, str): - create_own_container = \ - parser.parse_substitution(create_own_container) - kwargs['create_own_container'] = create_own_container - - if isinstance(namespace, str): - namespace = parser.parse_substitution(namespace) - kwargs['namespace'] = namespace - - if isinstance(use_composition, str): - use_composition = parser.parse_substitution(use_composition) - kwargs['use_composition'] = use_composition - - if isinstance(use_respawn, str): - use_respawn = parser.parse_substitution(use_respawn) - kwargs['use_respawn'] = use_respawn - - if isinstance(log_level, str): - log_level = parser.parse_substitution(log_level) - kwargs['log_level'] = log_level - - return cls, kwargs - - def execute(self, context: LaunchContext) -> Optional[List[Action]]: - """Execute the action.""" - ros_gz_bridge_description = IncludeLaunchDescription( - PythonLaunchDescriptionSource( - [PathJoinSubstitution([FindPackageShare('ros_gz_bridge'), - 'launch', - 'ros_gz_bridge.launch.py'])]), - launch_arguments=[('bridge_name', self.__bridge_name), - ('config_file', self.__config_file), - ('container_name', self.__container_name), - ('create_own_container', self.__create_own_container), - ('namespace', self.__namespace), - ('use_composition', self.__use_composition), - ('use_respawn', self.__use_respawn), - ('log_level', self.__log_level), ]) - - return [ros_gz_bridge_description] diff --git a/ros_gz_bridge/ros_gz_bridge/mappings.py b/ros_gz_bridge/ros_gz_bridge/mappings.py index 203e33d1a..d5923ffaf 100644 --- a/ros_gz_bridge/ros_gz_bridge/mappings.py +++ b/ros_gz_bridge/ros_gz_bridge/mappings.py @@ -36,7 +36,6 @@ Mapping('PoseArray', 'Pose_V'), Mapping('PoseStamped', 'Pose'), Mapping('PoseWithCovariance', 'PoseWithCovariance'), - Mapping('PoseWithCovarianceStamped', 'PoseWithCovariance'), Mapping('Quaternion', 'Quaternion'), Mapping('Transform', 'Pose'), Mapping('TransformStamped', 'Pose'), @@ -62,14 +61,12 @@ Mapping('Altimeter', 'Altimeter'), Mapping('Contact', 'Contact'), Mapping('Contacts', 'Contacts'), - Mapping('Dataframe', 'Dataframe'), Mapping('Entity', 'Entity'), Mapping('EntityWrench', 'EntityWrench'), Mapping('Float32Array', 'Float_V'), Mapping('GuiCamera', 'GUICamera'), Mapping('JointWrench', 'JointWrench'), Mapping('Light', 'Light'), - Mapping('MaterialColor', 'MaterialColor'), Mapping('ParamVec', 'Param'), Mapping('ParamVec', 'Param_V'), Mapping('SensorNoise', 'SensorNoise'), @@ -117,3 +114,15 @@ Mapping('Detection3D', 'AnnotatedOriented3DBox'), ], } + +MAPPINGS_8_4_0 = { + 'ros_gz_interfaces': [ + Mapping('Dataframe', 'Dataframe'), + ], +} + +MAPPINGS_10_1_0 = { + 'ros_gz_interfaces': [ + Mapping('MaterialColor', 'MaterialColor'), + ], +} diff --git a/ros_gz_bridge/setup.cfg b/ros_gz_bridge/setup.cfg deleted file mode 100644 index 1f7078137..000000000 --- a/ros_gz_bridge/setup.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[options.entry_points] -launch.frontend.launch_extension = - ros_gz_bridge = ros_gz_bridge diff --git a/ros_gz_bridge/src/bridge_config.cpp b/ros_gz_bridge/src/bridge_config.cpp index d15e0dd83..52d649f76 100644 --- a/ros_gz_bridge/src/bridge_config.cpp +++ b/ros_gz_bridge/src/bridge_config.cpp @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include #include #include @@ -41,6 +40,12 @@ constexpr const char kBidirectional[] = "BIDIRECTIONAL"; constexpr const char kGzToRos[] = "GZ_TO_ROS"; constexpr const char kRosToGz[] = "ROS_TO_GZ"; +/// \TODO(mjcarroll) Remove these in releases past Humble/Garden +constexpr const char kIgnTypeName[] = "ign_type_name"; +constexpr const char kIgnTopicName[] = "ign_topic_name"; +constexpr const char kIgnToRos[] = "IGN_TO_ROS"; +constexpr const char kRosToIgn[] = "ROS_TO_IGN"; + /// \brief Parse a single sequence entry into a BridgeConfig /// \param[in] yaml_node A node containing a map of bridge config params /// \return BridgeConfig on success, nullopt on failure @@ -55,37 +60,43 @@ std::optional parseEntry(const YAML::Node & yaml_node) return {}; } - auto getValue = [yaml_node](const char * key) -> std::string - { - if (yaml_node[key]) { - return yaml_node[key].as(); - } else { - return ""; - } - }; - - const auto topic_name = getValue(kTopicName); - const auto ros_topic_name = getValue(kRosTopicName); - const auto ros_type_name = getValue(kRosTypeName); - const auto gz_topic_name = getValue(kGzTopicName); - const auto gz_type_name = getValue(kGzTypeName); - const auto direction = getValue(kDirection); - - if (!topic_name.empty() && !ros_topic_name.empty()) { + /// \TODO(mjcarroll) Remove gz_type_name logic in releases past Humble + std::string gz_type_name = ""; + if (yaml_node[kIgnTypeName] && !yaml_node[kGzTypeName]) { + gz_type_name = yaml_node[kIgnTypeName].as(); + RCLCPP_ERROR( + logger, + "%s is deprecated, migrate to %s", kIgnTypeName, kGzTypeName); + } else if (yaml_node[kGzTypeName]) { + gz_type_name = yaml_node[kGzTypeName].as(); + } + + /// \TODO(mjcarroll) Remove gz_topic_name logic in releases past Humble + std::string gz_topic_name = ""; + if (yaml_node[kIgnTopicName] && !yaml_node[kGzTopicName]) { + gz_topic_name = yaml_node[kIgnTopicName].as(); + RCLCPP_ERROR( + logger, + "%s is deprecated, migrate to %s", kIgnTopicName, kGzTopicName); + } else if (yaml_node[kGzTopicName]) { + gz_topic_name = yaml_node[kGzTopicName].as(); + } + + if (yaml_node[kTopicName] && yaml_node[kRosTopicName]) { RCLCPP_ERROR( logger, "Could not parse entry: %s and %s are mutually exclusive", kTopicName, kRosTopicName); return {}; } - if (!topic_name.empty() && !gz_topic_name.empty()) { + if (yaml_node[kTopicName] && !gz_topic_name.empty()) { RCLCPP_ERROR( logger, "Could not parse entry: %s and %s are mutually exclusive", kTopicName, kGzTopicName); return {}; } - if (ros_type_name.empty() || gz_type_name.empty()) { + if (!yaml_node[kRosTypeName] || gz_type_name.empty()) { RCLCPP_ERROR( logger, "Could not parse entry: both %s and %s must be set", kRosTypeName, kGzTypeName); @@ -97,40 +108,52 @@ std::optional parseEntry(const YAML::Node & yaml_node) ret.direction = BridgeDirection::BIDIRECTIONAL; if (yaml_node[kDirection]) { - if (direction == kBidirectional) { + auto dirStr = yaml_node[kDirection].as(); + + if (dirStr == kBidirectional) { ret.direction = BridgeDirection::BIDIRECTIONAL; - } else if (direction == kGzToRos) { + } else if (dirStr == kGzToRos) { ret.direction = BridgeDirection::GZ_TO_ROS; - } else if (direction == kRosToGz) { + } else if (dirStr == kRosToGz) { ret.direction = BridgeDirection::ROS_TO_GZ; + } else if (dirStr == kIgnToRos) { + ret.direction = BridgeDirection::GZ_TO_ROS; + RCLCPP_WARN( + logger, + "%s constant is deprecated, migrate to %s", kIgnToRos, kGzToRos); + } else if (dirStr == kRosToIgn) { + ret.direction = BridgeDirection::ROS_TO_GZ; + RCLCPP_WARN( + logger, + "%s constant is deprecated, migrate to %s", kRosToIgn, kRosToGz); } else { RCLCPP_ERROR( logger, - "Could not parse entry: invalid direction [%s]", direction.c_str()); + "Could not parse entry: invalid direction [%s]", dirStr.c_str()); return {}; } } - if (!topic_name.empty()) { + if (yaml_node[kTopicName]) { // Only "topic_name" is set - ret.gz_topic_name = topic_name; - ret.ros_topic_name = topic_name; - } else if (!ros_topic_name.empty() && gz_topic_name.empty()) { + ret.gz_topic_name = yaml_node[kTopicName].as(); + ret.ros_topic_name = yaml_node[kTopicName].as(); + } else if (yaml_node[kRosTopicName] && gz_topic_name.empty()) { // Only "ros_topic_name" is set - ret.gz_topic_name = ros_topic_name; - ret.ros_topic_name = ros_topic_name; - } else if (!gz_topic_name.empty() && ros_topic_name.empty()) { + ret.gz_topic_name = yaml_node[kRosTopicName].as(); + ret.ros_topic_name = yaml_node[kRosTopicName].as(); + } else if (!gz_topic_name.empty() && !yaml_node[kRosTopicName]) { // Only kGzTopicName is set ret.gz_topic_name = gz_topic_name; ret.ros_topic_name = gz_topic_name; } else { // Both are set ret.gz_topic_name = gz_topic_name; - ret.ros_topic_name = ros_topic_name; + ret.ros_topic_name = yaml_node[kRosTopicName].as(); } ret.gz_type_name = gz_type_name; - ret.ros_type_name = ros_type_name; + ret.ros_type_name = yaml_node[kRosTypeName].as(); if (yaml_node[kPublisherQueue]) { ret.publisher_queue_size = yaml_node[kPublisherQueue].as(); @@ -147,16 +170,16 @@ std::optional parseEntry(const YAML::Node & yaml_node) std::vector readFromYaml(std::istream & in) { - auto logger = rclcpp::get_logger("readFromYaml"); auto ret = std::vector(); YAML::Node yaml_node; yaml_node = YAML::Load(in); + auto logger = rclcpp::get_logger("readFromYaml"); if (!yaml_node.IsSequence()) { RCLCPP_ERROR( logger, - "Could not parse config: top level must be a YAML sequence"); + "Could not parse config, top level must be a YAML sequence"); return ret; } @@ -174,29 +197,6 @@ std::vector readFromYamlFile(const std::string & filename) { std::vector ret; std::ifstream in(filename); - - auto logger = rclcpp::get_logger("readFromYamlFile"); - if (!in.is_open()) { - RCLCPP_ERROR( - logger, - "Could not parse config: failed to open file [%s]", filename.c_str()); - return ret; - } - - // Compute file size to warn on empty configuration - const auto fbegin = in.tellg(); - in.seekg(0, std::ios::end); - const auto fend = in.tellg(); - const auto fsize = fend - fbegin; - - if (fsize == 0) { - RCLCPP_ERROR( - logger, - "Could not parse config: file empty [%s]", filename.c_str()); - return ret; - } - - in.seekg(0, std::ios::beg); return readFromYaml(in); } diff --git a/ros_gz_bridge/src/bridge_handle.cpp b/ros_gz_bridge/src/bridge_handle.cpp index eb8b1f906..c7851503d 100644 --- a/ros_gz_bridge/src/bridge_handle.cpp +++ b/ros_gz_bridge/src/bridge_handle.cpp @@ -30,8 +30,6 @@ BridgeHandle::BridgeHandle( config_(config), factory_(get_factory(config.ros_type_name, config.gz_type_name)) { - ros_node_->get_parameter("override_timestamps_with_wall_time", - override_timestamps_with_wall_time_); } BridgeHandle::~BridgeHandle() = default; diff --git a/ros_gz_bridge/src/bridge_handle.hpp b/ros_gz_bridge/src/bridge_handle.hpp index 777496ac9..d1751fb9e 100644 --- a/ros_gz_bridge/src/bridge_handle.hpp +++ b/ros_gz_bridge/src/bridge_handle.hpp @@ -101,9 +101,6 @@ class BridgeHandle /// \brief Typed factory used to create publishers/subscribers std::shared_ptr factory_; - - /// \brief Override the header.stamp field of the outgoing messages with the wall time - bool override_timestamps_with_wall_time_ = false; }; } // namespace ros_gz_bridge diff --git a/ros_gz_bridge/src/bridge_handle_gz_to_ros.cpp b/ros_gz_bridge/src/bridge_handle_gz_to_ros.cpp index ce90c8759..ddc292f8e 100644 --- a/ros_gz_bridge/src/bridge_handle_gz_to_ros.cpp +++ b/ros_gz_bridge/src/bridge_handle_gz_to_ros.cpp @@ -70,8 +70,7 @@ void BridgeHandleGzToRos::StartSubscriber() this->gz_node_, this->config_.gz_topic_name, this->config_.subscriber_queue_size, - this->ros_publisher_, - override_timestamps_with_wall_time_); + this->ros_publisher_); this->gz_subscriber_ = this->gz_node_; } diff --git a/ros_gz_bridge/src/convert/actuator_msgs.cpp b/ros_gz_bridge/src/convert/actuator_msgs.cpp index ddcdd2058..0c9bcde34 100644 --- a/ros_gz_bridge/src/convert/actuator_msgs.cpp +++ b/ros_gz_bridge/src/convert/actuator_msgs.cpp @@ -17,6 +17,7 @@ namespace ros_gz_bridge { + template<> void convert_ros_to_gz( diff --git a/ros_gz_bridge/src/convert/geometry_msgs.cpp b/ros_gz_bridge/src/convert/geometry_msgs.cpp index 01c782a58..ad83ba6dc 100644 --- a/ros_gz_bridge/src/convert/geometry_msgs.cpp +++ b/ros_gz_bridge/src/convert/geometry_msgs.cpp @@ -165,27 +165,6 @@ convert_gz_to_ros( } } -template<> -void -convert_ros_to_gz( - const geometry_msgs::msg::PoseWithCovarianceStamped & ros_msg, - gz::msgs::PoseWithCovariance & gz_msg) -{ - convert_ros_to_gz(ros_msg.header, (*gz_msg.mutable_pose()->mutable_header())); - convert_ros_to_gz(ros_msg.pose, gz_msg); -} - -template<> -void -convert_gz_to_ros( - const gz::msgs::PoseWithCovariance & gz_msg, - geometry_msgs::msg::PoseWithCovarianceStamped & ros_msg) -{ - convert_gz_to_ros(gz_msg.pose().header(), ros_msg.header); - convert_gz_to_ros(gz_msg, ros_msg.pose); -} - - template<> void convert_ros_to_gz( diff --git a/ros_gz_bridge/src/convert/gps_msgs.cpp b/ros_gz_bridge/src/convert/gps_msgs.cpp index cf2774d66..de8062394 100644 --- a/ros_gz_bridge/src/convert/gps_msgs.cpp +++ b/ros_gz_bridge/src/convert/gps_msgs.cpp @@ -55,7 +55,7 @@ convert_gz_to_ros( ros_msg.track = atan2(gz_msg.velocity_north(), gz_msg.velocity_east()); ros_msg.climb = gz_msg.velocity_up(); - // position_covariance is not supported in gz::msgs::NavSat. + // position_covariance is not supported in Ignition::Msgs::NavSat. ros_msg.position_covariance_type = gps_msgs::msg::GPSFix::COVARIANCE_TYPE_UNKNOWN; ros_msg.status.status = gps_msgs::msg::GPSStatus::STATUS_GBAS_FIX; } diff --git a/ros_gz_bridge/src/convert/ros_gz_interfaces.cpp b/ros_gz_bridge/src/convert/ros_gz_interfaces.cpp index eb923b344..57688b485 100644 --- a/ros_gz_bridge/src/convert/ros_gz_interfaces.cpp +++ b/ros_gz_bridge/src/convert/ros_gz_interfaces.cpp @@ -245,6 +245,7 @@ convert_gz_to_ros( } } +#if HAVE_DATAFRAME template<> void convert_ros_to_gz( @@ -294,6 +295,7 @@ convert_gz_to_ros( gz_msg.data().begin() + gz_msg.data().size(), ros_msg.data.begin()); } +#endif // HAVE_DATAFRAME template<> void @@ -400,6 +402,7 @@ convert_gz_to_ros( ros_msg.intensity = gz_msg.intensity(); } +#if HAVE_MATERIALCOLOR template<> void convert_ros_to_gz( @@ -457,6 +460,7 @@ convert_gz_to_ros( ros_msg.shininess = gz_msg.shininess(); } +#endif // HAVE_MATERIALCOLOR template<> void @@ -493,7 +497,9 @@ convert_gz_to_ros( ros_msg.type = 0; } else if (gz_msg.type() == gz::msgs::SensorNoise_Type::SensorNoise_Type_GAUSSIAN) { ros_msg.type = 2; - } else if (gz_msg.type() == gz::msgs::SensorNoise_Type::SensorNoise_Type_GAUSSIAN_QUANTIZED) { + } else if (gz_msg.type() == // NOLINT + gz::msgs::SensorNoise_Type::SensorNoise_Type_GAUSSIAN_QUANTIZED) // NOLINT + { // NOLINT ros_msg.type = 3; } diff --git a/ros_gz_bridge/src/convert/sensor_msgs.cpp b/ros_gz_bridge/src/convert/sensor_msgs.cpp index f537f9087..887f16d0f 100644 --- a/ros_gz_bridge/src/convert/sensor_msgs.cpp +++ b/ros_gz_bridge/src/convert/sensor_msgs.cpp @@ -166,11 +166,13 @@ convert_gz_to_ros( ros_msg.is_bigendian = false; ros_msg.step = ros_msg.width * num_channels * octets_per_channel; - ros_msg.data.resize(ros_msg.step * ros_msg.height); - // Prefer memcpy over std::copy for performance reasons, - // see https://github.com/gazebosim/ros_gz/pull/565 - memcpy(ros_msg.data.data(), gz_msg.data().c_str(), gz_msg.data().size()); + auto count = ros_msg.step * ros_msg.height; + ros_msg.data.resize(ros_msg.step * ros_msg.height); + std::copy( + gz_msg.data().begin(), + gz_msg.data().begin() + count, + ros_msg.data.begin()); } template<> @@ -518,7 +520,7 @@ convert_gz_to_ros( ros_msg.longitude = gz_msg.longitude_deg(); ros_msg.altitude = gz_msg.altitude(); - // position_covariance is not supported in gz::msgs::NavSat. + // position_covariance is not supported in Ignition::Msgs::NavSat. ros_msg.position_covariance_type = sensor_msgs::msg::NavSatFix::COVARIANCE_TYPE_UNKNOWN; ros_msg.status.status = sensor_msgs::msg::NavSatStatus::STATUS_FIX; } diff --git a/ros_gz_bridge/src/factory.hpp b/ros_gz_bridge/src/factory.hpp index 3a8b380b6..35daf4162 100644 --- a/ros_gz_bridge/src/factory.hpp +++ b/ros_gz_bridge/src/factory.hpp @@ -15,14 +15,11 @@ #ifndef FACTORY_HPP_ #define FACTORY_HPP_ -#include #include #include #include -#include #include -#include // include ROS 2 #include @@ -30,16 +27,6 @@ #include "factory_interface.hpp" -template -struct has_header : std::false_type -{ -}; - -template -struct has_header>: std::true_type -{ -}; - namespace ros_gz_bridge { @@ -65,11 +52,9 @@ class Factory : public FactoryInterface auto options = rclcpp::PublisherOptions(); options.qos_overriding_options = rclcpp::QosOverridingOptions { { - rclcpp::QosPolicyKind::Deadline, rclcpp::QosPolicyKind::Depth, rclcpp::QosPolicyKind::Durability, rclcpp::QosPolicyKind::History, - rclcpp::QosPolicyKind::Liveliness, rclcpp::QosPolicyKind::Reliability }, }; @@ -118,19 +103,20 @@ class Factory : public FactoryInterface std::shared_ptr node, const std::string & topic_name, size_t /*queue_size*/, - rclcpp::PublisherBase::SharedPtr ros_pub, - bool override_timestamps_with_wall_time) + rclcpp::PublisherBase::SharedPtr ros_pub) { - std::function subCb = - [this, ros_pub, override_timestamps_with_wall_time](const GZ_T & _msg) + std::function subCb = + [this, ros_pub](const GZ_T & _msg, + const gz::transport::MessageInfo & _info) { - this->gz_callback(_msg, ros_pub, override_timestamps_with_wall_time); + // Ignore messages that are published from this bridge. + if (!_info.IntraProcess()) { + this->gz_callback(_msg, ros_pub); + } }; - // Ignore messages that are published from this bridge. - gz::transport::SubscribeOptions opts; - opts.SetIgnoreLocalMessages(true); - node->Subscribe(topic_name, subCb, opts); + node->Subscribe(topic_name, subCb); } protected: @@ -154,20 +140,10 @@ class Factory : public FactoryInterface static void gz_callback( const GZ_T & gz_msg, - rclcpp::PublisherBase::SharedPtr ros_pub, - bool override_timestamps_with_wall_time) + rclcpp::PublisherBase::SharedPtr ros_pub) { ROS_T ros_msg; convert_gz_to_ros(gz_msg, ros_msg); - if constexpr (has_header::value) { - if (override_timestamps_with_wall_time) { - auto now = std::chrono::system_clock::now().time_since_epoch(); - auto ns = - std::chrono::duration_cast(now).count(); - ros_msg.header.stamp.sec = ns / 1e9; - ros_msg.header.stamp.nanosec = ns - ros_msg.header.stamp.sec * 1e9; - } - } std::shared_ptr> pub = std::dynamic_pointer_cast>(ros_pub); if (pub != nullptr) { diff --git a/ros_gz_bridge/src/factory_interface.hpp b/ros_gz_bridge/src/factory_interface.hpp index 3ea64ee8f..b760bcd87 100644 --- a/ros_gz_bridge/src/factory_interface.hpp +++ b/ros_gz_bridge/src/factory_interface.hpp @@ -60,8 +60,7 @@ class FactoryInterface std::shared_ptr node, const std::string & topic_name, size_t queue_size, - rclcpp::PublisherBase::SharedPtr ros_pub, - bool override_timestamps_with_wall_time) = 0; + rclcpp::PublisherBase::SharedPtr ros_pub) = 0; }; } // namespace ros_gz_bridge diff --git a/ros_gz_bridge/src/parameter_bridge.cpp b/ros_gz_bridge/src/parameter_bridge.cpp index d7c80dc46..12aac8ab6 100644 --- a/ros_gz_bridge/src/parameter_bridge.cpp +++ b/ros_gz_bridge/src/parameter_bridge.cpp @@ -46,19 +46,19 @@ void usage() "the ROS service will forward request to the Gazebo service and then forward\n" "the response back to the ROS client.\n\n" "A bidirectional bridge example:\n" << - " parameter_bridge /chatter@std_msgs/String@gz.msgs" << + " parameter_bridge /chatter@std_msgs/String@ignition.msgs" << ".StringMsg\n\n" << "A bridge from Gazebo to ROS example:\n" << - " parameter_bridge /chatter@std_msgs/String[gz.msgs" << + " parameter_bridge /chatter@std_msgs/String[ignition.msgs" << ".StringMsg\n\n" << "A bridge from ROS to Gazebo example:\n" << - " parameter_bridge /chatter@std_msgs/String]gz.msgs" << + " parameter_bridge /chatter@std_msgs/String]ignition.msgs" << ".StringMsg\n" << "A service bridge:\n" << " parameter_bridge /world/default/control@ros_gz_interfaces/srv/ControlWorld\n" << "Or equivalently:\n" << " parameter_bridge /world/default/control@ros_gz_interfaces/srv/ControlWorld@" - "gz.msgs.WorldControl@gz.msgs.Boolean\n" << std::endl; + "ignition.msgs.WorldControl@ignition.msgs.Boolean\n" << std::endl; } using RosGzBridge = ros_gz_bridge::RosGzBridge; diff --git a/ros_gz_bridge/src/ros_gz_bridge.cpp b/ros_gz_bridge/src/ros_gz_bridge.cpp index c3752d953..46a86d987 100644 --- a/ros_gz_bridge/src/ros_gz_bridge.cpp +++ b/ros_gz_bridge/src/ros_gz_bridge.cpp @@ -33,7 +33,6 @@ 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); - this->declare_parameter("override_timestamps_with_wall_time", false); int heartbeat; this->get_parameter("subscription_heartbeat", heartbeat); diff --git a/ros_gz_bridge/src/service_factories/ros_gz_interfaces.cpp b/ros_gz_bridge/src/service_factories/ros_gz_interfaces.cpp index a3c25a825..17fb52898 100644 --- a/ros_gz_bridge/src/service_factories/ros_gz_interfaces.cpp +++ b/ros_gz_bridge/src/service_factories/ros_gz_interfaces.cpp @@ -35,15 +35,15 @@ get_service_factory__ros_gz_interfaces( { if ( ros_type_name == "ros_gz_interfaces/srv/ControlWorld" && - (gz_req_type_name.empty() || gz_req_type_name == "gz.msgs.WorldControl") && - (gz_rep_type_name.empty() || gz_rep_type_name == "gz.msgs.Boolean")) + (gz_req_type_name.empty() || gz_req_type_name == "ignition.msgs.WorldControl") && + (gz_rep_type_name.empty() || gz_rep_type_name == "ignition.msgs.Boolean")) { return std::make_shared< ServiceFactory< ros_gz_interfaces::srv::ControlWorld, gz::msgs::WorldControl, gz::msgs::Boolean> - >(ros_type_name, "gz.msgs.WorldControl", "gz.msgs.Boolean"); + >(ros_type_name, "ignition.msgs.WorldControl", "ignition.msgs.Boolean"); } return nullptr; diff --git a/ros_gz_bridge/src/service_factory.hpp b/ros_gz_bridge/src/service_factory.hpp index 4a02503f1..f771d6180 100644 --- a/ros_gz_bridge/src/service_factory.hpp +++ b/ros_gz_bridge/src/service_factory.hpp @@ -35,7 +35,7 @@ template bool send_response_on_error(RosResT & ros_response); -template +template class ServiceFactory : public ServiceFactoryInterface { public: @@ -60,12 +60,12 @@ class ServiceFactory : public ServiceFactoryInterface std::shared_ptr reqid, std::shared_ptr ros_req) { - std::function callback; + std::function callback; callback = [ srv_handle = std::move(srv_handle), reqid ]( - const GzReplyT & reply, + const IgnReplyT & reply, const bool result) { typename RosServiceT::Response ros_res; @@ -77,7 +77,7 @@ class ServiceFactory : public ServiceFactoryInterface convert_gz_to_ros(reply, ros_res); srv_handle->send_response(*reqid, ros_res); }; - GzRequestT gz_req; + IgnRequestT gz_req; convert_ros_to_gz(*ros_req, gz_req); gz_node->Request( service_name, diff --git a/ros_gz_bridge/src/static_bridge.cpp b/ros_gz_bridge/src/static_bridge.cpp index 7692866a7..bf5cd38d6 100644 --- a/ros_gz_bridge/src/static_bridge.cpp +++ b/ros_gz_bridge/src/static_bridge.cpp @@ -37,7 +37,7 @@ int main(int argc, char * argv[]) config.ros_topic_name = "chatter"; config.gz_topic_name = "chatter"; config.ros_type_name = "std_msgs/msg/String"; - config.gz_type_name = "gz.msgs.StringMsg"; + config.gz_type_name = "ignition.msgs.StringMsg"; config.is_lazy = lazy_subscription; bridge_node->add_bridge(config); diff --git a/ros_gz_bridge/test/bridge_config.cpp b/ros_gz_bridge/test/bridge_config.cpp index 079878655..45ace8a5b 100644 --- a/ros_gz_bridge/test/bridge_config.cpp +++ b/ros_gz_bridge/test/bridge_config.cpp @@ -16,61 +16,56 @@ #include -#include "rcutils/logging.h" - -size_t g_log_calls = 0; - -struct LogEvent +TEST(BridgeConfig, Minimum) { - const rcutils_log_location_t * location; - int level; - std::string name; - rcutils_time_point_value_t timestamp; - std::string message; -}; -LogEvent g_last_log_event; + auto results = ros_gz_bridge::readFromYamlFile("test/config/minimum.yaml"); + EXPECT_EQ(4u, results.size()); -class BridgeConfig : public ::testing::Test -{ -public: - rcutils_logging_output_handler_t previous_output_handler; - void SetUp() { - g_log_calls = 0; - ASSERT_EQ(RCUTILS_RET_OK, rcutils_logging_initialize()); - rcutils_logging_set_default_logger_level(RCUTILS_LOG_SEVERITY_DEBUG); - - auto rcutils_logging_console_output_handler = []( - const rcutils_log_location_t * location, - int level, const char * name, rcutils_time_point_value_t timestamp, - const char * format, va_list * args) -> void - { - g_log_calls += 1; - g_last_log_event.location = location; - g_last_log_event.level = level; - g_last_log_event.name = name ? name : ""; - g_last_log_event.timestamp = timestamp; - char buffer[1024]; - vsnprintf(buffer, sizeof(buffer), format, *args); - g_last_log_event.message = buffer; - }; - - this->previous_output_handler = rcutils_logging_get_output_handler(); - rcutils_logging_set_output_handler(rcutils_logging_console_output_handler); + auto config = results[0]; + EXPECT_EQ("chatter", config.ros_topic_name); + EXPECT_EQ("chatter", config.gz_topic_name); + EXPECT_EQ("std_msgs/msg/String", config.ros_type_name); + EXPECT_EQ("ignition.msgs.StringMsg", config.gz_type_name); + EXPECT_EQ(ros_gz_bridge::kDefaultPublisherQueue, config.publisher_queue_size); + EXPECT_EQ(ros_gz_bridge::kDefaultSubscriberQueue, config.subscriber_queue_size); + EXPECT_EQ(ros_gz_bridge::kDefaultLazy, config.is_lazy); } - - void TearDown() { - rcutils_logging_set_output_handler(this->previous_output_handler); - ASSERT_EQ(RCUTILS_RET_OK, rcutils_logging_shutdown()); - EXPECT_FALSE(g_rcutils_logging_initialized); + auto config = results[1]; + EXPECT_EQ("chatter_ros", config.ros_topic_name); + EXPECT_EQ("chatter_ros", config.gz_topic_name); + EXPECT_EQ("std_msgs/msg/String", config.ros_type_name); + EXPECT_EQ("ignition.msgs.StringMsg", config.gz_type_name); + EXPECT_EQ(ros_gz_bridge::kDefaultPublisherQueue, config.publisher_queue_size); + EXPECT_EQ(ros_gz_bridge::kDefaultSubscriberQueue, config.subscriber_queue_size); + EXPECT_EQ(ros_gz_bridge::kDefaultLazy, config.is_lazy); } -}; - + { + auto config = results[2]; + EXPECT_EQ("chatter_gz", config.ros_topic_name); + EXPECT_EQ("chatter_gz", config.gz_topic_name); + EXPECT_EQ("std_msgs/msg/String", config.ros_type_name); + EXPECT_EQ("ignition.msgs.StringMsg", config.gz_type_name); + EXPECT_EQ(ros_gz_bridge::kDefaultPublisherQueue, config.publisher_queue_size); + EXPECT_EQ(ros_gz_bridge::kDefaultSubscriberQueue, config.subscriber_queue_size); + EXPECT_EQ(ros_gz_bridge::kDefaultLazy, config.is_lazy); + } + { + auto config = results[3]; + EXPECT_EQ("chatter_both_ros", config.ros_topic_name); + EXPECT_EQ("chatter_both_gz", config.gz_topic_name); + EXPECT_EQ("std_msgs/msg/String", config.ros_type_name); + EXPECT_EQ("ignition.msgs.StringMsg", config.gz_type_name); + EXPECT_EQ(ros_gz_bridge::kDefaultPublisherQueue, config.publisher_queue_size); + EXPECT_EQ(ros_gz_bridge::kDefaultSubscriberQueue, config.subscriber_queue_size); + EXPECT_EQ(ros_gz_bridge::kDefaultLazy, config.is_lazy); + } +} -TEST_F(BridgeConfig, Minimum) +TEST(BridgeConfig, MinimumIgn) { - auto results = ros_gz_bridge::readFromYamlFile("test/config/minimum.yaml"); + auto results = ros_gz_bridge::readFromYamlFile("test/config/minimum_ign.yaml"); EXPECT_EQ(4u, results.size()); { @@ -115,7 +110,38 @@ TEST_F(BridgeConfig, Minimum) } } -TEST_F(BridgeConfig, FullGz) + +TEST(BridgeConfig, FullGz) +{ + auto results = ros_gz_bridge::readFromYamlFile("test/config/full.yaml"); + EXPECT_EQ(2u, results.size()); + + { + auto config = results[0]; + EXPECT_EQ("ros_chatter", config.ros_topic_name); + EXPECT_EQ("gz_chatter", config.gz_topic_name); + EXPECT_EQ("std_msgs/msg/String", config.ros_type_name); + EXPECT_EQ("ignition.msgs.StringMsg", config.gz_type_name); + EXPECT_EQ(6u, config.publisher_queue_size); + EXPECT_EQ(5u, config.subscriber_queue_size); + EXPECT_EQ(true, config.is_lazy); + EXPECT_EQ(ros_gz_bridge::BridgeDirection::ROS_TO_GZ, config.direction); + } + + { + auto config = results[1]; + EXPECT_EQ("ros_chatter", config.ros_topic_name); + EXPECT_EQ("gz_chatter", config.gz_topic_name); + EXPECT_EQ("std_msgs/msg/String", config.ros_type_name); + EXPECT_EQ("ignition.msgs.StringMsg", config.gz_type_name); + EXPECT_EQ(20u, config.publisher_queue_size); + EXPECT_EQ(10u, config.subscriber_queue_size); + EXPECT_EQ(false, config.is_lazy); + EXPECT_EQ(ros_gz_bridge::BridgeDirection::GZ_TO_ROS, config.direction); + } +} + +TEST(BridgeConfig, FullIgn) { auto results = ros_gz_bridge::readFromYamlFile("test/config/full.yaml"); EXPECT_EQ(2u, results.size()); @@ -145,7 +171,7 @@ TEST_F(BridgeConfig, FullGz) } } -TEST_F(BridgeConfig, InvalidSetTwoRos) +TEST(BridgeConfig, InvalidSetTwoRos) { // Cannot set topic_name and ros_topic_name auto yaml = R"( @@ -154,12 +180,9 @@ TEST_F(BridgeConfig, InvalidSetTwoRos) auto results = ros_gz_bridge::readFromYamlString(yaml); EXPECT_EQ(0u, results.size()); - EXPECT_EQ( - "Could not parse entry: topic_name and ros_topic_name are mutually exclusive", - g_last_log_event.message); } -TEST_F(BridgeConfig, InvalidSetTwoGz) +TEST(BridgeConfig, InvalidSetTwoGz) { // Cannot set topic_name and gz_topic_name auto yaml = R"( @@ -168,12 +191,9 @@ TEST_F(BridgeConfig, InvalidSetTwoGz) auto results = ros_gz_bridge::readFromYamlString(yaml); EXPECT_EQ(0u, results.size()); - EXPECT_EQ( - "Could not parse entry: topic_name and gz_topic_name are mutually exclusive", - g_last_log_event.message); } -TEST_F(BridgeConfig, InvalidSetTypes) +TEST(BridgeConfig, InvalidSetTypes) { // Both ros_type_name and gz_type_name must be set auto yaml = R"( @@ -182,12 +202,9 @@ TEST_F(BridgeConfig, InvalidSetTypes) auto results = ros_gz_bridge::readFromYamlString(yaml); EXPECT_EQ(0u, results.size()); - EXPECT_EQ( - "Could not parse entry: both ros_type_name and gz_type_name must be set", - g_last_log_event.message); } -TEST_F(BridgeConfig, ParseDirection) +TEST(BridgeConfig, ParseDirection) { { // Check that default is bidirectional @@ -247,38 +264,10 @@ TEST_F(BridgeConfig, ParseDirection) - topic_name: foo ros_type_name: std_msgs/msg/String gz_type_name: ignition.msgs.StringMsg - direction: foobar + direction: asdfasdfasdfasdf )"; auto results = ros_gz_bridge::readFromYamlString(yaml); EXPECT_EQ(0u, results.size()); - EXPECT_EQ("Could not parse entry: invalid direction [foobar]", g_last_log_event.message); } } - -TEST_F(BridgeConfig, InvalidFileDoesntExist) -{ - auto results = ros_gz_bridge::readFromYamlFile("this/should/never/be/a/file.yaml"); - EXPECT_EQ(0u, results.size()); - EXPECT_EQ( - "Could not parse config: failed to open file [this/should/never/be/a/file.yaml]", - g_last_log_event.message); -} - -TEST_F(BridgeConfig, InvalidTopLevel) -{ - auto results = ros_gz_bridge::readFromYamlFile("test/config/invalid.yaml"); - EXPECT_EQ(0u, results.size()); - EXPECT_EQ( - "Could not parse config: top level must be a YAML sequence", - g_last_log_event.message); -} - -TEST_F(BridgeConfig, EmptyYAML) -{ - auto results = ros_gz_bridge::readFromYamlFile("test/config/empty.yaml"); - EXPECT_EQ(0u, results.size()); - EXPECT_EQ( - "Could not parse config: file empty [test/config/empty.yaml]", - g_last_log_event.message); -} diff --git a/ros_gz_bridge/test/config/empty.yaml b/ros_gz_bridge/test/config/empty.yaml deleted file mode 100644 index e69de29bb..000000000 diff --git a/ros_gz_bridge/test/config/full_ign.yaml b/ros_gz_bridge/test/config/full_ign.yaml new file mode 100644 index 000000000..fbee755bb --- /dev/null +++ b/ros_gz_bridge/test/config/full_ign.yaml @@ -0,0 +1,18 @@ +# Full set of configurations +- ros_topic_name: "ros_chatter" + ign_topic_name: "gz_chatter" + ros_type_name: "std_msgs/msg/String" + ign_type_name: "ignition.msgs.StringMsg" + subscriber_queue: 5 + publisher_queue: 6 + lazy: true + direction: ROS_TO_IGN + +- ros_topic_name: "ros_chatter" + ign_topic_name: "gz_chatter" + ros_type_name: "std_msgs/msg/String" + ign_type_name: "ignition.msgs.StringMsg" + subscriber_queue: 10 + publisher_queue: 20 + lazy: false + direction: IGN_TO_ROS diff --git a/ros_gz_bridge/test/config/invalid.yaml b/ros_gz_bridge/test/config/invalid.yaml deleted file mode 100644 index c14740add..000000000 --- a/ros_gz_bridge/test/config/invalid.yaml +++ /dev/null @@ -1,8 +0,0 @@ -ros_topic_name: "ros_chatter" -gz_topic_name: "gz_chatter" -ros_type_name: "std_msgs/msg/String" -gz_type_name: "ignition.msgs.StringMsg" -subscriber_queue: 5 -publisher_queue: 6 -lazy: true -direction: ROS_TO_GZ diff --git a/ros_gz_bridge/test/config/minimum_ign.yaml b/ros_gz_bridge/test/config/minimum_ign.yaml new file mode 100644 index 000000000..3fe8bc27b --- /dev/null +++ b/ros_gz_bridge/test/config/minimum_ign.yaml @@ -0,0 +1,20 @@ +# Set just topic name, applies to both +- topic_name: "chatter" + ros_type_name: "std_msgs/msg/String" + ign_type_name: "ignition.msgs.StringMsg" + +# Set just ROS topic name, applies to both +- ros_topic_name: "chatter_ros" + ros_type_name: "std_msgs/msg/String" + ign_type_name: "ignition.msgs.StringMsg" + +# Set just GZ topic name, applies to both +- gz_topic_name: "chatter_gz" + ros_type_name: "std_msgs/msg/String" + ign_type_name: "ignition.msgs.StringMsg" + +# Set each topic name explicitly +- ros_topic_name: "chatter_both_ros" + ign_topic_name: "chatter_both_gz" + ros_type_name: "std_msgs/msg/String" + ign_type_name: "ignition.msgs.StringMsg" diff --git a/ros_gz_bridge/test/resource/gz_publisher.cpp.em b/ros_gz_bridge/test/resource/gz_publisher.cpp.em index 07a1de912..9cccbfe0f 100644 --- a/ros_gz_bridge/test/resource/gz_publisher.cpp.em +++ b/ros_gz_bridge/test/resource/gz_publisher.cpp.em @@ -55,8 +55,8 @@ int main(int /*argc*/, char **/*argv*/) @[for m in mappings]@ // @(m.gz_string()). auto @(m.unique())_pub = - node.Advertise<@(m.gz_type())>("@(m.unique())"); - @(m.gz_type()) @(m.unique())_msg; + node.Advertise<@(m.ign_type())>("@(m.unique())"); + @(m.ign_type()) @(m.unique())_msg; ros_gz_bridge::testing::createTestMsg(@(m.unique())_msg); @[end for]@ diff --git a/ros_gz_bridge/test/resource/gz_subscriber.cpp.em b/ros_gz_bridge/test/resource/gz_subscriber.cpp.em index c6b899ed5..6edae074c 100644 --- a/ros_gz_bridge/test/resource/gz_subscriber.cpp.em +++ b/ros_gz_bridge/test/resource/gz_subscriber.cpp.em @@ -54,7 +54,7 @@ private: gz::transport::Node node; ///////////////////////////////////////////////// TEST(GzSubscriberTest, @(m.unique())) { - MyTestClass<@(m.gz_type())> client("@(m.unique())"); + MyTestClass<@(m.ign_type())> client("@(m.unique())"); using namespace std::chrono_literals; ros_gz_bridge::testing::waitUntilBoolVar( diff --git a/ros_gz_bridge/test/utils/gz_test_msg.cpp b/ros_gz_bridge/test/utils/gz_test_msg.cpp index b13a06b1c..a966b8f73 100644 --- a/ros_gz_bridge/test/utils/gz_test_msg.cpp +++ b/ros_gz_bridge/test/utils/gz_test_msg.cpp @@ -376,7 +376,6 @@ void createTestMsg(gz::msgs::PoseWithCovariance & _msg) { createTestMsg(*_msg.mutable_pose()->mutable_position()); createTestMsg(*_msg.mutable_pose()->mutable_orientation()); - createTestMsg(*_msg.mutable_pose()->mutable_header()); for (int i = 0; i < 36; i++) { _msg.mutable_covariance()->add_data(i); } @@ -641,6 +640,7 @@ void compareTestMsg(const std::shared_ptr & _msg) } } +#if HAVE_DATAFRAME void createTestMsg(gz::msgs::Dataframe & _msg) { gz::msgs::Header header_msg; @@ -678,6 +678,7 @@ void compareTestMsg(const std::shared_ptr & _msg) EXPECT_EQ(expected_msg.dst_address(), _msg->dst_address()); EXPECT_EQ(expected_msg.data(), _msg->data()); } +#endif // HAVE_DATAFRAME void createTestMsg(gz::msgs::Image & _msg) { @@ -1370,6 +1371,7 @@ 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()); @@ -1398,6 +1400,7 @@ void compareTestMsg(const std::shared_ptr & _msg) 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) { diff --git a/ros_gz_bridge/test/utils/gz_test_msg.hpp b/ros_gz_bridge/test/utils/gz_test_msg.hpp index cff1a0af8..c815a46d5 100644 --- a/ros_gz_bridge/test/utils/gz_test_msg.hpp +++ b/ros_gz_bridge/test/utils/gz_test_msg.hpp @@ -30,8 +30,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include @@ -72,6 +71,13 @@ #include #include +#if HAVE_DATAFRAME +#include +#endif // HAVE_DATAFRAME + +#if HAVE_MATERIALCOLOR +#include +#endif // HAVE_MATERIALCOLOR namespace ros_gz_bridge { @@ -165,6 +171,10 @@ void createTestMsg(gz::msgs::Clock & _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::StringMsg & _msg); + /// \brief Create a message used for testing. /// \param[out] _msg The message populated. void createTestMsg(gz::msgs::SensorNoise & _msg); @@ -173,10 +183,6 @@ void createTestMsg(gz::msgs::SensorNoise & _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::StringMsg & _msg); - /// \brief Compare a message with the populated for testing. /// \param[in] _msg The message to compare. void compareTestMsg(const std::shared_ptr & _msg); @@ -317,6 +323,7 @@ void createTestMsg(gz::msgs::Contacts & _msg); /// \param[in] _msg The message to compare. void compareTestMsg(const std::shared_ptr & _msg); +#if HAVE_DATAFRAME /// \brief Create a message used for testing. /// \param[out] _msg The message populated. void createTestMsg(gz::msgs::Dataframe & _msg); @@ -324,6 +331,7 @@ void createTestMsg(gz::msgs::Dataframe & _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_DATAFRAME /// \brief Create a message used for testing. /// \param[out] _msg The message populated. @@ -461,6 +469,7 @@ 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); @@ -468,6 +477,7 @@ 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. diff --git a/ros_gz_bridge/test/utils/ros_test_msg.cpp b/ros_gz_bridge/test/utils/ros_test_msg.cpp index 42f9b90b7..fcba33f43 100644 --- a/ros_gz_bridge/test/utils/ros_test_msg.cpp +++ b/ros_gz_bridge/test/utils/ros_test_msg.cpp @@ -58,14 +58,6 @@ void compareTestMsg(const std::shared_ptr & _msg) EXPECT_EQ(expected_msg.data, _msg->data); } -void createTestMsg(std_msgs::msg::ColorRGBA & _msg) -{ - _msg.r = 0.2; - _msg.g = 0.4; - _msg.b = 0.6; - _msg.a = 0.8; -} - void createTestMsg(actuator_msgs::msg::Actuators & _msg) { std_msgs::msg::Header header_msg; @@ -97,6 +89,14 @@ void compareTestMsg(const std::shared_ptr & _msg) } } +void createTestMsg(std_msgs::msg::ColorRGBA & _msg) +{ + _msg.r = 0.2; + _msg.g = 0.4; + _msg.b = 0.6; + _msg.a = 0.8; +} + void compareTestMsg(const std::shared_ptr & _msg) { std_msgs::msg::ColorRGBA expected_msg; @@ -381,18 +381,6 @@ void compareTestMsg(const std::shared_ptr & _msg) -{ - compareTestMsg(std::make_shared(_msg->pose)); - compareTestMsg(std::make_shared(_msg->header)); -} - void createTestMsg(geometry_msgs::msg::PoseStamped & _msg) { createTestMsg(_msg.header); @@ -622,6 +610,7 @@ 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); @@ -649,6 +638,7 @@ void compareTestMsg(const std::shared_ptr 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) { @@ -945,6 +935,7 @@ void compareTestMsg(const std::shared_ptr & _m } } +#if HAVE_DATAFRAME void createTestMsg(ros_gz_interfaces::msg::Dataframe & _msg) { createTestMsg(_msg.header); @@ -970,6 +961,7 @@ void compareTestMsg(const std::shared_ptr & _ EXPECT_EQ(expected_msg.data[ii], _msg->data[ii]); } } +#endif // HAVE_DATAFRAME void createTestMsg(nav_msgs::msg::Odometry & _msg) { diff --git a/ros_gz_bridge/test/utils/ros_test_msg.hpp b/ros_gz_bridge/test/utils/ros_test_msg.hpp index 682113055..5fea27586 100644 --- a/ros_gz_bridge/test/utils/ros_test_msg.hpp +++ b/ros_gz_bridge/test/utils/ros_test_msg.hpp @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include @@ -56,9 +55,13 @@ #include #include #include +#if HAVE_DATAFRAME #include +#endif // HAVE_DATAFRAME #include +#if HAVE_MATERIALCOLOR #include +#endif // HAVE_MATERIALCOLOR #include #include #include @@ -266,18 +269,6 @@ void createTestMsg(geometry_msgs::msg::PoseWithCovariance & _msg); /// \param[in] _msg The message to compare. void compareTestMsg(const std::shared_ptr & _msg); -/// \brief Compare a message with the populated for testing. -/// \param[in] _msg The message to compare. -void compareTestMsg(const geometry_msgs::msg::PoseWithCovarianceStamped & _msg); - -/// \brief Create a message used for testing. -/// \param[out] _msg The message populated. -void createTestMsg(geometry_msgs::msg::PoseWithCovarianceStamped & _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(geometry_msgs::msg::PoseStamped & _msg); @@ -410,6 +401,7 @@ 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); @@ -417,6 +409,7 @@ 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. @@ -450,6 +443,7 @@ void createTestMsg(ros_gz_interfaces::msg::Contacts & _msg); /// \param[in] _msg The message to compare. void compareTestMsg(const std::shared_ptr & _msg); +#if HAVE_DATAFRAME /// \brief Create a message used for testing. /// \param[out] _msg The message populated. void createTestMsg(ros_gz_interfaces::msg::Dataframe & _msg); @@ -457,6 +451,7 @@ void createTestMsg(ros_gz_interfaces::msg::Dataframe & _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_DATAFRAME /// \brief Create a message used for testing. /// \param[out] _msg The message populated. diff --git a/ros_gz_image/CHANGELOG.rst b/ros_gz_image/CHANGELOG.rst index 3906f3b9e..810b80e64 100644 --- a/ros_gz_image/CHANGELOG.rst +++ b/ros_gz_image/CHANGELOG.rst @@ -2,102 +2,26 @@ Changelog for package ros1_ign_image ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -1.0.7 (2024-11-08) ------------------- - -1.0.6 (2024-10-31) ------------------- +0.244.16 (2024-07-22) +--------------------- -1.0.5 (2024-10-14) ------------------- +0.244.15 (2024-07-03) +--------------------- -1.0.4 (2024-08-29) ------------------- +0.244.14 (2024-04-08) +--------------------- -1.0.3 (2024-07-22) ------------------- +0.244.13 (2024-01-23) +--------------------- -1.0.2 (2024-07-03) ------------------- -* Merge pull request `#569 `_ from azeey/iron_to_jazzy - Merge iron ➡️ jazzy -* Merge iron into jazzy -* Prepare for 1.0.0 Release (`#495 `_) -* Use gz_vendor packages (`#531 `_) -* 0.244.14 -* Changelog -* ign to gz (`#519 `_) -* 0.244.13 -* Changelog -* 0.244.12 -* Changelog -* 0.246.0 -* Update changelogs -* Add harmonic CI (`#447 `_) - * Add harmonic CI - * Include garden options - * Add harmonic stanza - * Additional message headers - --------- -* Port: humble to ros2 (`#386 `_) -* Merge branch 'humble' into mjcarroll/humble_to_ros2 -* Update maintainers (`#376 `_) -* Fix linter error by reordering headers (`#373 `_) -* Add QoS profile parameter to image bridge (`#335 `_) -* Fix double wait in ros_gz_bridge (`#347 `_) -* Humble ➡️ ROS2 (`#323 `_) - Humble ➡️ ROS2 -* Merge branch 'humble' into ports/humble_to_ros2 -* 0.245.0 -* Changelog -* humble to ros2 (`#311 `_) - Co-authored-by: Michael Carroll -* Merge remote-tracking branch 'origin/humble' into ahcorde/humble_to_ros2 -* Remove all ignition references on ROS 2 branch (`#302 `_) - * Remove all shims - * Update CMakeLists and package.xml for garden - * Complete garden gz renaming - * Drop fortress CI -* Contributors: Addisu Z. Taddese, Aditya Pande, Alejandro Hernández Cordero, Jose Luis Rivero, Michael Carroll, Sebastian Castro, ahcorde, ymd-stella - -1.0.0 (2024-04-24) ------------------- -* Use gz_vendor packages (`#531 `_) -* ign to gz (`#519 `_) +0.244.12 (2023-12-13) +--------------------- +* Add support for Harmonic/Humble pairing (`#462 `_) +* Fix double wait in ros_gz_bridge (`#347 `_) (`#450 `_) * Contributors: Addisu Z. Taddese, Alejandro Hernández Cordero -0.246.0 (2023-08-31) --------------------- -* Add harmonic CI (`#447 `_) - * Add harmonic CI - * Include garden options - * Add harmonic stanza - * Additional message headers - --------- -* Update maintainers (`#376 `_) -* Fix linter error by reordering headers (`#373 `_) -* Add QoS profile parameter to image bridge (`#335 `_) -* Fix double wait in ros_gz_bridge (`#347 `_) -* Humble ➡️ ROS2 (`#323 `_) -* Remove all ignition references on ROS 2 branch (`#302 `_) - * Remove all shims - * Update CMakeLists and package.xml for garden - * Complete garden gz renaming - * Drop fortress CI -* Contributors: Addisu Z. Taddese, Aditya Pande, Alejandro Hernández Cordero, Michael Carroll, Sebastian Castro, ahcorde, ymd-stella - -0.245.0 (2022-10-12) --------------------- -* humble to ros2 (`#311 `_) - Co-authored-by: Michael Carroll -* Merge remote-tracking branch 'origin/humble' into ahcorde/humble_to_ros2 -* Remove all ignition references on ROS 2 branch (`#302 `_) - * Remove all shims - * Update CMakeLists and package.xml for garden - * Complete garden gz renaming - * Drop fortress CI -* Contributors: Alejandro Hernández Cordero, Michael Carroll, ahcorde - +0.244.11 (2023-05-23) +--------------------- 0.244.10 (2023-05-03) --------------------- diff --git a/ros_gz_image/CMakeLists.txt b/ros_gz_image/CMakeLists.txt index e6e8de260..545b0b27d 100644 --- a/ros_gz_image/CMakeLists.txt +++ b/ros_gz_image/CMakeLists.txt @@ -16,11 +16,55 @@ find_package(ros_gz_bridge REQUIRED) find_package(rclcpp REQUIRED) find_package(sensor_msgs REQUIRED) -find_package(gz_transport_vendor REQUIRED) -find_package(gz-transport REQUIRED) +# TODO(CH3): Deprecated. Remove on tock. +if("$ENV{GZ_VERSION}" STREQUAL "" AND NOT "$ENV{IGNITION_VERSION}" STREQUAL "") + message(DEPRECATION "Environment variable [IGNITION_VERSION] is deprecated. Use [GZ_VERSION] instead.") + set(ENV{GZ_VERSION} $ENV{IGNITION_VERSION}) +endif() + +# Edifice +if("$ENV{GZ_VERSION}" STREQUAL "edifice") + find_package(ignition-transport10 REQUIRED) + set(GZ_TRANSPORT_VER ${ignition-transport10_VERSION_MAJOR}) + + find_package(ignition-msgs7 REQUIRED) + set(GZ_MSGS_VER ${ignition-msgs7_VERSION_MAJOR}) + + set(GZ_TARGET_PREFIX ignition) + + message(STATUS "Compiling against Ignition Edifice") +# Fortress +elseif("$ENV{GZ_VERSION}" STREQUAL "garden") + find_package(gz-transport12 REQUIRED) + set(GZ_TRANSPORT_VER ${gz-transport12_VERSION_MAJOR}) + + find_package(gz-msgs9 REQUIRED) + set(GZ_MSGS_VER ${gz-msgs9_VERSION_MAJOR}) + + set(GZ_TARGET_PREFIX gz) -find_package(gz_msgs_vendor REQUIRED) -find_package(gz-msgs REQUIRED) + message(STATUS "Compiling against Gazebo Garden") +elseif("$ENV{GZ_VERSION}" STREQUAL "harmonic") + find_package(gz-transport13 REQUIRED) + find_package(gz-msgs10 REQUIRED) + + set(GZ_TARGET_PREFIX gz) + set(GZ_MSGS_VER ${gz-msgs10_VERSION_MAJOR}) + set(GZ_TRANSPORT_VER ${gz-transport13_VERSION_MAJOR}) + + message(STATUS "Compiling against Gazebo Harmonic") +# Default to Fortress +else() + find_package(ignition-transport11 REQUIRED) + set(GZ_TRANSPORT_VER ${ignition-transport11_VERSION_MAJOR}) + + find_package(ignition-msgs8 REQUIRED) + set(GZ_MSGS_VER ${ignition-msgs8_VERSION_MAJOR}) + + set(GZ_TARGET_PREFIX ignition) + + message(STATUS "Compiling against Ignition Fortress") +endif() include_directories(include) @@ -33,8 +77,8 @@ add_executable(${executable} ) target_link_libraries(${executable} - gz-msgs::core - gz-transport::core + ${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}::core + ${GZ_TARGET_PREFIX}-transport${GZ_TRANSPORT_VER}::core ) ament_target_dependencies(${executable} @@ -68,8 +112,8 @@ if(BUILD_TESTING) # ) # target_link_libraries(${test_publisher}_image # ${catkin_LIBRARIES} - # $gz-msgs$::core - # $gz-transport::core + # ${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}::core + # ${GZ_TARGET_PREFIX}-transport${GZ_TRANSPORT_VER}::core # gtest # gtest_main # ) @@ -81,8 +125,8 @@ if(BUILD_TESTING) # test/subscribers/${test_subscriber}.cpp) # target_link_libraries(test_${test_subscriber}_image # ${catkin_LIBRARIES} - # $gz-msgs::core - # $gz-transport::core + # ${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}::core + # ${GZ_TARGET_PREFIX}-transport${GZ_TRANSPORT_VER}::core # ) # endforeach(test_subscriber) endif() diff --git a/ros_gz_image/README.md b/ros_gz_image/README.md index a6c9c5e51..b7b27e1fd 100644 --- a/ros_gz_image/README.md +++ b/ros_gz_image/README.md @@ -1,7 +1,7 @@ # Image utilities for using ROS and Gazebo Transport This package provides a unidirectional bridge for images from Gazebo to ROS. -The bridge subscribes to Gazebo image messages (`gz::msgs::Image`) +The bridge subscribes to Gazebo image messages (`ignition::msgs::Image`) and republishes them to ROS using [image_transport](http://wiki.ros.org/image_transport). For compressed images, install @@ -9,14 +9,3 @@ For compressed images, install and the bridge will publish `/compressed` images. The same goes for other `image_transport` plugins. -To run the bridge from the command line: - -```shell -ros2 run ros_gz_image image_bridge /topic1 /topic2 -``` - -You can also modify the [Quality of Service (QoS) policy](https://docs.ros.org/en/rolling/Concepts/About-Quality-of-Service-Settings.html#qos-policies) used to publish images using an additional `qos` ROS parameter. For example: - -```shell -ros2 run ros_gz_image image_bridge /topic1 /topic2 --ros-args qos:=sensor_data -``` diff --git a/ros_gz_image/package.xml b/ros_gz_image/package.xml index e17e83467..2a367440c 100644 --- a/ros_gz_image/package.xml +++ b/ros_gz_image/package.xml @@ -1,12 +1,9 @@ ros_gz_image - 1.0.7 + 0.244.16 Image utilities for Gazebo simulation with ROS. Apache 2.0 - Aditya Pande - Alejandro Hernandez - - Louise Poubel + Louise Poubel ament_cmake pkg-config @@ -16,8 +13,20 @@ rclcpp sensor_msgs - gz_msgs_vendor - gz_transport_vendor + + gz-msgs10 + gz-transport13 + + gz-msgs9 + gz-transport12 + + ignition-msgs8 + ignition-transport11 + ignition-msgs8 + ignition-transport11 + + ignition-msgs7 + ignition-transport10 ament_lint_auto ament_lint_common diff --git a/ros_gz_image/src/image_bridge.cpp b/ros_gz_image/src/image_bridge.cpp index db21f42c6..c167c67c4 100644 --- a/ros_gz_image/src/image_bridge.cpp +++ b/ros_gz_image/src/image_bridge.cpp @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include #include #include @@ -20,42 +19,25 @@ #include #include -#include #include +#include #include ////////////////////////////////////////////////// -/// \brief Bridges one image topic +/// \brief Bridges one topic class Handler { public: /// \brief Constructor /// \param[in] _topic Image base topic - /// \param[in] _node Pointer to ROS node + /// \param[in] _it_node Pointer to image transport node /// \param[in] _gz_node Pointer to Gazebo node Handler( const std::string & _topic, - std::shared_ptr _node, + std::shared_ptr _it_node, std::shared_ptr _gz_node) { - // Get QoS profile from parameter - rmw_qos_profile_t qos_profile = rmw_qos_profile_default; - const auto qos_str = - _node->get_parameter("qos").get_parameter_value().get(); - if (qos_str == "system_default") { - qos_profile = rmw_qos_profile_system_default; - } else if (qos_str == "sensor_data") { - qos_profile = rmw_qos_profile_sensor_data; - } else if (qos_str != "default") { - RCLCPP_ERROR( - _node->get_logger(), - "Invalid QoS profile %s specified. Using default profile.", - qos_str.c_str()); - } - - // Create publishers and subscribers - this->ros_pub = image_transport::create_publisher( - _node.get(), _topic, qos_profile); + this->ros_pub = _it_node->advertise(_topic, 1); _gz_node->Subscribe(_topic, &Handler::OnImage, this); } @@ -95,7 +77,7 @@ int main(int argc, char * argv[]) // ROS node auto node_ = rclcpp::Node::make_shared("ros_gz_image"); - node_->declare_parameter("qos", "default"); + auto it_node = std::make_shared(node_); // Gazebo node auto gz_node = std::make_shared(); @@ -109,7 +91,7 @@ int main(int argc, char * argv[]) // Create publishers and subscribers for (auto topic : args) { - handlers.push_back(std::make_shared(topic, node_, gz_node)); + handlers.push_back(std::make_shared(topic, it_node, gz_node)); } // Spin ROS and Gz until shutdown diff --git a/ros_gz_interfaces/CHANGELOG.rst b/ros_gz_interfaces/CHANGELOG.rst index 07ef6c816..6f201eb58 100644 --- a/ros_gz_interfaces/CHANGELOG.rst +++ b/ros_gz_interfaces/CHANGELOG.rst @@ -2,37 +2,29 @@ Changelog for package ros_gz_interfaces ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -1.0.7 (2024-11-08) ------------------- - -1.0.6 (2024-10-31) ------------------- - -1.0.5 (2024-10-14) ------------------- - -1.0.4 (2024-08-29) ------------------- +0.244.16 (2024-07-22) +--------------------- -1.0.3 (2024-07-22) ------------------- -* Add support for gz.msgs.EntityWrench (base branch: ros2) (`#573 `_) (`#574 `_) +0.244.15 (2024-07-03) +--------------------- +* Add support for gz.msgs.EntityWrench (base branch: ros2) (backport `#573 `_) (`#575 `_) + * Add support for gz.msgs.EntityWrench (base branch: ros2) (`#573 `_) (cherry picked from commit f9afb69d1163633dd978024bb7271a28cf7b551a) + # Conflicts: + # ros_gz_bridge/README.md + # ros_gz_bridge/test/utils/gz_test_msg.hpp + * Fixed merge + * Update ros_gz_bridge/README.md + Co-authored-by: Addisu Z. Taddese + --------- Co-authored-by: Victor T. Noppeney + Co-authored-by: Alejandro Hernández Cordero + Co-authored-by: Addisu Z. Taddese * Contributors: mergify[bot] -1.0.2 (2024-07-03) ------------------- -* Add option to change material color from ROS. (`#521 `_) - Forward port of `#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. - (cherry picked from commit 78dc4823121f085594e6028a93f1e571eb04f58b) -* Prepare for 1.0.0 Release (`#495 `_) -* 0.244.14 -* Changelog -* Add option to change material color from ROS. (`#486 `_) +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. @@ -40,49 +32,19 @@ Changelog for package ros_gz_interfaces Co-authored-by: Alejandro Hernández Cordero Co-authored-by: Addisu Z. Taddese Co-authored-by: Addisu Z. Taddese -* 0.244.13 -* Changelog -* 0.244.12 -* Changelog -* 0.246.0 -* Update changelogs -* SensorNoise msg bridging (`#417 `_) -* Added Altimeter msg bridging (`#413 `_) -* Port: humble to ros2 (`#386 `_) -* Merge branch 'humble' into mjcarroll/humble_to_ros2 -* Update maintainers (`#376 `_) -* Humble ➡️ ROS2 (`#323 `_) - Humble ➡️ ROS2 -* Merge branch 'humble' into ports/humble_to_ros2 -* Export rcl_interfaces exec dependency (`#317 `_) -* 0.245.0 -* Changelog -* humble to ros2 (`#311 `_) - Co-authored-by: Michael Carroll -* Merge remote-tracking branch 'origin/humble' into ahcorde/humble_to_ros2 -* Contributors: Addisu Z. Taddese, Aditya Pande, Alejandro Hernández Cordero, Benjamin Perseghetti, Jose Luis Rivero, Michael Carroll, ahcorde - -1.0.0 (2024-04-24) ------------------- - -0.246.0 (2023-08-31) --------------------- -* SensorNoise msg bridging (`#417 `_) -* Added Altimeter msg bridging (`#413 `_) -* Port: humble to ros2 (`#386 `_) -* Merge branch 'humble' into mjcarroll/humble_to_ros2 -* Update maintainers (`#376 `_) -* Humble ➡️ ROS2 (`#323 `_) -* Export rcl_interfaces exec dependency (`#317 `_) -* Contributors: Aditya Pande, Alejandro Hernández Cordero, Michael Carroll, ahcorde +* Contributors: Benjamin Perseghetti -0.245.0 (2022-10-12) --------------------- -* humble to ros2 (`#311 `_) - Co-authored-by: Michael Carroll -* Merge remote-tracking branch 'origin/humble' into ahcorde/humble_to_ros2 -* Contributors: Alejandro Hernández Cordero, ahcorde +0.244.13 (2024-01-23) +--------------------- +0.244.12 (2023-12-13) +--------------------- +* [backport humble] SensorNoise msg bridging (`#417 `_) +* [backport humble] Added Altimeter msg bridging (`#413 `_) (`#414 `_) (`#426 `_) +* Contributors: Alejandro Hernández Cordero + +0.244.11 (2023-05-23) +--------------------- 0.244.10 (2023-05-03) --------------------- diff --git a/ros_gz_interfaces/package.xml b/ros_gz_interfaces/package.xml index 9297df156..464d3ff55 100644 --- a/ros_gz_interfaces/package.xml +++ b/ros_gz_interfaces/package.xml @@ -1,12 +1,10 @@ ros_gz_interfaces - 1.0.7 + 0.244.16 Message and service data structures for interacting with Gazebo from ROS2. Apache 2.0 - Louise Poubel + Louise Poubel Zhenpeng Ge - Aditya Pande - Alejandro Hernandez ament_cmake rosidl_default_generators diff --git a/ros_gz_point_cloud/examples/depth_camera.sdf b/ros_gz_point_cloud/examples/depth_camera.sdf index 85beba43f..574d0ce44 100644 --- a/ros_gz_point_cloud/examples/depth_camera.sdf +++ b/ros_gz_point_cloud/examples/depth_camera.sdf @@ -15,7 +15,7 @@ 3. Load the example world, unpaused: - gz sim -r examples/depth_camera.sdf + ign gazebo -r examples/depth_camera.sdf 4. Launch RViz to visualize the point cloud: diff --git a/ros_gz_point_cloud/examples/gpu_lidar.sdf b/ros_gz_point_cloud/examples/gpu_lidar.sdf index 3feed57a4..7bd3a7354 100644 --- a/ros_gz_point_cloud/examples/gpu_lidar.sdf +++ b/ros_gz_point_cloud/examples/gpu_lidar.sdf @@ -15,7 +15,7 @@ 3. Load this world, unpaused: - gz sim -r examples/gpu_lidar.sdf + ign gazebo -r examples/gpu_lidar.sdf 4. Launch RViz to visualize the point cloud: diff --git a/ros_gz_point_cloud/examples/rgbd_camera.sdf b/ros_gz_point_cloud/examples/rgbd_camera.sdf index 235cb2255..64a77b6a9 100644 --- a/ros_gz_point_cloud/examples/rgbd_camera.sdf +++ b/ros_gz_point_cloud/examples/rgbd_camera.sdf @@ -16,7 +16,7 @@ 3. Load the example world, unpaused: - gz sim -r examples/rgbd_camera.sdf + ign gazebo -r examples/rgbd_camera.sdf 4. Launch RViz to visualize the point cloud: diff --git a/ros_gz_point_cloud/package.xml b/ros_gz_point_cloud/package.xml index 9256c482a..b3d5717e5 100644 --- a/ros_gz_point_cloud/package.xml +++ b/ros_gz_point_cloud/package.xml @@ -3,10 +3,7 @@ 0.7.0 Point cloud utilities for Gazebo simulation with ROS. Apache 2.0 - Aditya Pande - Alejandro Hernandez - - Louise Poubel + Louise Poubel catkin diff --git a/ros_gz_sim/CHANGELOG.rst b/ros_gz_sim/CHANGELOG.rst index a02a63c62..e6a5d773e 100644 --- a/ros_gz_sim/CHANGELOG.rst +++ b/ros_gz_sim/CHANGELOG.rst @@ -2,86 +2,15 @@ Changelog for package ros_gz_sim ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -1.0.7 (2024-11-08) ------------------- -* Bugfix: `if "false"` is always `True` (`#617 `_) (`#640 `_) - There is an issue in this launch file when passing the string 'false' as - an argument. In Python, non-empty strings are always evaluated as True, - regardless of their content. This means that even if you pass 'false', - the system will still evaluate it as True. - This bug results in the launch system incorrectly calling the OnShutdown - method twice. When any ROS launch action invokes a RosAdapter, it - triggers the following exception: "Cannot shutdown a ROS adapter that is - not running." - To temporarily work around this issue, you can launch gz_sim_launch.py - with the on_exit_shutdown argument set to an empty string. This prevents - the erroneous shutdown sequence and avoids the associated exception. - (cherry picked from commit 1e30af0105058d68c8f1c98f37904505f613cf97) - Co-authored-by: Ignacio Vizzo -* Contributors: mergify[bot] - -1.0.6 (2024-10-31) ------------------- -* Create ros_gz_spawn_model.launch (`#604 `_) (`#627 `_) - Co-authored-by: Alejandro Hernández Cordero - (cherry picked from commit 02faa5b2383df27aa36321766c940924febdc5c6) - Co-authored-by: Aarav Gupta -* Add create_own_container argument to ros_gz_spawn_model.launch.py (`#622 `_) (`#625 `_) - (cherry picked from commit e8a3ec5404c71e75468d6ebf233d9cab09715b36) - Co-authored-by: Amronos <134804732+Amronos@users.noreply.github.com> -* Fix ros_gz_sim.launch.py when create_own_container is enabled. (`#620 `_) (`#621 `_) - (cherry picked from commit a4d00a90a1a5f753a0d0fbfe8e1e142540aebadd) - Co-authored-by: Carlos Agüero -* Extra parameter to start a container (`#616 `_) (`#618 `_) - (cherry picked from commit 8115ccaaedea718841367eb64e500e13df392fd7) - Co-authored-by: Carlos Agüero -* Contributors: mergify[bot] - -1.0.5 (2024-10-14) ------------------- -* Merge pull request `#607 `_ from Amronos/ros2-jazzy-backport -* Fix changelogs and versions -* Name gazebo sim node (`#611 `_) (`#612 `_) -* Change world_string to model_string in gz_spawn_model files (`#606 `_) -* Use model string in ros_gz_spawn_model.launch.py (`#605 `_) -* Remove default_value for required arguments (`#602 `_) -* Fix errors with name of bridge not being given (`#600 `_) -* Restore launch file (`#603 `_) -* Use optional parameters in actions (`#601 `_) -* Wait for create service to be available. (`#588 `_) -* Update launch files with name parameter (`#556 `_) -* Launch gz_spawn_model from xml (`#551 `_) -* Launch ros_gz_bridge from xml (`#550 `_) -* Launch gzserver and the bridge as composable nodes (`#528 `_) -* Name gazebo sim node (`#611 `_) (`#612 `_) -* Contributors: Aarav Gupta, Addisu Z. Taddese, Alejandro Hernández Cordero, Amronos, Carlos Agüero, Sebastian Kasperski, mergify[bot] - -1.0.4 (2024-08-29) ------------------- +0.244.16 (2024-07-22) +--------------------- -1.0.3 (2024-07-22) ------------------- +0.244.15 (2024-07-03) +--------------------- -1.0.2 (2024-07-03) ------------------- -* Merge pull request `#569 `_ from azeey/iron_to_jazzy - Merge iron ➡️ jazzy -* Merge remote-tracking branch 'origin/jazzy' into iron_to_jazzy -* Add a ROS node that runs Gazebo (`#500 `_) (`#567 `_) - * Add gzserver with ability to load an SDF file or string - --------- - (cherry picked from commit 92a2891f4adf35e4a4119aca2447dee93e22a06a) - Co-authored-by: Addisu Z. Taddese -* Merge iron into jazzy -* Merge pull request `#564 `_ from azeey/humble_to_iron - Humble ➡️ Iron -* Merge humble -> iron -* Prepare for 1.0.0 Release (`#495 `_) -* Use gz_vendor packages (`#531 `_) -* 0.244.14 -* Changelog -* ign to gz (`#519 `_) -* Support `` in `package.xml` exports (`#492 `_) +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`. ``` @@ -93,89 +22,19 @@ Changelog for package ros_gz_sim 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` --------- -* Undeprecate use of commandline flags (`#491 `_) -* 0.244.13 -* Changelog -* Remove deprecations using ros_gz_sim_create (`#476 `_) -* Added support for using ROS 2 parameters to spawn entities in Gazebo using ros_gz_sim::create (`#475 `_) -* Fix bug in `create` where command line arguments were truncated (`#472 `_) -* 0.244.12 -* Changelog -* Filter ROS arguments before gflags parsing (`#453 `_) -* 0.246.0 -* Update changelogs -* Add harmonic CI (`#447 `_) - * Add harmonic CI - * Include garden options - * Add harmonic stanza - * Additional message headers - --------- -* Replace deprecated ign_find_package with gz_find_package (`#432 `_) - Co-authored-by: jmackay2 -* Port: humble to ros2 (`#386 `_) -* Merge branch 'humble' into mjcarroll/humble_to_ros2 -* Update maintainers (`#376 `_) -* set on_exit_shutdown argument for gz-sim ExecuteProcess (`#355 `_) -* Humble ➡️ ROS2 (`#323 `_) - Humble ➡️ ROS2 -* Merge branch 'humble' into ports/humble_to_ros2 -* 0.245.0 -* Changelog -* humble to ros2 (`#311 `_) - Co-authored-by: Michael Carroll -* Merge remote-tracking branch 'origin/humble' into ahcorde/humble_to_ros2 -* Remove all ignition references on ROS 2 branch (`#302 `_) - * Remove all shims - * Update CMakeLists and package.xml for garden - * Complete garden gz renaming - * Drop fortress CI -* Contributors: Addisu Z. Taddese, Aditya Pande, Alejandro Hernández Cordero, Ayush Singh, Jose Luis Rivero, Michael Carroll, ahcorde, andermi, jmackay2, mergify[bot] +* Contributors: Addisu Z. Taddese -1.0.0 (2024-04-24) ------------------- -* Use gz_vendor packages (`#531 `_) -* ign to gz (`#519 `_) -* Undeprecate use of commandline flags (`#491 `_) -* Remove deprecations using ros_gz_sim_create (`#476 `_) -* Added support for using ROS 2 parameters to spawn entities in Gazebo using ros_gz_sim::create (`#475 `_) -* Fix bug in `create` where command line arguments were truncated (`#472 `_) -* Filter ROS arguments before gflags parsing (`#453 `_) -* Contributors: Addisu Z. Taddese, Alejandro Hernández Cordero, Ayush Singh, Michael Carroll - -0.246.0 (2023-08-31) --------------------- -* Add harmonic CI (`#447 `_) - * Add harmonic CI - * Include garden options - * Add harmonic stanza - * Additional message headers - --------- -* Replace deprecated ign_find_package with gz_find_package (`#432 `_) - Co-authored-by: jmackay2 -* Port: humble to ros2 (`#386 `_) -* Merge branch 'humble' into mjcarroll/humble_to_ros2 -* Update maintainers (`#376 `_) -* set on_exit_shutdown argument for gz-sim ExecuteProcess (`#355 `_) -* Humble ➡️ ROS2 (`#323 `_) -* Remove all ignition references on ROS 2 branch (`#302 `_) - * Remove all shims - * Update CMakeLists and package.xml for garden - * Complete garden gz renaming - * Drop fortress CI -* Contributors: Aditya Pande, Alejandro Hernández Cordero, Michael Carroll, ahcorde, andermi, jmackay2 +0.244.13 (2024-01-23) +--------------------- -0.245.0 (2022-10-12) --------------------- -* humble to ros2 (`#311 `_) - Co-authored-by: Michael Carroll -* Merge remote-tracking branch 'origin/humble' into ahcorde/humble_to_ros2 -* Remove all ignition references on ROS 2 branch (`#302 `_) - * Remove all shims - * Update CMakeLists and package.xml for garden - * Complete garden gz renaming - * Drop fortress CI -* Contributors: Alejandro Hernández Cordero, Michael Carroll, ahcorde +0.244.12 (2023-12-13) +--------------------- +* Add support for Harmonic/Humble pairing (`#462 `_) +* Set on_exit_shutdown argument for gz-sim ExecuteProcess (`#355 `_) (`#451 `_) +* Contributors: Addisu Z. Taddese, Michael Carroll +0.244.11 (2023-05-23) +--------------------- 0.244.10 (2023-05-03) --------------------- diff --git a/ros_gz_sim/CMakeLists.txt b/ros_gz_sim/CMakeLists.txt index dc21f8a9b..29d2ea283 100644 --- a/ros_gz_sim/CMakeLists.txt +++ b/ros_gz_sim/CMakeLists.txt @@ -12,31 +12,94 @@ endif() find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) -find_package(rclcpp_components REQUIRED) find_package(std_msgs REQUIRED) -find_package(gz_math_vendor REQUIRED) -find_package(gz-math REQUIRED) +# TODO(CH3): Deprecated. Remove on tock. +if("$ENV{GZ_VERSION}" STREQUAL "" AND NOT "$ENV{IGNITION_VERSION}" STREQUAL "") + message(DEPRECATION "Environment variable [IGNITION_VERSION] is deprecated. Use [GZ_VERSION] instead.") + set(ENV{GZ_VERSION} $ENV{IGNITION_VERSION}) +endif() + +# Edifice +if("$ENV{GZ_VERSION}" STREQUAL "edifice") + find_package(ignition-math6 REQUIRED) + set(GZ_MATH_VER ${ignition-math6_VERSION_MAJOR}) + + find_package(ignition-transport10 REQUIRED) + set(GZ_TRANSPORT_VER ${ignition-transport10_VERSION_MAJOR}) + + find_package(ignition-msgs7 REQUIRED) + set(GZ_MSGS_VER ${ignition-msgs7_VERSION_MAJOR}) + + find_package(ignition-gazebo5 REQUIRED) + set(GZ_SIM_VER ${ignition-gazebo5_VERSION_MAJOR}) + + set(GZ_TARGET_PREFIX ignition) + macro(gz_find_package) + ign_find_package(${ARGV}) + endmacro() + + message(STATUS "Compiling against Gazebo Edifice") +# Garden +elseif("$ENV{GZ_VERSION}" STREQUAL "garden") + find_package(gz-math7 REQUIRED) + set(GZ_MATH_VER ${gz-math7_VERSION_MAJOR}) + + find_package(gz-transport12 REQUIRED) + set(GZ_TRANSPORT_VER ${gz-transport12_VERSION_MAJOR}) + + find_package(gz-msgs9 REQUIRED) + set(GZ_MSGS_VER ${gz-msgs9_VERSION_MAJOR}) + + find_package(gz-sim7 REQUIRED) + set(GZ_SIM_VER ${gz-sim7_VERSION_MAJOR}) + + set(GZ_TARGET_PREFIX gz) + + message(STATUS "Compiling against Gazebo Garden") +elseif("$ENV{GZ_VERSION}" STREQUAL "harmonic") + find_package(gz-math7 REQUIRED) + set(GZ_MATH_VER ${gz-math7_VERSION_MAJOR}) + + find_package(gz-transport13 REQUIRED) + set(GZ_TRANSPORT_VER ${gz-transport13_VERSION_MAJOR}) + + find_package(gz-msgs10 REQUIRED) + set(GZ_MSGS_VER ${gz-msgs10_VERSION_MAJOR}) + + find_package(gz-sim8 REQUIRED) + set(GZ_SIM_VER ${gz-sim8_VERSION_MAJOR}) + + set(GZ_TARGET_PREFIX gz) + + message(STATUS "Compiling against Gazebo Harmonic") +# Default to Fortress +else() + find_package(ignition-math6 REQUIRED) + set(GZ_MATH_VER ${ignition-math6_VERSION_MAJOR}) + + find_package(ignition-transport11 REQUIRED) + set(GZ_TRANSPORT_VER ${ignition-transport11_VERSION_MAJOR}) + + find_package(ignition-msgs8 REQUIRED) + set(GZ_MSGS_VER ${ignition-msgs8_VERSION_MAJOR}) -find_package(gz_transport_vendor REQUIRED) -find_package(gz-transport REQUIRED) + find_package(ignition-gazebo6 REQUIRED) + set(GZ_SIM_VER ${ignition-gazebo6_VERSION_MAJOR}) -find_package(gz_msgs_vendor REQUIRED) -find_package(gz-msgs REQUIRED) + set(GZ_TARGET_PREFIX ignition) -find_package(gz_sim_vendor REQUIRED) -find_package(gz-sim REQUIRED) -# Needed in launch/gz_sim.launch.py.in -set(GZ_SIM_VER ${gz-sim_VERSION_MAJOR}) + message(STATUS "Compiling against Gazebo Fortress") + macro(gz_find_package) + ign_find_package(${ARGV}) + endmacro() +endif() gz_find_package(gflags REQUIRED PKGCONFIG gflags) find_package(std_msgs REQUIRED) -# Install the python module for this package -ament_python_install_package(${PROJECT_NAME}) - add_executable(create src/create.cpp) ament_target_dependencies(create rclcpp @@ -44,9 +107,9 @@ ament_target_dependencies(create ) target_link_libraries(create gflags - gz-math::core - gz-msgs::core - gz-transport::core + ${GZ_TARGET_PREFIX}-math${GZ_MATH_VER}::core + ${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}::core + ${GZ_TARGET_PREFIX}-transport${GZ_TRANSPORT_VER}::core ) ament_target_dependencies(create std_msgs) @@ -64,23 +127,6 @@ install(DIRECTORY include/${PROJECT_NAME}/ DESTINATION include/${PROJECT_NAME} ) -add_library(gzserver_component SHARED src/gzserver.cpp) -rclcpp_components_register_nodes(gzserver_component "ros_gz_sim::GzServer") -ament_target_dependencies(gzserver_component - rclcpp - rclcpp_components - std_msgs -) -target_link_libraries(gzserver_component - gz-sim::core -) -ament_target_dependencies(gzserver_component std_msgs) -rclcpp_components_register_node( - gzserver_component - PLUGIN "ros_gz_sim::GzServer" - EXECUTABLE gzserver -) - configure_file( launch/gz_sim.launch.py.in launch/gz_sim.launch.py.configured @@ -96,33 +142,10 @@ install(FILES DESTINATION share/${PROJECT_NAME}/launch ) -install(FILES - "launch/gz_server.launch" - "launch/gz_server.launch.py" - "launch/gz_spawn_model.launch" - "launch/gz_spawn_model.launch.py" - "launch/ros_gz_sim.launch" - "launch/ros_gz_sim.launch.py" - "launch/ros_gz_spawn_model.launch.py" - DESTINATION share/${PROJECT_NAME}/launch -) - install(TARGETS create DESTINATION lib/${PROJECT_NAME} ) -install(TARGETS - gzserver - DESTINATION lib/${PROJECT_NAME} -) - -ament_export_targets(export_gzserver_component) -install(TARGETS gzserver_component - EXPORT export_gzserver_component - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib - RUNTIME DESTINATION bin -) install( TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME} @@ -141,15 +164,11 @@ if(BUILD_TESTING) # GTest find_package(ament_cmake_gtest REQUIRED) - find_package(launch_testing_ament_cmake REQUIRED) ament_find_gtest() ament_add_gtest_executable(test_stopwatch test/test_stopwatch.cpp ) - ament_add_gtest_executable(test_create - test/test_create.cpp - ) ament_target_dependencies(test_stopwatch rclcpp) @@ -161,16 +180,12 @@ if(BUILD_TESTING) target_link_libraries(test_stopwatch ${PROJECT_NAME} ) - target_link_libraries(test_create - gz-transport::core - ) install( - TARGETS test_stopwatch test_create + TARGETS test_stopwatch DESTINATION lib/${PROJECT_NAME} ) ament_add_gtest_test(test_stopwatch) - add_launch_test(test/test_create_node.launch.py TIMEOUT 200) endif() ament_package() diff --git a/ros_gz_sim/README.md b/ros_gz_sim/README.md index cf4e8ca97..e0b05f624 100644 --- a/ros_gz_sim/README.md +++ b/ros_gz_sim/README.md @@ -29,7 +29,7 @@ ros2 launch ros_gz_sim gz_sim.launch.py then spawn a model: ``` -ros2 run ros_gz_sim create -world default -file 'https://fuel.gazebosim.org/1.0/openrobotics/models/Gazebo' +ros2 run ros_gz_sim create -world default -file 'https://fuel.ignitionrobotics.org/1.0/openrobotics/models/Gazebo' ``` See more options with: diff --git a/ros_gz_sim/launch/gz_server.launch b/ros_gz_sim/launch/gz_server.launch deleted file mode 100644 index 6f29648cd..000000000 --- a/ros_gz_sim/launch/gz_server.launch +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/ros_gz_sim/launch/gz_server.launch.py b/ros_gz_sim/launch/gz_server.launch.py deleted file mode 100644 index 70de5d24d..000000000 --- a/ros_gz_sim/launch/gz_server.launch.py +++ /dev/null @@ -1,104 +0,0 @@ -# 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. - -"""Launch gz_server in a component container.""" - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument -from launch.conditions import IfCondition -from launch.substitutions import LaunchConfiguration, PythonExpression, TextSubstitution -from launch_ros.actions import ComposableNodeContainer, LoadComposableNodes, Node -from launch_ros.descriptions import ComposableNode - - -def generate_launch_description(): - - declare_world_sdf_file_cmd = DeclareLaunchArgument( - 'world_sdf_file', default_value=TextSubstitution(text=''), - description='Path to the SDF world file') - declare_world_sdf_string_cmd = DeclareLaunchArgument( - 'world_sdf_string', default_value=TextSubstitution(text=''), - description='SDF world string') - declare_container_name_cmd = DeclareLaunchArgument( - 'container_name', default_value='ros_gz_container', - description='Name of container that nodes will load in if use composition',) - declare_create_own_container_cmd = DeclareLaunchArgument( - 'create_own_container', default_value='False', - description='Whether we should start our own ROS container when using composition.',) - declare_use_composition_cmd = DeclareLaunchArgument( - 'use_composition', default_value='False', - description='Use composed bringup if True') - - load_nodes = Node( - condition=IfCondition(PythonExpression(['not ', LaunchConfiguration('use_composition')])), - package='ros_gz_sim', - executable='gzserver', - output='screen', - parameters=[{'world_sdf_file': LaunchConfiguration('world_sdf_file'), - 'world_sdf_string': LaunchConfiguration('world_sdf_string')}], - ) - - load_composable_nodes_with_container = ComposableNodeContainer( - condition=IfCondition( - PythonExpression([LaunchConfiguration('use_composition'), ' and ', - LaunchConfiguration('create_own_container')])), - name=LaunchConfiguration('container_name'), - namespace='', - package='rclcpp_components', - executable='component_container', - composable_node_descriptions=[ - ComposableNode( - package='ros_gz_sim', - plugin='ros_gz_sim::GzServer', - name='gz_server', - parameters=[{'world_sdf_file': LaunchConfiguration('world_sdf_file'), - 'world_sdf_string': LaunchConfiguration('world_sdf_string')}], - extra_arguments=[{'use_intra_process_comms': True}], - ), - ], - output='screen', - ) - - load_composable_nodes_without_container = LoadComposableNodes( - condition=IfCondition( - PythonExpression([LaunchConfiguration('use_composition'), ' and not ', - LaunchConfiguration('create_own_container')])), - target_container=LaunchConfiguration('container_name'), - composable_node_descriptions=[ - ComposableNode( - package='ros_gz_sim', - plugin='ros_gz_sim::GzServer', - name='gz_server', - parameters=[{'world_sdf_file': LaunchConfiguration('world_sdf_file'), - 'world_sdf_string': LaunchConfiguration('world_sdf_string')}], - extra_arguments=[{'use_intra_process_comms': True}], - ), - ], - ) - - # Create the launch description and populate - ld = LaunchDescription() - - # Declare the launch options - ld.add_action(declare_world_sdf_file_cmd) - ld.add_action(declare_world_sdf_string_cmd) - ld.add_action(declare_container_name_cmd) - ld.add_action(declare_create_own_container_cmd) - ld.add_action(declare_use_composition_cmd) - # Add the actions to launch all of the gz_server nodes - ld.add_action(load_nodes) - ld.add_action(load_composable_nodes_with_container) - ld.add_action(load_composable_nodes_without_container) - - return ld diff --git a/ros_gz_sim/launch/gz_sim.launch.py.in b/ros_gz_sim/launch/gz_sim.launch.py.in index 8859d1324..8f87fb7d1 100644 --- a/ros_gz_sim/launch/gz_sim.launch.py.in +++ b/ros_gz_sim/launch/gz_sim.launch.py.in @@ -87,12 +87,25 @@ def launch_gz(context, *args, **kwargs): 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) @@ -121,14 +134,13 @@ def launch_gz(context, *args, **kwargs): else: debug_prefix = None - if on_exit_shutdown != 'false': + if on_exit_shutdown: on_exit = Shutdown() else: on_exit = None return [ExecuteProcess( cmd=[exec, exec_args, '--force-version', gz_version], - name='gazebo', output='screen', additional_env=env, shell=True, diff --git a/ros_gz_sim/launch/gz_spawn_model.launch b/ros_gz_sim/launch/gz_spawn_model.launch deleted file mode 100644 index 9285ceff2..000000000 --- a/ros_gz_sim/launch/gz_spawn_model.launch +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/ros_gz_sim/launch/gz_spawn_model.launch.py b/ros_gz_sim/launch/gz_spawn_model.launch.py deleted file mode 100644 index 5fe3ae413..000000000 --- a/ros_gz_sim/launch/gz_spawn_model.launch.py +++ /dev/null @@ -1,94 +0,0 @@ -# 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. - -"""Launch create to spawn models in gz sim.""" - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument -from launch.substitutions import LaunchConfiguration, TextSubstitution -from launch_ros.actions import Node - - -def generate_launch_description(): - - world = LaunchConfiguration('world') - file = LaunchConfiguration('file') - model_string = LaunchConfiguration('model_string') - topic = LaunchConfiguration('topic') - entity_name = LaunchConfiguration('entity_name') - allow_renaming = LaunchConfiguration('allow_renaming') - x = LaunchConfiguration('x', default='0.0') - y = LaunchConfiguration('y', default='0.0') - z = LaunchConfiguration('z', default='0.0') - roll = LaunchConfiguration('R', default='0.0') - pitch = LaunchConfiguration('P', default='0.0') - yaw = LaunchConfiguration('Y', default='0.0') - - declare_world_cmd = DeclareLaunchArgument( - 'world', default_value=TextSubstitution(text=''), - description='World name') - declare_file_cmd = DeclareLaunchArgument( - 'file', default_value=TextSubstitution(text=''), - description='SDF filename') - declare_model_string_cmd = DeclareLaunchArgument( - 'model_string', - default_value='', - description='XML(SDF) string', - ) - declare_topic_cmd = DeclareLaunchArgument( - 'topic', default_value=TextSubstitution(text=''), - description='Get XML from this topic' - ) - declare_entity_name_cmd = DeclareLaunchArgument( - 'entity_name', default_value=TextSubstitution(text=''), - description='Name of the entity' - ) - declare_allow_renaming_cmd = DeclareLaunchArgument( - 'allow_renaming', default_value='False', - description='Whether the entity allows renaming or not' - ) - - load_nodes = Node( - package='ros_gz_sim', - executable='create', - output='screen', - parameters=[{'world': world, - 'file': file, - 'string': model_string, - 'topic': topic, - 'name': entity_name, - 'allow_renaming': allow_renaming, - 'x': x, - 'y': y, - 'z': z, - 'R': roll, - 'P': pitch, - 'Y': yaw, - }], - ) - - # Create the launch description and populate - ld = LaunchDescription() - - # Declare the launch options - ld.add_action(declare_world_cmd) - ld.add_action(declare_file_cmd) - ld.add_action(declare_model_string_cmd) - ld.add_action(declare_topic_cmd) - ld.add_action(declare_entity_name_cmd) - ld.add_action(declare_allow_renaming_cmd) - # Add the actions to launch all of the create nodes - ld.add_action(load_nodes) - - return ld diff --git a/ros_gz_sim/launch/ros_gz_sim.launch b/ros_gz_sim/launch/ros_gz_sim.launch deleted file mode 100644 index 6130ce0db..000000000 --- a/ros_gz_sim/launch/ros_gz_sim.launch +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/ros_gz_sim/launch/ros_gz_sim.launch.py b/ros_gz_sim/launch/ros_gz_sim.launch.py deleted file mode 100644 index aba047eeb..000000000 --- a/ros_gz_sim/launch/ros_gz_sim.launch.py +++ /dev/null @@ -1,129 +0,0 @@ -# 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. - -"""Launch gzsim + ros_gz_bridge in a component container.""" - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, PathJoinSubstitution, TextSubstitution -from launch_ros.substitutions import FindPackageShare - - -def generate_launch_description(): - - bridge_name = LaunchConfiguration('bridge_name') - config_file = LaunchConfiguration('config_file') - container_name = LaunchConfiguration('container_name') - create_own_container = LaunchConfiguration('create_own_container') - namespace = LaunchConfiguration('namespace') - use_composition = LaunchConfiguration('use_composition') - use_respawn = LaunchConfiguration('use_respawn') - bridge_log_level = LaunchConfiguration('bridge_log_level') - - world_sdf_file = LaunchConfiguration('world_sdf_file') - world_sdf_string = LaunchConfiguration('world_sdf_string') - - declare_bridge_name_cmd = DeclareLaunchArgument( - 'bridge_name', description='Name of the bridge' - ) - - declare_config_file_cmd = DeclareLaunchArgument( - 'config_file', description='YAML config file' - ) - - declare_container_name_cmd = DeclareLaunchArgument( - 'container_name', - default_value='ros_gz_container', - description='Name of container that nodes will load in if use composition', - ) - - declare_create_own_container_cmd = DeclareLaunchArgument( - 'create_own_container', - default_value='False', - description='Whether we should start a ROS container when using composition.', - ) - - declare_namespace_cmd = DeclareLaunchArgument( - 'namespace', default_value='', description='Top-level namespace' - ) - - declare_use_composition_cmd = DeclareLaunchArgument( - 'use_composition', default_value='False', description='Use composed bringup if True' - ) - - declare_use_respawn_cmd = DeclareLaunchArgument( - 'use_respawn', - default_value='False', - description='Whether to respawn if a node crashes. Applied when composition is disabled.', - ) - - declare_bridge_log_level_cmd = DeclareLaunchArgument( - 'bridge_log_level', default_value='info', description='Bridge log level' - ) - - declare_world_sdf_file_cmd = DeclareLaunchArgument( - 'world_sdf_file', default_value=TextSubstitution(text=''), - description='Path to the SDF world file' - ) - - declare_world_sdf_string_cmd = DeclareLaunchArgument( - 'world_sdf_string', default_value=TextSubstitution(text=''), - description='SDF world string' - ) - - gz_server_description = IncludeLaunchDescription( - PythonLaunchDescriptionSource( - [PathJoinSubstitution([FindPackageShare('ros_gz_sim'), - 'launch', - 'gz_server.launch.py'])]), - launch_arguments=[('world_sdf_file', world_sdf_file), - ('world_sdf_string', world_sdf_string), - ('container_name', container_name), - ('create_own_container', create_own_container), - ('use_composition', use_composition), ]) - - bridge_description = IncludeLaunchDescription( - PythonLaunchDescriptionSource( - [PathJoinSubstitution([FindPackageShare('ros_gz_bridge'), - 'launch', - 'ros_gz_bridge.launch.py'])]), - launch_arguments=[('bridge_name', bridge_name), - ('config_file', config_file), - ('container_name', container_name), - ('namespace', namespace), - ('create_own_container', str(False)), - ('use_composition', use_composition), - ('use_respawn', use_respawn), - ('bridge_log_level', bridge_log_level), ]) - - # Create the launch description and populate - ld = LaunchDescription() - - # Declare the launch options - ld.add_action(declare_bridge_name_cmd) - ld.add_action(declare_config_file_cmd) - ld.add_action(declare_container_name_cmd) - ld.add_action(declare_create_own_container_cmd) - ld.add_action(declare_namespace_cmd) - ld.add_action(declare_use_composition_cmd) - ld.add_action(declare_use_respawn_cmd) - ld.add_action(declare_bridge_log_level_cmd) - ld.add_action(declare_world_sdf_file_cmd) - ld.add_action(declare_world_sdf_string_cmd) - # Add the actions to launch all of the bridge + gz_server nodes - ld.add_action(gz_server_description) - ld.add_action(bridge_description) - - return ld diff --git a/ros_gz_sim/launch/ros_gz_spawn_model.launch b/ros_gz_sim/launch/ros_gz_spawn_model.launch deleted file mode 100644 index b1ff49dee..000000000 --- a/ros_gz_sim/launch/ros_gz_spawn_model.launch +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ros_gz_sim/launch/ros_gz_spawn_model.launch.py b/ros_gz_sim/launch/ros_gz_spawn_model.launch.py deleted file mode 100644 index 4d779a79b..000000000 --- a/ros_gz_sim/launch/ros_gz_spawn_model.launch.py +++ /dev/null @@ -1,169 +0,0 @@ -# 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. - -"""Launch gzsim + ros_gz_bridge in a component container.""" - -from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.substitutions import LaunchConfiguration, PathJoinSubstitution, TextSubstitution -from launch_ros.substitutions import FindPackageShare - - -def generate_launch_description(): - - bridge_name = LaunchConfiguration('bridge_name') - config_file = LaunchConfiguration('config_file') - container_name = LaunchConfiguration('container_name') - create_own_container = LaunchConfiguration('create_own_container') - namespace = LaunchConfiguration('namespace') - use_composition = LaunchConfiguration('use_composition') - use_respawn = LaunchConfiguration('use_respawn') - log_level = LaunchConfiguration('log_level') - - world = LaunchConfiguration('world') - file = LaunchConfiguration('file') - model_string = LaunchConfiguration('model_string') - topic = LaunchConfiguration('topic') - entity_name = LaunchConfiguration('entity_name') - allow_renaming = LaunchConfiguration('allow_renaming') - x = LaunchConfiguration('x', default='0.0') - y = LaunchConfiguration('y', default='0.0') - z = LaunchConfiguration('z', default='0.0') - roll = LaunchConfiguration('R', default='0.0') - pitch = LaunchConfiguration('P', default='0.0') - yaw = LaunchConfiguration('Y', default='0.0') - - declare_bridge_name_cmd = DeclareLaunchArgument( - 'bridge_name', description='Name of the bridge' - ) - - declare_config_file_cmd = DeclareLaunchArgument( - 'config_file', description='YAML config file' - ) - - declare_container_name_cmd = DeclareLaunchArgument( - 'container_name', - default_value='ros_gz_container', - description='Name of container that nodes will load in if use composition', - ) - - declare_create_own_container_cmd = DeclareLaunchArgument( - 'create_own_container', - default_value='False', - description='Whether we should start a ROS container when using composition.', - ) - - declare_namespace_cmd = DeclareLaunchArgument( - 'namespace', default_value='', description='Top-level namespace' - ) - - declare_use_composition_cmd = DeclareLaunchArgument( - 'use_composition', default_value='False', description='Use composed bringup if True' - ) - - declare_use_respawn_cmd = DeclareLaunchArgument( - 'use_respawn', - default_value='False', - description='Whether to respawn if a node crashes. Applied when composition is disabled.', - ) - - declare_log_level_cmd = DeclareLaunchArgument( - 'log_level', default_value='info', description='log level' - ) - - declare_world_cmd = DeclareLaunchArgument( - 'world', default_value=TextSubstitution(text=''), - description='World name') - - declare_file_cmd = DeclareLaunchArgument( - 'file', default_value=TextSubstitution(text=''), - description='SDF filename') - - declare_model_string_cmd = DeclareLaunchArgument( - 'model_string', - default_value='', - description='XML(SDF) string', - ) - - declare_topic_cmd = DeclareLaunchArgument( - 'topic', default_value=TextSubstitution(text=''), - description='Get XML from this topic' - ) - - declare_entity_name_cmd = DeclareLaunchArgument( - 'entity_name', default_value=TextSubstitution(text=''), - description='Name of the entity' - ) - - declare_allow_renaming_cmd = DeclareLaunchArgument( - 'allow_renaming', default_value='False', - description='Whether the entity allows renaming or not' - ) - - bridge_description = IncludeLaunchDescription( - PythonLaunchDescriptionSource( - [PathJoinSubstitution([FindPackageShare('ros_gz_bridge'), - 'launch', - 'ros_gz_bridge.launch.py'])]), - launch_arguments=[('bridge_name', bridge_name), - ('config_file', config_file), - ('container_name', container_name), - ('create_own_container', create_own_container), - ('namespace', namespace), - ('use_composition', use_composition), - ('use_respawn', use_respawn), - ('log_level', log_level), ]) - - spawn_model_description = IncludeLaunchDescription( - PythonLaunchDescriptionSource( - [PathJoinSubstitution([FindPackageShare('ros_gz_sim'), - 'launch', - 'gz_spawn_model.launch.py'])]), - launch_arguments=[('world', world), - ('file', file), - ('model_string', model_string), - ('topic', topic), - ('entity_name', entity_name), - ('allow_renaming', allow_renaming), - ('x', x), - ('y', y), - ('z', z), - ('R', roll), - ('P', pitch), - ('Y', yaw), ]) - - # Create the launch description and populate - ld = LaunchDescription() - - # Declare the launch options - ld.add_action(declare_bridge_name_cmd) - ld.add_action(declare_config_file_cmd) - ld.add_action(declare_container_name_cmd) - ld.add_action(declare_create_own_container_cmd) - ld.add_action(declare_namespace_cmd) - ld.add_action(declare_use_composition_cmd) - ld.add_action(declare_use_respawn_cmd) - ld.add_action(declare_log_level_cmd) - ld.add_action(declare_world_cmd) - ld.add_action(declare_file_cmd) - ld.add_action(declare_model_string_cmd) - ld.add_action(declare_topic_cmd) - ld.add_action(declare_entity_name_cmd) - ld.add_action(declare_allow_renaming_cmd) - # Add the actions to launch all of the bridge + spawn_model nodes - ld.add_action(bridge_description) - ld.add_action(spawn_model_description) - - return ld diff --git a/ros_gz_sim/package.xml b/ros_gz_sim/package.xml index 37452f3ae..9471bfb3c 100644 --- a/ros_gz_sim/package.xml +++ b/ros_gz_sim/package.xml @@ -2,39 +2,41 @@ ros_gz_sim - 1.0.7 + 0.244.16 Tools for using Gazebo Sim simulation with ROS. Alejandro Hernandez - Aditya Pande Apache 2.0 Alejandro Hernandez - Addisu Taddese - Carlos Agüero ament_cmake - ament_cmake_python pkg-config ament_index_python - launch - launch_ros libgflags-dev rclcpp - rclcpp_components std_msgs - gz_math_vendor - gz_msgs_vendor - gz_sim_vendor - gz_transport_vendor + + gz-math7 + gz-msgs10 + gz-sim8 + gz-transport13 + + gz-sim7 + gz-math7 + + ignition-gazebo6 + ignition-math6 + ignition-gazebo6 + ignition-math6 + + ignition-gazebo5 + ignition-math6 ament_lint_auto ament_lint_common - launch_testing_ament_cmake - launch_ros - launch_testing ament_cmake diff --git a/ros_gz_sim/resource/ros_gz_sim b/ros_gz_sim/resource/ros_gz_sim deleted file mode 100644 index e69de29bb..000000000 diff --git a/ros_gz_sim/ros_gz_sim/__init__.py b/ros_gz_sim/ros_gz_sim/__init__.py deleted file mode 100644 index 17b9696c3..000000000 --- a/ros_gz_sim/ros_gz_sim/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# 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. - -"""Main entry point for the `ros_gz_sim` package.""" - -from . import actions - - -__all__ = [ - 'actions', -] diff --git a/ros_gz_sim/ros_gz_sim/actions/__init__.py b/ros_gz_sim/ros_gz_sim/actions/__init__.py deleted file mode 100644 index bea18edd1..000000000 --- a/ros_gz_sim/ros_gz_sim/actions/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# 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. - -"""Actions module.""" - -from .gz_spawn_model import GzSpawnModel -from .gzserver import GzServer - - -__all__ = [ - 'GzSpawnModel', - 'GzServer', -] diff --git a/ros_gz_sim/ros_gz_sim/actions/gz_spawn_model.py b/ros_gz_sim/ros_gz_sim/actions/gz_spawn_model.py deleted file mode 100644 index a8fced109..000000000 --- a/ros_gz_sim/ros_gz_sim/actions/gz_spawn_model.py +++ /dev/null @@ -1,207 +0,0 @@ -# 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. - -"""Module for the gz_spawn_model action.""" - -from typing import List -from typing import Optional - -from launch.action import Action -from launch.actions import IncludeLaunchDescription -from launch.frontend import Entity, expose_action, Parser -from launch.launch_context import LaunchContext -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.some_substitutions_type import SomeSubstitutionsType -from launch.substitutions import PathJoinSubstitution -from launch_ros.substitutions import FindPackageShare - - -@expose_action('gz_spawn_model') -class GzSpawnModel(Action): - """Action that executes a gz_spawn_model ROS node.""" - - def __init__( - self, - *, - world: Optional[SomeSubstitutionsType] = None, - file: Optional[SomeSubstitutionsType] = None, - model_string: Optional[SomeSubstitutionsType] = None, - topic: Optional[SomeSubstitutionsType] = None, - entity_name: Optional[SomeSubstitutionsType] = None, - allow_renaming: Optional[SomeSubstitutionsType] = None, - x: Optional[SomeSubstitutionsType] = None, - y: Optional[SomeSubstitutionsType] = None, - z: Optional[SomeSubstitutionsType] = None, - roll: Optional[SomeSubstitutionsType] = None, - pitch: Optional[SomeSubstitutionsType] = None, - yaw: Optional[SomeSubstitutionsType] = None, - **kwargs - ) -> None: - """ - Construct a gz_spawn_model action. - - All arguments are forwarded to `ros_gz_sim.launch.gz_spawn_model.launch.py`, - so see the documentation of that class for further details. - - :param: world World name. - :param: file SDF filename. - :param: model_string XML(SDF) string. - :param: topic Get XML from this topic. - :param: entity_name Name of the entity. - :param: allow_renaming Whether the entity allows renaming or not. - :param: x X coordinate. - :param: y Y coordinate. - :param: z Z coordinate. - :param: roll Roll orientation. - :param: pitch Pitch orientation. - :param: yaw Yaw orientation. - """ - super().__init__(**kwargs) - self.__world = world - self.__file = file - self.__model_string = model_string - self.__topic = topic - self.__entity_name = entity_name - self.__allow_renaming = allow_renaming - self.__x = x - self.__y = y - self.__z = z - self.__roll = roll - self.__pitch = pitch - self.__yaw = yaw - - @classmethod - def parse(cls, entity: Entity, parser: Parser): - """Parse gz_spawn_model.""" - _, kwargs = super().parse(entity, parser) - - world = entity.get_attr( - 'world', data_type=str, - optional=True) - - file = entity.get_attr( - 'file', data_type=str, - optional=True) - - model_string = entity.get_attr( - 'model_string', data_type=str, - optional=True) - - topic = entity.get_attr( - 'topic', data_type=str, - optional=True) - - entity_name = entity.get_attr( - 'entity_name', data_type=str, - optional=True) - - allow_renaming = entity.get_attr( - 'allow_renaming', data_type=str, - optional=True) - - x = entity.get_attr( - 'x', data_type=str, - optional=True) - - y = entity.get_attr( - 'y', data_type=str, - optional=True) - - z = entity.get_attr( - 'z', data_type=str, - optional=True) - - roll = entity.get_attr( - 'roll', data_type=str, - optional=True) - - pitch = entity.get_attr( - 'pitch', data_type=str, - optional=True) - - yaw = entity.get_attr( - 'yaw', data_type=str, - optional=True) - - if isinstance(world, str): - world = parser.parse_substitution(world) - kwargs['world'] = world - - if isinstance(file, str): - file = parser.parse_substitution(file) - kwargs['file'] = file - - if isinstance(model_string, str): - model_string = parser.parse_substitution(model_string) - kwargs['model_string'] = model_string - - if isinstance(topic, str): - topic = parser.parse_substitution(topic) - kwargs['topic'] = topic - - if isinstance(entity_name, str): - entity_name = parser.parse_substitution(entity_name) - kwargs['entity_name'] = entity_name - - if isinstance(allow_renaming, str): - allow_renaming = parser.parse_substitution(allow_renaming) - kwargs['allow_renaming'] = allow_renaming - - if isinstance(x, str): - x = parser.parse_substitution(x) - kwargs['x'] = x - - if isinstance(y, str): - y = parser.parse_substitution(y) - kwargs['y'] = y - - if isinstance(z, str): - z = parser.parse_substitution(z) - kwargs['z'] = z - - if isinstance(roll, str): - roll = parser.parse_substitution(roll) - kwargs['roll'] = roll - - if isinstance(pitch, str): - pitch = parser.parse_substitution(pitch) - kwargs['pitch'] = pitch - - if isinstance(yaw, str): - yaw = parser.parse_substitution(yaw) - kwargs['yaw'] = yaw - - return cls, kwargs - - def execute(self, context: LaunchContext) -> Optional[List[Action]]: - """Execute the action.""" - gz_spawn_model_description = IncludeLaunchDescription( - PythonLaunchDescriptionSource( - [PathJoinSubstitution([FindPackageShare('ros_gz_sim'), - 'launch', - 'gz_spawn_model.launch.py'])]), - launch_arguments=[('world', self.__world), - ('file', self.__file), - ('model_string', self.__model_string), - ('topic', self.__topic), - ('entity_name', self.__entity_name), - ('allow_renaming', self.__allow_renaming), - ('x', self.__x), - ('y', self.__y), - ('z', self.__z), - ('roll', self.__roll), - ('pitch', self.__pitch), - ('yaw', self.__yaw), ]) - - return [gz_spawn_model_description] diff --git a/ros_gz_sim/ros_gz_sim/actions/gzserver.py b/ros_gz_sim/ros_gz_sim/actions/gzserver.py deleted file mode 100644 index 64461baf4..000000000 --- a/ros_gz_sim/ros_gz_sim/actions/gzserver.py +++ /dev/null @@ -1,124 +0,0 @@ -# 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. - -"""Module for the GzServer action.""" - -from typing import List -from typing import Optional - -from launch.action import Action -from launch.actions import IncludeLaunchDescription -from launch.frontend import Entity, expose_action, Parser -from launch.launch_context import LaunchContext -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch.some_substitutions_type import SomeSubstitutionsType -from launch.substitutions import PathJoinSubstitution -from launch_ros.substitutions import FindPackageShare - - -@expose_action('gz_server') -class GzServer(Action): - """Action that executes a gz_server ROS [composable] node.""" - - def __init__( - self, - *, - world_sdf_file: Optional[SomeSubstitutionsType] = '', - world_sdf_string: Optional[SomeSubstitutionsType] = '', - container_name: Optional[SomeSubstitutionsType] = 'ros_gz_container', - create_own_container: Optional[SomeSubstitutionsType] = 'False', - use_composition: Optional[SomeSubstitutionsType] = 'False', - **kwargs - ) -> None: - """ - Construct a gz_server action. - - All arguments are forwarded to `ros_gz_sim.launch.gz_server.launch.py`, - so see the documentation of that class for further details. - - :param: world_sdf_file Path to the SDF world file. - :param: world_sdf_string SDF world string. - :param: container_name Name of container that nodes will load in if use composition. - :param: create_own_container Whether to start a ROS container when using composition. - :param: use_composition Use composed bringup if True. - """ - super().__init__(**kwargs) - self.__world_sdf_file = world_sdf_file - self.__world_sdf_string = world_sdf_string - self.__container_name = container_name - self.__create_own_container = create_own_container - self.__use_composition = use_composition - - @classmethod - def parse(cls, entity: Entity, parser: Parser): - """Parse gz_server.""" - _, kwargs = super().parse(entity, parser) - - world_sdf_file = entity.get_attr( - 'world_sdf_file', data_type=str, - optional=True) - - world_sdf_string = entity.get_attr( - 'world_sdf_string', data_type=str, - optional=True) - - container_name = entity.get_attr( - 'container_name', data_type=str, - optional=True) - - create_own_container = entity.get_attr( - 'create_own_container', data_type=str, - optional=True) - - use_composition = entity.get_attr( - 'use_composition', data_type=str, - optional=True) - - if isinstance(world_sdf_file, str): - world_sdf_file = parser.parse_substitution(world_sdf_file) - kwargs['world_sdf_file'] = world_sdf_file - - if isinstance(world_sdf_string, str): - world_sdf_string = parser.parse_substitution(world_sdf_string) - kwargs['world_sdf_string'] = world_sdf_string - - if isinstance(container_name, str): - container_name = parser.parse_substitution(container_name) - kwargs['container_name'] = container_name - - if isinstance(create_own_container, str): - create_own_container = \ - parser.parse_substitution(create_own_container) - kwargs['create_own_container'] = create_own_container - - if isinstance(use_composition, str): - use_composition = parser.parse_substitution(use_composition) - kwargs['use_composition'] = use_composition - - return cls, kwargs - - def execute(self, context: LaunchContext) -> Optional[List[Action]]: - """Execute the action.""" - gz_server_description = IncludeLaunchDescription( - PythonLaunchDescriptionSource( - [PathJoinSubstitution([FindPackageShare('ros_gz_sim'), - 'launch', - 'gz_server.launch.py'])]), - launch_arguments=[('world_sdf_file', self.__world_sdf_file), - ('world_sdf_string', self.__world_sdf_string), - ('container_name', self.__container_name), - ('create_own_container', self.__create_own_container), - ('use_composition', self.__use_composition), ]) - - return [gz_server_description] diff --git a/ros_gz_sim/setup.cfg b/ros_gz_sim/setup.cfg deleted file mode 100644 index c044b41d2..000000000 --- a/ros_gz_sim/setup.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[options.entry_points] -launch.frontend.launch_extension = - ros_gz_sim = ros_gz_sim diff --git a/ros_gz_sim/src/create.cpp b/ros_gz_sim/src/create.cpp index 83aca0af7..6b12daf8e 100644 --- a/ros_gz_sim/src/create.cpp +++ b/ros_gz_sim/src/create.cpp @@ -21,10 +21,6 @@ #include #include -#include -#include -#include -#include #include #include @@ -33,11 +29,7 @@ #include #include -// ROS interface for spawning entities into Gazebo. -// Suggested for use with roslaunch and loading entities from ROS param. -// If these are not needed, just use the `gz service` command line instead. -// \TODO(anyone) Remove GFlags in ROS-J DEFINE_string(world, "", "World name."); DEFINE_string(file, "", "Load XML from a file."); DEFINE_string(param, "", "Load XML from a ROS param."); @@ -52,94 +44,23 @@ DEFINE_double(R, 0, "Roll component of initial orientation, in radians."); DEFINE_double(P, 0, "Pitch component of initial orientation, in radians."); DEFINE_double(Y, 0, "Yaw component of initial orientation, in radians."); -// Utility Function to avoid code duplication - -bool set_XML_from_topic( - const std::string & topic_name, const rclcpp::Node::SharedPtr ros2_node, - gz::msgs::EntityFactory & req) -{ - const auto timeout = std::chrono::seconds(1); - std::promise xml_promise; - std::shared_future xml_future(xml_promise.get_future()); - - std::function fun = - [&xml_promise](const std_msgs::msg::String::SharedPtr msg) { - xml_promise.set_value(msg->data); - }; - - rclcpp::executors::SingleThreadedExecutor executor; - executor.add_node(ros2_node); - rclcpp::Subscription::SharedPtr description_subs; - // Transient local is similar to latching in ROS 1. - description_subs = ros2_node->create_subscription( - topic_name, rclcpp::QoS(1).transient_local(), fun); - - rclcpp::FutureReturnCode future_ret; - do { - RCLCPP_INFO(ros2_node->get_logger(), "Waiting messages on topic [%s].", topic_name.c_str()); - future_ret = executor.spin_until_future_complete(xml_future, timeout); - } while (rclcpp::ok() && future_ret != rclcpp::FutureReturnCode::SUCCESS); - - if (future_ret == rclcpp::FutureReturnCode::SUCCESS) { - req.set_sdf(xml_future.get()); - return true; - } else { - RCLCPP_ERROR( - ros2_node->get_logger(), "Failed to get XML from topic [%s].", topic_name.c_str()); - return false; - } -} - +// ROS interface for spawning entities into Gazebo. +// Suggested for use with roslaunch and loading entities from ROS param. +// If these are not needed, just use the `gz service` command line instead. int main(int _argc, char ** _argv) { - auto filtered_arguments = rclcpp::init_and_remove_ros_arguments(_argc, _argv); + rclcpp::init(_argc, _argv); auto ros2_node = rclcpp::Node::make_shared("ros_gz_sim"); - // Construct a new argc/argv pair from the flags that weren't parsed by ROS - // Gflags wants a mutable pointer to argv, which is why we can't use a - // vector of strings here - int filtered_argc = filtered_arguments.size(); - char ** filtered_argv = new char *[(filtered_argc + 1)]; - for (int ii = 0; ii < filtered_argc; ++ii) { - filtered_argv[ii] = new char[filtered_arguments[ii].size() + 1]; - snprintf( - filtered_argv[ii], - filtered_arguments[ii].size() + 1, "%s", filtered_arguments[ii].c_str()); - } - filtered_argv[filtered_argc] = nullptr; - gflags::AllowCommandLineReparsing(); gflags::SetUsageMessage( R"(Usage: create -world [arg] [-file FILE] [-param PARAM] [-topic TOPIC] [-string STRING] [-name NAME] [-allow_renaming RENAMING] [-x X] [-y Y] [-z Z] [-R ROLL] [-P PITCH] [-Y YAW])"); - gflags::ParseCommandLineFlags(&filtered_argc, &filtered_argv, false); - - // Free our temporary argc/argv pair - for (size_t ii = 0; filtered_argv[ii] != nullptr; ++ii) { - delete[] filtered_argv[ii]; - } - delete[] filtered_argv; - - // Declare ROS 2 parameters to be passed from launch file - ros2_node->declare_parameter("world", ""); - ros2_node->declare_parameter("file", ""); - ros2_node->declare_parameter("string", ""); - ros2_node->declare_parameter("topic", ""); - ros2_node->declare_parameter("name", ""); - ros2_node->declare_parameter("allow_renaming", false); - ros2_node->declare_parameter("x", static_cast(0)); - ros2_node->declare_parameter("y", static_cast(0)); - ros2_node->declare_parameter("z", static_cast(0)); - ros2_node->declare_parameter("R", static_cast(0)); - ros2_node->declare_parameter("P", static_cast(0)); - ros2_node->declare_parameter("Y", static_cast(0)); + gflags::ParseCommandLineFlags(&_argc, &_argv, true); // World - std::string world_name = ros2_node->get_parameter("world").as_string(); - if (world_name.empty() && !FLAGS_world.empty()) { - world_name = FLAGS_world; - } + std::string world_name = FLAGS_world; if (world_name.empty()) { // If caller doesn't provide a world name, get list of worlds from gz-sim server gz::transport::Node node; @@ -173,107 +94,91 @@ int main(int _argc, char ** _argv) // Request message gz::msgs::EntityFactory req; - // Get ROS parameters - std::string file_name = ros2_node->get_parameter("file").as_string(); - std::string xml_string = ros2_node->get_parameter("string").as_string(); - std::string topic_name = ros2_node->get_parameter("topic").as_string(); - // Check for the SDF filename or XML string or topic name - if (!file_name.empty()) { - req.set_sdf_filename(file_name); - } else if (!xml_string.empty()) { - req.set_sdf(xml_string); - } else if (!topic_name.empty()) { - // set XML string by fetching it from the given topic - if (!set_XML_from_topic(topic_name, ros2_node, req)) { + // File + if (!FLAGS_file.empty()) { + req.set_sdf_filename(FLAGS_file); + } else if (!FLAGS_param.empty()) { // Param + ros2_node->declare_parameter(FLAGS_param); + + std::string xmlStr; + if (ros2_node->get_parameter(FLAGS_param, xmlStr)) { + req.set_sdf(xmlStr); + } else { + RCLCPP_ERROR( + ros2_node->get_logger(), "Failed to get XML from param [%s].", FLAGS_param.c_str()); return -1; } - } else if (filtered_arguments.size() > 1) { - // Revert to Gflags, if ROS parameters aren't specified - // File - if (!FLAGS_file.empty()) { - req.set_sdf_filename(FLAGS_file); - } else if (!FLAGS_param.empty()) { // Param - ros2_node->declare_parameter(FLAGS_param); - std::string xmlStr; - if (ros2_node->get_parameter(FLAGS_param, xmlStr)) { - req.set_sdf(xmlStr); - } else { - RCLCPP_ERROR( - ros2_node->get_logger(), "Failed to get XML from param [%s].", FLAGS_param.c_str()); - return -1; - } - } else if (!FLAGS_string.empty()) { // string - req.set_sdf(FLAGS_string); - } else if (!FLAGS_topic.empty()) { // topic - // set XML string by fetching it from the given topic - if (!set_XML_from_topic(FLAGS_topic, ros2_node, req)) { - return -1; - } + } else if (!FLAGS_string.empty()) { // string + req.set_sdf(FLAGS_string); + } else if (!FLAGS_topic.empty()) { // topic + const auto timeout = std::chrono::seconds(1); + std::promise xml_promise; + std::shared_future xml_future(xml_promise.get_future()); + + std::function fun = + [&xml_promise](const std_msgs::msg::String::SharedPtr msg) { + xml_promise.set_value(msg->data); + }; + + rclcpp::executors::SingleThreadedExecutor executor; + executor.add_node(ros2_node); + rclcpp::Subscription::SharedPtr description_subs; + // Transient local is similar to latching in ROS 1. + description_subs = ros2_node->create_subscription( + FLAGS_topic, rclcpp::QoS(1).transient_local(), fun); + + rclcpp::FutureReturnCode future_ret; + do { + RCLCPP_INFO(ros2_node->get_logger(), "Waiting messages on topic [%s].", FLAGS_topic.c_str()); + future_ret = executor.spin_until_future_complete(xml_future, timeout); + } while (rclcpp::ok() && future_ret != rclcpp::FutureReturnCode::SUCCESS); + + if (future_ret == rclcpp::FutureReturnCode::SUCCESS) { + req.set_sdf(xml_future.get()); } else { RCLCPP_ERROR( - ros2_node->get_logger(), "Must specify either -file, -param, -string or -topic"); + ros2_node->get_logger(), "Failed to get XML from topic [%s].", FLAGS_topic.c_str()); return -1; } - // TODO(azeey) Deprecate use of command line flags in ROS 2 K-turtle in - // favor of ROS 2 parameters. } else { - RCLCPP_ERROR( - ros2_node->get_logger(), "Must specify either file, string or topic as ROS 2 parameters"); + RCLCPP_ERROR(ros2_node->get_logger(), "Must specify either -file, -param, -stdin or -topic"); return -1; } // Pose - double x_coords = ros2_node->get_parameter("x").as_double(); - double y_coords = ros2_node->get_parameter("y").as_double(); - double z_coords = ros2_node->get_parameter("z").as_double(); - double roll = ros2_node->get_parameter("R").as_double(); - double pitch = ros2_node->get_parameter("P").as_double(); - double yaw = ros2_node->get_parameter("Y").as_double(); - - FLAGS_x = (x_coords != 0.0) ? x_coords : FLAGS_x; - FLAGS_y = (y_coords != 0.0) ? y_coords : FLAGS_y; - FLAGS_z = (z_coords != 0.0) ? z_coords : FLAGS_z; - FLAGS_R = (roll != 0.0) ? roll : FLAGS_R; - FLAGS_P = (pitch != 0.0) ? pitch : FLAGS_P; - FLAGS_Y = (yaw != 0.0) ? yaw : FLAGS_Y; gz::math::Pose3d pose{FLAGS_x, FLAGS_y, FLAGS_z, FLAGS_R, FLAGS_P, FLAGS_Y}; gz::msgs::Set(req.mutable_pose(), pose); // Name - std::string entity_name = ros2_node->get_parameter("name").as_string(); - if (!entity_name.empty()) { - req.set_name(entity_name); - } else { + if (!FLAGS_name.empty()) { req.set_name(FLAGS_name); } - // Allow Renaming - bool allow_renaming = ros2_node->get_parameter("allow_renaming").as_bool(); - req.set_allow_renaming((allow_renaming || FLAGS_allow_renaming)); + if (FLAGS_allow_renaming) { + req.set_allow_renaming(FLAGS_allow_renaming); + } // Request gz::transport::Node node; gz::msgs::Boolean rep; bool result; unsigned int timeout = 5000; + bool executed = node.Request(service, req, timeout, rep, result); - while(rclcpp::ok()) { - if (node.Request(service, req, timeout, rep, result)) { - if (result && rep.data()) { - RCLCPP_INFO(ros2_node->get_logger(), "Entity creation successfull."); - return 0; - } else { - RCLCPP_ERROR( - ros2_node->get_logger(), "Entity creation failed.\n %s", - req.DebugString().c_str()); - return 1; - } + if (executed) { + if (result && rep.data()) { + RCLCPP_INFO(ros2_node->get_logger(), "Requested creation of entity."); } else { - RCLCPP_WARN( - ros2_node->get_logger(), "Waiting for service [%s] to become available ...", - service.c_str()); + RCLCPP_ERROR( + ros2_node->get_logger(), "Failed request to create entity.\n %s", + req.DebugString().c_str()); } + } else { + RCLCPP_ERROR( + ros2_node->get_logger(), "Request to create entity from service [%s] timed out..\n %s", + service.c_str(), req.DebugString().c_str()); } - RCLCPP_INFO(ros2_node->get_logger(), "Entity creation was interrupted."); + RCLCPP_INFO(ros2_node->get_logger(), "OK creation of entity."); + return 0; } diff --git a/ros_gz_sim/src/gzserver.cpp b/ros_gz_sim/src/gzserver.cpp deleted file mode 100644 index aa4361f51..000000000 --- a/ros_gz_sim/src/gzserver.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// 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 -#include -#include -#include -#include -#include -#include - -// ROS node that executes a gz-sim Server given a world SDF file or string. -namespace ros_gz_sim -{ -class GzServer : public rclcpp::Node -{ -public: - // Class constructor. - explicit GzServer(const rclcpp::NodeOptions & options) - : Node("gzserver", options) - { - thread_ = std::thread(std::bind(&GzServer::OnStart, this)); - } - -public: - // Class destructor. - ~GzServer() - { - // Make sure to join the thread on shutdown. - if (thread_.joinable()) { - thread_.join(); - } - } - -public: - /// \brief Run the gz sim server. - void OnStart() - { - auto world_sdf_file = this->declare_parameter("world_sdf_file", ""); - auto world_sdf_string = this->declare_parameter("world_sdf_string", ""); - - gz::common::Console::SetVerbosity(4); - gz::sim::ServerConfig server_config; - - if (!world_sdf_file.empty()) { - server_config.SetSdfFile(world_sdf_file); - } else if (!world_sdf_string.empty()) { - server_config.SetSdfString(world_sdf_string); - } else { - RCLCPP_ERROR( - this->get_logger(), - "Must specify either 'world_sdf_file' or 'world_sdf_string'"); - rclcpp::shutdown(); - return; - } - - gz::sim::Server server(server_config); - server.Run(true /*blocking*/, 0, false /*paused*/); - rclcpp::shutdown(); - } - -private: - /// \brief We don't want to block the ROS thread. - std::thread thread_; -}; -} // namespace ros_gz_sim - -RCLCPP_COMPONENTS_REGISTER_NODE(ros_gz_sim::GzServer) diff --git a/ros_gz_sim/test/test_create.cpp b/ros_gz_sim/test/test_create.cpp deleted file mode 100644 index ee92bf39c..000000000 --- a/ros_gz_sim/test/test_create.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2023 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 -#include -#include -#include -#include -#include -#include - -// Simple application that provides a `/create` service and prints out the -// sdf_filename of the request. This works in conjection with -// test_create_node.launch.py -int main() -{ - std::mutex m; - std::condition_variable cv; - bool test_complete = false; - - gz::transport::Node node; - auto cb = std::function( - [&]( - const gz::msgs::EntityFactory & _req, - gz::msgs::Boolean & _res) -> bool { - std::cout << _req.sdf_filename() << std::endl; - _res.set_data(true); - - { - std::lock_guard lk(m); - test_complete = true; - } - cv.notify_one(); - return true; - }); - - node.Advertise("/world/default/create", cb); - // wait until we receive a message. - std::unique_lock lk(m); - cv.wait(lk, [&] {return test_complete;}); - // Sleep so that the service response can be sent before exiting. - std::this_thread::sleep_for(std::chrono::seconds(1)); -} diff --git a/ros_gz_sim/test/test_create_node.launch.py b/ros_gz_sim/test/test_create_node.launch.py deleted file mode 100644 index 79c31c5c1..000000000 --- a/ros_gz_sim/test/test_create_node.launch.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright 2023 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. - -import unittest - -from launch import LaunchDescription - -from launch_ros.actions import Node - -import launch_testing - - -def generate_test_description(): - expected_file_name = 'nonexistent/long/file_name' - create = Node(package='ros_gz_sim', executable='create', - parameters=[{'world': 'default', 'file': expected_file_name}], output='screen') - test_create = Node(package='ros_gz_sim', executable='test_create', output='screen') - return LaunchDescription([ - create, - test_create, - launch_testing.util.KeepAliveProc(), - launch_testing.actions.ReadyToTest(), - ]), locals() - - -class WaitForTests(unittest.TestCase): - - def test_termination(self, test_create, proc_info): - proc_info.assertWaitForShutdown(process=test_create, timeout=200) - - -@launch_testing.post_shutdown_test() -class CreateTest(unittest.TestCase): - - def test_output(self, expected_file_name, test_create, proc_output): - launch_testing.asserts.assertInStdout(proc_output, expected_file_name, test_create) diff --git a/ros_gz_sim_demos/CHANGELOG.rst b/ros_gz_sim_demos/CHANGELOG.rst index e8d270145..36f3b6c8d 100644 --- a/ros_gz_sim_demos/CHANGELOG.rst +++ b/ros_gz_sim_demos/CHANGELOG.rst @@ -2,115 +2,30 @@ Changelog for package ros1_gz_sim_demos ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -1.0.7 (2024-11-08) ------------------- - -1.0.6 (2024-10-31) ------------------- - -1.0.5 (2024-10-14) ------------------- - -1.0.4 (2024-08-29) ------------------- - -1.0.3 (2024-07-22) ------------------- +0.244.16 (2024-07-22) +--------------------- -1.0.2 (2024-07-03) ------------------- -* Prepare for 1.0.0 Release (`#495 `_) -* Use gz_vendor packages (`#531 `_) +0.244.15 (2024-07-03) +--------------------- * [backport Humble] Create bridge for GPSFix msg (`#316 `_) (`#538 `_) Co-authored-by: Rousseau Vincent -* [backport Iron] Create bridge for GPSFix msg (`#316 `_) (`#537 `_) - Co-authored-by: Rousseau Vincent -* 0.244.14 -* Changelog -* 0.244.13 -* Changelog -* Remove deprecations using ros_gz_sim_create (`#476 `_) -* 0.244.12 -* Changelog -* 0.246.0 -* Update changelogs -* Added more topic to the bridge (`#422 `_) -* Fix incorrect subscription on demo (`#405 `_) (`#408 `_) - This PR fixes an incorrect subscription on one of the demos. Running - ``` - ros2 launch ros_gz_sim_demos gpu_lidar_bridge.launch.py - ``` - causes rviz2 to crash and exit with the error: - ``` - rviz2-3] - [rviz2-3] >>> [rcutils|error_handling.c:108] rcutils_set_error_state() - [rviz2-3] This error state is being overwritten: - [rviz2-3] - [rviz2-3] 'create_subscription() called for existing topic name rt/lidar with incompatible type sensor_msgs::msg::dds\_::PointCloud2\_, at ./src/subscription.cpp:146, at ./src/rcl/subscription.c:108' - [rviz2-3] - [rviz2-3] with this new error message: - [rviz2-3] - [rviz2-3] 'invalid allocator, at ./src/rcl/subscription.c:218' - [rviz2-3] - [rviz2-3] rcutils_reset_error() should be called after error handling to avoid this. - ``` - This is due to an incorrect subscription on the part of the demo. This - PR fixes it by getting a subscription to the right topic for the - pointcloud display. (`lidar/points` instead of `lidar`). Was tested on - garden + humble. - Co-authored-by: Arjo Chakravarty -* Port: humble to ros2 (`#386 `_) -* Merge branch 'humble' into mjcarroll/humble_to_ros2 -* Update maintainers (`#376 `_) -* Rename 'ign gazebo' to 'gz sim' (`#343 `_) -* Create bridge for GPSFix msg (`#316 `_) -* Humble ➡️ ROS2 (`#323 `_) - Humble ➡️ ROS2 -* Merge branch 'humble' into ports/humble_to_ros2 -* Fixed ros_gz_sim_demos launch files (`#319 `_) -* 0.245.0 -* Changelog -* humble to ros2 (`#311 `_) - Co-authored-by: Michael Carroll -* Merge remote-tracking branch 'origin/humble' into ahcorde/humble_to_ros2 -* Remove all ignition references on ROS 2 branch (`#302 `_) - * Remove all shims - * Update CMakeLists and package.xml for garden - * Complete garden gz renaming - * Drop fortress CI -* Contributors: Addisu Z. Taddese, Aditya Pande, Alejandro Hernández Cordero, Clyde McQueen, Jose Luis Rivero, Michael Carroll, Rousseau Vincent, ahcorde - -1.0.0 (2024-04-24) ------------------- -* Use gz_vendor packages (`#531 `_) -* Remove deprecations using ros_gz_sim_create (`#476 `_) -* Contributors: Addisu Z. Taddese, Alejandro Hernández Cordero +* Contributors: Alejandro Hernández Cordero -0.246.0 (2023-08-31) --------------------- +0.244.14 (2024-04-08) +--------------------- + +0.244.13 (2024-01-23) +--------------------- + +0.244.12 (2023-12-13) +--------------------- +* [backport Humble] Added more topic to the bridge (`#422 `_) * Added more topic to the bridge (`#422 `_) -* Fix incorrect subscription on demo (`#405 `_) (`#408 `_) - Co-authored-by: Arjo Chakravarty -* Port: humble to ros2 (`#386 `_) -* Merge branch 'humble' into mjcarroll/humble_to_ros2 -* Update maintainers (`#376 `_) -* Rename 'ign gazebo' to 'gz sim' (`#343 `_) -* Create bridge for GPSFix msg (`#316 `_) -* Humble ➡️ ROS2 (`#323 `_) -* Fixed ros_gz_sim_demos launch files (`#319 `_) -* Contributors: Aditya Pande, Alejandro Hernández Cordero, Clyde McQueen, Michael Carroll, Rousseau Vincent, ahcorde - -0.245.0 (2022-10-12) --------------------- -* humble to ros2 (`#311 `_) - Co-authored-by: Michael Carroll -* Merge remote-tracking branch 'origin/humble' into ahcorde/humble_to_ros2 -* Remove all ignition references on ROS 2 branch (`#302 `_) - * Remove all shims - * Update CMakeLists and package.xml for garden - * Complete garden gz renaming - * Drop fortress CI -* Contributors: Alejandro Hernández Cordero, Michael Carroll, ahcorde +* Fix incorrect subscription on demo (`#405 `_) +* Contributors: Alejandro Hernández Cordero, Arjo Chakravarty + +0.244.11 (2023-05-23) +--------------------- 0.244.10 (2023-05-03) --------------------- diff --git a/ros_gz_sim_demos/launch/joint_states.launch.py b/ros_gz_sim_demos/launch/joint_states.launch.py index 7b61c350e..95bf1906d 100644 --- a/ros_gz_sim_demos/launch/joint_states.launch.py +++ b/ros_gz_sim_demos/launch/joint_states.launch.py @@ -63,8 +63,10 @@ def generate_launch_description(): spawn = Node( package='ros_gz_sim', executable='create', - parameters=[{'name': 'rrbot', - 'topic': 'robot_description'}], + arguments=[ + '-name', 'rrbot', + '-topic', 'robot_description', + ], output='screen', ) diff --git a/ros_gz_sim_demos/launch/robot_description_publisher.launch.py b/ros_gz_sim_demos/launch/robot_description_publisher.launch.py index 7fa6fc09d..882cdee38 100755 --- a/ros_gz_sim_demos/launch/robot_description_publisher.launch.py +++ b/ros_gz_sim_demos/launch/robot_description_publisher.launch.py @@ -77,12 +77,12 @@ def generate_launch_description(): # Spawn spawn = Node(package='ros_gz_sim', executable='create', - parameters=[{ - 'name': 'my_custom_model', - 'x': 1.2, - 'z': 2.3, - 'Y': 3.4, - 'topic': '/robot_description'}], + arguments=[ + '-name', 'my_custom_model', + '-x', '1.2', + '-z', '2.3', + '-Y', '3.4', + '-topic', '/robot_description'], output='screen') return LaunchDescription([ diff --git a/ros_gz_sim_demos/launch/tf_bridge.launch.py b/ros_gz_sim_demos/launch/tf_bridge.launch.py index 42f76cdc6..e134864ee 100644 --- a/ros_gz_sim_demos/launch/tf_bridge.launch.py +++ b/ros_gz_sim_demos/launch/tf_bridge.launch.py @@ -27,7 +27,7 @@ def generate_launch_description(): # Launch gazebo ExecuteProcess( cmd=[ - 'gz', 'sim', '-r', + 'ign', 'gazebo', '-r', os.path.join( pkg_ros_gz_sim_demos, 'models', diff --git a/ros_gz_sim_demos/package.xml b/ros_gz_sim_demos/package.xml index d04eddb4a..34f57eff1 100644 --- a/ros_gz_sim_demos/package.xml +++ b/ros_gz_sim_demos/package.xml @@ -1,16 +1,17 @@ ros_gz_sim_demos - 1.0.7 + 0.244.16 Demos using Gazebo Sim simulation with ROS. Apache 2.0 - Aditya Pande - Alejandro Hernandez - - Louise Poubel + Louise Poubel ament_cmake - gz_sim_vendor + + ignition-gazebo6 + ignition-gazebo6 + + ignition-gazebo5 image_transport_plugins robot_state_publisher diff --git a/ros_ign/CHANGELOG.rst b/ros_ign/CHANGELOG.rst new file mode 100644 index 000000000..2341fa2dd --- /dev/null +++ b/ros_ign/CHANGELOG.rst @@ -0,0 +1,44 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package ros_ign +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +0.244.16 (2024-07-22) +--------------------- + +0.244.15 (2024-07-03) +--------------------- + +0.244.14 (2024-04-08) +--------------------- + +0.244.13 (2024-01-23) +--------------------- + +0.244.12 (2023-12-13) +--------------------- + +0.244.11 (2023-05-23) +--------------------- + +0.244.10 (2023-05-03) +--------------------- + +0.244.9 (2022-11-03) +-------------------- + +0.244.8 (2022-10-28) +-------------------- + +0.244.7 (2022-10-12) +-------------------- + +0.244.6 (2022-09-14) +-------------------- +* Restructured directories (`#296 `_) +* Contributors: Alejandro Hernández Cordero + +0.244.5 (2022-09-12) +-------------------- +* ign -> gz : ros_gz Migration (Shims) (`#281 `_) + Co-authored-by: Louise Poubel +* Contributors: methylDragon diff --git a/ros_ign/CMakeLists.txt b/ros_ign/CMakeLists.txt new file mode 100644 index 000000000..80524e92e --- /dev/null +++ b/ros_ign/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.5) +project(ros_ign) +find_package(ament_cmake REQUIRED) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/ros_ign/README.md b/ros_ign/README.md new file mode 100644 index 000000000..7f2dcc880 --- /dev/null +++ b/ros_ign/README.md @@ -0,0 +1,2 @@ +# This is a shim meta-package +For [ros_gz](https://github.com/gazebosim/ros_gz/tree/ros2/ros_gz) diff --git a/ros_ign/package.xml b/ros_ign/package.xml new file mode 100644 index 000000000..e7e16863a --- /dev/null +++ b/ros_ign/package.xml @@ -0,0 +1,24 @@ + + + + ros_ign + 0.244.16 + Shim meta-package to redirect to ros_gz. + Brandon Ong + Apache 2.0 + + ament_cmake + ros_gz + + ros_ign_bridge + ros_ign_gazebo + ros_ign_gazebo_demos + ros_ign_image + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/ros_ign_bridge/CHANGELOG.rst b/ros_ign_bridge/CHANGELOG.rst new file mode 100644 index 000000000..caab9d06a --- /dev/null +++ b/ros_ign_bridge/CHANGELOG.rst @@ -0,0 +1,82 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package ros_ign_bridge +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +0.244.16 (2024-07-22) +--------------------- + +0.244.15 (2024-07-03) +--------------------- + +0.244.14 (2024-04-08) +--------------------- + +0.244.13 (2024-01-23) +--------------------- + +0.244.12 (2023-12-13) +--------------------- + +0.244.11 (2023-05-23) +--------------------- + +0.244.10 (2023-05-03) +--------------------- + +0.244.9 (2022-11-03) +-------------------- + +0.244.8 (2022-10-28) +-------------------- + +0.244.7 (2022-10-12) +-------------------- +* Merge branch 'ros2' into ports/galactic_to_ros2 +* Merge branch 'galactic' into ports/galactic_to_ros2 +* Make tests faster and more robust (`#272 `_) +* Improve documentation around yaml configuration (`#271 `_) +* Fix small typo in bridge README (`#270 `_) +* Port NavSat (`#224 `_) from ROS 1 to ROS 2 (`#268 `_) + Co-authored-by: Tyler Howell <76003804+TyHowellWork@users.noreply.github.com> +* Add ParamVec and bridge from Ignition (`#261 `_) + * Introduces `ros_ign_interfaces::msg::ParamVec` for storing a list of Parameters that are int, bool, double, or string. + * Introduces bridge for `ignition::msgs::param` to `ros_ign_interfaces::msg::ParamVec` + * Introduces bridge for `ignition::msgs::param_v` to `ros_ign_interfaces::msg::ParamVec` +* Add support for converting Any <-> ParamValue (`#260 `_) + * Add support for converting Any <-> ParamValue +* Feature: set QoS options to override durability (`#250 `_) (`#259 `_) + Co-authored-by: Louise Poubel + Co-authored-by: Daisuke Nishimatsu <42202095+wep21@users.noreply.github.com> +* Add node component and yaml-configured bridge node (`#238 `_) + * Refactor in support of adding yaml-configured node +* Add rssi to Dataframe.msg (`#249 `_) + * Adding rssi field to ros_ign_interfaces/Dataframe.msg +* Use the python generator for tests as well (`#234 `_) + * Use the python generator for tests as well +* Generate boilerplate files from Python scripts (`#233 `_) + The way that we add factories can be a bit error-prone, as there are a lot of strings that cannot be checked at compilation time. This changes several of the boilerplate files to be generated automatically by python scripts, in line with how ros1_bridge does it. +* [galactic] Backport GuiCamera, StringVec, TrackVisual, VideoRecord (`#241 `_) + * [ros_ign_interfaces] Add more interface definitions. + * Add converion functions for the added messages + * Update the factory factory function with the new messages + * Add new messages to docs + * Add test cases for the new messages conversions + Co-authored-by: Ivan Santiago Paunovic +* Add Dataframe message and bridging (`#239 `_) +* Factory interface needs virtual destructor (`#232 `_) +* Optional "lazy" bridge subscribers (`#225 `_) + This allows for the bridge to be created in such a way that it is "lazy". In this case "lazy" means: + * The publication (output) side of the bridge is always on and actively looking for subscriptions. + * The subscription (input) side of the bridge is only turned on in the case that there are subscriptions on the output side. +* Contributors: Carlos Agüero, Louise Poubel, Michael Carroll + +0.244.6 (2022-09-14) +-------------------- +* Restructured directories (`#296 `_) +* Contributors: Alejandro Hernández Cordero + +0.244.5 (2022-09-12) +-------------------- +* ign -> gz : ros_gz Migration (Shims) (`#281 `_) + Co-authored-by: Louise Poubel +* Contributors: methylDragon diff --git a/ros_ign_bridge/CMakeLists.txt b/ros_ign_bridge/CMakeLists.txt new file mode 100644 index 000000000..7be505b07 --- /dev/null +++ b/ros_ign_bridge/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.5) +project(ros_ign_bridge) + +# Default to C++14 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 14) +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(ament_index_cpp REQUIRED) + +add_executable(parameter_bridge src/parameter_bridge_shim.cpp) +ament_target_dependencies(parameter_bridge ament_index_cpp) + +add_executable(static_bridge src/static_bridge_shim.cpp) +ament_target_dependencies(static_bridge ament_index_cpp) + +ament_export_dependencies(ament_index_cpp ros_gz_bridge) + +install(TARGETS + parameter_bridge + static_bridge + DESTINATION lib/${PROJECT_NAME} +) + +ament_package() diff --git a/ros_ign_bridge/README.md b/ros_ign_bridge/README.md new file mode 100644 index 000000000..e93d8f261 --- /dev/null +++ b/ros_ign_bridge/README.md @@ -0,0 +1,2 @@ +# This is a shim package +For [ros_gz_bridge](https://github.com/gazebosim/ros_gz/tree/ros2/ros_gz_bridge) diff --git a/ros_ign_bridge/package.xml b/ros_ign_bridge/package.xml new file mode 100644 index 000000000..3f08a3768 --- /dev/null +++ b/ros_ign_bridge/package.xml @@ -0,0 +1,19 @@ + + + + ros_ign_bridge + 0.244.16 + Shim package to redirect to ros_gz_bridge. + Brandon Ong + + Apache 2.0 + + ament_cmake + ament_index_cpp + + ros_gz_bridge + + + ament_cmake + + diff --git a/ros_ign_bridge/src/parameter_bridge_shim.cpp b/ros_ign_bridge/src/parameter_bridge_shim.cpp new file mode 100644 index 000000000..df6882bb4 --- /dev/null +++ b/ros_ign_bridge/src/parameter_bridge_shim.cpp @@ -0,0 +1,43 @@ +// Copyright 2022 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. + +// Shim to redirect "ros_ign_bridge parameter_bridge" call to "ros_gz_bridge parameter_bridge" + +#include +#include +#include + +#include + + +int main(int argc, char * argv[]) +{ + std::stringstream cli_call; + + cli_call << ament_index_cpp::get_package_prefix("ros_gz_bridge") + << "/lib/ros_gz_bridge/parameter_bridge"; + + if (argc > 1) + { + for (int i = 1; i < argc; i++) + cli_call << " " << argv[i]; + } + + std::cerr << "[ros_ign_bridge] is deprecated! " + << "Redirecting to use [ros_gz_bridge] instead!" + << std::endl << std::endl; + system(cli_call.str().c_str()); + + return 0; +} diff --git a/ros_ign_bridge/src/static_bridge_shim.cpp b/ros_ign_bridge/src/static_bridge_shim.cpp new file mode 100644 index 000000000..4ee0fdde1 --- /dev/null +++ b/ros_ign_bridge/src/static_bridge_shim.cpp @@ -0,0 +1,43 @@ +// Copyright 2022 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. + +// Shim to redirect "ros_ign_bridge static_bridge" call to "ros_gz_bridge static_bridge" + +#include +#include +#include + +#include + + +int main(int argc, char * argv[]) +{ + std::stringstream cli_call; + + cli_call << ament_index_cpp::get_package_prefix("ros_gz_bridge") + << "/lib/ros_gz_bridge/static_bridge"; + + if (argc > 1) + { + for (int i = 1; i < argc; i++) + cli_call << " " << argv[i]; + } + + std::cerr << "[ros_ign_bridge] is deprecated! " + << "Redirecting to use [ros_gz_bridge] instead!" + << std::endl << std::endl; + system(cli_call.str().c_str()); + + return 0; +} diff --git a/ros_ign_gazebo/CHANGELOG.rst b/ros_ign_gazebo/CHANGELOG.rst new file mode 100644 index 000000000..ab173c5ef --- /dev/null +++ b/ros_ign_gazebo/CHANGELOG.rst @@ -0,0 +1,62 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package ros_ign_gazebo +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +0.244.16 (2024-07-22) +--------------------- + +0.244.15 (2024-07-03) +--------------------- + +0.244.14 (2024-04-08) +--------------------- + +0.244.13 (2024-01-23) +--------------------- + +0.244.12 (2023-12-13) +--------------------- +* Add support for Harmonic/Humble pairing (`#462 `_) +* Contributors: Addisu Z. Taddese + +0.244.11 (2023-05-23) +--------------------- + +0.244.10 (2023-05-03) +--------------------- + +0.244.9 (2022-11-03) +-------------------- + +0.244.8 (2022-10-28) +-------------------- + +0.244.7 (2022-10-12) +-------------------- +* Fix launch substitutions for ign_args (`#309 `_) + * Fix launch substitutions for ign_args +* Merge branch 'ros2' into ports/galactic_to_ros2 +* Merge branch 'galactic' into ports/galactic_to_ros2 +* Add ROS2 version of Stopwatch (`#287 `_) +* Add debugger option in launch (`#286 `_) + * add debugger option in launch + * remove xterm dependency; rely on x-terminal-emulator from update-alternatives +* [galactic] Backport: Add std_msgs as dependency of ros_ign_gazebo (`#264 `_) + Co-authored-by: Kenji Brameld +* Contributors: Michael Carroll, andermi + +0.244.6 (2022-09-14) +-------------------- +* Restructured directories (`#296 `_) +* Contributors: Alejandro Hernández Cordero + +0.244.5 (2022-09-12) +-------------------- +* Fix missing msgs include and packages.xml deps (`#292 `_) + * Fix missing msgs include and packages.xml deps + * Add additional conditions to support gz sim invocation + * Fix cpplint +* Add missing GZ_VERSION ticktocks (`#289 `_) +* ign -> gz : ros_gz Migration (Shims) (`#281 `_) + Co-authored-by: Louise Poubel +* Contributors: methylDragon diff --git a/ros_ign_gazebo/CMakeLists.txt b/ros_ign_gazebo/CMakeLists.txt new file mode 100644 index 000000000..52fefe619 --- /dev/null +++ b/ros_ign_gazebo/CMakeLists.txt @@ -0,0 +1,58 @@ +cmake_minimum_required(VERSION 3.5) +project(ros_ign_gazebo) + +# Default to C++14 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 14) +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(ament_index_cpp REQUIRED) + +add_executable(create + src/create_shim.cpp +) +ament_target_dependencies(create ament_index_cpp) + +ament_export_dependencies( + ament_index_cpp + ros_gz_bridge +) + +# TODO(CH3): Deprecated. Remove on tock. +if("$ENV{GZ_VERSION}" STREQUAL "" AND NOT "$ENV{IGNITION_VERSION}" STREQUAL "") + message(DEPRECATION "Environment variable [IGNITION_VERSION] is deprecated. Use [GZ_VERSION] instead.") + set(ENV{GZ_VERSION} $ENV{IGNITION_VERSION}) +endif() + +# Edifice +if("$ENV{GZ_VERSION}" STREQUAL "edifice") + find_package(ignition-gazebo5 REQUIRED) + message(STATUS "Compiling against Gazebo Edifice") +# Garden +elseif("$ENV{GZ_VERSION}" STREQUAL "garden") + find_package(gz-sim7 REQUIRED) + message(STATUS "Compiling against Gazebo Garden") +elseif("$ENV{GZ_VERSION}" STREQUAL "harmonic") + find_package(gz-sim8 REQUIRED) + message(STATUS "Compiling against Gazebo Harmonic") +# Default to Fortress +else() + find_package(ignition-gazebo6 REQUIRED) + message(STATUS "Compiling against Gazebo Fortress") +endif() + +install(FILES + "${CMAKE_CURRENT_SOURCE_DIR}/launch/ign_gazebo.launch.py" + DESTINATION share/${PROJECT_NAME}/launch +) + +install(TARGETS + create + DESTINATION lib/${PROJECT_NAME} +) + +ament_package() diff --git a/ros_ign_gazebo/README.md b/ros_ign_gazebo/README.md new file mode 100644 index 000000000..2df45d4a9 --- /dev/null +++ b/ros_ign_gazebo/README.md @@ -0,0 +1,2 @@ +# This is a shim package +For [ros_gz_sim](https://github.com/gazebosim/ros_gz/tree/ros2/ros_gz_sim) diff --git a/ros_ign_gazebo/launch/ign_gazebo.launch.py b/ros_ign_gazebo/launch/ign_gazebo.launch.py new file mode 100644 index 000000000..e8cb53426 --- /dev/null +++ b/ros_ign_gazebo/launch/ign_gazebo.launch.py @@ -0,0 +1,33 @@ +# Copyright 2020 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. + +"""Launch Gazebo Sim with command line arguments.""" + +import os + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource + +def generate_launch_description(): + ros_gz_sim_dir = get_package_share_directory('ros_gz_sim') + launch_dir = os.path.join(ros_gz_sim_dir, 'launch') + + return LaunchDescription([ + IncludeLaunchDescription( + PythonLaunchDescriptionSource(os.path.join(launch_dir, 'gz_sim.launch.py')) + ) + ]) diff --git a/ros_ign_gazebo/package.xml b/ros_ign_gazebo/package.xml new file mode 100644 index 000000000..62079e39f --- /dev/null +++ b/ros_ign_gazebo/package.xml @@ -0,0 +1,19 @@ + + + + ros_ign_gazebo + 0.244.16 + Shim package to redirect to ros_gz_sim. + Brandon Ong + + Apache 2.0 + + ament_cmake + ament_index_cpp + + ros_gz_sim + + + ament_cmake + + diff --git a/ros_ign_gazebo/src/create_shim.cpp b/ros_ign_gazebo/src/create_shim.cpp new file mode 100644 index 000000000..8a6d366a2 --- /dev/null +++ b/ros_ign_gazebo/src/create_shim.cpp @@ -0,0 +1,43 @@ +// Copyright 2022 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. + +// Shim to redirect "ros_ign_bridge parameter_bridge" call to "ros_gz_sim parameter_bridge" + +#include +#include +#include + +#include + + +int main(int argc, char * argv[]) +{ + std::stringstream cli_call; + + cli_call << ament_index_cpp::get_package_prefix("ros_gz_sim") + << "/lib/ros_gz_sim/create"; + + if (argc > 1) + { + for (int i = 1; i < argc; i++) + cli_call << " " << argv[i]; + } + + std::cerr << "[ros_ign_gazebo] is deprecated! " + << "Redirecting to use [ros_gz_sim] instead!" + << std::endl << std::endl; + system(cli_call.str().c_str()); + + return 0; +} diff --git a/ros_ign_gazebo_demos/CHANGELOG.rst b/ros_ign_gazebo_demos/CHANGELOG.rst new file mode 100644 index 000000000..305cd9f86 --- /dev/null +++ b/ros_ign_gazebo_demos/CHANGELOG.rst @@ -0,0 +1,50 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package ros_ign_gazebo_demos +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +0.244.16 (2024-07-22) +--------------------- + +0.244.15 (2024-07-03) +--------------------- + +0.244.14 (2024-04-08) +--------------------- + +0.244.13 (2024-01-23) +--------------------- + +0.244.12 (2023-12-13) +--------------------- + +0.244.11 (2023-05-23) +--------------------- + +0.244.10 (2023-05-03) +--------------------- +* Humbly bringing the Joy to gazebo. (`#353 `_) +* Contributors: Benjamin Perseghetti + +0.244.9 (2022-11-03) +-------------------- + +0.244.8 (2022-10-28) +-------------------- + +0.244.7 (2022-10-12) +-------------------- +* Merge branch 'galactic' into ports/galactic_to_ros2 +* Port NavSat (`#224 `_) from ROS 1 to ROS 2 (`#268 `_) + Co-authored-by: Tyler Howell <76003804+TyHowellWork@users.noreply.github.com> +* Contributors: Michael Carroll + +0.244.6 (2022-09-14) +-------------------- +* Restructured directories (`#296 `_) +* Contributors: Alejandro Hernández Cordero + +0.244.5 (2022-09-12) +-------------------- +* ign -> gz : ros_gz Migration (Shims) (`#281 `_) + Co-authored-by: Louise Poubel +* Contributors: methylDragon diff --git a/ros_ign_gazebo_demos/CMakeLists.txt b/ros_ign_gazebo_demos/CMakeLists.txt new file mode 100644 index 000000000..68b53fed5 --- /dev/null +++ b/ros_ign_gazebo_demos/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.5) +project(ros_ign_gazebo_demos) + +find_package(ament_cmake REQUIRED) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() +endif() + +install( + DIRECTORY + launch/ + DESTINATION share/${PROJECT_NAME}/launch +) + +ament_package() diff --git a/ros_ign_gazebo_demos/README.md b/ros_ign_gazebo_demos/README.md new file mode 100644 index 000000000..18ccd4242 --- /dev/null +++ b/ros_ign_gazebo_demos/README.md @@ -0,0 +1,2 @@ +# This is a shim package +For [ros_gz_sim_demos](https://github.com/gazebosim/ros_gz/tree/ros2/ros_gz_sim_demos) diff --git a/ros_ign_gazebo_demos/launch/air_pressure.launch.py b/ros_ign_gazebo_demos/launch/air_pressure.launch.py new file mode 100644 index 000000000..f2e97f762 --- /dev/null +++ b/ros_ign_gazebo_demos/launch/air_pressure.launch.py @@ -0,0 +1,34 @@ +# Copyright 2022 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. + +import os + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource + + +def generate_launch_description(): + print('ros_ign_gazebo_demos is deprecated! Please use ros_gz_sim_demos instead!') + + pkg_ros_gz_sim_demos = get_package_share_directory('ros_gz_sim_demos') + + return LaunchDescription([ + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim_demos, 'launch', 'air_pressure.launch.py')), + ) + ]) diff --git a/ros_ign_gazebo_demos/launch/battery.launch.py b/ros_ign_gazebo_demos/launch/battery.launch.py new file mode 100644 index 000000000..b85a0f215 --- /dev/null +++ b/ros_ign_gazebo_demos/launch/battery.launch.py @@ -0,0 +1,34 @@ +# Copyright 2022 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. + +import os + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource + + +def generate_launch_description(): + print('ros_ign_gazebo_demos is deprecated! Please use ros_gz_sim_demos instead!') + + pkg_ros_gz_sim_demos = get_package_share_directory('ros_gz_sim_demos') + + return LaunchDescription([ + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim_demos, 'launch', 'battery.launch.py')), + ) + ]) diff --git a/ros_ign_gazebo_demos/launch/camera.launch.py b/ros_ign_gazebo_demos/launch/camera.launch.py new file mode 100644 index 000000000..40cf2132b --- /dev/null +++ b/ros_ign_gazebo_demos/launch/camera.launch.py @@ -0,0 +1,34 @@ +# Copyright 2022 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. + +import os + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource + + +def generate_launch_description(): + print('ros_ign_gazebo_demos is deprecated! Please use ros_gz_sim_demos instead!') + + pkg_ros_gz_sim_demos = get_package_share_directory('ros_gz_sim_demos') + + return LaunchDescription([ + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim_demos, 'launch', 'camera.launch.py')), + ) + ]) diff --git a/ros_ign_gazebo_demos/launch/depth_camera.launch.py b/ros_ign_gazebo_demos/launch/depth_camera.launch.py new file mode 100644 index 000000000..0eda5b4ba --- /dev/null +++ b/ros_ign_gazebo_demos/launch/depth_camera.launch.py @@ -0,0 +1,34 @@ +# Copyright 2022 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. + +import os + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource + + +def generate_launch_description(): + print('ros_ign_gazebo_demos is deprecated! Please use ros_gz_sim_demos instead!') + + pkg_ros_gz_sim_demos = get_package_share_directory('ros_gz_sim_demos') + + return LaunchDescription([ + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim_demos, 'launch', 'depth_camera.launch.py')), + ) + ]) diff --git a/ros_ign_gazebo_demos/launch/diff_drive.launch.py b/ros_ign_gazebo_demos/launch/diff_drive.launch.py new file mode 100644 index 000000000..aa1f70adf --- /dev/null +++ b/ros_ign_gazebo_demos/launch/diff_drive.launch.py @@ -0,0 +1,34 @@ +# Copyright 2022 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. + +import os + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource + + +def generate_launch_description(): + print('ros_ign_gazebo_demos is deprecated! Please use ros_gz_sim_demos instead!') + + pkg_ros_gz_sim_demos = get_package_share_directory('ros_gz_sim_demos') + + return LaunchDescription([ + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim_demos, 'launch', 'diff_drive.launch.py')), + ) + ]) diff --git a/ros_ign_gazebo_demos/launch/gpu_lidar.launch.py b/ros_ign_gazebo_demos/launch/gpu_lidar.launch.py new file mode 100644 index 000000000..49df92850 --- /dev/null +++ b/ros_ign_gazebo_demos/launch/gpu_lidar.launch.py @@ -0,0 +1,34 @@ +# Copyright 2022 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. + +import os + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource + + +def generate_launch_description(): + print('ros_ign_gazebo_demos is deprecated! Please use ros_gz_sim_demos instead!') + + pkg_ros_gz_sim_demos = get_package_share_directory('ros_gz_sim_demos') + + return LaunchDescription([ + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim_demos, 'launch', 'gpu_lidar.launch.py')), + ) + ]) diff --git a/ros_ign_gazebo_demos/launch/gpu_lidar_bridge.launch.py b/ros_ign_gazebo_demos/launch/gpu_lidar_bridge.launch.py new file mode 100644 index 000000000..d4c89975e --- /dev/null +++ b/ros_ign_gazebo_demos/launch/gpu_lidar_bridge.launch.py @@ -0,0 +1,34 @@ +# Copyright 2022 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. + +import os + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource + + +def generate_launch_description(): + print('ros_ign_gazebo_demos is deprecated! Please use ros_gz_sim_demos instead!') + + pkg_ros_gz_sim_demos = get_package_share_directory('ros_gz_sim_demos') + + return LaunchDescription([ + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim_demos, 'launch', 'gpu_lidar_bridge.launch.py')), + ) + ]) diff --git a/ros_ign_gazebo_demos/launch/image_bridge.launch.py b/ros_ign_gazebo_demos/launch/image_bridge.launch.py new file mode 100644 index 000000000..421a1c080 --- /dev/null +++ b/ros_ign_gazebo_demos/launch/image_bridge.launch.py @@ -0,0 +1,34 @@ +# Copyright 2022 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. + +import os + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource + + +def generate_launch_description(): + print('ros_ign_gazebo_demos is deprecated! Please use ros_gz_sim_demos instead!') + + pkg_ros_gz_sim_demos = get_package_share_directory('ros_gz_sim_demos') + + return LaunchDescription([ + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim_demos, 'launch', 'image_bridge.launch.py')), + ) + ]) diff --git a/ros_ign_gazebo_demos/launch/imu.launch.py b/ros_ign_gazebo_demos/launch/imu.launch.py new file mode 100644 index 000000000..6a580aeff --- /dev/null +++ b/ros_ign_gazebo_demos/launch/imu.launch.py @@ -0,0 +1,34 @@ +# Copyright 2022 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. + +import os + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource + + +def generate_launch_description(): + print('ros_ign_gazebo_demos is deprecated! Please use ros_gz_sim_demos instead!') + + pkg_ros_gz_sim_demos = get_package_share_directory('ros_gz_sim_demos') + + return LaunchDescription([ + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim_demos, 'launch', 'imu.launch.py')), + ) + ]) diff --git a/ros_ign_gazebo_demos/launch/joint_states.launch.py b/ros_ign_gazebo_demos/launch/joint_states.launch.py new file mode 100644 index 000000000..004a88006 --- /dev/null +++ b/ros_ign_gazebo_demos/launch/joint_states.launch.py @@ -0,0 +1,34 @@ +# Copyright 2022 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. + +import os + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource + + +def generate_launch_description(): + print('ros_ign_gazebo_demos is deprecated! Please use ros_gz_sim_demos instead!') + + pkg_ros_gz_sim_demos = get_package_share_directory('ros_gz_sim_demos') + + return LaunchDescription([ + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim_demos, 'launch', 'joint_states.launch.py')), + ) + ]) diff --git a/ros_ign_gazebo_demos/launch/magnetometer.launch.py b/ros_ign_gazebo_demos/launch/magnetometer.launch.py new file mode 100644 index 000000000..3b66da7c0 --- /dev/null +++ b/ros_ign_gazebo_demos/launch/magnetometer.launch.py @@ -0,0 +1,34 @@ +# Copyright 2022 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. + +import os + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource + + +def generate_launch_description(): + print('ros_ign_gazebo_demos is deprecated! Please use ros_gz_sim_demos instead!') + + pkg_ros_gz_sim_demos = get_package_share_directory('ros_gz_sim_demos') + + return LaunchDescription([ + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim_demos, 'launch', 'magnetometer.launch.py')), + ) + ]) diff --git a/ros_ign_gazebo_demos/launch/rgbd_camera.launch.py b/ros_ign_gazebo_demos/launch/rgbd_camera.launch.py new file mode 100644 index 000000000..ba3e890bc --- /dev/null +++ b/ros_ign_gazebo_demos/launch/rgbd_camera.launch.py @@ -0,0 +1,34 @@ +# Copyright 2022 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. + +import os + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource + + +def generate_launch_description(): + print('ros_ign_gazebo_demos is deprecated! Please use ros_gz_sim_demos instead!') + + pkg_ros_gz_sim_demos = get_package_share_directory('ros_gz_sim_demos') + + return LaunchDescription([ + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim_demos, 'launch', 'rgbd_camera.launch.py')), + ) + ]) diff --git a/ros_ign_gazebo_demos/launch/rgbd_camera_bridge.launch.py b/ros_ign_gazebo_demos/launch/rgbd_camera_bridge.launch.py new file mode 100644 index 000000000..d7812aac3 --- /dev/null +++ b/ros_ign_gazebo_demos/launch/rgbd_camera_bridge.launch.py @@ -0,0 +1,34 @@ +# Copyright 2022 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. + +import os + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource + + +def generate_launch_description(): + print('ros_ign_gazebo_demos is deprecated! Please use ros_gz_sim_demos instead!') + + pkg_ros_gz_sim_demos = get_package_share_directory('ros_gz_sim_demos') + + return LaunchDescription([ + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim_demos, 'launch', 'rgbd_camera_bridge.launch.py')), + ) + ]) diff --git a/ros_ign_gazebo_demos/launch/robot_description_publisher.launch.py b/ros_ign_gazebo_demos/launch/robot_description_publisher.launch.py new file mode 100755 index 000000000..f2158c69e --- /dev/null +++ b/ros_ign_gazebo_demos/launch/robot_description_publisher.launch.py @@ -0,0 +1,37 @@ +# Copyright 2022 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. + +import os + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource + + +def generate_launch_description(): + print('ros_ign_gazebo_demos is deprecated! Please use ros_gz_sim_demos instead!') + + pkg_ros_gz_sim_demos = get_package_share_directory('ros_gz_sim_demos') + + return LaunchDescription([ + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join( + pkg_ros_gz_sim_demos, + 'launch', + 'robot_description_publisher.launch.py')), + ) + ]) diff --git a/ros_ign_gazebo_demos/launch/tf_bridge.launch.py b/ros_ign_gazebo_demos/launch/tf_bridge.launch.py new file mode 100644 index 000000000..2b6fd52fa --- /dev/null +++ b/ros_ign_gazebo_demos/launch/tf_bridge.launch.py @@ -0,0 +1,34 @@ +# Copyright 2022 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. + +import os + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource + + +def generate_launch_description(): + print('ros_ign_gazebo_demos is deprecated! Please use ros_gz_sim_demos instead!') + + pkg_ros_gz_sim_demos = get_package_share_directory('ros_gz_sim_demos') + + return LaunchDescription([ + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim_demos, 'launch', 'tf_bridge.launch.py')), + ) + ]) diff --git a/ros_ign_gazebo_demos/launch/triggered_camera.launch.py b/ros_ign_gazebo_demos/launch/triggered_camera.launch.py new file mode 100644 index 000000000..b5a852c52 --- /dev/null +++ b/ros_ign_gazebo_demos/launch/triggered_camera.launch.py @@ -0,0 +1,34 @@ +# Copyright 2022 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. + +import os + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource + + +def generate_launch_description(): + print('ros_ign_gazebo_demos is deprecated! Please use ros_gz_sim_demos instead!') + + pkg_ros_gz_sim_demos = get_package_share_directory('ros_gz_sim_demos') + + return LaunchDescription([ + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim_demos, 'launch', 'triggered_camera.launch.py')), + ) + ]) diff --git a/ros_ign_gazebo_demos/package.xml b/ros_ign_gazebo_demos/package.xml new file mode 100644 index 000000000..8774a895c --- /dev/null +++ b/ros_ign_gazebo_demos/package.xml @@ -0,0 +1,17 @@ + + ros_ign_gazebo_demos + 0.244.16 + Shim package to redirect to ros_gz_sim_demos. + Apache 2.0 + Brandon Ong + + ament_cmake + ros_gz_sim_demos + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/ros_ign_image/CHANGELOG.rst b/ros_ign_image/CHANGELOG.rst new file mode 100644 index 000000000..ec869ceaf --- /dev/null +++ b/ros_ign_image/CHANGELOG.rst @@ -0,0 +1,46 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package ros_ign_image +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +0.244.16 (2024-07-22) +--------------------- + +0.244.15 (2024-07-03) +--------------------- + +0.244.14 (2024-04-08) +--------------------- + +0.244.13 (2024-01-23) +--------------------- + +0.244.12 (2023-12-13) +--------------------- + +0.244.11 (2023-05-23) +--------------------- + +0.244.10 (2023-05-03) +--------------------- + +0.244.9 (2022-11-03) +-------------------- + +0.244.8 (2022-10-28) +-------------------- + +0.244.7 (2022-10-12) +-------------------- +* Merge branch 'ros2' into ports/galactic_to_ros2 +* Contributors: Michael Carroll + +0.244.6 (2022-09-14) +-------------------- +* Restructured directories (`#296 `_) +* Contributors: Alejandro Hernández Cordero + +0.244.5 (2022-09-12) +-------------------- +* ign -> gz : ros_gz Migration (Shims) (`#281 `_) + Co-authored-by: Louise Poubel +* Contributors: methylDragon diff --git a/ros_ign_image/CMakeLists.txt b/ros_ign_image/CMakeLists.txt new file mode 100644 index 000000000..5fa4b91bb --- /dev/null +++ b/ros_ign_image/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.5) + +project(ros_ign_image) + +# Default to C++14 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 14) +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(ament_index_cpp REQUIRED) + +add_executable(image_bridge src/image_bridge_shim.cpp) +ament_target_dependencies(image_bridge ament_index_cpp) + +ament_export_dependencies(ament_index_cpp ros_gz_image) + +install(TARGETS + image_bridge + DESTINATION lib/${PROJECT_NAME} +) + +ament_package() diff --git a/ros_ign_image/README.md b/ros_ign_image/README.md new file mode 100644 index 000000000..517ccdd80 --- /dev/null +++ b/ros_ign_image/README.md @@ -0,0 +1,2 @@ +# This is a shim package +For [ros_gz_image](https://github.com/gazebosim/ros_gz/tree/ros2/ros_gz_image) diff --git a/ros_ign_image/package.xml b/ros_ign_image/package.xml new file mode 100644 index 000000000..166d6b202 --- /dev/null +++ b/ros_ign_image/package.xml @@ -0,0 +1,19 @@ + + + + ros_ign_image + 0.244.16 + Shim package to redirect to ros_gz_image. + Brandon Ong + + Apache 2.0 + + ament_cmake + ament_index_cpp + + ros_gz_image + + + ament_cmake + + diff --git a/ros_ign_image/src/image_bridge_shim.cpp b/ros_ign_image/src/image_bridge_shim.cpp new file mode 100644 index 000000000..0d44a1cb2 --- /dev/null +++ b/ros_ign_image/src/image_bridge_shim.cpp @@ -0,0 +1,43 @@ +// Copyright 2022 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. + +// Shim to redirect "ros_ign_image image_bridge" call to "ros_gz_image image_bridge" + +#include +#include +#include + +#include + + +int main(int argc, char * argv[]) +{ + std::stringstream cli_call; + + cli_call << ament_index_cpp::get_package_prefix("ros_gz_image") + << "/lib/ros_gz_image/image_bridge"; + + if (argc > 1) + { + for (int i = 1; i < argc; i++) + cli_call << " " << argv[i]; + } + + std::cerr << "[ros_ign_bridge] is deprecated! " + << "Redirecting to use [ros_gz_image] instead!" + << std::endl << std::endl; + system(cli_call.str().c_str()); + + return 0; +} diff --git a/ros_ign_interfaces/CHANGELOG.rst b/ros_ign_interfaces/CHANGELOG.rst new file mode 100644 index 000000000..a3f8ade53 --- /dev/null +++ b/ros_ign_interfaces/CHANGELOG.rst @@ -0,0 +1,61 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package ros_ign_interfaces +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +0.244.16 (2024-07-22) +--------------------- + +0.244.15 (2024-07-03) +--------------------- + +0.244.14 (2024-04-08) +--------------------- + +0.244.13 (2024-01-23) +--------------------- + +0.244.12 (2023-12-13) +--------------------- + +0.244.11 (2023-05-23) +--------------------- + +0.244.10 (2023-05-03) +--------------------- + +0.244.9 (2022-11-03) +-------------------- + +0.244.8 (2022-10-28) +-------------------- + +0.244.7 (2022-10-12) +-------------------- +* Merge branch 'ros2' into ports/galactic_to_ros2 +* Merge branch 'galactic' into ports/galactic_to_ros2 +* Add ParamVec and bridge from Ignition (`#261 `_) + * Introduces `ros_ign_interfaces::msg::ParamVec` for storing a list of Parameters that are int, bool, double, or string. + * Introduces bridge for `ignition::msgs::param` to `ros_ign_interfaces::msg::ParamVec` + * Introduces bridge for `ignition::msgs::param_v` to `ros_ign_interfaces::msg::ParamVec` +* Add rssi to Dataframe.msg (`#249 `_) + * Adding rssi field to ros_ign_interfaces/Dataframe.msg +* [galactic] Backport GuiCamera, StringVec, TrackVisual, VideoRecord (`#241 `_) + * [ros_ign_interfaces] Add more interface definitions. + * Add converion functions for the added messages + * Update the factory factory function with the new messages + * Add new messages to docs + * Add test cases for the new messages conversions + Co-authored-by: Ivan Santiago Paunovic +* Add Dataframe message and bridging (`#239 `_) +* Contributors: Carlos Agüero, Michael Carroll + +0.244.6 (2022-09-14) +-------------------- +* Restructured directories (`#296 `_) +* Contributors: Alejandro Hernández Cordero + +0.244.5 (2022-09-12) +-------------------- +* ign -> gz : ros_gz Migration (Shims) (`#281 `_) + Co-authored-by: Louise Poubel +* Contributors: methylDragon diff --git a/ros_ign_interfaces/CMakeLists.txt b/ros_ign_interfaces/CMakeLists.txt new file mode 100644 index 000000000..b397466bb --- /dev/null +++ b/ros_ign_interfaces/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.5) +project(ros_ign_interfaces) + +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 14) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +find_package(ament_cmake REQUIRED) +find_package(builtin_interfaces REQUIRED) +find_package(std_msgs REQUIRED) +find_package(geometry_msgs REQUIRED) +find_package(ros_gz_interfaces REQUIRED) +find_package(rosidl_default_generators REQUIRED) + +set(msg_files + "msg/Contact.msg" + "msg/Contacts.msg" + "msg/Entity.msg" + "msg/EntityFactory.msg" + "msg/GuiCamera.msg" + "msg/JointWrench.msg" + "msg/Light.msg" + "msg/StringVec.msg" + "msg/TrackVisual.msg" + "msg/VideoRecord.msg" + "msg/WorldControl.msg" + "msg/WorldReset.msg" +) + +set(srv_files + "srv/ControlWorld.srv" + "srv/DeleteEntity.srv" + "srv/SetEntityPose.srv" + "srv/SpawnEntity.srv" +) + +rosidl_generate_interfaces(${PROJECT_NAME} + ${msg_files} + ${srv_files} + DEPENDENCIES builtin_interfaces std_msgs geometry_msgs ros_gz_interfaces + ADD_LINTER_TESTS +) + +ament_export_dependencies(rosidl_default_runtime) +ament_package() diff --git a/ros_ign_interfaces/README.md b/ros_ign_interfaces/README.md new file mode 100644 index 000000000..15a7fafb0 --- /dev/null +++ b/ros_ign_interfaces/README.md @@ -0,0 +1,2 @@ +# This is a shim package +For [ros_gz_interfaces](https://github.com/gazebosim/ros_gz/tree/ros2/ros_gz_interfaces) diff --git a/ros_ign_interfaces/msg/Contact.msg b/ros_ign_interfaces/msg/Contact.msg new file mode 100644 index 000000000..98d2cef8e --- /dev/null +++ b/ros_ign_interfaces/msg/Contact.msg @@ -0,0 +1,6 @@ +ros_gz_interfaces/Entity collision1 # Contact collision1 +ros_gz_interfaces/Entity collision2 # Contact collision2 +geometry_msgs/Vector3[] positions # List of contact position +geometry_msgs/Vector3[] normals # List of contact normals +float64[] depths # List of penetration depths +ros_gz_interfaces/JointWrench[] wrenches # List of joint wrenches including forces/torques diff --git a/ros_ign_interfaces/msg/Contacts.msg b/ros_ign_interfaces/msg/Contacts.msg new file mode 100644 index 000000000..40232e516 --- /dev/null +++ b/ros_ign_interfaces/msg/Contacts.msg @@ -0,0 +1,2 @@ +std_msgs/Header header # Time stamp +ros_gz_interfaces/Contact[] contacts # List of contacts diff --git a/ros_ign_interfaces/msg/Entity.msg b/ros_ign_interfaces/msg/Entity.msg new file mode 100644 index 000000000..b785c7edf --- /dev/null +++ b/ros_ign_interfaces/msg/Entity.msg @@ -0,0 +1,13 @@ +# Entity type: constant definition +uint8 NONE = 0 +uint8 LIGHT = 1 +uint8 MODEL = 2 +uint8 LINK = 3 +uint8 VISUAL = 4 +uint8 COLLISION = 5 +uint8 SENSOR = 6 +uint8 JOINT = 7 + +uint64 id # Entity unique identifier accross all types. Defaults to 0 +string name # Entity name, which is not guaranteed to be unique. +uint8 type # Entity type. diff --git a/ros_ign_interfaces/msg/EntityFactory.msg b/ros_ign_interfaces/msg/EntityFactory.msg new file mode 100644 index 000000000..4576c003e --- /dev/null +++ b/ros_ign_interfaces/msg/EntityFactory.msg @@ -0,0 +1,11 @@ +string name # New name for the entity, overrides the name on the SDF +bool allow_renaming false # Whether the server is allowed to rename the entity in case of + # overlap with existing entities. + +# Only one method is supported at a time (sdf,sdf_filename,clone_name) +string sdf # SDF description in string format +string sdf_filename # Full path to SDF file. +string clone_name # Name of entity to clone + +geometry_msgs/Pose pose # Pose where the entity will be spawned in the world. +string relative_to "world" # Pose is defined relative to the frame of this entity. diff --git a/ros_ign_interfaces/msg/GuiCamera.msg b/ros_ign_interfaces/msg/GuiCamera.msg new file mode 100644 index 000000000..d45fd9b83 --- /dev/null +++ b/ros_ign_interfaces/msg/GuiCamera.msg @@ -0,0 +1,12 @@ +# Message for a GUI Camera. + +# Optional header data. +std_msgs/Header header + +string name +string view_controller +geometry_msgs/Pose pose +TrackVisual track + +# Type of projection: "perspective" or "orthographic". +string projection_type diff --git a/ros_ign_interfaces/msg/JointWrench.msg b/ros_ign_interfaces/msg/JointWrench.msg new file mode 100644 index 000000000..2da890944 --- /dev/null +++ b/ros_ign_interfaces/msg/JointWrench.msg @@ -0,0 +1,8 @@ +std_msgs/Header header # Time stamp +std_msgs/String body_1_name # Body 1 name string +std_msgs/UInt32 body_1_id # Body 1 id +std_msgs/String body_2_name # Body 2 name string +std_msgs/UInt32 body_2_id # Body 2 id + +geometry_msgs/Wrench body_1_wrench # Body 1 wrench +geometry_msgs/Wrench body_2_wrench # Body 2 wrench diff --git a/ros_ign_interfaces/msg/Light.msg b/ros_ign_interfaces/msg/Light.msg new file mode 100644 index 000000000..911e20bd3 --- /dev/null +++ b/ros_ign_interfaces/msg/Light.msg @@ -0,0 +1,29 @@ +std_msgs/Header header # Optional header data + +string name # Light name + +# Light type: constant definition +uint8 POINT = 0 +uint8 SPOT = 1 +uint8 DIRECTIONAL = 2 + +uint8 type # Light type (from constant definitions) + +geometry_msgs/Pose pose # Light pose +std_msgs/ColorRGBA diffuse # Light diffuse emission +std_msgs/ColorRGBA specular # Light specular emission +float32 attenuation_constant # Constant variable in attenuation formula +float32 attenuation_linear # Linear variable in attenuation formula +float32 attenuation_quadratic # Quadratic variable in attenuation formula +geometry_msgs/Vector3 direction # Light direction +float32 range # Light range +bool cast_shadows # Enable/disable shadow casting +float32 spot_inner_angle # Spotlight inner cone angle +float32 spot_outer_angle # Spotlight outer cone angle +float32 spot_falloff # Falloff between inner and outer cone + +uint32 id # Unique id of the light + +uint32 parent_id # Unique id of the light's parent + +float32 intensity # Light intensity diff --git a/ros_ign_interfaces/msg/StringVec.msg b/ros_ign_interfaces/msg/StringVec.msg new file mode 100644 index 000000000..15a7dda9e --- /dev/null +++ b/ros_ign_interfaces/msg/StringVec.msg @@ -0,0 +1,7 @@ +# A message for a vector of string data. + +# Optional header data. +std_msgs/Header header + +# The vector of strings. +string[] data diff --git a/ros_ign_interfaces/msg/TrackVisual.msg b/ros_ign_interfaces/msg/TrackVisual.msg new file mode 100644 index 000000000..c09f4785f --- /dev/null +++ b/ros_ign_interfaces/msg/TrackVisual.msg @@ -0,0 +1,33 @@ +# Message for a tracking a rendering::Visual with a rendering::Camera. + +# Optional header data. +std_msgs/Header header + +# Name of the visual to track. +string name + +# Id of the visual to track. +uint32 id + +# True to have the tracking camera inherit the orientation of +# the tracked visual. +bool inherit_orientation + +# Minimum follow distance. +float64 min_dist + +# Maximum follow distance. +float64 max_dist + +# If set to true, the position of the camera is fixed. +bool is_static + +# If set to true, the position of the camera is relative to the. +# model reference frame. +bool use_model_frame + +# Position of the camera. +geometry_msgs/Vector3 xyz + +# If set to true, the camera inherits the yaw rotation of the model. +bool inherit_yaw diff --git a/ros_ign_interfaces/msg/VideoRecord.msg b/ros_ign_interfaces/msg/VideoRecord.msg new file mode 100644 index 000000000..02bafde0d --- /dev/null +++ b/ros_ign_interfaces/msg/VideoRecord.msg @@ -0,0 +1,16 @@ +# A message that allows for control of video recording functions. + +# Optional header data. +std_msgs/Header header + +# True to start video recording. +bool start + +# True to stop video recording. +bool stop + +# Video encoding format, e.g. "mp4", "ogv". +string format + +# filename of the recorded video. +string save_filename diff --git a/ros_ign_interfaces/msg/WorldControl.msg b/ros_ign_interfaces/msg/WorldControl.msg new file mode 100644 index 000000000..efa22fb9c --- /dev/null +++ b/ros_ign_interfaces/msg/WorldControl.msg @@ -0,0 +1,7 @@ +bool pause # Paused state. +bool step # +uint32 multi_step 0 # Paused after stepping multi_step. +ros_gz_interfaces/WorldReset reset # +uint32 seed # +builtin_interfaces/Time run_to_sim_time # A simulation time in the future to run to and + # then pause. diff --git a/ros_ign_interfaces/msg/WorldReset.msg b/ros_ign_interfaces/msg/WorldReset.msg new file mode 100644 index 000000000..46f5971ff --- /dev/null +++ b/ros_ign_interfaces/msg/WorldReset.msg @@ -0,0 +1,3 @@ +bool all false # Reset time and model +bool time_only false # Reset time only +bool model_only false # Reset model only diff --git a/ros_ign_interfaces/package.xml b/ros_ign_interfaces/package.xml new file mode 100644 index 000000000..bb215f809 --- /dev/null +++ b/ros_ign_interfaces/package.xml @@ -0,0 +1,27 @@ + + ros_ign_interfaces + 0.244.16 + Shim package to redirect to ros_gz_interfaces. + Apache 2.0 + Brandon Ong + Zhenpeng Ge + ament_cmake + rosidl_default_generators + + builtin_interfaces + ros_gz_interfaces + std_msgs + geometry_msgs + + builtin_interfaces + ros_gz_interfaces + std_msgs + geometry_msgs + rosidl_default_runtime + + ament_lint_common + rosidl_interface_packages + + ament_cmake + + diff --git a/ros_ign_interfaces/srv/ControlWorld.srv b/ros_ign_interfaces/srv/ControlWorld.srv new file mode 100644 index 000000000..d8e41f245 --- /dev/null +++ b/ros_ign_interfaces/srv/ControlWorld.srv @@ -0,0 +1,3 @@ +ros_gz_interfaces/WorldControl world_control # Message to Control world in Gazebo Sim +--- +bool success # Return true if control is successful. diff --git a/ros_ign_interfaces/srv/DeleteEntity.srv b/ros_ign_interfaces/srv/DeleteEntity.srv new file mode 100644 index 000000000..13b3e1fd8 --- /dev/null +++ b/ros_ign_interfaces/srv/DeleteEntity.srv @@ -0,0 +1,3 @@ +ros_gz_interfaces/Entity entity # Gazebo Sim entity to be deleted. +--- +bool success # Return true if deletion is successful. diff --git a/ros_ign_interfaces/srv/SetEntityPose.srv b/ros_ign_interfaces/srv/SetEntityPose.srv new file mode 100644 index 000000000..b749488cc --- /dev/null +++ b/ros_ign_interfaces/srv/SetEntityPose.srv @@ -0,0 +1,4 @@ +ros_gz_interfaces/Entity entity # Gazebo Sim entity. +geometry_msgs/Pose pose # Pose of entity. +--- +bool success # Return true if set successfully. diff --git a/ros_ign_interfaces/srv/SpawnEntity.srv b/ros_ign_interfaces/srv/SpawnEntity.srv new file mode 100644 index 000000000..35d5df59e --- /dev/null +++ b/ros_ign_interfaces/srv/SpawnEntity.srv @@ -0,0 +1,3 @@ +ros_gz_interfaces/EntityFactory entity_factory # Message to create a new entity +--- +bool success # Return true if spawned successfully. diff --git a/test_ros_gz_bridge/CHANGELOG.rst b/test_ros_gz_bridge/CHANGELOG.rst deleted file mode 100644 index 27e517da9..000000000 --- a/test_ros_gz_bridge/CHANGELOG.rst +++ /dev/null @@ -1,32 +0,0 @@ -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Changelog for package test_ros_gz_bridge -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -1.0.7 (2024-11-08) ------------------- - -1.0.6 (2024-10-31) ------------------- - -1.0.5 (2024-10-14) ------------------- - -1.0.4 (2024-08-29) ------------------- - -1.0.3 (2024-07-22) ------------------- - -1.0.2 (2024-07-03) ------------------- -* Prepare for 1.0.0 Release (`#495 `_) -* 0.244.14 -* Changelog -* Correctly export ros_gz_bridge for downstream targets (`#503 `_) (`#506 `_) -* Correctly export ros_gz_bridge for downstream targets (`#503 `_) -* Contributors: Addisu Z. Taddese, Alejandro Hernández Cordero, Michael Carroll - -1.0.0 (2024-04-24) ------------------- -* Correctly export ros_gz_bridge for downstream targets (`#503 `_) -* Contributors: Michael Carroll diff --git a/test_ros_gz_bridge/package.xml b/test_ros_gz_bridge/package.xml index e309d650a..fc1429ed5 100644 --- a/test_ros_gz_bridge/package.xml +++ b/test_ros_gz_bridge/package.xml @@ -2,7 +2,7 @@ test_ros_gz_bridge - 1.0.7 + 0.244.16 Bridge communication between ROS and Gazebo Transport Aditya Pande Alejandro Hernandez