ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

VL53L1CB多区域扫描:SPAD阵列与ROI实现多目标测距

VL53L1CB多区域扫描:SPAD阵列与ROI实现多目标测距 简介面向VL53L1CB ToF传感器开发者的多区域扫描模式资源包聚焦飞行时间测距中的视场划分与多区域并行测量。该模式将FoV划分为多个独立ROI每个区域单独测距最高支持16个区域同时工作并可按需扩展至169个适合投影机梯形校正、多点距离检测、环境映射等需要广域监控或多目标追踪的应用。资源包为zip压缩格式体积约6.91MB内容围绕多区域扫描的配置与实现展开可配合CSDN文字教程与B站教学视频对照学习。目前已有239人浏览学习。通过该资源开发者可以系统理解ROI设置、区域测量结果独立读取的完整流程获得针对多区域扫描调试的代码思路与工程经验缩短同类ToF项目开发周期。对于正在从单点测距转向多区域应用的开发者这套资源能提供直接可用的参照。1. 多区域扫描一颗ToF传感器解决多目标测距在ToF测距应用中最常见的不满不是精度不够而是视野太窄。VL53L1CB这类单点ToF传感器默认只输出一个距离值视线里出现第二个目标时只能靠切换ROIRegion of Interest感兴趣区域位置来逐个测量。多区域扫描模式正是针对这个场景设计的把一个SPAD阵列拆成多个子区域按照顺序在不同位置做测距把“单点测距”扩展成“空间扫描”。这套能力在避障机器人、屏幕方向检测、人体存在感应等需要tof雷达式感知的场景里非常实用因为它不需要额外的机械旋转部件就能获得多个方向的距离信息。本文会从VL53L1CB的多区域扫描原理入手解释ROI、SPAD阵列和测距状态机的关系再给出在嵌入式环境下跑通多区域扫描的最小实现最后把扫描时序、距离稳定性、串扰标定这些实际工程里容易翻车的点展开讲。对正在做tof测距产品选型或已经踩进多区域坑里的开发者这篇文章可以直接当参考笔记用。2. VL53L1CB测距原理与ROI在扫描中的角色2.1 SPAD阵列与ROI的映射关系VL53L1CB内部有一个SPAD单光子雪崩二极管阵列物理上支撑了整个测距区域。可以认为传感器能“看见”的范围就是这个SPAD阵列的视场。单区域测距模式下驱动库会把整个阵列或者一个较大的ROI作为接收窗口输出一个测距结果。但ToF测距的物理分辨率是有代价的SPAD阵列接收到的有效光子是叠加在同一个histogram上的。如果把整个视场看作一个区域那所有反射信号都会落在同一个计数分布里驱动只能取最显著的那个峰值加以换算。对多目标场景来说这个“单峰假设”在近距离有遮挡时容易被破坏。多区域扫描模式的核心是把SPAD阵列的ROI缩小让传感器在不同的ROI位置间依次读取把整片视场分成若干个独立测距窗口。例如典型配置把SPAD阵列划分为左上、右上、左下、右下四个区域系统轮流让这四个区域“出头”做测距最终一次扫描周期里就能输出四个方向的距离。这种“分时复用”的思路在tof雷达固件里非常常见本质是用时间换空间分辨率。2.1.1 关键参数ROI_Width、ROI_Height与ROI中心坐标在寄存器层面ROI大小和位置是通过两组参数控制的ROI宽高决定子区域在SPAD阵列上占多少像素中心点决定子区域偏移到哪。这里的坐标系统以SPAD阵列的左上角为原点横纵坐标的单位是SPAD像素。配置示例如果要把ROI缩到8×8像素在VL53L1CB驱动中常见做法是调用vl53l1_set_roi()这类接口传入宽、高、中心坐标。中心坐标的计算公式一般近似为x_center 8 width / 2左移4位后填充寄存器实际开发时更稳妥的做法是直接查表用官方驱动里的常量定义。2.2 测距状态机在多区域切换中的行为每片区域的测距都会触发一次完整的测距序列从初始化、发射激光、接收回波、生成直方图、提取峰值到计算出距离。多区域模式本质上就是状态机不断循环执行这个小序列每个周期ROI参数不一样。2.2.1 连续模式与软件触发的取舍VL53L1CB支持两种常见的运行方式硬件连续测距模式和软件触发模式。多区域扫描更常见的实现是在单次测距模式下由主控MCU在两次测距之间修改ROI参数再触发下一次测距。选择单次触发而不是连续模式的理由有两点。第一修改ROI寄存器必须在测距空闲状态进行连续模式下测距状态机会自动跳转改寄存器的时机不好对齐第二多区域扫描本身是顺序执行的软件触发能保证每次测距前ROI已经落到正确位置。硬要硬切换ROI的前提是先调用停止测距接口等待设备回到空闲态再改寄存器否则改不生效。3. 多区域扫描的寄存器配置与最小可用代码3.1 驱动结构概览在开始写代码前先明确 VL53L1CB 驱动中与多区域扫描相关的三个层次API层负责命令和状态核心层负责I2C寄存器的封装应用层才是我们需要改的地方。官方STSW-IMG007驱动里VL53L1_SetROI()、VL53L1_StartRanging()和VL53L1_GetDistance()这三个接口串起来就是一次最基本的多区域扫描循环。下面是一个在裸机环境下用I2C操作VL53L1CB完成多区域扫描的最小流程。#include vl53l1_api.h #define MAX_REGIONS 4 typedef struct { uint8_t roi_width; uint8_t roi_height; uint16_t roi_x_center; uint16_t roi_y_center; } tof_roi_cfg_t; static tof_roi_cfg_t region_cfg[MAX_REGIONS] { {8, 8, 24, 24}, /* 左上 */ {8, 8, 40, 24}, /* 右上 */ {8, 8, 24, 40}, /* 左下 */ {8, 8, 40, 40} /* 右下 */ }; int tof_multi_region_scan(VL53L1_Dev_t *dev, uint16_t *distances) { VL53L1_Error status; VL53L1_RangingMeasurementData_t measure; for (int i 0; i MAX_REGIONS; i) { /* 切换到目标ROI */ status VL53L1_SetROI(dev, region_cfg[i].roi_width, region_cfg[i].roi_height, region_cfg[i].roi_x_center, region_cfg[i].roi_y_center); if (status ! VL53L1_ERROR_NONE) { return -1; } /* 单次测距 */ status VL53L1_StartRanging(dev, VL53L1_DEVICESCHEDULERMODE_SINGLE); if (status ! VL53L1_ERROR_NONE) { return -2; } /* 等待数据就绪典型等待时间为30~100ms */ do { status VL53L1_WaitMeasurementDataReady(dev); } while (status ! VL53L1_ERROR_NONE); status VL53L1_GetRangingMeasurementData(dev, measure); if (status ! VL53L1_ERROR_NONE) { return -3; } distances[i] measure.RangeMilliMeter; VL53L1_ClearInterruptAndEnableMeasurement(dev); } return 0; }这段代码的逻辑很直白先用结构体数组把四个区域的位置描述出来然后在循环里依次执行“设ROI → 单次测距 → 等数据 → 读取 → 清中断”。多区域扫描的耗时和区域数量线性相关四区域一轮大概在120到400毫秒之间具体取决于测距预算的设置。3.1.1 ROI中心坐标的换算细节示例中的roi_x_center参数使用的是官方API的原始数值这个数和SPAD坐标轴有个线性映射关系。官方驱动里ROI中心点的有效范围通常是16到48横坐标、12到44纵坐标并不是任意的SPAD下标都能用。设置之前最好调用VL53L1_GetSPADInfo()读取当前器件支持的SPAD数量再按实际范围计算中心点。如果不加这一步常见问题就是把中心点写到边界之外驱动不会报错但测距数据会出现整片偏移甚至目标丢失。3.2 关键寄存器字段说明上面API调用背后真正起作用的是一组寄存器字段寄存器字段功能典型值备注ROI_CONFIG__MODE_ROI_CENTRE_SPADROI中心点X坐标24/40高位为X低位为YROI_CONFIG__USER_ROI_CENTRE_SPAD用户ROI中心点24/40实际生效中心ROI_CONFIG__USER_ROI_REQUESTED_GLOBAL_XY_SIZEROI宽高0x88高4位为height-1低4位为width-1RANGE_CONFIG__VCSEL_PERIODVCSEL周期12~18影响最大测距距离SYSRANGE__TIMING_BUDGET测距预算0x001420ms对应值其中ROI_CONFIG__USER_ROI_REQUESTED_GLOBAL_XY_SIZE字段是8位寄存器高4位表示高度低4位表示宽度且寄存器里存的是像素数减1。比如要设8×8区域写入0x77。提示修改ROI后必须等待至少1ms再触发测距否则I2C写入和内部状态机切换可能存在竞争。3.3 初始化阶段必须做的额外配置多区域扫描和普通单区域测距的初始化流程大部分重叠但有三个点是扫描模式特有的。第一需要把VL53L1_SetDistanceMode()设置为长距离模式否则ROI缩小后信号衰减更快近距离还行、远距离直接测不到。第二调用VL53L1_SetMeasurementTimingBudget()时预算要按单个区域设置不是按整个扫描周期设置。第三如果固件里之前跑过连续测距必须先VL53L1_StopRanging()再切换到单次模式否则寄存器的中间状态会污染下一次扫描。4. 多区域扫描中的测距预算与数据稳定性4.1 Timing Budget的选择决定扫描帧率多区域扫描里最容易忽视的参数是测距预算Timing Budget。VL53L1CB官方库提供了几个预设值从短距离的10ms到远距离的100ms。预算越大信号积累越多距离越稳定但单个区域耗时也越长。四区域扫描的目标帧率可以按下面公式粗略估算扫描帧率 1000 / (区域数量 × 单区域测距预算)。如果单区域预算设为20ms四区域耗时接近80ms算上ROI切换和I2C读写实际帧率在10到12Hz左右。这个水平对避障、风扇跟随这类tof雷达场景够用如果目标帧率要超过30Hz要么减小预算到10ms要么减少区域数量。4.1.1 不同预算下的数据表现以墙面测距为例在0.5米到2米范围内20ms预算下距离抖动一般在±1到3厘米之间预算降到10ms抖动可能扩大到±5厘米。对扫描模式来说这是需要接受的折中扫描模式下每片区域内激光照射时间天然变短数据方差本来就比单区域模式大。如果网上资料同时提到“多区域扫描”和“高精度”基本都指的是牺牲帧率换精度。工程上一般用双模式并行无人靠近时用低预算做快速扫描检测到目标后切到高预算单区域模式做精确测距。4.2 数据处理中值滤波与区域间跳变抑制多区域扫描输出的是时间上有间隔的多路距离序列直接送控制逻辑会导致两个问题。第一相邻两次扫描同一区域的数据突变会很大第二四个区域的距离值不是同一时刻采样的直接做空间判断会把时间差当成距离差。我一般会在驱动层之上加一个针对通道的中值滤波器每个区域维护一个长度为5的滑窗取中间值作为有效输出。这样可以把单点毛刺压掉代价是引入约2到3帧的反应延迟。#define WINDOW_SIZE 5 typedef struct { uint16_t buf[WINDOW_SIZE]; uint8_t idx; } region_filter_t; static region_filter_t filters[MAX_REGIONS]; uint16_t tof_median_filter(uint8_t region_id, uint16_t new_value) { region_filter_t *f filters[region_id]; uint16_t tmp[WINDOW_SIZE]; /* 写入新样本 */ f-buf[f-idx] new_value; f-idx (f-idx 1) % WINDOW_SIZE; /* 拷贝并排序 */ memcpy(tmp, f-buf, sizeof(tmp)); for (int i 0; i WINDOW_SIZE - 1; i) { for (int j 0; j WINDOW_SIZE - 1 - i; j) { if (tmp[j] tmp[j 1]) { uint16_t t tmp[j]; tmp[j] tmp[j 1]; tmp[j 1] t; } } } return tmp[WINDOW_SIZE / 2]; }这段代码维护的是一个环形缓冲每来一个新距离值就覆盖最老的样本然后对整个窗口做一次排序取中位数。WINDOW_SIZE为5时单通道处理耗时可忽略不计但距离输出的平滑度提升明显。提示中值滤波适合滤掉脉冲噪声但对持续偏移没有效果。多区域模式下持续偏移通常来自ROI边缘的串扰需要靠下面的标定方法处理。4.3 串扰与ROI边缘效应把ROI缩小后发光的VCSEL垂直腔面发射激光器和接收SPAD之间在光学路径上不再是完全匹配的。原本大ROI下被平均掉的内部反射串扰在小ROI模式下会变成明显的距离偏小问题尤其是近距离墙面。处理方式有两个方向。一是做遮挡板设计和光学隔离这对结构设计有要求二是利用VL53L1CB的VL53L1_SetXTalk()接口注入校准后的串扰值。开发阶段建议先用官方EVK在不同距离下采集数据拟合一条串扰补偿曲线固化到固件里。需要注意的是串扰校准受温度影响较大温度漂移超过10度后建议重新校准。4.4 扫描模式下的信号率监控多区域扫描一个很重要的调试习惯是看信号率而不仅仅是看距离值。RangingMeasurementData_t里的SignalRateRtnMegaCps字段表示返回信号强度。四个区域如果信号率差异超过一个数量级说明ROI位置没有对准或者某一个区域正好对着吸光材质。信号率还有一个用途距离值跳变时信号率能帮忙区分是真实目标还是串扰。真实目标靠近时信号率会同步上升串扰引起的距离变化信号率基本不变。这个特征在算法层可以做简单的真伪判断避免误触发。5. 非对称ROI布局与扫描性能验证5.1 非对称ROI布局实现横向更宽的扫描条带多区域扫描不限于2×2方块同样可以做成1×4的横条模式。这在检测走廊多个门洞的场景里更适用。把四个区域排成一行每个区域尺寸调整为16×4像素中心点沿X轴方向偏移。关键是用上面提到的ROI_CONFIG__USER_ROI_REQUESTED_GLOBAL_XY_SIZE寄存器高度位写3代表4像素宽度位写15代表16像素。中心点的Y坐标保持一致X坐标每次增加8。这种布局在水平方向上的覆盖范围近似等于整个视场宽度但垂直方向只扫一条窄线适合安装位置较高的设备做人员经过检测。反过来如果想扫垂直方向就交换宽高和X/Y的偏移方向。5.2 用RangeStatus判断多区域数据的可用性多区域模式下一个容易踩坑的地方是直接信任RangeMilliMeter字段。VL53L1CB驱动返回的测量结果里有个RangeStatus字段当它不等于0时距离值的可信度是有问题的。区域切换过程中因为ROI中心和SPAD数量变化信号完整性标志位会有波动把这部分数据筛掉比事后修复更划算。uint8_t tof_is_valid_measurement(VL53L1_RangingMeasurementData_t *m) { /* 0: 有效1: 信号过低2: 相位噪声过大4: 包装目标 */ if (m-RangeStatus ! 0) { return 0; } /* 距离明显越界时直接丢弃 */ if (m-RangeMilliMeter 10 || m-RangeMilliMeter 4000) { return 0; } /* 信号率过低的数据不可靠 */ if (m-SignalRateRtnMegaCps 1.0f) { return 0; } return 1; }这个函数放在数据进入滤波器之前调用。我的经验是多区域模式下无效数据的比例在5%到15%之间浮动信号率阈值设到1.0基本能挡掉大部分瞬时异常。如果无效比例持续超过20%优先去查ROI中心坐标是否落在有效范围内其次是检查VCSEL的驱动电流配置。5.3 验证多区域扫描时序与空间一致性的手段开发板联调阶段验证方法比代码本身更重要。推荐两种手段并行一是用示波器抓VL53L1CB的GPIO1中断引脚正常工作时应该看到周期性的等间隔脉冲四个脉冲为一组组与组之间的间隔就是扫描周期二是让目标物只出现在某一个区域内确认其他区域的距离读数不发生联动变化。第二种验证能暴露ROI切换时序的问题。如果出现联动多半是因为连续模式没有关干净或者ROI寄存器写入后没有等足够时间就触发了测距。此时回到第2章提到的单次触发流程在每个区域测距完成后主动加一个1到2ms的延时看联动现象是否消失。最后一点容易被忽略四个区域的信号率如果差异很大不需要调放大增益更可能是ROI中心坐标在写寄存器时存在偏移。对照驱动里的VL53L1_GetCalibrationData()输出实际中心点会有一个固定偏移量修正后再看信号率是否落到同一水平线上。多区域扫描实现难度不在原理而在于把ROI、预算、数据筛选和标定四件事各自做到位每一件都能单独验证时整个扫描链路距离稳定就不远了。本文还有配套的精品资源点击获取
返回列表