十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

台球自动计分系统实战:俯视视觉识别、追踪与规则引擎全解析

台球自动计分系统实战:俯视视觉识别、追踪与规则引擎全解析 这套系统做出来之后我最大的感受是视觉识别模型反而是整个项目里最不头疼的部分真正难的是把“看见球”变成“看懂一局球”。台球桌面上十几颗球的检测、跟踪、进球判定、犯规判定每一步拆开看都不算黑科技但串在一起就是一台完整的产品。这篇文章把整个项目的思路、选型、代码逻辑和踩坑记录都整理出来给想搞体育自动计分的同学一个参考。1. 项目整体设计与思路拆解1.1 需求起点为什么台球计分需要自动化经常打台球的人都知道计分这件事看起来简单实际上一局打下来非常费神。尤其是斯诺克红球、彩球交替得分彩球被进球之后还需要重新摆回点位要不要复活、自由球怎么算、犯规罚几分这些规则叠加在一起光靠人脑记很容易出错。我在球房里见过好几次双方为了比分争论半天最后只能翻监控回放。8球和9球的计分稍微简单一点但同样存在争议——开球有没有进球、谁先打进了哪一组花色、黑八是提前进袋还是最后进袋这些关键节点如果没人盯住打完之后根本说不清楚。所以这个项目的核心需求不是“做一个人工智能玩台球”而是做一套能够替代人眼、稳定记录每一次击球结果的视觉系统。这个系统的落地形态是在球台正上方安装一台俯视相机持续拍摄整张球台的画面通过视觉识别模型检测每一颗球的位置和颜色再通过跟踪算法持续锁定每颗球的运动轨迹最后根据轨迹变化和进袋事件自动计算出当前比分。整套流程全自动不需要人手操作也不需要球员主动确认。1.2 方案选型俯视相机是唯一合理的切入点项目最初讨论过三个方案侧面多相机、球杆和袋口传感器、俯视单相机。侧面多相机方案就是在球台两侧各装两台相机通过多视角立体视觉重建球的3D位置。这个方案在学术上很性感能捕捉到球在桌面上弹跳的瞬间但落地时问题很多侧面视角下球体相互遮挡非常严重尤其是球堆密集的时候中间几颗球根本看不见立体配准计算量也大实时性不好保证更麻烦的是台球厅的灯光环境复杂侧面相机很容易被环境光直射产生过曝。球杆和袋口传感器方案就是在每个袋口装红外感应在球杆上装陀螺仪。这个方案能准确知道“进了几颗球”但完全不知道是哪颗球进了、是红球还是彩球、是白球还是目标球。而且一套六袋传感器加同步电路的成本不低维护也麻烦球房清扫、台呢更换都得拆装。所以最后选定了俯视单相机方案。俯视视角下所有球天然分布在台面平面上基本不存在相互遮挡的问题球堆时会有部分重叠但不会像侧面那样完全遮挡而且整张球台的所有位置都清晰可见规则判断所需的全局信息一步到位。再加上透视变换之后可以直接把像素坐标映射成台面坐标后续的距离、速度、角度计算都很直接。1.3 系统整体架构整个系统的技术链路分为五层视频采集层俯视相机实时输出视频流分辨率1080p起步帧率60fps。目标检测层每帧图像中检测所有台球的边界框和颜色类别。目标跟踪层将相邻帧的检测结果关联起来为每颗球分配稳定ID持续输出轨迹。事件判定层解析轨迹数据识别进球、开球、开球进球、白球落袋等关键事件。计分规则层将事件与台球规则结合维护当前比分、剩余球、回合状态。我一开始的误区是试图把检测和跟踪合并成一个模型直接端到端输出球的运动轨迹。但后来发现这样做非常难调因为跟踪本质上是一个时间维度上的关联问题和检测的空间识别问题解耦之后每一层都可以独立调试、独立优化。检测错了就去调检测数据跟踪丢了就去优化跟踪逻辑问题定位清楚得多。2. 相机选型与安装标定2.1 相机参数怎么定分辨率、帧率与快门方式相机是整个系统的“眼睛”选型直接决定了后面所有算法的上限。分辨率方面我建议至少1080p。以标准8尺台球桌为例台面尺寸大约是2.54米乘以1.27米在1920×1080分辨率下每颗直径52.5毫米的标准球大约覆盖20到30个像素的范围。这个尺寸对YOLO系列检测模型来说已经够用了但如果用720p甚至更低球的像素尺寸会掉到10像素以下小目标检测难度会明显上升。帧率方面我强烈建议60fps。台球在击球瞬间的速度非常高职业选手的击球可以使白球达到每秒数米的速度在30fps下两帧之间球可以移动十几厘米这会导致两个问题一是球在帧间出现大幅度跳跃跟踪算法容易跟丢二是进球瞬间球进入袋口到完全消失可能只经历两三帧低帧率下很难捕捉到“进袋”的过程。实测下来60fps比30fps的进球判定准确率高一大截。快门方式也要注意。卷帘快门在拍摄高速运动物体时会产生果冻效应画面中快速移动的球会被拉成斜线影响检测框的稳定性。有条件的话优先选全局快门相机。如果只能用卷帘快门的工业相机尽量把曝光时间调短减少拖影。2.2 安装高度与角度的取舍相机安装高度不是越高越好。太高了单颗球的像素尺寸会变小检测难度增加太低了镜头视场角不够覆盖不了整张台面。我用下来比较合适的范围是2.2米到2.8米之间以球台上方垂直投影点为中心。有一个细节容易被忽略相机最好稍微偏离正中心往球台长边方向平移约二三十厘米同时镜头朝向台面中心略微倾斜。这样做的好处是镜头的光轴不再垂直于台面而是带一个小角度台面四周的畸变会比完全垂直安装时更均匀而且能减少天花板上灯具在画面中的反光干扰。当然这个倾斜带来的透视形变需要通过后面的透视变换标定来消除。镜头选型方面避免使用鱼眼或广角镜头。鱼眼镜头的桶形畸变非常严重虽然可以通过去畸变算法矫正但边缘矫正后分辨率损失很大还会引入新的重采样误差。用普通定焦镜头焦距视安装高度而定原则是让台面四周稍微留出一点余量但不要留太多保证台面区域占画面面积的三分之二以上。2.3 像素坐标到台面坐标的标定方法有了畸变校正之后下一步是把图像中的像素坐标映射到台面的真实物理坐标。这一步影响所有后续的距离和速度计算必须做准确。常规做法是基于张正友标定法先标定相机内参和畸变系数然后做一个四点透视变换。具体操作是在台面四个袋口中心点放置醒目的标定标记记录它们在图像中的像素坐标同时量好四点在台面坐标系下的真实坐标例如以球台左上角为原点然后用OpenCV的getPerspectiveTransform计算出单应矩阵。之后图像中任意一个球的位置乘以单应矩阵就能得到台面上的厘米坐标。这里有个经验四个标定点一定要选在球台的实际边界上而不是画面边缘。如果选点在画面边缘透视变换后球台区域会被过度拉伸导致台面中心区域像素利用率下降。另外标定板建议用带背胶的哑光纸打印贴在台呢上不会反光测试完再揭掉。整个标定过程只需要在相机固定后做一次前提是相机后续不能被碰到。我在球房里碰到过好几次有人不小心把相机支架碰歪了结果整张桌子的坐标全部偏移进球判定全部失效。后来我在支架上做了标记线固定之后如果被碰到可以通过对比当前画面和初始画面自动检测偏移然后提示重新标定。3. 球体检测与跟踪的实现3.1 目标检测模型选型与训练数据准备检测部分我用的是YOLOv8s输入分辨率640×640在RTX 3060上推理耗时大约10毫秒完全够实时。为什么不直接用官方预训练权重因为COCO数据集里的80个类别根本没有“台球”这一类所以必须自己标注训练。但这里有一个更实际的坑台球在图像中太小了而YOLOv8s的检测头对大目标更友好。我试过直接在640分辨率下训练效果一般小球的召回率只有九成出头。后面把输入分辨率提升到1280×1280检测精度有明显提升但推理耗时也翻倍了。如果机器性能有限可以保留640×640输入但在数据增强阶段切一个crop区域放大训练让模型见过“放大的小球”。数据集的构建是重头戏。我的标注集大约包含8000张图片覆盖了不同台呢颜色、不同自然光/灯光条件、不同相机角度。球类共分9个类别白球、1号到9号球9球标准色斯诺克用的棕色球和蓝色球单独建类。如果只做中式8球9类就够了但如果要兼容斯诺克得把所有彩球都建模。最费力的是数据增强。台球检测场景里台呢颜色、高光、球与球之间的相对遮挡都是变量。我用上了HSV颜色抖动、旋转、缩放、亮度和对比度扰动还专门收集了一批“有反光”的样本强行加大高光区域防止模型把高光误判成球。训练的时候把验证集和训练集按球桌分开避免同一张球桌的不同帧出现在两个集合里否则验证集精度会虚高。3.2 跟踪算法IOU匹配加卡尔曼简单但有效跟踪模块是整个系统里被质疑最多的地方。很多人一上来就想用DeepSORT、ByteTrack甚至Transformer-based的跟踪器但我在这个场景里最终用的是最简单的方案基于IOU的匈牙利匹配加上卡尔曼滤波预测。为什么这个简单方案够用因为台球桌是一个完全平面运动场景球的速度和方向在短时间间隔内高度连续而且相机是固定的不存在相机运动带来的背景补偿问题。相邻两帧之间同一颗球的位移通常小于球的直径这意味着仅靠边界框位置信息就能非常可靠地关联同一颗球。颜色特征反而是次要的因为在转动和光线下颜色会漂移而边界框位置不会骗人。具体匹配逻辑如下对当前帧检测到的每个框与上一帧每个活跃球的预测框计算IOU。用匈牙利算法使所有匹配对的总IOU最大。匹配成功的球更新轨迹位置再用卡尔曼滤波预测下一帧位置。对连续三帧没有匹配上的球标记为丢失暂时保留ID但不参与计分。丢失球如果之后重新出现用颜色直方图和位置双重校验找回ID。一个我踩过的坑IOU阈值设置。初始用0.5结果球快速移动时丢帧率高。后面降到0.2匹配率明显提升。原因是球速快的时候两帧之间球的位移可能超过其直径边界框只有一小部分重叠IOU不到0.3但依然是同一个球。所以IOU阈值宁可低一点靠卡尔曼预测去补偿也别设太高导致大量误丢。3.3 难度场景处理遮挡、粘连与贴库虽然俯视视角下遮挡大幅减少但球堆密集时球体之间是会出现粘连的。典型场景是开球前球整整齐齐摆成三角形从上方看中间几颗球完全重叠成一颗检测模型可能只能输出两三个框与真实球数不相符。这里关键点在于开球瞬间球堆爆开之后粘连会迅速解除这时跟踪模块不需要在粘连状态下维持每一个球的独立ID只要在爆开的第一时间重新初始化各球ID就好。我采用的方法是如果检测到“球堆区域连续N帧未变化且检测球数与实际球数不符”就把该区域所有球标记为“聚合态”聚合态不参与计分判定直到球散开后重新按检测框独立建ID。另一个是贴库球。球紧贴库边时检测框会有一部分超出台面边界导致框的中心点偏向库外。这个很好解决因为台面边界已经通过标定确定了把检测框修正为与台面区域求交的结果即可。还有一类问题是球的阴影。俯视相机下如果灯光从侧上方照射球会在台呢上留下阴影颜色接近深色球尤其黑8容易造成误检。我的解法是在标注时大量引入含阴影的负样本同时在后处理时用“阴影通常紧贴球体边缘且形状不规则”这个先验来过滤。4. 计分逻辑从“看见球”到“看懂一局球”4.1 计分事件的判定流程跟踪模块输出了每个球的实时坐标和速度但这些原始数据还不能直接当比分用。台球计分的核心在于“事件”而不是“位置”。系统需要识别的核心事件有三类第一类进球事件。球进入袋口后从画面中消失。判定方式当球的中心点进入预先标定的某个袋口区域袋口中心周围划一个半径约8厘米的圆并且该球在下方连续2帧内不再出现则判定进球。这里注意一个问题球可能撞击袋口边缘后弹出来并没有真正落袋所以不能只凭进入袋口区域就判进球必须加上“连续消失”这个条件。第二类开球事件。开球是每一局的起点判定逻辑为检测到球堆从“密集静止”状态变为“散开运动”状态且白球出现明显的高速移动。识别到这个事件后系统自动重置本局状态把比分清零。第三类白球落袋。白球落袋意味着犯规对方获得自由球或线后球。判定逻辑与进球类似但白球一旦消失要立即报出“白球落袋”事件并且不能简单地把它标记为进球需要走犯规流程。事件判定层输出的是一串带有时间戳的语义事件比如“红球第3帧进中袋”“白球第7帧落袋”“开球事件”。这些事件再送进规则引擎去换算成比分变化。4.2 规则引擎的配置化设计这一部分是我觉得整个项目里最值得分享的。台球规则并不是只有一种——中式8球、美式9球、斯诺克三者的计分逻辑差异很大如果直接在代码里写死以后换规则就得改逻辑改到崩溃。我把规则引擎做成了配置驱动。系统维护一个规则配置对象里面定义了当前规则类型8球/9球/斯诺克。各球的颜色和分值。进球后的行为是 permanente永久消失还是需要复活回点位。当前回合的合法目标球类型。犯规条件。开球后是否允许直接选花色8球规则中开球进球是否算有效选择。以斯诺克为例规则引擎的状态机会跟踪当前合法目标球的顺序——红球阶段、彩球阶段、自由球阶段。每次进球事件进入后状态机判断该球是否是当前阶段可选球如果是则加分如果是彩球阶段结束后又重新出现“活球”则标记为复活并触发重新放置。彩球复活后系统会把球放回其原始点位屏幕上的计分界面同步更新。8球规则相对简单但有一个容易忽视的点开球进球时不能立即判断花色必须在后续第一次有效击球进球后才锁定主队的花色。规则引擎里专门为这个逻辑设置了一个“花色锁定等待”状态。计分展示层我用了WebSocket加一个简单的React页面实时显示当前比分、各色球的剩余数量、当前回合合法目标球。这个页面挂在球房的一台触屏一体机上不需要球员做任何操作。如果观众想看也可以二维码扫码打开同一页面在手机上同步观看。5. 实操过程与问题排查实录5.1 台呢反光和灯光干扰的解决方案实际部署时台球厅的灯光是最不受控的因素。有些球房用的是暖色高瓦数吊灯有些用的是RGB氛围灯台呢颜色还会在灯光下变色。我的系统在开发环境自然光模拟下识别很准一到球房实测就翻车——大量浅色球被漏检深色球误检率飙升。排查看下来核心原因是台呢上大面积的高光反射和球的表面镜面反射。台呢本身是粗糙纤维材质理论上不会反光但球房的灯光如果角度很斜台呢上的绒毛会产生类似镜面的反射区域颜色和白色球极其接近模型根本分不清。我做了三个修复第一个是在预处理阶段增加CLAHE对比度限制自适应直方图均衡化把局部高光压下来第二个是数据层面专门去球房拍了两三千张带高光的照片加进训练集让模型见过“高光下的球”长什么样第三个是后处理层面对检测框做置信度过滤时对高置信度的球建立颜色直方图模型低置信度的检测框如果颜色直方图和某个已有球的模型高度接近才保留否则丢弃。三管齐下之后误检率才压到可控范围。5.2 ID跳变与轨迹交叉的修正跟踪模块遇到的最大问题是两颗球发生十字交叉运动时ID会互换。比如红球从左上往右下运动白球从右上往左下运动两球在中心位置相遇并交叉如果匹配逻辑只看位置极大概率把两者的ID搞反。处理这个问题我加了两重校验。第一重是速度方向校验——同一颗球在交叉前后的运动方向不能发生剧变如果上一帧速度方向朝右下方这一帧突然变成朝左上方多半是ID错配了。第二重是颜色校验——每颗球在跟踪过程中会缓存一个颜色直方图模型ID匹配时同时考虑颜色相似度和位置距离因为球的颜色不会因为交叉而改变。这两重校验加进去之后ID跳变减少了大约八成。但说实话完全杜绝是不可能的尤其是两颗颜色相近的球比如黄色和橙色的1号球、2号球在俯视角度下色差很小在某些光线下确实不好区分。这种情况下我的策略是“宁可放走也不乱绑”——如果颜色相似度太低就把匹配的置信度降下来该球短暂标记为“不确定”不参与计分等下一帧再确认。实际测试中这种不确定状态平均持续不到3帧基本不影响计分体验。5.3 计分逻辑的边界情况测试规则引擎写完之后我找了一位在球房上班的老裁判帮我看了一遍逻辑发现了一大堆边界情况是我完全没有考虑到的。比如斯诺克里有一种情况当台面只剩彩球时打进一颗黄球如果黄球不复活而下一颗该打绿球这个时候黄球应该是要复活的因为所有彩球在最后一轮之前都需要复活回点位。我的规则状态机一开始没有处理“只剩彩球阶段”的复活逻辑导致连续打进两颗以上彩球后比分比实际少算了很多分。后来修改为在红球清零后所有彩球进球一律先标记为“需要复活”再由状态机判断当前该打哪一颗彩球只有该彩球被正确打进时才计分。还有8球规则里的一个隐藏坑开球时如果黑八直接进袋这一局是直接判负还是重新摆放不同球房执行不一样。规则引擎里我专门做了一个开关让球房老板自己配置。这种细节如果不在项目初期想清楚后面改规则引擎的状态机是很痛苦的。5.4 实测效果与性能调优参考整套系统在标准8尺球台上、使用全局快门工业相机分辨率1920×108060fps、RTX 3060显卡推理的方案下实测数据如下整体pipeline延迟从视频帧到事件输出约120毫秒其中检测耗时约35毫秒跟踪约5毫秒事件判定约2毫秒视频采集和显示链路占主要延迟。进球事件准确率在200次有效进球测试中系统正确识别196次准确率98%其中4次漏判均出现在球快速弹库之后直接入袋球在画面中停留帧数过少。ID稳定性单局8球比赛约30次击球中平均发生ID错配1.2次均能通过颜色校验自动恢复。误计分率100局8球测试中因为环境光突变或极端反光导致的误计分为2局均为低置信度情况球房管理员通过管理后台手动修正后恢复。调优方面当显卡性能不足时可以优先降低检测分辨率而不是帧率。因为检测分辨率下降会直接导致漏检而降低显示帧率对计分影响不大。另一个优化点是检测并非每帧都要跑可以在跟踪确定时每两帧跑一次检测中间帧用卡尔曼预测补位。这个策略能把整体GPU占用降低一半适合多球台共用一台机器的场景。最后分享一个我的长期维护心得这套系统在球房跑了一段时间后我最大的体会是视觉模型和跟踪算法不是项目的终点规则引擎和部署运维才是真正花时间的地方。台球计的永远是“规则”而不是“球在哪”。如果一开始就把大部分精力放在模型精度上后期大概率要为了规则适配重构架构。我现在的版本里规则引擎已经和视觉模块完全解耦——视觉模块只管输出“某色球在什么时间进了哪个袋口”规则引擎独立运行甚至可以在不重启视觉服务的情况下动态切换规则类型。8球打完切斯诺克后台改个配置就行完全不用动检测和跟踪代码。这套架构也让我后续加“犯规检测”比如白球落袋后的自由球摆放提示变得非常轻松。如果让我重做一遍我会更早把规则引擎的配置化做进去至少提前一个月落地。如果你也要做类似的体育自动计分项目建议最优先设计好事件与规则的解耦这比挑一个重型跟踪模型重要得多。
返回列表