Carma-platform v4.10.0
CARMA Platform is built on robot operating system (ROS) and utilizes open source software (OSS) that enables Cooperative Driving Automation (CDA) features to allow Automated Driving Systems to interact and cooperate with infrastructure and other vehicles through communication.
sdsm_to_detection_list_component.hpp
Go to the documentation of this file.
1// Copyright 2023 Leidos
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#ifndef CARMA_COOPERATIVE_PERCEPTION__SDSM_TO_DETECTION_LIST_COMPONENT_HPP_
16#define CARMA_COOPERATIVE_PERCEPTION__SDSM_TO_DETECTION_LIST_COMPONENT_HPP_
17
18#include <string>
19
20#include <carma_cooperative_perception_interfaces/msg/detection_list.hpp>
21#include <carma_ros2_utils/carma_lifecycle_node.hpp>
22#include <carma_v2x_msgs/msg/sensor_data_sharing_message.hpp>
23#include <rclcpp/rclcpp.hpp>
24#include <rosgraph_msgs/msg/clock.hpp>
25#include <std_msgs/msg/string.hpp>
26
28
30{
31
32class SdsmToDetectionListNode : public carma_ros2_utils::CarmaLifecycleNode
33{
34 using input_msg_type = carma_v2x_msgs::msg::SensorDataSharingMessage;
35 using input_msg_shared_pointer = typename input_msg_type::SharedPtr;
36 using output_msg_type = carma_cooperative_perception_interfaces::msg::DetectionList;
37
38public:
39 auto handle_on_configure(const rclcpp_lifecycle::State & /* previous_state */)
40 -> carma_ros2_utils::CallbackReturn override
41 {
42 // Declare parameters
43 declare_parameter("overwrite_covariance", config_.overwrite_covariance);
44 declare_parameter("pose_covariance_x", config_.pose_covariance_x);
45 declare_parameter("pose_covariance_y", config_.pose_covariance_y);
46 declare_parameter("pose_covariance_z", config_.pose_covariance_z);
47 declare_parameter("pose_covariance_yaw", config_.pose_covariance_yaw);
48 declare_parameter("twist_covariance_x", config_.twist_covariance_x);
49 declare_parameter("twist_covariance_z", config_.twist_covariance_z);
50 declare_parameter("twist_covariance_yaw", config_.twist_covariance_yaw);
51 declare_parameter("adjust_pose", config_.adjust_pose);
52 declare_parameter("x_offset", config_.x_offset);
53 declare_parameter("y_offset", config_.y_offset);
54 declare_parameter("yaw_offset", config_.yaw_offset);
55
56 // Get parameters
57 config_.overwrite_covariance = get_parameter("overwrite_covariance").as_bool();
58 config_.pose_covariance_x = get_parameter("pose_covariance_x").as_double();
59 config_.pose_covariance_y = get_parameter("pose_covariance_y").as_double();
60 config_.pose_covariance_z = get_parameter("pose_covariance_z").as_double();
61 config_.pose_covariance_yaw = get_parameter("pose_covariance_yaw").as_double();
62 config_.twist_covariance_x = get_parameter("twist_covariance_x").as_double();
63 config_.twist_covariance_z = get_parameter("twist_covariance_z").as_double();
64 config_.adjust_pose = get_parameter("adjust_pose").as_bool();
65 config_.x_offset = get_parameter("x_offset").as_double();
66 config_.y_offset = get_parameter("y_offset").as_double();
67 config_.yaw_offset = get_parameter("yaw_offset").as_double();
68
69 // Set up parameter validation callback
70 on_set_parameters_callback_ = add_on_set_parameters_callback(
71 [this](const std::vector<rclcpp::Parameter> & parameters) {
72 return update_parameters(parameters);
73 });
74
75 // Use the stream operator for debug output
76 RCLCPP_INFO_STREAM(get_logger(), "SdsmToDetectionListNode Configuration: " << config_);
77
78 return carma_ros2_utils::CallbackReturn::SUCCESS;
79 }
80
81 explicit SdsmToDetectionListNode(const rclcpp::NodeOptions & options)
82 : CarmaLifecycleNode{options},
83 publisher_{create_publisher<output_msg_type>("output/detections", 1)},
84 subscription_{create_subscription<input_msg_type>(
85 "input/sdsm", 100, [this](input_msg_shared_pointer msg_ptr) { sdsm_msg_callback(*msg_ptr); })},
86 georeference_subscription_{create_subscription<std_msgs::msg::String>(
87 "input/georeference", 1,
88 [this](std_msgs::msg::String::SharedPtr msg_ptr) { georeference_ = msg_ptr->data; })},
89 cdasim_clock_sub_{create_subscription<rosgraph_msgs::msg::Clock>(
90 "input/cdasim_clock", 1,
91 [this](rosgraph_msgs::msg::Clock::ConstSharedPtr msg_ptr) { cdasim_time_ = msg_ptr->clock; })}
92 {
93 // Initialize the valid parameter names to check when setting the at runtime
94 valid_parameter_names_ = {
95 "overwrite_covariance",
96 "pose_covariance_x",
97 "pose_covariance_y",
98 "pose_covariance_z",
99 "pose_covariance_yaw",
100 "twist_covariance_x",
101 "twist_covariance_z",
102 "twist_covariance_yaw",
103 "adjust_pose",
104 "x_offset",
105 "y_offset",
106 "yaw_offset"
107 };
108 }
109
110 auto sdsm_msg_callback(const input_msg_type & msg) const -> void
111 {
112 if (georeference_.empty()){
113 RCLCPP_WARN_STREAM(get_logger(), "Georeference not defined yet, ignoring SDSM.");
114 return;
115 }
116
117 try {
118 std::optional<SdsmToDetectionListConfig> conversion_adjustment = std::nullopt;
119 if (config_.overwrite_covariance || config_.adjust_pose) {
120 conversion_adjustment = config_;
121 }
122 auto detection_list_msg{
123 to_detection_list_msg(msg, georeference_, cdasim_time_ != std::nullopt,
124 conversion_adjustment)
125 };
126
127 if (cdasim_time_) {
128 // When in simulation, ROS time is CARLA time, but SDSMs use CDASim time
129 const auto time_delta{now() - cdasim_time_.value()};
130
131 for (auto & detection : detection_list_msg.detections) {
132 detection.header.stamp = rclcpp::Time(detection.header.stamp) + time_delta;
133 }
134 }
135 RCLCPP_DEBUG_STREAM(
136 get_logger(), "SDSM to detection list conversion: "
137 << detection_list_msg.detections.size() << " detections");
138 publisher_->publish(detection_list_msg);
139 } catch (const std::runtime_error & e) {
140 RCLCPP_ERROR_STREAM(get_logger(), "Failed to convert SDSM to detection list: " << e.what());
141 }
142 }
143
144private:
145 rclcpp::Publisher<output_msg_type>::SharedPtr publisher_;
146 rclcpp::Subscription<input_msg_type>::SharedPtr subscription_;
147 rclcpp::Subscription<std_msgs::msg::String>::SharedPtr georeference_subscription_;
148
149 rclcpp::Subscription<rosgraph_msgs::msg::Clock>::SharedPtr cdasim_clock_sub_;
150 std::optional<rclcpp::Time> cdasim_time_{std::nullopt};
151
152 std::string georeference_{""};
153
154 // Configuration object
156
157 OnSetParametersCallbackHandle::SharedPtr on_set_parameters_callback_{nullptr};
158
159 // Parameter validation function
160 rcl_interfaces::msg::SetParametersResult update_parameters(
161 const std::vector<rclcpp::Parameter> & parameters)
162 {
163 rcl_interfaces::msg::SetParametersResult result;
164 result.successful = true;
165 result.reason = "success";
166
167 for (const auto & parameter : parameters) {
168 // Check if parameter name is valid
169 if (valid_parameter_names_.find(parameter.get_name()) == valid_parameter_names_.end()) {
170 result.successful = false;
171 result.reason = "Unexpected parameter name '" + parameter.get_name() + "'";
172 RCLCPP_ERROR_STREAM(get_logger(), "Cannot change parameter: " << result.reason);
173 break;
174 }
175
176 // Validate covariance values (must be non-negative)
177 if (parameter.get_name() != "adjust_pose" &&
178 parameter.get_name() != "overwrite_covariance") {
179 if (const auto value{parameter.as_double()}; value < 0.0) {
180 result.successful = false;
181 result.reason = "Covariance parameter must be non-negative";
182 RCLCPP_ERROR_STREAM(
183 get_logger(),
184 "Cannot change parameter '" << parameter.get_name() << "': " << result.reason);
185 break;
186 } else {
187 // Update the appropriate parameter in the config
188 update_config_double_parameter(parameter);
189 }
190 } else if (parameter.get_name() == "overwrite_covariance"){
191 config_.overwrite_covariance = parameter.as_bool();
192 } else if (parameter.get_name() == "adjust_pose"){
193 config_.adjust_pose = parameter.as_bool();
194 }
195 }
196
197 // If parameters were successfully updated, log the new configuration
198 if (result.successful) {
199 RCLCPP_DEBUG_STREAM(get_logger(), "Updated configuration: " << config_);
200 }
201
202 return result;
203 }
204
205 // Helper function to update a specific parameter in the config
206 void update_config_double_parameter(const rclcpp::Parameter & parameter) {
207 const std::string & name = parameter.get_name();
208 const double value = parameter.as_double();
209
210 if (name == "pose_covariance_x") {
211 config_.pose_covariance_x = value;
212 } else if (name == "pose_covariance_y") {
213 config_.pose_covariance_y = value;
214 } else if (name == "pose_covariance_z") {
215 config_.pose_covariance_z = value;
216 } else if (name == "pose_covariance_yaw") {
217 config_.pose_covariance_yaw = value;
218 } else if (name == "twist_covariance_x") {
219 config_.twist_covariance_x = value;
220 } else if (name == "twist_covariance_z") {
221 config_.twist_covariance_z = value;
222 } else if (name == "twist_covariance_yaw") {
223 config_.twist_covariance_yaw = value;
224 } else if (name == "x_offset") {
225 config_.x_offset = value;
226 } else if (name == "y_offset") {
227 config_.y_offset = value;
228 } else if (name == "yaw_offset") {
229 config_.yaw_offset = value;
230 }
231 }
232
233 // Set of valid parameter names for validation
234 std::unordered_set<std::string> valid_parameter_names_;
235};
236
237} // namespace carma_cooperative_perception
238
239#endif // CARMA_COOPERATIVE_PERCEPTION__SDSM_TO_DETECTION_LIST_COMPONENT_HPP_
rclcpp::Subscription< input_msg_type >::SharedPtr subscription_
carma_cooperative_perception_interfaces::msg::DetectionList output_msg_type
rcl_interfaces::msg::SetParametersResult update_parameters(const std::vector< rclcpp::Parameter > &parameters)
carma_v2x_msgs::msg::SensorDataSharingMessage input_msg_type
rclcpp::Subscription< std_msgs::msg::String >::SharedPtr georeference_subscription_
rclcpp::Publisher< output_msg_type >::SharedPtr publisher_
rclcpp::Subscription< rosgraph_msgs::msg::Clock >::SharedPtr cdasim_clock_sub_
auto handle_on_configure(const rclcpp_lifecycle::State &) -> carma_ros2_utils::CallbackReturn override
auto sdsm_msg_callback(const input_msg_type &msg) const -> void
auto to_detection_list_msg(const carma_v2x_msgs::msg::SensorDataSharingMessage &sdsm, std::string_view georeference, bool is_simulation, const std::optional< SdsmToDetectionListConfig > &conversion_adjustment) -> carma_cooperative_perception_interfaces::msg::DetectionList
Converts a carma_v2x_msgs::msg::SensorDataSharingMessage (SDSM) to carma_cooperative_perception_inter...