☰
毫米波雷达人员计数实验lab0011:点云处理与调参实战
2026/10/17 7:22:29 网站建设 项目流程

简介:这套TI毫米波实验资源对应实验编号lab0011-pplcount,主题是利用毫米波传感器进行人数计数,面向物联网、智能硬件及无线感知方向的开发者与学生,适合想了解毫米波雷达配置、目标检测与数据处理的人群。压缩包共309个文件,大小14.67MB,内容涵盖C/H源码、JSON及CFG传感器配置、GUI工程、快速启动指南、docs文档目录等,可支撑从硬件配置到应用开发的全流程实践。目前已有360人学习。通过该实验可深入理解毫米波传感器的参数设置、信号处理与目标跟踪算法,并动手完成实时人数统计的完整方案;同时,工程中规范的Git子模块配置与目录结构,也为团队协作和代码管理提供了良好范例。配套资料还包括多种格式的工程文件与可视化资源,便于对照学习与实际部署。

1. 毫米波雷达人员计数实验 lab0011:从点云到人数统计的完整链路

毫米波(mmWave)雷达做人员计数,这几年在智能楼宇、门店客流统计和会议室占用检测里出现得越来越频繁。lab0011 是 TI 毫米波系列实验里的一个典型工程,pplcount 就是 people count 的缩写,目标非常直接:把雷达回波生成的稀疏点云,一步步变成“有几个人经过、走进了哪个区域”的可靠数字。它不依赖摄像头成像,所以天然规避了隐私争议,也不受灯光和遮挡影响,适合装在走廊口、会议室门口这种固定位置。我给某个做区域占用检测的项目组拆过这套资源,后面几章把链路、参数和实际踩过的一些坑完整过一遍。新手可以从编译烧录跟起,熟手可以直接跳到第四章看排错。

2. 算法链路与关键参数:CFAR、聚类、跟踪器怎么配合不翻车

把整套实验拆开看,其实链路不算复杂,难点在每一步的参数配合。点云本身是稀疏的,不像图像有丰富纹理,单纯靠“看到点”去数人一定会翻车,必须靠完整的信号处理和跟踪逻辑把点云变成稳定的轨迹,再从轨迹里提取计数结果。

2.1 为什么是毫米波点云而不是摄像头识别

先解决选型问题。摄像头做人员计数确实成熟,但它在实际部署里有两条硬伤:一是隐私合规压力,很多商业场所不愿意让镜头对着公共区域,装了也容易被投诉;二是光照鲁棒性,逆光、暗光、阴影都会让识别率明显下降。毫米波雷达输出的是三维点云坐标和多普勒速度,不构成图像,隐私压力小很多,而且它主动发射电磁波,不受环境光照影响。

相比激光雷达,毫米波在成本和安装便利性上也有明显优势。激光雷达点云确实更密,但价格高出一个量级,而且机械结构在长期通电场景下可靠性存疑。毫米波固态器件寿命长,直接输出速度信息,这对区分“人是否在移动”特别有用。代价是点云非常稀疏,角度分辨率有限,两个人贴得近时可能分不开,所以后续算法要做得足够小心,这就是 lab0011 这套实验存在的意义。

2.2 链路四步:从距离-多普勒图到目标轨迹

整套处理流程可以拆成四个阶段,每个阶段都有对应参数,我按实际处理顺序过一遍。

第一步是距离和速度维的 FFT。发射的 chirp 信号经目标反射后,接收端混频得到中频信号,再做距离维 FFT 和速度维 FFT,最终形成一张距离-多普勒图(Range-Doppler Map)。这张图上每个峰值点都代表“某个距离上存在一个以某种速度运动的目标”。这一步的配置决定了雷达的最大探测距离、距离分辨率和速度分辨率,直接影响后续点云的分布密度。

第二步是 CFAR 检测。距离-多普勒图上噪声底是随距离变化的,用固定阈值必然出问题,所以一般用 CFAR(恒虚警检测)做自适应阈值选峰。CFAR 阈值设得太严,人身上的反射点会被滤掉大半;设得太松,墙、金属推车、风扇这类反射源全变成噪点。常见做法是先让雷达对着空场地跑一段时间,记下底噪水平,再在这个基础上加 10~15 dB 的余量作为初始阈值。

第三步是角度估计和点云生成。利用多根接收天线的相位差,估算每个检测点的水平角和俯仰角,把距离-多普勒图上的二维峰值投影成三维空间点。这一步输出的是每帧的一批点云,每个点带 x、y、z 坐标和速度值。室内人员计数场景里,常见做法是把最大探测距离限制在 6~8 米,太远的位置点云密度急剧下降,角度误差也变大,后面聚类和跟踪都会跟着翻车。

第四步是聚类、跟踪和计数。同一个人的身体表面会反射出多个点,需要按空间距离聚成一个簇,再用跟踪器把连续帧的簇关联成一条稳定的轨迹。跟踪器一般用卡尔曼滤波,预测目标下一帧位置,再和当前帧的检测做匹配。每个轨迹分配一个唯一 ID,连续多帧没有匹配后 ID 被注销。计数逻辑就是检测轨迹是否跨过了预设的计数区域或线段,跨过了就给计数器加一。

# 关键配置示意,具体数值以实验包自带的 cfg 文件为准 # frameCfg: startChirp, endChirp, loops, bursts, 周期配置 frameCfg 0 1 0 50 0 0 1 # 距离分辨率约 4cm,最大探测距离需要结合采样点数计算 # 低于 6m 时点云密度最好,超过 8m 后基本不建议用于计数

这一小段只是示意帧结构,实际实验包里 cfg 文件包含 profileCfg、chirpCfg、frameCfg 等多行配置,每个数字改动都会影响点云形态。我拆的时候习惯一次性只改一个参数,改完跑一组数据对比,否则几个参数互相影响,很难定位问题。

2.3 参数表:阈值、聚类半径、目标生命周期的最佳实践

lab0011 工程里最关键的参数集中在下面这张表,这也是我调参时反复来回改的几个点:

参数常见取值影响
CFAR 阈值底噪 + 10~15 dB太低噪声点多,太高漏检严重
最大探测距离6~8 m超过 8m 点云稀疏,跟踪不稳定
帧率10~20 fps帧率越高轨迹越稳,但 CPU 占用翻倍
聚类半径0.3~0.5 m太小一个人被拆成多簇,太大并排的人被合并
目标生命周期0.5~2 s太短则短暂遮挡后重复计数
静止目标抑制开启去掉墙和家具的静态反射点,降低误报

聚类半径这个参数最值得反复调。人的肩宽大约 0.5 米,雷达照射人体时反射点分布在躯干和四肢,在俯视安装模式下这些点会形成一个小簇。如果聚类半径大于 0.5 米,两个并排行走的人就会落进同一个簇,计数直接少算一个。但如果小于 0.3 米,同一个人的手臂和躯干又会被拆成两个簇,跟踪器把它当两个人处理。实验包默认值一般是 0.5 米左右,我在实际项目里通常压到 0.3 米,同时把单帧 chirp 数增加一些来补充点云密度,效果会好不少。

目标生命周期是另一个容易忽略的坑。人从雷达正下方经过时,雷达俯视角度大,反射点可能瞬间消失一两帧,跟踪器如果立刻注销 ID,人走出探测区会被当成一个新目标再计数一次。我一般在 0.5 米聚类半径下配合 1.5 秒左右的生命周期,短时间遮挡不会丢 ID,计数稳定很多。

3. 复现 lab0011:环境准备、编译烧录与串口数据解析

这一章直接进入操作。lab0011 的复现流程不算复杂,但环境版本对齐是第一道坎,我见过不少同好在编译阶段就卡住,基本都是 SDK 版本和配套工程对不上导致的。

3.1 硬件与 SDK 版本:怎么选、怎么对应

先明确硬件选型。室内人员计数场景优先选 60GHz 频段的毫米波评估板,天线阵列密度更高,点云在近距离的分辨率表现更好,功耗也可控。77GHz 频段主要面向车载,用在人员计数上不是不行,但近距离盲区大,功耗也偏高,室内固定安装场景没必要选它。

开发环境上,需要安装 TI 官方的集成开发环境、毫米波软件开发套件(SDK)和对应的烧录工具。lab0011 实验包一般会注明它基于哪个 SDK 版本开发,这是最关键的对应关系。版本不对最常见的问题是头文件路径找不到或者 API 函数签名不匹配,编译时爆出一堆看不懂的宏定义错误。我通常会把实验包自带的说明文档里列出的版本号先确认清楚,再决定安装哪个 SDK,不盲目追新版本。

# 检查本机 SDK 路径是否已配置到环境变量 export SDK_INSTALL_PATH=/opt/mmwave_sdk_xx echo $SDK_INSTALL_PATH

3.2 编译烧录流程:从导入工程到板子跑起来

工程导入这一步相对直观:打开 IDE,把实验包里的工程目录导入工作区,然后在工程属性里确认 SDK 路径指向正确位置。编译前还要确认目标芯片型号和评估板型号匹配,工程属性里会有一个设备型号选项,选错了编译虽然能通过,烧录进板子后大概率跑不起来。

编译命令可以直接用 IDE 自带的构建按钮,也可以在工程根目录下用脚本构建。实验包一般会提供 makefile 或构建脚本,命令行方式更适合批量调参的场景,改完配置直接重建镜像。

# 在工程根目录下执行构建(具体命令以实验包脚本为准) make clean make SDK_INSTALL_PATH=/opt/mmwave_sdk_xx # 编译成功后在工程目录下生成 .bin 镜像文件

编译成功后,用官方烧录工具把 bin 文件烧进评估板。连接板子的 USB 串口,打开烧录工具选择对应的串口号和 bin 文件路径,点击烧录即可。烧录完成后板子会自动复位运行固件,这时打开串口助手,能看到雷达启动和配置加载的日志信息。如果串口没有输出,先检查板子供电和 USB 驱动,再检查烧录工具里选择的串口号是否被其他程序占用。

3.3 串口输出与点云可视化:确认数据是合理的

固件启动后,雷达就开始按配置的帧率采集数据并通过串口输出。输出数据不是 ASCII 文本,而是二进制 TLV 格式,需要写脚本解析。链路的数据结构大致是:每帧数据有一个帧头,后面跟着若干 TLV,包含检测点云、目标列表和计数器值等。

import serial import struct # 串口参数要与 cfg 文件中配置的波特率保持一致 ser = serial.Serial('COM10', 921600, timeout=0.1) # 帧头结构:magic + version + total_len + frame_id,按 SDK 头文件定义 header_fmt = '<HHI' header_size = struct.calcsize(header_fmt) while True: buf = ser.read(header_size) if len(buf) < header_size: continue magic, version, frame_id = struct.unpack(header_fmt, buf) if magic != 0xA1B2: # 示例魔数,具体以头文件为准 continue # 继续读取该帧的 TLV 数据,按点云和 track 两种类型分别解析 # 点云解出 (x, y, z, velocity, snr),track 解出 (id, 位置, 速度, 生命周期)

这段脚本只做了帧头读取的示意,完整的 TLV 解析还要按类型逐一读取长度和内容。写脚本时最容易错的点是字节对齐和大小端,建议先抓一段原始数据用十六进制对照 SDK 头文件里的结构体定义,逐字节核对一遍再批量解析。可视化方面,TI 官方配套工具可以直观看到点云和轨迹,把它和串口脚本的输出对照着看,能快速确认数据是否合理。

4. 避坑指南:lab0011 复现中的高频问题与排查记录

这部分是血泪经验。复现 lab0011 的过程中,我遇到过不少看上去很“玄学”的问题,最后定位下来基本都是参数边界或安装细节导致的。按现象、原因、解决三个维度整理几条典型的,方便后来人直接对照。

4.1 点云层的问题:静态杂波、稀疏点云、安装角度

现象一:板子刚通电,人还没走进探测区域,点云画面里已经有几十个噪点在飘,墙、柜子、金属门框全都出现在点云里。人走过去时轨迹完全淹没在这些杂波里,计数基本没法用。

原因:静态杂波抑制没有生效,或者雷达的自校准没有在安装后重新执行。毫米波雷达对静止物体非常敏感,墙面反射和人体反射在距离-多普勒图上可能同样强度,不把静止分量滤掉,动目标检测就会失灵。

解决:在配置中开启静态杂波消除功能,并设置合适的静止目标抑制阈值。另外,每次更换安装位置后,都要让雷达在无人场景下重新采集一版底噪做校准,不能偷懒沿用出厂默认。

现象二:人明明站在雷达前方 2 米处,点云却只有三四个点,跟踪器输出时断时续,ID 频繁自动切换,计数结果忽大忽小。

原因:CFAR 阈值定得太高,大量低信噪比的反射点被滤掉;或者最大探测距离设置过大,算法把大量计算资源浪费在远距离稀疏区域,近处点云密度反而没保障。

解决:把 CFAR 阈值从底噪 + 20 dB 降到 + 10 dB 左右,同时把最大探测距离从默认的 10 米缩到 6 米,近处点云密度会有明显改善。我一般还会让板子对着空旷区域跑几分钟,观察点云的平均数量,低于每帧 20 个点就该怀疑阈值有问题。

现象三:雷达斜装在墙面上方,人从下方走过时,点云在三维空间里被拉成一条竖直的长条,聚类后同一个人的身体被切成上下两段,轨迹频繁分裂。

原因:安装角度导致人身体在雷达视角下产生高度方向的大范围投影,单纯按三维欧氏距离聚类会把同一目标拆成多个簇。

解决:把聚类操作前的点云投影到水平面上,再按二维距离聚类,或者把聚类半径放宽到 0.5 米量级同时取消高度维的严格限制。实测中投影到平面再聚类是最稳定的做法,计数逻辑也简单很多。

4.2 计数层的问题:重复计数、并行人漏计、静止目标丢失

现象四:人从门内走到门外,计数器不是加一次,而是加了两次甚至三次。

原因:目标生命周期设得太短。人走到门框位置时雷达视角切换,反射点短暂消失一帧,跟踪器立刻判定目标丢失并注销 ID,下一帧人出现在门另一侧时被当成全新目标重新计数。

解决:把目标生命周期从默认的 0.5 秒延长到 1.5~2 秒。这个值的物理含义是目标连续消失多少帧后判定为真正离开,而不是简单地跟丢。调完之后重复计数问题基本消失。

现象五:两个人并肩从雷达前方走过,输出人数是 1,跟踪器从头到尾只报了一个 ID。

原因:聚类半径偏大,两个人的点云在空间上连成一坨,被合并成一个簇。跟踪器拿到的是合并后的单目标,自然不会拆出两个 ID。

解决:把聚类半径压到 0.3 米以内,并增加单帧 chirp 数提升点云密度。还要注意并排行走的两个人速度通常不完全一致,跟踪器速度维的分辨率足够时能辅助拆开两个目标。

现象六:人站在会议室某个位置连续几分钟不动,跟踪器丢失了目标,等人再迈步时系统把它识别成新目标重新计数,区域人数统计虚高。

原因:静止目标抑制把完全静止的反射点滤掉了,跟踪器失去输入,生命周期倒计时结束就注销了 ID。人再次移动时重新检测,自然被当成新人。

解决:针对区域占用场景,关闭静止目标抑制或者为完全静止的目标单独保留“驻留”轨迹。更彻底的办法是改变检查逻辑,不是靠“被移动检测到”来计人,而是靠轨迹 ID 是否保持在区域内来判断。

5. 把 lab0011 改成区域占用统计:一个常用扩展与验证技巧

lab0011 默认是过线计数,也就是统计有多少人先后穿过某条线或某个门。但实际项目里经常需要的是“某个区域内当前有多少人”,像会议室占用检测、工位空闲判断之类。这个扩展其实不难,核心思路是把“过线触发计数器”改成“每帧统计活动轨迹 ID 在区域内的数量”。

修改方法是这样的:拿到跟踪器输出的每帧目标列表后,先用置信度和生命周期过滤掉不可靠的目标,再判断轨迹坐标是否落在预设的多边形区域内,最后对区域内唯一的轨迹 ID 计数。

def count_occupancy(track_list, zone_polygon): active_ids = set() for t in track_list: # t.confidence 是目标置信度,t.lifetime 是目标存活帧数 if t.confidence < 0.5 or t.lifetime < 5: continue if point_in_polygon(t.x, t.y, zone_polygon): active_ids.add(t.id) return len(active_ids)

这份伪代码里有两个关键点:置信度和生命周期的阈值不能一刀切,需要结合现场点云稀疏程度调整;点是否在区域内用射线法判断即可,性能完全够用。改完之后配合参数表里的帧率设置,把输出频率降低到 5~10 fps 做验证,人工记录每帧真实在场人数与系统输出的 occupancy 做对比,连续测几组就能看出参数是否合理。

验证时我习惯把每一帧的轨迹 ID 日志一起打出来,而不是只记录最终人数。之前做某个项目时就吃过这个亏:最终计数看着对,但实际是两个目标交替补位造成的假正确。加日志回放之后才发现跟踪器在 1 秒内连续丢 ID 又建 ID。从那以后,我每次改参数都强制走一遍“记录轨迹日志 → 离线回放 → 对照真实人数 → 再改参数”的流程,不再直接信最终数字。这套方法在 lab0011 上同样好用,希望帮到你少走一段弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询