【Apollo源码分析】系列的第五部分【localization】

来源:互联网 发布:阿里云 docker服务器 编辑:程序博客网 时间:2024/06/09 00:48

【Apollo源码分析】系列的第五部分【localization】

localization 是车辆 定位模块,是自动驾驶的核心模块之一。 
目前Apollo的定位模块大概有1000行代码。

localization的输入:

  • GPS全球信号
  • IMU惯性测量单元信号

localization的输出:

  • 由localization/proto/localization.proto文件描述的LocalizationEstimate定位信息

目前Apollo 1.0采用的是 RTK-GPS定位法。

RTK-GPS定位法:

  • RTK(Real - time kinematic)载波相位差分技术,是实时处理两个测量站载波相位观测量的差分方法,将基准站采集的载波相位发给用户接收机,进行求差解算坐标。这是一种新的常用的GPS测量方法,以前的静态、快速静态、动态测量都需要事后进行解算才能获得厘米级的精度,而RTK是能够在野外实时得到厘米级定位精度的测量方法,它采用了载波相位动态实时差分方法,是GPS应用的重大里程碑,它的出现为工程放样、地形测图,各种控制测量带来了新曙光,极大地提高了外业作业效率。
  • 高精度的GPS测量必须采用载波相位观测值,RTK定位技术就是基于载波相位观测值的实时动态定位技术,它能够实时地提供测站点在指定坐标系中的三维定位结果,并达到厘米级精度。在RTK作业模式下,基准站通过数据链将其观测值和测站坐标信息一起传送给流动站。流动站不仅通过数据链接收来自基准站的数据,还要采集GPS观测数据,并在系统内组成差分观测值进行实时处理,同时给出厘米级定位结果,历时不足一秒钟。流动站可处于静止状态,也可处于运动状态;可在固定点上先进行初始化后再进入动态作业,也可在动态条件下直接开机,并在动态环境下完成整周模糊度的搜索求解。在整周未知数解固定后,即可进行每个历元的实时处理,只要能保持四颗以上卫星相位观测值的跟踪和必要的几何图形,则流动站可随时给出厘米级定位结果。

GPS-RTK 至少有一个基准站接收机,一个流动站接收机。

基准站的接收机有两个任务:

  • 1)接收卫星信号
  • 2)把自己的观测量通过发送给流动站

流动站有三个任务:

  • 1)接收卫星信号
  • 2)接收基准站所发送的基准站观测量
  • 3)对卫星信号与基准站观测量进行对比处理,从而对自己的观测量进行修正

关于RTK-GPS可以参阅: 
[1]https://en.wikipedia.org/wiki/Real_Time_Kinematic 
[2]https://baike.baidu.com/item/RTK 
[3]https://www.zhihu.com/question/24471733/answer/28676022 
[4]http://www.sbsm.gov.cn/article/zszygx/chzs/chkp/ddcl/200912/20091200059052.shtml


localization/main.cc

照例先看看 localization文件夹下面最重要的文件main.cc 写了什么:

  1. #include "modules/common/apollo_app.h"
  2. #include "modules/localization/localization.h"
  3. APOLLO_MAIN(::apollo::localization::Localization)

结合common/apollo_app.h文件,上述代码一看便知是用于注册localization模块,并运行本模块。而本文件没有重写ApolloApp类就不能实例化,那么只能是localization.h文件重写了ApolloApp类。

localization/localization.h

  1. class Localization : public apollo::common::ApolloApp
  2. 果然,Localization类继承了ApolloApp类,并重写了父类的纯虚函数接口。

重写的接口有:

  1. std::string Name() const override;
  2. 模块名称
  1. apollo::common::Status Init() override;
  2. 初始化步骤
  1. apollo::common::Status Start() override;
  2. 模块执行内容
  1. void Stop() override;
  2. 模块清理内容

以上全都是public访问权限。

本模块独有的 private函数有:

  1. void RegisterLocalizationMethods();
  2. 工厂函数。注册RTK定位实例和CAMERA定位实例。

再看看localization.cc.cc文件源码:

  1. std::string Localization::Name() const {
  2. return FLAGS_localization_module_name;//模块名称
  3. }
  4. void Localization::RegisterLocalizationMethods() {
  5. localization_factory_.Register(//工厂函数。注册RTK定位方法的实例。
  6. LocalizationConfig::RTK,
  7. []() -> LocalizationBase* { return new RTKLocalization(); });
  8. localization_factory_.Register(
  9. LocalizationConfig::CAMERA,//工厂函数。注册CAMERA定位方法实例。
  10. []() -> LocalizationBase* { return new CameraLocalization(); });
  11. }
  12. Status Localization::Init() { //初始化操作,包括获取配置文件信息+注册定位方法实例
  13. RegisterLocalizationMethods();
  14. if (!apollo::common::util::GetProtoFromFile(FLAGS_localization_config_file,
  15. &config_)) {
  16. AERROR << "failed to load localization config file "
  17. << FLAGS_localization_config_file;
  18. return Status(ErrorCode::LOCALIZATION_ERROR,
  19. "failed to load localization config file: " +
  20. FLAGS_localization_config_file);
  21. }
  22. return Status::OK();
  23. }
  24. Status Localization::Start() { //开始本模块的工作内容
  25. localization_ =
  26. localization_factory_.CreateObject(config_.localization_type());//注册定位方法工厂信息
  27. if (!localization_) {
  28. return Status(ErrorCode::LOCALIZATION_ERROR,
  29. "localization is not initialized with config : " +
  30. config_.DebugString());
  31. }
  32. localization_->Start(); //开始定位
  33. return Status::OK();
  34. }
  35. void Localization::Stop() {}

localization/localization.h先分析到这里。先分析其他然后再回过头来总结整理。

localization/common/localization_gflags.h

.h文件和.cc文件声明和定义了一系列localization模块会用到的string/int32/bool/double变量。概览如下:

localization_gflags.h:

  1. #include "gflags/gflags.h"
  2. DECLARE_string(localization_module_name); //声明变量
  3. DECLARE_double(localization_publish_freq);//声明变量
  4. DECLARE_string(localization_config_file); //声明变量
  5. 以下略...

localization_gflags.cc:

  1. DEFINE_string(localization_module_name, "localization",
  2. "localization module name");
  3. //定义变量
  4. DEFINE_double(localization_publish_freq, 100,
  5. "localization publishing frequency.");
  6. //定义变量
  7. DEFINE_string(rtk_adapter_config_file,
  8. "modules/localization/conf/rtk_adapter.conf",
  9. "rtk adapter configuration");
  10. //定义变量
  11. 以下略...

整个模块需要用到的预定义变量还是很多的。上面只是概览。

下面分析具体的定位算法,包括RTK-GPS定位算法,基于图像的定位算法,IMU惯性测量单元定位算法。

首先分析定位算法基类class LocalizationBase 。

modules/localization/localization_base.h

LocalizationBase是定位算法的基类。其他的任何定位算法都继承自这个类。 
包括 RTKLocalization和CameraLocalization都继承自这个类。

该类提供定位算法的公有抽象接口。 
2个public成员函数,纯虚函数,子类必须重写这2个接口才能实例化。

  1. class LocalizationBase {
  2. public:
  3. virtual ~LocalizationBase() = default;//虚基类应该有虚析构函数
  4. /**
  5. * @brief module start function
  6. * @return start status
  7. 本模块执行任务,返回状态码
  8. */
  9. virtual apollo::common::Status Start() = 0;
  10. /**
  11. * @brief module stop function
  12. * @return stop status
  13. 清理工作
  14. */
  15. virtual apollo::common::Status Stop() = 0;
  16. };

基类比较简单。下面分析具体的定位算法。

camera.proto文件定义了camera相机image。

  1. message Camera {
  2. optional apollo.common.Header header = 1;
  3. // The image from camera
  4. optional bytes image = 2;
  5. // The image width
  6. optional uint32 width = 3;
  7. // The image height
  8. optional uint32 height = 4;
  9. }

camera_parameter.proto定义了相机内参和外参

  1. //内参。
  2. message CameraIntrinsicParameter {
  3. // focus x pixels
  4. optional double fx = 1; // pixels
  5. // focus y pixels
  6. optional double fy = 2; // pixels
  7. // center x pixels
  8. optional double cx = 3; // pixels
  9. // center y pixels
  10. optional double cy = 4; // pixels
  11. }
  12. //外参
  13. message CameraExtrinsicParameter {
  14. // camera rotation parameters [-pi - pi]
  15. optional double roll = 1;
  16. optional double pitch = 2;
  17. optional double yaw = 3;
  18. // coordinates in the vehicle coordinates [meters]
  19. optional double tx = 4; // meters
  20. optional double ty = 5; // meters
  21. optional double tz = 6; // meters
  22. }
  23. message CameraParameter {
  24. optional CameraIntrinsicParameter intrisic_parameter = 1;
  25. optional CameraExtrinsicParameter extrisic_parameter = 2;
  26. }

localization/camera/camera_localization.h

  1. class RTKLocalization : public LocalizationBase

CameraLocalization类继承自LocalizationBase,重写了Start()和Stop() 2个函数。 
该类主要是利用摄像机image进行定位算法。 
补充】基于相机的定位算法主要是基于视觉传感器摄像机(简称VO)和惯性测量单元的 visual inertial odometry(简称VIO)由于现在Apollo也没有具体的算法,暂时不表。更多相关知识可以搜索vslam。


  1. CameraLocalization】类 继承自【LocalizationBase】,重写了Start()和Stop() 2个函数。
  2. 该类主要是利用摄像机进行定位算法。但是目前Apollo1.0版本还没有用具体的相机定位算法。可能后续会补充。
  3. 目前只是添加了这个功能原型,搭建了消息收发的接口。但是目前没有实用。
  4. */
  5. class CameraLocalization : public LocalizationBase {
  6. public:
  7. CameraLocalization();
  8. virtual ~CameraLocalization() = default;
  9. /**
  10. * @brief module start function
  11. * @return start status
  12. 初始化配置,并执行本类的定位算法
  13. */
  14. apollo::common::Status Start() override;
  15. /**
  16. * @brief module stop function
  17. * @return stop status
  18. 清理工作
  19. */
  20. apollo::common::Status Stop() override;
  21. private:
  22. void OnTimer(const ros::TimerEvent &event);//数据到达,进行一次定位算法
  23. bool PublishLocalization();//发布gps和camera二者的综合定位结果
  24. void RunWatchDog();//检查时间合理性,是否延迟过大。
  25. bool PrepareLocalizationMsg(LocalizationEstimate *localization);
  26. //该函数尚未实现。应该是利用camera产生定位结果
  27. //根据gps_msg和camera_msg,得到综合的定位结果
  28. bool CreateLocalizationMsg(const ::apollo::localization::Gps &gps_msg,
  29. const ::apollo::localization::Camera &camera_msg,
  30. LocalizationEstimate *localization);
  31. private:
  32. ros::Timer timer_; //ros系统的时间
  33. //监视本模块工作状态,以便于发送给ros topic。
  34. apollo::common::monitor::Monitor monitor_;
  35. CameraParameter camera_parameter_; //相机参数,包括内参和外参。啥是内参,啥是外参?google一下 ^_^。
  36. double last_received_timestamp_sec_ = 0.0;//最近接收data的时间
  37. double last_reported_timestamp_sec_ = 0.0;//最近发布定位的时间
  38. bool use_imu_ = false; //融合imu
  39. bool service_started_ = false; //开始工作
  40. };

localization/rtk/rtk_localization.h

Real_Time_Kinematic,即差分GPS技术:将一台GPS接收机安置在基准站上进行观测。根据基准站已知精密坐标,计算出基准站到卫星的距离改正数,并由基准站实时将这一数据发送出去。用户接收机在进行GPS观测的同时,也接收到基准站发出的改正数,并对其定位结果进行改正,从而提高定位精度。

目前Apollo的1.0主要依靠RTK-GPS定位算法。其他算法目前尚为实现。 
关于RTK-GPS可以参阅https://en.wikipedia.org/wiki/Real_Time_Kinematic

优点:精度能达到厘米级。 
缺点:要安装基站,成本高,在没有基站的户外就不适用了。

RTKLocalization类继承自LocalizationBase,重写了Start()和Stop() 2个函数。

  1. class RTKLocalization : public LocalizationBase {
  2. public:
  3. RTKLocalization();
  4. virtual ~RTKLocalization() = default;
  5. /**
  6. * @brief module start function
  7. * @return start status
  8. */
  9. apollo::common::Status Start() override;
  10. /**
  11. * @brief module stop function
  12. * @return stop status
  13. */
  14. apollo::common::Status Stop() override;
  15. private:
  16. void OnTimer(const ros::TimerEvent &event);//数据到达,进行一次定位算法
  17. void PublishLocalization();//发布 定位结果
  18. void RunWatchDog();//检查时间合理性,是否延迟过大。
  19. void PrepareLocalizationMsg(LocalizationEstimate *localization);
  20. ///计算gps的定位结果
  21. void ComposeLocalizationMsg(const ::apollo::localization::Gps &gps,
  22. const ::apollo::localization::Imu &imu,
  23. LocalizationEstimate *localization);
  24. ///将gps和imu做数据融合。
  25. ///根据时间戳找到大于该时间戳的第一个imu数据
  26. bool FindMatchingIMU(const double gps_timestamp_sec, Imu *imu_msg);
  27. ///对imu数据进行插值计算,得到给定timestamp_sec的值
  28. void InterpolateIMU(const Imu &imu1, const Imu &imu2,
  29. const double timestamp_sec, Imu *msgbuf);
  30. template <class T>
  31. T InterpolateXYZ(const T &p1, const T &p2, const double &frac1);
  32. private:
  33. ros::Timer timer_;//ros系统的时间
  34. //监视本模块工作状态,以便于发送给ros topic。
  35. apollo::common::monitor::Monitor monitor_;
  36. const std::vector<double> map_offset_;//在地图上的偏移量
  37. double last_received_timestamp_sec_ = 0.0;//最近接收data的时间
  38. double last_reported_timestamp_sec_ = 0.0;//最近发布定位的时间
  39. bool service_started_ = false; //开始工作

rtk_localization_test.cc

理解RTK-GPS的定位算法最好还是通过test测量代码理解。并且还有定位实例:

  1. TEST_F(RTKLocalizationTest, InterpolateIMU) {
  2. // timestamp inbetween + time_diff is big enough(>0.001), interpolate
  3. {
  4. //加载imu1测量结果
  5. apollo::localization::Imu imu1;
  6. load_data("modules/localization/testdata/1_imu_1.pb.txt", &imu1);
  7. //加载imu2测量结果
  8. apollo::localization::Imu imu2;
  9. load_data("modules/localization/testdata/1_imu_2.pb.txt", &imu2);
  10. //期望结果
  11. apollo::localization::Imu expected_result;
  12. load_data("modules/localization/testdata/1_imu_result.pb.txt",
  13. &expected_result);
  14. //2和exp完全一样。
  15. apollo::localization::Imu imu;
  16. double timestamp = 1173545122.69;
  17. ///imu返回的是timestamp处的插值结果。
  18. rtk_localizatoin_->InterpolateIMU(imu1, imu2, timestamp, &imu);
  19. ///插值结果就是2
  20. EXPECT_EQ(expected_result.DebugString(), imu.DebugString());
  21. }
  22. // timestamp inbetween + time_diff is too small(<0.001), no interpolate
  23. {
  24. ///1和exp在时间上不一致,其余一致。
  25. apollo::localization::Imu imu1;
  26. load_data("modules/localization/testdata/2_imu_1.pb.txt", &imu1);
  27. ///imu2与imu1时间戳很小,则直接使用imu1的数据。
  28. apollo::localization::Imu imu2;
  29. load_data("modules/localization/testdata/2_imu_2.pb.txt", &imu2);
  30. apollo::localization::Imu expected_result;
  31. load_data("modules/localization/testdata/2_imu_result.pb.txt",
  32. &expected_result);
  33. apollo::localization::Imu imu;
  34. double timestamp = 1173545122.2001;
  35. //返回在timestamp处的插值结果。
  36. rtk_localizatoin_->InterpolateIMU(imu1, imu2, timestamp, &imu);
  37. ///不插值,直接使用imu1的数据
  38. EXPECT_EQ(expected_result.DebugString(), imu.DebugString());
  39. }
  40. // timestamp < imu1.timestamp
  41. {
  42. apollo::localization::Imu imu1;
  43. load_data("modules/localization/testdata/1_imu_1.pb.txt", &imu1);
  44. apollo::localization::Imu imu2;
  45. load_data("modules/localization/testdata/1_imu_2.pb.txt", &imu2);
  46. apollo::localization::Imu expected_result;
  47. load_data("modules/localization/testdata/1_imu_1.pb.txt", &expected_result);
  48. apollo::localization::Imu imu;
  49. double timestamp = 1173545122;
  50. //timestamp小于imu1的时间戳,则直接使用imu1的数据,不插值
  51. rtk_localizatoin_->InterpolateIMU(imu1, imu2, timestamp, &imu);
  52. EXPECT_EQ(expected_result.DebugString(), imu.DebugString());
  53. }
  54. // timestamp > imu2.timestamp
  55. {
  56. apollo::localization::Imu imu1;
  57. load_data("modules/localization/testdata/1_imu_1.pb.txt", &imu1);
  58. apollo::localization::Imu imu2;
  59. load_data("modules/localization/testdata/1_imu_2.pb.txt", &imu2);
  60. apollo::localization::Imu expected_result;
  61. load_data("modules/localization/testdata/1_imu_1.pb.txt", &expected_result);
  62. apollo::localization::Imu imu;
  63. double timestamp = 1173545122.70;
  64. //timestamp大于imu2的时间戳,则直接使用imu1的数据。
  65. rtk_localizatoin_->InterpolateIMU(imu1, imu2, timestamp, &imu);
  66. EXPECT_EQ(expected_result.DebugString(), imu.DebugString());
  67. }
  68. }
  1. TEST_F(RTKLocalizationTest, ComposeLocalizationMsg) {
  2. // FLAGS_enable_map_reference_unify: false
  3. {
  4. FLAGS_enable_map_reference_unify = false;
  5. apollo::localization::Gps gps;
  6. load_data("modules/localization/testdata/3_gps_1.pb.txt", &gps);
  7. apollo::localization::Imu imu;
  8. load_data("modules/localization/testdata/3_imu_1.pb.txt", &imu);
  9. apollo::localization::LocalizationEstimate expected_result;
  10. load_data("modules/localization/testdata/3_localization_result_1.pb.txt",
  11. &expected_result);
  12. apollo::localization::LocalizationEstimate localization;
  13. //使用GPS和imu对定位结果进行数据融合。
  14. rtk_localizatoin_->ComposeLocalizationMsg(gps, imu, &localization);
  15. EXPECT_EQ(1, localization.header().sequence_num());
  16. EXPECT_STREQ("localization", localization.header().module_name().c_str());
  17. EXPECT_STREQ(expected_result.pose().DebugString().c_str(),
  18. localization.pose().DebugString().c_str());
  19. }

总结

localization模块主要定义了使用RTK-GPS和camera相结合的定位方法,并且融合了imu传感器。但是基于图像的定位方法尚未发布。目前Apollo主要采用RKT-GPS并辅助imu用于车辆定位。总的来说,目前还有很大的改进空间,但是Apollo的定位框架目前已搭建完毕,能run起来,精度+普适廉价性+可靠性+多传感器融合算法应该是未来改进的地方。每一个方向都值得深究。


本文首发于微信公众号slamcode。

 注释版源码:源码



阅读全文
0 0
原创粉丝点击