From baba7b16cad008f0935eb8ff04106893e9d678b9 Mon Sep 17 00:00:00 2001 From: Woosik Lee <31552565+WoosikLee2510@users.noreply.github.com> Date: Thu, 9 Jul 2026 22:55:22 -0700 Subject: [PATCH 1/3] ros-free: build core + headless cli without ros, add no-ros CI --- .github/workflows/build_rosfree.yml | 19 ++++ Dockerfile_rosfree_ubuntu_22_04 | 25 ++++++ mins/cmake/ROS1.cmake | 131 ++++++++++++++++++--------- mins/src/run_simulation_cli.cpp | 132 ++++++++++++++++++++++++++++ mins/src/sim/Simulator.h | 6 +- 5 files changed, 267 insertions(+), 46 deletions(-) create mode 100644 .github/workflows/build_rosfree.yml create mode 100644 Dockerfile_rosfree_ubuntu_22_04 create mode 100644 mins/src/run_simulation_cli.cpp diff --git a/.github/workflows/build_rosfree.yml b/.github/workflows/build_rosfree.yml new file mode 100644 index 0000000..bf0a2d3 --- /dev/null +++ b/.github/workflows/build_rosfree.yml @@ -0,0 +1,19 @@ +name: ROS-free Workflow + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + + build_rosfree_linux: + name: "No ROS - Ubuntu 22.04" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Build core + headless CLI with no ROS + run: docker build . --file Dockerfile_rosfree_ubuntu_22_04 --tag mins:rosfree diff --git a/Dockerfile_rosfree_ubuntu_22_04 b/Dockerfile_rosfree_ubuntu_22_04 new file mode 100644 index 0000000..7a82935 --- /dev/null +++ b/Dockerfile_rosfree_ubuntu_22_04 @@ -0,0 +1,25 @@ +# Builds the MINS core library + headless CLI with NO ROS at all, to prove the +# core stays ROS-free. Plain cmake, only apt + bundled thirdparty deps. +FROM ubuntu:22.04 + +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install --no-install-recommends -y \ + build-essential cmake git ca-certificates \ + libeigen3-dev libboost-all-dev libopencv-dev libpcl-dev libyaml-cpp-dev \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /work +COPY . /work/src/MINS + +# MINS links libnabo + libpointmatcher; build them standalone (they are plain cmake). +RUN cmake -S src/MINS/thirdparty/libnabo -B build/libnabo \ + -DCMAKE_BUILD_TYPE=Release -DLIBNABO_BUILD_TESTS=OFF -DLIBNABO_BUILD_EXAMPLES=OFF -DLIBNABO_BUILD_PYTHON=OFF \ + && cmake --build build/libnabo -j"$(nproc)" && cmake --install build/libnabo +RUN cmake -S src/MINS/thirdparty/libpointmatcher -B build/libpointmatcher \ + -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF \ + && cmake --build build/libpointmatcher -j"$(nproc)" && cmake --install build/libpointmatcher + +# MINS core + headless CLI, no ROS. ov_core is pulled in via add_subdirectory by ROS1.cmake. +RUN cmake -S src/MINS/mins -B build/mins \ + -DCMAKE_BUILD_TYPE=Release -DENABLE_ROS=OFF -DENABLE_ARUCO_TAGS=OFF \ + && cmake --build build/mins -j"$(nproc)" diff --git a/mins/cmake/ROS1.cmake b/mins/cmake/ROS1.cmake index 3bf2a18..af20074 100644 --- a/mins/cmake/ROS1.cmake +++ b/mins/cmake/ROS1.cmake @@ -1,23 +1,22 @@ cmake_minimum_required(VERSION 3.5.1) -find_package(catkin REQUIRED COMPONENTS roscpp rosbag tf std_msgs geometry_msgs sensor_msgs nav_msgs image_geometry visualization_msgs image_transport cv_bridge ov_core pcl_ros) +# Find ROS1 (catkin). QUIET (not REQUIRED) so this same file can also configure a +# ROS-free build of the core library when catkin is not present. +find_package(catkin QUIET COMPONENTS roscpp rosbag tf std_msgs geometry_msgs sensor_msgs nav_msgs image_geometry visualization_msgs image_transport cv_bridge ov_core pcl_ros) +option(ENABLE_ROS "Build the ROS integration and nodes when ROS is found" ON) -add_definitions(-DROS_AVAILABLE=1) - -# Add catkin packages -catkin_package( - CATKIN_DEPENDS roscpp rosbag tf std_msgs geometry_msgs sensor_msgs nav_msgs image_geometry visualization_msgs image_transport cv_bridge ov_core pcl_ros - INCLUDE_DIRS src/ - LIBRARIES mins_lib -) +# When ROS is absent, ov_core (normally a catkin package) is pulled in as a subproject. +# Do it here, before we populate LIBRARY_SOURCES: add_subdirectory shares this scope's +# variables with the child, and ov_core also appends to LIBRARY_SOURCES. +if (NOT (catkin_FOUND AND ENABLE_ROS)) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/open_vins/ov_core ${CMAKE_BINARY_DIR}/ov_core) +endif () # Include our header files include_directories( src ${EIGEN3_INCLUDE_DIR} - ${PCL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} - ${catkin_INCLUDE_DIRS} ${libpointmatcher_INCLUDE_DIRS} ) @@ -25,14 +24,10 @@ include_directories( list(APPEND thirdparty_libraries ${Boost_LIBRARIES} ${OpenCV_LIBRARIES} - ${catkin_LIBRARIES} ${libpointmatcher_LIBRARIES} ) -################################################## -# Make the shared library -################################################## - +# Core (ROS-free) library sources list(APPEND LIBRARY_SOURCES src/options/Options.cpp src/options/OptionsCamera.cpp @@ -45,15 +40,11 @@ list(APPEND LIBRARY_SOURCES src/options/OptionsSystem.cpp src/options/OptionsVicon.cpp src/options/OptionsWheel.cpp - src/core/ROSPublisher.cpp - src/core/ROSSubscriber.cpp - src/core/ROSHelper.cpp - src/sim/Simulator.cpp - src/sim/ConstBsplineSE3.cpp - src/sim/SimVisualizer.cpp src/utils/PackagePath.cpp src/utils/Print_Logger.cpp src/utils/Jabdongsani.cpp + src/sim/Simulator.cpp + src/sim/ConstBsplineSE3.cpp src/state/State.cpp src/state/StateHelper.cpp src/state/Propagator.cpp @@ -74,6 +65,46 @@ list(APPEND LIBRARY_SOURCES src/init/imu_wheel/IW_Initializer.cpp ) +if (catkin_FOUND AND ENABLE_ROS) + message(STATUS "MINS: building WITH ROS1") + add_definitions(-DROS_AVAILABLE=1) + + # Add catkin packages + catkin_package( + CATKIN_DEPENDS roscpp rosbag tf std_msgs geometry_msgs sensor_msgs nav_msgs image_geometry visualization_msgs image_transport cv_bridge ov_core pcl_ros + INCLUDE_DIRS src/ + LIBRARIES mins_lib + ) + include_directories(${catkin_INCLUDE_DIRS} ${PCL_INCLUDE_DIRS}) + list(APPEND thirdparty_libraries ${catkin_LIBRARIES}) + + # ROS integration lives at the boundary - compiled in only when we have ROS + list(APPEND LIBRARY_SOURCES + src/core/ROSPublisher.cpp + src/core/ROSSubscriber.cpp + src/core/ROSHelper.cpp + src/sim/SimVisualizer.cpp + ) +else () + message(WARNING "MINS: building WITHOUT ROS - core library + headless CLI only") + add_definitions(-DROS_AVAILABLE=0) + include(GNUInstallDirs) + set(CATKIN_PACKAGE_LIB_DESTINATION "${CMAKE_INSTALL_LIBDIR}") + set(CATKIN_PACKAGE_BIN_DESTINATION "${CMAKE_INSTALL_BINDIR}") + set(CATKIN_GLOBAL_INCLUDE_DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/mins/") + + # ov_core was pulled in above (before LIBRARY_SOURCES); just link + find PCL here. + # (In a ROS build PCL comes via catkin/pcl_ros; standalone we link it ourselves.) + find_package(PCL REQUIRED) + include_directories(${PCL_INCLUDE_DIRS}) + link_directories(${PCL_LIBRARY_DIRS}) + add_definitions(${PCL_DEFINITIONS}) + list(APPEND thirdparty_libraries ov_core_lib ${PCL_LIBRARIES}) +endif () + +################################################## +# Make the shared library +################################################## file(GLOB_RECURSE LIBRARY_HEADERS "src/*.h") add_library(mins_lib SHARED ${LIBRARY_SOURCES} ${LIBRARY_HEADERS}) @@ -93,26 +124,40 @@ install(DIRECTORY src/ # Make binary files! ################################################## -add_executable(simulation src/run_simulation.cpp) -target_link_libraries(simulation mins_lib) -install(TARGETS simulation - ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} - LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} - RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} - ) +if (catkin_FOUND AND ENABLE_ROS) -add_executable(bag src/run_bag.cpp) -target_link_libraries(bag mins_lib) -install(TARGETS bag - ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} - LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} - RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} - ) + add_executable(simulation src/run_simulation.cpp) + target_link_libraries(simulation mins_lib) + install(TARGETS simulation + ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} + LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} + RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} + ) -add_executable(subscribe src/run_subscribe.cpp) -target_link_libraries(subscribe mins_lib) -install(TARGETS subscribe - ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} - LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} - RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} - ) \ No newline at end of file + add_executable(bag src/run_bag.cpp) + target_link_libraries(bag mins_lib) + install(TARGETS bag + ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} + LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} + RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} + ) + + add_executable(subscribe src/run_subscribe.cpp) + target_link_libraries(subscribe mins_lib) + install(TARGETS subscribe + ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} + LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} + RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} + ) + +else () + + # Headless CLI: runs the simulator through the estimator with no ROS, writes + # results to file. This is the ROS-free runnable target. + add_executable(mins_cli src/run_simulation_cli.cpp) + target_link_libraries(mins_cli mins_lib) + install(TARGETS mins_cli + RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} + ) + +endif () diff --git a/mins/src/run_simulation_cli.cpp b/mins/src/run_simulation_cli.cpp new file mode 100644 index 0000000..7b9c99e --- /dev/null +++ b/mins/src/run_simulation_cli.cpp @@ -0,0 +1,132 @@ +/* + * MINS: Efficient and Robust Multisensor-aided Inertial Navigation System + * Copyright (C) 2023 Woosik Lee + * Copyright (C) 2023 Guoquan Huang + * Copyright (C) 2023 MINS Contributors + * + * This code is implemented based on: + * OpenVINS: An Open Platform for Visual-Inertial Research + * Copyright (C) 2018-2023 Patrick Geneva + * Copyright (C) 2018-2023 Guoquan Huang + * Copyright (C) 2018-2023 OpenVINS Contributors + * Copyright (C) 2018-2019 Kevin Eckenhoff + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// Headless simulation runner - no ROS. Runs the simulator through the estimator +// and prints/saves accuracy, so the core can be built and exercised without ROS. + +#include + +#include "core/SystemManager.h" +#include "options/Options.h" +#include "options/OptionsEstimator.h" +#include "options/OptionsGPS.h" +#include "options/OptionsSystem.h" +#include "sim/Simulator.h" +#include "state/State.h" +#include "state/StateHelper.h" +#include "update/cam/CamTypes.h" +#include "update/gps/GPSTypes.h" +#include "update/gps/UpdaterGPS.h" +#include "update/vicon/ViconTypes.h" +#include "update/wheel/WheelTypes.h" +#include "utils/Print_Logger.h" +#include "utils/State_Logger.h" +#include "utils/TimeChecker.h" +#include "utils/colors.h" +#include "utils/opencv_yaml_parse.h" +#include "utils/sensor_data.h" +#include +#include + +using namespace mins; +using namespace std; +using namespace Eigen; + +int main(int argc, char **argv) { + // Config path comes from argv only (no ROS param server in this build) + string config_path = "unset_path_to_config.yaml"; + if (argc > 1) + config_path = argv[1]; + + auto parser = make_shared(config_path); + auto op = make_shared(); + op->load_print(parser); + op->sys->save_prints ? Print_Logger::open_file(op->sys->path_state, true) : void(); + + auto sim = make_shared(op); + auto sys = make_shared(op->est, sim); + auto save = make_shared(op, sim); + + if (!parser->successful()) { + PRINT4(RED "unable to parse all parameters, please fix\n" RESET); + exit(EXIT_FAILURE); + } + + // Running accuracy (same math the ROS visualizer does, minus the publishing) + double sum_rmse_ori = 0, sum_rmse_pos = 0, sum_nees_ori = 0, sum_nees_pos = 0; + int sum_cnt = 0; + + while (sim->ok()) { + // IMU: propagate, and on a filter update print/accumulate accuracy + ov_core::ImuData imu; + if (sim->get_next_imu(imu)) { + if (sys->feed_measurement_imu(imu)) { + sim->trans_gt_to_ENU = op->est->gps->enabled && sys->up_gps->initialized; + auto imu_pose = sys->state->imu->pose(); + Vector4d rn = sim->imu_rmse_nees(sys->state->time, imu_pose->value(), StateHelper::get_marginal_covariance(sys->state, {imu_pose})); + if (!isnan(rn(2)) && !isnan(rn(3))) { + sum_rmse_ori += rn(0); + sum_rmse_pos += rn(1); + sum_nees_ori += rn(2); + sum_nees_pos += rn(3); + sum_cnt++; + } + PRINT2("\033[A%.2f | RMSE: %.3f, %.3f (deg,m) | RMSE avg: %.3f, %.3f | NEES avg: %.1f, %.1f\n\n", sys->state->time, rn(0), rn(1), + sum_rmse_ori / sum_cnt, sum_rmse_pos / sum_cnt, sum_nees_ori / sum_cnt, sum_nees_pos / sum_cnt); + op->sys->save_state ? save->save_state_to_file(sys, sim) : void(); + op->sys->save_trajectory ? save->save_trajectory_to_file(sys) : void(); + } + } + + CamSimData cam; + if (sim->get_next_cam(cam)) + sys->feed_measurement_camsim(cam); + + GPSData gps; + if (sim->get_next_gps(gps)) + sys->feed_measurement_gps(gps, false); + + WheelData wheel; + if (sim->get_next_wheel(wheel)) + sys->feed_measurement_wheel(wheel); + + std::shared_ptr> lidar(new pcl::PointCloud); + if (sim->get_next_lidar(lidar)) + sys->feed_measurement_lidar(lidar); + + ViconData vicon; + if (sim->get_next_vicon(vicon)) + sys->feed_measurement_vicon(vicon); + } + + sys->visualize_final(); + PRINT2(BOLDYELLOW "RMSE average: %.3f, %.3f (deg,m)\n" RESET, sum_rmse_ori / sum_cnt, sum_rmse_pos / sum_cnt); + PRINT2(BOLDYELLOW "NEES average: %.3f, %.3f (deg,m)\n" RESET, sum_nees_ori / sum_cnt, sum_nees_pos / sum_cnt); + op->sys->save_timing ? save->save_timing_to_file(sys->tc_sensors->get_total_sum()) : void(); + save->check_files(); + return EXIT_SUCCESS; +} diff --git a/mins/src/sim/Simulator.h b/mins/src/sim/Simulator.h index f9c167b..9b563f8 100644 --- a/mins/src/sim/Simulator.h +++ b/mins/src/sim/Simulator.h @@ -128,6 +128,9 @@ class Simulator { /// boolean for transforming groundtruth after GPS initialization bool trans_gt_to_ENU = false; + /// Returns RMSE and NEES of IMU pose (ori rmse, pos rmse, ori nees, pos nees) + Vector4d imu_rmse_nees(double time, Matrix imu, Matrix cov); + protected: friend class Initializer; friend class SimVisualizer; @@ -143,9 +146,6 @@ class Simulator { /// Returns the true 3d map of camera features std::unordered_map get_cam_map(); - /// Returns RMSE and NEES of IMU pose (ori rmse, pos rmse, ori nees, pos nees) - Vector4d imu_rmse_nees(double time, Matrix imu, Matrix cov); - /// a wrapper function of spline that returns IMU pose & velocities & accelerations bool get_imu_acceleration(double timestamp, Matrix3d &R_GtoI, Vector3d &p_IinG, Vector3d &w_IinI, Vector3d &v_IinG, Vector3d &alpha_IinI, Vector3d &a_IinG); From 011fe12268ae93ff080126ec22c0e167de078152 Mon Sep 17 00:00:00 2001 From: Woosik Lee <31552565+WoosikLee2510@users.noreply.github.com> Date: Thu, 9 Jul 2026 23:38:19 -0700 Subject: [PATCH 2/3] ros-free: make headless cli runnable via MINS_ROOT and smoke-run it in ci --- .github/workflows/build_rosfree.yml | 8 ++++++++ Dockerfile_rosfree_ubuntu_22_04 | 11 ++++++++--- mins/src/utils/PackagePath.cpp | 6 +++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_rosfree.yml b/.github/workflows/build_rosfree.yml index bf0a2d3..634c68f 100644 --- a/.github/workflows/build_rosfree.yml +++ b/.github/workflows/build_rosfree.yml @@ -17,3 +17,11 @@ jobs: submodules: recursive - name: Build core + headless CLI with no ROS run: docker build . --file Dockerfile_rosfree_ubuntu_22_04 --tag mins:rosfree + - name: Run headless CLI (no ROS) end to end + run: | + docker run --rm -e MINS_ROOT=/work/src/MINS mins:rosfree bash -c ' + set -eo pipefail + mkdir -p /outputs/tmp/0 + timeout 600 /work/build/mins/mins_cli /work/src/MINS/mins/config/simulation/config.yaml 2>&1 | tee /tmp/cli.log + grep -q "RMSE average" /tmp/cli.log + ' diff --git a/Dockerfile_rosfree_ubuntu_22_04 b/Dockerfile_rosfree_ubuntu_22_04 index 7a82935..bcefb65 100644 --- a/Dockerfile_rosfree_ubuntu_22_04 +++ b/Dockerfile_rosfree_ubuntu_22_04 @@ -9,17 +9,22 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ && rm -rf /var/lib/apt/lists/* WORKDIR /work -COPY . /work/src/MINS -# MINS links libnabo + libpointmatcher; build them standalone (they are plain cmake). +# Build the standalone thirdparty libs first (they change rarely) so editing MINS +# source does not rebuild them - COPY them alone before the rest of the tree. +COPY thirdparty/libnabo /work/src/MINS/thirdparty/libnabo RUN cmake -S src/MINS/thirdparty/libnabo -B build/libnabo \ -DCMAKE_BUILD_TYPE=Release -DLIBNABO_BUILD_TESTS=OFF -DLIBNABO_BUILD_EXAMPLES=OFF -DLIBNABO_BUILD_PYTHON=OFF \ && cmake --build build/libnabo -j"$(nproc)" && cmake --install build/libnabo + +COPY thirdparty/libpointmatcher /work/src/MINS/thirdparty/libpointmatcher RUN cmake -S src/MINS/thirdparty/libpointmatcher -B build/libpointmatcher \ -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF \ && cmake --build build/libpointmatcher -j"$(nproc)" && cmake --install build/libpointmatcher -# MINS core + headless CLI, no ROS. ov_core is pulled in via add_subdirectory by ROS1.cmake. +# Now the rest of the tree. MINS core + headless CLI, no ROS. +# ov_core is pulled in via add_subdirectory by ROS1.cmake. +COPY . /work/src/MINS RUN cmake -S src/MINS/mins -B build/mins \ -DCMAKE_BUILD_TYPE=Release -DENABLE_ROS=OFF -DENABLE_ARUCO_TAGS=OFF \ && cmake --build build/mins -j"$(nproc)" diff --git a/mins/src/utils/PackagePath.cpp b/mins/src/utils/PackagePath.cpp index 400c563..4304da8 100644 --- a/mins/src/utils/PackagePath.cpp +++ b/mins/src/utils/PackagePath.cpp @@ -21,6 +21,7 @@ // This is the only translation unit that touches the ROS package-path API, // keeping ros::package / ament out of the shared core. #include "utils/PackagePath.h" +#include #if ROS_AVAILABLE == 1 #include #elif ROS_AVAILABLE == 2 @@ -33,6 +34,9 @@ std::string mins::get_package_path(const std::string &pkg) { #elif ROS_AVAILABLE == 2 return ament_index_cpp::get_package_share_directory(pkg); #else - return std::string(); + // No ROS package system. Resolve "" under $MINS_ROOT (the source/repo root) + // so the headless build can still find config + data; empty if unset. + const char *root = std::getenv("MINS_ROOT"); + return root ? std::string(root) + "/" + pkg : std::string(); #endif } From 811e8024bd094e89d3fd505f309310ab0078d659 Mon Sep 17 00:00:00 2001 From: Woosik Lee <31552565+WoosikLee2510@users.noreply.github.com> Date: Fri, 10 Jul 2026 06:56:56 -0700 Subject: [PATCH 3/3] readme: add ros-free workflow badge --- ReadMe.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ReadMe.md b/ReadMe.md index 01d595a..9e37104 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,6 +1,7 @@ # MINS [![ROS 1 Workflow](https://github.com/rpng/MINS/actions/workflows/build_ros1.yml/badge.svg)](https://github.com/rpng/MINS/actions/workflows/build_ros1.yml) [![ROS 2 Workflow](https://github.com/rpng/MINS/actions/workflows/build_ros2.yml/badge.svg)](https://github.com/rpng/MINS/actions/workflows/build_ros2.yml) +[![ROS-free Workflow](https://github.com/rpng/MINS/actions/workflows/build_rosfree.yml/badge.svg)](https://github.com/rpng/MINS/actions/workflows/build_rosfree.yml) An efficient, robust, and tightly-coupled **Multisensor-aided Inertial Navigation System (MINS)** which is capable of flexibly fusing all five sensing modalities (**IMU**, **wheel** **encoders**, **camera**, **GNSS**, and **LiDAR**) in a filtering