This repository contains support packages that can be used with real KUKA robots as well as with simulations.
If you find something confusing, not working, or would like to contribute, please read our contributing guide before opening an issue or creating a pull request.
| ROS2 Distro | Branch | Github CI |
|---|---|---|
| Jazzy | master |
|
| Humble | humble |
kuka_resourcescontains general, common files. It is copied from kuka_experimental and is ported from ROS to ROS2.kuka_agilus_supportcontains urdf, config and mesh files for KUKA Agilus robots, it is copied from kuka_experimental and ported to ROS2.kuka_cybertech_supportcontains urdf, config and mesh files for KUKA cybertech robots.kuka_fortec_supportcontains urdf, config and mesh files for KUKA fortec robots.kuka_iontec_supportcontains urdf, config and mesh files for KUKA iontec robots.kuka_quantec_supportcontains urdf, config and mesh files for KUKA quantec robots.kuka_kl_supportcontains urdf, config and mesh files for KUKA KL units.kuka_kr_moveit_configcontains configuration files for KUKA KR robots necessary for planning with MoveIt.kuka_lbr_iico_supportcontains urdf, config and mesh files for KUKA iico robots.kuka_lbr_iico_moveit_configcontains configuration files for KUKA LBR iico robots necessary for planning with MoveIt.kuka_lbr_iisy_supportcontains urdf, config and mesh files for KUKA iisy robots.kuka_lbr_iisy_moveit_configcontains configuration files for KUKA LBR iisy robots necessary for planning with MoveIt.kuka_lbr_iiwa_supportcontains urdf, config and mesh files for KUKA LBR iiwa robotskuka_lbr_iiwa_moveit_configcontains configuration files for KUKA LBR iiwa robots necessary for planning with MoveIt.kuka_mock_hardware_interfacecontains a custom mock hardware interface for KUKA robotskuka_gazebocontains a launch file to start Gazebo Ignition and an example moveit + Gazebo implementation
All support packages consist of 4 folders:
config: contains joint limits, necessary for time parametrization of trajectorieslaunch: contains launch files to be able to visualize the robot modelsmeshes: contains collision and visual meshes for the robotsurdf: contains the xacro files describing the robots, includingros2_controlintegration (with fake hardware argument)
Each robot has two specific xacro files: a macro ({robot_name}_macro.xacro) and another file instantiating this macro ({robot_name}.urdf.xacro). Additionally there is a xacro providing ros2_control integration, including the name and type of the hardware interface, hardware parameters and the supported state and command interfaces.
Additionally a transmission xacro is provided for gazebo support, but the mechanicalReduction parameters contained within are not valid, only placeholders.
The macro files contain the links and joints of the main serial chain, including transformations, rotation axes, inertial properties, joint position, velocity and effort limits and the location of the mesh files.
The macro file follows the ROS-Industrial conventions:
- link names are
link_{i} - joint names are
joint_{i} - all link and joint names have a
prefixargument - includes
baseframe: equivalent to the base frame defined by the industrial controller ($ROBROOT) - includes
flangeframe: attachment point for EEF models - includes
tool0frame: all-zeros tool frame, identical to the tool frame defined by the industrial controller ($TOOL)
All macros additionally contain a world fixed frame (without prefix). The transform from world to base_link can be given with the block parameter *origin.
All robots in the xacros are named according to the following pattern:
{kr/lbr_iisy/lbr_iiwa}{payload}_r{reach}_{version},
where version is omitted, if the official product name does not contain it. (e.g. KR 120 R3100-2 is named kr120_r3100_2 and LBR iisy 3 R760 is lbr_iisy3_r760)
The MoveIt configuration packages also contain xacros, that describe the semantic information of the robots: planning groups, default states and link-pairs, for which collision checking should not be done. The default planning group (from base_link to tool0) is named manipulator for all robot arms. An end effector, named end_effector is also defined for all robots, which enables visualising end effector paths in rviz.
To visualise the robot models, the launch files in the launch directory of the support packages can be used. These also start a joint_state_publisher_gui to enable visualisation of the robot meshes and frames with different joint configurations. However they have only visualisation purposes and cannot connect to real or fake hardware.
The frames of the main serial chain in the xacros (base_link to link_6 or link_7) follow the Denavit–Hartenberg conventions of Khalil-Dombre.
The other frames, which are added to conform to ROS-Industrial follow the conventions defined there: base and tool0 are defined to be identical to the frames on the controller, while flange follows REP-103, meaning that in default position x+ points forwards and z+ upwards.
Collision meshes are provided for the robots to speed up collision avoidance and detection calculations. These are automatically generated from the visual meshes using the Blender python API (remesh modifier) with fixed parameter values. This generation process will be fine-tuned in the future to further optimize collision calculations.
The support packages contain a joint limits file for every supported robot model, necessary time parametrization of MoveIt-planned paths. They contain the velocity limits also available in the URDF model and additional acceleration limits. Acceleration limits can never be global, these values are calculated from the worst-case ramp-up time to reach maximum velocity. The easiest way to modify the allowed velocities and accelerations is to change the velocity and acceleration scaling factors also available in the same configuration files. (The scaling factor can never be greater than 1.)
The drivers for the supported packages include a GPIO configuration file, which contains an extension for GPIO control. These xacro files can be found in <drive_package>/config/gpio_config.xacro and offer a template for users to set the desired GPIOs according to their use case. The driver uses this list to export the command and state interfaces for ROS 2 Control. If GPIO control is not required, the use_gpio tag can be set to false, preventing the GPIO configuration file from loading.
In real applications, it's likely that the description will be more complex, involving multiple objects next to the robot and optionally end effectors. It is recommended to create a new, dedicated ROS2 package specifically for managing this extended description by including the xacro of the the base robot model and extending it.
Example of attaching an end effector (with link name eef_base_link) to the flange frame, which could be defined in a different xacro file:
<joint name="${prefix}flange-${prefix}eef" type="fixed">
<origin xyz="0 0 0" rpy="0 0 0" />
<parent link="${prefix}flange" />
<child link="${prefix}eef_base_link" />
</joint>Robots marked as supporting external axes in the supported features can be composed with a rail model through shared templates.
The composition is now template-based and parameterized in kuka_resources:
- URDF template:
kuka_resources/urdf/robot_with_external_axis_template.urdf.xacro - SRDF template:
kuka_resources/srdf/robot_with_external_axis_template.srdf.xacro - MoveIt launch template wiring:
kuka_resources/launch/moveit_server_template.launch.py
This means you can use any supported combination of robot_model and kl_model via launch arguments. A separate examples repository is not required for model composition.
The URDF template composes the model in this order:
worldlink and world-to-rail base joint- external-axis links
- robot links
- external-axis joints connecting the rail to
prefix + base_link
Maintaining this order is required for a valid URDF.
Prefixing behavior for external-axis joints and links:
- External-axis names use
kl_prefix. - The global robot
prefixis applied on top ofkl_prefix. - Effective external-axis prefix is
prefix + kl_prefix.
When you change kl_prefix, replace rail_ with the new prefix in the following files:
- Using with RViz:
view_6_axis_kl_urdf.rviz - Using with MoveIt:
- The joint limits of the KL model, so e.g.
kl100_2_joint_limits.yaml moveit_controllers_6_axis_kl.yamlplanning_6_axis_kl.rviz
- The joint limits of the KL model, so e.g.
- Using with Gazebo:
fake_hardware_config_6_axis_kl.yaml
Without any external axes, the end of a robot URDF still looks like this (with robotfamily and robotmodel as placeholders):
<xacro:kuka_robotfamily_ros2_control ...>
<ext_axes_ros2_control_joints/>
</xacro:kuka_robotfamily_ros2_control>
<!-- world link -->
<link name="world"/>
<!-- robot links, joints -->
<xacro:robotmodel prefix="$(arg prefix)" package_name="kuka_robotfamily_support"/>
<!-- default world - base_link joint -->
<joint name="$(arg prefix)world-base_link" type="fixed">
<parent link="world"/>
<child link="$(arg prefix)base_link"/>
<origin xyz="$(arg x) $(arg y) $(arg z)" rpy="$(arg roll) $(arg pitch) $(arg yaw)"/>
</joint>With an external axis (KL100-2 in this example), the template composes the rail and robot as follows:
<xacro:kuka_robotfamily_ros2_control ...>
<ext_axes_ros2_control_joints>
<xacro:kuka_kl_ros2_control_joints prefix="$(arg prefix)$(arg kl_prefix)" mode="$(arg mode)"/>
</ext_axes_ros2_control_joints>
</xacro:kuka_robotfamily_ros2_control>
<!-- world link -->
<link name="world"/>
<!-- world - external-axis base joint -->
<joint name="world-$(arg prefix)$(arg kl_prefix)base_link" type="fixed">
<parent link="world"/>
<child link="$(arg prefix)$(arg kl_prefix)base_link"/>
<origin xyz="$(arg x) $(arg y) $(arg z)" rpy="$(arg roll) $(arg pitch) $(arg yaw)"/>
</joint>
<!-- external-axis links -->
<xacro:kl100_2_links prefix="$(arg prefix)$(arg kl_prefix)"/>
<!-- robot links -->
<xacro:robotmodel prefix="$(arg prefix)" package_name="kuka_robotfamily_support"/>
<!-- external-axis joints -->
<xacro:kl100_2_joints prefix="$(arg prefix)$(arg kl_prefix)" robot_base_link="$(arg prefix)base_link">
<origin xyz="$(arg x) $(arg y) $(arg z)" rpy="$(arg roll) $(arg pitch) $(arg yaw)"/>
</xacro:kl100_2_joints>To support different external axis types (prismatic and revolute), custom ros2_control joint parameters were introduced: type and is_external. An example can be found in kl_ros2_control_macro.xacro. These parameters are optional; if omitted, the driver assumes revolute internal joints.
Although these parameters increase configuration complexity, they are necessary. Without them, the driver could not correctly distinguish between internal and external joints, which is critical for the RobotSensorInterface option package. They also allow the driver to convert between ROS 2 units (meters/radians) and KUKA units (millimetres/degrees).
To support integration with third-party tracks or linear rails, the launch and xacro wiring expose these parameters:
kl_support_packagekl_ros2_control_macro_filekl_ros2_control_joints_macrokl_srdf_macro_filekl_srdf_adjacent_links_macro
These parameters are required to keep external-axis integration generic, so custom rail packages can provide their own URDF/SRDF and ros2_control macro entry points.
To demonstrate external axis integration in the KUKA ecosystem, we provide a support package for KUKA KL units, with the KL100‑2 as the first example, see the kuka_kl_support directory. This package differs from the others: instead of full URDFs, it provides xacro macros used to build a robot URDF with integrated KL units.
The following table shows what data is verified for each robot in the support packages:
| Robot name | Robot family | Transformations | Joint position limits | Joint velocity limits | Joint effort limits | Inertial values | Simplified collision meshes |
|---|---|---|---|---|---|---|---|
| lbr_iico7_r900 | lbr_iico | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| lbr_iico12_r1260 | lbr_iico | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| lbr_iisy3_r760 | lbr_iisy | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| lbr_iisy8_r930 | lbr_iisy | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| lbr_iisy11_r1300 | lbr_iisy | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| lbr_iisy15_r930 | lbr_iisy | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| lbr_iiwa14_r820 | lbr_iiwa | ✓ | ✓ | ✓ | ✓ | ||
| kr4_r600 | agilus | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr6_r700_2 | agilus | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr6_r700_sixx | agilus | ✓ | ✓ | ✓ | ✓ | ||
| kr6_r900_2 | agilus | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr6_r900_sixx | agilus | ✓ | ✓ | ✓ | ✓ | ||
| kr7_r900_3 | agilus | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr10_r900_2 | agilus | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr10_r1100_2 | agilus | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr10_r1100_3 | agilus | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr13_r900_3 | agilus | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr16_r1100_3 | agilus | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr8_r1440_2_arc_hw | cybertech | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr8_r2100_2_arc_hw | cybertech | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr12_r1450_3_hw | cybertech | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr16_r1610_2 | cybertech | ✓ | ✓ | ✓ | ✓ | ✓ | |
| kr16_r2010_2 | cybertech | ✓ | ✓ | ✓ | ✓ | ✓ | |
| kr20_r1810_2 | cybertech | ✓ | ✓ | ✓ | ✓ | ✓ | |
| kr20_r1820_2_e | cybertech | ✓ | ✓ | ✓ | ✓ | ✓ | |
| kr35_r1840_3_hw | cybertech | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr20_r3100 | iontec | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr30_r2100 | iontec | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr50_r2500 | iontec | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr50_r2100 | iontec | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr70_r2100 | iontec | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr120_r2700_2 | quantec | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr150_r3100 | quantec | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr210_r2700_2 | quantec | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr210_r3100_2 | quantec | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr210_r3100_ultra | quantec | ✓ | ✓ | ✓ | ✓ | ✓ | |
| kr210_r3300_2_k | quantec | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr240_r2900_2 | quantec | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr300_r2700_2 | quantec | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr240_r3330 | fortec | ✓ | ✓ | ✓ | ✓ | ✓ | |
| kr300_r2800_2_mt | fortec | ✓ | ✓ | ✓ | ✓ | ✓ | |
| kr340_r3400_2 | fortec | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr360_r2830 | fortec | ✓ | ✓ | ✓ | ✓ | ✓ | |
| kr500_r2800_2 | fortec | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr560_r3100_2 | fortec | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr600_r2830 | fortec | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kr800_r2800_2 | fortec | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
The following table shows the supported customizable features for each robot in the support packages:
| Robot name | Robot family | GPIO support | External axis support | Gazebo support |
|---|---|---|---|---|
| lbr_iico7_r900 | lbr_iico | ✓ | ✓ | ✓ |
| lbr_iico12_r1260 | lbr_iico | ✓ | ✓ | ✓ |
| lbr_iisy3_r760 | lbr_iisy | ✓ | ✓ | |
| lbr_iisy8_r930 | lbr_iisy | ✓ | ✓ | ✓ |
| lbr_iisy11_r1300 | lbr_iisy | ✓ | ✓ | |
| lbr_iisy15_r930 | lbr_iisy | ✓ | ✓ | |
| lbr_iiwa14_r820 | lbr_iiwa | ✓ | ||
| kr4_r600 | agilus | ✓ | ✓ | ✓ |
| kr6_r700_2 | agilus | ✓ | ✓ | ✓ |
| kr6_r700_sixx | agilus | ✓ | ✓ | |
| kr6_r900_2 | agilus | ✓ | ✓ | ✓ |
| kr6_r900_sixx | agilus | ✓ | ✓ | |
| kr7_r900_3 | agilus | ✓ | ✓ | ✓ |
| kr10_r900_2 | agilus | ✓ | ✓ | ✓ |
| kr10_r1100_2 | agilus | ✓ | ✓ | ✓ |
| kr10_r1100_3 | agilus | ✓ | ✓ | ✓ |
| kr13_r900_3 | agilus | ✓ | ✓ | ✓ |
| kr16_r1100_3 | agilus | ✓ | ✓ | ✓ |
| kr8_r1440_2_arc_hw | cybertech | ✓ | ✓ | ✓ |
| kr8_r2100_2_arc_hw | cybertech | ✓ | ✓ | ✓ |
| kr12_r1450_3_hw | cybertech | ✓ | ✓ | ✓ |
| kr16_r1610_2 | cybertech | ✓ | ✓ | |
| kr16_r2010_2 | cybertech | ✓ | ✓ | |
| kr20_r1810_2 | cybertech | ✓ | ✓ | |
| kr20_r1820_2_e | cybertech | ✓ | ✓ | ✓ |
| kr35_r1840_3_hw | cybertech | ✓ | ✓ | ✓ |
| kr20_r3100 | iontec | ✓ | ✓ | ✓ |
| kr30_r2100 | iontec | ✓ | ✓ | ✓ |
| kr50_r2500 | iontec | ✓ | ✓ | ✓ |
| kr50_r2100 | iontec | ✓ | ✓ | ✓ |
| kr70_r2100 | iontec | ✓ | ✓ | ✓ |
| kr120_r2700_2 | quantec | ✓ | ✓ | ✓ |
| kr210_r2700_2 | quantec | ✓ | ✓ | ✓ |
| kr210_r3100_2 | quantec | ✓ | ✓ | ✓ |
| kr210_r3100_ultra | quantec | ✓ | ✓ | |
| kr210_r3300_2_k | quantec | ✓ | ✓ | ✓ |
| kr240_r2900_2 | quantec | ✓ | ✓ | ✓ |
| kr300_r2700_2 | quantec | ✓ | ✓ | ✓ |
| kr240_r3330 | fortec | ✓ | ✓ | |
| kr300_r2800_2_mt | fortec | ✓ | ✓ | ✓ |
| kr340_r3400_2 | fortec | ✓ | ✓ | ✓ |
| kr360_r2830 | fortec | ✓ | ✓ | |
| kr500_r2800_2 | fortec | ✓ | ✓ | ✓ |
| kr560_r3100_2 | fortec | ✓ | ✓ | ✓ |
| kr600_r2830 | fortec | ✓ | ✓ | ✓ |
| kr800_r2800_2 | fortec | ✓ | ✓ | ✓ |
The repository also contains a mock hardware interface implementation, that extends the mock_components::GenericSystem defined in the hardware_interface package.
This is necessary, as the driver workflow also activates controllers, which is possible only if all of the interfaces claimed by the controller is provided by the hardware interface. This would not be the case for the default GenericSystem, therefore all of the custom state and command interfaces used by the drivers are exported by the KukaMockHardwareInterface.
Additionally two hardware parameters are added:
- To support similar timing behaviour as the actual robots, the mock hardware was extended with a blocking wait, so that the read function does not return immediately, but cyclically. The frequency of the loops is defined by the
cycle_time_msparameter. Default value is 4 [ms]. - To be able to test whether a specific setup would fit into the roundtrip time enforced by a real robot, the
roundtrip_time_microparameter can be used. If thewrite()method is not called before the given timeout is exceeded (starting from the previousread()function), a warning message is logged (but the return value of thewrite()will be still SUCCESS). Default value is 0 [us], which means, that the roundtrip time should not be monitored. If such a warning is triggered, two root causes are possible: - Controller update takes too long, therefore
writeis also delayed - Scheduling jitter is high, main thread could not wake up in time
The mock hardware was implemented in this repository to allow testing moveit capabilities for the robots without having to build the driver code.
The repository distinguishes between three modes through a mode parameter: hardware, mock, and gazebo:
hardwaremode should be used to set up robot descriptions to use a specific KUKA drivermockoption specifies the use of the mock hardware interfacegazebooption configures Gazebo's hardware interface
To start rviz with the motion planning plugin using fake hardware, the following launch files can be used:
ros2 launch kuka_kr_moveit_config moveit_planning_fake_hardware.launch.pyMatching robot_model and robot_family arguments can be added after the command (e.g. robot_model:=kr16_r2010_2 robot_family:=cybertech). The default robot model is kr6_r700_sixx
ros2 launch kuka_lbr_iisy_moveit_config moveit_planning_fake_hardware.launch.pyros2 launch kuka_lbr_iiwa_moveit_config moveit_planning_fake_hardware.launch.pyA robot_model argument can be added after the command (e.g. robot_model:=lbr_iisy11_r1300). The default robot model is lbr_iisy3_r760
These launch files do not use the actual driver implementation. Instead, they start RViz, the move_group server, and a ros2_control_node configured with fake hardware, along with the joint_state_broadcaster and joint_trajectory_controller controllers. The server can accept planning requests from the MoveIt plugin or directly from user code. An example of how to create such a request in C++ can be found in the moveit_example package in the examples repository.
It is also possible to plan with moveit for robots spawned in Gazebo, first the gazebo_startup launch file has to be started. By default, the launch file will start the gazebo server with UI and spawn the lbr_iisy3_r760 robot model with the mode parameter set to gazebo. To launch Gazebo with a different robot model and family (KR 210 R2700-2 in the example), the following command can be used:
ros2 launch kuka_gazebo gazebo_startup.launch.py robot_model:=kr210_r2700_2 robot_family:=quantecStarting the launch file also starts the joint_trajectory_controller, which claims the position command interface and joint_state_broadcaster. Once Gazebo is launched, the move group server can be started as well, with the use_sim_time argument set to True:
ros2 launch kuka_kr_moveit_config moveit_server.launch.py robot_model:=kr210_r2700_2 robot_family:=quantec use_sim_time:=TrueThe moveit server will be able to accept planning requests from the rviz plugin or from code, similarly to the mock hardware. The same launch file can also be used to start a moveit server that will command real robots.
Note: LBR iiwa robots do not have acceleration limits available, therefore planning currently fails for them.
The tests for Gazebo-supported robots have been successfully integrated into the existing Continuous Integration (CI) architecture. The testing process follows a two-part structure, as illustrated in the diagram below.
Immediately after the build phase (but before the testing phase), the CI pipeline triggers the after_build_hook.sh script. This script launches run_gazebo_tests.py, which performs the following tasks:
- Reads robot names and families from the
README.mdfile to ensure all Gazebo-supported robots are included. - Executes parameterized tests using
gazebo_support_test.py, a parametrized test script, which:- Launches Gazebo in headless mode, launching the server and the ros gazebo bridge separately.
- Checks whether the simulation successfully configures and activates:
- Hardware interfaces
- Joint State Broadcaster
- Joint Trajectory Controller
- Collects and returns the test results.
However, Gazebo does not shut down automatically after the test. To handle this, run_gazebo_tests.py manually terminates Gazebo using a kill command. Since invoking kill within a test causes an automatic test failure, this step is performed outside the testing framework.
To bridge the gap between the actual Gazebo tests and the CI testing framework, we use the following solution:
- Test results are written to a text file:
gazebo_test.txt. - During the testing phase,
test_gazebo_robot_support.pyreads and evaluates the results from this file. - To keep the test alive long enough for evaluation, we launch
gazebo_test_keep_alive.cpp, a simple publisher node that ensures the test file remains active.
Note: The current test uses Gazebo Harmonic (Gazebo Sim v8.9.0) for verification.
graph TD
%% CI Pipeline Section
subgraph CI Pipeline
A[industrial_ci.yml] -->|runs| B[after_build_hook.sh]
A -->|Colcon test runs| H[test_gazebo_robot_support.py]
H -->|Launch| J[gazebo_test_keep_alive.cpp]
J -->|Keep alive| H
end
%% Test Execution Section
subgraph Test Execution
B -->|executes| C[run_gazebo_tests.py]
F[README.md] -->|reads robot, family| C
C -->|executes test| D[gazebo_support_test.py]
K[bridge_config.yaml] -->|configures bridge| E
D -->|launch ros_gz_bridge| E[ros_gz_bridge.launch.py]
D -->|launch Gazebo server| I[gz_server.launch.py]
E -->|translate| L[headless Gazebo]
I -->|launches server| L
L -->|outputs to| D
C -->|KILL| L
D -->|returns result| C
C -->|writes results| G[gazebo_test.txt]
G -->|test results| H
end