PX4 Firmware
PX4 Autopilot Software http://px4.io
terrain_estimator.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2015 Estimation and Control Library (ECL). All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name ECL nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /**
35  * @file terrain_estimator.cpp
36  * Function for fusing rangefinder measurements to estimate terrain vertical position/
37  *
38  * @author Paul Riseborough <p_riseborough@live.com.au>
39  *
40  */
41 
42 #include "ekf.h"
43 #include <ecl.h>
44 #include <mathlib/mathlib.h>
45 
47 {
48  bool initialized = false;
49  // get most recent range measurement from buffer
50  const rangeSample &latest_measurement = _range_buffer.get_newest();
51 
53  // if on ground, do not trust the range sensor, but assume a ground clearance
55  // use the ground clearance value as our uncertainty
57  initialized = true;
58 
59  } else if (_rng_hgt_valid
60  && (_time_last_imu - latest_measurement.time_us) < (uint64_t)2e5
62  // if we have a fresh measurement, use it to initialise the terrain estimator
63  _terrain_vpos = _state.pos(2) + latest_measurement.rng * _R_rng_to_earth_2_2;
64  // initialise state variance to variance of measurement
66  // success
67  initialized = true;
68 
69  } else if (_flow_for_terrain_data_ready) {
70  // initialise terrain vertical position to origin as this is the best guess we have
71  _terrain_vpos = fmaxf(0.0f, _state.pos(2));
72  _terrain_var = 100.0f;
73  initialized = true;
74 
75  } else {
76  // no information - cannot initialise
77  initialized = false;
78  }
79 
80  if (initialized) {
81  // has initialized with valid data
83  }
84 
85  return initialized;
86 }
87 
89 {
90  // Perform initialisation check and
91  // on ground, continuously reset the terrain estimator
94 
95  } else {
96 
97  // predict the state variance growth where the state is the vertical position of the terrain underneath the vehicle
98 
99  // process noise due to errors in vehicle height estimate
101 
102  // process noise due to terrain gradient
104  * (sq(_state.vel(0)) + sq(_state.vel(1)));
105 
106  // limit the variance to prevent it becoming badly conditioned
108 
109  // Fuse range finder data if available
111  fuseHagl();
112 
113  // update range sensor angle parameters in case they have changed
114  // we do this here to avoid doing those calculations at a high rate
117  }
118 
122  }
123 
124  // constrain _terrain_vpos to be a minimum of _params.rng_gnd_clearance larger than _state.pos(2)
127  }
128  }
129 
131 }
132 
134 {
135  // If the vehicle is excessively tilted, do not try to fuse range finder observations
137  // get a height above ground measurement from the range finder assuming a flat earth
138  float meas_hagl = _range_sample_delayed.rng * _R_rng_to_earth_2_2;
139 
140  // predict the hagl from the vehicle position and terrain height
141  float pred_hagl = _terrain_vpos - _state.pos(2);
142 
143  // calculate the innovation
144  _hagl_innov = pred_hagl - meas_hagl;
145 
146  // calculate the observation variance adding the variance of the vehicles own height uncertainty
147  float obs_variance = fmaxf(P[9][9] * _params.vehicle_variance_scaler, 0.0f)
150 
151  // calculate the innovation variance - limiting it to prevent a badly conditioned fusion
152  _hagl_innov_var = fmaxf(_terrain_var + obs_variance, obs_variance);
153 
154  // perform an innovation consistency check and only fuse data if it passes
155  float gate_size = fmaxf(_params.range_innov_gate, 1.0f);
156  _terr_test_ratio = sq(_hagl_innov) / (sq(gate_size) * _hagl_innov_var);
157 
158  if (_terr_test_ratio <= 1.0f) {
159  // calculate the Kalman gain
160  float gain = _terrain_var / _hagl_innov_var;
161  // correct the state
162  _terrain_vpos -= gain * _hagl_innov;
163  // correct the variance
164  _terrain_var = fmaxf(_terrain_var * (1.0f - gain), 0.0f);
165  // record last successful fusion event
168 
169  } else {
170  // If we have been rejecting range data for too long, reset to measurement
171  if ((_time_last_imu - _time_last_hagl_fuse) > (uint64_t)10E6) {
172  _terrain_vpos = _state.pos(2) + meas_hagl;
173  _terrain_var = obs_variance;
174 
175  } else {
177  }
178  }
179 
180  } else {
182  }
183 }
184 
186 {
187  // calculate optical LOS rates using optical flow rates that have had the body angular rate contribution removed
188  // correct for gyro bias errors in the data used to do the motion compensation
189  // Note the sign convention used: A positive LOS rate is a RH rotation of the scene about that axis.
190  Vector2f opt_flow_rate;
191  opt_flow_rate(0) = _flowRadXYcomp(0) / _flow_sample_delayed.dt + _flow_gyro_bias(0);
192  opt_flow_rate(1) = _flowRadXYcomp(1) / _flow_sample_delayed.dt + _flow_gyro_bias(1);
193 
194  // get latest estimated orientation
195  float q0 = _state.quat_nominal(0);
196  float q1 = _state.quat_nominal(1);
197  float q2 = _state.quat_nominal(2);
198  float q3 = _state.quat_nominal(3);
199 
200  // calculate the optical flow observation variance
201  float R_LOS = calcOptFlowMeasVar();
202 
203  // get rotation matrix from earth to body
204  Dcmf earth_to_body = quat_to_invrotmat(_state.quat_nominal);
205 
206  // calculate the sensor position relative to the IMU
207  Vector3f pos_offset_body = _params.flow_pos_body - _params.imu_pos_body;
208 
209  // calculate the velocity of the sensor relative to the imu in body frame
210  // Note: _flow_sample_delayed.gyroXYZ is the negative of the body angular velocity, thus use minus sign
211  Vector3f vel_rel_imu_body = cross_product(-_flow_sample_delayed.gyroXYZ / _flow_sample_delayed.dt, pos_offset_body);
212 
213  // calculate the velocity of the sensor in the earth frame
214  Vector3f vel_rel_earth = _state.vel + _R_to_earth * vel_rel_imu_body;
215 
216  // rotate into body frame
217  Vector3f vel_body = earth_to_body * vel_rel_earth;
218 
219  float t0 = q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3;
220 
221  // constrain terrain to minimum allowed value and predict height above ground
223  float pred_hagl = _terrain_vpos - _state.pos(2);
224 
225  // Calculate observation matrix for flow around the vehicle x axis
226  float Hx = vel_body(1) * t0 / (pred_hagl * pred_hagl);
227 
228  // Constrain terrain variance to be non-negative
229  _terrain_var = fmaxf(_terrain_var, 0.0f);
230 
231  // Cacluate innovation variance
232  _flow_innov_var[0] = Hx * Hx * _terrain_var + R_LOS;
233 
234  // calculate the kalman gain for the flow x measurement
235  float Kx = _terrain_var * Hx / _flow_innov_var[0];
236 
237  // calculate prediced optical flow about x axis
238  float pred_flow_x = vel_body(1) * earth_to_body(2, 2) / pred_hagl;
239 
240  // calculate flow innovation (x axis)
241  _flow_innov[0] = pred_flow_x - opt_flow_rate(0);
242 
243  // calculate correction term for terrain variance
244  float KxHxP = Kx * Hx * _terrain_var;
245 
246  // innovation consistency check
247  float gate_size = fmaxf(_params.flow_innov_gate, 1.0f);
248  float flow_test_ratio = sq(_flow_innov[0]) / (sq(gate_size) * _flow_innov_var[0]);
249 
250  // do not perform measurement update if badly conditioned
251  if (flow_test_ratio <= 1.0f) {
252  _terrain_vpos += Kx * _flow_innov[0];
253  // guard against negative variance
254  _terrain_var = fmaxf(_terrain_var - KxHxP, 0.0f);
256  }
257 
258  // Calculate observation matrix for flow around the vehicle y axis
259  float Hy = -vel_body(0) * t0 / (pred_hagl * pred_hagl);
260 
261  // Calculuate innovation variance
262  _flow_innov_var[1] = Hy * Hy * _terrain_var + R_LOS;
263 
264  // calculate the kalman gain for the flow y measurement
265  float Ky = _terrain_var * Hy / _flow_innov_var[1];
266 
267  // calculate prediced optical flow about y axis
268  float pred_flow_y = -vel_body(0) * earth_to_body(2, 2) / pred_hagl;
269 
270  // calculate flow innovation (y axis)
271  _flow_innov[1] = pred_flow_y - opt_flow_rate(1);
272 
273  // calculate correction term for terrain variance
274  float KyHyP = Ky * Hy * _terrain_var;
275 
276  // innovation consistency check
277  flow_test_ratio = sq(_flow_innov[1]) / (sq(gate_size) * _flow_innov_var[1]);
278 
279  if (flow_test_ratio <= 1.0f) {
280  _terrain_vpos += Ky * _flow_innov[1];
281  // guard against negative variance
282  _terrain_var = fmaxf(_terrain_var - KyHyP, 0.0f);
284  }
285 }
286 
288 {
289  return _hagl_valid;
290 }
291 
293 {
294  // we have been fusing range finder measurements in the last 5 seconds
295  bool recent_range_fusion = (_time_last_imu - _time_last_hagl_fuse) < (uint64_t)5e6;
296 
297  // we have been fusing optical flow measurements for terrain estimation within the last 5 seconds
298  // this can only be the case if the main filter does not fuse optical flow
299  bool recent_flow_for_terrain_fusion = ((_time_last_imu - _time_last_of_fuse) < (uint64_t)5e6)
301 
302  _hagl_valid = (_terrain_initialised && (recent_range_fusion || recent_flow_for_terrain_fusion));
303 }
304 
305 // get the estimated vertical position of the terrain relative to the NED origin
306 void Ekf::getTerrainVertPos(float *ret)
307 {
308  memcpy(ret, &_terrain_vpos, sizeof(float));
309 }
310 
311 void Ekf::getHaglInnov(float *hagl_innov)
312 {
313  memcpy(hagl_innov, &_hagl_innov, sizeof(_hagl_innov));
314 }
315 
316 
317 void Ekf::getHaglInnovVar(float *hagl_innov_var)
318 {
319  memcpy(hagl_innov_var, &_hagl_innov_var, sizeof(_hagl_innov_var));
320 }
Matrix3f quat_to_invrotmat(const Quatf &quat)
constexpr _Tp constrain(_Tp val, _Tp min_val, _Tp max_val)
Definition: Limits.hpp:66
float _cos_tilt_rng
cosine of the range finder tilt rotation about the Y body axis
Definition: ekf.h:464
struct estimator::filter_control_status_u::@60 flags
Adapter / shim layer for system calls needed by ECL.
stateSample _state
state struct of the ekf running at the delayed time horizon
Definition: ekf.h:288
float _flow_innov[2]
flow measurement innovation (rad/sec)
Definition: ekf.h:392
float range_cos_max_tilt
cosine of the maximum tilt angle from the vertical that permits use of range finder and flow data ...
Definition: common.h:299
Dcm< float > Dcmf
Definition: Dcm.hpp:185
const data_type & get_newest()
Definition: RingBuffer.h:121
float terrain_p_noise
process noise for terrain offset (m/sec)
Definition: common.h:248
void updateTerrainValidity()
bool _flow_for_terrain_data_ready
Definition: ekf.h:324
bool reject_hagl
9 - true if the height above ground observation has been rejected
Definition: common.h:413
Vector3f _flow_gyro_bias
bias errors in optical flow sensor rate gyro outputs (rad/sec)
Definition: ekf.h:394
float terrain_gradient
gradient of terrain used to estimate process noise due to changing position (m/m) ...
Definition: common.h:249
Vector3f cross_product(const Vector3f &vecIn1, const Vector3f &vecIn2)
float _terrain_var
variance of terrain position estimate (m**2)
Definition: ekf.h:458
float vehicle_variance_scaler
gain applied to vehicle height variance used in calculation of height above ground observation varian...
Definition: common.h:294
rangeSample _range_sample_delayed
Vector3f gyroXYZ
measured delta angle of the inertial frame about the body axes obtained from rate gyro measurements (...
Definition: common.h:150
float dt
amount of integration time (sec)
Definition: common.h:151
float P[_k_num_states][_k_num_states]
state covariance matrix
Definition: ekf.h:367
float rng
range (distance to ground) measurement (m)
Definition: common.h:136
float _terrain_vpos
estimated vertical position of the terrain underneath the vehicle in local NED frame (m) ...
Definition: ekf.h:457
uint64_t _time_last_of_fuse
time the last fusion of optical flow measurements were performed (uSec)
Definition: ekf.h:334
float _hagl_innov_var
innovation variance for the last height above terrain measurement (m**2)
Definition: ekf.h:460
void getHaglInnovVar(float *hagl_innov_var) override
Dcmf _R_to_earth
transformation matrix from body frame to earth frame from last EKF prediction
Definition: ekf.h:348
bool _terrain_initialised
true when the terrain estimator has been initialized
Definition: ekf.h:462
float flow_innov_gate
optical flow fusion innovation consistency gate size (STD)
Definition: common.h:311
Vector2f _flowRadXYcomp
measured delta angle of the image about the X and Y body axes after removal of body rotation (rad)...
Definition: ekf.h:400
Vector3f flow_pos_body
xyz position of range sensor focal point in body frame (m)
Definition: common.h:328
Vector< float, 6 > f(float t, const Matrix< float, 6, 1 > &, const Matrix< float, 3, 1 > &)
Definition: integration.cpp:8
Vector2< float > Vector2f
Definition: Vector2.hpp:69
void fuseFlowForTerrain()
float rng_gnd_clearance
minimum valid value for range when on ground (m)
Definition: common.h:291
filter_control_status_u _control_status
Vector3f vel
NED velocity in earth frame in m/s.
Definition: common.h:369
void fuseHagl()
float range_noise
observation noise for range finder measurements (m)
Definition: common.h:289
static constexpr float sq(float var)
Definition: ekf.h:695
bool _rng_hgt_valid
true if range finder sample retrieved from buffer is valid
Definition: ekf.h:472
Quatf quat_nominal
quaternion defining the rotation from body to earth frame
Definition: common.h:368
Vector3f pos
NED position in earth frame in m.
Definition: common.h:370
void runTerrainEstimator()
innovation_fault_status_u _innov_check_fail_status
void getTerrainVertPos(float *ret) override
float rng_sens_pitch
Pitch offset of the range sensor (rad). Sensor points out along Z axis when offset is zero...
Definition: common.h:292
Vector3< float > Vector3f
Definition: Vector3.hpp:136
bool _hagl_valid
true when the height above ground estimate is valid
Definition: ekf.h:467
uint32_t opt_flow
3 - true if optical flow measurements are being fused
Definition: common.h:444
float _R_rng_to_earth_2_2
2,2 element of the rotation matrix from sensor frame to earth frame
Definition: ekf.h:465
float _hagl_innov
innovation of the last height above terrain measurement (m)
Definition: ekf.h:459
float _sin_tilt_rng
sine of the range finder tilt rotation about the Y body axis
Definition: ekf.h:463
float _flow_innov_var[2]
flow innovation variance ((rad/sec)**2)
Definition: ekf.h:393
uint32_t in_air
7 - true when the vehicle is airborne
Definition: common.h:448
RingBuffer< rangeSample > _range_buffer
float range_noise_scaler
scaling from range measurement to noise (m/m)
Definition: common.h:293
bool initHagl()
float range_innov_gate
range finder fusion innovation consistency gate size (STD)
Definition: common.h:290
Vector3f imu_pos_body
xyz position of IMU in body frame (m)
Definition: common.h:325
void getHaglInnov(float *hagl_innov) override
struct estimator::innovation_fault_status_u::@58 flags
uint64_t _time_last_hagl_fuse
last system time that the hagl measurement failed it&#39;s checks (uSec)
Definition: ekf.h:461
Class for core functions for ekf attitude and position estimator.
bool isTerrainEstimateValid() const override
uint64_t time_us
timestamp of the measurement (uSec)
Definition: common.h:137
float delta_vel_dt
delta velocity integration period (sec)
Definition: common.h:110
float calcOptFlowMeasVar()
bool _range_data_ready
true when new range finder data has fallen behind the fusion time horizon and is available to be fuse...
Definition: ekf.h:320