第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的核心知识点串起来了。你可以把它当作速查手册,写配置时瞄一眼:
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基础就讲到这里。说白了就是三句话:缩进定层级,冒号定键值,短横定列表。把这些记牢了,后面写速度规划配置的时候就不会在格式上栽跟头。