DLIO: LiDAR-inertial odometry that corrects motion in continuous time
[IEEE ICRA'23] A new lightweight LiDAR-inertial odometry algorithm with a novel coarse-to-fine approach in constructing continuous-time trajectories for precise motion correction.
At a glance
- What is it?
- DLIO is a lightweight LiDAR-inertial odometry node from the UCLA VECTR lab, published at ICRA 2023, that builds continuous-time trajectories with a coarse-to-fine scheme so that a moving platform's own motion is removed from the points it scans. It runs on ROS1 with a ROS2 branch, supports Ouster, Velodyne, Hesai and Livox, and is MIT licensed.
- Who is it for?
- DLIO is worth adopting for a research robot that carries a LiDAR and an IMU and needs a local map with the platform's own motion removed, because the deskewing and the motion correction are the reason the algorithm exists and the code is small enough to read.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The contribution is continuous-time motion correction, not a new matcher
DLIO stands for Direct LiDAR-Inertial Odometry, and it was presented at the IEEE International Conference on Robotics and Automation, ICRA, in London in 2023. The paper, an arXiv entry, a video and a presentation are all linked from the header of the README.
The one-line description is a lightweight LiDAR-inertial odometry algorithm with a coarse-to-fine approach in constructing continuous-time trajectories for precise motion correction. Two words in that sentence carry the technical weight. Continuous time means the trajectory is not a sequence of poses at discrete timestamps but a function of time, which is what makes it possible to ask where the sensor was at the exact moment each laser returned, rather than at the scan's start. Coarse to fine describes how that trajectory is built, first roughly and then refined.
The purpose of all this is deskewing. A spinning LiDAR takes a fraction of a second to complete a revolution, and a robot that moves during that revolution smears every object in the scan. The corrections in a point cloud are what stop a wall from appearing curved and a pole from appearing tilted, and the accuracy of the map depends on getting them right.
The README says the algorithm features several algorithmic improvements over its predecessor, DLO, and the arXiv and IEEE links are how you find out what they are. The relationship to DLO is worth noting for anyone deciding whether to use this or its ancestor: DLIO is the successor, and the DLO repository is linked for comparison.
The project is C++ under the MIT licence, with the last push on 2026-04-03 and releases v1.1.1 from 2023-06-27, v1.1.0 from 2023-06-14 and v1.0.0 from 2023-06-09. The gap between the last release and the last push is more than a year, which tells you the code has moved since the last tag.
Sensor support is broad, but the message type decides which branch you use
The compatibility section is precise about what DLIO accepts, and the interesting decision is about message types rather than about sensor brands.
DLIO has been extensively tested using a variety of sensor configurations and currently supports Ouster, Velodyne, Hesai and Livox LiDARs. The point cloud should be of input type sensor_msgs::PointCloud2 and the 6-axis IMU input type sensor_msgs::Imu. Those two message types are the contract, and everything else follows from them.
For Livox sensors the README splits the world in two. The master branch works directly if the Livox point cloud is of type sensor_msgs::PointCloud2, which is called xfer_format: 0. If it is of type livox_ros_driver2::CustomMsg, called xfer_format: 1, then you need the feature/livox-support branch together with the latest livox_ros_driver2 package, and there is an issue link with more detail.
That is a real operational wrinkle, since the Livox driver can be configured to publish either format and the two require different code paths. It also means the master branch is not the branch to use for a Livox setup with the custom message, which is a detail worth checking before cloning.
The IMU requirement is stated in the same place and it is not a detail. The LiDAR and IMU sensors need to be properly time-synchronised, otherwise DLIO will not work, and the recommendation is to use a LiDAR with an integrated IMU, such as an Ouster, for simplicity of extrinsics and synchronisation. That recommendation is doing real work in practice, because a separate IMU and a separate LiDAR need both a hardware trigger and a software time offset estimated, and the quality of the odometry degrades with the quality of that estimate.
The supported message set also means the node drops into an existing ROS pipeline without a custom driver in the middle, provided your driver publishes the standard types.
Calibration goes into cfg/dlio.yaml, and defaults are only for a first run
The calibration section explains what to change before the algorithm can do its job, and the tone is practical rather than academic.
For best performance, the extrinsic calibration between the LiDAR and IMU sensors and the robot's centre of gravity should be inputted into cfg/dlio.yaml. If the exact values are unavailable, a rough LiDAR-to-IMU extrinsic can be used, and the README notes that performance will be degraded in that case. The emphasis is on the transform between the sensors and the centre of gravity, not only between the two sensors, which is the part that is easy to forget because a lidar-to-imu transform is the thing most calibration tools output.
IMU intrinsics are also necessary for best performance, and the README says there are several open-source calibration tools to get these values, with the values also going into cfg/dlio.yaml. It then gives the escape hatch: in practice, if you are just testing this work, using the default ideal values and performing the initial calibration procedure should be fine.
So the configuration file is the control surface for everything the algorithm needs to know about the rig, and the defaults in it are described as ideal values, which means they are a starting point rather than a physically meaningful calibration of anybody's robot.
For a reader trying to judge the project, the honest summary is that the algorithm is well specified and the calibration burden is the price of a LiDAR-inertial system in general. Nothing here is a defect of DLIO, but it does mean the first successful run on a new robot takes a calibration session, and the README's advice to try the provided test data with deskewing on and off is a good way to see the difference the calibration makes.
The repository layout supports that reading. The root holds a cfg/ directory, a doc/ directory with images, include/, src/, launch/, srv/ and package.xml, which is a conventional catkin package layout with the addition of a server definition for saving maps.
The dependency set pins Ubuntu 20.04 and ROS Noetic, with ROS2 on a branch
The verified dependency list is short and dated in a way that matters for anyone picking this up now.
The README lists what has been verified to be compatible, while noting that other configurations may work too. That list is Ubuntu 20.04, ROS Noetic with roscpp, std_msgs, sensor_msgs, geometry_msgs, nav_msgs and pcl_ros, C++ 14, CMake 3.12.4 or later, OpenMP 4.5 or later, Point Cloud Library 1.10.0 or later, and Eigen 3.3.7 or later. The system libraries install with a single apt line:
sudo apt install libomp-dev libpcl-dev libeigen3-devThe ROS story is split across branches. DLIO supports ROS1 by default, and ROS2 using the feature/ros2 branch. The README also credits a contributor for porting DLIO to ROS2, linking the pull request, so the ROS2 support exists as a specific contribution rather than as a maintained release line.
The build is a catkin_tools workflow, and the README gives the sequence in full:
mkdir ws && cd ws && mkdir src && catkin init && cd src
git clone https://github.com/vectr-ucla/direct_lidar_inertial_odometry.git
catkin buildThe topic list on the repository covers 3d-mapping, imu, lidar, lidar-inertial-odometry, lidar-odometry, lidar-slam, localization, mapping, odometry, robotics, ros and slam, which is the vocabulary of the field. The fact that the package is a node rather than a library is confirmed by the package.xml at the root and by the launch file.
For an evaluation, the practical point is that the verified target is Ubuntu 20.04 with ROS Noetic, which is a specific and increasingly dated combination, and that a ROS2 user is picking up a branch rather than a release.
Running it is a roslaunch with topic remapping, and the map is saved by a service call
The execution section is short, and the launch file takes the two topics you care about.
roslaunch direct_lidar_inertial_odometry dlio.launch \
rviz:={true, false} \
pointcloud_topic:=/robot/lidar \
imu_topic:=/robot/imuThat form is for Ouster, Velodyne, Hesai, or Livox sensors with xfer_format: 0. For Livox with xfer_format: 1 the launch uses a different topic argument:
roslaunch direct_lidar_inertial_odometry dlio.launch \
rviz:={true, false} \
livox_topic:=/livox/lidar \
imu_topic:=/robot/imuThe README says to change the topic names to your corresponding topics, or to edit the launch file directly if desired. The rviz argument is a boolean pair, so you can have the visualizer open or closed independently, which matters when the node runs headless on a robot.
The expected result is described by a terminal screenshot in the repository, so what a good run looks like is a console output rather than a map, and that is the first thing to check when a run appears to hang.
Saving the map is done through a ROS service, and the call takes two arguments:
rosservice call /robot/dlio_map/save_pcd LEAF_SIZE SAVE_PATHSo the map is written as a .pcd file, and the leaf size is a parameter of the call rather than of the launch file. The srv/ directory at the repository root holds the service definition.
The test data is the other practical item. Two datasets are provided, one of 1.2GB lasting 1m 13s recorded with an Ouster OS1-32 showing aggressive motion to test the motion correction scheme, and one of 16.5GB lasting 4m 21s recorded with an Ouster OSDome showing a longer trajectory outside with lots of trees. The README suggests trying both with deskewing on and off, which is the cleanest way to see what the algorithm actually contributes.
Where DLIO is the wrong tool
The limits are the ordinary limits of a research release, and the README is mostly honest about them.
The first is the release timeline. The tags stop at v1.1.1 in June 2023, and the last push was on 2026-04-03. So there are years of unreleased work on the default branch, which means anyone building from master is running code that has never been tagged, and anyone using the tags is running something from 2023. For a research project that is normal, and for anything else it is a reason to treat the repository as a source of algorithms rather than as a supported product.
The second is the ROS version. The verified stack is ROS Noetic on Ubuntu 20.04, and ROS2 lives on a feature branch contributed by someone else. A team standardising on ROS2 Humble or Jazzy is running a branch of a branch, and the Livox custom message case means the branch choice interacts with the driver configuration.
The third is the sensor and calibration floor. The requirement for time-synchronised LiDAR and IMU data is absolute, and the README says DLIO will not work without it. There is also the extrinsic calibration between the sensors and the centre of gravity, and the IMU intrinsics, both of which need to be in cfg/dlio.yaml. A team with an unsynchronised rig, or with a LiDAR and an IMU from different vendors and no way to characterise the offset, cannot use this and will not find the problem in an error message.
The fourth is scope. DLIO does odometry and it can save a map, and the topic list mentions lidar-slam, but the package is an odometry node rather than a full SLAM system with loop closure. A team that needs global consistency over a long trajectory, or a pose graph that corrects accumulated drift, needs more than this node provides.
The fifth is the licensing of what it builds on. DLIO itself is MIT, and the README thanks the authors of FastGICP, a voxelized GICP for fast and accurate 3D point cloud registration, and NanoFLANN, a header-only fork of FLANN with KD-trees. Those are the registration and nearest-neighbour components doing the heavy work in the matcher.
Editorial conclusion
DLIO is worth adopting for a research robot that carries a LiDAR and an IMU and needs a local map with the platform's own motion removed, because the deskewing and the motion correction are the reason the algorithm exists and the code is small enough to read. It is the wrong tool for a production vehicle, since the last release is v1.1.1 from 2023 and the last push was on 2026-04-03, and it is the wrong tool if your sensors are not time synchronised, because the README states plainly that it will not work in that case. Before running it, put the real extrinsic and IMU intrinsic calibration into cfg/dlio.yaml, run the two provided datasets with deskewing on and off to see the difference, and check that your point cloud is a sensor_msgs::PointCloud2 rather than a Livox custom message.
Frequently asked questions
What is DLIO and what does it do?
DLIO is a lightweight LiDAR-inertial odometry algorithm that builds continuous-time trajectories with a coarse-to-fine approach for precise motion correction. It was presented at the IEEE International Conference on Robotics and Automation in London in 2023 and is an improvement over its predecessor DLO.
Which LiDAR sensors and ROS versions does DLIO support?
It supports Ouster, Velodyne, Hesai and Livox LiDARs, with the point cloud as sensor_msgs::PointCloud2 and the 6-axis IMU as sensor_msgs::Imu. ROS1 is supported by default with ROS Noetic, and ROS2 through the feature/ros2 branch.
How do I build and run DLIO?
Install libomp-dev, libpcl-dev and libeigen3-dev, then use catkin_tools with mkdir ws, catkin init, a git clone of the repository and catkin build. Run it with roslaunch direct_lidar_inertial_odometry dlio.launch, remapping pointcloud_topic or livox_topic and imu_topic to your own topics.
What calibration does DLIO need?
For best performance the extrinsic calibration between the LiDAR and IMU sensors and the robot's centre of gravity, plus the IMU intrinsics, should be put into cfg/dlio.yaml. The README notes that default ideal values are enough for a first test, but that performance degrades with rough extrinsics, and the sensors must be properly time-synchronised or the node will not work.
How do I save the map DLIO generates?
Call the save service with a leaf size and a path, for example rosservice call /robot/dlio_map/save_pcd LEAF_SIZE SAVE_PATH, and the map is written as a .pcd file.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/vectr-ucla-direct-lidar-inertial-odometry)Was this useful?