飞控简析-从入门到跑路 第二章PX4的位置控制(1)

2023-05-16

一、前言

首先,我们要清楚的我们的需求,PX4的位置控制需要完成什么样的工作。位置控制需要完成的是,从期望位置得到期望姿态的一个过程,然后把期望姿态传递给姿态控制模块,所以位置控制的输入是期望位置,输出是期望姿态(当然可能还需要其他信息)。然后,PX4的位置又可以分为两种,一种是自动控制,即地面站规划航线,无人机自己飞,第二种是手动控制,即遥控器控制期望位置(或者叫GPS定点模式)。这两种控制需要分开处理。下面我们来看下代码。

首先下载PX41.5.4的源代码,具体方法可以查看https://blog.csdn.net/weixin_38693938/article/details/83794666

解压文件后,定位到Firmware-master\src\modules\mc_pos_control\mc_pos_control_main.cpp

二、源码分析

1.辅助代码

首先,找到位置控制的主函数MulticopterPositionControl::task_main(),它位于文件的第1304行。

	/*
	 * do subscriptions
	 */
	_vehicle_status_sub = orb_subscribe(ORB_ID(vehicle_status));
	_vehicle_land_detected_sub = orb_subscribe(ORB_ID(vehicle_land_detected));
	_ctrl_state_sub = orb_subscribe(ORB_ID(control_state));
	_att_sp_sub = orb_subscribe(ORB_ID(vehicle_attitude_setpoint));
	_control_mode_sub = orb_subscribe(ORB_ID(vehicle_control_mode));
	_params_sub = orb_subscribe(ORB_ID(parameter_update));
	_manual_sub = orb_subscribe(ORB_ID(manual_control_setpoint));
	_arming_sub = orb_subscribe(ORB_ID(actuator_armed));
	_local_pos_sub = orb_subscribe(ORB_ID(vehicle_local_position));
	_pos_sp_triplet_sub = orb_subscribe(ORB_ID(position_setpoint_triplet));
	_local_pos_sp_sub = orb_subscribe(ORB_ID(vehicle_local_position_setpoint));
	_global_vel_sp_sub = orb_subscribe(ORB_ID(vehicle_global_velocity_setpoint));

首先代码会进行一大堆的订阅,用来更新无人机的一些状态和数据。这些可以跳过。

	parameters_update(true);

	/* initialize values of critical structs until first regular update */
	_arming.armed = false;

	/* get an initial update for all sensor and status data */
	poll_subscriptions();

	/* We really need to know from the beginning if we're landed or in-air. */
	orb_copy(ORB_ID(vehicle_land_detected), _vehicle_land_detected_sub, &_vehicle_land_detected);

	bool reset_int_z = true;
	bool reset_int_z_manual = false;
	bool reset_int_xy = true;
	bool reset_yaw_sp = true;
	bool was_armed = false;

	hrt_abstime t_prev = 0;

	math::Vector<3> thrust_int;
	thrust_int.zero();

	// Let's be safe and have the landing gear down by default
	_att_sp.landing_gear = -1.0f;


	matrix::Dcmf R;
	R.identity();

然后,进行参数初始化更新,还有一些变量的初始化工作,这些也可以跳过。上述代码都是初始化的代码,正常工作后循环运行是不会执行到这里的。

	px4_pollfd_struct_t fds[1];

	fds[0].fd = _local_pos_sub;
	fds[0].events = POLLIN;

上述代码是位置控制的主函数task_main的唤醒条件。使用操作系统的工程,当任务代码执行完成后,都会进入挂起状态,当满足一定的条件后,才会继续执行,而位置控制的唤醒条件就是更新位置信息的时候就会唤醒函数。

	while (!_task_should_exit) {
		/* wait for up to 20ms for data */
		int pret = px4_poll(&fds[0], (sizeof(fds) / sizeof(fds[0])), 20);

		/* timed out - periodic check for _task_should_exit */
		if (pret == 0) {
			// Go through the loop anyway to copy manual input at 50 Hz.
		}

		/* this is undesirable but not much we can do */
		if (pret < 0) {
			warn("poll error %d, %d", pret, errno);
			continue;
		}

		poll_subscriptions();

		parameters_update(false);

		hrt_abstime t = hrt_absolute_time();
		float dt = t_prev != 0 ? (t - t_prev) * 0.000001f : 0.0f;
		t_prev = t;

		// set dt for control blocks
		setDt(dt);

然后进入函数的唤醒阶段,当fds更新时,函数就会被唤醒,最大的等待时间是20ms,超过20ms函数也会被唤醒。函数被唤醒后,就开始订阅位置,姿态等数据,然后更新参数,计算两次位置控制的间隔时间。

2.主要代码

		if (_control_mode.flag_armed && !was_armed) {
			/* reset setpoints and integrals on arming */
			_reset_pos_sp = true;
			_reset_alt_sp = true;
			_do_reset_alt_pos_flag = true;
			_vel_sp_prev.zero();
			reset_int_z = true;
			reset_int_xy = true;
			reset_yaw_sp = true;
		}

		/* reset yaw and altitude setpoint for VTOL which are in fw mode */
		if (_vehicle_status.is_vtol) {
			if (!_vehicle_status.is_rotary_wing) {
				reset_yaw_sp = true;
				_reset_alt_sp = true;
			}
		}

		//Update previous arming state
		was_armed = _control_mode.flag_armed;

		update_ref();

首先,如果是刚解锁,那么需要重置位置期望值,高度期望值,PID控制的积分等。如果无人机是垂直起降固定翼VTOL且处于固定翼FW模式下,需要重置航向期望值和高度期望值。接着更新was_armed的标志位,重置位置坐标系的原点。正常我们无人机描述的位置是GPS信息,即无人机的经纬度,但是经过EKF或者INAV进行数据的融合后,会把经纬度坐标转换为直角坐标系,这个用来描述位置的坐标系也是北东天坐标系,既然是一个坐标系,那么必然会有一个原点,这个原点在PX4中就被称为ref。这个原点一般是上电的时候就采集好了的。

	/* Update velocity derivative,
		 * independent of the current flight mode
		 */
		if (_local_pos.timestamp > 0) {

			if (PX4_ISFINITE(_local_pos.x) &&
			    PX4_ISFINITE(_local_pos.y) &&
			    PX4_ISFINITE(_local_pos.z)) {

				_pos(0) = _local_pos.x;
				_pos(1) = _local_pos.y;

				if (_params.alt_mode == 1 && _local_pos.dist_bottom_valid) {
					_pos(2) = -_local_pos.dist_bottom;

				} else {
					_pos(2) = _local_pos.z;
				}
			}

			if (PX4_ISFINITE(_local_pos.vx) &&
			    PX4_ISFINITE(_local_pos.vy) &&
			    PX4_ISFINITE(_local_pos.vz)) {

				_vel(0) = _local_pos.vx;
				_vel(1) = _local_pos.vy;

				if (_params.alt_mode == 1 && _local_pos.dist_bottom_valid) {
					_vel(2) = -_local_pos.dist_bottom_rate;

				} else {
					_vel(2) = _local_pos.vz;
				}
			}

			_vel_err_d(0) = _vel_x_deriv.update(-_vel(0));
			_vel_err_d(1) = _vel_y_deriv.update(-_vel(1));
			_vel_err_d(2) = _vel_z_deriv.update(-_vel(2));
		}

然后开始计算速度的微分值,即无人机的实际加速度。首先为_pos()变量赋值,对应x,y,z的坐标,然后给_vel()变量赋值,对应无人机在x,y,z三个方向上的速度,然后调用更新函数求出速度的微分。其中dist_bottom是激光测距或者超声波测距得到的值,然后数值有效且参数设置了就会采用它们测出来的值,否则就是加速度,气压计,GPS等传感器融合出来的高度值。

		if (!_control_mode.flag_control_position_enabled || !_control_mode.flag_control_manual_enabled) {
			_pos_hold_engaged = false;
		}

		if (!_control_mode.flag_control_altitude_enabled || !_control_mode.flag_control_manual_enabled) {
			_alt_hold_engaged = false;
		}

然后如果不是手动模式或者不允许位置控制、高度控制就置零水平位置,垂直位置的保持标记。在我们用遥控器把飞控切到ALTCTL(高度控制)或者POSCTL(位置控制)的时候,当遥杆回到中位,无人机就会记录当前位置作为期望值,如果不是手动模式,相应的标志位就要清零,上面的代码就是清零标志的。

		if (_control_mode.flag_control_altitude_enabled ||
		    _control_mode.flag_control_position_enabled ||
		    _control_mode.flag_control_climb_rate_enabled ||
		    _control_mode.flag_control_velocity_enabled ||
		    _control_mode.flag_control_acceleration_enabled)

如果允许高度控制/位置控制/爬升率控制/速度控制/加速度控制才进入主代码,否则就会清除相关标志位。


			_vel_ff.zero();

			/* by default, run position/altitude controller. the control_* functions
			 * can disable this and run velocity controllers directly in this cycle */
			_run_pos_control = true;
			_run_alt_control = true;

			/* select control source */
			if (_control_mode.flag_control_manual_enabled) {
				/* manual control */
				control_manual(dt);

			} else if (_control_mode.flag_control_offboard_enabled) {
				/* offboard control */
				control_offboard(dt);
				_mode_auto = false;

			} else {
				/* AUTO */
				control_auto(dt);
			}

然后,首先清除变量_vel_ff,该变量1.5.4版本里面没有用到......。然后给标志位置一,默认运行高度控制和位置控制。如果是手动控制,就运行control_manual(dt),如果是仿真,就运行control_offboard(dt),如果是自动控制,就运行control_auto(dt)。手动控制与自动控制是我们最需要关注的两个点。

3.手动控制control_manual

	/* Entering manual control from non-manual control mode, reset alt/pos setpoints */
	if (_mode_auto) {
		_mode_auto = false;

		/* Reset alt pos flags if resetting is enabled */
		if (_do_reset_alt_pos_flag) {
			_reset_pos_sp = true;
			_reset_alt_sp = true;
		}
	}

首先,把标志位_mode_auto清零,然后判断是否需要重置位置,高度的期望值。

	math::Vector<3> req_vel_sp; // X,Y in local frame and Z in global (D), in [-1,1] normalized range
	req_vel_sp.zero();

	if (_control_mode.flag_control_altitude_enabled) {
		/* set vertical velocity setpoint with throttle stick */
		req_vel_sp(2) = -scale_control(_manual.z - 0.5f, 0.5f, _params.alt_ctl_dz, _params.alt_ctl_dy); // D
	}

	if (_control_mode.flag_control_position_enabled) {
		/* set horizontal velocity setpoint with roll/pitch stick */
		req_vel_sp(0) = _manual.x;
		req_vel_sp(1) = _manual.y;
	}

采集遥控器的位置,可以看到,x,y,z分别代表着俯仰,横滚,油门。

	if (_control_mode.flag_control_altitude_enabled) {
		/* reset alt setpoint to current altitude if needed */
		reset_alt_sp();
	}

	if (_control_mode.flag_control_position_enabled) {
		/* reset position setpoint to current position if needed */
		reset_pos_sp();
	}

然后根据相关标志决定,是否需要清零期望值。

	/* limit velocity setpoint */
	float req_vel_sp_norm = req_vel_sp.length();

	if (req_vel_sp_norm > 1.0f) {
		req_vel_sp /= req_vel_sp_norm;
	}

对遥控器的值进行归一化处理。

	/* _req_vel_sp scaled to 0..1, scale it to max speed and rotate around yaw */
	math::Matrix<3, 3> R_yaw_sp;
	R_yaw_sp.from_euler(0.0f, 0.0f, _att_sp.yaw_body);
	math::Vector<3> req_vel_sp_scaled = R_yaw_sp * req_vel_sp.emult(
			_params.vel_cruise); // in NED and scaled to actual velocity

用遥控器俯仰,横滚,油门的杆量的比例乘以巡航速度,在把速度向量转换到大地坐标系内。这一步是什么意思呢?我们知道,在GPS定点模式下,我们推俯仰,飞机就会往机头方向飞行,该步就是把实际需要飞行速度重新转换到NED大地坐标系下。有一点需要注意,如果是自己写程序,_att_sp.yaw_body最好更换为实际的航向值,因为当航向期望值与实际航向值相差过大,很容易出现速度分解出错的问题。

	/*
	 * assisted velocity mode: user controls velocity, but if	velocity is small enough, position
	 * hold is activated for the corresponding axis
	 */

	/* horizontal axes */
	if (_control_mode.flag_control_position_enabled) {
		/* check for pos. hold */
		if (fabsf(req_vel_sp(0)) < _params.hold_xy_dz && fabsf(req_vel_sp(1)) < _params.hold_xy_dz) {
			if (!_pos_hold_engaged) {

				float vel_xy_mag = sqrtf(_vel(0) * _vel(0) + _vel(1) * _vel(1));

				if (_params.hold_max_xy < FLT_EPSILON || vel_xy_mag < _params.hold_max_xy) {
					/* reset position setpoint to have smooth transition from velocity control to position control */
					_pos_hold_engaged = true;
					_pos_sp(0) = _pos(0);
					_pos_sp(1) = _pos(1);

				} else {
					_pos_hold_engaged = false;
				}
			}

		} else {
			_pos_hold_engaged = false;
		}

		/* set requested velocity setpoint */
		if (!_pos_hold_engaged) {
			_pos_sp(0) = _pos(0);
			_pos_sp(1) = _pos(1);
			_run_pos_control = false; /* request velocity setpoint to be used, instead of position setpoint */
			_vel_sp(0) = req_vel_sp_scaled(0);
			_vel_sp(1) = req_vel_sp_scaled(1);
		}
	}

代码首先判断,期望速度是否运行位置控制,如果运行,再判断期望速度是否小于0.1m/s,或者判断实际速度是否小于0.8m/s。如果期望的XY速度小于0.1m/s,或者实际XY的合速度小于0.8m/s,就可以进入位置保持模式,此时直接将实际位置赋值给期望位置。如果不满足上述条件,就是把当前位置赋值给位置期望值,把遥控器的期望速度赋值给期望速度,要注意_run_pos_control这个变量,当它为false时,是不会根据期望位置计算期望速度的,也就是说,不满足前面的条件下,无人机会直接进入速度控制,期望位置只是个摆设而已。这也是比较符合逻辑的,当速度比较慢的时候,无人机用位置控制比较稳定,当无人机速度比较快的速度,直接控制速度会比较稳定。

	/* vertical axis */
	if (_control_mode.flag_control_altitude_enabled) {
		/* check for pos. hold */
		if (fabsf(req_vel_sp(2)) < FLT_EPSILON) {
			if (!_alt_hold_engaged) {
				if (_params.hold_max_z < FLT_EPSILON || fabsf(_vel(2)) < _params.hold_max_z) {
					/* reset position setpoint to have smooth transition from velocity control to position control */
					_alt_hold_engaged = true;
					_pos_sp(2) = _pos(2);

				} else {
					_alt_hold_engaged = false;
				}
			}

		} else {
			_alt_hold_engaged = false;
			_pos_sp(2) = _pos(2);
		}

		/* set requested velocity setpoint */
		if (!_alt_hold_engaged) {
			_run_alt_control = false; /* request velocity setpoint to be used, instead of altitude setpoint */
			_vel_sp(2) = req_vel_sp_scaled(2);
		}
	

垂向速度的控制和水平是差不多的,首先判断标志位是否运行高度控制,如果运行,再判断期望速度是否小于FLT_EPSILON,FLT_EPSILON代表着最小的浮点数,比它还小的就是0了。然后判断期望速度是否小于FLT_EPSILON。如果期望的垂向速度小于FLT_EPSILON,或者实际垂向速度小于0.6,就可以进入高度保持模式,此时直接将实际高度赋值给期望高度。如果不满足上述条件,就是把实际高度赋值给期望高度,把遥控器的期望速度赋值给期望速度。同样的,要注意_run_alt_control这个变量,当它为false时,是不会根据期望高度计算期望垂向速度的,也就是说,不满足前面的条件下,无人机会直接进入垂向速度控制,期望高度只是个摆设而已。

至此,control_manual就解析完了,我们已经获得了x,y,z三个轴的期望位置,如果不是位置保持模式,我们还得到了三个轴的期望速度(此时没有位置控制,只有速度控制)。

4.FLT_EPSILON

首先有#define FLT_EPSILON  1.192092896e-07F // smallest such that 1.0+FLT_EPSILON != 1.0

据说,这个数是为了解决计算机判断1.0和10.0/10.0之间的误差而存在的,计算机或者单片机存储浮点数是有误差,所以如果两个浮点数之间的差小于FLT_EPSILON ,那么就认为它们两个是相等的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

飞控简析-从入门到跑路 第二章PX4的位置控制(1) 的相关文章

  • Guava-Collections2源码解析

    构造器 private Collections2 私有构造器 xff0c 也没有静态构造器 xff0c 所以可以很明确它是一个纯工具类了 功能方法 filter过滤方法 传入一个带过滤的容器 xff0c 和一个实现过滤规则的函数类 xff0
  • QGC 汉化

    效果如下 一 xff0c pro文件修改 不添加 lupdate xff0c qml文件中的 qstr 无法识别 二 xff0c 生成 ts 文件 三 xff0c 利用 Qt 自带的 linguist软件 加载ts文件 翻译后 生成 qm
  • QGC android版 ubuntu编译

    一 xff0c 环境搭建 参照 xff1a https blog csdn net foxbryant article details 51813685 1 软件安装 Ubuntu 版本 xff1a 14 04 Qt xff1a 5 7 1
  • QGC 增加禁飞区显示功能

    效果 xff1a 本功能客只实现地面站上禁飞区静态展示 xff0c 具体限制需飞控端实现 步骤 xff1a 下载禁飞区数据 处理禁飞区数据 禁飞区数据 导入sqlite数据库 初始化读取禁飞区数据 绘制 打包 1 xff0c 下载禁飞区数据
  • Tx2 通过内核编译来获取ttyUSB* 以及ttyACM*的设备名

    最近由于学校实验室准备参加ICRA的DJI AI Challenge xff0c 所以我在使用TX2跑ROS 我用的是ubuntu 16 04 ros kinetic版本 然后遇到了一些问题 再插上通信串口之后usb并没有反应 通过lsus
  • Tx2上运行rplidar A2

    首先 xff0c 有一个比较坑的地方 xff0c Tx2默认情况下是没有rplidar A2的这个驱动的 xff0c 需要我们自己进行内核编译安装 具体安装方法可以看我的Tx2 通过内核编译来获取ttyUSB 以及ttyACM 的设备名 的
  • TX2安装caffe(从头开始,最全教程)

    xff08 大二时记录在有道云笔记的记录 xff0c 现在开始搬运 xff09 准备工作 xff1a 1 刷机 第一次使用TX2 xff0c 那是需要刷机的 由于TX2自带的ubuntu系统比较老 xff0c 不适合我们接下来的工作 xff
  • Ubuntu下C/C++网络编程基本:socket实现tcp的例子

    1 说明 待编辑 2 代码 test server h ifndef INCLUDE TEST SERVER H define INCLUDE TEST SERVER H include lt unistd h gt include lt
  • mavros常用消息类型表

    mavros订阅消息 xff1a global position 订阅GPS数据 消息名称 xff1a mavros global position global 类型名称 xff1a sensor msgs NavSatFix h 类型所
  • 记一次美团校招内推笔试经历

    2019年4月23日星期二晚上七点 这天刚好项目上生产我负责留守做远程技术支持 留下的还有个重要的原因就是今晚我有一个美团的笔试 七点笔试准时开始 信心满满打开网页地址 美团用的是赛码网来做的笔试题库 xff0c 再次之前收到了邀请码输入进
  • git 推送出现 &quot;fatal: The remote end hung up unexpectedly&quot; 解决方案

    在使用git更新或提交 项目时候出现 34 fatal The remote end hung up unexpectedly 34 原因是推送的文件太大 那就简单了 xff0c 要么是缓存不够 xff0c 要么是网络不行 xff0c 要么
  • 双色球规则及c++实现代码

    双色球规则 xff1a 第一章 总 则 第一条 本规则依据财政部 彩票发行与销售管理暂行规定 和 中国福利彩票 xff08 电脑型 xff09 联合发行与销售管理暂行办法 xff08 以下简称 管理办法 xff09 制定 第二条 中国福利彩
  • 卡尔曼滤波以及Matlab实现_参考书籍_核心剖析_经验分享_EKF(1)

    版权声明 xff1a 本文为博主原创文章 xff0c 未经博主允许不得转载 https mp csdn net mdeditor 1 前言 首先 xff0c 关于卡尔曼滤波理论的定义 种类 算法实现过程步骤以及相关例子方面 xff0c 博主
  • 用JAVA实现MapReduce

    这里的MapReduce实现的是分词计数 pom xml文件 版本号需要跟你自己安装的hadoop版本号一样 hadoop commonhadoop hdfshadoop authhadoop clienthadoop mapreduce
  • 高通导航器软件开发包使用指南(10)

    高通导航器软件开发包使用指南 xff08 10 xff09 8 9位置控制参数8 10状态 机器 进程8 11高度 估算器 图8 12最大加速度偏差8 13电压监控图8 14 att control params xff08 附件控制参数
  • VTK 多平面重建(MPR)及三维切片显示

    在此之前翻阅很多资料 爬过很多的坑 花费不少时间 xff0c 终于还是完成了 xff01 虽然实现地很简单 xff0c 但是大体功能是实现了 后面需要完善功能的话 xff0c 可以用qt与vtk结合开发 MPR MPR multi plan
  • 【实战】从零搭建SSO单点登录服务器 - CAS认证流程

    前言 因系统逐渐增多 xff0c 各个业务系统间无法共享用户状态 xff0c 每个系统都需要用户登录 这对于用户来说很不友好 xff0c 于是需要搭建一个SSO单点登录服务器 xff0c 来做统一的登录 注销 写这个系列的文章有两个目的 x
  • ubuntu18.04安装IntelRealsense D435的SDK及相机标定记录

    写在前面的话 早上去实验室临时被塞的活 xff0c 说让标一下D435这个相机 xff0c 那就做呗 xff01 相机认识 Intel RealSense深度摄像头 D435 xff0c 设备采用USB供电形式 xff0c usb3 0的口
  • PHPStorm 2018版本 破解教程

    破解步骤 1 前提 xff1a 下载好需要的文件 xff08 进入网址后 xff0c 点击download now xff09 PHPStorm xff1a https www jetbrains com phpstorm 点击downlo
  • centos7 安装vnc-server 与卸载

    安装vnc server 前题 Linux桌面程序已安装 安装 vnc server 以root用户运行以下命令来安装vncserver yum install tigervnc server tigervnc server module

随机推荐