diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index f5e9921f23..f89baba15b 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -18,3 +18,17 @@ updates:
schedule:
interval: "weekly"
target-branch: "humble"
+ - package-ecosystem: "github-actions"
+ # Workflow files stored in the
+ # default location of `.github/workflows`
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ target-branch: "jazzy"
+ - package-ecosystem: "github-actions"
+ # Workflow files stored in the
+ # default location of `.github/workflows`
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ target-branch: "kilted"
diff --git a/.github/mergify.yml b/.github/mergify.yml
index fd185e02d0..8293ec1b06 100644
--- a/.github/mergify.yml
+++ b/.github/mergify.yml
@@ -8,14 +8,23 @@ pull_request_rules:
branches:
- humble
- - name: Backport to iron at reviewers discretion
+ - name: Backport to jazzy at reviewers discretion
conditions:
- base=master
- - "label=backport-iron"
+ - "label=backport-jazzy"
actions:
backport:
branches:
- - iron
+ - jazzy
+
+ - name: Backport to kilted at reviewers discretion
+ conditions:
+ - base=master
+ - "label=backport-kilted"
+ actions:
+ backport:
+ branches:
+ - kilted
- name: Ask to resolve conflict
conditions:
diff --git a/.github/workflows/README.md b/.github/workflows/README.md
deleted file mode 100644
index 8e1845c5b9..0000000000
--- a/.github/workflows/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-
-ROS2 Distro | Branch | Build status | Documentation | Released packages
-:---------: | :----: | :----------: | :-----------: | :---------------:
-**Rolling** | [`master`](https://github.com/ros-controls/ros2_control/tree/master) | [](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-binary-build.yml?branch=master)
[](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-semi-binary-build.yml?branch=master)
[](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-source-build.yml?branch=master)
[](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-debian-build.yml)
[](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-rhel-binary-build.yml) | [Documentation](https://control.ros.org/master/index.html)
[API Reference](https://control.ros.org/master/doc/api/index.html) | [ros2_control](https://index.ros.org/p/ros2_control/#rolling)
-**Jazzy** | [`master`](https://github.com/ros-controls/ros2_control/tree/master) | [](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-binary-build.yml?branch=master)
[](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-semi-binary-build.yml?branch=master)
[](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-source-build.yml?branch=master)
[](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-debian-build.yml)
[](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-rhel-binary-build.yml) | [Documentation](https://control.ros.org/jazzy/index.html)
[API Reference](https://control.ros.org/jazzy/doc/api/index.html) | [ros2_control](https://index.ros.org/p/ros2_control/#jazzy)
-**Humble** | [`humble`](https://github.com/ros-controls/ros2_control/tree/humble) | [](https://github.com/ros-controls/ros2_control/actions/workflows/humble-binary-build.yml?branch=master)
[](https://github.com/ros-controls/ros2_control/actions/workflows/humble-semi-binary-build.yml?branch=master)
[](https://github.com/ros-controls/ros2_control/actions/workflows/humble-source-build.yml?branch=master)
[](https://github.com/ros-controls/ros2_control/actions/workflows/humble-debian-build.yml)
[](https://github.com/ros-controls/ros2_control/actions/workflows/humble-rhel-binary-build.yml) | [Documentation](https://control.ros.org/humble/index.html)
[API Reference](https://control.ros.org/humble/doc/api/index.html) | [ros2_control](https://index.ros.org/p/ros2_control/#humble)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 70366d4033..1cf5d72108 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -22,7 +22,7 @@ jobs:
uses: docker/setup-buildx-action@v3
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Log in to the Container registry
uses: docker/login-action@v3
@@ -62,7 +62,7 @@ jobs:
uses: docker/setup-buildx-action@v3
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Log in to the Container registry
uses: docker/login-action@v3
diff --git a/.github/workflows/humble-abi-compatibility.yml b/.github/workflows/humble-abi-compatibility.yml
index f4172ee3e6..2240ef7b46 100644
--- a/.github/workflows/humble-abi-compatibility.yml
+++ b/.github/workflows/humble-abi-compatibility.yml
@@ -9,15 +9,16 @@ on:
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/humble-abi-compatibility.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control-not-released.humble.repos'
concurrency:
- # cancel previous runs of the same workflow, except for pushes on humble branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
abi_check:
diff --git a/.github/workflows/humble-binary-build.yml b/.github/workflows/humble-binary-build.yml
index 0fabfda92f..4437819647 100644
--- a/.github/workflows/humble-binary-build.yml
+++ b/.github/workflows/humble-binary-build.yml
@@ -4,19 +4,7 @@ name: Humble Binary Build
on:
workflow_dispatch:
- pull_request:
- branches:
- - humble
- paths:
- - '**.hpp'
- - '**.h'
- - '**.cpp'
- - '**.py'
- - '.github/workflows/humble-binary-build.yml'
- - '**/package.xml'
- - '**/CMakeLists.txt'
- - 'ros2_control-not-released.humble.repos'
- push:
+ pull_request: &event
branches:
- humble
paths:
@@ -24,18 +12,20 @@ on:
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/humble-binary-build.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control-not-released.humble.repos'
+ push: *event
schedule:
# Run every morning to detect flakiness and broken dependencies
- - cron: '03 1 * * *'
+ - cron: '03 1 * * MON-FRI'
concurrency:
- # cancel previous runs of the same workflow, except for pushes on humble branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
binary:
diff --git a/.github/workflows/humble-check-docs.yml b/.github/workflows/humble-check-docs.yml
index 078a541bf0..33483ffa55 100644
--- a/.github/workflows/humble-check-docs.yml
+++ b/.github/workflows/humble-check-docs.yml
@@ -8,7 +8,14 @@ on:
paths:
- '**.rst'
- '**.md'
+ - '**.jpg'
+ - '**.jpeg'
+ - '**.png'
+ - '**.svg'
+ - '**.yml'
- '**.yaml'
+ - '!.github/**' # exclude yaml files in .github directory
+ - '.github/workflows/humble-check-docs.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
diff --git a/.github/workflows/humble-coverage-build.yml b/.github/workflows/humble-coverage-build.yml
index 8255b02bb4..68f1a48106 100644
--- a/.github/workflows/humble-coverage-build.yml
+++ b/.github/workflows/humble-coverage-build.yml
@@ -1,20 +1,7 @@
name: Coverage Build - Humble
on:
workflow_dispatch:
- push:
- branches:
- - humble
- paths:
- - '**.hpp'
- - '**.h'
- - '**.cpp'
- - '**.py'
- - '.github/workflows/humble-coverage-build.yml'
- - '**/package.xml'
- - '**/CMakeLists.txt'
- - 'ros2_control.humble.repos'
- - 'codecov.yml'
- pull_request:
+ pull_request: &event
branches:
- humble
paths:
@@ -22,16 +9,18 @@ on:
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/humble-coverage-build.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control.humble.repos'
- 'codecov.yml'
+ push: *event
concurrency:
- # cancel previous runs of the same workflow, except for pushes on humble branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
coverage_humble:
diff --git a/.github/workflows/humble-debian-build.yml b/.github/workflows/humble-debian-build.yml
index d613d99ed3..d592d50444 100644
--- a/.github/workflows/humble-debian-build.yml
+++ b/.github/workflows/humble-debian-build.yml
@@ -1,4 +1,4 @@
-name: Debian Humble Source Build
+name: Humble - Debian Semi-Binary Build
on:
workflow_dispatch:
pull_request:
@@ -9,29 +9,26 @@ on:
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/humble-debian-build.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control.humble.repos'
schedule:
# Run every day to detect flakiness and broken dependencies
- - cron: '03 1 * * *'
+ - cron: '03 1 * * MON-FRI'
concurrency:
- # cancel previous runs of the same workflow, except for pushes on humble branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
- debian_source_build:
+ debian_semi_binary_build:
uses: ros-controls/ros2_control_ci/.github/workflows/reusable-debian-build.yml@master
- strategy:
- fail-fast: false
- matrix:
- ROS_DISTRO: [humble]
with:
- ros_distro: ${{ matrix.ROS_DISTRO }}
- upstream_workspace: ros2_control.${{ matrix.ROS_DISTRO }}.repos
- ref_for_scheduled_build: master
+ ros_distro: humble
+ upstream_workspace: ros2_control.humble.repos
+ ref_for_scheduled_build: humble
skip_packages: rqt_controller_manager
skip_packages_test: controller_manager_msgs
diff --git a/.github/workflows/humble-pre-commit.yml b/.github/workflows/humble-pre-commit.yml
index 38a76ee025..e6cebafa83 100644
--- a/.github/workflows/humble-pre-commit.yml
+++ b/.github/workflows/humble-pre-commit.yml
@@ -5,6 +5,9 @@ on:
pull_request:
branches:
- humble
+ push:
+ branches:
+ - humble
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
diff --git a/.github/workflows/humble-pre-release.yml b/.github/workflows/humble-pre-release.yml
new file mode 100644
index 0000000000..f54d97745d
--- /dev/null
+++ b/.github/workflows/humble-pre-release.yml
@@ -0,0 +1,28 @@
+name: Humble - pre-release
+# author: Christoph Froehlich
+
+on:
+ workflow_dispatch:
+ inputs:
+ downstream_depth:
+ description: 'The depth of the depends-on tree to be included in the overlay workspace (-1 implies unlimited, default: 0)'
+ required: false
+ default: 0
+ type: number
+ pull_request:
+ branches:
+ - humble
+ types:
+ - opened # default
+ - reopened # default
+ - synchronize # default
+ - labeled # also if a label changes
+
+jobs:
+ default:
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-prerelease.yml@master
+ with:
+ ros_distro: humble
+ # downstream_depth is not set on pull_request event
+ prerelease_downstream_depth: ${{ github.event_name == 'pull_request' && '0' || inputs.downstream_depth }}
+ prerelease_exclude_pkg: ackermann_steering_controller admittance_controller bicycle_steering_controller chained_filter_controller diff_drive_controller effort_controllers force_torque_sensor_broadcaster forward_command_controller gpio_controllers gps_sensor_broadcaster imu_sensor_broadcaster joint_state_broadcaster joint_trajectory_controller mecanum_drive_controller motion_primitives_controllers omni_wheel_drive_controller parallel_gripper_controller pid_controller pose_broadcaster position_controllers range_sensor_broadcaster ros2_controllers ros2_controllers_test_nodes rqt_joint_trajectory_controller steering_controllers_library tricycle_controller tricycle_steering_controller velocity_controllers gz_ros2_control gz_ros2_control_demos gz_ros2_control_tests
diff --git a/.github/workflows/humble-rhel-binary-build.yml b/.github/workflows/humble-rhel-binary-build.yml
index e986bf361e..10b97eb5f2 100644
--- a/.github/workflows/humble-rhel-binary-build.yml
+++ b/.github/workflows/humble-rhel-binary-build.yml
@@ -1,4 +1,4 @@
-name: RHEL Humble Semi-Binary Build
+name: Humble - RHEL Semi-Binary Build
on:
workflow_dispatch:
pull_request:
@@ -9,18 +9,19 @@ on:
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/humble-rhel-binary-build.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control.humble.repos'
schedule:
# Run every day to detect flakiness and broken dependencies
- - cron: '03 1 * * *'
+ - cron: '03 1 * * MON-FRI'
concurrency:
- # cancel previous runs of the same workflow, except for pushes on humble branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
rhel_semi_binary_build:
diff --git a/.github/workflows/humble-semi-binary-build.yml b/.github/workflows/humble-semi-binary-build.yml
index 7b0c5f1fd3..d9e5c1f5ad 100644
--- a/.github/workflows/humble-semi-binary-build.yml
+++ b/.github/workflows/humble-semi-binary-build.yml
@@ -4,19 +4,7 @@ name: Humble Semi-Binary Build
on:
workflow_dispatch:
- pull_request:
- branches:
- - humble
- paths:
- - '**.hpp'
- - '**.h'
- - '**.cpp'
- - '**.py'
- - '.github/workflows/humble-semi-binary-build.yml'
- - '**/package.xml'
- - '**/CMakeLists.txt'
- - 'ros2_control.humble.repos'
- push:
+ pull_request: &event
branches:
- humble
paths:
@@ -24,18 +12,20 @@ on:
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/humble-semi-binary-build.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control.humble.repos'
+ push: *event
schedule:
# Run every morning to detect flakiness and broken dependencies
- - cron: '03 1 * * *'
+ - cron: '03 1 * * MON-FRI'
concurrency:
- # cancel previous runs of the same workflow, except for pushes on humble branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
semi-binary:
@@ -44,10 +34,9 @@ jobs:
fail-fast: false
matrix:
ROS_DISTRO: [humble]
- ROS_REPO: [testing]
with:
ros_distro: ${{ matrix.ROS_DISTRO }}
- ros_repo: ${{ matrix.ROS_REPO }}
+ ros_repo: testing
upstream_workspace: ros2_control.${{ matrix.ROS_DISTRO }}.repos
ref_for_scheduled_build: humble
semi-binary-clang:
diff --git a/.github/workflows/humble-semi-binary-downstream-build.yml b/.github/workflows/humble-semi-binary-downstream-build.yml
index 117516b767..a1bc86a853 100644
--- a/.github/workflows/humble-semi-binary-downstream-build.yml
+++ b/.github/workflows/humble-semi-binary-downstream-build.yml
@@ -35,17 +35,3 @@ jobs:
# we test the downstream packages, which are part of our organization
downstream_workspace: ros_controls.humble.repos
not_test_downstream: false
- build-downstream-3rd-party:
- uses: ros-controls/ros2_control_ci/.github/workflows/reusable-industrial-ci-with-cache.yml@master
- with:
- ros_distro: humble
- ros_repo: testing
- ref_for_scheduled_build: humble
- upstream_workspace: ros2_control.humble.repos
- # we don't test target_workspace, we just build it
- not_test_build: true
- # we don't test the downstream packages, which are outside of our organization
- downstream_workspace: | # build also the ros2_control packages
- ros_controls.humble.repos
- downstream.humble.repos
- not_test_downstream: true
diff --git a/.github/workflows/humble-source-build.yml b/.github/workflows/humble-source-build.yml
index 878ad92677..8773e4c265 100644
--- a/.github/workflows/humble-source-build.yml
+++ b/.github/workflows/humble-source-build.yml
@@ -9,13 +9,19 @@ on:
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/humble-source-build.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control.humble.repos'
+ pull_request:
+ branches:
+ - humble
+ paths:
+ - .github/workflows/humble-source-build.yml
schedule:
# Run every day to detect flakiness and broken dependencies
- - cron: '03 3 * * *'
+ - cron: '03 3 * * MON-FRI'
jobs:
source:
@@ -23,5 +29,4 @@ jobs:
with:
ros_distro: humble
ref: humble
- ros2_repo_branch: humble
- os_name: ubuntu-22.04
+ container: ubuntu:22.04
diff --git a/.github/workflows/jazzy-abi-compatibility.yml b/.github/workflows/jazzy-abi-compatibility.yml
index 83a81fec44..1fa362ccda 100644
--- a/.github/workflows/jazzy-abi-compatibility.yml
+++ b/.github/workflows/jazzy-abi-compatibility.yml
@@ -3,21 +3,22 @@ on:
workflow_dispatch:
pull_request:
branches:
- - master
+ - jazzy
paths:
- '**.hpp'
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/jazzy-abi-compatibility.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control-not-released.jazzy.repos'
concurrency:
- # cancel previous runs of the same workflow, except for pushes on master branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
abi_check:
diff --git a/.github/workflows/jazzy-binary-build.yml b/.github/workflows/jazzy-binary-build.yml
index 599a075d9a..2d27459096 100644
--- a/.github/workflows/jazzy-binary-build.yml
+++ b/.github/workflows/jazzy-binary-build.yml
@@ -4,38 +4,28 @@ name: Jazzy Binary Build
on:
workflow_dispatch:
- pull_request:
+ pull_request: &event
branches:
- - master
- paths:
- - '**.hpp'
- - '**.h'
- - '**.cpp'
- - '**.py'
- - '.github/workflows/jazzy-binary-build.yml'
- - '**/package.xml'
- - '**/CMakeLists.txt'
- - 'ros2_control-not-released.jazzy.repos'
- push:
- branches:
- - master
+ - jazzy
paths:
- '**.hpp'
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/jazzy-binary-build.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control-not-released.jazzy.repos'
+ push: *event
schedule:
# Run every morning to detect flakiness and broken dependencies
- - cron: '03 1 * * *'
+ - cron: '03 1 * * MON-FRI'
concurrency:
- # cancel previous runs of the same workflow, except for pushes on master branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
binary:
@@ -49,4 +39,4 @@ jobs:
ros_distro: ${{ matrix.ROS_DISTRO }}
ros_repo: ${{ matrix.ROS_REPO }}
upstream_workspace: ros2_control-not-released.${{ matrix.ROS_DISTRO }}.repos
- ref_for_scheduled_build: master
+ ref_for_scheduled_build: jazzy
diff --git a/.github/workflows/jazzy-check-docs.yml b/.github/workflows/jazzy-check-docs.yml
index 7910cde0b5..2c85a88404 100644
--- a/.github/workflows/jazzy-check-docs.yml
+++ b/.github/workflows/jazzy-check-docs.yml
@@ -4,11 +4,18 @@ on:
workflow_dispatch:
pull_request:
branches:
- - master
+ - jazzy
paths:
- '**.rst'
- '**.md'
+ - '**.jpg'
+ - '**.jpeg'
+ - '**.png'
+ - '**.svg'
+ - '**.yml'
- '**.yaml'
+ - '!.github/**' # exclude yaml files in .github directory
+ - '.github/workflows/jazzy-check-docs.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
diff --git a/.github/workflows/jazzy-coverage-build.yml b/.github/workflows/jazzy-coverage-build.yml
index aa345d1e80..0c83ddc029 100644
--- a/.github/workflows/jazzy-coverage-build.yml
+++ b/.github/workflows/jazzy-coverage-build.yml
@@ -1,31 +1,21 @@
name: Coverage Build - Jazzy
on:
workflow_dispatch:
- # TODO(anyone) activate when branched for Jazzy
- # push:
- # branches:
- # - master
- # paths:
- # - '**.hpp'
- # - '**.h'
- # - '**.cpp'
- # - '.github/workflows/jazzy-coverage-build.yml'
- # - '**/package.xml'
- # - '**/CMakeLists.txt'
- # - 'ros2_control.jazzy.repos'
- # - 'codecov.yml'
- # pull_request:
- # branches:
- # - master
- # paths:
- # - '**.hpp'
- # - '**.h'
- # - '**.cpp'
- # - '.github/workflows/jazzy-coverage-build.yml'
- # - '**/package.xml'
- # - '**/CMakeLists.txt'
- # - 'ros2_control.jazzy.repos'
- # - 'codecov.yml'
+ pull_request: &event
+ branches:
+ - jazzy
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '**.py'
+ - '**.yaml'
+ - '.github/workflows/jazzy-coverage-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.jazzy.repos'
+ - 'codecov.yml'
+ push: *event
jobs:
coverage_jazzy:
diff --git a/.github/workflows/jazzy-debian-build.yml b/.github/workflows/jazzy-debian-build.yml
index 0f695ed8bc..002b8723ff 100644
--- a/.github/workflows/jazzy-debian-build.yml
+++ b/.github/workflows/jazzy-debian-build.yml
@@ -1,29 +1,30 @@
-name: Debian Jazzy Source Build
+name: Jazzy - Debian Semi-Binary Build
on:
workflow_dispatch:
pull_request:
branches:
- - master
+ - jazzy
paths:
- '**.hpp'
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/jazzy-debian-build.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control.jazzy.repos'
schedule:
# Run every day to detect flakiness and broken dependencies
- - cron: '03 1 * * *'
+ - cron: '03 1 * * MON-FRI'
concurrency:
- # cancel previous runs of the same workflow, except for pushes on master branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
- debian_source_build:
+ debian_semi_binary_build:
uses: ros-controls/ros2_control_ci/.github/workflows/reusable-debian-build.yml@master
strategy:
fail-fast: false
@@ -32,5 +33,5 @@ jobs:
with:
ros_distro: ${{ matrix.ROS_DISTRO }}
upstream_workspace: ros2_control.${{ matrix.ROS_DISTRO }}.repos
- ref_for_scheduled_build: master
+ ref_for_scheduled_build: jazzy
skip_packages: rqt_controller_manager
diff --git a/.github/workflows/jazzy-pre-commit.yml b/.github/workflows/jazzy-pre-commit.yml
index aab5ba52ac..5f94855963 100644
--- a/.github/workflows/jazzy-pre-commit.yml
+++ b/.github/workflows/jazzy-pre-commit.yml
@@ -4,7 +4,10 @@ on:
workflow_dispatch:
pull_request:
branches:
- - master
+ - jazzy
+ push:
+ branches:
+ - jazzy
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
diff --git a/.github/workflows/jazzy-pre-release.yml b/.github/workflows/jazzy-pre-release.yml
new file mode 100644
index 0000000000..18a3a0a78b
--- /dev/null
+++ b/.github/workflows/jazzy-pre-release.yml
@@ -0,0 +1,28 @@
+name: Jazzy - pre-release
+# author: Christoph Froehlich
+
+on:
+ workflow_dispatch:
+ inputs:
+ downstream_depth:
+ description: 'The depth of the depends-on tree to be included in the overlay workspace (-1 implies unlimited, default: 0)'
+ required: false
+ default: 0
+ type: number
+ pull_request:
+ branches:
+ - jazzy
+ types:
+ - opened # default
+ - reopened # default
+ - synchronize # default
+ - labeled # also if a label changes
+
+jobs:
+ default:
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-prerelease.yml@master
+ with:
+ ros_distro: jazzy
+ # downstream_depth is not set on pull_request event
+ prerelease_downstream_depth: ${{ github.event_name == 'pull_request' && '0' || inputs.downstream_depth }}
+ prerelease_exclude_pkg: ackermann_steering_controller admittance_controller bicycle_steering_controller chained_filter_controller diff_drive_controller effort_controllers force_torque_sensor_broadcaster forward_command_controller gpio_controllers gps_sensor_broadcaster imu_sensor_broadcaster joint_state_broadcaster joint_trajectory_controller mecanum_drive_controller motion_primitives_controllers omni_wheel_drive_controller parallel_gripper_controller pid_controller pose_broadcaster position_controllers range_sensor_broadcaster ros2_controllers ros2_controllers_test_nodes rqt_joint_trajectory_controller steering_controllers_library tricycle_controller tricycle_steering_controller velocity_controllers gz_ros2_control gz_ros2_control_demos gz_ros2_control_tests
diff --git a/.github/workflows/jazzy-rhel-binary-build.yml b/.github/workflows/jazzy-rhel-binary-build.yml
index a6404d2dbd..887e1c7bf2 100644
--- a/.github/workflows/jazzy-rhel-binary-build.yml
+++ b/.github/workflows/jazzy-rhel-binary-build.yml
@@ -1,26 +1,27 @@
-name: RHEL Jazzy Semi-Binary Build
+name: Jazzy - RHEL Semi-Binary Build
on:
workflow_dispatch:
pull_request:
branches:
- - master
+ - jazzy
paths:
- '**.hpp'
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/jazzy-rhel-binary-build.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control.jazzy.repos'
schedule:
# Run every day to detect flakiness and broken dependencies
- - cron: '03 1 * * *'
+ - cron: '03 1 * * MON-FRI'
concurrency:
- # cancel previous runs of the same workflow, except for pushes on master branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
rhel_semi_binary_build:
@@ -32,5 +33,5 @@ jobs:
with:
ros_distro: ${{ matrix.ROS_DISTRO }}
upstream_workspace: ros2_control.${{ matrix.ROS_DISTRO }}.repos
- ref_for_scheduled_build: master
+ ref_for_scheduled_build: jazzy
skip_packages: rqt_controller_manager
diff --git a/.github/workflows/jazzy-semi-binary-build.yml b/.github/workflows/jazzy-semi-binary-build.yml
index d5e3a96835..805061ea47 100644
--- a/.github/workflows/jazzy-semi-binary-build.yml
+++ b/.github/workflows/jazzy-semi-binary-build.yml
@@ -4,59 +4,44 @@ name: Jazzy Semi-Binary Build
on:
workflow_dispatch:
- pull_request:
+ pull_request: &event
branches:
- - master
- paths:
- - '**.hpp'
- - '**.h'
- - '**.cpp'
- - '**.py'
- - '.github/workflows/jazzy-semi-binary-build.yml'
- - '**/package.xml'
- - '**/CMakeLists.txt'
- - 'ros2_control.jazzy.repos'
- push:
- branches:
- - master
+ - jazzy
paths:
- '**.hpp'
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/jazzy-semi-binary-build.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control.jazzy.repos'
+ push: *event
schedule:
# Run every morning to detect flakiness and broken dependencies
- - cron: '03 1 * * *'
+ - cron: '03 1 * * MON-FRI'
concurrency:
- # cancel previous runs of the same workflow, except for pushes on master branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
semi-binary:
uses: ros-controls/ros2_control_ci/.github/workflows/reusable-industrial-ci-with-cache.yml@master
- strategy:
- fail-fast: false
- matrix:
- ROS_DISTRO: [jazzy]
- ROS_REPO: [testing]
with:
- ros_distro: ${{ matrix.ROS_DISTRO }}
- ros_repo: ${{ matrix.ROS_REPO }}
- upstream_workspace: ros2_control.${{ matrix.ROS_DISTRO }}.repos
- ref_for_scheduled_build: master
+ ros_distro: jazzy
+ ros_repo: testing
+ upstream_workspace: ros2_control.jazzy.repos
+ ref_for_scheduled_build: jazzy
semi-binary-clang:
uses: ros-controls/ros2_control_ci/.github/workflows/reusable-industrial-ci-with-cache.yml@master
with:
ros_distro: jazzy
ros_repo: testing
upstream_workspace: ros2_control.jazzy.repos
- ref_for_scheduled_build: master
+ ref_for_scheduled_build: jazzy
additional_debs: clang
c_compiler: clang
cxx_compiler: clang++
diff --git a/.github/workflows/jazzy-semi-binary-downstream-build.yml b/.github/workflows/jazzy-semi-binary-downstream-build.yml
index 645941ee95..825a5bc53b 100644
--- a/.github/workflows/jazzy-semi-binary-downstream-build.yml
+++ b/.github/workflows/jazzy-semi-binary-downstream-build.yml
@@ -6,7 +6,7 @@ on:
workflow_dispatch:
pull_request:
branches:
- - master
+ - jazzy
paths:
- '**.hpp'
- '**.h'
@@ -28,24 +28,10 @@ jobs:
with:
ros_distro: jazzy
ros_repo: testing
- ref_for_scheduled_build: master
+ ref_for_scheduled_build: jazzy
upstream_workspace: ros2_control.jazzy.repos
# we don't test target_workspace, we just build it
not_test_build: true
# we test the downstream packages, which are part of our organization
downstream_workspace: ros_controls.jazzy.repos
not_test_downstream: false
- build-downstream-3rd-party:
- uses: ros-controls/ros2_control_ci/.github/workflows/reusable-industrial-ci-with-cache.yml@master
- with:
- ros_distro: jazzy
- ros_repo: testing
- ref_for_scheduled_build: master
- upstream_workspace: ros2_control.jazzy.repos
- # we don't test target_workspace, we just build it
- not_test_build: true
- # we don't test the downstream packages, which are outside of our organization
- downstream_workspace: | # build also the ros_controls packages
- ros_controls.jazzy.repos
- downstream.jazzy.repos
- not_test_downstream: true
diff --git a/.github/workflows/jazzy-source-build.yml b/.github/workflows/jazzy-source-build.yml
index 65066a4bf2..a63511aa9f 100644
--- a/.github/workflows/jazzy-source-build.yml
+++ b/.github/workflows/jazzy-source-build.yml
@@ -3,7 +3,7 @@ on:
workflow_dispatch:
push:
branches:
- - master
+ - jazzy
paths:
- '**.hpp'
- '**.h'
@@ -13,15 +13,19 @@ on:
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control.jazzy.repos'
+ pull_request:
+ branches:
+ - jazzy
+ paths:
+ - .github/workflows/jazzy-source-build.yml
schedule:
# Run every day to detect flakiness and broken dependencies
- - cron: '03 3 * * *'
+ - cron: '03 3 * * MON-FRI'
jobs:
source:
uses: ros-controls/ros2_control_ci/.github/workflows/reusable-ros-tooling-source-build.yml@master
with:
ros_distro: jazzy
- ref: master
- ros2_repo_branch: master
+ ref: jazzy
container: ubuntu:24.04
diff --git a/.github/workflows/kilted-abi-compatibility.yml b/.github/workflows/kilted-abi-compatibility.yml
new file mode 100644
index 0000000000..ac5b1ca50b
--- /dev/null
+++ b/.github/workflows/kilted-abi-compatibility.yml
@@ -0,0 +1,27 @@
+name: Kilted - ABI Compatibility Check
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - kilted
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '**.py'
+ - '**.yaml'
+ - '.github/workflows/kilted-abi-compatibility.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control-not-released.kilted.repos'
+
+concurrency:
+ # cancel previous runs of the same workflow, except for pushes on given branches
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
+
+jobs:
+ abi_check:
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-abi-check.yml@master
+ with:
+ ros_distro: kilted
diff --git a/.github/workflows/kilted-binary-build.yml b/.github/workflows/kilted-binary-build.yml
new file mode 100644
index 0000000000..a6035d6e1c
--- /dev/null
+++ b/.github/workflows/kilted-binary-build.yml
@@ -0,0 +1,42 @@
+name: Kilted Binary Build
+# author: Denis Štogl
+# description: 'Build & test all dependencies from released (binary) packages.'
+
+on:
+ workflow_dispatch:
+ pull_request: &event
+ branches:
+ - kilted
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '**.py'
+ - '**.yaml'
+ - '.github/workflows/kilted-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control-not-released.kilted.repos'
+ push: *event
+ schedule:
+ # Run every morning to detect flakiness and broken dependencies
+ - cron: '03 1 * * MON-FRI'
+
+concurrency:
+ # cancel previous runs of the same workflow, except for pushes on given branches
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
+
+jobs:
+ binary:
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-industrial-ci-with-cache.yml@master
+ strategy:
+ fail-fast: false
+ matrix:
+ ROS_DISTRO: [kilted]
+ ROS_REPO: [main, testing]
+ with:
+ ros_distro: ${{ matrix.ROS_DISTRO }}
+ ros_repo: ${{ matrix.ROS_REPO }}
+ upstream_workspace: ros2_control-not-released.${{ matrix.ROS_DISTRO }}.repos
+ ref_for_scheduled_build: kilted
diff --git a/.github/workflows/kilted-check-docs.yml b/.github/workflows/kilted-check-docs.yml
new file mode 100644
index 0000000000..7998258907
--- /dev/null
+++ b/.github/workflows/kilted-check-docs.yml
@@ -0,0 +1,29 @@
+name: Kilted Check Docs
+
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - kilted
+ paths:
+ - '**.rst'
+ - '**.md'
+ - '**.jpg'
+ - '**.jpeg'
+ - '**.png'
+ - '**.svg'
+ - '**.yml'
+ - '**.yaml'
+ - '!.github/**' # exclude yaml files in .github directory
+ - '.github/workflows/kilted-check-docs.yml'
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ check-docs:
+ name: Check Docs
+ uses: ros-controls/control.ros.org/.github/workflows/reusable-sphinx-check-single-version.yml@kilted
+ with:
+ ROS2_CONTROL_PR: ${{ github.ref }}
diff --git a/.github/workflows/kilted-coverage-build.yml b/.github/workflows/kilted-coverage-build.yml
new file mode 100644
index 0000000000..5146b14c81
--- /dev/null
+++ b/.github/workflows/kilted-coverage-build.yml
@@ -0,0 +1,25 @@
+name: Coverage Build - Kilted
+on:
+ workflow_dispatch:
+ pull_request: &event
+ branches:
+ - kilted
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '**.py'
+ - '**.yaml'
+ - '.github/workflows/kilted-coverage-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.kilted.repos'
+ - 'codecov.yml'
+ push: *event
+
+jobs:
+ coverage_kilted:
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-build-coverage.yml@master
+ secrets: inherit
+ with:
+ ros_distro: kilted
diff --git a/.github/workflows/kilted-debian-build.yml b/.github/workflows/kilted-debian-build.yml
new file mode 100644
index 0000000000..e8faa042f7
--- /dev/null
+++ b/.github/workflows/kilted-debian-build.yml
@@ -0,0 +1,37 @@
+name: Kilted - Debian Semi-Binary Build
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - kilted
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '**.py'
+ - '**.yaml'
+ - '.github/workflows/kilted-debian-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.kilted.repos'
+ schedule:
+ # Run every day to detect flakiness and broken dependencies
+ - cron: '03 1 * * MON-FRI'
+
+concurrency:
+ # cancel previous runs of the same workflow, except for pushes on given branches
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
+
+jobs:
+ debian_semi_binary_build:
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-debian-build.yml@master
+ strategy:
+ fail-fast: false
+ matrix:
+ ROS_DISTRO: [kilted]
+ with:
+ ros_distro: ${{ matrix.ROS_DISTRO }}
+ upstream_workspace: ros2_control.${{ matrix.ROS_DISTRO }}.repos
+ ref_for_scheduled_build: kilted
+ skip_packages: rqt_controller_manager
diff --git a/.github/workflows/kilted-pre-commit.yml b/.github/workflows/kilted-pre-commit.yml
new file mode 100644
index 0000000000..fc2d6d0858
--- /dev/null
+++ b/.github/workflows/kilted-pre-commit.yml
@@ -0,0 +1,20 @@
+name: Pre-Commit - Kilted
+
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - kilted
+ push:
+ branches:
+ - kilted
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ pre-commit:
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-pre-commit.yml@master
+ with:
+ ros_distro: kilted
diff --git a/.github/workflows/kilted-pre-release.yml b/.github/workflows/kilted-pre-release.yml
new file mode 100644
index 0000000000..c2c6b4042b
--- /dev/null
+++ b/.github/workflows/kilted-pre-release.yml
@@ -0,0 +1,27 @@
+name: Kilted - pre-release
+# author: Christoph Froehlich
+
+on:
+ workflow_dispatch:
+ inputs:
+ downstream_depth:
+ description: 'The depth of the depends-on tree to be included in the overlay workspace (-1 implies unlimited, default: 0)'
+ required: false
+ default: 0
+ type: number
+ pull_request:
+ branches:
+ - kilted
+ types:
+ - opened # default
+ - reopened # default
+ - synchronize # default
+ - labeled # also if a label changes
+
+jobs:
+ default:
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-prerelease.yml@master
+ with:
+ ros_distro: kilted
+ # downstream_depth is not set on pull_request event
+ prerelease_downstream_depth: ${{ github.event_name == 'pull_request' && '0' || inputs.downstream_depth }}
diff --git a/.github/workflows/kilted-rhel-binary-build.yml b/.github/workflows/kilted-rhel-binary-build.yml
new file mode 100644
index 0000000000..2f227d1233
--- /dev/null
+++ b/.github/workflows/kilted-rhel-binary-build.yml
@@ -0,0 +1,37 @@
+name: Kilted - RHEL Semi-Binary Build
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - kilted
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '**.py'
+ - '**.yaml'
+ - '.github/workflows/kilted-rhel-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.kilted.repos'
+ schedule:
+ # Run every day to detect flakiness and broken dependencies
+ - cron: '03 1 * * MON-FRI'
+
+concurrency:
+ # cancel previous runs of the same workflow, except for pushes on given branches
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
+
+jobs:
+ rhel_semi_binary_build:
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-rhel-binary-build.yml@master
+ strategy:
+ fail-fast: false
+ matrix:
+ ROS_DISTRO: [kilted]
+ with:
+ ros_distro: ${{ matrix.ROS_DISTRO }}
+ upstream_workspace: ros2_control.${{ matrix.ROS_DISTRO }}.repos
+ ref_for_scheduled_build: kilted
+ skip_packages: rqt_controller_manager
diff --git a/.github/workflows/kilted-semi-binary-build.yml b/.github/workflows/kilted-semi-binary-build.yml
new file mode 100644
index 0000000000..45a96814c9
--- /dev/null
+++ b/.github/workflows/kilted-semi-binary-build.yml
@@ -0,0 +1,48 @@
+name: Kilted Semi-Binary Build
+# author: Denis Štogl
+# description: 'Build & test all ros2_control dependencies from source.'
+
+on:
+ workflow_dispatch:
+ pull_request: &event
+ branches:
+ - kilted
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '**.py'
+ - '**.yaml'
+ - '.github/workflows/kilted-semi-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.kilted.repos'
+ push: *event
+ schedule:
+ # Run every morning to detect flakiness and broken dependencies
+ - cron: '03 1 * * MON-FRI'
+
+concurrency:
+ # cancel previous runs of the same workflow, except for pushes on given branches
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
+
+jobs:
+ semi-binary:
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-industrial-ci-with-cache.yml@master
+ with:
+ ros_distro: kilted
+ ros_repo: testing
+ upstream_workspace: ros2_control.kilted.repos
+ ref_for_scheduled_build: kilted
+ semi-binary-clang:
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-industrial-ci-with-cache.yml@master
+ with:
+ ros_distro: kilted
+ ros_repo: testing
+ upstream_workspace: ros2_control.kilted.repos
+ ref_for_scheduled_build: kilted
+ additional_debs: clang
+ c_compiler: clang
+ cxx_compiler: clang++
+ not_test_build: true
diff --git a/.github/workflows/kilted-semi-binary-downstream-build.yml b/.github/workflows/kilted-semi-binary-downstream-build.yml
new file mode 100644
index 0000000000..4f45ead4c4
--- /dev/null
+++ b/.github/workflows/kilted-semi-binary-downstream-build.yml
@@ -0,0 +1,37 @@
+name: Kilted Downstream Build
+# description: 'Build & test downstream packages from source.'
+# author: Christoph Froehlich
+
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - kilted
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '**.py'
+ - '**.yaml'
+ - '.github/workflows/kilted-semi-binary-downstream-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros_controls.kilted.repos'
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build-downstream:
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-industrial-ci-with-cache.yml@master
+ with:
+ ros_distro: kilted
+ ros_repo: testing
+ ref_for_scheduled_build: kilted
+ upstream_workspace: ros2_control.kilted.repos
+ # we don't test target_workspace, we just build it
+ not_test_build: true
+ # we test the downstream packages, which are part of our organization
+ downstream_workspace: ros_controls.kilted.repos
+ not_test_downstream: false
diff --git a/.github/workflows/kilted-source-build.yml b/.github/workflows/kilted-source-build.yml
new file mode 100644
index 0000000000..53bf277d12
--- /dev/null
+++ b/.github/workflows/kilted-source-build.yml
@@ -0,0 +1,32 @@
+name: Kilted Source Build
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - kilted
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '**.py'
+ - '**.yaml'
+ - '.github/workflows/kilted-source-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.kilted.repos'
+ pull_request:
+ branches:
+ - kilted
+ paths:
+ - .github/workflows/kilted-source-build.yml
+ schedule:
+ # Run every day to detect flakiness and broken dependencies
+ - cron: '03 3 * * MON-FRI'
+
+jobs:
+ source:
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-ros-tooling-source-build.yml@master
+ with:
+ ros_distro: kilted
+ ref: kilted
+ container: ubuntu:24.04
diff --git a/.github/workflows/prerelease-check.yml b/.github/workflows/prerelease-check.yml
deleted file mode 100644
index 182df6e22b..0000000000
--- a/.github/workflows/prerelease-check.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-name: Pre-Release Check
-
-on:
- workflow_dispatch:
- inputs:
- ros_distro:
- description: 'Chose ROS distribution'
- required: true
- default: 'rolling'
- type: choice
- options:
- - humble
- - iron
- - rolling
- branch:
- description: 'Chose branch for distro'
- required: true
- default: 'master'
- type: choice
- options:
- - humble
- - iron
- - master
-
-jobs:
- pre_release:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- with:
- ref: ${{ github.event.inputs.branch }}
- - name: industrial_ci
- uses: ros-industrial/industrial_ci@master
- env:
- ROS_DISTRO: ${{ github.event.inputs.ros_distro }}
- PRERELEASE: true
- BASEDIR: ${{ github.workspace }}/.work
diff --git a/.github/workflows/rolling-abi-compatibility.yml b/.github/workflows/rolling-abi-compatibility.yml
index 02a38494b5..fe865b8e88 100644
--- a/.github/workflows/rolling-abi-compatibility.yml
+++ b/.github/workflows/rolling-abi-compatibility.yml
@@ -9,18 +9,24 @@ on:
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/rolling-abi-compatibility.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control-not-released.rolling.repos'
+ - 'ros2_control-not-released.kilted.repos'
concurrency:
- # cancel previous runs of the same workflow, except for pushes on master branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
abi_check:
uses: ros-controls/ros2_control_ci/.github/workflows/reusable-abi-check.yml@master
+ strategy:
+ fail-fast: false
+ matrix:
+ ROS_DISTRO: [rolling]
with:
- ros_distro: rolling
+ ros_distro: ${{ matrix.ROS_DISTRO }}
diff --git a/.github/workflows/rolling-binary-build.yml b/.github/workflows/rolling-binary-build.yml
index 0859169b15..05c3e09b97 100644
--- a/.github/workflows/rolling-binary-build.yml
+++ b/.github/workflows/rolling-binary-build.yml
@@ -4,19 +4,7 @@ name: Rolling Binary Build
on:
workflow_dispatch:
- pull_request:
- branches:
- - master
- paths:
- - '**.hpp'
- - '**.h'
- - '**.cpp'
- - '**.py'
- - '.github/workflows/rolling-binary-build.yml'
- - '**/package.xml'
- - '**/CMakeLists.txt'
- - 'ros2_control-not-released.rolling.repos'
- push:
+ pull_request: &event
branches:
- master
paths:
@@ -24,18 +12,21 @@ on:
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/rolling-binary-build.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control-not-released.rolling.repos'
+ - 'ros2_control-not-released.kilted.repos'
+ push: *event
schedule:
# Run every morning to detect flakiness and broken dependencies
- - cron: '03 1 * * *'
+ - cron: '03 1 * * MON-FRI'
concurrency:
- # cancel previous runs of the same workflow, except for pushes on master branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
binary:
diff --git a/.github/workflows/rolling-check-docs.yml b/.github/workflows/rolling-check-docs.yml
index 085ee7f9d1..bac1a6dd86 100644
--- a/.github/workflows/rolling-check-docs.yml
+++ b/.github/workflows/rolling-check-docs.yml
@@ -8,7 +8,13 @@ on:
paths:
- '**.rst'
- '**.md'
+ - '**.jpg'
+ - '**.jpeg'
+ - '**.png'
+ - '**.svg'
+ - '**.yml'
- '**.yaml'
+ - '!.github/**' # exclude yaml files in .github directory
- '.github/workflows/rolling-check-docs.yml'
concurrency:
diff --git a/.github/workflows/rolling-compatibility-humble-binary-build.yml b/.github/workflows/rolling-compatibility-humble-binary-build.yml
index 35957bbc0e..a5bec4e952 100644
--- a/.github/workflows/rolling-compatibility-humble-binary-build.yml
+++ b/.github/workflows/rolling-compatibility-humble-binary-build.yml
@@ -4,19 +4,7 @@ name: Check Rolling Compatibility on Humble
on:
workflow_dispatch:
- pull_request:
- branches:
- - master
- paths:
- - '**.hpp'
- - '**.h'
- - '**.cpp'
- - '**.py'
- - '.github/workflows/rolling-compatibility-humble-binary-build.yml'
- - '**/package.xml'
- - '**/CMakeLists.txt'
- - 'ros2_control.rolling.repos'
- push:
+ pull_request: &event
branches:
- master
paths:
@@ -24,26 +12,23 @@ on:
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/rolling-compatibility-humble-binary-build.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control.rolling.repos'
+ push: *event
concurrency:
- # cancel previous runs of the same workflow, except for pushes on master branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
build-on-humble:
uses: ros-controls/ros2_control_ci/.github/workflows/reusable-industrial-ci-with-cache.yml@master
- strategy:
- fail-fast: false
- matrix:
- ROS_DISTRO: [humble]
- ROS_REPO: [testing]
with:
- ros_distro: ${{ matrix.ROS_DISTRO }}
- ros_repo: ${{ matrix.ROS_REPO }}
+ ros_distro: humble
+ ros_repo: testing
upstream_workspace: ros2_control.rolling.repos
ref_for_scheduled_build: master
diff --git a/.github/workflows/rolling-compatibility-jazzy-binary-build.yml b/.github/workflows/rolling-compatibility-jazzy-binary-build.yml
index bddbd14878..01d8ba423f 100644
--- a/.github/workflows/rolling-compatibility-jazzy-binary-build.yml
+++ b/.github/workflows/rolling-compatibility-jazzy-binary-build.yml
@@ -4,19 +4,7 @@ name: Check Rolling Compatibility on Jazzy
on:
workflow_dispatch:
- pull_request:
- branches:
- - master
- paths:
- - '**.hpp'
- - '**.h'
- - '**.cpp'
- - '**.py'
- - '.github/workflows/rolling-compatibility-jazzy-binary-build.yml'
- - '**/package.xml'
- - '**/CMakeLists.txt'
- - 'ros2_control.rolling.repos'
- push:
+ pull_request: &event
branches:
- master
paths:
@@ -24,26 +12,23 @@ on:
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/rolling-compatibility-jazzy-binary-build.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control.rolling.repos'
+ push: *event
concurrency:
- # cancel previous runs of the same workflow, except for pushes on master branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
build-on-jazzy:
uses: ros-controls/ros2_control_ci/.github/workflows/reusable-industrial-ci-with-cache.yml@master
- strategy:
- fail-fast: false
- matrix:
- ROS_DISTRO: [jazzy]
- ROS_REPO: [testing]
with:
- ros_distro: ${{ matrix.ROS_DISTRO }}
- ros_repo: ${{ matrix.ROS_REPO }}
+ ros_distro: jazzy
+ ros_repo: testing
upstream_workspace: ros2_control.rolling.repos
ref_for_scheduled_build: master
diff --git a/.github/workflows/rolling-compatibility-kilted-binary-build.yml b/.github/workflows/rolling-compatibility-kilted-binary-build.yml
new file mode 100644
index 0000000000..45097e424e
--- /dev/null
+++ b/.github/workflows/rolling-compatibility-kilted-binary-build.yml
@@ -0,0 +1,34 @@
+name: Check Rolling Compatibility on Kilted
+# author: Christoph Froehlich
+# description: 'Build & test the rolling version on Kilted distro.'
+
+on:
+ workflow_dispatch:
+ pull_request: &event
+ branches:
+ - master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '**.py'
+ - '**.yaml'
+ - '.github/workflows/rolling-compatibility-kilted-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.rolling.repos'
+ push: *event
+
+concurrency:
+ # cancel previous runs of the same workflow, except for pushes on given branches
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
+
+jobs:
+ build-on-kilted:
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-industrial-ci-with-cache.yml@master
+ with:
+ ros_distro: kilted
+ ros_repo: testing
+ upstream_workspace: ros2_control.rolling.repos
+ ref_for_scheduled_build: master
diff --git a/.github/workflows/rolling-coverage-build.yml b/.github/workflows/rolling-coverage-build.yml
index ad4e467606..bef06d3622 100644
--- a/.github/workflows/rolling-coverage-build.yml
+++ b/.github/workflows/rolling-coverage-build.yml
@@ -1,20 +1,7 @@
name: Coverage Build - Rolling
on:
workflow_dispatch:
- push:
- branches:
- - master
- paths:
- - '**.hpp'
- - '**.h'
- - '**.cpp'
- - '**.py'
- - '.github/workflows/rolling-coverage-build.yml'
- - '**/package.xml'
- - '**/CMakeLists.txt'
- - 'ros2_control.rolling.repos'
- - 'codecov.yml'
- pull_request:
+ pull_request: &event
branches:
- master
paths:
@@ -22,16 +9,18 @@ on:
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/rolling-coverage-build.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control.rolling.repos'
- 'codecov.yml'
+ push: *event
concurrency:
- # cancel previous runs of the same workflow, except for pushes on master branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
coverage_rolling:
diff --git a/.github/workflows/rolling-debian-build.yml b/.github/workflows/rolling-debian-build.yml
index a54e7ad0d1..01d59bbc28 100644
--- a/.github/workflows/rolling-debian-build.yml
+++ b/.github/workflows/rolling-debian-build.yml
@@ -1,4 +1,4 @@
-name: Debian Rolling Source Build
+name: Rolling - Debian Semi-Binary Build
on:
workflow_dispatch:
pull_request:
@@ -9,22 +9,24 @@ on:
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/rolling-debian-build.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control.rolling.repos'
+ - 'ros2_control.kilted.repos'
schedule:
# Run every day to detect flakiness and broken dependencies
- - cron: '03 1 * * *'
+ - cron: '03 1 * * MON-FRI'
concurrency:
- # cancel previous runs of the same workflow, except for pushes on master branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
- debian_source_build:
+ debian_semi_binary_build:
uses: ros-controls/ros2_control_ci/.github/workflows/reusable-debian-build.yml@master
strategy:
fail-fast: false
diff --git a/.github/workflows/rolling-pre-commit.yml b/.github/workflows/rolling-pre-commit.yml
index 792278d6d2..54d7e90adf 100644
--- a/.github/workflows/rolling-pre-commit.yml
+++ b/.github/workflows/rolling-pre-commit.yml
@@ -5,6 +5,9 @@ on:
pull_request:
branches:
- master
+ push:
+ branches:
+ - master
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -13,5 +16,9 @@ concurrency:
jobs:
pre-commit:
uses: ros-controls/ros2_control_ci/.github/workflows/reusable-pre-commit.yml@master
+ strategy:
+ fail-fast: false
+ matrix:
+ ROS_DISTRO: [rolling]
with:
- ros_distro: rolling
+ ros_distro: ${{ matrix.ROS_DISTRO }}
diff --git a/.github/workflows/rolling-pre-release.yml b/.github/workflows/rolling-pre-release.yml
new file mode 100644
index 0000000000..71ac765177
--- /dev/null
+++ b/.github/workflows/rolling-pre-release.yml
@@ -0,0 +1,33 @@
+name: Rolling - pre-release
+# author: Christoph Froehlich
+
+on:
+ workflow_dispatch:
+ inputs:
+ downstream_depth:
+ description: 'The depth of the depends-on tree to be included in the overlay workspace (-1 implies unlimited, default: 0)'
+ required: false
+ default: 0
+ type: number
+ pull_request:
+ branches:
+ - master
+ types:
+ - opened # default
+ - reopened # default
+ - synchronize # default
+ - labeled # also if a label changes
+
+jobs:
+ default:
+ strategy:
+ fail-fast: false
+ matrix:
+ ROS_DISTRO: [rolling]
+
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-prerelease.yml@master
+ with:
+ ros_distro: ${{ matrix.ROS_DISTRO }}
+ # downstream_depth is not set on pull_request event
+ prerelease_downstream_depth: ${{ github.event_name == 'pull_request' && '0' || inputs.downstream_depth }}
+ prerelease_exclude_pkg: ackermann_steering_controller admittance_controller bicycle_steering_controller chained_filter_controller diff_drive_controller effort_controllers force_torque_sensor_broadcaster forward_command_controller gpio_controllers gps_sensor_broadcaster imu_sensor_broadcaster joint_state_broadcaster joint_trajectory_controller mecanum_drive_controller motion_primitives_controllers omni_wheel_drive_controller parallel_gripper_controller pid_controller pose_broadcaster position_controllers range_sensor_broadcaster ros2_controllers ros2_controllers_test_nodes rqt_joint_trajectory_controller steering_controllers_library tricycle_controller tricycle_steering_controller velocity_controllers gz_ros2_control gz_ros2_control_demos gz_ros2_control_tests
diff --git a/.github/workflows/rolling-rhel-binary-build.yml b/.github/workflows/rolling-rhel-binary-build.yml
index 95d6759da6..3d39bd3262 100644
--- a/.github/workflows/rolling-rhel-binary-build.yml
+++ b/.github/workflows/rolling-rhel-binary-build.yml
@@ -1,4 +1,4 @@
-name: RHEL Rolling Semi-Binary Build
+name: Rolling - RHEL Semi-Binary Build
on:
workflow_dispatch:
pull_request:
@@ -9,18 +9,20 @@ on:
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/rolling-rhel-binary-build.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- - 'ros2_control.jazzy.repos'
+ - 'ros2_control.rolling.repos'
+ - 'ros2_control.kilted.repos'
schedule:
# Run every day to detect flakiness and broken dependencies
- - cron: '03 1 * * *'
+ - cron: '03 1 * * MON-FRI'
concurrency:
- # cancel previous runs of the same workflow, except for pushes on master branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
rhel_semi_binary_build:
diff --git a/.github/workflows/rolling-semi-binary-build-win.yml b/.github/workflows/rolling-semi-binary-build-win.yml
new file mode 100644
index 0000000000..efc66c2fac
--- /dev/null
+++ b/.github/workflows/rolling-semi-binary-build-win.yml
@@ -0,0 +1,33 @@
+name: Rolling Windows Semi-Binary Build
+# author: Christoph Fröhlich
+# description: 'Build & test all dependencies from semi-binary packages.'
+
+on:
+ workflow_dispatch:
+ pull_request: &event
+ branches:
+ - master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '**.py'
+ - '**.yaml'
+ - '.github/workflows/rolling-semi-binary-build-win.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ push: *event
+
+concurrency:
+ # cancel previous runs of the same workflow, except for pushes on given branches
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
+
+jobs:
+ binary-windows:
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-ros-tooling-win-build.yml@master
+ with:
+ ros_distro: rolling
+ pixi_dependencies: typeguard jinja2 boost compilers
+ ninja_packages: rsl
+ target_cmake_args: -DBUILD_TESTING=OFF
diff --git a/.github/workflows/rolling-semi-binary-build.yml b/.github/workflows/rolling-semi-binary-build.yml
index 6431b9f4fa..b8256d39f5 100644
--- a/.github/workflows/rolling-semi-binary-build.yml
+++ b/.github/workflows/rolling-semi-binary-build.yml
@@ -4,19 +4,7 @@ name: Rolling Semi-Binary Build
on:
workflow_dispatch:
- pull_request:
- branches:
- - master
- paths:
- - '**.hpp'
- - '**.h'
- - '**.cpp'
- - '**.py'
- - '.github/workflows/rolling-semi-binary-build.yml'
- - '**/package.xml'
- - '**/CMakeLists.txt'
- - 'ros2_control.rolling.repos'
- push:
+ pull_request: &event
branches:
- master
paths:
@@ -24,18 +12,21 @@ on:
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/rolling-semi-binary-build.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control.rolling.repos'
+ - 'ros2_control.kilted.repos'
+ push: *event
schedule:
# Run every morning to detect flakiness and broken dependencies
- - cron: '03 1 * * *'
+ - cron: '03 1 * * MON-FRI'
concurrency:
- # cancel previous runs of the same workflow, except for pushes on master branch
+ # cancel previous runs of the same workflow, except for pushes on given branches
group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ !startsWith(github.ref, '/refs/heads') }}
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/heads') }}
jobs:
semi-binary:
@@ -44,19 +35,21 @@ jobs:
fail-fast: false
matrix:
ROS_DISTRO: [rolling]
- ROS_REPO: [testing]
with:
ros_distro: ${{ matrix.ROS_DISTRO }}
- ros_repo: ${{ matrix.ROS_REPO }}
+ ros_repo: testing
upstream_workspace: ros2_control.${{ matrix.ROS_DISTRO }}.repos
ref_for_scheduled_build: master
semi-binary-clang:
uses: ros-controls/ros2_control_ci/.github/workflows/reusable-industrial-ci-with-cache.yml@master
+ strategy:
+ fail-fast: false
+ matrix:
+ ROS_DISTRO: [rolling]
with:
- # job for building only, no tests -> one distro is enough
- ros_distro: rolling
+ ros_distro: ${{ matrix.ROS_DISTRO }}
ros_repo: testing
- upstream_workspace: ros2_control.rolling.repos
+ upstream_workspace: ros2_control.${{ matrix.ROS_DISTRO }}.repos
ref_for_scheduled_build: master
additional_debs: clang
c_compiler: clang
diff --git a/.github/workflows/rolling-semi-binary-downstream-build.yml b/.github/workflows/rolling-semi-binary-downstream-build.yml
index 67d22def26..13735752a9 100644
--- a/.github/workflows/rolling-semi-binary-downstream-build.yml
+++ b/.github/workflows/rolling-semi-binary-downstream-build.yml
@@ -17,6 +17,7 @@ on:
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros_controls.rolling.repos'
+ - 'ros_controls.kilted.repos'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -39,21 +40,3 @@ jobs:
# we test the downstream packages, which are part of our organization
downstream_workspace: ros_controls.${{ matrix.ROS_DISTRO }}.repos
not_test_downstream: false
- build-downstream-3rd-party:
- uses: ros-controls/ros2_control_ci/.github/workflows/reusable-industrial-ci-with-cache.yml@master
- strategy:
- fail-fast: false
- matrix:
- ROS_DISTRO: [rolling]
- with:
- ros_distro: ${{ matrix.ROS_DISTRO }}
- ros_repo: testing
- ref_for_scheduled_build: master
- upstream_workspace: ros2_control.${{ matrix.ROS_DISTRO }}.repos
- # we don't test target_workspace, we just build it
- not_test_build: true
- # we don't test the downstream packages, which are outside of our organization
- downstream_workspace: | # build also the ros_controls packages
- ros_controls.${{ matrix.ROS_DISTRO }}.repos
- downstream.${{ matrix.ROS_DISTRO }}.repos
- not_test_downstream: true
diff --git a/.github/workflows/rolling-source-build.yml b/.github/workflows/rolling-source-build.yml
index 9bbf09cda4..4c07772dcd 100644
--- a/.github/workflows/rolling-source-build.yml
+++ b/.github/workflows/rolling-source-build.yml
@@ -9,19 +9,29 @@ on:
- '**.h'
- '**.cpp'
- '**.py'
+ - '**.yaml'
- '.github/workflows/rolling-source-build.yml'
- '**/package.xml'
- '**/CMakeLists.txt'
- 'ros2_control.rolling.repos'
+ - 'ros2_control.kilted.repos'
+ pull_request:
+ branches:
+ - master
+ paths:
+ - .github/workflows/rolling-source-build.yml
schedule:
# Run every day to detect flakiness and broken dependencies
- - cron: '03 3 * * *'
+ - cron: '03 3 * * MON-FRI'
jobs:
source:
uses: ros-controls/ros2_control_ci/.github/workflows/reusable-ros-tooling-source-build.yml@master
+ strategy:
+ fail-fast: false
+ matrix:
+ ROS_DISTRO: [rolling]
with:
- ros_distro: rolling
+ ros_distro: ${{ matrix.ROS_DISTRO }}
ref: master
- ros2_repo_branch: master
container: ubuntu:24.04
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index f30de29b75..75b6b01885 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -12,7 +12,7 @@ jobs:
issues: write
pull-requests: write
steps:
- - uses: actions/stale@v9
+ - uses: actions/stale@v10
with:
stale-issue-label: 'stale'
stale-pr-label: 'stale'
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index ee0e31793a..78866ca277 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -16,7 +16,7 @@
repos:
# Standard hooks
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v5.0.0
+ rev: v6.0.0
hooks:
- id: check-added-large-files
- id: check-ast
@@ -37,7 +37,7 @@ repos:
# Python hooks
- repo: https://github.com/asottile/pyupgrade
- rev: v3.19.1
+ rev: v3.21.0
hooks:
- id: pyupgrade
args: [--py36-plus]
@@ -50,20 +50,20 @@ repos:
args: ["--ignore=D100,D101,D102,D103,D104,D105,D106,D107,D203,D212,D404"]
- repo: https://github.com/psf/black
- rev: 25.1.0
+ rev: 25.9.0
hooks:
- id: black
args: ["--line-length=99"]
- repo: https://github.com/pycqa/flake8
- rev: 7.2.0
+ rev: 7.3.0
hooks:
- id: flake8
args: ["--extend-ignore=E501"]
# CPP hooks
- repo: https://github.com/pre-commit/mirrors-clang-format
- rev: v20.1.3
+ rev: v21.1.2
hooks:
- id: clang-format
args: ['-fallback-style=none', '-i']
@@ -109,7 +109,7 @@ repos:
# Docs - RestructuredText hooks
- repo: https://github.com/PyCQA/doc8
- rev: v1.1.2
+ rev: v2.0.0
hooks:
- id: doc8
args: ['--max-line-length=100', '--ignore=D001']
@@ -133,7 +133,7 @@ repos:
exclude: CHANGELOG\.rst|\.(svg|pyc|drawio)$
- repo: https://github.com/python-jsonschema/check-jsonschema
- rev: 0.33.0
+ rev: 0.34.1
hooks:
- id: check-github-workflows
args: ["--verbose"]
diff --git a/README.md b/README.md
index 0842949073..5dbd61f951 100644
--- a/README.md
+++ b/README.md
@@ -13,11 +13,12 @@ If you are new to the project, please read the [contributing guide](https://cont
## Build status
-ROS2 Distro | Branch | Build status | Documentation | Released packages
+ROS2 Distro | Branch | Build status | Documentation | Package Build
:---------: | :----: | :----------: | :-----------: | :---------------:
-**Rolling** | [`master`](https://github.com/ros-controls/ros2_control/tree/master) | [](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-binary-build.yml?branch=master)
[](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-semi-binary-build.yml?branch=master)
[](https://build.ros2.org/job/Rdev__ros2_control__ubuntu_noble_amd64/) | [Documentation](https://control.ros.org/master/index.html)
[API Reference](https://control.ros.org/master/doc/api/index.html) | [ros2_control](https://index.ros.org/p/ros2_control/#rolling)
-**Jazzy** | [`master`](https://github.com/ros-controls/ros2_control/tree/master) | [](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-binary-build.yml?branch=master)
[](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-semi-binary-build.yml?branch=master)
[](https://build.ros2.org/job/Jdev__ros2_control__ubuntu_noble_amd64/) | [Documentation](https://control.ros.org/jazzy/index.html)
[API Reference](https://control.ros.org/jazzy/doc/api/index.html) | [ros2_control](https://index.ros.org/p/ros2_control/#jazzy)
-**Humble** | [`humble`](https://github.com/ros-controls/ros2_control/tree/humble) | [](https://github.com/ros-controls/ros2_control/actions/workflows/humble-binary-build.yml?branch=master)
[](https://github.com/ros-controls/ros2_control/actions/workflows/humble-semi-binary-build.yml?branch=master)
[](https://build.ros2.org/job/Hdev__ros2_control__ubuntu_jammy_amd64/) | [Documentation](https://control.ros.org/humble/index.html)
[API Reference](https://control.ros.org/humble/doc/api/index.html) | [ros2_control](https://index.ros.org/p/ros2_control/#humble)
+**Rolling** | [`master`](https://github.com/ros-controls/ros2_control/tree/master) | [](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-binary-build.yml?branch=master)
[](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-semi-binary-build.yml?branch=master)
[](https://build.ros2.org/job/Rdev__ros2_control__ubuntu_noble_amd64/) | [Documentation](https://control.ros.org/master/index.html)
[API Reference](https://control.ros.org/master/doc/api/index.html) | [](https://build.ros2.org/job/Rbin_uN64__ros2_control__ubuntu_noble_amd64__binary/)
+**Kilted** | [`kilted`](https://github.com/ros-controls/ros2_control/tree/kilted) | [](https://github.com/ros-controls/ros2_control/actions/workflows/kilted-binary-build.yml?branch=master)
[](https://github.com/ros-controls/ros2_control/actions/workflows/kilted-semi-binary-build.yml?branch=master)
[](https://build.ros2.org/job/Kdev__ros2_control__ubuntu_noble_amd64/) | [Documentation](https://control.ros.org/kilted/index.html)
[API Reference](https://control.ros.org/kilted/doc/api/index.html) | [](https://build.ros2.org/job/Kbin_uN64__ros2_control__ubuntu_noble_amd64__binary/)
+**Jazzy** | [`jazzy`](https://github.com/ros-controls/ros2_control/tree/jazzy) | [](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-binary-build.yml?branch=master)
[](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-semi-binary-build.yml?branch=master)
[](https://build.ros2.org/job/Jdev__ros2_control__ubuntu_noble_amd64/) | [Documentation](https://control.ros.org/jazzy/index.html)
[API Reference](https://control.ros.org/jazzy/doc/api/index.html) | [](https://build.ros2.org/job/Jbin_uN64__ros2_control__ubuntu_noble_amd64__binary/)
+**Humble** | [`humble`](https://github.com/ros-controls/ros2_control/tree/humble) | [](https://github.com/ros-controls/ros2_control/actions/workflows/humble-binary-build.yml?branch=master)
[](https://github.com/ros-controls/ros2_control/actions/workflows/humble-semi-binary-build.yml?branch=master)
[](https://build.ros2.org/job/Hdev__ros2_control__ubuntu_jammy_amd64/) | [Documentation](https://control.ros.org/humble/index.html)
[API Reference](https://control.ros.org/humble/doc/api/index.html) | [](https://build.ros2.org/job/Hbin_uJ64__ros2_control__ubuntu_jammy_amd64__binary/)
## Docker images
diff --git a/controller_interface/CHANGELOG.rst b/controller_interface/CHANGELOG.rst
index 9148df31b8..6c83dc6a91 100644
--- a/controller_interface/CHANGELOG.rst
+++ b/controller_interface/CHANGELOG.rst
@@ -2,6 +2,53 @@
Changelog for package controller_interface
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+6.0.1 (2025-11-03)
+------------------
+
+6.0.0 (2025-10-27)
+------------------
+* Add magnetic_field_sensor semantic component (`#2627 `_)
+* Fix `-Wreturn-local-addr` compiler warning (`#2628 `_)
+* [Controllers] Set async thread properties via parameters (`#2613 `_)
+* Contributors: Aarav Gupta, Christoph Fröhlich, Sai Kishor Kothakota
+
+5.7.0 (2025-10-03)
+------------------
+* Cleanup deprecations for kilted release (`#2605 `_)
+* Add `ControllerInterfaceParams` to initialize the Controllers (`#2390 `_)
+* Let `get_ordered_interfaces` throw if input vector size does not fit (`#2528 `_)
+* Update message dependencies for tests (`#2497 `_)
+* Contributors: Christoph Fröhlich, Sai Kishor Kothakota
+
+5.6.0 (2025-08-26)
+------------------
+
+5.5.0 (2025-07-31)
+------------------
+* Fix missing include for std::find (`#2425 `_)
+* Document order of interfaces (`#2394 `_)
+* Contributors: Christoph Fröhlich, Guilhem Saurel
+
+5.4.0 (2025-07-21)
+------------------
+
+5.3.0 (2025-07-02)
+------------------
+
+5.2.0 (2025-06-07)
+------------------
+
+5.1.0 (2025-05-24)
+------------------
+* Use target_link_libraries instead of ament_target_dependencies (`#2266 `_)
+* Cleanup deprecations in `ros_control` (`#2258 `_)
+* Contributors: Sai Kishor Kothakota
+
+5.0.0 (2025-05-21)
+------------------
+* Statically allocate string concatenations using FMT formatting (`#2205 `_)
+* Contributors: mini-1235
+
4.29.0 (2025-05-04)
-------------------
* Add common reusable helper methods header (`#2099 `_)
diff --git a/controller_interface/CMakeLists.txt b/controller_interface/CMakeLists.txt
index 069f968528..52105df453 100644
--- a/controller_interface/CMakeLists.txt
+++ b/controller_interface/CMakeLists.txt
@@ -9,6 +9,7 @@ set(THIS_PACKAGE_INCLUDE_DEPENDS
hardware_interface
rclcpp_lifecycle
realtime_tools
+ fmt
)
find_package(ament_cmake REQUIRED)
@@ -27,12 +28,17 @@ target_include_directories(controller_interface PUBLIC
$
$
)
-ament_target_dependencies(controller_interface PUBLIC ${THIS_PACKAGE_INCLUDE_DEPENDS})
+target_link_libraries(controller_interface PUBLIC
+ hardware_interface::hardware_interface
+ rclcpp_lifecycle::rclcpp_lifecycle
+ realtime_tools::realtime_tools
+ fmt::fmt)
if(BUILD_TESTING)
find_package(ament_cmake_gmock REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(sensor_msgs REQUIRED)
+ find_package(std_msgs REQUIRED)
ament_add_gmock(test_controller_interface test/test_controller_interface.cpp)
target_link_libraries(test_controller_interface
@@ -63,29 +69,35 @@ if(BUILD_TESTING)
target_link_libraries(test_force_torque_sensor
controller_interface
hardware_interface::hardware_interface
+ ${geometry_msgs_TARGETS}
)
ament_add_gmock(test_imu_sensor test/test_imu_sensor.cpp)
target_link_libraries(test_imu_sensor
controller_interface
hardware_interface::hardware_interface
+ ${sensor_msgs_TARGETS}
)
- ament_target_dependencies(test_imu_sensor
- sensor_msgs
+
+ ament_add_gmock(test_magnetic_field_sensor test/test_magnetic_field_sensor.cpp)
+ target_link_libraries(test_magnetic_field_sensor
+ controller_interface
+ hardware_interface::hardware_interface
+ ${sensor_msgs_TARGETS}
)
ament_add_gmock(test_pose_sensor test/test_pose_sensor.cpp)
target_link_libraries(test_pose_sensor
controller_interface
hardware_interface::hardware_interface
+ ${geometry_msgs_TARGETS}
)
- ament_target_dependencies(test_pose_sensor
- geometry_msgs
- )
+
ament_add_gmock(test_gps_sensor test/test_gps_sensor.cpp)
target_link_libraries(test_gps_sensor
controller_interface
hardware_interface::hardware_interface
+ ${sensor_msgs_TARGETS}
)
# Semantic component command interface tests
@@ -96,15 +108,14 @@ if(BUILD_TESTING)
target_link_libraries(test_semantic_component_command_interface
controller_interface
hardware_interface::hardware_interface
+ ${geometry_msgs_TARGETS}
)
ament_add_gmock(test_led_rgb_device test/test_led_rgb_device.cpp)
target_link_libraries(test_led_rgb_device
controller_interface
hardware_interface::hardware_interface
- )
- ament_target_dependencies(test_led_rgb_device
- std_msgs
+ ${std_msgs_TARGETS}
)
endif()
diff --git a/controller_interface/include/controller_interface/controller_interface_base.hpp b/controller_interface/include/controller_interface/controller_interface_base.hpp
index a4d88e7153..94e36d4fc0 100644
--- a/controller_interface/include/controller_interface/controller_interface_base.hpp
+++ b/controller_interface/include/controller_interface/controller_interface_base.hpp
@@ -17,11 +17,13 @@
#include
#include
+#include
#include
#include
#include "realtime_tools/async_function_handler.hpp"
+#include "controller_interface/controller_interface_params.hpp"
#include "hardware_interface/handle.hpp"
#include "hardware_interface/introspection.hpp"
#include "hardware_interface/loaned_command_interface.hpp"
@@ -151,10 +153,15 @@ class ControllerInterfaceBase : public rclcpp_lifecycle::node_interfaces::Lifecy
*/
virtual void release_interfaces();
+ [[deprecated(
+ "Use the init(const controller_interface::ControllerInterfaceParams & params) method instead. "
+ "This method will be removed in the future ROS 2 releases.")]]
return_type init(
const std::string & controller_name, const std::string & urdf, unsigned int cm_update_rate,
const std::string & node_namespace, const rclcpp::NodeOptions & node_options);
+ return_type init(const controller_interface::ControllerInterfaceParams & params);
+
/// Custom configure method to read additional parameters for controller-nodes
/*
* Override default implementation for configure of LifecycleNode to get parameters.
@@ -201,6 +208,17 @@ class ControllerInterfaceBase : public rclcpp_lifecycle::node_interfaces::Lifecy
const std::string & get_robot_description() const;
+ /**
+ * Get the unordered map of joint limits that are defined in the robot description.
+ */
+ const std::unordered_map & get_hard_joint_limits() const;
+
+ /**
+ * Get the unordered map of soft joint limits that are defined in the robot description.
+ */
+ const std::unordered_map & get_soft_joint_limits()
+ const;
+
/**
* Method used by the controller_manager for base NodeOptions to instantiate the Lifecycle node
* of the controller upon loading the controller.
@@ -328,7 +346,25 @@ class ControllerInterfaceBase : public rclcpp_lifecycle::node_interfaces::Lifecy
void enable_introspection(bool enable);
protected:
+ /** Loaned command interfaces.
+ * \note The order of these interfaces is determined by the return value of
+ * \ref command_interface_configuration():
+ * If interface_configuration_type::INDIVIDUAL is specified, the order matches that of the
+ * returned vector.
+ * If interface_configuration_type::ALL is specified, the order is determined by the internal
+ * memory of the resource_manager and may not be deterministic. To obtain a consistent order, use
+ * \ref get_ordered_interfaces() from \ref helpers.hpp.
+ */
std::vector command_interfaces_;
+ /** Loaned state interfaces.
+ * \note The order of these interfaces is determined by the return value of
+ * \ref state_interface_configuration():
+ * If interface_configuration_type::INDIVIDUAL is specified, the order matches that of the
+ * returned vector.
+ * If interface_configuration_type::ALL is specified, the order is determined by the internal
+ * memory of the resource_manager and may not be deterministic. To obtain a consistent order, use
+ * \ref get_ordered_interfaces() from \ref helpers.hpp.
+ */
std::vector state_interfaces_;
private:
@@ -340,9 +376,8 @@ class ControllerInterfaceBase : public rclcpp_lifecycle::node_interfaces::Lifecy
std::shared_ptr node_;
std::unique_ptr> async_handler_;
- unsigned int update_rate_ = 0;
bool is_async_ = false;
- std::string urdf_ = "";
+ controller_interface::ControllerInterfaceParams ctrl_itf_params_;
std::atomic_bool skip_async_triggers_ = false;
ControllerUpdateStats trigger_stats_;
diff --git a/controller_interface/include/controller_interface/controller_interface_params.hpp b/controller_interface/include/controller_interface/controller_interface_params.hpp
new file mode 100644
index 0000000000..eddd29117e
--- /dev/null
+++ b/controller_interface/include/controller_interface/controller_interface_params.hpp
@@ -0,0 +1,60 @@
+// Copyright 2025 ros2_control Development Team
+//
+// 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.
+
+#ifndef CONTROLLER_INTERFACE__CONTROLLER_INTERFACE_PARAMS_HPP_
+#define CONTROLLER_INTERFACE__CONTROLLER_INTERFACE_PARAMS_HPP_
+
+#include
+#include
+
+#include "joint_limits/joint_limits.hpp"
+#include "rclcpp/node_options.hpp"
+
+/**
+ * @brief Parameters for the Controller Interface
+ * This struct holds the parameters required to initialize a controller interface.
+ *
+ * @var controller_name Name of the controller.
+ * @var robot_description The URDF or SDF description of the robot.
+ * @var cm_update_rate The update rate of the controller manager in Hz.
+ * @var node_namespace The namespace for the controller node.
+ * @var node_options Options for the controller node.
+ * @var joint_limits A map of joint names to their limits.
+ * @var soft_joint_limits A map of joint names to their soft limits.
+ *
+ * This struct is used to pass parameters to the controller interface during initialization.
+ * It allows for easy configuration of the controller's behavior and interaction with the robot's
+ * joints.
+ */
+namespace controller_interface
+{
+
+struct ControllerInterfaceParams
+{
+ ControllerInterfaceParams() = default;
+
+ std::string controller_name = "";
+ std::string robot_description = "";
+ unsigned int update_rate = 0;
+
+ std::string node_namespace = "";
+ rclcpp::NodeOptions node_options = rclcpp::NodeOptions();
+
+ std::unordered_map hard_joint_limits = {};
+ std::unordered_map soft_joint_limits = {};
+};
+
+} // namespace controller_interface
+
+#endif // CONTROLLER_INTERFACE__CONTROLLER_INTERFACE_PARAMS_HPP_
diff --git a/controller_interface/include/controller_interface/helpers.hpp b/controller_interface/include/controller_interface/helpers.hpp
index d9ca03eb5b..ffc1fc9b4f 100644
--- a/controller_interface/include/controller_interface/helpers.hpp
+++ b/controller_interface/include/controller_interface/helpers.hpp
@@ -15,7 +15,10 @@
#ifndef CONTROLLER_INTERFACE__HELPERS_HPP_
#define CONTROLLER_INTERFACE__HELPERS_HPP_
+#include
#include
+#include
+#include
#include
#include
@@ -36,7 +39,10 @@ namespace controller_interface
* If joint names are used for ordering, \p interface_type specifies valid interface.
* If full interface names are used for ordering, \p interface_type should be empty string ("").
* \param[in] interface_type used for ordering interfaces with respect to joint names.
- * \param[out] ordered_interfaces vector with ordered interfaces.
+ * \param[out] ordered_interfaces vector with ordered interfaces. Has to have the same capacity as
+ * \p ordered_names size. Throws otherwise.
+ * \throws std::range_error if the capacity of ordered_interfaces is less than the size of
+ * ordered_names.
* \return true if all interfaces or joints in \p ordered_names are found, otherwise false.
*/
template
@@ -44,7 +50,13 @@ bool get_ordered_interfaces(
std::vector & unordered_interfaces, const std::vector & ordered_names,
const std::string & interface_type, std::vector> & ordered_interfaces)
{
- ordered_interfaces.reserve(ordered_names.size());
+ if (ordered_interfaces.capacity() < ordered_names.size())
+ {
+ throw std::range_error(
+ "Capacity of ordered_interfaces (" + std::to_string(ordered_interfaces.capacity()) +
+ ") has to be equal or higher as size of ordered_names (" +
+ std::to_string(ordered_names.size()) + ") for realtime reasons.");
+ }
for (const auto & name : ordered_names)
{
for (auto & interface : unordered_interfaces)
@@ -81,15 +93,6 @@ inline bool interface_list_contains_interface_type(
interface_type_list.end();
}
-template
-[[deprecated(
- "Use ros2_control::add_item method instead. This method will be removed by the ROS 2 Kilted "
- "Kaiju release.")]] void
-add_element_to_list(std::vector & list, const T & element)
-{
- ros2_control::add_item(list, element);
-}
-
} // namespace controller_interface
#endif // CONTROLLER_INTERFACE__HELPERS_HPP_
diff --git a/controller_interface/include/semantic_components/gps_sensor.hpp b/controller_interface/include/semantic_components/gps_sensor.hpp
index 6d70e8be53..38c788db05 100644
--- a/controller_interface/include/semantic_components/gps_sensor.hpp
+++ b/controller_interface/include/semantic_components/gps_sensor.hpp
@@ -52,6 +52,7 @@ class GPSSensor : public SemanticComponentInterface
interface_names_.emplace_back(name + "/" + "latitude_covariance");
interface_names_.emplace_back(name + "/" + "longitude_covariance");
interface_names_.emplace_back(name + "/" + "altitude_covariance");
+ state_interfaces_.reserve(state_interfaces_.capacity() + 3);
}
}
diff --git a/controller_interface/include/semantic_components/magnetic_field_sensor.hpp b/controller_interface/include/semantic_components/magnetic_field_sensor.hpp
new file mode 100644
index 0000000000..7ce424cc3f
--- /dev/null
+++ b/controller_interface/include/semantic_components/magnetic_field_sensor.hpp
@@ -0,0 +1,73 @@
+// Copyright 2025 Aarav Gupta
+//
+// 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.
+
+#ifndef SEMANTIC_COMPONENTS__MAGNETIC_FIELD_SENSOR_HPP_
+#define SEMANTIC_COMPONENTS__MAGNETIC_FIELD_SENSOR_HPP_
+
+#include
+
+#include "semantic_components/semantic_component_interface.hpp"
+#include "sensor_msgs/msg/magnetic_field.hpp"
+
+namespace semantic_components
+{
+class MagneticFieldSensor : public SemanticComponentInterface
+{
+public:
+ explicit MagneticFieldSensor(const std::string & name)
+ : SemanticComponentInterface(
+ name, {name + "/" + "magnetic_field.x", name + "/" + "magnetic_field.y",
+ name + "/" + "magnetic_field.z"})
+ {
+ }
+
+ /// Returns values as sensor_msgs::msg::MagneticField
+ /**
+ * \return MagneticField message from values
+ */
+ bool get_values_as_message(sensor_msgs::msg::MagneticField & message)
+ {
+ update_data_from_interfaces();
+ message.magnetic_field.x = data_[0];
+ message.magnetic_field.y = data_[1];
+ message.magnetic_field.z = data_[2];
+ return true;
+ }
+
+private:
+ /**
+ * @brief Update the data array from the state interfaces.
+ * @note This method is thread-safe and non-blocking.
+ * @note This method might return stale data if the data is not updated. This is to ensure that
+ * the data from the sensor is not discontinuous.
+ */
+ void update_data_from_interfaces()
+ {
+ for (auto i = 0u; i < data_.size(); ++i)
+ {
+ const auto data = state_interfaces_[i].get().get_optional();
+ if (data.has_value())
+ {
+ data_[i] = data.value();
+ }
+ }
+ }
+
+ // Array to store the data of the magnetic field sensor
+ std::array data_{{0.0, 0.0, 0.0}};
+};
+
+} // namespace semantic_components
+
+#endif // SEMANTIC_COMPONENTS__MAGNETIC_FIELD_SENSOR_HPP_
diff --git a/controller_interface/include/semantic_components/semantic_component_interface.hpp b/controller_interface/include/semantic_components/semantic_component_interface.hpp
index 76a34aaddd..ac652aa9fb 100644
--- a/controller_interface/include/semantic_components/semantic_component_interface.hpp
+++ b/controller_interface/include/semantic_components/semantic_component_interface.hpp
@@ -104,6 +104,10 @@ class SemanticComponentInterface
*/
bool get_values_as_message(MessageReturnType & /* message */) { return false; }
+ // delete copy constructor, because
+ // copy will change capacity of member variables
+ SemanticComponentInterface(const SemanticComponentInterface &) = delete;
+
protected:
std::string name_;
std::vector interface_names_;
diff --git a/controller_interface/package.xml b/controller_interface/package.xml
index 028f323c9e..2b0683dce0 100644
--- a/controller_interface/package.xml
+++ b/controller_interface/package.xml
@@ -2,7 +2,7 @@
controller_interface
- 4.29.0
+ 6.0.1
Base classes for controllers and syntax cookies for supporting common sensor types in controllers and broadcasters
Bence Magyar
Denis Štogl
@@ -17,7 +17,7 @@
rclcpp_lifecycle
realtime_tools
ros2_control_cmake
- sensor_msgs
+ fmt
hardware_interface
realtime_tools
@@ -28,6 +28,7 @@
ament_cmake_gmock
geometry_msgs
sensor_msgs
+ std_msgs
ament_cmake
diff --git a/controller_interface/src/chainable_controller_interface.cpp b/controller_interface/src/chainable_controller_interface.cpp
index 703e84b474..d13ec19129 100644
--- a/controller_interface/src/chainable_controller_interface.cpp
+++ b/controller_interface/src/chainable_controller_interface.cpp
@@ -14,6 +14,8 @@
#include "controller_interface/chainable_controller_interface.hpp"
+#include
+
#include
#include "controller_interface/helpers.hpp"
@@ -62,12 +64,12 @@ ChainableControllerInterface::export_state_interfaces()
{
if (interface.get_prefix_name().find(get_node()->get_name()) != 0)
{
- std::string error_msg =
- "The prefix of the interface '" + interface.get_prefix_name() +
- "' should begin with the controller's name '" + get_node()->get_name() +
- "'. This is mandatory for state interfaces. No state interface will be exported. Please "
- "correct and recompile the controller with name '" +
- get_node()->get_name() + "' and try again.";
+ const std::string error_msg = fmt::format(
+ FMT_COMPILE(
+ "The prefix of the interface '{}' should begin with the controller's name '{}'. "
+ "This is mandatory for state interfaces. No state interface will be exported. "
+ "Please correct and recompile the controller with name '{}' and try again."),
+ interface.get_prefix_name(), get_node()->get_name(), get_node()->get_name());
throw std::runtime_error(error_msg);
}
auto state_interface = std::make_shared(interface);
@@ -78,13 +80,13 @@ ChainableControllerInterface::export_state_interfaces()
// inform cm by throwing exception
if (!succ)
{
- std::string error_msg =
- "Could not insert StateInterface<" + interface_name +
- "> into exported_state_interfaces_ map. Check if you export duplicates. The "
- "map returned iterator with interface_name<" +
- it->second->get_name() +
- ">. If its a duplicate adjust exportation of InterfacesDescription so that all the "
- "interface names are unique.";
+ std::string error_msg = fmt::format(
+ FMT_COMPILE(
+ "Could not insert StateInterface<{}> into exported_state_interfaces_ map. "
+ "Check if you export duplicates. The map returned iterator with interface_name<{}>. "
+ "If its a duplicate adjust exportation of InterfacesDescription so that all the "
+ "interface names are unique."),
+ interface_name, it->second->get_name());
exported_state_interfaces_.clear();
exported_state_interface_names_.clear();
state_interfaces_ptrs_vec.clear();
@@ -98,14 +100,13 @@ ChainableControllerInterface::export_state_interfaces()
if (exported_state_interfaces_.size() != state_interfaces.size())
{
- std::string error_msg =
- "The internal storage for state interface ptrs 'exported_state_interfaces_' variable has "
- "size '" +
- std::to_string(exported_state_interfaces_.size()) +
- "', but it is expected to have the size '" + std::to_string(state_interfaces.size()) +
- "' equal to the number of exported reference interfaces. Please correct and recompile the "
- "controller with name '" +
- get_node()->get_name() + "' and try again.";
+ std::string error_msg = fmt::format(
+ FMT_COMPILE(
+ "The internal storage for state interface ptrs 'exported_state_interfaces_' variable has "
+ "size '{}', but it is expected to have the size '{}' equal to the number of exported "
+ "reference interfaces. Please correct and recompile the controller with name '{}' and try "
+ "again."),
+ exported_state_interfaces_.size(), state_interfaces.size(), get_node()->get_name());
throw std::runtime_error(error_msg);
}
@@ -128,13 +129,12 @@ ChainableControllerInterface::export_reference_interfaces()
// check if the "reference_interfaces_" variable is resized to number of interfaces
if (reference_interfaces_.size() != reference_interfaces.size())
{
- std::string error_msg =
- "The internal storage for reference values 'reference_interfaces_' variable has size '" +
- std::to_string(reference_interfaces_.size()) + "', but it is expected to have the size '" +
- std::to_string(reference_interfaces.size()) +
- "' equal to the number of exported reference interfaces. Please correct and recompile the "
- "controller with name '" +
- get_node()->get_name() + "' and try again.";
+ std::string error_msg = fmt::format(
+ FMT_COMPILE(
+ "The internal storage for reference values 'reference_interfaces_' variable has size '{}', "
+ "but it is expected to have the size '{}' equal to the number of exported reference "
+ "interfaces. Please correct and recompile the controller with name '{}' and try again."),
+ reference_interfaces_.size(), reference_interfaces.size(), get_node()->get_name());
throw std::runtime_error(error_msg);
}
// END
@@ -145,12 +145,12 @@ ChainableControllerInterface::export_reference_interfaces()
{
if (interface.get_prefix_name().find(get_node()->get_name()) != 0)
{
- std::string error_msg = "The prefix of the interface '" + interface.get_prefix_name() +
- "' should begin with the controller's name '" +
- get_node()->get_name() +
- "'. This is mandatory for reference interfaces. Please correct and "
- "recompile the controller with name '" +
- get_node()->get_name() + "' and try again.";
+ std::string error_msg = fmt::format(
+ FMT_COMPILE(
+ "The prefix of the interface '{}' should begin with the controller's name '{}'. "
+ "This is mandatory for reference interfaces. Please correct and recompile the "
+ "controller with name '{}' and try again."),
+ interface.get_prefix_name(), get_node()->get_name(), get_node()->get_name());
throw std::runtime_error(error_msg);
}
@@ -164,13 +164,13 @@ ChainableControllerInterface::export_reference_interfaces()
// inform cm by throwing exception
if (!succ)
{
- std::string error_msg =
- "Could not insert Reference interface<" + interface_name +
- "> into reference_interfaces_ map. Check if you export duplicates. The "
- "map returned iterator with interface_name<" +
- it->second->get_name() +
- ">. If its a duplicate adjust exportation of InterfacesDescription so that all the "
- "interface names are unique.";
+ std::string error_msg = fmt::format(
+ FMT_COMPILE(
+ "Could not insert Reference interface<{}> into reference_interfaces_ map. "
+ "Check if you export duplicates. The map returned iterator with interface_name<{}>. "
+ "If its a duplicate adjust exportation of InterfacesDescription so that all the "
+ "interface names are unique."),
+ interface_name, it->second->get_name());
reference_interfaces_.clear();
exported_reference_interface_names_.clear();
reference_interfaces_ptrs_vec.clear();
@@ -183,14 +183,13 @@ ChainableControllerInterface::export_reference_interfaces()
if (exported_reference_interfaces_.size() != ref_interface_size)
{
- std::string error_msg =
- "The internal storage for exported reference ptrs 'exported_reference_interfaces_' variable "
- "has size '" +
- std::to_string(exported_reference_interfaces_.size()) +
- "', but it is expected to have the size '" + std::to_string(ref_interface_size) +
- "' equal to the number of exported reference interfaces. Please correct and recompile the "
- "controller with name '" +
- get_node()->get_name() + "' and try again.";
+ std::string error_msg = fmt::format(
+ FMT_COMPILE(
+ "The internal storage for exported reference ptrs 'exported_reference_interfaces_' "
+ "variable has size '{}', but it is expected to have the size '{}' equal to the number of "
+ "exported reference interfaces. Please correct and recompile the controller with name '{}' "
+ "and try again."),
+ exported_reference_interfaces_.size(), ref_interface_size, get_node()->get_name());
throw std::runtime_error(error_msg);
}
diff --git a/controller_interface/src/controller_interface_base.cpp b/controller_interface/src/controller_interface_base.cpp
index 532544ab30..50bef315f1 100644
--- a/controller_interface/src/controller_interface_base.cpp
+++ b/controller_interface/src/controller_interface_base.cpp
@@ -43,19 +43,32 @@ return_type ControllerInterfaceBase::init(
const std::string & controller_name, const std::string & urdf, unsigned int cm_update_rate,
const std::string & node_namespace, const rclcpp::NodeOptions & node_options)
{
- urdf_ = urdf;
- update_rate_ = cm_update_rate;
+ controller_interface::ControllerInterfaceParams params;
+ params.controller_name = controller_name;
+ params.robot_description = urdf;
+ params.update_rate = cm_update_rate;
+ params.node_namespace = node_namespace;
+ params.node_options = node_options;
+
+ return init(params);
+}
+
+return_type ControllerInterfaceBase::init(
+ const controller_interface::ControllerInterfaceParams & params)
+{
+ ctrl_itf_params_ = params;
node_ = std::make_shared(
- controller_name, node_namespace, node_options,
+ ctrl_itf_params_.controller_name, ctrl_itf_params_.node_namespace,
+ ctrl_itf_params_.node_options,
false); // disable LifecycleNode service interfaces
try
{
// no rclcpp::ParameterValue unsigned int specialization
- auto_declare("update_rate", static_cast(update_rate_));
+ auto_declare("update_rate", static_cast(ctrl_itf_params_.update_rate));
auto_declare("is_async", false);
- auto_declare("thread_priority", 50);
+ auto_declare("thread_priority", -100);
}
catch (const std::exception & e)
{
@@ -149,32 +162,51 @@ const rclcpp_lifecycle::State & ControllerInterfaceBase::configure()
RCLCPP_ERROR(get_node()->get_logger(), "Update rate cannot be a negative value!");
return get_lifecycle_state();
}
- if (update_rate_ != 0u && update_rate > update_rate_)
+ if (ctrl_itf_params_.update_rate != 0u && update_rate > ctrl_itf_params_.update_rate)
{
RCLCPP_WARN(
get_node()->get_logger(),
"The update rate of the controller : '%ld Hz' cannot be higher than the update rate of the "
"controller manager : '%d Hz'. Setting it to the update rate of the controller manager.",
- update_rate, update_rate_);
+ update_rate, ctrl_itf_params_.update_rate);
}
else
{
- update_rate_ = static_cast(update_rate);
+ ctrl_itf_params_.update_rate = static_cast(update_rate);
}
is_async_ = get_node()->get_parameter("is_async").as_bool();
}
if (is_async_)
{
- const int thread_priority =
+ realtime_tools::AsyncFunctionHandlerParams async_params;
+ async_params.thread_priority = 50; // default value
+ const int thread_priority_param =
static_cast(get_node()->get_parameter("thread_priority").as_int());
+ if (thread_priority_param >= 0 && thread_priority_param <= 99)
+ {
+ async_params.thread_priority = thread_priority_param;
+ RCLCPP_WARN(
+ get_node()->get_logger(),
+ "The parsed 'thread_priority' parameter will be deprecated and not be functional from "
+ "ROS 2 Lyrical Luth release. Please use the 'async_parameters.thread_priority' parameter "
+ "instead.");
+ }
+ async_params.initialize(node_, "async_parameters.");
+ if (async_params.scheduling_policy == realtime_tools::AsyncSchedulingPolicy::DETACHED)
+ {
+ RCLCPP_ERROR(
+ get_node()->get_logger(),
+ "The controllers are not supported to run asynchronously in detached mode!");
+ return get_node()->get_current_state();
+ }
RCLCPP_INFO(
get_node()->get_logger(), "Starting async handler with scheduler priority: %d",
- thread_priority);
+ async_params.thread_priority);
async_handler_ = std::make_unique>();
async_handler_->init(
std::bind(
&ControllerInterfaceBase::update, this, std::placeholders::_1, std::placeholders::_2),
- thread_priority);
+ async_params);
async_handler_->start_thread();
}
REGISTER_ROS2_CONTROL_INTROSPECTION("total_triggers", &trigger_stats_.total_triggers);
@@ -273,11 +305,29 @@ std::shared_ptr ControllerInterfaceBase::
return node_;
}
-unsigned int ControllerInterfaceBase::get_update_rate() const { return update_rate_; }
+unsigned int ControllerInterfaceBase::get_update_rate() const
+{
+ return ctrl_itf_params_.update_rate;
+}
bool ControllerInterfaceBase::is_async() const { return is_async_; }
-const std::string & ControllerInterfaceBase::get_robot_description() const { return urdf_; }
+const std::string & ControllerInterfaceBase::get_robot_description() const
+{
+ return ctrl_itf_params_.robot_description;
+}
+
+const std::unordered_map &
+ControllerInterfaceBase::get_hard_joint_limits() const
+{
+ return ctrl_itf_params_.hard_joint_limits;
+}
+
+const std::unordered_map &
+ControllerInterfaceBase::get_soft_joint_limits() const
+{
+ return ctrl_itf_params_.soft_joint_limits;
+}
void ControllerInterfaceBase::wait_for_trigger_update_to_finish()
{
diff --git a/controller_interface/test/test_chainable_controller_interface.cpp b/controller_interface/test/test_chainable_controller_interface.cpp
index b0b82f05f0..3f0b564c7d 100644
--- a/controller_interface/test/test_chainable_controller_interface.cpp
+++ b/controller_interface/test/test_chainable_controller_interface.cpp
@@ -26,10 +26,13 @@ TEST_F(ChainableControllerInterfaceTest, default_returns)
TestableChainableControllerInterface controller;
// initialize, create node
- const auto node_options = controller.define_custom_node_options();
- ASSERT_EQ(
- controller.init(TEST_CONTROLLER_NAME, "", 50.0, "", node_options),
- controller_interface::return_type::OK);
+ controller_interface::ControllerInterfaceParams params;
+ params.controller_name = TEST_CONTROLLER_NAME;
+ params.robot_description = "";
+ params.update_rate = 50;
+ params.node_namespace = "";
+ params.node_options = controller.define_custom_node_options();
+ ASSERT_EQ(controller.init(params), controller_interface::return_type::OK);
ASSERT_NO_THROW(controller.get_node());
EXPECT_TRUE(controller.is_chainable());
@@ -41,10 +44,13 @@ TEST_F(ChainableControllerInterfaceTest, export_state_interfaces)
TestableChainableControllerInterface controller;
// initialize, create node
- const auto node_options = controller.define_custom_node_options();
- ASSERT_EQ(
- controller.init(TEST_CONTROLLER_NAME, "", 50.0, "", node_options),
- controller_interface::return_type::OK);
+ controller_interface::ControllerInterfaceParams params;
+ params.controller_name = TEST_CONTROLLER_NAME;
+ params.robot_description = "";
+ params.update_rate = 50;
+ params.node_namespace = "";
+ params.node_options = controller.define_custom_node_options();
+ ASSERT_EQ(controller.init(params), controller_interface::return_type::OK);
ASSERT_NO_THROW(controller.get_node());
auto exported_state_interfaces = controller.export_state_interfaces();
@@ -68,10 +74,13 @@ TEST_F(ChainableControllerInterfaceTest, export_reference_interfaces)
TestableChainableControllerInterface controller;
// initialize, create node
- const auto node_options = controller.define_custom_node_options();
- ASSERT_EQ(
- controller.init(TEST_CONTROLLER_NAME, "", 50.0, "", node_options),
- controller_interface::return_type::OK);
+ controller_interface::ControllerInterfaceParams params;
+ params.controller_name = TEST_CONTROLLER_NAME;
+ params.robot_description = "";
+ params.update_rate = 50;
+ params.node_namespace = "";
+ params.node_options = controller.define_custom_node_options();
+ ASSERT_EQ(controller.init(params), controller_interface::return_type::OK);
ASSERT_NO_THROW(controller.get_node());
auto reference_interfaces = controller.export_reference_interfaces();
@@ -95,10 +104,13 @@ TEST_F(ChainableControllerInterfaceTest, interfaces_prefix_is_not_node_name)
TestableChainableControllerInterface controller;
// initialize, create node
- const auto node_options = controller.define_custom_node_options();
- ASSERT_EQ(
- controller.init(TEST_CONTROLLER_NAME, "", 50.0, "", node_options),
- controller_interface::return_type::OK);
+ controller_interface::ControllerInterfaceParams params;
+ params.controller_name = TEST_CONTROLLER_NAME;
+ params.robot_description = "";
+ params.update_rate = 50;
+ params.node_namespace = "";
+ params.node_options = controller.define_custom_node_options();
+ ASSERT_EQ(controller.init(params), controller_interface::return_type::OK);
ASSERT_NO_THROW(controller.get_node());
controller.set_name_prefix_of_reference_interfaces("some_not_correct_interface_prefix");
@@ -121,10 +133,13 @@ TEST_F(ChainableControllerInterfaceTest, setting_chained_mode)
TestableChainableControllerInterface controller;
// initialize, create node
- const auto node_options = controller.define_custom_node_options();
- ASSERT_EQ(
- controller.init(TEST_CONTROLLER_NAME, "", 50.0, "", node_options),
- controller_interface::return_type::OK);
+ controller_interface::ControllerInterfaceParams params;
+ params.controller_name = TEST_CONTROLLER_NAME;
+ params.robot_description = "";
+ params.update_rate = 50;
+ params.node_namespace = "";
+ params.node_options = controller.define_custom_node_options();
+ ASSERT_EQ(controller.init(params), controller_interface::return_type::OK);
ASSERT_NO_THROW(controller.get_node());
auto reference_interfaces = controller.export_reference_interfaces();
@@ -176,10 +191,13 @@ TEST_F(ChainableControllerInterfaceTest, test_update_logic)
TestableChainableControllerInterface controller;
// initialize, create node
- const auto node_options = controller.define_custom_node_options();
- ASSERT_EQ(
- controller.init(TEST_CONTROLLER_NAME, "", 50.0, "", node_options),
- controller_interface::return_type::OK);
+ controller_interface::ControllerInterfaceParams params;
+ params.controller_name = TEST_CONTROLLER_NAME;
+ params.robot_description = "";
+ params.update_rate = 50;
+ params.node_namespace = "";
+ params.node_options = controller.define_custom_node_options();
+ ASSERT_EQ(controller.init(params), controller_interface::return_type::OK);
ASSERT_NO_THROW(controller.get_node());
EXPECT_FALSE(controller.set_chained_mode(false));
diff --git a/controller_interface/test/test_controller_interface.cpp b/controller_interface/test/test_controller_interface.cpp
index 286e132003..11b782eb65 100644
--- a/controller_interface/test/test_controller_interface.cpp
+++ b/controller_interface/test/test_controller_interface.cpp
@@ -44,10 +44,13 @@ TEST(TestableControllerInterface, init)
ASSERT_THROW(controller.get_lifecycle_state(), std::runtime_error);
// initialize, create node
- const auto node_options = controller.define_custom_node_options();
- ASSERT_EQ(
- controller.init(TEST_CONTROLLER_NAME, "", 10.0, "", node_options),
- controller_interface::return_type::OK);
+ controller_interface::ControllerInterfaceParams params;
+ params.controller_name = TEST_CONTROLLER_NAME;
+ params.robot_description = "";
+ params.update_rate = 10;
+ params.node_namespace = "";
+ params.node_options = controller.define_custom_node_options();
+ ASSERT_EQ(controller.init(params), controller_interface::return_type::OK);
ASSERT_NO_THROW(controller.get_node());
ASSERT_NO_THROW(const_controller.get_node());
ASSERT_NO_THROW(controller.get_lifecycle_state());
@@ -76,9 +79,13 @@ TEST(TestableControllerInterface, setting_negative_update_rate_in_configure)
node_options_arguments.push_back("-p");
node_options_arguments.push_back("update_rate:=-100");
node_options = node_options.arguments(node_options_arguments);
- ASSERT_EQ(
- controller.init(TEST_CONTROLLER_NAME, "", 1000.0, "", node_options),
- controller_interface::return_type::OK);
+ controller_interface::ControllerInterfaceParams params;
+ params.controller_name = TEST_CONTROLLER_NAME;
+ params.robot_description = "";
+ params.update_rate = 1000;
+ params.node_namespace = "";
+ params.node_options = node_options;
+ ASSERT_EQ(controller.init(params), controller_interface::return_type::OK);
// update_rate is set to controller_manager's rate
ASSERT_EQ(controller.get_update_rate(), 1000u);
@@ -104,9 +111,21 @@ TEST(TestableControllerInterface, setting_update_rate_in_configure)
node_options_arguments.push_back("-p");
node_options_arguments.push_back("update_rate:=2812");
node_options = node_options.arguments(node_options_arguments);
- ASSERT_EQ(
- controller.init(TEST_CONTROLLER_NAME, "", 5000.0, "", node_options),
- controller_interface::return_type::OK);
+ controller_interface::ControllerInterfaceParams params;
+ params.controller_name = TEST_CONTROLLER_NAME;
+ params.robot_description = "";
+ params.update_rate = 5000; // set a different update rate than the one in the node options
+ params.node_namespace = "";
+ params.node_options = node_options;
+ joint_limits::JointLimits joint_limits;
+ joint_limits.has_velocity_limits = true;
+ joint_limits.max_velocity = 1.0;
+ params.hard_joint_limits["joint1"] = joint_limits;
+ joint_limits::SoftJointLimits soft_joint_limits;
+ soft_joint_limits.min_position = -1.0;
+ soft_joint_limits.max_position = 1.0;
+ params.soft_joint_limits["joint1"] = soft_joint_limits;
+ ASSERT_EQ(controller.init(params), controller_interface::return_type::OK);
// initialize executor to be able to get parameter update
auto executor =
@@ -122,6 +141,21 @@ TEST(TestableControllerInterface, setting_update_rate_in_configure)
// update_rate is set to controller_manager's rate
ASSERT_EQ(controller.get_update_rate(), 5000u);
+ const auto hard_limits = controller.get_hard_joint_limits();
+ const auto soft_limits = controller.get_soft_joint_limits();
+ ASSERT_THAT(hard_limits, testing::SizeIs(1));
+ ASSERT_TRUE(hard_limits.find("joint1") != hard_limits.end());
+ ASSERT_THAT(hard_limits.at("joint1").max_velocity, joint_limits.max_velocity);
+ ASSERT_TRUE(hard_limits.at("joint1").has_velocity_limits);
+ ASSERT_FALSE(hard_limits.at("joint1").has_position_limits);
+ ASSERT_FALSE(hard_limits.at("joint1").has_acceleration_limits);
+ ASSERT_FALSE(hard_limits.at("joint1").has_deceleration_limits);
+ ASSERT_FALSE(hard_limits.at("joint1").has_jerk_limits);
+ ASSERT_FALSE(hard_limits.at("joint1").has_effort_limits);
+ ASSERT_THAT(soft_limits, testing::SizeIs(1));
+ ASSERT_TRUE(soft_limits.find("joint1") != soft_limits.end());
+ ASSERT_THAT(soft_limits.at("joint1").min_position, soft_joint_limits.min_position);
+ ASSERT_THAT(soft_limits.at("joint1").max_position, soft_joint_limits.max_position);
// Even after configure is 0
controller.configure();
@@ -156,6 +190,23 @@ TEST(TestableControllerInterface, setting_update_rate_in_configure)
controller.configure();
ASSERT_EQ(controller.get_update_rate(), 623u);
+ // Should stay same after multiple cleanups as it is set during initialization
+ const auto hard_limits_final = controller.get_hard_joint_limits();
+ const auto soft_limits_final = controller.get_soft_joint_limits();
+ ASSERT_THAT(hard_limits_final, testing::SizeIs(1));
+ ASSERT_TRUE(hard_limits_final.find("joint1") != hard_limits_final.end());
+ ASSERT_THAT(hard_limits_final.at("joint1").max_velocity, joint_limits.max_velocity);
+ ASSERT_TRUE(hard_limits_final.at("joint1").has_velocity_limits);
+ ASSERT_FALSE(hard_limits_final.at("joint1").has_position_limits);
+ ASSERT_FALSE(hard_limits_final.at("joint1").has_acceleration_limits);
+ ASSERT_FALSE(hard_limits_final.at("joint1").has_deceleration_limits);
+ ASSERT_FALSE(hard_limits_final.at("joint1").has_jerk_limits);
+ ASSERT_FALSE(hard_limits_final.at("joint1").has_effort_limits);
+ ASSERT_THAT(soft_limits_final, testing::SizeIs(1));
+ ASSERT_TRUE(soft_limits_final.find("joint1") != soft_limits_final.end());
+ ASSERT_THAT(soft_limits_final.at("joint1").min_position, soft_joint_limits.min_position);
+ ASSERT_THAT(soft_limits_final.at("joint1").max_position, soft_joint_limits.max_position);
+
executor->cancel();
controller.get_node()->shutdown();
rclcpp::shutdown();
@@ -170,10 +221,13 @@ TEST(TestableControllerInterfaceInitError, init_with_error)
TestableControllerInterfaceInitError controller;
// initialize, create node
- const auto node_options = controller.define_custom_node_options();
- ASSERT_EQ(
- controller.init(TEST_CONTROLLER_NAME, "", 100.0, "", node_options),
- controller_interface::return_type::ERROR);
+ controller_interface::ControllerInterfaceParams params;
+ params.controller_name = TEST_CONTROLLER_NAME;
+ params.robot_description = "";
+ params.update_rate = 100;
+ params.node_namespace = "";
+ params.node_options = controller.define_custom_node_options();
+ ASSERT_EQ(controller.init(params), controller_interface::return_type::ERROR);
ASSERT_EQ(
controller.get_lifecycle_state().id(), lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED);
@@ -189,10 +243,13 @@ TEST(TestableControllerInterfaceInitFailure, init_with_failure)
TestableControllerInterfaceInitFailure controller;
// initialize, create node
- const auto node_options = controller.define_custom_node_options();
- ASSERT_EQ(
- controller.init(TEST_CONTROLLER_NAME, "", 50.0, "", node_options),
- controller_interface::return_type::ERROR);
+ controller_interface::ControllerInterfaceParams params;
+ params.controller_name = TEST_CONTROLLER_NAME;
+ params.robot_description = "";
+ params.update_rate = 50;
+ params.node_namespace = "";
+ params.node_options = controller.define_custom_node_options();
+ ASSERT_EQ(controller.init(params), controller_interface::return_type::ERROR);
ASSERT_EQ(
controller.get_lifecycle_state().id(), lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED);
diff --git a/controller_interface/test/test_controller_with_options.cpp b/controller_interface/test/test_controller_with_options.cpp
index 8efedd757b..6330532434 100644
--- a/controller_interface/test/test_controller_with_options.cpp
+++ b/controller_interface/test/test_controller_with_options.cpp
@@ -41,9 +41,13 @@ TEST(ControllerWithOption, init_with_overrides)
rclcpp::init(argc, argv);
// creates the controller
FriendControllerWithOptions controller;
- ASSERT_EQ(
- controller.init("controller_name", "", 50.0, "", controller.define_custom_node_options()),
- controller_interface::return_type::OK);
+ controller_interface::ControllerInterfaceParams params;
+ params.controller_name = "controller_name";
+ params.robot_description = "";
+ params.update_rate = 50;
+ params.node_namespace = "";
+ params.node_options = controller.define_custom_node_options();
+ ASSERT_EQ(controller.init(params), controller_interface::return_type::OK);
// checks that the node options have been updated
const auto & node_options = controller.get_node()->get_node_options();
EXPECT_TRUE(node_options.allow_undeclared_parameters());
@@ -70,9 +74,13 @@ TEST(ControllerWithOption, init_with_node_options_arguments_parameters)
controller_node_options.arguments(
{"--ros-args", "-p", "parameter_list.parameter1:=1.", "-p", "parameter_list.parameter2:=2.",
"-p", "parameter_list.parameter3:=3."});
- ASSERT_EQ(
- controller.init("controller_name", "", 50.0, "", controller_node_options),
- controller_interface::return_type::OK);
+ controller_interface::ControllerInterfaceParams params;
+ params.controller_name = "controller_name";
+ params.robot_description = "";
+ params.update_rate = 50;
+ params.node_namespace = "";
+ params.node_options = controller_node_options;
+ ASSERT_EQ(controller.init(params), controller_interface::return_type::OK);
// checks that the node options have been updated
const auto & node_options = controller.get_node()->get_node_options();
EXPECT_TRUE(node_options.allow_undeclared_parameters());
@@ -100,9 +108,13 @@ TEST(ControllerWithOption, init_with_node_options_arguments_parameters_file)
std::cerr << params_file_path << std::endl;
auto controller_node_options = controller.define_custom_node_options();
controller_node_options.arguments({"--ros-args", "--params-file", params_file_path});
- ASSERT_EQ(
- controller.init("controller_name", "", 50.0, "", controller_node_options),
- controller_interface::return_type::OK);
+ controller_interface::ControllerInterfaceParams params;
+ params.controller_name = "controller_name";
+ params.robot_description = "";
+ params.update_rate = 50;
+ params.node_namespace = "";
+ params.node_options = controller_node_options;
+ ASSERT_EQ(controller.init(params), controller_interface::return_type::OK);
// checks that the node options have been updated
const auto & node_options = controller.get_node()->get_node_options();
EXPECT_TRUE(node_options.allow_undeclared_parameters());
@@ -136,9 +148,13 @@ TEST(
controller_node_options.arguments(
{"--ros-args", "--params-file", params_file_path, "-p", "parameter_list.parameter1:=562.785",
"-p", "use_sim_time:=true"});
- ASSERT_EQ(
- controller.init("controller_name", "", 50.0, "", controller_node_options),
- controller_interface::return_type::OK);
+ controller_interface::ControllerInterfaceParams params;
+ params.controller_name = "controller_name";
+ params.robot_description = "";
+ params.update_rate = 50;
+ params.node_namespace = "";
+ params.node_options = controller_node_options;
+ ASSERT_EQ(controller.init(params), controller_interface::return_type::OK);
// checks that the node options have been updated
const auto & node_options = controller.get_node()->get_node_options();
EXPECT_TRUE(node_options.allow_undeclared_parameters());
@@ -165,9 +181,13 @@ TEST(ControllerWithOption, init_without_overrides)
rclcpp::init(argc, argv);
// creates the controller
FriendControllerWithOptions controller;
- ASSERT_EQ(
- controller.init("controller_name", "", 50.0, "", controller.define_custom_node_options()),
- controller_interface::return_type::ERROR);
+ controller_interface::ControllerInterfaceParams params;
+ params.controller_name = "controller_name";
+ params.robot_description = "";
+ params.update_rate = 50;
+ params.node_namespace = "";
+ params.node_options = controller.define_custom_node_options();
+ ASSERT_EQ(controller.init(params), controller_interface::return_type::ERROR);
// checks that the node options have been updated
const auto & node_options = controller.get_node()->get_node_options();
EXPECT_TRUE(node_options.allow_undeclared_parameters());
diff --git a/controller_interface/test/test_force_torque_sensor.cpp b/controller_interface/test/test_force_torque_sensor.cpp
index f1087a0758..8f8405054e 100644
--- a/controller_interface/test/test_force_torque_sensor.cpp
+++ b/controller_interface/test/test_force_torque_sensor.cpp
@@ -48,20 +48,20 @@ TEST_F(ForceTorqueSensorTest, validate_all_with_default_names)
std::vector interface_names = force_torque_sensor_->get_state_interface_names();
// assign values to force
- hardware_interface::StateInterface force_x{
- sensor_name_, fts_interface_names_[0], &force_values_[0]};
- hardware_interface::StateInterface force_y{
- sensor_name_, fts_interface_names_[1], &force_values_[1]};
- hardware_interface::StateInterface force_z{
- sensor_name_, fts_interface_names_[2], &force_values_[2]};
+ auto force_x = std::make_shared(
+ sensor_name_, fts_interface_names_[0], &force_values_[0]);
+ auto force_y = std::make_shared(
+ sensor_name_, fts_interface_names_[1], &force_values_[1]);
+ auto force_z = std::make_shared(
+ sensor_name_, fts_interface_names_[2], &force_values_[2]);
// assign values to torque
- hardware_interface::StateInterface torque_x{
- sensor_name_, fts_interface_names_[3], &torque_values_[0]};
- hardware_interface::StateInterface torque_y{
- sensor_name_, fts_interface_names_[4], &torque_values_[1]};
- hardware_interface::StateInterface torque_z{
- sensor_name_, fts_interface_names_[5], &torque_values_[2]};
+ auto torque_x = std::make_shared(
+ sensor_name_, fts_interface_names_[3], &torque_values_[0]);
+ auto torque_y = std::make_shared(
+ sensor_name_, fts_interface_names_[4], &torque_values_[1]);
+ auto torque_z = std::make_shared(
+ sensor_name_, fts_interface_names_[5], &torque_values_[2]);
// create local state interface vector
std::vector temp_state_interfaces;
@@ -132,16 +132,16 @@ TEST_F(ForceTorqueSensorTest, validate_all_with_custom_names)
std::vector interface_names = force_torque_sensor_->get_state_interface_names();
// assign values to force.x and force.z
- hardware_interface::StateInterface force_x{
- sensor_name_, fts_interface_names_[0], &force_values_[0]};
- hardware_interface::StateInterface force_z{
- sensor_name_, fts_interface_names_[2], &force_values_[2]};
+ auto force_x = std::make_shared(
+ sensor_name_, fts_interface_names_[0], &force_values_[0]);
+ auto force_z = std::make_shared(
+ sensor_name_, fts_interface_names_[2], &force_values_[2]);
// assign values to torque.y and torque.z
- hardware_interface::StateInterface torque_y{
- sensor_name_, fts_interface_names_[4], &torque_values_[1]};
- hardware_interface::StateInterface torque_z{
- sensor_name_, fts_interface_names_[5], &torque_values_[2]};
+ auto torque_y = std::make_shared(
+ sensor_name_, fts_interface_names_[4], &torque_values_[1]);
+ auto torque_z = std::make_shared(
+ sensor_name_, fts_interface_names_[5], &torque_values_[2]);
// create local state interface vector
std::vector temp_state_interfaces;
@@ -214,20 +214,20 @@ TEST_F(ForceTorqueSensorTest, validate_all_custom_names)
ASSERT_EQ(force_torque_sensor_->interface_names_[5], sensor_name_ + "/" + "torque.z");
// assign values to force
- hardware_interface::StateInterface force_x{
- sensor_name_, fts_interface_names_[0], &force_values_[0]};
- hardware_interface::StateInterface force_y{
- sensor_name_, fts_interface_names_[1], &force_values_[1]};
- hardware_interface::StateInterface force_z{
- sensor_name_, fts_interface_names_[2], &force_values_[2]};
+ auto force_x = std::make_shared(
+ sensor_name_, fts_interface_names_[0], &force_values_[0]);
+ auto force_y = std::make_shared(
+ sensor_name_, fts_interface_names_[1], &force_values_[1]);
+ auto force_z = std::make_shared(
+ sensor_name_, fts_interface_names_[2], &force_values_[2]);
// assign values to torque
- hardware_interface::StateInterface torque_x{
- sensor_name_, fts_interface_names_[3], &torque_values_[0]};
- hardware_interface::StateInterface torque_y{
- sensor_name_, fts_interface_names_[4], &torque_values_[1]};
- hardware_interface::StateInterface torque_z{
- sensor_name_, fts_interface_names_[5], &torque_values_[2]};
+ auto torque_x = std::make_shared(
+ sensor_name_, fts_interface_names_[3], &torque_values_[0]);
+ auto torque_y = std::make_shared(
+ sensor_name_, fts_interface_names_[4], &torque_values_[1]);
+ auto torque_z = std::make_shared(
+ sensor_name_, fts_interface_names_[5], &torque_values_[2]);
// create local state interface vector
std::vector temp_state_interfaces;
diff --git a/controller_interface/test/test_gps_sensor.cpp b/controller_interface/test/test_gps_sensor.cpp
index 5358692e3b..e2cd748127 100644
--- a/controller_interface/test/test_gps_sensor.cpp
+++ b/controller_interface/test/test_gps_sensor.cpp
@@ -46,16 +46,21 @@ struct GPSSensorTest : public testing::Test
gps_sensor_name};
std::vector full_interface_names;
- hardware_interface::StateInterface gps_state{
- gps_sensor_name, gps_interface_names.at(0), &gps_states.at(0)};
- hardware_interface::StateInterface gps_service{
- gps_sensor_name, gps_interface_names.at(1), &gps_states.at(1)};
- hardware_interface::StateInterface latitude{
- gps_sensor_name, gps_interface_names.at(2), &gps_states.at(2)};
- hardware_interface::StateInterface longitude{
- gps_sensor_name, gps_interface_names.at(3), &gps_states.at(3)};
- hardware_interface::StateInterface altitude{
- gps_sensor_name, gps_interface_names.at(4), &gps_states.at(4)};
+ hardware_interface::StateInterface::SharedPtr gps_state =
+ std::make_shared(
+ gps_sensor_name, gps_interface_names.at(0), &gps_states.at(0));
+ hardware_interface::StateInterface::SharedPtr gps_service =
+ std::make_shared(
+ gps_sensor_name, gps_interface_names.at(1), &gps_states.at(1));
+ hardware_interface::StateInterface::SharedPtr latitude =
+ std::make_shared(
+ gps_sensor_name, gps_interface_names.at(2), &gps_states.at(2));
+ hardware_interface::StateInterface::SharedPtr longitude =
+ std::make_shared(
+ gps_sensor_name, gps_interface_names.at(3), &gps_states.at(3));
+ hardware_interface::StateInterface::SharedPtr altitude =
+ std::make_shared(
+ gps_sensor_name, gps_interface_names.at(4), &gps_states.at(4));
std::vector state_interface;
};
@@ -144,22 +149,30 @@ struct GPSSensorWithCovarianceTest : public testing::Test
gps_sensor_name};
std::vector full_interface_names;
- hardware_interface::StateInterface gps_state{
- gps_sensor_name, gps_interface_names.at(0), &gps_states.at(0)};
- hardware_interface::StateInterface gps_service{
- gps_sensor_name, gps_interface_names.at(1), &gps_states.at(1)};
- hardware_interface::StateInterface latitude{
- gps_sensor_name, gps_interface_names.at(2), &gps_states.at(2)};
- hardware_interface::StateInterface longitude{
- gps_sensor_name, gps_interface_names.at(3), &gps_states.at(3)};
- hardware_interface::StateInterface altitude{
- gps_sensor_name, gps_interface_names.at(4), &gps_states.at(4)};
- hardware_interface::StateInterface latitude_covariance{
- gps_sensor_name, gps_interface_names.at(5), &gps_states.at(5)};
- hardware_interface::StateInterface longitude_covariance{
- gps_sensor_name, gps_interface_names.at(6), &gps_states.at(6)};
- hardware_interface::StateInterface altitude_covariance{
- gps_sensor_name, gps_interface_names.at(7), &gps_states.at(7)};
+ hardware_interface::StateInterface::SharedPtr gps_state =
+ std::make_shared(
+ gps_sensor_name, gps_interface_names.at(0), &gps_states.at(0));
+ hardware_interface::StateInterface::SharedPtr gps_service =
+ std::make_shared(
+ gps_sensor_name, gps_interface_names.at(1), &gps_states.at(1));
+ hardware_interface::StateInterface::SharedPtr latitude =
+ std::make_shared(
+ gps_sensor_name, gps_interface_names.at(2), &gps_states.at(2));
+ hardware_interface::StateInterface::SharedPtr longitude =
+ std::make_shared(
+ gps_sensor_name, gps_interface_names.at(3), &gps_states.at(3));
+ hardware_interface::StateInterface::SharedPtr altitude =
+ std::make_shared(
+ gps_sensor_name, gps_interface_names.at(4), &gps_states.at(4));
+ hardware_interface::StateInterface::SharedPtr latitude_covariance =
+ std::make_shared(
+ gps_sensor_name, gps_interface_names.at(5), &gps_states.at(5));
+ hardware_interface::StateInterface::SharedPtr longitude_covariance =
+ std::make_shared(
+ gps_sensor_name, gps_interface_names.at(6), &gps_states.at(6));
+ hardware_interface::StateInterface::SharedPtr altitude_covariance =
+ std::make_shared(
+ gps_sensor_name, gps_interface_names.at(7), &gps_states.at(7));
std::vector state_interface;
};
diff --git a/controller_interface/test/test_imu_sensor.cpp b/controller_interface/test/test_imu_sensor.cpp
index f21a5a37ff..e6f8de44e5 100644
--- a/controller_interface/test/test_imu_sensor.cpp
+++ b/controller_interface/test/test_imu_sensor.cpp
@@ -47,30 +47,30 @@ TEST_F(IMUSensorTest, validate_all)
std::vector interface_names = imu_sensor_->get_state_interface_names();
// assign values to orientation
- hardware_interface::StateInterface orientation_x{
- sensor_name_, imu_interface_names_[0], &orientation_values_[0]};
- hardware_interface::StateInterface orientation_y{
- sensor_name_, imu_interface_names_[1], &orientation_values_[1]};
- hardware_interface::StateInterface orientation_z{
- sensor_name_, imu_interface_names_[2], &orientation_values_[2]};
- hardware_interface::StateInterface orientation_w{
- sensor_name_, imu_interface_names_[3], &orientation_values_[4]};
+ auto orientation_x = std::make_shared(
+ sensor_name_, imu_interface_names_[0], &orientation_values_[0]);
+ auto orientation_y = std::make_shared(
+ sensor_name_, imu_interface_names_[1], &orientation_values_[1]);
+ auto orientation_z = std::make_shared(
+ sensor_name_, imu_interface_names_[2], &orientation_values_[2]);
+ auto orientation_w = std::make_shared(
+ sensor_name_, imu_interface_names_[3], &orientation_values_[4]);
// assign values to angular velocity
- hardware_interface::StateInterface angular_velocity_x{
- sensor_name_, imu_interface_names_[4], &angular_velocity_values_[0]};
- hardware_interface::StateInterface angular_velocity_y{
- sensor_name_, imu_interface_names_[5], &angular_velocity_values_[1]};
- hardware_interface::StateInterface angular_velocity_z{
- sensor_name_, imu_interface_names_[6], &angular_velocity_values_[2]};
+ auto angular_velocity_x = std::make_shared(
+ sensor_name_, imu_interface_names_[4], &angular_velocity_values_[0]);
+ auto angular_velocity_y = std::make_shared(
+ sensor_name_, imu_interface_names_[5], &angular_velocity_values_[1]);
+ auto angular_velocity_z = std::make_shared(
+ sensor_name_, imu_interface_names_[6], &angular_velocity_values_[2]);
// assign values to linear acceleration
- hardware_interface::StateInterface linear_acceleration_x{
- sensor_name_, imu_interface_names_[7], &linear_acceleration_values_[0]};
- hardware_interface::StateInterface linear_acceleration_y{
- sensor_name_, imu_interface_names_[8], &linear_acceleration_values_[1]};
- hardware_interface::StateInterface linear_acceleration_z{
- sensor_name_, imu_interface_names_[9], &linear_acceleration_values_[2]};
+ auto linear_acceleration_x = std::make_shared(
+ sensor_name_, imu_interface_names_[7], &linear_acceleration_values_[0]);
+ auto linear_acceleration_y = std::make_shared(
+ sensor_name_, imu_interface_names_[8], &linear_acceleration_values_[1]);
+ auto linear_acceleration_z = std::make_shared(
+ sensor_name_, imu_interface_names_[9], &linear_acceleration_values_[2]);
// create local state interface vector
std::vector temp_state_interfaces;
diff --git a/controller_interface/test/test_led_rgb_device.cpp b/controller_interface/test/test_led_rgb_device.cpp
index 9f39726fa4..4be4242b1e 100644
--- a/controller_interface/test/test_led_rgb_device.cpp
+++ b/controller_interface/test/test_led_rgb_device.cpp
@@ -47,9 +47,12 @@ TEST_F(LedDeviceTest, validate_all)
std::vector interface_names = led_device_->get_command_interface_names();
// Assign values to position
- hardware_interface::CommandInterface led_r{device_name_, interface_names_[0], &led_values_[0]};
- hardware_interface::CommandInterface led_g{device_name_, interface_names_[1], &led_values_[1]};
- hardware_interface::CommandInterface led_b{device_name_, interface_names_[2], &led_values_[2]};
+ auto led_r = std::make_shared(
+ device_name_, interface_names_[0], &led_values_[0]);
+ auto led_g = std::make_shared(
+ device_name_, interface_names_[1], &led_values_[1]);
+ auto led_b = std::make_shared(
+ device_name_, interface_names_[2], &led_values_[2]);
// Create command interface vector in jumbled order
std::vector temp_command_interfaces;
diff --git a/controller_interface/test/test_magnetic_field_sensor.cpp b/controller_interface/test/test_magnetic_field_sensor.cpp
new file mode 100644
index 0000000000..629dbb2dd8
--- /dev/null
+++ b/controller_interface/test/test_magnetic_field_sensor.cpp
@@ -0,0 +1,82 @@
+// Copyright 2025 Aarav Gupta
+//
+// 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 "test_magnetic_field_sensor.hpp"
+
+#include
+#include
+#include
+#include "sensor_msgs/msg/magnetic_field.hpp"
+
+void MagneticFieldSensorTest::TearDown() { magnetic_field_sensor_.reset(nullptr); }
+
+TEST_F(MagneticFieldSensorTest, validate_all)
+{
+ // Create the magnetic field sensor
+ magnetic_field_sensor_ = std::make_unique(sensor_name_);
+
+ // validate the component name
+ ASSERT_EQ(magnetic_field_sensor_->name_, sensor_name_);
+
+ // Validate the space reserved for interface_names_ and state_interfaces_
+ // Note : Using capacity() for state_interfaces_ as no such interfaces are defined yet
+ ASSERT_EQ(magnetic_field_sensor_->interface_names_.size(), size_);
+ ASSERT_EQ(magnetic_field_sensor_->state_interfaces_.capacity(), size_);
+
+ // Validate the default interface_names_
+ ASSERT_TRUE(
+ std::equal(
+ magnetic_field_sensor_->interface_names_.begin(),
+ magnetic_field_sensor_->interface_names_.end(), full_interface_names_.begin(),
+ full_interface_names_.end()));
+
+ // Get the interface names
+ std::vector interface_names = magnetic_field_sensor_->get_state_interface_names();
+
+ // Assign values
+ auto magnetic_field_x = std::make_shared(
+ sensor_name_, magnetic_field_interface_names_[0], &magnetic_field_values_[0]);
+ auto magnetic_field_y = std::make_shared(
+ sensor_name_, magnetic_field_interface_names_[1], &magnetic_field_values_[1]);
+ auto magnetic_field_z = std::make_shared(
+ sensor_name_, magnetic_field_interface_names_[2], &magnetic_field_values_[2]);
+
+ // Create local state interface vector
+ std::vector temp_state_interfaces;
+ temp_state_interfaces.reserve(10);
+
+ // Insert the interfaces in jumbled sequence
+ temp_state_interfaces.emplace_back(magnetic_field_y);
+ temp_state_interfaces.emplace_back(magnetic_field_z);
+ temp_state_interfaces.emplace_back(magnetic_field_x);
+
+ // Now call the function to make them in order like interface_names
+ magnetic_field_sensor_->assign_loaned_state_interfaces(temp_state_interfaces);
+
+ // Validate the count of state_interfaces_
+ ASSERT_EQ(magnetic_field_sensor_->state_interfaces_.size(), size_);
+
+ // Validate get_values_as_message()
+ sensor_msgs::msg::MagneticField temp_message;
+ ASSERT_TRUE(magnetic_field_sensor_->get_values_as_message(temp_message));
+ ASSERT_EQ(temp_message.magnetic_field.x, magnetic_field_values_[0]);
+ ASSERT_EQ(temp_message.magnetic_field.y, magnetic_field_values_[1]);
+ ASSERT_EQ(temp_message.magnetic_field.z, magnetic_field_values_[2]);
+
+ // release the state_interfaces_
+ magnetic_field_sensor_->release_interfaces();
+
+ // validate the count of state_interfaces_
+ ASSERT_EQ(magnetic_field_sensor_->state_interfaces_.size(), 0u);
+}
diff --git a/controller_interface/test/test_magnetic_field_sensor.hpp b/controller_interface/test/test_magnetic_field_sensor.hpp
new file mode 100644
index 0000000000..84e31cf9f4
--- /dev/null
+++ b/controller_interface/test/test_magnetic_field_sensor.hpp
@@ -0,0 +1,63 @@
+// Copyright 2025 Aarav Gupta
+//
+// 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.
+
+#ifndef TEST_MAGNETIC_FIELD_SENSOR_HPP_
+#define TEST_MAGNETIC_FIELD_SENSOR_HPP_
+
+#include
+#include
+#include
+
+#include "gmock/gmock.h"
+#include "semantic_components/magnetic_field_sensor.hpp"
+
+// implementing and friending so we can access member variables
+class TestableMagneticFieldSensor : public semantic_components::MagneticFieldSensor
+{
+ FRIEND_TEST(MagneticFieldSensorTest, validate_all);
+
+public:
+ // Use generation of interface names
+ explicit TestableMagneticFieldSensor(const std::string & name) : MagneticFieldSensor(name) {}
+
+ virtual ~TestableMagneticFieldSensor() = default;
+};
+
+class MagneticFieldSensorTest : public ::testing::Test
+{
+public:
+ void SetUp()
+ {
+ full_interface_names_.reserve(size_);
+ for (auto index = 0u; index < size_; ++index)
+ {
+ full_interface_names_.emplace_back(
+ sensor_name_ + "/" + magnetic_field_interface_names_[index]);
+ }
+ }
+
+ void TearDown();
+
+protected:
+ const size_t size_ = 3;
+ const std::string sensor_name_ = "test_magnetometer";
+ std::array magnetic_field_values_ = {{4.4, 5.5, 6.6}};
+ std::unique_ptr magnetic_field_sensor_;
+
+ std::vector full_interface_names_;
+ const std::vector magnetic_field_interface_names_ = {
+ "magnetic_field.x", "magnetic_field.y", "magnetic_field.z"};
+};
+
+#endif // TEST_MAGNETIC_FIELD_SENSOR_HPP_
diff --git a/controller_interface/test/test_pose_sensor.cpp b/controller_interface/test/test_pose_sensor.cpp
index 6a06f2f224..6d9cf2dce9 100644
--- a/controller_interface/test/test_pose_sensor.cpp
+++ b/controller_interface/test/test_pose_sensor.cpp
@@ -45,22 +45,22 @@ TEST_F(PoseSensorTest, validate_all)
std::vector interface_names = pose_sensor_->get_state_interface_names();
// Assign values to position
- hardware_interface::StateInterface position_x{
- sensor_name_, interface_names_[0], &position_values_[0]};
- hardware_interface::StateInterface position_y{
- sensor_name_, interface_names_[1], &position_values_[1]};
- hardware_interface::StateInterface position_z{
- sensor_name_, interface_names_[2], &position_values_[2]};
+ auto position_x = std::make_shared(
+ sensor_name_, interface_names_[0], &position_values_[0]);
+ auto position_y = std::make_shared(
+ sensor_name_, interface_names_[1], &position_values_[1]);
+ auto position_z = std::make_shared(
+ sensor_name_, interface_names_[2], &position_values_[2]);
// Assign values to orientation
- hardware_interface::StateInterface orientation_x{
- sensor_name_, interface_names_[3], &orientation_values_[0]};
- hardware_interface::StateInterface orientation_y{
- sensor_name_, interface_names_[4], &orientation_values_[1]};
- hardware_interface::StateInterface orientation_z{
- sensor_name_, interface_names_[5], &orientation_values_[2]};
- hardware_interface::StateInterface orientation_w{
- sensor_name_, interface_names_[6], &orientation_values_[3]};
+ auto orientation_x = std::make_shared(
+ sensor_name_, interface_names_[3], &orientation_values_[0]);
+ auto orientation_y = std::make_shared(
+ sensor_name_, interface_names_[4], &orientation_values_[1]);
+ auto orientation_z = std::make_shared(
+ sensor_name_, interface_names_[5], &orientation_values_[2]);
+ auto orientation_w = std::make_shared(
+ sensor_name_, interface_names_[6], &orientation_values_[3]);
// Create state interface vector in jumbled order
std::vector temp_state_interfaces;
diff --git a/controller_interface/test/test_semantic_component_command_interface.cpp b/controller_interface/test/test_semantic_component_command_interface.cpp
index 07d4d54f5e..1251e5f4d5 100644
--- a/controller_interface/test/test_semantic_component_command_interface.cpp
+++ b/controller_interface/test/test_semantic_component_command_interface.cpp
@@ -38,9 +38,12 @@ TEST_F(SemanticCommandInterfaceTest, validate_command_interfaces)
std::vector interface_values = {
std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(),
std::numeric_limits::quiet_NaN()};
- hardware_interface::CommandInterface cmd_interface_1{component_name_, "1", &interface_values[0]};
- hardware_interface::CommandInterface cmd_interface_2{component_name_, "2", &interface_values[1]};
- hardware_interface::CommandInterface cmd_interface_3{component_name_, "3", &interface_values[2]};
+ auto cmd_interface_1 = std::make_shared(
+ component_name_, "1", &interface_values[0]);
+ auto cmd_interface_2 = std::make_shared(
+ component_name_, "2", &interface_values[1]);
+ auto cmd_interface_3 = std::make_shared(
+ component_name_, "3", &interface_values[2]);
// create local command interface vector
std::vector temp_command_interfaces;
diff --git a/controller_interface/test/test_semantic_component_interface.cpp b/controller_interface/test/test_semantic_component_interface.cpp
index 8a7817c2a3..1a0c0aa2b1 100644
--- a/controller_interface/test/test_semantic_component_interface.cpp
+++ b/controller_interface/test/test_semantic_component_interface.cpp
@@ -95,9 +95,12 @@ TEST_F(SemanticComponentInterfaceTest, validate_state_interfaces)
std::vector interface_values = {1.1, 3.3, 5.5};
// assign 1.1 to interface_1, 3.3 to interface_3 and 5.5 to interface_5
- hardware_interface::StateInterface interface_1{component_name_, "1", &interface_values[0]};
- hardware_interface::StateInterface interface_3{component_name_, "3", &interface_values[1]};
- hardware_interface::StateInterface interface_5{component_name_, "5", &interface_values[2]};
+ auto interface_1 = std::make_shared(
+ component_name_, "1", &interface_values[0]);
+ auto interface_3 = std::make_shared(
+ component_name_, "3", &interface_values[1]);
+ auto interface_5 = std::make_shared(
+ component_name_, "5", &interface_values[2]);
// create local state interface vector
std::vector temp_state_interfaces;
diff --git a/controller_manager/CHANGELOG.rst b/controller_manager/CHANGELOG.rst
index 812b40fc02..ad6ab672e1 100644
--- a/controller_manager/CHANGELOG.rst
+++ b/controller_manager/CHANGELOG.rst
@@ -2,6 +2,95 @@
Changelog for package controller_manager
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+6.0.1 (2025-11-03)
+------------------
+* Fail early for the resource conflicts between the activating controllers (`#2760 `_)
+* Contributors: Sai Kishor Kothakota
+
+6.0.0 (2025-10-27)
+------------------
+* Switch the default strictness to STRICT (`#2742 `_)
+* Deactivate the whole controller chain if one of the update results in ERROR (`#2681 `_)
+* Deactivate the controller chain upon failed group activation (`#2669 `_)
+* Add test_test_utils to CMakeLists (`#2729 `_)
+* Fix concurrent spinning of the test_node (`#2721 `_)
+* [Spawner] Fix failing makedirs on multiple spawners at startup (`#2698 `_)
+* [Spawner] Create the FileLock in the ROS_HOME location (`#2677 `_)
+* Fix the same hardware component node naming issue with multiple controller managers setup (`#2657 `_)
+* Move clock availability check to controller manager thread (`#2654 `_)
+* increase tolerance of the controller manager tests (`#2629 `_)
+* [Controllers] Set async thread properties via parameters (`#2613 `_)
+* Contributors: Christoph Fröhlich, Erik Holum, Sai Kishor Kothakota
+
+5.7.0 (2025-10-03)
+------------------
+* [CM] Ability to switch controllers in non-realtime loop (`#2452 `_)
+* Added parameters to handle the overruns behaviour and prints (`#2546 `_)
+* [Controllers] Receive a valid period on the first update cycle (`#2572 `_)
+* Add `ControllerInterfaceParams` to initialize the Controllers (`#2390 `_)
+* Add parameter to allow controllers with inactive hardware components (`#2501 `_)
+* Fix exclusive hardware control mode switching on controller failed activation (`#1522 `_)
+* Fix the skipped cycles of controllers running at different rate (`#2557 `_)
+* Fix shadowed variables, redefinition and old-style casts (`#2569 `_)
+* [Spawner] Release the lock while waiting for the interrupt (`#2559 `_)
+* Fix the reloading controller with failed activation (`#2544 `_)
+* Don't print the overrun warnings for the simulations (`#2539 `_)
+* Readd the ResourceManagerParams initialization to ControllerManager (`#2522 `_)
+* Publish controller manager statistics to better introspect the timings (`#2449 `_)
+* Fix the CPU affinity of the ros2_control_node (`#2509 `_)
+* Contributors: Christoph Fröhlich, Sai Kishor Kothakota
+
+5.6.0 (2025-08-26)
+------------------
+
+5.5.0 (2025-07-31)
+------------------
+* Fix the prepare_command_mode_switch behaviour when HW is INACTIVE (`#2347 `_)
+* Contributors: Sai Kishor Kothakota
+
+5.4.0 (2025-07-21)
+------------------
+* Increase controller period tolerance further (`#2405 `_)
+* Fix controller activation crash on macOS (Fixes `#604 `_) (`#2391 `_)
+* Increase controller period tolerance in tests (`#2388 `_)
+* [spawner] Fix Lock timeout error crashes (`#2386 `_)
+* [Spawner] Fix the scope issue of the logger (`#2382 `_)
+* Increase tolerance to improve the success rate of the tests (`#2373 `_)
+* [Spawner] Change strategy for `--unload-on-kill` option (`#2372 `_)
+* Contributors: Dhruv Patel, Jasper van Brakel, Sai Kishor Kothakota
+
+5.3.0 (2025-07-02)
+------------------
+* Cleanup old internal API (`#2346 `_)
+* added params approach to allow propagation in gz_ros2_control (`#2340 `_)
+* Deactivate controllers with command interfaces to hardware on DEACTIVATE (`#2334 `_)
+* Shift to Struct based Method and Constructors, with Executor passed from CM to `on_init()` (`#2323 `_)
+* Contributors: Marq Rasmussen, Sai Kishor Kothakota, Soham Patil
+
+5.2.0 (2025-06-07)
+------------------
+* Move `enforce_command_limits` parameter to GPL parameters (`#2305 `_)
+* Cleanup test name (`#2295 `_)
+* check_controllers_running: Make timeout a parameter (`#2278 `_)
+* Contributors: Christoph Fröhlich, Sai Kishor Kothakota
+
+5.1.0 (2025-05-24)
+------------------
+* Use target_link_libraries instead of ament_target_dependencies (`#2266 `_)
+* Cleanup deprecations in `ros_control` (`#2258 `_)
+* Contributors: Sai Kishor Kothakota
+
+5.0.0 (2025-05-21)
+------------------
+* [CM] Add option to avoid shutting down on hardware initial state failure (`#2230 `_)
+* Statically allocate string concatenations using FMT formatting (`#2205 `_)
+* Suppress the deprecation warnings of the hardware_interface API (`#2223 `_)
+* Use `warning` attribute of RcutilsLogger (`#2244 `_)
+* [CM] Set default strictness of switch_controllers using parameters (`#2168 `_)
+* Add `data_type` field to the HardwareInterfaces message (`#2204 `_)
+* Add new strictness modes to SwitchController service (`#2224 `_)
+* Contributors: Marq Rasmussen, Sai Kishor Kothakota, mini-1235
+
4.29.0 (2025-05-04)
-------------------
* Use the new reusable helpers methods in hardware diagnostics (`#2216 `_)
diff --git a/controller_manager/CMakeLists.txt b/controller_manager/CMakeLists.txt
index 0a2b103fbc..739fdc020d 100644
--- a/controller_manager/CMakeLists.txt
+++ b/controller_manager/CMakeLists.txt
@@ -16,6 +16,7 @@ set(THIS_PACKAGE_INCLUDE_DEPENDS
std_msgs
libstatistics_collector
generate_parameter_library
+ fmt
)
find_package(ament_cmake REQUIRED)
@@ -40,9 +41,18 @@ target_include_directories(controller_manager PUBLIC
$
)
target_link_libraries(controller_manager PUBLIC
- controller_manager_parameters
-)
-ament_target_dependencies(controller_manager PUBLIC ${THIS_PACKAGE_INCLUDE_DEPENDS})
+ controller_manager_parameters
+ controller_interface::controller_interface
+ diagnostic_updater::diagnostic_updater
+ hardware_interface::hardware_interface
+ pluginlib::pluginlib
+ rclcpp::rclcpp
+ rclcpp_lifecycle::rclcpp_lifecycle
+ realtime_tools::realtime_tools
+ libstatistics_collector::libstatistics_collector
+ fmt::fmt
+ ${std_msgs_TARGETS}
+ ${controller_manager_msgs_TARGETS})
add_executable(ros2_control_node src/ros2_control_node.cpp)
target_link_libraries(ros2_control_node PRIVATE
@@ -53,11 +63,13 @@ if(BUILD_TESTING)
find_package(ament_cmake_gmock REQUIRED)
find_package(ros2_control_test_assets REQUIRED)
find_package(example_interfaces REQUIRED)
+ find_package(ament_cmake_gtest REQUIRED)
# Plugin Libraries that are built and installed for use in testing
add_library(test_controller SHARED test/test_controller/test_controller.cpp)
- target_link_libraries(test_controller PUBLIC controller_manager)
- ament_target_dependencies(test_controller PUBLIC example_interfaces)
+ target_link_libraries(test_controller PUBLIC
+ controller_manager
+ ${example_interfaces_TARGETS})
pluginlib_export_plugin_description_file(controller_interface test/test_controller/test_controller.xml)
install(
TARGETS test_controller
@@ -78,8 +90,9 @@ if(BUILD_TESTING)
add_library(test_chainable_controller SHARED
test/test_chainable_controller/test_chainable_controller.cpp
)
- ament_target_dependencies(test_chainable_controller PUBLIC realtime_tools)
- target_link_libraries(test_chainable_controller PUBLIC controller_manager)
+ target_link_libraries(test_chainable_controller PUBLIC
+ controller_manager
+ realtime_tools::realtime_tools)
pluginlib_export_plugin_description_file(
controller_interface test/test_chainable_controller/test_chainable_controller.xml)
install(
@@ -146,11 +159,9 @@ if(BUILD_TESTING)
test_controller
test_chainable_controller
ros2_control_test_assets::ros2_control_test_assets
+ ${controller_manager_msgs_TARGETS}
)
set_tests_properties(test_controller_manager_srvs PROPERTIES TIMEOUT 120)
- ament_target_dependencies(test_controller_manager_srvs
- controller_manager_msgs
- )
ament_add_gmock(test_controller_manager_urdf_passing
test/test_controller_manager_urdf_passing.cpp
)
@@ -158,9 +169,7 @@ if(BUILD_TESTING)
controller_manager
test_controller
ros2_control_test_assets::ros2_control_test_assets
- )
- ament_target_dependencies(test_controller_manager_urdf_passing
- controller_manager_msgs
+ ${controller_manager_msgs_TARGETS}
)
add_library(test_controller_with_interfaces SHARED
@@ -176,6 +185,20 @@ if(BUILD_TESTING)
DESTINATION lib
)
+ add_library(test_controller_failed_activate SHARED
+ test/test_controller_failed_activate/test_controller_failed_activate.cpp
+ )
+ target_link_libraries(test_controller_failed_activate PUBLIC
+ controller_manager
+ )
+ target_compile_definitions(test_controller_failed_activate PRIVATE "CONTROLLER_MANAGER_BUILDING_DLL")
+ pluginlib_export_plugin_description_file(
+ controller_interface test/test_controller_failed_activate/test_controller_failed_activate.xml)
+ install(
+ TARGETS test_controller_failed_activate
+ DESTINATION lib
+ )
+
ament_add_gmock(test_release_interfaces
test/test_release_interfaces.cpp
APPEND_ENV AMENT_PREFIX_PATH=${ament_index_build_path}_$
@@ -184,6 +207,7 @@ if(BUILD_TESTING)
controller_manager
test_controller
test_controller_with_interfaces
+ test_controller_failed_activate
ros2_control_test_assets::ros2_control_test_assets
)
@@ -217,15 +241,23 @@ if(BUILD_TESTING)
controller_manager
test_controller
ros2_control_test_assets::ros2_control_test_assets
+ ${controller_manager_msgs_TARGETS}
)
- ament_target_dependencies(test_hardware_management_srvs
- controller_manager_msgs
+
+ ament_add_gtest(test_controller_manager_with_resource_manager
+ test/test_controller_manager_with_resource_manager.cpp
+ )
+ target_link_libraries(test_controller_manager_with_resource_manager
+ controller_manager
+ hardware_interface::hardware_interface
+ ros2_control_test_assets::ros2_control_test_assets
)
find_package(ament_cmake_pytest REQUIRED)
install(FILES test/test_ros2_control_node.yaml
DESTINATION test)
ament_add_pytest_test(test_ros2_control_node test/test_ros2_control_node_launch.py)
+ ament_add_pytest_test(test_test_utils test/test_test_utils.py)
endif()
install(
diff --git a/controller_manager/controller_manager/controller_manager_services.py b/controller_manager/controller_manager/controller_manager_services.py
index 8fa7452ad3..dc2647993f 100644
--- a/controller_manager/controller_manager/controller_manager_services.py
+++ b/controller_manager/controller_manager/controller_manager_services.py
@@ -133,7 +133,7 @@ def service_caller(
f"Could not contact service {fully_qualified_service_name}"
)
elif not cli.wait_for_service(10.0):
- node.get_logger().warn(f"Could not contact service {fully_qualified_service_name}")
+ node.get_logger().warning(f"Could not contact service {fully_qualified_service_name}")
node.get_logger().debug(f"requester: making request: {request}\n")
future = None
@@ -275,7 +275,7 @@ def switch_controllers(
controller_manager_name,
deactivate_controllers,
activate_controllers,
- strict,
+ strictness,
activate_asap,
timeout,
call_timeout=10.0,
@@ -283,10 +283,7 @@ def switch_controllers(
request = SwitchController.Request()
request.activate_controllers = activate_controllers
request.deactivate_controllers = deactivate_controllers
- if strict:
- request.strictness = SwitchController.Request.STRICT
- else:
- request.strictness = SwitchController.Request.BEST_EFFORT
+ request.strictness = strictness
request.activate_asap = activate_asap
request.timeout = rclpy.duration.Duration(seconds=timeout).to_msg()
return service_caller(
diff --git a/controller_manager/controller_manager/hardware_spawner.py b/controller_manager/controller_manager/hardware_spawner.py
index 4f7afe714c..abba851432 100644
--- a/controller_manager/controller_manager/hardware_spawner.py
+++ b/controller_manager/controller_manager/hardware_spawner.py
@@ -70,11 +70,11 @@ def handle_set_component_state_service_call(
f"{bcolors.OKGREEN}{action} component '{component}'. Hardware now in state: {response.state}.{bcolors.ENDC}"
)
elif response.ok and not response.state == target_state:
- node.get_logger().warn(
+ node.get_logger().warning(
f"{bcolors.WARNING}Could not {action} component '{component}'. Service call returned ok=True, but state: {response.state} is not equal to target state '{target_state}'.{bcolors.ENDC}"
)
else:
- node.get_logger().warn(
+ node.get_logger().warning(
f"{bcolors.WARNING}Could not {action} component '{component}'. Service call failed. Wrong component name?{bcolors.ENDC}"
)
@@ -156,7 +156,7 @@ def main(args=None):
if not is_hardware_component_loaded(
node, controller_manager_name, hardware_component, controller_manager_timeout
):
- node.get_logger().warn(
+ node.get_logger().warning(
f"{bcolors.WARNING}Hardware Component is not loaded - state can not be changed.{bcolors.ENDC}"
)
elif activate:
diff --git a/controller_manager/controller_manager/spawner.py b/controller_manager/controller_manager/spawner.py
index 2502f0f534..38628d0af8 100644
--- a/controller_manager/controller_manager/spawner.py
+++ b/controller_manager/controller_manager/spawner.py
@@ -30,6 +30,7 @@
set_controller_parameters_from_param_files,
bcolors,
)
+from controller_manager_msgs.srv import SwitchController
from controller_manager.controller_manager_services import ServiceNotFoundError
from filelock import Timeout, FileLock
@@ -92,13 +93,6 @@ def main(args=None):
action="append",
required=False,
)
- parser.add_argument(
- "-n",
- "--namespace",
- help="DEPRECATED Namespace for the controller_manager and the controller(s)",
- default=None,
- required=False,
- )
parser.add_argument(
"--load-only",
help="Only load the controller and leave unconfigured.",
@@ -147,6 +141,13 @@ def main(args=None):
action="store_true",
required=False,
)
+ parser.add_argument(
+ "--switch-asap",
+ help="Option to switch the controllers in the realtime loop at the earliest possible time or in the non-realtime loop.",
+ required=False,
+ default=False,
+ action=argparse.BooleanOptionalAction,
+ )
parser.add_argument(
"--controller-ros-args",
help="The --ros-args to be passed to the controller node, e.g., for remapping topics. "
@@ -164,57 +165,56 @@ def main(args=None):
controller_manager_timeout = args.controller_manager_timeout
service_call_timeout = args.service_call_timeout
switch_timeout = args.switch_timeout
+ strictness = SwitchController.Request.STRICT
+ unload_controllers_upon_exit = False
+ switch_asap = args.switch_asap
+ node = None
if param_files:
for param_file in param_files:
if not os.path.isfile(param_file):
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), param_file)
+ logger = rclpy.logging.get_logger("ros2_control_controller_spawner_" + controller_names[0])
try:
spawner_node_name = "spawner_" + controller_names[0]
- lock = FileLock("/tmp/ros2-control-controller-spawner.lock")
+ # Get the environment variable $ROS_HOME or default to ~/.ros
+ ros_home = os.getenv("ROS_HOME", os.path.join(os.path.expanduser("~"), ".ros"))
+ ros_control_lock_dir = os.path.join(ros_home, "locks")
+ if not os.path.exists(ros_control_lock_dir):
+ try:
+ os.makedirs(ros_control_lock_dir)
+ except FileExistsError:
+ pass
+ lock = FileLock(f"{ros_control_lock_dir}/ros2-control-controller-spawner.lock")
max_retries = 5
retry_delay = 3 # seconds
for attempt in range(max_retries):
- tmp_logger = rclpy.logging.get_logger(spawner_node_name)
try:
- tmp_logger.debug(
+ logger.debug(
bcolors.OKGREEN + "Waiting for the spawner lock to be acquired!" + bcolors.ENDC
)
# timeout after 20 seconds and try again
lock.acquire(timeout=20)
- tmp_logger.debug(bcolors.OKGREEN + "Spawner lock acquired!" + bcolors.ENDC)
+ logger.debug(bcolors.OKGREEN + "Spawner lock acquired!" + bcolors.ENDC)
break
except Timeout:
- tmp_logger.warn(
+ logger.warning(
bcolors.WARNING
+ f"Attempt {attempt+1} failed. Retrying in {retry_delay} seconds..."
+ bcolors.ENDC
)
time.sleep(retry_delay)
else:
- tmp_logger.error(
- bcolors.ERROR + "Failed to acquire lock after multiple attempts." + bcolors.ENDC
+ logger.error(
+ bcolors.FAIL + "Failed to acquire lock after multiple attempts." + bcolors.ENDC
)
return 1
node = Node(spawner_node_name)
+ logger = node.get_logger()
- if node.get_namespace() != "/" and args.namespace:
- raise RuntimeError(
- f"Setting namespace through both '--namespace {args.namespace}' arg and the ROS 2 standard way "
- f"'--ros-args -r __ns:={node.get_namespace()}' is not allowed!"
- )
-
- if args.namespace:
- warnings.filterwarnings("always")
- warnings.warn(
- "The '--namespace' argument is deprecated and will be removed in future releases."
- " Use the ROS 2 standard way of setting the node namespacing using --ros-args -r __ns:=",
- DeprecationWarning,
- )
-
- spawner_namespace = args.namespace if args.namespace else node.get_namespace()
+ spawner_namespace = node.get_namespace()
if not spawner_namespace.startswith("/"):
spawner_namespace = f"/{spawner_namespace}"
@@ -234,7 +234,7 @@ def main(args=None):
controller_manager_timeout,
service_call_timeout,
):
- node.get_logger().warn(
+ logger.warning(
bcolors.WARNING
+ "Controller already loaded, skipping load_controller"
+ bcolors.ENDC
@@ -267,7 +267,7 @@ def main(args=None):
service_call_timeout,
)
if not ret.ok:
- node.get_logger().fatal(
+ logger.fatal(
bcolors.FAIL
+ "Failed loading controller "
+ bcolors.BOLD
@@ -275,7 +275,7 @@ def main(args=None):
+ bcolors.ENDC
)
return 1
- node.get_logger().info(
+ logger.info(
bcolors.OKBLUE + "Loaded " + bcolors.BOLD + controller_name + bcolors.ENDC
)
@@ -288,9 +288,7 @@ def main(args=None):
service_call_timeout,
)
if not ret.ok:
- node.get_logger().error(
- bcolors.FAIL + "Failed to configure controller" + bcolors.ENDC
- )
+ logger.error(bcolors.FAIL + "Failed to configure controller" + bcolors.ENDC)
return 1
if not args.inactive and not args.activate_as_group:
@@ -299,18 +297,18 @@ def main(args=None):
controller_manager_name,
[],
[controller_name],
- True,
- True,
+ strictness,
+ switch_asap,
switch_timeout,
service_call_timeout,
)
if not ret.ok:
- node.get_logger().error(
+ logger.error(
f"{bcolors.FAIL}Failed to activate controller : {controller_name}{bcolors.ENDC}"
)
return 1
- node.get_logger().info(
+ logger.info(
bcolors.OKGREEN
+ "Configured and activated "
+ bcolors.BOLD
@@ -324,54 +322,52 @@ def main(args=None):
controller_manager_name,
[],
controller_names,
- True,
- True,
+ strictness,
+ switch_asap,
switch_timeout,
service_call_timeout,
)
if not ret.ok:
- node.get_logger().error(
+ logger.error(
f"{bcolors.FAIL}Failed to activate the parsed controllers list : {controller_names}{bcolors.ENDC}"
)
return 1
- node.get_logger().info(
+ logger.info(
bcolors.OKGREEN
+ f"Configured and activated all the parsed controllers list : {controller_names}!"
+ bcolors.ENDC
)
-
- if not args.unload_on_kill:
+ unload_controllers_upon_exit = args.unload_on_kill
+ if not unload_controllers_upon_exit:
return 0
- try:
- node.get_logger().info("Waiting until interrupt to unload controllers")
- while True:
- time.sleep(1)
- except KeyboardInterrupt:
- node.get_logger().info("KeyboardInterrupt successfully captured!")
+ # The lock has to be released to not block other spawner instances while waiting for the interrupt
+ lock.release()
+ logger.info("Waiting until interrupt to unload controllers")
+ while True:
+ time.sleep(1)
+ except KeyboardInterrupt:
+ if unload_controllers_upon_exit:
+ logger.info("KeyboardInterrupt successfully captured!")
if not args.inactive:
- node.get_logger().info("Deactivating and unloading controllers...")
+ logger.info("Deactivating and unloading controllers...")
# TODO(saikishor) we might have an issue in future, if any of these controllers is in chained mode
ret = switch_controllers(
node,
controller_manager_name,
controller_names,
[],
- True,
- True,
+ strictness,
+ switch_asap,
switch_timeout,
service_call_timeout,
)
if not ret.ok:
- node.get_logger().error(
- bcolors.FAIL + "Failed to deactivate controller" + bcolors.ENDC
- )
+ logger.error(bcolors.FAIL + "Failed to deactivate controller" + bcolors.ENDC)
return 1
- node.get_logger().info(
- f"Successfully deactivated controllers : {controller_names}"
- )
+ logger.info(f"Successfully deactivated controllers : {controller_names}")
unload_status = True
for controller_name in controller_names:
@@ -384,26 +380,27 @@ def main(args=None):
)
if not ret.ok:
unload_status = False
- node.get_logger().error(
+ logger.error(
bcolors.FAIL
+ f"Failed to unload controller : {controller_name}"
+ bcolors.ENDC
)
if unload_status:
- node.get_logger().info(f"Successfully unloaded controllers : {controller_names}")
+ logger.info(f"Successfully unloaded controllers : {controller_names}")
else:
return 1
- return 0
- except KeyboardInterrupt:
- node.get_logger().info("KeyboardInterrupt received! Exiting....")
- pass
+ else:
+ logger.info("KeyboardInterrupt received! Exiting....")
+ pass
except ServiceNotFoundError as err:
- node.get_logger().fatal(str(err))
+ logger.fatal(str(err))
return 1
finally:
- node.destroy_node()
- lock.release()
+ if node:
+ node.destroy_node()
+ if lock.is_locked:
+ lock.release()
rclpy.shutdown()
diff --git a/controller_manager/controller_manager/test_utils.py b/controller_manager/controller_manager/test_utils.py
index 8fb4ef8d97..c5b90b4ee4 100644
--- a/controller_manager/controller_manager/test_utils.py
+++ b/controller_manager/controller_manager/test_utils.py
@@ -36,6 +36,17 @@
def check_node_running(node, node_name, timeout=5.0):
+ """
+ Checks if a node with the specified name is running within a given timeout.
+
+ Args:
+ node (Node): The ROS 2 node instance to use for discovering nodes.
+ node_name (str): The name of the node to check for.
+ timeout (float, optional): The maximum time to wait for the node to appear, in seconds. Defaults to 5.0.
+
+ Raises:
+ AssertionError: If the node with the specified name is not found within the timeout period.
+ """
start = time.time()
found = False
@@ -45,15 +56,16 @@ def check_node_running(node, node_name, timeout=5.0):
assert found, f"{node_name} not found!"
-def check_controllers_running(node, cnames, namespace="", state="active"):
+def check_controllers_running(node, cnames, namespace="", state="active", timeout=10.0):
"""
Check if the specified controllers are running on the given node.
Args:
- node (Node): The ROS2 node instance to check for controllers.
+ node (Node): The ROS 2 node instance to use for discovering controllers.
cnames (list of str): List of controller names to check.
namespace (str, optional): The namespace in which to look for controllers. Defaults to "".
state (str, optional): The desired state of the controllers. Defaults to "active".
+ timeout (float, optional): The maximum time to wait for the node to appear, in seconds. Defaults to 10.0.
Raises:
AssertionError: If any of the specified controllers are not found or not in the desired state within the timeout period.
@@ -72,7 +84,7 @@ def check_controllers_running(node, cnames, namespace="", state="active"):
else:
namespace_api = "/"
- while time.time() - start < 10.0 and not all(found.values()):
+ while time.time() - start < timeout and not all(found.values()):
node_names_namespaces = node.get_node_names_and_namespaces()
for cname in cnames:
if any(name == cname and ns == namespace_api for name, ns in node_names_namespaces):
@@ -92,7 +104,7 @@ def check_controllers_running(node, cnames, namespace="", state="active"):
cm = namespace + "controller_manager"
else:
cm = namespace + "/controller_manager"
- while time.time() - start < 10.0 and not all(found.values()):
+ while time.time() - start < timeout and not all(found.values()):
controllers = list_controllers(node, cm, 5.0).controller
assert controllers, "No controllers found!"
for c in controllers:
diff --git a/controller_manager/controller_manager/unspawner.py b/controller_manager/controller_manager/unspawner.py
index 9e380f5086..f715936138 100644
--- a/controller_manager/controller_manager/unspawner.py
+++ b/controller_manager/controller_manager/unspawner.py
@@ -19,6 +19,7 @@
import warnings
from controller_manager import switch_controllers, unload_controller
+from controller_manager_msgs.srv import SwitchController
from controller_manager.controller_manager_services import ServiceNotFoundError
import rclpy
@@ -51,6 +52,7 @@ def main(args=None):
controller_names = args.controller_names
controller_manager_name = args.controller_manager
switch_timeout = args.switch_timeout
+ strictness = SwitchController.Request.STRICT
node = Node("unspawner_" + controller_names[0])
try:
@@ -60,7 +62,7 @@ def main(args=None):
controller_manager_name,
controller_names,
[],
- True,
+ strictness,
True,
switch_timeout,
)
diff --git a/controller_manager/doc/userdoc.rst b/controller_manager/doc/userdoc.rst
index 9e720fc681..b5566b72a7 100644
--- a/controller_manager/doc/userdoc.rst
+++ b/controller_manager/doc/userdoc.rst
@@ -79,11 +79,6 @@ robot_description [std_msgs::msg::String]
Parameters
-----------
-enforce_command_limits (optional; bool; default: ``false`` for Jazzy and earlier distro and ``true`` for Rolling and newer distros)
- Enforces the joint limits to the joint command interfaces.
- If the command is outside the limits, the command is clamped to be within the limits depending on the type of configured joint limits in the URDF.
- If the command is within the limits, the command is passed through without any changes.
-
.type
Name of a plugin exported using ``pluginlib`` for a controller.
This is a class from which controller's instance with name "``controller_name``" is created.
@@ -168,9 +163,9 @@ There are two scripts to interact with controller manager from launch files:
.. code-block:: console
$ ros2 run controller_manager spawner -h
- usage: spawner [-h] [-c CONTROLLER_MANAGER] [-p PARAM_FILE] [-n NAMESPACE] [--load-only] [--inactive] [-u] [--controller-manager-timeout CONTROLLER_MANAGER_TIMEOUT]
- [--switch-timeout SWITCH_TIMEOUT] [--activate-as-group] [--service-call-timeout SERVICE_CALL_TIMEOUT] [--controller-ros-args CONTROLLER_ROS_ARGS]
- controller_names [controller_names ...]
+ usage: spawner [-h] [-c CONTROLLER_MANAGER] [-p PARAM_FILE] [--load-only] [--inactive] [-u] [--controller-manager-timeout CONTROLLER_MANAGER_TIMEOUT] [--switch-timeout SWITCH_TIMEOUT]
+ [--service-call-timeout SERVICE_CALL_TIMEOUT] [--activate-as-group] [--switch-asap | --no-switch-asap] [--controller-ros-args CONTROLLER_ROS_ARGS]
+ controller_names [controller_names ...]
positional arguments:
controller_names List of controllers
@@ -181,21 +176,20 @@ There are two scripts to interact with controller manager from launch files:
Name of the controller manager ROS node
-p PARAM_FILE, --param-file PARAM_FILE
Controller param file to be loaded into controller node before configure. Pass multiple times to load different files for different controllers or to override the parameters of the same controller.
- -n NAMESPACE, --namespace NAMESPACE
- DEPRECATED Namespace for the controller_manager and the controller(s)
--load-only Only load the controller and leave unconfigured.
--inactive Load and configure the controller, however do not activate them
-u, --unload-on-kill Wait until this application is interrupted and unload controller
--controller-manager-timeout CONTROLLER_MANAGER_TIMEOUT
Time to wait for the controller manager service to be available
+ --switch-timeout SWITCH_TIMEOUT
+ Time to wait for a successful state switch of controllers. Useful when switching cannot be performed immediately, e.g., paused simulations at startup
--service-call-timeout SERVICE_CALL_TIMEOUT
Time to wait for the service response from the controller manager
- --switch-timeout SWITCH_TIMEOUT
- Time to wait for a successful state switch of controllers. Useful if controllers cannot be switched immediately, e.g., paused
- simulations at startup
--activate-as-group Activates all the parsed controllers list together instead of one by one. Useful for activating all chainable controllers altogether
+ --switch-asap, --no-switch-asap
+ Option to switch the controllers in the realtime loop at the earliest possible time or in the non-realtime loop.
--controller-ros-args CONTROLLER_ROS_ARGS
- The --ros-args to be passed to the controller node for remapping topics etc
+ The --ros-args to be passed to the controller node, e.g., for remapping topics. Pass multiple times for every argument.
The parsed controller config file can follow the same conventions as the typical ROS 2 parameter file format. Now, the spawner can handle config files with wildcard entries and also the controller name in the absolute namespace. See the following examples on the config files:
@@ -391,6 +385,12 @@ thread_priority (optional; int; default: 50)
use_sim_time (optional; bool; default: false)
Enables the use of simulation time in the ``controller_manager`` node.
+overruns.manage (optional; bool; default: true)
+ Enables or disables the handling of overruns in the real-time loop of the ``controller_manager`` node.
+ If set to true, the controller manager will detect overruns caused by system time changes or longer execution times of the controllers and hardware components.
+ If an overrun is detected, the controller manager will print a warning message to the console.
+ When used with ``use_sim_time`` set to true, this parameter is ignored and the overrun handling is disabled.
+
Concepts
-----------
@@ -409,7 +409,14 @@ Hardware and Controller Errors
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If the hardware during it's ``read`` or ``write`` method returns ``return_type::ERROR``, the controller manager will stop all controllers that are using the hardware's command and state interfaces.
-Likewise, if a controller returns ``return_type::ERROR`` from its ``update`` method, the controller manager will deactivate the respective controller. In future, the controller manager will try to start any fallback controllers if available.
+Likewise, if a controller returns ``return_type::ERROR`` from its ``update`` method, the controller manager will deactivate the respective controller (or) the entire controller chain it is part of, then the controller manager will try to start any available fallback controllers.
+
+Factors that affect Determinism
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+When run under the conditions determined in the above section, the determinism is assured up to the limitations of the hardware and the real-time kernel. However, there are some situations that can affect determinism:
+
+* When a controller fails to activate in the realtime loop, the controller_manager will call the methods ``prepare_command_mode_switch`` and ``perform_command_mode_switch`` to stop the started interfaces. These calls can cause jitter in the main control loop.
+* If a controller does not complete a successful update cycle in the realtime loop (for example, returns ``return_type::ERROR``), the controller manager will deactivate that controller (or) the entire controller chain it is part of. It will then invoke ``prepare_command_mode_switch`` and ``perform_command_mode_switch`` to stop the interfaces used by the affected controller(s). These actions can introduce jitter into the main control loop.
Support for Asynchronous Updates
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -431,6 +438,21 @@ The async update support is transparent to each controller implementation. A con
update_rate: 20 # Hz
...
+You can set more parameters of the ``AsyncFunctionHandler``, which handles the thread of the controller, using the ``async_parameters`` namespace. For example:
+
+.. code-block:: yaml
+
+ example_async_controller:
+ ros__parameters:
+ type: example_controller/ExampleAsyncController
+ update_rate: 20 # Hz
+ is_async: true
+ async_parameters:
+ cpu_affinity: [2, 4]
+ thread_priority: 50
+ wait_until_initial_trigger: false
+ ...
+
will result in the controller being loaded and configured to run at 20Hz, while the controller manager runs at 100Hz. The description of the parameters can be found in the `Common Controller Parameters `_ section of the ros2_controllers documentation.
Scheduling Behavior
diff --git a/controller_manager/include/controller_manager/controller_manager.hpp b/controller_manager/include/controller_manager/controller_manager.hpp
index 671b6f19a2..259bee233b 100644
--- a/controller_manager/include/controller_manager/controller_manager.hpp
+++ b/controller_manager/include/controller_manager/controller_manager.hpp
@@ -40,6 +40,7 @@
#include "controller_manager_msgs/srv/unload_controller.hpp"
#include "diagnostic_updater/diagnostic_updater.hpp"
+#include "hardware_interface/helpers.hpp"
#include "hardware_interface/resource_manager.hpp"
#include "pluginlib/class_loader.hpp"
@@ -51,7 +52,7 @@
namespace controller_manager
{
class ParamListener;
-class Params;
+struct Params;
using ControllersListIterator = std::vector::const_iterator;
rclcpp::NodeOptions get_cm_node_options();
@@ -241,6 +242,17 @@ class ControllerManager : public rclcpp::Node
*/
rclcpp::Clock::SharedPtr get_trigger_clock() const;
+ /**
+ * @brief Return the robot description timer.
+ *
+ * It can be used to determine whether the Controller Manager
+ * is currently waiting for a robot description to be published.
+ *
+ * @return rclcpp::TimerBase::SharedPtr if the timer exists, nullptr otherwise
+ */
+ rclcpp::TimerBase::SharedPtr get_robot_description_timer() const {
+ return robot_description_notification_timer_;
+ }
protected:
void init_services();
@@ -259,7 +271,7 @@ class ControllerManager : public rclcpp::Node
*/
void deactivate_controllers(
const std::vector & rt_controller_list,
- const std::vector controllers_to_deactivate);
+ const std::vector & controllers_to_deactivate);
/**
* Switch chained mode for all the controllers with respect to the following cases:
@@ -279,25 +291,11 @@ class ControllerManager : public rclcpp::Node
*
* \param[in] rt_controller_list controllers in the real-time list.
* \param[in] controllers_to_activate names of the controller that have to be activated.
+ * \param[in] strictness level of strictness for activation.
*/
void activate_controllers(
const std::vector & rt_controller_list,
- const std::vector controllers_to_activate);
-
- /// Activate chosen controllers from real-time controller list.
- /**
- * Activate controllers with names \p controllers_to_activate from list \p rt_controller_list.
- * The controller list will be iterated as many times as there are controller names.
- *
- * *NOTE*: There is currently not difference to `activate_controllers` method.
- * Check https://github.com/ros-controls/ros2_control/issues/263 for more information.
- *
- * \param[in] rt_controller_list controllers in the real-time list.
- * \param[in] controllers_to_activate names of the controller that have to be activated.
- */
- void activate_controllers_asap(
- const std::vector & rt_controller_list,
- const std::vector controllers_to_activate);
+ const std::vector & controllers_to_activate, int strictness);
void list_controllers_srv_cb(
const std::shared_ptr request,
@@ -471,12 +469,13 @@ class ControllerManager : public rclcpp::Node
*
* \param[in] controllers list with controllers.
* \param[in] activation_list list with controllers to activate.
+ * \param[in] deactivation_list list with controllers to deactivate.
* \param[out] message describing the result of the check.
* \return return_type::OK if all interfaces are available, otherwise return_type::ERROR.
*/
controller_interface::return_type check_for_interfaces_availability_to_activate(
const std::vector & controllers, const std::vector activation_list,
- std::string & message);
+ const std::vector deactivation_list, std::string & message);
/**
* @brief Inserts a controller into an ordered list based on dependencies to compute the
@@ -501,6 +500,14 @@ class ControllerManager : public rclcpp::Node
const std::string & ctrl_name, std::vector::iterator controller_iterator,
bool append_to_controller);
+ /**
+ * @brief Build the controller chain topology information based on the provided controllers.
+ * This method constructs a directed graph representing the dependencies between controllers.
+ * It analyzes the relationships between controllers, such as which controllers depend on others,
+ * and builds a directed graph to represent these dependencies.
+ */
+ void build_controllers_topology_info(const std::vector & controllers);
+
/**
* @brief Method to publish the state of the controller manager.
* The state includes the list of controllers and the list of hardware interfaces along with
@@ -630,6 +637,7 @@ class ControllerManager : public rclcpp::Node
std::function on_switch_callback_ = nullptr;
};
+ bool use_sim_time_;
rclcpp::Clock::SharedPtr trigger_clock_ = nullptr;
std::unique_ptr preshutdown_cb_handle_{nullptr};
RTControllerListWrapper rt_controllers_wrapper_;
@@ -661,11 +669,6 @@ class ControllerManager : public rclcpp::Node
rclcpp::Service::SharedPtr
set_hardware_component_state_service_;
- std::vector activate_request_, deactivate_request_;
- std::vector to_chained_mode_request_, from_chained_mode_request_;
- std::vector activate_command_interface_request_,
- deactivate_command_interface_request_;
-
std::map> controller_chained_reference_interfaces_cache_;
std::map> controller_chained_state_interfaces_cache_;
@@ -673,7 +676,21 @@ class ControllerManager : public rclcpp::Node
std::string robot_description_;
rclcpp::Subscription::SharedPtr robot_description_subscription_;
rclcpp::TimerBase::SharedPtr robot_description_notification_timer_;
- bool enforce_command_limits_;
+
+ struct ControllerManagerExecutionTime
+ {
+ double read_time = 0.0;
+ double update_time = 0.0;
+ double write_time = 0.0;
+ double switch_time = 0.0;
+ double total_time = 0.0;
+ double switch_chained_mode_time = 0.0;
+ double switch_perform_mode_time = 0.0;
+ double deactivation_time = 0.0;
+ double activation_time = 0.0;
+ };
+
+ ControllerManagerExecutionTime execution_time_;
controller_manager::MovingAverageStatistics periodicity_stats_;
@@ -687,17 +704,34 @@ class ControllerManager : public rclcpp::Node
activate_asap = false;
}
- bool do_switch;
+ std::atomic_bool do_switch;
bool started;
// Switch options
int strictness;
- bool activate_asap;
+ std::atomic_bool activate_asap;
std::chrono::nanoseconds timeout;
// conditional variable and mutex to wait for the switch to complete
std::condition_variable cv;
std::mutex mutex;
+
+ bool skip_cycle(const controller_manager::ControllerSpec & spec) const
+ {
+ const std::string controller_name = spec.info.name;
+ return ros2_control::has_item(activate_request, controller_name) ||
+ ros2_control::has_item(deactivate_request, controller_name) ||
+ ros2_control::has_item(to_chained_mode_request, controller_name) ||
+ ros2_control::has_item(from_chained_mode_request, controller_name);
+ }
+
+ // The controllers list to activate and deactivate
+ std::vector activate_request;
+ std::vector deactivate_request;
+ std::vector to_chained_mode_request;
+ std::vector from_chained_mode_request;
+ std::vector