PX4中的uavcan进程

news/2025/3/5 10:09:03/

概述

PX4 中的 uavcan 模块梳理起来是有点杂的,就像 commander 中的内容一个较为杂乱。如果要梳理划分的话,可以按照主题消息的类型来划分,PX4 中的 uavcan 模块需要接收处理的消息主题非常多,因此将其主要分为三类:

1)需要收发的非传感器主题消息

2)只需接收的传感器主题消息

3)服务器消息(用于固件升级)

框图如下:

在 PX4 中的 uavcan 进程中,在 uavcan_main 中创建主节点,并且在创建主节点时,会将所有的主题消息挂载在主节点上,主节点也就是飞控。

服务器消息有一定的条件,即需要根据 UAVCAN_ENABLE 参数设置,大于 1 时,才会开启。固件升级功能需要动态节点 ID 分配,因此在服务器文件 uavcan_servers 中,包含的就是这两个功能,固件升级与动态节点 ID 分配,不过也可以理解为就是一个固件升级功能,因为动态节点 ID 分配是为固件升级服务的。

传感器消息是非常多的,传感器模块都在 uavcan/sensors 目录下,正是由于种类多,因此 PX4 单独为传感器创建了一个管理器 sensor_bridge ,在管理器中统一创建各个传感器的实例,并且创建了一个默认4个通道的列表,管理所有传感器消息的 uORB 主题发布。

px4 中的目录对应如下:

主流程

要梳理主流程的脉络的话,对照 DroneCAN 的标准流程就可以。按照 DroneCAN 的流程,创建节点一般需要几个步骤:

1)创建节点,并且在创建节点时需要指定 CAN 底层驱动实例以及系统时钟实例

2)创建消息订阅器与发布器

3)创建进程运行节点

主流程的步骤相比上面的三步要复杂一些,一方面在主循环中进行时间同步的操作,另一方面也有更多的主题消息,不过按照上述的步骤,可以大致梳理出 PX4 的 uavcan 创建节点的主流程步骤:

1)创建节点,指定 CAN 底层实例与系统时钟实例

2)为节点创建各类主题消息

3)创建进程运行节点

4)在主循环中定时进行时间同步

根据上述的主流程步骤,uavcan_main 文件中的主要内容与功能:

1.UavcanNode::start 函数中创建节点,并指定 CAN 底层实例与系统时钟实例;

2.UavcanNode::UavcanNode 构造函数中,会创建需要收发的主题消息各控制器并挂在节点上;

3.UavcanNode::init 函数中:

1)设置节点名称与 ID 基础信息

2)初始化需要收发的各消息控制器(在构造函数中已经创建,此时只初始化)

3)创建传感器主题消息链表(由于传感器较多,所以使用一个链表来节省资源)

4)获取 UAVCAN_ENABLE 参数设置,大于1时,启动服务器,用于固件升级

4.UavcanNode::Run() 函数中进行线程创建,时间同步,参数获取等

在构造函数中,就可以看出需要收发的主题消息控制器有哪些,这些消息,在PX4中是跟着构造函数一起创建的:

在初始化函数 init 中,会设置节点名称与 ID :

/*******************设置节点名称与ID 基础信息*******************/
_node.setName("org.pixhawk.pixhawk");  //设置节点名称
_node.setNodeID(node_id);   //设置节点ID
fill_node_info();

 

之后创建主题消息控制器:

/********************初始化各个其他模块的主题消息控制器***********************/
ret = _beep_controller.init();  //蜂鸣器控制器if (ret < 0) {return ret;
}// Actuators
ret = _esc_controller.init();   //电调控制器if (ret < 0) {return ret;
}
...

而传感器消息太多,因此传感器消息由一个管理器类 IUavcanSensorBridge,初始化函数中只需要调用此管理器类的一个成员函数 make_all 即可完成全部的创建:

// Sensor bridges
//创建所有传感器的消息链表(由于传感器太多,因此创建一个链表节省资源)
IUavcanSensorBridge::make_all(_node, _sensor_bridges);

传感器类主题消息的内容在下面的内容详细叙述,在此先跳过。

服务器的创建需要条件,需要根据 UAVCAN_ENABLE 参数:

/*获取 UAVCAN_ENABLE 参数设置,一共有0,1,2,3四种情况,大于1时就需要开启服务节点来支持升级*/
if (uavcan_enable > 1) {_servers = new UavcanServers(_node, _node_info_retriever);if (_servers) {int rv = _servers->init();if (rv < 0) {PX4_ERR("UavcanServers init: %d", ret);}}
}

UAVCAN_ENABLE 参数的定义如下:

0 - UAVCAN 禁用
1 - UAVCAN 对传感器支持,但没有动态节点ID分配与固件升级
2 - UAVCAN 对传感器支持,同时也支持动态节点ID分配与固件升级
3 - UAVCAN 对传感器支持,同时支持动态节点ID分配与固件升级,并且将电机控制信号也用UAVCAN输出

时间同步与固件升级

时间同步的流程可以参照《DroneCAN的实现库Libuavcan及基础功能示例》一文中的示例程序。主要需要注意的地方是主机的冗余机制: 如果当前模块是主机的话,会同时再创建一个从机运行,当检测到网络中有更高优先级的主机时,当前节点会启用从机 (suppress(false)),之后与新主机进行同步:

/** 检查网络中是否又更高优先级的主机* 如果有的话,激活本地的从机节点,当前模块由主机变从机,与新主机同步*/
if (_time_sync_slave.isActive()) { // "Active" 表示从设备网络中至少有一个远程主机if (_node.getNodeID() < _time_sync_slave.getMasterNodeID()) {/** 当前节点是优先级最高的主机节点  调用 suppress 函数,压制从机模式*/_time_sync_slave.suppress(true);  // SUPPRESS} else {/** 网络中存在更高优先级的主机,因此取消压制从机,允许从机调整本地时钟*/_time_sync_slave.suppress(false);  // UNSUPPRESS}} else {/** 当网络中没有主机时,必须压制从机,本主机是唯一时钟源*/_time_sync_slave.suppress(true);
}/** 发布时间同步主题消息*/
_time_sync_master.publish();

固件升级的流程也同样可以参照《DroneCAN的实现库Libuavcan及基础功能示例》一文中的示例程序。

需要收发的主题消息流程

此部分也是两种类型,不过大体上的类成员函数框架都是复制的。伪代码如下:

类名::类名(uavcan::INode &node) :要发布的主题消息(node),要订阅的主题消息(node),_timer(node)  //软件定时器
{要发布的消息.setPriority(uavcan::TransferPriority::Default);  //设置优先级
}int 类名::init()
{//启动要订阅的主题消息的回调函数int res = 要订阅的消息.start(StatusCbBinder(this, &类名::回调函数名));if (res < 0) {return res;}    // 设置软件定时器的周期与回调函数,用于定时广播主题消息if (!_timer.isRunning()) {_timer.setCallback(TimerCbBinder(this, &UavcanSafetyState::periodic_update));_timer.startPeriodic(uavcan::MonotonicDuration::fromMSec(1000 / MAX_RATE_HZ));}return 0;
}//周期更新状态,然后发布消息
void 类名::广播回调函数(const uavcan::TimerEvent &)
{更新消息(void)要发布的主题消息.broadcast(cmd);
}//处理接受到的主题消息
void 类名::订阅回调函数()
{
}

如果是仅仅广播消息,不接收消息的控制器的话,例如安全开关,就不需要订阅,其代码也基本上是上述的模板套上去即可:

1.构造函数中创建主题消息并设置优先级:

2.初始化函数中设置软件定时器的周期与回调函数:

3.回调函数中更新数据并调用 broadcast 库函数来发送消息:

如果是订阅与发布都需要的话,例如电调,则在初始化过程中会再多增加一个启动主题消息的订阅,并指定回调函数:

传感器类主题消息流程

传感器类的主题消息控制器,在 PX4 中,又创建了一个管理器来管理这么多的传感器消息控制器,即 sensor_bridge。在 sensor_bridge 中,创建了一个列表,在 make_all 函数中,将创建的传感器对象都添加到列表中:

不过更重要的是,在 sensor_bridge 中,创建了一个默认为4的通道:

_channels(new uavcan_bridge::Channel[max_channels]),

由于传感器种类太多,PX4 不仅要接收传感器发送的主题数据,同时在接收之后,还要根据数据更新相应的 uORB 主题消息,因此 PX4 使用这 4 个通道来发布相应的 uORB 主题消息,避免同时发布的数据太多,节省资源,在 publish 函数中实现:

当通道中有 ID 与要发布的消息的传感器 ID 相同时,优先选择此通道,如果没有,则需按照空闲通道,当有空闲通道时,则发布 uORB 主题消息,如果没有空闲通道,则退出函数,暂不发布数据。

其余的单传感器的消息控制器的框架与上面一节的框架是一样的,只是没有广播消息,只订阅消息,例如电池,也是在构造函数中创建消息类型:

UavcanBatteryBridge::UavcanBatteryBridge(uavcan::INode &node) :UavcanSensorBridgeBase("uavcan_battery", ORB_ID(battery_status)),ModuleParams(nullptr),_sub_battery(node),_sub_battery_aux(node),_warning(battery_status_s::BATTERY_WARNING_NONE),_last_timestamp(0)
{
}

在初始化函数中,开启消息订阅,并指定回调函数:

int UavcanBatteryBridge::init()
{/*启动主题消息订阅器,并指定其回调函数*/int res = _sub_battery.start(BatteryInfoCbBinder(this, &UavcanBatteryBridge::battery_sub_cb));if (res < 0) {PX4_ERR("failed to start uavcan sub: %d", res);return res;}...

在回调函数中,更新数据,并且调用 sensor_bridge 的发布函数来发布 uORB 主题:

if (battery_aux_support[instance] == false) {publish(msg.getSrcNodeID().get(), &battery_status[instance]);
}

px4自身id与消息订阅使能

px4自身的id与各类外设的消息是否订阅的使能,在uavcan_param.c中,如果要修改飞控的id,以及是否订阅相关外设的消息,调节参数即可,或者在QGC中修改:


http://www.ppmy.cn/news/1576792.html

相关文章

web安全渗透测试 APP安全渗透漏洞测试详情

前言 小小白承包了一块20亩的土地&#xff0c;依山傍水&#xff0c;风水不错。听朋友说去年玉米大卖&#xff0c;他也想尝尝甜头&#xff0c;也就种上了玉米。 看着玉米茁壮成长&#xff0c;别提小小白心里多开心&#xff0c;心里盘算着玉米大买后&#xff0c;吃香喝辣的富贵…

Scaling Laws(缩放法则)详解

Scaling Laws&#xff08;缩放法则&#xff09;详解 1. 定义与核心概念 Scaling Laws&#xff08;缩放法则&#xff09;描述的是模型性能&#xff08;如准确率、任务表现&#xff09;与计算资源&#xff08;模型参数量、训练数据量、训练时间&#xff09;之间的数学关系。其核…

PyCharm接入本地部署DeepSeek 实现AI编程!【支持windows与linux】

今天尝试在pycharm上接入了本地部署的deepseek&#xff0c;实现了AI编程&#xff0c;体验还是很棒的。下面详细叙述整个安装过程。 本次搭建的框架组合是 DeepSeek-r1:1.5b/7b Pycharm专业版或者社区版 Proxy AI&#xff08;CodeGPT&#xff09; 首先了解不同版本的deepsee…

Qt 文件操作+多线程+网络

文章目录 1. 文件操作1.1 API1.2 例子1&#xff0c;简单记事本1.3 例子2&#xff0c;输出文件的属性 2. Qt 多线程2.1 常用API2.2 例子1&#xff0c;自定义定时器 3. 线程安全3.1 互斥锁3.2 条件变量 4. 网络编程4.1 UDP Socket4.2 UDP Server4.3 UDP Client4.4 TCP Socket4.5 …

WDM_OTN_基础知识_波分系统基本构成-无源器件

在波分系统中通常将发光,对光进行放大以及产生光电转换的器件称之为有源器件&#xff0c;例如光放&#xff0c;激光器&#xff0c;与此相反&#xff0c;将那些不发光&#xff0c;不对光进行放大&#xff0c;也不产生光电转换的器件称之为无源器件&#xff0c;波分系统中的无源器…

【Verilog编程】基于QUartus和Modesim的4位全加器和3-8译码器

目录 一、数字逻辑电路基础知识复习 1.1与逻辑和与门电路 1.2或逻辑和或门电路 1.3非运算 1.4逻辑代数运算 1.4.1基本公式 1.4.2逻辑函数的常见表达式 1.5组合逻辑电路的例题 二、3-8译码器和4位全加器Verilog编程复习 2.1 3-8译码器Logsim 和Verilog的电路图对比 2.…

24、《Spring Boot 的 Actuator 监控深度解析》

Spring Boot 的 Actuator 监控深度解析 引言 在微服务架构盛行的今天&#xff0c;应用监控已成为保障系统可靠性的关键环节。Spring Boot Actuator 作为官方提供的监控解决方案&#xff0c;通过暴露丰富的端点&#xff08;Endpoints&#xff09;帮助开发者实时掌握应用运行时…

iOS 实现UIButton自动化点击埋点

思路&#xff1a;我们HOOK UIControl的 addtarget:action:forControlEvents方法&#xff0c;交换UIControl的 addtarget:action:forControlEvents 方法的实现&#xff0c; 在交换的方法中添加原来响应的同时&#xff0c;再添加一个埋点响应&#xff0c;该响应方法实现了点击埋点…