目录
- PX4 CAN 总线系统架构概述
- DRONE CAN 核心代码解析
- 工作原理详解
- 二次开发接口说明
- UML 软件工程图表
- 外部设备扩展指南
- 测距雷达扩展实现
- 光流传感器扩展实现
- 调试与测试方法
- 常见问题与解决方案
1. PX4 CAN 总线系统架构概述
CAN(Controller Area Network)总线是 PX4 自动驾驶系统中连接各类传感器和执行器的关键通信协议,具有高可靠性、实时性和抗干扰能力,非常适合无人机等嵌入式系统应用。
1.1 CAN 总线在 PX4 中的角色
功能 | 描述 | 优势 |
---|---|---|
传感器数据采集 | 连接 GPS、IMU、气压计、磁力计等 | 减少布线,提高可靠性 |
执行器控制 | 控制电调、舵机等执行机构 | 实时性高,支持分布式控制 |
外部设备扩展 | 连接测距雷达、光流、避障传感器等 | 标准化接口,易于扩展 |
冗余设计 | 支持双 CAN 总线配置 | 提高系统容错能力 |
1.2 PX4 CAN 系统软件架构
PX4 的 CAN 系统采用分层设计,从上到下分为:
层级 | 组件 | 功能 |
---|---|---|
应用层 | 各类设备驱动、uORB 消息处理 | 实现具体设备功能,处理传感器数据 |
协议层 | CAN 驱动、DRONE CAN 协议栈 | 处理 CAN 消息的编码、解码和传输 |
硬件抽象层 | CAN 控制器驱动 | 与具体硬件 CAN 控制器交互 |
硬件层 | CAN 控制器、收发器 | 物理层和数据链路层实现 |
1.3 DRONE CAN 协议简介
DRONE CAN 是针对无人机应用优化的 CAN 总线协议,基于 CAN 2.0B 标准,定义了无人机系统中常用设备的通信规范。
特性 | 说明 |
---|---|
数据帧格式 | 扩展帧 (29 位 ID) 为主,支持标准帧 (11 位 ID) |
波特率 | 默认 1Mbps,支持 500kbps 等其他速率 |
节点类型 | 定义了传感器、执行器、控制器等节点类型 |
消息类型 | 包含设备信息、传感器数据、控制指令等 |
即插即用 | 支持自动识别和配置新接入的设备 |
2. DRONE CAN 核心代码解析
2.1 代码目录结构
PX4 中与 CAN 和 DRONE CAN 相关的代码主要分布在以下目录:
目录 | 功能 | 核心文件 |
---|---|---|
drivers/can | CAN 驱动核心实现 | can.h 、can.cpp 、can_dev.h |
drivers/can/dronecan | DRONE CAN 协议栈 | dronecan.h 、dronecan.cpp 、node.hpp |
drivers/distance_sensor | 距离传感器驱动 | can_based_distance_sensor.cpp |
drivers/flow | 光流传感器驱动 | can_flow.cpp |
src/modules/uORB/topics | uORB 消息定义 | distance_sensor.msg 、optical_flow.msg |
src/lib/drivers | 设备驱动抽象 | device.h 、sensor.h |
2.2 CAN 驱动核心代码
can.h
和 can.cpp
实现了 CAN 总线的基本操作接口:
cpp
运行
// can.h 核心接口定义
class CANDriver : public Device
{
public:
// 初始化CAN控制器
virtual int init() = 0;
// 发送CAN消息
virtual int send(const can_message_t &msg, uint64_t timeout = 0) = 0;
// 接收CAN消息
virtual int receive(can_message_t &msg, uint64_t timeout = 0) = 0;
// 设置CAN波特率
virtual int set_bitrate(unsigned int bitrate) = 0;
// 注册消息回调函数
virtual void register_callback(CanCallback callback, void *user, uint32_t id, uint32_t mask) = 0;
};
can_message_t
结构体定义了 CAN 消息的格式:
cpp
运行
struct can_message_t {
uint32_t id; // CAN消息ID
uint8_t data[8]; // 数据内容
uint8_t len; // 数据长度
uint8_t flags; // 标志位(扩展帧/标准帧等)
uint64_t timestamp; // 时间戳
};
2.3 DRONE CAN 协议栈实现
dronecan.h
和 dronecan.cpp
实现了 DRONE CAN 协议的核心功能:
cpp
运行
// dronecan.h 核心类定义
class DroneCAN : public ModuleBase<DroneCAN>
{
public:
// 初始化DRONE CAN节点
bool init(const char *can_device_path);
// 启动节点
void start();
// 发送DRONE CAN消息
template <typename T>
int send(const T &msg, uint8_t priority = CAN_PRIO_DEFAULT);
// 注册消息回调
template <typename T>
void register_message_handler(DroneCANMessageHandler<T> handler, void *user_data = nullptr);
// 处理接收到的CAN消息
void handle_can_message(const can_message_t &msg);
// 获取节点状态
dronecan_node_status_t get_node_status() const;
};
DRONE CAN 消息处理采用注册回调的方式,当接收到特定类型的消息时,会自动调用相应的处理函数:
cpp
运行
// 消息处理示例
void handle_distance_sensor(const dronecan::messages::DistanceSensor &msg,
const dronecan::TransferMetadata &meta,
void *user_data)
{
// 处理距离传感器数据
DistanceSensorData data;
data.distance = msg.distance;
data.timestamp = hrt_absolute_time();
data.type = msg.sensor_type;
// 发布到uORB
publish_distance_sensor(data);
}
// 注册消息处理
dronecan.register_message_handler<dronecan::messages::DistanceSensor>(handle_distance_sensor, this);
2.4 设备驱动实现
以 CAN-based 距离传感器为例,其驱动实现结构如下:
cpp
运行
class CanBasedDistanceSensor : public DistanceSensor
{
public:
CanBasedDistanceSensor(const char *can_device_path);
~CanBasedDistanceSensor() override;
int init() override;
int ioctl(unsigned int cmd, unsigned long arg) override;
private:
// DRONE CAN节点指针
DroneCAN *_dronecan;
// 设备信息
dronecan::NodeID _node_id;
uint8_t _sensor_type;
// 消息处理函数
void handle_distance_message(const dronecan::messages::DistanceSensor &msg,
const dronecan::TransferMetadata &meta);
// 工作线程
static void work_trampoline(void *arg);
void work();
// 设备状态
enum class State {
INIT,
WAITING_FOR_DEVICE,
RUNNING,
ERROR
} _state;
};
3. 工作原理详解
3.1 CAN 总线初始化流程
PX4 系统启动时,CAN 总线的初始化流程如下:
步骤 | 函数调用 | 描述 |
---|---|---|
1 | boardctl(BOARDCtl_INIT_CAN, 0) | 触发 CAN 初始化 |
2 | can_init() | 初始化 CAN 硬件和驱动 |
3 | can_start() | 启动 CAN 控制器 |
4 | DroneCAN::init() | 初始化 DRONE CAN 协议栈 |
5 | DroneCAN::start() | 启动 DRONE CAN 节点 |
6 | can_device_register() | 注册 CAN 设备驱动 |
7 | uORB::Publication::advertise() | 发布传感器数据到 uORB |
3.2 消息发送流程
DRONE CAN 消息发送的完整流程:
- 应用层准备数据(如传感器读数)
- 将数据封装为相应的 DRONE CAN 消息结构体
- 调用
DroneCAN::send()
方法发送消息 - 协议栈将消息编码为 CAN 帧格式
- 调用 CAN 驱动的
send()
方法发送 CAN 帧 - 硬件层将数据发送到 CAN 总线
3.3 消息接收流程
DRONE CAN 消息接收的完整流程:
- CAN 硬件接收到总线上的数据
- 触发中断,调用 CAN 驱动的中断处理函数
- CAN 驱动将数据封装为
can_message_t
结构体 - 调用 DRONE CAN 协议栈的消息处理函数
- 协议栈解析 CAN 帧,得到 DRONE CAN 消息
- 调用注册的消息回调函数处理消息
- 处理后的数据发布到 uORB 消息总线上
3.4 设备发现与配置
DRONE CAN 支持即插即用功能,设备发现与配置流程:
步骤 | 描述 | 相关消息 |
---|---|---|
1 | 新设备上电,发送节点状态消息 | NodeStatus |
2 | 主控节点接收到新节点消息 | - |
3 | 主控节点发送节点 ID 分配消息 | NodeIDAllocation |
4 | 新设备确认节点 ID | NodeIDAllocationFeedback |
5 | 主控节点请求设备信息 | GetInfo |
6 | 新设备返回设备信息 | Info |
7 | 主控节点配置设备参数 | SetParameter |
8 | 设备确认参数配置 | ParameterValue |
9 | 设备开始正常工作,发送数据 | 各类传感器数据消息 |
4. 二次开发接口说明
4.1 CAN 驱动接口
接口 | 函数原型 | 功能描述 | 参数说明 | 返回值 |
---|---|---|---|---|
初始化 CAN | int can_init(const char *device, unsigned int bitrate) | 初始化指定 CAN 设备 | device: CAN 设备路径 bitrate: 波特率 | 0: 成功 -1: 失败 |
发送 CAN 消息 | int can_send(int fd, const can_message_t *msg, uint64_t timeout) | 发送 CAN 消息 | fd: CAN 设备文件描述符 msg: 要发送的消息 timeout: 超时时间 | 0: 成功 -1: 失败 |
接收 CAN 消息 | int can_recv(int fd, can_message_t *msg, uint64_t timeout) | 接收 CAN 消息 | fd: CAN 设备文件描述符 msg: 接收消息缓冲区 timeout: 超时时间 | 0: 成功 -1: 失败 |
注册消息过滤器 | int can_set_filter(int fd, uint32_t id, uint32_t mask) | 设置 CAN 消息过滤器 | fd: CAN 设备文件描述符 id: 消息 ID mask: 掩码 | 0: 成功 -1: 失败 |
4.2 DRONE CAN 协议栈接口
接口 | 函数原型 | 功能描述 | 参数说明 | 返回值 |
---|---|---|---|---|
初始化节点 | bool DroneCAN::init(const char *can_device_path) | 初始化 DRONE CAN 节点 | can_device_path: CAN 设备路径 | true: 成功 false: 失败 |
启动节点 | void DroneCAN::start() | 启动 DRONE CAN 节点 | - | - |
发送消息 | template <typename T> int send(const T &msg, uint8_t priority) | 发送 DRONE CAN 消息 | msg: 要发送的消息对象 priority: 消息优先级 | 0: 成功 -1: 失败 |
注册消息处理 | template <typename T> void register_message_handler(DroneCANMessageHandler<T> handler, void *user_data) | 注册消息处理回调 | handler: 回调函数 user_data: 用户数据 | - |
获取节点 ID | dronecan::NodeID get_node_id() const | 获取当前节点 ID | - | 节点 ID |
设置节点模式 | void set_node_mode(dronecan::NodeMode mode) | 设置节点工作模式 | mode: 节点模式 | - |
4.3 设备驱动开发接口
接口 | 函数原型 | 功能描述 | 适用场景 |
---|---|---|---|
设备基类 | class Device | 所有设备的基类 | 开发新设备驱动 |
传感器基类 | class Sensor | 传感器设备基类 | 开发传感器驱动 |
距离传感器接口 | class DistanceSensor | 距离传感器抽象接口 | 开发测距设备 |
光流传感器接口 | class OpticalFlow | 光流传感器抽象接口 | 开发光流设备 |
uORB 发布接口 | uORB::Publication<T> | 发布消息到 uORB | 传感器数据发布 |
uORB 订阅接口 | uORB::Subscription<T> | 从 uORB 订阅消息 | 接收控制指令 |
4.4 配置参数接口
PX4 提供了参数系统用于配置 CAN 设备,常用接口:
接口 | 函数原型 | 功能描述 |
---|---|---|
定义参数 | PARAM_DEFINE_INT32(name, default_value) | 定义整数参数 |
查找参数 | param_t param_find(const char *name) | 查找参数 ID |
获取参数 | int param_get(param_t param, void *val) | 获取参数值 |
设置参数 | int param_set(param_t param, const void *val) | 设置参数值 |
参数更新回调 | void param_changed(param_t param) | 参数变化时调用 |
常用 CAN 相关参数:
参数名 | 描述 | 取值范围 | 默认值 |
---|---|---|---|
CAN1_BITRATE | CAN1 总线波特率 | 125000, 250000, 500000, 1000000 | 1000000 |
CAN2_BITRATE | CAN2 总线波特率 | 125000, 250000, 500000, 1000000 | 1000000 |
CAN_DRV_LOG_LEVEL | CAN 驱动日志级别 | 0-5 | 1 |
CAN_POWER_ENABLE | CAN 总线电源使能 | 0-1 | 1 |
UAVCAN_ENABLE | DRONE CAN 使能 | 0-1 | 1 |
5. UML 软件工程图表
5.1 系统类图
5.2 消息处理时序图
5.3 设备初始化流程图
6. 外部设备扩展指南
6.1 扩展流程概述
为 PX4 添加新的 CAN 外部设备通常需要以下步骤:
步骤 | 内容 | 相关文件 |
---|---|---|
1 | 定义设备通信协议 | - |
2 | 实现 DRONE CAN 消息结构 | 协议定义文件 |
3 | 开发设备驱动 | drivers/ 目录下新建驱动文件 |
4 | 实现 uORB 消息发布 | 使用现有或新建 uORB 消息 |
5 | 添加参数配置 | 定义设备相关参数 |
6 | 修改编译配置 | CMakeLists.txt |
7 | 测试与调试 | 日志文件、mavlink 调试 |
6.2 设备协议设计原则
设计新设备的 DRONE CAN 通信协议时应遵循以下原则:
原则 | 说明 |
---|---|
兼容性 | 尽量使用 DRONE CAN 标准消息类型 |
高效性 | 数据格式紧凑,减少不必要的字段 |
实时性 | 关键数据采用较高优先级 |
可靠性 | 重要数据添加校验机制 |
可扩展性 | 预留未来功能扩展的空间 |
一致性 | 同类设备保持协议一致性 |
6.3 驱动开发模板
新 CAN 设备驱动的基本模板:
cpp
运行
#include <px4_platform_common/px4_config.h>
#include <px4_platform_common/log.h>
#include <drivers/device/device.h>
#include <lib/drivers/sensor.h>
#include <uORB/Publication.hpp>
#include <uORB/topics/[topic_name].h>
#include <drivers/can/dronecan/dronecan.h>
class Can[DeviceType] : public [BaseClass]
{
public:
Can[DeviceType](const char *can_device_path);
~Can[DeviceType]() override;
int init() override;
// 设备特定接口
[device_specific_methods]
private:
// DRONE CAN节点
DroneCAN *_dronecan;
// 设备信息
dronecan::NodeID _node_id;
char _device_name[32];
// uORB发布器
uORB::Publication<[topic_type]> _pub;
// 消息处理回调
void handle_message(const [dronecan_message_type] &msg,
const dronecan::TransferMetadata &meta);
// 工作线程
void work();
static void work_trampoline(void *arg);
px4::WorkItem *_work_item;
// 设备状态
enum class State {
INIT,
WAITING_FOR_DEVICE,
RUNNING,
ERROR
} _state;
// 设备参数
param_t _param1;
param_t _param2;
};
// 构造函数
Can[DeviceType]::Can[DeviceType](const char *can_device_path)
: [BaseClass]("Can[DeviceType]", "", [DeviceType]_DEVICE_PATH),
_node_id(0),
_state(State::INIT),
_work_item(nullptr)
{
// 初始化代码
}
// 析构函数
Can[DeviceType]::~Can[DeviceType]()
{
if (_work_item) {
delete _work_item;
}
}
// 初始化函数
int Can[DeviceType]::init()
{
// 初始化DRONE CAN
_dronecan = DroneCAN::get_instance(_can_device_path);
if (!_dronecan) {
PX4_ERR("Failed to get DroneCAN instance");
return -1;
}
// 注册消息回调
_dronecan->register_message_handler<[dronecan_message_type]>(
[this](const [dronecan_message_type] &msg, const dronecan::TransferMetadata &meta) {
this->handle_message(msg, meta);
}, this);
// 初始化工作线程
_work_item = new px4::WorkItem(this, &Can[DeviceType]::work);
// 查找参数
_param1 = param_find("[PARAM_NAME1]");
_param2 = param_find("[PARAM_NAME2]");
_state = State::WAITING_FOR_DEVICE;
return 0;
}
// 消息处理函数
void Can[DeviceType]::handle_message(const [dronecan_message_type] &msg,
const dronecan::TransferMetadata &meta)
{
// 处理接收到的消息
_node_id = meta.source_node_id;
_state = State::RUNNING;
// 处理数据...
}
// 工作函数
void Can[DeviceType]::work()
{
switch (_state) {
case State::INIT:
// 初始化状态处理
break;
case State::WAITING_FOR_DEVICE:
// 等待设备连接
break;
case State::RUNNING:
// 运行状态处理,发布数据
[topic_type] data = {};
// 填充数据...
_pub.publish(data);
break;
case State::ERROR:
// 错误状态处理
break;
}
}
// 驱动注册
extern "C" __EXPORT int can_[device_type]_start(const char *can_device_path)
{
// 检查设备是否已存在
if (device_exists("[device_path]")) {
PX4_ERR("Device already exists");
return -1;
}
// 创建驱动实例
Can[DeviceType] *dev = new Can[DeviceType](can_device_path);
if (!dev) {
PX4_ERR("Failed to allocate driver");
return -1;
}
// 初始化驱动
if (dev->init() != 0) {
PX4_ERR("Failed to initialize driver");
delete dev;
return -1;
}
PX4_INFO("CAN [DeviceType] driver started");
return 0;
}
7. 测距雷达扩展实现
7.1 测距雷达 DRONE CAN 协议定义
7.1.1 设备信息消息
字段 | 类型 | 描述 | 单位 | 范围 |
---|---|---|---|---|
sensor_type | uint8_t | 传感器类型 | - | 0-255 |
min_distance | float | 最小测量距离 | m | 0-100 |
max_distance | float | 最大测量距离 | m | 0-1000 |
orientation | uint8_t | 安装方向 | - | 0-15 |
update_rate | float | 数据更新率 | Hz | 0-1000 |
7.1.2 距离数据消息
字段 | 类型 | 描述 | 单位 | 范围 |
---|---|---|---|---|
distance | float | 测量距离 | m | 0-1000 |
signal_quality | uint8_t | 信号质量 | % | 0-100 |
timestamp | uint32_t | 时间戳 | ms | 0-4294967295 |
flags | uint8_t | 状态标志 | - | 0-255 |
7.1.3 配置消息
字段 | 类型 | 描述 | 单位 | 范围 |
---|---|---|---|---|
update_rate | float | 数据更新率 | Hz | 0-1000 |
field_of_view | float | 视场角 | 度 | 0-180 |
mode | uint8_t | 工作模式 | - | 0-3 |
filter_strength | uint8_t | 滤波强度 | - | 0-10 |
enable | bool | 使能状态 | - | 0-1 |
7.1.4 消息 ID 定义
消息类型 | 消息 ID | 优先级 | 传输方向 |
---|---|---|---|
设备信息 | 0x1001 | 低 | 传感器→飞控 |
距离数据 | 0x1002 | 中 | 传感器→飞控 |
配置消息 | 0x1003 | 中 | 飞控→传感器 |
配置确认 | 0x1004 | 中 | 传感器→飞控 |
设备状态 | 0x1005 | 低 | 传感器→飞控 |
命令消息 | 0x1006 | 高 | 飞控→传感器 |
7.2 测距雷达驱动实现
7.2.1 驱动头文件 (can_rangefinder.h)
cpp
运行
#ifndef DRIVERS_CAN_RANGFINDER_H_
#define DRIVERS_CAN_RANGFINDER_H_
#include <px4_platform_common/px4_config.h>
#include <px4_platform_common/log.h>
#include <drivers/device/device.h>
#include <lib/drivers/rangefinder/rangefinder.h>
#include <uORB/Publication.hpp>
#include <uORB/topics/distance_sensor.h>
#include <drivers/can/dronecan/dronecan.h>
#include <drivers/can/dronecan/messages/rangefinder.hpp>
class CanRangefinder : public Rangefinder
{
public:
CanRangefinder(const char *can_device_path);
~CanRangefinder() override;
int init() override;
int ioctl(unsigned int cmd, unsigned long arg) override;
void print_info() override;
private:
DroneCAN *_dronecan;
dronecan::NodeID _node_id{0};
char _vendor_name[32]{"Unknown"};
char _model_name[32]{"Unknown"};
uORB::Publication<distance_sensor_s> _pub{ORB_ID(distance_sensor)};
// 传感器参数
float _min_distance{0.1f};
float _max_distance{10.0f};
float _fov{30.0f};
int _orientation{distance_sensor_s::ROTATION_DOWNWARD};
// 状态变量
bool _initialized{false};
hrt_abstime _last_measurement_time{0};
uint32_t _consecutive_failures{0};
// 消息处理函数
void handle_device_info(const dronecan::messages::rangefinder::DeviceInfo &msg,
const dronecan::TransferMetadata &meta);
void handle_distance_measurement(const dronecan::messages::rangefinder::DistanceMeasurement &msg,
const dronecan::TransferMetadata &meta);
void handle_config_ack(const dronecan::messages::rangefinder::ConfigAck &msg,
const dronecan::TransferMetadata &meta);
// 工作线程
void work();
static void work_trampoline(void *arg);
px4::WorkItem *_work_item{nullptr};
// 发送配置到传感器
int send_config();
// 参数处理
static int param_update(int param_id, void *arg);
param_t _param_orientation;
param_t _param_update_rate;
param_t _param_filter_strength;
};
#endif /* DRIVERS_CAN_RANGFINDER_H_ */
7.2.2 驱动实现文件 (can_rangefinder.cpp)
cpp
运行
#include "can_rangefinder.h"
#include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
#include <parameters/param.h>
CanRangefinder::CanRangefinder(const char *can_device_path)
: Rangefinder("CAN_Rangefinder", can_device_path)
{
_work_item = new px4::WorkItem(this, &CanRangefinder::work);
}
CanRangefinder::~CanRangefinder()
{
if (_work_item) {
delete _work_item;
}
}
int CanRangefinder::init()
{
// 获取DRONE CAN实例
_dronecan = DroneCAN::get_instance(_device_path);
if (!_dronecan) {
PX4_ERR("Failed to get DroneCAN instance for %s", _device_path);
return PX4_ERROR;
}
// 注册消息回调
_dronecan->register_message_handler<dronecan::messages::rangefinder::DeviceInfo>(
[this](const dronecan::messages::rangefinder::DeviceInfo &msg, const dronecan::TransferMetadata &meta) {
this->handle_device_info(msg, meta);
}, this);
_dronecan->register_message_handler<dronecan::messages::rangefinder::DistanceMeasurement>(
[this](const dronecan::messages::rangefinder::DistanceMeasurement &msg, const dronecan::TransferMetadata &meta) {
this->handle_distance_measurement(msg, meta);
}, this);
_dronecan->register_message_handler<dronecan::messages::rangefinder::ConfigAck>(
[this](const dronecan::messages::rangefinder::ConfigAck &msg, const dronecan::TransferMetadata &meta) {
this->handle_config_ack(msg, meta);
}, this);
// 查找参数
_param_orientation = param_find("RNGFND_CAN_ORIENT");
_param_update_rate = param_find("RNGFND_CAN_RATE");
_param_filter_strength = param_find("RNGFND_CAN_FILT");
// 设置参数更新回调
param_set_callback(_param_orientation, this, &CanRangefinder::param_update);
param_set_callback(_param_update_rate, this, &CanRangefinder::param_update);
param_set_callback(_param_filter_strength, this, &CanRangefinder::param_update);
// 加载参数
param_update(0, this);
// 启动工作线程
_work_item->ScheduleOnInterval(100000); // 10Hz
_initialized = true;
return PX4_OK;
}
int CanRangefinder::ioctl(unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case RANGEFINDERIOC_SET_ORIENTATION:
_orientation = arg;
send_config();
return PX4_OK;
case RANGEFINDERIOC_GET_INFO: {
struct rangefinder_info_s info = {};
strncpy(info.vendor_name, _vendor_name, sizeof(info.vendor_name) - 1);
strncpy(info.model_name, _model_name, sizeof(info.model_name) - 1);
info.min_distance = _min_distance;
info.max_distance = _max_distance;
info.fov = _fov;
info.orientation = _orientation;
return copy_to_user((void *)arg, &info, sizeof(info)) ? PX4_ERROR : PX4_OK;
}
default:
return Rangefinder::ioctl(cmd, arg);
}
}
void CanRangefinder::print_info()
{
PX4_INFO("CAN Rangefinder:");
PX4_INFO(" Device Path: %s", _device_path);
PX4_INFO(" Node ID: %d", (int)_node_id);
PX4_INFO(" Vendor: %s", _vendor_name);
PX4_INFO(" Model: %s", _model_name);
PX4_INFO(" Min Distance: %.2f m", _min_distance);
PX4_INFO(" Max Distance: %.2f m", _max_distance);
PX4_INFO(" Field of View: %.1f degrees", _fov);
PX4_INFO(" Orientation: %d", _orientation);
PX4_INFO(" Last Measurement: %lld us ago", hrt_elapsed_time(&_last_measurement_time));
PX4_INFO(" Status: %s", _initialized ? "Initialized" : "Not initialized");
}
void CanRangefinder::handle_device_info(const dronecan::messages::rangefinder::DeviceInfo &msg,
const dronecan::TransferMetadata &meta)
{
_node_id = meta.source_node_id;
strncpy(_vendor_name, msg.vendor_name.c_str(), sizeof(_vendor_name) - 1);
strncpy(_model_name, msg.model_name.c_str(), sizeof(_model_name) - 1);
_min_distance = msg.min_distance;
_max_distance = msg.max_distance;
_fov = msg.field_of_view;
PX4_INFO("Discovered CAN rangefinder: %s %s (Node ID: %d)",
_vendor_name, _model_name, (int)_node_id);
// 收到设备信息后发送配置
send_config();
}
void CanRangefinder::handle_distance_measurement(const dronecan::messages::rangefinder::DistanceMeasurement &msg,
const dronecan::TransferMetadata &meta)
{
_node_id = meta.source_node_id;
_last_measurement_time = hrt_absolute_time();
_consecutive_failures = 0;
// 检查数据有效性
if (msg.distance < 0 || msg.distance > _max_distance * 1.2f) {
PX4_DEBUG("Invalid distance measurement: %.2f m", (double)msg.distance);
_consecutive_failures++;
return;
}
// 发布距离传感器数据
distance_sensor_s data = {};
data.timestamp = hrt_absolute_time();
data.min_distance = _min_distance;
data.max_distance = _max_distance;
data.current_distance = msg.distance;
data.signal_quality = msg.signal_quality;
data.type = distance_sensor_s::MAV_DISTANCE_SENSOR_LASER;
data.orientation = _orientation;
data.covariance = 0.01f; // 1cm 方差
data.timestamp_sample = data.timestamp;
_pub.publish(data);
// 更新基类数据
set_data(data.current_distance, data.timestamp);
}
void CanRangefinder::handle_config_ack(const dronecan::messages::rangefinder::ConfigAck &msg,
const dronecan::TransferMetadata &meta)
{
if (msg.success) {
PX4_INFO("Rangefinder config applied successfully");
} else {
PX4_ERR("Rangefinder config failed: %s", msg.error_message.c_str());
}
}
int CanRangefinder::send_config()
{
if (!_dronecan || _node_id == 0) {
return PX4_ERROR;
}
// 获取当前参数值
int32_t orientation = _orientation;
param_get(_param_orientation, &orientation);
_orientation = orientation;
float update_rate = 10.0f;
param_get(_param_update_rate, &update_rate);
int32_t filter_strength = 5;
param_get(_param_filter_strength, &filter_strength);
// 创建配置消息
dronecan::messages::rangefinder::Config cfg;
cfg.update_rate = update_rate;
cfg.field_of_view = _fov; // 保持传感器默认FOV
cfg.orientation = _orientation;
cfg.filter_strength = filter_strength;
cfg.enable = true;
// 发送配置消息到指定节点
return _dronecan->send(cfg, CAN_PRIO_NORMAL, _node_id);
}
void CanRangefinder::work()
{
if (!_initialized) {
return;
}
// 检查设备是否在线
if (hrt_elapsed_time(&_last_measurement_time) > 5000000) { // 5秒无数据
if (_consecutive_failures < 5) {
_consecutive_failures++;
PX4_WARN("No data from CAN rangefinder (count: %d)", _consecutive_failures);
}
}
// 如果已发现设备但未配置,尝试发送配置
if (_node_id != 0 && hrt_elapsed_time(&_last_measurement_time) < 5000000) {
static hrt_abstime last_config_send = 0;
if (hrt_elapsed_time(&last_config_send) > 1000000) { // 每1秒发送一次配置
send_config();
last_config_send = hrt_absolute_time();
}
}
}
int CanRangefinder::param_update(int param_id, void *arg)
{
CanRangefinder *dev = (CanRangefinder *)arg;
if (dev) {
dev->send_config();
}
return 0;
}
extern "C" __EXPORT int can_rangefinder_start(const char *can_device_path)
{
if (!can_device_path) {
PX4_ERR("Missing CAN device path");
return PX4_ERROR;
}
// 检查设备是否已存在
if (device_exists("can/rangefinder")) {
PX4_ERR("Device already exists");
return PX4_ERROR;
}
// 创建驱动实例
CanRangefinder *dev = new CanRangefinder(can_device_path);
if (!dev) {
PX4_ERR("Failed to allocate driver");
return PX4_ERROR;
}
// 初始化驱动
if (dev->init() != PX4_OK) {
PX4_ERR("Failed to initialize driver");
delete dev;
return PX4_ERROR;
}
// 注册设备
if (dev->register_class_devname("can/rangefinder") != PX4_OK) {
PX4_ERR("Failed to register device");
delete dev;
return PX4_ERROR;
}
PX4_INFO("CAN rangefinder driver started on %s", can_device_path);
return PX4_OK;
}
extern "C" __EXPORT int can_rangefinder_stop()
{
// 查找并删除设备
Device *dev = device_find("can/rangefinder");
if (!dev) {
PX4_ERR("Device not found");
return PX4_ERROR;
}
delete dev;
PX4_INFO("CAN rangefinder driver stopped");
return PX4_OK;
}
extern "C" __EXPORT int can_rangefinder_main(int argc, char *argv[])
{
if (argc == 2 && strcmp(argv[1], "start") == 0) {
const char *can_device = argc > 2 ? argv[2] : "/dev/can0";
return can_rangefinder_start(can_device);
} else if (argc == 1 && strcmp(argv[0], "stop") == 0) {
return can_rangefinder_stop();
} else if (argc == 1 && strcmp(argv[0], "status") == 0) {
Device *dev = device_find("can/rangefinder");
if (!dev) {
PX4_ERR("Device not running");
return PX4_ERROR;
}
CanRangefinder *rf = (CanRangefinder *)dev;
rf->print_info();
return PX4_OK;
}
PX4_INFO("Usage: can_rangefinder {start|stop|status} [can_device]");
return PX4_ERROR;
}
7.3 测距雷达参数配置
参数名 | 描述 | 取值范围 | 默认值 |
---|---|---|---|
RNGFND_CAN_ORIENT | 传感器安装方向 | 0-23 | 25 (向下) |
RNGFND_CAN_RATE | 数据更新率 | 1-100 | 10 Hz |
RNGFND_CAN_FILT | 滤波强度 | 0-10 | 5 |
RNGFND_CAN_MIN | 最小距离 | 0.01-10 | 0.1 m |
RNGFND_CAN_MAX | 最大距离 | 0.1-1000 | 10 m |
RNGFND_CAN_FOV | 视场角 | 1-180 | 30 度 |
RNGFND_CAN_MODE | 工作模式 | 0-3 | 0 (默认) |
7.4 初始化与应用代码
7.4.1 驱动注册与启动
在 CMakeLists.txt 中添加:
cmake
px4_add_module(
MODULE drivers__can_rangefinder
MAIN can_rangefinder
SRCS
can_rangefinder.cpp
DEPENDS
platforms__common
drivers__device
drivers__can__dronecan
)
在启动脚本中添加(例如 posix-configs/px4fmu_common/init.d/rc.sensors):
bash
# 启动CAN测距雷达
if can_rangefinder start /dev/can0; then
echo "Started CAN rangefinder on /dev/can0"
else
echo "Failed to start CAN rangefinder on /dev/can0"
fi
7.4.2 应用层使用示例
cpp
运行
#include <uORB/Subscription.hpp>
#include <uORB/topics/distance_sensor.h>
class RangefinderUser
{
public:
RangefinderUser() : _distance_sub(ORB_ID(distance_sensor)) {}
void update()
{
distance_sensor_s dist;
if (_distance_sub.update(&dist)) {
// 检查数据是否有效
if (dist.current_distance >= dist.min_distance &&
dist.current_distance <= dist.max_distance) {
// 使用距离数据
PX4_INFO("Distance: %.2f m", (double)dist.current_distance);
// 根据安装方向处理数据
switch (dist.orientation) {
case distance_sensor_s::ROTATION_DOWNWARD:
handle_downward_distance(dist.current_distance);
break;
case distance_sensor_s::ROTATION_FRONT:
handle_front_distance(dist.current_distance);
break;
// 处理其他方向...
}
} else {
PX4_WARN("Distance out of range: %.2f m", (double)dist.current_distance);
}
}
}
private:
uORB::Subscription<distance_sensor_s> _distance_sub;
void handle_downward_distance(float distance)
{
// 处理下视距离数据,例如用于定高
}
void handle_front_distance(float distance)
{
// 处理前视距离数据,例如用于避障
}
};
8. 光流传感器扩展实现
8.1 光流传感器 DRONE CAN 协议定义
8.1.1 设备信息消息
字段 | 类型 | 描述 | 单位 | 范围 |
---|---|---|---|---|
resolution_x | uint16_t | X 方向分辨率 | 像素 | 0-65535 |
resolution_y | uint16_t | Y 方向分辨率 | 像素 | 0-65535 |
max_flow_rate | float | 最大流速 | 像素 / 秒 | 0-10000 |
update_rate | float | 更新率 | Hz | 0-1000 |
fov_x | float | X 方向视场角 | 度 | 0-180 |
fov_y | float | Y 方向视场角 | 度 | 0-180 |
8.1.2 光流数据消息
字段 | 类型 | 描述 | 单位 | 范围 |
---|---|---|---|---|
flow_x | float | X 方向流速 | 像素 / 秒 | -10000-10000 |
flow_y | float | Y 方向流速 | 像素 / 秒 | -10000-10000 |
flow_x_integral | float | X 方向积分流速 | 像素 | -1e6-1e6 |
flow_y_integral | float | Y 方向积分流速 | 像素 | -1e6-1e6 |
ground_distance | float | 地面距离 | m | 0-100 |
quality | uint8_t | 数据质量 | % | 0-100 |
timestamp | uint32_t | 时间戳 | ms | 0-4294967295 |
flags | uint8_t | 状态标志 | - | 0-255 |
8.1.3 配置消息
字段 | 类型 | 描述 | 单位 | 范围 |
---|---|---|---|---|
update_rate | float | 数据更新率 | Hz | 0-1000 |
gain | uint8_t | 增益设置 | - | 0-100 |
enable_gyro | bool | 使能陀螺仪融合 | - | 0-1 |
enable_lpf | bool | 使能低通滤波 | - | 0-1 |
lpf_cutoff | float | 低通滤波截止频率 | Hz | 0-100 |
orientation | uint8_t | 安装方向 | - | 0-15 |
8.1.4 消息 ID 定义
消息类型 | 消息 ID | 优先级 | 传输方向 |
---|---|---|---|
设备信息 | 0x1101 | 低 | 传感器→飞控 |
光流数据 | 0x1102 | 高 | 传感器→飞控 |
配置消息 | 0x1103 | 中 | 飞控→传感器 |
配置确认 | 0x1104 | 中 | 传感器→飞控 |
设备状态 | 0x1105 | 低 | 传感器→飞控 |
陀螺仪数据 | 0x1106 | 高 | 传感器→飞控 |
8.2 光流传感器驱动实现
8.2.1 驱动头文件 (can_optical_flow.h)
cpp
运行
#ifndef DRIVERS_CAN_OPTICAL_FLOW_H_
#define DRIVERS_CAN_OPTICAL_FLOW_H_
#include <px4_platform_common/px4_config.h>
#include <px4_platform_common/log.h>
#include <drivers/device/device.h>
#include <lib/drivers/optical_flow/optical_flow.h>
#include <uORB/Publication.hpp>
#include <uORB/topics/optical_flow.h>
#include <uORB/topics/vehicle_angular_velocity.h>
#include <drivers/can/dronecan/dronecan.h>
#include <drivers/can/dronecan/messages/optical_flow.hpp>
class CanOpticalFlow : public OpticalFlow
{
public:
CanOpticalFlow(const char *can_device_path);
~CanOpticalFlow() override;
int init() override;
int ioctl(unsigned int cmd, unsigned long arg) override;
void print_info() override;
private:
DroneCAN *_dronecan;
dronecan::NodeID _node_id{0};
char _vendor_name[32]{"Unknown"};
char _model_name[32]{"Unknown"};
uORB::Publication<optical_flow_s> _pub{ORB_ID(optical_flow)};
uORB::Subscription<vehicle_angular_velocity_s> _ang_vel_sub{ORB_ID(vehicle_angular_velocity)};
// 传感器参数
uint16_t _resolution_x{0};
uint16_t _resolution_y{0};
float _fov_x{0.0f};
float _fov_y{0.0f};
float _max_flow_rate{0.0f};
int _orientation{0};
// 状态变量
bool _initialized{false};
hrt_abstime _last_measurement_time{0};
uint32_t _consecutive_failures{0};
float _last_ground_distance{0.5f};
// 积分变量
float _flow_x_integral{0.0f};
float _flow_y_integral{0.0f};
hrt_abstime _last_integral_time{0};
// 消息处理函数
void handle_device_info(const dronecan::messages::optical_flow::DeviceInfo &msg,
const dronecan::TransferMetadata &meta);
void handle_flow_measurement(const dronecan::messages::optical_flow::FlowMeasurement &msg,
const dronecan::TransferMetadata &meta);
void handle_gyro_data(const dronecan::messages::optical_flow::GyroData &msg,
const dronecan::TransferMetadata &meta);
void handle_config_ack(const dronecan::messages::optical_flow::ConfigAck &msg,
const dronecan::TransferMetadata &meta);
// 工作线程
void work();
static void work_trampoline(void *arg);
px4::WorkItem *_work_item{nullptr};
// 发送配置到传感器
int send_config();
// 参数处理
static int param_update(int param_id, void *arg);
param_t _param_orientation;
param_t _param_update_rate;
param_t _param_gain;
param_t _param_lpf_cutoff;
};
#endif /* DRIVERS_CAN_OPTICAL_FLOW_H_ */
8.2.2 驱动实现文件 (can_optical_flow.cpp)
cpp
运行
#include "can_optical_flow.h"
#include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
#include <parameters/param.h>
#include <mathlib/mathlib.h>
CanOpticalFlow::CanOpticalFlow(const char *can_device_path)
: OpticalFlow("CAN_OpticalFlow", can_device_path)
{
_work_item = new px4::WorkItem(this, &CanOpticalFlow::work);
}
CanOpticalFlow::~CanOpticalFlow()
{
if (_work_item) {
delete _work_item;
}
}
int CanOpticalFlow::init()
{
// 获取DRONE CAN实例
_dronecan = DroneCAN::get_instance(_device_path);
if (!_dronecan) {
PX4_ERR("Failed to get DroneCAN instance for %s", _device_path);
return PX4_ERROR;
}
// 注册消息回调
_dronecan->register_message_handler<dronecan::messages::optical_flow::DeviceInfo>(
[this](const dronecan::messages::optical_flow::DeviceInfo &msg, const dronecan::TransferMetadata &meta) {
this->handle_device_info(msg, meta);
}, this);
_dronecan->register_message_handler<dronecan::messages::optical_flow::FlowMeasurement>(
[this](const dronecan::messages::optical_flow::FlowMeasurement &msg, const dronecan::TransferMetadata &meta) {
this->handle_flow_measurement(msg, meta);
}, this);
_dronecan->register_message_handler<dronecan::messages::optical_flow::GyroData>(
[this](const dronecan::messages::optical_flow::GyroData &msg, const dronecan::TransferMetadata &meta) {
this->handle_gyro_data(msg, meta);
}, this);
_dronecan->register_message_handler<dronecan::messages::optical_flow::ConfigAck>(
[this](const dronecan::messages::optical_flow::ConfigAck &msg, const dronecan::TransferMetadata &meta) {
this->handle_config_ack(msg, meta);
}, this);
// 查找参数
_param_orientation = param_find("FLOW_CAN_ORIENT");
_param_update_rate = param_find("FLOW_CAN_RATE");
_param_gain = param_find("FLOW_CAN_GAIN");
_param_lpf_cutoff = param_find("FLOW_CAN_LPF");
// 设置参数更新回调
param_set_callback(_param_orientation, this, &CanOpticalFlow::param_update);
param_set_callback(_param_update_rate, this, &CanOpticalFlow::param_update);
param_set_callback(_param_gain, this, &CanOpticalFlow::param_update);
param_set_callback(_param_lpf_cutoff, this, &CanOpticalFlow::param_update);
// 加载参数
param_update(0, this);
// 启动工作线程
_work_item->ScheduleOnInterval(50000); // 20Hz
_last_integral_time = hrt_absolute_time();
_initialized = true;
return PX4_OK;
}
int CanOpticalFlow::ioctl(unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case OPTICAL_FLOW_IOC_SET_ORIENTATION:
_orientation = arg;
send_config();
return PX4_OK;
case OPTICAL_FLOW_IOC_GET_INFO: {
struct optical_flow_info_s info = {};
strncpy(info.vendor_name, _vendor_name, sizeof(info.vendor_name) - 1);
strncpy(info.model_name, _model_name, sizeof(info.model_name) - 1);
info.resolution_x = _resolution_x;
info.resolution_y = _resolution_y;
info.fov_x = _fov_x;
info.fov_y = _fov_y;
info.max_flow_rate = _max_flow_rate;
info.orientation = _orientation;
return copy_to_user((void *)arg, &info, sizeof(info)) ? PX4_ERROR : PX4_OK;
}
default:
return OpticalFlow::ioctl(cmd, arg);
}
}
void CanOpticalFlow::print_info()
{
PX4_INFO("CAN Optical Flow:");
PX4_INFO(" Device Path: %s", _device_path);
PX4_INFO(" Node ID: %d", (int)_node_id);
PX4_INFO(" Vendor: %s", _vendor_name);
PX4_INFO(" Model: %s", _model_name);
PX4_INFO(" Resolution: %dx%d pixels", _resolution_x, _resolution_y);
PX4_INFO(" Field of View: %.1fx%.1f degrees", _fov_x, _fov_y);
PX4_INFO(" Max Flow Rate: %.0f pixels/s", _max_flow_rate);
PX4_INFO(" Orientation: %d", _orientation);
PX4_INFO(" Last Measurement: %lld us ago", hrt_elapsed_time(&_last_measurement_time));
PX4_INFO(" Last Ground Distance: %.2f m", _last_ground_distance);
PX4_INFO(" Status: %s", _initialized ? "Initialized" : "Not initialized");
}
void CanOpticalFlow::handle_device_info(const dronecan::messages::optical_flow::DeviceInfo &msg,
const dronecan::TransferMetadata &meta)
{
_node_id = meta.source_node_id;
strncpy(_vendor_name, msg.vendor_name.c_str(), sizeof(_vendor_name) - 1);
strncpy(_model_name, msg.model_name.c_str(), sizeof(_model_name) - 1);
_resolution_x = msg.resolution_x;
_resolution_y = msg.resolution_y;
_fov_x = msg.fov_x;
_fov_y = msg.fov_y;
_max_flow_rate = msg.max_flow_rate;
PX4_INFO("Discovered CAN optical flow: %s %s (Node ID: %d)",
_vendor_name, _model_name, (int)_node_id);
// 收到设备信息后发送配置
send_config();
}
void CanOpticalFlow::handle_flow_measurement(const dronecan::messages::optical_flow::FlowMeasurement &msg,
const dronecan::TransferMetadata &meta)
{
_node_id = meta.source_node_id;
_last_measurement_time = hrt_absolute_time();
_consecutive_failures = 0;
// 更新地面距离
if (msg.ground_distance > 0.01f) { // 有效距离
_last_ground_distance = msg.ground_distance;
}
// 计算时间差
const hrt_abstime now = hrt_absolute_time();
const float dt = (now - _last_integral_time) / 1e6f;
_last_integral_time = now;
// 更新积分
_flow_x_integral += msg.flow_x * dt;
_flow_y_integral += msg.flow_y * dt;
// 限制积分值范围
_flow_x_integral = math::constrain(_flow_x_integral, -1e6f, 1e6f);
_flow_y_integral = math::constrain(_flow_y_integral, -1e6f, 1e6f);
// 获取机体角速度用于补偿
vehicle_angular_velocity_s ang_vel{};
_ang_vel_sub.copy(&ang_vel);
// 发布光流数据
optical_flow_s data = {};
data.timestamp = now;
data.sensor_id = _node_id;
data.flow_x = msg.flow_x;
data.flow_y = msg.flow_y;
data.flow_x_integral = _flow_x_integral;
data.flow_y_integral = _flow_y_integral;
data.ground_distance = _last_ground_distance;
data.quality = msg.quality;
data.gyro_x = ang_vel.xyz[0];
data.gyro_y = ang_vel.xyz[1];
data.gyro_z = ang_vel.xyz[2];
data.timestamp_gyro = ang_vel.timestamp;
data.orientation = _orientation;
_pub.publish(data);
// 更新基类数据
set_data(data.flow_x, data.flow_y, data.quality, data.ground_distance, data.timestamp);
}
void CanOpticalFlow::handle_gyro_data(const dronecan::messages::optical_flow::GyroData &msg,
const dronecan::TransferMetadata &meta)
{
// 如果光流传感器内置陀螺仪,处理其数据
// 这里可以将数据发布到相应的uORB主题或用于内部补偿
}
void CanOpticalFlow::handle_config_ack(const dronecan::messages::optical_flow::ConfigAck &msg,
const dronecan::TransferMetadata &meta)
{
if (msg.success) {
PX4_INFO("Optical flow config applied successfully");
} else {
PX4_ERR("Optical flow config failed: %s", msg.error_message.c_str());
}
}
int CanOpticalFlow::send_config()
{
if (!_dronecan || _node_id == 0) {
return PX4_ERROR;
}
// 获取当前参数值
int32_t orientation = _orientation;
param_get(_param_orientation, &orientation);
_orientation = orientation;
float update_rate = 50.0f;
param_get(_param_update_rate, &update_rate);
int32_t gain = 50;
param_get(_param_gain, &gain);
float lpf_cutoff = 20.0f;
param_get(_param_lpf_cutoff, &lpf_cutoff);
// 创建配置消息
dronecan::messages::optical_flow::Config cfg;
cfg.update_rate = update_rate;
cfg.gain = gain;
cfg.enable_gyro = true;
cfg.enable_lpf = true;
cfg.lpf_cutoff = lpf_cutoff;
cfg.orientation = _orientation;
// 发送配置消息到指定节点
return _dronecan->send(cfg, CAN_PRIO_NORMAL, _node_id);
}
void CanOpticalFlow::work()
{
if (!_initialized) {
return;
}
// 检查设备是否在线
if (hrt_elapsed_time(&_last_measurement_time) > 5000000) { // 5秒无数据
if (_consecutive_failures < 5) {
_consecutive_failures++;
PX4_WARN("No data from CAN optical flow (count: %d)", _consecutive_failures);
}
}
// 如果已发现设备但未配置,尝试发送配置
if (_node_id != 0 && hrt_elapsed_time(&_last_measurement_time) < 5000000) {
static hrt_abstime last_config_send = 0;
if (hrt_elapsed_time(&last_config_send) > 1000000) { // 每1秒发送一次配置
send_config();
last_config_send = hrt_absolute_time();
}
}
}
int CanOpticalFlow::param_update(int param_id, void *arg)
{
CanOpticalFlow *dev = (CanOpticalFlow *)arg;
if (dev) {
dev->send_config();
}
return 0;
}
extern "C" __EXPORT int can_optical_flow_start(const char *can_device_path)
{
if (!can_device_path) {
PX4_ERR("Missing CAN device path");
return PX4_ERROR;
}
// 检查设备是否已存在
if (device_exists("can/optical_flow")) {
PX4_ERR("Device already exists");
return PX4_ERROR;
}
// 创建驱动实例
CanOpticalFlow *dev = new CanOpticalFlow(can_device_path);
if (!dev) {
PX4_ERR("Failed to allocate driver");
return PX4_ERROR;
}
// 初始化驱动
if (dev->init() != PX4_OK) {
PX4_ERR("Failed to initialize driver");
delete dev;
return PX4_ERROR;
}
// 注册设备
if (dev->register_class_devname("can/optical_flow") != PX4_OK) {
PX4_ERR("Failed to register device");
delete dev;
return PX4_ERROR;
}
PX4_INFO("CAN optical flow driver started on %s", can_device_path);
return PX4_OK;
}
extern "C" __EXPORT int can_optical_flow_stop()
{
// 查找并删除设备
Device *dev = device_find("can/optical_flow");
if (!dev) {
PX4_ERR("Device not found");
return PX4_ERROR;
}
delete dev;
PX4_INFO("CAN optical flow driver stopped");
return PX4_OK;
}
extern "C" __EXPORT int can_optical_flow_main(int argc, char *argv[])
{
if (argc >= 2 && strcmp(argv[1], "start") == 0) {
const char *can_device = argc > 2 ? argv[2] : "/dev/can0";
return can_optical_flow_start(can_device);
} else if (argc == 2 && strcmp(argv[1], "stop") == 0) {
return can_optical_flow_stop();
} else if (argc == 2 && strcmp(argv[1], "status") == 0) {
Device *dev = device_find("can/optical_flow");
if (!dev) {
PX4_ERR("Device not running");
return PX4_ERROR;
}
CanOpticalFlow *flow = (CanOpticalFlow *)dev;
flow->print_info();
return PX4_OK;
}
PX4_INFO("Usage: can_optical_flow {start|stop|status} [can_device]");
return PX4_ERROR;
}
8.3 光流传感器参数配置
参数名 | 描述 | 取值范围 | 默认值 |
---|---|---|---|
FLOW_CAN_ORIENT | 传感器安装方向 | 0-23 | 25 (向下) |
FLOW_CAN_RATE | 数据更新率 | 1-200 | 50 Hz |
FLOW_CAN_GAIN | 传感器增益 | 0-100 | 50 |
FLOW_CAN_LPF | 低通滤波截止频率 | 1-100 | 20 Hz |
FLOW_CAN_MAX | 最大流速 | 100-10000 | 5000 像素 / 秒 |
FLOW_CAN_SCALE | 流速缩放因子 | 0.001-10 | 1.0 |
FLOW_CAN_GYRO | 使能陀螺仪融合 | 0-1 | 1 |
8.4 初始化与应用代码
8.4.1 驱动注册与启动
在 CMakeLists.txt 中添加:
cmake
px4_add_module(
MODULE drivers__can_optical_flow
MAIN can_optical_flow
SRCS
can_optical_flow.cpp
DEPENDS
platforms__common
drivers__device
drivers__can__dronecan
)
在启动脚本中添加(例如 posix-configs/px4fmu_common/init.d/rc.sensors):
bash
# 启动CAN光流传感器
if can_optical_flow start /dev/can0; then
echo "Started CAN optical flow on /dev/can0"
else
echo "Failed to start CAN optical flow on /dev/can0"
fi
8.4.2 应用层使用示例
cpp
运行
#include <uORB/Subscription.hpp>
#include <uORB/topics/optical_flow.h>
#include <uORB/topics/distance_sensor.h>
class OpticalFlowUser
{
public:
OpticalFlowUser() :
_flow_sub(ORB_ID(optical_flow)),
_distance_sub(ORB_ID(distance_sensor)) {}
void update()
{
optical_flow_s flow;
if (_flow_sub.update(&flow)) {
// 检查数据质量
if (flow.quality > 50) { // 质量大于50%
// 获取地面距离(如果光流未提供)
float ground_distance = flow.ground_distance;
if (ground_distance < 0.01f) {
distance_sensor_s dist;
if (_distance_sub.update(&dist) &&
dist.orientation == distance_sensor_s::ROTATION_DOWNWARD) {
ground_distance = dist.current_distance;
}
}
// 计算机体坐标系下的速度 (m/s)
if (ground_distance > 0.1f) { // 有效距离
float velocity_x = calculate_velocity(flow.flow_x, ground_distance, flow.fov_x, flow.resolution_x);
float velocity_y = calculate_velocity(flow.flow_y, ground_distance, flow.fov_y, flow.resolution_y);
PX4_INFO("Velocity: x=%.2f m/s, y=%.2f m/s",
(double)velocity_x, (double)velocity_y);
// 使用速度数据,例如用于位置控制
use_velocity_data(velocity_x, velocity_y);
}
} else {
PX4_WARN("Low flow quality: %d%%", flow.quality);
}
}
}
private:
uORB::Subscription<optical_flow_s> _flow_sub;
uORB::Subscription<distance_sensor_s> _distance_sub;
// 将像素流速转换为米/秒
float calculate_velocity(float flow_rate, float distance, float fov, uint16_t resolution)
{
if (resolution == 0 || distance < 0.01f) {
return 0.0f;
}
// 计算每像素对应的弧度
float rad_per_pixel = (fov * M_PI / 180.0f) / resolution;
// 转换为弧度/秒
float angular_velocity = flow_rate * rad_per_pixel;
// 转换为米/秒
return angular_velocity * distance;
}
void use_velocity_data(float vx, float vy)
{
// 使用速度数据进行位置控制或其他用途
}
};
9. 调试与测试方法
9.1 日志查看
命令 | 描述 | 常用选项 |
---|---|---|
logger on | 启动日志记录 | - |
logger off | 停止日志记录 | - |
log show | 查看日志内容 | -f can 筛选 CAN 相关日志 |
log dump <topic> | 导出指定主题日志 | distance_sensor , optical_flow |
dmesg | 查看系统启动日志 | -w 实时监控 |
9.2 CAN 总线调试工具
工具 | 描述 | 常用命令 |
---|---|---|
canutils | CAN 总线调试工具集 | candump can0 , cansend can0 123#112233 |
uorb top | 查看 uORB 消息频率 | - |
uorb info <topic> | 查看指定 uORB 主题信息 | uorb info distance_sensor |
param show <pattern> | 查看参数 | param show CAN_* |
param set <name> <value> | 设置参数 | param set CAN1_BITRATE 1000000 |
9.3 测试流程
9.3.1 测距雷达测试流程
- 连接硬件并上电
- 检查 CAN 总线状态:
canstatus can0
- 启动驱动:
can_rangefinder start /dev/can0
- 检查驱动状态:
can_rangefinder status
- 查看 uORB 消息:
uorb top distance_sensor
- 移动目标物,观察距离变化:
log dump distance_sensor
- 测试不同距离下的测量精度
- 测试传感器在不同光照条件下的表现
- 测试传感器在不同材质表面的表现
9.3.2 光流传感器测试流程
- 连接硬件并上电
- 检查 CAN 总线状态:
canstatus can0
- 启动驱动:
can_optical_flow start /dev/can0
- 检查驱动状态:
can_optical_flow status
- 查看 uORB 消息:
uorb top optical_flow
- 移动传感器,观察流速变化:
log dump optical_flow
- 测试不同光照条件下的表现
- 测试不同纹理表面的表现
- 测试不同高度下的表现
- 测试不同运动速度下的表现
10. 常见问题与解决方案
10.1 硬件连接问题
问题 | 可能原因 | 解决方案 |
---|---|---|
无 CAN 通信 | 1. 接线错误 2. 电源问题 3. 终端电阻缺失 | 1. 检查 CAN_H 和 CAN_L 是否接反 2. 检查传感器供电电压和电流 3. 在总线两端添加 120Ω 终端电阻 |
通信不稳定 | 1. 线缆过长 2. 电磁干扰 3. 接触不良 | 1. 缩短 CAN 总线长度 2. 使用屏蔽线 3. 检查连接器是否牢固 |
设备不被识别 | 1. 设备未上电 2. 波特率不匹配 3. 设备故障 | 1. 检查设备电源 2. 确认波特率设置一致 3. 更换设备测试 |
10.2 驱动问题
问题 | 可能原因 | 解决方案 |
---|---|---|
驱动无法启动 | 1. CAN 设备不存在 2. 权限问题 3. 驱动代码错误 | 1. 确认 CAN 设备路径正确 2. 检查文件权限 3. 查看系统日志,修复代码错误 |
无数据输出 | 1. 设备未正确初始化 2. 消息过滤器设置错误 3. 设备未发送数据 | 1. 检查设备初始化流程 2. 检查 CAN 过滤器设置 3. 使用 candump 确认总线上是否有数据 |
数据频率低 | 1. 设备配置问题 2. 工作线程周期过长 3. 总线负载过高 | 1. 调整设备更新率参数 2. 减小工作线程周期 3. 检查总线上的其他设备 |
10.3 数据问题
问题 | 可能原因 | 解决方案 |
---|---|---|
数据噪声大 | 1. 传感器本身噪声 2. 滤波参数不当 3. 安装不牢固 | 1. 检查传感器规格 2. 增大滤波强度 3. 确保传感器安装牢固 |
数据偏移 | 1. 校准问题 2. 安装倾斜 3. 参数设置错误 | 1. 执行传感器校准 2. 调整传感器安装角度 3. 检查相关参数设置 |
数据不更新 | 1. 传感器停止工作 2. 通信中断 3. 驱动崩溃 | 1. 检查传感器电源 2. 检查 CAN 总线连接 3. 重启驱动,查看日志 |
通过以上详细解析和实现指南,开发者可以深入理解 PX4 中 DRONE CAN 的工作原理,并成功扩展支持新的测距雷达和光流传感器设备。这些扩展将增强无人机的环境感知能力,为自主导航和避障等功能提供关键数据支持。