mirror of
https://github.com/introlab/rtabmap.git
synced 2026-09-03 10:00:23 +08:00
Adding doc and tests (#1492)
* added doc and tests for util2d.h * updated cmake-ros ci * Added util3d.h doc and tests * util3d_transforms.h: Added doc and tests * util3d_filtering.h: started doc and test * util3d_filtering.h: more tests and doc * Added more doc/tests * finished util3d_filtering doc and tests * added test for util2d::depthBleedingFiltering * Added util3d_registration tests * Added util3d_features.h doc/tests * added doc/tests for util3d_correspondences.h * added doc/gtest for util3d_mapping.h (missing hpp functions) * finished testing util3d_mapping.hpp * Added util3d_motion_estimation.h tests (2D->3D done) * finished util3d_motion_estimation.h tests * minimal util3d_surface.h * Added Transform and VisualWord tests * Added doc for CameraModel and StereoCameraModel * Added more logs in ros ci * Passing tests on fical * improved all devcontainer * added devcontainer kilted, fixed source setup.bash, removed ldconfig in ros-cmake workflow * cleanup * source ros * Added utilite tests * Added testing to appveyor, github actions cancellable on re-commit on same branch * appveyor testing without all targets * appveyor: specifying ALL_BUILD target * Fixed Util2dTest.NMSImageBoundsRespected test * Fixing PCL Indices error on old pcl * Added VWDictionary tests and doc. Fixed LSH not working (fix from https://github.com/flann-lib/flann/pull/472 * fixing some appveyor CI errors, added test to check dictionary serialization against all type * Added StereoDense, StereoBM and StereoSGBM doc and tests * Added Stereo tests * Added CameraModel and StereoCameraModel tests * Added doc and test for Statistics * Added doc/tests for Signature * Added doc/test for SensorEvent, added doc for SensorCaptureInfo * Added doc to SensorData * Added SensorData tests * Added SensorCapture and SensorCaptureThread doc and tests * fixed sensordata test * updated SSC test and doc * Added doc and tests for BayesFilter class * Enabled testing on mac, updated windows testing like on linux * added test_link * fixed unresolved on windows * fixed ThreadHandle error on macos ci * Added GPS and GeodeticCoords tests * Added tests for compression * Added Odometry tests (base class only) * Added DBDriver tests * Added coverage report * uniformized test names * fixing concurancy and coverage ci * dont built tools, examples and app for coverage build * fixed report tool rebuilt without qt compilation error * updated coverage option * updated coverage config * added doc CI job * fixing windows and mac ci errors * Added DBDriverSqlite3 tests * Added IMU tests * Added Graph tests * fixing flaky macos test * Added IMUThread and IMUFilter tests * Added Landmarks tests * Added LASWriter tests * fixing seed flaky test * fixing flaky macos timing tests * Added LocalGrid tests * Added LocalGridMaker tests * fixing ci errors * Added GlobalMap tests * Added doc for EnvSensor * Added Features2D tests * Added Registration tests * Added RegistrationVis tests * Added doc for Rtabmap and Memory classes * Added Memory and Rtabmap tests * making some tests less flaky * lcov 1.14 support * updated compatible tool arguments * Added integration tests (RGB-D, Stereo, Lidar2d, Lidar3d) * More octomap checks * Refactored how/when python interpretor is created to simplify library usage * Added python tests * fixed some flaky tests * suppressed some third party related warnings * fixed ceres tests * more flaky fixes * Fixing tests without libpointmatcher * Added RANSAC rejection filter to PCL ICP * fixing multi platform flakiness * Added test to detect regression * Fixing windows pcl link error * fixed some macos flakiness * bigger 2D2D registration error on opencv 4.6.0 * flakiness * fixing flaky tests on windows and mac * flaky thread test on slow mac VM * windows slow test * fixing more ci erros * fxing temp dir on windows * Added Optimizer tests and discovered some bugs (fixed) * fixing flaky tests in mac and windows * Added Optimizer doc * Added GTSAM BA, updated Ceres to use g2o ba parameters. Renamed g2o's ba related parameters to Optimizer group and used by both gtsam and ceres. * fixing build without gtsam * fixing home dir * fixing python ci isssues * Added multicam ba tests * Added Ceres multicam BA support * Aligned BundleAdjustment parameters with Optimizer/Strategy to avoid confusion in the code * Added BA integration test * Added robust graph optimization integration test * Added loop3it test * Added stereo20Hz test * Added smartfactor gtsam * Fixed bugged check and warn if python didn't return any descriptors * Fixing gtsam version build issues * fixing tilt on windows ci * loosing ceres integration test for ci * mac ci flakiness * updating missing param in gui * updating test bound for mac * added appearance-based tests, set min gftt quality to quality level * testing more stuff * improving features2d tests * ci flakiness * fixing flaky ci * ci fixes * flaky fixes * Added RegistrationIcp tests * Added icp integration test with real-worl corridor like env * intermediate nodes * fixing enum * Updated test to catch #1714 * Fixed 2d corridor failing on pcl * flaky pnp test * flaky brisk test * Set rtabmap_integration test as long * updating loop closure test * flaky ci tests * TEsting roundtrip g2o/toro save/load * loosing test bound * fixed cuda capable checks * flaky tests * Debugging test hanging * more debugging stuff * updating limit * windows: disabled cuda on ci to avoid incompatible driver issue. Fixing a bad test mem allocation * trying fixing cuda hanging issue * fixing ci flakyness * flaky tests * Updated BOW flaky tests by checking min precision/recall instead of recall@100precision. Fixed signature test * CameraModel::load() test initRectificationMap param * test dbdriver load dictionary idsOnly * Memory: test keepLinkedInDb param * added dummyDictionary tests * test intermediate nodes count * Added MarkerDetector tests * reverted breaking change of UMutex and USemaphore * Features2d: fixed compiltion warnings with clang about override * clang warnings * fixing test build with pcl 1.8 * g2o and gtsam build errors on android * opencv5 test fixes * disabled testing for ios and android builds * normalized endline characters for easier diff * added LF CRLF rule * bump 0.23.10. fixing doc version * Publish rtabmap website doc from ci * fixing MSCVC build error * macos icp flaky test * fixing ceres macos test bound * ficing more flaky tests * fixing opencv5 related test errors. Also fixed an actual bug in ENU_WGS84ToGeocentric_WGS84() * added comment about mrpt change * removed rosdoc2 (will add it for rtabmap_ros later) * fixing website style * updated download links * locally deployable website with api * sweep doxygen issues * improved/revised doxygen main pages * removed examples empty page * Updated doxygen style * more concise doxygen groups * added api link on main readme * fixing utilite test error * fixing CommonFilteringGroundNormalsUp test * updated precisionRecall test bounds for Freak and brief descriptors * fixing scale check in ba tests * disabled tests on windows cuda build (missing dlls amd runner cannot test cuda anyway) * ceres: missing suitesparse dep in windows ci * adjusting recall thr for fast/freak * ficing more flaky tests * fixing flaky tests * disabled coverage in ros ci * Enable integration tests for ros ci jobs * loosing up some threshold for failing tests * trigger cache * fixing test data in ros ci. Updated flaky test for mac * slaking some test limit * Fixed rtabmap-detectMoreLoopClosures inverted output value * loosing up sift recall on mac * optimizer re-ordered distribution for reproducible results (mac g2o) * macos dump test crash log * combining all tests to save time on shared library reload. Also fixed Logs with missing arguments. * Added ENABLE_FORMAT_ERRORS cmake option * do test only one time * fixed all format warnings * format security android build errors * less verbose tests * updated ImuUThread test * fixed a log * Fixed libpointmatcher 2d normals eigen issue * Fixing libpointmatcher conversion issues * fixing libpointmatcher test on windows ci * cleanup comments, relax some test thr * disabled sequoia-intel ci build (too flaky, would need extensive testing directly on that machine)
This commit is contained in:
55
doxygen/custom.css
Normal file
55
doxygen/custom.css
Normal file
@@ -0,0 +1,55 @@
|
||||
/* RTAB-Map's own tweaks, loaded after the Doxygen Awesome theme.
|
||||
*
|
||||
* 1. Navigation tree
|
||||
* ------------------
|
||||
* The theme reveals the expand/collapse arrows only while the pointer is over
|
||||
* a row, so nothing shows that a page has children until you happen to hover
|
||||
* it -- stock Doxygen shows them at all times. Both opacities are theme
|
||||
* variables, made for exactly this override.
|
||||
*/
|
||||
|
||||
html {
|
||||
--side-nav-arrow-opacity: 0.75;
|
||||
--side-nav-arrow-hover-opacity: 1;
|
||||
}
|
||||
|
||||
/* 2. Version dropdown
|
||||
* -------------------
|
||||
* Injected by version-switcher.js into Doxygen's #projectnumber element.
|
||||
*
|
||||
* Everything is expressed with Doxygen Awesome's custom properties, with the
|
||||
* plain-Doxygen value as fallback, so the control follows whichever theme is
|
||||
* in use. The native widget is kept (no `appearance: none`): the theme sets
|
||||
* `color-scheme: dark` in dark mode, which is what makes the browser render
|
||||
* the popup list dark as well -- something a re-styled <select> cannot do.
|
||||
*/
|
||||
|
||||
.rtabmap-version-switcher {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: 1.4;
|
||||
margin-left: var(--spacing-small, 5px);
|
||||
padding: 1px var(--spacing-small, 5px);
|
||||
color: var(--page-foreground-color, inherit);
|
||||
background-color: var(--page-background-color, transparent);
|
||||
border: 1px solid var(--separator-color, #a0a0a0);
|
||||
border-radius: var(--border-radius-small, 4px);
|
||||
cursor: pointer;
|
||||
vertical-align: baseline;
|
||||
max-width: 12em;
|
||||
}
|
||||
|
||||
.rtabmap-version-switcher:hover {
|
||||
border-color: var(--primary-color, #1779c4);
|
||||
}
|
||||
|
||||
.rtabmap-version-switcher:focus-visible {
|
||||
outline: 2px solid var(--primary-color, #1779c4);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
/* #projectnumber is rendered at 60% in the theme; the dropdown would end up
|
||||
* unreadably small if it inherited that on top of its own font-size. */
|
||||
#projectnumber .rtabmap-version-switcher {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
338
doxygen/generate_parameters_page.py
Normal file
338
doxygen/generate_parameters_page.py
Normal file
@@ -0,0 +1,338 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate the Doxygen "Parameter reference" page from Parameters.h.
|
||||
|
||||
The parameters are declared with the RTABMAP_PARAM* macros, so Doxygen only
|
||||
ever sees the generated accessors -- one entry per parameter, spread over a
|
||||
1900-member class page. This script reads the declarations instead and emits a
|
||||
Markdown page with one table per group, giving the key, type, default value and
|
||||
description side by side.
|
||||
|
||||
Handles what the declarations actually use:
|
||||
- RTABMAP_PARAM, RTABMAP_PARAM_STR and RTABMAP_PARAM_COND
|
||||
- declarations spanning several lines
|
||||
- descriptions built with uFormat("... %s ...", kOther().c_str(), ...), whose
|
||||
placeholders are resolved to the referenced parameter keys and linked
|
||||
- the same key declared several times under different #if branches (build
|
||||
options), whose defaults are all reported with their condition
|
||||
|
||||
Usage: generate_parameters_page.py --input Parameters.h --output parameters.md
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
MACROS = ('RTABMAP_PARAM_COND', 'RTABMAP_PARAM_STR', 'RTABMAP_PARAM')
|
||||
|
||||
# Groups are emitted in the order they first appear in Parameters.h (which
|
||||
# follows the pipeline), with a short blurb where the prefix is not obvious.
|
||||
GROUP_BLURBS = {
|
||||
'Rtabmap': 'Top-level loop closure detection and map management.',
|
||||
'Mem': 'Memory management: what is kept in STM/WM, what is transferred to LTM.',
|
||||
'Kp': 'Bag-of-words dictionary used for global loop closure detection.',
|
||||
'Vis': 'Visual registration (feature extraction, matching and PnP).',
|
||||
'Icp': 'Geometric registration by iterative closest point.',
|
||||
'Reg': 'Registration strategy shared by loop closures and proximity detection.',
|
||||
'RGBD': 'Metric SLAM: graph, proximity detection and localization.',
|
||||
'Grid': 'Local occupancy grid generation from each node.',
|
||||
'GridGlobal': 'Assembly of the local grids into the global map.',
|
||||
'Optimizer': 'Graph optimization back-end.',
|
||||
'Marker': 'Fiducial marker (ArUco/AprilTag) detection and landmarks.',
|
||||
'Odom': 'Odometry front-end shared settings.',
|
||||
'Bayes': 'Bayes filter used for loop closure hypotheses.',
|
||||
'Db': 'Database (long-term memory) storage.',
|
||||
'Stereo': 'Stereo correspondence.',
|
||||
}
|
||||
|
||||
|
||||
def negate(condition):
|
||||
"""Negation of a #if condition, kept readable for the generated page."""
|
||||
if condition.startswith('!') and '&&' not in condition and '||' not in condition:
|
||||
return condition[1:]
|
||||
if re.match(r'^defined\([A-Za-z0-9_]+\)$', condition):
|
||||
return '!' + condition
|
||||
return '!(%s)' % condition
|
||||
|
||||
|
||||
class Param(object):
|
||||
def __init__(self, prefix, name, type_, default, description, branch):
|
||||
self.prefix = prefix
|
||||
self.name = name
|
||||
self.type = type_
|
||||
self.description = description
|
||||
# (default, branch) pairs: more than one when the parameter is declared
|
||||
# in several #if branches. `branch` is the innermost condition only:
|
||||
# the branches are read in order, first match wins, so the enclosing
|
||||
# negations would only repeat what the previous rows already said.
|
||||
self.defaults = [(default, branch)]
|
||||
|
||||
@property
|
||||
def key(self):
|
||||
return '%s/%s' % (self.prefix, self.name)
|
||||
|
||||
@property
|
||||
def accessor(self):
|
||||
return 'k%s%s' % (self.prefix, self.name)
|
||||
|
||||
@property
|
||||
def anchor(self):
|
||||
"""Doxygen anchor of this parameter's row, for in-page links."""
|
||||
return 'param_%s%s' % (self.prefix, self.name)
|
||||
|
||||
|
||||
def split_args(text):
|
||||
"""Split a macro argument list on top-level commas."""
|
||||
args, depth, current, i, in_string = [], 0, [], 0, False
|
||||
while i < len(text):
|
||||
c = text[i]
|
||||
if in_string:
|
||||
if c == '\\':
|
||||
current.append(text[i:i + 2])
|
||||
i += 2
|
||||
continue
|
||||
in_string = c != '"'
|
||||
elif c == '"':
|
||||
in_string = True
|
||||
elif c in '([':
|
||||
depth += 1
|
||||
elif c in ')]':
|
||||
depth -= 1
|
||||
elif c == ',' and depth == 0:
|
||||
args.append(''.join(current).strip())
|
||||
current = []
|
||||
i += 1
|
||||
continue
|
||||
current.append(c)
|
||||
i += 1
|
||||
args.append(''.join(current).strip())
|
||||
return args
|
||||
|
||||
|
||||
def unescape(literal):
|
||||
"""Concatenate adjacent C string literals and undo their escapes."""
|
||||
out = []
|
||||
for chunk in re.findall(r'"((?:[^"\\]|\\.)*)"', literal):
|
||||
out.append(chunk.replace('\\"', '"').replace('\\n', ' ')
|
||||
.replace('\\t', ' ').replace('\\\\', '\\'))
|
||||
return ''.join(out) if out else literal.strip()
|
||||
|
||||
|
||||
def parse(path):
|
||||
"""Return the parameters of Parameters.h, in declaration order."""
|
||||
with open(path, newline='') as f:
|
||||
lines = f.read().replace('\r\n', '\n').split('\n')
|
||||
|
||||
params, order = {}, []
|
||||
conditions = [] # #if nesting, innermost last; None marks an #else
|
||||
statement, collecting = '', False
|
||||
in_class = False
|
||||
|
||||
for line in lines:
|
||||
stripped = line.strip()
|
||||
|
||||
# Everything before the class (include guard, macro definitions) would
|
||||
# only add noise to the conditions.
|
||||
if not in_class:
|
||||
in_class = stripped.startswith('class ') and 'Parameters' in stripped
|
||||
continue
|
||||
|
||||
if not collecting and stripped.startswith('#'):
|
||||
directive = re.match(r'#\s*(ifdef|ifndef|if|elif|else|endif)\b\s*(.*)', stripped)
|
||||
if directive:
|
||||
kind, expr = directive.group(1), directive.group(2).strip()
|
||||
if kind in ('ifdef', 'ifndef', 'if'):
|
||||
if kind == 'ifdef':
|
||||
expr = 'defined(%s)' % expr
|
||||
elif kind == 'ifndef':
|
||||
expr = '!defined(%s)' % expr
|
||||
conditions.append(expr)
|
||||
elif kind in ('elif', 'else') and conditions:
|
||||
conditions[-1] = None if kind == 'else' else expr
|
||||
elif kind == 'endif' and conditions:
|
||||
conditions.pop()
|
||||
continue
|
||||
|
||||
if not collecting:
|
||||
if not re.match(r'\s*RTABMAP_PARAM', line):
|
||||
continue
|
||||
statement, collecting = line.strip(), True
|
||||
else:
|
||||
statement += ' ' + stripped
|
||||
|
||||
if statement.count('(') != statement.count(')'):
|
||||
continue # declaration continues on the next line
|
||||
collecting = False
|
||||
|
||||
macro = next(m for m in MACROS if statement.startswith(m))
|
||||
args = split_args(statement[len(macro) + 1:statement.rindex(')')])
|
||||
|
||||
prefix, name = args[0], args[1]
|
||||
if macro == 'RTABMAP_PARAM_STR':
|
||||
type_, default, description = 'string', unescape(args[2]), args[3]
|
||||
elif macro == 'RTABMAP_PARAM_COND':
|
||||
type_, description = args[2], args[6]
|
||||
default = '%s if `%s`, %s otherwise' % (args[4], args[3], args[5])
|
||||
else:
|
||||
type_, default, description = args[2], args[3], args[4]
|
||||
|
||||
branch = conditions[-1] if conditions else ''
|
||||
key = '%s/%s' % (prefix, name)
|
||||
if key in params:
|
||||
params[key].defaults.append((default, branch))
|
||||
else:
|
||||
params[key] = Param(prefix, name, type_, default, description, branch)
|
||||
order.append(key)
|
||||
|
||||
return [params[k] for k in order]
|
||||
|
||||
|
||||
def accessor_link(param):
|
||||
"""A link to the C++ accessor, labelled with the parameter key."""
|
||||
return '@ref rtabmap::Parameters::%s() "%s"' % (param.accessor, param.key)
|
||||
|
||||
|
||||
def key_cell(param):
|
||||
"""The key column: the row's own anchor, then the link to the accessor."""
|
||||
return '@anchor %s %s' % (param.anchor, accessor_link(param))
|
||||
|
||||
|
||||
def page_link(param):
|
||||
"""A link to the parameter's own row on this page."""
|
||||
return '@ref %s "%s"' % (param.anchor, param.key)
|
||||
|
||||
|
||||
def resolve_description(param, by_accessor):
|
||||
"""Render the description, resolving uFormat() placeholders to key links."""
|
||||
text = param.description.strip()
|
||||
if text.startswith('uFormat('):
|
||||
args = split_args(text[len('uFormat('):text.rindex(')')])
|
||||
text = unescape(args[0])
|
||||
for arg in args[1:]:
|
||||
match = re.search(r'\bk([A-Za-z0-9]+)\(\)', arg)
|
||||
replacement = arg.strip()
|
||||
if match:
|
||||
other = by_accessor.get('k' + match.group(1))
|
||||
replacement = page_link(other) if other else match.group(1)
|
||||
if '"%s"' in text:
|
||||
text = text.replace('"%s"', replacement, 1)
|
||||
else:
|
||||
text = text.replace('%s', replacement, 1)
|
||||
else:
|
||||
text = unescape(text)
|
||||
return escape_cell(text)
|
||||
|
||||
|
||||
def escape_cell(text):
|
||||
"""A table cell cannot contain a raw '|', and '<' would open an HTML tag."""
|
||||
return text.replace('|', '\\|').replace('<', '<').replace('>', '>')
|
||||
|
||||
|
||||
def format_defaults(param):
|
||||
def code(value):
|
||||
value = value.strip()
|
||||
return '`%s`' % value if value else '`""`'
|
||||
|
||||
# Consecutive branches giving the same value are merged, so that a value
|
||||
# that only changes in the last #else does not repeat three times.
|
||||
merged = []
|
||||
for value, branch in param.defaults:
|
||||
if merged and merged[-1][0] == value:
|
||||
merged[-1][1].append(branch)
|
||||
else:
|
||||
merged.append([value, [branch]])
|
||||
|
||||
if len(merged) == 1:
|
||||
value, branches = merged[0]
|
||||
if not any(branches) or None in branches:
|
||||
return code(value)
|
||||
return '%s with `%s`' % (code(value), escape_cell(' or '.join(branches)))
|
||||
|
||||
parts = []
|
||||
for value, branches in merged:
|
||||
if None in branches or not any(branches):
|
||||
parts.append('%s otherwise' % code(value))
|
||||
else:
|
||||
parts.append('%s with `%s`' % (code(value), escape_cell(' or '.join(branches))))
|
||||
return '<br>'.join(parts)
|
||||
|
||||
|
||||
def render(params, source_name):
|
||||
groups, order = {}, []
|
||||
for p in params:
|
||||
groups.setdefault(p.prefix, []).append(p)
|
||||
if p.prefix not in order:
|
||||
order.append(p.prefix)
|
||||
|
||||
by_accessor = {p.accessor: p for p in params}
|
||||
|
||||
out = []
|
||||
out.append('Parameter reference {#parameters}')
|
||||
out.append('===================')
|
||||
out.append('')
|
||||
out.append('Every setting in RTAB-Map is a `Group/Name` string key with a string value,')
|
||||
out.append('collected in a rtabmap::ParametersMap. The %d parameters below are declared in'
|
||||
% len(params))
|
||||
out.append('`%s`; the same keys are used by rtabmap::Rtabmap and rtabmap::Odometry, by the'
|
||||
% source_name)
|
||||
out.append('applications, by the `--Param Group/Name value` command-line arguments of the')
|
||||
out.append('tools and by the ROS wrappers, so a setting found here applies everywhere.')
|
||||
out.append('')
|
||||
out.append('~~~{.cpp}')
|
||||
out.append('rtabmap::ParametersMap parameters;')
|
||||
out.append('parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kMemSTMSize(), "20"));')
|
||||
out.append('rtabmap.init(parameters, "map.db");')
|
||||
out.append('~~~')
|
||||
out.append('')
|
||||
out.append('Each key links to its accessor on rtabmap::Parameters, which is also how the')
|
||||
out.append('key is spelled in code (`Parameters::kMemSTMSize()` for `Mem/STMSize`).')
|
||||
out.append('Defaults given with a condition depend on how RTAB-Map was built; call')
|
||||
out.append('rtabmap::Parameters::getDefaultParameters() to read the values of the build in use.')
|
||||
out.append('')
|
||||
out.append('**Groups:** ' + ', '.join(
|
||||
'@ref parameters_%s "%s"' % (p, p) for p in order))
|
||||
out.append('')
|
||||
|
||||
for prefix in order:
|
||||
# @anchor rather than a "{#id}" heading: the latter would turn the
|
||||
# heading into a section, which lands in the navigation tree whatever
|
||||
# TOC_INCLUDE_HEADINGS says.
|
||||
out.append('@anchor parameters_%s' % prefix)
|
||||
out.append(prefix)
|
||||
out.append('-' * len(prefix))
|
||||
out.append('')
|
||||
if prefix in GROUP_BLURBS:
|
||||
out.append(GROUP_BLURBS[prefix])
|
||||
out.append('')
|
||||
out.append('| Key | Type | Default | Description |')
|
||||
out.append('| --- | ---- | ------- | ----------- |')
|
||||
for p in groups[prefix]:
|
||||
out.append('| %s | %s | %s | %s |' % (
|
||||
key_cell(p), p.type, format_defaults(p), resolve_description(p, by_accessor)))
|
||||
out.append('')
|
||||
|
||||
return '\n'.join(out) + '\n'
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--input', required=True, help='path to Parameters.h')
|
||||
parser.add_argument('--output', required=True, help='Markdown page to write')
|
||||
args = parser.parse_args()
|
||||
|
||||
params = parse(args.input)
|
||||
if not params:
|
||||
sys.stderr.write('error: no parameter found in %s\n' % args.input)
|
||||
return 1
|
||||
|
||||
directory = os.path.dirname(os.path.abspath(args.output))
|
||||
if directory and not os.path.isdir(directory):
|
||||
os.makedirs(directory)
|
||||
with open(args.output, 'w') as f:
|
||||
f.write(render(params, os.path.basename(args.input)))
|
||||
sys.stderr.write('%s: %d parameters\n' % (args.output, len(params)))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
330
doxygen/mainpage.md
Normal file
330
doxygen/mainpage.md
Normal file
@@ -0,0 +1,330 @@
|
||||
RTAB-Map C++ API {#mainpage}
|
||||
================
|
||||
|
||||
RTAB-Map (Real-Time Appearance-Based Mapping) is a RGB-D, stereo and lidar
|
||||
graph-based SLAM library built around an incremental appearance-based loop
|
||||
closure detector, with memory management that keeps the online constraints
|
||||
satisfiable on large-scale, long-term maps.
|
||||
|
||||
These pages document the public C++ API of the `rtabmap_core` and
|
||||
`rtabmap_utilite` libraries. For installation, tutorials and the ROS packages,
|
||||
see the [project website](https://introlab.github.io/rtabmap/) and the
|
||||
[wiki](https://github.com/introlab/rtabmap/wiki).
|
||||
|
||||
Start here
|
||||
----------
|
||||
|
||||
rtabmap::Rtabmap is the entry point: it owns the map and runs one full SLAM
|
||||
iteration per call to rtabmap::Rtabmap::process(). A minimal loop feeds it a
|
||||
rtabmap::SensorData and the odometry pose that goes with it:
|
||||
|
||||
~~~{.cpp}
|
||||
#include <rtabmap/core/Rtabmap.h>
|
||||
#include <rtabmap/core/Odometry.h>
|
||||
|
||||
rtabmap::Odometry * odometry = rtabmap::Odometry::create();
|
||||
rtabmap::Rtabmap rtabmap;
|
||||
rtabmap.init(); // optionally: init(parameters, databasePath)
|
||||
|
||||
const double mapUpdateRate = 1.0; // Hz, i.e. Rtabmap/DetectionRate
|
||||
double lastProcessStamp = -1.0;
|
||||
rtabmap::Transform mapToOdom = rtabmap::Transform::getIdentity();
|
||||
|
||||
while(/* frames available */)
|
||||
{
|
||||
rtabmap::SensorData data = camera.takeImage();
|
||||
|
||||
// Odometry sees every frame: dropping any would break the motion tracking.
|
||||
rtabmap::Transform odomPose = odometry->process(data);
|
||||
|
||||
// The map is updated at a lower rate. The frames skipped here are not lost
|
||||
// work: the motion they carry is already integrated in the pose above, so
|
||||
// the next accepted frame arrives with an up-to-date odometry pose.
|
||||
if(lastProcessStamp < 0.0 ||
|
||||
data.stamp() - lastProcessStamp >= 1.0/mapUpdateRate)
|
||||
{
|
||||
lastProcessStamp = data.stamp();
|
||||
|
||||
if(rtabmap.process(data, odomPose)) // true when a new node was added
|
||||
{
|
||||
const rtabmap::Statistics & stats = rtabmap.getStatistics();
|
||||
|
||||
// A loop closure or a proximity detection re-optimizes the graph,
|
||||
// which shifts the map frame under the odometry frame.
|
||||
if(!stats.mapCorrection().isNull())
|
||||
{
|
||||
mapToOdom = stats.mapCorrection();
|
||||
}
|
||||
|
||||
if(rtabmap.getLoopClosureId() > 0)
|
||||
{
|
||||
// a loop closure was accepted on this iteration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Robot pose in the map frame, on every frame and always with the matching
|
||||
// correction: composed after the block above, so an iteration that just
|
||||
// re-optimized the graph uses its new correction rather than the previous
|
||||
// one. In ROS terms: (/map -> /odom) * (/odom -> /base_link).
|
||||
rtabmap::Transform mapPose = mapToOdom * odomPose;
|
||||
}
|
||||
~~~
|
||||
|
||||
Throttling is the caller's job here: rtabmap::Rtabmap::process() maps every
|
||||
frame it is given. rtabmap::RtabmapThread does this same stamp comparison
|
||||
internally, from @ref rtabmap::Parameters::kRtabmapDetectionRate() "Rtabmap/DetectionRate",
|
||||
so the threaded pipeline only needs the parameter to be set.
|
||||
|
||||
Odometry drifts and the graph gets re-optimized, so the odometry pose is not a
|
||||
map pose. rtabmap::Statistics::mapCorrection() is what reconciles the two, and
|
||||
since it only changes on a map update it can be applied to every incoming frame
|
||||
-- which is how the pose stays available at full rate while the map is built at
|
||||
1 Hz. This is the transform published as `/map` → `/odom` by the ROS
|
||||
wrapper, and what the `MapBuilder` of each example composes with the live
|
||||
odometry pose to place the clouds. It is also readable outside the statistics,
|
||||
as rtabmap::Rtabmap::getMapCorrection().
|
||||
|
||||
Complete programs live under
|
||||
[`examples/`](https://github.com/introlab/rtabmap/tree/master/examples) in the
|
||||
source tree:
|
||||
|
||||
| Example | What it shows |
|
||||
| ------- | ------------- |
|
||||
| [BOWMapping](https://github.com/introlab/rtabmap/blob/master/examples/BOWMapping/main.cpp) | The smallest useful loop: images from disk into rtabmap::Rtabmap, appearance-only loop closure detection (no odometry, no GUI) |
|
||||
| [NoEventsExample](https://github.com/introlab/rtabmap/blob/master/examples/NoEventsExample/main.cpp) | Driving the pipeline by direct calls -- camera, rtabmap::Odometry and rtabmap::Rtabmap in one explicit loop, without the event system |
|
||||
| [RGBDMapping](https://github.com/introlab/rtabmap/blob/master/examples/RGBDMapping/main.cpp) | The threaded event-based pipeline (rtabmap::SensorCaptureThread → rtabmap::OdometryThread → rtabmap::RtabmapThread) with any supported RGB-D or stereo camera |
|
||||
| [LidarMapping](https://github.com/introlab/rtabmap/blob/master/examples/LidarMapping/main.cpp) | The same threaded pipeline driven by a 3D lidar (rtabmap::LidarVLP16) instead of a camera |
|
||||
|
||||
What each iteration does, and which parameters influence it, is documented on
|
||||
rtabmap::Rtabmap itself -- memory update, loop-closure hypothesis, hypothesis
|
||||
selection, retrieval, proximity detection and transfer to long-term memory.
|
||||
|
||||
Occupancy grid
|
||||
--------------
|
||||
|
||||
With @ref rtabmap::Parameters::kRGBDCreateOccupancyGrid() "RGBD/CreateOccupancyGrid"
|
||||
enabled, every node carries a local occupancy grid computed from its depth images
|
||||
or laser scan (rtabmap::LocalGridMaker). Assembling those into a global grid is
|
||||
left to the caller, so that the result always follows the optimized poses:
|
||||
|
||||
~~~{.cpp}
|
||||
#include <rtabmap/core/global_map/OccupancyGrid.h>
|
||||
|
||||
rtabmap::LocalGridCache localGrids;
|
||||
rtabmap::OccupancyGrid grid(&localGrids, parameters); // reads the Grid/... parameters
|
||||
|
||||
// ... inside the "if(rtabmap.process(data, odomPose))" block of the loop above:
|
||||
const rtabmap::Signature & node = stats.getLastSignatureData();
|
||||
if(node.sensorData().gridCellSize() > 0.0f &&
|
||||
grid.addedNodes().find(node.id()) == grid.addedNodes().end())
|
||||
{
|
||||
// Local grid of the new node, as stored in the database (compressed).
|
||||
cv::Mat ground, obstacles, empty;
|
||||
node.sensorData().uncompressDataConst(0, 0, 0, 0, &ground, &obstacles, &empty);
|
||||
localGrids.add(node.id(), ground, obstacles, empty,
|
||||
node.sensorData().gridCellSize(),
|
||||
node.sensorData().gridViewPoint());
|
||||
}
|
||||
|
||||
// Draws the nodes that are not assembled yet. If the last optimization moved
|
||||
// poses by more than GridGlobal/UpdateError, the grid is cleared first and
|
||||
// redrawn entirely from the cache -- which is why the cache is kept around.
|
||||
grid.update(stats.poses());
|
||||
|
||||
float xMin, yMin; // grid origin (m), in the map frame
|
||||
cv::Mat map = grid.getMap(xMin, yMin); // CV_8S: -1 unknown, 0 free, 100 occupied
|
||||
~~~
|
||||
|
||||
For the node that was just added, the cells are already there uncompressed, and
|
||||
rtabmap::SensorData::uncompressDataConst() returns them as they are -- it only
|
||||
decompresses what comes back empty, which is what makes the same code work for
|
||||
a node retrieved from the database. The occupancy grid is kept on the published
|
||||
copy even with
|
||||
@ref rtabmap::Parameters::kRtabmapPublishLastSignature() "Rtabmap/PublishLastSignature"
|
||||
disabled (only images, scans and user data are dropped), precisely so that the
|
||||
global grid can still be assembled; statistics themselves must be published
|
||||
(@ref rtabmap::Parameters::kRtabmapPublishStats() "Rtabmap/PublishStats", on by
|
||||
default). rtabmap::OccupancyGrid is one of the rtabmap::GlobalMap
|
||||
back-ends: rtabmap::OctoMap, rtabmap::CloudMap and rtabmap::GridMap consume the
|
||||
same cache the same way. Each example's `MapBuilder` does exactly this, then
|
||||
hands the result to the viewer.
|
||||
|
||||
For a 3D map, rtabmap::CloudMap assembles the very same cells into PCL clouds
|
||||
instead of a 2D grid. It shares the cache, so both can be kept up to date from
|
||||
one set of local grids:
|
||||
|
||||
~~~{.cpp}
|
||||
#include <rtabmap/core/global_map/CloudMap.h>
|
||||
#include <pcl/io/pcd_io.h>
|
||||
|
||||
rtabmap::CloudMap cloudMap(&localGrids, parameters); // same cache as above
|
||||
|
||||
// ... right after the localGrids.add() of the block above:
|
||||
cloudMap.update(stats.poses());
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr ground = cloudMap.getMapGround();
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr obstacles = cloudMap.getMapObstacles();
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr emptySpace = cloudMap.getMapEmptyCells();
|
||||
|
||||
pcl::io::savePCDFileBinary("obstacles.pcd", *obstacles);
|
||||
~~~
|
||||
|
||||
The clouds are in the map frame and voxelized at
|
||||
@ref rtabmap::Parameters::kGridCellSize() "Grid/CellSize". Points keep the colour
|
||||
of the local grid when it has one, otherwise ground is green and obstacles red.
|
||||
Note that this assembles the *cells*, not the raw sensor clouds: with
|
||||
@ref rtabmap::Parameters::kGrid3D() "Grid/3D" disabled they are flattened onto
|
||||
the xy plane, so it must stay enabled for a 3D result.
|
||||
|
||||
For a full-resolution cloud, assemble the nodes themselves rather than their
|
||||
cells. Ask rtabmap::Rtabmap::getGraph() for the optimized poses along with the
|
||||
node data, then rebuild a cloud per node and transform it to its pose:
|
||||
|
||||
~~~{.cpp}
|
||||
#include <rtabmap/core/util3d.h>
|
||||
#include <rtabmap/core/util3d_filtering.h>
|
||||
#include <rtabmap/core/util3d_transforms.h>
|
||||
|
||||
std::map<int, rtabmap::Transform> poses;
|
||||
std::multimap<int, rtabmap::Link> links;
|
||||
std::map<int, rtabmap::Signature> nodes;
|
||||
rtabmap.getGraph(poses, links, true, true, &nodes, true); // optimized, global, with images
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr assembled(new pcl::PointCloud<pcl::PointXYZRGB>);
|
||||
for(std::map<int, rtabmap::Transform>::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
|
||||
{
|
||||
rtabmap::SensorData data = nodes.at(iter->first).sensorData();
|
||||
data.uncompressData();
|
||||
|
||||
pcl::IndicesPtr indices(new std::vector<int>);
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud = rtabmap::util3d::cloudRGBFromSensorData(
|
||||
data,
|
||||
4, // image decimation
|
||||
4.0f, // max depth (m), 0 = no limit
|
||||
0.0f, // min depth (m)
|
||||
indices.get());
|
||||
cloud = rtabmap::util3d::voxelize(cloud, indices, 0.01f); // 1 cm
|
||||
*assembled += *rtabmap::util3d::transformPointCloud(cloud, iter->second);
|
||||
}
|
||||
assembled = rtabmap::util3d::voxelize(assembled, 0.01f); // one last pass over the overlaps
|
||||
pcl::io::savePCDFileBinary("cloud.pcd", *assembled);
|
||||
~~~
|
||||
|
||||
@note Do this once the session is over, not on every iteration. It decompresses
|
||||
and re-projects every node, so the cost grows with the whole map, and the poses
|
||||
are only worth exporting once the graph has been optimized -- the same cloud
|
||||
assembled mid-session would carry the drift that later loop closures correct.
|
||||
This is what @ref tool_export "rtabmap-export" does, with more filtering options.
|
||||
|
||||
Memory management
|
||||
-----------------
|
||||
|
||||
RTAB-Map keeps the map in three tiers (rtabmap::Memory): a **short-term memory**
|
||||
of the last @ref rtabmap::Parameters::kMemSTMSize() "Mem/STMSize" nodes, where
|
||||
neighbours are too similar to be loop closure candidates; a **working memory**
|
||||
holding everything loop closure detection compares against; and a **long-term
|
||||
memory**, the part of the map that stays in the database and is not searched.
|
||||
|
||||
By default nothing leaves the working memory, so the iteration time grows with
|
||||
the map. Memory management caps it, and is enabled by setting a budget -- either
|
||||
one, or both:
|
||||
|
||||
~~~{.cpp}
|
||||
rtabmap::ParametersMap parameters;
|
||||
// Keep each update under 700 ms...
|
||||
parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRtabmapTimeThr(), "700"));
|
||||
// ... and/or keep at most 500 nodes in the working memory.
|
||||
parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRtabmapMemoryThr(), "500"));
|
||||
rtabmap.init(parameters, "map.db");
|
||||
~~~
|
||||
|
||||
When an iteration goes over budget, the nodes of lowest weight are moved to the
|
||||
long-term memory at the end of it -- age only breaks ties between equal weights,
|
||||
so it is not simply the oldest that go. Weight is how often a place has been
|
||||
seen: while a node is still in the short-term memory, a new node similar enough
|
||||
to it (@ref rtabmap::Parameters::kMemRehearsalSimilarity() "Mem/RehearsalSimilarity")
|
||||
is merged into it and raises its weight -- the rehearsal mechanism. Places the
|
||||
robot dwells on or revisits therefore stay in the working memory, while views
|
||||
seen once leave first. They are not lost: when a loop
|
||||
closure is found, their neighbours are brought back into the working memory for
|
||||
the next iterations, up to
|
||||
@ref rtabmap::Parameters::kRtabmapMaxRetrieved() "Rtabmap/MaxRetrieved" nodes
|
||||
(plus @ref rtabmap::Parameters::kRGBDMaxLocalRetrieved() "RGBD/MaxLocalRetrieved"
|
||||
around the current pose and along a planned path). This is what makes long-term
|
||||
mapping practical: the robot keeps a bounded, relevant working set and pulls the
|
||||
rest back as it recognizes where it is. Retrieval and node immunization only run
|
||||
when memory management is on.
|
||||
|
||||
Which nodes go first is controlled by three parameters:
|
||||
@ref rtabmap::Parameters::kMemRecentWmRatio() "Mem/RecentWmRatio" protects the
|
||||
most recent part of the working memory,
|
||||
@ref rtabmap::Parameters::kRGBDLocalImmunizationRatio() "RGBD/LocalImmunizationRatio"
|
||||
protects the nodes around the current pose, and
|
||||
@ref rtabmap::Parameters::kMemTransferSortingByWeightId() "Mem/TransferSortingByWeightId"
|
||||
selects the ordering. The step-by-step behaviour is documented on
|
||||
rtabmap::Rtabmap (steps 4 and 6), and the `Memory/Working_memory_size` and
|
||||
`Memory/Signatures_retrieved` entries of rtabmap::Statistics report what
|
||||
happens at runtime.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Every parameter is a string key/value pair in a rtabmap::ParametersMap, declared
|
||||
with its default and description in `Parameters.h`
|
||||
(for example `Parameters::kMemSTMSize()`, `Parameters::kRGBDLinearUpdate()`).
|
||||
The same keys are used by the applications, the ROS wrappers and the
|
||||
`--Param value` command-line arguments of the tools, so a setting found here
|
||||
applies everywhere.
|
||||
|
||||
The @ref parameters "Parameter reference" lists all of them, grouped, with
|
||||
their type, default value and description.
|
||||
|
||||
~~~{.cpp}
|
||||
rtabmap::ParametersMap parameters;
|
||||
parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kMemSTMSize(), "20"));
|
||||
rtabmap.init(parameters, "map.db");
|
||||
~~~
|
||||
|
||||
|
||||
The main classes
|
||||
----------------
|
||||
|
||||
Doxygen lists the classes alphabetically; this is the same set arranged by the
|
||||
role they play, as a starting point into the API.
|
||||
|
||||
### The map structure
|
||||
|
||||
| Class | Role |
|
||||
| ----- | ---- |
|
||||
| rtabmap::Rtabmap | The entry point: one SLAM iteration per call, owning everything below |
|
||||
| rtabmap::Memory | Three-tiered memory (STM / WM / LTM) holding the map and deciding what stays online |
|
||||
| rtabmap::Signature | One node: sensor data, visual words, pose and links |
|
||||
| rtabmap::Link | One edge: neighbour, loop closure, landmark or prior constraint |
|
||||
| rtabmap::DBDriver | Persistence of the map to the database (see rtabmap::DBDriverSqlite3) |
|
||||
| rtabmap::Statistics | Everything the pipeline reports about an iteration |
|
||||
|
||||
### Inputs
|
||||
|
||||
| Class | Role |
|
||||
| ----- | ---- |
|
||||
| rtabmap::SensorData | An observation: images, depth, laser scan, IMU, GPS, landmarks |
|
||||
| rtabmap::CameraModel, rtabmap::StereoCameraModel | Intrinsics, extrinsics and rectification |
|
||||
| rtabmap::LaserScan | Point cloud / laser scan container and its formats |
|
||||
| rtabmap::Transform | The 3D rigid transform used everywhere in the API |
|
||||
| rtabmap::SensorCapture, rtabmap::SensorCaptureThread | Drivers and the thread that pumps them |
|
||||
|
||||
### Building blocks
|
||||
|
||||
| Class | Role |
|
||||
| ----- | ---- |
|
||||
| rtabmap::Odometry | Visual / lidar odometry front-ends |
|
||||
| rtabmap::Registration, rtabmap::RegistrationVis, rtabmap::RegistrationIcp | Relative transform between two nodes |
|
||||
| rtabmap::Optimizer | Graph optimization back-ends (g2o, GTSAM, Ceres, TORO) |
|
||||
| rtabmap::Feature2D, rtabmap::VWDictionary | Keypoint detectors/descriptors and the bag-of-words dictionary |
|
||||
| rtabmap::BayesFilter | Loop-closure hypothesis estimation |
|
||||
| rtabmap::LocalGridMaker, rtabmap::GlobalMap | Occupancy grid generation and assembly |
|
||||
|
||||
Free functions for point cloud, image and geometry processing are grouped in
|
||||
`util2d.h`, `util3d.h`, `util3d_filtering.h`, `util3d_registration.h`,
|
||||
`util3d_surface.h`, `util3d_transforms.h` and `util3d_mapping.h`.
|
||||
66
doxygen/tools.md
Normal file
66
doxygen/tools.md
Normal file
@@ -0,0 +1,66 @@
|
||||
Command-line tools {#tools}
|
||||
==================
|
||||
|
||||
Building RTAB-Map installs a set of executables next to the libraries. They are
|
||||
built on the same API these pages document, so each one is also a worked example
|
||||
of it -- and the quickest way to inspect, replay or convert a map without
|
||||
writing code.
|
||||
|
||||
All of them take the `--Param Group/Name value` arguments described in the
|
||||
@ref parameters "Parameter reference", and print their options with `--help`.
|
||||
|
||||
Working with a map
|
||||
------------------
|
||||
|
||||
| Command | What it does |
|
||||
| ------- | ------------ |
|
||||
| @anchor tool_info `rtabmap-info` | Prints a database summary: version, sizes, parameters and per-node statistics. `--diff` shows only the parameters that differ from the defaults, or from another database. |
|
||||
| @anchor tool_export `rtabmap-export` | Exports the map: assembled point cloud, mesh or textured mesh (`.ply`, `.pcd`, `.obj`), 2D occupancy grid, poses and camera images. Filtering, decimation, colour and texturing are all options. |
|
||||
| @anchor tool_report `rtabmap-report` | Reports the statistics recorded in one or several databases: RMSE against ground truth, timings, loop closures. Plots them when built with Qt. |
|
||||
| @anchor tool_recovery `rtabmap-recovery` | Repairs a database whose session was not closed properly (e.g. after a crash), by rebuilding it from the nodes and links that can still be read. The original is kept as `*.backup.db` unless `-d` is given. |
|
||||
| @anchor tool_reprocess `rtabmap-reprocess` | Replays the data of a database through a fresh @ref rtabmap::Rtabmap "Rtabmap" instance, with different parameters, and writes a new database. Several databases can be merged in one run. The way to try a new configuration on recorded data. |
|
||||
| @anchor tool_detectmoreloopclosures `rtabmap-detectMoreLoopClosures` | Looks for additional loop closures in an existing map, by clustering nodes that are close in the optimized graph, and re-optimizes it. |
|
||||
| @anchor tool_globalba `rtabmap-globalBundleAdjustment` | Runs a global bundle adjustment over the whole graph and saves the refined poses. |
|
||||
| @anchor tool_reducegraph `rtabmap-reduceGraph` | Merges nodes of the same location to shrink the graph, keeping the map usable for localization. |
|
||||
| @anchor tool_cleanuplocalgrids `rtabmap-cleanupLocalGrids` | Clears from the local occupancy grids the space that the assembled global grid shows as empty, so that removed obstacles do not reappear when the map is regenerated. |
|
||||
|
||||
Recording and datasets
|
||||
----------------------
|
||||
|
||||
| Command | What it does |
|
||||
| ------- | ------------ |
|
||||
| @anchor tool_console `rtabmap-console` | Runs loop closure detection without a GUI on a directory of images, a video or a database, and reports the loop closures found. Appearance-only: it forces @ref rtabmap::Parameters::kRGBDEnabled() "RGBD/Enabled" to false and feeds images without a pose, so there is no odometry, no graph optimization and no metric map -- unlike the dataset tools below, which run the whole SLAM pipeline. |
|
||||
| @anchor tool_datarecorder `rtabmap-dataRecorder` | Records a live sensor into a database, using a configuration file exported from the GUI preferences. |
|
||||
| @anchor tool_kitti `rtabmap-kitti_dataset` | Runs a [KITTI](https://www.cvlibs.net/datasets/kitti/) odometry sequence (stereo, optionally Velodyne) and reports the KITTI errors against ground truth. |
|
||||
| @anchor tool_rgbd `rtabmap-rgbd_dataset` | Same for a [TUM RGB-D](https://cvg.cit.tum.de/data/datasets/rgbd-dataset) sequence. |
|
||||
| @anchor tool_euroc `rtabmap-euroc_dataset` | Same for a [EuRoC MAV](https://projects.asl.ethz.ch/datasets/doku.php?id=kmavvisualinertialdatasets) sequence (stereo and IMU). |
|
||||
| @anchor tool_cidsims `rtabmap-cidsims_dataset` | Same for a CID-SIMS sequence (RGB-D, IMU and wheel odometry). |
|
||||
| @anchor tool_imagesjoiner `rtabmap-imagesJoiner` | Joins images of two directories side by side, to build a stereo sequence out of two monocular ones. |
|
||||
|
||||
Sensors and calibration
|
||||
-----------------------
|
||||
|
||||
These need the GUI library (Qt), so they are only built when it is available.
|
||||
|
||||
| Command | What it does |
|
||||
| ------- | ------------ |
|
||||
| @anchor tool_camera `rtabmap-camera` | Streams a USB camera, a video file or a directory of images, and shows what the driver delivers. |
|
||||
| @anchor tool_rgbdcamera `rtabmap-rgbd_camera` | Same for the RGB-D and stereo drivers (OpenNI, Freenect, RealSense, Kinect for Azure, ZED...), to check a device before mapping with it. |
|
||||
| @anchor tool_lidarviewer `rtabmap-lidar_viewer` | Shows the scans of a network lidar (VLP-16). |
|
||||
| @anchor tool_calibration `rtabmap-calibration` | Calibrates a camera or a stereo pair from a chessboard, and saves the model RTAB-Map reads. |
|
||||
| @anchor tool_odometryviewer `rtabmap-odometryViewer` | Runs @ref rtabmap::Odometry "Odometry" alone on a live sensor and shows the features, the local map and the estimated trajectory. Useful to tune the `Odom/` and `Vis/` parameters. |
|
||||
| @anchor tool_databaseviewer `rtabmap-databaseViewer` | Inspects a database: browse the nodes and their data, the graph and its links, add or remove constraints, re-optimize, regenerate the grids and export. |
|
||||
|
||||
Algorithm evaluation
|
||||
--------------------
|
||||
|
||||
| Command | What it does |
|
||||
| ------- | ------------ |
|
||||
| @anchor tool_matcher `rtabmap-matcher` | Registers two images with the @ref rtabmap::RegistrationVis "visual" or @ref rtabmap::RegistrationIcp "ICP" pipeline and draws the correspondences. The direct way to compare feature detectors and matching parameters on a hard pair. |
|
||||
| @anchor tool_stereoeval `rtabmap-stereoEval` | Evaluates the stereo correspondence parameters against a ground truth disparity map (Middlebury format). |
|
||||
| @anchor tool_epipolar `rtabmap-epipolar_geometry` | Shows the epipolar geometry between two images. |
|
||||
| @anchor tool_extractobject `rtabmap-extractObject` | Extracts an object lying on a plane from a point cloud file. |
|
||||
| @anchor tool_vocabulary `rtabmap-vocabularyComparison` | Compares the nearest-neighbour strategies of the visual dictionary on a saved vocabulary. Built only with the OpenCV non-free module. |
|
||||
|
||||
The GUI application itself is `rtabmap`; everything the tools above do offline is
|
||||
also reachable from its menus.
|
||||
74
doxygen/version-switcher.js
Normal file
74
doxygen/version-switcher.js
Normal file
@@ -0,0 +1,74 @@
|
||||
/* Renders the version dropdown into Doxygen's #projectnumber element.
|
||||
*
|
||||
* Expects two things set up by the generated HTML header (see Doxyfile.in):
|
||||
* - window.RTABMAP_DOC_ROOT: Doxygen's $relpath^, i.e. the path from the
|
||||
* current page back to the root of *this* version's documentation.
|
||||
* - versions.js loaded from the API root, defining RTABMAP_DOC_VERSIONS.
|
||||
*
|
||||
* Everything is computed relative to those two, so the same files work for a
|
||||
* local preview (file:// or a static server) and for the published site,
|
||||
* whatever prefix it is served under.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function onReady(fn) {
|
||||
if (document.readyState !== 'loading') {
|
||||
fn();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', fn);
|
||||
}
|
||||
}
|
||||
|
||||
onReady(function () {
|
||||
var versions = window.RTABMAP_DOC_VERSIONS;
|
||||
var holder = document.getElementById('projectnumber');
|
||||
if (!versions || !versions.length || !holder) {
|
||||
return; // no version list deployed, or an unexpected Doxygen layout
|
||||
}
|
||||
|
||||
// Absolute path of this version's doc root, then of the API root above it.
|
||||
var anchor = document.createElement('a');
|
||||
anchor.href = window.RTABMAP_DOC_ROOT || './';
|
||||
var versionRoot = anchor.pathname.replace(/[^/]*$/, ''); // .../api/<version>/
|
||||
var apiRoot = versionRoot.replace(/[^/]+\/$/, ''); // .../api/
|
||||
var currentDir = versionRoot.slice(apiRoot.length).replace(/\/$/, '');
|
||||
var pageInVersion = window.location.pathname.slice(versionRoot.length);
|
||||
|
||||
var select = document.createElement('select');
|
||||
select.className = 'rtabmap-version-switcher';
|
||||
select.setAttribute('aria-label', 'Documentation version');
|
||||
|
||||
var matched = false;
|
||||
versions.forEach(function (entry) {
|
||||
var option = document.createElement('option');
|
||||
option.textContent = entry[0];
|
||||
option.value = entry[1];
|
||||
if (entry[1] === currentDir) {
|
||||
option.selected = true;
|
||||
matched = true;
|
||||
}
|
||||
select.appendChild(option);
|
||||
});
|
||||
|
||||
// Version not in the list (a local build, or a folder not published yet):
|
||||
// show it so the box never lies about which docs are open.
|
||||
if (!matched && currentDir) {
|
||||
var current = document.createElement('option');
|
||||
current.textContent = currentDir;
|
||||
current.value = currentDir;
|
||||
current.selected = true;
|
||||
select.insertBefore(current, select.firstChild);
|
||||
}
|
||||
|
||||
select.addEventListener('change', function () {
|
||||
// Keep the reader on the same page in the target version. Pages that
|
||||
// did not exist back then 404, which is the usual trade-off; the
|
||||
// alternative (always landing on index.html) is worse for deep links.
|
||||
window.location.href = apiRoot + select.value + '/' + pageInVersion;
|
||||
});
|
||||
|
||||
holder.textContent = '';
|
||||
holder.appendChild(select);
|
||||
});
|
||||
})();
|
||||
17
doxygen/versions.js
Normal file
17
doxygen/versions.js
Normal file
@@ -0,0 +1,17 @@
|
||||
/* Single source of truth for the API documentation version dropdown.
|
||||
*
|
||||
* This file is deployed ONCE at the root of the API docs (e.g. /api/versions.js),
|
||||
* NOT inside each version folder. Every published version loads this same file,
|
||||
* so adding an entry here makes the new release appear in the dropdown of all
|
||||
* previously published versions at once.
|
||||
*
|
||||
* To publish a new release, add ONE entry at the top and redeploy this file to
|
||||
* the API docs root. Format: ['<label>', '<folder name under the API root>'],
|
||||
* newest first. Folder names are resolved relative to the API root, so the same
|
||||
* file works for a local preview and for the published site, whatever prefix it
|
||||
* is served under.
|
||||
*/
|
||||
window.RTABMAP_DOC_VERSIONS = [
|
||||
['latest', 'latest'],
|
||||
['0.23.10', '0.23.10'],
|
||||
];
|
||||
Reference in New Issue
Block a user