跳转至

视觉、运动控制与底层 Skill

这一页更像是在讲“执行层的气质”。如果 Python 顶层是教练席,那么这里讲的视觉、路径规划、运动控制和底层 Skill,就是场上真正干活的那批人。它们不是各自独立的小模块,而是一条从“看见世界”到“算出控制命令”的连续链。

视觉模块:整套系统共享的世界状态入口

Medusa/src/Vision/VisionModule.h 对自己的定位其实非常准确:它负责接收原始视觉数据、做滤波与预测、维护球和机器人的世界状态、处理裁判盒消息,并结合下位机反馈做修正。

对上层开发者来说,最重要的不是记住它内部每个滤波细节,而是先建立一个稳定认知:

VisionModule 是这一帧全场状态的权威来源。

无论你在 Python 里调用 Vision.ball()Player.pos(),还是在 C++ Skill 里读取 pVision->ourPlayer(i)pVision->ball(),本质上都在围绕同一份世界状态工作。

上层最常从视觉层拿什么

战术开发里最常用的视觉信息通常有这些:

  • 我方 / 对方球员状态;
  • 当前球位置和速度;
  • 当前 cycle;
  • 当前裁判盒消息;
  • 摆球目标点;
  • 以及一些结合传感器和世界模型推出来的高层条件。

这些能力后面又会通过 pybind 暴露到 CppPackage.VisionModule 以及 Python 侧的包装模块中。所以你可以把视觉层理解成“所有上层判断的共同地基”。

为什么底层 Skill 主要还留在 C++

原因很简单,但很硬核:很多底层行为既要高频执行,又要同时满足复杂几何约束、路径规划、避障规则和实时性要求。把这部分长期留在 C++,能保证整套系统不会因为顶层脚本改得频繁,就把执行层的实时性拖垮。

所以现在的自然分工是:

  • Python 决定这一帧该选哪种 Skill;
  • C++ Skill 再决定这个 Skill 到底如何落地执行。

这也是为什么你会经常看到 Python 侧说“让这台车去 RushTo / WBack / TurnAndShoot”,但真正的规划细节都在 Medusa/src/Strategy/skill/ 下面。

最值得反复看的底层样本:SmartGotoPositionV4

如果你只挑一个底层 Skill 来认真读,SmartGotoPositionV4.cpp 依然是最推荐的起点。它几乎把执行层最典型的思维方式都浓缩在一起了。

这份文件会先读取当前 TaskT 的目标点、朝向、速度和标志位,再结合 TaskMediator 判断当前执行者是不是守门员、后卫、主攻等特殊角色。接着它会根据角色身份调整运动能力上限,处理 stop 球圈、摆球区、禁区等规则约束,构造障碍物集合,调用路径规划模块生成中间点,再把结果继续往更底层的轨迹控制任务交下去。

这段流程特别能体现本队底层 Skill 的一个共同特点:它们不是只做一条直线上的数学计算,而是在把战术语义、比赛规则、角色身份和几何约束一起折进执行层。

TaskMediator 在这里为什么又出现了

你会发现,越往底层读,TaskMediator 反而越常出现。原因是很多执行细节都依赖“这台车当前是什么角色”。

例如在 SmartGotoPositionV4 里,系统会去看:

  • 这台车是不是 goalie;
  • 是不是 back;
  • 是否属于某些特殊执行身份。

这些信息不是路径规划器自己凭空知道的,而是通过顶层 registerRole() 写进 TaskMediator,再被底层 Skill 读出来。所以 TaskMediator 的角色标签,不只是给调试看,而是真的会影响运动能力和规则处理方式。

Skill、路径规划、运动控制之间怎么分层更好理解

读这部分代码时,最有效的方式不是从数学公式往上抠,而是先把它粗分成三层:

  1. Skill 层:决定“我要去哪、为什么去、带着什么比赛语义去”;
  2. 路径规划层:决定“在当前障碍和规则约束下,该走哪条路更合适”;
  3. 运动控制层:决定“沿着这条路,具体该怎么给速度、加速度和转向命令”。

当前仓库里大致对应的代码位置是:

  • Strategy/skill/*
  • PathPlan/*
  • MotionControl/*

只要先把这三层想清楚,后面即使没有一下子吃透每个数学细节,也不会迷路。

读一个底层 Skill 时,应该盯哪三件事

很多人读 Skill 容易陷进“这一行公式在算什么”,结果读了半天还是不知道这份 Skill 在整条链里扮演什么角色。更推荐的方式是始终盯住这三个问题:

  1. 它为什么会在这一帧被选中;
  2. 它从 TaskT、视觉层和 TaskMediator 里拿了哪些输入;
  3. 它最后把什么子任务继续往下交。

比如 TurnAndShoot 不是简单“转身就踢”,它先判断是否已经控住球和对准方向,再把继续调姿态的工作交给 GetBallV4SmartGotoPositionV4 也不是简单“算一条轨迹”,它更像是把比赛规则和障碍条件先融进目标表达,再继续下发底层轨迹任务。

防守、盯人、接球类 Skill 的共同味道

WMarkingWBackGetBallV4/V5CrossoverZAttackV2 这些 Skill,看上去作用差很多,但它们有几个共同特征:

  • 都强依赖当前球和机器人状态;
  • 都会受 TaskMediator 的角色标签影响;
  • 都不是独立运行的小岛,而是和同一套路径规划与运动控制框架协作;
  • 最后几乎都会继续把任务递归下发到更基础的动作层。

所以读它们时,千万不要只盯某个局部几何公式。更有价值的问题往往是:“这个 Skill 现在代表哪种比赛语义,它想把哪件事交给下游执行引擎完成?”

第一次深入这部分代码,建议怎么读

建议你把顺序定成:

  1. 先看 VisionModule 这一层给出了什么世界状态;
  2. 再看一个典型 Skill,例如 SmartGotoPositionV4
  3. 然后顺着它看到 PathPlanMotionControl
  4. 最后再回头看其他防守或接球 Skill。

这样读的好处是,你不会一上来就被各类特殊 Skill 的业务语义淹没,而能先建立“执行层到底是怎样把一个目标点变成实际控制命令”的总感觉。