Carma-platform v4.2.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.
external_object_list_to_detection_list_component.cpp
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
16
17#include <rclcpp_components/register_node_macro.hpp>
18#include <string>
19#include <utility>
20#include <vector>
21
24
26{
28 carma_cooperative_perception_interfaces::msg::DetectionList detection_list,
29 const std::string & map_origin) -> carma_cooperative_perception_interfaces::msg::DetectionList
30{
31 gsl::owner<PJ_CONTEXT *> context = proj_context_create();
32 proj_log_level(context, PJ_LOG_NONE);
33
34 if (context == nullptr) {
35 const std::string error_string{proj_errno_string(proj_context_errno(context))};
36 throw std::invalid_argument("Could not create PROJ context: " + error_string + '.');
37 }
38
39 gsl::owner<PJ *> map_transformation = proj_create(context, map_origin.c_str());
40
41 if (map_transformation == nullptr) {
42 const std::string error_string{proj_errno_string(proj_context_errno(context))};
43 throw std::invalid_argument(
44 "Could not create PROJ transform to origin '" + map_origin + "': " + error_string + '.');
45 }
46
47 std::vector<carma_cooperative_perception_interfaces::msg::Detection> new_detections;
48 for (auto detection : detection_list.detections) {
49 // Coordinate order is easting (meters), northing (meters)
50 const auto position_planar{
51 proj_coord(detection.pose.pose.position.x, detection.pose.pose.position.y, 0, 0)};
52 const auto proj_inverse{proj_trans(map_transformation, PJ_DIRECTION::PJ_INV, position_planar)};
53 const Wgs84Coordinate position_wgs84{
54 units::angle::radian_t{proj_inverse.lp.phi}, units::angle::radian_t{proj_inverse.lp.lam},
55 units::length::meter_t{detection.pose.pose.position.z}};
56
57 const auto utm_zone{calculate_utm_zone(position_wgs84)};
58 const auto position_utm{project_to_utm(position_wgs84)};
59
60 detection.header.frame_id = to_string(utm_zone);
61 detection.pose.pose.position.x = remove_units(position_utm.easting);
62 detection.pose.pose.position.y = remove_units(position_utm.northing);
63
64 new_detections.push_back(std::move(detection));
65 }
66
67 std::swap(detection_list.detections, new_detections);
68
69 proj_destroy(map_transformation);
70 proj_context_destroy(context);
71
72 return detection_list;
73}
74
76 const rclcpp::NodeOptions & options)
77: CarmaLifecycleNode{options}
78{
79 lifecycle_publishers_.push_back(publisher_);
80 param_callback_handles_.push_back(on_set_parameters_callback_);
81}
82
84 const rclcpp_lifecycle::State & /* previous_state */) -> carma_ros2_utils::CallbackReturn
85{
86 publisher_ = create_publisher<output_msg_type>("output/detections", 1);
87
88 external_objects_subscription_ = create_subscription<input_msg_type>(
89 "input/external_objects", 1, [this](input_msg_shared_pointer msg_ptr) {
90 const auto current_state{this->get_current_state().label()};
91
92 if (current_state == "active") {
93 publish_as_detection_list(*msg_ptr);
94 } else {
95 RCLCPP_WARN(
96 this->get_logger(),
97 "Trying to receive message on the topic '%s', but the containing node is not activated. "
98 "Current node state: '%s'",
99 this->external_objects_subscription_->get_topic_name(), current_state.c_str());
100 }
101 });
102
103 georeference_subscription_ = create_subscription<std_msgs::msg::String>(
104 "input/georeference", 1, [this](std_msgs::msg::String::SharedPtr msg_ptr) {
105 const auto current_state{this->get_current_state().label()};
106
107 if (current_state == "active") {
108 update_georeference(*msg_ptr);
109 } else {
110 RCLCPP_WARN(
111 this->get_logger(),
112 "Trying to receive message on the topic '%s', but the containing node is not activated. "
113 "Current node state: '%s'",
114 this->georeference_subscription_->get_topic_name(), current_state.c_str());
115 }
116 });
117
118 on_set_parameters_callback_ =
119 add_on_set_parameters_callback([this](const std::vector<rclcpp::Parameter> & parameters) {
120 rcl_interfaces::msg::SetParametersResult result;
121 result.successful = true;
122 result.reason = "success";
123
124 for (const auto & parameter : parameters) {
125 if (parameter.get_name() == "small_vehicle_motion_model") {
126 this->motion_model_mapping_.small_vehicle_model =
127 static_cast<std::uint8_t>(parameter.as_int());
128 } else if (parameter.get_name() == "large_vehicle_motion_model") {
129 this->motion_model_mapping_.large_vehicle_model =
130 static_cast<std::uint8_t>(parameter.as_int());
131 } else if (parameter.get_name() == "motorcycle_motion_model") {
132 this->motion_model_mapping_.motorcycle_model =
133 static_cast<std::uint8_t>(parameter.as_int());
134 } else if (parameter.get_name() == "pedestrian_motion_model") {
135 this->motion_model_mapping_.pedestrian_model =
136 static_cast<std::uint8_t>(parameter.as_int());
137 } else if (parameter.get_name() == "unknown_motion_model") {
138 this->motion_model_mapping_.unknown_model = static_cast<std::uint8_t>(parameter.as_int());
139 } else {
140 result.successful = false;
141 result.reason = "Unexpected parameter name '" + parameter.get_name() + '\'';
142 }
143 }
144
145 return result;
146 });
147
148 // TODO(Adam Morrissett): Look into how the motion model mapping can be re-architected
149 // to avoid requiring this sequence. Maybe something like a mapping class that calls the
150 // declared parameters. Then the to_detected_object() function could be templated on a
151 // mapping strategy.
152
153 // Declarations must come after on_set_parameters_callback_ assignment because the ROS
154 // runtime will call the callback if declare_parameter() succeeds.
155 declare_parameter("small_vehicle_motion_model", motion_model_mapping_.small_vehicle_model);
156 declare_parameter("large_vehicle_motion_model", motion_model_mapping_.large_vehicle_model);
157 declare_parameter("motorcycle_motion_model", motion_model_mapping_.motorcycle_model);
158 declare_parameter("pedestrian_motion_model", motion_model_mapping_.pedestrian_model);
159 declare_parameter("unknown_motion_model", motion_model_mapping_.unknown_model);
160
161 return carma_ros2_utils::CallbackReturn::SUCCESS;
162}
163
165 const rclcpp_lifecycle::State & /* previous_state */) -> carma_ros2_utils::CallbackReturn
166{
167 // CarmaLifecycleNode does not handle subscriber pointer reseting for us
168 external_objects_subscription_.reset();
169 georeference_subscription_.reset();
170
171 return carma_ros2_utils::CallbackReturn::SUCCESS;
172}
173
175 const rclcpp_lifecycle::State & /* previous_state */) -> carma_ros2_utils::CallbackReturn
176{
177 // CarmaLifecycleNode does not handle subscriber pointer reseting for us
178 external_objects_subscription_.reset();
179 georeference_subscription_.reset();
180
181 return carma_ros2_utils::CallbackReturn::SUCCESS;
182}
183
185 const input_msg_type & msg) const -> void
186{
187 try {
188 publisher_->publish(to_detection_list_msg(msg, motion_model_mapping_));
189 } catch (const std::invalid_argument & e) {
190 RCLCPP_ERROR(
191 this->get_logger(), "Could not convert external object list to detection list: %s", e.what());
192 }
193}
194
196 -> void
197{
198 map_georeference_ = msg.data;
199}
200
201} // namespace carma_cooperative_perception
202
203// This is not our macro, so we should not worry about linting it.
204// clang-tidy added support for ignoring system macros in release 14.0.0 (see the release notes
205// here: https://releases.llvm.org/14.0.0/tools/clang/tools/extra/docs/ReleaseNotes.html), but
206// ament_clang_tidy for ROS 2 Foxy specifically looks for clang-tidy-6.0.
207RCLCPP_COMPONENTS_REGISTER_NODE( // NOLINT
auto handle_on_cleanup(const rclcpp_lifecycle::State &) -> carma_ros2_utils::CallbackReturn override
auto handle_on_configure(const rclcpp_lifecycle::State &) -> carma_ros2_utils::CallbackReturn override
auto handle_on_shutdown(const rclcpp_lifecycle::State &) -> carma_ros2_utils::CallbackReturn override
auto to_detection_list_msg(const carma_v2x_msgs::msg::SensorDataSharingMessage &sdsm, std::string_view georeference) -> carma_cooperative_perception_interfaces::msg::DetectionList
auto transform_from_map_to_utm(carma_cooperative_perception_interfaces::msg::DetectionList detection_list, const std::string &map_origin) -> carma_cooperative_perception_interfaces::msg::DetectionList
auto to_string(const UtmZone &zone) -> std::string
Definition: utm_zone.cpp:21
auto calculate_utm_zone(const Wgs84Coordinate &coordinate) -> UtmZone
Get the UTM zone number from a WGS-84 coordinate.
Definition: geodetic.cpp:27
constexpr auto remove_units(const T &value)
auto project_to_utm(const Wgs84Coordinate &coordinate) -> UtmCoordinate
Projects a Wgs84Coordinate to its corresponding UTM zone.
Definition: geodetic.cpp:87
Represents a position using WGS-84 coordinates.
Definition: geodetic.hpp:33