PX4代码解析(2)

前言

在大致了解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 Sky
	PX4_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 apps
				 the 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 us

int32 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 us

uint8 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 posix
	VENDOR px4
	MODEL sitl
	LABEL default
	TESTING
	DRIVERS
		#barometer # all available barometer drivers
		#batt_smbus
		camera_capture
		camera_trigger
		#differential_pressure # all available differential pressure drivers
		#distance_sensor # all available distance sensor drivers
		gps
		#imu # all available imu drivers
		#magnetometer # all available magnetometer drivers
		pwm_out_sim
		#telemetry # all available telemetry drivers
		tone_alarm
		#uavcan
	MODULES
		airspeed_selector
		attitude_estimator_q
		camera_feedback
		commander
		dataman
		ekf2
		events
		fw_att_control
		fw_pos_control_l1
		land_detector
		landing_target_estimator
		#load_mon
		local_position_estimator
		logger
		mavlink
		mc_att_control
		mc_hover_thrust_estimator
		mc_pos_control
		mc_rate_control
		navigator
		rc_update
		replay
		rover_pos_control
		sensors
		#sih
		simulator
		temperature_compensation
		vmount
		vtol_att_control
		uuv_att_control

	SYSTEMCMDS
		#config
		#dumpfile
		dyn
		esc_calib
		led_control
		mixer
		motor_ramp
		motor_test
		#mtd
		#nshterm
		param
		perf
		pwm
		reboot
		sd_bench
		shutdown
		tests # tests and test runner
		#top
		topic_listener
		tune_control
		ver
		work_queue
	EXAMPLES
		dyn_hello # dynamically loading modules example
		fixedwing_control # Tutorial code from https://px4.io/dev/example_fixedwing_control
		hello
		my_demoapp
		#hwtest # Hardware test
		#matlab_csv_serial
		px4_mavlink_debug # Tutorial code from http://dev.px4.io/en/debug/debug_values.html
		px4_simple_app # Tutorial code from http://dev.px4.io/en/apps/hello_sky.html
		rover_steering_control # Rover example app
		uuv_example_app
		work_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源码中添加自己的程序

  • 18
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
`postMessage` 方法可以用于在不同的窗口或 iframe 之间进行安全的跨域通信。它能够向指定窗口发送一个消息,并且在接收窗口中设置一个监听器来接收该消息。 `postMessage` 方法的基本语法如下: ```javascript otherWindow.postMessage(message, targetOrigin, [transfer]); ``` 其中,`otherWindow` 表示指定的窗口或 iframe,`message` 表示要发送的消息,`targetOrigin` 表示接收消息的窗口的源(如果不指定,表示不限制源),`transfer` 表示要传递的数据(可选)。 例如,下面的代码可以向指定的窗口发送一个消息: ```javascript var otherWindow = window.open('http://example.com/'); otherWindow.postMessage('Hello, world!', 'http://example.com'); ``` 在上面的代码中,我们首先使用 `window.open` 方法打开了一个新的窗口,并将其引用赋值给了 `otherWindow` 变量。然后,我们使用 `postMessage` 方法向该窗口发送了一个消息。由于指定的源为 `http://example.com`,因此只有来自 `http://example.com` 源的窗口才能接收到该消息。 接收窗口可以使用以下代码来监听消息: ```javascript window.addEventListener('message', function(event) { if (event.origin !== 'http://example.com') { return; } console.log(event.data); }); ``` 在上面的代码中,我们使用 `addEventListener` 方法向当前窗口添加了一个监听器,用于监听 `message` 事件。当接收到事件时,我们首先检查消息的源是否为 `http://example.com`,如果不是,则直接返回。否则,我们就可以使用 `event.data` 属性来获取消息内容,并进行相应的处理。 需要注意的是,`postMessage` 方法虽然可以进行跨域通信,但也存在一定的安全风险。因此,在使用时需要仔细考虑安全问题,并避免发生跨站点脚本(XSS)攻击等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值