From 1bdc1227daa8913e39f95179a278fde0287db8e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven!=20Ragnaro=CC=88k?= Date: Sat, 13 Jul 2024 13:51:54 -0700 Subject: [PATCH] Imported upstream version '1.0.1' 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 | 38 ++-- README_RENAME.md | 2 +- img/video_img.png | Bin 0 -> 221780 bytes ros_gz/CHANGELOG.rst | 50 ++++- ros_gz/package.xml | 7 +- ros_gz_bridge/CHANGELOG.rst | 212 +++++++++++++++--- ros_gz_bridge/CMakeLists.txt | 96 +++----- ros_gz_bridge/README.md | 191 ++++++++-------- .../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 | 33 +-- .../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 | 18 ++ ros_gz_bridge/launch/ros_gz_bridge.launch.py | 114 ++++++++++ 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 | 147 ++++++++++++ ros_gz_bridge/ros_gz_bridge/mappings.py | 16 +- ros_gz_bridge/setup.cfg | 3 + ros_gz_bridge/src/bridge_config.cpp | 116 +++++----- 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 | 30 ++- ros_gz_bridge/src/convert/sensor_msgs.cpp | 12 +- ros_gz_bridge/src/factory.hpp | 17 +- ros_gz_bridge/src/parameter_bridge.cpp | 8 +- .../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 | 29 ++- ros_gz_bridge/test/utils/gz_test_msg.hpp | 31 ++- ros_gz_bridge/test/utils/ros_test_msg.cpp | 49 +++- ros_gz_bridge/test/utils/ros_test_msg.hpp | 30 ++- ros_gz_image/CHANGELOG.rst | 97 ++++++-- 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 | 76 +++++-- ros_gz_interfaces/CMakeLists.txt | 1 + ros_gz_interfaces/README.md | 1 + ros_gz_interfaces/msg/EntityWrench.msg | 3 + 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 | 135 +++++++++-- ros_gz_sim/CMakeLists.txt | 147 ++++++------ ros_gz_sim/README.md | 2 +- ros_gz_sim/launch/gz_server.launch | 12 + ros_gz_sim/launch/gz_server.launch.py | 80 +++++++ ros_gz_sim/launch/gz_sim.launch.py.in | 13 -- 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 | 26 +++ ros_gz_sim/launch/ros_gz_sim.launch.py | 111 +++++++++ .../launch/ros_gz_spawn_model.launch.py | 154 +++++++++++++ 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 | 111 +++++++++ ros_gz_sim/setup.cfg | 3 + ros_gz_sim/src/create.cpp | 195 +++++++++++----- 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 | 107 +++++++-- .../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 | 41 ---- ros_ign/CMakeLists.txt | 10 - ros_ign/README.md | 2 - ros_ign/package.xml | 24 -- ros_ign_bridge/CHANGELOG.rst | 79 ------- 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 | 59 ----- 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 | 47 ---- 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 | 43 ---- 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 | 58 ----- 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 | 17 ++ test_ros_gz_bridge/package.xml | 2 +- 154 files changed, 2809 insertions(+), 2512 deletions(-) create mode 100644 img/video_img.png create mode 100644 ros_gz_bridge/launch/ros_gz_bridge.launch create mode 100644 ros_gz_bridge/launch/ros_gz_bridge.launch.py create mode 100644 ros_gz_bridge/resource/ros_gz_bridge create mode 100644 ros_gz_bridge/ros_gz_bridge/actions/__init__.py create mode 100644 ros_gz_bridge/ros_gz_bridge/actions/ros_gz_bridge.py create mode 100644 ros_gz_bridge/setup.cfg create mode 100644 ros_gz_bridge/test/config/empty.yaml delete mode 100644 ros_gz_bridge/test/config/full_ign.yaml create mode 100644 ros_gz_bridge/test/config/invalid.yaml delete mode 100644 ros_gz_bridge/test/config/minimum_ign.yaml create mode 100644 ros_gz_interfaces/msg/EntityWrench.msg create mode 100644 ros_gz_sim/launch/gz_server.launch create mode 100644 ros_gz_sim/launch/gz_server.launch.py create mode 100644 ros_gz_sim/launch/gz_spawn_model.launch create mode 100644 ros_gz_sim/launch/gz_spawn_model.launch.py create mode 100644 ros_gz_sim/launch/ros_gz_sim.launch create mode 100644 ros_gz_sim/launch/ros_gz_sim.launch.py create mode 100644 ros_gz_sim/launch/ros_gz_spawn_model.launch.py create mode 100644 ros_gz_sim/resource/ros_gz_sim create mode 100644 ros_gz_sim/ros_gz_sim/__init__.py create mode 100644 ros_gz_sim/ros_gz_sim/actions/__init__.py create mode 100644 ros_gz_sim/ros_gz_sim/actions/gz_spawn_model.py create mode 100644 ros_gz_sim/ros_gz_sim/actions/gzserver.py create mode 100644 ros_gz_sim/setup.cfg create mode 100644 ros_gz_sim/src/gzserver.cpp create mode 100644 ros_gz_sim/test/test_create.cpp create mode 100644 ros_gz_sim/test/test_create_node.launch.py delete mode 100644 ros_ign/CHANGELOG.rst delete mode 100644 ros_ign/CMakeLists.txt delete mode 100644 ros_ign/README.md delete mode 100644 ros_ign/package.xml delete mode 100644 ros_ign_bridge/CHANGELOG.rst delete mode 100644 ros_ign_bridge/CMakeLists.txt delete mode 100644 ros_ign_bridge/README.md delete mode 100644 ros_ign_bridge/package.xml delete mode 100644 ros_ign_bridge/src/parameter_bridge_shim.cpp delete mode 100644 ros_ign_bridge/src/static_bridge_shim.cpp delete mode 100644 ros_ign_gazebo/CHANGELOG.rst delete mode 100644 ros_ign_gazebo/CMakeLists.txt delete mode 100644 ros_ign_gazebo/README.md delete mode 100644 ros_ign_gazebo/launch/ign_gazebo.launch.py delete mode 100644 ros_ign_gazebo/package.xml delete mode 100644 ros_ign_gazebo/src/create_shim.cpp delete mode 100644 ros_ign_gazebo_demos/CHANGELOG.rst delete mode 100644 ros_ign_gazebo_demos/CMakeLists.txt delete mode 100644 ros_ign_gazebo_demos/README.md delete mode 100644 ros_ign_gazebo_demos/launch/air_pressure.launch.py delete mode 100644 ros_ign_gazebo_demos/launch/battery.launch.py delete mode 100644 ros_ign_gazebo_demos/launch/camera.launch.py delete mode 100644 ros_ign_gazebo_demos/launch/depth_camera.launch.py delete mode 100644 ros_ign_gazebo_demos/launch/diff_drive.launch.py delete mode 100644 ros_ign_gazebo_demos/launch/gpu_lidar.launch.py delete mode 100644 ros_ign_gazebo_demos/launch/gpu_lidar_bridge.launch.py delete mode 100644 ros_ign_gazebo_demos/launch/image_bridge.launch.py delete mode 100644 ros_ign_gazebo_demos/launch/imu.launch.py delete mode 100644 ros_ign_gazebo_demos/launch/joint_states.launch.py delete mode 100644 ros_ign_gazebo_demos/launch/magnetometer.launch.py delete mode 100644 ros_ign_gazebo_demos/launch/rgbd_camera.launch.py delete mode 100644 ros_ign_gazebo_demos/launch/rgbd_camera_bridge.launch.py delete mode 100755 ros_ign_gazebo_demos/launch/robot_description_publisher.launch.py delete mode 100644 ros_ign_gazebo_demos/launch/tf_bridge.launch.py delete mode 100644 ros_ign_gazebo_demos/launch/triggered_camera.launch.py delete mode 100644 ros_ign_gazebo_demos/package.xml delete mode 100644 ros_ign_image/CHANGELOG.rst delete mode 100644 ros_ign_image/CMakeLists.txt delete mode 100644 ros_ign_image/README.md delete mode 100644 ros_ign_image/package.xml delete mode 100644 ros_ign_image/src/image_bridge_shim.cpp delete mode 100644 ros_ign_interfaces/CHANGELOG.rst delete mode 100644 ros_ign_interfaces/CMakeLists.txt delete mode 100644 ros_ign_interfaces/README.md delete mode 100644 ros_ign_interfaces/msg/Contact.msg delete mode 100644 ros_ign_interfaces/msg/Contacts.msg delete mode 100644 ros_ign_interfaces/msg/Entity.msg delete mode 100644 ros_ign_interfaces/msg/EntityFactory.msg delete mode 100644 ros_ign_interfaces/msg/GuiCamera.msg delete mode 100644 ros_ign_interfaces/msg/JointWrench.msg delete mode 100644 ros_ign_interfaces/msg/Light.msg delete mode 100644 ros_ign_interfaces/msg/StringVec.msg delete mode 100644 ros_ign_interfaces/msg/TrackVisual.msg delete mode 100644 ros_ign_interfaces/msg/VideoRecord.msg delete mode 100644 ros_ign_interfaces/msg/WorldControl.msg delete mode 100644 ros_ign_interfaces/msg/WorldReset.msg delete mode 100644 ros_ign_interfaces/package.xml delete mode 100644 ros_ign_interfaces/srv/ControlWorld.srv delete mode 100644 ros_ign_interfaces/srv/DeleteEntity.srv delete mode 100644 ros_ign_interfaces/srv/SetEntityPose.srv delete mode 100644 ros_ign_interfaces/srv/SpawnEntity.srv create mode 100644 test_ros_gz_bridge/CHANGELOG.rst diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9847eb2f5..627c951b7 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,5 @@ # More info: # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners -* @chapulina +* @adityapande-1995 +* @ahcorde diff --git a/.github/workflows/build-and-test.sh b/.github/workflows/build-and-test.sh index 86b2fd8ba..8909db553 100755 --- a/.github/workflows/build-and-test.sh +++ b/.github/workflows/build-and-test.sh @@ -10,31 +10,15 @@ export ROS_PYTHON_VERSION=3 apt update -qq apt install -qq -y lsb-release wget curl build-essential -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 +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 - # 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 $GZ_DEPS \ - python3-colcon-common-extensions \ - python3-rosdep +apt-get install -y 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 f5040c0be..bf1cbd4e3 100644 --- a/.github/workflows/ros2-ci.yml +++ b/.github/workflows/ros2-ci.yml @@ -10,15 +10,9 @@ jobs: fail-fast: false matrix: include: - - 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" + - docker-image: "ubuntu:24.04" gz-version: "harmonic" - ros-distro: "humble" + ros-distro: "rolling" container: image: ${{ matrix.docker-image }} steps: @@ -30,9 +24,3 @@ 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 736670e0e..2c94852da 100644 --- a/.github/workflows/triage.yml +++ b/.github/workflows/triage.yml @@ -10,10 +10,8 @@ jobs: runs-on: ubuntu-latest steps: - name: Add ticket to inbox - uses: technote-space/create-project-card-action@v1 + uses: actions/add-to-project@v0.5.0 with: - PROJECT: Core development - COLUMN: Inbox - GITHUB_TOKEN: ${{ secrets.TRIAGE_TOKEN }} - CHECK_ORG_PROJECT: true + project-url: https://github.com/orgs/gazebosim/projects/7 + github-token: ${{ secrets.TRIAGE_TOKEN }} diff --git a/README.md b/README.md index 8e1d74d0a..1445dacfb 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,20 @@ 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] -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 +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 | [ros2](https://github.com/gazebosim/ros_gz/tree/ros2) | https://packages.ros.org +Rolling | Fortress | [humble](https://github.com/gazebosim/ros_gz/tree/humble) | 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 2 and Gazebo compatibility, refer to the [melodic branch README](https://github.com/gazebosim/ros_gz/tree/melodic) +* ROS 2 Jazzy Jalisco is slated for release on May 23rd, 2024. [Full ROS 2 release information is available in REP-2000.] + +For information on ROS(1) and Gazebo compatibility, refer to the [noetic branch README](https://github.com/gazebosim/ros_gz/tree/noetic) > Please [ticket an issue](https://github.com/gazebosim/ros_gz/issues/) if you'd like support to be added for some combination. @@ -55,11 +61,11 @@ This repository holds packages that provide integration between ## Install -This branch supports ROS Humble. See above for other ROS versions. +This branch supports ROS Rolling. See above for other ROS versions. ### Binaries -Humble binaries are available for Fortress. +Rolling binaries are available for Fortress. They are hosted at https://packages.ros.org. 1. Add https://packages.ros.org @@ -70,14 +76,14 @@ They are hosted at https://packages.ros.org. 1. Install `ros_gz` - sudo apt install ros-humble-ros-gz + sudo apt install ros-rolling-ros-gz ### From source #### ROS Be sure you've installed -[ROS Humble](https://docs.ros.org/en/humble/Installation.html) +[ROS Rolling](https://index.ros.org/doc/ros2/Installation/) (at least ROS-Base). More ROS dependencies will be installed below. #### Gazebo @@ -103,7 +109,7 @@ The following steps are for Linux and OSX. cd ~/ws/src # Download needed software - git clone https://github.com/gazebosim/ros_gz.git -b humble + git clone https://github.com/gazebosim/ros_gz.git -b ros2 ``` 1. Install dependencies (this may also install Gazebo): @@ -130,22 +136,6 @@ 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 17007f4e2..92ba754f2 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_ign parameter_bridge [...] # Will emit deprecation warning +ros2 run ros_gz 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 new file mode 100644 index 0000000000000000000000000000000000000000..626c73539ef721f72bf8226d3a413cfd8d544f2a GIT binary patch literal 221780 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�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∨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 LNkvXXu0mjf@~H7@ literal 0 HcmV?d00001 diff --git a/ros_gz/CHANGELOG.rst b/ros_gz/CHANGELOG.rst index a78d57886..5fec4881a 100644 --- a/ros_gz/CHANGELOG.rst +++ b/ros_gz/CHANGELOG.rst @@ -2,20 +2,46 @@ Changelog for package ros_gz ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -0.244.15(2024-07-03) ---------------------- - -0.244.14 (2024-04-08) ---------------------- - -0.244.13 (2024-01-23) ---------------------- +1.0.1 (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.12 (2023-12-13) ---------------------- +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.11 (2023-05-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 +* Contributors: Alejandro Hernández Cordero, ahcorde 0.244.10 (2023-05-03) --------------------- diff --git a/ros_gz/package.xml b/ros_gz/package.xml index 569756662..d55e4d163 100644 --- a/ros_gz/package.xml +++ b/ros_gz/package.xml @@ -4,11 +4,14 @@ ros_gz - 0.244.15 + 1.0.1 Meta-package containing interfaces for using ROS 2 with Gazebo simulation. - Louise Poubel + Aditya Pande + Alejandro Hernandez 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 4f5a2587f..851a202cb 100644 --- a/ros_gz_bridge/CHANGELOG.rst +++ b/ros_gz_bridge/CHANGELOG.rst @@ -2,22 +2,70 @@ Changelog for package ros_gz_bridge ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -0.244.15(2024-07-03) ---------------------- +1.0.1 (2024-07-03) +------------------ +* Add support for gz.msgs.EntityWrench (base branch: ros2) (`#573 `_) +* Merge pull request `#571 `_ from azeey/jazzy_to_ros2 + Merge jazzy ➡️ ros2 +* Merge branch 'ros2' into jazzy_to_ros2 +* Use memcpy instead of std::copy when bridging images (`#565 `_) + 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 +* Merge jazzy into ros2 +* 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 +* Use `ignoreLocalMessages` in the bridge (`#559 `_) + * Ignore local messages +* Update launch files with name parameter (`#556 `_) + * Name is required. +* Ensure the same container is used for the bridge and gz_server (`#553 `_) + This also adds a required `name` parameter for the bridge so that + multiple different bridges can be created without name collision +* Launch ros_gz_bridge from xml (`#550 `_) + * Add gzserver with ability to load an SDF file or string +* Launch gzserver and the bridge as composable nodes (`#528 `_) + * Add gzserver with ability to load an SDF file or string +* 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. +* 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 `_) * [backport Humble] Create bridge for GPSFix msg (`#316 `_) (`#538 `_) Co-authored-by: Rousseau Vincent -* Contributors: Alejandro Hernández Cordero - -0.244.14 (2024-04-08) ---------------------- -* Added conversion for Detection3D and Detection3DArray (`#523 `_) (`#526 `_) +* [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> -* Add ROS namespaces to GZ topics (`#512 `_) +* 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 `_) +* 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 -* Add option to change material color from ROS. (`#486 `_) +* 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 `_) * Message and bridge for MaterialColor. This allows bridging MaterialColor from ROS to GZ and is important for allowing simulation users to create status lights. @@ -25,30 +73,126 @@ 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 -* 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 `_) +* 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** -* 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 +* 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, Carlos Agüero, El Jawad Alaa, Jose Luis Rivero, Krzysztof Wojciechowski, Michael Carroll, Rousseau Vincent, Victor T. Noppeney, 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 `_) + 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 0.244.10 (2023-05-03) --------------------- diff --git a/ros_gz_bridge/CMakeLists.txt b/ros_gz_bridge/CMakeLists.txt index 8f4cc27d1..44911577c 100644 --- a/ros_gz_bridge/CMakeLists.txt +++ b/ros_gz_bridge/CMakeLists.txt @@ -14,58 +14,18 @@ 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) -# 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() +find_package(gz_transport_vendor REQUIRED) +find_package(gz-transport REQUIRED) -# 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() +find_package(gz_msgs_vendor REQUIRED) +find_package(gz-msgs REQUIRED) -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}) +# Install the python module for this package +ament_python_install_package(${PROJECT_NAME}) + +set(GZ_MSGS_VERSION_FULL ${gz-msgs_VERSION}) set(BRIDGE_MESSAGE_TYPES builtin_interfaces @@ -138,15 +98,18 @@ add_library(${bridge_lib} ) target_link_libraries(${bridge_lib} - ${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}::core - ${GZ_TARGET_PREFIX}-transport${GZ_TRANSPORT_VER}::core + PUBLIC + gz-msgs::core + gz-transport::core + PRIVATE + yaml-cpp::yaml-cpp ) ament_target_dependencies(${bridge_lib} - rclcpp - rclcpp_components - yaml_cpp_vendor - ${BRIDGE_MESSAGE_TYPES} + PUBLIC + rclcpp + rclcpp_components + ${BRIDGE_MESSAGE_TYPES} ) target_include_directories(${bridge_lib} @@ -158,11 +121,6 @@ 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 @@ -178,6 +136,11 @@ install( DESTINATION include/${PROJECT_NAME} ) +install( + DIRECTORY launch/ + DESTINATION share/${PROJECT_NAME}/launch +) + set(bridge_executables parameter_bridge static_bridge @@ -213,7 +176,7 @@ if(BUILD_TESTING) ${PROJECT_SOURCE_DIR}/src/convert/rcl_interfaces_TEST.cpp ) target_link_libraries(test_rcl_interfaces - ${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}::core + gz-msgs::core ${rcl_interfaces_TARGETS} gtest gtest_main @@ -248,8 +211,8 @@ if(BUILD_TESTING) ) target_link_libraries(test_utils ${GTEST_LIBRARIES} - ${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}::core - ${GZ_TARGET_PREFIX}-transport${GZ_TRANSPORT_VER}::core + gz-msgs::core + gz-transport::core ) ament_target_dependencies(test_utils rclcpp @@ -361,9 +324,10 @@ ament_export_targets(export_${PROJECT_NAME}) # specific order: dependents before dependencies ament_export_dependencies(rclcpp) ament_export_dependencies(rclcpp_components) -ament_export_dependencies(${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}) -ament_export_dependencies(${GZ_TARGET_PREFIX}-transport${GZ_TRANSPORT_VER}) -ament_export_dependencies(yaml_cpp_vendor) +ament_export_dependencies(gz_msgs_vendor) +ament_export_dependencies(gz-msgs) +ament_export_dependencies(gz_transport_vendor) +ament_export_dependencies(gz-transport) ament_export_dependencies(${BRIDGE_MESSAGE_TYPES}) ament_package() diff --git a/ros_gz_bridge/README.md b/ros_gz_bridge/README.md index f89837c20..572892347 100644 --- a/ros_gz_bridge/README.md +++ b/ros_gz_bridge/README.md @@ -5,76 +5,91 @@ between ROS and Gazebo Transport. The following message types can be bridged for topics: -| ROS type | Gazebo type | -|---------------------------------------------|:-------------------------------------------:| -| builtin_interfaces/msg/Time | ignition::msgs::Time | -| std_msgs/msg/Bool | ignition::msgs::Boolean | -| std_msgs/msg/ColorRGBA | ignition::msgs::Color | -| std_msgs/msg/Empty | ignition::msgs::Empty | -| std_msgs/msg/Float32 | ignition::msgs::Float | -| std_msgs/msg/Float64 | ignition::msgs::Double | -| std_msgs/msg/Header | ignition::msgs::Header | -| std_msgs/msg/Int32 | ignition::msgs::Int32 | -| std_msgs/msg/UInt32 | ignition::msgs::UInt32 | -| std_msgs/msg/String | ignition::msgs::StringMsg | -| geometry_msgs/msg/Wrench | ignition::msgs::Wrench | -| geometry_msgs/msg/WrenchStamped | ignition::msgs::Wrench | -| geometry_msgs/msg/Quaternion | ignition::msgs::Quaternion | -| geometry_msgs/msg/Vector3 | ignition::msgs::Vector3d | -| geometry_msgs/msg/Point | ignition::msgs::Vector3d | -| geometry_msgs/msg/Pose | ignition::msgs::Pose | -| geometry_msgs/msg/PoseArray | ignition::msgs::Pose_V | -| geometry_msgs/msg/PoseWithCovariance | ignition::msgs::PoseWithCovariance | -| geometry_msgs/msg/PoseStamped | ignition::msgs::Pose | -| geometry_msgs/msg/Transform | ignition::msgs::Pose | -| geometry_msgs/msg/TransformStamped | ignition::msgs::Pose | -| geometry_msgs/msg/Twist | ignition::msgs::Twist | -| geometry_msgs/msg/TwistStamped | ignition::msgs::Twist | -| geometry_msgs/msg/TwistWithCovariance | ignition::msgs::TwistWithCovariance | -| geometry_msgs/msg/TwistWithCovarianceStamped| ignition::msgs::TwistWithCovariance | -| 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/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 | +| 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 | And the following for services: | ROS type | Gazebo request | Gazebo response | |--------------------------------------|:--------------------------:| --------------------- | -| ros_gz_interfaces/srv/ControlWorld | ignition.msgs.WorldControl | ignition.msgs.Boolean | +| ros_gz_interfaces/srv/ControlWorld | gz.msgs.WorldControl | gz.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 +``` + ## Example 1a: Gazebo Transport talker and ROS 2 listener Start the parameter bridge which will watch the specified topics. @@ -82,14 +97,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@ignition.msgs.StringMsg +ros2 run ros_gz_bridge parameter_bridge /chatter@std_msgs/msg/String@gz.msgs.StringMsg ``` Now we start the ROS listener. ``` # Shell B: -. /opt/ros/humble/setup.bash +. /opt/ros/rolling/setup.bash ros2 topic echo /chatter ``` @@ -97,7 +112,7 @@ Now we start the Gazebo Transport talker. ``` # Shell C: -ign topic -t /chatter -m ignition.msgs.StringMsg -p 'data:"Hello"' +gz topic -t /chatter -m gz.msgs.StringMsg -p 'data:"Hello"' ``` ## Example 1b: ROS 2 talker and Gazebo Transport listener @@ -107,21 +122,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@ignition.msgs.StringMsg +ros2 run ros_gz_bridge parameter_bridge /chatter@std_msgs/msg/String@gz.msgs.StringMsg ``` Now we start the Gazebo Transport listener. ``` # Shell B: -ign topic -e -t /chatter +gz topic -e -t /chatter ``` Now we start the ROS talker. ``` # Shell C: -. /opt/ros/humble/setup.bash +. /opt/ros/rolling/setup.bash ros2 topic pub /chatter std_msgs/msg/String "data: 'Hi'" --once ``` @@ -135,14 +150,14 @@ First we start Gazebo Sim (don't forget to hit play, or Gazebo Sim won't generat ``` # Shell A: -ign gazebo sensors_demo.sdf +gz sim sensors_demo.sdf ``` Let's see the topic where camera images are published. ``` # Shell B: -ign topic -l | grep image +gz topic -l | grep image /rgbd_camera/depth_image /rgbd_camera/image ``` @@ -152,14 +167,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@ignition.msgs.Image +ros2 run ros_gz_bridge parameter_bridge /rgbd_camera/image@sensor_msgs/msg/Image@gz.msgs.Image ``` Now we start the ROS GUI: ``` # Shell C: -. /opt/ros/humble/setup.bash +. /opt/ros/rolling/setup.bash ros2 run rqt_image_view rqt_image_view /rgbd_camera/image ``` @@ -193,15 +208,15 @@ On terminal B, we start a ROS 2 listener: And terminal C, publish an Gazebo message: -`ign topic -t /chatter -m ignition.msgs.StringMsg -p 'data:"Hello"'` +`gz topic -t /chatter -m gz.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 Igntion listener: +On terminal D, start an Gazebo listener: -`ign topic -e -t /chatter` +`gz topic -e -t /chatter` And on terminal E, publish a ROS 2 message: @@ -219,7 +234,7 @@ On terminal A, start the service bridge: On terminal B, start Gazebo, it will be paused by default: -`ign gazebo shapes.sdf` +`gz sim shapes.sdf` On terminal C, make a ROS request to unpause simulation: @@ -241,35 +256,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: "ignition.msgs.StringMsg" + gz_type_name: "gz.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: "ignition.msgs.StringMsg" + gz_type_name: "gz.msgs.StringMsg" # Set just GZ topic name, applies to both -- gz_topic_name: "chatter_ign" +- gz_topic_name: "chatter_gz" ros_type_name: "std_msgs/msg/String" - gz_type_name: "ignition.msgs.StringMsg" + gz_type_name: "gz.msgs.StringMsg" # Set each topic name explicitly - ros_topic_name: "chatter_both_ros" - gz_topic_name: "chatter_both_ign" + gz_topic_name: "chatter_both_gz" ros_type_name: "std_msgs/msg/String" - gz_type_name: "ignition.msgs.StringMsg" + gz_type_name: "gz.msgs.StringMsg" # Full set of configurations - ros_topic_name: "ros_chatter" - gz_topic_name: "ign_chatter" + gz_topic_name: "gz_chatter" ros_type_name: "std_msgs/msg/String" - gz_type_name: "ignition.msgs.StringMsg" + gz_type_name: "gz.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 Ignition topic to ROS - # "ROS_TO_GZ" - Bridge ROS topic to Ignition + # "GZ_TO_ROS" - Bridge Gz topic to ROS + # "ROS_TO_GZ" - Bridge ROS topic to Gz ``` To run the bridge node with the above configuration: @@ -297,14 +312,14 @@ Now we start the Gazebo Transport listener. ```bash # Shell B: -ign topic -e -t /demo/chatter +gz topic -e -t /demo/chatter ``` Now we start the ROS talker. ```bash # Shell C: -. /opt/ros/humble/setup.bash +. /opt/ros/rolling/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 05c11b726..6e5d12e47 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":32}| {"Gazebo Transport Type":32}|') - rows.append(f'|{"-":-<33}|:{"-":-<31}:|') + rows.append(f'| {"ROS type":35}| {"Gazebo Transport Type":35}|') + rows.append(f'|{"-":-<36}|:{"-":-<34}:|') for mapping in mappings(msgs_ver): - rows.append('| {:32}| {:32}|'.format( + rows.append('| {:35}| {:35}|'.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 70537def2..6416fa357 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,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -112,6 +113,18 @@ 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 261849c5f..a81e35a7d 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_ -// Ignition messages +// GZ 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 136c8d9b6..b49f02d7f 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 @@ -18,12 +18,15 @@ // Gazebo Msgs #include #include +#include #include #include #include +#include #include #include #include +#include #include #include #include @@ -35,12 +38,15 @@ // ROS 2 messages #include #include +#include #include #include #include +#include #include #include #include +#include #include #include #include @@ -48,19 +54,8 @@ #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 @@ -102,6 +97,18 @@ convert_gz_to_ros( const gz::msgs::Entity & gz_msg, ros_gz_interfaces::msg::Entity & ros_msg); +template<> +void +convert_ros_to_gz( + const ros_gz_interfaces::msg::EntityWrench & ros_msg, + gz::msgs::EntityWrench & gz_msg); + +template<> +void +convert_gz_to_ros( + const gz::msgs::EntityWrench & gz_msg, + ros_gz_interfaces::msg::EntityWrench & ros_msg); + template<> void convert_ros_to_gz( @@ -126,7 +133,6 @@ 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( @@ -138,7 +144,6 @@ void convert_gz_to_ros( const gz::msgs::Dataframe & ign_msg, ros_gz_interfaces::msg::Dataframe & ros_msg); -#endif // HAVE_DATAFRAME template<> void @@ -164,7 +169,6 @@ 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( @@ -176,7 +180,6 @@ 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 7916aaa73..d4d7d5100 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 71abf1d7a..735cdecce 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,25 +24,6 @@ #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 @@ -56,7 +37,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-IGN bridge to the node + /// \brief Add a new ROS-GZ 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 new file mode 100644 index 000000000..929c72fe5 --- /dev/null +++ b/ros_gz_bridge/launch/ros_gz_bridge.launch @@ -0,0 +1,18 @@ + + + + + + + + + + + diff --git a/ros_gz_bridge/launch/ros_gz_bridge.launch.py b/ros_gz_bridge/launch/ros_gz_bridge.launch.py new file mode 100644 index 000000000..5ad46b9df --- /dev/null +++ b/ros_gz_bridge/launch/ros_gz_bridge.launch.py @@ -0,0 +1,114 @@ +# 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 LoadComposableNodes, Node +from launch_ros.descriptions import ComposableNode + + +def generate_launch_description(): + + name = LaunchConfiguration('name') + config_file = LaunchConfiguration('config_file') + container_name = LaunchConfiguration('container_name') + namespace = LaunchConfiguration('namespace') + use_composition = LaunchConfiguration('use_composition') + use_respawn = LaunchConfiguration('use_respawn') + log_level = LaunchConfiguration('log_level') + + declare_name_cmd = DeclareLaunchArgument( + 'name', description='Name of ros_gz_bridge node' + ) + + declare_config_file_cmd = DeclareLaunchArgument( + 'config_file', default_value='', 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_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=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 = LoadComposableNodes( + condition=IfCondition(use_composition), + target_container=container_name, + composable_node_descriptions=[ + ComposableNode( + package='ros_gz_bridge', + plugin='ros_gz_bridge::RosGzBridge', + name=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_name_cmd) + ld.add_action(declare_config_file_cmd) + ld.add_action(declare_container_name_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) + + return ld diff --git a/ros_gz_bridge/package.xml b/ros_gz_bridge/package.xml index 7290a2546..bd06f26ba 100644 --- a/ros_gz_bridge/package.xml +++ b/ros_gz_bridge/package.xml @@ -2,20 +2,27 @@ ros_gz_bridge - 0.244.15 + 1.0.1 Bridge communication between ROS and Gazebo Transport - Louise Poubel + Aditya Pande + Alejandro Hernandez 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 @@ -28,20 +35,8 @@ yaml_cpp_vendor vision_msgs - - gz-msgs10 - gz-transport13 - - gz-msgs9 - gz-transport12 - - ignition-msgs8 - ignition-transport11 - ignition-msgs8 - ignition-transport11 - - ignition-msgs7 - ignition-transport10 + gz_msgs_vendor + gz_transport_vendor 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 ef81673e9..2d9bcfaa0 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.ign_type()) + @(m.gz_type()) > >("@(m.ros2_string())", "@(m.gz_string())"); } @@ -52,10 +52,10 @@ template<> void Factory< @(m.ros2_type()), - @(m.ign_type()) + @(m.gz_type()) >::convert_ros_to_gz( const @(m.ros2_type()) & ros_msg, - @(m.ign_type()) & gz_msg) + @(m.gz_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.ign_type()) + @(m.gz_type()) >::convert_gz_to_ros( - const @(m.ign_type()) & gz_msg, + const @(m.gz_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 new file mode 100644 index 000000000..e69de29bb diff --git a/ros_gz_bridge/ros_gz_bridge/__init__.py b/ros_gz_bridge/ros_gz_bridge/__init__.py index ef021faf8..f6ea05eed 100644 --- a/ros_gz_bridge/ros_gz_bridge/__init__.py +++ b/ros_gz_bridge/ros_gz_bridge/__init__.py @@ -16,9 +16,15 @@ import os -from ros_gz_bridge.mappings import MAPPINGS, MAPPINGS_10_1_0, MAPPINGS_8_4_0 +from ros_gz_bridge.mappings import MAPPINGS -from rosidl_cmake import expand_template +from rosidl_pycommon import expand_template + +from . import actions + +__all__ = [ + 'actions', +] @dataclass @@ -66,23 +72,6 @@ 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 new file mode 100644 index 000000000..0a2c26657 --- /dev/null +++ b/ros_gz_bridge/ros_gz_bridge/actions/__init__.py @@ -0,0 +1,22 @@ +# 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 new file mode 100644 index 000000000..9704f44e8 --- /dev/null +++ b/ros_gz_bridge/ros_gz_bridge/actions/ros_gz_bridge.py @@ -0,0 +1,147 @@ +# 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, + *, + name: SomeSubstitutionsType, + config_file: Optional[SomeSubstitutionsType] = None, + container_name: Optional[SomeSubstitutionsType] = None, + namespace: Optional[SomeSubstitutionsType] = None, + use_composition: Optional[SomeSubstitutionsType] = None, + use_respawn: Optional[SomeSubstitutionsType] = None, + log_level: Optional[SomeSubstitutionsType] = None, + **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: 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: 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.__name = name + self.__config_file = config_file + self.__container_name = container_name + 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) + + name = entity.get_attr( + '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) + + 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(name, str): + name = parser.parse_substitution(name) + kwargs['name'] = 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(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=[('name', self.__name), + ('config_file', self.__config_file), + ('container_name', self.__container_name), + ('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 d17fb56d8..203e33d1a 100644 --- a/ros_gz_bridge/ros_gz_bridge/mappings.py +++ b/ros_gz_bridge/ros_gz_bridge/mappings.py @@ -36,6 +36,7 @@ Mapping('PoseArray', 'Pose_V'), Mapping('PoseStamped', 'Pose'), Mapping('PoseWithCovariance', 'PoseWithCovariance'), + Mapping('PoseWithCovarianceStamped', 'PoseWithCovariance'), Mapping('Quaternion', 'Quaternion'), Mapping('Transform', 'Pose'), Mapping('TransformStamped', 'Pose'), @@ -61,11 +62,14 @@ 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'), @@ -113,15 +117,3 @@ 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 new file mode 100644 index 000000000..1f7078137 --- /dev/null +++ b/ros_gz_bridge/setup.cfg @@ -0,0 +1,3 @@ +[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 52d649f76..d15e0dd83 100644 --- a/ros_gz_bridge/src/bridge_config.cpp +++ b/ros_gz_bridge/src/bridge_config.cpp @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include @@ -40,12 +41,6 @@ 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 @@ -60,43 +55,37 @@ std::optional parseEntry(const YAML::Node & yaml_node) return {}; } - /// \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]) { + 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()) { RCLCPP_ERROR( logger, "Could not parse entry: %s and %s are mutually exclusive", kTopicName, kRosTopicName); return {}; } - if (yaml_node[kTopicName] && !gz_topic_name.empty()) { + if (!topic_name.empty() && !gz_topic_name.empty()) { RCLCPP_ERROR( logger, "Could not parse entry: %s and %s are mutually exclusive", kTopicName, kGzTopicName); return {}; } - if (!yaml_node[kRosTypeName] || gz_type_name.empty()) { + if (ros_type_name.empty() || gz_type_name.empty()) { RCLCPP_ERROR( logger, "Could not parse entry: both %s and %s must be set", kRosTypeName, kGzTypeName); @@ -108,52 +97,40 @@ std::optional parseEntry(const YAML::Node & yaml_node) ret.direction = BridgeDirection::BIDIRECTIONAL; if (yaml_node[kDirection]) { - auto dirStr = yaml_node[kDirection].as(); - - if (dirStr == kBidirectional) { + if (direction == kBidirectional) { ret.direction = BridgeDirection::BIDIRECTIONAL; - } else if (dirStr == kGzToRos) { + } else if (direction == kGzToRos) { ret.direction = BridgeDirection::GZ_TO_ROS; - } else if (dirStr == kRosToGz) { + } else if (direction == 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]", dirStr.c_str()); + "Could not parse entry: invalid direction [%s]", direction.c_str()); return {}; } } - if (yaml_node[kTopicName]) { + if (!topic_name.empty()) { // Only "topic_name" is set - 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()) { + ret.gz_topic_name = topic_name; + ret.ros_topic_name = topic_name; + } else if (!ros_topic_name.empty() && gz_topic_name.empty()) { // Only "ros_topic_name" is set - 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]) { + ret.gz_topic_name = ros_topic_name; + ret.ros_topic_name = ros_topic_name; + } else if (!gz_topic_name.empty() && ros_topic_name.empty()) { // 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 = yaml_node[kRosTopicName].as(); + ret.ros_topic_name = ros_topic_name; } ret.gz_type_name = gz_type_name; - ret.ros_type_name = yaml_node[kRosTypeName].as(); + ret.ros_type_name = ros_type_name; if (yaml_node[kPublisherQueue]) { ret.publisher_queue_size = yaml_node[kPublisherQueue].as(); @@ -170,16 +147,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; } @@ -197,6 +174,29 @@ 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/convert/actuator_msgs.cpp b/ros_gz_bridge/src/convert/actuator_msgs.cpp index 0c9bcde34..ddcdd2058 100644 --- a/ros_gz_bridge/src/convert/actuator_msgs.cpp +++ b/ros_gz_bridge/src/convert/actuator_msgs.cpp @@ -17,7 +17,6 @@ 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 ad83ba6dc..01c782a58 100644 --- a/ros_gz_bridge/src/convert/geometry_msgs.cpp +++ b/ros_gz_bridge/src/convert/geometry_msgs.cpp @@ -165,6 +165,27 @@ 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 de8062394..cf2774d66 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 Ignition::Msgs::NavSat. + // position_covariance is not supported in gz::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 0d6199551..eb923b344 100644 --- a/ros_gz_bridge/src/convert/ros_gz_interfaces.cpp +++ b/ros_gz_bridge/src/convert/ros_gz_interfaces.cpp @@ -139,6 +139,28 @@ convert_gz_to_ros( } } +template<> +void +convert_ros_to_gz( + const ros_gz_interfaces::msg::EntityWrench & ros_msg, + gz::msgs::EntityWrench & gz_msg) +{ + convert_ros_to_gz(ros_msg.header, (*gz_msg.mutable_header())); + convert_ros_to_gz(ros_msg.entity, (*gz_msg.mutable_entity())); + convert_ros_to_gz(ros_msg.wrench, (*gz_msg.mutable_wrench())); +} + +template<> +void +convert_gz_to_ros( + const gz::msgs::EntityWrench & gz_msg, + ros_gz_interfaces::msg::EntityWrench & ros_msg) +{ + convert_gz_to_ros(gz_msg.header(), ros_msg.header); + convert_gz_to_ros(gz_msg.entity(), ros_msg.entity); + convert_gz_to_ros(gz_msg.wrench(), ros_msg.wrench); +} + template<> void convert_ros_to_gz( @@ -223,7 +245,6 @@ convert_gz_to_ros( } } -#if HAVE_DATAFRAME template<> void convert_ros_to_gz( @@ -273,7 +294,6 @@ convert_gz_to_ros( gz_msg.data().begin() + gz_msg.data().size(), ros_msg.data.begin()); } -#endif // HAVE_DATAFRAME template<> void @@ -380,7 +400,6 @@ convert_gz_to_ros( ros_msg.intensity = gz_msg.intensity(); } -#if HAVE_MATERIALCOLOR template<> void convert_ros_to_gz( @@ -438,7 +457,6 @@ convert_gz_to_ros( ros_msg.shininess = gz_msg.shininess(); } -#endif // HAVE_MATERIALCOLOR template<> void @@ -475,9 +493,7 @@ 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() == // NOLINT - gz::msgs::SensorNoise_Type::SensorNoise_Type_GAUSSIAN_QUANTIZED) // NOLINT - { // NOLINT + } else if (gz_msg.type() == gz::msgs::SensorNoise_Type::SensorNoise_Type_GAUSSIAN_QUANTIZED) { 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 887f16d0f..f537f9087 100644 --- a/ros_gz_bridge/src/convert/sensor_msgs.cpp +++ b/ros_gz_bridge/src/convert/sensor_msgs.cpp @@ -166,13 +166,11 @@ convert_gz_to_ros( ros_msg.is_bigendian = false; ros_msg.step = ros_msg.width * num_channels * octets_per_channel; - - 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()); + + // 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()); } template<> @@ -520,7 +518,7 @@ convert_gz_to_ros( ros_msg.longitude = gz_msg.longitude_deg(); ros_msg.altitude = gz_msg.altitude(); - // position_covariance is not supported in Ignition::Msgs::NavSat. + // position_covariance is not supported in gz::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 35daf4162..1ee9ded7d 100644 --- a/ros_gz_bridge/src/factory.hpp +++ b/ros_gz_bridge/src/factory.hpp @@ -20,6 +20,7 @@ #include #include +#include // include ROS 2 #include @@ -105,18 +106,16 @@ class Factory : public FactoryInterface size_t /*queue_size*/, rclcpp::PublisherBase::SharedPtr ros_pub) { - std::function subCb = - [this, ros_pub](const GZ_T & _msg, - const gz::transport::MessageInfo & _info) + std::function subCb = + [this, ros_pub](const GZ_T & _msg) { - // Ignore messages that are published from this bridge. - if (!_info.IntraProcess()) { - this->gz_callback(_msg, ros_pub); - } + this->gz_callback(_msg, ros_pub); }; - node->Subscribe(topic_name, subCb); + // Ignore messages that are published from this bridge. + gz::transport::SubscribeOptions opts; + opts.SetIgnoreLocalMessages(true); + node->Subscribe(topic_name, subCb, opts); } protected: diff --git a/ros_gz_bridge/src/parameter_bridge.cpp b/ros_gz_bridge/src/parameter_bridge.cpp index 12aac8ab6..d7c80dc46 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@ignition.msgs" << + " parameter_bridge /chatter@std_msgs/String@gz.msgs" << ".StringMsg\n\n" << "A bridge from Gazebo to ROS example:\n" << - " parameter_bridge /chatter@std_msgs/String[ignition.msgs" << + " parameter_bridge /chatter@std_msgs/String[gz.msgs" << ".StringMsg\n\n" << "A bridge from ROS to Gazebo example:\n" << - " parameter_bridge /chatter@std_msgs/String]ignition.msgs" << + " parameter_bridge /chatter@std_msgs/String]gz.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@" - "ignition.msgs.WorldControl@ignition.msgs.Boolean\n" << std::endl; + "gz.msgs.WorldControl@gz.msgs.Boolean\n" << std::endl; } using RosGzBridge = ros_gz_bridge::RosGzBridge; 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 17fb52898..a3c25a825 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 == "ignition.msgs.WorldControl") && - (gz_rep_type_name.empty() || gz_rep_type_name == "ignition.msgs.Boolean")) + (gz_req_type_name.empty() || gz_req_type_name == "gz.msgs.WorldControl") && + (gz_rep_type_name.empty() || gz_rep_type_name == "gz.msgs.Boolean")) { return std::make_shared< ServiceFactory< ros_gz_interfaces::srv::ControlWorld, gz::msgs::WorldControl, gz::msgs::Boolean> - >(ros_type_name, "ignition.msgs.WorldControl", "ignition.msgs.Boolean"); + >(ros_type_name, "gz.msgs.WorldControl", "gz.msgs.Boolean"); } return nullptr; diff --git a/ros_gz_bridge/src/service_factory.hpp b/ros_gz_bridge/src/service_factory.hpp index f771d6180..4a02503f1 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 IgnReplyT & reply, + const GzReplyT & 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); }; - IgnRequestT gz_req; + GzRequestT 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 bf5cd38d6..7692866a7 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 = "ignition.msgs.StringMsg"; + config.gz_type_name = "gz.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 45ace8a5b..079878655 100644 --- a/ros_gz_bridge/test/bridge_config.cpp +++ b/ros_gz_bridge/test/bridge_config.cpp @@ -16,56 +16,61 @@ #include -TEST(BridgeConfig, Minimum) +#include "rcutils/logging.h" + +size_t g_log_calls = 0; + +struct LogEvent { - auto results = ros_gz_bridge::readFromYamlFile("test/config/minimum.yaml"); - EXPECT_EQ(4u, results.size()); + 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; +class BridgeConfig : public ::testing::Test +{ +public: + rcutils_logging_output_handler_t previous_output_handler; + void SetUp() { - 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); - } - { - 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); + 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); } + + void TearDown() { - 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); + rcutils_logging_set_output_handler(this->previous_output_handler); + ASSERT_EQ(RCUTILS_RET_OK, rcutils_logging_shutdown()); + EXPECT_FALSE(g_rcutils_logging_initialized); } -} +}; -TEST(BridgeConfig, MinimumIgn) + +TEST_F(BridgeConfig, Minimum) { - auto results = ros_gz_bridge::readFromYamlFile("test/config/minimum_ign.yaml"); + auto results = ros_gz_bridge::readFromYamlFile("test/config/minimum.yaml"); EXPECT_EQ(4u, results.size()); { @@ -110,38 +115,7 @@ TEST(BridgeConfig, MinimumIgn) } } - -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) +TEST_F(BridgeConfig, FullGz) { auto results = ros_gz_bridge::readFromYamlFile("test/config/full.yaml"); EXPECT_EQ(2u, results.size()); @@ -171,7 +145,7 @@ TEST(BridgeConfig, FullIgn) } } -TEST(BridgeConfig, InvalidSetTwoRos) +TEST_F(BridgeConfig, InvalidSetTwoRos) { // Cannot set topic_name and ros_topic_name auto yaml = R"( @@ -180,9 +154,12 @@ TEST(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(BridgeConfig, InvalidSetTwoGz) +TEST_F(BridgeConfig, InvalidSetTwoGz) { // Cannot set topic_name and gz_topic_name auto yaml = R"( @@ -191,9 +168,12 @@ TEST(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(BridgeConfig, InvalidSetTypes) +TEST_F(BridgeConfig, InvalidSetTypes) { // Both ros_type_name and gz_type_name must be set auto yaml = R"( @@ -202,9 +182,12 @@ TEST(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(BridgeConfig, ParseDirection) +TEST_F(BridgeConfig, ParseDirection) { { // Check that default is bidirectional @@ -264,10 +247,38 @@ TEST(BridgeConfig, ParseDirection) - topic_name: foo ros_type_name: std_msgs/msg/String gz_type_name: ignition.msgs.StringMsg - direction: asdfasdfasdfasdf + direction: foobar )"; 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 new file mode 100644 index 000000000..e69de29bb diff --git a/ros_gz_bridge/test/config/full_ign.yaml b/ros_gz_bridge/test/config/full_ign.yaml deleted file mode 100644 index fbee755bb..000000000 --- a/ros_gz_bridge/test/config/full_ign.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# 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 new file mode 100644 index 000000000..c14740add --- /dev/null +++ b/ros_gz_bridge/test/config/invalid.yaml @@ -0,0 +1,8 @@ +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 deleted file mode 100644 index 3fe8bc27b..000000000 --- a/ros_gz_bridge/test/config/minimum_ign.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# 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 9cccbfe0f..07a1de912 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.ign_type())>("@(m.unique())"); - @(m.ign_type()) @(m.unique())_msg; + node.Advertise<@(m.gz_type())>("@(m.unique())"); + @(m.gz_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 6edae074c..c6b899ed5 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.ign_type())> client("@(m.unique())"); + MyTestClass<@(m.gz_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 71a651b3b..b13a06b1c 100644 --- a/ros_gz_bridge/test/utils/gz_test_msg.cpp +++ b/ros_gz_bridge/test/utils/gz_test_msg.cpp @@ -376,6 +376,7 @@ 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); } @@ -525,6 +526,30 @@ void compareTestMsg(const std::shared_ptr & _msg) EXPECT_EQ(expected_msg.type(), _msg->type()); } +void createTestMsg(gz::msgs::EntityWrench & _msg) +{ + gz::msgs::Header header_msg; + gz::msgs::Entity entity_msg; + gz::msgs::Wrench wrench_msg; + + createTestMsg(header_msg); + createTestMsg(entity_msg); + createTestMsg(wrench_msg); + + _msg.mutable_header()->CopyFrom(header_msg); + _msg.mutable_entity()->CopyFrom(entity_msg); + _msg.mutable_wrench()->CopyFrom(wrench_msg); +} + +void compareTestMsg(const std::shared_ptr & _msg) +{ + gz::msgs::EntityWrench expected_msg; + createTestMsg(expected_msg); + compareTestMsg(std::make_shared(_msg->header())); + compareTestMsg(std::make_shared(_msg->entity())); + compareTestMsg(std::make_shared(_msg->wrench())); +} + void createTestMsg(gz::msgs::Contact & _msg) { gz::msgs::Entity collision1; @@ -616,7 +641,6 @@ void compareTestMsg(const std::shared_ptr & _msg) } } -#if HAVE_DATAFRAME void createTestMsg(gz::msgs::Dataframe & _msg) { gz::msgs::Header header_msg; @@ -654,7 +678,6 @@ 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) { @@ -1347,7 +1370,6 @@ 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()); @@ -1376,7 +1398,6 @@ 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 ad063b628..cff1a0af8 100644 --- a/ros_gz_bridge/test/utils/gz_test_msg.hpp +++ b/ros_gz_bridge/test/utils/gz_test_msg.hpp @@ -29,8 +29,9 @@ #include #include #include +#include +#include #include -#include #include #include #include @@ -43,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -70,13 +72,6 @@ #include #include -#if HAVE_DATAFRAME -#include -#endif // HAVE_DATAFRAME - -#if HAVE_MATERIALCOLOR -#include -#endif // HAVE_MATERIALCOLOR namespace ros_gz_bridge { @@ -170,10 +165,6 @@ 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); @@ -182,6 +173,10 @@ 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); @@ -298,6 +293,14 @@ void createTestMsg(gz::msgs::Entity & _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::EntityWrench & _msg); + +/// \brief Compare a message with the populated for testing. +/// \param[in] _msg The message to compare. +void compareTestMsg(const std::shared_ptr & _msg); + /// \brief Create a message used for testing. /// \param[out] _msg The message populated. void createTestMsg(gz::msgs::Contact & _msg); @@ -314,7 +317,6 @@ 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); @@ -322,7 +324,6 @@ 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. @@ -460,7 +461,6 @@ 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,7 +468,6 @@ 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 76e80d292..42f9b90b7 100644 --- a/ros_gz_bridge/test/utils/ros_test_msg.cpp +++ b/ros_gz_bridge/test/utils/ros_test_msg.cpp @@ -58,6 +58,14 @@ 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; @@ -89,14 +97,6 @@ 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,6 +381,18 @@ 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); @@ -610,7 +622,6 @@ 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); @@ -638,7 +649,6 @@ 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) { @@ -857,6 +867,23 @@ void compareTestMsg(const std::shared_ptr & _msg EXPECT_EQ(expected_msg.type, _msg->type); } +void createTestMsg(ros_gz_interfaces::msg::EntityWrench & _msg) +{ + createTestMsg(_msg.header); + createTestMsg(_msg.entity); + createTestMsg(_msg.wrench); +} + +void compareTestMsg(const std::shared_ptr & _msg) +{ + ros_gz_interfaces::msg::EntityWrench expected_msg; + createTestMsg(expected_msg); + + compareTestMsg(std::make_shared(_msg->header)); + compareTestMsg(std::make_shared(_msg->entity)); + compareTestMsg(std::make_shared(_msg->wrench)); +} + void createTestMsg(ros_gz_interfaces::msg::Contact & _msg) { createTestMsg(_msg.collision1); @@ -918,7 +945,6 @@ void compareTestMsg(const std::shared_ptr & _m } } -#if HAVE_DATAFRAME void createTestMsg(ros_gz_interfaces::msg::Dataframe & _msg) { createTestMsg(_msg.header); @@ -944,7 +970,6 @@ 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 6bc395cbb..682113055 100644 --- a/ros_gz_bridge/test/utils/ros_test_msg.hpp +++ b/ros_gz_bridge/test/utils/ros_test_msg.hpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -49,18 +50,15 @@ #include #include #include +#include #include #include #include #include #include -#if HAVE_DATAFRAME #include -#endif // HAVE_DATAFRAME #include -#if HAVE_MATERIALCOLOR #include -#endif // HAVE_MATERIALCOLOR #include #include #include @@ -268,6 +266,18 @@ 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); @@ -400,7 +410,6 @@ 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); @@ -408,7 +417,6 @@ 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. @@ -418,6 +426,14 @@ void createTestMsg(ros_gz_interfaces::msg::Entity & _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(ros_gz_interfaces::msg::EntityWrench & _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(ros_gz_interfaces::msg::Contact & _msg); @@ -434,7 +450,6 @@ 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); @@ -442,7 +457,6 @@ 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 6d62b38da..10997b1df 100644 --- a/ros_gz_image/CHANGELOG.rst +++ b/ros_gz_image/CHANGELOG.rst @@ -2,23 +2,90 @@ Changelog for package ros1_ign_image ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -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 `_) -* Fix double wait in ros_gz_bridge (`#347 `_) (`#450 `_) +1.0.1 (2024-07-03) +------------------ +* Merge pull request `#571 `_ from azeey/jazzy_to_ros2 + Merge jazzy ➡️ ros2 +* Merge jazzy into ros2 +* 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 `_) * Contributors: Addisu Z. Taddese, Alejandro Hernández Cordero -0.244.11 (2023-05-23) ---------------------- +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.10 (2023-05-03) --------------------- diff --git a/ros_gz_image/CMakeLists.txt b/ros_gz_image/CMakeLists.txt index 545b0b27d..e6e8de260 100644 --- a/ros_gz_image/CMakeLists.txt +++ b/ros_gz_image/CMakeLists.txt @@ -16,55 +16,11 @@ find_package(ros_gz_bridge REQUIRED) find_package(rclcpp REQUIRED) find_package(sensor_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() - -# 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_transport_vendor REQUIRED) +find_package(gz-transport 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() +find_package(gz_msgs_vendor REQUIRED) +find_package(gz-msgs REQUIRED) include_directories(include) @@ -77,8 +33,8 @@ add_executable(${executable} ) target_link_libraries(${executable} - ${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}::core - ${GZ_TARGET_PREFIX}-transport${GZ_TRANSPORT_VER}::core + gz-msgs::core + gz-transport::core ) ament_target_dependencies(${executable} @@ -112,8 +68,8 @@ if(BUILD_TESTING) # ) # target_link_libraries(${test_publisher}_image # ${catkin_LIBRARIES} - # ${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}::core - # ${GZ_TARGET_PREFIX}-transport${GZ_TRANSPORT_VER}::core + # $gz-msgs$::core + # $gz-transport::core # gtest # gtest_main # ) @@ -125,8 +81,8 @@ if(BUILD_TESTING) # test/subscribers/${test_subscriber}.cpp) # target_link_libraries(test_${test_subscriber}_image # ${catkin_LIBRARIES} - # ${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}::core - # ${GZ_TARGET_PREFIX}-transport${GZ_TRANSPORT_VER}::core + # $gz-msgs::core + # $gz-transport::core # ) # endforeach(test_subscriber) endif() diff --git a/ros_gz_image/README.md b/ros_gz_image/README.md index b7b27e1fd..a6c9c5e51 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 (`ignition::msgs::Image`) +The bridge subscribes to Gazebo image messages (`gz::msgs::Image`) and republishes them to ROS using [image_transport](http://wiki.ros.org/image_transport). For compressed images, install @@ -9,3 +9,14 @@ 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 34aa86e67..1ea41b206 100644 --- a/ros_gz_image/package.xml +++ b/ros_gz_image/package.xml @@ -1,9 +1,12 @@ ros_gz_image - 0.244.15 + 1.0.1 Image utilities for Gazebo simulation with ROS. Apache 2.0 - Louise Poubel + Aditya Pande + Alejandro Hernandez + + Louise Poubel ament_cmake pkg-config @@ -13,20 +16,8 @@ rclcpp sensor_msgs - - gz-msgs10 - gz-transport13 - - gz-msgs9 - gz-transport12 - - ignition-msgs8 - ignition-transport11 - ignition-msgs8 - ignition-transport11 - - ignition-msgs7 - ignition-transport10 + gz_msgs_vendor + gz_transport_vendor 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 c167c67c4..db21f42c6 100644 --- a/ros_gz_image/src/image_bridge.cpp +++ b/ros_gz_image/src/image_bridge.cpp @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include @@ -19,25 +20,42 @@ #include #include -#include #include +#include #include ////////////////////////////////////////////////// -/// \brief Bridges one topic +/// \brief Bridges one image topic class Handler { public: /// \brief Constructor /// \param[in] _topic Image base topic - /// \param[in] _it_node Pointer to image transport node + /// \param[in] _node Pointer to ROS node /// \param[in] _gz_node Pointer to Gazebo node Handler( const std::string & _topic, - std::shared_ptr _it_node, + std::shared_ptr _node, std::shared_ptr _gz_node) { - this->ros_pub = _it_node->advertise(_topic, 1); + // 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); _gz_node->Subscribe(_topic, &Handler::OnImage, this); } @@ -77,7 +95,7 @@ int main(int argc, char * argv[]) // ROS node auto node_ = rclcpp::Node::make_shared("ros_gz_image"); - auto it_node = std::make_shared(node_); + node_->declare_parameter("qos", "default"); // Gazebo node auto gz_node = std::make_shared(); @@ -91,7 +109,7 @@ int main(int argc, char * argv[]) // Create publishers and subscribers for (auto topic : args) { - handlers.push_back(std::make_shared(topic, it_node, gz_node)); + handlers.push_back(std::make_shared(topic, node_, gz_node)); } // Spin ROS and Gz until shutdown diff --git a/ros_gz_interfaces/CHANGELOG.rst b/ros_gz_interfaces/CHANGELOG.rst index 43ce449bd..3d35db530 100644 --- a/ros_gz_interfaces/CHANGELOG.rst +++ b/ros_gz_interfaces/CHANGELOG.rst @@ -2,12 +2,24 @@ Changelog for package ros_gz_interfaces ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -0.244.15(2024-07-03) ---------------------- - -0.244.14 (2024-04-08) ---------------------- -* Add option to change material color from ROS. (`#486 `_) +1.0.1 (2024-07-03) +------------------ +* Add support for gz.msgs.EntityWrench (base branch: ros2) (`#573 `_) +* 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) +* 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. +* Prepare for 1.0.0 Release (`#495 `_) +* 0.244.14 +* Changelog +* 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. @@ -15,19 +27,49 @@ 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 -* Contributors: Benjamin Perseghetti - -0.244.13 (2024-01-23) ---------------------- +* 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, Victor T. Noppeney, 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 -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.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_interfaces/CMakeLists.txt b/ros_gz_interfaces/CMakeLists.txt index 5f3b312e4..9f3d7a1fb 100644 --- a/ros_gz_interfaces/CMakeLists.txt +++ b/ros_gz_interfaces/CMakeLists.txt @@ -23,6 +23,7 @@ set(msg_files "msg/Dataframe.msg" "msg/Entity.msg" "msg/EntityFactory.msg" + "msg/EntityWrench.msg" "msg/Float32Array.msg" "msg/GuiCamera.msg" "msg/JointWrench.msg" diff --git a/ros_gz_interfaces/README.md b/ros_gz_interfaces/README.md index 88ed27d76..610cb7e34 100644 --- a/ros_gz_interfaces/README.md +++ b/ros_gz_interfaces/README.md @@ -8,6 +8,7 @@ This package currently contains some Gazebo-specific ROS message and service dat * [Contacts](msg/Contacts.msg): related to [ignition::msgs::Contacts](https://github.com/gazebosim/gz-msgs/blob/ign-msgs7/proto/ignition/msgs/contacts.proto). A list of contacts. * [Entity](msg/Entity.msg): related to [ignition::msgs::Entity](https://github.com/gazebosim/gz-msgs/blob/ign-msgs7/proto/ignition/msgs/entity.proto). Entity of Gazebo Sim. * [EntityFactory](msg/EntityFactory.msg): related to [ignition::msgs::EntityFactory](https://github.com/gazebosim/gz-msgs/blob/ign-msgs7/proto/ignition/msgs/entity_factory.proto). Message to create a new entity. +* [EntityWrench](msg/EntityWrench.msg): related to [ignition::msgs::EntityWrench](https://github.com/gazebosim/gz-msgs/blob/ign-msgs7/proto/gz/msgs/entity_wrench.proto). Wrench to be applied to a specified Entity of Gazebo Sim. * [Light](msg/Light.msg): related to [ignition::msgs::Light](https://github.com/gazebosim/gz-msgs/blob/ign-msgs7/proto/ignition/msgs/light.proto). Light info in Gazebo Sim. * [WorldControl](msg/WorldControl.msg): related to [ignition::msgs::WorldControl](https://github.com/gazebosim/gz-msgs/blob/ign-msgs7/proto/ignition/msgs/world_control.proto). Message to control world of Gazebo Sim. * [WorldReset](msg/WorldReset.msg): related to [ignition::msgs::WorldReset](https://github.com/gazebosim/gz-msgs/blob/ign-msgs7/proto/ignition/msgs/world_reset.proto). Reset time and model of simulation. diff --git a/ros_gz_interfaces/msg/EntityWrench.msg b/ros_gz_interfaces/msg/EntityWrench.msg new file mode 100644 index 000000000..c1a039875 --- /dev/null +++ b/ros_gz_interfaces/msg/EntityWrench.msg @@ -0,0 +1,3 @@ +std_msgs/Header header # Time stamp +ros_gz_interfaces/Entity entity # Entity +geometry_msgs/Wrench wrench # Wrench to be applied to entity \ No newline at end of file diff --git a/ros_gz_interfaces/package.xml b/ros_gz_interfaces/package.xml index 68c01037c..33a6db4d9 100644 --- a/ros_gz_interfaces/package.xml +++ b/ros_gz_interfaces/package.xml @@ -1,10 +1,12 @@ ros_gz_interfaces - 0.244.15 + 1.0.1 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 574d0ce44..85beba43f 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: - ign gazebo -r examples/depth_camera.sdf + gz sim -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 7bd3a7354..3feed57a4 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: - ign gazebo -r examples/gpu_lidar.sdf + gz sim -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 64a77b6a9..235cb2255 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: - ign gazebo -r examples/rgbd_camera.sdf + gz sim -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 b3d5717e5..9256c482a 100644 --- a/ros_gz_point_cloud/package.xml +++ b/ros_gz_point_cloud/package.xml @@ -3,7 +3,10 @@ 0.7.0 Point cloud utilities for Gazebo simulation with ROS. Apache 2.0 - Louise Poubel + Aditya Pande + Alejandro Hernandez + + Louise Poubel catkin diff --git a/ros_gz_sim/CHANGELOG.rst b/ros_gz_sim/CHANGELOG.rst index dfa238352..7aef01615 100644 --- a/ros_gz_sim/CHANGELOG.rst +++ b/ros_gz_sim/CHANGELOG.rst @@ -2,12 +2,41 @@ Changelog for package ros_gz_sim ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -0.244.15(2024-07-03) ---------------------- - -0.244.14 (2024-04-08) ---------------------- -* Support `` in `package.xml` exports (`#492 `_) +1.0.1 (2024-07-03) +------------------ +* Merge pull request `#571 `_ from azeey/jazzy_to_ros2 + Merge jazzy ➡️ ros2 +* Merge jazzy into ros2 +* 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 +* Update launch files with name parameter (`#556 `_) + * Name is required. +* Launch gz_spawn_model from xml (`#551 `_) + Spawn models from XML. + Co-authored-by: Addisu Z. Taddese +* Launch ros_gz_bridge from xml (`#550 `_) + * Add gzserver with ability to load an SDF file or string +* Launch gzserver and the bridge as composable nodes (`#528 `_) + * Add gzserver with ability to load an SDF file or string +* Add a ROS node that runs Gazebo (`#500 `_) + * Add gzserver with ability to load an SDF file or string + --------- +* 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 `_) This copies the implementation from `gazebo_ros_paths.py` to provide a way for packages to set resource paths from `package.xml`. ``` @@ -19,19 +48,89 @@ 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` --------- -* Contributors: Addisu Z. Taddese - -0.244.13 (2024-01-23) ---------------------- - -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 +* 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, Carlos Agüero, Jose Luis Rivero, Michael Carroll, ahcorde, andermi, jmackay2, mergify[bot] + +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.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_sim/CMakeLists.txt b/ros_gz_sim/CMakeLists.txt index 29d2ea283..dc21f8a9b 100644 --- a/ros_gz_sim/CMakeLists.txt +++ b/ros_gz_sim/CMakeLists.txt @@ -12,94 +12,31 @@ endif() find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) +find_package(rclcpp_components REQUIRED) find_package(std_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() - -# 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_math_vendor REQUIRED) +find_package(gz-math REQUIRED) - find_package(ignition-gazebo6 REQUIRED) - set(GZ_SIM_VER ${ignition-gazebo6_VERSION_MAJOR}) +find_package(gz_transport_vendor REQUIRED) +find_package(gz-transport REQUIRED) - set(GZ_TARGET_PREFIX ignition) +find_package(gz_msgs_vendor REQUIRED) +find_package(gz-msgs REQUIRED) - message(STATUS "Compiling against Gazebo Fortress") - macro(gz_find_package) - ign_find_package(${ARGV}) - endmacro() -endif() +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}) 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 @@ -107,9 +44,9 @@ ament_target_dependencies(create ) target_link_libraries(create gflags - ${GZ_TARGET_PREFIX}-math${GZ_MATH_VER}::core - ${GZ_TARGET_PREFIX}-msgs${GZ_MSGS_VER}::core - ${GZ_TARGET_PREFIX}-transport${GZ_TRANSPORT_VER}::core + gz-math::core + gz-msgs::core + gz-transport::core ) ament_target_dependencies(create std_msgs) @@ -127,6 +64,23 @@ 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 @@ -142,10 +96,33 @@ 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} @@ -164,11 +141,15 @@ 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) @@ -180,12 +161,16 @@ if(BUILD_TESTING) target_link_libraries(test_stopwatch ${PROJECT_NAME} ) + target_link_libraries(test_create + gz-transport::core + ) install( - TARGETS test_stopwatch + TARGETS test_stopwatch test_create 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 e0b05f624..cf4e8ca97 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.ignitionrobotics.org/1.0/openrobotics/models/Gazebo' +ros2 run ros_gz_sim create -world default -file 'https://fuel.gazebosim.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 new file mode 100644 index 000000000..9a617649f --- /dev/null +++ b/ros_gz_sim/launch/gz_server.launch @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/ros_gz_sim/launch/gz_server.launch.py b/ros_gz_sim/launch/gz_server.launch.py new file mode 100644 index 000000000..c73be0fea --- /dev/null +++ b/ros_gz_sim/launch/gz_server.launch.py @@ -0,0 +1,80 @@ +# 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, 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_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 = ComposableNodeContainer( + condition=IfCondition(LaunchConfiguration('use_composition')), + 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', + ) + + # 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_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) + + 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 8f87fb7d1..d41bede62 100644 --- a/ros_gz_sim/launch/gz_sim.launch.py.in +++ b/ros_gz_sim/launch/gz_sim.launch.py.in @@ -87,25 +87,12 @@ 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) diff --git a/ros_gz_sim/launch/gz_spawn_model.launch b/ros_gz_sim/launch/gz_spawn_model.launch new file mode 100644 index 000000000..3f2890484 --- /dev/null +++ b/ros_gz_sim/launch/gz_spawn_model.launch @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + diff --git a/ros_gz_sim/launch/gz_spawn_model.launch.py b/ros_gz_sim/launch/gz_spawn_model.launch.py new file mode 100644 index 000000000..59cb43617 --- /dev/null +++ b/ros_gz_sim/launch/gz_spawn_model.launch.py @@ -0,0 +1,94 @@ +# 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') + xml_string = LaunchConfiguration('string') + topic = LaunchConfiguration('topic') + name = LaunchConfiguration('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_xml_string_cmd = DeclareLaunchArgument( + 'string', + default_value='', + description='XML string', + ) + declare_topic_cmd = DeclareLaunchArgument( + 'topic', default_value=TextSubstitution(text=''), + description='Get XML from this topic' + ) + declare_name_cmd = DeclareLaunchArgument( + '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': xml_string, + 'topic': topic, + 'name': 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_xml_string_cmd) + ld.add_action(declare_topic_cmd) + ld.add_action(declare_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 new file mode 100644 index 000000000..89b048b24 --- /dev/null +++ b/ros_gz_sim/launch/ros_gz_sim.launch @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + diff --git a/ros_gz_sim/launch/ros_gz_sim.launch.py b/ros_gz_sim/launch/ros_gz_sim.launch.py new file mode 100644 index 000000000..cad862ea7 --- /dev/null +++ b/ros_gz_sim/launch/ros_gz_sim.launch.py @@ -0,0 +1,111 @@ +# 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(): + + config_file = LaunchConfiguration('config_file') + container_name = LaunchConfiguration('container_name') + 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_config_file_cmd = DeclareLaunchArgument( + 'config_file', default_value='', 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_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' + ) + + bridge_description = IncludeLaunchDescription( + PythonLaunchDescriptionSource( + [PathJoinSubstitution([FindPackageShare('ros_gz_bridge'), + 'launch', + 'ros_gz_bridge.launch.py'])]), + launch_arguments=[('config_file', config_file), + ('container_name', container_name), + ('namespace', namespace), + ('use_composition', use_composition), + ('use_respawn', use_respawn), + ('bridge_log_level', bridge_log_level)]) + + 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), + ('use_composition', use_composition), ]) + + # Create the launch description and populate + ld = LaunchDescription() + + # Declare the launch options + ld.add_action(declare_config_file_cmd) + ld.add_action(declare_container_name_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(bridge_description) + ld.add_action(gz_server_description) + + return ld diff --git a/ros_gz_sim/launch/ros_gz_spawn_model.launch.py b/ros_gz_sim/launch/ros_gz_spawn_model.launch.py new file mode 100644 index 000000000..e5de33aa4 --- /dev/null +++ b/ros_gz_sim/launch/ros_gz_spawn_model.launch.py @@ -0,0 +1,154 @@ +# 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(): + + config_file = LaunchConfiguration('config_file') + container_name = LaunchConfiguration('container_name') + namespace = LaunchConfiguration('namespace') + use_composition = LaunchConfiguration('use_composition') + use_respawn = LaunchConfiguration('use_respawn') + log_level = LaunchConfiguration('log_level') + + world = LaunchConfiguration('world') + file = LaunchConfiguration('file') + xml_string = LaunchConfiguration('string') + topic = LaunchConfiguration('topic') + name = LaunchConfiguration('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_config_file_cmd = DeclareLaunchArgument( + 'config_file', default_value='', 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_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_xml_string_cmd = DeclareLaunchArgument( + 'string', + default_value='', + description='XML string', + ) + + declare_topic_cmd = DeclareLaunchArgument( + 'topic', default_value=TextSubstitution(text=''), + description='Get XML from this topic' + ) + + declare_name_cmd = DeclareLaunchArgument( + '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=[('config_file', config_file), + ('container_name', container_name), + ('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), + ('xml_string', xml_string), + ('topic', topic), + ('name', name), + ('allow_renaming', allow_renaming), + ('x', x), + ('y', y), + ('z', z), + ('R', roll), + ('P', pitch), + ('Y', yaw), + ('use_composition', use_composition), ]) + + # Create the launch description and populate + ld = LaunchDescription() + + # Declare the launch options + ld.add_action(declare_config_file_cmd) + ld.add_action(declare_container_name_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_xml_string_cmd) + ld.add_action(declare_topic_cmd) + ld.add_action(declare_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 617270e7a..7d8a0bf48 100644 --- a/ros_gz_sim/package.xml +++ b/ros_gz_sim/package.xml @@ -2,41 +2,39 @@ ros_gz_sim - 0.244.15 + 1.0.1 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-math7 - gz-msgs10 - gz-sim8 - gz-transport13 - - gz-sim7 - gz-math7 - - ignition-gazebo6 - ignition-math6 - ignition-gazebo6 - ignition-math6 - - ignition-gazebo5 - ignition-math6 + gz_math_vendor + gz_msgs_vendor + gz_sim_vendor + gz_transport_vendor 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 new file mode 100644 index 000000000..e69de29bb diff --git a/ros_gz_sim/ros_gz_sim/__init__.py b/ros_gz_sim/ros_gz_sim/__init__.py new file mode 100644 index 000000000..17b9696c3 --- /dev/null +++ b/ros_gz_sim/ros_gz_sim/__init__.py @@ -0,0 +1,22 @@ +# 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 new file mode 100644 index 000000000..bea18edd1 --- /dev/null +++ b/ros_gz_sim/ros_gz_sim/actions/__init__.py @@ -0,0 +1,24 @@ +# Copyright 2024 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""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 new file mode 100644 index 000000000..7626eb019 --- /dev/null +++ b/ros_gz_sim/ros_gz_sim/actions/gz_spawn_model.py @@ -0,0 +1,207 @@ +# 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, + xml_string: Optional[SomeSubstitutionsType] = None, + topic: Optional[SomeSubstitutionsType] = None, + 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: xml_string XML string. + :param: topic Get XML from this topic. + :param: 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.__xml_string = xml_string + self.__topic = topic + self.__name = 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) + + xml_string = entity.get_attr( + 'xml_string', data_type=str, + optional=True) + + topic = entity.get_attr( + 'topic', data_type=str, + optional=True) + + name = entity.get_attr( + '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(xml_string, str): + xml_string = parser.parse_substitution(xml_string) + kwargs['xml_string'] = xml_string + + if isinstance(topic, str): + topic = parser.parse_substitution(topic) + kwargs['topic'] = topic + + if isinstance(name, str): + name = parser.parse_substitution(name) + kwargs['name'] = 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), + ('xml_string', self.__xml_string), + ('topic', self.__topic), + ('name', self.__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 new file mode 100644 index 000000000..14df9aaba --- /dev/null +++ b/ros_gz_sim/ros_gz_sim/actions/gzserver.py @@ -0,0 +1,111 @@ +# 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] = None, + world_sdf_string: Optional[SomeSubstitutionsType] = None, + container_name: Optional[SomeSubstitutionsType] = None, + use_composition: Optional[SomeSubstitutionsType] = None, + **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: 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.__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) + + 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(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), + ('use_composition', self.__use_composition), ]) + + return [gz_server_description] diff --git a/ros_gz_sim/setup.cfg b/ros_gz_sim/setup.cfg new file mode 100644 index 000000000..c044b41d2 --- /dev/null +++ b/ros_gz_sim/setup.cfg @@ -0,0 +1,3 @@ +[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 6b12daf8e..db2b7452e 100644 --- a/ros_gz_sim/src/create.cpp +++ b/ros_gz_sim/src/create.cpp @@ -21,6 +21,10 @@ #include #include +#include +#include +#include +#include #include #include @@ -29,7 +33,11 @@ #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."); @@ -44,23 +52,94 @@ 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."); -// 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. +// 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; + } +} + int main(int _argc, char ** _argv) { - rclcpp::init(_argc, _argv); + auto filtered_arguments = rclcpp::init_and_remove_ros_arguments(_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(&_argc, &_argv, true); + 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)); // World - std::string world_name = FLAGS_world; + std::string world_name = ros2_node->get_parameter("world").as_string(); + if (world_name.empty() && !FLAGS_world.empty()) { + 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; @@ -94,69 +173,83 @@ int main(int _argc, char ** _argv) // Request message gz::msgs::EntityFactory 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()); + // 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)) { 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 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 { RCLCPP_ERROR( - ros2_node->get_logger(), "Failed to get XML from topic [%s].", FLAGS_topic.c_str()); + ros2_node->get_logger(), "Must specify either -file, -param, -string or -topic"); 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, -param, -stdin or -topic"); + RCLCPP_ERROR( + ros2_node->get_logger(), "Must specify either file, string or topic as ROS 2 parameters"); 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 - if (!FLAGS_name.empty()) { + std::string entity_name = ros2_node->get_parameter("name").as_string(); + if (!entity_name.empty()) { + req.set_name(entity_name); + } else { req.set_name(FLAGS_name); } - if (FLAGS_allow_renaming) { - req.set_allow_renaming(FLAGS_allow_renaming); - } + // Allow Renaming + bool allow_renaming = ros2_node->get_parameter("allow_renaming").as_bool(); + req.set_allow_renaming((allow_renaming || FLAGS_allow_renaming)); // Request gz::transport::Node node; diff --git a/ros_gz_sim/src/gzserver.cpp b/ros_gz_sim/src/gzserver.cpp new file mode 100644 index 000000000..aa4361f51 --- /dev/null +++ b/ros_gz_sim/src/gzserver.cpp @@ -0,0 +1,80 @@ +// 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 new file mode 100644 index 000000000..ee92bf39c --- /dev/null +++ b/ros_gz_sim/test/test_create.cpp @@ -0,0 +1,56 @@ +// 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 new file mode 100644 index 000000000..79c31c5c1 --- /dev/null +++ b/ros_gz_sim/test/test_create_node.launch.py @@ -0,0 +1,47 @@ +# 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 ad3e5b177..6a5f8f7a0 100644 --- a/ros_gz_sim_demos/CHANGELOG.rst +++ b/ros_gz_sim_demos/CHANGELOG.rst @@ -2,27 +2,100 @@ Changelog for package ros1_gz_sim_demos ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -0.244.15(2024-07-03) ---------------------- +1.0.1 (2024-07-03) +------------------ +* Prepare for 1.0.0 Release (`#495 `_) +* Use gz_vendor packages (`#531 `_) * [backport Humble] Create bridge for GPSFix msg (`#316 `_) (`#538 `_) Co-authored-by: Rousseau Vincent -* Contributors: Alejandro Hernández Cordero - -0.244.14 (2024-04-08) ---------------------- - -0.244.13 (2024-01-23) ---------------------- +* [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 -0.244.12 (2023-12-13) ---------------------- -* [backport Humble] Added more topic to the bridge (`#422 `_) +0.246.0 (2023-08-31) +-------------------- * Added more topic to the bridge (`#422 `_) -* Fix incorrect subscription on demo (`#405 `_) -* Contributors: Alejandro Hernández Cordero, Arjo Chakravarty - -0.244.11 (2023-05-23) ---------------------- +* 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 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 95bf1906d..7b61c350e 100644 --- a/ros_gz_sim_demos/launch/joint_states.launch.py +++ b/ros_gz_sim_demos/launch/joint_states.launch.py @@ -63,10 +63,8 @@ def generate_launch_description(): spawn = Node( package='ros_gz_sim', executable='create', - arguments=[ - '-name', 'rrbot', - '-topic', 'robot_description', - ], + parameters=[{'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 882cdee38..7fa6fc09d 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', - arguments=[ - '-name', 'my_custom_model', - '-x', '1.2', - '-z', '2.3', - '-Y', '3.4', - '-topic', '/robot_description'], + parameters=[{ + '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 e134864ee..42f76cdc6 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=[ - 'ign', 'gazebo', '-r', + 'gz', 'sim', '-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 6c88eea8c..a169b8e71 100644 --- a/ros_gz_sim_demos/package.xml +++ b/ros_gz_sim_demos/package.xml @@ -1,17 +1,16 @@ ros_gz_sim_demos - 0.244.15 + 1.0.1 Demos using Gazebo Sim simulation with ROS. Apache 2.0 - Louise Poubel + Aditya Pande + Alejandro Hernandez + + Louise Poubel ament_cmake - - ignition-gazebo6 - ignition-gazebo6 - - ignition-gazebo5 + gz_sim_vendor image_transport_plugins robot_state_publisher diff --git a/ros_ign/CHANGELOG.rst b/ros_ign/CHANGELOG.rst deleted file mode 100644 index b96051954..000000000 --- a/ros_ign/CHANGELOG.rst +++ /dev/null @@ -1,41 +0,0 @@ -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Changelog for package ros_ign -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -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 deleted file mode 100644 index 80524e92e..000000000 --- a/ros_ign/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index 7f2dcc880..000000000 --- a/ros_ign/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# 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 deleted file mode 100644 index dc010d108..000000000 --- a/ros_ign/package.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - ros_ign - 0.244.15 - 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 deleted file mode 100644 index 34aab9542..000000000 --- a/ros_ign_bridge/CHANGELOG.rst +++ /dev/null @@ -1,79 +0,0 @@ -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Changelog for package ros_ign_bridge -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -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 deleted file mode 100644 index 7be505b07..000000000 --- a/ros_ign_bridge/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -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 deleted file mode 100644 index e93d8f261..000000000 --- a/ros_ign_bridge/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# 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 deleted file mode 100644 index 363675b17..000000000 --- a/ros_ign_bridge/package.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - ros_ign_bridge - 0.244.15 - 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 deleted file mode 100644 index df6882bb4..000000000 --- a/ros_ign_bridge/src/parameter_bridge_shim.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// 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 deleted file mode 100644 index 4ee0fdde1..000000000 --- a/ros_ign_bridge/src/static_bridge_shim.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// 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 deleted file mode 100644 index 4e9784950..000000000 --- a/ros_ign_gazebo/CHANGELOG.rst +++ /dev/null @@ -1,59 +0,0 @@ -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Changelog for package ros_ign_gazebo -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -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 deleted file mode 100644 index 52fefe619..000000000 --- a/ros_ign_gazebo/CMakeLists.txt +++ /dev/null @@ -1,58 +0,0 @@ -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 deleted file mode 100644 index 2df45d4a9..000000000 --- a/ros_ign_gazebo/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# 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 deleted file mode 100644 index e8cb53426..000000000 --- a/ros_ign_gazebo/launch/ign_gazebo.launch.py +++ /dev/null @@ -1,33 +0,0 @@ -# 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 deleted file mode 100644 index 17e703f19..000000000 --- a/ros_ign_gazebo/package.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - ros_ign_gazebo - 0.244.15 - 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 deleted file mode 100644 index 8a6d366a2..000000000 --- a/ros_ign_gazebo/src/create_shim.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// 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 deleted file mode 100644 index 9e9a65a59..000000000 --- a/ros_ign_gazebo_demos/CHANGELOG.rst +++ /dev/null @@ -1,47 +0,0 @@ -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Changelog for package ros_ign_gazebo_demos -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -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 deleted file mode 100644 index 68b53fed5..000000000 --- a/ros_ign_gazebo_demos/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -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 deleted file mode 100644 index 18ccd4242..000000000 --- a/ros_ign_gazebo_demos/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# 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 deleted file mode 100644 index f2e97f762..000000000 --- a/ros_ign_gazebo_demos/launch/air_pressure.launch.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 deleted file mode 100644 index b85a0f215..000000000 --- a/ros_ign_gazebo_demos/launch/battery.launch.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 deleted file mode 100644 index 40cf2132b..000000000 --- a/ros_ign_gazebo_demos/launch/camera.launch.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 deleted file mode 100644 index 0eda5b4ba..000000000 --- a/ros_ign_gazebo_demos/launch/depth_camera.launch.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 deleted file mode 100644 index aa1f70adf..000000000 --- a/ros_ign_gazebo_demos/launch/diff_drive.launch.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 deleted file mode 100644 index 49df92850..000000000 --- a/ros_ign_gazebo_demos/launch/gpu_lidar.launch.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 deleted file mode 100644 index d4c89975e..000000000 --- a/ros_ign_gazebo_demos/launch/gpu_lidar_bridge.launch.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 deleted file mode 100644 index 421a1c080..000000000 --- a/ros_ign_gazebo_demos/launch/image_bridge.launch.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 deleted file mode 100644 index 6a580aeff..000000000 --- a/ros_ign_gazebo_demos/launch/imu.launch.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 deleted file mode 100644 index 004a88006..000000000 --- a/ros_ign_gazebo_demos/launch/joint_states.launch.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 deleted file mode 100644 index 3b66da7c0..000000000 --- a/ros_ign_gazebo_demos/launch/magnetometer.launch.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 deleted file mode 100644 index ba3e890bc..000000000 --- a/ros_ign_gazebo_demos/launch/rgbd_camera.launch.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 deleted file mode 100644 index d7812aac3..000000000 --- a/ros_ign_gazebo_demos/launch/rgbd_camera_bridge.launch.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 deleted file mode 100755 index f2158c69e..000000000 --- a/ros_ign_gazebo_demos/launch/robot_description_publisher.launch.py +++ /dev/null @@ -1,37 +0,0 @@ -# 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 deleted file mode 100644 index 2b6fd52fa..000000000 --- a/ros_ign_gazebo_demos/launch/tf_bridge.launch.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 deleted file mode 100644 index b5a852c52..000000000 --- a/ros_ign_gazebo_demos/launch/triggered_camera.launch.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 deleted file mode 100644 index 0e4f64816..000000000 --- a/ros_ign_gazebo_demos/package.xml +++ /dev/null @@ -1,17 +0,0 @@ - - ros_ign_gazebo_demos - 0.244.15 - 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 deleted file mode 100644 index a8de0d965..000000000 --- a/ros_ign_image/CHANGELOG.rst +++ /dev/null @@ -1,43 +0,0 @@ -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Changelog for package ros_ign_image -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -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 deleted file mode 100644 index 5fa4b91bb..000000000 --- a/ros_ign_image/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -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 deleted file mode 100644 index 517ccdd80..000000000 --- a/ros_ign_image/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# 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 deleted file mode 100644 index 7a6b5444c..000000000 --- a/ros_ign_image/package.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - ros_ign_image - 0.244.15 - 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 deleted file mode 100644 index 0d44a1cb2..000000000 --- a/ros_ign_image/src/image_bridge_shim.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// 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 deleted file mode 100644 index effc8e90d..000000000 --- a/ros_ign_interfaces/CHANGELOG.rst +++ /dev/null @@ -1,58 +0,0 @@ -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Changelog for package ros_ign_interfaces -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -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 deleted file mode 100644 index b397466bb..000000000 --- a/ros_ign_interfaces/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -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 deleted file mode 100644 index 15a7fafb0..000000000 --- a/ros_ign_interfaces/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# 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 deleted file mode 100644 index 98d2cef8e..000000000 --- a/ros_ign_interfaces/msg/Contact.msg +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index 40232e516..000000000 --- a/ros_ign_interfaces/msg/Contacts.msg +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index b785c7edf..000000000 --- a/ros_ign_interfaces/msg/Entity.msg +++ /dev/null @@ -1,13 +0,0 @@ -# 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 deleted file mode 100644 index 4576c003e..000000000 --- a/ros_ign_interfaces/msg/EntityFactory.msg +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index d45fd9b83..000000000 --- a/ros_ign_interfaces/msg/GuiCamera.msg +++ /dev/null @@ -1,12 +0,0 @@ -# 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 deleted file mode 100644 index 2da890944..000000000 --- a/ros_ign_interfaces/msg/JointWrench.msg +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index 911e20bd3..000000000 --- a/ros_ign_interfaces/msg/Light.msg +++ /dev/null @@ -1,29 +0,0 @@ -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 deleted file mode 100644 index 15a7dda9e..000000000 --- a/ros_ign_interfaces/msg/StringVec.msg +++ /dev/null @@ -1,7 +0,0 @@ -# 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 deleted file mode 100644 index c09f4785f..000000000 --- a/ros_ign_interfaces/msg/TrackVisual.msg +++ /dev/null @@ -1,33 +0,0 @@ -# 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 deleted file mode 100644 index 02bafde0d..000000000 --- a/ros_ign_interfaces/msg/VideoRecord.msg +++ /dev/null @@ -1,16 +0,0 @@ -# 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 deleted file mode 100644 index efa22fb9c..000000000 --- a/ros_ign_interfaces/msg/WorldControl.msg +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index 46f5971ff..000000000 --- a/ros_ign_interfaces/msg/WorldReset.msg +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index a97cea452..000000000 --- a/ros_ign_interfaces/package.xml +++ /dev/null @@ -1,27 +0,0 @@ - - ros_ign_interfaces - 0.244.15 - 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 deleted file mode 100644 index d8e41f245..000000000 --- a/ros_ign_interfaces/srv/ControlWorld.srv +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 13b3e1fd8..000000000 --- a/ros_ign_interfaces/srv/DeleteEntity.srv +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index b749488cc..000000000 --- a/ros_ign_interfaces/srv/SetEntityPose.srv +++ /dev/null @@ -1,4 +0,0 @@ -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 deleted file mode 100644 index 35d5df59e..000000000 --- a/ros_ign_interfaces/srv/SpawnEntity.srv +++ /dev/null @@ -1,3 +0,0 @@ -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 new file mode 100644 index 000000000..72a2d2143 --- /dev/null +++ b/test_ros_gz_bridge/CHANGELOG.rst @@ -0,0 +1,17 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package test_ros_gz_bridge +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +1.0.1 (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 d547be576..bbe05aebe 100644 --- a/test_ros_gz_bridge/package.xml +++ b/test_ros_gz_bridge/package.xml @@ -2,7 +2,7 @@ test_ros_gz_bridge - 0.244.15 + 1.0.1 Bridge communication between ROS and Gazebo Transport Aditya Pande Alejandro Hernandez