第3章 YAML基础入门:语法规则、缩进与层级关系

说实话,我刚接触自动驾驶系统时,最头疼的不是算法本身,而是配置文件。那时候各家用的格式五花八门,有的用XML,有的用JSON,还有直接用C++头文件当配置的。直到后来团队统一用YAML,整个世界清静了。

YAML这玩意儿,说白了就是「人类可读的数据序列化格式」。它的设计哲学很直白——让你用眼睛扫一眼就能看懂配置结构。在速度规划里,我们用它来定义场景参数、车辆约束、优化权重,几乎所有的调参工作都在YAML文件里完成。

核心认知:YAML不是编程语言,它不执行逻辑。它只做一件事——用结构化的方式描述数据。你写的每一行配置,最终都会被解析成Python字典或C++结构体。

3.1 缩进与层级关系——YAML的命根子

YAML用缩进来表达层级关系。这一点和Python很像,但比Python更严格。我见过太多新人因为缩进问题排查半天,最后发现是空格和Tab混用了。

规则只有三条:

  • 同一层级的元素必须左对齐
  • 子层级比父层级多缩进2个空格(我习惯用2个,也有人用4个,但团队必须统一)
  • 绝对不要用Tab——这是铁律

来看个实际例子。这是速度规划里常见的场景配置:

# 场景参数定义
scenario:
  type: highway
  speed_limit: 120
  curvature:
    min_radius: 250
    max_radius: 1000
  obstacles:
    - id: 1
      type: vehicle
      speed: 80
    - id: 2
      type: pedestrian
      speed: 5

你看,scenario下面缩进了type、speed_limit、curvature和obstacles,它们都是scenario的子节点。curvature下面又缩进了min_radius和max_radius,这是两层嵌套。

我曾经踩过的坑:有一次我写了一个多层的配置,编辑器里看着对齐了,但解析器一直报错。折腾了半小时才发现,某一行我用了3个空格,其他行都是2个。YAML解析器对空格数量极其敏感,多一个少一个都不行。

3.2 列表与字典——YAML的两种基本数据结构

YAML里就两种核心结构:字典(Mapping)和列表(Sequence)。搞懂这两个,你就掌握了YAML的80%。

字典(Mapping)

字典就是键值对。用key: value的格式,冒号后面必须跟一个空格。我习惯把字典理解成「属性的集合」:

# 车辆参数 - 字典结构
vehicle:
  mass: 1500
  wheelbase: 2.8
  max_steering_angle: 0.6
  tire_friction: 0.85

字典可以嵌套,嵌套就是值本身又是一个字典:

# 嵌套字典
controller:
  pid:
    kp: 1.2
    ki: 0.05
    kd: 0.1
  mpc:
    horizon: 20
    dt: 0.1

列表(Sequence)

列表用短横线-开头,后面跟一个空格。它表示一组有序的元素:

# 路径点序列 - 列表结构
waypoints:
  - x: 0.0
    y: 0.0
    speed: 10
  - x: 10.0
    y: 5.0
    speed: 15
  - x: 20.0
    y: 10.0
    speed: 20

你看,每个-代表一个列表元素,而每个元素本身又是一个字典。这种「列表里套字典」的模式,在速度规划配置里随处可见。

我的个人习惯:当列表元素是简单值时,我会写成一行:obstacle_ids: [1, 3, 5, 7]。当元素是复杂对象时,用多行格式,每个元素独立一行,清晰可读。

3.3 注释——你的配置需要「说明书」

YAML的注释用#开头。注释可以单独一行,也可以跟在代码后面。我强烈建议你在每个关键参数后面加注释,说明它的单位、取值范围或者物理含义。

# 速度规划核心参数
planning:
  max_speed: 33.3      # 最大速度,单位 m/s (约120km/h)
  min_speed: -2.0      # 最小速度,倒车场景,单位 m/s
  max_accel: 3.0       # 最大加速度,单位 m/s²
  max_jerk: 2.0        # 最大加加速度,单位 m/s³
  comfort_accel: 1.5   # 舒适加速度阈值,单位 m/s²

为什么要写注释?因为三个月后你回来看自己的配置,大概率会忘记max_jerk: 2.0到底是什么意思。我吃过这个亏——有一次上线前调参,忘了某个参数的单位是km/h还是m/s,差点把车开飞起来。

3.4 多行字符串——长文本的优雅写法

有时候你需要写一段较长的描述文本,比如场景说明或者算法备注。YAML提供了两种多行字符串的写法:

写法 符号 行为 示例
保留换行 | 保留所有换行符 description: |
第一行
第二行
折叠换行 > 将换行替换为空格 description: >
这是一个很长的
描述文本

实际使用场景:

# 保留换行 - 用于代码片段或多段文本
algorithm_note: |
  本场景使用S-T图优化方法。
  纵向规划采用动态规划+二次规划。
  横向规划采用五次多项式。

# 折叠换行 - 用于长段落描述
scenario_description: >
  这是一个高速公路汇入场景,
  主路车辆速度为100km/h,
  匝道车辆需要加速至80km/h后汇入。
  汇入点位于里程桩K123+456处。

我个人更常用|,因为保留换行让配置看起来结构更清晰。但如果你需要把一段长文本塞进某个字段(比如日志消息),>会更合适。

3.5 本章知识体系总览

下面这张图把YAML的核心知识点串起来了。你可以把它当作速查手册,写配置时瞄一眼:

YAML基础语法知识体系 YAML 配置文件 缩进规则 核心要点 • 同一层级左对齐 • 子层级缩进2空格 • 禁止使用Tab 数据结构 字典 key: value 属性集合 列表 - item 有序序列 辅助功能 注释 & 多行字符串 • # 单行注释 • | 保留换行 • > 折叠换行 一句话总结 缩进定层级,冒号定键值,短横定列表

3.6 避坑指南——我踩过的那些YAML的坑

最后分享几个实战中容易翻车的地方:

  • 布尔值的坑:YAML里yes、true、on都会被解析为布尔值True。如果你要表示字符串"yes",记得加引号:"yes"。我见过有人写enable: yes,结果解析出来是布尔值,后面做字符串比较时死活对不上。
  • 数字的坑:012在YAML里会被解析为八进制数10,而不是十进制12。如果你要表示字符串"012",必须加引号。这个坑我踩过两次才长记性。
  • 空值的坑:YAML里null、~、什么都不写都表示空值。但不同解析器的行为可能略有差异,我建议统一用null,清晰明确。
  • 重复键的坑:YAML允许重复的键,但后面的值会覆盖前面的。解析器不会报错,但你的配置可能和你想象的不一样。写配置时务必检查是否有重复键。

我的调试小技巧:写完YAML配置后,先用python -c "import yaml; yaml.safe_load(open('config.yaml'))"验证一下。如果解析成功,说明语法没问题。这一步花不了10秒钟,但能省下你半小时的排查时间。

嗯,YAML基础就讲到这里。说白了就是三句话:缩进定层级,冒号定键值,短横定列表。把这些记牢了,后面写速度规划配置的时候就不会在格式上栽跟头。