国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

PX4代碼解析(6)

這篇具有很好參考價值的文章主要介紹了PX4代碼解析(6)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

一、前言

上一節(jié)介紹了PX4姿態(tài)估計調(diào)用函數(shù)的流程,這一節(jié)分享一下我對PX4姿態(tài)解算的解讀.首先,要理解PX4姿態(tài)解算的程序,要先從傳感器的特性入手,這里主要介紹的傳感器有加速度計,磁力計,陀螺儀.

二、傳感器特性

1.加速度計

pixhawk上使用的為三軸加速度計,主要用于測量x,y,z三軸的加速度值,常用的傳感器例如mpu6000與mpu9250,在進(jìn)行PX4二次開發(fā)時,我們并不需要編寫加速度計相關(guān)驅(qū)動程序,其代碼已經(jīng)在PX4_Firmware/src/drivers/imu下進(jìn)行了實現(xiàn),感興趣可以自己查閱.這里我們只需了解加速度計的原理與特性.
px4程序分析,四旋翼,PX4,代碼問題,PX4,無人機,姿態(tài)解算

如圖所示,該圖為加速度計簡易模型,由彈簧與質(zhì)量塊構(gòu)成,當(dāng)外界有加速度時,質(zhì)量塊位置會發(fā)生變化,從而使得電容兩端距離改變,流經(jīng)電容的電流也會發(fā)生變化,通過測量電流大小,就可以得到質(zhì)量塊移動距離,從而得到加速度大小。理想狀態(tài)下,當(dāng)沒有外界作用時,質(zhì)量塊會處于零點位置,但由于工藝,使用時間長短等各種因素,質(zhì)量塊可能會處于非零點位置,即所謂零偏,因此,在飛無人機之前往往需要進(jìn)行校準(zhǔn)。
此外,加速度計校準(zhǔn)還涉及到另一個參數(shù),這個參數(shù)是標(biāo)度因數(shù),在這里可以理解為一個比例系數(shù),測量值乘以這個比例系數(shù)后得到實際值。
從加速度原理可以看出,在測量加速度時質(zhì)量塊需要不斷移動,移動需要時間,因此,在高頻情況下加速度計值不一定準(zhǔn)確,低頻率特性比較好.

2.陀螺儀

pixhawk上另一個比較重要的傳感器就是陀螺儀,陀螺儀用于測量x,y,z三軸角速度,其基本的原理是動量守恒。當(dāng)外部系統(tǒng)發(fā)生旋轉(zhuǎn)時,內(nèi)部轉(zhuǎn)動裝置會保持恒定的速度與方向旋轉(zhuǎn),測量這兩個系統(tǒng)的差就可以得到當(dāng)前系統(tǒng)角速度。為了測量x,y,z三軸角速度,通常采用萬向節(jié)構(gòu)成轉(zhuǎn)動裝置
px4程序分析,四旋翼,PX4,代碼問題,PX4,無人機,姿態(tài)解算
px4程序分析,四旋翼,PX4,代碼問題,PX4,無人機,姿態(tài)解算

以其中一個方向為例,當(dāng)陀螺儀測量裝置隨著轉(zhuǎn)動軸轉(zhuǎn)動時,在半徑方向上會存在力,使得質(zhì)量塊在半徑方向進(jìn)行周期往復(fù)運動,從而得到旋轉(zhuǎn)角速度.但由于存在零點漂移,陀螺儀在低頻特性較差,高頻特性較好.

3.磁力計

磁力計主要用于測量當(dāng)前磁場強度。為了能夠測量地磁方向,通常將地磁分為水平與垂直方向,水平方向可以近似表示地磁方向。但地球的磁軸與地軸有著夾角,一般稱為磁偏角,磁偏角在不同地理位置不同,因此在無人機航向計算時,需要gps獲得當(dāng)前經(jīng)緯度,然后查表得到當(dāng)前位置磁偏角,對航向進(jìn)行修正(后續(xù)代碼中會看到).同樣的磁力計校正也涉及到偏移與標(biāo)度因數(shù).

三、姿態(tài)解算算法

1.為什么采用四元數(shù)計算

在介紹姿態(tài)解算算法前,先來談?wù)勛藨B(tài)的表示方式,常用的姿態(tài)表示方法有:四元數(shù),歐拉角,方向余弦這幾種方式,并且這幾種方式是可以 互相轉(zhuǎn)換,歐拉角雖然計算簡單,但是存在退化現(xiàn)象;方向余弦有9個參數(shù),導(dǎo)致其計算量過大,實時性不好;因此,PX4源碼中采用四元數(shù)來表示姿態(tài)。

2.姿態(tài)解算的坐標(biāo)系

在無人機的坐標(biāo)變換過程中,一般會涉及到以下四個坐標(biāo)系
1.傳感器坐標(biāo)系
傳感器本身具有自己的測量坐標(biāo)系,在安裝過程中會存在安裝誤差,而傳感器讀數(shù)是在傳感器坐標(biāo)系下測得,為了能讓無人機使用,需要將其轉(zhuǎn)換到機體坐標(biāo)系。但加速度計,磁力計,陀螺儀這些傳感器安裝時一般與無人機機體中心位置與方向都重合,所以可以粗略認(rèn)為傳感器坐標(biāo)系與無人機坐標(biāo)系重合
2.機體坐標(biāo)系
一般以無人機幾何中心為中心,以右手定則建立的三維直角坐標(biāo)系,x軸位于無人機機體平面,以無人機前進(jìn)方向為正方向;y軸在機體平面且垂直x軸,z軸垂直機體平面,以向下為正.
3.本地坐標(biāo)系(local)
為了確定無人機相對于地面的速度與位置,需要引入本地坐標(biāo)系(仿真中常用的地面坐標(biāo)系)。一般情況下,本地坐標(biāo)系是以無人機起點為坐標(biāo)中心,在水平面上正北方向為x軸,正東為y軸方向,z軸垂直地面向下,這也是所謂的北東地(NED)坐標(biāo)系
4.全局坐標(biāo)系(global)
前面提到,在不同經(jīng)緯度下,地軸與磁軸之間的磁偏角是不一樣的,為了修正無人機航向,還需要引入GPS測得的地球經(jīng)緯度.

3.Mahony濾波算法

下面來講解算法,如圖為mahony濾波的流程圖,取自文獻(xiàn)[1]
px4程序分析,四旋翼,PX4,代碼問題,PX4,無人機,姿態(tài)解算
先來解釋一下上圖:
1. a , m a,m a,m ω \omega ω分別是加速度計測得的加速度,磁力計測得的磁場強度以及陀螺儀測得的角速度。其實這里就引出了一個問題,為什么需要加速度計與磁力計,光靠陀螺儀不就可以得到無人機姿態(tài)嗎?
確實,光靠陀螺儀是可以得到無人機姿態(tài)的,通過對得到的角速度積分就得到了姿態(tài),但靠積分得到的姿態(tài)會存在積分誤差,這個光靠陀螺儀是無法解決的,因此引入了加速度計與磁力計來解決陀螺儀積分誤差。
2.四元數(shù)微分方程為 Q ˙ = 1 2 ? ω n b b \dot{Q} =\frac{1}{2} \otimes \omega _{nb}^ Q˙?=21??ωnbb?,式中 Q Q Q為姿態(tài)四元數(shù), ω n b b \omega _{nb}^ ωnbb?為無人機機體坐標(biāo)系b相對于北東地(NED)坐標(biāo)系n的角速度,這個式子是姿態(tài)解算的核心
3.由加速度計與磁力計得來的姿態(tài)同樣存在誤差,因此需要PI控制器來對誤差進(jìn)行修正,PI控制器的輸入是由加速度計與磁力計算出的姿態(tài)與最終通過四元數(shù)微分方程計算出的實際姿態(tài)的差值,輸出角速度修正值,補償?shù)酵勇輧x,抵消陀螺儀誤差
4.這個過程在無人機運行過程中循環(huán)計算,不斷進(jìn)行姿態(tài)解算

四、姿態(tài)解算算法代碼講解

在PX4代碼解析(5)中我已經(jīng)介紹了代碼運行流程,本節(jié)只針對姿態(tài)解算算法重點部分進(jìn)行講解,我將這部分代碼分為以下幾個部分:
1.AttitudeEstimatorQ對象建立以及相關(guān)數(shù)據(jù)獲取
2.四元數(shù)q的初始化
3.姿態(tài)解算

1.AttitudeEstimatorQ對象的構(gòu)造函數(shù)

先來看看.AttitudeEstimatorQ對象的構(gòu)造函數(shù)

//在對象的構(gòu)造函數(shù)中,主要是對對象成員變量清0
AttitudeEstimatorQ::AttitudeEstimatorQ() :
	ModuleParams(nullptr),
	WorkItem(MODULE_NAME, px4::wq_configurations::att_pos_ctrl)
{
	_vel_prev.zero();
	_pos_acc.zero();
//gyro是陀螺儀數(shù)據(jù),是一個1*3或者3*1向量
	_gyro.zero();
//accel表示的是加速度計數(shù)據(jù)
	_accel.zero();
//mag是磁力計數(shù)據(jù)
	_mag.zero();
//vision是視覺數(shù)據(jù),mocap是動作捕捉的數(shù)據(jù),暫時用不到
	_vision_hdg.zero();
	_mocap_hdg.zero();
//四元數(shù)q清0
	_q.zero();
	_rates.zero();
	_gyro_bias.zero();
	//將參數(shù)文件中參數(shù)拷貝到當(dāng)前進(jìn)程使用,這些參數(shù)就是你在地面站里可以修改的那些,比如加速度計的偏移等數(shù)據(jù)
    update_parameters(true);
}

2.Run函數(shù)

在執(zhí)行完構(gòu)造函數(shù)后,該進(jìn)程會執(zhí)行run函數(shù)

//該函數(shù)主要是讀取各傳感器數(shù)據(jù),并分配給相應(yīng)變量,為后續(xù)姿態(tài)解算做前置工作
void
AttitudeEstimatorQ::Run()
{
//第一個if主要用于判斷傳感器那邊數(shù)據(jù)準(zhǔn)備好沒有
	if (should_exit()) {
		_sensors_sub.unregisterCallback();
		exit_and_cleanup();
		return;
	}
//定義了一個sensor_combine的結(jié)構(gòu)體,用于接收數(shù)據(jù)
	sensor_combined_s sensors;
//將數(shù)據(jù)拷貝到sensors
	if (_sensors_sub.update(&sensors)) {

        update_parameters();//默認(rèn)參數(shù)為force
    //檢查數(shù)據(jù)是否為最新陀螺儀
		// Feed validator with recent sensor data
		if (sensors.timestamp > 0) {
			_gyro(0) = sensors.gyro_rad[0];
			_gyro(1) = sensors.gyro_rad[1];
			_gyro(2) = sensors.gyro_rad[2];
		}
//更新加速度計數(shù)據(jù)
		if (sensors.accelerometer_timestamp_relative != sensor_combined_s::RELATIVE_TIMESTAMP_INVALID) {
			_accel(0) = sensors.accelerometer_m_s2[0];
			_accel(1) = sensors.accelerometer_m_s2[1];
			_accel(2) = sensors.accelerometer_m_s2[2];

			if (_accel.length() < 0.01f) {
				PX4_ERR("degenerate accel!");
				return;
			}
		}

        // U更新磁力計數(shù)據(jù)
		if (_magnetometer_sub.updated()) {
			vehicle_magnetometer_s magnetometer;

			if (_magnetometer_sub.copy(&magnetometer)) {
				_mag(0) = magnetometer.magnetometer_ga[0];
				_mag(1) = magnetometer.magnetometer_ga[1];
				_mag(2) = magnetometer.magnetometer_ga[2];
//如果磁力計數(shù)據(jù)太小,則返回報錯
				if (_mag.length() < 0.01f) {
					PX4_ERR("degenerate mag!");
					return;
				}
			}

		}
//數(shù)據(jù)更新完成標(biāo)志位
		_data_good = true;

		// Update vision and motion capture heading
        //是否有視覺或者動作捕捉,false不使用
		_ext_hdg_good = false;

		if (_vision_odom_sub.updated()) {
			vehicle_odometry_s vision;

			if (_vision_odom_sub.copy(&vision)) {
				// validation check for vision attitude data
				bool vision_att_valid = PX4_ISFINITE(vision.q[0])
							&& (PX4_ISFINITE(vision.pose_covariance[vision.COVARIANCE_MATRIX_ROLL_VARIANCE]) ? sqrtf(fmaxf(
									vision.pose_covariance[vision.COVARIANCE_MATRIX_ROLL_VARIANCE],
									fmaxf(vision.pose_covariance[vision.COVARIANCE_MATRIX_PITCH_VARIANCE],
											vision.pose_covariance[vision.COVARIANCE_MATRIX_YAW_VARIANCE]))) <= _eo_max_std_dev : true);

				if (vision_att_valid) {
					Dcmf Rvis = Quatf(vision.q);
					Vector3f v(1.0f, 0.0f, 0.4f);

					// Rvis is Rwr (robot respect to world) while v is respect to world.
					// Hence Rvis must be transposed having (Rwr)' * Vw
					// Rrw * Vw = vn. This way we have consistency
					_vision_hdg = Rvis.transpose() * v;

					// vision external heading usage (ATT_EXT_HDG_M 1)
					if (_param_att_ext_hdg_m.get() == 1) {
						// Check for timeouts on data
						_ext_hdg_good = vision.timestamp > 0 && (hrt_elapsed_time(&vision.timestamp) < 500000);
					}
				}
			}
		}
        //動捕
		if (_mocap_odom_sub.updated()) {
			vehicle_odometry_s mocap;

			if (_mocap_odom_sub.copy(&mocap)) {
				// validation check for mocap attitude data
				bool mocap_att_valid = PX4_ISFINITE(mocap.q[0])
						       && (PX4_ISFINITE(mocap.pose_covariance[mocap.COVARIANCE_MATRIX_ROLL_VARIANCE]) ? sqrtf(fmaxf(
								       mocap.pose_covariance[mocap.COVARIANCE_MATRIX_ROLL_VARIANCE],
								       fmaxf(mocap.pose_covariance[mocap.COVARIANCE_MATRIX_PITCH_VARIANCE],
										       mocap.pose_covariance[mocap.COVARIANCE_MATRIX_YAW_VARIANCE]))) <= _eo_max_std_dev : true);

				if (mocap_att_valid) {
					Dcmf Rmoc = Quatf(mocap.q);
					Vector3f v(1.0f, 0.0f, 0.4f);

					// Rmoc is Rwr (robot respect to world) while v is respect to world.
					// Hence Rmoc must be transposed having (Rwr)' * Vw
					// Rrw * Vw = vn. This way we have consistency
					_mocap_hdg = Rmoc.transpose() * v;

					// Motion Capture external heading usage (ATT_EXT_HDG_M 2)
					if (_param_att_ext_hdg_m.get() == 2) {
						// Check for timeouts on data
						_ext_hdg_good = mocap.timestamp > 0 && (hrt_elapsed_time(&mocap.timestamp) < 500000);
					}
				}
			}
		}
//上面的代碼是讀取視覺與動捕數(shù)據(jù),沒有這些傳感器的用不到
        //如果gps數(shù)據(jù)更新
		if (_gps_sub.updated()) {
            vehicle_gps_position_s gps;//定義結(jié)構(gòu)體,該結(jié)構(gòu)體在msg文件夾中

			if (_gps_sub.copy(&gps)) {
			//如果gps數(shù)據(jù)獲取成功并且精度不錯,則使用gps數(shù)據(jù)查閱磁偏角
				if (_param_att_mag_decl_a.get() && (gps.eph < 20.0f)) {
//通過gps得到的經(jīng)緯度查表,得到磁偏角,去修正磁力計數(shù)據(jù)
					update_mag_declination(math::radians(get_mag_declination(gps.lat, gps.lon)));
				}
			}
		}
//更新在NED坐標(biāo)系的位置
		if (_local_position_sub.updated()) {
			vehicle_local_position_s lpos;

			if (_local_position_sub.copy(&lpos)) {
//是否進(jìn)行運動加速度計算,加速度計數(shù)據(jù)比較好
				if (_param_att_acc_comp.get() && (hrt_elapsed_time(&lpos.timestamp) < 20_ms)
				    && lpos.v_xy_valid && lpos.v_z_valid && (lpos.eph < 5.0f) && _inited) {

					/* position data is actual */
					const Vector3f vel(lpos.vx, lpos.vy, lpos.vz);
//更新速度
					/* velocity updated */
					if (_vel_prev_t != 0 && lpos.timestamp != _vel_prev_t) {
					//將時間由us轉(zhuǎn)為s
						float vel_dt = (lpos.timestamp - _vel_prev_t) / 1e6f;
                        /* calculate acceleration in body frame *///補償加速度計
						_pos_acc = _q.conjugate_inversed((vel - _vel_prev) / vel_dt);
					}

					_vel_prev_t = lpos.timestamp;
					_vel_prev = vel;

                } else {//數(shù)據(jù)過期,重置加速度
					/* position data is outdated, reset acceleration */
					_pos_acc.zero();
					_vel_prev.zero();
					_vel_prev_t = 0;
				}
			}
		}
//以上是獲取數(shù)據(jù),對數(shù)據(jù)簡單處理
//上一次迭代時間
		/* time from previous iteration */
     hrt_abstime now = hrt_absolute_time();//高精度定時器,得到當(dāng)前時間
     //得到用于計算積分的步長時間
		const float dt = math::constrain((now - _last_time) / 1e6f, _dt_min, _dt_max);
		//更新last_time
		_last_time = now;

        if (update(dt)) {//姿態(tài)解算
            vehicle_attitude_s att = {};//
			att.timestamp = sensors.timestamp;
			_q.copyTo(att.q);

			/* the instance count is not used here */
            _att_pub.publish(att);//姿態(tài)發(fā)布

#if defined(ENABLE_LOCKSTEP_SCHEDULER)

			if (_param_est_group.get() == 3) {
				// In this case the estimator_q is running without LPE and needs
				// to publish ekf2_timestamps because SITL lockstep requires it.
				ekf2_timestamps_s ekf2_timestamps;
				ekf2_timestamps.timestamp = now;
				ekf2_timestamps.airspeed_timestamp_rel = 0;
				ekf2_timestamps.distance_sensor_timestamp_rel = 0;
				ekf2_timestamps.optical_flow_timestamp_rel = 0;
				ekf2_timestamps.vehicle_air_data_timestamp_rel = 0;
				ekf2_timestamps.vehicle_magnetometer_timestamp_rel = 0;
				ekf2_timestamps.visual_odometry_timestamp_rel = 0;
				_ekf2_timestamps_pub.publish(ekf2_timestamps);
			}

#endif
		}
	}
}

代碼中,我對其進(jìn)行了注釋,下面有幾個需要強調(diào)的點

1.sensor_combined_s是什么?
他是一個結(jié)構(gòu)體,這個結(jié)構(gòu)體是通過定義的消息自動生成,可以在msg文件中查看,在msg文件中找到sensor_combine,文件內(nèi)容如下:

# Sensor readings in SI-unit form.
# These fields are scaled and offset-compensated where possible and do not
# change with board revisions and sensor updates.

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
//陀螺儀數(shù)據(jù)
# 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
//加速度計數(shù)據(jù)
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

可以看到,在結(jié)構(gòu)體sensor_combine_s中,存放了陀螺儀與加速度計的數(shù)據(jù),所以run函數(shù)一直到_data_good = true;代碼之前的工作就是更新陀螺儀,加速度計,磁力計.當(dāng)_data_good=true,說明數(shù)據(jù)準(zhǔn)備完成

2._pos_acc是運動加速度,運動加速度與加速度計測量的值不同,加速度計的值=運動加速度+重力加速度;在這里,代碼利用兩次速度之差除以時間得到運動加速度,這個運動加速度在后續(xù)姿態(tài)解算有用

3.update(dt)函數(shù)就是姿態(tài)解算的核心代碼,下面對這部分代碼進(jìn)行解讀

3.update姿態(tài)解算函數(shù)

我先把姿態(tài)解算的代碼貼上來

bool
AttitudeEstimatorQ::update(float dt)
{
  //判斷是否存在四元數(shù)初值
	if (!_inited) {
//如果數(shù)據(jù)沒有準(zhǔn)備好,就是加速度計,磁力計這些數(shù)據(jù)沒有更新,則不進(jìn)行下面操作
		if (!_data_good) {
			return false;
		}
		//執(zhí)行四元數(shù)初始化函數(shù)
        return init_attq();//進(jìn)行初始姿態(tài)獲取
	}

1.從前面的原理可知,姿態(tài)解算的核心公式是四元數(shù)的微分方程,而為了實現(xiàn)微分方程的計算,我們需要一個四元數(shù)初值,四元數(shù)初值是通過init_attq()函數(shù)產(chǎn)生,代碼如下:

bool
AttitudeEstimatorQ::init_attq()
{
	// Rotation matrix can be easily constructed from acceleration and mag field vectors
	//初始方向余弦矩陣可以由加速度計與磁力計獲得
	// 'k' is Earth Z axis (Down) unit vector in body frame
	//以加速度計測得向量反方向作為z軸,因為加速度計測量坐標(biāo)系z軸向上,與NED坐標(biāo)系的z軸方向相反
  Vector3f k = -_accel;
  //進(jìn)行歸一化,k向量除以它的二范數(shù),目的是為了構(gòu)成余弦矩陣
	k.normalize();

	// 'i' is Earth X axis (North) unit vector in body frame, orthogonal with 'k'
	//_mag*k可以看做
   Vector3f i = (_mag - k * (_mag * k));//以磁力計作為x軸,保證x與z軸正交
	i.normalize();

	// 'j' is Earth Y axis (East) unit vector in body frame, orthogonal with 'k' and 'i'
	向量叉乘得到y(tǒng)軸
    Vector3f j = k % i;//向量叉乘得到y(tǒng)軸

	// Fill rotation matrix
	//填充旋轉(zhuǎn)矩陣
    Dcmf R;//旋轉(zhuǎn)矩陣
	R.row(0) = i;
	R.row(1) = j;
	R.row(2) = k;

	// Convert to quaternion
	//轉(zhuǎn)換為四元數(shù),PX4中重載了賦值號
	_q = R;

	// Compensate for magnetic declination
	//補償磁力計偏移
	Quatf decl_rotation = Eulerf(0.0f, 0.0f, _mag_decl);
//四元數(shù)更新并重新初始化
	_q = _q * decl_rotation;
	_q.normalize();
//判斷更新是否成功
	if (PX4_ISFINITE(_q(0)) && PX4_ISFINITE(_q(1)) &&
	    PX4_ISFINITE(_q(2)) && PX4_ISFINITE(_q(3)) &&
	    _q.length() > 0.95f && _q.length() < 1.05f) {
		_inited = true;

	} else {
		_inited = false;
	}
	return _inited;
}

這里面需要提醒的是這句代碼
Vector3f i = (_mag - k * (_mag * k))
代碼中_mag與k都為向量,且向量k在經(jīng)歷歸一化后為單位向量,因此_mag*k可以看做_mag在k向量上的投影長度,然后乘以單位向量k就變成了與k同向,長度為|_mag|*cos θ \theta θ的向量,再使用_mag減去該向量就得到垂直于k的向量i,具體可見下圖
px4程序分析,四旋翼,PX4,代碼問題,PX4,無人機,姿態(tài)解算這里就完成了第一段講解,接著向下看

//還是update函數(shù)
//記錄上一次四元數(shù)值
	Quatf q_last = _q;

	// Angular rate of correction
	Vector3f corr;//
	float spinRate = _gyro.length();
//_ext_hdg_good==false,不使用動作捕捉與視覺
	if (_param_att_ext_hdg_m.get() > 0 && _ext_hdg_good) {
		if (_param_att_ext_hdg_m.get() == 1) {
			// Vision heading correction
			// Project heading to global frame and extract XY component
			Vector3f vision_hdg_earth = _q.conjugate(_vision_hdg);
			float vision_hdg_err = wrap_pi(atan2f(vision_hdg_earth(1), vision_hdg_earth(0)));
			// Project correction to body frame
			corr += _q.conjugate_inversed(Vector3f(0.0f, 0.0f, -vision_hdg_err)) * _param_att_w_ext_hdg.get();
		}

		if (_param_att_ext_hdg_m.get() == 2) {
			// Mocap heading correction
			// Project heading to global frame and extract XY component
			Vector3f mocap_hdg_earth = _q.conjugate(_mocap_hdg);
			float mocap_hdg_err = wrap_pi(atan2f(mocap_hdg_earth(1), mocap_hdg_earth(0)));
			// Project correction to body frame
			corr += _q.conjugate_inversed(Vector3f(0.0f, 0.0f, -mocap_hdg_err)) * _param_att_w_ext_hdg.get();
		}
	}
//使用磁力計
	if (_param_att_ext_hdg_m.get() == 0 || !_ext_hdg_good) {
		// Magnetometer correction
    //我們使用磁力計的目的是得到無人機的偏航角,因此我們需要將磁力計的值投影到NED坐標(biāo)系的XY平面,
    //磁力計得到數(shù)據(jù)是位于機體坐標(biāo)系,需要把其轉(zhuǎn)換到NED坐標(biāo)系后投影
		// Project mag field vector to global frame and extract XY component
		//從機體坐標(biāo)系轉(zhuǎn)到NED系
		Vector3f mag_earth = _q.conjugate(_mag);
		//_mag_decl就是剛剛查表得到的磁偏角,這里是對磁力計補償,得到磁偏角誤差
		//wrap_pi()函數(shù)是個限幅函數(shù),保證磁力計誤差在-pi到pi(因為我們拿磁力計去算偏航,偏航范圍是這個)
		float mag_err = wrap_pi(atan2f(mag_earth(1), mag_earth(0)) - _mag_decl);
		//以下四句代碼,個人理解是在對磁力計進(jìn)行標(biāo)度因數(shù)的校正
		float gainMult = 1.0f;//標(biāo)度因數(shù)
		const float fifty_dps = 0.873f;
		if (spinRate > fifty_dps) {
			gainMult = math::min(spinRate / fifty_dps, 10.0f);
		}
//轉(zhuǎn)換到機體坐標(biāo)系,conjugate_inversed()是將NED坐標(biāo)系向量轉(zhuǎn)為機體坐標(biāo)系,_param_att_w_mag為磁力計濾波系數(shù)
//磁力計標(biāo)度因子
        // Project magnetometer correction to body frame
		corr += _q.conjugate_inversed(Vector3f(0.0f, 0.0f, -mag_err)) * _param_att_w_mag.get() * gainMult;
	}
//四元數(shù)歸一化
	_q.normalize();

//加速度計的修正
//首先,實際加速度 =加速度計的值-重力加速度
//下面的k向量是重力加速度歸一化后從NED系轉(zhuǎn)到機體坐標(biāo)系后的表示
	// Accelerometer correction
	// Project 'k' unit vector of earth frame to body frame
	// Vector3f k = _q.conjugate_inversed(Vector3f(0.0f, 0.0f, 1.0f));
	// Optimized version with dropped zeros
 

	Vector3f k(
		2.0f * (_q(1) * _q(3) - _q(0) * _q(2)),
		2.0f * (_q(2) * _q(3) + _q(0) * _q(1)),
		(_q(0) * _q(0) - _q(1) * _q(1) - _q(2) * _q(2) + _q(3) * _q(3))
	);

	// If we are not using acceleration compensation based on GPS velocity,
	// fuse accel data only if its norm is close to 1 g (reduces drift).
	//對數(shù)據(jù)進(jìn)行限幅處理
	const float accel_norm_sq = _accel.norm_squared();
	const float upper_accel_limit = CONSTANTS_ONE_G * 1.1f;
	const float lower_accel_limit = CONSTANTS_ONE_G * 0.9f;

	if (_param_att_acc_comp.get() || ((accel_norm_sq > lower_accel_limit * lower_accel_limit) &&
					  (accel_norm_sq < upper_accel_limit * upper_accel_limit))) {
//這句代碼是重點,我來解釋一下:_accel這是加速度計的值,_pos_acc是通過兩次速度之差計算出的運動加速度,都在機體坐標(biāo)系下
//那么,_accel-_pos_acc就是機體坐標(biāo)系下的重力加速度,然后再歸一化。這里%代表向量叉乘,在傳感器都是理想情況下,k向量與
//(_accel - _pos_acc).normalized()向量的叉乘應(yīng)該為0,但由于存在傳感器誤差,這一項不為0,于是就得到了相應(yīng)的加速度計誤差.
//_param_att_w_acc.get()為互補濾波中加速度計權(quán)重
		corr += (k % (_accel - _pos_acc).normalized()) * _param_att_w_acc.get();
	}
//陀螺儀偏移估計
//_param_att_w_gyro_bias.get()陀螺儀偏移的初始值
	// Gyro bias estimation
	if (spinRate < 0.175f) {
		_gyro_bias += corr * (_param_att_w_gyro_bias.get() * dt);
//對陀螺儀偏移每一項的限幅
		for (int i = 0; i < 3; i++) {
			_gyro_bias(i) = math::constrain(_gyro_bias(i), -_bias_max, _bias_max);
		}

	}
//經(jīng)過修正后的角速度
	_rates = _gyro + _gyro_bias;
//得到角速度的校正值
	// Feed forward gyro
	corr += _rates;

	// Apply correction to state
    //四元數(shù)微分方程求解
	_q += _q.derivative1(corr) * dt;

	// Normalize quaternion
    //四元數(shù)歸一化
	_q.normalize();
//如果四元數(shù)數(shù)據(jù)出現(xiàn)異常,恢復(fù)到最近一次的正常的狀態(tài),并將漂移和角速率置為零.
	if (!(PX4_ISFINITE(_q(0)) && PX4_ISFINITE(_q(1)) &&
	      PX4_ISFINITE(_q(2)) && PX4_ISFINITE(_q(3)))) {

		// Reset quaternion to last good state
		_q = q_last;
		_rates.zero();
		_gyro_bias.zero();
		return false;
	}
	return true;
}

五、參考文獻(xiàn)及博客

[1]儲開斌, 趙爽, 馮成濤. 基于Mahony-EKF的無人機姿態(tài)解算算法[J]. 電子測量與儀器學(xué)報, 2020, 32(12):7.
[2]Valenti, Roberto G , Dryanovski, et al. Sensors, Vol. 15, Pages 19302-19330: Keeping a Good Attitude: A Quaternion-Based Orientation Filter for IMUs and MARGs. 2015.
[3]米剛, 田增山, 金悅,等. 基于MIMU和磁力計的姿態(tài)更新算法研究[J]. 傳感技術(shù)學(xué)報, 2015, 28(1):6.
[4]姿態(tài)估計(互補濾波)文章來源地址http://www.zghlxwxcb.cn/news/detail-621893.html

到了這里,關(guān)于PX4代碼解析(6)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • PX4之飛行控制框架

    PX4之飛行控制框架

    PX4的飛行控制程序通過模塊來實現(xiàn),與飛控相關(guān)的模塊主要有commander,navigator,pos_control,att_control這幾個,分別可以在src/modules目錄中找到。 commander - 指令/事件處理模塊,處理指令、遙控器輸入和各種事件,設(shè)定飛行器狀態(tài)和控制模式 navigator - 導(dǎo)航模塊,根據(jù)指定的任務(wù)輸

    2024年02月14日
    瀏覽(29)
  • 【PX4仿真】使用PX4+Gazebo+MAVROS+ROS進(jìn)行無人機仿真中提高IMU消息頻率的方法

    在無人機仿真中,IMU(慣性測量單元)消息頻率對于路徑規(guī)劃和感知的仿真至關(guān)重要。然而,在使用PX4+Gazebo+MAVROS+ROS進(jìn)行仿真時,可能會遇到頻率受限的情況。本文將介紹如何提高IMU消息頻率。 通過以下命令可以查看到IMU消息的發(fā)布頻率 通常情況下固定在50Hz。 然而,通過

    2024年04月14日
    瀏覽(181)
  • PX4無人機調(diào)參

    PX4無人機調(diào)參

    PX4 1.13.2 日志分析軟件:flight review https://logs.px4.io/ 產(chǎn)生震動的原因: 1,槳葉損壞、動平衡差 2,電機槳座不垂直,電機動平衡差 3,機架剛性不足 4,部件松動 降低震動的方法: 軟件濾波:調(diào)低通濾波或者陷波濾波器參數(shù) 硬件減震:調(diào)減震球的軟度或者加配重 調(diào)參時可以用

    2024年02月15日
    瀏覽(22)
  • 【PX4】Ubuntu20.04+ROS Noetic 配置PX4-v1.13和Gazebo11聯(lián)合仿真環(huán)境【教程】

    【PX4】Ubuntu20.04+ROS Noetic 配置PX4-v1.13和Gazebo11聯(lián)合仿真環(huán)境【教程】

    寫在前面,目前中文互聯(lián)網(wǎng)上關(guān)于 PX4 飛控的學(xué)習(xí)資料較少,筆者查閱了大量的資料整理成這篇博客,貢獻(xiàn)一些學(xué)習(xí)內(nèi)容,碼字不易,如果幫助到您,請您幫我點點贊。 安裝Ubuntu可以查看這篇教程,安裝ros可以查看這篇教程,這里就不再贅述了。 ● 在使用apt安裝的過程中(

    2024年02月08日
    瀏覽(35)
  • PX4與TX2通信

    PX4與TX2通信

    PX4與TX2通信以及相關(guān)數(shù)據(jù)的獲取 目錄 ?1. PX4硬件接口 2. TELEM1、2接口線序 3.??PX4與TX2通信 ?PX4 IO口定義: ??PX4硬件: 4. 通信測試 5. RTPS+ROS Jetson TX2終端: ?pixhawk: 6. 提高IMU數(shù)據(jù)發(fā)布頻率? 方法一:通過mavros包話題訂閱頻率 ?方法二:更改PX4啟動文件 7. GPS數(shù)據(jù)獲取 ? ? PX4是

    2024年02月14日
    瀏覽(24)
  • roslaunch運行px4功能包 報錯

    roslaunch運行px4功能包 報錯

    運行條件ubuntu-16.04 ros-kinetic 隔段時間運行roslaunch 會如下錯誤 [mavros_posix_sitl.launch] is neither a launch file in package [px4] nor is [px4] a launch file name The traceback for the exception was written to the log file 解決: 1.在Firmware下面打開終端,打開環(huán)境變量設(shè)置,查看為Firmware設(shè)置的路徑 2.Firmware下新終端

    2024年02月16日
    瀏覽(17)
  • PX4編譯過程中報錯通用解決辦法

    時刻兩年,再次配置PX4環(huán)境,又踩了一遍坑,過程中遇到報錯真的是欲哭無淚,但是解決完回頭再來看其實問題并不復(fù)雜。 本篇文章面向在PX4-Autopilot目錄執(zhí)行命令 make px4_sitl gazebo 檢測環(huán)境是否配置成功時出現(xiàn)的子模塊缺失的問題,是這次配置環(huán)境時的血淚教訓(xùn)。 這類問題在

    2024年02月07日
    瀏覽(32)
  • 【無人機】PIXHAWK、PX4、APM區(qū)別

    PIXHAWK、PX4、APM APM固件 專為Arduupilot開發(fā)的固件,現(xiàn)也用于PIXHAWK。有ArduCopter社區(qū)支撐、開放,功能全、迭代升級快,適合直接用。由于有較多的歷史兼容性需求,軟件代碼體系相對雜亂,還封裝了PX4的內(nèi)核,學(xué)習(xí)起來困難些。 PX4固件 專為PIXHAWK開發(fā)的固件。相對封閉,代碼體

    2024年02月20日
    瀏覽(86)
  • Ubuntu PX4無人機仿真環(huán)境配置

    Ubuntu PX4無人機仿真環(huán)境配置

    ?目錄 一、VM虛擬機安裝ubuntu18.04 ? 1、VMware安裝 ? 2、新建虛擬機 二、Ubuntu系統(tǒng)配置 ? 1、更改軟件安裝源 ? 2、安裝中文輸入法 三、PX4環(huán)境搭建 ? 1、安裝git ? 2、下載px4源碼 ? 3、安裝ROS ? 4、安裝MAVROS ? 5、安裝QGC ? 6、仿真測試 四、其他工具安裝 ? 1、VScode安裝 ?????

    2024年02月02日
    瀏覽(1048)
  • ubuntu20 安裝px4、mavros、QGroundControl

    ubuntu20 安裝px4、mavros、QGroundControl

    一、安裝PX4 jjm2是我的主文件夾名,可以根據(jù)自己的主文件夾名修改 下載PX4 由于網(wǎng)速原因,我用的是別人已經(jīng)下載好的壓縮包。 鏈接:https://pan.baidu.com/s/1TRwHxNYsfs7p14mdmt0jeA? 提取碼:wstc? 里面有PX4-Autopilot壓縮包,libawt_xawt.so,libjawt.so和已經(jīng)下載好的QGroundControl.AppImage 運行

    2024年02月06日
    瀏覽(20)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包