layout | title | package | category | tags | |||
---|---|---|---|---|---|---|---|
reference |
oort_ros |
oort |
node |
|
2D laser-based SLAM package for Kuri. To map using Kuri's low-cost depth sensor with its limited range and resolution, Oort formulates the 2D SLAM problem with switchable constraints (with Ceres as the optimizer) using primarily libfrsm's scan matcher as a front end to generate graph links. The final map rendering process uses a combination of Cartographer's probability grids and OpenCV to generate maps appropriate for navigation. Beyond basic mapping, Oort supports:
- Looking for loop closure hints using Kuri's dock.
- Mapping over multiple sessions.
- CRD (create, read, delete) operations over pointers to 2D locations in the map.
Note that although Cartographer's scan matchers have been integrated and
can be selected in Oort's configuration (params.json
), they tend not to work as robustly in this setting; with limited resolution sensors, libfrsm's matching using detected line segments is often more reliable.
To support multi-session mapping, Oort stores its maps as Cap'n Proto (a relative format of protobuf) files. The Cap'n Proto definition is included at the end of this document for reference. Each new session needs an approximate start pose (refined internally by scan matching), and is added as a separate chain of graph nodes that may or may not have loop closure links to the existing graph.
To run the ROS node do:
rosrun oort oort_ros scan:=scan_reduced_mapping
To process bags online, in separate terminals, do:
roscore
rviz
rosbag play --clock yourbagname.bag
rosparam set use_sim_time true
rosrun oort oort_ros scan:=scan_reduced_mapping
Note that normally Kuri's main process assumes that it has sole control of oort_ros
, and so changing the map representation using Oort's services independently while it's running as part of Kuri's nodes is not advised. It's much better to run Oort while the rest of Kuri's processes have been shutdown with sudo service gizmo stop
.
# Launch Oort with robot at the dock.
rosservice call oort_slam/map/start "/home/account/map.capnp"
rosservice call oort_slam/graph_loc/create "dock"
# ... drive robot around to map.
# (optional) rosservice call oort_slam/graph_loc/create "waypoint"
# ... drive robot back to dock.
rosservice call oort_slam/map/notify_docked
rosservice call oort_slam/map/stop
# Start localizer (amcl) and wait till the below returns true.
rosservice call oort_slam/graph_loc/is_ready
# (optional) rosservice call oort_slam/graph_loc/create "waypoint"
The dock related calls are optional but highly suggested to make good maps.
rosservice call oort_slam/map/clear
rosservice call oort_slam/map/start "/home/account/new_map.capnp"
# ... same as above
# Launch Oort
rosservice call oort_slam/map/load "/home/account/new_map.capnp"
# Start localizer (amcl) and wait till the below returns true.
rosservice call oort_slam/graph_loc/is_ready
# (optional) rosservice call oort_slam/graph_loc/create "waypoint"
Docks are treated as locations that live under the namespace "dock". For on-boarding purposes, clients would use the following sequence:
rosservice call oort_slam/graph_loc/create dock # After docking for the very first time.
rosservice call oort_slam/map/notify_docked # For subsequent dockings, so that Oort knows to loop close.
The nominal sequence for creating new maps starts with calling map/start
with a path and then calling map/stop
.
rosservice call oort_slam/map/start "/home/account/map.capnp"
# ... drive robot around
rosservice call oort_slam/map/stop
Oort will save a map to the path given after processing the first map/start
, every 15 seconds, whenever it
processes a command to create/delete/clear locations, and when map/stop
is called.
To load a map:
rosservice call oort_slam/map/load "/home/account/map.capnp"
After a map/stop
or map/load
call, Oort will listen for transforms between the sensor and map frame to
support location services and will save a new map file to the path supplied (to either map/start
or map/load
)
whenever changes to locations are made using create/delete/clear calls.
NOTE: This means that loaded maps will be modified (overwritten with new versions) whenever the locations inside are modified.
First, load a map:
rosservice call oort_slam/map/load "/home/account/map.capnp"
And then call resume with a new path and a known pose:
rosservice call oort_slam/map/resume "new_map_loc: '/home/account/new_map.capnp'
pose:
x: 0.0
y: 0.0
theta: 0.0"
nav_msgs/OccupancyGrid Latched
Number of messages (laser scans, & start/stop/pause/etc requests) that's currently in Oort's queue on systems where sensor processing lags behind realtime.
Notification for when Oort write the map & metadata file to disk or just the
metadata file. Will contain either map
or meta
as values.
Position of laser on robot. Required once on startup.
Position of laser according to odometry. Required to be published as often as there are laser scans.
Position of the laser frame in the map frame but only when Oort is NOT mapping.
/odom
=> /map
, position of the odom frame in the map frame but only when Oort is mapping.
oort_msgs/SetString
IMPORTANT!!! This must be called once to tell Oort to start mapping, otherwise, it'll sit there and do nothing.
map_path
should be a/fully/qualified/absolute/path/map.capnp
and sets the path for map saving triggered by other service calls. An empty string will tell Oort to not save anything to disk.- Begins publishing maps to
map
, andodom => map
totf
. - Also used to resume after
map/pause
. - Triggers map saving every 15 seconds.
map/state
will returnmapping
after this takes effect.
oort_msgs/SetString
map_path
should be a/fully/qualified/absolute/path/map.capnp
.- Instead of calling
map/start
, call this to load a map made in a previous mapping session. - Has similar effects as
map/clear
in that it'll clear out any currently active mapping session (but it'll replace the current map with a loaded map). - Begins publishing maps to
map
, and listens forodom => map
fromtf
. map/state
will remainnot_mapping
.map/has_map
will return true.- If
map_path
is invalid (doesn't exist, fails md5 checksum, etc), the service call will fail.
oort_msgs/MapResume
- Assumes that client has
map/load()
ed a map. map_path
should be a/fully/qualified/absolute/path/map.capnp
to a file where the new map will be saved.- Pose2D should be pose of the sensor frame in the map frame.
- Instead of calling
map/start
, call this to resuming mapping on a previously created map at the given pose 2D. - Begins publishing maps to
map
, andodom => map
totf
. - Triggers map saving every 15 seconds.
map/state
will returnmapping
after this takes effect.map/has_map
will return true.
std_srvs/Empty
- Notifies Oort that the current position is the dock that it saw earlier.
- Links the closest dock node and the current node in the map.
- Should be used in conjunction with
graph_loc/create dock
.
std_srvs/Empty
- Can publish new maps to
map
if there are any queued, and will provide old maps asmap
is a latched topic. - Continues publishing the last known
odom => map
totf
. - Mapping can be resumed again by calling
map/resume_from_paused
ormap/start
. - Used when input laser scan & tf data are not reliable (e.g. when robot is picked up).
map/state
will returnpaused
after this takes effect.
std_srvs/Empty
- Used for resuming mapping after pausing. This call inserts node with a large covariance link in the graph so that the optimizer knows that the pose is uncertain due, presumably, to interruptions such as the robot being picked up while mapping was paused.
- Resumes publishing maps to
map
, andodom => map
totf
. - Re-triggers map saving every 15 seconds.
map/state
will returnmapping
after this takes effect.
std_srvs/Empty
- Publishes maps to
map
, andodom => map
totf
. - Does not publish to
tf
. - This call exists to allow future implementations of Oort to save maps to disk/close maps when stopped.
map/state
will returnnot_mapping
after this takes effect.- Triggers map saving.
- IMPORTANT: Blocks until Oort finishes processing all mapping inputs so far.
This means that
queue_size
needs to go to zero beforemap/stop
will return.
std_srvs/Empty
- Stops mapping & clears any map made and ALL known locations.
- To map again, clients have to start over with
map/start
. map/state
will returnnot_mapping
after this takes effect.
oort_msgs/GetString
- Current mapping state. Is either
mapping
,paused
, ornot_mapping
withnot_mapping
being the default value on startup.
oort_msgs/GetMap
- Returns most updated occupancy grid map.
- Will wait up to 10 seconds for a map if one is not available. When this call
time out, it'll return false, resulting in a
ServiceException
.
oort_msgs/GetString
- Returns the current path used for map saving.
oort_msgs/GetBool
- True whenever at least one start_map/stop_map pair has been called.
- Republish currently loaded map.
oort_msgs/GetUInt
- Number of nodes in current map.
A location is a 2D (x, y, theta) pose stored by Oort in the reference frame of the closest graph node while mapping. Storing 2D poses as relative poses in the graph in this way makes sure that as SLAM warps the map during updates, 2D poses stored will move with the map rather than left behind (which is what would happen if they instead were anchored to a global reference frame).
Here, each location has a namespace which it gets created under so that
different clients can create namespaces for their own set of locations. To use
this API, clients would first ask Oort to create()
a location at the current
map pose and then clients would then call locate()
on the UUID returned to
get the current estimate of that location.
oort_msgs/LocCreate
- Records the current position in the graph.
- ID can be used to look up location's (x, y, theta) pose.
- Will fail (throws ROS Service Exception) if
graph_loc/is_ready
is false. - Triggers map saving.
- IMPORTANT If Oort is backed up (
queue_size
topic has values larger than 1), the returned UUID can only be used for pose lookups once Oort catches up to the time the location creation request was made.
oort_msgs/LocCreatePoints
- Given a set of points in the map's coordinate frame, each with a possibly different namespace, each with its own ID, create a location for each point such that all the resulting locations are attached to the same graph node. This selects the graph node to attach to by looking for the node closest to the geometry center of the given points array.
- The given IDs can be used to look up each location's (x, y, theta) pose.
oort_msgs/GetBool
- True if
graph_loc/create
is currently supported. - Clients should check this after calling
map/stop
ormap/load
before callinggraph_loc/create
. This is because while SLAM is not running, Oort does not have localization information to allow it to know where on the SLAM graph a location should be attached. To make locations work, Oort listens to transforms between thehokuyo_laser_link
andmap
frame to get the current pose of the robot. As Oort depends on an outside source for these transforms, waiting untilis_ready
returns true ensure that when clients callgraph_loc/create
, Oort will be able to create the location.
oort_msgs/LocNamespace
- Returns array of UUIDs for locations stored under this namespace and the namespace each location is defined under.
If use as prefix is True, list ids in all namespaces with
nspace
as prefix.
oort_msgs/GetStrings
- Returns all namespaces known to Oort.
oort_msgs/LocDelete
- Remove a place from the graph.
- Triggers map saving.
- Returns whether it was successful.
oort_msgs/Place
- Gets the pose for given place ID.
- Returns the pose as determined from our graph
- IMPORTANT This call will fail if Oort doesn't know about the location OR if Oort's sensor processing has not caught up to populate the location with a valid relative pose estimate.
oort_msgs/LocNameSpace
- Remove all places stored. If use as prefix is True, will additionally remove all namespaces with
nspace
as prefix. - Triggers map saving.
Included below is the params.json
file included with Oort, but with
explanatory comments (these aren't provided in the actual file itself as json
doesn't quite support comments).
{
# Robot's TF frame, used to relate to odom frame.
"base_frame": "base_footprint",
# Odom's TF frame.
"odom_frame": "odom",
# Frame of the depth sensor/laser scanner.
"sensor_frame": "hokuyo_laser_link",
# Frame of the map.
"map_frame": "map",
# Amount of linear translation of base_frame in odom_frame (in meters)
# before creating a new graph node.
"linear_update": 0.2,
# Amount of angular rotation (in radians) of base_frame in odom_frame (in
# meters) before creating a new graph node.
"angular_update": 0.6,
# Point clouds with less points than this will not be used by Oort for
# mapping.
"min_pointcloud_size": 3,
# Scalar to apply to switchable constraint prior's error.
"switchable_constraint_scale": 2,
# Scale on prior constraint for switching variable. By default, switching
# variables are biased to to turn on so larger values make turning off loop
# closure constraints more expensive, and thus less likely.
"switchable_constraint_scale": 2,
# Params controlling what published rendered maps look like.
"output_map": {
# Params controlling rendering laser hits to a probability grid.
# Mostly from https://google-cartographer.readthedocs.io/
# en/latest/configuration.html#cartographer-mapping-2d-proto-rangedatainserteroptions
"probability_grid": {
# Resolution of map to generate in meters.
"resolution": 0.05,
# If ‘false’, free space will not change the probabilities in the
# occupancy grid.
"insert_free_space": true,
# Probability change for a hit (this will be converted to odds and
# therefore must be greater than 0.5).
"hit_probability": 0.55,
# Probability change for a miss (this will be converted to odds and
# therefore must be less than 0.5).
"miss_probability": 0.49,
# Whether to interpret inf readings as evidence of freespace.
"clear_infs": true,
# Distance in meters to clear when processing inf readings.
"inf_clear_dist": 0.5
},
# Params controlling thresholding of generated probability grid to
# create ROS OccupancyGrid
"threshold":
{
# Max occupancy probability for a cell to be labeled freespace.
"max_free": 48,
# Min occupancy probability for a cell to be labeled occupied
"min_occupied": 55
},
# Footprint of robot in meters to use for clearing OccupancyGrid in
# spots where the robot traveled. Helpful for keeping doorways &
# hallways navigable.
"robot_radius": 0.15,
# Strategy to use for clearing speckle noise in final rendered map.
# either "median" or "morphological".
"speckle_strategy": "median",
# In the morphological strategy, Oort splits the rendered map into a
# map of freespace pixels, and another map of occupied pixels and then
# it runs morphological iterations on each separately. The two maps
# are then layered, with freespace first and occupied cells last.
"morphological":
{
# Number of morphological iterations to run on freespace layer.
"free_space_morpho_iterations": 1,
# Number of morphological iterations to run on occupied layer.
"occupied_morpho_iterations": 1
},
# In the median strategy, Oort runs a median filter.
"median":
{
# Size of kernel in pixels.
"kernel_size": 3,
# Number of iterations of median filtering to run.
"iter": 2
}
},
# Params controlling how links between consecutive poses are formed in
# graph.
"incremental_graph_builder":
{
# Covariance to use in the x, y, and theta direction when falling
$ back to using wheel odometry.
"odom_cov_xx": 0.02,
"odom_cov_yy": 0.02,
"odom_cov_tt": 0.01,
# Which scanmatcher to use.
# Either "frsm_matcher" or "cartographer_matcher".
"scanmatcher": "frsm_matcher",
# Params when using libfrsm's scanmatcher. This scanmatcher tends
# to perform better when the pointcloud is less dense as it's able
# to connect points into line segments.
"frsm_matcher":
{
# Whether to show opencv debug viewer for the scanmatcher
# (need to run Oort in environment with X11 available to
# work).
"debug": false,
# Params for the debug viewer.
"debug_viewer":
{
# How much to scale up the debug viewer.
"viewer_scale": 4,
# Length of pose history to show.
"history_length": 30,
# Resolution of debug map.
"resolution": 0.025,
# Params for probability grid used by debug viewer (see
# "probability_grid" above).
"insert_free_space": false,
"hit_probability": 0.55,
"miss_probability": 0.49
},
# Unused.
"meters_per_pixel": 0.01,
# Unused.
"theta_resolution": 0.01,
# Max number of scans to keep as history in scanmatcher.
# Note: This is fairly important as it controls how much
# information the scanmatcher's incremental mini-map
# contains.
"max_num_scans": 20,
# Params from libfrsm (see
# https://github.com/abachrach/frsm/blob/master/src/libfrsm/ScanMatcher.hpp#L64)
"initial_search_range_xy": 0.01,
"initial_search_range_theta": 0.01,
"max_search_range_xy": 0.1,
"max_search_range_theta": 0.1,
"add_scan_hit_thresh": 0.95,
"stationary_motion_model": false,
# Standard deviation of motion model estimate. Below .1 the
# prior isn't used, and otherwise specifies the standard
# deviation for a Gaussian around the odometry estimate.
# This controls how closely scanmatching will try to keep
# to the odometry estimate.
"std_mot_model": 0.4,
# Amount to scale the covariance returned by the
# scanmatcher.
"scanmatch_cov_scale": 5,
# Amount to scale the covariance by when mapping is paused
# and then "resume from paused".
"inflate_scanmatch_cov_scale": 40,
# Amount to scale covariance by when a new mapping session
# is initiated. This type of resume mapping assumes that
# mapping was stopped and then restarted with a guess of an
# initial location.
"resume_scanmatch_cov_scale": 50,
# Unused
"sm_dist_accept_threshold": 0.06,
# Unused.
"sm_angle_accept_threshold": 0.07,
# Unused.
"min_sm_accept_score": 0.45,
# Params controlling whether we should trust scanmatching
# over initial odometry estimate.
"match_filtering":
{
# Unused.
"dist_accept_threshold": 0.06,
# Params below apply to difference in pose resulting
# from taking the scanmatcher's estimated pose and
# subtracting the estimated pose according to odometry.
# Mininum value of x translation to accept in meters.
# Used to keep from accepting results where the
# scanmatcher says that the robot has moved backwards.
"x_min_accept": -0.05,
# Maximum value of x translation to accept in meters.
# Used to filter out outlier scanmatcher estimates.
"x_max_accept": 0.12,
# Maximum absolute value of y translation in meters.
# As our robot doesn't translate sideways, this keeps
# Oort from accepting crazy scanmatching results.
"y_abs_accept": 0.11,
# Maximum absolute value rotation difference in
# radians to accept.
"theta_abs_accept": 0.07,
# Minimum score for scan matching result to be
# accepted.
"min_accept_score": 0.45
}
},
# Params when using cartographer's RealTimeCorrelativeScanMatcher.
# Unused by default.
"cartographer_matcher":
{
"debug": false,
"debug_viewer_scale": 4,
"history_length": 30,
"resolution": 0.05,
"insert_free_space": true,
"hit_probability": 0.55,
"miss_probability": 0.49,
"linear_search_window": 0.1,
"angular_search_window": 0.1,
"translation_delta_cost_weight": 0.1,
"rotation_delta_cost_weight": 0.1,
"use_ceres_refinement": true,
"ceres_scan_matcher":
{
"resolution": 0.025,
"insert_free_space": false,
"hit_probability": 0.55,
"miss_probability": 0.49,
"occupied_space_cost_functor_weight": 20.0,
"previous_pose_translation_delta_cost_functor_weight": 1.0,
"initial_pose_estimate_rotation_delta_cost_functor_weight": 100.0,
"covariance_scale": 0.50,
"ceres_solver_options":
{
"use_nonmonotonic_steps": true,
"max_num_iterations": 50,
"num_threads": 1
}
},
"scanmatch_cov_scale": 10,
"inflate_scanmatch_cov_scale": 40,
"resume_scanmatch_cov_scale": 50,
"sm_dist_accept_threshold": 0.05,
"sm_dist_accept_x_min": -0.12,
"sm_dist_accept_x_max": 0.05,
"sm_dist_accept_threshold_y": 0.11,
"sm_angle_accept_threshold": 0.10,
# Minimum hit percent a scanmatch result has to have for it
# to be accepted (else Oort uses the odometry estimate).
"min_sm_accept_score": 0.0,
"use_second_stage_matcher": false,
"second_stage_matcher": {
"ceres_scan_matcher":
{
"resolution": 0.01,
"insert_free_space": false,
"hit_probability": 0.55,
"miss_probability": 0.49,
"occupied_space_cost_functor_weight": 20.0,
"previous_pose_translation_delta_cost_functor_weight": 20.0,
"initial_pose_estimate_rotation_delta_cost_functor_weight": 50.0,
"covariance_scale": 6.00,
"ceres_solver_options":
{
"use_nonmonotonic_steps": true,
"max_num_iterations": 50,
"num_threads": 1
}
},
# Max distance from odometry estimate scanmatching result
# can be before Oort falls back to using just the odometry
# estimate.
"sm_dist_accept_threshold": 0.051,
# Max angle from odometry estimate.
"sm_angle_accept_threshold": 0.07,
"min_sm_accept_score": 0.0
}
}
},
# Params controlling loop closure searches.
"loop_closure_graph_builder":
{
"candidate_search":
{
# Number of k nearest neighbor candidates to search. Larger
# values increase CPU usage, but also increase number of
# loop closure matches.
"knearest_neighbors": 5,
# When considering a node's pointcloud as a loop closure
# candidate, how large of a neighborhood (in number of
# nodes on its chain) to use to build the submap for that
# node.
# And if a node is selected as a candidate,
# nodes within max(ceil(submap_neighborhood_size/2), 1)
# hops of that node will not be considered as loop closure
# candidates. This is as these nodes' scans are accounted
# for in the original node's submap.
"submap_neighborhood_size": 15,
# Given that nodes are given consecutive integer IDs, this
# parameter control how close a candidate node's ID can be
# before it's considered.
"closest_allowed_id_threshold_large": 30,
# If the k-nearest candidate search yields zero nodes
# using "closest_allowed_id_threshold_large", Oort runs
# a less restrictive search using this param.
"closest_allowed_id_threshold_small": 5,
# Unused.
"max_dist_allowed_threshold": 4.0
},
# Which scanmatcher to use to look for loop closures.
# Either "frsm_matcher" or "branch_and_bound_matcher".
"scanmatcher": "frsm_matcher",
"frsm_matcher":
{
# Minimum number of neighbors a node can have before it's
# rejected from being a candidate.
"submap_min_neighborhood_size": 3,
# Hit percent threshold for initial rough match.
"rough_match_hitpct_threshold": 0.3,
# Hit percent threshold for fine scale matching.
"fine_match_hitpct_threshold": 0.6,
"match_filter":
{
# If the node ID distance (difference in ID between
# candidate node and query node) is less than
# closest_allowed_id_threshold_large use thresholds
# below to decide whether to accept the loop closure
# match result. And for the params below, Oort
# defines the match difference as the difference
# between the candidate's pose according to the
# current estimate, and the pose the loop closure
# scanmatch predicts.
# If a match's hit percentage is at least
# "close_node_hitpct_threshold", and its match
# difference is distance less than
# "close_node_max_dist", and angle is less than
# "match_max_ang_diff", then accept the match.
"close_node_hitpct_threshold": 0.8,
"close_node_max_dist": 0.5,
"match_max_ang_diff": 2.09,
# If the node ID distance is equal or greater than
# "closest_allowed_id_threshold_large", use the
# params bellow to decide whether to accept
# candidate matches.
# If the hit percentage is greater than
# "match_hitpct_threshold", and the match
# difference is less than "match_max_dist", then we
# accept the match.
"match_hitpct_threshold": 0.7,
"match_max_dist": 0.5,
# If the hit percentage is greater than
# "match_hitpct_threshold_high", and the match
# difference is less than "match_max_dist_high",
# then we also accept the match.
"match_hitpct_threshold_high": 0.80,
"match_max_dist_high": 2.0
# Alternatively, if the candidate's estimated
# heading before scanmatching is in the opposite
# direction (controlled by
# "opposite_dir_threshold"), then use
# "match_hitpct_threshold_opposite_dir" and
# "match_hitpct_threshold_opposite_dir".
"opposite_dir_threshold": 1.74,
"match_hitpct_threshold_opposite_dir": 0.67,
"match_hitpct_threshold_high_opposite_dir": 0.75,
}
},
"branch_and_bound_matcher":
{
"debug": false,
"debug_draw_original_cloud": false,
"debug_only_show_matched": true,
"submap_min_neighborhood_size": 3,
"global_localization_min_score": 0.21,
"min_score": 0.55,
"probability_grid":
{
"resolution": 0.05,
"insert_free_space": false,
"hit_probability": 0.55,
"miss_probability": 0.49
},
"adaptive_voxel_filter":
{
"max_length": 0.9,
"min_num_points": 150,
"max_range": 50.0
},
"fast_correlative_scan_matcher" :
{
"linear_search_window": 7.0,
"angular_search_window": 0.524,
"branch_and_bound_depth": 7
},
"ceres_scan_matcher":
{
"occupied_space_cost_functor_weight": 20.0,
"previous_pose_translation_delta_cost_functor_weight": 10.0,
"initial_pose_estimate_rotation_delta_cost_functor_weight": 1.0,
"covariance_scale": 0.125,
"ceres_solver_options":
{
"use_nonmonotonic_steps": true,
"max_num_iterations": 10,
"num_threads": 1
}
},
"match_filter":
{
"close_node_hitpct_threshold": 0.0,
"close_node_max_dist": 0.5,
"match_max_ang_diff": 2.09,
"match_hitpct_threshold": 0.0,
"match_hitpct_threshold_high": 0.0,
"opposite_dir_threshold": 1.74,
"match_hitpct_threshold_opposite_dir": 0.0,
"match_hitpct_threshold_high_opposite_dir": 0.0,
"match_max_dist": 0.5,
"match_max_dist_high": 2.0
}
}
}
# Params for loop closure matching when Oort gets a hint that the robot
# has returned to its dock (using the service call map/notify_docked).
"dock_graph_builder":
{
# Number of nodes at the beginning of the graph to use to create a
# reference submap for matching.
"dock_submap_size": 31,
# Number of nodes at the end of the graph to use to create a submap
# to match to the dock reference submap.
# IMPORTANT:
# Should be smaller than dock_submap_size to avoid scenes with lots
# of aliasing (like hallways).
"dock_match_submap_size": 7,
# Size of contacts on the dock. We need this as the robot will
# report that it's on the dock whenever it touches any part of the
# contact.
"dock_contacts_size": 0.15,
# Max distance we assume user will move dock between when we see
# it.
"dock_max_translation": 0.25,
# Max distance we expect for loop closure constraints to the same
# dock.
"dock_match_max_dist": 0.4,
# Minimum hit percent (overlap) before accepting a dock closure
# hypothesis.
"dock_match_min_hit_pct": 0.6
}
}
@0xfb2c8b83b3af128b;
using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("oort::capnp");
struct Point2d @0x86eb3abaf30b3dab {
x @0 :Float64;
# X coordinate. Can be nan or +inf.
y @1 :Float64;
# Y coordinate. If x is nan or +inf, stores the angle in radians of the
# original sensor beam.
z @2 :Float64 = 1.0;
# Stores a homogeneous coordinate 1 for a valid point, or nan which
# represents either:
# 1. A point with normal x & y but is less than than min range or greater
# or equal to max range in the original laser scan message.
# 2. A point with a nan or +inf range.
}
struct Pose2d @0xef9347c8a4ec3f3b {
x @0 :Float64;
y @1 :Float64;
t @2: Float64;
}
struct Matrix3d @0x8bf10fa249db2c04 {
v @0 :List(Float64);
}
struct Pose2dCov @0x8ca5f336b2fcc26d {
pose @0 :Pose2d;
cov @1 :Matrix3d;
}
struct Node2d @0xe51b2aac4150d637 {
id @0 :UInt64;
pose @1 :Pose2d;
}
struct Sensor2dConstraint @0x9e8d9650484abd02 {
time @0 :Float64;
# ID of node this constraint was calculated at (has no implications on the
# direction of transforms).
id @1 :UInt64;
pose @2 :Pose2dCov;
type @3 :Type;
enum Type {
laser @0;
odom @1;
}
}
struct Edge2d @0xdcbcc7844d8936a0 {
id @0 :UInt64;
i @1 :UInt64;
j @2 :UInt64;
# i_T_j
props @3 :Sensor2dConstraint;
}
struct Edge2dSwitchable @0xaafd6e34d5cc28cf {
id @0 :UInt64;
i @1 :UInt64;
j @2 :UInt64;
# i_T_j
props @3 :Sensor2dConstraint;
switch @4 :Float64;
}
struct Graph2d @0xf0829dca2aeac818 {
nodes @0 :List(Node2d);
edges @1 :List(Edge2d);
edgesSwitch @2 :List(Edge2dSwitchable);
}
struct PointCloud2d @0xf0211b9259d37f5f {
# ID of node this pointcloud was captured at.
id @0 :UInt64;
points @1 :List(Point2d);
time @2 :Float64;
}
struct Location @0xd16e2058832c0e78 {
namespace @0 :Text;
nodeid @1 :UInt64;
nTsensor @2 :Pose2d;
uuid @3 :Text;
}
struct IncrGraphResult @0x815530c5e1acb0df {
# ID of node this result was produced at.
id @0 :UInt64;
target @1 :UInt64;
# posecov: Pose in scan matcher's global frame
smTid @2 :Pose2dCov;
# pose_delta: pose between adjacent time steps.
targetTid @3 :Pose2d;
hitPct @4 :Float64;
}
struct LoopGraphResult @0xd836671ff6e61ea9 {
# ID of node this result was produced at.
id @0 :UInt64;
target @1 :UInt64;
idTtarget @2 :Pose2dCov;
hitPct @3 :Float64;
}
struct SidecarDb @0xad039fab7cfb237c {
clouds @0 :List(PointCloud2d);
incrs @1 :List(IncrGraphResult);
loops @2 :List(LoopGraphResult);
}
struct Map2dMetadata @0xa74d65c2b1e77767 {
locations @0 :List(Location);
}
struct Map2d @0xd9ca05175c083d2b {
graph @0 :Graph2d;
sidecardb @1 :SidecarDb;
}