一开始我也觉得参数就是把配置从代码挪到 YAML,纯整理工作。后来在一个节点上把 buffer_size 做成可运行时修改,改完没崩,但分配好的缓冲区大小没跟着变,行为变得很奇怪。
那时候才明白,参数不只是“可运行时修改的变量”,它带类型、默认值、约束和变更通知。这些没声明清楚,参数系统就没法判断这次改动是否合法。
判断标准:这次改动要不要重建内部结构
参数的名称和类型在声明时固定,值可以被覆盖。所以不是所有参数都适合做成可运行时修改。如果它决定内存布局、队列容量或者数组维度,中途改它可能要求重新分配资源,而参数回调里通常不适合干这个。
我自己的判断很简单:这次修改要不要重建内部结构。要的话,这个参数更适合当作启动参数,变更时直接拒绝,或者要求重启。
范围写在声明里
最基础的校验是范围,声明时就能表达:
1 | node->declare_parameter("control_period_ms", 10, |
声明之后,越界的覆盖会被直接拒掉。这一步能挡下大量配置错误,而且是在修改的那一刻拒绝,不用等到控制循环里出现除零或者数组越界。
单参数约束表达不了的是关系。比如“最小速度必须小于最大速度”,或者“超时时间必须小于父任务预算”。这类得靠参数回调。
参数回调是最后一道防线
1 | node->add_on_set_parameters_callback( |
回调返回失败,这次修改就不生效。这比“先接受,下一周期再发现不一致”安全,因为拒绝是原子的,不会留下应用了一半的配置。
回调里别干重活。参数修改是低频操作,但如果在回调里加锁等控制线程,就可能反过来影响控制循环的时序。需要重建资源的变更,更稳妥的是标记成“重启后生效”。
三层验证
一次完整的参数验证至少覆盖:默认值能不能用、覆盖有没有按预期生效、非法值有没有被拒。
1 | # 看实际生效的参数和类型 |
第三条最容易被跳过。参数文件的路径、节点名匹配规则和命令行优先级如果不记录,就会出现“改了文件没生效”,而真实原因是节点名和 YAML 里的键对不上,或者命令行把它覆盖了。
别拿参数当状态同步
参数适合描述配置,不适合承载运行状态。用参数发布当前速度、剩余电量或者任务进度,会让修改语义变乱:外部到底是在读状态,还是想改它。
这类信息该走话题或服务。参数的价值在于“可声明、可校验、可记录”的配置契约,一旦用来传高频状态,校验和语义都没意义了。
参考资料
证据边界:本文不含任何具体节点的参数清单。上面的范围约束和回调是通用结构,某个参数到底适不适合运行时修改,取决于它会不会触发资源重建,得按节点判断。