Apollo 11.0
自动驾驶开放平台
transform_wrapper.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2018 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 <limits>
19
20#include "cyber/common/log.h"
23
24namespace apollo {
25namespace perception {
26namespace onboard {
27
28DEFINE_string(obs_sensor2novatel_tf2_frame_id, "novatel",
29 "sensor to novatel frame id");
30DEFINE_string(obs_novatel2world_tf2_frame_id, "world",
31 "novatel to world frame id");
32DEFINE_string(obs_novatel2world_tf2_child_frame_id, "novatel",
33 "novatel to world child frame id");
34DEFINE_double(obs_tf2_buff_size, 0.01, "query Cyber TF buffer size in second");
35DEFINE_double(obs_transform_cache_size, 1.0, "transform cache size in second");
36DEFINE_double(obs_max_local_pose_extrapolation_latency, 0.15,
37 "max local pose extrapolation period in second");
38DEFINE_bool(obs_enable_local_pose_extrapolation, true,
39 "use local pose extrapolation");
40DEFINE_bool(hardware_trigger, true, "camera trigger method");
41
43 if (transforms_.empty()) {
44 transforms_.push_back(transform);
45 return;
46 }
47 double delt = transform.timestamp - transforms_.back().timestamp;
48 if (delt < 0.0) {
49 AINFO << "ERROR: add earlier transform to transform cache";
50 return;
51 }
52
53 do {
54 delt = transform.timestamp - transforms_.front().timestamp;
55 if (delt < cache_duration_) {
56 break;
57 }
58 transforms_.pop_front();
59 } while (!transforms_.empty());
60
61 transforms_.push_back(transform);
62}
63
64Eigen::Quaterniond Slerp(const Eigen::Quaterniond& source, const double& t,
65 const Eigen::Quaterniond& other) {
66 const double one = 1.0 - std::numeric_limits<double>::epsilon();
67 double d = source.x() * other.x() + source.y() * other.y() +
68 source.z() * other.z() + source.w() * other.w();
69 double abs_d = std::abs(d);
70
71 double scale0;
72 double scale1;
73
74 if (abs_d >= one) {
75 scale0 = 1.0 - t;
76 scale1 = t;
77 } else {
78 // theta is the angle between the 2 quaternions
79 double theta = std::acos(abs_d);
80 double sin_theta = std::sin(theta);
81
82 scale0 = std::sin((1.0 - t) * theta) / sin_theta;
83 scale1 = std::sin((t * theta)) / sin_theta;
84 }
85 if (d < 0) scale1 = -scale1;
86
87 return Eigen::Quaterniond(scale0 * source.w() + scale1 * other.w(),
88 scale0 * source.x() + scale1 * other.x(),
89 scale0 * source.y() + scale1 * other.y(),
90 scale0 * source.z() + scale1 * other.z());
91}
92
93bool TransformCache::QueryTransform(double timestamp,
94 StampedTransform* transform,
95 double max_duration) {
96 if (transforms_.empty() || transform == nullptr) {
97 return false;
98 }
99
100 double delt = timestamp - transforms_.back().timestamp;
101 if (delt > max_duration) {
102 AINFO << "ERROR: query timestamp is " << delt
103 << "s later than cached timestamp";
104 return false;
105 } else if (delt < 0.0) {
106 AINFO << "ERROR: query earlier timestamp than transform cache";
107 return false;
108 }
109
110 int size = static_cast<int>(transforms_.size());
111 if (size == 1) {
112 (*transform) = transforms_.back();
113 transform->timestamp = timestamp;
114 AINFO << "use transform at " << transforms_.back().timestamp << " for "
115 << timestamp;
116 } else {
117 double ratio =
118 (timestamp - transforms_[size - 2].timestamp) /
119 (transforms_[size - 1].timestamp - transforms_[size - 2].timestamp);
120
121 transform->rotation = Slerp(transforms_[size - 2].rotation, ratio,
122 transforms_[size - 1].rotation);
123
124 transform->translation.x() =
125 transforms_[size - 2].translation.x() * (1 - ratio) +
126 transforms_[size - 1].translation.x() * ratio;
127 transform->translation.y() =
128 transforms_[size - 2].translation.y() * (1 - ratio) +
129 transforms_[size - 1].translation.y() * ratio;
130 transform->translation.z() =
131 transforms_[size - 2].translation.z() * (1 - ratio) +
132 transforms_[size - 1].translation.z() * ratio;
133
134 AINFO << "estimate pose at " << timestamp << " from poses at "
135 << transforms_[size - 2].timestamp << " and "
136 << transforms_[size - 1].timestamp;
137 }
138 return true;
139}
140
142 const std::string& sensor2novatel_tf2_child_frame_id) {
143 sensor2novatel_tf2_frame_id_ = FLAGS_obs_sensor2novatel_tf2_frame_id;
144 sensor2novatel_tf2_child_frame_id_ = sensor2novatel_tf2_child_frame_id;
145 novatel2world_tf2_frame_id_ = FLAGS_obs_novatel2world_tf2_frame_id;
146 novatel2world_tf2_child_frame_id_ =
147 FLAGS_obs_novatel2world_tf2_child_frame_id;
148 transform_cache_.SetCacheDuration(FLAGS_obs_transform_cache_size);
149 inited_ = true;
150}
151
153 const std::string& sensor2novatel_tf2_frame_id,
154 const std::string& sensor2novatel_tf2_child_frame_id,
155 const std::string& novatel2world_tf2_frame_id,
156 const std::string& novatel2world_tf2_child_frame_id) {
157 sensor2novatel_tf2_frame_id_ = sensor2novatel_tf2_frame_id;
158 sensor2novatel_tf2_child_frame_id_ = sensor2novatel_tf2_child_frame_id;
159 novatel2world_tf2_frame_id_ = novatel2world_tf2_frame_id;
160 novatel2world_tf2_child_frame_id_ = novatel2world_tf2_child_frame_id;
161 transform_cache_.SetCacheDuration(FLAGS_obs_transform_cache_size);
162 inited_ = true;
163}
164
166 double timestamp, Eigen::Affine3d* sensor2world_trans,
167 Eigen::Affine3d* novatel2world_trans) {
168 if (!inited_) {
169 AERROR << "TransformWrapper not Initialized,"
170 << " unable to call GetSensor2worldTrans.";
171 return false;
172 }
173
174 if (sensor2novatel_extrinsics_ == nullptr) {
175 StampedTransform trans_sensor2novatel;
176 if (!QueryTrans(timestamp, &trans_sensor2novatel,
177 sensor2novatel_tf2_frame_id_,
178 sensor2novatel_tf2_child_frame_id_)) {
179 return false;
180 }
181 sensor2novatel_extrinsics_.reset(new Eigen::Affine3d);
182 *sensor2novatel_extrinsics_ =
183 trans_sensor2novatel.translation * trans_sensor2novatel.rotation;
184 AINFO << "Get sensor2novatel extrinsics successfully.";
185 }
186
187 StampedTransform trans_novatel2world;
188 trans_novatel2world.timestamp = timestamp;
189 Eigen::Affine3d novatel2world;
190
191 if (!QueryTrans(timestamp, &trans_novatel2world, novatel2world_tf2_frame_id_,
192 novatel2world_tf2_child_frame_id_)) {
193 if (FLAGS_obs_enable_local_pose_extrapolation) {
194 if (!transform_cache_.QueryTransform(
195 timestamp, &trans_novatel2world,
196 FLAGS_obs_max_local_pose_extrapolation_latency)) {
197 return false;
198 }
199 } else {
200 return false;
201 }
202 } else if (FLAGS_obs_enable_local_pose_extrapolation) {
203 transform_cache_.AddTransform(trans_novatel2world);
204 }
205
206 novatel2world =
207 trans_novatel2world.translation * trans_novatel2world.rotation;
208 *sensor2world_trans = novatel2world * (*sensor2novatel_extrinsics_);
209 if (novatel2world_trans != nullptr) {
210 *novatel2world_trans = novatel2world;
211 }
212 AINFO << "Get pose timestamp: " << timestamp << ", pose: \n"
213 << (*sensor2world_trans).matrix();
214 return true;
215}
216
217bool TransformWrapper::GetExtrinsics(Eigen::Affine3d* trans) {
218 if (!inited_ || trans == nullptr || sensor2novatel_extrinsics_ == nullptr) {
219 AERROR << "TransformWrapper get extrinsics failed";
220 return false;
221 }
222 *trans = *sensor2novatel_extrinsics_;
223 return true;
224}
225
226bool TransformWrapper::GetExtrinsics(double timestamp, Eigen::Affine3d* trans) {
227 if (sensor2novatel_extrinsics_ == nullptr) {
228 StampedTransform trans_sensor2novatel;
229 if (!QueryTrans(timestamp, &trans_sensor2novatel,
230 sensor2novatel_tf2_frame_id_,
231 sensor2novatel_tf2_child_frame_id_)) {
232 return false;
233 }
234 sensor2novatel_extrinsics_.reset(new Eigen::Affine3d);
235 *sensor2novatel_extrinsics_ =
236 trans_sensor2novatel.translation * trans_sensor2novatel.rotation;
237 AINFO << "Get sensor2novatel extrinsics successfully.";
238 }
239 if (!inited_ || trans == nullptr || sensor2novatel_extrinsics_ == nullptr) {
240 AERROR << "TransformWrapper get extrinsics failed";
241 return false;
242 }
243 *trans = *sensor2novatel_extrinsics_;
244 return true;
245}
246
247bool TransformWrapper::GetTrans(double timestamp, Eigen::Affine3d* trans,
248 const std::string& frame_id,
249 const std::string& child_frame_id) {
250 StampedTransform transform;
251 if (!QueryTrans(timestamp, &transform, frame_id, child_frame_id)) {
252 if (!FLAGS_obs_enable_local_pose_extrapolation ||
253 !transform_cache_.QueryTransform(
254 timestamp, &transform,
255 FLAGS_obs_max_local_pose_extrapolation_latency)) {
256 return false;
257 }
258 }
259
260 if (FLAGS_obs_enable_local_pose_extrapolation) {
261 transform_cache_.AddTransform(transform);
262 }
263
264 *trans = transform.translation * transform.rotation;
265 return true;
266}
267
268bool TransformWrapper::QueryTrans(double timestamp, StampedTransform* trans,
269 const std::string& frame_id,
270 const std::string& child_frame_id) {
271 cyber::Time query_time(timestamp);
272 std::string err_string;
273 if (!tf2_buffer_->canTransform(frame_id, child_frame_id, query_time,
274 static_cast<float>(FLAGS_obs_tf2_buff_size),
275 &err_string)) {
277 double latest_buffer_time;
278 if (!FLAGS_hardware_trigger) {
279 try {
280 latest_transform = tf2_buffer_->lookupTransform(
281 frame_id, child_frame_id, apollo::cyber::Time(0));
282 latest_buffer_time = latest_transform.header().timestamp_sec();
283 } catch (tf2::TransformException& ex) {
284 AERROR << ex.what();
285 return false;
286 }
287 }
288 // In simulation mode, use the latest transform information if query failed.
289 if (!cyber::common::GlobalData::Instance()->IsRealityMode()) {
290 query_time = cyber::Time(0);
291 } else if (!FLAGS_hardware_trigger &&
292 (timestamp - latest_buffer_time < 0.015) &&
293 (timestamp - latest_buffer_time > 0)) {
294 // soft trigger and the latency is within the tolerance range
295 query_time = apollo::cyber::Time(0);
296 } else {
297 AERROR << "Can not find transform. " << FORMAT_TIMESTAMP(timestamp)
298 << " frame_id: " << frame_id
299 << " child_frame_id: " << child_frame_id
300 << " Error info: " << err_string;
301 return false;
302 }
303 }
304
305 apollo::transform::TransformStamped stamped_transform;
306 try {
307 stamped_transform =
308 tf2_buffer_->lookupTransform(frame_id, child_frame_id, query_time);
309
310 trans->translation =
311 Eigen::Translation3d(stamped_transform.transform().translation().x(),
312 stamped_transform.transform().translation().y(),
313 stamped_transform.transform().translation().z());
314 trans->rotation =
315 Eigen::Quaterniond(stamped_transform.transform().rotation().qw(),
316 stamped_transform.transform().rotation().qx(),
317 stamped_transform.transform().rotation().qy(),
318 stamped_transform.transform().rotation().qz());
319 } catch (tf2::TransformException& ex) {
320 AERROR << ex.what();
321 return false;
322 }
323 return true;
324}
325
327 const std::string& from_sensor_id, const std::string& to_sensor_id,
328 Eigen::Affine3d* trans) {
329 if (trans == nullptr) {
330 AERROR << "TransformWrapper get extrinsics failed";
331 return false;
332 }
333
334 algorithm::SensorManager* sensor_manager =
335 algorithm::SensorManager::Instance();
336 std::string frame_id = sensor_manager->GetFrameId(to_sensor_id);
337 std::string child_frame_id = sensor_manager->GetFrameId(from_sensor_id);
338
339 StampedTransform transform;
340 bool status = QueryTrans(0.0, &transform, frame_id, child_frame_id);
341 if (status) {
342 *trans = transform.translation * transform.rotation;
343 }
344 return status;
345}
346
347} // namespace onboard
348} // namespace perception
349} // namespace apollo
Cyber has builtin time type Time.
Definition time.h:31
std::string GetFrameId(const std::string &name) const
bool QueryTransform(double timestamp, StampedTransform *transform, double max_duration=0.0)
void AddTransform(const StampedTransform &transform)
std::deque< StampedTransform > transforms_
bool QueryTrans(double timestamp, StampedTransform *trans, const std::string &frame_id, const std::string &child_frame_id)
bool GetExtrinsicsBySensorId(const std::string &from_sensor_id, const std::string &to_sensor_id, Eigen::Affine3d *trans)
bool GetTrans(double timestamp, Eigen::Affine3d *trans, const std::string &frame_id, const std::string &child_frame_id)
bool GetSensor2worldTrans(double timestamp, Eigen::Affine3d *sensor2world_trans, Eigen::Affine3d *novatel2world_trans=nullptr)
void Init(const std::string &sensor2novatel_tf2_child_frame_id)
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_double(obs_buffer_match_precision, 0.01, "match_precision for odometry_subscriber")
Eigen::Quaterniond Slerp(const Eigen::Quaterniond &source, const double &t, const Eigen::Quaterniond &other)
DEFINE_string(obs_screen_output_dir, "./", "output dir. for saving visualization screenshots")
DEFINE_bool(obs_enable_hdmap_input, true, "enable hdmap input for roi filter")
class register implement
Definition arena_queue.h:37
Some string util functions.
#define FORMAT_TIMESTAMP(timestamp)
Definition string_util.h:29
optional double timestamp_sec
Definition header.proto:9
optional apollo::common::Header header
optional apollo::common::Quaternion rotation
optional apollo::common::Point3D translation