☰
TI毫米波雷达人数统计实战:lab0011-pplcount从点云到人数输出全链路解析
2026/10/21 3:08:41 网站建设 项目流程

简介:这份资源是TI毫米波实验lab0011-pplcount的完整工程包,面向学习毫米波雷达与嵌入式开发的工程师及学生,用于实践基于毫米波传感器的人数统计方案。压缩包共309个文件、约14.67MB,以C源码与头文件为核心,配合大量工程配置、目标文件、图像素材及PDF文档,覆盖硬件配置、信号处理与GUI开发等环节。其中配置文件定义传感器频率、增益与采样率等参数,快速启动指南与docs文档帮助理解实验流程,项目主体代码实现检测阈值设定、目标跟踪与滤波等算法,GUI部分则提供交互式数据监控界面。目前已有360人学习下载,适合希望掌握毫米波传感器配置、数据处理与实时人数统计的读者参考,也可为智能交通、安防监控等场景的二次开发提供基础。

1. 毫米波雷达做人数统计:lab0011-pplcount 到底在解决什么问题

第一次拿到 lab0011-pplcount 这个实验名,很多人会以为它只是个点云可视化的 Demo,跑起来看到一堆散点就完事了。实际上它要解决的是一个更硬核的问题:用 TI 毫米波雷达的原始回波,在没有人脸、没有摄像头、不依赖可见光的条件下,稳定输出一个区域内的人数。这件事在会议室占用检测、电梯轿厢人数限制、公共区域人流统计里都是刚需,因为摄像头方案有隐私争议,红外方案又受环境温度干扰严重。

lab0011-pplcount 这个实验名里的 pplcount 就是 people counting 的缩写,它属于 TI 毫米波雷达工业工具箱里比较典型的一类应用实验。它跑通之后你能得到的不只是点云,还有聚类后的目标簇、每个簇的质心坐标,以及最终的人数估计值。适合谁看?适合已经拿到 TI 毫米波雷达板子(比如 IWR6843 这类单芯片方案)、装好了 SDK 和工业工具箱、但卡在“点云有了,人数怎么算”这一步的嵌入式工程师和算法工程师。接下来的内容会从数据流、参数配置、聚类算法、避坑到进阶调参,把这条链路拆开讲清楚。

2. 从原始 ADC 到人数输出:lab0011-pplcount 的数据链路拆解

2.1 毫米波雷达测人为什么比测车难

毫米波雷达测车的逻辑很直接:车是一个大面积、高反射率、运动规律的目标,雷达截面积大,多普勒特征明显。但人不一样,人的雷达截面积小,而且人体表面是曲面,回波能量分散,加上人会有微动(呼吸、手臂摆动),点云在帧与帧之间会“抖”。更麻烦的是多人场景,两个人站得近,点云在距离-角度二维平面上会糊成一团,聚类算法稍微激进一点就把两个人合成一个,保守一点又把一个人拆成三四个。

lab0011-pplcount 的应对思路不是去追求单帧的完美检测,而是利用多帧累积和聚类稳定性来投票。它先在距离-多普勒-角度三维空间里做恒虚警率检测,得到稀疏点云,再用基于密度的聚类把属于同一个人的点归到一起,最后对聚类簇做质心跟踪和计数。这个链路里,CFAR 的参数决定了点云是“太干净”还是“太脏”,DBSCAN 的邻域半径和最小点数决定了聚类是“太紧”还是“太松”,两者是耦合的,不能分开调。

2.2 在工业工具箱里定位 lab0011-pplcount 的配置入口

TI 的毫米波工业工具箱通常把每个实验放在独立的目录下,lab0011-pplcount 也不例外。你拿到工具箱后,先确认三件事:SDK 版本和工具箱版本是否匹配、雷达板型号是否在实验支持列表里、以及实验目录下是否有预编译的二进制和配置文件。常见做法是进入实验目录后,先看docs文件夹里的说明,再用mmwave_sdk提供的烧录工具把xwr64xx或对应平台的 bin 文件写进板子。

下面这段 bash 是典型的目录确认和烧录前检查流程,路径按你本地实际解压位置替换:

# 进入工业工具箱的实验目录,确认 lab0011-pplcount 存在 cd ~/mmwave_industrial_toolbox/labs/lab0011-pplcount # 查看实验目录结构,确认有预编译 bin 和配置文件 ls -R | head -50 # 确认雷达板串口设备号,通常有两个:一个配置口,一个数据口 ls /dev/ttyUSB* # 用 TI 的烧录工具把 bin 写进板子(工具名按你本地实际为准) # 这一步只是示意,具体命令以工具箱文档为准

逻辑说明:第一步是确认实验目录完整,避免拿到残缺的包。第二步看目录结构,重点是prebuilt_binaries和chirp_config这两个文件夹,前者放烧录文件,后者放雷达波形配置。第三步确认串口,毫米波雷达板一般枚举出两个串口,配置口用来发命令,数据口用来收点云。参数说明:/dev/ttyUSB*在 Linux 下是常见命名,Windows 下对应 COM 口,设备号会因插拔顺序变化,每次实验前都要重新确认,这是血泪经验。

2.3 雷达波形配置:chirp 参数怎么影响人数统计

lab0011-pplcount 的检测效果,七成取决于 chirp 配置。配置文件里几个关键参数:起始频率、斜率、空闲时间、ADC 采样数、帧周期、chirp 循环数。这些参数共同决定了距离分辨率、速度分辨率、最大不模糊距离和最大不模糊速度。人数统计场景下,最需要关注的是距离分辨率和角度分辨率,因为要把相邻的人分开。

常见做法是:距离分辨率做到 4 到 5 厘米,角度分辨率靠多接收天线虚拟阵列做到 10 度以内。帧周期不要设得太短,太短了点云累积不够,聚类不稳定;也不要太长,太长了人走动时点云拖尾严重。我一般会把帧周期设在 50 到 100 毫秒之间,具体看场景里人的移动速度。

下面是一个典型的 chirp 配置片段,用文本形式给出,方便你对照自己的配置文件:

% 雷达波形配置示例,参数需按你的板子和场景调整 profileCfg 0 60 7 5 40 0 0 8 1 256 5000 0 0 30 % 起始频率 60GHz,空闲时间 7us,ADC 起始时间 5us % 斜率 40MHz/us,ADC 采样数 256,采样率 5000ksps frameCfg 0 1 16 0 100 1 0 % 两个 chirp 段,16 个 chirp 循环,帧周期 100ms

逻辑说明:profileCfg定义单个 chirp 的射频参数,frameCfg定义一帧里怎么排布这些 chirp。参数说明:起始频率决定波长,波长影响多普勒灵敏度;斜率乘以 ADC 采样时间得到有效带宽,带宽决定距离分辨率;帧周期决定点云刷新率。注意:斜率不是越大越好,斜率大了最大不模糊距离会变小,人站在稍远处就检测不到,这是新手最容易翻车的地方。

2.4 点云聚类:DBSCAN 在人数统计里的参数怎么定

点云出来之后,下一步是聚类。lab0011-pplcount 用的是基于密度的聚类思路,核心参数有两个:邻域半径 eps 和最小点数 minPts。eps 决定了多远算“邻居”,minPts 决定了一个簇至少要有多少个点。这两个参数直接决定人数估计的准不准。

我的调参习惯是:先固定 minPts 为 3 到 5,然后从小到大扫 eps。eps 太小,一个人被拆成多个簇,人数虚高;eps 太大,相邻的人被合并,人数虚低。判断标准不是看单帧,而是看连续 100 帧的人数输出是否稳定。如果人数在真实值上下跳变超过 1,说明参数还没到位。

下面这段 Python 是离线复现聚类逻辑的简化版,用来在 PC 上拿录制的点云数据调参,不用每次烧板子:

import numpy as np from sklearn.cluster import DBSCAN # 假设 points 是 N x 3 的数组,列分别是 x, y, z(单位米) # 这些点来自雷达数据口解析后的点云 points = np.loadtxt("frame_points.csv", delimiter=",") # eps 单位是米,minPts 是最小点数 # 人数统计场景下 eps 一般从 0.3 开始试,minPts 从 3 开始 clustering = DBSCAN(eps=0.5, min_samples=3).fit(points) # 标签 -1 表示噪声点,不参与计数 labels = clustering.labels_ n_clusters = len(set(labels)) - (1 if -1 in labels else 0) print("估计人数:", n_clusters) # 打印每个簇的质心,方便判断聚类是否合理 for k in set(labels): if k == -1: continue cluster_points = points[labels == k] centroid = cluster_points.mean(axis=0) print(f"簇 {k} 质心: {centroid}, 点数: {len(cluster_points)}")

逻辑说明:这段代码把点云喂给 DBSCAN,输出簇的数量作为人数估计。参数说明:eps=0.5表示 0.5 米内的点算邻居,这个值要结合你的距离分辨率来定;min_samples=3表示一个簇至少 3 个点,低于这个数的当噪声。注意:质心可以用来做跨帧跟踪,如果两帧之间质心位移小于某个阈值,就认为是同一个人,这样可以避免同一个人被重复计数。这个离线调参流程能帮你省下大量烧板子的时间,是我自己常用的做法。

3. 把 lab0011-pplcount 跑起来的完整操作步骤

3.1 环境准备:SDK、工具箱和串口工具

跑 lab0011-pplcount 之前,环境要齐。你需要:毫米波 SDK(提供底层驱动和烧录工具)、工业工具箱(提供实验源码和预编译 bin)、一个串口终端(用来发配置命令和收数据)、以及 Python 环境(用来解析点云和跑聚类)。版本匹配是第一个坑,SDK 版本和工具箱版本不匹配时,预编译 bin 可能跑不起来,或者跑起来但数据格式对不上。

常见做法是:先看工具箱 release note 里写的推荐 SDK 版本,然后严格按那个版本装。不要图省事用最新版,最新版和工具箱之间经常有兼容性问题。串口工具在 Linux 下可以用 minicom 或 picocom,Windows 下用你习惯的串口助手,只要能发文本命令、能保存接收数据就行。

3.2 烧录与上电:bin 文件怎么写进板子

烧录这一步,不同板子操作不一样。以常见的单芯片板为例,需要先把板子打到烧录模式(通常是拨码开关或跳线),然后用 TI 的烧录工具把 bin 写进去,写完再打回运行模式,重新上电。烧录模式进不去是高频问题,现象是烧录工具找不到设备,原因通常是拨码没拨对或者 USB 线只供电不传数据。

下面这段 bash 是烧录前后的检查流程,具体工具名以你本地为准:

# 确认板子进入烧录模式后,系统识别到设备 lsusb | grep -i texas # 用烧录工具写 bin,这里用占位命令示意 # 实际命令请参考工具箱文档里的烧录说明 flash_tool --port /dev/ttyUSB0 --bin prebuilt_binaries/pplcount.bin # 烧录完成后,把板子打回运行模式,重新上电 # 再次确认串口枚举正常 ls /dev/ttyUSB*

逻辑说明:第一步确认系统认到了板子,认不到就别往下走。第二步写 bin,注意 bin 文件要和你的板子型号对应,写错型号的 bin 板子起不来。第三步重新上电后确认串口,运行模式下应该能看到两个串口。参数说明:--port指定烧录口,--bin指定固件路径,这两个参数写错都会导致烧录失败。注意:烧录完成后一定要断电再上电,热复位有时候不生效,这是踩过的坑。

3.3 发送配置并接收点云:数据口怎么解析

板子跑起来后,通过配置口发送 chirp 配置和帧配置,然后数据口会开始吐点云。点云是二进制格式,需要按 TI 的 TLV(Type-Length-Value)结构解析。TLV 里常见的类型有:点云列表、目标列表、聚类结果。lab0011-pplcount 的输出里,点云和聚类结果都会通过 TLV 发出来。

下面这段 Python 是解析 TLV 的简化逻辑,用来把数据口收到的字节流转成点云数组:

import struct def parse_tlv(data): """解析 TI 毫米波雷达的 TLV 数据流""" points = [] offset = 0 while offset < len(data): # TLV 头:类型 4 字节,长度 4 字节 tlv_type, tlv_length = struct.unpack_from("<II", data, offset) offset += 8 if tlv_type == 1: # 类型 1 通常是点云 # 每个点包含 x, y, z, 多普勒, 信噪比等字段 # 具体字段数和顺序以你的 SDK 文档为准 point_size = 16 # 假设每个点 16 字节 num_points = tlv_length // point_size for i in range(num_points): x, y, z, doppler = struct.unpack_from("<ffff", data, offset + i * point_size) points.append((x, y, z, doppler)) offset += tlv_length return points

逻辑说明:TLV 是自描述结构,先读类型和长度,再按长度读值。参数说明:<II表示小端无符号整数,TI 的数据一般是小端;point_size要按你的 SDK 版本确认,不同版本字段数可能不同。注意:解析时一定要做长度校验,数据流里可能有帧头帧尾,不校验的话会解析出乱码点,导致聚类结果完全不可信。这个解析逻辑建议先在 PC 上用录制数据跑通,再上板子实时跑。

3.4 从点云到人数:把聚类和计数串起来

点云解析出来后,接上第 2 章讲的 DBSCAN 聚类,就能得到人数。但实际工程里还要加一层跨帧跟踪,否则同一个人在两帧里被算两次。常见做法是维护一个质心列表,每帧新来的簇质心和已有质心做匹配,匹配上就更新,匹配不上就新建,连续多帧没匹配上的质心就删除。

下面这段 Python 是跨帧跟踪的简化实现:

# 维护一个跟踪列表,每个元素是 (质心, 存活帧数) tracks = [] MATCH_THRESHOLD = 0.5 # 质心匹配距离阈值,单位米 MAX_MISS = 5 # 连续多少帧没匹配上就删除 def update_tracks(new_centroids): global tracks matched = set() for i, centroid in enumerate(new_centroids): best_idx = -1 best_dist = MATCH_THRESHOLD for j, (track_centroid, _) in enumerate(tracks): dist = np.linalg.norm(np.array(centroid) - np.array(track_centroid)) if dist < best_dist: best_dist = dist best_idx = j if best_idx >= 0: tracks[best_idx] = (centroid, 0) matched.add(i) else: tracks.append((centroid, 0)) # 未匹配的跟踪目标,存活帧数加一,超限删除 tracks = [(c, miss + 1) for c, miss in tracks if miss + 1 <= MAX_MISS] return len(tracks)

逻辑说明:每帧拿新簇质心和已有跟踪目标匹配,匹配上就更新位置,匹配不上就新建跟踪。参数说明:MATCH_THRESHOLD决定多近算同一个人,设太大人会合并,设太小同一个人会分裂;MAX_MISS决定目标消失多久才删除,设太小人数会闪断,设太大离开的人会被一直计数。注意:这两个参数和 DBSCAN 的 eps 是联动的,调的时候要一起看,单独调一个往往按下葫芦浮起瓢。

4. lab0011-pplcount 避坑与常见问题排查

4.1 点云稀疏到无法聚类

现象:数据口有数据,但解析出来的点云每帧只有几个点,DBSCAN 聚不出任何簇,人数一直是 0。原因:chirp 配置里的发射功率或检测阈值设得太保守,或者 CFAR 的虚警率设得太低,导致大部分真实目标被当成噪声滤掉了。解决:先把 CFAR 的虚警率调高一点,让点云“脏”一些,确认能看到人形点云后,再逐步收紧。不要一上来就追求干净点云,干净和漏检往往是一回事。

4.2 人数虚高:一个人被拆成多个簇

现象:场景里只有一个人,但人数输出是 3 或 4。原因:DBSCAN 的 eps 设得太小,或者点云在角度维度上分散,同一个人的点被分到多个簇。解决:先把 eps 从 0.3 逐步加到 0.6 甚至 0.8,观察人数是否收敛到 1。如果加了 eps 还是虚高,检查角度分辨率是不是太差,考虑增加接收天线数量或调整虚拟阵列配置。注意:eps 加大会增加相邻人被合并的风险,所以要在“拆”和“合”之间找平衡点。

4.3 人数虚低:两个人被合并成一个簇

现象:场景里两个人站得比较近,人数输出一直是 1。原因:eps 设得太大,或者两个人的点云在距离-角度平面上本来就分不开。解决:先确认雷达的距离分辨率和角度分辨率是否足够,如果两个人间距小于分辨率极限,任何聚类算法都无能为力,这是物理限制。如果分辨率够,就减小 eps,同时提高 minPts,让簇更紧凑。常见做法是把 minPts 从 3 提到 5,配合较小的 eps,能在一定程度上分开相邻目标。

4.4 人数在真实值上下跳变

现象:人数输出不稳定,一会儿 2 一会儿 3,没有明显规律。原因:跨帧跟踪的匹配阈值和存活帧数没调好,或者帧周期太短导致点云累积不够。解决:先把帧周期从 50ms 加到 100ms,给点云更多累积时间;然后调 MATCH_THRESHOLD,让同一个人跨帧能稳定匹配上;最后调 MAX_MISS,让短暂漏检不会导致目标被删除。这三个参数要一起调,单独调一个往往解决不了问题。

4.5 烧录后板子无响应

现象:bin 烧进去了,但重新上电后配置口没反应,发命令不回。原因:烧录的 bin 和板子型号不匹配,或者烧录模式没退出干净。解决:确认 bin 文件名里带的型号和你的板子一致,重新进烧录模式擦除再写一次,写完断电等几秒再上电。如果还是不行,换一根 USB 线,有些线只供电不传数据,这个坑很隐蔽,我遇到过好几次。

5. 进阶调参:让 lab0011-pplcount 在真实场景里稳下来

5.1 用录制数据做离线回归测试

真实场景调参最怕的就是“现场调好,换个场景又不行”。我的习惯是:在几个典型场景里各录一段点云数据,存成 CSV,然后在 PC 上跑离线聚类和跟踪,用同一套参数去测所有场景。这样调出来的参数泛化性更好,也方便做回归测试。录制的时候要记录真实人数作为标签,离线跑完算准确率,准确率低于 90% 的参数直接淘汰。

下面这段 Python 是离线批量测试的框架:

import glob import numpy as np from sklearn.cluster import DBSCAN def count_people(points, eps=0.5, min_pts=3): labels = DBSCAN(eps=eps, min_samples=min_pts).fit(points).labels_ return len(set(labels)) - (1 if -1 in labels else 0) # 遍历所有录制场景,每个场景一个 CSV,文件名里带真实人数 for filepath in glob.glob("recordings/*.csv"): true_count = int(filepath.split("_")[-1].split(".")[0]) points = np.loadtxt(filepath, delimiter=",") pred_count = count_people(points) print(f"场景 {filepath}: 真实 {true_count}, 预测 {pred_count}")

逻辑说明:这段代码把每个录制场景跑一遍聚类,输出预测人数和真实人数的对比。参数说明:eps和min_pts是你要扫的变量,可以写两层循环做网格搜索。注意:离线测试用的点云要和在线跑的时候同源,否则参数迁移会失效。这个框架搭起来之后,调参效率会高很多,不用每次都去现场。

5.2 多帧投票代替单帧输出

单帧聚类的结果受噪声影响大,工程上更稳的做法是多帧投票。具体是:维护一个长度为 N 的滑动窗口,每帧的聚类人数放进窗口,输出窗口里的众数。N 一般取 5 到 10,太小了投票没效果,太大了响应迟钝。这个技巧在人数统计里特别管用,能把跳变压下去,代价是人数变化时会有几百毫秒的延迟,看你能不能接受。

5.3 参数敏感度排序与调参顺序

调参最忌讳一把抓。我的经验是按敏感度排序:先调 chirp 配置里的斜率和帧周期,这两个决定了点云的质量上限;再调 CFAR 的虚警率,决定点云的密度;然后调 DBSCAN 的 eps 和 minPts,决定聚类效果;最后调跨帧跟踪的匹配阈值和存活帧数,决定输出稳定性。顺序反了的话,前面没调好,后面怎么调都是白费。

下面这张表是我常用的参数范围和影响方向,供你起步参考:

参数典型范围调大后的影响调小后的影响
chirp 斜率30-70 MHz/us距离分辨率变好,最大距离变小最大距离变大,分辨率变差
帧周期50-100 ms点云累积多,响应变慢响应快,点云稀疏
CFAR 虚警率1e-3 到 1e-5点云变多,噪声也变多点云变干净,漏检增加
DBSCAN eps0.3-0.8 m簇变大,相邻人易合并簇变小,一个人易拆分
DBSCAN minPts3-8簇更紧凑,小目标被滤掉小目标保留,噪声簇增加
跟踪匹配阈值0.3-0.8 m同一个人易匹配,不同人易混不同人易分开,同一个人易断

这张表不是标准答案,是起点。每个场景的电磁环境、人的密度、走动速度都不一样,最终参数一定要在真实场景里微调。我自己的习惯是每次换场景先跑一遍默认参数,看人数输出和真实值的偏差方向,偏高了就往“合”的方向调,偏低了就往“拆”的方向调,一般两三轮就能收敛。

最后说一个我踩过的坑:有一次在会议室场景调好了参数,搬到电梯轿厢里直接翻车,人数一直虚高。后来发现是轿厢金属壁多径反射严重,点云里多了很多虚假簇。解决办法是在聚类前加一层基于信噪比的过滤,把低信噪比的点先剔掉,再聚类。这个经验告诉我,毫米波雷达的参数没有一劳永逸,换环境就得重新看数据。希望帮到你。

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

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

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

立即咨询