Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ colcon build --packages-select rko_lio rko_slam
Two entrypoints: `slam.launch.py` (`mode:=online|offline`) and `align.launch.py`. `-s` lists every parameter
with its documentation, and anything you leave unset keeps the node's own default.

Online, next to a running rko_lio, consuming its deskewed scan. The base frame is autodetected when you leave it
unset:
Online, next to a running rko_lio, consuming its deskewed scan:

```bash
ros2 launch rko_slam slam.launch.py lidar_topic:=/rko_lio/deskewed_scan rviz:=true
Expand All @@ -74,7 +73,8 @@ ros2 launch rko_slam slam.launch.py lidar_topic:=/rko_lio/deskewed_scan rviz:=tr
Offline, self-draining a bag, with the odometry from the bag's own `/tf`, and writing the run to disk:

```bash
ros2 launch rko_slam slam.launch.py mode:=offline bag_path:=/data/my_bag dump_results:=true
ros2 launch rko_slam slam.launch.py mode:=offline bag_path:=/data/my_bag \
lidar_topic:=/rko_lio/deskewed_scan dump_results:=true
```

Multi-session alignment of the run directories those runs wrote:
Expand All @@ -84,7 +84,7 @@ ros2 launch rko_slam align.launch.py run_dirs:="[results/run_1, results/run_2]"
```

Running with another odometry, spawning rko_lio from the same launch file, taking the odometry from a TUM file,
what gets autodetected, what a run writes to disk, every parameter and what it does, and how the system works
what a run writes to disk, every parameter and what it does, and how the system works
are all in the [docs](https://prbonn.github.io/rko_slam/).

## Acknowledgments and Citation
Expand Down
7 changes: 4 additions & 3 deletions config/ros_default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
# ros2 launch rko_slam slam.launch.py config_file:=<path_to>/ros_default.yaml
# #############################################################################
# --- input ---
# lidar_topic: <autodetected when exactly one PointCloud2 topic is published>
# base_frame: <base_link, base_footprint or base if the TF tree has one, else the scan's frame>
# odom_frame: odom
# lidar_topic: <required; with odometry:=true, rko_lio's deskewed scan>
# base_frame: <the scan's frame; with odometry:=true, rko_lio's base_frame>
# odom_frame: odom # with odometry:=true, rko_lio's odom_frame
# map_frame: map
# invert_map_tf: false # with odometry:=true, rko_lio's invert_odom_tf
# deskew: false # leave false when the odometry already publishes a deskewed cloud and you consume that
# lidar_timestamps.multiplier_to_seconds: 0.0
# lidar_timestamps.force_absolute: false
Expand Down
71 changes: 44 additions & 27 deletions docs/pages/build_and_run.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,22 +46,18 @@ ros2 launch rko_slam slam.launch.py -s

### Online

Next to a running rko_lio, give it rko_lio's deskewed scan. The base frame is found for you (see
[below](#what-gets-autodetected)):
Next to a running rko_lio, give rko_slam rko_lio's deskewed scan:

```bash
ros2 launch rko_slam slam.launch.py lidar_topic:=/rko_lio/deskewed_scan
```

Add `rviz:=true` to open RViz with the default view: sub-maps, keypose graph and closures.

The same launch file can spawn the rko_lio front-end for you. rko_lio's `publish_deskewed_scan` is forced on and
rko_slam consumes that topic. Give `base_frame` here, since rko_lio is not running yet when it would be detected:
The same launch file can start rko_lio for you, see [Starting rko_lio](#starting-rko_lio):

```bash
ros2 launch rko_slam slam.launch.py odometry:=true base_frame:=base_link \
rko_lio_config_file:=my_rko_lio.yaml \
rko_lio_lidar_topic:=/os_cloud_node/points rko_lio_imu_topic:=/os_cloud_node/imu
ros2 launch rko_slam slam.launch.py odometry:=true
```

### The odometry
Expand All @@ -77,31 +73,35 @@ With another odometry the scans are usually raw, so set `deskew:=true` and rko_s
the same TF:

```bash
ros2 launch rko_slam slam.launch.py deskew:=true
ros2 launch rko_slam slam.launch.py lidar_topic:=/points base_frame:=base_link deskew:=true
```

If the odometry publishes under other names, give `lidar_topic`, `base_frame` and `odom_frame` explicitly.
If its odometry frame is not `odom`, give `odom_frame` as well. If it publishes `odom_frame` as the TF child of
`base_frame`, set `invert_map_tf:=true`, and rko_slam publishes `map_frame` as the child of `odom_frame`.

### Offline

Offline, the node self-drains a bag (every scan is processed faster than frame-rate if possible). The odometry
comes from the bag's own `/tf`:

```bash
ros2 launch rko_slam slam.launch.py mode:=offline bag_path:=/data/my_bag
ros2 launch rko_slam slam.launch.py mode:=offline bag_path:=/data/my_bag \
lidar_topic:=/rko_lio/deskewed_scan
```

If the odometry you want is not in the bag, `odom_tum_path` takes it from a TUM trajectory file instead, see
[Frames](#frames):

```bash
ros2 launch rko_slam slam.launch.py mode:=offline bag_path:=/data/my_bag odom_tum_path:=/data/odometry_tum.txt
ros2 launch rko_slam slam.launch.py mode:=offline bag_path:=/data/my_bag \
lidar_topic:=/points base_frame:=base_link deskew:=true odom_tum_path:=/data/odometry_tum.txt
```

Nothing is written unless you ask for it, see [Outputs](#outputs):

```bash
ros2 launch rko_slam slam.launch.py mode:=offline bag_path:=/data/my_bag \
lidar_topic:=/rko_lio/deskewed_scan \
dump_results:=true results_dir:=results run_name:=my_run
```

Expand All @@ -115,22 +115,39 @@ dumped by default):
ros2 launch rko_slam align.launch.py run_dirs:="[results/run_1, results/run_2]"
```

### What gets autodetected
### Starting rko_lio

`lidar_topic` is required and has no default, and `base_frame` is optional. With `autodetect:=true`, the
default, the launch file fills in whichever of the two you left unset:
`odometry:=true` starts an rko_lio online node next to rko_slam. With `rko_lio_config_file`, rko_lio runs on that
file as it is. rko_slam reads rko_lio's deskewed scan unless you give it a `lidar_topic` of your own, so the file
has to set `publish_deskewed_scan: true` and the launch stops if it does not. Without the file, rko_lio
configures itself with its own
[autodetection](https://prbonn.github.io/rko_lio/pages/ros.html#launch-parameter-autodetection), waiting for the
topics and TF to show up, and publishes its deskewed scan. For anything else, run rko_lio with its own launch
file.

- `lidar_topic` is the one `sensor_msgs/PointCloud2` topic there is. If several exist, the launch stops and lists
them so you can pick. Next to a running rko_lio there are at least two, the raw scan and the deskewed one, so
give `lidar_topic` in that case.
- `base_frame` is the first of `base_link`, `base_footprint`, `base` found in the TF tree, otherwise the scan's
own `frame_id`. Either way the TF tree must connect it to the scan's frame, or the launch stops and says so.
Whatever you leave unset on rko_slam is taken from how rko_lio is configured:

Online this means waiting for the topics and TF to show up, `autodetect_timeout` (10 s) long. Offline it is read
from the bag. Anything you did give is used as is, and with both given nothing is detected at all;
`autodetect:=false` turns it off. With `odometry:=true`, `lidar_topic` is set to rko_lio's deskewed scan topic
and not searched for, but the base frame detection needs a message on that topic and rko_lio only starts
afterwards, so give `base_frame` too.
| rko_slam | rko_lio |
|---|---|
| `lidar_topic`, the scans rko_slam subscribes to | `deskewed_scan_topic`, the scans rko_lio publishes |
| `base_frame` | `base_frame` |
| `odom_frame` | `odom_frame` |
| `invert_map_tf` | `invert_odom_tf` |

rko_lio's own `lidar_topic`, the raw scans it subscribes to, is not in that table and rko_slam never sets it:
rko_lio takes it from `rko_lio_config_file` or finds it itself, as it does its IMU topic.

`lidar_topic` always resolves: from `rko_lio_config_file` if it defines `deskewed_scan_topic`, otherwise from
rko_lio's default `rko_lio/deskewed_scan`. Taking it also turns `deskew` off, since that cloud is already
deskewed. The rest are taken only if rko_lio has a value for them, which means what `rko_lio_config_file`
defines, or what rko_lio's autodetection found when there is no config file. A parameter that neither you nor
rko_lio defines keeps rko_slam's own default: `odom_frame` is `odom`, `invert_map_tf` is `false`, and
`base_frame` unset means the scan's frame.

The two frames also go the other way: set `base_frame` or `odom_frame` on rko_slam and rko_lio is started with
them, so both nodes work in the same body and publish the odometry in the same frame. They override
`rko_lio_config_file` if it defines them too, the same way `use_sim_time` does, since what you set on rko_slam is
for this run and rko_lio's file is not.

## Frames

Expand All @@ -152,15 +169,15 @@ Subscribed:

| Topic / transform | What |
|---|---|
| `lidar_topic` (`sensor_msgs/PointCloud2`) | the scans, deskewed unless `deskew:=true` |
| `lidar_topic` (`sensor_msgs/PointCloud2`) | the scans, taken as already deskewed unless you set `deskew:=true` |
| `odom_frame <- base_frame` on TF | the odometry, looked up at each scan's timestamp |
| `base_frame <- the scan's frame` on TF | static, read once on the first scan to bring every scan into `base_frame` |

Published:

| Topic / transform | What |
|---|---|
| `map_frame <- odom_frame` on TF | the correction; `map_frame <- base_frame` is then the SLAM estimate |
| `map_frame <- odom_frame` on TF | the correction, flipped by `invert_map_tf`; `map_frame <- base_frame` is then the SLAM estimate |
| `rko_slam/sub_maps` (`PointCloud2`) | each closed sub-map, with a `sub_map_<i>` TF chain (`publish_sub_maps:=true`) |
| `rko_slam/keypose_graph` (`MarkerArray`) | the keyposes with their odometry and closure edges (`publish_keypose_graph:=true`) |
| `rko_slam/closure_maps` (`PointCloud2`) | each accepted closure pair as a two-tone cloud (`publish_closure_maps:=true`) |
Expand All @@ -177,7 +194,7 @@ Nothing is published for display unless you ask for it. Three publishers, each o
- `publish_closure_maps:=true`, each accepted closure as the two sub-maps it matched, one colour each, on
`rko_slam/closure_maps`, so you can see what got matched to what.

`rviz:=true` turns all three on and opens RViz with the default view, `config/default.rviz`, patched with your
`rviz:=true` turns all three on and opens RViz with the default view, `config/default.rviz`, customised with your
frames. With `odometry:=true` it also shows rko_lio's local map and deskewed scan. Pass your own config with
`rviz_config_file:=` and it is used unchanged, with nothing forced on.

Expand Down
70 changes: 39 additions & 31 deletions docs/pages/configuration.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Configuration

Every parameter is a launch argument and a node parameter by the same name. Set it on the command line as
`name:=value`, or put any number of them in a YAML file and pass that with `config_file:=`. Command line values
override the file, and anything you leave unset keeps the default listed here.
Every parameter is a launch argument. Set it on the command line as `name:=value`, or put any number of them in
a YAML file and pass that with `config_file:=`. Command line values override the file, and anything you leave
unset keeps the default listed here.

```bash
ros2 launch rko_slam slam.launch.py -s # the same list, with defaults, from the launch file itself
Expand All @@ -11,10 +11,9 @@ ros2 launch rko_slam slam.launch.py -s # the same list, with defaults, from th
[`config/ros_default.yaml`](https://github.com/PRBonn/rko_slam/blob/master/config/ros_default.yaml) has every
parameter with its default, commented out, as a file to start from.

One of them is required and has no default, `lidar_topic`. It is
[autodetected](build_and_run.md#what-gets-autodetected) when you leave it unset, and so is `base_frame`.
Everything else has a default that works as is, and the ones worth changing first are `splitting_distance` and
`overlap_threshold`.
One of them is required and has no default, `lidar_topic`; with `odometry:=true` it and `base_frame` default
to rko_lio's, see [Starting rko_lio](build_and_run.md#starting-rko_lio). Everything else has a default that
works as is, and the ones worth changing first are `splitting_distance` and `overlap_threshold`.

## Mode

Expand All @@ -37,33 +36,40 @@ Everything else has a default that works as is, and the ones worth changing firs

## Input

- **lidar_topic** (required, autodetected)
- **lidar_topic** (required)

The `PointCloud2` topic with the scans. Deskewed scans unless you set `deskew`. With `odometry:=true` the launch
file fills in `/rko_lio/deskewed_scan`.
The `PointCloud2` topic with the scans, taken as already deskewed unless you set `deskew`. With
`odometry:=true` it defaults to rko_lio's deskewed scan.

- **base_frame** (autodetected)
- **base_frame** (optional)

The frame rko_slam works in, usually `base_link`, see [Frames](build_and_run.md#frames).
The frame rko_slam works in, usually `base_link`, see [Frames](build_and_run.md#frames). With
`odometry:=true` it is shared with rko_lio: set it and rko_lio is started with it, leave it and rko_lio's is
used. Unset with no rko_lio to take it from, it is the scan's own frame.

- **odom_frame** (default `odom`), **map_frame** (default `map`)

The odometry's frame and the frame rko_slam publishes, see [Frames](build_and_run.md#frames).
The odometry's frame and the frame rko_slam publishes, see [Frames](build_and_run.md#frames):
rko_slam broadcasts `map_frame <- odom_frame`, inverted with `invert_map_tf`. With `odometry:=true`,
`odom_frame` is shared with rko_lio the same way `base_frame` is.

- **autodetect** (`bool`, default `true`), **autodetect_timeout** (`float`, default `10.0`)
- **invert_map_tf** (`bool`, default `false`)

Fill in `lidar_topic` and `base_frame` when you did not give them, from the running graph or from the bag. The
rules are under [What gets autodetected](build_and_run.md#what-gets-autodetected). The timeout is how long to
wait for the topics and TF tree this needs, online only.
Publish `map_frame` as the TF child of `odom_frame`, with the inverted transform. Set it when the odometry
publishes `odom_frame` as the child of `base_frame`, as rko_lio does with `invert_odom_tf`. With `odometry:=true`
it defaults to rko_lio's `invert_odom_tf`.

- **deskew** (`bool`, default `false`)

Deskew the scan in rko_slam, from its per-point timestamps and the odometry TF. Leave it off when the odometry
already publishes a deskewed cloud and you consume that, which is the case with `/rko_lio/deskewed_scan`.
already publishes a deskewed cloud and you consume that, which is the case with `/rko_lio/deskewed_scan`. When
the launch file picks that topic for you, under `odometry:=true` with no `lidar_topic` of your own, it turns
this off with it.

- **tf_lookup_timeout_ms** (`int`, online only, default `80`)

How long an `odom_frame <- base_frame` TF lookup blocks before the scan is dropped.
How long a TF lookup blocks before the scan is dropped, both the odometry and the static
`base_frame <- the scan's frame`.

- **lidar_timestamps.multiplier_to_seconds** (`float`, default `0.0`), **lidar_timestamps.force_absolute**
(`bool`, default `false`), **lidar_timestamps.force_relative** (`bool`, default `false`)
Expand Down Expand Up @@ -175,24 +181,26 @@ These can be left at their defaults.

- **rviz** (`bool`, default `false`), **rviz_config_file** (default `config/default.rviz`)

Launch RViz alongside. With the default config, the launch file patches in your frames, adds the odometry
layers when `odometry:=true`, and turns the three publishers above on. Any other config is passed to RViz
unchanged and you can configure the behaviour as desired.
Launch RViz alongside. With the default config, the launch file customises it for this run: your frames, plus
rko_lio's deskewed scan when `odometry:=true` and its local map when rko_lio publishes one, and it turns the
three publishers above on. Any other config is passed to RViz unchanged and you can configure the behaviour as
desired.

## Spawning rko_lio

- **odometry** (`bool`, default `false`)

Also start an rko_lio online node as the odometry. Its `publish_deskewed_scan` and `publish_local_map` are
forced on and `lidar_topic` is pinned to its deskewed scan topic. Give `base_frame` as well, see
[What gets autodetected](build_and_run.md#what-gets-autodetected).
Also start an rko_lio online node as the odometry, see [Starting rko_lio](build_and_run.md#starting-rko_lio).

- **rko_lio_config_file**, **rko_lio_lidar_topic**, **rko_lio_imu_topic**
- **rko_lio_config_file**

The YAML config, the raw LiDAR topic and the IMU topic for that rko_lio node. rko_lio autodetects its topics and
frames as well when you leave them unset, see its
[ROS docs](https://prbonn.github.io/rko_lio/pages/ros.html#launch-parameter-autodetection); what goes in the
file is in its [configuration](https://prbonn.github.io/rko_lio/pages/config.html).
The YAML config the rko_lio node runs on, as it is; what goes in it is in rko_lio's
[configuration](https://prbonn.github.io/rko_lio/pages/config.html), except that `base_frame`, `odom_frame`
and `use_sim_time` set on rko_slam override what it defines. If you let `lidar_topic` default to
rko_lio's deskewed scan, the file has to define `publish_deskewed_scan: true` and the launch stops if it does
not; give rko_slam a `lidar_topic` of your own and it does not matter. Without the file, rko_lio configures
itself with its
[autodetection](https://prbonn.github.io/rko_lio/pages/ros.html#launch-parameter-autodetection).

## Other

Expand All @@ -207,7 +215,7 @@ These can be left at their defaults.
## Multi-session alignment

`align.launch.py` reads the run directories and nothing live, so none of the input, mode, output or
visualization parameters above apply and there is nothing to autodetect. The sub-map parameters come from each
visualization parameters above apply. The sub-map parameters come from each
run's dumped config, and all runs must agree on `voxel_size` and `max_points_per_voxel`. What it takes is the
[Finding a revisit](#finding-a-revisit), [Accepting a closure](#accepting-a-closure) and
[Pose graph](#pose-graph) parameters with the same defaults, `config_file` and
Expand Down
3 changes: 2 additions & 1 deletion launch/rko_slam_launch_common.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import sys
from typing import NoReturn

import yaml
from launch.actions import DeclareLaunchArgument
Expand Down Expand Up @@ -76,7 +77,7 @@
]


def fail(*lines):
def fail(*lines) -> NoReturn:
print("\n\n" + "=" * 40)
for line in lines:
print(line)
Expand Down
Loading
Loading