百度开源自动驾驶apollo源码分析

Posted MBWorld

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了百度开源自动驾驶apollo源码分析相关的知识,希望对你有一定的参考价值。

我们先来看一下源码的结构


                 一级目录结构

docker文件夹放的是和docker容器有关的文件

docs文件夹放的是说明文档

modules放的是apollo中各个模块的源代码,也是之后我们将要详细说明的部分

scripts放的是一些百度写的部署和运行apollo的脚本

third_party存放了第三方的依赖库

tools文件夹和其他文件都是和apollo构建有关的配置文件


进入modules文件夹



                 modules目录结构


可以看到每个模块都分得很清楚,而且模块的功能通过文件夹的名称也能很直观的看懂。


Apollo 整体上由13个模块构成,分别是

 
   
   
 
  1. canbus -汽车CAN总线控制模块

  2. common - 公有的源码模块

  3. control -控制模块

  4. decision -决策模块

  5. dreamview -可视化模块

  6. drivers  -驱动模块

  7. hmi     -人机交互模块

  8. localization -定位模块

  9. monitor     -监控模块

  10. perception  -感知模块

  11. planning    -运动规划模块

  12. prediction  -预测模块

  13. tools    -通用监控与可视化工具

Apollo采用bazel 作为代码编译构建工具。 
每个源码文件夹下有一个BUILD文件,作用是按照bazel的格式来编译代码。 
关于如何使用bazel编译c++代码,可以查看以下网址: 
【1】https://docs.bazel.build/versions/master/tutorial/cpp.html 
【2】https://docs.bazel.build/versions/master/tutorial/cpp-use-cases.html


ok。现在开始分析源码。系列文章的第一篇,首先分析modules/common目录下面的源码。

modules/common/macro.h


宏定义 DISALLOW_COPY_AND_ASSIGN:

 
   
   
 
  1. #define DISALLOW_COPY_AND_ASSIGN(classname) \

  2. private:                                   \

  3.  classname(const classname &);             \

  4.  classname &operator=(const classname &);

用于在C++中禁止class的拷贝构造函数和赋值构造函数,良好的c++代码应该主动管理这2个操作符。
在caffe和cartographer或者其他的著名库中均有类似的操作。

宏定义 DISALLOW_IMPLICIT_CONSTRUCTORS:

 
   
   
 
  1. #define DISALLOW_IMPLICIT_CONSTRUCTORS(classname) \

  2. private:                                         \

  3.  classname();                                    \

  4.  DISALLOW_COPY_AND_ASSIGN(classname);

禁止class的无参构造函数。

宏定义 DECLARE_SINGLETON:

 
   
   
 
  1. #define DECLARE_SINGLETON(classname)        \

  2. public:                                    \

  3.  static classname *instance() {            \

  4.    static classname instance;              \

  5.    return &instance;                       \

  6.  }                                         \

  7.  DISALLOW_IMPLICIT_CONSTRUCTORS(classname) \

单例类定义,instance() 返回指向同一个class对象的指针。禁止拷贝/赋值运算符。

modules/common/log.h

apollo内部使用谷歌的glog作为日志库。 
有5个日志级别,分别是DEBUG,INFO,WARNING,ERROR,FATAL。

 
   
   
 
  1. #include "glog/logging.h"

  2. #define ADEBUG VLOG(4) << "[DEBUG] "

  3. #define AINFO VLOG(3) << "[INFO] "

  4. #define AWARN LOG(WARNING)

  5. #define AERROR LOG(ERROR)

  6. #define AFATAL LOG(FATAL)

modules/common/time/time.h

apollo内部使用c++ 11的 chrono库作为时间管理工具。默认精度是纳秒(1e-9). 
std::chrono::duration 表示时间间隔大小。 
std::chrono::time_point 表示时间中的一个点。

定义2个别名:

  • Duration,1纳秒,1e-9s。

  • Timestamp,以纳秒ns为单位的时间点。

全局函数:

  • int64_t AsInt64(const Duration &duration)

 
   
   
 
  1. 将纳秒ns转换为以PrecisionDuration为精度单位计的int64整数。

  • int64_t AsInt64(const Timestamp &timestamp) :

 
   
   
 
  1. Timestamp(时间点)转换为64位整数表示。

  • double ToSecond(const Duration &duration) :

 
   
   
 
  1. 将纳秒ns转换为秒s

  • inline double ToSecond(const Timestamp &timestamp) :

 
   
   
 
  1. 将以纳秒表示的时间点ns转换为秒s

  • Timestamp FromInt64(int64_t timestamp_value) :

 
   
   
 
  1. 将以PrecisionDuration为精度计的int64整数转换为Timestamp(时间点)。

  • Timestamp From(double timestamp_value):

 
   
   
 
  1. 将秒s转换为时间点Timestamp。即FromInt64的特化版本:先将时间转换为ns计的64位整数nanos_value,再转换为Timestamp


Clock 类:

Clock是封装c++ 11 chrono后抽象的时钟计时类class。

是线程安全的单例模式(c++ 11的语法可确保)。

数据成员:

 
   
   
 
  1. bool is_system_clock_; true表示系统时间,false表示模拟时间

  2. Timestamp mock_now_; 模拟时间的当前值。

为啥要标记模拟时间?:为了仿真训练。多次仿真,反复训练。 
Clock类没有公有的构造函数。私有构造函数初始化为使用cpu的系统时间。

Clock提供4个static 公有函数:

 
   
   
 
  1. static Timestamp Now()  

  2. 返回当前时间的最新值。

  3. static void UseSystemClock(bool is_system_clock)    

  4. 设置是否使用模拟时间。

  5. static bool IsSystemClock()

  6. 返回是否使用系统时间。

  7. static void SetNow(const Duration &duration)

  8. Clock使用mock时间时,将系统时间设定为给定值。

  9. 否则抛出运行时错误。(只有模拟时间才可调整值,系统时间不可调整)

modules/common/time/time_test.cc

 
   
   
 
  1.  Duration duration = std::chrono::milliseconds(12); //12ms

  2.  EXPECT_EQ(12000, AsInt64<micros>(duration));      //==121000us

 
   
   
 
  1.  Duration duration = std::chrono::microseconds(1234567);//1234567us

  2.  EXPECT_EQ(1234, AsInt64<millis>(duration));            //==1234ms

 
   
   
 
  1. Duration duration = std::chrono::microseconds(123 456 789 012);//123...us

  2.  EXPECT_FLOAT_EQ(123456.789012, ToSecond(duration));         //123456(s)

  3. ...略.

 
   
   
 
  1. EXPECT_TRUE(Clock::IsSystemClock()); //默认是cpu系统时间

  2. Clock::UseSystemClock(false);        //修改。

  3. EXPECT_FALSE(Clock::IsSystemClock());

  4. EXPECT_EQ(0, AsInt64<micros>(Clock::Now())); //模拟时间的初值是0.

  5. Clock::SetNow(micros(123));                  //修改为123 (us)

  6. EXPECT_EQ(123, AsInt64<micros>(Clock::Now()));


modules/common/util/util.h

函数原型:

 
   
   
 
  1. bool EndWith(const std::string &original, const std::string &pattern);

  • 当original字符串以 pattern 结尾时,返回true

  •  测试代码,util_test.cc:

 
   
   
 
  1. EXPECT_TRUE(EndWith("abc.def", "def"));

  2. EXPECT_TRUE(EndWith("abc.def", ".def"));

  3. EXPECT_FALSE(EndWith("abc.def", "abc"));

  4. EXPECT_FALSE(EndWith("abc.def", "de"));

modules/common/util/file.h

file.h提供了多个关于操作文件(关于protobuf文件的读,写,删)的函数。 
都是模板类函数:

 
   
   
 
  1. template <typename MessageType>

  •  (1). 将message存储到filename中,以ascii格式存储.

 
   
   
 
  1. bool SetProtoToASCIIFile(const MessageType &message,

  2.                         const std::string &file_name)

 
   
   
 
  1. bool GetProtoFromASCIIFile(const std::string &file_name, MessageType *message)

  •  (3) . 解析二进制filename文件的内容,并存储到message中。

 
   
   
 
  1. bool GetProtoFromBinaryFile(const std::string &file_name,

  2.                            MessageType *message)

  •  (4) 从filename中解析文件内容。filename可以是二进制或者ascii格式。

 
   
   
 
  1. bool GetProtoFromFile(const std::string &file_name, MessageType *message)

  •  (5) . 给定的目录路径是否存在。

 
   
   
 
  1. bool DirectoryExists(const std::string &directory_path);

  •  (6) . 按照directory_path创建文件夹。类似于mkdir -p选项。

 
   
   
 
  1. bool EnsureDirectory(const std::string &directory_path);

  •  (7) . 删除给定目录下所有的文件。(文件夹不删除)

 
   
   
 
  1. bool RemoveAllFiles(const std::string &directory_path);

modules/common/util/string_tokenizer.h

StringTokenizer类主要目的是使用特定的分割符将字符串分割成多个部分。 
作用与python的字符串分割函数split()类似。默认分割符是" " 
1个成员函数:

 
   
   
 
  1. std::string Next();

  2. 分割得到的下一个子串。

1个静态成员函数,可以指定切分标记delims:

 
   
   
 
  1. static std::vector<std::string> Split(const std::string &str,                                       const std::string &delims);

  2. 使用分隔符delims对给定的字符串str进行分割。分割结果可以用 Next()成员函数访问;

测试代码:

 
   
   
 
  1.  std::string str("aa,bbb,c");

  2.  std::string delim(",");

  3.  EXPECT_THAT(StringTokenizer::Split(str, delim),

  4.              ElementsAre("aa", "bbb", "c"));

 
   
   
 
  1. std::string str("     aa,  bbb , c   ");

  2.  std::string delim(", ");

  3.  StringTokenizer stn(str, delim);

  4.  auto t0 = stn.Next();

  5.  auto t1 = stn.Next();

  6.  auto t2 = stn.Next();

  7.  auto t3 = stn.Next();

  8.  auto t4 = stn.Next();

  9.  EXPECT_EQ(t0, "aa");

  10.  EXPECT_EQ(t1, "bbb");

  11.  EXPECT_EQ(t2, "c");

  12.  EXPECT_EQ(t3, "");

  13.  EXPECT_EQ(t4, "");

modules/common/util/factory.h

创建对象的工厂模式。所有需要创建的对象使用ProductCreator函数指针创建。 
默认使用unordered_map管理元素。

 
   
   
 
  1. IdentifierType:唯一标识符,如string字符串。

  2. AbstractProduct 如具有相同接口的纯虚基类class

  3. ProductCreator= AbstractProduct *(*)():函数指针。形参是空。返回值是指针,指向抽象class

  4. MapContainer = std::map<IdentifierType, ProductCreator>:按照IdentifierType存储需要创建的对象的方法。创建方法是ProductCreator

3个公有的函数:

 
   
   
 
  1. Register():注册某class的信息到unordered_map中,但是没有创建该class的实例对象。

  2. Unregister():删除已经注册的信息。

  3. CreateObject(): 创建某class的实例对象。

测试代码:

 
   
   
 
  1. Factory<std::string, Base> factory;

  2.  //注册信息。

  3.  EXPECT_TRUE(factory.Register("derived_class",

  4.                               []() -> Base* { return new Derived(); }));

  5.  //创建实例对象

  6.  auto derived_ptr = factory.CreateObject("derived_class");

  7.  EXPECT_TRUE(derived_ptr != nullptr);

  8.  EXPECT_EQ("derived", derived_ptr->Name());

  9.  auto non_exist_ptr = factory.CreateObject("non_exist_class");

  10.  EXPECT_TRUE(non_exist_ptr == nullptr);

 
   
   
 
  1.  Factory<std::string, Base> factory;

  2.  //注册信息

  3.  EXPECT_TRUE(factory.Register("derived_class",

  4.                               []() -> Base* { return new Derived(); }));

  5.  //没有fake_class,则提示删除失败。

  6.  EXPECT_FALSE(factory.Unregister("fake_class"));

  7.  auto derived_ptr = factory.CreateObject("derived_class");

  8.  EXPECT_TRUE(derived_ptr != nullptr);

  9.  EXPECT_TRUE(factory.Unregister("derived_class"));

  10.  auto non_exist_ptr = factory.CreateObject("derived_class");

  11.  EXPECT_TRUE(non_exist_ptr == nullptr);

modules/common/status/status.h

apollo内部定义了一系列的状态码用于标识各个模块的工作状态。 
状态码的含义可以在modules/common/proto/error_code.proto文件查看,以下是该文件内容:

 
   
   
 
  1. // Error codes enum for API's categorized by modules.

  2. enum ErrorCode {

  3.  // No error, reutrns on success.

  4.  OK = 0;

  5.  // Control module error codes start from here.

  6.  CONTROL_ERROR = 1000;

  7.  CONTROL_INIT_ERROR = 1001;

  8.  CONTROL_COMPUTE_ERROR = 1002;

  9.  // Canbus module error codes start from here.

  10.  CANBUS_ERROR = 2000;

  11.  CAN_CLIENT_ERROR_BASE = 2100;

  12.  CAN_CLIENT_ERROR_OPEN_DEVICE_FAILED = 2101;

  13.  CAN_CLIENT_ERROR_FRAME_NUM = 2102;

  14.  CAN_CLIENT_ERROR_SEND_FAILED = 2103;

  15.  CAN_CLIENT_ERROR_RECV_FAILED = 2104;

  16.  // Localization module error codes start from here.

  17.  LOCALIZATION_ERROR = 3000;

  18.  // Perception module error codes start from here.

  19.  PERCEPTION_ERROR = 4000;

  20.  PERCEPTION_ERROR_TF = 4001;

  21.  PERCEPTION_ERROR_PROCESS = 4002;

  22.  // Prediction module error codes start from here.

  23.  PREDICTION_ERROR = 5000;

  24. }

  25. message StatusPb {

  26.    optional ErrorCode error_code = 1 [default = OK];

  27.    optional string msg = 2;

  28. }

Status类用于标记各个API模块的返回值。

  • 构造函数:

 
   
   
 
  1. Status() : code_(ErrorCode::OK), msg_() {}

  2. 默认构造函数为ErrorCode::OK(调用成功)。

 
   
   
 
  1. Status(ErrorCode code, const std::string &msg) : code_(code), msg_(msg) {}

  2. 指定错误码和错误message

  • 成员函数:

 
   
   
 
  1. 1)  ok() 是否调用成功。

  2. 2)  code() 错误码

  3. 3 string &error_message() 错误信息。

  4. 4 Save(StatusPb *status_pb) 序列化到文件。

  5. 5)  string ToString() 状态信息转换为字符串格式。

测试代码:

 
   
   
 
  1. TEST(Status, OK) {

  2.  EXPECT_EQ(Status::OK().code(), ErrorCode::OK);

  3.  EXPECT_EQ(Status::OK().error_message(), "");

  4.  EXPECT_EQ(Status::OK(), Status::OK());

  5.  EXPECT_EQ(Status::OK(), Status());

  6.  Status s;

  7.  EXPECT_TRUE(s.ok());

  8. }

 
   
   
 
  1. TEST(Status, Set) {

  2.  Status status;

  3.  status = Status(ErrorCode::CONTROL_ERROR, "Error message");

  4.  EXPECT_EQ(status.code(), ErrorCode::CONTROL_ERROR);

  5.  EXPECT_EQ(status.error_message(), "Error message");

  6. }

 
   
   
 
  1. TEST(Status, ToString) {

  2.  EXPECT_EQ("OK", Status().ToString());

  3.  const Status a(ErrorCode::CONTROL_ERROR, "Error message");

  4.  EXPECT_EQ("CONTROL_ERROR: Error message", a.ToString());

  5. }


common/adapters/adapter_gflags.h

用gflags的宏解析命令行参数。 
声明:DECLARE_xxx(变量名) 
定义:DEFINE_xxxxx(变量名,默认值,help-string)。

 
   
   
 
  1. DEFINE_string(adapter_config_path, "", "the file path of adapter config file");

  2. DEFINE_bool(enable_adapter_dump, false,

  3.            "Whether enable dumping the messages to "

  4.            "/tmp/adapters/<topic_name>/<seq_num>.txt for debugging purposes.");

  5. DEFINE_string(gps_topic, "/apollo/sensor/gnss/odometry", "GPS topic name");

  6. DEFINE_string(imu_topic, "/apollo/sensor/gnss/corrected_imu", "IMU topic name");

  7. DEFINE_string(chassis_topic, "/apollo/canbus/chassis", "chassis topic name");

  8. ...

common/adapters/adapter.h

Adapter是来自传感器的底层数据和Apollo各个模块交互的统一接口。(c++ 适配器模式) 使得Apollo各个模块不强依赖于ROS框架:将数据IO抽象。上层是Apollo框架,底层是data IO,二者松耦合。Adapter持有一段时间内的所有历史数据,因此所有的模块可获取这些数据而无需使用ROS通信机制进行数据交换(提高通信效率)。模板参数可以是message消息类型,不一定是传感器数据。

  • 数据成员

 
   
   
 
  1.  /// 需要监听的ROS的话题名称,message的来源topic

  2.  std::string topic_name_;

  3.  /// 数据队列的最大容量

  4.  size_t message_num_ = 0;

  5.  //  接收的数据队列,原始数据。该部分数据尚未进行观测

  6.  std::list<std::shared_ptr<D>> data_queue_;

  7.  /// Observe() is called.

  8.  /// 备份已经观测过的数据。

  9.  std::list<std::shared_ptr<D>> observed_queue_;

  10.  /// 消息到达的回调函数(用于处理到达的消息)

  11.  Callback receive_callback_ = nullptr;

  12.  ///互斥锁保证线程安全

  13.  mutable std::mutex mutex_;

  14.  /// 是否输出到dump路径

  15.  bool enable_dump_ = false;

  16.  /// dump输出路径

  17.  std::string dump_path_;

  18.  /// 消息的序列号,单调递增。此值随着消息的增加而单调增加。

  19.  uint32_t seq_num_ = 0;

 
   
   
 
  1. 构造函数初始化成员变量:

  2. Adapter(const std::string &adapter_name, const std::string &topic_name,size_t message_num, const std::string &dump_dir = "/tmp")

  3. 构造函数参数:

  4. 1Adapter标识自己的名称,

  5. 2,监听的ros_topic

  6. 3,保存历史消息的最大数量,

  7. 4dump路径

 
   
   
 
  1. void FeedProtoFile(const std::string &message_file)

  2. proto文件读取data

 
   
   
 
  1. OnReceive(const D &message)

  2. 接收原始数据data。并调用相关回调函数。

 
   
   
 
  1. void Observe()

  2. 进行一次观察/测量。只有调用了Observe(),接收的数据才能被有效处理。

 
   
   
 
  1. const D &GetLatestObserved()

  2. 获取观测集中的最新值

 
   
   
 
  1. begin(),end(),迭代器模式指向观测数据集。支持for循环。

 
   
   
 
  1. void SetCallback(Callback callback) 设置消息到达时的回调函数。

测试代码:

 
   
   
 
  1. TEST(AdapterTest, Empty) {

  2.  //创建一个IntegerAdapter对象,监听integer_topic话题,保存历史消息数量是10

  3.  IntegerAdapter对象, adapter("Integer", "integer_topic", 10);

  4.  EXPECT_TRUE(adapter.Empty());

  5. }

  6. TEST(AdapterTest, Observe) {

  7.  IntegerAdapter adapter("Integer", "integer_topic", 10);

  8.  adapter.OnReceive(173);//接收到了一个数据,但是尚未处理数据。

  9.  // Before calling Observe.

  10.  EXPECT_TRUE(adapter.Empty());

  11.  // After calling Observe.

  12.  //进行一次观测,即接收待处理数据。

  13.  adapter.Observe();

  14.  EXPECT_FALSE(adapter.Empty());

  15. }

 
   
   
 
  1. TEST(AdapterTest, Callback) {

  2.  IntegerAdapter adapter("Integer", "integer_topic", 3);

  3.  // Set the callback to act as a counter of messages.

  4.  int count = 0;

  5.  adapter.SetCallback([&count](int x) { count += x; });

  6.  adapter.OnReceive(11);

  7.  adapter.OnReceive(41);

  8.  adapter.OnReceive(31);

  9.  EXPECT_EQ(11 + 41 + 31, count);//接收一次消息就回调一次函数。

  10. }

 
   
   
 
  1. TEST(AdapterTest, Dump) {

  2.  FLAGS_enable_adapter_dump = true;

  3.  std::string temp_dir = std::getenv("TEST_TMPDIR");//dump输出路径

  4.  MyLocalizationAdapter adapter("local", "local_topic", 3, temp_dir);

  5.  localization::LocalizationEstimate loaded;

  6.  localization::LocalizationEstimate msg;

  7.  msg.mutable_header()->set_sequence_num(17);

  8.  adapter.OnReceive(msg);//接收时,即已经写入dump文件。

  9.  //从dump文件读取信息到loaded中。

  10.  apollo::common::util::GetProtoFromASCIIFile(temp_dir + "/local/17.pb.txt",

  11.                                              &loaded);

  12.  EXPECT_EQ(17, loaded.header().sequence_num());

  13.  msg.mutable_header()->set_sequence_num(23);

  14.  adapter.OnReceive(msg);

  15.  //同上。

  16.  apollo::common::util::GetProtoFromASCIIFile(temp_dir + "/local/23.pb.txt",

  17.                                              &loaded);

  18.  EXPECT_EQ(23, loaded.header().sequence_num());

  19. }

common/adapters/adapter_manager.h

AdapterManager 类用于管理多个适配器,单例模式。所有的message/IO适配器均需要通过REGISTER_ADAPTER(name)在这里注册。所有的数据交互也通过AdapterManager来进行统一的管理。

由于是单例模式,AdapterManager只有静态成员函数。

加载默认config:

 
   
   
 
  1. Init()

加载给定filename的config:

 
   
   
 
  1. static void Init(const std::string &adapter_config_filename)

加载proto格式的配置,创建 Adapter对象:

 
   
   
 
  1. static void Init(const AdapterManagerConfig &configs);

例如imu:

 
   
   
 
  1.      case AdapterConfig::IMU:

  2.        EnableImu(FLAGS_imu_topic, config.mode(),

  3.                  config.message_history_limit());

  4.        break;

对所有的adapter对象执行observe()函数,调用每个Adapter自身的函数Observe()。observers_的元素由宏定义产生。:

 
   
   
 
  1. static void Observe();

ROS节点回调函数:

 
   
   
 
  1. static ros::Timer CreateTimer

  2. (ros::Duration period,

  3. void (T::*callback)(const ros::TimerEvent &),

  4. T *obj, bool oneshot = false,

  5. bool autostart = true)

common/vehicle_state/vehicle_state.h

VehicleState类是标识车辆状态信息的class。 
主要包含线速度.角速度.加速度.齿轮状态.车辆坐标x,y,z

  •  构造函数:

 
   
   
 
  1. explicit VehicleState(const localization::LocalizationEstimate &localization);

  2. 只根据定位来初始化车辆信息。

 
   
   
 
  1. VehicleState(const localization::LocalizationEstimate *localization,const canbus::Chassis *chassis);

  2. 根据定位信息和车辆底盘信息初始化。


  • 成员函数: 

    • x坐标 
      double x() const;

    • y坐标 
      double y() const;

    • z坐标。 
      double z() const;

    • pitch角,绕y轴。(车身左右倾斜角度)。 
      double pitch() const;

    • yaw角,绕z轴。表征车头转向角度。0°与x轴重合,90°与y轴重合 
      double heading() const;

    • 线速度 
      double linear_velocity() const;

    • 角速度 
      double angular_velocity() const;

    • y方向线性加速度 
      double linear_acceleration() const;

    • 齿轮状态 
      GearPosition gear() const;

    • 设置上述值的函数set_**()


  •  状态估计

 
   
   
 
  1. 根据当前信息估计t时刻后的车辆位置。注意,Apollo的状态估计比较简单,很多因素都没有考虑。所以1.0版本只能在简单道路上行驶。

  2. Eigen::Vector2d EstimateFuturePosition(const double t) const;

 
   
   
 
  1.  Eigen::Vector2d ComputeCOMPosition(const double rear_to_com_distance) const;

  2. 给定后轮到质心的距离。 估计质心的位置(x,y)  

测试代码:

 
   
   
 
  1. TEST_F(VehicleStateTest, Accessors) {

  2.  VehicleState vehicle_state(&localization_, &chassis_);

  3.  //都是proto文件的内容。

  4.  EXPECT_DOUBLE_EQ(vehicle_state.x(), 357.51331791372041);

  5.  EXPECT_DOUBLE_EQ(vehicle_state.y(), 96.165912376788725);

  6.  EXPECT_DOUBLE_EQ(vehicle_state.heading(), -1.8388082455104939);

  7.  EXPECT_DOUBLE_EQ(vehicle_state.pitch(), -0.010712737572581465);

  8.  EXPECT_DOUBLE_EQ(vehicle_state.linear_velocity(), 3.0);

  9.  EXPECT_DOUBLE_EQ(vehicle_state.angular_velocity(), -0.0079623083093763921);

  10.  EXPECT_DOUBLE_EQ(vehicle_state.linear_acceleration(), -0.079383290718229638);

  11.  EXPECT_DOUBLE_EQ(vehicle_state.gear(), canbus::Chassis::GEAR_DRIVE);

  12. }

 
   
   
 
  1. TEST_F(VehicleStateTest, EstimateFuturePosition) {

  2.  VehicleState vehicle_state(&localization_, &chassis_);

  3.  //估计1s后的车辆位置

  4.  Eigen::Vector2d future_position = vehicle_state.EstimateFuturePosition(1.0);

  5.  EXPECT_NEAR(future_position[0], 356.707, 1e-3);

  6.  EXPECT_NEAR(future_position[1], 93.276, 1e-3);

  7.  //估计2s后的车辆位置

  8.  future_position = vehicle_state.EstimateFuturePosition(2.0);

  9.  EXPECT_NEAR(future_position[0], 355.879, 1e-3);

  10.  EXPECT_NEAR(future_position[1], 90.393, 1e-3);

  11. }

common/monitor/monitor_buffer.h

MonitorBuffer主要用于缓存多条log的消息日志。将多个相同level的log组合到一起。避免冗余信息。 不同level的log,将不会被组合。但是相同level的log日志将被组合到一起。

  • 构造函数 
    explicit MonitorBuffer(Monitor *monitor);

成员函数:

 
   
   
 
  1. void PrintLog();

  2. monitor_msg_items_的尾部消息输出。

 
   
   
 
  1. REG_MSG_TYPE(INFO);

  2. 注册INFO级别的log

  3. REG_MSG_TYPE(WARN);

  4. REG_MSG_TYPE(ERROR);

  5. REG_MSG_TYPE(FATAL);

 
   
   
 
  1. void AddMonitorMsgItem(const MonitorMessageItem::LogLevel log_level,const std::string &msg);

  2. 添加一个msgitemlevel+msg.

 
   
   
 
  1. MonitorBuffer &operator<<(const T &msg)

  2. 重载<<运算符,将msg写入MonitorBufferlevel是当前的level_

 
   
   
 
  1. void Publish();

  2. 调用monitor的成员函数Publish(),发布消息。

测试代码:

 
   
   
 
  1. TEST_F(MonitorBufferTest, PrintLog) {

  2.  FLAGS_logtostderr = true;

  3.  FLAGS_v = 4;

  4.  {

  5.    testing::internal::CaptureStderr();

  6.    buffer_->PrintLog();

  7.    EXPECT_TRUE(testing::internal::GetCapturedStderr().empty());

  8.    //没有log日志,所以为空。

  9.  }

  10.  {

  11.    buffer_->INFO("INFO_msg");//写入"INFO_msg".

  12.    testing::internal::CaptureStderr();

  13.    buffer_->PrintLog();

  14.    EXPECT_NE(std::string::npos,//找到string,不为npos。

  15.              testing::internal::GetCapturedStderr().find("[INFO] INFO_msg"));

  16.  }

  17.  {

  18.    buffer_->ERROR("ERROR_msg");//写入"ERROR_msg"

  19.    testing::internal::CaptureStderr();

  20.    buffer_->PrintLog();

  21.    EXPECT_NE(std::string::npos,

  22.              testing::internal::GetCapturedStderr().find("ERROR_msg"));

  23.  }

  24.  {

  25.    buffer_->WARN("WARN_msg");

  26.    testing::internal::CaptureStderr();

  27.    buffer_->PrintLog();

  28.    EXPECT_NE(std::string::npos,

  29.              testing::internal::GetCapturedStderr().find("WARN_msg"));

  30.  }

  31.  {

  32.    buffer_->FATAL("FATAL_msg");

  33.    EXPECT_DEATH(buffer_->PrintLog(), "");

  34.  }

  35. }

 
   
   
 
  1. TEST_F(MonitorBufferTest, Operator) {

  2.  buffer_->ERROR() << "Hi";//使用<<运算符写入ERROR级别的"Hi"

  3.  EXPECT_EQ(MonitorMessageItem::ERROR, buffer_->level_);

  4.  ASSERT_EQ(1, buffer_->monitor_msg_items_.size());

  5.  auto &item = buffer_->monitor_msg_items_.back();

  6.  EXPECT_EQ(MonitorMessageItem::ERROR, item.first);

  7.  EXPECT_EQ("Hi", item.second);

  8.  //level_是最近一次调用的level。

  9.  (*buffer_) << " How"

  10.             << " are"

  11.             << " you";

  12.  EXPECT_EQ(MonitorMessageItem::ERROR, buffer_->level_);

  13.  ASSERT_EQ(1, buffer_->monitor_msg_items_.size());

  14.  EXPECT_EQ(MonitorMessageItem::ERROR, item.first);

  15.  EXPECT_EQ("Hi How are you", item.second);

  16.  buffer_->INFO() << 3 << "pieces";

  17.  EXPECT_EQ(MonitorMessageItem::INFO, buffer_->level_);

  18.  ASSERT_EQ(2, buffer_->monitor_msg_items_.size());

  19.  item = buffer_->monitor_msg_items_.back();

  20.  EXPECT_EQ(MonitorMessageItem::INFO, item.first);

  21.  EXPECT_EQ("3pieces", item.second);

  22.  const char *fake_input = nullptr;

  23.  EXPECT_TRUE(&(buffer_->INFO() << fake_input) == buffer_);

  24. }

common/monitor/monitor.h

Monitor类主要用于收集各个模块的message信息,并发布到相关的topic中。

  • 构造函数

 
   
   
 
  1. explicit Monitor(const MonitorMessageItem::MessageSource &source)

  2.      : source_(source) {}

  3. 创建一个消息项,指定message来源哪一个模块。

  • 成员函数:

 
   
   
 
  1. virtual void Publish(const std::vector<MessageItem> &messages) const;

  2. 组合多条消息,并发布到相关的topic

测试代码:

 
   
   
 
  1.  MonitorTest monitor(MonitorMessageItem::CONTROL);

  2.  std::vector<std::pair<MonitorMessageItem::LogLevel, std::string>> items{

  3.      {MonitorMessageItem::INFO, "info message"},

  4.      {MonitorMessageItem::WARN, "warn message"},

  5.      {MonitorMessageItem::ERROR, "error message"},

  6.  };

  7.  monitor.Publish(items);//一次性发布多条消息。

总的来说Monitor类就是收集各个模块的工作log日志并发布到相应的topic用于监控。其中用到了MonitorBuffer,目的是合并相同level的log消息日志并一次性发布——减少冗余信息。


common/math

math目录下面的函数都是一些数学class封装。只做粗略的介绍。细致探究还是要看源码。

  • vec2d.h 2D的向量, 与Eigen::Vector2d相似。

  • aabox2d.h 2D区域

  • aaboxkdtree2d.h kd树组成的2D区域集

  • sin_table.h 查表法,快速计算sin值。

  • angle.h 角度弧度转换

  • box2d.h 2D框

  • euler_angles_zxy.h 欧拉角

  • integral.h 求GaussLegendre高斯-勒让德积分结果

  • kalman_filter.h 卡尔曼滤波,没有ekf,没有ukf。只有最简单的kf。

  • linear_interpolation.h 插值函数

  • polygon2d.h 2D多边形

  • search.h 在给定的区间内寻找极小值。 
    math目录下都是常规代码。快速了解其API,建议阅读对应的测试代码。


common/apollo_app.h

ApolloApp类用于各个模块注册信息。各个模块每调用一次APOLLO_MAIN(APP),即创建一个module模块进程, ApolloApp类规范了每个模块APP类的公有接口。 正常情况下,该进程将持续运行。ApolloApp是纯虚函数。这意味着每个模块需要重写多个纯虚函数接口才能实例化本模块。而重写的正是每个模块不同的地方。

虚函数接口:

 
   
   
 
  1. virtual std::string Name() const = 0;

  2. 模块名称,进程名。

 
   
   
 
  1. virtual int Spin();

  2. 初始化模块app的信息,并持续运行直到shutdown

 
   
   
 
  1. virtual apollo::common::Status Init() = 0;

  2. 执行初始化加载配置文件和传感器数据等任务。

 
   
   
 
  1. virtual apollo::common::Status Start() = 0;

  2. 开始执行模块任务。若由上层message到来触发,则执行与message相关的回调函数。

  3. 若由时间time触发,则调用时间处理回调函数。

 
   
   
 
  1. virtual void Stop() = 0;

  2. 模块停止运行。在ros::shutdown()执行完毕后的清理工作。

 
   
   
 
  1. virtual void ReportModuleStatus();

  2. HMI人机交互界面发送状态status码。

最后本文件还有一个宏定义方便各个模块运行。

 
   
   
 
  1. 每个模块是一个进程。

  2. #define APOLLO_MAIN(APP)                                       \

  3.  int main(int argc, char **argv) {                            \

  4.    google::InitGoogleLogging(argv[0]);                        \

  5.    google::ParseCommandLineFlags(&argc, &argv, true);         \

  6.    signal(SIGINT, apollo::common::apollo_app_sigint_handler); \

  7.    APP apollo_app_;                                           \

  8.    ros::init(argc, argv, apollo_app_.Name());                 \

  9.    apollo_app_.Spin();                                        \

  10.    return 0;                                                  \

  11.  }


其他文件夹的内容还没看,今天就先写这么多吧

总结

common的代码已经分析完毕。该目录下主要提供了各个模块公用的函数和class以及一些数学API还有公共的宏定义。

  •  在Apollo 1.0中,common是整个框架的基础。 

  •  configs是配置文件加载。

  •  adapters是数据交互的抽象接口。

  •  math提供了数学几何api接口。

  •  monitor提供监控log信息。

  •  status提供各个模块工作状态。

  •  time提供计时类。 

  •  util提供文件io管理功能。  

  •  vehicle_state提供车辆状态信息与预期状态估计。


以上是关于百度开源自动驾驶apollo源码分析的主要内容,如果未能解决你的问题,请参考以下文章

自动驾驶Apollo源码分析系统,CyberRT篇:简述CyberRT框架基础概念

自动驾驶Apollo源码分析系统,CyberRT篇:简述CyberRT框架基础概念

自动驾驶Apollo源码分析系统,CyberRT篇:简述CyberRT框架基础概念

自动驾驶 Apollo 源码分析系列,感知篇:感知融合代码的基本流程

自动驾驶 Apollo 源码分析系列,感知篇:感知融合代码的基本流程

自动驾驶 Apollo 源码分析系列,感知篇:感知融合代码的基本流程