From a4dc4f5e878f51262a6fe1f4a62e0319a9979fbe Mon Sep 17 00:00:00 2001 From: Roland Arsenault Date: Wed, 28 Feb 2024 09:55:07 -0500 Subject: [PATCH 1/4] Update ROS2 CI --- .github/workflows/ros2.yml | 51 ++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ros2.yml b/.github/workflows/ros2.yml index 80a9df17..cb62866f 100644 --- a/.github/workflows/ros2.yml +++ b/.github/workflows/ros2.yml @@ -1,28 +1,37 @@ name: ROS2 CI -on: + +on: # this determines when this workflow is run push: + branches: + - ros2 pull_request: - schedule: - - cron: "0 0 * * *" + workflow_dispatch: # allow manually starting this workflow + jobs: industrial_ci: - strategy: - matrix: - env: - - ROS_DISTRO: foxy - ROS_REPO: testing - CMAKE_ARGS: '-DCMAKE_BUILD_TYPE=Debug' - - ROS_DISTRO: foxy - ROS_REPO: testing - CMAKE_ARGS: '-DCMAKE_BUILD_TYPE=Release' - - ROS_DISTRO: galactic - ROS_REPO: testing - CMAKE_ARGS: '-DCMAKE_BUILD_TYPE=Debug' - - ROS_DISTRO: galactic - ROS_REPO: testing - CMAKE_ARGS: '-DCMAKE_BUILD_TYPE=Release' + name: ROS ${{ matrix.ROS_DISTRO }} (${{ matrix.ROS_REPO }}) runs-on: ubuntu-latest + strategy: + # fail-fast: false # uncomment if failing jobs should not cancel the others immediately + matrix: # matrix is the product of entries + ROS_DISTRO: [humble, iron, rolling] + ROS_REPO: [testing, main] + env: + CCACHE_DIR: "${{ github.workspace }}/.ccache" # directory for ccache (and how we enable ccache in industrial_ci) steps: - - uses: actions/checkout@v1 - - uses: 'ros-industrial/industrial_ci@master' - env: ${{matrix.env}} + - uses: actions/checkout@v3 # clone target repository + - uses: actions/cache@v2 # fetch/store the directory used by ccache before/after the ci run + with: + path: ${{ env.CCACHE_DIR }} + # This configuration will always create a new ccache cache starting off from the previous one (if any). + # In this simple version it will be shared between all builds of the same ROS_REPO and ROS_REPO + # and might need some fine-tuning to match the use case + key: ccache-${{ matrix.ROS_DISTRO }}-${{ matrix.ROS_REPO }}-${{github.run_id}} + restore-keys: | + ccache-${{ matrix.ROS_DISTRO }}-${{ matrix.ROS_REPO }}- + - uses: 'ros-industrial/industrial_ci@master' # run industrial_ci + env: # either pass all entries explicitly + ROS_DISTRO: ${{ matrix.ROS_DISTRO }} + ROS_REPO: ${{ matrix.ROS_REPO }} + # with: # or pass the full matrix as config + # config: ${{toJSON(matrix)}} \ No newline at end of file From 7cad7fd4f18e06f60b4d7947c418efdb58b7d7ba Mon Sep 17 00:00:00 2001 From: Roland Arsenault Date: Wed, 28 Feb 2024 10:51:17 -0500 Subject: [PATCH 2/4] Split sound_play interface out into sound_play_msgs package --- sound_play/CMakeLists.txt | 18 +--------- sound_play/package.xml | 7 ++-- sound_play/scripts/soundclient_example.py | 2 +- sound_play/scripts/soundplay_node.py | 4 +-- sound_play/scripts/test.py | 2 +- sound_play/scripts/test_actionlib_client.py | 4 +-- sound_play/sound_play/libsoundplay.py | 4 +-- sound_play_msgs/CMakeLists.txt | 33 +++++++++++++++++++ .../action/SoundRequest.action | 0 .../msg/SoundRequest.msg | 0 sound_play_msgs/package.xml | 33 +++++++++++++++++++ 11 files changed, 77 insertions(+), 30 deletions(-) create mode 100644 sound_play_msgs/CMakeLists.txt rename {sound_play => sound_play_msgs}/action/SoundRequest.action (100%) rename {sound_play => sound_play_msgs}/msg/SoundRequest.msg (100%) create mode 100644 sound_play_msgs/package.xml diff --git a/sound_play/CMakeLists.txt b/sound_play/CMakeLists.txt index 23be34e7..a7760840 100644 --- a/sound_play/CMakeLists.txt +++ b/sound_play/CMakeLists.txt @@ -13,26 +13,10 @@ find_package(ament_cmake REQUIRED) find_package(rosidl_default_generators REQUIRED) find_package(audio_common_msgs REQUIRED) find_package(action_msgs REQUIRED) +find_package(sound_play_msgs) include_directories(rclcpp audio_common_msgs action_msgs builtin_interfaces) -set(msg_files - "msg/SoundRequest.msg" -) - -set(action_files - "action/SoundRequest.action" -) - -rosidl_generate_interfaces(${PROJECT_NAME} - ${msg_files} - ${action_files} - DEPENDENCIES - action_msgs - audio_common_msgs - builtin_interfaces -) - ament_python_install_package(${PROJECT_NAME}) install(PROGRAMS diff --git a/sound_play/package.xml b/sound_play/package.xml index 5bd98e9c..c614f42d 100644 --- a/sound_play/package.xml +++ b/sound_play/package.xml @@ -18,10 +18,10 @@ ament_cmake ament_cmake_python - rosidl_default_generators - rosidl_generator_py python3-setuptools + sound_play_msgs + action_msgs audio_common_msgs boost @@ -33,7 +33,6 @@ festival launch_xml rclpy - rosidl_default_runtime gstreamer1.0 gstreamer1.0-alsa @@ -42,8 +41,6 @@ gstreamer1.0-plugins-good python3-gi - rosidl_interface_packages - ament_cmake diff --git a/sound_play/scripts/soundclient_example.py b/sound_play/scripts/soundclient_example.py index 8f5a09bc..7a037ad3 100755 --- a/sound_play/scripts/soundclient_example.py +++ b/sound_play/scripts/soundclient_example.py @@ -12,7 +12,7 @@ from sound_play.libsoundplay import SoundClient -from sound_play.msg import SoundRequest +from sound_play_msgs.msg import SoundRequest def play_explicit(node): diff --git a/sound_play/scripts/soundplay_node.py b/sound_play/scripts/soundplay_node.py index e3886109..c518ff56 100755 --- a/sound_play/scripts/soundplay_node.py +++ b/sound_play/scripts/soundplay_node.py @@ -51,8 +51,8 @@ from diagnostic_msgs.msg import DiagnosticArray from diagnostic_msgs.msg import DiagnosticStatus from diagnostic_msgs.msg import KeyValue -from sound_play.action import SoundRequest as SoundRequestAction -from sound_play.msg import SoundRequest +from sound_play_msgs.action import SoundRequest as SoundRequestAction +from sound_play_msgs.msg import SoundRequest try: diff --git a/sound_play/scripts/test.py b/sound_play/scripts/test.py index 4c50a800..7414504c 100755 --- a/sound_play/scripts/test.py +++ b/sound_play/scripts/test.py @@ -41,7 +41,7 @@ from sound_play.libsoundplay import SoundClient -from sound_play.msg import SoundRequest +from sound_play_msgs.msg import SoundRequest if __name__ == '__main__': diff --git a/sound_play/scripts/test_actionlib_client.py b/sound_play/scripts/test_actionlib_client.py index 0609eba9..4d9a0b0d 100755 --- a/sound_play/scripts/test_actionlib_client.py +++ b/sound_play/scripts/test_actionlib_client.py @@ -6,8 +6,8 @@ import rclpy import rclpy.action -from sound_play.action import SoundRequest as SoundRequestAction -from sound_play.msg import SoundRequest +from sound_play_msgs.action import SoundRequest as SoundRequestAction +from sound_play_msgs.msg import SoundRequest def sound_play_client(node, volume=1.0): diff --git a/sound_play/sound_play/libsoundplay.py b/sound_play/sound_play/libsoundplay.py index 3dfaf587..fe082b0f 100644 --- a/sound_play/sound_play/libsoundplay.py +++ b/sound_play/sound_play/libsoundplay.py @@ -39,8 +39,8 @@ import rclpy.action from action_msgs.msg import GoalStatusArray -from sound_play.action import SoundRequest as SoundRequestAction -from sound_play.msg import SoundRequest +from sound_play_msgs.action import SoundRequest as SoundRequestAction +from sound_play_msgs.msg import SoundRequest # \brief Class that publishes messages to the sound_play node. diff --git a/sound_play_msgs/CMakeLists.txt b/sound_play_msgs/CMakeLists.txt new file mode 100644 index 00000000..d18942f2 --- /dev/null +++ b/sound_play_msgs/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.5) +project(sound_play_msgs) + +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +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(rosidl_default_generators REQUIRED) + +set(msg_files + "msg/SoundRequest.msg" +) + +set(action_files + "action/SoundRequest.action" +) + +rosidl_generate_interfaces(${PROJECT_NAME} + ${msg_files} + ${action_files} + DEPENDENCIES + builtin_interfaces +) + +ament_export_dependencies(rosidl_default_runtime) + +ament_package() diff --git a/sound_play/action/SoundRequest.action b/sound_play_msgs/action/SoundRequest.action similarity index 100% rename from sound_play/action/SoundRequest.action rename to sound_play_msgs/action/SoundRequest.action diff --git a/sound_play/msg/SoundRequest.msg b/sound_play_msgs/msg/SoundRequest.msg similarity index 100% rename from sound_play/msg/SoundRequest.msg rename to sound_play_msgs/msg/SoundRequest.msg diff --git a/sound_play_msgs/package.xml b/sound_play_msgs/package.xml new file mode 100644 index 00000000..b97845c0 --- /dev/null +++ b/sound_play_msgs/package.xml @@ -0,0 +1,33 @@ + + + + sound_play_msgs + 0.3.12 + + sound_play_msgs provides the interface for sound_play. + sound_play provides a ROS node that translates commands on a ROS topic (robotsound) into sounds. The node supports built-in sounds, playing OGG/WAV files, and doing speech synthesis via festival. C++ and Python bindings allow this node to be used without understanding the details of the message format, allowing faster development and resilience to message format changes. + + Austin Hendrix + Shingo Kitagawa + Blaise Gassend + BSD + http://ros.org/wiki/sound_play + https://github.com/ros-drivers/audio_common + https://github.com/ros-drivers/audio_common/issues + + ament_cmake + rosidl_default_generators + + builtin_interfaces + + rosidl_default_runtime + + rosidl_interface_packages + + + ament_cmake + + + From 0fb619eaa8a5bd13f86210a7cf8ad6f921d3fc36 Mon Sep 17 00:00:00 2001 From: Roland Arsenault Date: Wed, 28 Feb 2024 15:34:37 -0500 Subject: [PATCH 3/4] Port sound_play.h to ROS2 --- sound_play/CMakeLists.txt | 3 +- sound_play/include/sound_play/sound_play.h | 75 +++++++++++----------- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/sound_play/CMakeLists.txt b/sound_play/CMakeLists.txt index a7760840..6663e2e1 100644 --- a/sound_play/CMakeLists.txt +++ b/sound_play/CMakeLists.txt @@ -34,6 +34,7 @@ install(PROGRAMS install(DIRECTORY include/${PROJECT_NAME}/ DESTINATION include/${PROJECT_NAME} ) +ament_export_include_directories(include) install(DIRECTORY launch DESTINATION share/${PROJECT_NAME} @@ -43,6 +44,6 @@ install(DIRECTORY sounds DESTINATION share/${PROJECT_NAME} ) -ament_export_dependencies(rosidl_default_runtime) +ament_export_dependencies(rosidl_default_runtime sound_play_msgs) ament_package() diff --git a/sound_play/include/sound_play/sound_play.h b/sound_play/include/sound_play/sound_play.h index d5fe4614..99636d7b 100644 --- a/sound_play/include/sound_play/sound_play.h +++ b/sound_play/include/sound_play/sound_play.h @@ -38,10 +38,9 @@ #define __SOUND_PLAY__SOUND_PLAY__H__ #include -#include -#include -#include -#include +#include +#include +#include namespace sound_play { @@ -87,7 +86,7 @@ class SoundClient */ void play() { - client_->sendMsg(snd_, SoundRequest::PLAY_ONCE, arg_, arg2_, vol_); + client_->sendMsg(snd_, sound_play_msgs::msg::SoundRequest::PLAY_ONCE, arg_, arg2_, vol_); } /** \brief Play the Sound repeatedly. @@ -97,7 +96,7 @@ class SoundClient */ void repeat() { - client_->sendMsg(snd_, SoundRequest::PLAY_START, arg_, arg2_, vol_); + client_->sendMsg(snd_, sound_play_msgs::msg::SoundRequest::PLAY_START, arg_, arg2_, vol_); } /** \brief Stop Sound playback. @@ -106,7 +105,7 @@ class SoundClient */ void stop() { - client_->sendMsg(snd_, SoundRequest::PLAY_STOP, arg_, arg2_, vol_); + client_->sendMsg(snd_, sound_play_msgs::msg::SoundRequest::PLAY_STOP, arg_, arg2_, vol_); } }; @@ -119,7 +118,7 @@ class SoundClient * * \param topic Topic to publish to. */ - SoundClient(ros::NodeHandle &nh, const std::string &topic) + SoundClient(rclcpp::Node::SharedPtr nh, const std::string &topic) { init(nh, topic); } @@ -127,10 +126,12 @@ class SoundClient /** \brief Create a SoundClient with the default topic * * Creates a SoundClient that publishes to "robotsound". + * + * \param nh Node handle to use when creating the topic. */ - SoundClient() + SoundClient(rclcpp::Node::SharedPtr nh) { - init(ros::NodeHandle(), "robotsound"); + init(nh, "robotsound"); } /** \brief Create a voice Sound. @@ -142,7 +143,7 @@ class SoundClient */ Sound voiceSound(const std::string &s, float volume = 1.0f) { - return Sound(this, SoundRequest::SAY, s, "", volume); + return Sound(this, sound_play_msgs::msg::SoundRequest::SAY, s, "", volume); } /** \brief Create a wave Sound. @@ -155,7 +156,7 @@ class SoundClient */ Sound waveSound(const std::string &s, float volume = 1.0f) { - return Sound(this, SoundRequest::PLAY_FILE, s, "", volume); + return Sound(this, sound_play_msgs::msg::SoundRequest::PLAY_FILE, s, "", volume); } /** \brief Create a wave Sound from a package. @@ -169,7 +170,7 @@ class SoundClient */ Sound waveSoundFromPkg(const std::string &p, const std::string &s, float volume = 1.0f) { - return Sound(this, SoundRequest::PLAY_FILE, s, p, volume); + return Sound(this, sound_play_msgs::msg::SoundRequest::PLAY_FILE, s, p, volume); } /** \brief Create a builtin Sound. @@ -194,7 +195,7 @@ class SoundClient */ void say(const std::string &s, const std::string &voice="voice_kal_diphone", float volume = 1.0f) { - sendMsg(SoundRequest::SAY, SoundRequest::PLAY_ONCE, s, voice, volume); + sendMsg(sound_play_msgs::msg::SoundRequest::SAY, sound_play_msgs::msg::SoundRequest::PLAY_ONCE, s, voice, volume); } /** \brief Say a string repeatedly @@ -206,7 +207,7 @@ class SoundClient */ void repeat(const std::string &s, float volume = 1.0f) { - sendMsg(SoundRequest::SAY, SoundRequest::PLAY_START, s, "", volume); + sendMsg(sound_play_msgs::msg::SoundRequest::SAY, sound_play_msgs::msg::SoundRequest::PLAY_START, s, "", volume); } /** \brief Stop saying a string @@ -218,7 +219,7 @@ class SoundClient */ void stopSaying(const std::string &s) { - sendMsg(SoundRequest::SAY, SoundRequest::PLAY_STOP, s, ""); + sendMsg(sound_play_msgs::msg::SoundRequest::SAY, sound_play_msgs::msg::SoundRequest::PLAY_STOP, s, ""); } /** \brief Plays a WAV or OGG file @@ -232,7 +233,7 @@ class SoundClient */ void playWave(const std::string &s, float volume = 1.0f) { - sendMsg(SoundRequest::PLAY_FILE, SoundRequest::PLAY_ONCE, s, "", volume); + sendMsg(sound_play_msgs::msg::SoundRequest::PLAY_FILE, sound_play_msgs::msg::SoundRequest::PLAY_ONCE, s, "", volume); } /** \brief Plays a WAV or OGG file repeatedly @@ -245,7 +246,7 @@ class SoundClient */ void startWave(const std::string &s, float volume = 1.0f) { - sendMsg(SoundRequest::PLAY_FILE, SoundRequest::PLAY_START, s, "", volume); + sendMsg(sound_play_msgs::msg::SoundRequest::PLAY_FILE, sound_play_msgs::msg::SoundRequest::PLAY_START, s, "", volume); } /** \brief Stop playing a WAV or OGG file @@ -257,7 +258,7 @@ class SoundClient */ void stopWave(const std::string &s) { - sendMsg(SoundRequest::PLAY_FILE, SoundRequest::PLAY_STOP, s); + sendMsg(sound_play_msgs::msg::SoundRequest::PLAY_FILE, sound_play_msgs::msg::SoundRequest::PLAY_STOP, s); } /** \brief Plays a WAV or OGG file from a package @@ -272,7 +273,7 @@ class SoundClient */ void playWaveFromPkg(const std::string &p, const std::string &s, float volume = 1.0f) { - sendMsg(SoundRequest::PLAY_FILE, SoundRequest::PLAY_ONCE, s, p, volume); + sendMsg(sound_play_msgs::msg::SoundRequest::PLAY_FILE, sound_play_msgs::msg::SoundRequest::PLAY_ONCE, s, p, volume); } /** \brief Plays a WAV or OGG file repeatedly @@ -286,7 +287,7 @@ class SoundClient */ void startWaveFromPkg(const std::string &p, const std::string &s, float volume = 1.0f) { - sendMsg(SoundRequest::PLAY_FILE, SoundRequest::PLAY_START, s, p, volume); + sendMsg(sound_play_msgs::msg::SoundRequest::PLAY_FILE, sound_play_msgs::msg::SoundRequest::PLAY_START, s, p, volume); } /** \brief Stop playing a WAV or OGG file @@ -300,7 +301,7 @@ class SoundClient */ void stopWaveFromPkg(const std::string &p, const std::string &s) { - sendMsg(SoundRequest::PLAY_FILE, SoundRequest::PLAY_STOP, s, p); + sendMsg(sound_play_msgs::msg::SoundRequest::PLAY_FILE, sound_play_msgs::msg::SoundRequest::PLAY_STOP, s, p); } /** \brief Play a buildin sound @@ -313,7 +314,7 @@ class SoundClient */ void play(int sound, float volume = 1.0f) { - sendMsg(sound, SoundRequest::PLAY_ONCE, "", "", volume); + sendMsg(sound, sound_play_msgs::msg::SoundRequest::PLAY_ONCE, "", "", volume); } /** \brief Play a buildin sound repeatedly @@ -326,7 +327,7 @@ class SoundClient */ void start(int sound, float volume = 1.0f) { - sendMsg(sound, SoundRequest::PLAY_START, "", "", volume); + sendMsg(sound, sound_play_msgs::msg::SoundRequest::PLAY_START, "", "", volume); } /** \brief Stop playing a built-in sound @@ -337,7 +338,7 @@ class SoundClient */ void stop(int sound) { - sendMsg(sound, SoundRequest::PLAY_STOP); + sendMsg(sound, sound_play_msgs::msg::SoundRequest::PLAY_STOP); } /** \brief Stop all currently playing sounds @@ -346,7 +347,7 @@ class SoundClient */ void stopAll() { - stop(SoundRequest::ALL); + stop(sound_play_msgs::msg::SoundRequest::ALL); } /** \brief Turns warning messages on or off. @@ -363,21 +364,21 @@ class SoundClient } private: - void init(ros::NodeHandle nh, const std::string &topic) + void init(rclcpp::Node::SharedPtr nh, const std::string &topic) { nh_ = nh; - pub_ = nh.advertise(topic, 5); + pub_ = nh->create_publisher(topic, 5); quiet_ = false; } void sendMsg(int snd, int cmd, const std::string &s = "", const std::string &arg2 = "", const float &vol = 1.0f) { - boost::mutex::scoped_lock lock(mutex_); + const std::lock_guard lock(mutex_); - if (!nh_.ok()) + if (!nh_ || !pub_) return; - SoundRequest msg; + sound_play_msgs::msg::SoundRequest msg; msg.sound = snd; msg.command = cmd; msg.arg = s; @@ -391,16 +392,16 @@ class SoundClient else msg.volume = vol; - pub_.publish(msg); + pub_->publish(msg); - if (pub_.getNumSubscribers() == 0 && !quiet_) - ROS_WARN("Sound command issued, but no node is subscribed to the topic. Perhaps you forgot to run soundplay_node.py"); + if (pub_->get_subscription_count() == 0 && !quiet_) + RCLCPP_WARN(nh_->get_logger(), "Sound command issued, but no node is subscribed to the topic. Perhaps you forgot to run soundplay_node.py"); } bool quiet_; - ros::NodeHandle nh_; - ros::Publisher pub_; - boost::mutex mutex_; + rclcpp::Node::SharedPtr nh_; + rclcpp::Publisher::SharedPtr pub_; + std::mutex mutex_; }; typedef SoundClient::Sound Sound; From 2c6ab025029fdde1d4cb2c9b1541e259e5424d57 Mon Sep 17 00:00:00 2001 From: Roland Arsenault Date: Thu, 29 Feb 2024 08:21:45 -0500 Subject: [PATCH 4/4] Clean up per PR feedback --- .github/workflows/ros2.yml | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ros2.yml b/.github/workflows/ros2.yml index cb62866f..2119c389 100644 --- a/.github/workflows/ros2.yml +++ b/.github/workflows/ros2.yml @@ -1,37 +1,24 @@ name: ROS2 CI - -on: # this determines when this workflow is run +on: push: branches: - ros2 pull_request: - workflow_dispatch: # allow manually starting this workflow - + schedule: + - cron: "0 0 * * *" + workflow_dispatch: jobs: industrial_ci: - name: ROS ${{ matrix.ROS_DISTRO }} (${{ matrix.ROS_REPO }}) - runs-on: ubuntu-latest strategy: - # fail-fast: false # uncomment if failing jobs should not cancel the others immediately - matrix: # matrix is the product of entries + matrix: ROS_DISTRO: [humble, iron, rolling] ROS_REPO: [testing, main] - env: - CCACHE_DIR: "${{ github.workspace }}/.ccache" # directory for ccache (and how we enable ccache in industrial_ci) + CMAKE_ARGS: ['-DCMAKE_BUILD_TYPE=Debug', '-DCMAKE_BUILD_TYPE=Release'] + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 # clone target repository - - uses: actions/cache@v2 # fetch/store the directory used by ccache before/after the ci run - with: - path: ${{ env.CCACHE_DIR }} - # This configuration will always create a new ccache cache starting off from the previous one (if any). - # In this simple version it will be shared between all builds of the same ROS_REPO and ROS_REPO - # and might need some fine-tuning to match the use case - key: ccache-${{ matrix.ROS_DISTRO }}-${{ matrix.ROS_REPO }}-${{github.run_id}} - restore-keys: | - ccache-${{ matrix.ROS_DISTRO }}-${{ matrix.ROS_REPO }}- - - uses: 'ros-industrial/industrial_ci@master' # run industrial_ci - env: # either pass all entries explicitly + - uses: actions/checkout@v3 + - uses: 'ros-industrial/industrial_ci@master' + env: ROS_DISTRO: ${{ matrix.ROS_DISTRO }} - ROS_REPO: ${{ matrix.ROS_REPO }} - # with: # or pass the full matrix as config - # config: ${{toJSON(matrix)}} \ No newline at end of file + ROS_REP: ${{ matrix.ROS_REPO }} + CMAKE_ARGS: ${{ matrix.CMAKE_ARGS }}