├── .gitignore
├── .gitmodules
├── CMakeLists.txt
├── DEPENDENCIES.md
├── LICENSE
├── NOTICE
├── README.md
├── experiments
├── CMakeLists.txt
├── README.md
├── datasets
│ ├── README.md
│ ├── city10000
│ │ ├── README.md
│ │ └── city10000.g2o
│ ├── csail
│ │ ├── README.md
│ │ ├── csail.g2o
│ │ └── csail.toro
│ ├── cubicle
│ │ ├── README.md
│ │ └── cubicle.g2o
│ ├── garage
│ │ ├── README.md
│ │ └── parking-garage.g2o
│ ├── intel
│ │ ├── README.md
│ │ └── intel.g2o
│ ├── manhattan
│ │ ├── README.md
│ │ └── manhattanOlson3500.g2o
│ ├── manhattan_noisy
│ │ ├── README.md
│ │ └── manhattan_noisy.irl
│ ├── rim
│ │ ├── README.md
│ │ └── rim.g2o
│ ├── sphere2500
│ │ ├── README.md
│ │ └── sphere2500.g2o
│ └── sphere_a
│ │ ├── README.md
│ │ └── sphere_a.g2o
├── exp_runner
│ ├── CMakeLists.txt
│ ├── README.md
│ └── include
│ │ └── exp_runner
│ │ ├── DCRunner-inl.h
│ │ ├── DCRunner.h
│ │ ├── Factory.h
│ │ ├── GNCBatchRunner-inl.h
│ │ ├── GNCBatchRunner.h
│ │ ├── MEstRunner-inl.h
│ │ ├── MEstRunner.h
│ │ ├── MaxMixRunner-inl.h
│ │ ├── MaxMixRunner.h
│ │ ├── ModeDetermination.h
│ │ ├── PCMRunner-inl.h
│ │ ├── PCMRunner.h
│ │ ├── PseudoGtRunner-inl.h
│ │ ├── PseudoGtRunner.h
│ │ ├── RiSAM2Runner-inl.h
│ │ ├── RiSAM2Runner.h
│ │ ├── Runner-inl.h
│ │ └── Runner.h
├── irl
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── include
│ │ └── irl
│ │ │ ├── irl.h
│ │ │ ├── irl_types-inl.h
│ │ │ └── irl_types.h
│ └── python
│ │ ├── irl_parsing.py
│ │ └── irl_types.py
├── run-experiment.cpp
├── scripts
│ ├── README.md
│ ├── animate-traj
│ ├── comparisons.py
│ ├── g2o-2-irl
│ ├── gtg2o-2-values
│ ├── make-gridworld-dataset
│ ├── multi-plot
│ ├── plot-timing
│ ├── plot-traj
│ ├── plot.py
│ ├── precision-recall
│ ├── run-batch-g2o
│ ├── run-dataset-parallel
│ └── toro-2-g2o
└── thirdparty
│ └── CMakeLists.txt
├── media
└── result.png
└── risam
├── .clang-format
├── CMakeLists.txt
├── README.md
├── include
└── risam
│ ├── DoglegLineSearch.h
│ ├── GraduatedFactor.h
│ ├── GraduatedKernel.h
│ └── RISAM2.h
└── src
├── GraduatedFactor.cpp
├── GraduatedKernel.cpp
└── RISAM2.cpp
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | .vscode/
3 | *.so
4 | __pycache__/
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "experiments/thirdparty/Kimera-RPGO"]
2 | path = experiments/thirdparty/Kimera-RPGO
3 | url = https://github.com/MIT-SPARK/Kimera-RPGO
4 | [submodule "experiments/thirdparty/dcsam"]
5 | path = experiments/thirdparty/dcsam
6 | url = https://github.com/MarineRoboticsGroup/dcsam
7 | [submodule "dcsam"]
8 | path = dcsam
9 | url = https://github.com/MarineRoboticsGroup/dcsam
10 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | project(robust-inference CXX C)
2 | cmake_minimum_required(VERSION 3.10)
3 |
4 | message(STATUS "================ ROBUST-INFERENCE ======================")
5 |
6 | find_package(GTSAM REQUIRED)
7 | include_directories(${GTSAM_INCLUDE_DIR})
8 |
9 | # build riSAM
10 | add_subdirectory(risam)
11 |
12 | # Build Experiments directory
13 | add_subdirectory(experiments)
14 |
--------------------------------------------------------------------------------
/DEPENDENCIES.md:
--------------------------------------------------------------------------------
1 | # Dependencies
2 |
3 | This file outlines the direct dependencies of this code specifies the license that each dependency uses and specifies how this code interacts with that dependency.
4 |
5 | # EGNC-PGO
6 | * riSAM [MIT license] (Checkout NOTICE file)
7 |
8 | # riSAM
9 | * C++ and its standard libraries
10 | * GTSAM [BSD license] (unmodified linked dependency), (Modified source code to create riSAM algorithm see: RISAM2.h, RISAM2.cpp)
11 |
12 | # Experiments
13 | * Runners:
14 | * C++ and its builtin libraries
15 | * Kimera-RPGO [BSD 2-Clause License] (referenced as submodule)
16 | * dcsam [MIT License] (referenced as submodule)
17 | * GTSAM [BSD License] (unmodified linked dependency)
18 | * Boost [Boost Software License] (unmodified linked dependency)
19 |
20 | * Scripts
21 | * Python and its builtin Standard libraries
22 | * GTSAM [BSD license] (unmodified linked dependency)
23 | * NUMPY [Numpy License] (unmodified linked dependency)
24 | * Matplotlib [Matplotlib License] (unmodified linked dependency)
25 | * SciPy [BSD licensed] (unmodified linked dependency)
26 |
27 | * Datasets
28 | * Derived from various published works. Source reported in readme for each dataset.
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Daniel McGann
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | NOTICE
2 |
3 | This software is a modified version of the original riSAM, which is distributed under the terms of the MIT License by Daniel McGann.
4 |
5 | Modifications and distribution by: Wonseok Kang, Jaehyun Kim, Jiseong Chung, Seungwon Choi, and Tae-wan Kim
6 | Contact: eoid361@snu.ac.kr, jaedalong@snu.ac.kr, dntksdmfwmf@snu.ac.kr, csw3575@snu.ac.kr, taewan@snu.ac.kr
7 | Date: 10th, Oct., 2023
8 |
9 | Please refer to the original LICENSE file for the terms of the MIT License.
10 | Please refer to the README.md file for original repository information.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Efficient Graduated Non-Convexity for Pose Graph Optimization (EGNC-PGO)
2 |
3 | [](https://arxiv.org/abs/2310.06765)
4 |
5 |
6 |
9 |
10 |
11 | # Structure
12 | * `experiments`: Contains the implementations for experiments.
13 | * `risam`: Contains the implementation of the algorithm.
14 |
15 |
16 | ## Building Instructions (Validated as of Oct 2023)
17 | * Version Summary (tested and confirmed with the following dependency versions)
18 | * GTSAM: Tag=4.2a8, exact hash=9902ccc0a4f62123e91f057babe3612a95c15c20
19 | * KimeraRPGO: exact hash=8c5e163ba38345ff583d87403ad53bf966c0221b
20 | * dcsam: exact hash=b7f62295eec201fb00ee6d1d828fa551ac1f4bd7
21 | * GCC: 11.4.0
22 | * These should be checked out when the git submodules are initialized, but are included here for completeness
23 |
24 | * GTSAM
25 | * Download [GTSAM version 4.2a8](https://github.com/borglab/gtsam/releases/tag/4.2a8) **! 4.2a9 not working!**
26 | * Setup compile time options [required by KimeraRPGO](https://github.com/MIT-SPARK/Kimera-RPGO)
27 | * Build and optionally install GTSAM (Scripts assume GTSAM python is installed in the active python environment)
28 | * Clone EGNC-PGO and Submodules
29 | * `git clone --recursive https://github.com/SNU-DLLAB/EGNC-PGO.git`
30 | * Build GTSAM
31 | * Configure cmake with following options: `cmake .. -DGTSAM_POSE3_EXPMAP=ON -DGTSAM_ROT3_EXPMAP=ON -DGTSAM_USE_SYSTEM_EIGEN=ON`
32 | * Link GTSAM
33 | * If you `install` GTSAM this should be automatic
34 | * If you are working with a local build of GTSAM set `GTSAM_DIR` and `GTSAM_INCLUDE_DIR` to the appropriate directories.
35 | * Build EGNC-PGO with riSAM
36 | * `cd EGNC-PGO`
37 | * `mkdir build`
38 | * `cd build`
39 | * `cmake ..`
40 | * `make`
41 |
42 |
43 | ## Acknowlodgement
44 | The original code is from "Robot Perception Lab - Carnegie Mellon University". link: https://github.com/rpl-cmu/risam
45 |
--------------------------------------------------------------------------------
/experiments/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | message(STATUS "================ EXPERIMENTS ======================")
2 |
3 | # Prior Works for Comparison
4 | add_subdirectory(thirdparty)
5 |
6 | # Subprojects
7 | add_subdirectory(irl)
8 | add_subdirectory(exp_runner)
9 |
10 | # Extra Boost Libraries needed by experiments
11 | FIND_PACKAGE(Boost COMPONENTS program_options REQUIRED)
12 | INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
13 |
14 | # Declare and configure experiment runner executable
15 | add_executable(run-experiment "run-experiment.cpp")
16 | target_link_libraries(run-experiment gtsam ${Boost_LIBRARIES} exp_runner irl)
--------------------------------------------------------------------------------
/experiments/README.md:
--------------------------------------------------------------------------------
1 | # Experimental Validation
2 |
3 | This folder contains scripts, data and results used for experimental validation of EGNC-PGO, riSAM and relative prior works.
4 | This file provides general documentation on the process, file formats, and how to use.
5 |
6 | # Directory Structure
7 | * `dataset`: Contains benchmark SLAM datasets in g2o/toro format
8 | * `exp_runner`: Implements wrappers around all methods so that they all provide a standard interface
9 | * `scripts`: Contains a number of useful pythons scripts for manipulating datasets, evaluating results, and visualizing
10 | * `thirdparty`: Contains thirdparty code that implements prior works
11 | * `run-experiment.cpp`: Main entry point for running a method implemented in `exp_runner` on an IRL dataset (For details on IRL datasets see `experiments/irl/README.md`).
12 |
13 | Each subdirectory contains its own README with relevant information.
14 |
15 | # Experiment Runner
16 | The executable `run-experiment` is the main entry point to running EGNC-PGO, riSAM and prior works. Its arguments are as follows:
17 | * `irl_file`, `i`: Path to Incremental Robot Log file. (see `experiments/irl/README.md`)
18 | * `method`, `m`: The name of the method to run (e.g. pseudo-gt, see `exp_runners/include/Factory.h` for more options).
19 | * `save_every_n`, `n`: Runner will save intermediate result files every N iterations.
20 | * `output_dir`, `o`: Directory to which the results will be saved.
21 | * `mode`, `d`: Mode to run (only works with risam method. For the others, it doesn't work but required.). 0: original risam, 3: EGNC-PGO (1: B-spline without adaptive, 2: adaptive, from AGNC-PGO)
22 |
23 | All options are required. This script will produce an output whos contents are specified in the next section.
24 |
25 | ## Experiment Runner Quirks
26 | The Experiment Runner interface was originally written to support generic multi-modal measurements, rather than just instances of robust-SLAM problems where we have at most two modes (some measurement and the Null Hypothesis). However, not all of the method implementations are written this generically. All method implementations are guaranteed to work for IRL files (see `experiments/irl/README.md`) that contain, single modal priors, single modal odometry, and loop-closure measurements with two modes, a measurement and a Null Hypothesis mode. If you use the scripts in `scripts` to generate datasets you should be all set to use this interface.
27 |
28 | # Quick Start Guide
29 | The following is an example work-flow to play with EGNC-PGO and the prior works included in this repo. In this we assume the working directory is `experiments`
30 |
31 | ### 1. Corrupt a Gridworld dataset
32 | * First lets make a directory to store all of our data and results.
33 | * `mkdir ~//EGNC-PGO_test_environment`
34 | * `mkdir ~//EGNC-PGO_test_environment/datasets`
35 | * Next lets corrupt a 2d dataset with outliers and convert to IRL format using the `g2o-2-irl` script
36 | * `./scripts/g2o-2-irl -i datasets/csail/csail.g2o -n CSAIL -o ~//EGNC-PGO_test_environment/datasets/ --add_outliers --outlier_percentiles 10 --outlier_covariance 0.015 0.01 0.001 --outlier_chi2_thresh 0.95`
37 | * `-i` is the input dataset, in this case CSAIL
38 | * `-o` is the output directory for the datasets generated by the script
39 | * `--add_outliers` Flags that we are adding outliers while converting the dataset
40 | * `--outlier_percentiles` Are the percentiles of total loop closures that outliers should make up. If multiple are given the script generates multiple datasets.
41 | * `--outlier_covariance` specifies the noise model for outlier loop-closures and the value above is approximately the average loop-closure noise model from the CSAIL dataset. For other datasets, a model should be used to match the dataset.
42 | * `--outlier_chi2_thresh` specifies the chi-squared value that an outlier must have relative to the un-corrupted solution.
43 | * For more options run the script with `--help`
44 |
45 | * You should now see `~//EGNC-PGO_test_environment/datasets/10` that contains one CSAIL dataset with 10% outliers.
46 |
47 | ### 2. Run EGNC-PGO on the dataset
48 | * First lets make a directory to store the results
49 | * `mkdir ~//EGNC-PGO_test_environment/results`
50 | * Now lets run EGNC-PGO
51 | * `../build/experiments/run-experiment -i ~//EGNC-PGO_test_environment/datasets/10/CSAIL_10_random_0.irl -o ~//EGNC-PGO_test_environment/results/ -m risam -n 1 -d 3`
52 | * We should now see a directory in results like `results/CSAIL_10_random_0_risam_YYYY-MM-DD_HH-MM-SS`
53 | * You can run prior works by changing the `-m` ("method") option
54 | * You can run riSAM by using `-m risam -d 0` option
55 | * You can run AGNC-PGO by using `-m risam -d 1`, or `-m risam -d 2` option
56 | * You can run EGNC-PGO by using `-m risam -d 3` option
57 |
58 | ### 3. Plot our results
59 | * Lets visualize the results
60 | * `./scripts/plot-traj -i ~//EGNC-PGO_test_environment/datasets/10/CSAIL_10_random_0.irl -r ~//EGNC-PGO_test_environment/results/CSAIL_10_random_0_risam_YYYY-MM-DD_HH-MM-SS/ --legend`
61 | * You should see a plot of the final solution and print outs of the trajectory.
62 | * Further, lets animate the trajectory so we can see all the incremental steps
63 | * `./scripts/animate-traj -i ~//EGNC-PGO_test_environment/datasets/10/CSAIL_10_random_0.irl -r ~//EGNC-PGO_test_environment/results/CSAIL_10_random_0_risam_YYYY-MM-DD_HH-MM-SS/iterations/`
64 |
65 | ## Reproduction of the experimental results of the paper
66 | * We provide the dataset used in experimental results of our paper, shown in Fig. 6.
67 | * To reproduce the result, follow the steps:
68 | * First, make a directory to save the results
69 | * `mkdir ~//EGNC-PGO_test_environment/results`
70 | * Then, run EGNC-PGO
71 | * `../build/experiments/run-experiment -i ./datasets/manhattan_noisy/manhattan_noisy.irl -o ~//EGNC-PGO_test_environment/results/ -m risam -n 1 -d 3`
72 | * You can run prior works by changing the `-m` ("method") option
73 | * Lets visualize the results
74 | * `./scripts/plot-traj -i ~//EGNC-PGO/experiments/datasets/manhattan_noisy/manhattan_noisy.irl -r ~//EGNC-PGO_test_environment/results/manhattan_noisy_0_random_0_risam_YYYY-MM-DD_HH-MM-SS/ --legend`
75 |
76 | # Prior Works
77 |
78 | Each entry the name is followed by common abbreviation then "Method Name" as defined in `exp_runner/include/exp_runner/Factor.h`
79 |
80 | ### riSAM
81 | '''
82 | McGann, Daniel, John G. Rogers, and Michael Kaess. "Robust Incremental
83 | Smoothing and Mapping (riSAM)." In 2023 IEEE International Conference
84 | on Robotics and Automation (ICRA), pp. 4157-4163. IEEE, 2023.
85 | '''
86 | Implementation provided with mode 0
87 |
88 | ### Graduated Non-Convexity (GNC, gnc-batch)
89 | ```
90 | H. Yang, P. Antonante, V. Tzoumas, and L. Carlone, “Graduated non-
91 | convexity for robust spatial perception: From non-minimal solvers to
92 | global outlier rejection,” IEEE Robotics and Automation Letters (RA-
93 | L), vol. 5, no. 2, pp. 1127–1134, 2020.
94 | ```
95 | Implementation provided in GTSAM
96 |
97 | ### Pairwise Consistency Maximization (PCM, pcm)
98 | Note: different method names correspond to different hyper-parameters.
99 | ```
100 | J. Mangelson, D. Dominic, R. Eustice, and R. Vasudevan, “Pairwise
101 | consistent measurement set maximization for robust multi-robot map
102 | merging,” in Proc. IEEE Intl. Conf. on Robotics and Automation
103 | (ICRA), Brisbane, AU, 2018, pp. 2916–2923.
104 | ```
105 | Implementation provided in KimeraRPGO (`thirdparty/Kimera-RPGO`)
106 |
107 | ### MaxMixture (MaxMix, maxmix)
108 | ```
109 | E. Olson and P. Agarwal, “Inference on networks of mixtures for robust
110 | robot mapping,” Intl. J. of Robotics Research (IJRR), vol. 32, no. 7,
111 | pp. 826–840, 2013.
112 | ```
113 | Implementation internal.
114 |
115 | ### MEstimator (MEst, method name dependent on the M-Estimator used)
116 | ```
117 | Z. Zhang, “Parameter estimation techniques: a tutorial with application
118 | to conic fitting,” Image and Vision Computing, vol. 15, no. 1, pp. 59–
119 | 76, 1997.
120 | ```
121 | Implementation baked into GTSAM.
122 |
123 | ### Discrete Continuous Smoothing and Mapping (DC-SAM, dcsam)
124 | ```
125 | K. Doherty, Z. Lu, K. Singh, and J. Leonard, “Discrete-continuous
126 | smoothing and mapping,” arXiv preprint arXiv:2204.11936v2 [cs],
127 | 2022
128 | ```
129 | Implementation provided by DC-SAM (`thirdparty/dcsam`)
130 |
131 |
132 | ### Adaptive Graduated Non-Convexity for Pose Graph Optimization (AGNC-PGO)
133 | ```
134 | Choi, Seungwon, Wonseok Kang, Jiseong Chung, Jaehyun Kim, and Tae-wan Kim. "Adaptive Graduated Non-Convexity for Pose Graph Optimization." arXiv preprint arXiv:2308.11444 (2023).
135 | ```
136 | Our prior work. Provided with modes 1, 2
137 |
--------------------------------------------------------------------------------
/experiments/datasets/README.md:
--------------------------------------------------------------------------------
1 | This directory holds a number of benchmark pose-graph datasets. Some of which were used in the original riSAM publication. These benchmark datasets are all stored as g2o files and were originally meant for batch optimization. See `scripts` for info on how to convert these to IRL datasets for use with the rest of the `experiment` machinery.
2 |
3 | Datasets:
4 | * City1000 \[2d\] (also referred to as City10k) - Very long synthetic grid world dataset
5 | * CSAIL \[2d\] - Short real-world dataset with very few loop closures
6 | * Cubicle \[3d\] (broken) - longer term 3d, but still largely planar dataset
7 | * Garage \[3d\] - longer real-world 3d dataset with interesting elevation changes, however, the noise models are very inaccurate to reality
8 | * Manhattan \[2d\] - mid length synthetic grid world dataset
9 | * rim \[3d\] (broken) - longer term real-world 3d dataset, like cubicle is largely planar
10 | * sphere_a \[3d\] - Larger synthetic 3d dataset
11 | * sphere2500 \[3d\] - Larger synthetic 3d dataset, different (not sure how) from sphere_a
12 | * manhattan_noisy \[2d\] - added noise on Manhattan dataset
13 |
14 | Each subdirectory contains a readme with source from which from which the datasets were taken. These may not be the original source of the datasets.
15 |
16 | Note: Cubicle and RIM are broken from an incremental perspective. Both pose graphs are missing at least one odometry link this means that when running incrementally there are unconstrained variables for at least one step. They can still be optimized in batch because loop-closures ensure that all variables are constrained, but are useless when converting to an irl file and running incrementally.
--------------------------------------------------------------------------------
/experiments/datasets/city10000/README.md:
--------------------------------------------------------------------------------
1 | LOOPS: 10688
2 |
3 | This dataset is courtesy of Michael Kaess and was published along with iSAM2:
4 |
5 | M. Kaess, H. Johannsson, R. Roberts, V. Ila, J.J. Leonard, and F. Dellaert
6 | "iSAM2: Incremental Smoothing and Mapping Using the Bayes Tree",
7 | International Journal of Robotics Research,
8 | vol. 31, Feb. 2012, pp. 217-236.
9 |
--------------------------------------------------------------------------------
/experiments/datasets/csail/README.md:
--------------------------------------------------------------------------------
1 | LOOPS: 128
2 |
3 | Source: https://lucacarlone.mit.edu/datasets/
4 |
5 |
6 | Average Loop Closure Covariance
7 | [[0.01640127 0.00099003 0. ]
8 | [0.00099003 0.01011058 0. ]
9 | [0. 0. 0.0008421 ]]
10 |
11 | ~ DIAG[0.015, 0.01, 0.001]
--------------------------------------------------------------------------------
/experiments/datasets/cubicle/README.md:
--------------------------------------------------------------------------------
1 | LOOPS: 8021
2 |
3 | Source: https://lucacarlone.mit.edu/datasets/
4 |
5 | This dataset appears to be marginally broken. There is at least one odometry measurement that is missing (x14-x15)
--------------------------------------------------------------------------------
/experiments/datasets/garage/README.md:
--------------------------------------------------------------------------------
1 | LOOPS: 4615
2 |
3 | origin: https://lucacarlone.mit.edu/datasets/
--------------------------------------------------------------------------------
/experiments/datasets/intel/README.md:
--------------------------------------------------------------------------------
1 | TOTAL LOOPS: 1340
2 |
--------------------------------------------------------------------------------
/experiments/datasets/manhattan/README.md:
--------------------------------------------------------------------------------
1 | LOOPS: 2099
2 |
3 |
--------------------------------------------------------------------------------
/experiments/datasets/manhattan_noisy/README.md:
--------------------------------------------------------------------------------
1 | # EGNC-PGO original dataset
2 |
3 | To reproduce the result on paper, run this code:
4 | `cd experiments`
5 | `../build/experiments/run-experiment -i datasets/manhattan_noisy/manhattan_noisy.irl -o ~//EGNC-PGO_test_environment/results/ -m risam -n 1 -d `
6 |
7 | To plot, run this code: `./scripts/plot-traj -i datasets/manhattan_noisy/manhattan_noisy.irl -r ~//EGNC-PGO_test_environment/results/manhattan_noisy_risam_2023-10-10_13-50-46/ --legend`
8 |
9 | Plotting code is from original riSAM code.
--------------------------------------------------------------------------------
/experiments/datasets/rim/README.md:
--------------------------------------------------------------------------------
1 | LOOPS: 14156
2 |
3 | Source: https://lucacarlone.mit.edu/datasets/
4 |
5 | NOTE! Dataset appears to be marginally broken. There is at lease one odometry measurement missing (x33-x34)
6 |
--------------------------------------------------------------------------------
/experiments/datasets/sphere2500/README.md:
--------------------------------------------------------------------------------
1 | LOOPS:
2 |
3 |
--------------------------------------------------------------------------------
/experiments/datasets/sphere_a/README.md:
--------------------------------------------------------------------------------
1 | LOOPS: 6448
2 |
3 | Source: https://lucacarlone.mit.edu/datasets/
--------------------------------------------------------------------------------
/experiments/exp_runner/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(exp_runner INTERFACE)
2 | target_link_libraries(exp_runner INTERFACE gtsam risam irl KimeraRPGO dcsam)
3 | target_include_directories(exp_runner INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
4 |
--------------------------------------------------------------------------------
/experiments/exp_runner/README.md:
--------------------------------------------------------------------------------
1 | # Experiment Runner
2 |
3 | This directory contains two things. First is the definition of a common "runner" interface to regularize how each method interacts with the main entry point `run-experiment`. This interface is contained in `Runner.h` + `Runner-inl.h`. Second, are the wrappers for each method to be compatible with the standard runner interface. These implementations can be found in `Runner.h` and `Runner-inl.h`.
4 |
5 | All runners are templated by the type of the robot pose (Pose3, Pose2, Point2, Point3). Therefore they have to exist in headers. To maintain separation of definitions and implementations we treat the `-inl` ("inline") files are the source code files.
--------------------------------------------------------------------------------
/experiments/exp_runner/include/exp_runner/DCRunner-inl.h:
--------------------------------------------------------------------------------
1 | /** @brief Implementation for DC solver
2 | *
3 | *
4 | * @author Dan McGann
5 | * @date Feb 2022
6 | */
7 | #pragma once
8 | #include "dcsam/DCMixtureFactor.h"
9 | #include "dcsam/DiscretePriorFactor.h"
10 | #include "exp_runner/DCRunner.h"
11 |
12 | using gtsam::symbol_shorthand::D;
13 |
14 | namespace exp_runner {
15 | /***************************************************************************/
16 | template
17 | void DCRunner::init() {
18 | gtsam::ISAM2Params params;
19 | params.setOptimizationParams(gtsam::ISAM2DoglegParams());
20 | solver_ = boost::make_shared(params);
21 | }
22 |
23 | /***************************************************************************/
24 | template
25 | void DCRunner::handlePrior(irl::Prior& prior) {
26 | // ASSUME INLIER PRIORS!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
27 | dcsam::HybridFactorGraph new_factors;
28 | gtsam::Values new_values;
29 | dcsam::DiscreteValues new_discrete_values;
30 | // Initialize the factor based on the selected mode
31 | new_factors.push_nonlinear(prior.asFactor(0));
32 | new_values = prior.getGenerator(0)(current_estimate_.continuous);
33 |
34 | // Update the underlying solver
35 | solver_->update(new_factors, new_values);
36 | current_estimate_ = solver_->calculateEstimate();
37 | current_mode_sequence_.push_back(0);
38 | }
39 |
40 | /***************************************************************************/
41 | template
42 | void DCRunner::handleOdometry(irl::Odometry& odom) {
43 | // ASSUME INLIER ODOMETRY!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
44 | dcsam::HybridFactorGraph new_factors;
45 | gtsam::Values new_values;
46 | new_factors.push_nonlinear(odom.asFactor(0));
47 | solver_->update(new_factors, odom.getGenerator(0)(current_estimate_.continuous));
48 | current_mode_sequence_.push_back(0);
49 | // Update current estimate info
50 | current_estimate_ = solver_->calculateEstimate();
51 | }
52 |
53 | /***************************************************************************/
54 | template
55 | void DCRunner::handleLoop(irl::Loop& loop) {
56 | // Handle Single Hypothesis case
57 | if (loop.num_modes == 1) {
58 | dcsam::HybridFactorGraph new_factors;
59 | new_factors.push_nonlinear(loop.asFactor(0));
60 | solver_->update(new_factors, loop.getGenerator(0)(current_estimate_.continuous));
61 | current_mode_sequence_.push_back(0);
62 | }
63 | // Handle MultiHypothesis Case
64 | else {
65 | dcsam::HybridFactorGraph new_factors;
66 | gtsam::Values new_values;
67 | DiscreteValues new_discrete_values;
68 | gtsam::DiscreteKey discrete_key(D(discrete_var_counter_++), 2);
69 | gtsam::FastVector> generators;
70 |
71 | for (size_t i = 0; i < loop.num_modes; i++) {
72 | auto loop_measure = boost::dynamic_pointer_cast>(loop.measurements[i]);
73 | if (loop_measure) {
74 | // Add discrete variable for this mode
75 | new_discrete_values[discrete_key.first] = 0; // Assume inlier at first
76 | // Add the Loop factor for this mode
77 |
78 | std::vector> components;
79 | components.push_back( // Inlier Measurement
80 | gtsam::BetweenFactor(loop.pose_a_key, loop_measure->pose_b_key, loop_measure->rel_pose,
81 | gtsam::noiseModel::Gaussian::Covariance(loop_measure->covariance)));
82 | components.push_back( // Outlier Measurement
83 | gtsam::BetweenFactor(loop.pose_a_key, loop_measure->pose_b_key, loop_measure->rel_pose,
84 | gtsam::noiseModel::Isotropic::Sigma(loop_measure->covariance.rows(), 1.6e7)));
85 |
86 | new_factors.push_dc(dcsam::DCMixtureFactor>(components[0].keys(), discrete_key,
87 | components, false));
88 | new_factors.push_discrete(dcsam::DiscretePriorFactor(discrete_key, {1 - 10e-7, 10e-7}));
89 | }
90 | generators.push_back(loop.getGenerator(i));
91 | }
92 | // Determine linearization point
93 | new_values.insert(generators[0](current_estimate_.continuous));
94 |
95 | // Preform update
96 | solver_->update(new_factors, new_values, new_discrete_values);
97 |
98 | mh_measures_infos_.push_back(std::make_pair(current_mode_sequence_.size(), discrete_key));
99 | current_mode_sequence_.push_back(0);
100 | }
101 |
102 | // Update current estimate info
103 | current_estimate_ = solver_->calculateEstimate();
104 | }
105 |
106 | /***************************************************************************/
107 | template
108 | MethodEstimateResults DCRunner::getEstimate() {
109 | MethodEstimateResults result;
110 | // current_estimate_.discrete.print();
111 | result.push_back(current_estimate_.continuous);
112 | return result;
113 | }
114 |
115 | /***************************************************************************/
116 | template
117 | MethodModeSeqResults DCRunner::getModeSequence() {
118 | MethodModeSeqResults result;
119 | ModeSequence seq;
120 | for (auto& pair : mh_measures_infos_) {
121 | current_mode_sequence_[pair.first] = current_estimate_.discrete.at(pair.second.first);
122 | }
123 | seq.assign(current_mode_sequence_.begin(), current_mode_sequence_.end());
124 | result.push_back(seq);
125 | return result;
126 | }
127 |
128 | /***************************************************************************/
129 | template
130 | std::string DCRunner::getName() {
131 | return "dcsam";
132 | }
133 | } // namespace exp_runner
--------------------------------------------------------------------------------
/experiments/exp_runner/include/exp_runner/DCRunner.h:
--------------------------------------------------------------------------------
1 | /** @brief Experiment Interface for Discrete Continuous SAM
2 | *
3 | * @author Dan McGann
4 | * @date Feb 2022
5 | */
6 | #pragma once
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include "dcsam/DCFactor.h"
15 | #include "dcsam/DCSAM.h"
16 | #include "exp_runner/Runner.h"
17 |
18 | /**
19 | * DCBetweenFactor
20 | *
21 | * Based heavily on
22 | * dcsam:SemanticBearingRangeFactor
23 | */
24 | template
25 | class DCBetweenFactor : public dcsam::DCFactor {
26 | private:
27 | typedef DCBetweenFactor This;
28 |
29 | gtsam::BetweenFactor factor_;
30 | std::vector probs_;
31 |
32 | public:
33 | using Base = DCFactor;
34 |
35 | DCBetweenFactor() = default;
36 |
37 | DCBetweenFactor(const gtsam::Key& key1, const gtsam::Key& key2, const gtsam::DiscreteKey& discreteKey,
38 | const std::vector measuredProbs, const VALUE_TYPE& measured,
39 | const gtsam::SharedNoiseModel& model)
40 | : probs_(measuredProbs), factor_(key1, key2, measured, model) {
41 | gtsam::KeyVector keys{key1, key2};
42 | gtsam::DiscreteKeys dks(discreteKey);
43 | keys_ = keys;
44 | discreteKeys_ = dks;
45 | }
46 |
47 | virtual ~DCBetweenFactor() = default;
48 |
49 | DCBetweenFactor& operator=(const DCBetweenFactor& rhs) {
50 | Base::operator=(rhs);
51 | this->factor_ = rhs.factor_;
52 | this->probs_ = rhs.probs_;
53 | this->keys_ = rhs.keys_;
54 | this->discreteKeys_ = rhs.discreteKeys_;
55 | return *this;
56 | }
57 |
58 | // Error is the sum of the continuous and discrete negative
59 | // log-likelihoods
60 | double error(const gtsam::Values& continuousVals, const DiscreteValues& discreteVals) const override {
61 | size_t assignment = discreteVals.at(discreteKeys_[0].first);
62 | double discrete_error = log(probs_[assignment]);
63 |
64 | // Subtraction because -log(p(A,B)) = -log p(A)p(B) = -log p(A) - log p(B)
65 | return factor_.error(continuousVals) - discrete_error;
66 | }
67 |
68 | // dim is the dimension of the underlying between factor
69 | size_t dim() const override { return factor_.dim(); }
70 |
71 | boost::shared_ptr linearize(const gtsam::Values& continuousVals,
72 | const DiscreteValues& discreteVals) const override {
73 | return factor_.linearize(continuousVals);
74 | }
75 |
76 | bool equals(const DCFactor& other, double tol = 1e-9) const override {
77 | // We attempt a dynamic cast from DCFactor to DCBetweenFactor.
78 | // If it fails, return false.
79 | if (!dynamic_cast(&other)) return false;
80 |
81 | // If the cast is successful, we'll properly construct a
82 | // DCBetweenFactor object from `other`
83 | const DCBetweenFactor& f(static_cast(other));
84 |
85 | // compare the between factors
86 | if (!(factor_.equals(f.factor_, tol))) return false;
87 |
88 | // If everything above passes, and the keys_, discreteKeys_ and probs_
89 | // variables are identical, return true.
90 | return (std::equal(keys_.begin(), keys_.end(), f.keys().begin()) && (discreteKeys_ == f.discreteKeys_) &&
91 | (probs_ == f.probs_));
92 | }
93 |
94 | double logNormalizingConstant(const gtsam::Values& values) const override {
95 | return nonlinearFactorLogNormalizingConstant(this->factor_, values);
96 | }
97 | };
98 |
99 | namespace exp_runner {
100 |
101 | template
102 | class DCRunner : public Method {
103 | /** FIELDS **/
104 | private:
105 | /// @brief the actual solver
106 | boost::shared_ptr solver_;
107 | /// @brief internal tracker for current estimate
108 | dcsam::DCValues current_estimate_;
109 | /// @brief the current assignment of modes
110 | ModeSequence current_mode_sequence_;
111 | /// @brief information about mh measurements
112 | std::vector> mh_measures_infos_;
113 |
114 | /** PARAMETERS **/
115 | private:
116 | int discrete_var_counter_{0};
117 |
118 | /** HELPERS **/
119 | protected:
120 | /** INTERFACE IMPL **/
121 | public:
122 | void set_mode(size_t mode) override{}; // Just to get virtual inheritance to work
123 |
124 | void init() override;
125 | void handlePrior(irl::Prior& prior) override;
126 | void handleOdometry(irl::Odometry& odom) override;
127 | void handleLoop(irl::Loop& loop) override;
128 | MethodEstimateResults getEstimate() override;
129 | MethodModeSeqResults getModeSequence() override;
130 | std::string getName() override;
131 | };
132 | } // namespace exp_runner
133 |
134 | #include "exp_runner/DCRunner-inl.h"
--------------------------------------------------------------------------------
/experiments/exp_runner/include/exp_runner/Factory.h:
--------------------------------------------------------------------------------
1 | /** @brief Factor for Runner Methods
2 | *
3 | * @author Dan McGann
4 | * @date Jan 2022
5 | */
6 | #pragma once
7 | #include
8 |
9 | #include
10 |
11 | #include "exp_runner/DCRunner.h"
12 | #include "exp_runner/GNCBatchRunner.h"
13 | #include "exp_runner/MEstRunner.h"
14 | #include "exp_runner/MaxMixRunner.h"
15 | #include "exp_runner/PCMRunner.h"
16 | #include "exp_runner/PseudoGtRunner.h"
17 | #include "exp_runner/RiSAM2Runner.h"
18 | #include "exp_runner/Runner.h"
19 |
20 | namespace exp_runner {
21 |
22 | /// @brief Factor for Pose2 methods
23 | template
24 | boost::shared_ptr> method_factory(std::string method_name) {
25 | if (method_name == "pseudo-gt") {
26 | return boost::make_shared>();
27 | } else if (method_name == "gnc-batch") {
28 | return boost::make_shared>();
29 | } else if (method_name == "pcm") {
30 | return boost::make_shared>(3.0, 3.0, "pcm");
31 | } else if (method_name == "maxmix") {
32 | return boost::make_shared>();
33 | } else if (method_name == "dcsam") {
34 | return boost::make_shared>();
35 | } else if (method_name == "huber") {
36 | return boost::make_shared>(gtsam::noiseModel::mEstimator::Huber::Create(3),
37 | "huber");
38 | } else if (method_name == "gm") {
39 | return boost::make_shared>(gtsam::noiseModel::mEstimator::GemanMcClure::Create(3),
40 | "gm");
41 | } else if (method_name == "gauss") {
42 | return boost::make_shared>(gtsam::noiseModel::mEstimator::Null::Create(),
43 | "gauss");
44 | } else if (method_name == "risam") { // riSAM Paper Experimental Params
45 | risam::DoglegLineSearchParams opt_params;
46 | opt_params.search_type = risam::DoglegLineSearchType::OUTWARD;
47 | opt_params.init_delta = 1.0;
48 | opt_params.min_delta = 1.0;
49 | opt_params.max_delta = 50;
50 | risam::RISAM2::RISAM2Params riparams;
51 | riparams.converge_after_new_gnc = true;
52 | riparams.converge_mu = true;
53 | riparams.converge_values = false;
54 | riparams.increment_outlier_mu = true;
55 | riparams.optimization_params = opt_params;
56 | return boost::make_shared>(riparams, 3, method_name);
57 | } else {
58 | throw std::runtime_error("Unknown Method Name");
59 | }
60 | }
61 | } // namespace exp_runner
62 |
--------------------------------------------------------------------------------
/experiments/exp_runner/include/exp_runner/GNCBatchRunner-inl.h:
--------------------------------------------------------------------------------
1 | /** @brief Implementation for GNC solver
2 | *
3 | * @author Dan McGann
4 | * @date Feb 2022
5 | */
6 | #pragma once
7 | #include "exp_runner/GNCBatchRunner.h"
8 |
9 | namespace exp_runner {
10 | /***************************************************************************/
11 | template
12 | void GNCBatchRunner::init() {}
13 |
14 | /***************************************************************************/
15 | template
16 | void GNCBatchRunner::handleUpdate() {
17 | auto params = gtsam::GncParams(optimizer_params_);
18 | params.setKnownInliers(known_inliers_);
19 | gtsam::GncOptimizer> optimizer(graph_, initial_estimates_, params);
20 | current_estimate_ = optimizer.optimize();
21 | initial_estimates_.update(current_estimate_);
22 | }
23 |
24 | /***************************************************************************/
25 | template
26 | void GNCBatchRunner::handlePrior(irl::Prior& prior) {
27 | if (prior.num_modes == 1) {
28 | known_inliers_.push_back(graph_.nrFactors());
29 | graph_.push_back(prior.asFactor(0));
30 | initial_estimates_.insert(prior.getGenerator(0)(initial_estimates_));
31 | current_mode_sequence_.push_back(0);
32 | handleUpdate();
33 | } else {
34 | gtsam::NonlinearFactorGraph prior_factors;
35 | for (size_t i = 0; i < prior.num_modes; i++) {
36 | prior_factors.push_back(prior.asFactor(i));
37 | initial_estimates_.insert(prior.getGenerator(i)(initial_estimates_));
38 | }
39 | // Update the loop closure info
40 | mh_infos_.push_back(MHInfo(prior.correct_mode_idx, current_mode_sequence_.size(), prior_factors));
41 | current_mode_sequence_.push_back(0); // Push back temp index this will get updated in the next call
42 | graph_.push_back(prior_factors);
43 |
44 | handleUpdate();
45 | }
46 | }
47 |
48 | /***************************************************************************/
49 | template
50 | void GNCBatchRunner::handleOdometry(irl::Odometry& odom) {
51 | if (odom.num_modes == 1) {
52 | known_inliers_.push_back(graph_.nrFactors());
53 | graph_.push_back(odom.asFactor(0));
54 | initial_estimates_.insert(odom.getGenerator(0)(initial_estimates_));
55 | current_mode_sequence_.push_back(0);
56 | handleUpdate();
57 | } else {
58 | gtsam::NonlinearFactorGraph odom_factors;
59 | for (size_t i = 0; i < odom.num_modes; i++) {
60 | auto factor = odom.asFactor(i);
61 | if (factor) {
62 | odom_factors.push_back(factor);
63 | initial_estimates_.insert(odom.getGenerator(i)(initial_estimates_));
64 | }
65 | }
66 | // Update the loop closure info
67 | mh_infos_.push_back(MHInfo(odom.correct_mode_idx, current_mode_sequence_.size(), odom_factors));
68 | current_mode_sequence_.push_back(0); // Push back temp index this will get updated in the next call
69 | graph_.push_back(odom_factors);
70 | handleUpdate();
71 | }
72 | }
73 |
74 | /***************************************************************************/
75 | template
76 | void GNCBatchRunner::handleLoop(irl::Loop& loop) {
77 | gtsam::NonlinearFactorGraph loop_factors;
78 | for (size_t i = 0; i < loop.num_modes; i++) {
79 | auto factor = loop.asFactor(i);
80 | if (factor) {
81 | loop_factors.push_back(factor);
82 | initial_estimates_.insert(loop.getGenerator(i)(initial_estimates_));
83 | }
84 | }
85 | mh_infos_.push_back(MHInfo(loop.correct_mode_idx, current_mode_sequence_.size(), loop_factors));
86 | current_mode_sequence_.push_back(0); // Push back temp index this will get updated in the next call
87 | graph_.push_back(loop_factors);
88 | handleUpdate();
89 | }
90 |
91 | /***************************************************************************/
92 | template
93 | void GNCBatchRunner::handleFinal() {
94 | auto params = gtsam::GncParams(optimizer_params_);
95 | params.setKnownInliers(known_inliers_);
96 | gtsam::GncOptimizer> optimizer(graph_, initial_estimates_, params);
97 | current_estimate_ = optimizer.optimize();
98 | }
99 |
100 | /***************************************************************************/
101 | template
102 | MethodEstimateResults GNCBatchRunner::getEstimate() {
103 | MethodEstimateResults result;
104 | result.push_back(current_estimate_);
105 | return result;
106 | }
107 |
108 | /***************************************************************************/
109 | template
110 | MethodModeSeqResults GNCBatchRunner::getModeSequence() {
111 | MethodModeSeqResults result;
112 | ModeSequence seq;
113 | computeModeSequence(current_mode_sequence_, graph_, current_estimate_, mh_infos_, &chiSqInlierCheck);
114 | seq.assign(current_mode_sequence_.begin(), current_mode_sequence_.end());
115 | result.push_back(seq);
116 | return result;
117 | }
118 |
119 | /***************************************************************************/
120 | template
121 | std::string GNCBatchRunner::getName() {
122 | return "gnc-batch";
123 | }
124 | } // namespace exp_runner
--------------------------------------------------------------------------------
/experiments/exp_runner/include/exp_runner/GNCBatchRunner.h:
--------------------------------------------------------------------------------
1 | /** @brief Experiment Interface for Graduated Non-Convexity
2 | *
3 | * @author Dan McGann
4 | * @date Feb 2022
5 | */
6 | #pragma once
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | #include "exp_runner/ModeDetermination.h"
16 | #include "exp_runner/Runner.h"
17 |
18 | namespace exp_runner {
19 |
20 | template
21 | class GNCBatchRunner : public Method {
22 | /** FIELDS **/
23 | private:
24 | /// @brief iteration_counter
25 | size_t iter{0};
26 | /// @brief The graph that we are optimizing
27 | gtsam::NonlinearFactorGraph graph_;
28 | /// @brief Initial Conditions used at each batch solve
29 | gtsam::Values initial_estimates_;
30 |
31 | /// @brief internal tracker for current estimate
32 | gtsam::Values current_estimate_;
33 | /// @brief the current assignment of modes
34 | ModeSequence current_mode_sequence_;
35 | /// @brief list of known inlier factors (i.e. odometry) that we should not convexify
36 | gtsam::GncParams::IndexVector known_inliers_;
37 | /// @brief info about each loop closure
38 | std::vector mh_infos_;
39 |
40 | /** PARAMETERS **/
41 | private:
42 | /// @brief the parameters to use for Optimization
43 | gtsam::GaussNewtonParams optimizer_params_;
44 |
45 | /** HELPERS **/
46 | protected:
47 | /** @brief Checks iteration count and runs optimizer if needed
48 | */
49 | void handleUpdate();
50 |
51 | /** INTERFACE IMPL **/
52 | public:
53 | void set_mode(size_t mode) override{}; // Just to get virtual inheritance to work
54 |
55 | void init() override;
56 | void handlePrior(irl::Prior& prior) override;
57 | void handleOdometry(irl::Odometry& odom) override;
58 | void handleLoop(irl::Loop& loop) override;
59 | void handleFinal() override;
60 | MethodEstimateResults getEstimate() override;
61 | MethodModeSeqResults getModeSequence() override;
62 | std::string getName() override;
63 | };
64 | } // namespace exp_runner
65 |
66 | #include "exp_runner/GNCBatchRunner-inl.h"
--------------------------------------------------------------------------------
/experiments/exp_runner/include/exp_runner/MEstRunner-inl.h:
--------------------------------------------------------------------------------
1 | /** @brief Implementation for MEstimator Runner
2 | *
3 | * @author Dan McGann
4 | * @date May 2022
5 | */
6 | #pragma once
7 |
8 | #include "exp_runner/MEstRunner.h"
9 | namespace exp_runner {
10 |
11 | /***************************************************************************/
12 | template
13 | void MEstRunner::init() {
14 | gtsam::ISAM2Params params;
15 | params.setOptimizationParams(gtsam::ISAM2DoglegParams(0.1, 1e-10, gtsam::DoglegOptimizerImpl::TrustRegionAdaptationMode::ONE_STEP_PER_ITERATION));
16 | solver_ = boost::make_shared(params);
17 | }
18 |
19 | /***************************************************************************/
20 | template
21 | void MEstRunner::handleFinal() {
22 | for (size_t i = 0; i < 20; i++) {
23 | solver_->update();
24 | }
25 | current_estimate_ = solver_->calculateBestEstimate();
26 | }
27 |
28 | /***************************************************************************/
29 | template
30 | void MEstRunner::handlePrior(irl::Prior& prior) {
31 | gtsam::NonlinearFactorGraph new_factors;
32 | if (prior.num_modes == 1) {
33 | new_factors.push_back(prior.asFactor(0));
34 | } else {
35 | for (size_t i = 0; i < prior.num_modes; i++) {
36 | auto factor = prior.asFactor(i);
37 | if (factor) {
38 | auto nm_factor = boost::dynamic_pointer_cast(factor);
39 | new_factors.push_back(nm_factor->cloneWithNewNoiseModel(
40 | gtsam::noiseModel::Robust::Create(robust_kernel_, nm_factor->noiseModel())));
41 | }
42 | }
43 | // Update the loop closure info
44 | mh_infos_.push_back(MHInfo(prior.correct_mode_idx, current_mode_sequence_.size(), new_factors));
45 | }
46 |
47 | gtsam::Values new_values = prior.getGenerator(0)(current_estimate_);
48 | solver_->update(new_factors, new_values);
49 | current_estimate_ = solver_->calculateEstimate();
50 | current_mode_sequence_.push_back(0);
51 | }
52 |
53 | /***************************************************************************/
54 | template
55 | void MEstRunner::handleOdometry(irl::Odometry& odom) {
56 | gtsam::NonlinearFactorGraph new_factors;
57 | if (odom.num_modes == 1) {
58 | new_factors.push_back(odom.asFactor(0));
59 | } else {
60 | for (size_t i = 0; i < odom.num_modes; i++) {
61 | auto factor = odom.asFactor(i);
62 | if (factor) {
63 | auto nm_factor = boost::dynamic_pointer_cast(factor);
64 | new_factors.push_back(nm_factor->cloneWithNewNoiseModel(
65 | gtsam::noiseModel::Robust::Create(robust_kernel_, nm_factor->noiseModel())));
66 | }
67 | }
68 | // Update the loop closure info
69 | mh_infos_.push_back(MHInfo(odom.correct_mode_idx, current_mode_sequence_.size(), new_factors));
70 | }
71 |
72 | gtsam::Values new_values = odom.getGenerator(0)(current_estimate_);
73 | solver_->update(new_factors, new_values);
74 | current_estimate_ = solver_->calculateEstimate();
75 | current_mode_sequence_.push_back(0);
76 | }
77 |
78 | /***************************************************************************/
79 | template
80 | void MEstRunner::handleLoop(irl::Loop& loop) {
81 | gtsam::NonlinearFactorGraph new_factors;
82 | if (loop.num_modes == 1) {
83 | new_factors.push_back(loop.asFactor(0));
84 | } else {
85 | for (size_t i = 0; i < loop.num_modes; i++) {
86 | auto factor = loop.asFactor(i);
87 | if (factor) {
88 | auto nm_factor = boost::dynamic_pointer_cast(factor);
89 | new_factors.push_back(nm_factor->cloneWithNewNoiseModel(
90 | gtsam::noiseModel::Robust::Create(robust_kernel_, nm_factor->noiseModel())));
91 | }
92 | }
93 | // Update the loop closure info
94 | mh_infos_.push_back(MHInfo(loop.correct_mode_idx, current_mode_sequence_.size(), new_factors));
95 | }
96 |
97 | gtsam::Values new_values = loop.getGenerator(0)(current_estimate_);
98 | solver_->update(new_factors, new_values);
99 | current_estimate_ = solver_->calculateEstimate();
100 | current_mode_sequence_.push_back(0);
101 | }
102 |
103 | /***************************************************************************/
104 | template
105 | MethodEstimateResults MEstRunner::getEstimate() {
106 | MethodEstimateResults result;
107 | result.push_back(current_estimate_);
108 | return result;
109 | }
110 |
111 | /***************************************************************************/
112 | template
113 | MethodModeSeqResults MEstRunner::getModeSequence() {
114 | MethodModeSeqResults result;
115 | ModeSequence seq;
116 | computeModeSequence(current_mode_sequence_, solver_->getFactorsUnsafe(), current_estimate_, mh_infos_,
117 | &chiSqInlierCheck);
118 | seq.assign(current_mode_sequence_.begin(), current_mode_sequence_.end());
119 | result.push_back(seq);
120 | return result;
121 | }
122 |
123 | /***************************************************************************/
124 | template
125 | std::string MEstRunner::getName() {
126 | return kernel_name_;
127 | }
128 |
129 | } // namespace exp_runner
130 |
--------------------------------------------------------------------------------
/experiments/exp_runner/include/exp_runner/MEstRunner.h:
--------------------------------------------------------------------------------
1 | /** @brief Runner that adds all measurements with a robust estimator
2 | *
3 | * @author Dan McGann
4 | * @date January 2022
5 | */
6 | #pragma once
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include "exp_runner/Runner.h"
15 |
16 | namespace exp_runner {
17 |
18 | template
19 | class MEstRunner : public Method {
20 | /** FIELDS **/
21 | private:
22 | /// @brief the solver
23 | boost::shared_ptr solver_;
24 | /// @brief internal tracker for current estimate
25 | gtsam::Values current_estimate_;
26 | /// @brief Current Mode Sequence
27 | ModeSequence current_mode_sequence_;
28 | /// @brief information about mh measurements
29 | std::vector mh_infos_;
30 | /// @brief The robust kernel to use
31 | gtsam::noiseModel::mEstimator::Base::shared_ptr robust_kernel_;
32 | std::string kernel_name_;
33 |
34 | /** INTERFACE IMPL **/
35 | public:
36 | MEstRunner(gtsam::noiseModel::mEstimator::Base::shared_ptr robust_kernel, std::string kernel_name)
37 | : robust_kernel_(robust_kernel), kernel_name_(kernel_name) {}
38 |
39 | void set_mode(size_t mode) override{}; // Just to get virtual inheritance to work
40 |
41 | void init() override;
42 | void handlePrior(irl::Prior& prior) override;
43 | void handleOdometry(irl::Odometry& odom) override;
44 | void handleLoop(irl::Loop& loop) override;
45 | void handleFinal() override;
46 | MethodEstimateResults getEstimate() override;
47 | MethodModeSeqResults getModeSequence() override;
48 | std::string getName() override;
49 | };
50 | } // namespace exp_runner
51 |
52 | #include "exp_runner/MEstRunner-inl.h"
--------------------------------------------------------------------------------
/experiments/exp_runner/include/exp_runner/MaxMixRunner.h:
--------------------------------------------------------------------------------
1 | /** @brief Experiment Interface for MaxMixture SLAM
2 | *
3 | * @author Dan McGann
4 | * @date January 2022
5 | */
6 | #pragma once
7 | #include
8 | #include
9 | #include
10 |
11 | #include "exp_runner/Runner.h"
12 |
13 | namespace exp_runner {
14 |
15 | class MaxMixtureFactor : public gtsam::NonlinearFactor {
16 | /** Public Types **/
17 | public:
18 | typedef boost::shared_ptr shared_ptr;
19 | typedef gtsam::FastVector MixtureModel;
20 |
21 | /** Protected Fields **/
22 | protected:
23 | /// @brief The mixture model of Factors making up this MaxMixtureFactor.
24 | MixtureModel mixture_model_;
25 | /// @brief log(weight) assigned to each of the mixture components
26 | gtsam::FastVector log_weights_;
27 | /// @brief the Negative Log Normalization Constants for each Model @see computeNegLogNormalizationConstant
28 | gtsam::FastVector negative_log_normalizers_;
29 |
30 | public:
31 | /** @brief MaxMixtureFactor Constructor
32 | * @param keys: All keys referenced by this factor. The set-union of keys of all its components.
33 | * @param mixture_components: The factors describing the modes of this max mixture model. Mixture Components must be
34 | * NoiseModelFactors with Gaussian Noise Models. Additionally, all Mixture Components are expected to linearize to the
35 | * same number of rows.
36 | * @param weights: the importance weights assigned to each mode
37 | */
38 | MaxMixtureFactor(const gtsam::KeyVector& keys, const MixtureModel& mixture_components,
39 | const gtsam::FastVector& weights);
40 |
41 | /* PUBLIC INTERFACE */
42 | public:
43 | /** @brief Computes the negative log normalization constant of for the linearized (gaussian) component.
44 | * Computes: -\log\left(\frac{1}{(2\pi)^{d/2}|\Sigma_j|^{1/2}} \exp \left\{ -\frac{1}{2}||h(x_i) -
45 | * z_i||^2_{\Sigma_j} \right\} \right)
46 | * @param component: The mode for which to compute its constant.
47 | */
48 | static double computeNegLogNormalizationConstant(const gtsam::NonlinearFactor::shared_ptr& component);
49 |
50 | /** @brief Computes the Index and normalized error for the component that is most probable given
51 | * the estimate.
52 | * @param current_estimate: The current estimate at which to evaluate component probabilities
53 | */
54 | std::pair computeMaxIndexAndNormalizedError(const gtsam::Values& current_estimate) const;
55 |
56 | /// @brief Returns the number of modes in this factor
57 | size_t nrModes() const;
58 |
59 | /// @brief Returns the dimensionality of the factor (rows on linearization)
60 | size_t dim() const override;
61 |
62 | /** @brief Computes the normalized error for the factor 0.5 the squared mahalanobis distance
63 | * @param current_estimate: the variable estimate at which to compute the error
64 | */
65 | double error(const gtsam::Values& current_estimate) const override;
66 |
67 | /** @brief Linearize this factor at a specific point
68 | * @param current_estimate: the variable estimate at which to linearize the Factor
69 | */
70 | gtsam::GaussianFactor::shared_ptr linearize(const gtsam::Values& current_estimate) const override;
71 |
72 | /// @brief Creates a deep copy of this Factor
73 | gtsam::NonlinearFactor::shared_ptr clone() const override;
74 |
75 | /// @brief Prints to stdout information about this factor for params see gtsam print function interface
76 | void print(const std::string& s = "", const gtsam::KeyFormatter& kf = gtsam::DefaultKeyFormatter) const override;
77 | };
78 |
79 | template
80 | class MaxMixRunner : public Method {
81 | /** FIELDS **/
82 | private:
83 | /// @brief the actual solver
84 | boost::shared_ptr solver_;
85 | /// @brief internal tracker for current estimate
86 | gtsam::Values current_estimate_;
87 | /// @brief Current Mode Sequence
88 | ModeSequence current_mode_sequence_;
89 | /// @brief Flag to get the mode from the max mixture factor
90 | gtsam::FastVector get_mode_from_factor_;
91 |
92 | /** PARAMETERS **/
93 | private:
94 | /// @brief Weight (0,1] for the null hypo mode.
95 | const double null_hypo_weight_{1e-7};
96 | /// @brief Scale factor applied to measurement covariance for null hypothesis factors
97 | const double null_hypo_cov_scale_{1.6e7};
98 |
99 | /** HELPERS **/
100 | protected:
101 | /// @brief Internal Handler used for both Odometry and Loop Closures
102 | void handleBetweenPose(gtsam::Key start_key, gtsam::Key end_key, size_t num_modes,
103 | std::vector measurements, bool use_best_initialization);
104 |
105 | /** INTERFACE IMPL **/
106 | public:
107 | void set_mode(size_t mode) override{}; // Just to get virtual inheritance to work
108 |
109 | void init() override;
110 | void handlePrior(irl::Prior& prior) override;
111 | void handleOdometry(irl::Odometry& odom) override;
112 | void handleLoop(irl::Loop& loop) override;
113 | MethodEstimateResults getEstimate() override;
114 | MethodModeSeqResults getModeSequence() override;
115 | std::string getName() override;
116 | };
117 | } // namespace exp_runner
118 |
119 | #include "exp_runner/MaxMixRunner-inl.h"
--------------------------------------------------------------------------------
/experiments/exp_runner/include/exp_runner/ModeDetermination.h:
--------------------------------------------------------------------------------
1 | /** Helpers to figure out what modes are engaged and dis-engaged for different methods
2 | *
3 | * Author: Dan McGann
4 | * Date: April 2022
5 | */
6 | #pragma once
7 | #include
8 | #include
9 |
10 | #include "exp_runner/Runner.h"
11 |
12 | namespace exp_runner {
13 | /// @brief Structure for storing information about one ambiguous loop closure event
14 | struct MHInfo {
15 | MHInfo(size_t correct_mode, size_t seq_idx, gtsam::NonlinearFactorGraph& fg)
16 | : correct_mode_idx(correct_mode), seq_idx(seq_idx), factors(fg) {}
17 | size_t correct_mode_idx; /// @brief the index of the correct mode
18 | size_t seq_idx; /// @brief The index in the Mode sequence that this loop closure corresponds to
19 | gtsam::NonlinearFactorGraph factors; /// @brief the factors assciated with this loop closure event
20 | };
21 |
22 | /// @brief functor type for determining if a factor is in inlier
23 | typedef std::function InlierCheckFunc;
24 |
25 | /** Computes a mode sequence by looking at active factors and factors listed in a history associated with loop closures
26 | * @Mutates mode_seq
27 | */
28 | void computeModeSequence(ModeSequence& mode_seq, const gtsam::NonlinearFactorGraph& graph, const gtsam::Values& values,
29 | const std::vector& mh_info, const InlierCheckFunc& inlier_check_func) {
30 | // Compute all inlier factors
31 | gtsam::NonlinearFactorGraph inlier_factors;
32 | for (size_t i = 0; i < graph.size(); i++) {
33 | auto factor = graph[i];
34 | if (factor && inlier_check_func(factor, values)) {
35 | inlier_factors.push_back(factor);
36 | }
37 | }
38 |
39 | // Update mode for all loop closure events
40 | for (auto& lci : mh_info) {
41 | // Accumulate all the modes from this event that are active
42 | std::vector indices_of_active_factors;
43 | for (int i = 0; i < lci.factors.size(); i++) {
44 | // If the mode factor is found accumulate its index
45 | if (std::find_if(inlier_factors.begin(), inlier_factors.end(),
46 | [&lci, &i](const gtsam::NonlinearFactor::shared_ptr& a) {
47 | return lci.factors[i]->equals(*a);
48 | }) != inlier_factors.end()) {
49 | indices_of_active_factors.push_back(i);
50 | }
51 | }
52 |
53 | // Determine if the result is any of the special cases
54 | int mode_selection;
55 |
56 | // SPECIAL CASE: Correct mode is null hypothesis (nh is always last mode)
57 | if (indices_of_active_factors.size() == 0) {
58 | mode_selection = lci.factors.size();
59 | }
60 | // SPECIAL CASE: More than one mode active
61 | else if (indices_of_active_factors.size() > 1) {
62 | // CASE 1 multiple active modes and correct mode is one of them (-1)
63 | if (std::find(indices_of_active_factors.begin(), indices_of_active_factors.end(), lci.correct_mode_idx) !=
64 | indices_of_active_factors.end()) {
65 | mode_selection = -1;
66 | }
67 | // CASE 2 multiple active modes and correct mode is NOT one of them (-2)
68 | else {
69 | mode_selection = -2;
70 | }
71 | }
72 | // standard: one active mode
73 | else {
74 | mode_selection = indices_of_active_factors.front();
75 | }
76 |
77 | // Finally update the mode sequence for this event
78 | mode_seq[lci.seq_idx] = mode_selection;
79 | }
80 | }
81 |
82 | /** Common Inlier Check Functions **/
83 | bool chiSqInlierCheck(const gtsam::NonlinearFactor::shared_ptr& factor, const gtsam::Values& values) {
84 | auto nmfactor = boost::dynamic_pointer_cast(factor);
85 | auto err = sqrt(nmfactor->noiseModel()->squaredMahalanobisDistance(nmfactor->unwhitenedError(values))); // Mahalanobis Distance
86 | //auto err = std::sqrt(2.0 * nmfactor->error(values));
87 | double thresh = boost::math::quantile(boost::math::chi_squared_distribution(factor->dim()), 0.95);
88 | return err < thresh;
89 | }
90 |
91 | bool trueInlierCheck(const gtsam::NonlinearFactor::shared_ptr& factor, const gtsam::Values& values) { return true; }
92 |
93 | } // namespace exp_runner
--------------------------------------------------------------------------------
/experiments/exp_runner/include/exp_runner/PCMRunner-inl.h:
--------------------------------------------------------------------------------
1 | /** @brief Implementation for PCM solver
2 | *
3 | * @note ONLY WORKS FOR POSE GRAPH OPTIMIZATION
4 | *
5 | * @author Dan McGann
6 | * @date January 2022
7 | */
8 | #pragma once
9 | #include "exp_runner/PCMRunner.h"
10 |
11 | namespace exp_runner {
12 | /***************************************************************************/
13 | template
14 | void PCMRunner::init() {
15 | KimeraRPGO::RobustSolverParams params;
16 | if (std::is_same::value) {
17 | params.setPcm2DParams(odom_threshold_, lc_threshold_, KimeraRPGO::Verbosity::VERBOSE);
18 | // params.setIncremental();
19 | } else {
20 | params.setPcm3DParams(odom_threshold_, lc_threshold_, KimeraRPGO::Verbosity::VERBOSE);
21 | // params.setIncremental();
22 | }
23 | params.use_gnc_ = false;
24 | solver_ = boost::make_shared(params);
25 | }
26 |
27 | /***************************************************************************/
28 | template
29 | void PCMRunner::handleUpdate() {
30 | solver_->update(new_factors_, new_values_);
31 | current_estimate_ = solver_->calculateEstimate();
32 | new_factors_ = gtsam::NonlinearFactorGraph();
33 | new_values_ = gtsam::Values();
34 | }
35 |
36 | /***************************************************************************/
37 | template
38 | void PCMRunner::handlePrior(irl::Prior& prior) {
39 | // ASSUME INLIER PRIOR !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
40 | new_factors_.push_back(prior.asFactor(0));
41 | gtsam::Values merged(current_estimate_);
42 | merged.insert(new_values_);
43 | new_values_.insert(prior.getGenerator(0)(merged));
44 |
45 | // Update the underlying solver
46 | current_mode_sequence_.push_back(0);
47 | handleUpdate();
48 | }
49 |
50 | /***************************************************************************/
51 | template
52 | void PCMRunner::handleOdometry(irl::Odometry& odom) {
53 | // ASSUME INLIER ODOM !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
54 | new_factors_.push_back(odom.asFactor(0));
55 | gtsam::Values merged(current_estimate_);
56 | merged.insert(new_values_);
57 | new_values_.insert(odom.getGenerator(0)(merged));
58 |
59 | // Update the underlying solver
60 | current_mode_sequence_.push_back(0);
61 | handleUpdate();
62 | }
63 |
64 | /***************************************************************************/
65 | template
66 | void PCMRunner::handleLoop(irl::Loop& loop) {
67 | gtsam::NonlinearFactorGraph loop_factors;
68 | // PCM cannot handle "Either-Or" ambiguity, so add all potential loop closures
69 | for (size_t i = 0; i < loop.num_modes; i++) {
70 | auto factor = loop.asFactor(i);
71 | if (factor) {
72 | loop_factors.push_back(factor);
73 | }
74 | }
75 |
76 | // Update the loop closure info
77 | mh_infos_.push_back(MHInfo(loop.correct_mode_idx, current_mode_sequence_.size(), loop_factors));
78 | current_mode_sequence_.push_back(0); // Push back temp index this will get updated in the next call
79 | new_factors_.push_back(loop_factors);
80 | handleUpdate();
81 | }
82 |
83 | /***************************************************************************/
84 | template
85 | void PCMRunner::handleFinal() {
86 | solver_->update(new_factors_, new_values_);
87 | current_estimate_ = solver_->calculateEstimate();
88 | }
89 |
90 | /***************************************************************************/
91 | template
92 | MethodEstimateResults PCMRunner::getEstimate() {
93 | MethodEstimateResults result;
94 | result.push_back(current_estimate_);
95 | return result;
96 | }
97 |
98 | /***************************************************************************/
99 | template
100 | MethodModeSeqResults PCMRunner::getModeSequence() {
101 | MethodModeSeqResults result;
102 | ModeSequence seq;
103 | auto inlier_factors = solver_->getFactorsUnsafe();
104 | computeModeSequence(current_mode_sequence_, inlier_factors, current_estimate_, mh_infos_, &trueInlierCheck);
105 | seq.assign(current_mode_sequence_.begin(), current_mode_sequence_.end());
106 | result.push_back(seq);
107 | return result;
108 | }
109 |
110 | /***************************************************************************/
111 | template
112 | std::string PCMRunner::getName() {
113 | return name_;
114 | }
115 | } // namespace exp_runner
--------------------------------------------------------------------------------
/experiments/exp_runner/include/exp_runner/PCMRunner.h:
--------------------------------------------------------------------------------
1 | /** @brief Experiment Interface for Incremental Single Robot Pairwise Consistency Measure
2 | * Implementation provided by `thirdpart/Kimera-RPGO`
3 | *
4 | * @note ONLY WORKS FOR POSE GRAPH OPTIMIZATION
5 | *
6 | * @author Dan McGann
7 | * @date January 2022
8 | */
9 | #pragma once
10 | #define SLOW_BUT_CORRECT_BETWEENFACTOR
11 | #include
12 | #include
13 | #include
14 |
15 | #include "KimeraRPGO/RobustSolver.h"
16 | #include "KimeraRPGO/SolverParams.h"
17 | #include "exp_runner/ModeDetermination.h"
18 | #include "exp_runner/Runner.h"
19 |
20 | namespace exp_runner {
21 |
22 | template
23 | class PCMRunner : public Method {
24 | /** FIELDS **/
25 | private:
26 | /// @brief iteration_counter
27 | size_t iter{0};
28 | /// @brief The factors to add to the solver on the next update iteration
29 | gtsam::NonlinearFactorGraph new_factors_;
30 | /// @brief The new values to add to the solver on the next update iteration
31 | gtsam::Values new_values_;
32 | /// @brief the actual solver
33 | boost::shared_ptr solver_;
34 | /// @brief internal tracker for current estimate
35 | gtsam::Values current_estimate_;
36 | /// @brief the current assignment of modes
37 | ModeSequence current_mode_sequence_;
38 | /// @brief tracks information about loop closures
39 | std::vector mh_infos_;
40 |
41 | /** PARAMETERS **/
42 | private:
43 | std::string name_;
44 | /// @brief max allowable M distance deviation from odometry
45 | double odom_threshold_;
46 | /// @brief max allowable M distance deviation between pairs of measurements
47 | double lc_threshold_;
48 |
49 | /** HELPERS **/
50 | protected:
51 | /** @brief Checks iteration count and runs optimizer if needed
52 | */
53 | void handleUpdate();
54 |
55 | /** INTERFACE IMPL **/
56 | public:
57 | PCMRunner(double odom_threshold = 3.0, double loop_threshold = 3.0, std::string name = "pcm-o3-l3")
58 | : odom_threshold_(odom_threshold), lc_threshold_(loop_threshold), name_(name) {}
59 |
60 | void set_mode(size_t mode) override{}; // Just to get virtual inheritance to work
61 |
62 | void init() override;
63 | void handlePrior(irl::Prior& prior) override;
64 | void handleOdometry(irl::Odometry& odom) override;
65 | void handleLoop(irl::Loop& loop) override;
66 | void handleFinal() override;
67 | MethodEstimateResults getEstimate() override;
68 | MethodModeSeqResults getModeSequence() override;
69 | std::string getName() override;
70 | };
71 | } // namespace exp_runner
72 |
73 | #include "exp_runner/PCMRunner-inl.h"
--------------------------------------------------------------------------------
/experiments/exp_runner/include/exp_runner/PseudoGtRunner-inl.h:
--------------------------------------------------------------------------------
1 | /** @brief Implementation for Baseline Experiment Runner
2 | * This runner always uses the correct mode, and is intendend to be treatede as a reference solution
3 | *
4 | * @author Dan McGann
5 | * @date January 2022
6 | */
7 | #pragma once
8 | #include
9 |
10 | namespace exp_runner {
11 |
12 | /***************************************************************************/
13 | template
14 | void PseudoGtRunner::init() {
15 | gtsam::ISAM2Params params;
16 | params.enableDetailedResults = true;
17 | params.relinearizeThreshold = 0.01;
18 | params.setOptimizationParams(gtsam::ISAM2DoglegParams());
19 | solver_ = boost::make_shared(params);
20 | }
21 |
22 | /***************************************************************************/
23 | template
24 | void PseudoGtRunner::handlePrior(irl::Prior& prior) {
25 | gtsam::NonlinearFactorGraph new_factors;
26 | gtsam::Values new_values;
27 | new_factors.push_back(prior.asFactor(prior.correct_mode_idx));
28 | new_values = prior.getGenerator(prior.correct_mode_idx)(current_estimate_);
29 | auto result = solver_->update(new_factors, new_values);
30 | current_estimate_ = solver_->calculateEstimate();
31 | current_mode_sequence_.push_back(prior.correct_mode_idx);
32 | }
33 |
34 | /***************************************************************************/
35 | template
36 | void PseudoGtRunner::handleOdometry(irl::Odometry& odom) {
37 | gtsam::NonlinearFactorGraph new_factors;
38 | gtsam::Values new_values;
39 | new_factors.push_back(odom.asFactor(odom.correct_mode_idx));
40 | new_values = odom.getGenerator(odom.correct_mode_idx)(current_estimate_);
41 | auto result = solver_->update(new_factors, new_values);
42 | current_estimate_ = solver_->calculateEstimate();
43 | current_mode_sequence_.push_back(odom.correct_mode_idx);
44 | }
45 |
46 | /***************************************************************************/
47 | template
48 | void PseudoGtRunner::handleLoop(irl::Loop& loop) {
49 | gtsam::NonlinearFactorGraph new_factors;
50 | gtsam::Values new_values;
51 | new_factors.push_back(loop.asFactor(loop.correct_mode_idx));
52 | new_values = loop.getGenerator(loop.correct_mode_idx)(current_estimate_);
53 | auto result = solver_->update(new_factors, new_values);
54 | current_estimate_ = solver_->calculateEstimate();
55 | current_mode_sequence_.push_back(loop.correct_mode_idx);
56 | }
57 |
58 | /***************************************************************************/
59 | template
60 | MethodEstimateResults PseudoGtRunner::getEstimate() {
61 | MethodEstimateResults result;
62 | result.push_back(current_estimate_);
63 | return result;
64 | }
65 |
66 | /***************************************************************************/
67 | template
68 | MethodModeSeqResults PseudoGtRunner::getModeSequence() {
69 | MethodModeSeqResults result;
70 | ModeSequence seq;
71 | seq.assign(current_mode_sequence_.begin(), current_mode_sequence_.end());
72 | result.push_back(seq);
73 | return result;
74 | }
75 |
76 | /***************************************************************************/
77 | template
78 | std::string PseudoGtRunner::getName() {
79 | return "pseudo-gt";
80 | }
81 |
82 | } // namespace exp_runner
83 |
--------------------------------------------------------------------------------
/experiments/exp_runner/include/exp_runner/PseudoGtRunner.h:
--------------------------------------------------------------------------------
1 | /** @brief Experiment Interface for pseudo gt runner
2 | *
3 | * @author Dan McGann
4 | * @date January 2022
5 | */
6 | #pragma once
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #include "exp_runner/Runner.h"
14 |
15 | namespace exp_runner {
16 |
17 | template
18 | class PseudoGtRunner : public Method {
19 | /** FIELDS **/
20 | private:
21 | /// @brief the solver
22 | boost::shared_ptr solver_;
23 | /// @brief internal tracker for current estimate
24 | gtsam::Values current_estimate_;
25 | /// @brief Current Mode Sequence
26 | ModeSequence current_mode_sequence_;
27 | size_t count = 0;
28 |
29 | /** INTERFACE IMPL **/
30 | public:
31 |
32 | void set_mode(size_t mode) override{}; // Just to get virtual inheritance to work
33 |
34 | void init() override;
35 | void handlePrior(irl::Prior& prior) override;
36 | void handleOdometry(irl::Odometry& odom) override;
37 | void handleLoop(irl::Loop& loop) override;
38 | MethodEstimateResults getEstimate() override;
39 | MethodModeSeqResults getModeSequence() override;
40 | std::string getName() override;
41 | };
42 | } // namespace exp_runner
43 |
44 | #include "exp_runner/PseudoGtRunner-inl.h"
--------------------------------------------------------------------------------
/experiments/exp_runner/include/exp_runner/RiSAM2Runner-inl.h:
--------------------------------------------------------------------------------
1 | /** @brief Implementation for Baseline Experiment Runner
2 | * This runner always uses the correct mode, and is intendend to be treatede as a reference solution
3 | *
4 | * @author Dan McGann
5 | * @date January 2022
6 | */
7 | #pragma once
8 | #include
9 |
10 | #include "risam/RISAM2.h"
11 |
12 | namespace exp_runner {
13 |
14 | /***************************************************************************/
15 | template
16 | void RiSAM2Runner::set_mode(size_t mode) {
17 | mode_ = mode;
18 | solver_->set_mode(mode);
19 | }
20 |
21 | /***************************************************************************/
22 | template
23 | void RiSAM2Runner::init() {
24 | solver_ = boost::make_shared(riparams_);
25 | }
26 |
27 | /***************************************************************************/
28 | template
29 | void RiSAM2Runner::handleFinal() {
30 | for (size_t i = 0; i < 20; i++) {
31 | solver_->update(NonlinearFactorGraph(), Values(), false, ISAM2UpdateParams());
32 | }
33 | current_estimate_ = solver_->calculateEstimate();
34 | }
35 |
36 | /***************************************************************************/
37 | template
38 | void RiSAM2Runner::handlePrior(irl::Prior& prior) {
39 | gtsam::NonlinearFactorGraph new_factors;
40 | gtsam::Values new_values;
41 | if (prior.num_modes == 1) {
42 | new_factors.push_back(prior.asFactor(0));
43 | new_values.insert(prior.getGenerator(0)(current_estimate_));
44 | current_mode_sequence_.push_back(0);
45 | auto result = solver_->update(new_factors, new_values, true);
46 | current_estimate_ = solver_->calculateEstimate();
47 | } else {
48 | for (size_t i = 0; i < prior.num_modes; i++) {
49 | new_factors.push_back(prior.asGNCFactor(i, kernel_shape_param_));
50 | }
51 | new_values.insert(prior.getGenerator(0)(current_estimate_)); // TODO (dan) better method?
52 | mh_infos_.push_back(MHInfo(prior.correct_mode_idx, current_mode_sequence_.size(), new_factors));
53 | current_mode_sequence_.push_back(0);
54 | auto result = solver_->update(new_factors, new_values, false);
55 | current_estimate_ = solver_->calculateEstimate();
56 | }
57 | }
58 |
59 | /***************************************************************************/
60 | template
61 | void RiSAM2Runner::handleOdometry(irl::Odometry