Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EKF: support passing in an external position estimate #23903

Merged
merged 8 commits into from
Jun 6, 2023
6 changes: 6 additions & 0 deletions Tools/Replay/LR_MsgHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,12 @@ void LR_MsgHandler_REPH::process_message(uint8_t *msgbytes)
AP::dal().handle_message(msg, ekf2, ekf3);
}

void LR_MsgHandler_RSLL::process_message(uint8_t *msgbytes)
{
MSG_CREATE(RSLL, msgbytes);
AP::dal().handle_message(msg, ekf2, ekf3);
}

void LR_MsgHandler_REVH::process_message(uint8_t *msgbytes)
{
MSG_CREATE(REVH, msgbytes);
Expand Down
6 changes: 6 additions & 0 deletions Tools/Replay/LR_MsgHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ class LR_MsgHandler_REPH : public LR_MsgHandler_EKF
void process_message(uint8_t *msg) override;
};

class LR_MsgHandler_RSLL : public LR_MsgHandler_EKF
{
using LR_MsgHandler_EKF::LR_MsgHandler_EKF;
void process_message(uint8_t *msg) override;
};

class LR_MsgHandler_REVH : public LR_MsgHandler_EKF
{
using LR_MsgHandler_EKF::LR_MsgHandler_EKF;
Expand Down
2 changes: 2 additions & 0 deletions Tools/Replay/LogReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ bool LogReader::handle_log_format_msg(const struct log_Format &f)
msgparser[f.type] = new LR_MsgHandler_ROFH(formats[f.type], ekf2, ekf3);
} else if (streq(name, "REPH")) {
msgparser[f.type] = new LR_MsgHandler_REPH(formats[f.type], ekf2, ekf3);
} else if (streq(name, "RSLL")) {
msgparser[f.type] = new LR_MsgHandler_RSLL(formats[f.type], ekf2, ekf3);
} else if (streq(name, "REVH")) {
msgparser[f.type] = new LR_MsgHandler_REVH(formats[f.type], ekf2, ekf3);
} else if (streq(name, "RWOH")) {
Expand Down
90 changes: 90 additions & 0 deletions Tools/autotest/arduplane.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,95 @@ def DO_REPOSITION(self):

self.fly_home_land_and_disarm()

def ExternalPositionEstimate(self):
'''Test mavlink EXTERNAL_POSITION_ESTIMATE command'''
if not hasattr(mavutil.mavlink, 'MAV_CMD_EXTERNAL_POSITION_ESTIMATE'):
return
self.change_mode("TAKEOFF")
self.wait_ready_to_arm()
self.arm_vehicle()
self.wait_altitude(48, 52, relative=True)

loc = self.mav.location()
self.location_offset_ne(loc, 2000, 2000)

# setting external position fail while we have GPS lock
self.progress("set new position with GPS")
self.run_cmd_int(
mavutil.mavlink.MAV_CMD_EXTERNAL_POSITION_ESTIMATE,
self.get_sim_time()-1, # transmit time
0.5, # processing delay
50, # accuracy
0,
int(loc.lat * 1e7),
int(loc.lng * 1e7),
float("NaN"), # alt
frame=mavutil.mavlink.MAV_FRAME_GLOBAL,
want_result=mavutil.mavlink.MAV_RESULT_FAILED,
)

self.progress("disable the GPS")
self.run_auxfunc(
65,
2,
want_result=mavutil.mavlink.MAV_RESULT_ACCEPTED
)

# fly for a bit to get into non-aiding state
self.progress("waiting 20 seconds")
tstart = self.get_sim_time()
while self.get_sim_time() < tstart + 20:
self.wait_heartbeat()

self.progress("getting base position")
gpi = self.mav.recv_match(
type='GLOBAL_POSITION_INT',
blocking=True,
timeout=5
)
loc = mavutil.location(gpi.lat*1e-7, gpi.lon*1e-7, 0, 0)

self.progress("set new position with no GPS")
self.run_cmd_int(
mavutil.mavlink.MAV_CMD_EXTERNAL_POSITION_ESTIMATE,
self.get_sim_time()-1, # transmit time
0.5, # processing delay
50, # accuracy
0,
gpi.lat+1,
gpi.lon+1,
float("NaN"), # alt
frame=mavutil.mavlink.MAV_FRAME_GLOBAL,
want_result=mavutil.mavlink.MAV_RESULT_ACCEPTED
)

self.progress("waiting 3 seconds")
tstart = self.get_sim_time()
while self.get_sim_time() < tstart + 3:
self.wait_heartbeat()

gpi2 = self.mav.recv_match(
type='GLOBAL_POSITION_INT',
blocking=True,
timeout=5
)
loc2 = mavutil.location(gpi2.lat*1e-7, gpi2.lon*1e-7, 0, 0)
dist = self.get_distance(loc, loc2)

self.progress("dist is %.1f" % dist)
if dist > 200:
raise NotAchievedException("Position error dist=%.1f" % dist)

self.progress("re-enable the GPS")
self.run_auxfunc(
65,
0,
want_result=mavutil.mavlink.MAV_RESULT_ACCEPTED
)

self.progress("flying home")
self.fly_home_land_and_disarm()

def DeepStall(self):
'''Test DeepStall Landing'''
# self.fly_deepstall_absolute()
Expand Down Expand Up @@ -4603,6 +4692,7 @@ def tests(self):
self.SDCardWPTest,
self.NoArmWithoutMissionItems,
self.MODE_SWITCH_RESET,
self.ExternalPositionEstimate,
])
return ret

Expand Down
2 changes: 1 addition & 1 deletion Tools/autotest/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -2977,7 +2977,7 @@ def LoggerDocumentation(self):
REPLAY_MSGS = ['RFRH', 'RFRF', 'REV2', 'RSO2', 'RWA2', 'REV3', 'RSO3', 'RWA3', 'RMGI',
'REY3', 'RFRN', 'RISH', 'RISI', 'RISJ', 'RBRH', 'RBRI', 'RRNH', 'RRNI',
'RGPH', 'RGPI', 'RGPJ', 'RASH', 'RASI', 'RBCH', 'RBCI', 'RVOH', 'RMGH',
'ROFH', 'REPH', 'REVH', 'RWOH', 'RBOH']
'ROFH', 'REPH', 'REVH', 'RWOH', 'RBOH', 'RSLL']

docco_ids = {}
for thing in tree.logformat:
Expand Down
10 changes: 10 additions & 0 deletions libraries/AP_AHRS/AP_AHRS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,16 @@ bool AP_AHRS::set_origin(const Location &loc)
return false;
}

#if AP_AHRS_POSITION_RESET_ENABLED
bool AP_AHRS::handle_external_position_estimate(const Location &loc, float pos_accuracy, uint32_t timestamp_ms)
{
#if HAL_NAVEKF3_AVAILABLE
return EKF3.setLatLng(loc, pos_accuracy, timestamp_ms);
#endif
return false;
}
#endif

// return true if inertial navigation is active
bool AP_AHRS::have_inertial_nav(void) const
{
Expand Down
11 changes: 11 additions & 0 deletions libraries/AP_AHRS/AP_AHRS.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,17 @@ class AP_AHRS {
// from which to decide the origin on its own
bool set_origin(const Location &loc) WARN_IF_UNUSED;

#if AP_AHRS_POSITION_RESET_ENABLED
// Set the EKF's NE horizontal position states and their corresponding variances from the supplied WGS-84 location
// and 1-sigma horizontal position uncertainty. This can be used when the EKF is dead reckoning to periodically
// correct the position. If the EKF is is still using data from a postion sensor such as GPS, the position set
// will not be performed.
// pos_accuracy is the standard deviation of the horizontal position uncertainty in metres.
// The altitude element of the location is not used.
// Returns true if the set was successful.
bool handle_external_position_estimate(const Location &loc, float pos_accuracy, uint32_t timestamp_);
#endif

// returns the inertial navigation origin in lat/lon/alt
bool get_origin(Location &ret) const WARN_IF_UNUSED;

Expand Down
5 changes: 5 additions & 0 deletions libraries/AP_AHRS/AP_AHRS_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@
#ifndef AP_AHRS_SIM_ENABLED
#define AP_AHRS_SIM_ENABLED AP_SIM_ENABLED
#endif

#ifndef AP_AHRS_POSITION_RESET_ENABLED
#define AP_AHRS_POSITION_RESET_ENABLED (BOARD_FLASH_SIZE>1024)
#endif

22 changes: 22 additions & 0 deletions libraries/AP_DAL/AP_DAL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,17 @@ void AP_DAL::writeExtNavData(const Vector3f &pos, const Quaternion &quat, float
WRITE_REPLAY_BLOCK_IFCHANGED(REPH, _REPH, old);
}

void AP_DAL::log_SetLatLng(const Location &loc, float posAccuracy, uint32_t timestamp_ms)
{
end_frame();
const log_RSLL old = _RSLL;
_RSLL.lat = loc.lat;
_RSLL.lng = loc.lng;
_RSLL.posAccSD = posAccuracy;
_RSLL.timestamp_ms = timestamp_ms;
WRITE_REPLAY_BLOCK_IFCHANGED(RSLL, _RSLL, old);
}

// log external velocity data
void AP_DAL::writeExtNavVelData(const Vector3f &vel, float err, uint32_t timeStamp_ms, uint16_t delay_ms)
{
Expand Down Expand Up @@ -485,6 +496,17 @@ void AP_DAL::handle_message(const log_RBOH &msg, NavEKF2 &ekf2, NavEKF3 &ekf3)
// note that EKF2 does not support body frame odomotry
ekf3.writeBodyFrameOdom(msg.quality, msg.delPos, msg.delAng, msg.delTime, msg.timeStamp_ms, msg.delay_ms, msg.posOffset);
}

/*
handle position reset
*/
void AP_DAL::handle_message(const log_RSLL &msg, NavEKF2 &ekf2, NavEKF3 &ekf3)
{
_RSLL = msg;
// note that EKF2 does not support body frame odomotry
const Location loc {msg.lat, msg.lng, 0, Location::AltFrame::ABSOLUTE };
ekf3.setLatLng(loc, msg.posAccSD, msg.timestamp_ms);
}
#endif // APM_BUILD_Replay

namespace AP {
Expand Down
4 changes: 4 additions & 0 deletions libraries/AP_DAL/AP_DAL.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ class AP_DAL {

void log_event3(Event event);
void log_SetOriginLLH3(const Location &loc);
void log_SetLatLng(const Location &loc, float posAccuracy, uint32_t timestamp_ms);

void log_writeDefaultAirSpeed3(const float aspeed, const float uncertainty);
void log_writeEulerYawAngle(float yawAngle, float yawAngleErr, uint32_t timeStamp_ms, uint8_t type);

Expand Down Expand Up @@ -317,6 +319,7 @@ class AP_DAL {
void handle_message(const log_REVH &msg, NavEKF2 &ekf2, NavEKF3 &ekf3);
void handle_message(const log_RWOH &msg, NavEKF2 &ekf2, NavEKF3 &ekf3);
void handle_message(const log_RBOH &msg, NavEKF2 &ekf2, NavEKF3 &ekf3);
void handle_message(const log_RSLL &msg, NavEKF2 &ekf2, NavEKF3 &ekf3);

// map core number for replay
uint8_t logging_core(uint8_t c) const;
Expand All @@ -340,6 +343,7 @@ class AP_DAL {
struct log_REVH _REVH;
struct log_RWOH _RWOH;
struct log_RBOH _RBOH;
struct log_RSLL _RSLL;

// cached variables for speed:
uint32_t _micros;
Expand Down
13 changes: 13 additions & 0 deletions libraries/AP_DAL/LogStructure.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
LOG_RMGI_MSG, \
LOG_ROFH_MSG, \
LOG_REPH_MSG, \
LOG_RSLL_MSG, \
LOG_REVH_MSG, \
LOG_RWOH_MSG, \
LOG_RBOH_MSG
Expand Down Expand Up @@ -324,6 +325,16 @@ struct log_REPH {
uint8_t _end;
};

// @LoggerMessage: RSLL
// @Description: Replay Set Lat Lng event
struct log_RSLL {
int32_t lat; // WGS-84 latitude in 1E-7 degrees
int32_t lng; // WGS-84 longitude in 1E7 degrees
float posAccSD; // horizontal position 1 STD uncertainty (m)
uint32_t timestamp_ms;
uint8_t _end;
};

// @LoggerMessage: REVH
// @Description: Replay external position data
struct log_REVH {
Expand Down Expand Up @@ -417,6 +428,8 @@ struct log_RBOH {
"ROFH", "ffffIffffB", "FX,FY,GX,GY,Tms,PX,PY,PZ,HgtOvr,Qual", "----------", "----------" }, \
{ LOG_REPH_MSG, RLOG_SIZE(REPH), \
"REPH", "fffffffffIIH", "PX,PY,PZ,Q1,Q2,Q3,Q4,PEr,AEr,TS,RT,D", "------------", "------------" }, \
{ LOG_RSLL_MSG, RLOG_SIZE(RSLL), \
"RSLL", "IIfI", "Lat,Lng,PosAccSD,TS", "DU--", "GG--" }, \
{ LOG_REVH_MSG, RLOG_SIZE(REVH), \
"REVH", "ffffIH", "VX,VY,VZ,Er,TS,D", "------", "------" }, \
{ LOG_RWOH_MSG, RLOG_SIZE(RWOH), \
Expand Down
20 changes: 20 additions & 0 deletions libraries/AP_NavEKF3/AP_NavEKF3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1408,6 +1408,26 @@ bool NavEKF3::setOriginLLH(const Location &loc)
return ret;
}

bool NavEKF3::setLatLng(const Location &loc, float posAccuracy, uint32_t timestamp_ms)
{
#if EK3_FEATURE_POSITION_RESET
AP::dal().log_SetLatLng(loc, posAccuracy, timestamp_ms);

if (!core) {
return false;
}
bool ret = false;
for (uint8_t i=0; i<num_cores; i++) {
ret |= core[i].setLatLng(loc, posAccuracy, timestamp_ms);
}
// return true if any core accepts the new origin
return ret;
#else
return false;
#endif // EK3_FEATURE_POSITION_RESET
}


// return estimated height above ground level
// return false if ground height is not being estimated.
bool NavEKF3::getHAGL(float &HAGL) const
Expand Down
5 changes: 5 additions & 0 deletions libraries/AP_NavEKF3/AP_NavEKF3.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ class NavEKF3 {
// Returns false if the filter has rejected the attempt to set the origin
bool setOriginLLH(const Location &loc);

// Set the EKF's NE horizontal position states and their corresponding variances from a supplied WGS-84 location and uncertainty
// The altitude element of the location is not used.
// Returns true if the set was successful
bool setLatLng(const Location &loc, float posErr, uint32_t timestamp_ms);

// return estimated height above ground level
// return false if ground height is not being estimated.
bool getHAGL(float &HAGL) const;
Expand Down
36 changes: 36 additions & 0 deletions libraries/AP_NavEKF3/AP_NavEKF3_PosVelFusion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,42 @@ void NavEKF3_core::ResetPosition(resetDataSource posResetSource)
lastPosPassTime_ms = imuSampleTime_ms;
}

#if EK3_FEATURE_POSITION_RESET
// Sets the EKF's NE horizontal position states and their corresponding variances from a supplied WGS-84 location and uncertainty
// The altitude element of the location is not used.
// Returns true if the set was successful
bool NavEKF3_core::setLatLng(const Location &loc, float posAccuracy, uint32_t timestamp_ms)
{
if ((imuSampleTime_ms - lastPosPassTime_ms) < frontend->deadReckonDeclare_ms ||
(PV_AidingMode == AID_NONE)
|| !validOrigin) {
return false;
}

// Store the position before the reset so that we can record the reset delta
posResetNE.x = stateStruct.position.x;
posResetNE.y = stateStruct.position.y;

// reset the corresponding covariances
zeroRows(P,7,8);
zeroCols(P,7,8);

// set the variances using the position measurement noise parameter
P[7][7] = P[8][8] = sq(MAX(posAccuracy,frontend->_gpsHorizPosNoise));

// Correct the position for time delay relative to fusion time horizon assuming a constant velocity
// Limit time stamp to a range between current time and 5 seconds ago
const uint32_t timeStampConstrained_ms = MAX(MIN(timestamp_ms, imuSampleTime_ms), imuSampleTime_ms - 5000);
const int32_t delta_ms = int32_t(imuDataDelayed.time_ms - timeStampConstrained_ms);
const ftype delaySec = 1E-3F * ftype(delta_ms);
const Vector2F newPosNE = EKF_origin.get_distance_NE_ftype(loc) - stateStruct.velocity.xy() * delaySec;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@priseborough @tridge I might look at this the wrong way but shouldn't this be a positive sign here?

ResetPositionNE(newPosNE.x,newPosNE.y);

return true;
}
#endif // EK3_FEATURE_POSITION_RESET


// reset the stateStruct's NE position to the specified position
// posResetNE is updated to hold the change in position
// storedOutput, outputDataNew and outputDataDelayed are updated with the change in position
Expand Down
5 changes: 5 additions & 0 deletions libraries/AP_NavEKF3/AP_NavEKF3_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,11 @@ class NavEKF3_core : public NavEKF_core_common
// returns false if Absolute aiding and GPS is being used or if the origin is already set
bool setOriginLLH(const Location &loc);

// Set the EKF's NE horizontal position states and their corresponding variances from a supplied WGS-84 location and uncertainty
// The altitude element of the location is not used.
// Returns true if the set was successful
bool setLatLng(const Location &loc, float posAccuracy, uint32_t timestamp_ms);

// return estimated height above ground level
// return false if ground height is not being estimated.
bool getHAGL(float &HAGL) const;
Expand Down
5 changes: 5 additions & 0 deletions libraries/AP_NavEKF3/AP_NavEKF3_feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <AP_Vehicle/AP_Vehicle_Type.h>
#include <AP_HAL/AP_HAL_Boards.h>
#include <AP_Beacon/AP_Beacon_config.h>
#include <AP_AHRS/AP_AHRS_config.h>

// define for when to include all features
#define EK3_FEATURE_ALL APM_BUILD_TYPE(APM_BUILD_AP_DAL_Standalone) || APM_BUILD_TYPE(APM_BUILD_Replay)
Expand All @@ -30,3 +31,7 @@
#ifndef EK3_FEATURE_BEACON_FUSION
#define EK3_FEATURE_BEACON_FUSION AP_BEACON_ENABLED
#endif

#ifndef EK3_FEATURE_POSITION_RESET
#define EK3_FEATURE_POSITION_RESET EK3_FEATURE_ALL || AP_AHRS_POSITION_RESET_ENABLED
#endif
Loading