PX4 Pixhawk程序研究笔记.docx
《PX4 Pixhawk程序研究笔记.docx》由会员分享,可在线阅读,更多相关《PX4 Pixhawk程序研究笔记.docx(61页珍藏版)》请在冰豆网上搜索。
PX4Pixhawk程序研究笔记
PX4Pixhawk程序研究笔记
编译环境建立
参考链接:
http:
//pixhawk.org/dev/toolchain_installation_win
1、首先确保电脑安装了Java运行环境。
2、下载并安装PX4Toolchain,链接:
http:
//pixhawk.org/firmware/downloads#px4_arm_toolchain
3、在开始菜单中选择:
PX4Toolchain->PX4Softwaredownload来获取一个初始软件设置。
它会在安装路径下(默认为C:
\px4)下载如下文件夹:
▪px4
▪Firmware–PX4firmware(forallmodules),includesMAVLink
▪NuttX–TheNuttXRealTimeOperatingSystem(RTOS)
▪libopencm3–Optional:
OpenSourceCortexMxlibrary,usedonlyinthebootloaders
▪Bootloader–Optional:
Bootloaders,doesnormallynotneedtobetouched
4、配置Eclipse,开始菜单->所有程序–>PX4Toolchain->PX4Eclipse
默认的workspace路径是刚好正确的:
New→MakefileProjectwithExistingCode:
选择CrossGCC,并指定文件夹位置为:
“c:
\px4\Firmware”。
打开右边的“MakeTarget”并点击“NewMakeTarget”:
你应当创建如下Targets:
▪archives –buildstheNuttX OS
▪all –buildstheautopilotsoftware(dependsonarchives)
▪distclean –cleanseverything,includingtheNuttXbuild
▪clean –cleansonlytheapplication(autopilot)part
▪uploadpx4fmu-v1_default –uploadstoPX4FMUv1.xboards
▪uploadpx4fmu-v2_default –uploadstoPX4FMUv2.xboards
编译方法:
参考链接:
http:
//pixhawk.org/dev/flash_px4fmu_win
1、双击distclean;
2、双击archives;
3、双击all;
4、双击uploadpx4fmu-v1_default(PX4)或uploadpx4fmu-v2_default(Pixhawk)来上传固件。
注意:
只有在Nuttx更新或者改变时才需要进行”distclean“和”archives“。
如果你只是编辑了PX4的程序,最便捷的办法是直接运行uploadpx4fmu-v1_default(PX4)或uploadpx4fmu-v2_default(Pixhawk)来编译并上传固件。
Eclipse使用技巧
1、选中一个函数,鼠标不动,0.5秒后会弹出一个悬浮框显示该函数的定义,双击该框,会出现滑动条,这时可以使用这个框看此函数的全部定义。
以上操作可以由“F2”键代替。
2、选中一个函数,按“F3”直接跳转到该函数的定义处,而不是通过一个悬浮框显示。
板载软件结构
PX4自动驾驶仪软件可分为三大部分:
实时操作系统、中间件和飞行控制栈。
1.NuttX实时操作系统
提供POSIX-style的用户操作环境(如printf(),pthreads,/dev/ttyS1,open(),write(),poll(),ioctl()),进行底层的任务调度。
2.PX4中间件
PX4中间件运行于操作系统之上,提供设备驱动和一个微对象请求代理(microobjectrequestbroker,uORB)用于驾驶仪上运行的单个任务之间的异步通信。
3.PX4飞行控制栈
飞行控制栈可以使用PX4的控制软件栈,也可以使用其他的控制软件,如APM:
Plane、APM:
Copter,但必须运行于PX4中间件之上。
PX4飞行控制栈遵循BSD协议,可实现多旋翼和固定翼完全自主的航路点飞行。
采用了一套通用的基础代码和通用的飞行管理代码,提供了一种灵活的、结构化的方法,可以用相同的航路点和安全状态机来运行不同的固定翼控制器或旋翼机控制器。
其板载程序结构图如下:
参考链接:
http:
//pixhawk.org/dev/architecture
上图中每个框表示一个概念上的任务(task)。
图中不是所有模块都是默认使能的,一些模块是冗余的,比如当姿态控制(attitudecontrol)活动时,位置控制(positioncontrol)是不活动的。
浅灰色的框表示作为主模块(mainblocks)接口的关键外设。
图中许多模块被作为单独的任务(tasks)来完成的,不同任务间通过“interprocesscommunication”来通信。
源程序文件说明
本章列举源程序中各个文件夹、各个C、CPP文件以及头文件的作用。
●src/lib/geo/geo.c该文件定义的所有与地球坐标系相关的函数(geo:
geodesic,测地学的)。
提供了与经纬度、地图坐标、坐标系翻转等相关的函数。
●src/lib/launchdetection中包含了自动降落相关的程序。
●src/modules/commonder文件夹包含了所有的与地面站相关的命令:
commonder.cpp为主要程序,同时该文件夹中还包含了加速度计校准、空速计校准、磁罗盘校准、遥控器校准等程序。
●src/modules/uORB中包含了与uORB相关的程序。
●src/modules/px4iofirmware中包含了STM32F103那个单片机的源程序,它编译后的结果将作为ROM存储在FMU(STM32F427)单片机的固件中,位于程序文件系统的etc/extras/px4io-v2_default.bin中。
●src/modules/dataman中包含了与数据管理相关的函数。
●src/systemcmds/param包含了与系统参数相关的程序,这些参数包含机架类型、各种PID以及各种设置等参数。
●src/ROMFS/px4fmu_common/init.d包含了系统其中的各种脚本,其中最下面的“rcS”为主脚本,系统流程启动以它为准,同时它内部还会不断调用其他子脚本(如rc.sensors脚本,对应各种传感器,其调用命令为:
sh/etc/init.d/rc.sensors)。
通读“rcS”脚本文件,即可明白PX4的启动和运行流程
进程间通信(uORB)
参考链接:
http:
//blog.arm.so/docs/183-0503.html
uORB是Pixhawk系统中非常重要且关键的一个模块,它肩负了整个系统的数据传输任务,所有的传感器数据、GPS、PPM信号等都要从芯片获取后通过uORB进行传输到各个模块进行计算处理。
uORB的架构简述:
uORB全称为microobjectrequestbroker(uORB),即“微对象请求代理器”,实际上uORB是一套跨进程的IPC通讯模块。
在Pixhawk中,所有的功能被独立以进程模块为单位进行实现并工作。
而进程间的数据交互就由为重要,必须要能够符合实时、有序的特点。
Pixhawk使用NuttX实时ARM系统,而uORB对于NuttX而言,它仅仅是一个普通的文件设备对象,这个设备支持Open、Close、Read、Write、Ioctl以及Poll机制。
通过这些接口的实现,uORB提供了一套“点对多”的跨进程广播通讯机制,“点”指的是通讯消息的“源”,“多”指的是一个源可以有多个用户来接收、处理。
而“源”与“用户”的关系在于,源不需要去考虑用户是否可以收到某条被广播的消息或什么时候收到这条消息。
它只需要单纯的把要广播的数据推送到uORB的消息“总线”上。
对于用户而言,源推送了多少次的消息也不重要,重要的是取回最新的这条消息。
uORB实际上是多个进程打开同一个设备文件,进程间通过此文件节点进行数据交互和共享。
uORB的系统实现:
uORB的实现位于固件源码的src/modules/uORB/uORB.cpp文件,它通过重载CDev基类来组织一个uORB的设备实例。
并且完成Read/Write等功能的重载。
uORB的入口点是uorb_main函数,在这里它检查uORB的启动参数来完成对应的功能,uORB支持start/test/status这3条启动参数,在Pixhawk的rcS启动脚本中,使用start参数来进行初始化,其他2个参数分别用来进行uORB功能的自检和列出uORB的当前状态。
在rcS中使用start参数启动uORB后,uORB会创建并初始化它的设备实例,其中的实现大部分都在CDev基类完成。
这个过程类似于Linux设备驱动中的Probe函数,或者Windows内核的DriverEntry,通过init调用完成设备的创建,节点注册以及派遣例程的设置等。
************************下面是官网资料***********************
参考链接:
http:
//pixhawk.org/dev/shared_object_communication
进程(process)/程序(application)间通信(如将传感器信息从传感器app传送到姿态滤波app)是PX4程序结构的核心部分。
进程(process,在此处被称作nodes)通过被命名的总线(buses,在此处被称作topic)交换信息。
在PX4中,一个topic只包含一种信息类型,比如,vehicle_attitude这个topic将一个包含姿态结构体(roll、pitch、yaw)的信息传送出去。
Nodes可以在bus/topic上publish(发布)一个信息(即发送数据),也可以向一个bus/topicsubscribe(订阅)信息(即接收数据)。
它们(Nodes)并不知道它们在跟谁通信。
一个topic可以面向多个publishers(发布者)和多个subscribers(订阅者)。
这种方式可以避免死锁问题,在机器人中很常见。
为达到有效率,在bus/topic中,永远只有一个信息被传送,没有保持队列之说(即新来的信息会覆盖之前的信息,不存在有一串信息排队的情况)。
这个发布/订阅 (publisher/subscriber)机制是通过微对象请求代理(microobjectrequestbroker,简称uORB)来实现的。
✧系统已存的topics通过Doxygen工具自动生成了文档,其链接为:
https:
//pixhawk.ethz.ch/docs/group__topics.html
下面是关于publisher/subscriber的一个简单的例子,这个publisher(发布者)advertises(通告)一个名叫random_integer的topic,在这个topic中更新入随机数。
subscriber(订阅者)检查并打印出更新值。
✧发布(Publishing)
发布(Publishing)包含三个独立但是相关的过程:
定义(defining )一个topic,通告(advertising )这个topic,发布(publishing )更新。
1.定义一个topic(DefiningaTopic)
系统已经定义了许多标准的topic来为模块间提供通信接口。
如果一个发布者(Publisher)想要使用这些topic及其相关数据结构,不需要做额外的工作。
用户topic:
要定义一个用户topic,发布者(publisher)需要创建一个对订阅者(subscriber)可见的头文件(headerfile)(可以参考上面的topic.h文件)。
这个头文件(headerfile)中必须包含如下内容:
●以这个topic的名字为参数的ORB_DECLARE()宏的一个实例。
●一个描述将要publish(发布)的数据结构的结构体定义。
Topic名字必须是有意义的;PX4的惯例是“类别_name”;比如,原始传感器数据通过sensors_raw这个topic来发布。
除了头文件(headerfile),发布者(publisher)还需在源文件(sourcefile)中添加一个 ORB_DEFINE()宏的实例,这个实例将会在固件被bulid的时候被编译和链接(参考上面的publisher.c文件)。
这个宏创建了一个数据结构体,这个结构体将由ORB用来唯一地识别一个topic的身份。
如果一个topic是由一个软件组件发布的,并且是可选(optional)的,可能不会在固件中出现,那么头文件中可以代替使用ORB_DECLARE_OPTIONAL()宏。
这种方法声明的topic可需要专门的句柄(handling),但是下面将会讲到,订阅者在订阅这类topic时有一些额外需要考虑的地方。
2.通告一个topic(AdvertisingaTopic)
在数据可以被发布(publish)到一个topic之前,这个topic必须被通告(advertise)一下。
使用的是orb_advertise()函数,这个函数也将初始化数据发布到了topic中。
这个函数的原型如下:
/**
*Advertiseasthepublisherofatopic.
*
*Thisperformstheinitialadvertisementofatopic;itcreatesthetopic
*nodein/objifrequiredandwritestheinitialdata.
*
*@parammetaTheuORBmetadata(usuallyfromtheORB_ID()macro)
*forthetopic.
*@paramdataApointertotheinitialdatatobepublished.
*Fortopicspublishedbyinterrupthandlers,theadvertisement
*mustbeperformedfromnon-interruptcontext.
*@returnERRORonerror,otherwisereturnsahandle
*thatcanbeusedtopublishtothetopic.
*Ifthetopicinquestionisnotknown(duetoan
*ORB_DEFINE_OPTIONALwithnocorrespondingORB_DECLARE)
*thisfunctionwillreturn-1andseterrnotoENOENT.
*/
externintorb_advertise(conststructorb_metadata*meta,constvoid*data);
meta变量是指向由“ORB_DEFINE()”宏定义的数据的一个指针。
通常一个是使用“ORB_ID()”宏来提供的,这个宏起到了一个将topicname转换为topic原数据结构体name的作用。
注意,由于更新可以在中断中被发布,通告(advertise)一个topic必须在正常进程中进行。
多个发布者(MultiplePublishers):
一次只能有一个发布者来将一个
topic通告(advertise),但是topichandle(句柄)(句柄是一个filedescriptor,可以直接传递给close()函数)可以由一个publisher(发布者)关闭,然后由另一个发布者通告(advertise)。
3.发布更新(PublishingUpdates)
一个topic被通告(advertise)后,一个由通告函数返回的handle(句柄)可以用来向topic中发布更行。
/**
*Publishnewdatatoatopic.
*
*Thedataisatomicallypublishedtothetopicandanywaitingsubscribers
*willbenotified.Subscribersthatarenotwaitingcancheckthetopic
*forupdatesusingorb_checkand/ororb_stat.
*
*@handleThehandlereturnedfromorb_advertise.
*@parammetaTheuORBmetadata(usuallyfromtheORB()macro)
*forthetopic.
*@paramdataApointertothedatatobepublished.
*@returnOKonsuccess,ERRORotherwisewitherrnosetaccordingly.
*/
externintorb_publish(conststructorb_metadata*meta,inthandle,constvoid*data);
注意ORB不缓存多个更新,所以当一个订阅者检查一个topic的时候,它只能看到最新的更新。
✧订阅(Subscribing)
订阅(subscribling)一个topic需要如下步骤:
●一个ORB_DEFINE()或者ORB_DEFINE_OPTIONAL()宏(比如在由订阅者包含的一个头文件中)
●一个发布给topic的数据结构体的定义(通常也是在那个头文件中)
满足上述条件后,使用如下函数来订阅一个topic:
/**
*Subscribetoatopic.
*
*Thereturnedvalueisafiledescriptorthatcanbepassedtopoll()
*inordertowaitforupdatestoatopic,aswellasorb_read,
*orb_checkandorb_stat.
*
*Subscriptionwillsucceedevenifthetopichasnotbeenadvertised;
*inthiscasethetopicwillhaveatimestampofzero,itwillnever
*signalapoll()event,checkingwillalwaysreturnfalseanditcannot
*becopied.Whenthetopicissubsequentlyadvertised,poll,check,
*statandcopycallswillreacttotheinitialpublicationthatis
*performedaspartoftheadvertisement.
*
*Subscriptionwillfailifthetopicisnotknowntothesystem,i.e.
*thereisnothinginthesystemthathasdefinedthetopicandthusit
*canneverbepublished.
*
*@parammetaTheuORBmetadata(usuallyfromtheORB_ID()macro)
*forthetopic.
*@returnERRORonerror,otherwisereturnsahandle
*thatcanbeusedtoreadandcheckthetopicforupdates.
*Ifthetopicinquestionisnotknown(duetoan
*ORB_DEFINE_OPTIONALwithnocorrespondingORB_DECLARE)
*thisfunctionwillreturn-1andseterrnotoENOENT.
*/
externintorb_subscribe(conststructorb_metadata*meta);
如果一个optionaltopic没有被提供,那么对它的订阅将失败,但是别的订阅依然会成功,并且创建这个topic即使它还没被通告。
这很大程度低降低了系统启动顺序的安排难度。
一个任务中订阅者的数量没有上限。
取消订阅一个topic,使用如下函数:
/**
*Unsubscribefromatopic.
*
*@paramhandleAhandlereturnedfromorb_subscribe.
*@returnOKonsuccess,ERRORotherwisewitherrnosetaccordingly.
*/
externintorb_unsubscribe(inthandle);
从topic中复制出数据:
订阅者并不直接调用ORB中存储的数据,也不直接与其他订阅者共享它,而是将其从ORB中复制到一个临时缓存中。
这个复制过程避免了死锁问题,并且使得发布和订阅函数很简单。
并且也允许订阅者直接修改数据(如果需要的话)而不影响其他订阅者。
当一个订阅者想复制一份最新的副本时,使用如下函数:
/**
*Fetchdatafromatopic.
*
*@parammetaTheuORBmetadata(usuallyfromtheORB()macro)
*forthetopic.
*@paramhandleAhandlereturnedfromorb_subscribe.
*@parambufferPointertothebufferreceivingthedata.
*@returnOKonsuccess,ERRORotherwisewitherrnosetaccordingly