ESP-Drone 传感器驱动深入解析:MPU6050/VL53LXX I2C 驱动与 PMW3901 SPI 光流驱动

原创2026-09-17 10:36:121,992 阅读
文章标签:无人机嵌入式物联网智能硬件

ESP-Drone 传感器驱动深入解析:MPU6050/VL53LXX I2C 驱动与 PMW3901 SPI 光流驱动

本文以 ESP-Drone 官方文档 drivers.rst 为主体,系统讲解 ESP-Drone 固件中使用的 I2C 设备驱动(MPU6050 六轴惯性传感器、VL53LXX 系列 ToF 测距传感器)与 SPI 设备驱动(PMW3901 光学流量传感器)。读完本文,你将掌握三款核心传感器的工作原理、关键寄存器配置、标准初始化/测距/校准流程,以及它们在 esp-drone 固件源码中的实际调用方式,可直接指导硬件选型、驱动移植与定高/定点功能调试。

一、驱动体系概览

ESP-Drone 的传感器驱动主要分为两类,位于仓库的 drivers/i2c_devices 与 drivers/spi_devices 两个组件目录下:

总线类型 传感器 在无人机中的作用
I2C MPU6050 姿态估计(陀螺仪 + 加速度计),是稳定器(stabilizer)的输入源之一
I2C VL53L0X / VL53L1X 定高(ToF 测距),为高度环提供厘米级高度信息
SPI PMW3901 水平定点(光流测位移),为位置环提供 X/Y 位移与速度

下文分别展开三款传感器的核心内容。

二、I2C 驱动:MPU6050 六轴惯性传感器

2.1 概述与工作原理

MPU6050 是一款整合型 6 轴运动处理组件,内部集成 3 轴陀螺仪、3 轴加速度计和数字运动处理器(DMP,Digital Motion Processor)。其物理原理如下:

  • 陀螺仪:当陀螺仪围绕任何感应轴旋转时,由于科里奥利效应(Coriolis effect)会产生振动。这种振动可被电容式传感器检测到,信号经放大、解调、滤波后,产生一个与角速度成正比的电压输出;
  • 电子加速度计:当加速度沿特定轴作用于对应的检测质量块时,电容式传感器会检测到电容的变化,从而换算得到该轴加速度。

2.2 可配置测量范围

传感器 可选满量程范围
陀螺仪 ±250 °/s、±500 °/s、±1000 °/s、±2000 °/s
加速度计 ±2 g、±4 g、±8 g、±16 g

在 ESP-Drone 固件中,sensors_mpu6050_hm5883L_ms5611.c 将陀螺仪量程配置为 ±2000 °/s、加速度计量程配置为 ±16 g,并相应定义每 LSB 对应的物理量(sensors_mpu6050_hm5883L_ms5611.c):

#define SENSORS_GYRO_FS_CFG    MPU6050_GYRO_FS_2000
#define SENSORS_DEG_PER_LSB_CFG MPU6050_DEG_PER_LSB_2000
#define SENSORS_ACCEL_FS_CFG   MPU6050_ACCEL_FS_16
#define SENSORS_G_PER_LSB_CFG  MPU6050_G_PER_LSB_16

2.3 AUX I2C 接口与 FIFO

  • AUX I2C 接口:MPU6050 具有一条辅助 I2C 总线,用于与片外 3 轴数字磁力计或其他传感器通信。该接口支持两种工作模式:I2C Master 模式(由 MPU6050 作为主机主动读取从机)与 Pass-Through 模式(辅助总线直接透传给外部主机)。这正是 ESP-Drone 将 MPU6050 与 HMC5883L 磁力计、MS5611 气压计组合使用的基础(见 sensors_mpu6050_hm5883L_ms5611.h)。
  • FIFO:MPU6050 内含一个 1024 字节的 FIFO 寄存器,可通过串行接口访问。FIFO 配置寄存器决定写入 FIFO 的数据类型,可选包括:陀螺仪数据、加速度计数据、温度读数、辅助传感器读数和 FSYNC 输入。ESP-Drone 的传感器读取任务一次批量读取 14 字节(SENSORS_MPU6050_BUFF_LEN),即从加速度计 X 高位寄存器 0x3B 起连续读出加速度、温度、陀螺仪全部数据(sensors_mpu6050_hm5883L_ms5611.c)。

2.4 数字低通滤波器(DLPF)与 FSYNC

MPU6050 自带低通滤波器,可通过**寄存器 26(CONFIG)**中的 DLPF_CFG 字段控制滤波频段,以降低高频干扰,但代价是会降低传感器输入速率:

  • 开启 DLPF:加速度计输出 1 kHz;
  • 关闭 DLPF:加速度计输出 8 kHz。

寄存器 26 中的 EXT_SYNC_SET 字段则用于配置外部帧同步(FSYNC)管脚的采样方式。

2.5 数字运动处理器(DMP)

  • MPU6050 内部集成了数字运动处理器(DMP),可直接计算四元数等姿态数据,从而减轻主 CPU 的运算负担;
  • DMP 可通过管脚触发中断,主机可在中断中读取处理结果。

MPU6050 DMP 内部架构

MPU6050 DMP 架构示意(源自 docs/_static/mpu6050_dmp_en.png)

2.6 方向定义

MPU6050 的 X、Y、Z 轴方向定义如下:X 轴沿芯片长边方向,Y 轴沿短边方向,Z 轴垂直芯片表面向上。安装时需与机体坐标系对齐,否则姿态解算方向会出错。

MPU6050 X/Y/Z 轴方向

MPU6050 轴方向定义(源自 docs/_static/mpu6050_xyz.png)

2.7 初始化步骤

官方推荐的 MPU6050 初始化流程如下:

  1. 恢复寄存器默认值:将 PWR_MGMT_1 的 bit7(DEVICE_RESET)置 1,寄存器恢复默认后该位自动回 0,且 bit6(SLEEP)自动置 1,芯片进入睡眠模式;
  2. 唤醒传感器:将 PWR_MGMT_1 的 bit6 清零;
  3. 设置时钟源;
  4. 设置量程:分别配置陀螺仪与加速度计的量程;
  5. 设置采样率(SMPLRT_DIV);
  6. 设置数字低通滤波器(可选)。

2.8 关键寄存器详解

寄存器典型值一览

寄存器 典型值 功能
PWR_MGMT_1 0x00 正常启用
SMPLRT_DIV 0x07 陀螺仪采样率 125 Hz
CONFIG 0x06 DLPF 滤波器频率 5 Hz
GYRO_CONFIG 0x18 陀螺仪不自检,满量程 ±2000 °/s
ACCEL_CONFIG 0x01 加速度计不自检,满量程 ±2 g

寄存器 117:WHO_AM_I(设备地址)

该寄存器 bit6:1] 保存设备地址,默认值为 0x68,且不反映 AD0 管脚的电平值。在 ESP-Drone 驱动源码中,默认地址即定义为 0x68([mpu6050.h),初始化时使用 MPU6050_ADDRESS_AD0_LOW,连接测试则通过读取设备 ID 并比对 0b110100 完成(mpu6050.c)。

寄存器 107:PWR_MGMT_1(电源管理 1)

位 名称 功能
bit7 DEVICE_RESET 置位后所有寄存器恢复默认值
bit6 SLEEP 置位后芯片进入睡眠模式
bit5 CYCLE 在 SLEEP 禁用时置位,进入循环模式(Cycle mode):芯片在睡眠与唤醒之间循环,按 LP_WAKE_CTRL(寄存器 108)设定的速率完成一次活动传感器采样后再次入睡

寄存器 26:CONFIG(配置 DLPF)

DLPF_CFG 字段同时作用于加速度计与陀螺仪的滤波,其取值与带宽的关系(对应文档附图 DLPF_CFG.png)如下:

DLPF_CFG 加速度计带宽 陀螺仪带宽 陀螺仪输出速率
0 260 Hz 256 Hz 8 kHz
1 184 Hz 188 Hz 1 kHz
2 94 Hz 98 Hz 1 kHz
3 44 Hz 42 Hz 1 kHz
4 21 Hz 20 Hz 1 kHz
5 10 Hz 10 Hz 1 kHz
6 5 Hz 5 Hz 1 kHz
7 保留 保留 8 kHz

寄存器 27:GYRO_CONFIG(陀螺仪量程)

  • XG_ST 等自检位(XG_ST / YG_ST / ZG_ST):置位后对应轴陀螺仪执行自检;
  • FS_SEL:选择陀螺仪满量程,取值关系见下表(对应附图 FS_SEL.png):
FS_SEL 满量程
0 ±250 °/s
1 ±500 °/s
2 ±1000 °/s
3 ±2000 °/s

寄存器 28:ACCEL_CONFIG(加速度计量程)

通过 AFS_SEL 字段配置加速度计满量程(对应附图 AFS_SEL.png):

AFS_SEL 满量程
0 ±2 g
1 ±4 g
2 ±8 g
3 ±16 g

寄存器 25:SMPLRT_DIV(采样率分频器)

该寄存器指定陀螺仪输出速率的分频系数,用于生成 MPU6050 的采样率。传感器寄存器输出、FIFO 输出和 DMP 采样都基于该采样率。计算公式为:

Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)

其中陀螺仪输出速率与 DLPF 相关:DLPF 禁用(DLPF_CFG = 0 或 7)时为 8 kHz,DLPF 使能时为 1 kHz(见寄存器 26)。典型配置 SMPLRT_DIV = 7 时,若未开启 DLPF,芯片可产生 1 kHz 的中断信号(对应附图 mpu6050_int_plot.png),这也是 ESP-Drone 传感器任务以 1 kHz 频率读取数据的基础。

寄存器 59~64:加速度计测量值

六个字节分别对应 X、Y、Z 轴加速度高/低字节(寄存器地址 0x3B ~ 0x40,对应附图 REG_3B_40.png)。注意两个存储约定:

  • 大端序存放:地址低位存数据高位,地址高位存数据低位;
  • 补码存放:测量值为有符号整数,采用二进制补码表示。

ESP-Drone 驱动的批量读取正是从 MPU6050_RA_ACCEL_XOUT_H(0x3B)开始连续读取 14 字节,一次取回加速度、温度与陀螺仪数据(mpu6050.h)。

寄存器 65~66:温度测量

两字节有符号整数,地址 0x41 ~ 0x42,按公式换算为摄氏温度(对应附图 REG_41_42.png)。

寄存器 67~72:陀螺仪测量值

六个字节对应 X、Y、Z 轴陀螺仪高/低字节(地址 0x43 ~ 0x48),同样为大端序、补码存放(对应附图 REG_43_48.png)。

2.9 源码级印证:从寄存器到姿态数据

在 ESP-Drone 固件中,MPU6050 由 mpu6050.c 提供寄存器读写封装,由 sensors_mpu6050_hm5883L_ms5611.c 负责与磁力计、气压计组合读取。传感器读取通过 MPU6050 的中断触发(data ready),中断发生后通过 i2cdevReadReg8(I2C0_DEV, MPU6050_ADDRESS_AD0_LOW, MPU6050_RA_ACCEL_XOUT_H, ...) 一次读回全部测量数据,再换算为物理量送入稳定器模块,构成姿态估计的原始输入。

三、I2C 驱动:VL53LXX ToF 测距传感器

3.1 概述与工作原理

VL53L1X 是 ST 公司提供的 Time-of-Flight(ToF)测距与姿态检测传感器。VL53L0X / VL53L1X 芯片内部集成了激光发射器与 SPAD 红外接收器,通过探测光子发送与接收的时间差计算光子飞行距离,最远测量距离可达 2 米(VL53L1X 更可达 4 米),非常适合中短距离测量应用。

3.2 测量区域:Region-of-Interest(ROI)

VL53L0X / VL53L1X 的测量值为测量区域内的最短距离。测量区域可以根据实际场景放大或缩小,但较大的探测范围可能引起测量值的波动。ROI 的最小尺寸为 4×4,最大为 16×16(以 SPAD 阵列单元计)。仓库中 VL53L1X 的封装实现见 vl53l1x.c 与 vl53l1_api.h。

3.3 测量距离与精度

  • VL53L0X:存在 3 ~ 4 cm 的盲区,有效测量范围 3 ~ 200 cm,精度 ±3%;
  • VL53L1X:VL53L0X 的升级版本,探测距离可达 400 cm。

VL53L1X 提供多种测距模式,不同模式在测量时间、距离与精度上各有取舍:

精度模式 测量时间 (ms) 测量范围 (m) 应用场景
默认(Default) 30 1.2 标准应用
高精度(High Precision) 200 1.2(±3%) 精密距离测量
远距离(Long Range) 33 2 远距离,仅在暗环境有效
高速(High Speed) 20 1.2(±5%) 速度优先、低精度

需要特别强调的是,VL53LXX 的测量距离与光线环境强相关:黑暗环境下探测距离更远;而在室外强光下,激光传感器会受到大量干扰,导致测量精度下降。因此文档建议:室外场景应结合气压计进行定高,而不是单独依赖激光测距。

3.4 测量频率与硬件电路要点

  • VL53L0X 响应频率最快可达 50 Hz,测量误差 ±5%;
  • VL53L1X 的 I2C 最高时钟频率为 400 kHz,上拉电阻需要根据电压和总线电容值选取;
  • 两个关键管脚:
    • XSHUT:输入管脚,用于模式选择(休眠),必须始终被驱动以避免漏电流,需要上拉电阻;
    • GPIO1:中断输出管脚,用于输出测量数据就绪(data ready)中断。

3.5 工作模式:HW Standby 与 SW Standby

通过设置 XSHUT 管脚的电平,可切换传感器的工作模式,以实现有条件启动并降低待机功耗;如果主机放弃对传感器模式的管理,可将 XSHUT 默认上拉:

  • HW Standby(硬件待机):XSHUT 拉低,传感器电源关闭;
  • SW Standby(软件待机):XSHUT 拉高,传感器进入 Boot 与 SW Standby 模式。

3.6 VL53LXX 初始化步骤

初始化必须按如下顺序进行(除第 7 步 ROI 外,其余步骤不可省略):

  1. 等待硬件初始化完成;
  2. 数据初始化(DataInit,上电后立即执行);
  3. 静态初始化,装载参数(StaticInit);
  4. 设置测量距离模式;
  5. 设置单次测量最长等待时间;
  6. 设置测量频率(时间间隔);
  7. 设置测量区域 ROI(可选);
  8. 启动测量。

典型初始化代码(原样继承自文档,配合注释理解):

/*init vl53l1 module*/
void vl53l1_init()
{
    Roi0.TopLeftX = 0;    // 测量目标区(可选)最小 4*4,最大 16*16。
    Roi0.TopLeftY = 15;
    Roi0.BotRightX = 7;
    Roi0.BotRightY = 0;
    Roi1.TopLeftX = 8;
    Roi1.TopLeftY = 15;
    Roi1.BotRightX = 15;
    Roi1.BotRightY = 0;

    int status = VL53L1_WaitDeviceBooted(Dev); // 等待硬件初始化完成。
    status = VL53L1_DataInit(Dev);             // 数据初始化,上电后立刻执行。
    status = VL53L1_StaticInit(Dev);           // 静态初始化,装载参数。
    status = VL53L1_SetDistanceMode(Dev, VL53L1_DISTANCEMODE_LONG); // 设置测量模式。
    status = VL53L1_SetMeasurementTimingBudgetMicroSeconds(Dev, 50000); // 根据测量模式确定最长等待时间。
    status = VL53L1_SetInterMeasurementPeriodMilliSeconds(Dev, 100);   // 设置测量间隔。

    status = VL53L1_SetUserROI(Dev, &Roi0);    // 设置测量区域 ROI。
    status = VL53L1_StartMeasurement(Dev);     // 启动测量。
    if(status) {
        printf("VL53L1_StartMeasurement failed \n");
        while(1);
    }
}

3.7 测距步骤:轮询模式与中断模式

测距可通过两种模式完成:轮询模式与中断模式。

轮询模式

轮询模式又分为两种实现方式:Drivers polling mode(阻塞式) 与 Host polling mode(非阻塞式)。以下为阻塞式轮询的完整流程,核心要点是:每次完成测量与读取后,必须调用 VL53L1_ClearInterruptAndStartMeasurement 清除中断标志并重新启动测量:

/* Autonomous ranging loop*/
static void
AutonomousLowPowerRangingTest(void)
{
    printf("Autonomous Ranging Test\n");

    static VL53L1_RangingMeasurementData_t RangingData;
    VL53L1_UserRoi_t Roi1;
    int roi = 0;
    float left = 0, right = 0;
    if (0/*isInterrupt*/) {
    } else {
        do // polling mode
        {
            int status = VL53L1_WaitMeasurementDataReady(Dev); // 等待测量结果
            if(!status) {
                status = VL53L1_GetRangingMeasurementData(Dev, &RangingData); // 获取单次测量数据
                if(status==0) {
                    if (roi & 1) {
                        left = RangingData.RangeMilliMeter;
                        printf("L %3.1f R %3.1f\n", right/10.0, left/10.0);
                    } else
                        right = RangingData.RangeMilliMeter;
                }
                if (++roi & 1) {
                    status = VL53L1_SetUserROI(Dev, &Roi1);
                } else {
                    status = VL53L1_SetUserROI(Dev, &Roi0);
                }
                status = VL53L1_ClearInterruptAndStartMeasurement(Dev); // 释放中断
            }
        }
        while (1);
    }
    //  return status;
}

该示例还展示了双 ROI 交替测距的用法:通过交替设置 Roi0 与 Roi1,可以分别测量左、右两个方向的距离,实现类似"左右测距"的功能。

中断模式

中断模式使用中断管脚 GPIO1:当测量数据就绪时,GPIO1 电平被拉低,通知主机读取数据。主机在中断服务程序中读取测量值后,同样需要调用 VL53L1_ClearInterruptAndStartMeasurement 清除中断并开始下一次测量。

3.8 传感器校准

如果传感器接收器上方安装了光罩,或传感器安装在透明盖板背后,由于透光率的变化,传感器需要进行校准。校准可通过两种途径完成:

使用官方 API 编写校准程序

校准流程的调用顺序必须完全一致,校准程序如下:

/* VL53L1 模块校准 */
static VL53L1_CalibrationData_t vl53l1_calibration(VL53L1_Dev_t *dev)
{
    int status;
    int32_t targetDistanceMilliMeter = 703;
    VL53L1_CalibrationData_t calibrationData;
    status = VL53L1_WaitDeviceBooted(dev);
    status = VL53L1_DataInit(dev);            // 设备初始化
    status = VL53L1_StaticInit(dev);          // 为给定用例加载设备设置
    status = VL53L1_SetPresetMode(dev, VL53L1_PRESETMODE_AUTONOMOUS);
    status = VL53L1_PerformRefSpadManagement(dev);
    status = VL53L1_PerformOffsetCalibration(dev, targetDistanceMilliMeter);
    status = VL53L1_PerformSingleTargetXTalkCalibration(dev, targetDistanceMilliMeter);
    status = VL53L1_GetCalibrationData(dev, &calibrationData);

    if (status)
    {
        ESP_LOGE(TAG, "vl53l1_calibration failed \n");
        calibrationData.struct_version = 0;
        return calibrationData;
    } else {
        ESP_LOGI(TAG, "vl53l1_calibration done ! version = %u \n", calibrationData.struct_version);
        return calibrationData;
    }
}

校准流程依次执行:设备初始化 → 装载预设模式 → SPAD 参考管理(RefSpad Management)→ 偏移校准(Offset Calibration,需在已知目标距离处进行)→ 单目标串扰校准(XTalk Calibration)→ 获取校准数据。校准得到的数据结构体(VL53L1_CalibrationData_t)可保存并在初始化时重新加载。

使用官方 PC GUI 校准

ST 官方提供了用于配置和校准传感器的 PC GUI(STSW-IMG008)。使用流程为:

  1. 将传感器连接到 ST 官方提供的 STM32F401RE Nucleo 开发板;
  2. 使用 GUI 软件执行校准,得到基准参考值;
  3. 将该参考值在初始化时写入传感器。

VL53L1X PC GUI 校准界面

ST 官方 PC GUI 校准界面(源自 docs/_static/vl53lxx_calibrate_gui.png)

3.9 VL53L1X 例程说明

仓库文档还附带了一个完整的 VL53L1X 测高示例(独立示例仓库 esp32-vl53l1x-test,可通过 git 工具获取),其要点如下:

  • 实现功能:通过 VL53L1X 检测到高度变化(持续 1 秒)时红灯亮起;高度恢复正常值(持续 1 秒)时绿灯亮起;
  • 可配置参数:通过 make menuconfig 设置 I2C 编号、端口号、LED 端口号;
  • 注意事项:
    1. 该例程仅适用于 VL53L1X,老版本硬件 VL53L0X 不适用;
    2. 官方标称 400 cm 测量距离为黑暗环境下测得;室内正常灯光环境下,可保证 10 ~ 260 cm 范围的有效测量;
    3. 初始化函数 vl53l1_init(VL53L1_Dev_t *) 中的部分参数需根据实际使用环境确定,仍有优化空间;
    4. 传感器安装位置应确保在检测位置正上方;
    5. 模块上电时会自动矫正基准高度,若基准高度发生变化,需重新上电重置参数。

四、SPI 驱动:PMW3901 光学流量传感器

4.1 概述与主要参数

PMW3901 是 PixArt 公司的高精度、低功耗光学导航模组,可直接获取 X-Y 方向运动信息,实现对地高度 8 cm 以上的有效测量。其主要参数如下:

参数 参数值
供电电压 (V) VDD:1.8 ~ 2.1 V;VDDIO:1.8 ~ 3.6 V
工作范围 (mm) 80 ~ +∞
接口 4 线 SPI @ 2 MHz
封装 28 管脚 COB 封装,尺寸 6 × 6 × 2.28 mm

工作电流小于 9 mA。由于传感器工作电压较低,与 3.3 V 电平的 ESP32 通信时,VDD 与 VDDIO 需要提供不同的电压(VDD 1.8 ~ 2.1 V、VDDIO 1.8 ~ 3.6 V)。

PMW3901 管脚映射

PMW3901 管脚映射(源自 docs/_static/pmw3901_pinmap.png)

4.2 上电启动流程

PMW3901MB 虽然可执行内部上电自复位,但仍建议每次上电时对 Power_Up_Reset 寄存器执行写操作。推荐的上电顺序如下:

  1. 先对 VDDIO 供电,再对 VDD 供电,两步之间延迟不应超过 100 ms,并确保供电稳定;
  2. 等待至少 40 ms;
  3. 先将 NCS 拉高、再拉低,以复位 SPI 口;
  4. 向 Power_Up_Reset 寄存器写入 0x5A(或切换 NRESET 管脚);
  5. 等待至少 1 ms;
  6. 无论运动管脚状态如何,一次性读取寄存器 0x02、0x03、0x04、0x05 和 0x06;
  7. 参照 PMW3901MB 数据手册第 8.2 节"性能优化寄存器",配置所需寄存器以实现芯片最佳性能。

4.3 掉电与退出 Shutdown 流程

通过写 Shutdown 寄存器可将 PMW3901MB 设置为 Shutdown 模式。Shutdown 模式下,PMW3901MB 只响应对上电启动指令(写 0x5A 到寄存器 0x3A)的访问,不响应其他访问操作;同一 SPI 总线上的其他设备在 NCS 管脚不冲突的情况下仍可正常访问。

退出 Shutdown 模式(de-assert)的步骤与上电流程的后半段一致:

  1. 拉高再拉低 NCS,复位 SPI 口;
  2. 向 Power_Up_Reset 寄存器写入 0x5A(或切换 NRESET 管脚);
  3. 等待至少 1 ms;
  4. 一次性读取寄存器 0x02 ~ 0x06;
  5. 配置性能优化寄存器以达到最佳性能。

4.4 源码级印证:PMW3901 驱动实现

仓库中的 pmw3901.c 完整实现了上述流程:

  • 寄存器读写通过 4 线 SPI 以 2 MHz 波特率进行:写操作将寄存器地址最高位置 1,读操作置 0,并穿插 50 ~ 500 µs 延时满足芯片时序要求(pmw3901.c);
  • pmw3901Init() 中先读取芯片 ID(0x49 或反码 0xB6)做连接校验,随后执行 Power-on Reset(写 0x3A = 0x5A)、延时后一次性读取 0x02~0x06 运动寄存器,再调用 InitRegisters() 写入全套性能优化寄存器(pmw3901.c);
  • 运动数据的读取通过 pmw3901ReadMotion() 从地址 0x16 突发读取 motionBurst_t 结构体完成,并对 shutter 字段做字节序交换(pmw3901.c)。

在 ESP-Drone 中,PMW3901 通过 Flow Deck v1/v2 模块接入,模块初始化与测试逻辑见 flowdeck_v1v2.c。

4.5 关键结构体解读

opFlow_t:光流数据聚合结构

typedef struct opFlow_s
{
    float pixSum[2];       /* 累积像素 */
    float pixComp[2];      /* 像素补偿 */
    float pixValid[2];     /* 有效像素 */
    float pixValidLast[2]; /* 上一次有效像素 */
    float deltaPos[2];     /* 2 帧之间的位移 单位:cm */
    float deltaVel[2];     /* 速度 单位 cm/s */
    float posSum[2];       /* 累积位移 单位 cm */
    float velLpf[2];       /* 速度低通 单位 cm/s */
    bool isOpFlowOk;       /* 光流状态 */
    bool isDataValid;      /* 数据有效 */
} opFlow_t;

各字段含义:

  • 累积像素:飞行器起飞后的累积像素;
  • 像素补偿:补偿由于飞行器倾斜导致的像素误差;
  • 有效像素:经过补偿后的实际像素;
  • 2 帧之间的位移:由像素转换出的实际位移,单位 cm;
  • 速度:瞬时速度,由位移变化量微分得到,单位 cm/s;
  • 累积位移:实际位移,单位 cm;
  • 速度低通:对速度做低通滤波以增加平滑性;
  • 光流状态:检查光流传感器是否正常工作;
  • 数据有效:在一定高度范围内数据才有效。

motionBurst_t:SPI 突发读取结构

typedef struct motionBurst_s {
  union {
    uint8_t motion;
    struct {
      uint8_t frameFrom0    : 1;
      uint8_t runMode       : 2;
      uint8_t reserved1     : 1;
      uint8_t rawFrom0      : 1;
      uint8_t reserved2     : 2;
      uint8_t motionOccured : 1;
    };
  };

  uint8_t observation;
  int16_t deltaX;
  int16_t deltaY;

  uint8_t squal;

  uint8_t rawDataSum;
  uint8_t maxRawData;
  uint8_t minRawData;

  uint16_t shutter;
} __attribute__((packed)) motionBurst_t;

各字段含义:

  • motion:运动信息,可依据不同位判断帧检测(frameFrom0)、运行模式(runMode)和运动检测(motionOccured);
  • observation:用于检测 IC 是否出现 EFT/B 或 ESD 问题,传感器正常工作时读取值应为 0xBF;
  • deltaX / deltaY:传感器检测到的图像 X/Y 方向运动信息;
  • squal:运动信息质量,即运动信息的可信度;
  • rawDataSum:原始数据求和,可用于对一帧数据求平均;
  • maxRawData / minRawData:原始数据的最大值与最小值;
  • shutter:实时自动调整的曝光值,用于确保平均运动数据保持在正常可操作范围内;可搭配 squal 判断运动信息是否可用。

4.6 编程注意事项

官方文档给出了光流驱动的关键工程经验:

  • 若连续 1 秒内光流数据都为 0,说明出现故障,需要挂起光流任务等处理;
  • 传感器镜头必须朝下安装。由于地面位置固定,根据相对运动,传感器采集的位移数据与飞机实际运动方向相反;
  • 只有在定高模式测试稳定后才能进入定点模式:精确的高度信息用于确定图像像素与实际距离的对应关系;
  • 手动测试倾角补偿,实现飞行器有一定倾角时传感器输出基本不变;
  • 有了倾角补偿和运动累积像素即可得到实际累积像素,进而计算:
    • 2 帧之间变化像素 = 实际累积像素 − 上次实际像素;
    • 2 帧之间的位移变化 = 2 帧之间变化像素 × 系数。系数限制:高度小于 5 cm 时光流无法工作,系数应置 0;
    • 对位移变化积分得到四轴到起飞点的位移;微分得到瞬时速度;对速度做低通增加平滑性、做限幅增强数据安全性。

4.7 从光流到水平定点控制

通过光流获得四轴的位置与速度信息后,其下游数据流为:

  1. 位置与速度信息融合加速度计数据(对应 state_estimator.c 的状态估计,仓库实现见 estimator.c),得到估测位置和速度;
  2. 估测位置和速度参与 PID 运算,用于水平方向位置控制,位置环与速度环的 PID 处理可参考 position_controller_pid.c;
  3. 最终实现水平定点(position-hold)控制。

五、总结

ESP-Drone 的传感器驱动体系覆盖了姿态、高度与水平位移三类感知需求:

  • MPU6050(I2C):以 1 kHz 中断驱动批量读取,提供六轴惯性数据,是姿态稳定的基石;本文梳理了从 PWR_MGMT_1 电源管理、DLPF 滤波、SMPLRT_DIV 采样分频到 GYRO/ACCEL 量程配置的完整寄存器链路;
  • VL53LXX(I2C):以 ToF 原理提供厘米级高度测量,重点掌握初始化顺序、轮询/中断两种测距循环、以及光罩/盖板场景下的校准流程;
  • PMW3901(SPI):以 4 线 SPI 提供 X/Y 位移与速度,是水平定点控制的数据源头,需严格遵守上电时序,并理解"位移方向与机体系相反"等关键编程约束。

结合仓库源码(mpu6050.c、sensors_mpu6050_hm5883L_ms5611.c、vl53l1x.c、pmw3901.c)阅读,可以快速完成传感器移植、驱动调试与定高定点功能的落地。更多配套信息可继续阅读仓库文档目录下的 gettingstarted.rst 与 hardware.rst。

登录后查看全文
esp-drone