Formula Student Autonomous Systems
The code for the main driverless system
Loading...
Searching...
No Matches
perception_node.cpp
Go to the documentation of this file.
2
3static const std_msgs::msg::Header header;
4
5static const inline std::unordered_map<std::string, std::string> adapter_frame_map = {
6 {"vehicle", "lidar"},
7 {"eufs", "velodyne"},
8 {"fsds", "lidar"},
9 {"vehicle_preprocessed", "lidar"},
10 {"fst", "lidar"}};
11
14
15 const std::string global_config_path =
16 common_lib::config_load::get_config_yaml_path("perception", "global", "global_config");
17 RCLCPP_DEBUG(rclcpp::get_logger("perception"), "Loading global config from: %s",
18 global_config_path.c_str());
19 const YAML::Node global_config = YAML::LoadFile(global_config_path);
20
21 params.adapter_ = global_config["global"]["adapter"].as<std::string>();
22 params.vehicle_frame_id_ = global_config["global"]["vehicle_frame_id"].as<std::string>();
23
24 if (params.adapter_ == "pacsim") {
25 params.adapter_ = "vehicle";
26 }
27
28 const std::string perception_path =
29 common_lib::config_load::get_config_yaml_path("perception", "perception", params.adapter_);
30 RCLCPP_DEBUG(rclcpp::get_logger("perception"), "Loading perception config from: %s",
31 perception_path.c_str());
32 const YAML::Node perception = YAML::LoadFile(perception_path);
33
34 const auto perception_config = perception["perception"];
35 RCLCPP_DEBUG(rclcpp::get_logger("perception"), "Perception config contents: %s",
36 YAML::Dump(perception_config).c_str());
37
38 const auto default_mission_str = perception_config["default_mission"].as<std::string>();
39 params.default_mission_ =
40 static_cast<uint8_t>(common_lib::competition_logic::fsds_to_system.at(default_mission_str));
41
42 // Publishers
43 params.publish_fov_trimmed_cloud = perception_config["publish_fov_trimmed_cloud"].as<bool>();
45 perception_config["publish_ground_removed_cloud"].as<bool>();
46 params.publish_wall_removed_cloud = perception_config["publish_wall_removed_cloud"].as<bool>();
47
48 // FOV Trimming Parameters
49 TrimmingParameters trim_params;
50 trim_params.min_range = perception_config["min_range"].as<double>();
51 trim_params.max_height = perception_config["max_height"].as<double>();
52 trim_params.max_range = perception_config["max_range"].as<double>();
53 trim_params.acc_max_range = perception_config["acc_max_range"].as<double>();
54 trim_params.acc_max_y = perception_config["acc_max_y"].as<double>();
55 trim_params.skid_max_range = perception_config["skid_max_range"].as<double>();
56 trim_params.lidar_height = perception_config["lidar_height"].as<double>();
57 trim_params.lidar_horizontal_resolution =
58 perception_config["lidar_horizontal_resolution"].as<double>();
59 trim_params.lidar_vertical_resolution =
60 perception_config["lidar_vertical_resolution"].as<double>();
61 trim_params.apply_rotation = perception_config["apply_rotation"].as<bool>();
62 trim_params.rotation = perception_config["rotation"].as<double>();
63 trim_params.apply_fov_trimming = perception_config["apply_fov_trimming"].as<bool>();
64 trim_params.fov = perception_config["fov"].as<double>();
65 trim_params.is_raining = perception_config["is_raining"].as<bool>();
66 trim_params.minimum_intensity = perception_config["minimum_intensity"].as<float>();
67
68 auto acceleration_trimming = std::make_shared<AccelerationTrimming>(trim_params);
69 auto skidpad_trimming = std::make_shared<SkidpadTrimming>(trim_params);
70 auto cut_trimming = std::make_shared<CutTrimming>(trim_params);
71
72 auto temp_fov_trim_map = std::unordered_map<int16_t, std::shared_ptr<FovTrimming>>{
73 {static_cast<int16_t>(Mission::MANUAL), cut_trimming},
74 {static_cast<int16_t>(Mission::ACCELERATION), acceleration_trimming},
75 {static_cast<int16_t>(Mission::SKIDPAD), skidpad_trimming},
76 {static_cast<int16_t>(Mission::TRACKDRIVE), cut_trimming},
77 {static_cast<int16_t>(Mission::AUTOCROSS), cut_trimming},
78 {static_cast<int16_t>(Mission::INSPECTION), cut_trimming},
79 {static_cast<int16_t>(Mission::EBS_TEST), acceleration_trimming},
80 {static_cast<int16_t>(Mission::MANUAL), cut_trimming},
81 {static_cast<int16_t>(Mission::NONE), cut_trimming}};
82
83 params.fov_trim_map_ =
84 std::make_shared<std::unordered_map<int16_t, std::shared_ptr<FovTrimming>>>(
85 temp_fov_trim_map);
86
87 // Ground Grid Parameters
88 double ground_grid_range = perception_config["ground_grid_range"].as<double>();
89 double ground_grid_angle = perception_config["ground_grid_angle"].as<double>();
90 double ground_grid_radius = perception_config["ground_grid_radius"].as<double>();
91 double ground_grid_start_augmentation =
92 perception_config["ground_grid_start_augmentation"].as<double>();
93 double ground_grid_radius_augmentation =
94 perception_config["ground_grid_radius_augmentation"].as<double>();
95 params.ground_grid_ = std::make_shared<GroundGrid>(
96 ground_grid_range, ground_grid_angle, ground_grid_radius, ground_grid_start_augmentation,
97 ground_grid_radius_augmentation, trim_params.fov);
98
99 // Ground Removal Parameters
100 std::string ground_removal_algorithm = perception_config["ground_removal"].as<std::string>();
101 double ransac_epsilon = perception_config["ransac_epsilon"].as<double>();
102 int ransac_iterations = perception_config["ransac_iterations"].as<int>();
103 double himmelsbach_grid_angle = perception_config["himmelsbach_grid_angle"].as<double>();
104 double himmelsbach_max_slope = perception_config["himmelsbach_max_slope"].as<double>();
105 double himmelsbach_alpha = perception_config["himmelsbach_initial_alpha"].as<double>();
106 double himmelsbach_alpha_augmentation_m =
107 perception_config["himmelsbach_alpha_augmentation_m"].as<double>();
108 double himmelsbach_start_augmentation =
109 perception_config["himmelsbach_start_augmentation"].as<double>();
110
111 if (ground_removal_algorithm == "ransac") {
112 params.ground_removal_ = std::make_shared<RANSAC>(ransac_epsilon, ransac_iterations);
113 } else if (ground_removal_algorithm == "himmelsbach") {
114 params.ground_removal_ = std::make_shared<Himmelsbach>(
115 himmelsbach_grid_angle, himmelsbach_max_slope, himmelsbach_alpha,
116 himmelsbach_alpha_augmentation_m, himmelsbach_start_augmentation, trim_params);
117 } else {
118 RCLCPP_ERROR(rclcpp::get_logger("perception"),
119 "Ground removal algorithm not recognized: %s, using RANSAC as default",
120 ground_removal_algorithm.c_str());
121 params.ground_removal_ = std::make_shared<RANSAC>(ransac_epsilon, ransac_iterations);
122 }
123
124 // Wall Removal Parameters
125 double wall_removal_grid_angle = perception_config["wall_removal_grid_angle"].as<double>();
126 double wall_removal_grid_radius = perception_config["wall_removal_grid_radius"].as<double>();
127 double wall_removal_start_augmentation =
128 perception_config["wall_removal_start_augmentation"].as<double>();
129 double wall_removal_radius_augmentation =
130 perception_config["wall_removal_radius_augmentation"].as<double>();
131 int wall_removal_max_points_per_cluster =
132 perception_config["wall_removal_max_points_per_cluster"].as<int>();
133 params.wall_removal_ = std::make_shared<GridWallRemoval>(
134 wall_removal_grid_angle, wall_removal_grid_radius, wall_removal_start_augmentation,
135 wall_removal_radius_augmentation, trim_params.fov, wall_removal_max_points_per_cluster);
136
137 // Clustering Parameters
138 std::string clustering_algorithm = perception_config["clustering"].as<std::string>();
139 int DBSCAN_clustering_n_neighbours = perception_config["DBSCAN_n_neighbours"].as<int>();
140 double DBSCAN_clustering_epsilon = perception_config["DBSCAN_clustering_epsilon"].as<double>();
141 double grid_clustering_grid_angle = perception_config["grid_clustering_grid_angle"].as<double>();
142 double grid_clustering_grid_radius =
143 perception_config["grid_clustering_grid_radius"].as<double>();
144 double grid_clustering_start_augmentation =
145 perception_config["grid_clustering_start_augmentation"].as<double>();
146 double grid_clustering_radius_augmentation =
147 perception_config["grid_clustering_radius_augmentation"].as<double>();
148
149 if (clustering_algorithm == "DBSCAN") {
150 params.clustering_ =
151 std::make_shared<DBSCAN>(DBSCAN_clustering_n_neighbours, DBSCAN_clustering_epsilon);
152 } else if (clustering_algorithm == "GridClustering") {
153 params.clustering_ = std::make_shared<GridClustering>(
154 grid_clustering_grid_angle, grid_clustering_grid_radius, grid_clustering_start_augmentation,
155 grid_clustering_radius_augmentation, trim_params.fov);
156 } else {
157 RCLCPP_ERROR(rclcpp::get_logger("perception"),
158 "Clustering algorithm not recognized: %s, using DBSCAN as default",
159 clustering_algorithm.c_str());
160 params.clustering_ =
161 std::make_shared<DBSCAN>(DBSCAN_clustering_n_neighbours, DBSCAN_clustering_epsilon);
162 }
163
164 // Cone Evaluator Parameters
165 auto eval_params = std::make_shared<EvaluatorParameters>();
166 eval_params->small_cone_width = perception_config["small_cone_width"].as<double>();
167 eval_params->large_cone_width = perception_config["large_cone_width"].as<double>();
168 eval_params->small_cone_height = perception_config["small_cone_height"].as<double>();
169 eval_params->large_cone_height = perception_config["large_cone_height"].as<double>();
170 eval_params->n_out_points_ratio = perception_config["n_out_points_ratio"].as<double>();
171 eval_params->max_distance_from_ground_min =
172 perception_config["max_distance_from_ground_min"].as<double>();
173 eval_params->max_distance_from_ground_max =
174 perception_config["max_distance_from_ground_max"].as<double>();
175 eval_params->n_points_intial_max = perception_config["n_points_intial_max"].as<double>();
176 eval_params->n_points_intial_min = perception_config["n_points_intial_min"].as<double>();
177 eval_params->n_points_max_distance_reduction =
178 perception_config["n_points_max_distance_reduction"].as<double>();
179 eval_params->n_points_min_distance_reduction =
180 perception_config["n_points_min_distance_reduction"].as<double>();
181 params.cone_evaluator_ = std::make_shared<ConeEvaluator>(eval_params);
182
183 return params;
184}
185
187 : Node("perception"),
188 _vehicle_frame_id_(params.vehicle_frame_id_),
189 _mission_type_(params.default_mission_),
190 _publish_fov_trimmed_cloud_(params.publish_fov_trimmed_cloud),
191 _publish_ground_removed_cloud_(params.publish_ground_removed_cloud),
192 _publish_wall_removed_cloud_(params.publish_wall_removed_cloud),
193 _fov_trim_map_(params.fov_trim_map_),
194 _ground_grid_(params.ground_grid_),
195 _ground_removal_(params.ground_removal_),
196 _wall_removal_(params.wall_removal_),
197 _clustering_(params.clustering_),
198 _cone_differentiator_(params.cone_differentiator_),
199 _cone_evaluator_(params.cone_evaluator_) {
200 this->_cones_publisher =
201 this->create_publisher<custom_interfaces::msg::PerceptionOutput>("/perception/cones", 10);
202
204 this->create_publisher<sensor_msgs::msg::PointCloud2>("/perception/fov_trimmed_cloud", 10);
205
207 this->create_publisher<sensor_msgs::msg::PointCloud2>("/perception/ground_removed_cloud", 10);
208
210 this->create_publisher<sensor_msgs::msg::PointCloud2>("/perception/wall_removed_cloud", 10);
211
213 this->create_publisher<std_msgs::msg::Float64MultiArray>("/perception/execution_time", 10);
214 this->_execution_times_ = std::make_shared<std::vector<double>>(7, 0.0);
215
217 this->create_subscription<custom_interfaces::msg::OperationalStatus>(
218 "/vehicle/operational_status", rclcpp::QoS(10),
219 [this](const custom_interfaces::msg::OperationalStatus::SharedPtr msg) {
220 _mission_type_ = msg->as_mission;
221 });
222
223 // Determine which adapter is being used
224 std::unordered_map<std::string, std::tuple<std::string, rclcpp::QoS>> adapter_topic_map = {
225 {"vehicle", {"/lidar_points", rclcpp::QoS(10)}},
226 {"eufs",
227 {"/velodyne_points", rclcpp::QoS(1).reliability(RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT)}},
228 {"fsds", {"/lidar/Lidar1", rclcpp::QoS(10)}},
229 {"vehicle_preprocessed", {"/rslidar_points/pre_processed", rclcpp::QoS(10)}},
230 {"fst", {"/hesai/pandar", rclcpp::QoS(10)}}};
231
232 try {
233 this->_point_cloud_subscription = this->create_subscription<sensor_msgs::msg::PointCloud2>(
234 std::get<0>(adapter_topic_map.at(params.adapter_)),
235 std::get<1>(adapter_topic_map.at(params.adapter_)),
236 [this](const sensor_msgs::msg::PointCloud2::SharedPtr msg) {
237 this->point_cloud_callback(msg);
238 });
239 } catch (const std::out_of_range& e) {
240 RCLCPP_ERROR(this->get_logger(), "Adapter not recognized: %s", params.adapter_.c_str());
241 }
242
243 this->_velocities_subscription_ = this->create_subscription<custom_interfaces::msg::Velocities>(
244 "/state_estimation/velocities", rclcpp::QoS(10),
245 std::bind(&Perception::velocities_callback, this, std::placeholders::_1));
246 this->_cone_marker_array_ = this->create_publisher<visualization_msgs::msg::MarkerArray>(
247 "/perception/visualization/cones", 10);
248
249 this->lidar_off_timer_ = this->create_wall_timer(
250 std::chrono::milliseconds(2000), std::bind(&Perception::lidar_timer_callback, this));
251
252 emergency_client_ = this->create_client<std_srvs::srv::Trigger>("/as_srv/emergency");
253
254 RCLCPP_INFO(this->get_logger(), "Perception Node created with adapter: %s",
255 params.adapter_.c_str());
256}
257
258void Perception::point_cloud_callback(const sensor_msgs::msg::PointCloud2::SharedPtr msg) {
259 // Get start perception time
260 // rclcpp::Time pcl_time = msg->header.stamp;
261 rclcpp::Time pcl_time = this->now();
262
263 // Reset lidar off timer
264 this->lidar_off_timer_ = this->create_wall_timer(
265 std::chrono::milliseconds(2000), std::bind(&Perception::lidar_timer_callback, this));
266
267 rclcpp::Time start_time = this->now();
268 rclcpp::Time time1, time2, time3, time4, time5, time6;
269
270 // FOV Trimming
271 auto trimmed_cloud = std::make_shared<sensor_msgs::msg::PointCloud2>();
272 _fov_trim_map_->at(_mission_type_)->fov_trimming(msg, trimmed_cloud);
273
274 if (this->_publish_fov_trimmed_cloud_) {
275 trimmed_cloud->header.frame_id = _vehicle_frame_id_;
276 this->_fov_trimmed_publisher_->publish(*trimmed_cloud);
277 }
278 time1 = this->now();
279
280 // Ground Removal
281 auto ground_removed_cloud = std::make_shared<sensor_msgs::msg::PointCloud2>();
282 _ground_removal_->ground_removal(trimmed_cloud, ground_removed_cloud, *this->_ground_grid_);
283
285 ground_removed_cloud->header.frame_id = _vehicle_frame_id_;
286 this->_ground_removed_publisher_->publish(*ground_removed_cloud);
287 }
288 time2 = this->now();
289
290 // Wall Removal
291 auto wall_removed_cloud = std::make_shared<sensor_msgs::msg::PointCloud2>();
292 _wall_removal_->remove_walls(ground_removed_cloud, wall_removed_cloud);
293
295 wall_removed_cloud->header.frame_id = _vehicle_frame_id_;
296 this->_wall_removed_publisher_->publish(*wall_removed_cloud);
297 }
298 time3 = this->now();
299
300 // Deskewing
301 this->_deskew_->deskew_point_cloud(wall_removed_cloud, this->_vehicle_velocity_);
302 time4 = this->now();
303
304 // Clustering
305 std::vector<Cluster> clusters;
306 _clustering_->clustering(wall_removed_cloud, &clusters);
307 time5 = this->now();
308
309 // Evaluator
310 std::vector<Cluster> filtered_clusters;
311
312 for (auto& cluster : clusters) {
313 if (_cone_evaluator_->evaluateCluster(cluster, *this->_ground_grid_)) {
314 filtered_clusters.push_back(cluster);
315 }
316 }
317 time6 = this->now();
318
319 double perception_execution_time_seconds = (time6 - start_time).seconds();
320 this->_execution_times_->at(0) = perception_execution_time_seconds * 1000.0;
321 this->_execution_times_->at(1) = (time1 - start_time).seconds() * 1000.0;
322 this->_execution_times_->at(2) = (time2 - time1).seconds() * 1000.0;
323 this->_execution_times_->at(3) = (time3 - time2).seconds() * 1000.0;
324 this->_execution_times_->at(4) = (time4 - time3).seconds() * 1000.0;
325 this->_execution_times_->at(5) = (time5 - time4).seconds() * 1000.0;
326 this->_execution_times_->at(6) = (time6 - time5).seconds() * 1000.0;
327 std_msgs::msg::Float64MultiArray exec_time_msg;
328 exec_time_msg.data = *(this->_execution_times_);
329 this->_perception_execution_time_publisher_->publish(exec_time_msg);
330
331 publish_cones(&filtered_clusters, perception_execution_time_seconds, start_time);
332}
333
334void Perception::publish_cones(std::vector<Cluster>* cones, double exec_time,
335 rclcpp::Time start_time) {
336 auto message = custom_interfaces::msg::PerceptionOutput();
337 std::vector<custom_interfaces::msg::Cone> message_array = {};
338 message.header = ::header;
339 message.header.stamp = start_time;
340 for (int i = 0; i < static_cast<int>(cones->size()); i++) {
341 auto position = custom_interfaces::msg::Point2d();
342 position.x = cones->at(i).get_centroid().x();
343 position.y = cones->at(i).get_centroid().y();
344
345 auto cone_message = custom_interfaces::msg::Cone();
346 cone_message.position = position;
347 cone_message.color = cones->at(i).get_color();
348 cone_message.is_large = cones->at(i).get_is_large();
349 cone_message.confidence = cones->at(i).get_confidence();
350 message.cones.cone_array.push_back(cone_message);
351 message_array.push_back(cone_message);
352 }
353 message.exec_time = exec_time;
354
355 this->_cones_publisher->publish(message);
356 // TODO: correct frame id to LiDAR instead of vehicle
358 message_array, "perception", this->_vehicle_frame_id_, "green"));
359}
360
361void Perception::velocities_callback(const custom_interfaces::msg::Velocities& msg) {
362 this->_vehicle_velocity_ =
363 common_lib::structures::Velocities(msg.velocity_x, msg.velocity_y, msg.angular_velocity);
364}
365
367 emergency_client_->async_send_request(
368 std::make_shared<std_srvs::srv::Trigger::Request>(),
369 [this](rclcpp::Client<std_srvs::srv::Trigger>::SharedFuture future) {
370 if (future.get()->success) {
371 RCLCPP_WARN(this->get_logger(), "Emergency signal sent");
372 } else {
373 RCLCPP_ERROR(this->get_logger(), "Failed to send emergency signal");
374 }
375 });
376 return;
377}
rclcpp::Subscription< custom_interfaces::msg::Velocities >::SharedPtr _velocities_subscription_
std::string _vehicle_frame_id_
String for the vehicle's frame id.
std::shared_ptr< Clustering > _clustering_
Shared pointer to the Clustering object.
std::shared_ptr< ConeEvaluator > _cone_evaluator_
Shared pointer to ConeEvaluator object.
static PerceptionParameters load_config()
Turns the parameters in the yaml file into PerceptionParameters class.
rclcpp::Publisher< sensor_msgs::msg::PointCloud2 >::SharedPtr _fov_trimmed_publisher_
Point cloud after fov trimming publisher.
rclcpp::Subscription< custom_interfaces::msg::OperationalStatus >::SharedPtr _operational_status_subscription
Master Log subscription to aquire mission type.
std::shared_ptr< WallRemoval > _wall_removal_
Shared pointer to the WallRemoval object.
rclcpp::Subscription< sensor_msgs::msg::PointCloud2 >::SharedPtr _point_cloud_subscription
PointCloud2 subscription.
rclcpp::Publisher< visualization_msgs::msg::MarkerArray >::SharedPtr _cone_marker_array_
MarkerArray publisher.
void point_cloud_callback(const sensor_msgs::msg::PointCloud2::SharedPtr msg)
Callback function for the PointCloud2 subscription.
std::shared_ptr< GroundRemoval > _ground_removal_
Shared pointer to the GroundRemoval object.
void publish_cones(std::vector< Cluster > *cones, double exec_time, rclcpp::Time start_time)
Publishes information about clusters (cones) using a custom ROS2 message.
bool _publish_ground_removed_cloud_
Whether to publish the ground removed point cloud.
void velocities_callback(const custom_interfaces::msg::Velocities &msg)
Callback function for the Velocities subscription.
std::shared_ptr< std::unordered_map< int16_t, std::shared_ptr< FovTrimming > > > _fov_trim_map_
Shared pointer to the FovTrimming object.
std::shared_ptr< Deskew > _deskew_
Shared pointer to Deskew object.
int16_t _mission_type_
integer value for the current mission type running.
void lidar_timer_callback()
Callback function for the LiDAR timer to check for emergency.
rclcpp::Publisher< sensor_msgs::msg::PointCloud2 >::SharedPtr _ground_removed_publisher_
Point cloud after ground removal publisher.
bool _publish_wall_removed_cloud_
Whether to publish the wall removed point cloud.
Perception(const PerceptionParameters &params)
Constructor for the Perception node.
rclcpp::Publisher< std_msgs::msg::Float64MultiArray >::SharedPtr _perception_execution_time_publisher_
Perception execution time publisher.
std::shared_ptr< std::vector< double > > _execution_times_
Vector to store execution times.
std::shared_ptr< GroundGrid > _ground_grid_
Model for the ground grid.
rclcpp::Publisher< sensor_msgs::msg::PointCloud2 >::SharedPtr _wall_removed_publisher_
Point cloud after wall removal publisher.
rclcpp::Client< std_srvs::srv::Trigger >::SharedPtr emergency_client_
common_lib::structures::Velocities _vehicle_velocity_
bool _publish_fov_trimmed_cloud_
Whether to publish the FOV trimmed point cloud.
rclcpp::Publisher< custom_interfaces::msg::PerceptionOutput >::SharedPtr _cones_publisher
ConeArray + exec publisher.
rclcpp::TimerBase::SharedPtr lidar_off_timer_
Timer to check for lidar emergency.
visualization_msgs::msg::MarkerArray marker_array_from_structure_array(const std::vector< T > &structure_array, const std::string &name_space, const std::string &frame_id, const std::string &color="red", const std::string &shape="cylinder", float scale=0.5, int action=visualization_msgs::msg::Marker::MODIFY)
Converts a vector of cones to a marker array.
Definition marker.hpp:79
const std::unordered_map< std::string, Mission > fsds_to_system
std::string get_config_yaml_path(const std::string &package_name, const std::string &dir, const std::string &filename)
static const std_msgs::msg::Header header
static const std::unordered_map< std::string, std::string > adapter_frame_map
Struct containing parameters and interfaces used in perception.
std::shared_ptr< WallRemoval > wall_removal_
Shared pointer to the WallRemoval object.
std::shared_ptr< GroundRemoval > ground_removal_
Shared pointer to the GroundRemoval object.
std::string adapter_
String for the name of the current adapter.
std::string vehicle_frame_id_
String for the vehicle's frame id.
std::shared_ptr< ConeEvaluator > cone_evaluator_
Shared pointer to ConeEvaluator object.
std::shared_ptr< std::unordered_map< int16_t, std::shared_ptr< FovTrimming > > > fov_trim_map_
std::shared_ptr< GroundGrid > ground_grid_
Model for the ground grid.
std::shared_ptr< Clustering > clustering_
Shared pointer to the Clustering object.
Structure to hold parameters for trimming point cloud data.
double min_range
Maximum point cloud distance after trimming.