hi,你好!欢迎访问本站!登录
本站由网站地图腾讯云宝塔系统阿里云强势驱动
当前位置:首页 - 教程 - 杂谈 - 正文 君子好学,自强不息!

Apollo进修笔记(一):canbus模块与车辆底盘之间的CAN数据传输历程

2019-11-18杂谈搜奇网43°c
A+ A-

 

Apollo进修笔记(一):canbus模块与车辆底盘之间的CAN数据传输历程

博主现在从车载自组网信道分派和多跳路由转向了自动驾驶,没啥履历,想快些做出来个Demo照样得站在伟人的肩膀上才行,我挑选了Apollo,重要照样支撑国产而且它的开辟者套件有现成的底盘能够直接跑起来,然则apollo体系构造比较复杂,种种花梢的设想形式(音讯适配器工场形式等)绕得人头晕。日本那边有个autoware是基于原生ROS的,也用Apollo开辟者套件跑了下,就是平常的机器人开辟那套,难度合适门生用来做项目,照样得师夷长技以制夷,背面继承深切研讨一下。(注重代码内里是有我写的解释的)

此次的进修是基于Apollo3.0的,由于3.0照样基于ROS的,后期研讨autoware开辟本身的体系能用得上,而且谁人开辟者套件也要用3.0。

canbus模块启动历程

参考知行合一2018大佬的Apollo Planning模块源代码剖析能够晓得canbus模块的主进口为modules/canbus/main.cc

1 APOLLO_MAIN(apollo::canbus::Canbus);

 

该宏睁开后为:

 1 #define APOLLO_MAIN(APP)                                       \
 2   int main(int argc, char **argv) {                            \
 3     google::InitGoogleLogging(argv[0]);                        \
 4     google::ParseCommandLineFlags(&argc, &argv, true);         \
 5     signal(SIGINT, apollo::common::apollo_app_sigint_handler); \
 6     APP apollo_app_;                                           \
 7     ros::init(argc, argv, apollo_app_.Name());                 \
 8     apollo_app_.Spin();                                        \
 9     return 0;                                                  \
10   }

这里直接援用知行合一2018大神关于Apollo Planning模块的剖析:

Main函数完成以下事情:始化Google日记东西,运用Google敕令行剖析东西剖析相干参数,注册吸收中断信号“SIGINT”的处置惩罚函数:apollo::common::apollo_app_sigint_handler(该函数的功用非常简朴,就是收到中断信号“SIGINT”后,挪用ros::shutdown()封闭ROS),建立apollo::planning::Planning对象:apollo_app_,初始化ROS环境,挪用apollo_app_.Spin()函数最先音讯处置惩罚轮回。
————————————————
版权声明:本文为CSDN博主「知行合一2018」的原创文章,遵照 CC 4.0 BY-SA 版权协定,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/davidhopper/article/details/79176505

更细致的不再赘述,人人能够去上面链接进修。

apollo_app_.Spin();函数内会顺次挪用modules/canbus/canbus.ccInit()Start()

Init()重要历程源码剖析

  1 Status Canbus::Init() {
  2   AdapterManager::Init(FLAGS_canbus_adapter_config_filename);/*完成AdapterManager的初始化,
  3   FLAGS_canbus_adapter_config_filename对应于modules/canbus/common/canbus_gflags.cc中的
  4   DEFINE_string(canbus_adapter_config_filename, "modules/canbus/conf/adapter.conf", 
  5   "The adapter config file");
  6   adapter.conf中设置了canbus模块定阅和宣布的topic
  7   假如改成原生ROS的话,这里的AdapterManager设置删掉,改成ROS的topic定阅和宣布*/
  8   AINFO << "The adapter manager is successfully initialized.";
  9 
 10   // load conf
 11   //导入设置文件modules/canbus/conf/canbus_conf.pb.txt
 12   /*
 13   vehicle_parameter {
 14       brand: LINCOLN_MKZ//指定车辆,背面生成对应的vehicle_factory,进而生成对应的message_manager_
 15       max_enable_fail_attempt: 5
 16       driving_mode: COMPLETE_AUTO_DRIVE
 17     }
 18     
 19     can_card_parameter {
 20       brand: ESD_CAN//指定车辆,背面生成对应的can_client_
 21       type: PCI_CARD
 22       channel_id: CHANNEL_ID_ZERO
 23     }
 24     
 25     enable_debug_mode: false
 26     enable_receiver_log: false
 27     enable_sender_log: false
 28   */
 29   //假如改成原生ROS的话此处也能够删除,工场形式也能够放一边,上面导入的设置文件就是用来生成详细的产物工场
 30   //对象和产物对象,我们直接用ROS跑本身的工程平常车辆和CAN卡是牢固的,能够修改后直接生成对应的产物对象
 31   if (!common::util::GetProtoFromFile(FLAGS_canbus_conf_file, &canbus_conf_)) {
 32     return OnError("Unable to load canbus conf file: " +
 33                    FLAGS_canbus_conf_file);
 34   }
 35 
 36   AINFO << "The canbus conf file is loaded: " << FLAGS_canbus_conf_file;
 37   ADEBUG << "Canbus_conf:" << canbus_conf_.ShortDebugString();
 38 
 39   // Init can client
 40   auto *can_factory = CanClientFactory::instance();
 41   can_factory->RegisterCanClients();
 42   can_client_ = can_factory->CreateCANClient(canbus_conf_.can_card_parameter());
 43   /*
 44   std::unique_ptr<CanClient> CanClientFactory::CreateCANClient(
 45     const CANCardParameter& parameter) {
 46   auto factory = CreateObject(parameter.brand());//这里肯定了新建的can client对象,
 47   //此处新建的是ESD CAN卡对应的can client对象(ESD CAN卡是开辟者套件用的CAN卡),详细为
 48   //modules/drivers/canbus/can_client/esd/esd_can_client.cc的对象。
 49   //由此也能够看出modules/canbus/与modules/drivers/canbus/之间的联络是很严密的,
 50   //modules/canbus/倾向上层的针对差别车辆的针对性数据剖析
 51   //modules/drivers/canbus/倾向底层的一些共性功用的完成,比方
 52   //modules/drivers/canbus/can_comm/message_manager就是
 53   //modules/canbus/vehicle/lincoln/lincoln_message_manager的父类
 54   //改成原生ROS时这两个文件夹能够合到一同
 55   if (!factory) {
 56     AERROR << "Failed to create CAN client with parameter: "
 57            << parameter.DebugString();
 58       } else if (!factory->Init(parameter)) {
 59         AERROR << "Failed to initialize CAN card with parameter: "
 60                << parameter.DebugString();
 61       }
 62       return factory;
 63     }
 64   */
 65   if (!can_client_) {
 66     return OnError("Failed to create can client.");
 67   }
 68   AINFO << "Can client is successfully created.";
 69 
 70   VehicleFactory vehicle_factory;
 71   vehicle_factory.RegisterVehicleFactory();
 72   auto vehicle_object =
 73       vehicle_factory.CreateVehicle(canbus_conf_.vehicle_parameter());//相似地生成详细的车辆对象
 74   if (!vehicle_object) {
 75     return OnError("Failed to create vehicle:");
 76   }
 77 
 78   message_manager_ = vehicle_object->CreateMessageManager();//生成对应车辆的message_manager_
 79   if (message_manager_ == nullptr) {
 80     return OnError("Failed to create message manager.");
 81   }
 82   AINFO << "Message manager is successfully created.";
 83 //初始化can_receiver_,就是将can_client_、message_manager_赋给can_receiver_的成员变量
 84   if (can_receiver_.Init(can_client_.get(), message_manager_.get(),
 85                          canbus_conf_.enable_receiver_log()) != ErrorCode::OK) {
 86     return OnError("Failed to init can receiver.");
 87   }
 88   AINFO << "The can receiver is successfully initialized.";
 89 //初始化can_sender_,就是将can_client_赋给can_sender_的成员变量
 90   if (can_sender_.Init(can_client_.get(), canbus_conf_.enable_sender_log()) !=
 91       ErrorCode::OK) {
 92     return OnError("Failed to init can sender.");
 93   }
 94   AINFO << "The can sender is successfully initialized.";
 95 
 96     //生成对应车辆的vehicle_controller_ 
 97     //至此vehicle_object生成了message_manager_和vehicle_controller_ 
 98     //message_manager_ 用来剖析canbus从CAN吸收到的音讯
 99     //vehicle_controller_ 用来更新canbus发往CAN的数据对象,也叫作协定范例数据
100     //(如刹车这个敕令(协定)对应的对象,apollo将每种敕令建了个类,比方林肯的刹车对应的类是
101     //modules/canbus/vehicle/lincoln/protocol/brake_60.h),
102     //也就是canbus吸收到上层control的commond后经由过程vehicle_controller_ 
103     //更新协定范例数据内的详细成员变量(如刹车这个敕令对应的对象内的“刹车量pedal_cmd_”这一成员变量)
104   vehicle_controller_ = vehicle_object->CreateVehicleController();
105   if (vehicle_controller_ == nullptr) {
106     return OnError("Failed to create vehicle controller.");
107   }
108   AINFO << "The vehicle controller is successfully created.";
109 
110 /*vehicle_controller_->Init(...) (实际上是
111 modules/canbus/vehicle/lincoln/lincoln_controller.cc中的LincolnController::Init)内里
112 先生成brake_60_等协定范例数据,接着经由过程
113 can_sender_->AddMessage(Brake60::ID, brake_60_, false);等将brake_60_等协定范例数据
114 到场can_sender_的成员变量send_messages_(向量),背面can_sender_发送数据时就会将send_messages_
115 中的各个协定范例数据包括的CAN帧can_frame_to_send_发送到底盘
116 */
117   if (vehicle_controller_->Init(canbus_conf_.vehicle_parameter(), &can_sender_,
118                                 message_manager_.get()) != ErrorCode::OK) {
119     return OnError("Failed to init vehicle controller.");
120   }
121   AINFO << "The vehicle controller is successfully initialized.";
122   
123   CHECK(AdapterManager::GetControlCommand()) << "Control is not initialized.";
124   CHECK(AdapterManager::GetGuardian()) << "Guardian is not initialized.";
125   // TODO(QiL) : depreacte this
126   //相似原生ROS的添加回调函数,Apollo将topic的宣布和定阅都集成到了AdapterManager举行统一管理
127   //Canbus::OnControlCommand就是canbus模块对上层cotrol_command的回调函数
128   if (!FLAGS_receive_guardian) {
129     AdapterManager::AddControlCommandCallback(&Canbus::OnControlCommand, this);
130   } else {
131     AdapterManager::AddGuardianCallback(&Canbus::OnGuardianCommand, this);
132   }
133 
134   return Status::OK();
135 }

Init()重要历程流程图(为轻易整合不严厉根据代码里的递次)

Start()重要历程源码剖析

 1 /*Init()生成的对象有can_client_ , vehicle_object , message_manager_,
 2  vehicle_controller_ , can_receiver_ 和 can_sender_,
 3  Start()内里就是挪用can_client_, can_receiver_, can_sender_和vehicle_controller_的Start()将
 4  它们的功用各自启动起来,比方can_client_ 的Start() (实际上是
 5  modules/drivers/canbus/can_client/esd/esd_can_client.cc的Start())
 6  就是挪用third_party/can_card_library/esd_can/include/ntcan.h的内置函数canOpen()等设置端口等
 7  以启动CAN卡,ntcan.h是买CAN卡的时刻附带光盘里的文件,买了开辟者套件装好CAN卡今后要把这个文件拷到
 8  third_party/can_card_library/esd_can/include/下运用。由于ntcan.h是花钱买的所以就不把ntcan.h
 9  的代码放上来了。
10  
11  末了启动定时器轮回运转Canbus::OnTimer,OnTimer这个函数就是宣布底盘信息用的,宣布今后
12  定阅底盘topic的上层模块就可以吸收到底盘信息。
13 */
14 Status Canbus::Start() {
15   // 1. init and start the can card hardware
16   if (can_client_->Start() != ErrorCode::OK) {
17     return OnError("Failed to start can client");
18   }
19   AINFO << "Can client is started.";
20 
21   // 2. start receive first then send
22   if (can_receiver_.Start() != ErrorCode::OK) {
23     return OnError("Failed to start can receiver.");
24   }
25   AINFO << "Can receiver is started.";
26 
27   // 3. start send
28   if (can_sender_.Start() != ErrorCode::OK) {
29     return OnError("Failed to start can sender.");
30   }
31 
32   // 4. start controller
33   if (vehicle_controller_->Start() == false) {
34     return OnError("Failed to start vehicle controller.");
35   }
36 
37   // 5. set timer to triger publish info periodly
38   const double duration = 1.0 / FLAGS_chassis_freq;
39   timer_ = AdapterManager::CreateTimer(ros::Duration(duration),
40                                        &Canbus::OnTimer, this);
41 
42   // last step: publish monitor messages
43   apollo::common::monitor::MonitorLogBuffer buffer(&monitor_logger_);
44   buffer.INFO("Canbus is started.");
45 
46   return Status::OK();
47 }

Start()重要历程流程图


下面分canbus模块向底盘发送数据canbus模块从底盘吸收数据两部分举行深切剖析。

canbus模块向底盘发送数据

重要历程

canbus模块向底盘发送数据的末尾在canbus模块吸收到上层control_command,因而Canbus::OnControlCommand是发送的末尾。

 1 void Canbus::OnControlCommand(const ControlCommand &control_command) {
 2   int64_t current_timestamp =
 3       apollo::common::time::AsInt64<common::time::micros>(Clock::Now());
 4   // if command coming too soon, just ignore it.
 5   if (current_timestamp - last_timestamp_ < FLAGS_min_cmd_interval * 1000) {
 6     ADEBUG << "Control command comes too soon. Ignore.\n Required "
 7               "FLAGS_min_cmd_interval["
 8            << FLAGS_min_cmd_interval << "], actual time interval["
 9            << current_timestamp - last_timestamp_ << "].";
10     return;
11   }
12 
13   last_timestamp_ = current_timestamp;
14   ADEBUG << "Control_sequence_number:"
15          << control_command.header().sequence_num() << ", Time_of_delay:"
16          << current_timestamp - control_command.header().timestamp_sec();
17 
18 /*从此处最先的vehicle_controller_->Update(control_command)和can_sender_.Update()是症结
19 正如之前所提到的,vehicle_controller_->Update(control_command)用来更新协定范例数据的
20 成员变量,接着can_sender_.Update()把更新以后的数据经由过程can_frame_to_update_
21 赋值给can_frame_to_send_,can_sender_在Start()是开启的发送线程将can_frame_to_send_
22 经由过程CAN卡附带驱动内的canWrite()函数注入底盘CAN收集
23 
24 这两个Update()触及的函数比较多,中心挺多跳转,就不放源码了,跳转的历程在背面流程图中给出,详细代码的
25 完成人人照样须要本身去看代码
26 */
27   if (vehicle_controller_->Update(control_command) != ErrorCode::OK) {
28     AERROR << "Failed to process callback function OnControlCommand because "
29               "vehicle_controller_->Update error.";
30     return;
31   }
32   can_sender_.Update();
33 }

vehicle_controller_->Update流程图(以刹车对应的协定范例数据为例)

can_sender_.Update()流程图(以刹车对应的协定范例数据为例)

canbus模块从底盘吸收数据

重要历程

canbus Start()的时刻,can_receiver_.Start()启动了从底盘吸收数据的线程,线程内运转CanReceiver::RecvThreadFunc()

 1 // 2. start receive first then send
 2   if (can_receiver_.Start() != ErrorCode::OK) {
 3     return OnError("Failed to start can receiver.");
 4   }
 5   AINFO << "Can receiver is started.";
 6 
 7 template <typename SensorType>
 8 ::apollo::common::ErrorCode CanReceiver<SensorType>::Start() {
 9   if (is_init_ == false) {
10     return ::apollo::common::ErrorCode::CANBUS_ERROR;
11   }
12   is_running_ = true;
13 
14   thread_.reset(new std::thread([this] { RecvThreadFunc(); }));
15   if (thread_ == nullptr) {
16     AERROR << "Unable to create can client receiver thread.";
17     return ::apollo::common::ErrorCode::CANBUS_ERROR;
18   }
19   return ::apollo::common::ErrorCode::OK;
20 }

RecvThreadFunc()内轮回运转can_client_->Receive(&buf, &frame_num)

 1 template <typename SensorType>
 2 void CanReceiver<SensorType>::RecvThreadFunc() {
 3   AINFO << "Can client receiver thread starts.";
 4   CHECK_NOTNULL(can_client_);
 5   CHECK_NOTNULL(pt_manager_);
 6 
 7   int32_t receive_error_count = 0;
 8   int32_t receive_none_count = 0;
 9   const int32_t ERROR_COUNT_MAX = 10;
10   std::chrono::duration<double, std::micro> default_period{10 * 1000};
11 
12   while (IsRunning()) {
13     std::vector<CanFrame> buf;
14     int32_t frame_num = MAX_CAN_RECV_FRAME_LEN;
15     if (can_client_->Receive(&buf, &frame_num) !=
16         ::apollo::common::ErrorCode::OK) {
17 /*
18 can_client_为modules/drivers/canbus/can_client/esd/esd_can_client.cc的实例化对象,
19 其Receive()函数挪用了third_party/can_card_library/esd_can/include/ntcan.h的canRead函数从
20 CAN收集中读取数据并存入buf,
21 const int32_t ret = canRead(dev_handler_, recv_frames_, frame_num, nullptr);
22 buf的定义是std::vector<CanFrame> buf;
23 CanFrame在之前can_sender_.Update()流程图内有剖析。
24 */
25       LOG_IF_EVERY_N(ERROR, receive_error_count++ > ERROR_COUNT_MAX,
26                      ERROR_COUNT_MAX)
27           << "Received " << receive_error_count << " error messages.";
28       std::this_thread::sleep_for(default_period);
29       continue;
30     }
31     receive_error_count = 0;
32 
33     if (buf.size() != static_cast<size_t>(frame_num)) {
34       AERROR_EVERY(100) << "Receiver buf size [" << buf.size()
35                         << "] does not match can_client returned length["
36                         << frame_num << "].";
37     }
38 
39     if (frame_num == 0) {
40       LOG_IF_EVERY_N(ERROR, receive_none_count++ > ERROR_COUNT_MAX,
41                      ERROR_COUNT_MAX)
42           << "Received " << receive_none_count << " empty messages.";
43       std::this_thread::sleep_for(default_period);
44       continue;
45     }
46     receive_none_count = 0;
47 
48     for (const auto &frame : buf) {
49       uint8_t len = frame.len;
50       uint32_t uid = frame.id;
51       const uint8_t *data = frame.data;
52       pt_manager_->Parse(uid, data, len);
53 /*
54 pt_manager_在Init时被赋值为message_manager_(modules/canbus/vehicle/lincoln
55 /lincoln_message_manager.cc的实例化对象),message_manager_用来剖析从CAN收集猎取的CanFrame
56 */
57       if (enable_log_) {
58         ADEBUG << "recv_can_frame#" << frame.CanFrameString();
59       }
60     }
61     std::this_thread::yield();
62   }
63   AINFO << "Can client receiver thread stopped.";
64 }

吸收CAN数据流程图

至于chassis_datail找不到的题目人人能够从https://blog.csdn.net/M_Alan_walker/article/details/88823610的最背面取得解答。

跋文

博主重如果从https://blog.csdn.net/M_Alan_walker/article/details/88823610进修的,到场一些本身的明白,有些剖析得不完美的处所人人在上面的网址能够获得更细致的补充。

  选择打赏方式
微信赞助

打赏

QQ钱包

打赏

支付宝赞助

打赏

  移步手机端
Apollo进修笔记(一):canbus模块与车辆底盘之间的CAN数据传输历程

1、打开你手机的二维码扫描APP
2、扫描左则的二维码
3、点击扫描获得的网址
4、可以在手机端阅读此文章
未定义标签

本文来源:搜奇网

本文地址:https://www.sou7.cn/282020.html

关注我们:微信搜索“搜奇网”添加我为好友

版权声明: 本文仅代表作者个人观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。请记住本站网址https://www.sou7.cn/搜奇网。

发表评论

选填

必填

必填

选填

请拖动滑块解锁
>>