做热像仪开发、工业在线测温或者移动设备巡检的朋友,大概率都遇到过同一个需求:画面里有个热源在动,你要把它的“移动热源坐标参数”实时交给下一级系统。可能是云台要跟着它转,可能是机械臂要定位它,也可能是后台要画它的运动轨迹。很多人一开始觉得简单——不就是把图上最亮的那个点找出来吗?真做起来才发现,从“找亮点”到输出一套稳定可靠的坐标参数,中间隔着图像分割、坐标系换算、时间戳同步、滤波预测整整一长串问题。
这篇文章我就想把我这些年做移动热源坐标参数提取的经验整理干净,从参数定义讲到标定换算,再到卡尔曼滤波和工程调优,把那些常规文档里不会写的内容一并放出来。适合正在做红外热像仪二次开发、智慧巡检、焊接自动化、传送带高温工件定位的工程师参考,也适合刚接触热成像定位、想了解这套体系该怎么搭的初学者。
1. 移动热源坐标参数:先搞清你输出的到底是一组什么数
很多人拿到需求第一反应是“输出热源的x、y坐标”。但“移动热源坐标参数”这件事,远比两个数字复杂。如果一开始不把参数的定义、层级和坐标系统搞清楚,后面写出来的程序越往后越难改。
1.1 从帧级参数到目标级参数的分层
热像仪是一帧一帧出图的,每一帧上都有完整的温度矩阵。移动热源坐标参数最原始的形式,是在某一帧上记录下来的瞬时测量值。我习惯把这组参数拆成四个层级。
- 帧级参数:图像帧号、采集时间戳、热源像素坐标、热源温度值。这是最底层的测量结果,也是后续所有计算的输入。
- 目标级参数:热源识别框、轮廓、面积、质心坐标、峰值温度、平均温度。这一步把“点”扩展成了“目标”,能表达热源的空间范围。
- 运动级参数:当前速度、加速度、运动方向、历史轨迹。这是随时间累积之后得到的动态特征,用于预测下一帧热源位置。
- 场景级参数:经过坐标转换后得到的设备坐标、平面物理坐标或地理坐标,可以和机械臂、云台、电子地图对接。
这个分层不是我硬造的,而是实际项目中各系统对坐标参数的需求完全不同。比如云台联动只需要图像坐标或角度偏差,但机械臂抓取就必须要物理坐标,消防监测则可能要求经纬度。如果一开始就只输出一个二维像素坐标,到后期接设备时往往要返工。
1.2 多个坐标系同时存在,必须命名清楚
移动热源坐标参数最容易出乱子的地方,是坐标系归属不明确。我给一个典型的热成像定位项目梳理过,现场至少会涉及四套坐标。
| 坐标系 | 说明 | 典型单位 |
|---|---|---|
| 像素坐标系 | 图像左上角为原点,横纵以像素计 | pixel |
| 图像物理坐标系 | 图像中心为原点,以物理尺寸计 | mm |
| 相机坐标系 | 以镜头光心为原点,Z轴指向场景 | mm |
| 世界/设备坐标系 | 以现场设备或地面某点为原点 | mm 或 m |
很多误动作并不是算法算错了,而是把像素坐标当成物理坐标直接用。比如我用热像仪检测一条传送带上的高温工件,目标在图像里的x坐标移动了30个像素,但这30个像素对应多少毫米,取决于传送带在画面中的深度位置。没有坐标系转换,后面的机械臂根本没法对准。
1.3 一个完整的坐标参数结构示例
为了不让讨论停在概念层面,我放一个在实际项目中用过的JSON结构。这套结构的目的很简单:让下游系统拿到这一帧之后,不需要再猜测任何东西。
json复制{
"frame_id": 1024,
"timestamp": 1714000000.832,
"target_id": 7,
"category": "hot_spot",
"bbox_pixel": [586, 402, 84, 63],
"centroid_pixel": [628, 433],
"centroid_physical": [1.208, 0.541, 0.012],
"temperature_max": 186.5,
"temperature_avg": 143.2,
"area_pixel": 4126,
"velocity": [0.18, -0.02],
"lost": false
}
很多项目里,target_id和timestamp反而是最容易被忽略的两个字段。target_id用来区分多热源场景下的同一个目标,timestamp用来对齐后续运动轨迹。没有它们,坐标参数再准也是一堆孤立的点。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从热像图里抠出热源:这一步决定了坐标参数的上限
坐标参数的精度不会超过“热源位置检测”的精度。如果分割出错的区域、质心算偏了,后面坐标系转换做得再漂亮也是白搭。所以要先说清楚:移动热源是如何从一整幅热像图里被分离出来的。
2.1 温度阈值分割不是想当然的“大于多少度”
热源不一定意味着绝对高温。在电气设备巡检场景里,一个比周围环境高10℃的接点就是热源;在钢铁产线上,一个500℃的板坯和周围300℃的环境对比也很明显。移动热源检测最常用的办法,是做温度绝对阈值加相对阈值的组合判断。
具体操作时,我会先取一段无热源的背景温度序列,统计背景温度均值μ和标准差σ。初始阈值定为:
text复制T_threshold = μ + k * σ + T_offset
其中k一般取3到5,T_offset根据场景温差设定,通常取3℃到10℃。这个公式的好处是能适应环境温度漂移。比如白天和晚上背景温度不同,单纯用固定阈值60℃去分割,白天可能把大片地面都识别成热源,晚上又找不到真正的发热点。
拿到阈值后,对温度矩阵做二值化,把高于阈值的像素标记为1。这一步很基础,但要注意:热像仪输出的数据可能是14位RAW格式,也可能是经过伪彩色编码后的RGB图像。如果直接对伪彩色图像做颜色分割,精度会大打折扣。一定要先拿到与像素一一对应的温度矩阵,再做阈值处理。
2.2 形态学处理与连通域标记
阈值分割之后,图像上通常还会残留很多孤立点、细线和噪点。这些零碎区域如果不清理,下一步计算质心时会把坐标拉偏。我一般会先做一次形态学开运算,也就是先腐蚀后膨胀,把小于结构元素尺寸的噪声区域去掉,然后再做连通域标记。
连通域标记算法在OpenCV里对应connectedComponentsWithStats,在Python里直接用很方便。拿到每个连通域之后,还需要做一次面积筛选:太小的连通域大概率是噪声,太大的连通域则可能是热源与背景连通了,需要检查阈值是否合理。
这一步有个非常常见的坑:两个距离很近的热源,如果温度都比阈值高,二值化后可能连成一个连通域,坐标参数直接变成一个整体。要解决这个问题,通常需要在形态学处理之后增加分水岭分割,或者根据热源尺寸先验对二值图做距离变换。我在处理多热源场景时,习惯把这一步做在前面,而不是等到质心计算后再补救。
2.3 质心计算:温度加权比单纯几何中心更稳
连通域内定位热源坐标,最简单的方式是算几何中心。但我实际测试下来,几何中心容易受分割边界抖动影响,尤其是边缘像素不断变化时,每一帧的几何中心都会跳。更稳定的做法是温度加权质心。
对于连通域内的每个像素i,用其温度T_i减去背景温度T_base作为权重,然后计算加权坐标:
text复制x_c = Σ (x_i * (T_i - T_base)) / Σ (T_i - T_base)
y_c = Σ (y_i * (T_i - T_base)) / Σ (T_i - T_base)
这样做的道理很直观:温度越高的像素,对热源中心位置的贡献越大。当一个热源在移动时,边缘温度会下降,边缘像素权重变小,所以加权质心比几何中心平滑得多。如果热源是一个温度分布非常均匀的矩形物体,比如加热平板,质心法也足够;但如果热源是火焰或者局部热点,加权质心明显更抗干扰。
额外提一句,有些项目喜欢直接用“最高温度像素坐标”作为热源坐标。这在热源是单峰分布时确实简单有效,但会把个别噪点放大,容易造成坐标跳变。我一般把峰值温度和峰值坐标也单独输出来,用于诊断,但控制用的坐标参数还是用温度加权质心。
2.4 温度溢出与拖尾问题
热像仪的量程是有上限的。超过量程的物体,在温度矩阵里会顶到量程上限,形成一个“温度平台”。这时候质心计算会偏向高温平台的中心,看起来每帧都很稳定,但实际物理位置可能已经偏离了。
更麻烦的是运动造成的拖尾。热像仪探测器有积分时间,目标移动速度太快时,图像上会形成沿运动方向的拖尾,高温区域被拉长,质心会明显偏向运动方向的后方。解决这个问题的第一个思路,是调短积分时间,但这样会降低温度测量精度。第二个思路是在坐标参数后面做一个速度补偿:根据上一帧到当前帧的位移增量,把质心位置往前修正半个拖尾长度。具体修正量要通过现场标定实验来确定,没有通用公式。
3. 像素坐标到物理坐标:坐标换算才是硬骨头
好多项目死在坐标换算这一步。热像仪的成像原理和普通相机类似,镜头也存在畸变,所以在把像素坐标变成物理坐标之前,必须先搞定热像仪内参、外参和场景平面约束。
3.1 内参标定的难点:可见光的棋盘格在红外下看不见
普通相机标定用的是棋盘格或圆点标定板,但这些东西在红外图像里通常没有对比度。热像仪标定板的制作有几种常用方案:用电加热板在表面贴出圆形靶标,用高温胶带在铝板上贴出栅格图案,或者用高于环境温度的金属圆盘阵列。我试下来,最省事的是用一块导热性能好的金属板,在背面贴加热膜,正面加工出凹槽或者排列孔径,正面的温度分布就能形成清晰的棋盘格效果。
标定算法本身可以直接复用OpenCV的calibrateCamera,输入是红外图像上检测到的角点坐标和对应的物理尺寸。由于热像仪分辨率低,一般在320×240或640×512左右,角点检测精度会打折扣,所以标定时要多采集20组以上图像,覆盖画面不同位置。如果标定板边缘模糊,可以把阈值调高一点,尽量保留清晰的温度梯度。
3.2 单目热像仪只能给出射线,必须加平面约束
很多工程问题里,相机安装高度固定,场景地面或传送带平面可以预先标定。这时候不需要完整重建三维空间,只需要把像素坐标投影到已知平面上。
如果相机近似垂直于平面,可以用一个3×3单应矩阵H完成像素坐标到平面坐标的映射。用OpenCV的findHomography,输入至少4组像素坐标和对应的物理坐标,就能求出H。之后对任意像素点p,物理坐标就是:
text复制P_physical = H * p
这里要注意,单应矩阵只在物点位于同一平面上严格成立。如果热源在三维空间里上下浮动,比如机械臂末端带着加热头上下移动,就必须引入高度约束。对于移动机器人巡检的场景,通常可以假设热源位于地面高度,但地面不是完全水平时,误差会随距离增大而放大。
3.3 热像仪与可见光相机/云台的坐标对齐
移动热源坐标参数经常还要和可见光相机联动。热像仪分辨率低,人眼不容易看清楚目标和周围环境,这时会在旁边装一台高清可见光相机。两台相机要做联合标定,计算它们之间的旋转矩阵R和平移向量T。
联合标定最麻烦的是同一个标定板要同时出现在红外和可见光图像里。解决方法是做一块“双模标定板”:在铝板表面贴上常温可见的棋盘格图案,同时在铝板背面加热,让棋盘格在红外图像中也有足够的温度对比度。采集同步图像对之后,分别求出两相机的外参,再计算相对位姿。
如果你还要驱动云台跟踪移动热源,坐标参数至少要包含热源在图像中的像素坐标偏差或角度偏差。云台控制通常需要的是方位角和俯仰角,这要从相机坐标系下的射线向量换算出来。这个换算依赖内参与畸变参数,所以内参标定一定要做扎实。云台本身的回程误差和延迟也要单独测试,不然坐标参数给了,云台还是会顿挫。
3.4 低成本的替代方案:四点标定
在精度要求不高的项目里,比如用热像仪监测一片区域内的移动热源,判断它大概移动到了哪个网格,不需要做完整的内外参标定。我在现场经常用“四点标定”来快速落地。
做法是:在场景里放置四个已知位置的热源标记物,比如小型加热片或者反光板,记录它们在图像中的像素坐标,再用RTK或激光测距仪获得真实物理坐标。用这四组对应点求解单应矩阵,之后的热源坐标全部通过这个矩阵转换。此方法在平面场景下误差大约在可视范围的1%到2%以内,拿来给云台提供粗指向足够了。
4. 时间维度上的坐标参数:滤波、插值、预测
移动热源坐标参数最大的特点就是“移动”二字。一帧一帧的测量值如果不做时间维度上的处理,会有抖动、延迟和丢失。这部分的处理水平,往往直接决定系统是能用还是好用。
4.1 采样率与时间戳是坐标参数的地基
先说一个被忽视的问题。热像仪出帧率通常不高,常见的是9Hz、25Hz、30Hz。移动热源速度越快,相邻两帧之间的位移越大,越容易出现轨迹跳跃。所以在设计坐标参数结构时,采样率必须跟着运动速度走。
比如一个移动速度2m/s的热源,用25Hz热像仪拍摄,那么每帧之间目标移动80mm。如果用来驱动机械臂抓取,这个位移已经很大了,必须做插值。而9Hz热像仪的效果会更差,两帧之间隔110多毫米,中间热源可能在坐标参数里直接“消失”。
时间戳也一样重要。我在很多项目里发现,算法输出帧的时间戳用的是计算机收到数据的时刻,而不是热像仪传感器曝光的时刻。热像仪内部有处理延迟,网络传输还有延迟,如果要把坐标参数和可见光视频或外部运动系统对齐,必须用传感器曝光时间戳。有些热像仪SDK能直接给出帧时间戳,优先用这个;只有一帧时间戳时,也要记录接收时刻,并估算固定延迟。
4.2 卡尔曼滤波:把坐标抖动降下去
原始坐标参数每一帧都有随机噪声,直接给云台或机械臂用会抖得很厉害。卡尔曼滤波是处理这类问题最经典的方法。对移动热源,我通常把状态向量设计为:
text复制X = [x, y, vx, vy]^T
假设热源在短时间间隔内近似匀速运动,离散状态转移方程是:
text复制x_k = x_{k-1} + vx * dt
y_k = y_{k-1} + vy * dt
vx_k = vx_{k-1}
vy_k = vy_{k-1}
观测向量是热源质心的像素坐标或物理坐标,观测矩阵H只取状态中的位置部分。卡尔曼滤波的好处是,它不只输出平滑后的位置,还能同时估计速度。速度值本身就可以作为移动热源坐标参数的一部分,用于轨迹预测和异常判断。
如果热源运动不是匀速,比如人手持热源或者机械臂变速运动,可以把状态向量扩展为包含加速度的匀加速模型。状态维度增加后,滤波稳定性会下降,所以除非运动变化确实很快,我建议先用匀速模型。
4.3 目标丢失与遮挡恢复
移动热源在运动过程中经常会被其他物体遮挡。热像仪画面里的遮挡可能来自设备立柱、人体,也可能是另一个低温物体把热源盖住。目标一旦丢失,坐标参数不能直接断掉,要外推预测一段时间。
最简单的做法是用最近几帧的速度做线性外推:
text复制x_pred = x_last + vx * Δt
y_pred = y_last + vy * Δt
但外推时间不能太长。我在项目里通常设定一个“丢失容忍窗口”,如果目标在0.5秒内恢复,就用预测位置衔接;超过0.5秒,就用丢失前的轨迹方向估计一个更大的范围,同时把lost字段置为true,提醒下游不要做硬性跟随。盲目让云台继续按预测位置转动,可能越转越偏,最后目标回来时已经偏离画面。
4.4 轨迹平滑还有一招:滑动窗口拟合
卡尔曼滤波适合在线实时处理,而如果应用允许有一定延迟,比如做离线分析或者需要输出平滑的历史轨迹,可以用滑动窗口做多项式拟合。对窗口内的N个点做二阶或三阶多项式拟合,取拟合曲线在最新时刻的值作为输出。
我试过用Savitzky-Golay滤波处理坐标序列,它对保留轨迹形状很贴心,不像普通平均滤波那样会把急转弯拉平。不过这类方法会引入几个帧的延迟,不能用于要求实时闭环控制的场景。实时控制用卡尔曼滤波,离线记录用滑动窗口拟合,两者配合起来效果最好。
5. 工程落地:精度验证、参数调优与三个典型坑
移动热源坐标参数最终要放到现场去跑。实验室里再漂亮的算法,到了生产现场也可能被温差、辐射、烟尘、遮挡这些因素折腾得不像话。我把这几年踩过的坑整理成几个容易复现的检查点。
5.1 用可控热源做真值验证
要验证坐标参数精度,不能只靠目测。我通常做一套可控移动热源装置:在步进电机滑台上固定一个小型加热头,比如用陶瓷加热片或激光加热的铜块,设定电机按已知速度移动,同时热像仪记录坐标参数,最后把计算轨迹和电机编码器真值比对。
验证指标一般包括:
- 平均绝对误差(MAE):计算坐标参数与真值之间的平均偏差。
- 最大误差:判断是否有瞬时超差。
- 平滑度:相邻帧坐标差值的标准差,反映抖动程度。
- 响应延迟:从热源运动变化到坐标参数跟随变化的时间。
平均误差控制在几个像素以内时,在热像仪这种低分辨率传感器下已经算很好了。如果发现某一段轨迹误差突然增大,优先检查是不是热源移动到了画面边缘,那里畸变校正残差最大。
5.2 移动速度对坐标参数的影响有多大
速度对坐标参数的影响主要通过两个途径:拖尾导致质心偏后,以及帧间位移过大导致的时间对准误差。我做过一组对比实验,移动速度从0.2m/s增加到1.5m/s时,质心偏移从不到一个像素增加到5到8个像素。如果不做补偿,这个偏移在物理空间可能对应几十毫米甚至更多。
解决方案有两种。第一种是提高热像仪帧率,在条件允许时尽量换高帧率型号;第二种是做基于运动估计的补偿,利用卡尔曼滤波的速度估计值,把当前帧坐标前移v*dt。注意补偿量不能加得太大,否则会让坐标参数“超前”,云台会过冲。比较稳妥的做法是取50%到70%的补偿量,用现场实验调节。
5.3 三个最容易踩的坑
第一个坑是标定时差。热像仪标定板温度不均匀,角点附近温差太小,提取角点时会出现亚像素偏差。解决办法是把标定板充分预热到稳定状态,并且多拍几帧取平均,不能一开机温度还没稳就急着采集。
第二个坑是环境反射。很多工厂地面是不锈钢或光滑瓷砖,热源发出的红外辐射会被反射到另一个位置,导致热像仪在多个地方看到高温点。这时阈值分割和连通域处理都不管用,需要做反射识别。我的做法是给热源加一个“温度变化率”特征:真实热源的温度变化通常有连续性,反射点和真实热源在移动方向上不一致,可以通过运动轨迹交叉验证排除。
第三个坑是输出延迟被忽略。热像仪本身有积分时间和数据读出时间,算法处理还要几十毫秒,滤波又在时间上加了延迟。如果下游系统只关心坐标准不准,不关心这个准的坐标对应的是过去的哪个时刻,就会在控制中产生误差。所以一定要把所有延迟分摊清楚,尽量在输出参数里保留传感器曝光时间戳。
5.4 现场参数调优经验
参数调优没有万能公式,但有顺序可循。我一般是先从温度阈值开始,把分割稳定性调到肉眼可见“热源区域基本稳定”的程度;然后调质心计算方式和畸变校正;再开卡尔曼滤波,看平滑效果和延迟的平衡;最后才做速度补偿和丢失恢复。
具体到某个场景,如果现场环境温度变化剧烈,阈值模型里的背景均值要每帧更新,不能只在启动时统计一次。可以做一个滑动窗口,取最近5秒的背景温度均值。如果热源本身温度很高,超过量程,就要考虑降低发射率设置或者加衰减片,确保温度平台不出现。
如果两条热源轨迹经常交叉,目标id很容易互换。这个问题我在多目标移动热源跟踪项目中遇到过,简单做法是在卡尔曼滤波器里加入速度一致性约束,当两个目标接近到一定距离时用最小代价匹配来维护轨迹身份。当然,如果目标会长时间重叠,单目热像仪很难做到完美区别,考虑增加角度不同的第二台热像仪。
我在实际项目里的体会是,移动热源坐标参数这套东西,最难的不是某个独立算法,而是把“检测—定位—标定—滤波—协议”串成一条不中断的链路。每层只损失一点点精度,叠加起来就会让最后的坐标参数差得离谱。与其追求某一个环节做到极致,不如先保证每个环节都有误差记录、每个输出参数都有清晰定义。数据出来后,先做一段时间的“只记录不控制”,和真值轨迹对比,把系统脾气摸透了,再接到控制器上,成功率会高很多。
