
特别声明
本文以人工形式完成了原始草稿的撰写,在完稿后使用了 GPT 6.1 Sol 模型进行了多个段落的润色,并认真进行了人工审核确保 AI 润色后的表达没有偏离我个人的本意。同时封面图也由 GPT 6.1 Sol 辅助生成。
关于文章内容有任何疑问和意见,欢迎在评论区讨论
项目背景
我做 PitRadio 的起点,其实是想让《Gran Turismo 7》(以下简称 GT7)的画面再沉浸一点,特别是对于使用 PSVR2 来游玩 GT7 的玩家来说。
因为我平常是个很在意视听体验的人。家里 NAS 收藏的电影,能下载 4K 全都是 4K,而且最好是高码率;遇到喜欢的电影或剧集,也常常宁愿多等一阵,等到有满意的 4K、杜比版本再看。玩游戏时,这种习惯也没变。尤其戴上 PSVR2 玩 GT7 两三年后,就总想有没有办法可以在关闭 HUD 来获得干净沉浸游戏画面的同时不丢失游戏相关信息,想象一下:眼睛看着赛道,想要知道的消息由耳边的“工程师”告诉我,这种体验应该会很酷,而且最好说的是熟悉的普通话,或者粤语,听起来也自然一些。
找了一圈,没有找到在中文听感上足够让我满意的产品,自己动手的念头就冒出来了。作为一名码农,我也正好想借这个机会试一试:在 AI 编程 Agent 越来越成熟的今天,一个人从产品调研、需求梳理、设计原型,一路走到架构设计、开发和测试,究竟能做到什么程度?
于是就有了 PitRadio 这个项目。它首先服务于我自己游玩 GT7 时的一个小愿望,也逐渐变成了一次完整的 “Vibe” Coding 开发实践。
在座舱里,听到熟悉的中文

电脑和 PS5 接入同一局域网后,PitRadio 会接收 GT7 的遥测数据,也就是游戏持续发出的车辆状态与比赛数据,再把其中值得关注的变化转成语音。
声音可以从电脑的扬声器或耳机播放,也可以经由 Discord 语音频道投送到 PS5。在 PS5 加入对应频道之后,戴着 PSVR2 也能听到播报,体验更加沉浸。
目前支持普通话、粤语和英语。界面语言与播报语言可以分别设置,不过对我来说,普通话和粤语始终是最想做好的部分。
我希望它开口时像一个坐在无线电另一端的人:信息简短,数字清楚,该提醒时提醒,没必要的时候就安静。后面会聊到,为了这个听感,语音模型只是其中一环,文案怎么写、消息什么时候说,同样花了不少功夫。
工程师需要知道,什么时候该开口

最容易做出来的版本,是检测到事件就播报:名次变了就报名次,过线就报圈速,油少了就报油量。可这些事情可能同时发生,这会车手注意力也可能聚焦在重刹、过弯,或者救车。
于是在项目里设置了专门的 VoicePolicy,也就是播报策略。它负责在已经确认的事件里决定:哪些现在说,哪些等一等,哪些可以合成一句,哪些已经没有必要再说。
比如短时间内先掉了一位,紧接着又追回原位。如果前一条消息还没有开始播放,再按顺序念一遍“掉到第五”“回到第四”,只会增加干扰。PitRadio 会检查这一段变化的最终结果,取消已经失去意义的待播消息。
过线时也是一样。完成一圈和刷新个人最佳可以同时成立,但没必要连着播两段。能合并的就合并,一次最多保留两个核心事实;如果同时出现需要进站的燃油风险,普通圈速信息则应该让位。
适合等待的消息,会尽量避开重刹、弯中车手高负荷和车辆失稳的时刻。等车手驾驶负担下降,再播仍然有效的信息。如果错过时机、已经过期的消息就丢弃,不让工程师在半圈之后补报旧信息。比赛开始、过线摘要等有明确时间节点的信息,以及紧急燃油指令,则有各自的及时播报规则,并不是所有消息都要排队等直道。
这些处理各自听起来都是小事,但想要把这个播报策略处理好,需要把所有可能的赛况综合起来考虑。播报策略处理得好不好,直接决定了连续开几圈之后,我会不会想把播报关掉。应用也提供三档播报密度和类别开关:想安静一点,就少听几类;希望收到基础驾驶提醒,则可以选择“频繁”档并开启对应类别。
我对“接近真人工程师”的理解,很大一部分就在这些细节里:说话要跟得上当前的比赛,也要顾及车手此刻有没有余力听。
遥测数据能告诉我们什么,又不能告诉什么


遥测数据中包含了基础的比赛信息和车辆数据,例如当前排名、上一圈圈速、比赛总圈数和当前圈数、轮胎温度、油门开度和制动输入等等数据。
真正让我觉得有意思的,是那些不能直接读取、需要从几组数据里推算的东西,即我们说的派生数据。最初我很希望把燃油、轮胎和天气信息串起来,做出更完整的进站建议。尤其在开启油耗和胎耗的比赛里,这些信息往往比刚才那圈快了几十分之一秒更重要。
实际探索下来,有些问题可以逐步解决,有些则碰到了 GT7 遥测数据范围和主机平台封闭性的限制暂时没办法解决。
剩下的油,还能跑几圈?
“剩余油量 ÷ 每圈油耗”,看起来就是一道简单的除法计算。难点在于,每圈油耗该取哪个值?
如果过分依赖眼前的油门状态,松一下油门、再踩下去,预计圈数就可能跟着变化。对于要安排燃油进站策略的“工程师”来说,我更希望得到一个稳定、又能跟随驾驶方式变化的估算。
目前的做法是先用最近最多三个有效完整圈建立基线,越近的一圈权重越高。只有一圈、两圈时,就先使用已有的有效样本。这样得到一个相对稳定的单圈消耗,再用剩余油量计算预计可跑圈数。
但只看完整圈也有问题:如果我刚调整了燃油 Map,或者开始持续省油,要等跑完一整圈才反映出来,显然有点迟。因此又加了一层“同位置比较”:把参考圈分成二十个区间,比较当前区间与历史相同位置的油耗。连续几个区间都表现出同一个方向的变化后,才逐步修正原来的估算。
这里比较的是同一段路。直道和弯道原本就有不同的油耗,不能把正常的驾驶节奏误当成长期趋势。加油、暂停、数据中断或驶上松散路面等情况,也需要分别处理,不能不加区分地算进历史。
最终得到的,是用于判断燃油风险的预计圈数,而不是逐帧复刻游戏 HUD 中的剩余圈数读数。在条件满足的固定圈数比赛中,它可以进一步给出“这一圈结束时进站”的提醒。可惜的是固定时间赛比如耐力赛目前只提供燃油风险提示,因为现有数据没有可靠的剩余比赛时间,不能据此判断能否撑到终点。
胎耗和天气:有些信息确实算不出来
轮胎这一部分,我也尝试过类似的思路。
GT7 遥测数据提供四轮温度,但没有直接给出胎耗百分比。我曾考虑能否从其它字段找到间接线索,比如轮胎有效半径:如果磨损之后半径发生变化,是否就有机会据此估计消耗?
为了核对这个猜想,项目录制了一组使用中性赛车胎、开启九倍胎耗的比赛数据,覆盖明显磨损和换新胎的过程。结果发现:前后轮的半径字段分别始终保持固定值,换胎前后也没有重置。我感觉这也是为什么说 GT7 是 Sim-Cade,而不是 Sim 的原因吧。不知道 iRacing 那边的情况是怎么样。
这条路走不通了。至少在这组实测里,这个字段并没有反映我所期待的磨损数据变化。
胎温同样不能直接换算成胎耗。轮胎过热,可以提醒我注意持续高温带来的损耗风险,却不能据此说“还剩百分之多少”或者“还能跑三圈”。当前留下的是持续高胎温提醒,胎耗百分比、轮胎剩余寿命和换胎时机都没有被包装成现成功能。
天气雷达的限制更直接。游戏里能看到天气,不等于应用收到的遥测数据里也有对应信息。在目前能获得并验证的遥测数据中,我没有找到足以还原雷达、判断雨区移动或预测何时下雨的依据。仅凭车辆此刻的运动状态,也无法补出赛道远处的天气变化。
这些探索最后没有变成想象中的完整进站策略,确实有些遗憾。平台没有开放的信息,并不会因为多套几个公式就可以计算得出。也因此,在依赖胎耗或天气判断的比赛中,我仍建议保留游戏里必要的信息显示,如下图设置。保留这些游戏内信息,再搭配语音播报,可以兼顾准确信息与沉浸体验。
用车辆的实际运动,判断推头和甩尾
相比胎耗,车辆动态给了我更多可以验证的线索。
转向不足,俗称“推头”,可以粗略理解为:前轮已经转过去了,车却没有按预期转那么多。转向过度则可能表现为车尾向外滑,车辆转得比驾驶者希望的更多。
项目把前轮实际转角、车速和轴距放进一个简化的车辆运动学关系里,估算理论上的转弯响应:
理论横摆角速度 ≈ 车速 ÷ 轴距 × tan(前轮平均转角)
“横摆角速度”就是车身朝向每秒转过多少角度。拿估算结果与车辆实际的转动速度对照,再结合车身朝向和移动方向之间的夹角,就有了判断横向失稳的几条线索。
当然,公式本身不能直接充当结论。实际转动小于理论值,并不一定就是推头;轮胎打滑,也不一定就是甩尾。规则还要检查车速、状态是否持续、有没有反打方向,以及是否离地、越界或经过路肩。
目前的基础驾驶指导只覆盖持续驱动轮打滑、持续推头和明显持续甩尾。例如发现持续推头时,提醒减少转向角、适当降低入弯速度;发现明显车尾横滑时,提醒稳住方向、柔和控制油门。它们在“频繁”播报档中启用,也不是对每一次轻微车身摆动都作出播报。
这部分是我觉得项目里很有意思的一段:遥测数据中的原始数据,经过有依据的组合计算,确实能描述一小部分驾驶感受。但现有计算逻辑仍受标定样本限制,不能把已经跑通的场景直接外推到所有车型和所有工况。至于精确指导刹车点、走线和出弯油门曲线,那是我后面规划的功能。
让声音像无线电,也像一个人在说话
说完“什么时候说”和“说什么”,才轮到“怎么说”。
目前 PitRadio 使用云端 TTS,也就是把文字转换成语音的服务。采用这种大模型语音合成模型,是希望获得比以往常规 TTS 更连贯的发音、停顿和语气效果。不过,比赛判断与播报内容仍由规则引擎和播报内容模板生成,不让语言合成模型临场改写播报文案。
比如“1:32.876”,播出来应该是“一分三十二秒八七六”;这些转换在进入语音合成之前就完成了。圈速、名次、剩余圈数和赛车术语,都需要按口语重新组织。
粤语也有自己的整句模板和数字读法,不能只把普通话文案交给模型,再加一句“请用粤语说”。我想听到的是自然的表达,而不只是换了发音的书面中文。
动态播报采用整句合成,没有把“现在”“第三”“上一圈”之类的片段预先合成好再拼起来。拼接虽然能够节省语音合成的算力需求,但音高、停顿和句尾很容易断开,听久了尤其明显。
语气也需要克制。圈速汇报可以平静一点,燃油告警要清楚坚定,刷新最佳时可以带一点肯定,但不必变成热情的赛事解说。音频处理上再加少量无线电质感,让声音更贴近座舱里的交流。效果不能加得太重,否则发动机一响,最需要听清的数字反而被盖住了。
到底像不像一个人在说话,文字描述终究有限。我把真实游戏中的播报录屏放在了 PitRadio 官网,可以直接听普通话、粤语和英语的播报效果。
Vibe Coding 是真的 Vibe 吗?
做这个项目之前,我对 AI 辅助开发的好奇,主要是它能帮我写多少代码。做下来以后,我更在意的是:怎样让它持续朝着正确的方向工作,以及改完之后,我凭什么相信结果?
Agent 的能力越强,单次能够完成的工作越多,这两个问题反而越重要。
开发过程中,我也尝试落地一些 Harness 工程的理念,例如渐进式披露,把 AGENTS.md 作为目录,产品需求、技术架构和事件规则等各种项目信息进行分别归档,让 Agent 按需阅读,而不是一次塞入全部信息,包括某一次重要功能或者模块的开发也会归档这次的行为,也会对一些可能会反复出现的错误进行经验总结。这些工作做得越好,后续也就受益越多。

这个项目也让我更加认可测试驱动开发(TDD)的理念:先说清楚怎样才算做对,再去实现和验证。为了核对播报是否符合预期,我建立了原始遥测的录制与回放机制,让同一场比赛可以反复用于回归和冒烟测试,并要求回放与实时播报共用同一条处理流程。这样逐渐积累下来的上下文信息、真实样本和测试用例,反而是项目里最珍贵的资产,也让我在每次修改代码之后,有了可用来进行回归测试的用例,以及不同阶段,用来验证产品主流程的冒烟测试用例。同时再做一些主流程的集成测试和端到端测试,基本上能确保代码质量的下限。

架构选型同样需要认真取舍。AI 帮我更快地完成了不少小规模技术方案的验证,但方案能跑起来,不代表适合正式使用。比如我曾经考虑过自托管语音模型,经过延迟、听感和维护成本的比较,最终还是选择了云端服务。因此,开发速度提高之后,提前想清楚各部分的职责、验证关键假设,反而更加重要,否则省下的编码时间,很可能又花在后续返工上。
同样,规则判断、播报策略和语音输出需要保持清楚的边界。换一个语音服务,不应该连排名变化和燃油告警的逻辑都重写。AI 可以很快帮我完成一次修改,但边界如果没有想清楚,后面每一次修改都会更难验证。
所以,Vibe Coding 到底是不是很“Vibe”?至少在这个项目里,我没有体验到把想法丢给 Agent,当一个甩手掌柜,就能放心等成品的轻松。
变化更明显的是我自己的工作内容:手写代码的时间少了,定义问题、整理上下文信息、设计验证方法、核对真实结果的时间多了。有时最值得做的事,甚至是用一次实验确认某条路走不通,然后及时停下来。
不过,能从“我想在 GT7 里听到中文工程师”这样一个个人愿望出发,逐步把它做成可以使用的应用,这个过程还是很有意思的。
如果你也玩 GT7,尤其习惯戴着 PSVR2 游玩游戏,可以到 PitRadio 官网 看看演示、下载应用并申请公测。目前公测期间免费,因为服务器资源有限,所以语音播报需要申请通过后使用。目前应用支持 macOS 和 Windows 平台,后续计划推出 iOS/Android 应用。

