拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PX4 完整应用模块开发指南:Work Queue Task 与独立 Task 模板详解

PX4 完整应用模块开发指南:Work Queue Task 与独立 Task 模板详解 嵌入式物联网机器人自动驾驶智能硬件【免费下载链接】PX4-AutopilotPX4 Autopilot Software项目地址https://gitcode.com/gh_mirrors/px/PX4-Autopilot点击查看免费下载本篇指南以 PX4-Autopilot 仓库中的官方模块模板为核心系统讲解如何在 PX4 中编写一个完整应用模块既包括以**工作队列任务Work Queue Task方式运行的轻量模块也包括拥有独立栈与优先级的后台任务Task**模块。读完本文你将掌握px4_add_module构建配置、ScheduledWorkItem调度 API、ModuleBase生命周期、uORB 订阅/发布、参数响应、命令行参数解析以及如何把模块接入系统启动脚本。两种模块运行形态Task 与 Work Queue TaskPX4 中的应用模块既可以作为独立的task拥有自己的栈和进程优先级也可以作为work queue task运行在共享的工作队列线程上与队列中的其他任务共享栈和线程优先级。在大多数情况下优先选择 work queue task因为它能最大化降低资源占用RAM 更少、任务切换更少。两者的取舍可以从 架构概览 运行时环境 中看到更完整的描述Task模块拥有自己的栈和调度优先级可以独立sleep/阻塞等待消息Work Queue Task多个任务可共用一个队列且系统中可以有多个队列任务通过“指定未来某个时间点”或“uORB 主题更新回调”来被调度。其限制是不允许睡眠、不允许轮询阻塞消息、不允许执行阻塞式 I/O例如读文件重计算的长任务应放在独立 task 或至少独立的工作队列上。另外注意运行在 work queue 上的任务不会出现在top命令的模块列表中只能看到队列本身如wq:lp_default需要使用work_queue status查看所有活跃的工作队列条目。在动手之前建议先完成 首个应用教程Hello Sky——该教程讲述的基础概念uORB、任务循环、构建系统在编写完整应用时同样适用。方式一编写 Work Queue Task 模块PX4-Autopilot 仓库自带一个完整的 work queue task 模块模板src/examples/work_item包含WorkItemExample.cpp、WorkItemExample.hpp、CMakeLists.txt和Kconfig四个文件。一个 work queue task 应用与普通 task 应用大体相同区别仅在于需要在构建文件中声明对工作队列库的依赖并在初始化时将自己调度到某个队列上。第 1 步在 CMake 中声明依赖在模块的 CMakeLists.txt 中使用px4_add_module声明模块并在DEPENDS中显式加入px4_work_queuepx4_add_module( MODULE examples__work_item MAIN work_item_example SRCS WorkItemExample.cpp WorkItemExample.hpp DEPENDS px4_work_queue )其中MODULE模块在构建系统中的唯一标识采用目录路径__文件名的命名习惯MAIN生成的可执行目标名称对应模块入口函数work_item_example_mainDEPENDS px4_work_queue这是 work queue 任务与普通任务在构建配置上的关键区别缺少该依赖将无法链接工作队列相关符号。调试提示模板中的COMPILE_FLAGS预留了-DDEBUG_BUILD启用PX4_DEBUG输出和-O0关闭优化两个注释项排查问题时可以取消注释。第 2 步继承 ScheduledWorkItem除了ModuleBase之外work queue 任务还需要继承px4::ScheduledWorkItem。该头文件位于 platforms/common/include/px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp它提供了一组基于高分辨率定时器hrt的调度原语方法作用ScheduleDelayed(delay_us)延迟delay_us微秒后执行下一次Run()ScheduleOnInterval(interval_us, delay_us 0)按固定周期重复执行可附带初始延迟ScheduleAt(time_us)在指定的绝对时间点执行ScheduleClear()清除已安排的调度Scheduled()查询当前是否有待执行的调度模板中的类声明见 WorkItemExample.hppclass WorkItemExample : public ModuleBase, public ModuleParams, public px4::ScheduledWorkItem { public: static Descriptor desc; WorkItemExample(); ~WorkItemExample() override; static int task_spawn(int argc, char *argv[]); static int custom_command(int argc, char *argv[]); static int print_usage(const char *reason nullptr); bool init(); int print_status() override; private: void Run() override; // ... 订阅、发布、性能计数器、参数声明 };第 3 步构造函数中指定目标工作队列在构造函数初始化列表中将模块挂到某个工作队列配置wq_configurations上。模板挂到了px4::wq_configurations::test1见 WorkItemExample.cppWorkItemExample::WorkItemExample() : ModuleParams(nullptr), ScheduledWorkItem(MODULE_NAME, px4::wq_configurations::test1) { }ScheduledWorkItem构造函数的第一个参数MODULE_NAME是模块名第二个参数是队列配置对象wq_config_t。所有可用的工作队列配置定义在 platforms/common/include/px4_platform_common/px4_work_queue/WorkQueueManager.hpp 的wq_configurations命名空间中包括驱动相关SPI0–SPI6、I2C0–I2C4、ttyS0–ttyS9、ttyACM0、uavcan驱动通常按总线挂载系统也提供了device_bus_to_wq()、serial_port_to_wq()辅助函数做总线到队列的映射控制/估计相关rate_ctrl速率控制、nav_and_controllers姿态/位置控制器注释标明是传感器之后最高优先级、INS0–INS3惯性导航、vte通用队列hp_default高优先级默认队列、lp_default低优先级默认队列测试专用test1、test2栈大小 2000 字节、相对优先级 0模板即使用这两个队列。每个队列的栈大小与相对优先级均由 KConfig 选项如CONFIG_WQ_HP_DEFAULT_STACKSIZE在构建时生成可通过菜单配置调整。第 4 步实现 Run() 方法ScheduledWorkItem::Run()是纯虚函数必须在子类中实现——它就是“工作”本体。模板的Run()展示了典型的处理模式见 WorkItemExample.cppvoid WorkItemExample::Run() { if (should_exit()) { ScheduleClear(); exit_and_cleanup(desc); return; } perf_begin(_loop_perf); perf_count(_loop_interval_perf); // 检查参数是否发生变化 if (_parameter_update_sub.updated()) { parameter_update_s param_update; _parameter_update_sub.copy(param_update); updateParams(); // 更新 DEFINE_PARAMETERS 中声明的模块参数 } // 示例订阅 vehicle_status 检查解锁状态 if (_vehicle_status_sub.updated()) { vehicle_status_s vehicle_status; if (_vehicle_status_sub.copy(vehicle_status)) { const bool armed (vehicle_status.arming_state vehicle_status_s::ARMING_STATE_ARMED); if (armed !_armed) { PX4_WARN(vehicle armed due to %d, vehicle_status.latest_arming_reason); } else if (!armed _armed) { PX4_INFO(vehicle disarmed due to %d, vehicle_status.latest_disarming_reason); } _armed armed; } } // 示例获取最新加速度计数据 if (_sensor_accel_sub.updated()) { sensor_accel_s accel; if (_sensor_accel_sub.copy(accel)) { // DO WORK // 访问参数值如 SYS_AUTOSTART if (_param_sys_autostart.get() 1234) { // do something if SYS_AUTOSTART is 1234 } } } // 示例发布数据 orb_test_s data{}; data.val 314159; data.timestamp hrt_absolute_time(); _orb_test_pub.publish(data); perf_end(_loop_perf); }要点说明should_exit()用于响应stop命令退出前先ScheduleClear()清除调度再调用exit_and_cleanup(desc)perf_begin/perf_end是 PX4 的性能计数器lib/perf用于统计单次循环耗时print_status()通过perf_print_counter输出_parameter_update_sub是uORB::SubscriptionInterval以 1 Hz 的频率轮询parameter_update主题配合updateParams()实现参数热更新_sensor_accel_sub是uORB::SubscriptionCallbackWorkItem它不仅是订阅还负责在主题更新时触发本工作项调度。第 5 步实现 task_spawn 并声明为工作队列任务task_spawn负责创建模块实例。与普通 task 的关键区别是将desc.task_id设为task_id_is_work_queue表示该模块运行在工作队列上见 WorkItemExample.cppint WorkItemExample::task_spawn(int argc, char *argv[]) { WorkItemExample *instance new WorkItemExample(); if (instance) { desc.object.store(instance); desc.task_id task_id_is_work_queue; if (instance-init()) { return PX4_OK; } } else { PX4_ERR(alloc failed); } delete instance; desc.object.store(nullptr); desc.task_id -1; return PX4_ERROR; }模块描述符在文件顶部统一注册ModuleBase::Descriptor WorkItemExample::desc{task_spawn, custom_command, print_usage};第 6 步调度工作项调度发生在init()中。模板展示了两种典型调度方式见 WorkItemExample.cppbool WorkItemExample::init() { // 方式一每次 sensor_accel 发布新消息时触发 Run() if (!_sensor_accel_sub.registerCallback()) { PX4_ERR(callback registration failed); return false; } // 方式二替代固定周期调度例如 5000 us 周期200 Hz // ScheduleOnInterval(5000_us); return true; }事件驱动registerCallback()将 uORB 订阅与工作队列绑定主题一旦更新即触发Run()。这是传感器/控制器类模块的主流做法仓库中大量模块采用同一模式例如 attitude_estimator_q_main.cpp 对sensors主题、battery_status.cpp 对adc_report主题都使用registerCallback()驱动并在退出时调用unregisterCallback()解绑定时调度ScheduleOnInterval(5000_us)按固定周期执行适合周期性采集、状态刷新的场景ScheduleDelayed/ScheduleAt则用于一次性或指定时刻执行。模块入口与命令行用法最后通过extern C导出入口函数交给ModuleBase::main统一处理start/stop/status等命令见 WorkItemExample.cppextern C __EXPORT int work_item_example_main(int argc, char *argv[]) { return ModuleBase::main(WorkItemExample::desc, argc, argv); }print_usage使用PRINT_MODULE_*宏生成控制台帮助work_item_example help与 模块与命令参考 页面PRINT_MODULE_DESCRIPTION(RDESCR_STR( ### Description Example of a simple module running out of a work queue. )DESCR_STR); PRINT_MODULE_USAGE_NAME(work_item_example, template); PRINT_MODULE_USAGE_COMMAND(start); PRINT_MODULE_USAGE_DEFAULT_COMMANDS();PRINT_MODULE_USAGE_DEFAULT_COMMANDS()会展开为stop和status打印状态信息两条命令见 platforms/common/include/px4_platform_common/module.h 中的定义。方式二编写独立 Task 模块需要拥有独立栈、允许阻塞操作或进行重计算时应使用独立 task。仓库模板位于 src/templates/template_module包含template_module.cpp、template_module.h、CMakeLists.txt与Kconfig。该模板演示了完整应用所需的全部进阶要素访问参数并对参数更新做出响应uORB 订阅并等待主题更新通过start/stop/status控制后台任务module start [arguments]可直接写入启动脚本命令行参数解析文档化PRINT_MODULE_*方法。生命周期与入口与 work queue 版本不同独立 task 的task_spawn通过px4_task_spawn_cmd真正创建系统任务见 template_module.cppint TemplateModule::task_spawn(int argc, char *argv[]) { desc.task_id px4_task_spawn_cmd(module, SCHED_DEFAULT, SCHED_PRIORITY_DEFAULT, 1024, (px4_main_t)run_trampoline, (char *const *)argv); if (desc.task_id 0) { desc.task_id -1; return -errno; } return 0; }px4_task_spawn_cmd的参数依次为任务名、调度策略SCHED_DEFAULT、优先级SCHED_PRIORITY_DEFAULT、栈大小模板为 1024 字节以及任务入口。任务最终通过run_trampoline跳转到run()主循环。主循环订阅、参数更新与休眠run()是独立任务的主循环典型结构是“订阅检查 参数更新 短休眠”见 template_module.cppvoid TemplateModule::run() { // 初始化参数 parameters_update(true); while (!should_exit()) { if (_sensor_accel_sub.updated()) { sensor_accel_s accel{}; _sensor_accel_sub.copy(accel); // TODO: do something with the data... } parameters_update(); px4_usleep(10_ms); } }与 work queue 任务不同独立任务可以调用px4_usleep让出 CPU通过轮询updated()copy()消费 uORB 主题数据并以should_exit()作为退出条件。参数更新机制parameters_update()封装了参数热更新逻辑见 template_module.cpp首次调用传入forcetrue强制同步之后每当parameter_update主题有更新就调用updateParams()刷新DEFINE_PARAMETERS中声明的参数句柄void TemplateModule::parameters_update(bool force) { if (_parameter_update_sub.updated() || force) { parameter_update_s update; _parameter_update_sub.copy(update); updateParams(); // 从存储中更新参数 } }DEFINE_PARAMETERS在头文件中声明模块用到的参数见 template_module.h。这里的示例直接绑定了两个系统级参数SYS_AUTOSTART和SYS_AUTOCONFIG实际开发中应声明自己的模块参数param_idDEFINE_PARAMETERS( (ParamIntpx4::params::SYS_AUTOSTART) _param_sys_autostart, /** example parameter */ (ParamIntpx4::params::SYS_AUTOCONFIG) _param_sys_autoconfig /** another parameter */ )命令行参数解析instantiate()中演示了使用px4_getopt解析-p整数参数和-f布尔标志两个自定义选项见 template_module.cppwhile ((ch px4_getopt(argc, argv, p:f, myoptind, myoptarg)) ! EOF) { switch (ch) { case p: example_param (int)strtol(myoptarg, nullptr, 10); break; case f: example_flag true; break; case ?: error_flag true; break; default: PX4_WARN(unrecognized flag); error_flag true; break; } }对应的用法说明在print_usage中通过PRINT_MODULE_USAGE_PARAM_FLAG和PRINT_MODULE_USAGE_PARAM_INT声明见 template_module.cppPRINT_MODULE_USAGE_NAME(module, template); PRINT_MODULE_USAGE_COMMAND(start); PRINT_MODULE_USAGE_PARAM_FLAG(f, Optional example flag, true); PRINT_MODULE_USAGE_PARAM_INT(p, 0, 0, 1000, Optional example parameter, true); PRINT_MODULE_USAGE_DEFAULT_COMMANDS();PRINT_MODULE_USAGE_PARAM_INT的参数依次为选项字符、默认值、最小值、最大值、描述与是否可选。模板文档中的### Examples一节给出了完整调用示例module start -f -p 42。自定义命令支持custom_command是start/stop/status之外扩展自定义命令的入口。模板中预留了do-something命令的写法见 template_module.cpp先通过is_running(desc)判断模块是否在运行再通过get_instanceTemplateModule(desc)获取运行中的实例并调用其方法。PRINT_MODULE_*一份代码两个用途模板强调PRINT_MODULE_*宏具有双重职责API 定义见 platforms/common/include/px4_platform_common/module.h控制台帮助在控制台输入module help时打印命令行用法文档生成构建时由脚本Tools/px4moduledoc中的解析器自动提取这些宏生成 模块与命令参考 页面保证文档与源码实时同步。因此为模块编写规范的PRINT_MODULE_DESCRIPTION### Description/### Implementation/### Examples三段式与PRINT_MODULE_USAGE_*声明既是开发习惯也是模块文档化的必要条件。可用的宏包括PRINT_MODULE_USAGE_NAME、PRINT_MODULE_USAGE_SUBCATEGORY、PRINT_MODULE_USAGE_COMMAND(_DESCR)、PRINT_MODULE_USAGE_PARAM_INT/FLOAT/FLAG/STRING、PRINT_MODULE_USAGE_ARG以及为 I2C/SPI 驱动准备的PRINT_MODULE_USAGE_PARAMS_I2C_SPI_DRIVER等。构建与接入启动流程构建配置模块的Kconfig文件提供编译开关如 work_item 的 Kconfigmenuconfig EXAMPLES_WORK_ITEM bool work_item default n ---help--- Enable support for work_item构建时通过make px4_fmu-v5或对应目标板编译并通过px4_add_module自动纳入构建系统src/modules/CMakeLists.txt与各板级配置会汇总所有模块。接入启动脚本模块编译并烧录后通过 NuttShell/MAVLink Shell 执行module start即可手动启动。若需开机自启将命令加入系统启动脚本。启动流程以 ROMFS/px4fmu_common/init.d/rcSPosix 平台为 ROMFS/px4fmu_common/init.d-posix/rcS为入口脚本按机型配置依次调用各模块的start详见 系统启动。例如module start -f -p 42带参数的启动命令可直接写入对应的init.d脚本中实现开机自动加载。两种形态的选型建议维度Work Queue Task独立 Task栈与优先级共享队列线程的栈与优先级独立栈模板示例 1024 字节与独立优先级资源占用低共享栈、少切换较高调度方式uORB 回调registerCallback或定时ScheduleOnInterval等自循环 px4_usleep能否阻塞否禁止 sleep、阻塞轮询、阻塞 I/O可以适用场景传感器回调驱动、周期性轻任务、控制器重计算、需要独立优先级与栈、长任务从源码结构看PX4 的传感器驱动、姿态/位置控制器、EKF 等核心模块大量采用 work queue 模式它们都继承ScheduledWorkItem并依赖px4_work_queue而需要深度运算或强实时性的模块则保留独立任务形态。新模块开发应优先评估 work queue 方案仅在确有阻塞或长计算需求时选择独立 task。小结本文以仓库内两个官方模板为主线完整梳理了 PX4 模块开发的全链路构建配置px4_add_moduleDEPENDS px4_work_queue、类设计ModuleBaseScheduledWorkItem或独立run()循环、uORB 数据通路Subscription/Publication/SubscriptionCallbackWorkItem、参数响应DEFINE_PARAMETERSupdateParams()、CLI 解析px4_getopt与文档化PRINT_MODULE_*。参考模板文件即可快速搭建自己的应用Work queue 形态src/examples/work_item/WorkItemExample.cpp 与 src/examples/work_item/WorkItemExample.hpp独立 task 形态src/templates/template_module/template_module.cpp 与 src/templates/template_module/template_module.h调度 APIplatforms/common/include/px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp队列配置清单platforms/common/include/px4_platform_common/px4_work_queue/WorkQueueManager.hpp赞分享嵌入式物联网机器人自动驾驶智能硬件【免费下载链接】PX4-AutopilotPX4 Autopilot Software项目地址https://gitcode.com/gh_mirrors/px/PX4-Autopilot点击查看免费下载相关推荐TASK: 用户认证模块开发TASK: 用户认证模块开发 负责人: 张三 截止日期: 2025 11 15 需求: 实现基于JWT的用户认证系统包括注册、登录和权限验证 验收标准: 完整文档教程提示工程人工智能PX4-Autopilot 模块参考Template 模板模块与自定义模块开发指南PX4 Autopilot 模块参考Template 模板模块与自定义模块开发指南 导读 本文基于 PX4 Autopilot 仓库中的 Modules Re嵌入式物联网机器人自动驾驶智能硬件如何下载加密的 m3u8 视频N_m3u8DL-RE 快速上手指南如何下载加密的 m3u8 视频N_m3u8DL RE 快速上手指南 N_m3u8DL RE 是一个 .NET 编写的跨平台流媒体下载工具覆盖 HLSM3UCLI音视频上一篇如何在昇腾NPU上部署PatchCore98倍性能提升的完整指南下一篇graphw00f 与常见 GraphQL 引擎对比Apollo、Graphene、Hasura 等差异分析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表