PX4原生固件及其地面站网址 https://github.com/PX4 http://px4.io/ 原生固件代码:C++,构架清晰,容易维护修改,多进程 目前来讲功能方面和细节上不如APM固件
AutoPoilt 固件及其地面站网址 http://ardupilot.org/ https://github.com/ArduPilot/ardupilot https://github.com/ArduPilot/MissionPlanner APM固件代码:C++,稳定成熟,功能丰富,发起时间比较早 系统构架不如原生固件那么清晰,相对来讲不容易维护和修改
PIXHAWK控制器硬件 https://github.com/PX4/Hardware
经常有人将 Pixhawk、PX4、APM 还有 ArduPilot 弄混。这里首先还是简要 说明一下: Pixhawk 是飞控硬件平台,PX4 和 ArduPilot 都是开源的可以烧写到 Pixhawk 飞控中的自驾仪软件,PX4 称为原生固件,专为 Pixhawk 打造。APM(Ardupilot Mega)早期也是一款自驾仪硬件,到 APM3.0 版本,这款基于 Arduino Mega 的 自驾仪已经走到了它的终点。ArduPilot 早期是 APM 自驾仪的固件,Pixhawk 作 为 APM 的升级版,也兼容 ArduPilot 固件,APM 自驾仪卒了之后,ArduPilot 现 在全面支持 Pixhawk,现在大家亲切的称 ArduPilot 固件为 APM。
pixhack硬件介绍:https://docs.px4.io/master/en/flight_controller/pixhack_v3.html#pixhack-v3
11
51单片机,只有一个main函数,main函数中有一个主循环while。 全部代码被编译成为一个可执行文件
初始化硬件、初始化OS等代码被编译成一个可以行程序,上电后执行。 如:姿态估计代码、姿态控制算法代码分别被编译成两个独立的可执行程序。这些独立的可执行程序(如windows中的**.exe)由OS以进程的形式执行,OS为这些可执行程序创建进程时,可配置执行频率。
负责姿态估计的代码被编译为可执行程序,每一个之可执行程序都有一个入口main函数。 如该可执行程序中有一个循环,则对应的进程按照频率执行,若没有循环,执行完这段可执行程序后(遇到return),对应的进程即结束。
1
源码:即代码,还未编译,可以是多个.c文件(或其他语言的源文件);或是一个工程文件project 固件(Firmware):已经编译,并把各个已经编译好的文件打包成一个整体,以供烧录进单片机中。 PX4源码最后编译成以下固件: Firmware/ROMFS/px4fmu_common/init.d 文件夹下的 rcS 启动脚本(startup script)。 这个脚本位于被编译到固件中的 ROM 文件系统中。这个脚本检测可用的硬件,加载硬件驱动,并且根据你的设置启动系统正常运行所需的有 app(任务软件 , 包括位置和姿态估计,控制遥测等)。所有属于自启动程序的脚本文件可以在 init.d 文件夹中找到。
1
作用:只是初始化?(配置寄存器) 对应的进程:只执行一次?无while死循环? LED驱动的入口函数:
class AttitudeEstimatorQ; namespace attitude_estimator_q { AttitudeEstimatorQ *instance; }
class AttitudeEstimatorQ { public: … }
class AttitudeEstimatorQ; namespace attitude_estimator_q { AttitudeEstimatorQ *instance; }
class AttitudeEstimatorQ { public: … }
先声明有这个类,再定义一个类的对象的指针instance,将这个变量(对象指针)放在一个名为attitude_estimator_q的命名空间中。 _***的变量,以下划线开头的变量或者函数表示类的室友成员。
NUTTX启动(也就是上面的固件代码,汇编语句就是跳转到这个位置来执行stm32_start.c): 代码位置:Firmware/build_px4fmu-v2_default/px4fmu-v2/Nuttx/nuttx/arch/arm/src/stm32/stm32_start.c 注意:build_px4fmu-v2_default文件夹要编译整个工程之后才会生成。
stm32_start.c中处理器执行的第一条指令(px4使用的是stm32,入口在stm32_start.c中) stm32_clockconfig() #初始化时钟 rcc_reset() #复位rcc stm32_stdclockconfig() #初始化标准时钟 rcc_enableperipherals()#使能外设时钟 stm32_fpuconfig() #配置fpu stm32_lowsetup() #基本初始化串口,之后可以使用up_lowputc() stm32_gpioinit() #初始化gpio,只是调用stm32_gpioremap()设置重映射 up_earlyserialinit() #初始化串口,之后可以使用up_putc() stm32_boardinitialize() stm32_spiinitialize() #初始化spi,只是调用stm32_configgpio()设置gpio stm32_usbinitialize() #初始化usb,只是调用stm32_configgpio()设置gpio up_ledinit(); #初始化led,只是调用stm32_configgpio()设置gpio
在stm32_start.c文件中我们会看到这么一句话: /* Then start NuttX */ showprogress(’\r’); showprogress(’\n’); os_start(); #系统开始启动
以下是 os_start()内容: dq_init() #初始化各种状态的任务列表(置为null) g_pidhash[i]= #初始化唯一可以确定的元素–进程ID g_pidhash[PIDHASH(0)]= #分配空闲任务的进程ID为0 g_idletcb= #初始化空闲任务的任务控制块 sem_initialize()-- #初始化信号量 dq_init() #将信号量队列置为null sem_initholders() #初始化持有者结构以支持优先级继承 up_allocate_heap() #分配用户模式的堆(设置堆的起点和大小) kumm_initialize() #初始化用户模式的堆 up_allocate_kheap() #分配内核模式的堆 kmm_initialize() #初始化内核模式的堆 task_initialize() #初始化任务数据结构 irq_initialize() #将所有中断向量都指向同一个异常中断处理程序 wd_initialize() #初始化看门狗数据结构 clock_initialize() #初始化rtc timer_initialize() #配置POSIX定时器 sig_initialize() #初始化信号 mq_initialize() #初始化命名消息队列 pthread_initialize() #初始化线程特定的数据,空函数 fs_initialize()— #初始化文件系统 sem_init() #初始化节点信号量为1 files_initialize() #初始化文件数组,空函数 net_initialize()-- #初始化网络 uip_initialize() #初始化uIP层 net_initroute() #初始化路由表 netdev_seminit() #初始化网络设备信号量 arptimer_init() #初始化ARP定时器 up_initialize()— #处理器特定的初始化 up_calibratedelay() #校准定时器 up_addregion() #增加额外的内存段 up_irqinitialize() #设置中断优先级,关联硬件异常处理函数 up_pminitialize() #初始化电源管理 up_dmainitialize() #初始化DMA up_timerinit() #初始化定时器中断 devnull_register() #注册/dev/null devzero_register() #注册/dev/zero up_serialinit() #注册串口控制台/dev/console和串口/dev/ttyS0 up_rnginitialize() #初始化并注册随机数生成器 up_netinitialize() #初始化网络,是arch/arm/src/chip/stm32_eth.c中的 up_usbinitialize() #初始化usb驱动 board_led_on() #打开中断使能led,但很快会被其它地方的led操作改变状态 lib_initialize() #初始化c库,空函数 group_allocate() #分配空闲组 group_setupidlefiles() #在空闲任务上创建stdout、stderr、stdin group_initialize() #完全初始化空闲组 os_bringup()------ #创建初始任务 KEKERNEL_THREAD() #启动内核工作者线程 board_initialize() #最后一刻的板级初始化 TASK_CREATE() #启动默认应用程序 forup_idle() #空闲任务循环 for( ; ; ) #不应该到达这里(程序不该执行到此)
__start 负责STM32 芯片的底层初始化,包括时钟,FPU,GPIO 等单元的初始化;os_start 负责OS 的底层初始化,包括各种队列和进程结构的初始化;os_bringup 负责OS 基本进程的启动以及用户进程的启动,用户启动入口由;(CONFIG_USER_ENTRYPOINT)宏进行指定是执行nsh_main还是user_start。Pixhawk 整体逻辑大致为:
commander 和 navigator 产生期望位置position_estimator 估计当前位置通过 pos_ctrl 产生期望姿态attitude_estimator 估计当前姿态通过 att_ctrl 产生 PWM 数值最后通过 mixer 和 motor_driver 控制电机77
函数调用结构示意: 官方解析: 后台任务 px4_task_spawn_cmd() 用于启动与父任务独立运行的新任务(NuttX)或者新线程(POSIX - Linux/macOS):
independent_task = px4_task_spawn_cmd( "commander", // 进程名称 SCHED_DEFAULT, // 调度类型(RR 或 FIFO) SCHED_PRIORITY_DEFAULT + 40, // 调度优先级 3600, // 新任务或线程的堆栈大小 commander_thread_main, // 任务(或线程的主函数) (char * const *)&argv[0] // Void 指针传递到新任务 // (这里是命令行参数) );操作系统相关的信息 NuttX NuttX 是在飞控板上运行 PX4 的首选 RTOS 。 它是一个开源软件(BSD 许可), 非常轻量化,运行高效且稳定。
各模块以任务(Task)模式运行:他们有各自的文件描述列表,但共用一个地址空间。 单个任务可以使用同一个文件描述列表启动单个或者多个线程。
每一个任务/线程都有一个固定大小的栈堆,并且有一个周期性的任务会定期检查所有栈堆都有足够的可用空间(基于 stack coloring)。
Linux/MacOS 在 Linux 或者 macOS 系统上, PX4 在一个单独的进程中运行,各个模块在各自线程中运行(在 NuttX 中任务和线程没有任何区别)。
task_main函数中:
先订阅UORB消息请参考PX4开发者手册
源码大小638.2 Mb,1.11版本。
运行make 后悔多出一个build目录,运行 make distclean 会清除build目录以及编译过程中的所有中间文件。
先进入Firemare目录,再执行make命令。 运行编译之前,先运行
make distcleanThe following list shows the build commands for common boards:
Pixhawk 4: make px4_fmu-v5_defaultPixracer: make px4_fmu-v4_defaultPixhawk 3 Pro: make px4_fmu-v4pro_defaultPixhawk Mini: make px4_fmu-v3_defaultPixhawk 2: make px4_fmu-v3_defaultmRo Pixhawk: make px4_fmu-v3_default(支持 2MB 闪存)HKPilot32: make px4_fmu-v2_defaultPixfalcon: make px4_fmu-v2_defaultDropix: make px4_fmu-v2_defaultMindPX/MindRacer: make airmind_mindpx-v2_defaultmRo X-2.1: make mro_x21_defaultCrazyflie 2.0: make bitcraze_crazyflie_defaultIntel® Aero Ready to Fly Drone: make intel_aerofc-v1_defaultPixhawk 1: make px4_fmu-v2_default > Warning You must use a supported version of GCC to build this board (e.g. the same as used by CI/docker) or remove modules from the build. 使用不受支持的 GCC 构建可能会失败,因为 PX4 对飞控板有 1MB 的闪存限制。 Pixhawk 1 的 2 MB 闪存版: make px4_fmu-v3_default Linux下编译成功: 固件位置:Firmware/build/px4_fmu-v3_default/px4_fmu-v3_default.px4A successful run will end with this output:
Erase : [====================] 100.0% Program: [====================] 100.0% Verify : [====================] 100.0% Rebooting. [100%] Built target uploadThis section shows how make options are constructed and how to find the available choices. make命令语法: make [ VENDOR_MODEL_VARIANT ] [ VIEWER_MODEL_DEBUGGER_WORLD ] 例如: make px4_fmu-v4_default make px4_sitl jmavsim
VENDOR: The manufacturer of the board: px4, aerotenna, airmind, atlflight, auav, beaglebone, intel, nxp, etc. The vendor name for Pixhawk series boards is px4.MODEL: The board model “model”: sitl, fmu-v2, fmu-v3, fmu-v4, fmu-v5, navio2, etc.VARIANT: Indicates particular configurations: e.g. rtps, lpe, which contain components that are not present in the default configuration. Most commonly this is default, and may be omitted.如:
编译命令解析其他硬件target的编译针对仿真平台的编译 等问题,详细请参考网站PX4开发者手册General Build Errors Many build problems are caused by either mismatching submodules or an incompletely cleaned-up build environment. Updating the submodules and doing a distclean can fix these kinds of errors:
git submodule update --recursive make distclean更多请参考PX4开发者手册 / troubleshooting
33
44
task_main_trampoline 中的 trampoline :蹦床,跳床,弹床
px4_task_spawn_cmd 中的 spawn :引发;引起;导致;造成
00
PX4 由两个主要部分组成:一是 飞行控制栈(flight stack) ,该部分主要包括状态估计和飞行控制系统;另一个是 中间件 ,该部分是一个通用的机器人应用层,可支持任意类型的自主机器人,主要负责机器人的内部/外部通讯和硬件整合。
所有的 PX4 支持的 无人机机型 (包括其他诸如无人船、无人车、无人水下航行器等平台)均共用同一个代码库。 整个系统采用了 响应式(reactive) 设计,这意味着:
所有的功能都可以被分割成若干可替换、可重复使用的部件。通过异步消息传递进行通信。系统可以应对不同的工作负载。下面的架构图对 PX4 的各个积木模块以及各模块之间的联系进行了一个详细的概述。 图的上半部分包括了中间件模块,而下半部分展示的则是飞行控制栈的组件。
hello sky
The uORB is an asynchronous publish() / subscribe() messaging API used for inter-thread/inter-process communication. uORB是用于线程间/进程间通信的异步publish()/subscribe()消息传递API。 数据被封装成一个UORB消息,C++编程编程中,以结构体为消息载体,进行发布、订阅。 规则:一个发布者、多个订阅者。
订阅 发布 Topic的读写 读: 获取该Topic对应的UORB消息。 写:发布最新(更新)UORB消息。 通过句柄的方式对Topic进行读写。 句柄:在程序设计中,句柄是一种特殊的智能指针,当一个应用程序要引用其他系统(如数据库、操作系统)所管理的内存块或对象时,就要使用句柄 例如: //读: int sensor_sub_fd = orb_subscribe(ORB_ID(sensor_combined)); struct sensor_combined_s raw; while(true) { int poll_ret = px4_poll(fds, 1, 1000); orb_copy(ORB_ID(sensor_combined), sensor_sub_fd, &raw); } //写: struct vehicle_attitude_s att; orb_advert_t att_pub_fd = orb_advertise(ORB_ID(vehicle_attitude), &att); while(true) { att = 传感器硬件接口函数(或驱动); orb_publish(ORB_ID(vehicle_attitude), att_pub_fd, &att); }解析: 总结: 读: orb_subscribe( ) orb_copy( ) 写: orb_advertise( ) orb_publish( )
各应用之间的消息通道被称为 topics ;
消息包种类、通用的MAVLink消息(包)的定义和用途介绍可以查阅网站https://mavlink.io/en/messages/common.html#messages了解。
消息包结构封装的信息用于发送消息、接收消息、识别消息种类,而负载信息则用于描述消息所要传达的具体内容,可以理解为信封和信纸的关系
通过MavLink协议实现通信需要地面站软件和飞行控制软件的协作。地面站软件与飞行控制软件在发送、接收MavLink消息时需要依照预先设定的流程,本文以无人机与地面站连接时通信握手、参数列表请求、参数设定、状态消息包循环收发为例,在第四章介绍。
两个或多个MAVLink系统之间通信的方式:相互收发 Packet 。 Packet可以分为两类:
messagescommands 组成: Messages are encoded using message elements. Commands are defined as entries in the MAV_CMD enum, and encoded into real messages that are sent using the Mission Protocol or Command Protocol. 选择: 如果出现以下情况,请考虑发送message:所发送的information不适合comand(即,它不适合7个可用的数字字段)。该消息是另一个协议的一部分。消息必须广播或流式传输(即不需要ACK)如果满足以下条件,请考虑发送command:
该消息应作为任务的一部分执行。您希望在任务外部使用现有的任务命令。根据自动驾驶仪的不同,您可能可以在两种模式下使用相同的代码来处理消息。您正在使用MAVLink 1,并且没有新消息的免费ID(MAVLink 1具有比消息ID更大的MAVLink命令免费ID池)。重要的是,不要错过您的命令消息,因此需要ACK / NACK。使用现有协议确认可能比为确认定义另一个消息更快/更容易。否则,可以自由使用任何一种方法。
Use commands for actions in missions or if you need acknowledgment or retry logic from a request. Otherwise use messages.
关系: messages: command:
区分 information nessages data message-data command enum 的含义即联系。 message type
message set 消息集 message 消息 消息在XML文件中定义 大多数地面控制站和自动驾驶仪实现的参考消息集在common.xml中定义 MAVLink方言(Dialects)是XML文件,用于定义协议和特定于供应商的消息,枚举和命令。 枚举用于定义命名的值,这些值可以用作消息中的选项-例如,定义错误,状态或模式。还有一个特殊的枚举MAV_CMD,用于定义MAVLink命令。 MAVLink XML文件的大致结构:
<?xml version="1.0"?> <mavlink> <include>common.xml</include> <include>other_dialect.xml</include> <!-- NOTE: If the included file already contains a version tag, remove the version tag here, else uncomment to enable. --> <!-- <version>6</version> --> <dialect>8</dialect> <enums> <!-- Enums are defined here (optional) --> </enums> <messages> <!-- Messages are defined here (optional) --> </messages> </mavlink>MAVLink的enums、 messages,、commands 和 other elements在XML文件中定义,然后使用代码生成器转换为支持的编程语言的库。
本教程假定你在 msg/ca_trajectory.msg 文件中定义了一个名为 ca_trajectory 的 自定义 uORB 消息,以及在 mavlink/include/mavlink/v2.0/custom_messages/mavlink_msg_ca_trajectory.h 文件中定义了一个名为 ca_trajectory的 自定义 MAVLink 消息。
此章节旨在说明:如何使用一条自定义uORB消息,并将其作为一条MAVLink消息发送出去。