Apollo 11.0
自动驾驶开放平台
mask_pillars_detection.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2021 The Apollo Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *****************************************************************************/
17
18#include <algorithm>
19#include <numeric>
20#include <random>
21
22#include "cyber/common/log.h"
23#include "cyber/common/file.h"
31
32namespace apollo {
33namespace perception {
34namespace lidar {
35
36using base::Object;
37using base::PointD;
38using base::PointF;
39
41 : x_min_(Params::kMinXRange),
42 x_max_(Params::kMaxXRange),
43 y_min_(Params::kMinYRange),
44 y_max_(Params::kMaxYRange),
45 z_min_(Params::kMinZRange),
46 z_max_(Params::kMaxZRange) {}
47
48// TODO(chenjiahao):
49// specify score threshold and nms over lap threshold for each class.
51 std::string config_file =
52 GetConfigFile(options.config_path, options.config_file);
54
55 const auto &model_info = param_.info();
56 std::string model_path = GetModelPath(model_info.name());
57 std::string weight_file =
58 GetModelFile(model_path, model_info.weight_file().file());
59
60 point_pillars_ptr_.reset(
64 model_info.framework(),
65 weight_file));
66 if (param_.preprocess().enable_ground_removal()) {
67 z_min_ = std::max(z_min_,
68 static_cast<float>(
70 }
71
72 if (param_.has_plugins()) {
73 const auto &plugin = param_.plugins();
74 const auto &name = plugin.name();
78 if (!down_sample_) {
79 AINFO << "Failed to find down_sample plugin: " << name << ", skipped";
80 return false;
81 }
83 option.config_path = plugin.config_path();
84 option.config_file = plugin.config_file();
85 if (!down_sample_->Init(option)) {
86 AINFO << "Failed to init down_sample plugin: " << name << ", skipped";
87 return false;
88 }
89 }
90
91 return true;
92}
93
95 LidarFrame* frame) {
96 // check input
97 if (frame == nullptr) {
98 AERROR << "Input null frame ptr.";
99 return false;
100 }
101 if (frame->cloud == nullptr) {
102 AERROR << "Input null frame cloud.";
103 return false;
104 }
105 if (frame->cloud->size() == 0) {
106 AERROR << "Input none points.";
107 return false;
108 }
109
110 // record input cloud and lidar frame
111 original_cloud_ = frame->cloud;
112 original_world_cloud_ = frame->world_cloud;
113 lidar_frame_ref_ = frame;
114
115 // check output
116 frame->segmented_objects.clear();
117
118 if (cudaSetDevice(param_.preprocess().gpu_id()) != cudaSuccess) {
119 AERROR << "Failed to set device to gpu " << param_.preprocess().gpu_id();
120 return false;
121 }
122
123 Timer timer;
124
125 int num_points;
126 cur_cloud_ptr_ = std::shared_ptr<base::PointFCloud>(
127 new base::PointFCloud(*original_cloud_));
128
129 // down sample the point cloud through filtering beams
130 if (param_.preprocess().enable_downsample_beams()) {
131 base::PointFCloudPtr downsample_beams_cloud_ptr(new base::PointFCloud());
132 if (DownSamplePointCloudBeams(
133 original_cloud_, downsample_beams_cloud_ptr,
135 cur_cloud_ptr_ = downsample_beams_cloud_ptr;
136 } else {
137 AWARN << "Down-sample beams factor must be >= 1. Cancel down-sampling."
138 " Current factor: "
140 }
141 }
142
143 // down sample the point cloud through filtering voxel grid
145 DownSampleOptions down_sample_options;
146 ACHECK(down_sample_ != nullptr);
147 down_sample_->Process(down_sample_options, cur_cloud_ptr_);
148 }
149 downsample_time_ = timer.toc(true);
150
151 num_points = cur_cloud_ptr_->size();
152 AINFO << "num points before fusing: " << num_points;
153
154 // fuse clouds of preceding frames with current cloud
155 cur_cloud_ptr_->mutable_points_timestamp()->assign(cur_cloud_ptr_->size(),
156 0.0);
157 if (param_.preprocess().enable_fuse_frames() &&
158 param_.preprocess().num_fuse_frames() > 1) {
159 // before fusing
160 while (!prev_world_clouds_.empty() &&
161 frame->timestamp - prev_world_clouds_.front()->get_timestamp() >
162 param_.preprocess().fuse_time_interval()) {
163 prev_world_clouds_.pop_front();
164 }
165 // transform current cloud to world coordinate and save to a new ptr
166 base::PointDCloudPtr cur_world_cloud_ptr =
167 std::make_shared<base::PointDCloud>();
168 for (size_t i = 0; i < cur_cloud_ptr_->size(); ++i) {
169 auto& pt = cur_cloud_ptr_->at(i);
170 Eigen::Vector3d trans_point(pt.x, pt.y, pt.z);
171 trans_point = lidar_frame_ref_->lidar2world_pose * trans_point;
172 PointD world_point;
173 world_point.x = trans_point(0);
174 world_point.y = trans_point(1);
175 world_point.z = trans_point(2);
176 world_point.intensity = pt.intensity;
177 cur_world_cloud_ptr->push_back(world_point);
178 }
179 cur_world_cloud_ptr->set_timestamp(frame->timestamp);
180
181 // fusing clouds
182 for (auto& prev_world_cloud_ptr : prev_world_clouds_) {
183 num_points += prev_world_cloud_ptr->size();
184 }
185 FuseCloud(cur_cloud_ptr_, prev_world_clouds_);
186
187 // after fusing
188 while (static_cast<int>(prev_world_clouds_.size()) >=
189 param_.preprocess().num_fuse_frames() - 1) {
190 prev_world_clouds_.pop_front();
191 }
192 prev_world_clouds_.emplace_back(cur_world_cloud_ptr);
193 }
194 AINFO << "num points after fusing: " << num_points;
195 fuse_time_ = timer.toc(true);
196
197 // shuffle points and cut off
198 if (param_.preprocess().enable_shuffle_points()) {
199 num_points = std::min(num_points, param_.preprocess().max_num_points());
200 std::vector<int> point_indices = GenerateIndices(0, num_points, true);
201 base::PointFCloudPtr shuffle_cloud_ptr(
202 new base::PointFCloud(*cur_cloud_ptr_, point_indices));
203 cur_cloud_ptr_ = shuffle_cloud_ptr;
204 }
205 shuffle_time_ = timer.toc(true);
206
207 // point cloud to array
208 float* points_array =
209 new float[num_points * param_.preprocess().num_point_feature()]();
210 CloudToArray(cur_cloud_ptr_, points_array,
211 param_.preprocess().normalizing_factor());
212 cloud_to_array_time_ = timer.toc(true);
213
214 // inference
215 std::vector<float> out_detections;
216 std::vector<int> out_labels;
217 point_pillars_ptr_->DoInference(points_array, num_points, &out_detections,
218 &out_labels);
219 inference_time_ = timer.toc(true);
220
221 // transfer output bounding boxes to objects
222 GetObjects(&frame->segmented_objects, frame->lidar2world_pose,
223 &out_detections, &out_labels);
224 collect_time_ = timer.toc(true);
225
226 AINFO << "PointPillars: "
227 << "\n"
228 << "down sample: " << downsample_time_ << "\t"
229 << "fuse: " << fuse_time_ << "\t"
230 << "shuffle: " << shuffle_time_ << "\t"
231 << "cloud_to_array: " << cloud_to_array_time_ << "\t"
232 << "inference: " << inference_time_ << "\t"
233 << "collect: " << collect_time_;
234
235 delete[] points_array;
236 return true;
237}
238
239void MaskPillarsDetection::CloudToArray(const base::PointFCloudPtr& pc_ptr,
240 float* out_points_array,
241 const float normalizing_factor) {
242 for (size_t i = 0; i < pc_ptr->size(); ++i) {
243 const auto& point = pc_ptr->at(i);
244 float x = point.x;
245 float y = point.y;
246 float z = point.z;
247 float intensity = point.intensity;
248 if (z < z_min_ || z > z_max_ || y < y_min_ || y > y_max_ || x < x_min_ ||
249 x > x_max_) {
250 continue;
251 }
252 out_points_array[i * param_.preprocess().num_point_feature() + 0] = x;
253 out_points_array[i * param_.preprocess().num_point_feature() + 1] = y;
254 out_points_array[i * param_.preprocess().num_point_feature() + 2] = z;
255 out_points_array[i * param_.preprocess().num_point_feature() + 3] =
256 intensity / normalizing_factor;
257 // delta of timestamp between prev and cur frames
258 out_points_array[i * param_.preprocess().num_point_feature() + 4] =
259 static_cast<float>(pc_ptr->points_timestamp(i));
260 }
261}
262
263void MaskPillarsDetection::FuseCloud(
264 const base::PointFCloudPtr& out_cloud_ptr,
265 const std::deque<base::PointDCloudPtr>& fuse_clouds) {
266 for (auto iter = fuse_clouds.rbegin(); iter != fuse_clouds.rend(); ++iter) {
267 double delta_t = lidar_frame_ref_->timestamp - (*iter)->get_timestamp();
268 // transform prev world point cloud to current sensor's coordinates
269 for (size_t i = 0; i < (*iter)->size(); ++i) {
270 auto& point = (*iter)->at(i);
271 Eigen::Vector3d trans_point(point.x, point.y, point.z);
272 trans_point = lidar_frame_ref_->lidar2world_pose.inverse() * trans_point;
273 base::PointF pt;
274 pt.x = static_cast<float>(trans_point(0));
275 pt.y = static_cast<float>(trans_point(1));
276 pt.z = static_cast<float>(trans_point(2));
277 pt.intensity = static_cast<float>(point.intensity);
278 // delta of time between current and prev frame
279 out_cloud_ptr->push_back(pt, delta_t);
280 }
281 }
282}
283
284std::vector<int> MaskPillarsDetection::GenerateIndices(int start_index,
285 int size,
286 bool shuffle) {
287 // create a range number array
288 std::vector<int> indices(size);
289 std::iota(indices.begin(), indices.end(), start_index);
290
291 // shuffle the index array
292 if (shuffle) {
293 unsigned seed = 0;
294 std::shuffle(indices.begin(), indices.end(),
295 std::default_random_engine(seed));
296 }
297 return indices;
298}
299
300void MaskPillarsDetection::GetObjects(
301 std::vector<std::shared_ptr<Object>>* objects, const Eigen::Affine3d& pose,
302 std::vector<float>* detections, std::vector<int>* labels) {
303 int num_objects = detections->size() /
305
306 objects->clear();
307 base::ObjectPool::Instance().BatchGet(num_objects, objects);
308
309 for (int i = 0; i < num_objects; ++i) {
310 auto& object = objects->at(i);
311 object->id = i;
312
313 // read params of bounding box
314 float x = detections->at(
315 i * param_.postprocess().num_output_box_feature() + 0);
316 float y = detections->at(
317 i * param_.postprocess().num_output_box_feature() + 1);
318 float z = detections->at(
319 i * param_.postprocess().num_output_box_feature() + 2);
320 float dx = detections->at(
321 i * param_.postprocess().num_output_box_feature() + 4);
322 float dy = detections->at(
323 i * param_.postprocess().num_output_box_feature() + 3);
324 float dz = detections->at(
325 i * param_.postprocess().num_output_box_feature() + 5);
326 float yaw = detections->at(
327 i * param_.postprocess().num_output_box_feature() + 6);
328 yaw += M_PI / 2;
329 yaw = std::atan2(sinf(yaw), cosf(yaw));
330 yaw = -yaw;
331
332 // directions
333 object->theta = yaw;
334 object->direction[0] = cosf(yaw);
335 object->direction[1] = sinf(yaw);
336 object->direction[2] = 0;
337 object->lidar_supplement.is_orientation_ready = true;
338
339 // compute vertexes of bounding box and transform to world coordinate
340 object->lidar_supplement.num_points_in_roi = 8;
341 object->lidar_supplement.on_use = true;
342 object->lidar_supplement.is_background = false;
343 float roll = 0, pitch = 0;
344 Eigen::Quaternionf quater =
345 Eigen::AngleAxisf(roll, Eigen::Vector3f::UnitX()) *
346 Eigen::AngleAxisf(pitch, Eigen::Vector3f::UnitY()) *
347 Eigen::AngleAxisf(yaw, Eigen::Vector3f::UnitZ());
348 Eigen::Translation3f translation(x, y, z);
349 Eigen::Affine3f affine3f = translation * quater.toRotationMatrix();
350 for (float vx : std::vector<float>{dx / 2, -dx / 2}) {
351 for (float vy : std::vector<float>{dy / 2, -dy / 2}) {
352 for (float vz : std::vector<float>{0, dz}) {
353 Eigen::Vector3f v3f(vx, vy, vz);
354 v3f = affine3f * v3f;
355 PointF point;
356 point.x = v3f.x();
357 point.y = v3f.y();
358 point.z = v3f.z();
359 object->lidar_supplement.cloud.push_back(point);
360
361 Eigen::Vector3d trans_point(point.x, point.y, point.z);
362 trans_point = pose * trans_point;
363 PointD world_point;
364 world_point.x = trans_point(0);
365 world_point.y = trans_point(1);
366 world_point.z = trans_point(2);
367 object->lidar_supplement.cloud_world.push_back(world_point);
368 }
369 }
370 }
371
372 // classification
373 object->lidar_supplement.raw_probs.push_back(std::vector<float>(
374 static_cast<int>(base::ObjectType::MAX_OBJECT_TYPE), 0.f));
375 object->lidar_supplement.raw_classification_methods.push_back(Name());
376 object->sub_type = GetObjectSubType(labels->at(i));
377 object->type = base::kSubType2TypeMap.at(object->sub_type);
378 object->lidar_supplement.raw_probs.back()[static_cast<int>(object->type)] =
379 1.0f;
380 // copy to type
381 object->type_probs.assign(object->lidar_supplement.raw_probs.back().begin(),
382 object->lidar_supplement.raw_probs.back().end());
383 }
384}
385
386// TODO(all): update the base ObjectSubType with more fine-grained types
387// TODO(chenjiahao): move types into an array in the same order as offline
388base::ObjectSubType MaskPillarsDetection::GetObjectSubType(const int label) {
389 switch (label) {
390 case 0:
392 case 1:
394 case 2: // construction vehicle
396 default:
398 }
399}
400
402
403} // namespace lidar
404} // namespace perception
405} // namespace apollo
#define PERCEPTION_REGISTER_LIDARDETECTOR(name)
static PluginManager * Instance()
get singleton instance of PluginManager
std::shared_ptr< Base > CreateInstance(const std::string &derived_class)
create plugin instance of derived class based on Base
static std::string GetFullClassName(const std::string &class_name)
Given the class name of perception module, combine the namespace "apollo::perception::lidar::" with i...
MaskPillarsDetection()
Construct a new Mask Pillars Detection object
bool Init(const LidarDetectorInitOptions &options=LidarDetectorInitOptions()) override
Init of Mask Pillars Detection object
std::string Name() const override
Name of Mask Pillars Detection module
bool Detect(const LidarDetectorOptions &options, LidarFrame *frame) override
Detect foregorund objects using maskpillars
double toc(bool reset=false)
Definition lidar_timer.h:28
#define ACHECK(cond)
Definition log.h:80
#define AERROR
Definition log.h:44
#define AINFO
Definition log.h:42
#define AWARN
Definition log.h:43
bool GetProtoFromFile(const std::string &file_name, google::protobuf::Message *message)
Parses the content of the file specified by the file_name as a representation of protobufs,...
Definition file.cc:132
Point< float > PointF
Definition point.h:56
std::shared_ptr< PointDCloud > PointDCloudPtr
std::shared_ptr< PointFCloud > PointFCloudPtr
const std::map< ObjectSubType, ObjectType > kSubType2TypeMap
ObjectSubType mapping
Point< double > PointD
Definition point.h:57
std::string GetModelFile(const std::string &model_name, const std::string &file_name)
Get the model file path by model path and file name
Definition util.cc:55
std::string GetConfigFile(const std::string &config_path, const std::string &config_file)
Definition util.cc:80
std::string GetModelPath(const std::string &model_name)
Get the model path by model name, search from APOLLO_MODEL_PATH
Definition util.cc:44
class register implement
Definition arena_queue.h:37
Definition future.h:29
std::shared_ptr< base::AttributePointCloud< base::PointD > > world_cloud
Definition lidar_frame.h:37
std::vector< std::shared_ptr< base::Object > > segmented_objects
Definition lidar_frame.h:53
std::shared_ptr< base::AttributePointCloud< base::PointF > > cloud
Definition lidar_frame.h:35
optional common::PointCloudPostProcess postprocess
optional common::PointCloudPreProcess preprocess