19#include "Eigen/Geometry"
20#include "yaml-cpp/yaml.h"
27#include "modules/common_msgs/sensor_msgs/gnss_best_pose.pb.h"
31namespace localization {
35 tf_buffer_ = apollo::transform::Buffer::Instance();
39 zone_id_ = FLAGS_local_utm_zone_id;
40 online_resolution_ = FLAGS_online_resolution;
41 ndt_debug_log_flag_ = FLAGS_ndt_debug_log_flag;
42 tf_source_frame_id_ = FLAGS_broadcast_tf_frame_id;
43 tf_target_frame_id_ = FLAGS_broadcast_tf_child_frame_id;
44 std::string lidar_height_file = FLAGS_lidar_height_file;
45 std::string lidar_extrinsics_file = FLAGS_lidar_extrinsics_file;
46 bad_score_count_threshold_ = FLAGS_ndt_bad_score_count_threshold;
47 warnning_ndt_score_ = FLAGS_ndt_warnning_ndt_score;
48 error_ndt_score_ = FLAGS_ndt_error_ndt_score;
50 std::string map_path_ =
51 FLAGS_map_dir +
"/" + FLAGS_ndt_map_dir +
"/" + FLAGS_local_map_name;
52 AINFO <<
"map folder: " << map_path_;
53 velodyne_extrinsic_ = Eigen::Affine3d::Identity();
55 LoadLidarExtrinsic(lidar_extrinsics_file, &velodyne_extrinsic_);
57 AERROR <<
"LocalizationLidar: Fail to access the lidar"
59 << lidar_extrinsics_file;
62 Eigen::Quaterniond ext_quat(velodyne_extrinsic_.linear());
63 AINFO <<
"lidar extrinsics: " << velodyne_extrinsic_.translation().x() <<
", "
64 << velodyne_extrinsic_.translation().y() <<
", "
65 << velodyne_extrinsic_.translation().z() <<
", " << ext_quat.x() <<
", "
66 << ext_quat.y() <<
", " << ext_quat.z() <<
", " << ext_quat.w();
68 success = LoadLidarHeight(lidar_height_file, &lidar_height_);
70 AWARN <<
"LocalizationLidar: Fail to load the lidar"
72 << lidar_height_file <<
" Will use default value!";
73 lidar_height_.
height = FLAGS_lidar_height_default;
77 if (FLAGS_if_utm_zone_id_from_folder) {
78 bool success = LoadZoneIdFromFolder(map_path_, &zone_id_);
80 AWARN <<
"Can't load utm zone id from map folder, use default value.";
83 AINFO <<
"utm zone id: " << zone_id_;
89 static_cast<float>(online_resolution_));
91 odometry_buffer_.clear();
92 odometry_buffer_size_ = 0;
94 is_service_started_ =
false;
98 const std::shared_ptr<localization::Gps>& odometry_msg) {
99 double odometry_time = odometry_msg->header().timestamp_sec();
100 static double pre_odometry_time = odometry_time;
101 double time_delay = odometry_time - pre_odometry_time;
102 if (time_delay > 0.1) {
103 AINFO <<
"Odometry message loss more than 10ms, the pre time and cur time: "
104 << pre_odometry_time <<
", " << odometry_time;
105 }
else if (time_delay < 0.0) {
106 AINFO <<
"Odometry message's time is earlier than last one, "
107 <<
"the pre time and cur time: " << pre_odometry_time <<
", "
110 pre_odometry_time = odometry_time;
111 Eigen::Affine3d odometry_pose = Eigen::Affine3d::Identity();
112 if (odometry_msg->has_localization()) {
113 odometry_pose.translation()[0] =
114 odometry_msg->localization().position().x();
115 odometry_pose.translation()[1] =
116 odometry_msg->localization().position().y();
117 odometry_pose.translation()[2] =
118 odometry_msg->localization().position().z();
119 Eigen::Quaterniond tmp_quat(
120 odometry_msg->localization().orientation().qw(),
121 odometry_msg->localization().orientation().qx(),
122 odometry_msg->localization().orientation().qy(),
123 odometry_msg->localization().orientation().qz());
124 odometry_pose.linear() = tmp_quat.toRotationMatrix();
126 if (ZeroOdometry(odometry_pose)) {
127 AINFO <<
"Detect Zero Odometry";
132 timestamp_pose.
timestamp = odometry_time;
133 timestamp_pose.
pose = odometry_pose;
135 std::lock_guard<std::mutex> lock(odometry_buffer_mutex_);
136 if (odometry_buffer_size_ < max_odometry_buffer_size_) {
137 odometry_buffer_.push_back(timestamp_pose);
138 odometry_buffer_size_++;
140 odometry_buffer_.pop_front();
141 odometry_buffer_.push_back(timestamp_pose);
145 if (ndt_debug_log_flag_) {
146 AINFO <<
"NDTLocalization Debug Log: odometry msg: "
147 << std::setprecision(15) <<
"time: " << odometry_time <<
", "
148 <<
"x: " << odometry_msg->localization().position().x() <<
", "
149 <<
"y: " << odometry_msg->localization().position().y() <<
", "
150 <<
"z: " << odometry_msg->localization().position().z() <<
", "
151 <<
"qx: " << odometry_msg->localization().orientation().qx() <<
", "
152 <<
"qy: " << odometry_msg->localization().orientation().qy() <<
", "
153 <<
"qz: " << odometry_msg->localization().orientation().qz() <<
", "
154 <<
"qw: " << odometry_msg->localization().orientation().qw();
157 Eigen::Affine3d localization_pose = Eigen::Affine3d::Identity();
162 ComposeLocalizationEstimate(localization_pose, odometry_msg,
163 &localization_result_);
165 FindNearestOdometryStatus(odometry_time, &odometry_status);
166 ComposeLocalizationStatus(odometry_status, &localization_status_);
167 is_service_started_ =
true;
172 const std::shared_ptr<drivers::PointCloud>& lidar_msg) {
173 static unsigned int frame_idx = 0;
175 LidarMsgTransfer(lidar_msg, &lidar_frame);
178 Eigen::Affine3d odometry_pose = Eigen::Affine3d::Identity();
179 if (!QueryPoseFromBuffer(time_stamp, &odometry_pose)) {
180 if (!QueryPoseFromTF(time_stamp, &odometry_pose)) {
181 AERROR <<
"Can not query forecast pose";
184 AINFO <<
"Query pose from TF";
186 AINFO <<
"Query pose from buffer";
189 lidar_locator_.
Init(odometry_pose, resolution_id_, zone_id_);
192 lidar_locator_.
Update(frame_idx++, odometry_pose, lidar_frame);
193 lidar_pose_ = lidar_locator_.
GetPose();
195 ComposeLidarResult(time_stamp, lidar_pose_, &lidar_localization_result_);
197 if (ndt_score_ > warnning_ndt_score_) {
200 bad_score_count_ = 0;
202 if (ndt_debug_log_flag_) {
203 Eigen::Quaterniond tmp_quat(lidar_pose_.linear());
204 AINFO <<
"NDTLocalization Debug Log: lidar pose: " << std::setprecision(15)
205 <<
"time: " << time_stamp <<
", "
206 <<
"x: " << lidar_pose_.translation()[0] <<
", "
207 <<
"y: " << lidar_pose_.translation()[1] <<
", "
208 <<
"z: " << lidar_pose_.translation()[2] <<
", "
209 <<
"qx: " << tmp_quat.x() <<
", "
210 <<
"qy: " << tmp_quat.y() <<
", "
211 <<
"qz: " << tmp_quat.z() <<
", "
212 <<
"qw: " << tmp_quat.w();
214 Eigen::Quaterniond qbn(odometry_pose.linear());
215 AINFO <<
"NDTLocalization Debug Log: odometry for lidar pose: "
216 << std::setprecision(15) <<
"time: " << time_stamp <<
", "
217 <<
"x: " << odometry_pose.translation()[0] <<
", "
218 <<
"y: " << odometry_pose.translation()[1] <<
", "
219 <<
"z: " << odometry_pose.translation()[2] <<
", "
220 <<
"qx: " << qbn.x() <<
", "
221 <<
"qy: " << qbn.y() <<
", "
222 <<
"qz: " << qbn.z() <<
", "
223 <<
"qw: " << qbn.w();
228 const std::shared_ptr<drivers::gnss::InsStat>& status_msg) {
229 std::unique_lock<std::mutex> lock(odometry_status_list_mutex_);
230 if (odometry_status_list_.size() < odometry_status_list_max_size_) {
231 odometry_status_list_.push_back(*status_msg);
233 odometry_status_list_.pop_front();
234 odometry_status_list_.push_back(*status_msg);
240 *localization = localization_result_;
245 *localization = lidar_localization_result_;
250 *localization_status = localization_status_;
255void NDTLocalization::FillLocalizationMsgHeader(
257 auto* header = localization->mutable_header();
259 header->set_module_name(module_name_);
260 header->set_timestamp_sec(timestamp);
261 header->set_sequence_num(++localization_seq_num_);
264void NDTLocalization::ComposeLocalizationEstimate(
265 const Eigen::Affine3d& pose,
266 const std::shared_ptr<localization::Gps>& odometry_msg,
268 localization->Clear();
269 FillLocalizationMsgHeader(localization);
271 localization->set_measurement_time(odometry_msg->header().timestamp_sec());
272 auto mutable_pose = localization->mutable_pose();
273 mutable_pose->mutable_position()->set_x(pose.translation().x());
274 mutable_pose->mutable_position()->set_y(pose.translation().y());
275 mutable_pose->mutable_position()->set_z(pose.translation().z());
277 Eigen::Quaterniond quat(pose.linear());
278 mutable_pose->mutable_orientation()->set_qw(quat.w());
279 mutable_pose->mutable_orientation()->set_qx(quat.x());
280 mutable_pose->mutable_orientation()->set_qy(quat.y());
281 mutable_pose->mutable_orientation()->set_qz(quat.z());
284 mutable_pose->set_heading(heading);
287 mutable_pose->mutable_euler_angles()->set_x(euler.pitch());
288 mutable_pose->mutable_euler_angles()->set_y(euler.roll());
289 mutable_pose->mutable_euler_angles()->set_z(euler.yaw());
291 const auto& odometry_pose = odometry_msg->localization();
292 if (odometry_pose.has_linear_velocity()) {
293 mutable_pose->mutable_linear_velocity()->CopyFrom(
294 odometry_pose.linear_velocity());
296 if (odometry_pose.has_linear_acceleration()) {
297 mutable_pose->mutable_linear_acceleration()->CopyFrom(
298 odometry_pose.linear_acceleration());
300 if (odometry_pose.has_angular_velocity()) {
301 mutable_pose->mutable_angular_velocity()->CopyFrom(
302 odometry_pose.angular_velocity());
304 if (odometry_pose.has_linear_acceleration_vrf()) {
305 mutable_pose->mutable_linear_acceleration_vrf()->CopyFrom(
306 odometry_pose.linear_acceleration_vrf());
308 if (odometry_pose.has_angular_velocity_vrf()) {
309 mutable_pose->mutable_angular_velocity_vrf()->CopyFrom(
310 odometry_pose.angular_velocity_vrf());
314void NDTLocalization::ComposeLidarResult(
double time_stamp,
315 const Eigen::Affine3d& pose,
316 LocalizationEstimate* localization) {
317 localization->Clear();
318 FillLocalizationMsgHeader(localization);
320 localization->set_measurement_time(time_stamp);
321 auto mutable_pose = localization->mutable_pose();
322 mutable_pose->mutable_position()->set_x(pose.translation().x());
323 mutable_pose->mutable_position()->set_y(pose.translation().y());
324 mutable_pose->mutable_position()->set_z(pose.translation().z());
326 Eigen::Quaterniond quat(pose.linear());
327 mutable_pose->mutable_orientation()->set_qw(quat.w());
328 mutable_pose->mutable_orientation()->set_qx(quat.x());
329 mutable_pose->mutable_orientation()->set_qy(quat.y());
330 mutable_pose->mutable_orientation()->set_qz(quat.z());
333 mutable_pose->set_heading(heading);
336 mutable_pose->mutable_euler_angles()->set_x(euler.pitch());
337 mutable_pose->mutable_euler_angles()->set_y(euler.roll());
338 mutable_pose->mutable_euler_angles()->set_z(euler.yaw());
341bool NDTLocalization::QueryPoseFromTF(
double time, Eigen::Affine3d* pose) {
342 cyber::Time query_time(time);
343 const float time_out = 0.01f;
344 std::string err_msg =
"";
346 tf_target_frame_id_, tf_source_frame_id_, query_time, time_out, &err_msg);
347 if (!can_transform) {
348 AERROR <<
"Can not transform: " << err_msg;
354 tf_target_frame_id_, tf_source_frame_id_, query_time);
355 }
catch (tf2::TransformException ex) {
366 pose->linear() = tmp_quat.toRotationMatrix();
370void NDTLocalization::ComposeLocalizationStatus(
371 const drivers::gnss::InsStat& status,
372 LocalizationStatus* localization_status) {
375 header->set_timestamp_sec(timestamp);
376 localization_status->set_measurement_time(status.header().timestamp_sec());
378 if (!status.has_pos_type()) {
380 localization_status->set_state_message(
381 "Error: Current Localization Status Is Missing.");
386 if (ndt_score_ < warnning_ndt_score_ &&
389 localization_status->set_state_message(
"");
390 }
else if (bad_score_count_ > bad_score_count_threshold_ ||
391 ndt_score_ > error_ndt_score_ ||
395 localization_status->set_state_message(
396 "Error: Current Localization Is Very Unstable.");
399 localization_status->set_state_message(
400 "Warning: Current Localization Is Unstable.");
404bool NDTLocalization::QueryPoseFromBuffer(
double time, Eigen::Affine3d* pose) {
407 TimeStampPose pre_pose;
408 TimeStampPose next_pose;
410 std::lock_guard<std::mutex> lock(odometry_buffer_mutex_);
411 TimeStampPose timestamp_pose = odometry_buffer_.back();
413 if (time > timestamp_pose.timestamp) {
414 AERROR <<
"query time is newer than latest odometry time, it doesn't "
419 auto iter = odometry_buffer_.crbegin();
420 for (; iter != odometry_buffer_.crend(); ++iter) {
421 if (iter->timestamp < time) {
425 if (iter == odometry_buffer_.crend()) {
426 AINFO <<
"Cannot find matching pose from odometry buffer";
430 next_pose = *(--iter);
434 (next_pose.timestamp - time) / (next_pose.timestamp - pre_pose.timestamp);
436 (time - pre_pose.timestamp) / (next_pose.timestamp - pre_pose.timestamp);
437 pose->translation() =
438 pre_pose.pose.translation() * v1 + next_pose.pose.translation() * v2;
440 Eigen::Quaterniond pre_quat(pre_pose.pose.linear());
443 pre_quat.y(), pre_quat.z());
445 Eigen::Quaterniond next_quat(next_pose.pose.linear());
447 next_quat.y(), next_quat.z());
449 double tmp_euler[3] = {};
450 tmp_euler[0] = pre_euler.pitch() * v1 + next_euler.pitch() * v2;
451 tmp_euler[1] = pre_euler.roll() * v1 + next_euler.roll() * v2;
452 tmp_euler[2] = pre_euler.yaw() * v1 + next_euler.yaw() * v2;
454 Eigen::Quaterniond quat = euler.ToQuaternion();
456 pose->linear() = quat.toRotationMatrix();
460bool NDTLocalization::ZeroOdometry(
const Eigen::Affine3d& pose) {
461 double x = pose.translation().x();
462 double y = pose.translation().y();
463 double z = pose.translation().z();
464 double norm = std::sqrt(x * x + y * y + z * z);
471void NDTLocalization::LidarMsgTransfer(
472 const std::shared_ptr<drivers::PointCloud>& msg, LidarFrame* lidar_frame) {
473 CHECK_NOTNULL(lidar_frame);
475 if (msg->height() > 1 && msg->width() > 1) {
476 for (
unsigned int i = 0; i < msg->height(); ++i) {
477 for (
unsigned int j = 0; j < msg->width(); ++j) {
478 Eigen::Vector3f pt3d;
479 pt3d[0] = msg->point(i * msg->width() + j).x();
480 pt3d[1] = msg->point(i * msg->width() + j).y();
481 pt3d[2] = msg->point(i * msg->width() + j).z();
482 if (!std::isnan(pt3d[0])) {
483 Eigen::Vector3f pt3d_tem = pt3d;
485 if (pt3d_tem[2] > max_height_) {
488 unsigned char intensity =
static_cast<unsigned char>(
489 msg->point(i * msg->width() + j).intensity());
490 lidar_frame->pt_xs.push_back(pt3d[0]);
491 lidar_frame->pt_ys.push_back(pt3d[1]);
492 lidar_frame->pt_zs.push_back(pt3d[2]);
493 lidar_frame->intensities.push_back(intensity);
498 AINFO <<
"Receiving un-organized-point-cloud, width " << msg->width()
499 <<
" height " << msg->height() <<
"size " << msg->point_size();
500 for (
int i = 0; i < msg->point_size(); ++i) {
501 Eigen::Vector3f pt3d;
502 pt3d[0] = msg->point(i).x();
503 pt3d[1] = msg->point(i).y();
504 pt3d[2] = msg->point(i).z();
505 if (!std::isnan(pt3d[0])) {
506 Eigen::Vector3f pt3d_tem = pt3d;
508 if (pt3d_tem[2] > max_height_) {
511 unsigned char intensity =
512 static_cast<unsigned char>(msg->point(i).intensity());
513 lidar_frame->pt_xs.push_back(pt3d[0]);
514 lidar_frame->pt_ys.push_back(pt3d[1]);
515 lidar_frame->pt_zs.push_back(pt3d[2]);
516 lidar_frame->intensities.push_back(intensity);
521 lidar_frame->measurement_time =
522 cyber::Time(msg->measurement_time()).ToSecond();
523 if (ndt_debug_log_flag_) {
524 AINFO << std::setprecision(15)
525 <<
"NDTLocalization Debug Log: velodyne msg. "
526 <<
"[time:" << lidar_frame->measurement_time
527 <<
"][height:" << msg->height() <<
"][width:" << msg->width()
528 <<
"][point_cnt:" << msg->point_size() <<
"]";
532bool NDTLocalization::LoadLidarExtrinsic(
const std::string& file_path,
533 Eigen::Affine3d* lidar_extrinsic) {
534 CHECK_NOTNULL(lidar_extrinsic);
536 YAML::Node config = YAML::LoadFile(file_path);
537 if (config[
"transform"]) {
538 if (config[
"transform"][
"translation"]) {
539 lidar_extrinsic->translation()(0) =
540 config[
"transform"][
"translation"][
"x"].as<double>();
541 lidar_extrinsic->translation()(1) =
542 config[
"transform"][
"translation"][
"y"].as<double>();
543 lidar_extrinsic->translation()(2) =
544 config[
"transform"][
"translation"][
"z"].as<double>();
545 if (config[
"transform"][
"rotation"]) {
546 double qx = config[
"transform"][
"rotation"][
"x"].as<
double>();
547 double qy = config[
"transform"][
"rotation"][
"y"].as<
double>();
548 double qz = config[
"transform"][
"rotation"][
"z"].as<
double>();
549 double qw = config[
"transform"][
"rotation"][
"w"].as<
double>();
550 lidar_extrinsic->linear() =
551 Eigen::Quaterniond(qw, qx, qy, qz).toRotationMatrix();
559bool NDTLocalization::LoadLidarHeight(
const std::string& file_path,
560 LidarHeight* height) {
561 CHECK_NOTNULL(height);
567 YAML::Node config = YAML::LoadFile(file_path);
568 if (config[
"vehicle"]) {
569 if (config[
"vehicle"][
"parameters"]) {
570 height->height = config[
"vehicle"][
"parameters"][
"height"].as<
double>();
572 config[
"vehicle"][
"parameters"][
"height_var"].as<
double>();
579bool NDTLocalization::LoadZoneIdFromFolder(
const std::string& folder_path,
581 std::string map_zone_id_folder;
583 map_zone_id_folder = folder_path +
"/map/000/north";
585 map_zone_id_folder = folder_path +
"/map/000/south";
591 for (
auto itr = folder_list.begin(); itr != folder_list.end(); ++itr) {
592 *zone_id = std::stoi(*itr);
598bool NDTLocalization::FindNearestOdometryStatus(
599 const double odometry_timestamp, drivers::gnss::InsStat* status) {
600 CHECK_NOTNULL(status);
602 std::unique_lock<std::mutex> lock(odometry_status_list_mutex_);
603 auto odometry_status_list = odometry_status_list_;
606 double timestamp_diff_sec = 1e8;
607 auto nearest_itr = odometry_status_list.end();
608 for (
auto itr = odometry_status_list.begin();
609 itr != odometry_status_list.end(); ++itr) {
610 double diff = std::abs(itr->header().timestamp_sec() - odometry_timestamp);
611 if (diff < timestamp_diff_sec) {
612 timestamp_diff_sec = diff;
617 if (nearest_itr == odometry_status_list.end()) {
621 if (timestamp_diff_sec > odometry_status_time_diff_threshold_) {
625 *status = *nearest_itr;
static double NowInSeconds()
gets the current time in second.
void SetVelodyneExtrinsic(const Eigen::Affine3d &extrinsic)
Set the extrinsic calibration.
bool IsInitialized() const
Is the locator initialized.
void SetMapFolderPath(const std::string folder_path)
Set the map folder.
int Update(unsigned int frame_idx, const Eigen::Affine3d &pose, const LidarFrame &lidar_frame)
Update the histogram filter.
void Init(const Eigen::Affine3d &init_location, unsigned int resolution_id, int zone_id)
Initialize the locator.
void SetOnlineCloudResolution(const float &online_resolution)
Set online cloud resolution.
Eigen::Affine3d GetPose() const
Get the current optimal pose result.
void SetLidarHeight(double height)
Set the lidar height.
double GetFitnessScore() const
Get ndt matching score
Eigen::Affine3d UpdateOdometryPose(double timestamp, const Eigen::Affine3d &novatel_pose)
receive an odometry pose and estimate the output pose according to last lidar pose recorded.
void UpdateLidarPose(double timestamp, const Eigen::Affine3d &locator_pose, const Eigen::Affine3d &novatel_pose)
receive a pair of lidar pose and odometry pose which almost have the same timestame
void LidarCallback(const std::shared_ptr< drivers::PointCloud > &lidar_msg)
receive lidar pointcloud message
bool IsServiceStarted()
service start status
void GetLidarLocalization(LocalizationEstimate *lidar_localization) const
get ndt localization result
void Init()
init configuration
void OdometryCallback(const std::shared_ptr< localization::Gps > &odometry_msg)
receive odometry message
void OdometryStatusCallback(const std::shared_ptr< drivers::gnss::InsStat > &status_msg)
receive ins status message
void GetLocalizationStatus(LocalizationStatus *localization_status) const
get localization status
void GetLocalization(LocalizationEstimate *localization) const
output localization result
The gflags used by localization module
EulerAnglesZXY< double > EulerAnglesZXYd
double QuaternionToHeading(const double qw, const double qx, const double qy, const double qz)
bool PathExists(const std::string &path)
Check if the path exists.
bool DirectoryExists(const std::string &directory_path)
Check if the directory specified by directory_path exists and is indeed a directory.
std::vector< std::string > ListSubPaths(const std::string &directory_path, const unsigned char d_type)
List sub-paths.
uint32_t height
Height of point cloud
Contains a number of helper functions related to quaternions.
EIGEN_MAKE_ALIGNED_OPERATOR_NEW double timestamp