diff --git a/.github/workflows/README.md b/.github/workflows/README.md
index ed64bcd94c..62007ffc2d 100644
--- a/.github/workflows/README.md
+++ b/.github/workflows/README.md
@@ -2,5 +2,6 @@
ROS2 Distro | Branch | Build status | Documentation | Released packages
:---------: | :----: | :----------: | :-----------: | :---------------:
**Rolling** | [`master`](https://github.com/ros-controls/ros2_control/tree/master) | [![Rolling Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-binary-build.yml?branch=master)
[![Rolling Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-semi-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-semi-binary-build.yml?branch=master)
[![Rolling Source Build](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-source-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-source-build.yml?branch=master)
[![Debian Rolling Source Build](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-debian-build.yml/badge.svg)](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-debian-build.yml)
[![RHEL Rolling Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-rhel-binary-build.yml/badge.svg)](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) | [![Jazzy Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-binary-build.yml?branch=master)
[![Jazzy Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-semi-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-semi-binary-build.yml?branch=master)
[![Jazzy Source Build](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-source-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-source-build.yml?branch=master)
[![Debian Jazzy Source Build](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-debian-build.yml/badge.svg)](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-debian-build.yml)
[![RHEL Jazzy Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-rhel-binary-build.yml/badge.svg)](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)
**Iron** | [`iron`](https://github.com/ros-controls/ros2_control/tree/master) | [![Iron Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/iron-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/iron-binary-build.yml?branch=master)
[![Iron Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/iron-semi-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/iron-semi-binary-build.yml?branch=master)
[![Iron Source Build](https://github.com/ros-controls/ros2_control/actions/workflows/iron-source-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/iron-source-build.yml?branch=master)
[![Debian Iron Source Build](https://github.com/ros-controls/ros2_control/actions/workflows/iron-debian-build.yml/badge.svg)](https://github.com/ros-controls/ros2_control/actions/workflows/iron-debian-build.yml)
[![RHEL Iron Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/iron-rhel-binary-build.yml/badge.svg)](https://github.com/ros-controls/ros2_control/actions/workflows/iron-rhel-binary-build.yml) | [Documentation](https://control.ros.org/iron/index.html)
[API Reference](https://control.ros.org/iron/doc/api/index.html) | [ros2_control](https://index.ros.org/p/ros2_control/#iron)
**Humble** | [`humble`](https://github.com/ros-controls/ros2_control/tree/humble) | [![Humble Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/humble-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/humble-binary-build.yml?branch=master)
[![Humble Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/humble-semi-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/humble-semi-binary-build.yml?branch=master)
[![Humble Source Build](https://github.com/ros-controls/ros2_control/actions/workflows/humble-source-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/humble-source-build.yml?branch=master)
[![Debian Humble Source Build](https://github.com/ros-controls/ros2_control/actions/workflows/humble-debian-build.yml/badge.svg)](https://github.com/ros-controls/ros2_control/actions/workflows/humble-debian-build.yml)
[![RHEL Humble Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/humble-rhel-binary-build.yml/badge.svg)](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/humble-abi-compatibility.yml b/.github/workflows/humble-abi-compatibility.yml
index 5c288fabfb..8b1950babc 100644
--- a/.github/workflows/humble-abi-compatibility.yml
+++ b/.github/workflows/humble-abi-compatibility.yml
@@ -4,6 +4,14 @@ on:
pull_request:
branches:
- humble
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/humble-abi-compatibility.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control-not-released.humble.repos'
jobs:
abi_check:
diff --git a/.github/workflows/humble-binary-build.yml b/.github/workflows/humble-binary-build.yml
index 2cf14105f5..18dfae9666 100644
--- a/.github/workflows/humble-binary-build.yml
+++ b/.github/workflows/humble-binary-build.yml
@@ -7,9 +7,25 @@ on:
pull_request:
branches:
- humble
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/humble-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control-not-released.humble.repos'
push:
branches:
- humble
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/humble-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control-not-released.humble.repos'
schedule:
# Run every morning to detect flakiness and broken dependencies
- cron: '03 1 * * *'
diff --git a/.github/workflows/humble-check-docs.yml b/.github/workflows/humble-check-docs.yml
new file mode 100644
index 0000000000..f3c31703cd
--- /dev/null
+++ b/.github/workflows/humble-check-docs.yml
@@ -0,0 +1,18 @@
+name: Humble Check Docs
+
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - humble
+ paths:
+ - '**.rst'
+ - '**.md'
+ - '**.yaml'
+
+jobs:
+ check-docs:
+ name: Check Docs
+ uses: ros-controls/control.ros.org/.github/workflows/reusable-sphinx-check-single-version.yml@humble
+ with:
+ ROS2_CONTROL_PR: ${{ github.ref }}
diff --git a/.github/workflows/humble-coverage-build.yml b/.github/workflows/humble-coverage-build.yml
index 0910572227..d699e99278 100644
--- a/.github/workflows/humble-coverage-build.yml
+++ b/.github/workflows/humble-coverage-build.yml
@@ -4,9 +4,27 @@ on:
push:
branches:
- humble
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/humble-coverage-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.humble.repos'
+ - 'codecov.yml'
pull_request:
branches:
- humble
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/humble-coverage-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.humble.repos'
+ - 'codecov.yml'
jobs:
coverage_humble:
@@ -14,4 +32,3 @@ jobs:
secrets: inherit
with:
ros_distro: humble
- os_name: ubuntu-22.04
diff --git a/.github/workflows/humble-debian-build.yml b/.github/workflows/humble-debian-build.yml
index 3b8a1c6287..5f0c33fa5e 100644
--- a/.github/workflows/humble-debian-build.yml
+++ b/.github/workflows/humble-debian-build.yml
@@ -4,6 +4,14 @@ on:
pull_request:
branches:
- humble
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.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 * * *'
diff --git a/.github/workflows/humble-pre-commit.yml b/.github/workflows/humble-pre-commit.yml
index be8c84b05b..5bb2408578 100644
--- a/.github/workflows/humble-pre-commit.yml
+++ b/.github/workflows/humble-pre-commit.yml
@@ -11,4 +11,3 @@ jobs:
uses: ros-controls/ros2_control_ci/.github/workflows/reusable-pre-commit.yml@master
with:
ros_distro: humble
- os_name: ubuntu-22.04
diff --git a/.github/workflows/humble-rhel-binary-build.yml b/.github/workflows/humble-rhel-binary-build.yml
index 4c00d2f2ad..dacaf8fee5 100644
--- a/.github/workflows/humble-rhel-binary-build.yml
+++ b/.github/workflows/humble-rhel-binary-build.yml
@@ -4,6 +4,14 @@ on:
pull_request:
branches:
- humble
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.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 * * *'
diff --git a/.github/workflows/humble-semi-binary-build.yml b/.github/workflows/humble-semi-binary-build.yml
index 19637c4897..ec994cf7ff 100644
--- a/.github/workflows/humble-semi-binary-build.yml
+++ b/.github/workflows/humble-semi-binary-build.yml
@@ -7,9 +7,25 @@ on:
pull_request:
branches:
- humble
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/humble-semi-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.humble.repos'
push:
branches:
- humble
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/humble-semi-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.humble.repos'
schedule:
# Run every morning to detect flakiness and broken dependencies
- cron: '03 1 * * *'
diff --git a/.github/workflows/humble-source-build.yml b/.github/workflows/humble-source-build.yml
index 6325fd50d8..5f437f8b0d 100644
--- a/.github/workflows/humble-source-build.yml
+++ b/.github/workflows/humble-source-build.yml
@@ -4,6 +4,14 @@ on:
push:
branches:
- humble
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/humble-source-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.humble.repos'
schedule:
# Run every day to detect flakiness and broken dependencies
- cron: '03 3 * * *'
diff --git a/.github/workflows/iron-abi-compatibility.yml b/.github/workflows/iron-abi-compatibility.yml
index ab6642625f..d9476298a2 100644
--- a/.github/workflows/iron-abi-compatibility.yml
+++ b/.github/workflows/iron-abi-compatibility.yml
@@ -4,6 +4,14 @@ on:
pull_request:
branches:
- iron
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/iron-abi-compatibility.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control-not-released.iron.repos'
jobs:
abi_check:
diff --git a/.github/workflows/iron-binary-build.yml b/.github/workflows/iron-binary-build.yml
index 911ccafae5..0fb0adcb1b 100644
--- a/.github/workflows/iron-binary-build.yml
+++ b/.github/workflows/iron-binary-build.yml
@@ -7,9 +7,25 @@ on:
pull_request:
branches:
- iron
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/iron-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control-not-released.iron.repos'
push:
branches:
- iron
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/iron-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control-not-released.iron.repos'
schedule:
# Run every morning to detect flakiness and broken dependencies
- cron: '03 1 * * *'
diff --git a/.github/workflows/iron-check-docs.yml b/.github/workflows/iron-check-docs.yml
new file mode 100644
index 0000000000..e9295dad44
--- /dev/null
+++ b/.github/workflows/iron-check-docs.yml
@@ -0,0 +1,18 @@
+name: Iron Check Docs
+
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - iron
+ paths:
+ - '**.rst'
+ - '**.md'
+ - '**.yaml'
+
+jobs:
+ check-docs:
+ name: Check Docs
+ uses: ros-controls/control.ros.org/.github/workflows/reusable-sphinx-check-single-version.yml@iron
+ with:
+ ROS2_CONTROL_PR: ${{ github.ref }}
diff --git a/.github/workflows/iron-coverage-build.yml b/.github/workflows/iron-coverage-build.yml
index d82c52bf51..c74d66c3fd 100644
--- a/.github/workflows/iron-coverage-build.yml
+++ b/.github/workflows/iron-coverage-build.yml
@@ -4,9 +4,27 @@ on:
push:
branches:
- iron
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/iron-coverage-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.iron.repos'
+ - 'codecov.yml'
pull_request:
branches:
- iron
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/iron-coverage-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.iron.repos'
+ - 'codecov.yml'
jobs:
coverage_iron:
@@ -14,4 +32,3 @@ jobs:
secrets: inherit
with:
ros_distro: iron
- os_name: ubuntu-22.04
diff --git a/.github/workflows/iron-debian-build.yml b/.github/workflows/iron-debian-build.yml
index ff503d64a9..2e6ba2289c 100644
--- a/.github/workflows/iron-debian-build.yml
+++ b/.github/workflows/iron-debian-build.yml
@@ -4,6 +4,14 @@ on:
pull_request:
branches:
- iron
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/iron-debian-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.iron.repos'
schedule:
# Run every day to detect flakiness and broken dependencies
- cron: '03 1 * * *'
diff --git a/.github/workflows/iron-pre-commit.yml b/.github/workflows/iron-pre-commit.yml
index 60ad26d073..a128958031 100644
--- a/.github/workflows/iron-pre-commit.yml
+++ b/.github/workflows/iron-pre-commit.yml
@@ -11,4 +11,3 @@ jobs:
uses: ros-controls/ros2_control_ci/.github/workflows/reusable-pre-commit.yml@master
with:
ros_distro: iron
- os_name: ubuntu-22.04
diff --git a/.github/workflows/iron-rhel-binary-build.yml b/.github/workflows/iron-rhel-binary-build.yml
index 981893524d..a45244b336 100644
--- a/.github/workflows/iron-rhel-binary-build.yml
+++ b/.github/workflows/iron-rhel-binary-build.yml
@@ -4,6 +4,14 @@ on:
pull_request:
branches:
- iron
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/iron-rhel-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.iron.repos'
schedule:
# Run every day to detect flakiness and broken dependencies
- cron: '03 1 * * *'
diff --git a/.github/workflows/iron-semi-binary-build.yml b/.github/workflows/iron-semi-binary-build.yml
index 59f8f347dd..8392655c35 100644
--- a/.github/workflows/iron-semi-binary-build.yml
+++ b/.github/workflows/iron-semi-binary-build.yml
@@ -7,9 +7,25 @@ on:
pull_request:
branches:
- iron
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/iron-semi-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.iron.repos'
push:
branches:
- iron
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/iron-semi-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.iron.repos'
schedule:
# Run every morning to detect flakiness and broken dependencies
- cron: '03 1 * * *'
diff --git a/.github/workflows/iron-source-build.yml b/.github/workflows/iron-source-build.yml
index 2d12734df0..608702ab72 100644
--- a/.github/workflows/iron-source-build.yml
+++ b/.github/workflows/iron-source-build.yml
@@ -4,6 +4,14 @@ on:
push:
branches:
- iron
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/iron-source-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.iron.repos'
schedule:
# Run every day to detect flakiness and broken dependencies
- cron: '03 3 * * *'
diff --git a/.github/workflows/jazzy-abi-compatibility.yml b/.github/workflows/jazzy-abi-compatibility.yml
new file mode 100644
index 0000000000..331234d30f
--- /dev/null
+++ b/.github/workflows/jazzy-abi-compatibility.yml
@@ -0,0 +1,26 @@
+name: Jazzy - ABI Compatibility Check
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/jazzy-abi-compatibility.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control-not-released.jazzy.repos'
+
+jobs:
+ abi_check:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: ros-industrial/industrial_ci@master
+ env:
+ ROS_DISTRO: jazzy
+ ROS_REPO: testing
+ ABICHECK_URL: github:${{ github.repository }}#${{ github.base_ref }}
+ NOT_TEST_BUILD: true
diff --git a/.github/workflows/jazzy-binary-build.yml b/.github/workflows/jazzy-binary-build.yml
new file mode 100644
index 0000000000..ac335c79f4
--- /dev/null
+++ b/.github/workflows/jazzy-binary-build.yml
@@ -0,0 +1,45 @@
+name: Jazzy Binary Build
+# author: Denis Štogl
+# description: 'Build & test all dependencies from released (binary) packages.'
+
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/jazzy-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control-not-released.jazzy.repos'
+ push:
+ branches:
+ - master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/jazzy-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control-not-released.jazzy.repos'
+ schedule:
+ # Run every morning to detect flakiness and broken dependencies
+ - cron: '03 1 * * *'
+
+jobs:
+ 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: [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: master
diff --git a/.github/workflows/jazzy-check-docs.yml b/.github/workflows/jazzy-check-docs.yml
new file mode 100644
index 0000000000..cbdf6c30bd
--- /dev/null
+++ b/.github/workflows/jazzy-check-docs.yml
@@ -0,0 +1,18 @@
+name: Jazzy Check Docs
+
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - master
+ paths:
+ - '**.rst'
+ - '**.md'
+ - '**.yaml'
+
+jobs:
+ check-docs:
+ name: Check Docs
+ uses: ros-controls/control.ros.org/.github/workflows/reusable-sphinx-check-single-version.yml@jazzy
+ with:
+ ROS2_CONTROL_PR: ${{ github.ref }}
diff --git a/.github/workflows/jazzy-coverage-build.yml b/.github/workflows/jazzy-coverage-build.yml
new file mode 100644
index 0000000000..aa345d1e80
--- /dev/null
+++ b/.github/workflows/jazzy-coverage-build.yml
@@ -0,0 +1,35 @@
+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'
+
+jobs:
+ coverage_jazzy:
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-build-coverage.yml@master
+ secrets: inherit
+ with:
+ ros_distro: jazzy
diff --git a/.github/workflows/jazzy-debian-build.yml b/.github/workflows/jazzy-debian-build.yml
new file mode 100644
index 0000000000..b61f101ae5
--- /dev/null
+++ b/.github/workflows/jazzy-debian-build.yml
@@ -0,0 +1,31 @@
+name: Debian Jazzy Source Build
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.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 * * *'
+
+
+jobs:
+ debian_source_build:
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-debian-build.yml@master
+ strategy:
+ fail-fast: false
+ matrix:
+ ROS_DISTRO: [jazzy]
+ with:
+ ros_distro: ${{ matrix.ROS_DISTRO }}
+ upstream_workspace: ros2_control.${{ matrix.ROS_DISTRO }}.repos
+ ref_for_scheduled_build: master
+ skip_packages: rqt_controller_manager
diff --git a/.github/workflows/jazzy-pre-commit.yml b/.github/workflows/jazzy-pre-commit.yml
new file mode 100644
index 0000000000..d9ec610bbc
--- /dev/null
+++ b/.github/workflows/jazzy-pre-commit.yml
@@ -0,0 +1,13 @@
+name: Pre-Commit - Jazzy
+
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - master
+
+jobs:
+ pre-commit:
+ uses: ros-controls/ros2_control_ci/.github/workflows/reusable-pre-commit.yml@master
+ with:
+ ros_distro: jazzy
diff --git a/.github/workflows/jazzy-rhel-binary-build.yml b/.github/workflows/jazzy-rhel-binary-build.yml
new file mode 100644
index 0000000000..1320624345
--- /dev/null
+++ b/.github/workflows/jazzy-rhel-binary-build.yml
@@ -0,0 +1,30 @@
+name: RHEL Jazzy Semi-Binary Build
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.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 * * *'
+
+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: [jazzy]
+ with:
+ ros_distro: ${{ matrix.ROS_DISTRO }}
+ upstream_workspace: ros2_control.${{ matrix.ROS_DISTRO }}.repos
+ ref_for_scheduled_build: master
+ skip_packages: rqt_controller_manager
diff --git a/.github/workflows/jazzy-semi-binary-build.yml b/.github/workflows/jazzy-semi-binary-build.yml
new file mode 100644
index 0000000000..b6c669245a
--- /dev/null
+++ b/.github/workflows/jazzy-semi-binary-build.yml
@@ -0,0 +1,45 @@
+name: Jazzy Semi-Binary Build
+# author: Denis Štogl
+# description: 'Build & test all dependencies from released (binary) packages.'
+
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/jazzy-semi-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.jazzy.repos'
+ push:
+ branches:
+ - master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/jazzy-semi-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.jazzy.repos'
+ schedule:
+ # Run every morning to detect flakiness and broken dependencies
+ - cron: '03 1 * * *'
+
+jobs:
+ 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: [main, 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
diff --git a/.github/workflows/jazzy-source-build.yml b/.github/workflows/jazzy-source-build.yml
new file mode 100644
index 0000000000..1acadc4a12
--- /dev/null
+++ b/.github/workflows/jazzy-source-build.yml
@@ -0,0 +1,26 @@
+name: Jazzy Source Build
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/jazzy-source-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.jazzy.repos'
+ schedule:
+ # Run every day to detect flakiness and broken dependencies
+ - cron: '03 3 * * *'
+
+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
+ container: ubuntu:24.04
diff --git a/.github/workflows/rolling-abi-compatibility.yml b/.github/workflows/rolling-abi-compatibility.yml
index 73055ef3e5..52b3ae8c57 100644
--- a/.github/workflows/rolling-abi-compatibility.yml
+++ b/.github/workflows/rolling-abi-compatibility.yml
@@ -4,6 +4,14 @@ on:
pull_request:
branches:
- master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/rolling-abi-compatibility.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control-not-released.rolling.repos'
jobs:
abi_check:
diff --git a/.github/workflows/rolling-binary-build.yml b/.github/workflows/rolling-binary-build.yml
index b083cc46fc..f556962d3a 100644
--- a/.github/workflows/rolling-binary-build.yml
+++ b/.github/workflows/rolling-binary-build.yml
@@ -7,9 +7,25 @@ on:
pull_request:
branches:
- master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/rolling-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control-not-released.rolling.repos'
push:
branches:
- master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/rolling-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control-not-released.rolling.repos'
schedule:
# Run every morning to detect flakiness and broken dependencies
- cron: '03 1 * * *'
diff --git a/.github/workflows/ci-check-docs.yml b/.github/workflows/rolling-check-docs.yml
similarity index 67%
rename from .github/workflows/ci-check-docs.yml
rename to .github/workflows/rolling-check-docs.yml
index 90a822aa72..80e8287abd 100644
--- a/.github/workflows/ci-check-docs.yml
+++ b/.github/workflows/rolling-check-docs.yml
@@ -1,8 +1,14 @@
-name: Check Docs
+name: Rolling Check Docs
on:
workflow_dispatch:
pull_request:
+ branches:
+ - master
+ paths:
+ - '**.rst'
+ - '**.md'
+ - '**.yaml'
jobs:
check-docs:
diff --git a/.github/workflows/rolling-coverage-build.yml b/.github/workflows/rolling-coverage-build.yml
index 8374afe8dc..4c61b47cbc 100644
--- a/.github/workflows/rolling-coverage-build.yml
+++ b/.github/workflows/rolling-coverage-build.yml
@@ -4,9 +4,27 @@ on:
push:
branches:
- master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/rolling-coverage-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.rolling.repos'
+ - 'codecov.yml'
pull_request:
branches:
- master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/rolling-coverage-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.rolling.repos'
+ - 'codecov.yml'
jobs:
coverage_rolling:
@@ -14,4 +32,3 @@ jobs:
secrets: inherit
with:
ros_distro: rolling
- container: ubuntu:24.04
diff --git a/.github/workflows/rolling-debian-build.yml b/.github/workflows/rolling-debian-build.yml
index e792963cc6..38ee0d213a 100644
--- a/.github/workflows/rolling-debian-build.yml
+++ b/.github/workflows/rolling-debian-build.yml
@@ -4,6 +4,14 @@ on:
pull_request:
branches:
- master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/rolling-debian-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.rolling.repos'
schedule:
# Run every day to detect flakiness and broken dependencies
- cron: '03 1 * * *'
diff --git a/.github/workflows/rolling-pre-commit.yml b/.github/workflows/rolling-pre-commit.yml
index 4d91d19100..7bc07ba802 100644
--- a/.github/workflows/rolling-pre-commit.yml
+++ b/.github/workflows/rolling-pre-commit.yml
@@ -11,4 +11,3 @@ jobs:
uses: ros-controls/ros2_control_ci/.github/workflows/reusable-pre-commit.yml@master
with:
ros_distro: rolling
- container: ubuntu:24.04
diff --git a/.github/workflows/rolling-rhel-binary-build.yml b/.github/workflows/rolling-rhel-binary-build.yml
index 38f375d6b6..e7e0e93fd1 100644
--- a/.github/workflows/rolling-rhel-binary-build.yml
+++ b/.github/workflows/rolling-rhel-binary-build.yml
@@ -4,6 +4,14 @@ on:
pull_request:
branches:
- master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/rolling-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 * * *'
diff --git a/.github/workflows/rolling-semi-binary-build.yml b/.github/workflows/rolling-semi-binary-build.yml
index f5c7885139..07cbcfc2f0 100644
--- a/.github/workflows/rolling-semi-binary-build.yml
+++ b/.github/workflows/rolling-semi-binary-build.yml
@@ -7,9 +7,25 @@ on:
pull_request:
branches:
- master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/rolling-semi-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.rolling.repos'
push:
branches:
- master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/rolling-semi-binary-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.rolling.repos'
schedule:
# Run every morning to detect flakiness and broken dependencies
- cron: '03 1 * * *'
diff --git a/.github/workflows/rolling-source-build.yml b/.github/workflows/rolling-source-build.yml
index d9f1519ba3..ae6b40a4b4 100644
--- a/.github/workflows/rolling-source-build.yml
+++ b/.github/workflows/rolling-source-build.yml
@@ -4,6 +4,14 @@ on:
push:
branches:
- master
+ paths:
+ - '**.hpp'
+ - '**.h'
+ - '**.cpp'
+ - '.github/workflows/rolling-source-build.yml'
+ - '**/package.xml'
+ - '**/CMakeLists.txt'
+ - 'ros2_control.rolling.repos'
schedule:
# Run every day to detect flakiness and broken dependencies
- cron: '03 3 * * *'
diff --git a/README.md b/README.md
index e88ca5b7bd..40d2a3c189 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,7 @@ For more, please check the [documentation](https://control.ros.org/).
ROS2 Distro | Branch | Build status | Documentation | Released packages
:---------: | :----: | :----------: | :-----------: | :---------------:
**Rolling** | [`master`](https://github.com/ros-controls/ros2_control/tree/master) | [![Rolling Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-binary-build.yml?branch=master)
[![Rolling Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-semi-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/rolling-semi-binary-build.yml?branch=master) | [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) | [![Rolling Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-binary-build.yml?branch=master)
[![Rolling Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-semi-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/jazzy-semi-binary-build.yml?branch=master) | [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)
**Iron** | [`iron`](https://github.com/ros-controls/ros2_control/tree/iron) | [![Iron Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/iron-binary-build.yml/badge.svg?branch=iron)](https://github.com/ros-controls/ros2_control/actions/workflows/iron-binary-build.yml?branch=iron)
[![Iron Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/iron-semi-binary-build.yml/badge.svg?branch=iron)](https://github.com/ros-controls/ros2_control/actions/workflows/iron-semi-binary-build.yml?branch=iron) | [Documentation](https://control.ros.org/iron/index.html)
[API Reference](https://control.ros.org/iron/doc/api/index.html) | [ros2_control](https://index.ros.org/p/ros2_control/#iron)
**Humble** | [`humble`](https://github.com/ros-controls/ros2_control/tree/humble) | [![Humble Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/humble-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/humble-binary-build.yml?branch=master)
[![Humble Semi-Binary Build](https://github.com/ros-controls/ros2_control/actions/workflows/humble-semi-binary-build.yml/badge.svg?branch=master)](https://github.com/ros-controls/ros2_control/actions/workflows/humble-semi-binary-build.yml?branch=master) | [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/controller_interface/CHANGELOG.rst b/controller_interface/CHANGELOG.rst
index 1688434cfc..b8301e0cba 100644
--- a/controller_interface/CHANGELOG.rst
+++ b/controller_interface/CHANGELOG.rst
@@ -2,6 +2,11 @@
Changelog for package controller_interface
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+4.10.0 (2024-05-08)
+-------------------
+* Working async controllers and components [not synchronized] (`#1041 `_)
+* Contributors: Márk Szitanics
+
4.9.0 (2024-04-30)
------------------
* return the proper const object of the pointer in the const method (`#1494 `_)
diff --git a/controller_interface/CMakeLists.txt b/controller_interface/CMakeLists.txt
index 2b7ccb9203..3dc3bc4d0a 100644
--- a/controller_interface/CMakeLists.txt
+++ b/controller_interface/CMakeLists.txt
@@ -12,6 +12,7 @@ set(THIS_PACKAGE_INCLUDE_DEPENDS
)
find_package(ament_cmake REQUIRED)
+find_package(ament_cmake_gen_version_h REQUIRED)
foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS})
find_package(${Dependency} REQUIRED)
endforeach()
diff --git a/controller_interface/include/controller_interface/async_controller.hpp b/controller_interface/include/controller_interface/async_controller.hpp
new file mode 100644
index 0000000000..5601ff4703
--- /dev/null
+++ b/controller_interface/include/controller_interface/async_controller.hpp
@@ -0,0 +1,111 @@
+// Copyright 2024 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__ASYNC_CONTROLLER_HPP_
+#define CONTROLLER_INTERFACE__ASYNC_CONTROLLER_HPP_
+
+#include
+#include
+#include
+
+#include "controller_interface_base.hpp"
+#include "lifecycle_msgs/msg/state.hpp"
+
+namespace controller_interface
+{
+
+class AsyncControllerThread
+{
+public:
+ /// Constructor for the AsyncControllerThread object.
+ /**
+ *
+ * \param[in] controller shared pointer to a controller.
+ * \param[in] cm_update_rate the controller manager's update rate.
+ */
+ AsyncControllerThread(
+ std::shared_ptr & controller, int cm_update_rate)
+ : terminated_(false), controller_(controller), thread_{}, cm_update_rate_(cm_update_rate)
+ {
+ }
+
+ AsyncControllerThread(const AsyncControllerThread & t) = delete;
+ AsyncControllerThread(AsyncControllerThread && t) = delete;
+
+ // Destructor, called when the component is erased from its map.
+ ~AsyncControllerThread()
+ {
+ terminated_.store(true, std::memory_order_seq_cst);
+ if (thread_.joinable())
+ {
+ thread_.join();
+ }
+ }
+
+ /// Creates the controller's thread.
+ /**
+ * Called when the controller is activated.
+ *
+ */
+ void activate()
+ {
+ thread_ = std::thread(&AsyncControllerThread::controller_update_callback, this);
+ }
+
+ /// Periodically execute the controller's update method.
+ /**
+ * Callback of the async controller's thread.
+ * **Not synchronized with the controller manager's write and read currently**
+ *
+ */
+ void controller_update_callback()
+ {
+ using TimePoint = std::chrono::system_clock::time_point;
+ unsigned int used_update_rate =
+ controller_->get_update_rate() == 0 ? cm_update_rate_ : controller_->get_update_rate();
+
+ auto previous_time = controller_->get_node()->now();
+ while (!terminated_.load(std::memory_order_relaxed))
+ {
+ auto const period = std::chrono::nanoseconds(1'000'000'000 / used_update_rate);
+ TimePoint next_iteration_time =
+ TimePoint(std::chrono::nanoseconds(controller_->get_node()->now().nanoseconds()));
+
+ if (controller_->get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE)
+ {
+ auto const current_time = controller_->get_node()->now();
+ auto const measured_period = current_time - previous_time;
+ previous_time = current_time;
+ controller_->update(
+ controller_->get_node()->now(),
+ (controller_->get_update_rate() != cm_update_rate_ && controller_->get_update_rate() != 0)
+ ? rclcpp::Duration::from_seconds(1.0 / controller_->get_update_rate())
+ : measured_period);
+ }
+
+ next_iteration_time += period;
+ std::this_thread::sleep_until(next_iteration_time);
+ }
+ }
+
+private:
+ std::atomic terminated_;
+ std::shared_ptr controller_;
+ std::thread thread_;
+ unsigned int cm_update_rate_;
+};
+
+} // namespace controller_interface
+
+#endif // CONTROLLER_INTERFACE__ASYNC_CONTROLLER_HPP_
diff --git a/controller_interface/package.xml b/controller_interface/package.xml
index 9e8010eeb9..a4b89265e3 100644
--- a/controller_interface/package.xml
+++ b/controller_interface/package.xml
@@ -2,7 +2,7 @@
controller_interface
- 4.9.0
+ 4.10.0
Description of controller_interface
Bence Magyar
Denis Štogl
diff --git a/controller_interface/test/test_controller_interface.cpp b/controller_interface/test/test_controller_interface.cpp
index 3976b2a81e..03838b1a2e 100644
--- a/controller_interface/test/test_controller_interface.cpp
+++ b/controller_interface/test/test_controller_interface.cpp
@@ -88,7 +88,8 @@ TEST(TestableControllerInterface, setting_update_rate_in_configure)
ASSERT_EQ(controller.get_update_rate(), 2812u);
// Test updating of update_rate parameter
- EXPECT_EQ(std::system("ros2 param set /testable_controller_interface update_rate 623"), 0);
+ auto res = controller.get_node()->set_parameter(rclcpp::Parameter("update_rate", 623));
+ EXPECT_EQ(res.successful, true);
// Keep the same update rate until transition from 'UNCONFIGURED' TO 'INACTIVE' does not happen
controller.configure(); // No transition so the update rate should stay intact
ASSERT_NE(controller.get_update_rate(), 623u);
diff --git a/controller_manager/CHANGELOG.rst b/controller_manager/CHANGELOG.rst
index 74657cd12b..4c5e9579ef 100644
--- a/controller_manager/CHANGELOG.rst
+++ b/controller_manager/CHANGELOG.rst
@@ -2,6 +2,15 @@
Changelog for package controller_manager
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+4.10.0 (2024-05-08)
+-------------------
+* allow extra spawner arguments to not declare every argument in launch utils (`#1505 `_)
+* Working async controllers and components [not synchronized] (`#1041 `_)
+* Add fallback controllers list to the ControllerInfo (`#1503 `_)
+* Add a functionality to look for the controller type in the params file when not parsed (`#1502 `_)
+* Add controller exception handling in controller manager (`#1507 `_)
+* Contributors: Márk Szitanics, Sai Kishor Kothakota
+
4.9.0 (2024-04-30)
------------------
* Deactivate the controllers when they return error similar to hardware (`#1499 `_)
diff --git a/controller_manager/CMakeLists.txt b/controller_manager/CMakeLists.txt
index d99a7f7330..c3c0de7739 100644
--- a/controller_manager/CMakeLists.txt
+++ b/controller_manager/CMakeLists.txt
@@ -21,6 +21,7 @@ set(THIS_PACKAGE_INCLUDE_DEPENDS
find_package(ament_cmake REQUIRED)
find_package(ament_cmake_python REQUIRED)
find_package(ament_cmake_core REQUIRED)
+find_package(ament_cmake_gen_version_h REQUIRED)
find_package(backward_ros REQUIRED)
foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS})
find_package(${Dependency} REQUIRED)
diff --git a/controller_manager/controller_manager/launch_utils.py b/controller_manager/controller_manager/launch_utils.py
index c7c5974a94..01528552d0 100644
--- a/controller_manager/controller_manager/launch_utils.py
+++ b/controller_manager/controller_manager/launch_utils.py
@@ -21,7 +21,7 @@
def generate_load_controller_launch_description(
- controller_name, controller_type=None, controller_params_file=None
+ controller_name, controller_type=None, controller_params_file=None, extra_spawner_args=[]
):
"""
Generate launch description for loading a controller using spawner.
@@ -40,7 +40,8 @@ def generate_load_controller_launch_description(
'joint_state_broadcaster',
controller_type='joint_state_broadcaster/JointStateBroadcaster',
controller_params_file=os.path.join(get_package_share_directory('my_pkg'),
- 'config', 'controller_params.yaml')
+ 'config', 'controller_params.yaml'),
+ extra_spawner_args=[--load-only]
)
"""
@@ -86,6 +87,9 @@ def generate_load_controller_launch_description(
)
]
+ if extra_spawner_args:
+ spawner_arguments += extra_spawner_args
+
spawner = Node(
package="controller_manager",
executable="spawner",
diff --git a/controller_manager/include/controller_manager/controller_manager.hpp b/controller_manager/include/controller_manager/controller_manager.hpp
index 9a709c5f8f..845fa2dee0 100644
--- a/controller_manager/include/controller_manager/controller_manager.hpp
+++ b/controller_manager/include/controller_manager/controller_manager.hpp
@@ -23,6 +23,7 @@
#include
#include
+#include "controller_interface/async_controller.hpp"
#include "controller_interface/chainable_controller_interface.hpp"
#include "controller_interface/controller_interface.hpp"
#include "controller_interface/controller_interface_base.hpp"
@@ -196,6 +197,15 @@ class ControllerManager : public rclcpp::Node
CONTROLLER_MANAGER_PUBLIC
unsigned int get_update_rate() const;
+ /// Deletes all async controllers and components.
+ /**
+ * Needed to join the threads immediately after the control loop is ended
+ * to avoid unnecessary iterations. Otherwise
+ * the threads will be joined only when the controller manager gets destroyed.
+ */
+ CONTROLLER_MANAGER_PUBLIC
+ void shutdown_async_controllers_and_components();
+
protected:
CONTROLLER_MANAGER_PUBLIC
void init_services();
@@ -563,65 +573,7 @@ class ControllerManager : public rclcpp::Node
SwitchParams switch_params_;
- class ControllerThreadWrapper
- {
- public:
- ControllerThreadWrapper(
- std::shared_ptr & controller,
- int cm_update_rate)
- : terminated_(false), controller_(controller), thread_{}, cm_update_rate_(cm_update_rate)
- {
- }
-
- ControllerThreadWrapper(const ControllerThreadWrapper & t) = delete;
- ControllerThreadWrapper(ControllerThreadWrapper && t) = default;
- ~ControllerThreadWrapper()
- {
- terminated_.store(true, std::memory_order_seq_cst);
- if (thread_.joinable())
- {
- thread_.join();
- }
- }
-
- void activate()
- {
- thread_ = std::thread(&ControllerThreadWrapper::call_controller_update, this);
- }
-
- void call_controller_update()
- {
- using TimePoint = std::chrono::system_clock::time_point;
- unsigned int used_update_rate =
- controller_->get_update_rate() == 0
- ? cm_update_rate_
- : controller_
- ->get_update_rate(); // determines if the controller's or CM's update rate is used
-
- while (!terminated_.load(std::memory_order_relaxed))
- {
- auto const period = std::chrono::nanoseconds(1'000'000'000 / used_update_rate);
- TimePoint next_iteration_time =
- TimePoint(std::chrono::nanoseconds(controller_->get_node()->now().nanoseconds()));
-
- if (controller_->get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE)
- {
- // critical section, not implemented yet
- }
-
- next_iteration_time += period;
- std::this_thread::sleep_until(next_iteration_time);
- }
- }
-
- private:
- std::atomic terminated_;
- std::shared_ptr controller_;
- std::thread thread_;
- unsigned int cm_update_rate_;
- };
-
- std::unordered_map>
+ std::unordered_map>
async_controller_threads_;
};
diff --git a/controller_manager/package.xml b/controller_manager/package.xml
index 884ffa9384..ec5b0e4f0e 100644
--- a/controller_manager/package.xml
+++ b/controller_manager/package.xml
@@ -2,7 +2,7 @@
controller_manager
- 4.9.0
+ 4.10.0
Description of controller_manager
Bence Magyar
Denis Štogl
diff --git a/controller_manager/src/controller_manager.cpp b/controller_manager/src/controller_manager.cpp
index 6cb7b025b1..41fe1ad95d 100644
--- a/controller_manager/src/controller_manager.cpp
+++ b/controller_manager/src/controller_manager.cpp
@@ -707,7 +707,8 @@ controller_interface::return_type ControllerManager::configure_controller(
if (controller->is_async())
{
async_controller_threads_.emplace(
- controller_name, std::make_unique(controller, update_rate_));
+ controller_name,
+ std::make_unique(controller, update_rate_));
}
const auto controller_update_rate = controller->get_update_rate();
@@ -2317,6 +2318,13 @@ std::pair ControllerManager::split_command_interface(
unsigned int ControllerManager::get_update_rate() const { return update_rate_; }
+void ControllerManager::shutdown_async_controllers_and_components()
+{
+ async_controller_threads_.erase(
+ async_controller_threads_.begin(), async_controller_threads_.end());
+ resource_manager_->shutdown_async_components();
+}
+
void ControllerManager::propagate_deactivation_of_chained_mode(
const std::vector & controllers)
{
diff --git a/controller_manager/src/ros2_control_node.cpp b/controller_manager/src/ros2_control_node.cpp
index 2747e79a1b..6dd7d72fb2 100644
--- a/controller_manager/src/ros2_control_node.cpp
+++ b/controller_manager/src/ros2_control_node.cpp
@@ -83,6 +83,8 @@ int main(int argc, char ** argv)
next_iteration_time += period;
std::this_thread::sleep_until(next_iteration_time);
}
+
+ cm->shutdown_async_controllers_and_components();
});
executor->add_node(cm);
diff --git a/controller_manager_msgs/CHANGELOG.rst b/controller_manager_msgs/CHANGELOG.rst
index 6491f5e292..cf752dde2a 100644
--- a/controller_manager_msgs/CHANGELOG.rst
+++ b/controller_manager_msgs/CHANGELOG.rst
@@ -2,6 +2,9 @@
Changelog for package controller_manager_msgs
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+4.10.0 (2024-05-08)
+-------------------
+
4.9.0 (2024-04-30)
------------------
diff --git a/controller_manager_msgs/package.xml b/controller_manager_msgs/package.xml
index 32264bd09e..1f078be2cf 100644
--- a/controller_manager_msgs/package.xml
+++ b/controller_manager_msgs/package.xml
@@ -2,7 +2,7 @@
controller_manager_msgs
- 4.9.0
+ 4.10.0
Messages and services for the controller manager.
Bence Magyar
Denis Štogl
diff --git a/doc/release_notes/Jazzy.rst b/doc/release_notes/Jazzy.rst
index 52fc54a071..4074cbca63 100644
--- a/doc/release_notes/Jazzy.rst
+++ b/doc/release_notes/Jazzy.rst
@@ -70,6 +70,30 @@ hardware_interface
******************
* A portable version for string-to-double conversion was added: ``hardware_interface::stod`` (`#1257 `_)
* ``test_components`` was moved to its own package (`#1325 `_)
+* The ``ros2_control`` tag now supports parsing of the limits from the URDF into the ``HardwareInfo`` structure. More conservative limits can be defined using the ``min`` and ``max`` attributes per interface (`#1472 `_)
+
+ .. code:: xml
+
+
+
+ ros2_control_demo_hardware/RRBotSystemPositionOnlyHardware
+ 2.0
+ 3.0
+ 2.0
+
+
+
+ -1
+ 1
+
+
+
+
+
+
+
+
+* Soft limits are also parsed from the URDF into the ``HardwareInfo`` structure for the defined joints (`#1488 `_)
joint_limits
************
diff --git a/hardware_interface/CHANGELOG.rst b/hardware_interface/CHANGELOG.rst
index 6e1528bc15..a27d097d6e 100644
--- a/hardware_interface/CHANGELOG.rst
+++ b/hardware_interface/CHANGELOG.rst
@@ -2,6 +2,15 @@
Changelog for package hardware_interface
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+4.10.0 (2024-05-08)
+-------------------
+* Add hardware components exception handling in resource manager (`#1508 `_)
+* Working async controllers and components [not synchronized] (`#1041 `_)
+* Parse URDF joint hard limits into the HardwareInfo structure (`#1472 `_)
+* Add fallback controllers list to the ControllerInfo (`#1503 `_)
+* Add more common hardware interface type constants (`#1500 `_)
+* Contributors: Márk Szitanics, Sai Kishor Kothakota
+
4.9.0 (2024-04-30)
------------------
* Add missing calculate_dynamics (`#1498 `_)
diff --git a/hardware_interface/CMakeLists.txt b/hardware_interface/CMakeLists.txt
index a5debef3b7..c557a4970f 100644
--- a/hardware_interface/CMakeLists.txt
+++ b/hardware_interface/CMakeLists.txt
@@ -20,6 +20,7 @@ set(THIS_PACKAGE_INCLUDE_DEPENDS
)
find_package(ament_cmake REQUIRED)
+find_package(ament_cmake_gen_version_h REQUIRED)
foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS})
find_package(${Dependency} REQUIRED)
endforeach()
diff --git a/hardware_interface/include/hardware_interface/async_components.hpp b/hardware_interface/include/hardware_interface/async_components.hpp
index 2fa6fd7f85..73495c92f8 100644
--- a/hardware_interface/include/hardware_interface/async_components.hpp
+++ b/hardware_interface/include/hardware_interface/async_components.hpp
@@ -17,6 +17,7 @@
#include
#include
+#include
#include
#include "hardware_interface/actuator.hpp"
@@ -34,29 +35,23 @@ class AsyncComponentThread
{
public:
explicit AsyncComponentThread(
- Actuator * component, unsigned int update_rate,
+ unsigned int update_rate,
rclcpp::node_interfaces::NodeClockInterface::SharedPtr clock_interface)
- : hardware_component_(component), cm_update_rate_(update_rate), clock_interface_(clock_interface)
+ : cm_update_rate_(update_rate), clock_interface_(clock_interface)
{
}
- explicit AsyncComponentThread(
- System * component, unsigned int update_rate,
- rclcpp::node_interfaces::NodeClockInterface::SharedPtr clock_interface)
- : hardware_component_(component), cm_update_rate_(update_rate), clock_interface_(clock_interface)
- {
- }
-
- explicit AsyncComponentThread(
- Sensor * component, unsigned int update_rate,
- rclcpp::node_interfaces::NodeClockInterface::SharedPtr clock_interface)
- : hardware_component_(component), cm_update_rate_(update_rate), clock_interface_(clock_interface)
+ // Fills the internal variant with the desired component.
+ template
+ void register_component(T * component)
{
+ hardware_component_ = component;
}
AsyncComponentThread(const AsyncComponentThread & t) = delete;
- AsyncComponentThread(AsyncComponentThread && t) = default;
+ AsyncComponentThread(AsyncComponentThread && t) = delete;
+ // Destructor, called when the component is erased from its map.
~AsyncComponentThread()
{
terminated_.store(true, std::memory_order_seq_cst);
@@ -65,9 +60,19 @@ class AsyncComponentThread
write_and_read_.join();
}
}
-
+ /// Creates the component's thread.
+ /**
+ * Called when the component is activated.
+ *
+ */
void activate() { write_and_read_ = std::thread(&AsyncComponentThread::write_and_read, this); }
+ /// Periodically execute the component's write and read methods.
+ /**
+ * Callback of the async component's thread.
+ * **Not synchronized with the controller manager's update currently**
+ *
+ */
void write_and_read()
{
using TimePoint = std::chrono::system_clock::time_point;
@@ -88,8 +93,12 @@ class AsyncComponentThread
auto measured_period = current_time - previous_time;
previous_time = current_time;
- // write
- // read
+ if (!first_iteration)
+ {
+ component->write(clock_interface_->get_clock()->now(), measured_period);
+ }
+ component->read(clock_interface_->get_clock()->now(), measured_period);
+ first_iteration = false;
}
next_iteration_time += period;
std::this_thread::sleep_until(next_iteration_time);
@@ -104,6 +113,7 @@ class AsyncComponentThread
std::thread write_and_read_{};
unsigned int cm_update_rate_;
+ bool first_iteration = true;
rclcpp::node_interfaces::NodeClockInterface::SharedPtr clock_interface_;
};
diff --git a/hardware_interface/include/hardware_interface/hardware_info.hpp b/hardware_interface/include/hardware_interface/hardware_info.hpp
index cb548b9bf4..d94573bf5e 100644
--- a/hardware_interface/include/hardware_interface/hardware_info.hpp
+++ b/hardware_interface/include/hardware_interface/hardware_info.hpp
@@ -171,6 +171,13 @@ struct HardwareInfo
* The URDF parsed limits of the hardware components joint command interfaces
*/
std::unordered_map limits;
+
+ /**
+ * Map of software joint limits used for clamping the command where the key is the joint name.
+ * Optional If not specified or less restrictive than the JointLimits uses the previous
+ * JointLimits.
+ */
+ std::unordered_map soft_limits;
};
} // namespace hardware_interface
diff --git a/hardware_interface/include/hardware_interface/resource_manager.hpp b/hardware_interface/include/hardware_interface/resource_manager.hpp
index 743e548a4c..91a98d1e96 100644
--- a/hardware_interface/include/hardware_interface/resource_manager.hpp
+++ b/hardware_interface/include/hardware_interface/resource_manager.hpp
@@ -376,6 +376,12 @@ class HARDWARE_INTERFACE_PUBLIC ResourceManager
return_type set_component_state(
const std::string & component_name, rclcpp_lifecycle::State & target_state);
+ /// Deletes all async components from the resource storage
+ /**
+ * Needed to join the threads immediately after the control loop is ended.
+ */
+ void shutdown_async_components();
+
/// Reads all loaded hardware components.
/**
* Reads from all active hardware components.
diff --git a/hardware_interface/package.xml b/hardware_interface/package.xml
index b79d280a11..33dea58c73 100644
--- a/hardware_interface/package.xml
+++ b/hardware_interface/package.xml
@@ -1,7 +1,7 @@
hardware_interface
- 4.9.0
+ 4.10.0
ros2_control hardware interface
Bence Magyar
Denis Štogl
diff --git a/hardware_interface/src/component_parser.cpp b/hardware_interface/src/component_parser.cpp
index aad1d827ec..14e016df21 100644
--- a/hardware_interface/src/component_parser.cpp
+++ b/hardware_interface/src/component_parser.cpp
@@ -927,6 +927,16 @@ std::vector parse_control_resources_from_urdf(const std::string &
// Take the most restricted one. Also valid for continuous-joint type only
detail::update_interface_limits(joint.command_interfaces, limits);
hw_info.limits[joint.name] = limits;
+ joint_limits::SoftJointLimits soft_limits;
+ if (getSoftJointLimits(urdf_joint, soft_limits))
+ {
+ if (limits.has_position_limits)
+ {
+ soft_limits.min_position = std::max(soft_limits.min_position, limits.min_position);
+ soft_limits.max_position = std::min(soft_limits.max_position, limits.max_position);
+ }
+ hw_info.soft_limits[joint.name] = soft_limits;
+ }
}
}
diff --git a/hardware_interface/src/resource_manager.cpp b/hardware_interface/src/resource_manager.cpp
index 2e8ccc7b1f..92b6d01519 100644
--- a/hardware_interface/src/resource_manager.cpp
+++ b/hardware_interface/src/resource_manager.cpp
@@ -67,6 +67,26 @@ auto trigger_and_print_hardware_state_transition =
return result;
};
+std::string interfaces_to_string(
+ const std::vector & start_interfaces,
+ const std::vector & stop_interfaces)
+{
+ std::stringstream ss;
+ ss << "Start interfaces: " << std::endl << "[" << std::endl;
+ for (const auto & start_if : start_interfaces)
+ {
+ ss << " " << start_if << std::endl;
+ }
+ ss << "]" << std::endl;
+ ss << "Stop interfaces: " << std::endl << "[" << std::endl;
+ for (const auto & stop_if : stop_interfaces)
+ {
+ ss << " " << stop_if << std::endl;
+ }
+ ss << "]" << std::endl;
+ return ss.str();
+};
+
class ResourceStorage
{
static constexpr const char * pkg_name = "hardware_interface";
@@ -90,27 +110,62 @@ class ResourceStorage
}
template
- void load_hardware(
+ [[nodiscard]] bool load_hardware(
const HardwareInfo & hardware_info, pluginlib::ClassLoader & loader,
std::vector & container)
{
- RCUTILS_LOG_INFO_NAMED(
- "resource_manager", "Loading hardware '%s' ", hardware_info.name.c_str());
- // hardware_plugin_name has to match class name in plugin xml description
- auto interface = std::unique_ptr(
- loader.createUnmanagedInstance(hardware_info.hardware_plugin_name));
- HardwareT hardware(std::move(interface));
- container.emplace_back(std::move(hardware));
- // initialize static data about hardware component to reduce later calls
- HardwareComponentInfo component_info;
- component_info.name = hardware_info.name;
- component_info.type = hardware_info.type;
- component_info.plugin_name = hardware_info.hardware_plugin_name;
- component_info.is_async = hardware_info.is_async;
-
- hardware_info_map_.insert(std::make_pair(component_info.name, component_info));
- hardware_used_by_controllers_.insert(
- std::make_pair(component_info.name, std::vector()));
+ bool is_loaded = false;
+ try
+ {
+ RCUTILS_LOG_INFO_NAMED(
+ "resource_manager", "Loading hardware '%s' ", hardware_info.name.c_str());
+ // hardware_plugin_name has to match class name in plugin xml description
+ auto interface = std::unique_ptr(
+ loader.createUnmanagedInstance(hardware_info.hardware_plugin_name));
+ if (interface)
+ {
+ RCUTILS_LOG_INFO_NAMED(
+ "resource_manager", "Loaded hardware '%s' from plugin '%s'", hardware_info.name.c_str(),
+ hardware_info.hardware_plugin_name.c_str());
+ HardwareT hardware(std::move(interface));
+ container.emplace_back(std::move(hardware));
+ // initialize static data about hardware component to reduce later calls
+ HardwareComponentInfo component_info;
+ component_info.name = hardware_info.name;
+ component_info.type = hardware_info.type;
+ component_info.plugin_name = hardware_info.hardware_plugin_name;
+ component_info.is_async = hardware_info.is_async;
+
+ hardware_info_map_.insert(std::make_pair(component_info.name, component_info));
+ hardware_used_by_controllers_.insert(
+ std::make_pair(component_info.name, std::vector()));
+ is_loaded = true;
+ }
+ else
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Failed to load hardware '%s' from plugin '%s'",
+ hardware_info.name.c_str(), hardware_info.hardware_plugin_name.c_str());
+ }
+ }
+ catch (const pluginlib::PluginlibException & ex)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Exception while loading hardware: %s", ex.what());
+ }
+ catch (const std::exception & ex)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Exception occurred while loading hardware '%s': %s",
+ hardware_info.name.c_str(), ex.what());
+ }
+ catch (...)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Unknown exception occurred while loading hardware '%s'",
+ hardware_info.name.c_str());
+ }
+ return is_loaded;
}
template
@@ -119,30 +174,62 @@ class ResourceStorage
RCUTILS_LOG_INFO_NAMED(
"resource_manager", "Initialize hardware '%s' ", hardware_info.name.c_str());
- const rclcpp_lifecycle::State new_state = hardware.initialize(hardware_info);
-
- bool result = new_state.id() == lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED;
+ bool result = false;
+ try
+ {
+ const rclcpp_lifecycle::State new_state = hardware.initialize(hardware_info);
+ result = new_state.id() == lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED;
- if (result)
+ if (result)
+ {
+ RCUTILS_LOG_INFO_NAMED(
+ "resource_manager", "Successful initialization of hardware '%s'",
+ hardware_info.name.c_str());
+ }
+ else
+ {
+ RCUTILS_LOG_INFO_NAMED(
+ "resource_manager", "Failed to initialize hardware '%s'", hardware_info.name.c_str());
+ }
+ }
+ catch (const std::exception & ex)
{
- RCUTILS_LOG_INFO_NAMED(
- "resource_manager", "Successful initialization of hardware '%s'",
- hardware_info.name.c_str());
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Exception occurred while initializing hardware '%s': %s",
+ hardware_info.name.c_str(), ex.what());
}
- else
+ catch (...)
{
- RCUTILS_LOG_INFO_NAMED(
- "resource_manager", "Failed to initialize hardware '%s'", hardware_info.name.c_str());
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Unknown exception occurred while initializing hardware '%s'",
+ hardware_info.name.c_str());
}
+
return result;
}
template
bool configure_hardware(HardwareT & hardware)
{
- bool result = trigger_and_print_hardware_state_transition(
- std::bind(&HardwareT::configure, &hardware), "configure", hardware.get_name(),
- lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE);
+ bool result = false;
+ try
+ {
+ result = trigger_and_print_hardware_state_transition(
+ std::bind(&HardwareT::configure, &hardware), "configure", hardware.get_name(),
+ lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE);
+ }
+ catch (const std::exception & ex)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Exception occurred while configuring hardware '%s': %s",
+ hardware.get_name().c_str(), ex.what());
+ }
+ catch (...)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Unknown exception occurred while configuring hardware '%s'",
+ hardware.get_name().c_str());
+ }
if (result)
{
@@ -206,7 +293,9 @@ class ResourceStorage
{
async_component_threads_.emplace(
std::piecewise_construct, std::forward_as_tuple(hardware.get_name()),
- std::forward_as_tuple(&hardware, cm_update_rate_, clock_interface_));
+ std::forward_as_tuple(cm_update_rate_, clock_interface_));
+
+ async_component_threads_.at(hardware.get_name()).register_component(&hardware);
}
}
return result;
@@ -267,9 +356,25 @@ class ResourceStorage
template
bool cleanup_hardware(HardwareT & hardware)
{
- bool result = trigger_and_print_hardware_state_transition(
- std::bind(&HardwareT::cleanup, &hardware), "cleanup", hardware.get_name(),
- lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED);
+ bool result = false;
+ try
+ {
+ result = trigger_and_print_hardware_state_transition(
+ std::bind(&HardwareT::cleanup, &hardware), "cleanup", hardware.get_name(),
+ lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED);
+ }
+ catch (const std::exception & ex)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Exception occurred while cleaning up hardware '%s': %s",
+ hardware.get_name().c_str(), ex.what());
+ }
+ catch (...)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Unknown exception occurred while cleaning up hardware '%s'",
+ hardware.get_name().c_str());
+ }
if (result)
{
@@ -281,9 +386,25 @@ class ResourceStorage
template
bool shutdown_hardware(HardwareT & hardware)
{
- bool result = trigger_and_print_hardware_state_transition(
- std::bind(&HardwareT::shutdown, &hardware), "shutdown", hardware.get_name(),
- lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED);
+ bool result = false;
+ try
+ {
+ result = trigger_and_print_hardware_state_transition(
+ std::bind(&HardwareT::shutdown, &hardware), "shutdown", hardware.get_name(),
+ lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED);
+ }
+ catch (const std::exception & ex)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Exception occurred while shutting down hardware '%s': %s",
+ hardware.get_name().c_str(), ex.what());
+ }
+ catch (...)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Unknown exception occurred while shutting down hardware '%s'",
+ hardware.get_name().c_str());
+ }
if (result)
{
@@ -300,9 +421,25 @@ class ResourceStorage
template
bool activate_hardware(HardwareT & hardware)
{
- bool result = trigger_and_print_hardware_state_transition(
- std::bind(&HardwareT::activate, &hardware), "activate", hardware.get_name(),
- lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE);
+ bool result = false;
+ try
+ {
+ result = trigger_and_print_hardware_state_transition(
+ std::bind(&HardwareT::activate, &hardware), "activate", hardware.get_name(),
+ lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE);
+ }
+ catch (const std::exception & ex)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Exception occurred while activating hardware '%s': %s",
+ hardware.get_name().c_str(), ex.what());
+ }
+ catch (...)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Unknown exception occurred while activating hardware '%s'",
+ hardware.get_name().c_str());
+ }
if (result)
{
@@ -319,9 +456,25 @@ class ResourceStorage
template
bool deactivate_hardware(HardwareT & hardware)
{
- bool result = trigger_and_print_hardware_state_transition(
- std::bind(&HardwareT::deactivate, &hardware), "deactivate", hardware.get_name(),
- lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE);
+ bool result = false;
+ try
+ {
+ result = trigger_and_print_hardware_state_transition(
+ std::bind(&HardwareT::deactivate, &hardware), "deactivate", hardware.get_name(),
+ lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE);
+ }
+ catch (const std::exception & ex)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Exception occurred while deactivating hardware '%s': %s",
+ hardware.get_name().c_str(), ex.what());
+ }
+ catch (...)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Unknown exception occurred while deactivating hardware '%s'",
+ hardware.get_name().c_str());
+ }
if (result)
{
@@ -433,25 +586,60 @@ class ResourceStorage
template
void import_state_interfaces(HardwareT & hardware)
{
- auto interfaces = hardware.export_state_interfaces();
- std::vector interface_names;
- interface_names.reserve(interfaces.size());
- for (auto & interface : interfaces)
+ try
{
- auto key = interface.get_name();
- state_interface_map_.emplace(std::make_pair(key, std::move(interface)));
- interface_names.push_back(key);
+ auto interfaces = hardware.export_state_interfaces();
+ std::vector interface_names;
+ interface_names.reserve(interfaces.size());
+ for (auto & interface : interfaces)
+ {
+ auto key = interface.get_name();
+ state_interface_map_.emplace(std::make_pair(key, std::move(interface)));
+ interface_names.push_back(key);
+ }
+ hardware_info_map_[hardware.get_name()].state_interfaces = interface_names;
+ available_state_interfaces_.reserve(
+ available_state_interfaces_.capacity() + interface_names.size());
+ }
+ catch (const std::exception & e)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager",
+ "Exception occurred while importing state interfaces for the hardware '%s' : %s",
+ hardware.get_name().c_str(), e.what());
+ }
+ catch (...)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager",
+ "Unknown exception occurred while importing state interfaces for the hardware '%s'",
+ hardware.get_name().c_str());
}
- hardware_info_map_[hardware.get_name()].state_interfaces = interface_names;
- available_state_interfaces_.reserve(
- available_state_interfaces_.capacity() + interface_names.size());
}
template
void import_command_interfaces(HardwareT & hardware)
{
- auto interfaces = hardware.export_command_interfaces();
- hardware_info_map_[hardware.get_name()].command_interfaces = add_command_interfaces(interfaces);
+ try
+ {
+ auto interfaces = hardware.export_command_interfaces();
+ hardware_info_map_[hardware.get_name()].command_interfaces =
+ add_command_interfaces(interfaces);
+ }
+ catch (const std::exception & ex)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager",
+ "Exception occurred while importing command interfaces for the hardware '%s' : %s",
+ hardware.get_name().c_str(), ex.what());
+ }
+ catch (...)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager",
+ "Unknown exception occurred while importing command interfaces for the hardware '%s'",
+ hardware.get_name().c_str());
+ }
}
/// Adds exported command interfaces into internal storage.
@@ -514,7 +702,10 @@ class ResourceStorage
auto load_and_init_actuators = [&](auto & container)
{
check_for_duplicates(hardware_info);
- load_hardware(hardware_info, actuator_loader_, container);
+ if (!load_hardware(hardware_info, actuator_loader_, container))
+ {
+ return;
+ }
if (initialize_hardware(hardware_info, container.back()))
{
import_state_interfaces(container.back());
@@ -544,7 +735,10 @@ class ResourceStorage
auto load_and_init_sensors = [&](auto & container)
{
check_for_duplicates(hardware_info);
- load_hardware(hardware_info, sensor_loader_, container);
+ if (!load_hardware(hardware_info, sensor_loader_, container))
+ {
+ return;
+ }
if (initialize_hardware(hardware_info, container.back()))
{
import_state_interfaces(container.back());
@@ -573,7 +767,10 @@ class ResourceStorage
auto load_and_init_systems = [&](auto & container)
{
check_for_duplicates(hardware_info);
- load_hardware(hardware_info, system_loader_, container);
+ if (!load_hardware(hardware_info, system_loader_, container))
+ {
+ return;
+ }
if (initialize_hardware(hardware_info, container.back()))
{
import_state_interfaces(container.back());
@@ -1083,24 +1280,6 @@ bool ResourceManager::prepare_command_mode_switch(
return true;
}
- auto interfaces_to_string = [&]()
- {
- std::stringstream ss;
- ss << "Start interfaces: " << std::endl << "[" << std::endl;
- for (const auto & start_if : start_interfaces)
- {
- ss << " " << start_if << std::endl;
- }
- ss << "]" << std::endl;
- ss << "Stop interfaces: " << std::endl << "[" << std::endl;
- for (const auto & stop_if : stop_interfaces)
- {
- ss << " " << stop_if << std::endl;
- }
- ss << "]" << std::endl;
- return ss.str();
- };
-
// Check if interface exists
std::stringstream ss_not_existing;
ss_not_existing << "Not existing: " << std::endl << "[" << std::endl;
@@ -1122,7 +1301,8 @@ bool ResourceManager::prepare_command_mode_switch(
ss_not_existing << "]" << std::endl;
RCUTILS_LOG_ERROR_NAMED(
"resource_manager", "Not acceptable command interfaces combination: \n%s%s",
- interfaces_to_string().c_str(), ss_not_existing.str().c_str());
+ interfaces_to_string(start_interfaces, stop_interfaces).c_str(),
+ ss_not_existing.str().c_str());
return false;
}
@@ -1147,12 +1327,12 @@ bool ResourceManager::prepare_command_mode_switch(
ss_not_available << "]" << std::endl;
RCUTILS_LOG_ERROR_NAMED(
"resource_manager", "Not acceptable command interfaces combination: \n%s%s",
- interfaces_to_string().c_str(), ss_not_available.str().c_str());
+ interfaces_to_string(start_interfaces, stop_interfaces).c_str(),
+ ss_not_available.str().c_str());
return false;
}
- auto call_prepare_mode_switch =
- [&start_interfaces, &stop_interfaces, &interfaces_to_string](auto & components)
+ auto call_prepare_mode_switch = [&start_interfaces, &stop_interfaces](auto & components)
{
bool ret = true;
for (auto & component : components)
@@ -1161,14 +1341,38 @@ bool ResourceManager::prepare_command_mode_switch(
component.get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE ||
component.get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE)
{
- if (
- return_type::OK !=
- component.prepare_command_mode_switch(start_interfaces, stop_interfaces))
+ try
+ {
+ if (
+ return_type::OK !=
+ component.prepare_command_mode_switch(start_interfaces, stop_interfaces))
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager",
+ "Component '%s' did not accept command interfaces combination: \n%s",
+ component.get_name().c_str(),
+ interfaces_to_string(start_interfaces, stop_interfaces).c_str());
+ ret = false;
+ }
+ }
+ catch (const std::exception & e)
{
RCUTILS_LOG_ERROR_NAMED(
"resource_manager",
- "Component '%s' did not accept command interfaces combination: \n%s",
- component.get_name().c_str(), interfaces_to_string().c_str());
+ "Exception occurred while preparing command mode switch for component '%s' for the "
+ "interfaces: \n %s : %s",
+ component.get_name().c_str(),
+ interfaces_to_string(start_interfaces, stop_interfaces).c_str(), e.what());
+ ret = false;
+ }
+ catch (...)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager",
+ "Unknown exception occurred while preparing command mode switch for component '%s' for "
+ "the interfaces: \n %s",
+ component.get_name().c_str(),
+ interfaces_to_string(start_interfaces, stop_interfaces).c_str());
ret = false;
}
}
@@ -1202,13 +1406,37 @@ bool ResourceManager::perform_command_mode_switch(
component.get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE ||
component.get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE)
{
- if (
- return_type::OK !=
- component.perform_command_mode_switch(start_interfaces, stop_interfaces))
+ try
+ {
+ if (
+ return_type::OK !=
+ component.perform_command_mode_switch(start_interfaces, stop_interfaces))
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Component '%s' could not perform switch",
+ component.get_name().c_str());
+ ret = false;
+ }
+ }
+ catch (const std::exception & e)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager",
+ "Exception occurred while performing command mode switch for component '%s' for the "
+ "interfaces: \n %s : %s",
+ component.get_name().c_str(),
+ interfaces_to_string(start_interfaces, stop_interfaces).c_str(), e.what());
+ ret = false;
+ }
+ catch (...)
{
RCUTILS_LOG_ERROR_NAMED(
- "resource_manager", "Component '%s' could not perform switch",
- component.get_name().c_str());
+ "resource_manager",
+ "Unknown exception occurred while performing command mode switch for component '%s' "
+ "for "
+ "the interfaces: \n %s",
+ component.get_name().c_str(),
+ interfaces_to_string(start_interfaces, stop_interfaces).c_str());
ret = false;
}
}
@@ -1324,6 +1552,13 @@ return_type ResourceManager::set_component_state(
return result;
}
+void ResourceManager::shutdown_async_components()
+{
+ resource_storage_->async_component_threads_.erase(
+ resource_storage_->async_component_threads_.begin(),
+ resource_storage_->async_component_threads_.end());
+}
+
// CM API: Called in "update"-thread
HardwareReadWriteStatus ResourceManager::read(
const rclcpp::Time & time, const rclcpp::Duration & period)
@@ -1336,7 +1571,25 @@ HardwareReadWriteStatus ResourceManager::read(
{
for (auto & component : components)
{
- auto ret_val = component.read(time, period);
+ auto ret_val = return_type::OK;
+ try
+ {
+ ret_val = component.read(time, period);
+ }
+ catch (const std::exception & e)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Exception thrown durind read of the component '%s': %s",
+ component.get_name().c_str(), e.what());
+ ret_val = return_type::ERROR;
+ }
+ catch (...)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Unknown exception thrown during read of the component '%s'",
+ component.get_name().c_str());
+ ret_val = return_type::ERROR;
+ }
if (ret_val == return_type::ERROR)
{
read_write_status.ok = false;
@@ -1376,7 +1629,25 @@ HardwareReadWriteStatus ResourceManager::write(
{
for (auto & component : components)
{
- auto ret_val = component.write(time, period);
+ auto ret_val = return_type::OK;
+ try
+ {
+ ret_val = component.write(time, period);
+ }
+ catch (const std::exception & e)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Exception thrown during write of the component '%s': %s",
+ component.get_name().c_str(), e.what());
+ ret_val = return_type::ERROR;
+ }
+ catch (...)
+ {
+ RCUTILS_LOG_ERROR_NAMED(
+ "resource_manager", "Unknown exception thrown during write of the component '%s'",
+ component.get_name().c_str());
+ ret_val = return_type::ERROR;
+ }
if (ret_val == return_type::ERROR)
{
read_write_status.ok = false;
diff --git a/hardware_interface/test/test_component_parser.cpp b/hardware_interface/test/test_component_parser.cpp
index 946fda7c15..6a0c11cf72 100644
--- a/hardware_interface/test/test_component_parser.cpp
+++ b/hardware_interface/test/test_component_parser.cpp
@@ -141,6 +141,8 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_one_interface)
// Verify limits parsed from the URDF
ASSERT_THAT(hardware_info.limits, SizeIs(2));
+ ASSERT_THAT(hardware_info.soft_limits, SizeIs(1));
+ char joint2_name[] = "joint2";
for (const auto & joint : {"joint1", "joint2"})
{
EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits);
@@ -152,6 +154,13 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_one_interface)
EXPECT_TRUE(hardware_info.limits.at(joint).has_effort_limits);
EXPECT_THAT(hardware_info.limits.at(joint).max_velocity, DoubleNear(0.2, 1e-5));
EXPECT_THAT(hardware_info.limits.at(joint).max_effort, DoubleNear(0.1, 1e-5));
+ if (strcmp(joint, joint2_name) == 0)
+ {
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).max_position, DoubleNear(0.5, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).min_position, DoubleNear(-1.0, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).k_position, DoubleNear(10.0, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).k_velocity, DoubleNear(20.0, 1e-5));
+ }
}
}
@@ -193,6 +202,8 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_multi_interface
// Verify limits parsed from the URDF
ASSERT_THAT(hardware_info.limits, SizeIs(2));
+ ASSERT_THAT(hardware_info.soft_limits, SizeIs(1));
+ char joint2_name[] = "joint2";
for (const auto & joint : {"joint1", "joint2"})
{
EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits);
@@ -205,6 +216,13 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_multi_interface
// effort and velocity limits won't change as they are above the main URDF hard limits
EXPECT_THAT(hardware_info.limits.at(joint).max_velocity, DoubleNear(0.2, 1e-5));
EXPECT_THAT(hardware_info.limits.at(joint).max_effort, DoubleNear(0.1, 1e-5));
+ if (strcmp(joint, joint2_name) == 0)
+ {
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).max_position, DoubleNear(0.5, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).min_position, DoubleNear(-1.0, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).k_position, DoubleNear(10.0, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).k_velocity, DoubleNear(20.0, 1e-5));
+ }
}
}
@@ -253,6 +271,8 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_robot_with_sens
// Verify limits parsed from the URDF
ASSERT_THAT(hardware_info.limits, SizeIs(2));
+ ASSERT_THAT(hardware_info.soft_limits, SizeIs(1));
+ char joint2_name[] = "joint2";
for (const auto & joint : {"joint1", "joint2"})
{
EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits);
@@ -264,6 +284,13 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_robot_with_sens
EXPECT_TRUE(hardware_info.limits.at(joint).has_effort_limits);
EXPECT_THAT(hardware_info.limits.at(joint).max_velocity, DoubleNear(0.2, 1e-5));
EXPECT_THAT(hardware_info.limits.at(joint).max_effort, DoubleNear(0.1, 1e-5));
+ if (strcmp(joint, joint2_name) == 0)
+ {
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).max_position, DoubleNear(0.5, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).min_position, DoubleNear(-1.0, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).k_position, DoubleNear(10.0, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).k_velocity, DoubleNear(20.0, 1e-5));
+ }
}
}
@@ -297,6 +324,8 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_robot_with_exte
// Verify limits parsed from the URDF
ASSERT_THAT(hardware_info.limits, SizeIs(2));
+ ASSERT_THAT(hardware_info.soft_limits, SizeIs(1));
+ char joint2_name[] = "joint2";
for (const auto & joint : {"joint1", "joint2"})
{
EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits);
@@ -308,6 +337,13 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_robot_with_exte
EXPECT_TRUE(hardware_info.limits.at(joint).has_effort_limits);
EXPECT_THAT(hardware_info.limits.at(joint).max_velocity, DoubleNear(0.2, 1e-5));
EXPECT_THAT(hardware_info.limits.at(joint).max_effort, DoubleNear(0.1, 1e-5));
+ if (strcmp(joint, joint2_name) == 0)
+ {
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).max_position, DoubleNear(0.5, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).min_position, DoubleNear(-1.0, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).k_position, DoubleNear(10.0, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).k_velocity, DoubleNear(20.0, 1e-5));
+ }
}
hardware_info = control_hardware.at(1);
@@ -322,6 +358,7 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_robot_with_exte
EXPECT_EQ(hardware_info.sensors[0].type, "sensor");
EXPECT_EQ(hardware_info.sensors[0].parameters.at("frame_id"), "kuka_tcp");
ASSERT_THAT(hardware_info.limits, SizeIs(0));
+ ASSERT_THAT(hardware_info.soft_limits, SizeIs(0));
}
TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot)
@@ -346,6 +383,7 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot
EXPECT_EQ(hardware_info.joints[0].type, "joint");
// Verify limits parsed from the URDF
ASSERT_THAT(hardware_info.limits, SizeIs(1));
+ ASSERT_THAT(hardware_info.soft_limits, SizeIs(0));
for (const auto & joint : {"joint1"})
{
EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits);
@@ -373,6 +411,8 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot
EXPECT_EQ(hardware_info.joints[0].type, "joint");
// Verify limits parsed from the URDF
ASSERT_THAT(hardware_info.limits, SizeIs(1));
+ ASSERT_THAT(hardware_info.soft_limits, SizeIs(1));
+ char joint2_name[] = "joint2";
for (const auto & joint : {"joint2"})
{
EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits);
@@ -384,6 +424,13 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot
EXPECT_TRUE(hardware_info.limits.at(joint).has_effort_limits);
EXPECT_THAT(hardware_info.limits.at(joint).max_velocity, DoubleNear(0.2, 1e-5));
EXPECT_THAT(hardware_info.limits.at(joint).max_effort, DoubleNear(0.1, 1e-5));
+ if (strcmp(joint, joint2_name) == 0)
+ {
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).max_position, DoubleNear(0.5, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).min_position, DoubleNear(-1.0, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).k_position, DoubleNear(10.0, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).k_velocity, DoubleNear(20.0, 1e-5));
+ }
}
}
@@ -421,6 +468,7 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot
EXPECT_THAT(hardware_info.transmissions[0].actuators[0].name, "actuator1");
// Verify limits parsed from the URDF
ASSERT_THAT(hardware_info.limits, SizeIs(1));
+ ASSERT_THAT(hardware_info.soft_limits, SizeIs(0));
for (const auto & joint : {"joint1"})
{
EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits);
@@ -451,6 +499,8 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot
EXPECT_EQ(hardware_info.joints[0].command_interfaces[0].max, "1");
// Verify limits parsed from the URDF
ASSERT_THAT(hardware_info.limits, SizeIs(1));
+ ASSERT_THAT(hardware_info.soft_limits, SizeIs(1));
+ char joint2_name[] = "joint2";
for (const auto & joint : {"joint2"})
{
EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits);
@@ -461,6 +511,13 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot
// effort and velocity limits won't change as they are above the main URDF hard limits
EXPECT_THAT(hardware_info.limits.at(joint).max_velocity, DoubleNear(0.2, 1e-5));
EXPECT_THAT(hardware_info.limits.at(joint).max_effort, DoubleNear(0.1, 1e-5));
+ if (strcmp(joint, joint2_name) == 0)
+ {
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).max_position, DoubleNear(0.5, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).min_position, DoubleNear(-1.5, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).k_position, DoubleNear(10.0, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).k_velocity, DoubleNear(20.0, 1e-5));
+ }
}
hardware_info = control_hardware.at(2);
@@ -481,6 +538,7 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot
EXPECT_EQ(hardware_info.joints[0].state_interfaces[0].name, HW_IF_POSITION);
// Verify limits parsed from the URDF
ASSERT_THAT(hardware_info.limits, SizeIs(1));
+ ASSERT_THAT(hardware_info.soft_limits, SizeIs(0));
for (const auto & joint : {"joint1"})
{
EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits);
@@ -511,6 +569,7 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot
EXPECT_EQ(hardware_info.joints[0].state_interfaces[0].name, HW_IF_POSITION);
// Verify limits parsed from the URDF
ASSERT_THAT(hardware_info.limits, SizeIs(1));
+ ASSERT_THAT(hardware_info.soft_limits, SizeIs(1));
for (const auto & joint : {"joint2"})
{
EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits);
@@ -521,6 +580,13 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_modular_robot
// effort and velocity limits won't change as they are above the main URDF hard limits
EXPECT_THAT(hardware_info.limits.at(joint).max_velocity, DoubleNear(0.2, 1e-5));
EXPECT_THAT(hardware_info.limits.at(joint).max_effort, DoubleNear(0.1, 1e-5));
+ if (strcmp(joint, joint2_name) == 0)
+ {
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).max_position, DoubleNear(0.5, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).min_position, DoubleNear(-1.5, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).k_position, DoubleNear(10.0, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).k_velocity, DoubleNear(20.0, 1e-5));
+ }
}
}
@@ -596,6 +662,7 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_sensor_only)
EXPECT_EQ(hardware_info.sensors[1].state_interfaces[0].name, "image");
// There will be no limits as the ros2_control tag has only sensor info
ASSERT_THAT(hardware_info.limits, SizeIs(0));
+ ASSERT_THAT(hardware_info.soft_limits, SizeIs(0));
}
TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_only)
@@ -656,6 +723,7 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_actuator_only)
// effort and velocity limits won't change as they are above the main URDF hard limits
EXPECT_THAT(hardware_info.limits.at("joint1").max_velocity, DoubleNear(0.2, 1e-5));
EXPECT_THAT(hardware_info.limits.at("joint1").max_effort, DoubleNear(0.1, 1e-5));
+ ASSERT_THAT(hardware_info.soft_limits, SizeIs(0));
}
TEST_F(TestComponentParser, successfully_parse_locale_independent_double)
@@ -727,6 +795,8 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_robot_with_gpio
// Verify limits parsed from the URDF
ASSERT_THAT(hardware_info.limits, SizeIs(2));
+ ASSERT_THAT(hardware_info.soft_limits, SizeIs(1));
+ char joint2_name[] = "joint2";
for (const auto & joint : {"joint1", "joint2"})
{
// Position limits are limited in the ros2_control tag
@@ -737,6 +807,13 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_robot_with_gpio
EXPECT_TRUE(hardware_info.limits.at(joint).has_effort_limits);
EXPECT_THAT(hardware_info.limits.at(joint).max_velocity, DoubleNear(0.2, 1e-5));
EXPECT_THAT(hardware_info.limits.at(joint).max_effort, DoubleNear(0.1, 1e-5));
+ if (strcmp(joint, joint2_name) == 0)
+ {
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).max_position, DoubleNear(0.5, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).min_position, DoubleNear(-1.0, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).k_position, DoubleNear(10.0, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at(joint).k_velocity, DoubleNear(20.0, 1e-5));
+ }
}
}
@@ -905,6 +982,10 @@ TEST_F(TestComponentParser, successfully_parse_valid_urdf_system_and_disabled_in
EXPECT_FALSE(hardware_info.limits.at("joint2").has_jerk_limits);
EXPECT_THAT(hardware_info.limits.at("joint2").max_velocity, DoubleNear(0.2, 1e-5));
EXPECT_THAT(hardware_info.limits.at("joint2").max_effort, DoubleNear(0.1, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at("joint2").max_position, DoubleNear(0.5, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at("joint2").min_position, DoubleNear(-1.5, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at("joint2").k_position, DoubleNear(10.0, 1e-5));
+ EXPECT_THAT(hardware_info.soft_limits.at("joint2").k_velocity, DoubleNear(20.0, 1e-5));
EXPECT_TRUE(hardware_info.limits.at("joint3").has_position_limits);
EXPECT_THAT(hardware_info.limits.at("joint3").min_position, DoubleNear(-M_PI, 1e-5));
@@ -1169,6 +1250,7 @@ TEST_F(TestComponentParser, successfully_parse_parameter_empty)
// Verify limits parsed from the URDF
ASSERT_THAT(hardware_info.limits, SizeIs(1));
+ ASSERT_THAT(hardware_info.soft_limits, SizeIs(0));
for (const auto & joint : {"joint1"})
{
EXPECT_TRUE(hardware_info.limits.at(joint).has_position_limits);
diff --git a/hardware_interface_testing/CHANGELOG.rst b/hardware_interface_testing/CHANGELOG.rst
index 13161cdd4c..cecc4e220c 100644
--- a/hardware_interface_testing/CHANGELOG.rst
+++ b/hardware_interface_testing/CHANGELOG.rst
@@ -2,6 +2,9 @@
Changelog for package hardware_interface_testing
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+4.10.0 (2024-05-08)
+-------------------
+
4.9.0 (2024-04-30)
------------------
* Component parser: Get mimic information from URDF (`#1256 `_)
diff --git a/hardware_interface_testing/package.xml b/hardware_interface_testing/package.xml
index df6a035c6b..737d228df3 100644
--- a/hardware_interface_testing/package.xml
+++ b/hardware_interface_testing/package.xml
@@ -1,7 +1,7 @@
hardware_interface_testing
- 4.9.0
+ 4.10.0
ros2_control hardware interface testing
Bence Magyar
Denis Štogl
diff --git a/joint_limits/CHANGELOG.rst b/joint_limits/CHANGELOG.rst
index 2bf49a13ec..dba0758112 100644
--- a/joint_limits/CHANGELOG.rst
+++ b/joint_limits/CHANGELOG.rst
@@ -2,6 +2,9 @@
Changelog for package joint_limits
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+4.10.0 (2024-05-08)
+-------------------
+
4.9.0 (2024-04-30)
------------------
diff --git a/joint_limits/CMakeLists.txt b/joint_limits/CMakeLists.txt
index f8a45f8864..ef0aab6ee5 100644
--- a/joint_limits/CMakeLists.txt
+++ b/joint_limits/CMakeLists.txt
@@ -16,6 +16,7 @@ set(THIS_PACKAGE_INCLUDE_DEPENDS
)
find_package(ament_cmake REQUIRED)
+find_package(ament_cmake_gen_version_h REQUIRED)
find_package(backward_ros REQUIRED)
foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS})
find_package(${Dependency} REQUIRED)
@@ -145,4 +146,5 @@ install(TARGETS
ament_export_targets(export_joint_limits HAS_LIBRARY_TARGET)
ament_export_dependencies(${THIS_PACKAGE_INCLUDE_DEPENDS})
ament_package()
-ament_generate_version_header(${PROJECT_NAME})
+# TODO(anyone) uncomment if https://github.com/ament/ament_cmake/pull/526 is merged
+# ament_generate_version_header(${PROJECT_NAME})
diff --git a/joint_limits/package.xml b/joint_limits/package.xml
index 3a7fd9f17f..86b1498766 100644
--- a/joint_limits/package.xml
+++ b/joint_limits/package.xml
@@ -1,6 +1,6 @@
joint_limits
- 4.9.0
+ 4.10.0
Interfaces for handling of joint limits for controllers or hardware.
Bence Magyar
@@ -13,7 +13,8 @@
https://github.com/ros-controls/ros2_control
ament_cmake
- ament_cmake_gen_version_h
+
+
backward_ros
pluginlib
@@ -23,6 +24,7 @@
urdf
trajectory_msgs
+ launch_ros
ament_cmake_gmock
ament_cmake_gtest
generate_parameter_library
diff --git a/ros2_control-not-released.jazzy.repos b/ros2_control-not-released.jazzy.repos
new file mode 100644
index 0000000000..56f46b6f79
--- /dev/null
+++ b/ros2_control-not-released.jazzy.repos
@@ -0,0 +1 @@
+repositories:
diff --git a/ros2_control.jazzy.repos b/ros2_control.jazzy.repos
new file mode 100644
index 0000000000..c93d8f4ef6
--- /dev/null
+++ b/ros2_control.jazzy.repos
@@ -0,0 +1,9 @@
+repositories:
+ ros-controls/realtime_tools:
+ type: git
+ url: https://github.com/ros-controls/realtime_tools.git
+ version: master
+ ros-controls/control_msgs:
+ type: git
+ url: https://github.com/ros-controls/control_msgs.git
+ version: master
diff --git a/ros2_control/CHANGELOG.rst b/ros2_control/CHANGELOG.rst
index aa93e8cc1b..a6df0c09e3 100644
--- a/ros2_control/CHANGELOG.rst
+++ b/ros2_control/CHANGELOG.rst
@@ -2,6 +2,9 @@
Changelog for package ros2_control
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+4.10.0 (2024-05-08)
+-------------------
+
4.9.0 (2024-04-30)
------------------
diff --git a/ros2_control/package.xml b/ros2_control/package.xml
index acb69b2069..1fa309643f 100644
--- a/ros2_control/package.xml
+++ b/ros2_control/package.xml
@@ -1,7 +1,7 @@
ros2_control
- 4.9.0
+ 4.10.0
Metapackage for ROS2 control related packages
Bence Magyar
Denis Štogl
diff --git a/ros2_control_test_assets/CHANGELOG.rst b/ros2_control_test_assets/CHANGELOG.rst
index b3ab824fea..4057ceaa9e 100644
--- a/ros2_control_test_assets/CHANGELOG.rst
+++ b/ros2_control_test_assets/CHANGELOG.rst
@@ -2,6 +2,11 @@
Changelog for package ros2_control_test_assets
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+4.10.0 (2024-05-08)
+-------------------
+* Parse URDF joint hard limits into the HardwareInfo structure (`#1472 `_)
+* Contributors: Sai Kishor Kothakota
+
4.9.0 (2024-04-30)
------------------
* Component parser: Get mimic information from URDF (`#1256 `_)
diff --git a/ros2_control_test_assets/include/ros2_control_test_assets/descriptions.hpp b/ros2_control_test_assets/include/ros2_control_test_assets/descriptions.hpp
index f5f668fd12..31e630059b 100644
--- a/ros2_control_test_assets/include/ros2_control_test_assets/descriptions.hpp
+++ b/ros2_control_test_assets/include/ros2_control_test_assets/descriptions.hpp
@@ -86,6 +86,7 @@ const auto urdf_head =
+
diff --git a/ros2_control_test_assets/package.xml b/ros2_control_test_assets/package.xml
index fe862846c4..a76e1109a8 100644
--- a/ros2_control_test_assets/package.xml
+++ b/ros2_control_test_assets/package.xml
@@ -2,7 +2,7 @@
ros2_control_test_assets
- 4.9.0
+ 4.10.0
The package provides shared test resources for ros2_control stack
Bence Magyar
diff --git a/ros2controlcli/CHANGELOG.rst b/ros2controlcli/CHANGELOG.rst
index 16946acdce..84a64c1fd8 100644
--- a/ros2controlcli/CHANGELOG.rst
+++ b/ros2controlcli/CHANGELOG.rst
@@ -2,6 +2,9 @@
Changelog for package ros2controlcli
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+4.10.0 (2024-05-08)
+-------------------
+
4.9.0 (2024-04-30)
------------------
* [CI] Specify runner/container images and codecov for joint_limits (`#1504 `_)
diff --git a/ros2controlcli/package.xml b/ros2controlcli/package.xml
index fddcd2a0b4..8e2b02b266 100644
--- a/ros2controlcli/package.xml
+++ b/ros2controlcli/package.xml
@@ -2,7 +2,7 @@
ros2controlcli
- 4.9.0
+ 4.10.0
The ROS 2 command line tools for ROS2 Control.
diff --git a/ros2controlcli/setup.py b/ros2controlcli/setup.py
index 19fb09df99..d3ce54204e 100644
--- a/ros2controlcli/setup.py
+++ b/ros2controlcli/setup.py
@@ -19,7 +19,7 @@
setup(
name=package_name,
- version="4.9.0",
+ version="4.10.0",
packages=find_packages(exclude=["test"]),
data_files=[
("share/" + package_name, ["package.xml"]),
diff --git a/rqt_controller_manager/CHANGELOG.rst b/rqt_controller_manager/CHANGELOG.rst
index 1691c2c8d5..33660cc16d 100644
--- a/rqt_controller_manager/CHANGELOG.rst
+++ b/rqt_controller_manager/CHANGELOG.rst
@@ -2,6 +2,9 @@
Changelog for package rqt_controller_manager
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+4.10.0 (2024-05-08)
+-------------------
+
4.9.0 (2024-04-30)
------------------
diff --git a/rqt_controller_manager/package.xml b/rqt_controller_manager/package.xml
index 3c0e5ce652..c722043890 100644
--- a/rqt_controller_manager/package.xml
+++ b/rqt_controller_manager/package.xml
@@ -2,7 +2,7 @@
rqt_controller_manager
- 4.9.0
+ 4.10.0
Graphical frontend for interacting with the controller manager.
Bence Magyar
Denis Štogl
diff --git a/rqt_controller_manager/setup.py b/rqt_controller_manager/setup.py
index 107981322a..ae79268335 100644
--- a/rqt_controller_manager/setup.py
+++ b/rqt_controller_manager/setup.py
@@ -20,7 +20,7 @@
setup(
name=package_name,
- version="4.9.0",
+ version="4.10.0",
packages=[package_name],
data_files=[
("share/ament_index/resource_index/packages", ["resource/" + package_name]),
diff --git a/transmission_interface/CHANGELOG.rst b/transmission_interface/CHANGELOG.rst
index 94e13cd793..8ac4224337 100644
--- a/transmission_interface/CHANGELOG.rst
+++ b/transmission_interface/CHANGELOG.rst
@@ -2,6 +2,9 @@
Changelog for package transmission_interface
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+4.10.0 (2024-05-08)
+-------------------
+
4.9.0 (2024-04-30)
------------------
* rosdoc2 for transmission_interface (`#1496 `_)
diff --git a/transmission_interface/CMakeLists.txt b/transmission_interface/CMakeLists.txt
index 98dcdcd192..185fb32d3a 100644
--- a/transmission_interface/CMakeLists.txt
+++ b/transmission_interface/CMakeLists.txt
@@ -13,6 +13,7 @@ set(THIS_PACKAGE_INCLUDE_DEPENDS
)
find_package(ament_cmake REQUIRED)
+find_package(ament_cmake_gen_version_h REQUIRED)
foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS})
find_package(${Dependency} REQUIRED)
endforeach()
diff --git a/transmission_interface/package.xml b/transmission_interface/package.xml
index 36bbbd2a38..83da85a2ef 100644
--- a/transmission_interface/package.xml
+++ b/transmission_interface/package.xml
@@ -2,7 +2,7 @@
transmission_interface
- 4.9.0
+ 4.10.0
transmission_interface contains data structures for representing mechanical transmissions, methods for propagating values between actuator and joint spaces and tooling to support this.
Bence Magyar
Denis Štogl