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

您所在的位置:网站首页 apollo系统开源代码 Apollo学习笔记(一):canbus模块与车辆底盘之间的CAN数据传输过程

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

2023-08-30 00:00| 来源: 网络整理| 查看: 265

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:

APOLLO_MAIN(apollo::canbus::Canbus);

该宏展开后为:

#define APOLLO_MAIN(APP) \ int main(int argc, char **argv) { \ google::InitGoogleLogging(argv[0]); \ google::ParseCommandLineFlags(&argc, &argv, true); \ signal(SIGINT, apollo::common::apollo_app_sigint_handler); \ APP apollo_app_; \ ros::init(argc, argv, apollo_app_.Name()); \ apollo_app_.Spin(); \ return 0; \ }

这里直接引用知行合一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.cc的Init()和Start()。

Init()主要过程源码分析 Status Canbus::Init() { AdapterManager::Init(FLAGS_canbus_adapter_config_filename);/*完成AdapterManager的初始化, FLAGS_canbus_adapter_config_filename对应于modules/canbus/common/canbus_gflags.cc中的 DEFINE_string(canbus_adapter_config_filename, "modules/canbus/conf/adapter.conf", "The adapter config file"); adapter.conf中配置了canbus模块订阅和发布的topic 如果改成原生ROS的话,这里的AdapterManager配置删掉,改成ROS的topic订阅和发布*/ AINFO return OnError("Failed to create can client."); } AINFO return OnError("Failed to create message manager."); } AINFO return OnError("Failed to init can sender."); } AINFO return OnError("Failed to init vehicle controller."); } AINFO AdapterManager::AddGuardianCallback(&Canbus::OnGuardianCommand, this); } return Status::OK(); } Init()主要过程流程图(为方便整合不严格按照代码里的顺序)

在这里插入图片描述

Start()主要过程源码分析 /*Init()生成的对象有can_client_ , vehicle_object , message_manager_, vehicle_controller_ , can_receiver_ 和 can_sender_, Start()里面就是调用can_client_, can_receiver_, can_sender_和vehicle_controller_的Start()将 它们的功能各自启动起来,比如can_client_ 的Start() (实际上是 modules/drivers/canbus/can_client/esd/esd_can_client.cc的Start()) 就是调用third_party/can_card_library/esd_can/include/ntcan.h的内置函数canOpen()等设置端口等 以启动CAN卡,ntcan.h是买CAN卡的时候附带光盘里的文件,买了开发者套件装好CAN卡以后要把这个文件拷到 third_party/can_card_library/esd_can/include/下使用。因为ntcan.h是花钱买的所以就不把ntcan.h 的代码放上来了。 最后启动定时器循环运行Canbus::OnTimer,OnTimer这个函数就是发布底盘信息用的,发布以后 订阅底盘topic的上层模块就能接收到底盘信息。 */ Status Canbus::Start() { // 1. init and start the can card hardware if (can_client_->Start() != ErrorCode::OK) { return OnError("Failed to start can client"); } AINFO return OnError("Failed to start can sender."); } // 4. start controller if (vehicle_controller_->Start() == false) { return OnError("Failed to start vehicle controller."); } // 5. set timer to triger publish info periodly const double duration = 1.0 / FLAGS_chassis_freq; timer_ = AdapterManager::CreateTimer(ros::Duration(duration), &Canbus::OnTimer, this); // last step: publish monitor messages apollo::common::monitor::MonitorLogBuffer buffer(&monitor_logger_); buffer.INFO("Canbus is started."); return Status::OK(); } Start()主要过程流程图

在这里插入图片描述 下面分canbus模块向底盘发送数据和canbus模块从底盘接收数据两部分进行深入分析。

canbus模块向底盘发送数据 主要过程

canbus模块向底盘发送数据的开端在canbus模块接收到上层control_command,因此Canbus::OnControlCommand是发送的开端。

void Canbus::OnControlCommand(const ControlCommand &control_command) { int64_t current_timestamp = apollo::common::time::AsInt64(Clock::Now()); // if command coming too soon, just ignore it. if (current_timestamp - last_timestamp_ AERROR Update流程图(以刹车对应的协议类型数据为例)

在这里插入图片描述

can_sender_.Update()流程图(以刹车对应的协议类型数据为例)

在这里插入图片描述

canbus模块从底盘接收数据 主要过程

在canbus Start()的时候,can_receiver_.Start()启动了从底盘接收数据的线程,线程内运行CanReceiver::RecvThreadFunc()

// 2. start receive first then send if (can_receiver_.Start() != ErrorCode::OK) { return OnError("Failed to start can receiver."); } AINFO return ::apollo::common::ErrorCode::CANBUS_ERROR; } is_running_ = true; thread_.reset(new std::thread([this] { RecvThreadFunc(); })); if (thread_ == nullptr) { AERROR 10 * 1000}; while (IsRunning()) { std::vector buf; int32_t frame_num = MAX_CAN_RECV_FRAME_LEN; if (can_client_->Receive(&buf, &frame_num) != ::apollo::common::ErrorCode::OK) { /* can_client_为modules/drivers/canbus/can_client/esd/esd_can_client.cc的实例化对象, 其Receive()函数调用了third_party/can_card_library/esd_can/include/ntcan.h的canRead函数从 CAN网络中读取数据并存入buf, const int32_t ret = canRead(dev_handler_, recv_frames_, frame_num, nullptr); buf的定义是std::vector buf; CanFrame在之前can_sender_.Update()流程图内有分析。 */ LOG_IF_EVERY_N(ERROR, receive_error_count++ > ERROR_COUNT_MAX, ERROR_COUNT_MAX) LOG_IF_EVERY_N(ERROR, receive_none_count++ > ERROR_COUNT_MAX, ERROR_COUNT_MAX) ADEBUG


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3