LIO-SAM 맨땅에 머리박기 (ROS1 noetic)

임우진·2025년 5월 8일
4

ROS

목록 보기
1/1
post-thumbnail

들어가며


프로젝트에서 LIO-SAM 기반의 3D SLAM 알고리즘을 사용해야 하는 일이 생겼다.

ROS를 거의 처음 다뤄보는 나로써는 상당히 막막한 일이었지만 개발자라면 자고로 머리부터 들이밀어야 한다는 마음으로 프로젝트를 시작해 보았다.

혹시 나처럼 고통받고 있는 누군가에게 이 글이 도움이 되길 바라며 LIO-SAM을 설치하고 데이터를 수집하는 과정에서 일어난 우여곡절을 함께 나눠보고자 한다.

개발환경 구축하기


1. Linux 개발환경 구축 (Ubuntu 20.04)

이번 프로젝트에서는 ROS noetic 버전을 사용하여야 했기 때문에 Ubuntu 20.04 버전을 사용하기로 하였다.

Docker를 사용하여도 되지만 우리는 LG그램 노트북에 Ubuntu를 바로 올려서 사용하기로 하였다.

우분투를 설치하는 방법은 구글에 검색해 보면 많이 나오기에 따로 설명하지는 않겠다.

만약 나와 같은 환경에서 시작하고 싶은 사람은 Ubuntu 공식 홈페이지에서 20.04 버전 이미지를 다운받고 부팅 USB를 만들어 원하는 PC에 설치하면 된다.

부팅 USB는 잘 보관해 두자. 나중에 사용할 일이 있을지도 모른다..

2. ROS 설치 (ROS1 noetic)

상술한 것처럼 이번 프로젝트에서 ROS1 noetic 버전 사용이 필수적인 상황이었다.

돌아보면 이게 비극의 시작이었던 것 같지만 프로젝트에서 사용하는 모터 드라이버가 지원하는 버전이 noetic 뿐인지라 어쩔 수 없는 일이었다. (만약 Noetic 버전을 사용할 필요가 없는 사람이라면 LIO-SAM github에서 명시하는 테스트 된 버전으로 진행하는 것이 정신건강에 좋을 수 있다. 그렇다면 이 글도 읽을 필요가 없을 것이다..)

ROS는 공식문서를 참고하여 설치하였다.

1. 패키지 저장소 등록

sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'

여기서 주의할 점이 있다.

위에서 Ubuntu 이미지를 설치하는 방식으로 시작하였다면 lsb_release 패키지가 설치되어 있지 않아 오류가 날 것이다. 만약 위의 코드를 먼저 실행시켰고 오류가 났다면 축하한다. 이제 당신의 Ubunru에서는 어떤 apt install 명령어도 작동하지 않을 것이다.

아마 이를 복구하는 방법이 있을 것 같긴 한데 나는 그냥 우분투를 새로 까는 방법을 선택하였다. 아까 보관해 둔 부팅 USB를 다시 꺼내보자...

위 명령어를 무사히 실행시키려면 먼저 저 lsb 패키지를 설치하여야 한다. 참고로 lsb_release는 현재 우분투의 버전 이름을 확인하는 명령어이다.

sudo apt-get install lsb

이제 위 패키지를 다운로드하고 다시 패키지 저장소를 등록해 주자.

2. 키 셋업

sudo apt install curl # if you haven't already installed curl
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -

3. 설치

sudo apt update
sudo apt install ros-noetic-desktop-full

4. 환경변수 설정

설치가 이제 끝났다. 설치 이후에 환경변수를 추가해 주기 위해

source /opt/ros/noetic/setup.bash

위 명령어를 실행시켜 주자. 이 명령어는 ROS 환경을 현재의 셀 세션에 등록해 주는 명령어이다.
매번 터미널을 열 때마다 실행해 줘야 하지만 상당히 귀찮기 때문에 .bashrc 파일에 등록하여 자동화하는 방법도 있다.

echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
source ~/.bashrc

위 명령어를 입력하였다면 터미널을 열 때마다 자동으로 ROS 환경이 등록될 것이다.
혹여 여러 ROS 버전을 동시에 사용해야 한다면 위 자동화 명령어를 입력하면 안 된다.

LIO-SAM 설치 및 빌드하기


1. 작업공간 설정하기

LIO-SAM을 설치하기 전에 작업공간을 만들어 주자. 방법은 공식문서에도 잘 설명되어 있다.

mkdir -p ~/catkin_ws/src/

꼭 위 명령어로 작업공간을 만들 필요는 없다. 원하는 경로에 원하는 이름으로 설정하여도 된다. 다만 폴더 내에 src 폴더는 꼭 만들어주자.

mkdir -p {원하는 경로와 폴더 이름}/src

그리고 해당 폴더로 이동 후 기본 세팅을 해주자.

cd ~/catkin_ws/
catkin_make

여기서 catkin_make 명령어는 사실 빌드 명령어이다. 그래서 지금 꼭 실행시켜야 할 필요는 없지만 공식문서에서 하라고 하니 한번 실행해 보자. 빌드가 되면서 다양한 결과 폴더가 생기는 것을 확인할 수 있다. (코드가 없기 때문에 별 의미는 없다)

2. LIO-SAM 설치 및 빌드하기

이제 LIO-SAM을 설치해 보자. 설치 방법은 github 레포지토리에 잘 설명되어 있다.

1. 의존성 설치 (ros 패키지)

sudo apt-get install -y ros-noetic-navigation
sudo apt-get install -y ros-noetic-robot-localization
sudo apt-get install -y ros-noetic-robot-state-publisher

공식 문서에는 kinetic으로 설정되어 있기에 noetic으로 변경하여 설치해 준다.

2. 의존성 설치 (gtsam)

sudo add-apt-repository ppa:borglab/gtsam-release-4.0
sudo apt install libgtsam-dev libgtsam-unstable-dev

나의 경우 이 gtsam 패키지는 추후 빌드 시에 많은 문제를 일으켰었다. 당신이 운이 좋거나 깨끗한 우분투에서 설치를 진행하고 있다면 별일은 없을 것이다.

3. 코드 다운로드 및 빌드

모든 코드는 src 폴더 안에 있어야 하므로 src 폴더로 이동 후 git clone을 해준다.

cd ~/catkin_ws/src
git clone https://github.com/TixiaoShan/LIO-SAM.git

이후 다시 워크스페이스 폴더로 돌아가 빌드를 해준다.

cd ..
catkin_make

여기까지 잘 진행했다면 아마 빌드가 진행되는 듯하면서 에러가 발생했을 것이다.

아주 정상적인 현상이니 괜찮다. 에러는 뜨지 않는데 되는 게 아무것도 없는 경우보다는 낫지 않은가?

지금부터 하나하나 고쳐 나가보도록 하자.

에러 해결


1. OpenCV 에러

아마 첫 번째로 뜨는 에러는 opencv 에러일 것이다.

In file included from /home/ubicomp/Desktop/woojin-main-ws/src/LIO-SAM/src/imuPreintegration.cpp:1:
/home/ubicomp/Desktop/woojin-main-ws/src/LIO-SAM/include/utility.h:18:10: fatal error: opencv/cv.h: No such file or directory
   18 | #include <opencv/cv.h>
      |          ^~~~~~~~~~~~~
compilation terminated.
make[2]: *** [LIO-SAM/CMakeFiles/lio_sam_imuPreintegration.dir/build.make:63: LIO-SAM/CMakeFiles/lio_sam_imuPreintegration.dir/src/imuPreintegration.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:812: LIO-SAM/CMakeFiles/lio_sam_imuPreintegration.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....

이 에러는 LIO-SAM이 테스트 된 버전(Ununtu 18.04)에서는 OpenCV 3.x 버전을 사용하지만, 현재 버전(Ubuntu 20.04)에서는 OpenCV 4.x 버전을 사용하여 생긴 문제이다.

다행스럽게도 이 에러는 이 이슈에서 다루어졌기 때문에 우리는 그저 앞선 사람들을 따라가기만 하면 된다. (오픈소스 만세)

에러 해결을 위해서는 LIO-SAM/include/utility.h 파일을 수정해야 한다. #include <opencv/cv.h> 구문을 #include <opencv2/opencv.hpp>로 변경해 주게 되면 에러가 해결되는 것을 볼 수 있다.

2. C++14 에러

opencv를 고치고 신나게 catkin_make 명령어를 다시 입력해보면 말 그대로 에러가 뿜어져 나오는 것을 보게 될 것이다.

In file included from /usr/include/pcl-1.10/pcl/pcl_macros.h:77,
                 from /usr/include/pcl-1.10/pcl/PCLHeader.h:10,
                 from /usr/include/pcl-1.10/pcl/point_cloud.h:47,
                 from /home/ubicomp/Desktop/woojin-main-ws/src/LIO-SAM/include/utility.h:20,
                 from /home/ubicomp/Desktop/woojin-main-ws/src/LIO-SAM/src/imageProjection.cpp:1:
/usr/include/pcl-1.10/pcl/pcl_config.h:7:4: error: #error PCL requires C++14 or above
    7 |   #error PCL requires C++14 or above
      |    ^~~~~

PCL Requires C++14 or above 로 시작하는 이 에러는 위의 에러와 동일하게 개발 환경 버전 차이로 발생하는 것이다.

다시 말해 CMakeLists.txt에 설정된 C++ 표준이 과거 PCL 버전으로 맞춰져 있어 오류가 발생한 것이다.

에러 해결을 위해서는 LIO-SAM/CMakeLists.txt에서 set(CMAKE_CXX_FLAGS "-std=c++11") 구문을 set(CMAKE_CXX_FLAGS "-std=c++14") 로 변경해 주면 된다.

3. no member named ‘serialize’ 에러

이 에러는 어떨 때는 발생하지 않고 어떨 때는 발생하는 이상한 에러였다.

/usr/include/flann/util/serialization.h:34:14: error: ‘class std::unordered_map<unsigned int, std::vector<unsigned int> >’ has no member named ‘serialize’
   34 |         type.serialize(ar);
      |         ~~~~~^~~~~~~~~

정확한 발생 원인과 해결 방안은 정의하지 못하였다. 다만 위의 이슈 페이지에 비슷한 내용이 있었기에 해당 지시를 따라보았다.

에러 해결을 위해서는 LIO-SAM/include/utility.h 파일을 수정하면 되는데 #include <opencv2/opencv.hpp> 구문을 라인 33번(#include <pcl_conversions/pcl_conversions.h> 밑)으로 옮기게 되면 정상적으로 빌드되는 것을 확인하였다.

이를 바탕으로 의존성 순서 문제로 패키지가 정상적으로 불러와지지 않아서 발생하는 것이라고 추측하였다.

여기까지 문제없이 진행되었다면 이제 알고리즘을 돌리기 위한 기본 설정은 끝난 것이다.


번외 1. DSO missing from command line 에러

빌드 과정에서 프로젝트 초기에 발생하여 나를 4일동안 삽질하게 만들었던 에러이다.
패키지의 경로 설정이나 버전 충돌 문제인 듯한데 구글링 결과 너무나도 다양한 방법이 있었지만, 해결되는 방법은 없었다.
나의 경우 우분투를 새로 설치하는 것으로 문제를 해결하였다. (부팅 USB를 다시 꺼내보자..)

번외2. Could not found tf 에러

사실 이 에러는 대부분의 사람에게 발생하지 않을 것이다. 나도 도대체 왜 발생한 줄 모르겠는 에러로 그냥 ros 설치가 날아간 건지 모든 ros 명령어가 인식되지 않았다. 간단히 ros 재설치로 해결하였다.

LIO-SAM 테스트


이제 빌드된 코드가 정상적으로 작동하는지 확인해 보려고 한다.

Github 레포지토리를 확인해 보면 다양한 샘플 데이터를 제공하고 있다. 이 데이터를 가지고 테스트를 진행해 보자.

테스트는 3가지의 데이터셋을 가지고 진행해 보려고 한다. 사용할 데이터셋은 여기서 다운로드 받으면 된다.

0. 기본 설정

테스트를 시작하기 전에 처음 ROS 설치 후에 source 명령어를 입력하였던 것을 기억할 것이다.
이와 마찬가지로 빌드한 소스와 경로를 시스템에 적용하기 위해서는 동일하게 source 명령어를 작성하여야 한다.

cd ~/catkin_ws
source devel/setup.bash

해당 명령어를 작성하게 되면 빌드한 파일과 경로가 시스템에 적용되어 실행할 수 있게 된다.

1. Velodyne Park Dataset

처음으로 실행해 볼 데이터셋은 Park Dataset이다.

roslaunch lio-sam run.launch

데이터셋을 다운로드한 후 LIO-SAM을 실행시킨다.

rosbag play {파일 경로}/park_dataset.bag -r 3

이후 위 명령어를 실행시키면 LIO-SAM이 정상적으로 동작하는 것을 확인할 수 있다.

(비록 테스트 데이터지만 뭔가 작동하니까 행복하지 않은가? 소소한 행복을 함께 느껴보자.)

2. Velodyne Campus Dataset (small)

다음으로는 Campus 데이터셋이다. 이 데이터셋은 실행해 보기 전 약간의 설정 수정이 필요했다.

위에서 실행한 데이터셋은 IMU 데이터가 정확한 포맷으로 들어오지 않아 내부적으로 변환이 필요하였지만, 이 데이터셋은 IMU 데이터를 ROS REP105 standard 포맷으로 보내주어 따로 내부적인 변환이 필요 없다고 한다.

따라서 데이터셋을 실행하기 위해서는 LIO-SAM/config/params.yaml 에서 imuTopic 값을 imu_correct 로 변경하고 extrinsicRotextrinsicRPY를 단위행렬로 변경해 주어야 한다.

이후 위에서와 동일하게 LIO-SAM을 실행시키고 데이터셋을 재생해 주면 된다.

roslaunch lio-sam run.launch
rosbag play {파일 경로}/campus_small_dataset.bag -r 3

그러면 이렇게 결과가 나올 것이다!

3. Ouster Rooftop Dataset

마지막으로 실행시켜 볼 데이터셋은 Ouster 라이다로 측정된 Rooftop 데이터셋이다.

위의 두 데이터셋은 Velodyne으로 측정 되었지만 이 데이터셋은 Ouster 라이다로 측정되었다.

이 데이터셋을 실행시켜보기 위해서는 LIO-SAM/config/params.yaml 에서 라이다 설정을 변경해 주어야 한다.

먼저 sensor 값을 ouster 로 변경하고 N_SCAN 값을 128, Horizon_SCAN 값을 1024로 변경해준다.

이후에 위에서 2번째 데이터셋을 테스트할 때 변경하였던 설정을 다시 원상복귀 해주어야 한다.
만약 2번 데이터셋을 실행하지 않았다면 넘어가도 된다.

다 수정하였다면 이제 실행해보도록 하자.

roslaunch lio-sam run.launch
rosbag play {파일 경로}/rooftop_ouster_dataset.bag -r 3

그럼 이런 결과가 나온다.

LIO-SAM 데이터 수집


이제 제공되는 데이터셋만 돌리던 안락한 생활은 끝났다. 이제 실제 센서로 데이터를 수집할 차례이다.

0. 센서 드라이버 설치

나는 이 프로젝트에서는 Ouster 라이다, iAHRS IMU 센서를 사용하였다.

이 센서들을 사용하기 위해서는 ROS 드라이버 설치가 필요하다.

1. Ouster LiDAR

먼저 Ouster LiDAR이다. 라이다를 컴퓨터에 연결하고 데이터를 받아오는 부분은 이미 많은 사람들이 다루었기 때문에 따로 설명하지는 않겠다. 나는 이 블로그 글을 참고하였다. 자세한 부분까지 잘 설명되어 있어 쉽게 할 수 있을 것이다.

마찬가지로 설치를 완료하고 센서가 잘 돌아가는지 확인하려면 아래 명령어를 입력해보면 된다.

roslaunch ouster_ros sensor.launch

그럼 자동으로 rviz가 실행되면서 PCD 데이터를 시각화 해줄 것이다.

위의 LIO-SAM에서 처럼 새로운 터미널을 열었다면 source 명령어를 입력하는 것을 잊지 말자.

cd ~/catkin_ws
source devel/setup.bash

2. iAHRS IMU

다음으로는 IMU 센서의 드라이버를 설치하여야 한다. 마찬가지로 드라이버 github에 잘 정리되어 있다.
절대 귀찮아서 설명하지 않는 것은 아니다.

1. Ouster LiDAR + iAHRS IMU

드라이버 설치가 끝났다면 이제 실제 데이터를 수집해보도록 하자.

데이터를 수집하기 위해서는 LIO-SAM의 설정 파일을 변경해주어야 한다.
여기서 변경하는 값들은 개인의 센서에 따라 달라질 수 있으니 확인 후 변경해주어야 한다.

먼저 pointCloudTopicouster/points로 변경해 주었다.
그리고 imuTopicimu/data로 변경하였다.

다음으로는 sensorouster로 변경하였다.
우리는 OS1-32-U 모델을 사용하고 있었기 때문에 N_SCAN32Horizon_SCAN1024로 설정하였다.

실행하기 전에 위에서 설치한 ouster_rossensor.launch 파일에서도 동일하게 설정해주자.

이제 두근거리는 마음으로 SLAM을 돌려보자. 과연 성공적으로 작동할까?

에러1 : Point cloud is not in dense format

당연하게도 한 번에 될리가 없다. 에러 메세지를 한 번 확인해보자.

[lio_sam_imageProjection-2] restarting process
process[lio_sam_imageProjection-2]: started with pid [17045]
[ INFO] [1743588602.428283737]: ----> Image Projection Started.
[ERROR] [1743588602.844451925]: Point cloud is not in dense format, please remove NaN points first!

ouster 라이다에서 내보내는 is dense 항목이 false라고 한다.

rostopic echo /ouster/points/is_dense -n 30

위 명령어를 실행하면 현재 발행중인 ouster 라이다의 토픽을 볼 수 있다.

안타깝게도 정말 False가 뜨고 있다.
테스트 데이터도 확인해보자.

rosbag play -r 3 dataset/rooftop_ouster_dataset.bag
rostopic echo /points_raw/is_dense -n 3

테스트 데이터에서는 정상적으로 True가 뜨는 것을 확인할 수 있다.

해당 오류를 일으키는 코드를 확인해보면 imageProjection.cpp 파일에서 해당 예외를 처리하고 있다. 코드대로라면 들어온 데이터에서is_dense 값을 뽑아서 처리한다는 것을 알 수 있다.

그렇다면 저 is_dense 값은 라이다에서 생성하여 토픽으로 넘겨준다는 것이다. 해당 값을 생성하는 코드를 찾아보자.

ouster_ros 패키지의 point_cloud_compose.h파일을 확인해보면 코드가 있다. is_dense는 포인트 측정 간에 값 중 NaN 값이 들어있으면 false로 판단되는 것이다.

그렇다면 측정값에서 NaN을 없애면 되는 것 아니겠는가? 사실 공식 SDK를 수정하는 것이 맞는 선택일지는 잘 모르겠지만 지금 당장은 다른 방법이 없으니 한 번 해보도록 하자. 일단 돌아가면 장땡 아니겠는가?

for (int i = 0; i < 3; ++i) {
    if (std::isnan(xyz(i))) {
        xyz(i) = 0.0f;
    }
}

위와 같이 값에 필터를 씌워 NaN 값을 강제로 0으로 대체했다.

글로 보았을 때에는 금방 해결한 것 처럼 보이지만 사실 엄청난 삽질이 있었다. 하지만 반쯤은 뻘짓이었으니 넘어가도록 하겠다. 어쨌든 돌아가는 하나의 경우를 발견하였지 않은가?

에러2 : IMU 센서 축 설정 문제

이후 코드를 돌려보면 rviz 화면에 뭐가 뜨기 시작한다!

여기서부터는 사용하는 장비에 따라 정상적으로 코드가 실행되는 사람도 있을 것이고 나와 같이 별자리마냥 요상한 그림이 그려지고 있는 사람이 있을 수도 있다. 혹은 뭔가 뜨긴 하는데 공간이 뒤틀리고 있는 사람도 있을 것이다.

만약 한 번에 정상적으로 뜨는 사람들은 축하한다. 더 이상 글을 읽을 필요가 없다. 성공을 자축하며 떠나길 바란다.

그러나 그렇지 않은 사람은 나와 함께 조금 더 달려가보자.

참고로 아래는 나의 결과 화면이다. 별자리가 하나 생겼다.

엄청난 삽질 끝에 알아낸 원인은 imu 설정 문제였다. 위 화면에서 정상적인 결과가 나온 사람들은 운이 좋게도 imu 센서의 축 정렬이 설정된 값과 동일하였던 것이다.

깃허브에 보면 IMU 센서 축 관련 내용이 있다.

바로 이 화면이다.

여기서는 라이다의 축과 부착된 IMU 축이 달라서 따로 설정을 해줬다. 위에서 2번 데이터셋을 실행할 때 extrinsicRotextrinsicRPY를 설정한걸 기억하는가? 그것이 IMU 센서의 축을 라이다와 동일하게 맞춘 것이다.

그래서 우리는 extrinsicRotextrinsicRPY를 우리 IMU 센서에 맞게 축을 수정해주어야 한다. 만약 IMU 센서와 라이다의 축이 동일하다면 아래와 같이 단위행렬로 변경하면 된다.

그리고 실행해보면 별자리가 사라지고 나름 맵의 형태를 띄는 것을 볼 수 있다.

에러3 : IMU 센서 드리프트 현상

그러나 나의 경우에는 IMU 센서의 성능 문제로 지속적인 드리프트가 발생하였다. 위 사진이 약간 이상한 것도 드리프트가 발생해서 그렇다. 이 문제는 imu_utils 툴을 사용하여 imu 센서의 노이즈 값을 찾은 후 파라미터를 수정해주어야 한다.

자세한 적용 방식은 위의 깃허브 페이지에 자세히 설명되어 있으니 현재는 설명하지 않고 추후에 기회가 된다면 추가하도록 하겠다.

결론


결론적으로 나는 노이즈 값을 찾아서 적용하였지만 센서의 성능 문제인지 계속 드리프트가 일어나 IMU를 사용하는 SLAM 알고리즘을 포기하고 KISS-SLAM을 사용하였다. 이건 파이썬 기반이라 버전 탈 염려도 없고 잘 돌아간다... 만세! ROS와 연동하는 문제가 있긴 하지만 이건 KISS-ICP를 사용하거나 직접 ROS 패키지를 만들면 해결되는 문제이다.

이렇게 LIO-SAM을 적용해보려던 시도를 한 번 적어보았다. 결론적으로는 다른 알고리즘을 사용하게 되었지만 시도한 과정을 정리할 겸 누군가에게 도움이 될까 하여 포스트를 정리해본다. 다들 즐겁고 막힘없는 코딩 생활 되길 바란다.

0개의 댓글