示例#1
0
文件: control.cpp 项目: vvranjek/ecl
void Ekf::controlMagFusion()
{
	// If we are using external vision data for heading then no magnetometer fusion is used
	if (_control_status.flags.ev_yaw) {
		return;
	}

	// If we are on ground, store the local position and time to use as a reference
	if (!_control_status.flags.in_air) {
		_last_on_ground_posD = _state.pos(2);

	}

	// checs for new magnetometer data tath has fallen beind the fusion time horizon
	if (_mag_data_ready) {

		// Determine if we should use simple magnetic heading fusion which works better when there are large external disturbances
		// or the more accurate 3-axis fusion
		if (_params.mag_fusion_type == MAG_FUSE_TYPE_AUTO) {
			// start 3D fusion if in-flight and height has increased sufficiently
			// to be away from ground magnetic anomalies
			// don't switch back to heading fusion until we are back on the ground
			bool height_achieved = (_last_on_ground_posD - _state.pos(2)) > 1.5f;
			bool use_3D_fusion = _control_status.flags.in_air && (_control_status.flags.mag_3D || height_achieved);

			if (use_3D_fusion && _control_status.flags.tilt_align) {
				// if transitioning into 3-axis fusion mode, we need to initialise the yaw angle and field states
				if (!_control_status.flags.mag_3D) {
					_control_status.flags.yaw_align = resetMagHeading(_mag_sample_delayed.mag);
				}

				// use 3D mag fusion when airborne
				_control_status.flags.mag_hdg = false;
				_control_status.flags.mag_3D = true;

			} else {
				// use heading fusion when on the ground
				_control_status.flags.mag_hdg = true;
				_control_status.flags.mag_3D = false;
			}

		} else if (_params.mag_fusion_type == MAG_FUSE_TYPE_HEADING) {
			// always use heading fusion
			_control_status.flags.mag_hdg = true;
			_control_status.flags.mag_3D = false;

		} else if (_params.mag_fusion_type == MAG_FUSE_TYPE_3D) {
			// if transitioning into 3-axis fusion mode, we need to initialise the yaw angle and field states
			if (!_control_status.flags.mag_3D) {
				_control_status.flags.yaw_align = resetMagHeading(_mag_sample_delayed.mag);
			}

			// always use 3-axis mag fusion
			_control_status.flags.mag_hdg = false;
			_control_status.flags.mag_3D = true;

		} else {
			// do no magnetometer fusion at all
			_control_status.flags.mag_hdg = false;
			_control_status.flags.mag_3D = false;
		}

		// if we are using 3-axis magnetometer fusion, but without external aiding, then the declination must be fused as an observation to prevent long term heading drift
		// fusing declination when gps aiding is available is optional, but recommneded to prevent problem if the vehicle is static for extended periods of time
		if (_control_status.flags.mag_3D && (!_control_status.flags.gps || (_params.mag_declination_source & MASK_FUSE_DECL))) {
			_control_status.flags.mag_dec = true;

		} else {
			_control_status.flags.mag_dec = false;
		}

		// fuse magnetometer data using the selected methods
		if (_control_status.flags.mag_3D && _control_status.flags.yaw_align) {
			fuseMag();

			if (_control_status.flags.mag_dec) {
				fuseDeclination();
			}

		} else if (_control_status.flags.mag_hdg && _control_status.flags.yaw_align) {
			// fusion of an Euler yaw angle from either a 321 or 312 rotation sequence
			fuseHeading();

		} else {
			// do no fusion at all
		}
	}
}
示例#2
0
文件: control.cpp 项目: vvranjek/ecl
void Ekf::controlExternalVisionFusion()
{
	// Check for new exernal vision data
	if (_ev_data_ready) {

		// external vision position aiding selection logic
		if ((_params.fusion_mode & MASK_USE_EVPOS) && !_control_status.flags.ev_pos && _control_status.flags.tilt_align && _control_status.flags.yaw_align) {
			// check for a exernal vision measurement that has fallen behind the fusion time horizon
			if (_time_last_imu - _time_last_ext_vision < 2 * EV_MAX_INTERVAL) {
				// turn on use of external vision measurements for position and height
				_control_status.flags.ev_pos = true;
				ECL_INFO("EKF switching to external vision position fusion");
				// turn off other forms of height aiding
				_control_status.flags.baro_hgt = false;
				_control_status.flags.gps_hgt = false;
				_control_status.flags.rng_hgt = false;
				// reset the position, height and velocity
				resetPosition();
				resetVelocity();
				resetHeight();
			}
		}

		// external vision yaw aiding selection logic
		if ((_params.fusion_mode & MASK_USE_EVYAW) && !_control_status.flags.ev_yaw && _control_status.flags.tilt_align) {
			// check for a exernal vision measurement that has fallen behind the fusion time horizon
			if (_time_last_imu - _time_last_ext_vision < 2 * EV_MAX_INTERVAL) {
				// reset the yaw angle to the value from the observaton quaternion
				// get the roll, pitch, yaw estimates from the quaternion states
				matrix::Quaternion<float> q_init(_state.quat_nominal(0), _state.quat_nominal(1), _state.quat_nominal(2),
							    _state.quat_nominal(3));
				matrix::Euler<float> euler_init(q_init);

				// get initial yaw from the observation quaternion
				extVisionSample ev_newest = _ext_vision_buffer.get_newest();
				matrix::Quaternion<float> q_obs(ev_newest.quat(0), ev_newest.quat(1), ev_newest.quat(2), ev_newest.quat(3));
				matrix::Euler<float> euler_obs(q_obs);
				euler_init(2) = euler_obs(2);

				// save a copy of the quaternion state for later use in calculating the amount of reset change
				Quaternion quat_before_reset = _state.quat_nominal;

				// calculate initial quaternion states for the ekf
				_state.quat_nominal = Quaternion(euler_init);

				// calculate the amount that the quaternion has changed by
				_state_reset_status.quat_change = _state.quat_nominal * quat_before_reset.inversed();

				// add the reset amount to the output observer buffered data
				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 *= _state_reset_status.quat_change;
					_output_buffer.push_to_index(i,output_states);
				}

				// capture the reset event
				_state_reset_status.quat_counter++;

				// flag the yaw as aligned
				_control_status.flags.yaw_align = true;

				// turn on fusion of external vision yaw measurements and disable all magnetoemter fusion
				_control_status.flags.ev_yaw = true;
				_control_status.flags.mag_hdg = false;
				_control_status.flags.mag_3D = false;
				_control_status.flags.mag_dec = false;

				ECL_INFO("EKF switching to external vision yaw fusion");
			}
		}

		// determine if we should use the height observation
		if (_params.vdist_sensor_type == VDIST_SENSOR_EV) {
			_control_status.flags.baro_hgt = false;
			_control_status.flags.gps_hgt = false;
			_control_status.flags.rng_hgt = false;
			_control_status.flags.ev_hgt = true;
			_fuse_height = true;

		}

		// determine if we should use the horizontal position observations
		if (_control_status.flags.ev_pos) {
			_fuse_pos = true;

			// correct position and height for offset relative to IMU
			Vector3f pos_offset_body = _params.ev_pos_body - _params.imu_pos_body;
			Vector3f pos_offset_earth = _R_to_earth * pos_offset_body;
			_ev_sample_delayed.posNED(0) -= pos_offset_earth(0);
			_ev_sample_delayed.posNED(1) -= pos_offset_earth(1);
			_ev_sample_delayed.posNED(2) -= pos_offset_earth(2);
		}

		// determine if we should use the yaw observation
		if (_control_status.flags.ev_yaw) {
			fuseHeading();
		}
	}
}
示例#3
0
文件: ekf.cpp 项目: 9DSmart/ecl
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();

		// perform state and variance prediction for the terrain estimator
		if (!_terrain_initialised) {
			_terrain_initialised = initHagl();

		} else {
			predictHagl();
		}

		// control logic
		controlFusionModes();

		// measurement updates

		// Fuse magnetometer data using the selected fuson method and only if angular alignment is complete
		if (_mag_buffer.pop_first_older_than(_imu_sample_delayed.time_us, &_mag_sample_delayed)) {
			if (_control_status.flags.mag_3D && _control_status.flags.yaw_align) {
				fuseMag();

				if (_control_status.flags.mag_dec) {
					fuseDeclination();
				}

			} else if (_control_status.flags.mag_2D && _control_status.flags.yaw_align) {
				fuseMag2D();

			} else if (_control_status.flags.mag_hdg && _control_status.flags.yaw_align) {
				// fusion of a Euler yaw angle from either a 321 or 312 rotation sequence
				fuseHeading();

			} else {
				// do no fusion at all
			}
		}

		// determine if range finder data has fallen behind the fusin time horizon fuse it if we are
		// not tilted too much to use it
		if (_range_buffer.pop_first_older_than(_imu_sample_delayed.time_us, &_range_sample_delayed)
		    && (_R_prev(2, 2) > 0.7071f)) {
			// if we have range data we always try to estimate terrain height
			_fuse_hagl_data = true;

			// only use range finder as a height observation in the main filter if specifically enabled
			if (_control_status.flags.rng_hgt) {
				_fuse_height = true;
			}

		} else if ((_time_last_imu - _time_last_hgt_fuse) > 2 * RNG_MAX_INTERVAL && _control_status.flags.rng_hgt) {
			// If we are supposed to be using range finder data as the primary height sensor, have missed or rejected measurements
			// and are on the ground, then synthesise a measurement at the expected on ground value
			if (!_in_air) {
				_range_sample_delayed.rng = _params.rng_gnd_clearance;
				_range_sample_delayed.time_us = _imu_sample_delayed.time_us;

			}

			_fuse_height = true;
		}

		// determine if baro data has fallen behind the fuson time horizon and fuse it in the main filter if enabled
		if (_baro_buffer.pop_first_older_than(_imu_sample_delayed.time_us, &_baro_sample_delayed)) {
			if (_control_status.flags.baro_hgt) {
				_fuse_height = true;

			} else {
				// calculate a filtered offset between the baro origin and local NED origin if we are not using the baro  as a height reference
				float offset_error =  _state.pos(2) + _baro_sample_delayed.hgt - _hgt_sensor_offset - _baro_hgt_offset;
				_baro_hgt_offset += 0.02f * math::constrain(offset_error, -5.0f, 5.0f);
			}
		}

		// If we are using GPS aiding and data has fallen behind the fusion time horizon then fuse it
		if (_gps_buffer.pop_first_older_than(_imu_sample_delayed.time_us, &_gps_sample_delayed)) {
			// Only use GPS data for position and velocity aiding if enabled
			if (_control_status.flags.gps) {
				_fuse_pos = true;
				_fuse_vert_vel = true;
				_fuse_hor_vel = true;
			}

			// only use gps height observation in the main filter if specifically enabled
			if (_control_status.flags.gps_hgt) {
				_fuse_height = true;
			}

		}

		// If we are using optical flow aiding and data has fallen behind the fusion time horizon, then fuse it
		if (_flow_buffer.pop_first_older_than(_imu_sample_delayed.time_us, &_flow_sample_delayed)
		    && _control_status.flags.opt_flow && (_time_last_imu - _time_last_optflow) < 2e5
		    && (_R_prev(2, 2) > 0.7071f)) {
			_fuse_flow = true;
		}

		// if we aren't doing any aiding, fake GPS measurements at the last known position to constrain drift
		// Coincide fake measurements with baro data for efficiency with a minimum fusion rate of 5Hz
		if (!_control_status.flags.gps && !_control_status.flags.opt_flow
		    && ((_time_last_imu - _time_last_fake_gps > 2e5) || _fuse_height)) {
			_fuse_pos = true;
			_gps_sample_delayed.pos(0) = _last_known_posNE(0);
			_gps_sample_delayed.pos(1) = _last_known_posNE(1);
			_time_last_fake_gps = _time_last_imu;
		}

		// fuse available range finder data into a terrain height estimator if it has been initialised
		if (_fuse_hagl_data && _terrain_initialised) {
			fuseHagl();
			_fuse_hagl_data = false;
		}

		// Fuse available NED velocity and position data into the main filter
		if (_fuse_height || _fuse_pos || _fuse_hor_vel || _fuse_vert_vel) {
			fuseVelPosHeight();
			_fuse_hor_vel = _fuse_vert_vel = _fuse_pos = _fuse_height = false;
		}

		// Update optical flow bias estimates
		calcOptFlowBias();

		// Fuse optical flow LOS rate observations into the main filter
		if (_fuse_flow) {
			fuseOptFlow();
			_last_known_posNE(0) = _state.pos(0);
			_last_known_posNE(1) = _state.pos(1);
			_fuse_flow = false;
		}

		// TODO This is just to get the logic inside but we will only start fusion once we tested this again
		if (_airspeed_buffer.pop_first_older_than(_imu_sample_delayed.time_us, &_airspeed_sample_delayed) && false) {
			fuseAirspeed();
		}
	}

	// the output observer always runs
	calculateOutputStates();

	// check for NaN on attitude states
	if (isnan(_state.quat_nominal(0)) || isnan(_output_new.quat_nominal(0))) {
		return false;
	}

	// We don't have valid data to output until tilt and yaw alignment is complete
	if (_control_status.flags.tilt_align && _control_status.flags.yaw_align) {
		return true;

	} else {
		return false;
	}
}