参数是PX4飞控中掉电保存的变量,用于存储飞控的设置、校准系数等数据。
PX4有一套完善的参数框架,对于用户而言只需要非常简单的几个语句就可以完成参数的添加、读取和写入操作。
这一篇我们简单聊一下PX4的参数机制,这部分内容不是所有同学都会用的到,不过有一个框架性的认识对于熟悉PX4的框架也有一定的帮助。
我们以1.11.3版本为例,简单的描述一下PX4的参数操作流程:
代码构建
在编译构建时PX4执行src/lib/parameters/CMakeLists.txt 中脚本文件 px_process_params.py ,这个脚本会历遍所有的原名目录,找到xxx_params.c文件,根据这个文件生成parameters.xml文件。这个文件自动创建在build/xxx/parameters.xml中。
然后执行 px_generate_params.py 脚本,根据parameters.xml文件生成参数头文件和c文件,生成的文件在build/xxx/src/lib/parameters目录下。
稍微阅读这几个文件,里面主要定义了所有参数的名称、数据类型和默认值。
载入参数文件
在PX4启动脚本(ROMFS/px4fmu_common/init.d/rcS)中执行参数载入操作从存储器中读取参数
# Set the parameter file if mtd starts successfully.
if mtd start
then
set PARAM_FILE /fs/mtd_params
fi
# Load parameters.
param select $PARAM_FILE
if ! param load
then
param reset
fi
加载参数文件后,会更新build/xxx/src/lib/parameters/px4_parameters.c文件在中px4_parameters结构体的数据,这就是PX4所有参数的参数表了,在文件尾部显示了参数总数,有近千个参数。
应用加载参数
应用中使用参数比较简单,添加几条语句就可以,我们以代码比较简单的src/drivers/heater/heater.cpp模块为例。
-
参数变量定义
在头文件中通过参数宏结构进行参数变量的定义
DEFINE_PARAMETERS( (ParamFloat<px4::params::SENS_IMU_TEMP_I>) _param_sens_imu_temp_i, (ParamFloat<px4::params::SENS_IMU_TEMP_P>) _param_sens_imu_temp_p, (ParamInt<px4::params::SENS_TEMP_ID>) _param_sens_temp_id, (ParamFloat<px4::params::SENS_IMU_TEMP>) _param_sens_imu_temp )
-
更新参数
在源文件中添加变量更新函数,检测变量是否发生变化,更新变量
void Heater::update_params(const bool force) { // check for parameter updates if (_parameter_update_sub.updated() || force) { // clear update parameter_update_s pupdate; _parameter_update_sub.copy(&pupdate); // update parameters from storage ModuleParams::updateParams(); } }
应用加载与更新参数的机制
在应用的头文件中,通过 DEFINE_PARAMETERS 宏完成参数变量类的定义,我们可以看一下这个宏的内容:
#define _DEFINE_SINGLE_PARAMETER(x) \
do_not_explicitly_use_this_namespace::PAIR(x);
#define _CALL_UPDATE(x) \
STRIP(x).update();
#define _DEFINE_PARAMETER_UPDATE_METHOD(...) \
protected: \
void updateParamsImpl() final { \
APPLY_ALL(_CALL_UPDATE, __VA_ARGS__) \
} \
private:
#define DEFINE_PARAMETERS(...) \
APPLY_ALL(_DEFINE_SINGLE_PARAMETER, __VA_ARGS__) \
_DEFINE_PARAMETER_UPDATE_METHOD(__VA_ARGS__)
从内容我们可以大致了解,这里使用命名空间 do_not_explicitly_use_this_namespace中参数类定义参数变量,并且定义了应用中所有参数的更新类updateParamsImpl,这个函数在上一部分的ModuleParams::updateParams()中调用,里面的实现为
class ModuleParams
{
virtual void updateParams()
{
for (const auto &child : _children) {
child->updateParams();
}
updateParamsImpl();
}
}
在参数更新时,使用STRIP(x).update() 方法更新应用中每个使用到的参数,这个方法定义在do_not_explicitly_use_this_namespace中:
bool update()
{
int32_t value_int;
int ret = param_get(handle(), &value_int);
if (ret == 0) {
_val = value_int != 0;
return true;
}
return false;
}
这些宏定义中值得注意的是APPLY_ALL()方法,这个宏定义使代码中展开复杂的结构,为应用的头文件中定义的所有变量展开变量定义和update()方法,其目的是使代码更加简洁。
这样的宏定义方式大家感兴趣也可以参考一下,减少重复性代码可以使自己的代码结构更加简洁清晰。
参数更新消息
可以发现PX4使用了uORB机制向所有使用参数的应用广播参数的变化,每个使用参数的应用都会订阅 uORB::Subscription _parameter_update_sub{ORB_ID(parameter_update)}; 消息。在检测到参数更新时去更新应用使用到的参数值。
这样的设计有好处是处理方式统一,结构简洁。
当然它的代价是在一个参数发生更新时,所有的应用都会收到更新通知去检查更新自己使用的参数。
总结
以上是PX4的参数系统的大致机制,当然还有很多非常有意思的细节信息没有展开。这些信息对于使用而言没有作用不大,对于希望磨砺自己代码能力的同学却是不错的参考,里面使用到的宏定义、模板类、代码生成等等都是不错的学习参考。
–
我的微信公众号,文章同步更新,欢迎关注。