Skip to content

Commit a17390d

Browse files
oseiskarpekkaran
andcommitted
Miscellaneous SLAM tweaks
* Fix pos. uncertainty computation * Improve determinism across machines * Print errors for bad SLAM setup * Simplify odometry uncertainty handling with odometryPriorFixed=false * Add keyframeDecisionAlways option (off by default) * Add options to disable key frame and map point culling * Add command line option for turning off extra ORBs * Minor refactoring and cleanup Co-authored-by: Pekka Rantalankila <pekka.rantalankila@gmail.com>
1 parent 87fbaab commit a17390d

File tree

10 files changed

+104
-155
lines changed

10 files changed

+104
-155
lines changed

bow_index.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ BowVocabulary createBowVocabulary(std::string vocabularyPath) {
1313
BowVocabulary bowVocabulary;
1414
assert(!vocabularyPath.empty());
1515
const std::string inputSuffix = vocabularyPath.substr(util::removeFileSuffix(vocabularyPath).size());
16+
{
17+
std::ifstream file(vocabularyPath);
18+
assert(file.is_open() && "Failed to find BoW vocabulary. Run `scripts/download_orb_vocab.sh`.");
19+
}
1620
if (inputSuffix == ".txt") {
1721
log_debug("Loading BoW vocabulary from TEXT file %s", vocabularyPath.c_str());
1822
bowVocabulary.loadFromTextFile(vocabularyPath);

keyframe.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ Keyframe::Keyframe(const MapperInput &mapperInput) :
8181
id(KfId(mapperInput.poseTrail.at(0).frameNumber)),
8282
previousKfId(KfId(-1)),
8383
nextKfId(KfId(-1)),
84-
poseCW(Eigen::Matrix4d::Identity()),
85-
origPoseCW(mapperInput.poseTrail[0].pose),
86-
uncertainty(mapperInput.poseTrail[0].uncertainty),
84+
poseCW(Eigen::Matrix4d::Zero()), // not valid yet
85+
smoothPoseCW(Eigen::Matrix4d::Zero()), // not valid yet
86+
uncertainty(mapperInput.poseTrail.at(0).uncertainty),
8787
t(mapperInput.poseTrail[0].t),
8888
hasFullFeatures(false)
8989
{
@@ -141,7 +141,7 @@ Keyframe::Keyframe(const Keyframe &kf) :
141141
mapPoints(kf.mapPoints),
142142
keyPointDepth(kf.keyPointDepth),
143143
poseCW(kf.poseCW),
144-
origPoseCW(kf.origPoseCW),
144+
smoothPoseCW(kf.smoothPoseCW),
145145
uncertainty(kf.uncertainty),
146146
t(kf.t)
147147
{}
@@ -234,8 +234,8 @@ Eigen::Vector3d Keyframe::cameraCenter() const {
234234
return worldToCameraMatrixCameraCenter(poseCW);
235235
}
236236

237-
Eigen::Vector3d Keyframe::origPoseCameraCenter() const {
238-
return worldToCameraMatrixCameraCenter(origPoseCW);
237+
Eigen::Vector3d Keyframe::smoothPoseCameraCenter() const {
238+
return worldToCameraMatrixCameraCenter(smoothPoseCW);
239239
}
240240

241241
void Keyframe::getFeaturesAround(const Eigen::Vector2f &point, float r, std::vector<size_t> &indices) {
@@ -312,7 +312,7 @@ std::string Keyframe::toString() {
312312
ss << "nextKfId=" << std::to_string(nextKfId.v);
313313
ss << ", points=" << std::to_string(shared->keyPoints.size());
314314
ss << ", poseCW=" << util::eigenToString(poseCW);
315-
ss << ", origPoseCW=" << util::eigenToString(origPoseCW);
315+
ss << ", smoothPoseCW=" << util::eigenToString(smoothPoseCW);
316316
ss << ", camera=" << shared->camera->serialize();
317317
ss << ", keyPoints=";
318318
keyPointVectorToStringstream(&ss, shared->keyPoints);

keyframe.hpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ class Keyframe {
136136
bool reproject(const Eigen::Vector3d &point, Eigen::Vector2f &reprojected) const;
137137

138138
Eigen::Vector3d cameraCenter() const;
139-
Eigen::Vector3d origPoseCameraCenter() const;
139+
Eigen::Vector3d smoothPoseCameraCenter() const;
140140
inline Eigen::Matrix3d cameraToWorldRotation() const {
141141
return poseCW.topLeftCorner<3, 3>().transpose();
142142
}
@@ -170,11 +170,20 @@ class Keyframe {
170170
// KF positions in non-linear optimization.
171171
Eigen::Matrix4d poseCW;
172172

173-
// Pose computed by odometry with no (or minimal) influence from SLAM. It is used
174-
// mainly to setup priors between subsequent keyframes in non-linear optimization.
175-
Eigen::Matrix4d origPoseCW;
176-
177-
// Uncertainty matrix for position & rotation
173+
// These poses are meant to form a smooth path so that differences of these
174+
// from consecutive keyframes can be used as odometry priors
175+
// Especially if the odometry result is very good, the unfiltered odometry
176+
// output poses can be used for this purpose.
177+
Eigen::Matrix4d smoothPoseCW;
178+
179+
// Accumulated uncertainty (for position and orientation separately).
180+
// These are formed by summing covariance matrices that represent the uncertainty
181+
// between consecutive key frames, which corresponds to the assumption / simplification
182+
// that those noises are independent (which is not very accurate). However, this means
183+
// that these uncertainty matrices form a total order: if t1 >= t2, then
184+
// uncertainty2 - uncertainty1 is positive semidefinite. This simplifies the handling
185+
// of dropped keyframes as you can always just subtract these and still get a valid
186+
// covariance matrix
178187
Eigen::Matrix<double, 3, 6> uncertainty;
179188

180189
double t;
@@ -197,7 +206,7 @@ class Keyframe {
197206
mapPoints,
198207
keyPointDepth,
199208
poseCW,
200-
origPoseCW,
209+
smoothPoseCW,
201210
uncertainty,
202211
t,
203212
hasFullFeatures

mapdb.cpp

Lines changed: 28 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -38,48 +38,55 @@ std::shared_ptr<Keyframe> MapDB::insertNewKeyframeCandidate(
3838
const std::vector<slam::Pose> &poseTrail,
3939
const odometry::ParametersSlam &parameters)
4040
{
41-
Eigen::Matrix4d pose;
41+
Eigen::Matrix4d pose, smoothPose;
4242
std::shared_ptr<Keyframe> keyframe = std::move(keyframeUniq);
4343
Keyframe *previousKf = latestKeyframe();
4444
// debugPrintPoseTrail(poseTrail);
4545

46+
smoothPose = poseTrail[0].pose;
47+
// TODO: to be more accurate, should uncertainty "deltas" over pose trail
48+
Eigen::Matrix<double, 3, 6> curUncertainty = poseTrail[0].uncertainty * parameters.keyframeCandidateInterval;
49+
// assert(curUncertainty.squaredNorm() > 1e-20); // matters only if used
50+
4651
if (prevPoseKfId.v < 0) {
47-
pose = keyframe->origPoseCW;
52+
// First KF (or something bad happened): use odometry pose as-is
53+
pose = poseTrail[0].pose;
4854
// Tip: Use this to change direction of SLAM map and debug transform issues.
4955
// pose.topLeftCorner<3, 3>() = pose.topLeftCorner<3, 3>() * Eigen::AngleAxisd(0.5 * M_PI, Vector3d::UnitZ());
5056
// pose.block<3, 1>(0, 3) << 4, 1, -2;
5157
}
5258
else {
5359
assert(previousKf);
54-
// this "two-step" mechanism may be required to handle loop closures correctly
5560
Eigen::Matrix4d refPose = prevPose;
56-
if (parameters.useVariableLengthDeltas) {
57-
refPose = prevPoseToPrevKeyframeDelta * previousKf->poseCW;
58-
}
5961

60-
Eigen::Matrix4d poseTilted, refPrevPose;
61-
refPrevPose = prevInputPose;
62+
// Could update prevPose to previousKf->poseCW here, but this does not play nice
63+
// with loop closures or non-keyframes and the practical difference is very small
64+
6265
if (parameters.useOdometryPoseTrailDelta) {
6366
// note that this can fail if in certain cases with long
6467
// stationarity and no new keyframes unless special care is taken
6568
const auto *prevPoseInPoseTrail = findInPoseTrail(poseTrail, prevPoseKfId);
6669
if (prevPoseInPoseTrail == nullptr) {
67-
log_debug("keyframe %d not found in pose trail", prevPoseKfId.v);
70+
log_warn("keyframe %d not found in pose trail", prevPoseKfId.v);
6871
} else {
6972
assert(KfId(prevPoseInPoseTrail->frameNumber) != keyframe->id);
70-
refPrevPose = prevPoseInPoseTrail->pose;
73+
smoothPose = poseTrail[0].pose * prevPoseInPoseTrail->pose.inverse() * prevSmoothPose;
74+
// No need to remove Z-axis tilt here. "smoothPose" should only be used for computing
75+
// odometry priors between consecutive keyframes but never as an absolute pose
7176
}
7277
}
73-
poseTilted = keyframe->origPoseCW * refPrevPose.inverse() * refPose;
7478

79+
Eigen::Matrix4d poseTilted = poseTrail[0].pose * prevInputPose.inverse() * refPose;
7580
if (parameters.removeOdometryTransformZAxisTilt) {
76-
pose = removeZAxisTiltComparedToReference(poseTilted, keyframe->origPoseCW);
81+
pose = removeZAxisTiltComparedToReference(poseTilted, smoothPose);
7782
} else {
7883
pose = poseTilted;
7984
}
8085
}
8186

8287
keyframe->poseCW = pose;
88+
keyframe->smoothPoseCW = smoothPose;
89+
keyframe->uncertainty = prevUncertainty + curUncertainty;
8390

8491
if (previousKf) {
8592
keyframe->previousKfId = previousKf->id;
@@ -106,7 +113,8 @@ MapDB::MapDB(const MapDB &mapDB) {
106113

107114
prevPose = mapDB.prevPose;
108115
prevInputPose = mapDB.prevInputPose;
109-
discardedUncertainty = mapDB.discardedUncertainty;
116+
prevSmoothPose = mapDB.prevSmoothPose;
117+
prevUncertainty = mapDB.prevUncertainty;
110118

111119
nextMp = mapDB.nextMp;
112120
lastKfCandidateId = mapDB.lastKfCandidateId;
@@ -149,8 +157,8 @@ MapDB::MapDB(const MapDB &mapDB, const std::set<KfId> &activeKeyframes) {
149157

150158
prevPose = mapDB.prevPose;
151159
prevInputPose = mapDB.prevInputPose;
152-
prevPoseToPrevKeyframeDelta = mapDB.prevPoseToPrevKeyframeDelta;
153-
discardedUncertainty = mapDB.discardedUncertainty;
160+
prevSmoothPose = mapDB.prevSmoothPose;
161+
prevUncertainty = mapDB.prevUncertainty;
154162

155163
nextMp = mapDB.nextMp;
156164
prevPoseKfId = mapDB.prevPoseKfId;
@@ -225,45 +233,20 @@ Eigen::Matrix4d MapDB::poseDifference(KfId kfId1, KfId kfId2) const {
225233
assert(kfId1 <= kfId2);
226234
const Keyframe &kf1 = *keyframes.at(kfId1);
227235
const Keyframe &kf2 = *keyframes.at(kfId2);
228-
return kf1.origPoseCW * kf2.origPoseCW.inverse();
236+
return kf1.smoothPoseCW * kf2.smoothPoseCW.inverse();
229237
}
230238

231-
void MapDB::updatePrevPose(
232-
const Keyframe &currentKeyframe,
233-
bool keyframeDecision,
234-
const std::vector<slam::Pose> &poseTrail,
235-
const odometry::Parameters &parameters)
239+
void MapDB::updatePrevPose(const Keyframe &currentKeyframe, const Eigen::Matrix4d &inputPose)
236240
{
237-
if (!keyframeDecision && parameters.slam.useVariableLengthDeltas && findInPoseTrail(poseTrail, prevPoseKfId) == nullptr) {
238-
log_debug("prevPoseKfId %d lost in pose trail: must update", prevPoseKfId.v);
239-
// TODO: could rather try to update to an older pose isn the trail
240-
// TODO: such odometry non-keyframes should also be removed from the map
241-
keyframeDecision = true;
242-
}
243-
244-
if (!keyframeDecision && parameters.slam.useVariableLengthDeltas) {
245-
// TODO: this is overly complex
246-
const int nextKfCandidateAge = currentKeyframe.id.v - prevPoseKfId.v + parameters.slam.keyframeCandidateInterval;
247-
assert(nextKfCandidateAge > 0);
248-
// TODO: +1? (not so serious here)
249-
const int maxPoseTrailSize = parameters.odometry.cameraTrailLength - std::max(0, parameters.slam.delayIntervalMultiplier) * parameters.slam.keyframeCandidateInterval;
250-
// log_debug("next %d/%d", nextKfCandidateAge, maxPoseTrailSize);
251-
if (parameters.slam.useOdometryPoseTrailDelta && nextKfCandidateAge >= maxPoseTrailSize) {
252-
log_debug("storing prevPose of non-KF %d: max pose trail length will be reached", currentKeyframe.id.v);
253-
} else {
254-
// log_debug(" skipping non-keyframe %d as prev pose", currentKeyframe.id.v);
255-
return;
256-
}
257-
}
258-
259241
// log_debug("storing keyframe %d as prev pose", currentKeyframe.id.v);
260242
prevPoseKfId = currentKeyframe.id;
261-
prevInputPose = currentKeyframe.origPoseCW;
243+
prevInputPose = inputPose;
244+
prevSmoothPose = currentKeyframe.smoothPoseCW;
262245
prevPose = currentKeyframe.poseCW;
246+
prevUncertainty = currentKeyframe.uncertainty;
263247

264248
const auto *prevKf = latestKeyframe();
265249
assert(prevKf);
266-
prevPoseToPrevKeyframeDelta = prevPose * prevKf->poseCW.inverse();
267250
}
268251

269252
const MapDB& getMapWithId(MapId mapId, const MapDB &mapDB, const Atlas &atlas) {

mapdb.hpp

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ class MapDB {
1818
public:
1919

2020
MapDB() :
21-
discardedUncertainty(Eigen::MatrixXd::Zero(3, 6)),
2221
prevPose(Eigen::Matrix4d::Identity()),
23-
prevInputPose(Eigen::Matrix4d::Identity()) {};
22+
prevInputPose(Eigen::Matrix4d::Identity()),
23+
prevUncertainty(Eigen::Matrix<double, 3, 6>::Zero())
24+
{};
2425
MapDB(const MapDB &mapDB); // Copy constructor
2526
MapDB(const MapDB &mapDB, const std::set<KfId> &activeKeyframes); // Copy constructor that only copies given frames
2627

@@ -29,8 +30,6 @@ class MapDB {
2930
std::map<TrackId, MpId> trackIdToMapPoint;
3031
std::vector<LoopClosureEdge> loopClosureEdges;
3132

32-
Eigen::MatrixXd discardedUncertainty;
33-
3433
double firstKfTimestamp = -1.0;
3534

3635
std::shared_ptr<Keyframe> insertNewKeyframeCandidate(
@@ -56,21 +55,16 @@ class MapDB {
5655

5756
Eigen::Matrix4d poseDifference(KfId kfId1, KfId kfId2) const;
5857

59-
void updatePrevPose(
60-
const Keyframe &currentKeyframe,
61-
bool keyframeDecision,
62-
const std::vector<slam::Pose> &poseTrail,
63-
const odometry::Parameters &parameters);
58+
void updatePrevPose(const Keyframe &currentKeyframe, const Eigen::Matrix4d &inputPose);
6459

6560
// Visualization stuff stored here for convenience.
6661
std::map<MapKf, LoopStage> loopStages;
6762
std::vector<KfId> adjacentKfIds;
6863
std::map<MpId, MapPointRecord> mapPointRecords;
6964

7065
private:
71-
Eigen::Matrix4d prevPose;
72-
Eigen::Matrix4d prevInputPose;
73-
Eigen::Matrix4d prevPoseToPrevKeyframeDelta = Eigen::Matrix4d::Identity();
66+
Eigen::Matrix4d prevPose, prevInputPose, prevSmoothPose;
67+
Eigen::Matrix<double, 3, 6> prevUncertainty;
7468
int nextMp = 0;
7569
// id of the frame corresponding to prevPose & prevInput pose. May no longer exist
7670
KfId prevPoseKfId = KfId(-1);
@@ -89,7 +83,8 @@ class MapDB {
8983
loopClosureEdges,
9084
prevPose,
9185
prevInputPose,
92-
discardedUncertainty,
86+
prevSmoothPose,
87+
prevUncertainty,
9388
firstKfTimestamp,
9489
nextMp,
9590
lastKfCandidateId,

0 commit comments

Comments
 (0)