前言
在大致了解PX4代码架构后,我们需要了解PX4的通信机制。在PX4代码架构中,每通信总线主要分为两个部分,一是内部通信总线uORB,即PX4内部进程通信采用的协议,例如PX4内部姿态控制需要获取飞行器姿态,而飞行器姿态是由姿态解算产生,它们之间便是由uorb通信
二是外部通信总线Mavlink,它主要负责飞控端与地面站系统的通信协议,后续会详细讲解,本章主要先了解uorb。本章分为三个部分,先介绍uorb基础,然后结合源码讲解怎么使用uorb,最后实际操作练手
一、uorb基础
关于这一部分,CSDN上有很多大佬对uorb进行了详细的讲解,我写的不一定有他们好,就不献丑了,推荐阅读
1、http://shequ.dimianzhan.com/articles/315
2、https://blog.csdn.net/freeape/article/details/46880637
看完之后,你就会对uorb有一定的了解,说简单点,在uorb中,发送者只管发送消息到消息池中,不管谁去调用,接收者只管去消息池中获得需要的消息,不管消息是谁发布的,示意图如下
在了解uorb后 ,下面以官方例程开始讲解怎么使用uorb
二、uorb代码讲解
在官方提供的px4代码中,有一个历程就是讲解uorb使用的,首先进入PX4目录下src/example目录,找到px4_simple_app,如下图所示
在px4_simple_app下有一个c文件,直接打开
//需要使用的头文件
#include <px4_platform_common/px4_config.h>
#include <px4_platform_common/tasks.h>
#include <px4_platform_common/posix.h>
#include <unistd.h>
#include <stdio.h>
#include <poll.h>
#include <string.h>
#include <math.h>
#include <uORB/uORB.h>
#include <uORB/topics/sensor_combined.h>
#include <uORB/topics/vehicle_attitude.h>//声明主函数
__EXPORT int px4_simple_app_main(int argc, char *argv[]);
//主函数
int px4_simple_app_main(int argc, char *argv[])
{//在控制台输出Hello SkyPX4_INFO("Hello Sky!");//定义句柄接收名为sensor_combined的消息int sensor_sub_fd = orb_subscribe(ORB_ID(sensor_combined));//定义更新频率orb_set_interval(sensor_sub_fd, 200);//定义结构体变量,并完成初始化struct vehicle_attitude_s att;memset(&att, 0, sizeof(att));//向系统公告自己要发布消息,完成初始化,在系统中创建一个主题,并返回指针orb_advert_t att_pub = orb_advertise(ORB_ID(vehicle_attitude), &att);/* one could wait for multiple topics with this technique, just using one here */px4_pollfd_struct_t fds[] = {{ .fd = sensor_sub_fd, .events = POLLIN },/* there could be more file descriptors here, in the form like:* { .fd = other_sub_fd, .events = POLLIN },*/};int error_counter = 0;for (int i = 0; i < 5; i++) {/* wait for sensor update of 1 file descriptor for 1000 ms (1 second) *///检查1s内是否有数据更新//int poll_ret = px4_poll(fds, 1, 1000);/* handle the poll result *///返回值为0,证明1s内数据没有更新if (poll_ret == 0) {/* this means none of our providers is giving us data */PX4_ERR("Got no data within a second");} else if (poll_ret < 0) {//返回值为-1,证明出错/* this is seriously bad - should be an emergency */if (error_counter < 10 || error_counter % 50 == 0) {/* use a counter to prevent flooding (and slowing us down) */PX4_ERR("ERROR return value from poll(): %d", poll_ret);}error_counter++;} else {//返回值大于0,说明得到数据后还剩poll_ret时间if (fds[0].revents & POLLIN) {/* obtained data for the first file descriptor *///定义sensor_combined的结构体,接收sensor_combined的消息struct sensor_combined_s raw;/* copy sensors raw data into local buffer *///把消息复制出来orb_copy(ORB_ID(sensor_combined), sensor_sub_fd, &raw);PX4_INFO("Accelerometer:\t%8.4f\t%8.4f\t%8.4f",(double)raw.accelerometer_m_s2[0],(double)raw.accelerometer_m_s2[1],(double)raw.accelerometer_m_s2[2]);/* set att and publish this information for other appsthe following does not have any meaning, it's just an example*///填充到之前公告要发布的话题中att.q[0] = raw.accelerometer_m_s2[0];att.q[1] = raw.accelerometer_m_s2[1];att.q[2] = raw.accelerometer_m_s2[2];//发布vehicle_attitude消息orb_publish(ORB_ID(vehicle_attitude), att_pub, &att);}/* there could be more file descriptors here, in the form like:* if (fds[1..n].revents & POLLIN) {}*/}}PX4_INFO("exiting");return 0;
}
比较重要的注释已经在代码中标注,下面解释一下几个比较重要的点
1.在代码中我们看到了vehicle_attitude_s,sensor_combined_s的结构体,有人会好奇这些结构体怎么产生的。实际上,这些结构体是由我们之前定义好的msg编译产生,即系统根据你之前定义的消息自动生成。打开PX4目录,进入其中msg文件夹,你会看到许多以.msg结尾的文件,如下图
我们可以在其中找到sensor_combined.msg文件,打开文件
uint64 timestamp # time since system start (microseconds)int32 RELATIVE_TIMESTAMP_INVALID = 2147483647 # (0x7fffffff) If one of the relative timestamps is set to this value, it means the associated sensor values are invalid# gyro timstamp is equal to the timestamp of the message
float32[3] gyro_rad # average angular rate measured in the XYZ body frame in rad/s over the last gyro sampling period
uint32 gyro_integral_dt # gyro measurement sampling period in usint32 accelerometer_timestamp_relative # timestamp + accelerometer_timestamp_relative = Accelerometer timestamp
float32[3] accelerometer_m_s2 # average value acceleration measured in the XYZ body frame in m/s/s over the last accelerometer sampling period
uint32 accelerometer_integral_dt # accelerometer measurement sampling period in usuint8 CLIPPING_X = 1
uint8 CLIPPING_Y = 2
uint8 CLIPPING_Z = 4
uint8 accelerometer_clipping # bitfield indicating if there was any accelerometer clipping (per axis) during the sampling period
我们可以看到其中定义的消息,结合之前的代码,可以看到px4_simple_app这个例子实际上是取出sensor_combined的加速度数据,并把它以vehicle_attitude话题发布出去,不过这些消息都是系统定义好的,后面会介绍怎么定义和使用自己的消息。
2.代码讲解完毕,但该代码一般情况下不会运行,因为飞控板上的资源非常少,一般是不编译的。控制是否编译的文件在board/px4中(若是V1.11之前的版本可能在cmake文件中),如下
这些不同文件夹是针对不同硬件的编译配置,打开以fmu开头的文件夹或者sitl,你会发现以.cmake结尾的文件,例如sitl,该文件夹主要主管仿真的相关配置
在这些以.cmake结尾的文件中,我们常用default.cmake,打开文件
px4_add_board(PLATFORM posixVENDOR px4MODEL sitlLABEL defaultTESTINGDRIVERS#barometer # all available barometer drivers#batt_smbuscamera_capturecamera_trigger#differential_pressure # all available differential pressure drivers#distance_sensor # all available distance sensor driversgps#imu # all available imu drivers#magnetometer # all available magnetometer driverspwm_out_sim#telemetry # all available telemetry driverstone_alarm#uavcanMODULESairspeed_selectorattitude_estimator_qcamera_feedbackcommanderdatamanekf2eventsfw_att_controlfw_pos_control_l1land_detectorlanding_target_estimator#load_monlocal_position_estimatorloggermavlinkmc_att_controlmc_hover_thrust_estimatormc_pos_controlmc_rate_controlnavigatorrc_updatereplayrover_pos_controlsensors#sihsimulatortemperature_compensationvmountvtol_att_controluuv_att_controlSYSTEMCMDS#config#dumpfiledynesc_calibled_controlmixermotor_rampmotor_test#mtd#nshtermparamperfpwmrebootsd_benchshutdowntests # tests and test runner#toptopic_listenertune_controlverwork_queueEXAMPLESdyn_hello # dynamically loading modules examplefixedwing_control # Tutorial code from https://px4.io/dev/example_fixedwing_controlhellomy_demoapp#hwtest # Hardware test#matlab_csv_serialpx4_mavlink_debug # Tutorial code from http://dev.px4.io/en/debug/debug_values.htmlpx4_simple_app # Tutorial code from http://dev.px4.io/en/apps/hello_sky.htmlrover_steering_control # Rover example appuuv_example_appwork_item)set(config_sitl_viewer jmavsim CACHE STRING "viewer for sitl")
set_property(CACHE config_sitl_viewer PROPERTY STRINGS "jmavsim;none")set(config_sitl_debugger disable CACHE STRING "debugger for sitl")
set_property(CACHE config_sitl_debugger PROPERTY STRINGS "disable;gdb;lldb")# If the environment variable 'replay' is defined, we are building with replay
# support. In this case, we enable the orb publisher rules.
set(REPLAY_FILE "$ENV{replay}")
if(REPLAY_FILE)message(STATUS "Building with uorb publisher rules support")add_definitions(-DORB_USE_PUBLISHER_RULES)message(STATUS "Building without lockstep for replay")set(ENABLE_LOCKSTEP_SCHEDULER no)
else()set(ENABLE_LOCKSTEP_SCHEDULER yes)
endif()
你会看到一些比较熟悉的字母,例如DRIVERS,MODULES等,这正是我们src目录下的文件,而跟在这些文件夹之后的正是目录下的源代码,其实这份编译文件我已经修改过,在EXAMPLES下有px4_simple_app
三、px4_simple_app代码结果展示
验证代码结果,你可以将代码下载到支持PX4的硬件中,也可以采用jMavSim,gazebo等仿真软件进行半物理仿真,操作步骤如下
1.按上一章的讲解找到boards/px4/sitl文件夹,修改default.camke文件,在EXAMPLES下添加px4_simple_app
2.保存,在PX4目录下输入代码make px4_sitl_default jmavsim,等待编译成功
3.当出现下面界面就说明编译成功
4.这时返回终端窗口,输入help,你会看到可以运行的代码,你会在其中找到px4_simple_app
5.在终端输入px4_simple_app,会有以下结果
下章介绍如何在msg中添加自己的消息,并在PX4源码中添加自己的程序