Skip to content

Commit a6a4df8

Browse files
author
Yara Shahin
committed
add vda5050 safety state broadcaster
1 parent 34c5ff3 commit a6a4df8

12 files changed

+1071
-0
lines changed

ros2_controllers/package.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
<exec_depend>steering_controllers_library</exec_depend>
4242
<exec_depend>tricycle_controller</exec_depend>
4343
<exec_depend>tricycle_steering_controller</exec_depend>
44+
<exec_depend>vda5050_safety_state_broadcaster</exec_depend>
4445
<exec_depend>velocity_controllers</exec_depend>
4546

4647
<export>
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
cmake_minimum_required(VERSION 3.8)
2+
project(vda5050_safety_state_broadcaster)
3+
4+
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
5+
add_compile_options(-Wall -Wextra -Werror=conversion -Werror=unused-but-set-variable -Werror=return-type -Werror=shadow)
6+
endif()
7+
8+
set(THIS_PACKAGE_INCLUDE_DEPENDS
9+
builtin_interfaces
10+
control_msgs
11+
controller_interface
12+
generate_parameter_library
13+
pluginlib
14+
rclcpp_lifecycle
15+
realtime_tools
16+
urdf
17+
)
18+
19+
find_package(ament_cmake REQUIRED)
20+
foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS})
21+
find_package(${Dependency} REQUIRED)
22+
endforeach()
23+
24+
generate_parameter_library(vda5050_safety_state_broadcaster_parameters
25+
src/vda5050_safety_state_broadcaster.yaml
26+
)
27+
28+
add_library(vda5050_safety_state_broadcaster SHARED
29+
src/vda5050_safety_state_broadcaster.cpp
30+
)
31+
32+
target_compile_features(vda5050_safety_state_broadcaster PUBLIC cxx_std_17)
33+
target_include_directories(vda5050_safety_state_broadcaster
34+
PUBLIC
35+
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
36+
$<INSTALL_INTERFACE:include/vda5050_safety_state_broadcaster>
37+
)
38+
target_link_libraries(vda5050_safety_state_broadcaster PUBLIC
39+
vda5050_safety_state_broadcaster_parameters
40+
controller_interface::controller_interface
41+
pluginlib::pluginlib
42+
rclcpp::rclcpp
43+
rclcpp_lifecycle::rclcpp_lifecycle
44+
realtime_tools::realtime_tools
45+
${control_msgs_TARGETS}
46+
${builtin_interfaces_TARGETS})
47+
48+
49+
pluginlib_export_plugin_description_file(controller_interface vda5050_safety_state_broadcaster.xml)
50+
51+
if(BUILD_TESTING)
52+
find_package(ament_cmake_gmock REQUIRED)
53+
find_package(controller_manager REQUIRED)
54+
find_package(hardware_interface REQUIRED)
55+
find_package(ros2_control_test_assets REQUIRED)
56+
57+
add_definitions(-DTEST_FILES_DIRECTORY="${CMAKE_CURRENT_SOURCE_DIR}/test")
58+
ament_add_gmock(test_load_vda5050_safety_state_broadcaster test/test_load_vda5050_safety_state_broadcaster.cpp)
59+
target_include_directories(test_load_vda5050_safety_state_broadcaster PRIVATE include)
60+
target_link_libraries(test_load_vda5050_safety_state_broadcaster
61+
vda5050_safety_state_broadcaster
62+
controller_manager::controller_manager
63+
ros2_control_test_assets::ros2_control_test_assets
64+
)
65+
66+
add_rostest_with_parameters_gmock(test_vda5050_safety_state_broadcaster
67+
test/test_vda5050_safety_state_broadcaster.cpp
68+
${CMAKE_CURRENT_SOURCE_DIR}/test/vda5050_safety_state_broadcaster_params.yaml)
69+
target_include_directories(test_vda5050_safety_state_broadcaster PRIVATE include)
70+
target_link_libraries(test_vda5050_safety_state_broadcaster
71+
vda5050_safety_state_broadcaster
72+
)
73+
endif()
74+
75+
install(
76+
DIRECTORY include/
77+
DESTINATION include/vda5050_safety_state_broadcaster
78+
)
79+
install(
80+
TARGETS
81+
vda5050_safety_state_broadcaster
82+
vda5050_safety_state_broadcaster_parameters
83+
EXPORT export_vda5050_safety_state_broadcaster
84+
RUNTIME DESTINATION bin
85+
ARCHIVE DESTINATION lib
86+
LIBRARY DESTINATION lib
87+
)
88+
89+
ament_export_targets(export_vda5050_safety_state_broadcaster HAS_LIBRARY_TARGET)
90+
ament_export_dependencies(${THIS_PACKAGE_INCLUDE_DEPENDS})
91+
ament_package()
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
:github_url: https://github.com/ros-controls/ros2_controllers/blob/{REPOS_FILE_BRANCH}/vda5050_safety_state_broadcaster/doc/userdoc.rst
2+
3+
.. _vda5050_safety_state_broadcaster_userdoc:
4+
5+
VDA5050 Safety State Broadcaster
6+
--------------------------------
7+
The *VDA5050 Safety State Broadcaster* publishes safety state information as ``control_msgs/msg/VDA5050SafetyState`` messages, as defined by the VDA5050 standard.
8+
9+
It reads safety-related state interfaces from a ros2_control system and exposes them in a standard ROS 2 message format. This enables easy integration with VDA5050-compliant systems, safety monitoring, and higher-level fleet management.
10+
11+
Interfaces
12+
^^^^^^^^^^
13+
14+
The broadcaster can read the following state interfaces, configured via parameters:
15+
16+
- ``fieldViolation_interfaces`` (string_array)
17+
- ``eStop_manual_interfaces`` (string_array)
18+
- ``eStop_remote_interfaces`` (string_array)
19+
- ``eStop_autoack_interfaces`` (string_array)
20+
21+
Published Topics
22+
^^^^^^^^^^^^^^^^
23+
24+
The broadcaster publishes the following topic:
25+
26+
- ``~/vda5050_safety_state`` (``control_msgs/msg/VDA5050SafetyState``)
27+
Publishes the **combined safety state** of the system, reflecting the current field violation and E-stop status according to the configured interfaces and their priorities.
28+
29+
Message Fields
30+
^^^^^^^^^^^^^^
31+
32+
The published ``VDA5050SafetyState`` message contains:
33+
34+
+--------------------+-------------------------------------------------------------------------------------------------------+
35+
| Field | Description |
36+
+====================+=======================================================================================================+
37+
| ``field_violation``| True if any field violation interface is active |
38+
+--------------------+-------------------------------------------------------------------------------------------------------+
39+
| ``e_stop`` | E-stop state, one of: |
40+
| | |
41+
| | - ``none``: No E-stop active |
42+
| | - ``manual``: Any Manual E-stop Interface triggered |
43+
| | - ``remote``: Any Remote E-stop Interface triggered and manual is not active |
44+
| | - ``autoAck``: Any Auto-acknowledged E-stop Interface triggered and manual and remote are not active |
45+
+--------------------+-------------------------------------------------------------------------------------------------------+
46+
47+
The E-stop state is determined by the first active interface in the following priority:
48+
``manual > remote > autoAck > none``.
49+
50+
Parameters
51+
^^^^^^^^^^
52+
This controller uses the `generate_parameter_library <https://github.com/PickNikRobotics/generate_parameter_library>`_ to manage parameters.
53+
The parameter `definition file <https://github.com/ros-controls/ros2_controllers/blob/{REPOS_FILE_BRANCH}/vda5050_safety_state_broadcaster/src/vda5050_safety_state_broadcaster.yaml>`_ contains the full list and descriptions.
54+
55+
List of parameters
56+
==================
57+
.. generate_parameter_library_details:: ../src/vda5050_safety_state_broadcaster.yaml
58+
59+
Example Parameter File
60+
======================
61+
62+
An example parameter file for this controller is available in the `test directory <https://github.com/ros-controls/ros2_controllers/blob/{REPOS_FILE_BRANCH}/vda5050_safety_state_broadcaster/test/vda5050_safety_state_broadcaster_params.yaml>`_:
63+
64+
.. literalinclude:: ../test/vda5050_safety_state_broadcaster_params.yaml
65+
:language: yaml
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// Copyright (c) 2025, b-robotized
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
//
16+
// Source of this file are templates in
17+
// [RosTeamWorkspace](https://github.com/StoglRobotics/ros_team_workspace) repository.
18+
//
19+
20+
#ifndef VDA5050_SAFETY_STATE_BROADCASTER__VDA5050_SAFETY_STATE_BROADCASTER_HPP_
21+
#define VDA5050_SAFETY_STATE_BROADCASTER__VDA5050_SAFETY_STATE_BROADCASTER_HPP_
22+
23+
#include <memory>
24+
#include <string>
25+
#include <unordered_map>
26+
#include <vector>
27+
28+
#include "controller_interface/controller_interface.hpp"
29+
#include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp"
30+
#include "rclcpp_lifecycle/state.hpp"
31+
#include "realtime_tools/realtime_buffer.hpp"
32+
#include "realtime_tools/realtime_publisher.hpp"
33+
34+
#include <vda5050_safety_state_broadcaster/vda5050_safety_state_broadcaster_parameters.hpp>
35+
#include "control_msgs/msg/vda5050_safety_state.hpp"
36+
37+
namespace vda5050_safety_state_broadcaster
38+
{
39+
40+
/**
41+
* \brief VDA5050 safety state broadcaster for all or some state in a ros2_control system.
42+
*
43+
* Vda5050SafetyStateBroadcaster publishes state interfaces from ros2_control as ROS messages.
44+
* The state interfaces published can be configured via parameters:
45+
*
46+
* \param fieldViolation_interfaces that are used to acknowledge field violation events by setting
47+
* the interface to 1.0.
48+
* \param eStop_manual_interfaces that are used to manually acknowledge eStop events by setting the
49+
* interface to 1.0.
50+
* \param eStop_remote_interfaces that are used to remotely acknowledge eStop events by setting the
51+
* interface to 1.0.
52+
* \param eStop_autoack_interfaces that are used to autoacknowledge eStop events by setting the
53+
* interface to 1.0.
54+
*
55+
* Publishes to:
56+
*
57+
* - \b vda5050_safety_state (control_msgs::msg::VDA5050SafetyState): safety state of the combined
58+
* safety interfaces according the priority: eStop_manual > eStop_remote > eStop_autoack.
59+
*
60+
*/
61+
class Vda5050SafetyStateBroadcaster : public controller_interface::ControllerInterface
62+
{
63+
public:
64+
Vda5050SafetyStateBroadcaster();
65+
66+
controller_interface::InterfaceConfiguration command_interface_configuration() const override;
67+
68+
controller_interface::InterfaceConfiguration state_interface_configuration() const override;
69+
70+
controller_interface::CallbackReturn on_init() override;
71+
72+
controller_interface::CallbackReturn on_configure(
73+
const rclcpp_lifecycle::State & previous_state) override;
74+
75+
controller_interface::CallbackReturn on_activate(
76+
const rclcpp_lifecycle::State & previous_state) override;
77+
78+
controller_interface::CallbackReturn on_deactivate(
79+
const rclcpp_lifecycle::State & previous_state) override;
80+
81+
controller_interface::return_type update(
82+
const rclcpp::Time & time, const rclcpp::Duration & period) override;
83+
84+
protected:
85+
vda5050_safety_state_broadcaster::Params params_;
86+
87+
std::shared_ptr<realtime_tools::RealtimePublisher<control_msgs::msg::VDA5050SafetyState>>
88+
realtime_vda5050_safety_state_publisher_;
89+
90+
private:
91+
std::shared_ptr<vda5050_safety_state_broadcaster::ParamListener> param_listener_;
92+
std::shared_ptr<rclcpp::Publisher<control_msgs::msg::VDA5050SafetyState>>
93+
vda5050_safety_state_publisher_;
94+
95+
/**
96+
* @brief Determines the current E-stop state based on the state interfaces.
97+
* @return The E-stop type as defined in control_msgs::msg::VDA5050SafetyState.
98+
*/
99+
control_msgs::msg::VDA5050SafetyState::_e_stop_type determineEstopState();
100+
101+
struct InterfaceIds
102+
{
103+
int manual_start = 0;
104+
int remote_start = 0;
105+
int autoack_start = 0;
106+
int total_interfaces = 0;
107+
};
108+
109+
InterfaceIds itfs_ids_;
110+
bool fieldViolation_value = false;
111+
std::string estop_msg = control_msgs::msg::VDA5050SafetyState::NONE;
112+
113+
/**
114+
* @brief Safely converts a double value to bool, treating NaN as false.
115+
* @param value The double value to convert.
116+
* @return true if value is not NaN and not zero, false otherwise.
117+
*/
118+
bool safe_double_to_bool(double value) const
119+
{
120+
if (std::isnan(value))
121+
{
122+
return false;
123+
}
124+
return value != 0.0;
125+
}
126+
};
127+
128+
} // namespace vda5050_safety_state_broadcaster
129+
130+
#endif // VDA5050_SAFETY_STATE_BROADCASTER__VDA5050_SAFETY_STATE_BROADCASTER_HPP_
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?xml version="1.0"?>
2+
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
3+
<package format="3">
4+
<name>vda5050_safety_state_broadcaster</name>
5+
<version>0.1.0</version>
6+
<description>ros2 control VDA5050 safety state broadcaster</description>
7+
8+
<maintainer email="bence.magyar.robotics@gmail.com">Bence Magyar</maintainer>
9+
<maintainer email="denis.stogl@b-robotized.com">Denis Štogl</maintainer>
10+
<maintainer email="christoph.froehlich@ait.ac.at">Christoph Froehlich</maintainer>
11+
<maintainer email="sai.kishor@pal-robotics.com">Sai Kishor Kothakota</maintainer>
12+
13+
<license>Apache License 2.0</license>
14+
15+
<url type="website">https://control.ros.org</url>
16+
<url type="bugtracker">https://github.com/ros-controls/ros2_controllers/issues</url>
17+
<url type="repository">https://github.com/ros-controls/ros2_controllers/</url>
18+
19+
<author email="yara.shahin@b-robotized.com">Yara Shahin</author>
20+
21+
<buildtool_depend>ament_cmake</buildtool_depend>
22+
23+
<build_depend>ros2_control_cmake</build_depend>
24+
<build_depend>rosidl_default_generators</build_depend>
25+
26+
<depend>rclcpp</depend>
27+
<depend>controller_interface</depend>
28+
<depend>control_msgs</depend>
29+
30+
<exec_depend>rosidl_default_runtime</exec_depend>
31+
32+
<test_depend>ament_cmake_gmock</test_depend>
33+
<test_depend>controller_manager</test_depend>
34+
<test_depend>hardware_interface_testing</test_depend>
35+
<test_depend>ros2_control_test_assets</test_depend>
36+
37+
<export>
38+
<build_type>ament_cmake</build_type>
39+
</export>
40+
</package>

0 commit comments

Comments
 (0)