PX4_ECL_EKF代码分析2

2 篇文章 0 订阅
2 篇文章 1 订阅

写在前面

源码版本:1.6.0rc1
源码位置1:Firmware-1.6.0rc1\src\modules\ekf2_main.cpp
源码位置2:Firmware-1.6.0rc1\src\lib\ecl\EKF\

整体框架:
在这里插入图片描述
上图PX4的EKF代码框架,PX4的代码由两部分组成,一部分是在modules下的ekf2,另一部分是ecl代码库中EKF部分。

第一部分实现数据的订阅(subscribe)、整理、储存、经过处理的数据发布(publish)。
第二部分实现数据的处理。

第二部分:

bool Ekf::update()
{
	if (!_filter_initialised) {
		_filter_initialised = initialiseFilter();

		if (!_filter_initialised) {
			return false;
		}
	}
	// Only run the filter if IMU data in the buffer has been updated
	if (_imu_updated) {
		// perform state and covariance prediction for the main filter
		predictState();
		predictCovariance();
		
		// control fusion of observation data
		controlFusionModes();

		// run a separate filter for terrain estimation
		runTerrainEstimator();

	}
	// the output observer always runs
	calculateOutputStates();

	// check for NaN or inf on attitude states
	if (!ISFINITE(_state.quat_nominal(0)) || !ISFINITE(_output_new.quat_nominal(0))) {
		return false;
	}
	// We don't have valid data to output until tilt and yaw alignment is complete
	return _control_status.flags.tilt_align && _control_status.flags.yaw_align;
}

这个就是整个EKF代码的核心函数了,分别由几个函数组成,各自完成不同的功能。

在首次进入update()这个函数的时候,系统会对变量进行初始化initialiseFilter();这个函数只执行一次,跟进:

bool Ekf::initialiseFilter()
{
	// Keep accumulating measurements until we have a minimum of 10 samples for the required sensors
    //继续累积测量,直到有至少10个样品为所需的传感器
	// Sum the IMU delta angle measurements
	imuSample imu_init = _imu_buffer.get_newest();
	_delVel_sum += imu_init.delta_vel;
	// Sum the magnetometer measurements
	//对磁强计的测量值求和
	if (_mag_buffer.pop_first_older_than(_imu_sample_delayed.time_us, &_mag_sample_delayed)) {
		if ((_mag_counter == 0) && (_mag_sample_delayed.time_us != 0)) {
			// initialise the counter when we start getting data from the buffer
			_mag_counter = 1;
		} else if ((_mag_counter != 0) && (_mag_sample_delayed.time_us != 0)) {
			// increment the sample count and apply a LPF to the measurement
			_mag_counter ++;

			// don't start using data until we can be certain all bad initial data has been flushed
			//在确定所有坏的初始数据都已被清除之前,不要开始使用数据
			if (_mag_counter == (uint8_t)(_obs_buffer_length + 1)) {
				// initialise filter states
				_mag_filt_state = _mag_sample_delayed.mag;

			} else if (_mag_counter > (uint8_t)(_obs_buffer_length + 1)) {
				// noise filter the data
				_mag_filt_state = _mag_filt_state * 0.9f + _mag_sample_delayed.mag * 0.1f;
			}
		}
	}

磁力计数据出栈

	// set the default height source from the adjustable parameter
	if (_hgt_counter == 0) {
		_primary_hgt_source = _params.vdist_sensor_type;
	}

上面代码,选择高度源,可以来自于气压计,GPG,视觉、(毫米波)超声波等。下面开始选择高度源

	// accumulate enough height measurements to be confident in the qulaity of the data
	//积累足够的高度测量值以对保证数据的质量
	if (_primary_hgt_source == VDIST_SENSOR_RANGE) {//使用超声波
		if (_range_buffer.pop_first_older_than(_imu_sample_delayed.time_us, &_range_sample_delayed)) {
			if ((_hgt_counter == 0) && (_range_sample_delayed.time_us != 0)) {
				// initialise the counter height fusion method when we start getting data from the buffer
				_control_status.flags.baro_hgt = false;
				_control_status.flags.gps_hgt = false;
				_control_status.flags.rng_hgt = true;//使能超声波标志位
				_control_status.flags.ev_hgt = false;
				_hgt_counter = 1;

			} else if ((_hgt_counter != 0) && (_range_sample_delayed.time_us != 0)) {
				// increment the sample count and apply a LPF to the measurement
				_hgt_counter ++;
				// don't start using data until we can be certain all bad initial data has been flushed
				if (_hgt_counter == (uint8_t)(_obs_buffer_length + 1)) {
					// initialise filter states
					_rng_filt_state = _range_sample_delayed.rng;

				} else if (_hgt_counter > (uint8_t)(_obs_buffer_length + 1)) {
					// noise filter the data
					_rng_filt_state = 0.9f * _rng_filt_state + 0.1f * _range_sample_delayed.rng;
				}
			}
		}//超声波数据出栈
	} else if (_primary_hgt_source == VDIST_SENSOR_BARO || _primary_hgt_source == VDIST_SENSOR_GPS) {//使用气压计或者GPS
		// if the user parameter specifies use of GPS for height we use baro height initially and switch to GPS
		//如果user参数指定高度使用GPS,我们首先使用气压高度,然后切换到GPS
		// later when it passes checks.
		if (_baro_buffer.pop_first_older_than(_imu_sample_delayed.time_us, &_baro_sample_delayed)) {
			if ((_hgt_counter == 0) && (_baro_sample_delayed.time_us != 0)) {
				// initialise the counter and height fusion method when we start getting data from the buffer
				_control_status.flags.baro_hgt = true;//使用气压计
				_control_status.flags.gps_hgt = false;
				_control_status.flags.rng_hgt = false;
				_hgt_counter = 1;

			} else if ((_hgt_counter != 0) && (_baro_sample_delayed.time_us != 0)) {
				// increment the sample count and apply a LPF to the measurement
				_hgt_counter ++;

				// don't start using data until we can be certain all bad initial data has been flushed
				if (_hgt_counter == (uint8_t)(_obs_buffer_length + 1)) {
					// initialise filter states
					_baro_hgt_offset = _baro_sample_delayed.hgt;

				} else if (_hgt_counter > (uint8_t)(_obs_buffer_length + 1)) {
					// noise filter the data
					_baro_hgt_offset = 0.9f * _baro_hgt_offset + 0.1f * _baro_sample_delayed.hgt;
				}
			}
		}
	} else if (_primary_hgt_source == VDIST_SENSOR_EV) {//使用视觉传感器
		_hgt_counter = _ev_counter;
	} else {
		return false;
	}
	// check to see if we have enough measurements and return false if not
	bool hgt_count_fail = _hgt_counter <= 2 * _obs_buffer_length;
	bool mag_count_fail = _mag_counter <= 2 * _obs_buffer_length;
	bool ev_count_fail = ((_params.fusion_mode & MASK_USE_EVPOS) || (_params.fusion_mode & MASK_USE_EVYAW)) && (_ev_counter <= 2 * _obs_buffer_length);
	
		if (hgt_count_fail || mag_count_fail || ev_count_fail) {
		return false;
	} else {
		// reset variables that are shared with post alignment GPS checks重置与后校准GPS检查共享的变量
		_gps_drift_velD = 0.0f;
		_gps_alt_ref = 0.0f;
		// Zero all of the states
		_state.vel.setZero();
		_state.pos.setZero();
		_state.gyro_bias.setZero();
		_state.accel_bias.setZero();
		_state.mag_I.setZero();
		_state.mag_B.setZero();
		_state.wind_vel.setZero();

检测是否有足够的测量数据,如果没有则返回 false,复位状态

// get initial roll and pitch estimate from delta velocity vector, assuming vehicle is static
//假设飞行器是静态的,从速度矢量得到初始横滚和俯仰估计
	float pitch = 0.0f;
	float roll = 0.0f;
	if (_delVel_sum.norm() > 0.001f) {
		_delVel_sum.normalize();
		pitch = asinf(_delVel_sum(0));
		roll = atan2f(-_delVel_sum(1), -_delVel_sum(2));
	} else {
		return false;
	}
// calculate initial tilt alignment
		matrix::Euler<float> euler_init(roll, pitch, 0.0f);
		_state.quat_nominal = Quaternion(euler_init);
		_output_new.quat_nominal = _state.quat_nominal;
		_R_to_earth = quat_to_invrotmat(_state.quat_nominal);

根据上面得到的初始pitch 、roll计算初始倾斜角,并更新机体系到导航系的旋转矩阵。

	Vector3f mag_init = _mag_filt_state;
	
	// calculate the initial magnetic field and yaw alignment
	_control_status.flags.yaw_align = resetMagHeading(mag_init);

磁力计数值初始化,进行偏航校准,后面的超声波作为高度源的代码没有粘贴直接跳过。

		// initialise the state covariance matrix
		initialiseCovariance();

协方差初始化函数,进去看一下:

void Ekf::initialiseCovariance()
{
	for (unsigned i = 0; i < _k_num_states; i++) {//首先是协方差矩阵内容清零,以便赋值
		for (unsigned j = 0; j < _k_num_states; j++) {
			P[i][j] = 0.0f;
		}
	}
	// calculate average prediction time step in sec//计算平均预测时间 单位 秒
	float dt = 0.001f * (float)FILTER_UPDATE_PERIOD_MS;

	// define the initial angle uncertainty as variances for a rotation vector
	//定义初始角度不确定度为旋转矢量的方差
	Vector3f rot_vec_var;
	rot_vec_var(2) = rot_vec_var(1) = rot_vec_var(0) = sq(_params.initial_tilt_err);

	//使用旋转向量方差初始化四元数协方差
	initialiseQuatCovariances(rot_vec_var);

	// velocity
	P[4][4] = sq(fmaxf(_params.gps_vel_noise, 0.01f));
	P[5][5] = P[4][4];
	P[6][6] = sq(1.5f) * P[4][4];

	// position
	P[7][7] = sq(fmaxf(_params.gps_pos_noise, 0.01f));
	P[8][8] = P[7][7];
	if (_control_status.flags.rng_hgt) {//使用超声波 (这里不使用)
		P[9][9] = sq(fmaxf(_params.range_noise, 0.01f));
	} else if (_control_status.flags.gps_hgt) {//用GPS
		float lower_limit = fmaxf(_params.gps_pos_noise, 0.01f);
		float upper_limit = fmaxf(_params.pos_noaid_noise, lower_limit);
		P[9][9] = sq(1.5f * math::constrain(_gps_sample_delayed.vacc, lower_limit, upper_limit));
	} else {
		P[9][9] = sq(fmaxf(_params.baro_noise, 0.01f));
	}

	// gyro bias
	P[10][10] = sq(_params.switch_on_gyro_bias * dt);
	P[11][11] = P[10][10];
	P[12][12] = P[10][10];

	// accel bias
	_prev_dvel_bias_var(0) = P[13][13] = sq(_params.switch_on_accel_bias * dt);
	_prev_dvel_bias_var(1) = P[14][14] = P[13][13];
	_prev_dvel_bias_var(2) = P[15][15] = P[13][13];
	
	// earth frame and body frame magnetic field
	// set to observation variance
	for (uint8_t index=16; index <= 21; index ++) {
		P[index][index] = sq(_params.mag_noise);
	}

	// wind
	P[22][22] = 1.0f;
	P[23][23] = 1.0f;
}

initialiseQuatCovariances(rot_vec_var);这个函数可以到matlab模型里面可以找到 ,编译后会生成此函数,matlab路径:

Firmware-1.6.0rc1\src\lib\ecl\EKF\matlab\Inertial Nav EKF\QuatErrTransferEquations.m

生成的.c文件也在同路径下。

	//	重置基本的融合超时计数器
		_time_last_hgt_fuse = _time_last_imu;
		_time_last_pos_fuse = _time_last_imu;
		_time_last_vel_fuse = _time_last_imu;
		_time_last_hagl_fuse = _time_last_imu;
		_time_last_of_fuse = _time_last_imu;
	// reset the output predictor state history to match the EKF initial values
	//重置输出预测器状态历史以匹配EKF初始值
		alignOutputFilter();
		return true;
	}
}//end bool Ekf::initialiseFilter();
void Ekf::alignOutputFilter()
{
	// calculate the quaternion delta between the output and EKF quaternions at the EKF fusion time horizon
	//在EKF融合时间层计算输出和EKF四元数之间的delta四元数
	Quaternion quat_inv = _state.quat_nominal.inversed();
	Quaternion q_delta =  _output_sample_delayed.quat_nominal * quat_inv;
	q_delta.normalize();

	// calculate the velocity and posiiton deltas between the output and EKF at the EKF fusion time horizon
	//在EKF融合时间层计算输出和EKF之间的速度和位置增量
	Vector3f vel_delta = _state.vel - _output_sample_delayed.vel;
	Vector3f pos_delta = _state.pos - _output_sample_delayed.pos;

	// loop through the output filter state history and add the deltas
	//循环遍历输出过滤器状态历史记录并添加增量
	outputSample output_states = {};
	unsigned output_length = _output_buffer.get_length();

	for (unsigned i = 0; i < output_length; i++) {
		output_states = _output_buffer.get_from_index(i);
		output_states.quat_nominal *= q_delta;
		output_states.quat_nominal.normalize();
		output_states.vel += vel_delta;
		output_states.pos += pos_delta;
		_output_buffer.push_to_index(i, output_states);
	}
}

以上就是我对ECL库中EKF代码初始化函数的理解,有很多写在了注释里面,也有很多不理解的地方还在学习中,如有理解错误的地方请批评指正,欢迎一起学习填坑PX4,QQ:1103706199

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用Python编写的Kepler轨道预报代码: ```python import math import numpy as np # 定义常量 G = 6.67428e-11 # 万有引力常数 M = 5.98e24 # 地球质量 omega = 7.292115e-5 # 地球自转角速度 class Satellite: def __init__(self, name, a, e, i, RAAN, omega, M0, t0): """ :param name: 卫星名称 :param a: 长半轴 km :param e: 离心率 :param i: 倾角 弧度制 :param RAAN: 升交点赤经 弧度制 :param omega: 近地点幅角 弧度制 :param M0: 初始平近点角 弧度制 :param t0: 初始时间(相对于J2000时刻的秒数) """ self.name = name self.a = a self.e = e self.i = i self.RAAN = RAAN self.omega = omega self.M0 = M0 self.t0 = t0 self.n = 0 def mean_motion(self): """ 计算平均角速度 :return: """ self.n = math.sqrt(G * M / self.a ** 3) def eccentric_anomaly(self, M): """ 计算偏近点角 :param M: 平近点角 :return: """ E0 = M # 迭代初值 while True: E1 = M + self.e * math.sin(E0) if abs(E1 - E0) < 1e-10: break else: E0 = E1 return E1 def true_anomaly(self, E): """ 计算真近点角 :param E: 偏近点角 :return: """ tanf2 = math.sqrt((1 + self.e) / (1 - self.e)) * math.tan(E / 2) f = 2 * math.atan(tanf2) return f def orbital_plane(self, f): """ 计算轨道面上的r、v :param f: 真近点角 :return: """ r = self.a * (1 - self.e ** 2) / (1 + self.e * math.cos(f)) v = math.sqrt(G * M / self.a) / np.sqrt(1 - self.e ** 2) * np.array([-math.sin(f), self.e + math.cos(f)]) return r, v def ecliptic_plane(self, r, v): """ 计算黄道面上的坐标 :param r: 轨道面上的r(numpy数组) :param v: 轨道面上的v(numpy数组) :return: """ # 计算转移矩阵 R3_omega = np.array([[math.cos(self.omega), math.sin(self.omega), 0], [-math.sin(self.omega), math.cos(self.omega), 0], [0, 0, 1]]) R1_i = np.array([[1, 0, 0], [0, math.cos(self.i), math.sin(self.i)], [0, -math.sin(self.i), math.cos(self.i)]]) R3_RAAN = np.array([[math.cos(self.RAAN), math.sin(self.RAAN), 0], [-math.sin(self.RAAN), math.cos(self.RAAN), 0], [0, 0, 1]]) R = np.dot(np.dot(R3_RAAN, R1_i), R3_omega) # 计算黄道面上的坐标 r_ecl = np.dot(R, r) v_ecl = np.dot(R, v) + np.cross(np.array([0, 0, omega]), r_ecl) return r_ecl, v_ecl def get_position_velocity(self, t): """ 计算给定时刻的位置和速度 :param t: 相对于初始时刻的秒数 :return: """ M = self.M0 + self.n * (t - self.t0) E = self.eccentric_anomaly(M) f = self.true_anomaly(E) r, v = self.orbital_plane(f) r_ecl, v_ecl = self.ecliptic_plane(r, v) return r_ecl, v_ecl ``` 使用方法: 首先实例化一个`Satellite`对象,如下: ```python from satellite import Satellite sat = Satellite(name='卫星1', a=7000, e=0.01, i=math.radians(45), RAAN=math.radians(60), omega=math.radians(90), M0=0, t0=0) ``` 然后可以使用`get_position_velocity`方法计算轨道上某一时刻的黄道坐标。例如,计算J2000时刻(2000年1月1日12:00:00)过去了一小时后的卫星位置和速度: ```python r_ecl, v_ecl = sat.get_position_velocity(t=3600) print(r_ecl, v_ecl) ``` 输出: ``` [ 2673.07273907 -4630.10465513 -2780.04277882] [-1.73752816 0.95790365 5.54068453] ``` 其中,`r_ecl`是一个包含三个元素的numpy数组,表示卫星在黄道坐标系下的位置(单位:千米);`v_ecl`也是一个包含三个元素的numpy数组,表示卫星在黄道坐标系下的速度(单位:千米/秒)。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值