Skip to content

Commit 79b53d8

Browse files
authored
[example_5] Update demo to illustrate the usage of wrench transformer (#972)
1 parent b44a93b commit 79b53d8

File tree

4 files changed

+147
-43
lines changed

4 files changed

+147
-43
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fts_wrench_transformer:
2+
ros__parameters:
3+
target_frames:
4+
- "base_link"
5+
- "link1"
6+
tf_timeout: 0.1

example_5/bringup/launch/rrbot_system_with_external_sensor.launch.py

Lines changed: 45 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@
1313
# limitations under the License.
1414

1515
from launch import LaunchDescription
16-
from launch.actions import DeclareLaunchArgument, RegisterEventHandler
16+
from launch.actions import DeclareLaunchArgument
1717
from launch.conditions import IfCondition
18-
from launch.event_handlers import OnProcessExit
19-
from launch.substitutions import Command, FindExecutable, LaunchConfiguration, PathJoinSubstitution
18+
from launch.substitutions import Command, LaunchConfiguration, PathSubstitution
2019

2120
from launch_ros.actions import Node
2221
from launch_ros.substitutions import FindPackageShare
@@ -61,26 +60,30 @@ def generate_launch_description():
6160
description="Start RViz2 automatically with this launch file.",
6261
)
6362
)
63+
declared_arguments.append(
64+
DeclareLaunchArgument(
65+
"use_wrench_transformer",
66+
default_value="false",
67+
description="Enable the wrench transformer node to transform wrench messages to different frames.",
68+
)
69+
)
6470

6571
# Initialize Arguments
6672
prefix = LaunchConfiguration("prefix")
6773
use_mock_hardware = LaunchConfiguration("use_mock_hardware")
6874
mock_sensor_commands = LaunchConfiguration("mock_sensor_commands")
6975
slowdown = LaunchConfiguration("slowdown")
7076
gui = LaunchConfiguration("gui")
77+
use_wrench_transformer = LaunchConfiguration("use_wrench_transformer")
7178

7279
# Get URDF via xacro
7380
robot_description_content = Command(
7481
[
75-
PathJoinSubstitution([FindExecutable(name="xacro")]),
82+
"xacro",
7683
" ",
77-
PathJoinSubstitution(
78-
[
79-
FindPackageShare("ros2_control_demo_example_5"),
80-
"urdf",
81-
"rrbot_system_with_external_sensor.urdf.xacro",
82-
]
83-
),
84+
PathSubstitution(FindPackageShare("ros2_control_demo_example_5"))
85+
/ "urdf"
86+
/ "rrbot_system_with_external_sensor.urdf.xacro",
8487
" ",
8588
"prefix:=",
8689
prefix,
@@ -97,15 +100,18 @@ def generate_launch_description():
97100
)
98101
robot_description = {"robot_description": robot_description_content}
99102

100-
robot_controllers = PathJoinSubstitution(
101-
[
102-
FindPackageShare("ros2_control_demo_example_5"),
103-
"config",
104-
"rrbot_with_external_sensor_controllers.yaml",
105-
]
103+
robot_controllers = (
104+
PathSubstitution(FindPackageShare("ros2_control_demo_example_5"))
105+
/ "config"
106+
/ "rrbot_with_external_sensor_controllers.yaml"
106107
)
107-
rviz_config_file = PathJoinSubstitution(
108-
[FindPackageShare("ros2_control_demo_example_5"), "rviz", "rrbot.rviz"]
108+
wrench_transformer_params = (
109+
PathSubstitution(FindPackageShare("ros2_control_demo_example_5"))
110+
/ "config"
111+
/ "wrench_transformer_params.yaml"
112+
)
113+
rviz_config_file = (
114+
PathSubstitution(FindPackageShare("ros2_control_demo_example_5")) / "rviz" / "rrbot.rviz"
109115
)
110116

111117
control_node = Node(
@@ -148,29 +154,26 @@ def generate_launch_description():
148154
arguments=["fts_broadcaster", "--param-file", robot_controllers],
149155
)
150156

151-
# Delay rviz start after `joint_state_broadcaster`
152-
delay_rviz_after_joint_state_broadcaster_spawner = RegisterEventHandler(
153-
event_handler=OnProcessExit(
154-
target_action=joint_state_broadcaster_spawner,
155-
on_exit=[rviz_node],
156-
)
157+
# add the wrench transformer node (optional)
158+
wrench_transformer_node = Node(
159+
package="force_torque_sensor_broadcaster",
160+
executable="wrench_transformer_node",
161+
name="fts_wrench_transformer",
162+
parameters=[wrench_transformer_params],
163+
remappings=[("~/wrench", "/fts_broadcaster/wrench")],
164+
output="both",
165+
condition=IfCondition(use_wrench_transformer),
157166
)
158167

159-
# Delay start of robot_controller after `joint_state_broadcaster`
160-
delay_robot_controller_spawner_after_joint_state_broadcaster_spawner = RegisterEventHandler(
161-
event_handler=OnProcessExit(
162-
target_action=joint_state_broadcaster_spawner,
163-
on_exit=[robot_controller_spawner],
164-
)
168+
return LaunchDescription(
169+
declared_arguments
170+
+ [
171+
control_node,
172+
robot_state_pub_node,
173+
rviz_node,
174+
joint_state_broadcaster_spawner,
175+
robot_controller_spawner,
176+
fts_broadcaster_spawner,
177+
wrench_transformer_node,
178+
]
165179
)
166-
167-
nodes = [
168-
control_node,
169-
robot_state_pub_node,
170-
joint_state_broadcaster_spawner,
171-
delay_rviz_after_joint_state_broadcaster_spawner,
172-
delay_robot_controller_spawner_after_joint_state_broadcaster_spawner,
173-
fts_broadcaster_spawner,
174-
]
175-
176-
return LaunchDescription(declared_arguments + nodes)

example_5/doc/userdoc.rst

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ Tutorial steps
4747
If you can see two orange and one yellow rectangle in in *RViz* everything has started properly.
4848
Still, to be sure, let's introspect the control system before moving *RRBot*.
4949

50+
.. note::
51+
52+
The launch file supports an optional ``use_wrench_transformer`` argument to enable the wrench transformer node.
53+
See step 7 for details on using the wrench transformer feature.
54+
5055
3. Check if the hardware interface loaded properly, by opening another terminal and executing
5156

5257
.. code-block:: shell
@@ -163,11 +168,66 @@ Tutorial steps
163168
:width: 400
164169
:alt: Revolute-Revolute Manipulator Robot with wrench visualization
165170

171+
7. Access transformed wrench data in different frames using the *Wrench Transformer Node* (optional):
172+
173+
The launch file can optionally start a ``wrench_transformer_node`` that transforms wrench messages
174+
from the sensor frame (``tool_link``) to other target frames using TF2. This feature is disabled by default.
175+
176+
To enable the wrench transformer, launch with the ``use_wrench_transformer`` argument:
177+
178+
.. code-block:: shell
179+
180+
ros2 launch ros2_control_demo_example_5 rrbot_system_with_external_sensor.launch.py use_wrench_transformer:=true
181+
182+
Once enabled, check available transformed wrench topics:
183+
184+
.. code-block:: shell
185+
186+
ros2 topic list | grep wrench
187+
188+
You should see topics like:
189+
190+
.. code-block:: shell
191+
192+
/fts_wrench_transformer/base_link/wrench
193+
/fts_wrench_transformer/link1/wrench
194+
195+
View transformed wrench data in the ``base_link`` frame:
196+
197+
.. code-block:: shell
198+
199+
ros2 topic echo /fts_wrench_transformer/base_link/wrench
200+
201+
The transformed wrench messages will have the same structure as the original wrench, but with
202+
``frame_id`` set to the target frame (e.g., ``base_link``) and the force/torque values transformed
203+
to that coordinate frame.
204+
205+
.. code-block:: shell
206+
207+
header:
208+
stamp:
209+
sec: 1676444704
210+
nanosec: 332221422
211+
frame_id: base_link
212+
wrench:
213+
force:
214+
x: <transformed_value>
215+
y: <transformed_value>
216+
z: <transformed_value>
217+
torque:
218+
x: <transformed_value>
219+
y: <transformed_value>
220+
z: <transformed_value>
221+
222+
The wrench transformer configuration can be customized in the parameter file:
223+
`wrench_transformer_params.yaml <https://github.com/ros-controls/ros2_control_demos/tree/{REPOS_FILE_BRANCH}/example_5/bringup/config/wrench_transformer_params.yaml>`__
224+
166225
Files used for this demos
167226
--------------------------
168227

169228
* Launch file: `rrbot_system_with_external_sensor.launch.py <https://github.com/ros-controls/ros2_control_demos/tree/{REPOS_FILE_BRANCH}/example_5/bringup/launch/rrbot_system_with_external_sensor.launch.py>`__
170229
* Controllers yaml: `rrbot_with_external_sensor_controllers.yaml <https://github.com/ros-controls/ros2_control_demos/tree/{REPOS_FILE_BRANCH}/example_5/bringup/config/rrbot_with_external_sensor_controllers.yaml>`__
230+
* Wrench transformer params: `wrench_transformer_params.yaml <https://github.com/ros-controls/ros2_control_demos/tree/{REPOS_FILE_BRANCH}/example_5/bringup/config/wrench_transformer_params.yaml>`__
171231
* URDF: `rrbot_with_external_sensor_controllers.urdf.xacro <https://github.com/ros-controls/ros2_control_demos/blob/{REPOS_FILE_BRANCH}/example_5/description/urdf/rrbot_system_with_external_sensor.urdf.xacro>`__
172232

173233
* Description: `rrbot_description.urdf.xacro <https://github.com/ros-controls/ros2_control_demos/tree/{REPOS_FILE_BRANCH}/ros2_control_demo_description/rrbot/urdf/rrbot_description.urdf.xacro>`__

example_5/test/test_rrbot_system_with_external_sensor_launch.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040

4141
import launch_testing.markers
4242
import rclpy
43+
from geometry_msgs.msg import WrenchStamped
44+
from launch_testing_ros import WaitForTopics
4345
from controller_manager.test_utils import (
4446
check_controllers_running,
4547
check_if_js_published,
@@ -57,7 +59,7 @@ def generate_test_description():
5759
"launch/rrbot_system_with_external_sensor.launch.py",
5860
)
5961
),
60-
launch_arguments={"gui": "false"}.items(),
62+
launch_arguments={"gui": "false", "use_wrench_transformer": "true"}.items(),
6163
)
6264

6365
return LaunchDescription([launch_include, ReadyToTest()])
@@ -92,6 +94,39 @@ def test_controller_running(self, proc_output):
9294
def test_check_if_msgs_published(self):
9395
check_if_js_published("/joint_states", ["joint1", "joint2"])
9496

97+
def test_wrench_transformer_node_start(self, proc_output):
98+
"""Test that the wrench transformer node starts when use_wrench_transformer is true."""
99+
check_node_running(self.node, "fts_wrench_transformer")
100+
101+
def test_wrench_transformer_publishes(self):
102+
"""Test that the wrench transformer publishes messages to transformed wrench topics."""
103+
104+
expected_topic = "/fts_wrench_transformer/base_link/wrench"
105+
expected_frame_id = "base_link"
106+
107+
wait_for_topics = WaitForTopics(
108+
[(expected_topic, WrenchStamped)],
109+
timeout=5.0,
110+
)
111+
self.assertTrue(
112+
wait_for_topics.wait(),
113+
f"No messages received on {expected_topic} topic",
114+
)
115+
116+
# Verify the message has the expected frame_id
117+
received_messages = wait_for_topics.received_messages(expected_topic)
118+
self.assertGreater(
119+
len(received_messages),
120+
0,
121+
f"No messages received on {expected_topic} topic",
122+
)
123+
self.assertEqual(
124+
received_messages[0].header.frame_id,
125+
expected_frame_id,
126+
f"Wrench message frame_id should be {expected_frame_id}",
127+
)
128+
wait_for_topics.shutdown()
129+
95130

96131
@launch_testing.post_shutdown_test()
97132
# These tests are run after the processes in generate_test_description() have shutdown.

0 commit comments

Comments
 (0)