Apollo 11.0
自动驾驶开放平台
fusion_and_compensator_component.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2024 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 *****************************************************************************/
16
18
19#include <climits>
20#include <cstdlib>
21#include <memory>
22#include <thread>
23
24namespace apollo {
25namespace drivers {
26namespace lidar {
27
29
31 if (!GetProtoConfig(&conf_)) {
32 AWARN << "Load config failed, config file" << ConfigFilePath();
33 return false;
34 }
35 tf2_buffer_ptr_ = apollo::transform::Buffer::Instance();
36
37 writer_ = node_->CreateWriter<PointCloud>(conf_.output_channel());
38
39 for (const auto& channel : conf_.input_channel()) {
40 auto reader = node_->CreateReader<PointCloud>(channel);
41
42 readers_.emplace_back(reader);
43 }
44 for (const auto& filter_config : conf_.filter_config()) {
45 filter_map_.emplace(filter_config.frame_id(), &filter_config);
46 }
47 return true;
48}
49
50bool FusionAndCompensatorComponent::Proc(const std::shared_ptr<PointCloud>& point_cloud) {
51 auto target = writer_->AcquireMessage();
52 target->Clear();
53 target->mutable_point()->Reserve(reserved_point_size_);
54 target->mutable_header()->set_lidar_timestamp(point_cloud->header().lidar_timestamp());
55 if (conf_.has_target_frame_id()) {
56 target->mutable_header()->set_frame_id(conf_.target_frame_id());
57 } else {
58 target->mutable_header()->set_frame_id(point_cloud->header().frame_id());
59 }
60
61 auto start_time = Time::Now().ToSecond();
62 std::vector<std::shared_ptr<PointCloud>> point_clouds = {point_cloud};
63 auto fusion_readers = readers_;
64 while ((Time::Now().ToSecond() - start_time) < conf_.wait_time_s() && fusion_readers.size() > 0) {
65 for (auto itr = fusion_readers.begin(); itr != fusion_readers.end();) {
66 (*itr)->Observe();
67 if (!(*itr)->Empty()) {
68 auto source = (*itr)->GetLatestObserved();
69 if (conf_.drop_expired_data() && IsExpired(target, source)) {
70 ++itr;
71 } else {
72 point_clouds.emplace_back(source);
73 itr = fusion_readers.erase(itr);
74 }
75 } else {
76 ++itr;
77 }
78 }
79 std::this_thread::sleep_for(std::chrono::milliseconds(5));
80 }
81
82 auto diff = Time::Now().ToNanosecond() - target->header().lidar_timestamp();
83 AINFO << "Pointcloud fusion diff: " << diff / 1000000 << "ms";
84
85 uint64_t timestamp_min = 0;
86 uint64_t timestamp_max = 0;
87 GetTimestampInterval(point_clouds, &timestamp_min, &timestamp_max);
88
89 for (const auto& pc : point_clouds) {
90 if (!FusionAndCompensator(pc, timestamp_min, timestamp_max, target)) {
91 return false;
92 }
93 }
94
95 target->mutable_header()->set_sequence_num(seq_num_++);
96 target->mutable_header()->set_timestamp_sec(cyber::Time::Now().ToSecond());
97 target->set_height(point_cloud->height());
98 target->set_width(target->point_size() / target->height());
99 target->set_is_dense(point_cloud->is_dense());
100 target->set_measurement_time(point_cloud->measurement_time());
101
102 writer_->Write(target);
103 return true;
104}
105
106void FusionAndCompensatorComponent::GetTimestampInterval(
107 const std::vector<std::shared_ptr<PointCloud>>& point_clouds,
108 uint64_t* timestamp_min,
109 uint64_t* timestamp_max) {
110 *timestamp_max = 0;
111 *timestamp_min = std::numeric_limits<uint64_t>::max();
112 for (const auto& pc : point_clouds) {
113 for (const auto& point : pc->point()) {
114 if (point.timestamp() < *timestamp_min) {
115 *timestamp_min = point.timestamp();
116 }
117
118 if (point.timestamp() > *timestamp_max) {
119 *timestamp_max = point.timestamp();
120 }
121 }
122 }
123}
124
125bool FusionAndCompensatorComponent::IsExpired(
126 const std::shared_ptr<PointCloud>& target,
127 const std::shared_ptr<PointCloud>& source) {
128 auto diff = target->measurement_time() - source->measurement_time();
129 return diff * 1000 > conf_.max_interval_ms();
130}
131
132bool FusionAndCompensatorComponent::QueryPoseAffineFromTF2(
133 const uint64_t& timestamp,
134 const std::string& target_frame_id,
135 const std::string& source_frame_id,
136 Eigen::Affine3d* pose) {
137 cyber::Time query_time(timestamp);
138 std::string err_string;
139 if (!tf2_buffer_ptr_->canTransform(
140 target_frame_id, source_frame_id, query_time, conf_.transform_query_timeout(), &err_string)) {
141 AERROR << "Can not find transform, target_frame_id: " << target_frame_id
142 << ", source_frame_id: " << source_frame_id << ", Error info: " << err_string;
143 return false;
144 }
145
146 apollo::transform::TransformStamped stamped_transform;
147 try {
148 stamped_transform = tf2_buffer_ptr_->lookupTransform(target_frame_id, source_frame_id, query_time);
149 } catch (tf2::TransformException& ex) {
150 AERROR << ex.what();
151 return false;
152 }
153
154 *pose = Eigen::Translation3d(
155 stamped_transform.transform().translation().x(),
156 stamped_transform.transform().translation().y(),
157 stamped_transform.transform().translation().z())
158 * Eigen::Quaterniond(
159 stamped_transform.transform().rotation().qw(),
160 stamped_transform.transform().rotation().qx(),
161 stamped_transform.transform().rotation().qy(),
162 stamped_transform.transform().rotation().qz());
163 return true;
164}
165
166bool FusionAndCompensatorComponent::FusionAndCompensator(
167 const std::shared_ptr<PointCloud> source,
168 const uint64_t& timestamp_min,
169 const uint64_t& timestamp_max,
170 std::shared_ptr<PointCloud> target) {
171 auto& target_frame_id = target->header().frame_id();
172 auto& source_frame_id = source->header().frame_id();
173 // get static tf
174 auto lidar_tf_f = static_tf_map_.find(source_frame_id);
175 if (lidar_tf_f == static_tf_map_.end()) {
176 Eigen::Affine3d lidar_tf_d;
177 if (!QueryPoseAffineFromTF2(0, target_frame_id, source_frame_id, &lidar_tf_d)) {
178 return false;
179 }
180 static_tf_map_[source_frame_id] = lidar_tf_d.cast<float>();
181 }
182
183 // compensator
184 Eigen::Affine3d world_tf_min_time;
185 Eigen::Affine3d world_tf_max_time;
186 if (!QueryPoseAffineFromTF2(timestamp_min, conf_.world_frame_id(), target_frame_id, &world_tf_min_time)
187 || !QueryPoseAffineFromTF2(timestamp_max, conf_.world_frame_id(), target_frame_id, &world_tf_max_time)) {
188 AERROR << "compensator transform query error.";
189 return false;
190 }
191 MotionCompensation(
192 source,
193 timestamp_min,
194 timestamp_max,
195 world_tf_min_time,
196 world_tf_max_time,
197 static_tf_map_[source_frame_id],
198 target);
199
200 return true;
201}
202
203bool FusionAndCompensatorComponent::IsFilteredPoint(const Eigen::Vector3f& point, const std::string& frame_id) const {
204 if (filter_map_.find(frame_id) == filter_map_.end()) {
205 return false;
206 }
207 const auto& x = point.x();
208 const auto& y = point.y();
209 const auto& z = point.z();
210 const auto& filter = filter_map_.at(frame_id);
211 if (filter->has_min_x() && x < filter->min_x()) {
212 return false;
213 }
214 if (filter->has_max_x() && x > filter->max_x()) {
215 return false;
216 }
217 if (filter->has_min_y() && y < filter->min_y()) {
218 return false;
219 }
220 if (filter->has_max_y() && y > filter->max_y()) {
221 return false;
222 }
223 if (filter->has_min_z() && z < filter->min_z()) {
224 return false;
225 }
226 if (filter->has_max_z() && z > filter->max_z()) {
227 return false;
228 }
229 return true;
230}
231
232void FusionAndCompensatorComponent::MotionCompensation(
233 const std::shared_ptr<PointCloud>& source,
234 const uint64_t& timestamp_min,
235 const uint64_t& timestamp_max,
236 const Eigen::Affine3d& world_tf_min_time,
237 const Eigen::Affine3d& world_tf_max_time,
238 const Eigen::Affine3f& lidar_tf,
239 std::shared_ptr<PointCloud> target) {
240 Eigen::Vector3d translation_d = world_tf_min_time.translation() - world_tf_max_time.translation();
241 Eigen::Quaterniond q_max(world_tf_max_time.linear());
242 Eigen::Quaterniond q_min(world_tf_min_time.linear());
243 Eigen::Quaterniond q1_d(q_max.conjugate() * q_min);
244 Eigen::Quaterniond q0_d(Eigen::Quaterniond::Identity());
245 Eigen::Quaternionf q1 = q1.cast<float>();
246 Eigen::Quaternionf q0 = q0_d.cast<float>();
247 q1.normalize();
248 Eigen::Vector3f translation = (q_max.conjugate() * translation_d).cast<float>();
249
250 double d = q0.dot(q1);
251 double abs_d = std::abs(d);
252 float f = 1.0 / static_cast<float>(timestamp_max - timestamp_min);
253
254 // Threshold for a "significant" rotation from min_time to max_time:
255 // The LiDAR range accuracy is ~2 cm. Over 70 meters range, it means an angle
256 // of 0.02 / 70 =
257 // 0.0003 rad. So, we consider a rotation "significant" only if the scalar
258 // part of quaternion is
259 // less than cos(0.0003 / 2) = 1 - 1e-8.
260 if (conf_.rotation_compensation() && abs_d < 1.0 - 1.0e-8) {
261 float theta = static_cast<float>(std::acos(abs_d));
262 float sin_theta = std::sin(theta);
263 float c1_sign = (d > 0) ? 1 : -1;
264 for (const auto& point : source->point()) {
265 float x_scalar = point.x();
266 float y_scalar = point.y();
267 float z_scalar = point.z();
268 if (std::isnan(x_scalar) || std::isnan(y_scalar) || std::isnan(z_scalar)) {
269 // ignore nan point
270 continue;
271 }
272 Eigen::Vector3f p(x_scalar, y_scalar, z_scalar);
273
274 auto fusion_p = lidar_tf * p;
275 if (IsFilteredPoint(fusion_p, source->header().frame_id())) {
276 continue;
277 }
278
279 uint64_t tp = point.timestamp();
280 float t = static_cast<float>(timestamp_max - tp) * f;
281
282 Eigen::Translation3f ti(t * translation);
283
284 float c0 = std::sin((1 - t) * theta) / sin_theta;
285 float c1 = std::sin(t * theta) / sin_theta * c1_sign;
286 Eigen::Quaternionf qi(c0 * q0.coeffs() + c1 * q1.coeffs());
287
288 Eigen::Affine3f trans = ti * qi;
289 p = trans * fusion_p;
290
291 auto* point_new = target->add_point();
292 point_new->set_intensity(point.intensity());
293 point_new->set_timestamp(point.timestamp());
294 point_new->set_x(p.x());
295 point_new->set_y(p.y());
296 point_new->set_z(p.z());
297 }
298 } else {
299 // Not a "significant" rotation. Do translation only.
300 for (auto& point : source->point()) {
301 float x_scalar = point.x();
302 float y_scalar = point.y();
303 float z_scalar = point.z();
304 if (std::isnan(x_scalar) || std::isnan(y_scalar) || std::isnan(z_scalar)) {
305 // ignore nan point
306 continue;
307 }
308
309 Eigen::Vector3f p(x_scalar, y_scalar, z_scalar);
310
311 auto fusion_p = lidar_tf * p;
312 if (IsFilteredPoint(fusion_p, source->header().frame_id())) {
313 continue;
314 }
315
316 if (conf_.translation_compensation()) {
317 uint64_t tp = point.timestamp();
318 float t = static_cast<float>(timestamp_max - tp) * f;
319 Eigen::Translation3f ti(t * translation);
320 p = ti * fusion_p;
321 } else {
322 p = fusion_p;
323 }
324
325 auto* point_new = target->add_point();
326 point_new->set_intensity(point.intensity());
327 point_new->set_timestamp(point.timestamp());
328 point_new->set_x(p.x());
329 point_new->set_y(p.y());
330 point_new->set_z(p.z());
331 }
332 }
333}
334
335} // namespace lidar
336} // namespace drivers
337} // namespace apollo
double f
bool GetProtoConfig(T *config) const
const std::string & ConfigFilePath() const
std::shared_ptr< Node > node_
Cyber has builtin time type Time.
Definition time.h:31
uint64_t ToNanosecond() const
convert time to nanosecond.
Definition time.cc:83
static Time Now()
get the current time.
Definition time.cc:57
double ToSecond() const
convert time to second.
Definition time.cc:77
bool Proc(const std::shared_ptr< PointCloud > &point_cloud) override
virtual bool canTransform(const std::string &target_frame, const std::string &source_frame, const cyber::Time &target_time, const float timeout_second=0.01f, std::string *errstr=nullptr) const
Test if a transform is possible
Definition buffer.cc:195
virtual TransformStamped lookupTransform(const std::string &target_frame, const std::string &source_frame, const cyber::Time &time, const float timeout_second=0.01f) const
Get the transform between two frames by frame ID.
Definition buffer.cc:168
#define AERROR
Definition log.h:44
#define AINFO
Definition log.h:42
#define AWARN
Definition log.h:43
float diff(Image< float > *I, int x1, int y1, int x2, int y2)
Dissimilarity measure between pixels
class register implement
Definition arena_queue.h:37
optional apollo::common::Quaternion rotation
optional apollo::common::Point3D translation