先交代一下背景。这个项目是在朋友球房里折腾了大半年的成果,起因也很现实——球房里的积分赛靠人肉眼计分,漏判、争执、记错盘经常让人头大,偏偏台球这项运动对计分准确性要求极高。我当时就琢磨,能不能在球台正上方装一个俯视摄像头,配合视觉识别和球体跟踪,直接把整局球的进出袋过程自动记下来。今天这篇就把整个AI台球自动计分系统的方案选型、硬件部署、算法实现、排查经验一次说透,给想做类似项目的朋友一条能直接踩上去的路。
1. 整体设计与方案选型
1.1 为什么俯视相机是最稳的感知方式
台球自动计分说白了就两个硬需求:看得到每颗球在哪,判断出哪颗球进了哪个袋。市面上有几种思路,有人在台呢下面埋压力传感器,有人给球装RFID芯片,还有人在球台侧面架摄像头。传感器方案布线麻烦,RFID要在球里嵌芯片成本高,侧面相机最大的问题是视线遮挡太严重,球员俯身击球时手和身体几乎能把半张球台挡死。俯视相机走的是完全不同的路子,把相机架在球台上方正中央,垂直向下拍摄,整个台面就是一张干净的正射影像,球、袋口、库边全在一帧画面里,天然绕开了遮挡问题。只要高度够、畸变校正到位,俯视图就是一套极其标准的坐标系统,每颗球都能换算成台面上的物理位置,后续跟踪和计分全部基于坐标运算,逻辑能简化一大截。
1.2 系统架构与核心模块划分
整套系统我拆成了四个模块,各管一段:图像采集模块负责预览画面和帧同步;标定与预处理模块负责把畸变画面还原成真实比例、固定识别区域;球体检测与跟踪模块在每一帧里找出球、给球编号、持续跟踪位置;计分裁判模块在检测到进球事件后验证合法性、更新比分、处理复位。模块之间通过一个简单的共享内存队列通信,检测模块每处理完一帧就把结果推给跟踪模块,跟踪模块维护所有球的动态状态。这样拆的好处是每个环节都能独立调试,比如检测不准时可以直接看检测模块输出的人工标注图,不用连带着去翻跟踪逻辑的账。
2. 硬件部署与图像预处理
2.1 机位高度、相机选型与安装细节
先说结论:我最终用的是4K分辨率、最大30帧的USB工业相机,配合6mm定焦镜头,安装在球台正上方2.9米的位置,视角能完整覆盖2540×1270毫米的台面内沿,四周还留出一点余量看到全部6个袋口的外缘。为什么是2.9米而不是更低?因为台球桌上方往往还有球灯,灯架本身高度就在2.5米左右,相机再低就要撞到灯,再高又可能顶到天花板。这个位置实测下来,单颗球在4K画面里能占到70像素左右,完全足够识别颜色和球号,袋口区域也能看得清清楚楚。装支架时最容易被忽略的一点是固定刚性,别用那种可伸缩的自拍杆改装的架子,每次球员大力击球、球体撞击库边时振动都会传到相机上,哪怕只抖0.5像素,对跟踪稳定性都是灾难。我后来换成了三角形钢结构支架,固定在天花板承重梁上,和球台完全独立,彻底断了振动传递的路。
相机供电和数据线也值得单独说。USB线超过5米就必须用带信号放大器或者光纤USB线,否则帧率不稳定、偶尔丢帧,排查起来非常痛苦。我用的10米主动式光纤USB3.0线,实测稳定。另外强烈建议配一个工业级电源给相机独立供电,别和球房的灯光共用回路,不然灯光启动瞬间的压降会让相机掉线,一掉线整场自动计分就中断了。
2.2 镜头畸变校正与透视标定流程
任何广角镜头拍出来的画面都有明显畸变,尤其是球桌面这么大,边缘的球会变形,直接拿原始画面做坐标计算,离中心越远误差越大。所以标定这步不能省。我用的是张正友标定法,具体做法:打印一张10×7的黑白棋盘格,每个格子边长25毫米,在台面上平铺、倾斜、旋转各拍几十张图,用OpenCV的calibrateCamera提取角点,计算出相机内参和畸变系数,最后得到initUndistortRectifyMap的映射表。这一步做完,画面边缘的直线都变成直线,球从画面中心到边缘都保持正常的圆度。
透视标定同样关键。俯视相机如果装得很正,几乎可以当纯平面处理,但实际安装时难免有一点倾斜,这个时候就需要做透视变换。我在台面上固定四个标志点,分别选在四颗袋口的内沿交点位置,用它们的像素坐标跟实际桌面的物理坐标做一个单应矩阵(Homography),把图像坐标投影到标准的物理坐标系。打完这一个矩阵后,后面所有计算都在物理坐标里进行,球的真实速度、袋口距离都是毫米级概念,做进球判定和规则判断时直接能用上真实尺度。
2.3 灯光环境处理与拍摄参数固化
球房灯光对视觉识别来说是把双刃剑。光线充足能提升球与台呢的对比度,方便分离目标,但灯光的直射反光会把球的颜色区域打出高光,尤其新刷的台呢很亮,高光区会“吃掉”球的边缘信息。我的处理办法是给相机加偏振镜,在镜头前装一块线偏振片,调整角度后能滤掉大部分非金属表面的镜面反射。另一个更省事的方案是尽量使用漫反射灯带,或者在球桌上方加一块柔光布,把硬光打散,实测下来球体表面高光占比能降低百分之六七十。
拍摄参数必须固定。相机设为手动模式,曝光时间1/500秒左右,光圈按现场亮度调到F4到F5.6,ISO尽量低到100,白平衡手动固定为5200K。为什么曝光要快?因为球员击球瞬间白球速度可能超过3米每秒,在1/500秒曝光下运动模糊能控制在可接受范围。自动曝光和自动白平衡绝对要关掉,不然画面亮度随球员衣服颜色变化,检测阈值就容易飘,这是很多新手项目识别时好时坏的根源。
3. 核心算法:球体检测与分类
3.1 为什么选择传统视觉加深度模型的双层方案
台球识别看似简单,实际上比想象中要难,核心难点是球的颜色多、颜色之间还会互相污染(球与球的反射)、球员手臂和球杆经常大面积遮挡台面。单靠传统颜色分割很容易在遮挡和反光场景下崩盘,单靠深度学习检测模型又对算力有要求,而且小目标密集场景下的球体检测精度未必比几何约束下的传统方法高。我最终用的是双层方案:先用传统视觉做快速候选区域提取,再用轻量深度学习分类器对候选区域做最终类别判定。第一层在HSV颜色空间用阈值分割筛出所有可能存在球的区域,第二层对这些候选区域用训练好的分类模型识别颜色和球号。这样既能过滤大量背景干扰,又能绕开纯传统方法对阈值的极度敏感,同时比直接在整图上跑检测模型快很多。
我们训练的分类器输入是40×40像素的球体裁片,标签分9类:八种花球加一个白球。训练数据是自己采集的,把相机架好后拍了大概两万张不同场景下的画面,通过自动标注加人工复核的方式整理出来。训练时故意加入各种增广:平移、旋转、缩放、亮度扰动,还加入了高斯噪声模拟暗光场景。实测在验证集上准确率超过98%,对遮挡比较严重、只能看到半个球的场景,这种局部裁片分类的效果反而比完整检测好,因为裁剪时只截取可见部分,遮挡区域不会参与分类。
3.2 球体坐标定位与尺寸约束的细节
每个候选区域经过分类确认后,需要从像素坐标映射到物理坐标。我在检测阶段就用轮廓拟合圆,拿到圆心坐标和半径。标准台球直径是57.15毫米,在标定好的物理坐标里,某个球的理论半径应该是28.575毫米,这个固定尺寸是一个非常强的约束条件。跟踪模块会把所有检测结果里半径偏离理论值超过20%的目标直接丢弃,能滤掉大量误检,比如球杆头部、观众的指尖、台面杂物等。袋口的圆形区域也通过这个方法标注出来,六个袋口在物理坐标里各有自己的圆心和半径,作为进球判定的锚点。
角度和重叠问题也躲不开。当两颗球贴在一起时,轮廓会把两个圆合并成一个葫芦形,这时直接轮廓拟合会得到错误圆心。我的处理方式是在检测阶段引入分水岭算法做粘连目标分割,加上几何约束把合并的轮廓拆成两个圆。如果粘连得太严重,比如三颗球挤在一起,拆出来的圆心误差还是偏大,这种情况下游跟踪模块会用运动模型去修正,靠上一帧的位置和速度预测来补偿。
3.3 白球跟踪的单独设计
白球是整台球的“发动机”,每次击球都从白球开始,所以白球的连续精确跟踪直接决定计分系统的可靠性。我给白球单独开了一条处理链路:跟踪嵌套采用卡尔曼滤波加匈牙利匹配的框架,位置和速度都作为状态量,预测下一帧出现的区间,然后在区间内找检测结果做匹配。白球的优先级最高,当白球和其他球重叠导致漏检时,系统会持续用卡尔曼预测的位置补位,而不是马上判定白球消失。只有当连续预测误差超过一个球的半径、并且连续10帧画面里检测不到白球,才认为白球可能进袋或需要重新定位。球房实战里白球贴库、贴袋口是家常便饭,这个预测补偿机制非常管用。
4. 球体跟踪与进球判定逻辑
4.1 多目标跟踪框架与帧间匹配策略
球体跟踪本质上是一个多目标跟踪问题。我采用的是经典的检测加关联框架,第一层匹配用匈牙利算法基于IOU做全局最优匹配,第二层用卡尔曼滤波预测的位置作为补充匹配依据。每颗球维护一份状态:当前坐标、速度、置信度、连续未命中帧数、上帧是否有遮挡、是否处于袋口危险区。每帧检测完成后,先把检测结果和现有轨迹按IOU匹配,IOU大于0.3的直接关联;剩下的检测结果再和预测位置做距离匹配,距离小于1.5倍球半径的关联;最后仍未关联的检测结果视为新目标,但新目标必须连续3帧都出现在接近位置才转正,避免台面反光等瞬态噪声产生假球。
这里有个非常关键的设计:跟踪器内部保留“当前置信状态”。每一帧里,某颗球被匹配到一次,置信度加1,连续丢失一帧,置信度减2。当置信度低于阈值时,该球标记为“丢失”,系统进入怀疑状态,会在后台继续追踪它的轨迹,但不会输出到计分模块。同时考虑球的进洞行为:球一旦进入袋口区域边缘,它的运动会被袋口边沿减速、反弹,轨迹会出现明显异常,这时就要触发起一双重验证机制,而不是立刻判定进球。
4.2 进球判定与袋口确认机制
双重的进球判定是这样实现的。第一重,球进入某个袋口对应的圆形检测区(袋口区域半径设置为袋口物理半径的85%),并且球在连续N帧内没有从这个检测区出来,那系统先把这颗球标记为“准进袋”。第二重,确认球确实从台面消失,也就是连续若干帧检测不到这颗球的完整轮廓。只有这两重都满足时,才算真正认定进球。为什么要等“消失确认”?因为袋口边缘存在视野死角,球正好卡在袋口边沿上时,俯视图能看到半个球,但球并未真正落袋,如果只凭进入袋口区就判定进球,会频繁误报。测试中卡袋口的场景真实存在,而且在关键时刻直接改变比分走向。
进球还需要记录落袋时间戳、袋号、球号三个关键信息。时间戳用于后续回放对账,袋号用来校验计分规则(比如8号球必须进指定袋才算赢),球号用于累加得分。每次进球事件产生后,系统会保存触发判定前后各2秒的原始视频片段,方便人工复核。实测这个回溯机制在球房运营中特别受用,减少了大量关于“到底进没进”的争议。
4.3 计分规则引擎与复位处理
计分规则看似简单,真正实现起来有不少坑。国标八球的规则里,击球方必须先命中自己的目标球,白球不能先碰到对方球,白球不能落袋,8号球必须在打完己方所有目标球后才能打且必须按指定袋口落袋。这些规则都依赖球的位置关系和进球顺序。我把它做成了一个可配置的规则引擎,事件驱动,接收进球事件后根据当前比分、桌面球分布、上一杆是否犯规,输出下一杆状态。规则引擎本身不写死任何球桌信息,用一个JSON配置描述当前局型,比如主队球色、是否自由球、黑八状态,这样换别的玩法(九球、斯诺克)只需要换配置,不用改代码。
每次击球完成后还有一个隐藏动作:复位处理。当球员进球后,下一杆开始前摆球位置不会变;但如果没有进球,球员可能重新摆位或者白球被捡起放回开球区。计分系统必须识别这种人工干预。我在开球区设定了一个“白球放回区”,如果白球当前状态是丢失,并且在开球区检测到一个新的球,就判定为人工复位,不计进球,只更新白球坐标。这个逻辑刚开始没做,结果有一次球员打了违规球后自己去摆白球,系统直接给你算成进球,场面一度很尴尬。
5. 常见问题与排查技巧实录
5.1 高频故障:漏检、误检、球被遮挡
跑现场大半年,把这几个高频排查点也整理出来了。漏检多数不是因为算法弱,而是因为输入画质变差。相机长时间在球房灯光下工作,镜头会沾灰,画面发雾,球的边缘模糊,检测置信度骤降。如果某天系统报漏检率明显提升,先别急着调参数,拿一块镜头布擦一下镜头,往往立竿见影。其次是台呢颜色变化。新球房的台呢是全新的翠绿,用半年之后会发旧变灰,颜色统计模型的背景簇中心会偏移,阈值就可能不适配。我在系统里加了一个台面背景自动更新机制,每隔一定时间取画面边缘和球洞周边的区域重新估算背景颜色,让背景自适应缓慢变化。
误检的来源主要是反光和观众区域。在球台边沿的金属和木框上也会产生高光色块,看起来很像球,尤其是当这些色块正好在袋口附近时会干扰判断。我的对策是给每个候选目标加一个位置合法性检查,目标中心必须在台面内沿以内,袋口区域外沿往内收缩一个球的半径作为安全边界。另外在袋口附近的候选目标,还必须满足“半径约等于理论半径差不超过15%”的强约束,这样能把绝大多数非球目标挡在外面。
5.2 实战疑难:重叠粘连、台面反射与快速击球
三颗球挤成一条直线时,视觉上接近一条长条,拆分成三个圆会有多种拆分方案,检测结果在多个解之间跳变,导致跟踪轨迹断裂。我调试了很久,最后提供了一个过渡方案:当检测结果几何歧义无法消除时,跟踪模块允许目标暂时丢失,但保留“痕迹”。一旦后续帧里球分开、各个检测结果重新出现,MOT跟踪器能根据原来的轨迹ID重新关联。这个机制在“开球瞬间炸球”的场景下非常关键,开球之后球群剧烈散开,短时间内大量重叠,靠纯单帧检测根本撑不住,只有靠多帧累积重建轨迹。
台呢上的反射也是一大困扰,尤其是深色球在水晶般光滑的新台呢上会倒映出一个模糊的同色影像。反射影像在HSV空间里经常和真实球同色且形状接近,会形成“幽灵目标”。处理方法是利用真实球和反射影像的形变差异:真实球在俯视图里是完美圆形,反射影像会被台呢纹理拉长成椭圆形,并且边缘模糊。我用轮廓的圆度指标加边缘梯度强度组合过滤,圆度必须大于0.85,平均边缘强度必须大于阈值,两者都满足才认定为真实球。这套规则在绝大多数球房环境下都管用,只有极少数特别光滑的台面还需要额外在袋口区域做特殊处理。
快速击球瞬间球的运动模糊也不能忽视。我用1/500秒曝光已经把模糊压到最低,但白球出杆速度极快时,相邻两帧之间位移可能达到20像素,按我们的物理坐标换算就是好几厘米。跟踪模块的搜索半径就必须按最高可能速度来计算,否则高速球会直接跳出卡尔曼预测的关联窗口。我给搜索半径设定的是一个自适应的值:根据最近5帧该球的平均速度计算预测位置,然后在预测位置周围留出能覆盖最大可能位移的缓冲区域,具体数值预留了30%的余量。这样既不会漏掉快速球,也不会因为搜索范围太大匹配到旁边的假目标。
5.3 系统调试三板斧:录像回放、日志埋点和实时可视化
调试这种视觉系统,最忌讳闭眼改参数。我给自己立了一个规矩:任何一次误判、漏判,都要把原始视频片段、检测中间结果、最终计分输出三份数据一起保留下来。复盘时先看检测输出有没有问题,如果检测没问题,再看跟踪和判定逻辑,逐层定位。为了实现对账,我在系统里加了一套结构化日志,每条日志包含帧号、时间戳、检测结果、跟踪状态、事件类型,全部写入SQLite。发现有问题时直接按时间戳查日志,配合保存的视频,几分钟就能定位到出错环节。
还有一个非常实用的工具是实时可视化窗口。调试阶段我会把检测框、球号、置信度、袋口区域、轨迹线全部画在画面上,投到旁边的一块显示器上。这样现场操作人员能直观看到系统“眼中”的世界是什么样,任何识别异常都逃不过人的眼睛。可视化层和核心逻辑层完全解耦,只用于调试,正式运行时关闭,对系统性能几乎没有额外负担。
6. 系统集成与后续演进方向
6.1 与比分牌、手机端的数据联动
自动计分系统如果只停留在算法调试阶段,落不了地也白搭。我在算法稳定后做了一套集成方案:计分裁判模块每产生一个有效事件,就通过内部WebSocket推送给本地服务端,服务端维护一份实时比分状态,再通过HTTP接口输出给两块下游终端,一块是球房的壁挂LED比分牌,另一块是手机小程序。球员在小程序里能查看每局回放、进球时间线、个人统计数据,比如进球率、连杀次数、平均每杆用时。这样系统就从一个“识别工具”升级成了完整的“球房数字运营工具”,对球房老板来说,会员的台账数据也有了,日常运营价值一下子就出来了。
手机端和实时比分牌之间用WebSocket做增量同步,比分变化几乎是秒级到。有人问过我延迟问题,实测下来从进球事件发生、算法确认到比分牌刷新,整个链路在200毫秒以内,和人眼感知基本同步。这个延迟主要取决于视频帧率和判定窗口帧数,我设置的判定窗口是5帧约167毫秒,加上网络传输,正好200毫秒出头。
6.2 数据闭环与模型迭代
系统跑起来之后,真正有价值的东西其实是数据。每一颗球的轨迹、每一次进球、每一次犯规动作,都沉淀成结构化数据。我后来定期把实际比赛中遇到的困难样本导出,补充到检测分类模型的训练集里,每个月重训一次模型。刚开始每个月都能看到明显的精度提升,后面数据集大了之后,提升幅度放缓,但系统在特定球房的稳定性也确实越来越强。因此我建议所有做类似项目的朋友,从一开始就设计好数据回流机制,别等模型上线才想起来补数据集。
另外一个正在尝试的方向是用轨迹数据做更细粒度的行为分析,比如根据白球击球瞬间的加速度曲线判定出杆力度,根据进球后目标球的路径反推击球角度。这些数据对上专业训练系统非常有用,一个视觉计分系统慢慢就演变成了一套“球技分析工具”。虽然目前还比较初级,但我个人觉得,这条路会比单纯计分本身走得更远。
6.3 项目评估与投入产出
最后说说这个项目到底值不值。硬件成本粗算一下:工业相机加镜头一千五左右,支架和线材五百,补光和偏振镜三百,加上一台只用来跑算法的普通工控机三千左右,整套下来不超过六千块钱。对一家球房来说,这个投入也就是几个会员月卡的价格。而换来的东西包括:裁判人力节省、计分争议大幅减少、会员体验加分,以及一套能持续产生数据的产品底座。如果你是自己练球、想搞个个人训练数据助手,那硬件预算还能再砍一半,用普通的高清摄像头配一台带独立显卡的电脑也能跑。算法部分只要你有点Python和OpenCV基础,按我这篇文章的思路搭一套MVP,一个周末就能通。
这个系统后续还可以扩展,比如接上电动摆球机,实现进球后自动摆球复位,整套自动化就更完整了。总之,这套方案从零到能稳定运行,我最深的体会是:视觉识别看上去是个算法问题,但实际上多半是硬件部署、数据质量和逻辑工程在约束上限。先把相机装稳、灯光调匀、标定做准,算法才有发挥空间。希望这篇能把你的开始成本压到最低,少走点我已经踩平了的弯路。