31
31
32
32
#include < boost/algorithm/string/classification.hpp>
33
33
#include < boost/algorithm/string/split.hpp>
34
+ #include < cstdlib>
34
35
#include < fstream>
36
+ #include < iostream>
35
37
#include < string>
36
38
#include < vector>
37
39
38
- using namespace std ;
39
40
using namespace gtsam ;
40
41
using namespace boost ::algorithm;
41
42
42
43
using symbol_shorthand::L;
43
44
using symbol_shorthand::M;
44
45
using symbol_shorthand::X;
45
46
46
- const size_t kMaxLoopCount = 2000 ; // Example default value
47
- const size_t kMaxNrHypotheses = 10 ;
48
-
49
47
auto kOpenLoopModel = noiseModel::Diagonal::Sigmas(Vector3::Ones() * 10 );
48
+ const double kOpenLoopConstant = kOpenLoopModel ->negLogConstant ();
50
49
51
50
auto kPriorNoiseModel = noiseModel::Diagonal::Sigmas(
52
51
(Vector(3 ) << 0.0001 , 0.0001 , 0.0001 ).finished());
53
52
54
53
auto kPoseNoiseModel = noiseModel::Diagonal::Sigmas(
55
54
(Vector(3 ) << 1.0 / 30.0 , 1.0 / 30.0 , 1.0 / 100.0 ).finished());
55
+ const double kPoseNoiseConstant = kPoseNoiseModel ->negLogConstant ();
56
56
57
57
// Experiment Class
58
58
class Experiment {
59
+ public:
60
+ // Parameters with default values
61
+ size_t maxLoopCount = 3000 ;
62
+
63
+ // 3000: {1: 62s, 2: 21s, 3: 20s, 4: 31s, 5: 39s} No DT optimizations
64
+ // 3000: {1: 65s, 2: 20s, 3: 16s, 4: 21s, 5: 28s} With DT optimizations
65
+ // 3000: {1: 59s, 2: 19s, 3: 18s, 4: 26s, 5: 33s} With DT optimizations +
66
+ // merge
67
+ size_t updateFrequency = 3 ;
68
+
69
+ size_t maxNrHypotheses = 10 ;
70
+
59
71
private:
60
72
std::string filename_;
61
73
HybridSmoother smoother_;
@@ -72,7 +84,7 @@ class Experiment {
72
84
*/
73
85
void writeResult (const Values& result, size_t numPoses,
74
86
const std::string& filename = " Hybrid_city10000.txt" ) {
75
- ofstream outfile;
87
+ std:: ofstream outfile;
76
88
outfile.open (filename);
77
89
78
90
for (size_t i = 0 ; i < numPoses; ++i) {
@@ -98,34 +110,33 @@ class Experiment {
98
110
auto f1 = std::make_shared<BetweenFactor<Pose2>>(
99
111
X (keyS), X (keyT), measurement, kPoseNoiseModel );
100
112
101
- std::vector<NonlinearFactorValuePair> factors{
102
- {f0, kOpenLoopModel ->negLogConstant ()},
103
- {f1, kPoseNoiseModel ->negLogConstant ()}};
113
+ std::vector<NonlinearFactorValuePair> factors{{f0, kOpenLoopConstant },
114
+ {f1, kPoseNoiseConstant }};
104
115
HybridNonlinearFactor mixtureFactor (l, factors);
105
116
return mixtureFactor;
106
117
}
107
118
108
119
// / @brief Create hybrid odometry factor with discrete measurement choices.
109
120
HybridNonlinearFactor hybridOdometryFactor (
110
121
size_t numMeasurements, size_t keyS, size_t keyT, const DiscreteKey& m,
111
- const std::vector<Pose2>& poseArray,
112
- const SharedNoiseModel& poseNoiseModel) {
122
+ const std::vector<Pose2>& poseArray) {
113
123
auto f0 = std::make_shared<BetweenFactor<Pose2>>(
114
- X (keyS), X (keyT), poseArray[0 ], poseNoiseModel );
124
+ X (keyS), X (keyT), poseArray[0 ], kPoseNoiseModel );
115
125
auto f1 = std::make_shared<BetweenFactor<Pose2>>(
116
- X (keyS), X (keyT), poseArray[1 ], poseNoiseModel );
126
+ X (keyS), X (keyT), poseArray[1 ], kPoseNoiseModel );
117
127
118
- std::vector<NonlinearFactorValuePair> factors{{f0, 0.0 }, {f1, 0.0 }};
128
+ std::vector<NonlinearFactorValuePair> factors{{f0, kPoseNoiseConstant },
129
+ {f1, kPoseNoiseConstant }};
119
130
HybridNonlinearFactor mixtureFactor (m, factors);
120
131
return mixtureFactor;
121
132
}
122
133
123
134
// / @brief Perform smoother update and optimize the graph.
124
135
void smootherUpdate (HybridSmoother& smoother,
125
136
HybridNonlinearFactorGraph& graph, const Values& initial,
126
- size_t kMaxNrHypotheses , Values* result) {
137
+ size_t maxNrHypotheses , Values* result) {
127
138
HybridGaussianFactorGraph linearized = *graph.linearize (initial);
128
- smoother.update (linearized, kMaxNrHypotheses );
139
+ smoother.update (linearized, maxNrHypotheses );
129
140
// throw if x0 not in hybridBayesNet_:
130
141
const KeySet& keys = smoother.hybridBayesNet ().keys ();
131
142
if (keys.find (X (0 )) == keys.end ()) {
@@ -142,11 +153,11 @@ class Experiment {
142
153
: filename_(filename), smoother_(0.99 ) {}
143
154
144
155
// / @brief Run the main experiment with a given maxLoopCount.
145
- void run (size_t maxLoopCount ) {
156
+ void run () {
146
157
// Prepare reading
147
- ifstream in (filename_);
158
+ std:: ifstream in (filename_);
148
159
if (!in.is_open ()) {
149
- cerr << " Failed to open file: " << filename_ << endl;
160
+ std:: cerr << " Failed to open file: " << filename_ << std:: endl;
150
161
return ;
151
162
}
152
163
@@ -167,11 +178,14 @@ class Experiment {
167
178
168
179
// Initial update
169
180
clock_t beforeUpdate = clock ();
170
- smootherUpdate (smoother_, graph_, initial_, kMaxNrHypotheses , &result_);
181
+ smootherUpdate (smoother_, graph_, initial_, maxNrHypotheses , &result_);
171
182
clock_t afterUpdate = clock ();
172
183
std::vector<std::pair<size_t , double >> smootherUpdateTimes;
173
184
smootherUpdateTimes.push_back ({index, afterUpdate - beforeUpdate});
174
185
186
+ // Flag to decide whether to run smoother update
187
+ size_t numberOfHybridFactors = 0 ;
188
+
175
189
// Start main loop
176
190
size_t keyS = 0 , keyT = 0 ;
177
191
clock_t startTime = clock ();
@@ -192,21 +206,18 @@ class Experiment {
192
206
poseArray[i] = Pose2 (x, y, rad);
193
207
}
194
208
195
- // Flag to decide whether to run smoother update
196
- bool doSmootherUpdate = false ;
197
-
198
209
// Take the first one as the initial estimate
199
210
Pose2 odomPose = poseArray[0 ];
200
211
if (keyS == keyT - 1 ) {
201
212
// Odometry factor
202
213
if (numMeasurements > 1 ) {
203
214
// Add hybrid factor
204
215
DiscreteKey m (M (discreteCount), numMeasurements);
205
- HybridNonlinearFactor mixtureFactor = hybridOdometryFactor (
206
- numMeasurements, keyS, keyT, m, poseArray, kPoseNoiseModel );
216
+ HybridNonlinearFactor mixtureFactor =
217
+ hybridOdometryFactor ( numMeasurements, keyS, keyT, m, poseArray);
207
218
graph_.push_back (mixtureFactor);
208
219
discreteCount++;
209
- doSmootherUpdate = true ;
220
+ numberOfHybridFactors += 1 ;
210
221
std::cout << " mixtureFactor: " << keyS << " " << keyT << std::endl;
211
222
} else {
212
223
graph_.add (BetweenFactor<Pose2>(X (keyS), X (keyT), odomPose,
@@ -221,18 +232,20 @@ class Experiment {
221
232
// print loop closure event keys:
222
233
std::cout << " Loop closure: " << keyS << " " << keyT << std::endl;
223
234
graph_.add (loopFactor);
224
- doSmootherUpdate = true ;
235
+ numberOfHybridFactors += 1 ;
225
236
loopCount++;
226
237
}
227
238
228
- if (doSmootherUpdate) {
239
+ if (numberOfHybridFactors >= updateFrequency) {
240
+ // print the keys involved in the smoother update
241
+ std::cout << " Smoother update: " << graph_.size () << std::endl;
229
242
gttic_ (SmootherUpdate);
230
243
beforeUpdate = clock ();
231
- smootherUpdate (smoother_, graph_, initial_, kMaxNrHypotheses , &result_);
244
+ smootherUpdate (smoother_, graph_, initial_, maxNrHypotheses , &result_);
232
245
afterUpdate = clock ();
233
246
smootherUpdateTimes.push_back ({index, afterUpdate - beforeUpdate});
234
247
gttoc_ (SmootherUpdate);
235
- doSmootherUpdate = false ;
248
+ numberOfHybridFactors = 0 ;
236
249
}
237
250
238
251
// Record timing for odometry edges only
@@ -258,7 +271,7 @@ class Experiment {
258
271
259
272
// Final update
260
273
beforeUpdate = clock ();
261
- smootherUpdate (smoother_, graph_, initial_, kMaxNrHypotheses , &result_);
274
+ smootherUpdate (smoother_, graph_, initial_, maxNrHypotheses , &result_);
262
275
afterUpdate = clock ();
263
276
smootherUpdateTimes.push_back ({index, afterUpdate - beforeUpdate});
264
277
@@ -288,7 +301,7 @@ class Experiment {
288
301
// }
289
302
290
303
// Write timing info to file
291
- ofstream outfileTime;
304
+ std:: ofstream outfileTime;
292
305
std::string timeFileName = " Hybrid_City10000_time.txt" ;
293
306
outfileTime.open (timeFileName);
294
307
for (auto accTime : timeList) {
@@ -300,15 +313,47 @@ class Experiment {
300
313
};
301
314
302
315
/* ************************************************************************* */
303
- int main () {
316
+ // Function to parse command-line arguments
317
+ void parseArguments (int argc, char * argv[], size_t & maxLoopCount,
318
+ size_t & updateFrequency, size_t & maxNrHypotheses) {
319
+ for (int i = 1 ; i < argc; ++i) {
320
+ std::string arg = argv[i];
321
+ if (arg == " --max-loop-count" && i + 1 < argc) {
322
+ maxLoopCount = std::stoul (argv[++i]);
323
+ } else if (arg == " --update-frequency" && i + 1 < argc) {
324
+ updateFrequency = std::stoul (argv[++i]);
325
+ } else if (arg == " --max-nr-hypotheses" && i + 1 < argc) {
326
+ maxNrHypotheses = std::stoul (argv[++i]);
327
+ } else if (arg == " --help" ) {
328
+ std::cout << " Usage: " << argv[0 ] << " [options]\n "
329
+ << " Options:\n "
330
+ << " --max-loop-count <value> Set the maximum loop "
331
+ " count (default: 3000)\n "
332
+ << " --update-frequency <value> Set the update frequency "
333
+ " (default: 3)\n "
334
+ << " --max-nr-hypotheses <value> Set the maximum number of "
335
+ " hypotheses (default: 10)\n "
336
+ << " --help Show this help message\n " ;
337
+ std::exit (0 );
338
+ }
339
+ }
340
+ }
341
+
342
+ /* ************************************************************************* */
343
+ // Main function
344
+ int main (int argc, char * argv[]) {
304
345
Experiment experiment (findExampleDataFile (" T1_city10000_04.txt" ));
305
346
// Experiment experiment("../data/mh_T1_city10000_04.txt"); //Type #1 only
306
347
// Experiment experiment("../data/mh_T3b_city10000_10.txt"); //Type #3 only
307
348
// Experiment experiment("../data/mh_T1_T3_city10000_04.txt"); //Type #1 +
308
349
// Type #3
309
350
351
+ // Parse command-line arguments
352
+ parseArguments (argc, argv, experiment.maxLoopCount ,
353
+ experiment.updateFrequency , experiment.maxNrHypotheses );
354
+
310
355
// Run the experiment
311
- experiment.run (kMaxLoopCount );
356
+ experiment.run ();
312
357
313
358
return 0 ;
314
359
}
0 commit comments