<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>남소금</title>
    <link>https://2nnsv.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 26 May 2026 13:50:28 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>소-은</managingEditor>
    <image>
      <title>남소금</title>
      <url>https://tistory1.daumcdn.net/tistory/5352326/attach/67137a42a5bf4d4dadaa25c14a60322a</url>
      <link>https://2nnsv.tistory.com</link>
    </image>
    <item>
      <title>[SLAM] Ouster 데이터셋으로 LIO-SAM 구현하기</title>
      <link>https://2nnsv.tistory.com/225</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;환경: Ubuntu 22.04&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;ROS2 humble&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. driver 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;본인이 사용할 센서에 맞게 드라이버를 설치해야 한다. 이 논문은 Ouster lidar와 Xsens IMU, SBG Systems IMU를 기반으로 테스트되었기 때문에 드라이버를 설치해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Ouster Driver&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;a href=&quot;https://github.com/ros-drivers/ros2_ouster_drivers&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/ros-drivers/ros2_ouster_drivers&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1741134710712&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - ros-drivers/ros2_ouster_drivers: ROS2 Drivers for the Ouster OS-0, OS-1, and OS-2 Lidars&quot; data-og-description=&quot;ROS2 Drivers for the Ouster OS-0, OS-1, and OS-2 Lidars - ros-drivers/ros2_ouster_drivers&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/ros-drivers/ros2_ouster_drivers&quot; data-og-url=&quot;https://github.com/ros-drivers/ros2_ouster_drivers&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bRgIYq/hyYm9RwKjn/dehHFpaFsKL2FPNEjKP5gk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/6nzmq/hyYnbPjYQI/sw3EFEWcwV6HklP7bcM7kk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/ros-drivers/ros2_ouster_drivers&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/ros-drivers/ros2_ouster_drivers&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bRgIYq/hyYm9RwKjn/dehHFpaFsKL2FPNEjKP5gk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/6nzmq/hyYnbPjYQI/sw3EFEWcwV6HklP7bcM7kk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - ros-drivers/ros2_ouster_drivers: ROS2 Drivers for the Ouster OS-0, OS-1, and OS-2 Lidars&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;ROS2 Drivers for the Ouster OS-0, OS-1, and OS-2 Lidars - ros-drivers/ros2_ouster_drivers&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741134759678&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd ~/ros2_ws/src 
git clone https://github.com/ros-drivers/ros2_ouster_drivers
colcon build&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Xsens Driver 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;a href=&quot;https://github.com/bluespace-ai/bluespace_ai_xsens_ros_mti_driver/tree/ros2_0_galactic&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/bluespace-ai/bluespace_ai_xsens_ros_mti_driver/tree/ros2_0_galactic&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1741135209820&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - bluespace-ai/bluespace_ai_xsens_ros_mti_driver: xsens ros2 driver&quot; data-og-description=&quot;xsens ros2 driver. Contribute to bluespace-ai/bluespace_ai_xsens_ros_mti_driver development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/bluespace-ai/bluespace_ai_xsens_ros_mti_driver/tree/ros2_0_galactic&quot; data-og-url=&quot;https://github.com/bluespace-ai/bluespace_ai_xsens_ros_mti_driver&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cF4exv/hyYnbhuIXp/DPT67jqxydIjOIDx6xNXkK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/QGWfo/hyYmJyB2KE/k1VlEJcZ0XzTxSqFAWAlIk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/bluespace-ai/bluespace_ai_xsens_ros_mti_driver/tree/ros2_0_galactic&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/bluespace-ai/bluespace_ai_xsens_ros_mti_driver/tree/ros2_0_galactic&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cF4exv/hyYnbhuIXp/DPT67jqxydIjOIDx6xNXkK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/QGWfo/hyYmJyB2KE/k1VlEJcZ0XzTxSqFAWAlIk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - bluespace-ai/bluespace_ai_xsens_ros_mti_driver: xsens ros2 driver&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;xsens ros2 driver. Contribute to bluespace-ai/bluespace_ai_xsens_ros_mti_driver development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741135222991&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd ~/ros2_ws/src
git clone https://github.com/bluespace-ai/bluespace_ai_xsens_ros_mti_driver/tree/ros2_0_galactic
colcon build&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SBG-Systems Driver&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;a href=&quot;https://github.com/SBG-Systems/sbg_ros2_driver&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/SBG-Systems/sbg_ros2_driver&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1741135251313&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - SBG-Systems/sbg_ros2_driver: ROS 2 driver for SBG Systems IMU/AHRS/INS units such as ELLIPSE or QUANTA.&quot; data-og-description=&quot;ROS 2 driver for SBG Systems IMU/AHRS/INS units such as ELLIPSE or QUANTA. - SBG-Systems/sbg_ros2_driver&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/SBG-Systems/sbg_ros2_driver&quot; data-og-url=&quot;https://github.com/SBG-Systems/sbg_ros2_driver&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dcSTcg/hyYm70sXae/bQCpwI07PdKdnjzHODFEU1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/hv3wL/hyYndsQQ8R/nMmFosxTIvdkbTcEd1cLUk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/SBG-Systems/sbg_ros2_driver&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/SBG-Systems/sbg_ros2_driver&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dcSTcg/hyYm70sXae/bQCpwI07PdKdnjzHODFEU1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/hv3wL/hyYndsQQ8R/nMmFosxTIvdkbTcEd1cLUk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - SBG-Systems/sbg_ros2_driver: ROS 2 driver for SBG Systems IMU/AHRS/INS units such as ELLIPSE or QUANTA.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;ROS 2 driver for SBG Systems IMU/AHRS/INS units such as ELLIPSE or QUANTA. - SBG-Systems/sbg_ros2_driver&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741135314048&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt-get install ros-galactic-sbg-driver
cd ~/ros2_ws/src
git clone https://github.com/SBG-Systems/sbg_ros2_driver.git
cd sbg_ros2_driver
rosdep update
rosdep install --from-path .
cd ../..
colcon build
source install/setup.bash&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Dependencies&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ROS2&lt;/h3&gt;
&lt;pre id=&quot;code_1741135476026&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt install ros-humble-perception-pcl \
  	   ros-humble-pcl-msgs \
  	   ros-humble-vision-opencv \
  	   ros-humble-xacro&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;gtsam&lt;/h3&gt;
&lt;pre id=&quot;code_1741135493144&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo add-apt-repository ppa:borglab/gtsam-release-4.1
sudo apt install libgtsam-dev libgtsam-unstable-dev&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. LIO-SAM 빌드&lt;/h2&gt;
&lt;pre id=&quot;code_1741135509831&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd ~/ros2_ws/src
git clone https://github.com/TixiaoShan/LIO-SAM.git
cd LIO-SAM
git checkout ros2
cd ..
colcon build&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. lidar data 주의점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;imageProjection.cpp에 사용될 포인트 클라우드를 준비할 때 주의할 점이 있다. LIO-SAM은 포인트 클라우드 deskew(비뚤어진 이미지를 고쳐줌)하기 위해서 IMU 센서 데이터를 사용한다. 그래서 한 scan에서의 상대적인 포인트 시간 정보를 알아야 한다. imageProjection.cpp의 deskewPoint() 함수에서 시간 데이터를 사용해서 스캔 시작점 대비 포인트의 변환 정보를 계산한다. 참고로 Velodyne의 최신 ROS driver는 정보를 제공하고 있어 따로 수정할 필요가 없다. &lt;b&gt;다른 센서의 경우에는 time channel을 수정해주어야 한다. &lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;LIO-SAM은 포인트를 행렬 형태로 저장하는데 포인트 ring number이 필요하다. 마찬가지로 Velodyne driver는 자동으로 제공하는데, &lt;b&gt;다른 센서의 경우, ring number를 확인해 알맞게 수정해야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;본인은 ros1 환경에서 Ouster사의 OS1-64 센서 + Xsens IMU(9 axis)로 측정된 bag 파일을 사용했다. &lt;b&gt;주의할 점은 Ouster 내부에 부착된 IMU는 6축이기 때문에 LIO-SAM을 사용할 수 없다. 9축 IMU 센서를 추가로 설치하거나 &lt;a href=&quot;https://drive.google.com/drive/folders/1gJHwfdHCRdjP7vuT556pv8atqrCJPbUq&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;에서 데이터를 받아 사용할 수 있다.&lt;/b&gt;(Ouster 데이터는 rooftop_ouster_dataset.bag)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. IMU data 주의점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 센서에 맞게 코드 수정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;params.yaml&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;가지고 있는 bag 파일을 실행시켜 어떤 토픽을 발행하는지 확인하고, 토픽에 맞게 params.yaml을 수정할 거다.&lt;/p&gt;
&lt;pre id=&quot;code_1741141601978&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 topic list&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;내가 가진 센서는 /os1_points와 /imu/data_raw 토픽을 발행하고 있어 각각 바꾸어 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그리고 # Sensor Setting 부분에서 sensor 명, N_SCAN, Horizon SCAN을 변경해야 한다. 본인은 os1-64이기 때문에 제품 상세 설명을 보고 변경했다. 구글링하면 제품 상세 스펙이나 다른 알고리즘에 사람들이 os1-64 스펙에 맞게 적용해놓은 걸 찾을 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1741141662847&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# params.yaml

# Topics
    pointCloudTopic: &quot;/os1_points&quot;                   # Point cloud datas
    imuTopic: &quot;/imu/data_raw&quot;                        # IMU data
    # odomTopic: &quot;/os1_imu&quot;                    # IMU pre-preintegration odometry, same frequency as IMU
    gpsTopic: &quot;odometry/gpsz&quot;                    # GPS odometry topic from navsat, see module_navsat.launch file

    # Frames
    lidarFrame: &quot;lidar_link&quot;
    baselinkFrame: &quot;base_link&quot;
    odometryFrame: &quot;odom&quot;
    mapFrame: &quot;map&quot;

    # GPS Settings
    useImuHeadingInitialization: false           # if using GPS data, set to &quot;true&quot;
    useGpsElevation: false                       # if GPS elevation is bad, set to &quot;false&quot;
    gpsCovThreshold: 2.0                         # m^2, threshold for using GPS data
    poseCovThreshold: 25.0                       # m^2, threshold for using GPS data

    # Export settings
    savePCD: false                               # https://github.com/TixiaoShan/LIO-SAM/issues/3
    savePCDDirectory: &quot;/Downloads/LOAM/&quot;         # in your home folder, starts and ends with &quot;/&quot;. Warning: the code deletes &quot;LOAM&quot; folder then recreates it. See &quot;mapOptimization&quot; for implementation

    # Sensor Settings
    sensor: ouster                               # lidar sensor type, either 'velodyne', 'ouster' or 'livox'
    N_SCAN: 64                                   # number of lidar channels (i.e., Velodyne/Ouster: 16, 32, 64, 128, Livox Horizon: 6)
    Horizon_SCAN: 1024                            # lidar horizontal resolution (Velodyne:1800, Ouster:512,1024,2048, Livox Horizon: 4000)
    downsampleRate: 1                            # default: 1. Downsample your data if too many
    # points. i.e., 16 = 64 / 4, 16 = 16 / 1
    lidarMinRange: 1.0                           # default: 1.0, minimum lidar range to be used
    lidarMaxRange: 1000.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;imageProjection.cpp&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;앞서 말했던 것처럼 Velodyne이 아닌 이상 센서에 맞게 time과 ring을 변경해야 한다. 다행이게도 Ouster 구조체가 작성되어 있어 PintXYZIRT를 Velodyne에서 Ouster 구조체로 변경해주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1741141835368&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Use the Velodyne point format as a common representation
using PointXYZIRT = OusterPointXYZIRT; // changed into Ouster&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;구조체가 변경되었기 때문에, PointXYZIRT로 선언된 변수의 멤버도 바꾸어 줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;catchCloudPoint() 함수 내부에 PointXYZIRT로 선언된 dst가 time으로 설정되어 있는데, t로 바꿔준다.&lt;/p&gt;
&lt;pre id=&quot;code_1741142103579&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dst.t = src.t * 1e-9f;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;찾아보니 laserCloudIn으로 선언된 것들은 모두 time이라는 멤버를 참조하고 있어 Ctrl+F로 laserCloudIn을 검색해 time으로 참조한 경우 모조리 t로 바꾸어 주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1741142263762&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 266번째 줄
timeScanEnd = timeScanCur + laserCloudIn-&amp;gt;points.back().t;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1741142236485&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 612번째 줄
thisPoint = deskewPoint(&amp;amp;thisPoint, laserCloudIn-&amp;gt;points[i].t);&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 실행&lt;/h2&gt;
&lt;pre id=&quot;code_1741141526576&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 launch lio_sam run.launch.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741141536143&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 bag play your-bag&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>ROS</category>
      <author>소-은</author>
      <guid isPermaLink="true">https://2nnsv.tistory.com/225</guid>
      <comments>https://2nnsv.tistory.com/225#entry225comment</comments>
      <pubDate>Wed, 5 Mar 2025 13:53:35 +0900</pubDate>
    </item>
    <item>
      <title>[ROS2] Nav2를 사용한 자율주행 테스트</title>
      <link>https://2nnsv.tistory.com/224</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;환경: Ubuntu 22.05&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;ros2 humble&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;이전 글에 이어 Nav2를 사용해서 Navigating을 실제로 하려고 한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;a href=&quot;https://2nnsv.tistory.com/223&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://2nnsv.tistory.com/223&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1741074265797&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[ROS2] Cartographer SLAM&quot; data-og-description=&quot;환경: Ubuntu 22.05&amp;nbsp;ros2 humble&amp;nbsp;&amp;nbsp;먼저 navigation2를 사용하기 위해서 아래 명령어를 통해 필요한 패키지를 설치한다.sudo apt install ros-humble-navigation2 ros-humble-nav2-bringup&amp;nbsp;가제보로 시뮬레이션을 돌리고 싶&quot; data-og-host=&quot;2nnsv.tistory.com&quot; data-og-source-url=&quot;https://2nnsv.tistory.com/223&quot; data-og-url=&quot;https://2nnsv.tistory.com/223&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dkWGbG/hyYm7MLaOK/zfbs1XZjAKgni5lYnWSmA0/img.png?width=800&amp;amp;height=925&amp;amp;face=0_0_800_925,https://scrap.kakaocdn.net/dn/cwv6B2/hyYjlMqRSO/Q3lUvj7JX5EAgTZrB4Qw4K/img.png?width=800&amp;amp;height=925&amp;amp;face=0_0_800_925,https://scrap.kakaocdn.net/dn/dWU40j/hyYmKc1069/owTZmQdh0UDb1OeczFqYgK/img.png?width=2431&amp;amp;height=1134&amp;amp;face=0_0_2431_1134&quot;&gt;&lt;a href=&quot;https://2nnsv.tistory.com/223&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://2nnsv.tistory.com/223&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dkWGbG/hyYm7MLaOK/zfbs1XZjAKgni5lYnWSmA0/img.png?width=800&amp;amp;height=925&amp;amp;face=0_0_800_925,https://scrap.kakaocdn.net/dn/cwv6B2/hyYjlMqRSO/Q3lUvj7JX5EAgTZrB4Qw4K/img.png?width=800&amp;amp;height=925&amp;amp;face=0_0_800_925,https://scrap.kakaocdn.net/dn/dWU40j/hyYmKc1069/owTZmQdh0UDb1OeczFqYgK/img.png?width=2431&amp;amp;height=1134&amp;amp;face=0_0_2431_1134');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[ROS2] Cartographer SLAM&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;환경: Ubuntu 22.05&amp;nbsp;ros2 humble&amp;nbsp;&amp;nbsp;먼저 navigation2를 사용하기 위해서 아래 명령어를 통해 필요한 패키지를 설치한다.sudo apt install ros-humble-navigation2 ros-humble-nav2-bringup&amp;nbsp;가제보로 시뮬레이션을 돌리고 싶&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;2nnsv.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;먼저 환경 변수 설정을 해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1741074322386&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;source /opt/ros/humble/setup.bash 
export TURTLEBOT3_MODEL=waffle&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;하던 중에 아래와 같은 오류가 발생했다. 실제로 frame을 찍어보면 odom이 없다.&lt;/p&gt;
&lt;pre id=&quot;code_1741074364745&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;local_costmap.local_costmap]: Timed out waiting for transform from base_link to odom to become available, tf error: Invalid frame ID &quot;odom&quot; passed to canTransform argument target_frame - frame does not exist.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2111&quot; data-origin-height=&quot;1083&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yv0R2/btsMCb0IYtE/UuYVWlOKTxF5Jd84bk2CF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yv0R2/btsMCb0IYtE/UuYVWlOKTxF5Jd84bk2CF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yv0R2/btsMCb0IYtE/UuYVWlOKTxF5Jd84bk2CF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyv0R2%2FbtsMCb0IYtE%2FUuYVWlOKTxF5Jd84bk2CF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2111&quot; height=&quot;1083&quot; data-origin-width=&quot;2111&quot; data-origin-height=&quot;1083&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;찾아보니 gazebo 환경설정도 해주어야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1741074487905&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:/opt/ros/humble/share/turtlebot3_gazebo/models
source /usr/share/gazebo/setup.bash&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2540&quot; data-origin-height=&quot;1650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYcfrU/btsMAJj2gMG/9lw2pgezUQIwzF1JHlU0j0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYcfrU/btsMAJj2gMG/9lw2pgezUQIwzF1JHlU0j0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYcfrU/btsMAJj2gMG/9lw2pgezUQIwzF1JHlU0j0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYcfrU%2FbtsMAJj2gMG%2F9lw2pgezUQIwzF1JHlU0j0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2540&quot; height=&quot;1650&quot; data-origin-width=&quot;2540&quot; data-origin-height=&quot;1650&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이제 실행해보자. 아래 명령어를 실행하면 rviz와 gazebo가 자동적으로 실행될 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1741074575137&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 launch nav2_bringup tb3_simulation_launch.py headless:=False&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OrL3m/btsMA4nYwvK/nIbMhxWGopifvih6nnhK2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OrL3m/btsMA4nYwvK/nIbMhxWGopifvih6nnhK2K/img.png&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;2196&quot; data-is-animation=&quot;false&quot; width=&quot;600&quot; height=&quot;694&quot; style=&quot;width: 39.7558%; margin-right: 10px;&quot; data-widthpercent=&quot;40.22&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OrL3m/btsMA4nYwvK/nIbMhxWGopifvih6nnhK2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOrL3m%2FbtsMA4nYwvK%2FnIbMhxWGopifvih6nnhK2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1898&quot; height=&quot;2196&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WcZh2/btsMAISXQ9E/wZgvj2dnKkJXLc58xack8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WcZh2/btsMAISXQ9E/wZgvj2dnKkJXLc58xack8K/img.png&quot; data-origin-width=&quot;1585&quot; data-origin-height=&quot;1234&quot; data-is-animation=&quot;false&quot; style=&quot;width: 59.0814%;&quot; data-widthpercent=&quot;59.78&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WcZh2/btsMAISXQ9E/wZgvj2dnKkJXLc58xack8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWcZh2%2FbtsMAISXQ9E%2FwZgvj2dnKkJXLc58xack8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1585&quot; height=&quot;1234&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. Rviz의 상단부에 &lt;b&gt;2D Pose&lt;/b&gt; &lt;b&gt;Estimate&lt;/b&gt;로 로봇의 초기 위치를 설정해주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;마음대로 설정하는게 아니라 gazebo 로봇의 현재 위치에 맞게 설정해야 한다. 2D Pose Estimate를 누른 다음, 지도 내 한 군데를 클릭하면 설정된다. 이렇게 설정하지 않고 마음대로 설정하면 gazebo에서의 위치와 rviz에서의 위치가 얽히면서 제대로 동작하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2. Rvix의 상단부에 &lt;b&gt;Nav2 Goal&lt;/b&gt;을 사용해 로봇의 목표 위치를 설정해준다.&lt;br /&gt;본인이 설정한 초기 위치에서 목표 위치까지 알아서 이동하는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4523&quot; data-origin-height=&quot;2136&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEVVeW/btsMAljrgKw/9tABzuFdKvnDlGiBSbk740/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEVVeW/btsMAljrgKw/9tABzuFdKvnDlGiBSbk740/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEVVeW/btsMAljrgKw/9tABzuFdKvnDlGiBSbk740/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEVVeW%2FbtsMAljrgKw%2F9tABzuFdKvnDlGiBSbk740%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4523&quot; height=&quot;2136&quot; data-origin-width=&quot;4523&quot; data-origin-height=&quot;2136&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>ROS</category>
      <author>소-은</author>
      <guid isPermaLink="true">https://2nnsv.tistory.com/224</guid>
      <comments>https://2nnsv.tistory.com/224#entry224comment</comments>
      <pubDate>Tue, 4 Mar 2025 16:54:12 +0900</pubDate>
    </item>
    <item>
      <title>[ROS2] Cartographer SLAM</title>
      <link>https://2nnsv.tistory.com/223</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;환경: Ubuntu 22.05&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;ros2 humble&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;먼저 navigation2를 사용하기 위해서 아래 명령어를 통해 필요한 패키지를 설치한다.&lt;/p&gt;
&lt;pre id=&quot;code_1741054405544&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt install ros-humble-navigation2 ros-humble-nav2-bringup&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;가제보로 시뮬레이션을 돌리고 싶으면 아래 명령어로 설치할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1741064742872&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt install ros-humble-turtlebot3*&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Nav2로 맵을 만들기 전에 먼저 nav가 내 환경에서 잘 작동하도록 설정해주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;turtlebot3 model 변수를 설정해준다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741065109640&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export TURTLEBOT3_MODEL=waffle&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;다음 Gazebo가 제대로 작동하는지 확인해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1741067629509&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;2196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/85JsI/btsMzdTL9NY/08nTQzDWr691vNCTuesC0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/85JsI/btsMzdTL9NY/08nTQzDWr691vNCTuesC0K/img.png&quot; data-alt=&quot;.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/85JsI/btsMzdTL9NY/08nTQzDWr691vNCTuesC0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F85JsI%2FbtsMzdTL9NY%2F08nTQzDWr691vNCTuesC0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;694&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;2196&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Gazebo가 올바르게 동작하니까 이제 Cartographer SLAM을 위한 준비가 다 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;먼저 turtlebot을 켠다.&lt;/p&gt;
&lt;pre id=&quot;code_1741067810107&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그리고 SLAM을 작동시킨다. 참고로, use_sim_time은 시뮬레이션을 하고 있기 때문에 Gazebo 시간을 사용해야 한다. 실제 로봇인 경우에는 사용하지 않아도 된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741067832839&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 launch turtlebot3_cartographer cartographer.launch.py use_sim_time:=True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이제 로봇을 직접 움직여서 map을 그려보자. W A S D X로 조종할 수 있는데, 선속도와 각속도를 조종하기 때문에 화살표키로 제어하는 것과는 달라 주의해야 한다. a, d로 각속도를 조정, w와 x로 선속도를 조정할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741067919892&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 run turtlebot3_teleop teleop_keyboard&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2431&quot; data-origin-height=&quot;1134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyjv6g/btsMzqSXyKg/1Mfz0KYo5k2uqOdiINLfMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyjv6g/btsMzqSXyKg/1Mfz0KYo5k2uqOdiINLfMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyjv6g/btsMzqSXyKg/1Mfz0KYo5k2uqOdiINLfMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdyjv6g%2FbtsMzqSXyKg%2F1Mfz0KYo5k2uqOdiINLfMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2431&quot; height=&quot;1134&quot; data-origin-width=&quot;2431&quot; data-origin-height=&quot;1134&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;벽에 부딪치거나 너무 빠른 속도로 이동하면 맵이 일그러지기 때문에 조심해야 한다. 나는 조종이 어려워 기둥에 부딪치기도 했다. 로봇이 더이상 움직일 수 없는 상황이라고 판단되면 아예 처음부터 다시 실행하는게 좋다. 재실행을 했지만 로봇을 움직일 수 없는 경우에는 로봇 위치를 reset 해주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1741068164504&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 service call /reset_simulation std_srvs/srv/Empty&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이제 맵이 다 만들어졌다고 판단되면, 아래 명령어를 사용해 map을 저장할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1741067995148&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 run nav2_map_server map_saver_cli -f my_map&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;260&quot; data-origin-height=&quot;261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQu3oZ/btsMBY1zvQp/AVIAUr0B2VqMuXmXYbI3Bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQu3oZ/btsMBY1zvQp/AVIAUr0B2VqMuXmXYbI3Bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQu3oZ/btsMBY1zvQp/AVIAUr0B2VqMuXmXYbI3Bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQu3oZ%2FbtsMBY1zvQp%2FAVIAUr0B2VqMuXmXYbI3Bk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;260&quot; height=&quot;261&quot; data-origin-width=&quot;260&quot; data-origin-height=&quot;261&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>ROS</category>
      <author>소-은</author>
      <guid isPermaLink="true">https://2nnsv.tistory.com/223</guid>
      <comments>https://2nnsv.tistory.com/223#entry223comment</comments>
      <pubDate>Tue, 4 Mar 2025 15:03:46 +0900</pubDate>
    </item>
    <item>
      <title>[논문 리뷰] LeGO-LOAM: Lightweight and Ground-Opimized Lidar Odometry and Mapping on Variable Terrain</title>
      <link>https://2nnsv.tistory.com/222</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Abstract&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;ground plane을 활용해 기존의 LOAM보다 가벼운 real time 6자유도 pose estimation 방법을 소개한다. LeGO-LOAM은 저전력 임베디드 시스템에서 사용할 수 있을 정도로 가볍게 실시간 위치 추정이 가능하다. 또한 segmentation과 optimization 과정에서 ground plane을 활용하는 특징이 있다. 전체 과정을 간단하게 설명하자면, noise를 1차적으로 거르고, planar과 edge feature을 추출한다. 2 step의 Levenberg-Marquardt 최적화 과정에서 planar과 edge feature을 사용해 연속적인 스캔 데이터에서 서로 다른 컴포넌트를 구분한다. 그리고 LOAM과의 성능 비교를 위해서 다양한 지형에서 테스트하고, LeGO-LOAM이 LOAM에 비해 적은 연산으로 비슷하거나 더 좋은 정확도를 가진다는 것을 선보일 것이다. 또한 LeGO-LOAM을 slam 프레임워크와 통합하여 위치 추정에 있어 drift error를 제거한다는 것을 KITTI Dataset을 사용해 증명할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Introduction&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;비전이나 라이다 기반으로 6자유도 실시간 지도 작성 및 위치 추정에 많은 노력을 기울여왔다. 비전 기반 방법은 loop closure detection에 장점이 있지만 조명과 view point 변경에 대해 예민하기 때문에, 센서 하나로는 어려움이 있다. 반면 라이다 기반 방법은 밤에는 물론 작동되고 높은 해상도 덕분에 먼 거리까지 정확하게 환경에 대한 정보를 얻어올 수 있다. 그래서 이 논문은 3D 라이다 기반의 실시간 위치 추정과 지도 작성에 대해서 다룰 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;LOAM은 스캔 데이터에서 들어온 포인트들 간의 상관관계를 구하기 위해서 edge와 planar feature을 추출한다. 이 feature들은 해당 local region에서 포인트의 roughness를 계산해서 추출된다. roughness가 큰 값은 edge feature, 작은 값은 planar feature로 구분된다. 실시간 성능을 높이기 위해서 두 가지 알고리즘을 사용하는데, 한 알고리즘은 높은 frequency로 센서의 속도를 측정한다. 나머지 한 알고리즘은 낮은 frequency로 움직임을 추정한다. 이 두 가지 알고리즘의 결과로 높은 정확도, 높은 주기의 motion estimation이 추출된다. LOAM의 정확도는 라이다만을 기반으로 추정된 방법으로 성능이 증명되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이 논문에서는 신뢰성 있고, 실시간 6 자유도 자세 추정을 목표로 한다. 이 방법은 작은 규모의 임베디드 시스템에서도 돌아갈 수 있도록 가볍게 하는 것을 목표로 한다. 또한 서스펜션이 없거나 연산량이 많은 유닛에서 얻어진 데이터에서도 신뢰성 있도록 보장하는 것이다. 빠르거나 큰 움직임 때문에 연속적인 스캔 데이터에서 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;신뢰성 있는 특징점 상관관계를 얻기 어려운 경우도 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;아무튼, LeGO-LOAM에서는 ground에서 planar feature을 첫 번째로 추출하고, segmentation 단계를 거친 point cloud에서 edge feature을 추출할 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;System Hardware&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Lightweight Lidar Odometry and Mapping&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;System Overview&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;전체적인 시스템의 흐름은 아래 그림과 같다. 3D 라이다에서 데이터를 얻어 5가지 과정을 통해 6 자유도 pose estimation을 결과를 추출한다. 5가지 과정은 [segmentation-feature extraction-lidar odometry-lidar mapping-transform mapping-transform integration]이다. 먼저 segmentation 과정에서 입력된 포인트 클라우드를 range image*에 투영하여 feature extraction 과정으로 전달된다. feature extraction에서는 스캔 데이터에서 transform을 찾기 위해서 feature이 추출된다. lidar odometry 과정에서는 이전에 얻은 feature을 사용하며, feature들은 lidar mapping 과정에서 global point cloud map으로 프로세싱된다. 마지막으로 transform integration에서 lidar odometry와 lidar mapping 과정에서의 결과를 융합하여 pose estimation를 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VqY6w/btsMvuVI8mY/VgJ33pzxcombxpkWmfgoTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VqY6w/btsMvuVI8mY/VgJ33pzxcombxpkWmfgoTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VqY6w/btsMvuVI8mY/VgJ33pzxcombxpkWmfgoTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVqY6w%2FbtsMvuVI8mY%2FVgJ33pzxcombxpkWmfgoTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;422&quot; height=&quot;274&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;274&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*range image: 특정 위치에서 환경 속의 특정 포인트까지의 거리를 2D 이미지로 표현한 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;하나씩 자세히 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;807&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tJvfD/btsMw3ium3n/XZul5jLMal7N0gPsEG9tA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tJvfD/btsMw3ium3n/XZul5jLMal7N0gPsEG9tA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tJvfD/btsMw3ium3n/XZul5jLMal7N0gPsEG9tA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtJvfD%2FbtsMw3ium3n%2FXZul5jLMal7N0gPsEG9tA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;614&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;807&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. Segmentation&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;$P_t = {p_1, p_2, ..., p_n}$을 t 시점에 얻어진 포인트 클라우드라고 하자. $p_i$는 $P_t$ 안에 존재하는 하나의 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1) 포인트 클라우드를 1800 x 16 크기의 range image로 변환한다. 각각의 $p_i$는 range image 내에서 고유한 픽셀로 표현된다. 각 픽셀들은 센서로부터의 거리, 즉 유클리드 거리인 $r_i$를 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2) segmentation 과정 전에 ground를 먼저 extract 해야 한다. ground plane estimation은 range image의 행렬 계산을 통해서 도출된다. 그러고 나서 ground를 표현하는 점들은 ground points로 레이블링되어 segmentation에 사용되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3) image 기반 segmentation 방법을 사용해 range image에서 점 들을 여러 개의 클러스터로 그룹화한다. 같은 클러스터의 점들은 특정한 레이블을 할당받는다. 앞서 말했던 ground points도 특수 클러스터로 간주된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;4) 바람에 흔들리는 나뭇잎과 같은 작은 객체처럼 실제 환경에서는 노이즈가 많아 신뢰성이 떨어진다. 이런 객체들은 일관된 특징을 가지지 않고 다시 스캔 했을 때 같은 잎을 감지할 확률이 낮다. 빠르고 신뢰성 있는 특징 추출을 위해서 30개 미만의 점으로 구성된 클러스터는 제거한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이 과정을 통해 포인트 클라우드를 시각화한 모습은 Fig.2를 통해 확인할 수 있다. 이렇게 포인트 클라우드를 segment하면 처리 효율이 높아지고 특징 추출 정확도가 높아진다. segmentation 과정이 끝나면 Fig.2(b)에 보이는 그림처럼 큰 객체들만 이후 과정에서 사용되고 range image에 저장된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;2. Feature Extraction&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;특징점 추출 과정은 LOAM과 유사하다. 다른 점이 있다면 raw point cloud에서 특징점을 추출하는 대신에, ground points와 segmented points에서 특징점을 추출한다는 것이다. $S$를 range image에서 $p_i$가 속한 행에서 연속된 점들의 집합이다. 즉, 같은 행에 위치한 점 중에서 $p_i$를 중심으로 좌우에 일정 개수들의 점들을 포함하는 집합이다. 이 논문에서는 $|S| = 10$으로 설정하여 $p_i$ 양쪽에 각각 5개씩 총 10개의 점들을 포함한다. 이전 과정에서 계산된 거리 값($r_i$)을 사용해 $p_i$의 roughness를 평가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BnCH6/btsMwd6Wx5S/lT7CoMizYAs4rrYCNDkXK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BnCH6/btsMwd6Wx5S/lT7CoMizYAs4rrYCNDkXK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BnCH6/btsMwd6Wx5S/lT7CoMizYAs4rrYCNDkXK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBnCH6%2FbtsMwd6Wx5S%2FlT7CoMizYAs4rrYCNDkXK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;385&quot; height=&quot;72&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1) 360&amp;deg; 전체 range image를 6개의 수평 sub image로 나눈다. (기존 해상도가 1800x16이고 sub image는 300x16)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2) sub image에서 각 행의 점들을 $c$를 기준으로 정렬한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$c &amp;gt; c_{th}$ : Edge feature()&lt;/li&gt;
&lt;li&gt;$c &amp;lt; c_{th}$ : Planar feature()&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3) 각 행에서 $c$가 가장 큰 $n_{F_e}$개의 점들을 선택한다. 이때 이 점들은 지면이 아닌 벽, 장애물과 같이 뚜렷한 경계가 있는 물체여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;4) 각 행에서 $c$ 값이 가장 작은 $n_{F_p}$개의 점을 선택한다. 이때 이 점들은 지면이나 segment된 점(평평한 지형)일 수 있다.&lt;br /&gt;5) $F''_e$, $F_p$를 각각 sub image에서 추출한 edge feature, planar feature라고 하자. (Fig.2(d))&amp;nbsp; 3), 4) 과정을 한 번 더 반복해, 최종적으로 $F_e\subset F'_e$와 $F_p\subset F'_p$를 선정한다. 즉, $F_e$는 $F'_e$에서 더 중요한 edge feature을 추출, $F_p$도 $F'_p$에서 더 중요한 planar feature을 추출한 것이다. (Fig.2(c))&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;3. Lidar Odometry&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이 과정은 두 개의 연속적인 스캔에서 센서 움직임을 추정하는 과정이다. t-1과 t 사이의 센서 움직임을 계산하려면 공통된 특징점을 찾아야 하는데, point-to-edge와 point-to-plane라는 scan-matching해야 한다. 이전에 $F_e$와 $F_p$로 특징을 추출했었다. 현재 스캔 t의 특징점들인 $F_e^{t}$과 $F_p^{t}$에 대해서 이전 스캔 t-1에서 유사한 특징을 찾아야 한다. 즉, $F'_e^{t-1}$와 $F'_p^{t-1}$에서 현재 스캔에 대응되는 점들을 찾는다. 이 과정은 LOAM과 유사하기 때문에 feature matching에 있어 LOAM보다 정확도와 효율을 향상시키기 위한 방법들을 소개하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Label Matching: 특징점 매칭을 할 때 같은 객체에 속하는 특징점만 서로 대응하는 방법이다. 현재 스캔 t에서의 특징점과 이전 스캔 t-1에서의 특징점을 비교할 때 같은 레이블을 가진 점들끼리만 대응하도록 제한하면 매칭의 정확도가 높아진다. $F_e^t$와 $F_p^t$에 있는 특징점들은 segmentation 이후의 레이블을 통해 추출된 것이다. 이렇게 하면 같은 객체에서 추출된 특징점끼리만 매칭되어 매칭 오류를 줄일 수 있다.&lt;/li&gt;
&lt;li&gt;두 단계 L-M Optimization : Levenberg-Marquardt 최적화는 비선형 최소 제곱 문제를 해결하는 알고리즘으로 두 개의 연속된 스캔을 비교해 최적의 변환 행렬을 찾을 때 사용된다. 속도의 효율성을 높이기 위해 6 DOF transform $[t_x, t_y,t_z,\theta_{roll}, \theta_{pitch}, \theta_{yaw}]^T$을 한 번에 최적화하는 것이 아니라 두 단계로 나누어 한다. 아래 두 과정으로 나오는 결과를 fusing하여 최종 6D 변환 행렬 T를 완성한다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Planar Feature 기반 최적화: $F_p^t$와 $F'_p^{t-1}$를 사용해 $[t_z,\theta_{roll}, \theta_{pitch}]$를 최적화한다.&lt;/li&gt;
&lt;li&gt;Edge Feature 기반 최적화 : &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;$F_e^t$와 $F'_e^{t-1}$를 사용해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;$[t_x, t_y, \theta_{yaw}]$를 최적화한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. Lidar Mapping&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이 과정은 LiDAR Odometry 과정의 결과를 정밀하게 보정한다. Odometry는 두 개의 연속된 스캔을 비교해 센서의 움직임을 추정하지만, 이 단계에서는 주변 환경을 고려해 더욱 정밀한 pose refinement를 수행한다. 현재 스캔에서 얻은 특징점 ${F'_e^t, F'_p^t}$을 기존 지도인 $\bar{Q}^{t-1}$와 매칭한다.&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;$\bar{Q}^{t-1}$는 이전까지 저장된 포인트 클라우드 데이터로 구성된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;LeGO-LOAM은 LOAM과 달리 각 스캔의 특징점 집합 {$F'_e^t$, $F'_p^t$}를 개별적으로 저장한다. LOAM은 매핑 과정에서 생성된 포인트 클라우드 지도 전체를 저장한다. 이 방식은 메모리 사용량을 증가시키고 포인트 클라우드가 커질수록 매칭 속도가 느려지는 단점이 존재한다. LeGO-LOAM은 이를 개선해서 하나의 큰 지도를 저장하는 방식이 아니라 이전 스캔에서 추출한 특징점들을 따로 저장해서 활용한다. 기존 지도 $\bar{Q}^{t-1}$를 만들기 위해서 특징점을 저장한 집합인 $M^{t-1}$에서 특정한 특징점들만 선택해 활용한다. 두 가지 방법이 있는데, 첫 번째는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;센서의 Field of View 내의 특징점을 선택 : 현재 센서의 위치에서 100m 이내에 저장된 특징점만 선택해 생성한다.&lt;/li&gt;
&lt;li&gt;pose-graph SLAM과 LeGO-LOAM의 통합 : feature 집합의 센서 위치는 pose-graph로 모델링될 수 있는데, {$F'_e^t$, $F'_p^t$}를 하나의 노드로 저장한다. $\bar{Q}^{t-1}$는 최근 몇 개의 특징점 집합만을 선택해 구성한다. 현재 센서의 위치가 새로운 노드로 생성되면, 기존 노드 사이의 변환 관계를 L-M 최적화를 통해 구하고 이 값을 노드 간의 공간적 제약으로 추가한다. 더 나아가서, loop closure detection을 통해 drift 오류를 제거할 수 있다. ICP를 사용해 현재 feature 집합과 이전 feature 집합 간의 매칭을 찾으면 새로운 제약 조건이 추가된다. 그리고는 최적화 시스템으로 pose graph를 전송하여 센서의 위치가 추정된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Paper</category>
      <author>소-은</author>
      <guid isPermaLink="true">https://2nnsv.tistory.com/222</guid>
      <comments>https://2nnsv.tistory.com/222#entry222comment</comments>
      <pubDate>Wed, 26 Feb 2025 16:07:47 +0900</pubDate>
    </item>
    <item>
      <title>[논문 리뷰] LOAM: LiDAR Odometry and Mapping in Real-time</title>
      <link>https://2nnsv.tistory.com/221</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;저자 : Ji Zhang, Sanjiv Singh&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;발행연도 : 2014&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Abstract&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;LOAM은 6자유도로 움직이는 2축 라이다의 range 측정을 이용한 Odometry와 mapping을 실시간으로 계산하는 SLAM 알고리즘이다. range 측정 값은 수신되는 시간이 다르고 motion estimation 에러가 Point Cloud 결과에 오정합(misregistration)을 야기한다. 기존에는 offline에서 loop closure를 활용해 drift를 최소화한 3D 맵을 만드는 데 초점이 맞추어져 있다. 그러나&amp;nbsp;LOAM에서는 이 문제를 자체적인 Odometry 알고리즘과 Mapping 알고리즘 2개로 나누어 병렬로 처리하여 low-drift, low-computational complexity 특징을 지닌다. Odometry 알고리즘은 높은 주파수로 돌아가지만 낮은 신뢰도를 가지고, Mapping 알고리즘은 Point Cloud의 정밀한 매칭과 정합을 위해 낮은 주파수로 돌아간다. 이러한 두 알고리즘의 결합이 실시간으로 Mapping 할 수 있게 만든다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Introduction&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;라이다 센서가 정지한 채로 360도 회전하며 mapping 하는 일은 상대적으로 쉽다. 그러나 라이다 센서가 움직이면서 mapping 하는 경우, 라이다의 위치와 방향을 계속해서 추정하면서 mapping 해야 한다. 이 문제를 해결하기 위해서 기존에 제시된 방법들은 GPS나 INS 센서 같은 라이다와 독립적인 센서로 라이다의 움직임을 추정하거나 wheel encoder를 사용해 로봇의 odometry를 추정했다. 하지만 이러한 방법들은 매 루프마다 로봇의 움직임을 누적시키면서 odometry를 추정하기 때문에 drift 문제가 발생했다. 이 문제를 해결하기 위해서 LOAM을 개발하였고 drift를 최소화하는 것을 목표로 loop closure를 사용하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;System Overview&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LiDAR Hardware&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Hokuyo UTM-30LX 센서를 사용했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;495&quot; data-origin-height=&quot;238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lUMKJ/btsMuwEgL12/lXhxFns8GyQKv6Bs0KjHFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lUMKJ/btsMuwEgL12/lXhxFns8GyQKv6Bs0KjHFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lUMKJ/btsMuwEgL12/lXhxFns8GyQKv6Bs0KjHFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlUMKJ%2FbtsMuwEgL12%2FlXhxFns8GyQKv6Bs0KjHFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;495&quot; height=&quot;238&quot; data-origin-width=&quot;495&quot; data-origin-height=&quot;238&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Software System Overview&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;전체적인 흐름은 다음과 같다. LiDAR 센서 데이터가 들어오면 Point Cloud Registraion을 통해 LiDAR Odometry 과정으로 넘어간다. Odometry의 결과인 Pose를 이용해 LiDAR Mapping을 수행하고 Mapping의 결과와 Odometry 의 pose를 합쳐 final pose를 얻는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;649&quot; data-origin-height=&quot;159&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btLC2w/btsMsfxyzTz/ZNac4oFHawTsJZNrVKLgOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btLC2w/btsMsfxyzTz/ZNac4oFHawTsJZNrVKLgOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btLC2w/btsMsfxyzTz/ZNac4oFHawTsJZNrVKLgOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtLC2w%2FbtsMsfxyzTz%2FZNac4oFHawTsJZNrVKLgOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;649&quot; height=&quot;159&quot; data-origin-width=&quot;649&quot; data-origin-height=&quot;159&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LiDAR Odometry&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;LiDAR Odometry은 세 가지 알고리즘으로 구성된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Feature Point Extraction&lt;/li&gt;
&lt;li&gt;Finding Feature Point Correspondence&lt;/li&gt;
&lt;li&gt;Motion Estimation&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Feature Point Extraction&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;라이다 포인트 클라우드&lt;span style=&quot;font-size: 1.12em; letter-spacing: 0px;&quot;&gt;에서부터 feature point를 추출할 것이다. 이 논문에서는 edge와 planar 두 종류를 구분할 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$i$ : $P_{k}$ 안의 한 점&lt;/li&gt;
&lt;li&gt;$S$ : 같은 laser scan 데이터에서 $i$와 연속적으로 위치된 다른 점들의 집합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;두 포인트 간 간격은 0.25 degree이고 local surface의 smoothness를 평가하기 위한 지표로 아래와 같이 정의했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;368&quot; data-origin-height=&quot;68&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qQNUp/btsMuJDmDqb/MnkkpTvfghKzbCIna0AHGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qQNUp/btsMuJDmDqb/MnkkpTvfghKzbCIna0AHGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qQNUp/btsMuJDmDqb/MnkkpTvfghKzbCIna0AHGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqQNUp%2FbtsMuJDmDqb%2FMnkkpTvfghKzbCIna0AHGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;368&quot; height=&quot;68&quot; data-origin-width=&quot;368&quot; data-origin-height=&quot;68&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이 $c$ 값을 기준으로 스캔된 데이터의 포인트들을 정렬하고 feature point는 c의 최댓값인 edge point와 c의 최솟값인 planar point로 구분된다. 환경 내 feature point를 고르게 분류하려면, 스캔된 데이터들을 4개의 sub region으로 나누어준다. 각각의 sub region에서 최대 2개의 edge point, 4개의 planar point를 추출한다. threshold 값을 넘으면 edge나 planar로 구분될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;feature point들을 선택할 때 주의해야 할 점이 주변 점이 선택되었거나 레이저 빔과 거의 평행한 평면의 점은 피해야 한다. 또한 가려진 영역의 경계에 있는 점들을 피해야 한다. 왜냐하면 신뢰성이 떨어지기 때문이다. 예를 들어서 아래 그림을 보자. 점 A는 연결된 표면이 다른 물체에 의해서 차단되기 때문에 edge point로 분류된다. 그러나 라이다 센서가 다른 시점으로 이동하면 차단되었던 영역이 이제 관찰 가능한 상태로 바뀐다. 그래서 앞서 언급했던 포인트들이 선택되는 것을 막기 위해서 Point Set $S$를 다시 설정한다. $S$가 레이저 빔과 평행한 surface patch를 형성하지 않고 레이저 빔 방향의 간격에 의해 $i$가 분리되고 동시에 라이다에 더 가까운 점이 $S$에 없는 경우에 점 $i$가 선택될 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;요약하면, feature point들은 maximum $c$에서 시작하는 edge point와 minimum $c$에서 시작하는 planar point 둘 중 하나로 선택된다. 만약 한 점이 선택되면 선택된 edge point와 planar point들의 수는 sub region의 maximum을 초과할 수 없다. 선택된 점 주변의 점이 선택될 수 없고 레이저 빔과 평행인 surface patch일 수 없고 차단된 영역의 경계일 수 없다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;882&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nlBkB/btsMtbha3lL/KRB8JiaNjtUITlNuc1XHNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nlBkB/btsMtbha3lL/KRB8JiaNjtUITlNuc1XHNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nlBkB/btsMtbha3lL/KRB8JiaNjtUITlNuc1XHNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnlBkB%2FbtsMtbha3lL%2FKRB8JiaNjtUITlNuc1XHNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;250&quot; data-origin-width=&quot;882&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Finding Feature Point Correspondence&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;feature을 추출했다면, 정합(registration)을 위해서 서로 다른 sweep 간에 상관관계(correspondence)를 생성해야 한다. sweeping 과정을 시간 순서로 표현하면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;257&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n8ES1/btsMtogkhCP/Sy0zB8IjJJQkF2Vk0IKki1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n8ES1/btsMtogkhCP/Sy0zB8IjJJQkF2Vk0IKki1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n8ES1/btsMtogkhCP/Sy0zB8IjJJQkF2Vk0IKki1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn8ES1%2FbtsMtogkhCP%2FSy0zB8IjJJQkF2Vk0IKki1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;220&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;257&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$t_{k}$: k번째 sweep의 시작점&lt;/li&gt;
&lt;li&gt;$P_{k}$: k번째 sweep 동안 수집된 3D Point Cloud Data&lt;/li&gt;
&lt;li&gt;$\bar{P}_{k}$: k+1번째 sweep의 시작점에서 reprojection된 k번째 Point Cloud Data&lt;/li&gt;
&lt;li&gt;$P_{k+1}$: k+1번째 sweep동안 수집된 3D Point Cloud Data&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;$\bar{P}_{k}$와 $P_{k+1}$는 현재 사용 가능하다. $P_{k+1}$를 통해서 찾은 edge point Set과 planar point Set을 각각 $\varepsilon_{k+1}$, $H_{k+1}$이라고 하자.&amp;nbsp; $P_{k+1}$은 [$\varepsilon_{k+1}$, $H_{k+1}$]로 구성된다. 결국은 $\bar{P}_{k}$와 [$\varepsilon_{k+1}$, $H_{k+1}$]의 상관관계를 구하는 문제가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;k+1번째 sweep 동안 수집된 3D Point Cloud Data인 $P_{k+1}$를 다시 생각해보자. 이것은 $t_{k+1}$ 시점에서 아무것도 없는 공집합이다. 시간이 지남에 따라서 sweep한 포인트들이 누적된다. 즉 $P_{k+1}$는 k+1번째 sweep이 끝나는 시점인 $t_{k+2}$에 존재하는 데이터이다. 그래서 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;$\bar{P}_{k}$와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;$P_{k+1}$의 상관관계를 구하기 위해서는 $t_{k+2}$ 시점에서 $t_{k+1}$ 시점으로 reprojection이 필요하다. $\tilde{H}_{k+1}$, $\tilde{\varepsilon}_{k+1}$을 각각 $\bar{P}_{k}$에서 찾은 closest neighbor point라고 하자. 즉, $\bar{P}_{k}$와 [$\tilde{H}_{k+1}$, $\tilde{\varepsilon}_{k+1}$]는 closest neighbor point를 통해 상관 관계를 구할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BCnS1/btsMvQPPuQf/R7nm8UVvkX5XWlY87fZxRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BCnS1/btsMvQPPuQf/R7nm8UVvkX5XWlY87fZxRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BCnS1/btsMvQPPuQf/R7nm8UVvkX5XWlY87fZxRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBCnS1%2FbtsMvQPPuQf%2FR7nm8UVvkX5XWlY87fZxRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;590&quot; height=&quot;170&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;170&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Edge Point&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;edge point의 상관관계인 edge line을 찾는 과정은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;$i\in \tilde{\varepsilon}_{k+1}$를 만족하는 하나의 점 $i$를 선택한다.&lt;/li&gt;
&lt;li&gt;$i$와 가장 가까운 이웃 점인 $j$($j\in \bar{P}_{k}$)를 선택한다.&lt;/li&gt;
&lt;li&gt;$j$와 연속적으로 위치한 다른 laser scan 상의 점 $l$($l\in \bar{P}_{k}$)을 구한다.&lt;/li&gt;
&lt;li&gt;이렇게 구한 ($j$, $l$)이 edge point라는 것을 증명하기 위해서 local surface의 smoothness $c$를 계산한다.&lt;/li&gt;
&lt;li&gt;($j$, $l$)이 edge feature인 경우, 아래 공식을 사용해 $\tilde{\varepsilon}_{k+1}$와 $\bar{P}_{k}$ 간 거리를 구할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;462&quot; data-origin-height=&quot;99&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMwLoh/btsMufXDdVa/ToRK1iRNXKmQOin8x8fXw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMwLoh/btsMufXDdVa/ToRK1iRNXKmQOin8x8fXw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMwLoh/btsMufXDdVa/ToRK1iRNXKmQOin8x8fXw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMwLoh%2FbtsMufXDdVa%2FToRK1iRNXKmQOin8x8fXw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;462&quot; height=&quot;99&quot; data-origin-width=&quot;462&quot; data-origin-height=&quot;99&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이때 $X_{(k, i)}^{L}$는 라이다 좌표계 {$L$}에서 $i\in \tilde{\varepsilon}_{k+1}$를 만족하는 점 $i$의 좌표를 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Planar Point&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;planar point로 마찬가지다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;$i\in \tilde{H}_{k+1}$을 만족하는 하나의 점 $i$를 선택한다.&lt;/li&gt;
&lt;li&gt;$i$와 가장 가까운 이웃점인 $j$($j\in \bar{P}_{k}$)를 선택한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;$j$와 연속적으로 위치한 같은 laser scan 상의 점 $l$을 구한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;$j$와 연속적으로 위치한 다른 laser scan 상의 점 $m$을 구한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이렇게 구한 ($j$, $l$, $m$)이 planar point라는 것을 증명하기 위해서 local surface의 smoothness $c$를 계산한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;($j$, $l$, $m$)이 planar feature인 경우, 아래 공식을 사용해 $\tilde{H}_{k+1}$&lt;span style=&quot;text-align: left;&quot;&gt;&amp;nbsp;와 $\bar{P}_{k}$ 간 거리를 구할 수 있다.&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;467&quot; data-origin-height=&quot;121&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfEVyA/btsMuexGc4u/NEXoJtWMnAWiD1kJ5IXX11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfEVyA/btsMuexGc4u/NEXoJtWMnAWiD1kJ5IXX11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfEVyA/btsMuexGc4u/NEXoJtWMnAWiD1kJ5IXX11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfEVyA%2FbtsMuexGc4u%2FNEXoJtWMnAWiD1kJ5IXX11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;467&quot; height=&quot;121&quot; data-origin-width=&quot;467&quot; data-origin-height=&quot;121&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Motion Estimation&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이제 위에서 구한 correspondence를 기반으로 두 frame 사이의 motion estimation을 수행해야 한다. 라이다 센서 데이터는 모든 point들이 동시에 찍혀 나오는 게 아니라, 일정한 속도로 돌아가면서 한 프레임을 만들기 때문에 linear interpolation(선형 보간) 해주어야 한다. $t_{k+1}$ 시점에 sweeping을 시작한다고 가정하면, [$t_{k+1}$, $t$] 시간 사이 라이다 Pose Transform은&amp;nbsp; $T_{(k+1)}^{L}$로 정의할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;$T_{(k+1)}^L = [t_x, t_y, t_z, \theta_x,\theta_y,\theta_z]^{T}&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;$&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;$&lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;t_x$, $t_y&lt;/span&gt;$, $t_z$ : 라이다 좌표계 {$L$}에서 병진운동&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;$&lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;\theta_x$, &lt;/span&gt;$&lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;\theta_y$, &lt;/span&gt;$&lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;\theta_z$ : 라이다 좌표계 {$L$}에서 회전운동&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이때 Transform하는 점의 index를 $i$라고 하면 점 $i$의 Transform은 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;69&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bv8hFz/btsMtPrk4Gt/bMOVph73dHIJfBJRXVzKg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bv8hFz/btsMtPrk4Gt/bMOVph73dHIJfBJRXVzKg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bv8hFz/btsMtPrk4Gt/bMOVph73dHIJfBJRXVzKg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbv8hFz%2FbtsMtPrk4Gt%2FbMOVph73dHIJfBJRXVzKg0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;302&quot; height=&quot;69&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;69&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;선형 보간을 통해 [$\varepsilon_{k+1}$, $H_{k+1}$] -&amp;gt; [$\tilde{H}_{k+1}$, $\tilde{\varepsilon}_{k+1}$]변환을 수식으로 나타내면 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;417&quot; data-origin-height=&quot;49&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf4X6N/btsMt7kYIah/3Zr8HLRCcT0K4VUpxH7bQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf4X6N/btsMt7kYIah/3Zr8HLRCcT0K4VUpxH7bQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf4X6N/btsMt7kYIah/3Zr8HLRCcT0K4VUpxH7bQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf4X6N%2FbtsMt7kYIah%2F3Zr8HLRCcT0K4VUpxH7bQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;417&quot; height=&quot;49&quot; data-origin-width=&quot;417&quot; data-origin-height=&quot;49&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$X_{(k+1, i)}^{L}$: &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;[$\varepsilon_{k+1}$,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;$H_{k+1}$] 집합에 속한 임의의 한 점 $i$의 좌표&lt;/li&gt;
&lt;li&gt;$\tilde{X}_{(k+1, i)}^{L}$: &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;[$\tilde{\varepsilon}_{k+1}$,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;$\tilde{H}_{k+1}$] 집합에 속한 임의의 한 점 $i$ 좌표&lt;/li&gt;
&lt;li&gt;$T_{(k+1, i)}^{L}(1:3)$: $[t_x, t_y, t_z]^T$로 $T_{(k+1, i)}^{L}(1:3)$에서 1~3번 항목만 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이 식에서 $R$은 Rodrigues 공식을 통해 표현한 회전행렬이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;430&quot; data-origin-height=&quot;38&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5H3t6/btsMvQ3ruYA/Ua6g2Rwml05sZr4R9IlubK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5H3t6/btsMvQ3ruYA/Ua6g2Rwml05sZr4R9IlubK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5H3t6/btsMvQ3ruYA/Ua6g2Rwml05sZr4R9IlubK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5H3t6%2FbtsMvQ3ruYA%2FUa6g2Rwml05sZr4R9IlubK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;430&quot; height=&quot;38&quot; data-origin-width=&quot;430&quot; data-origin-height=&quot;38&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$\theta = \left\|&amp;nbsp;T_{(k+1,&amp;nbsp;i)}^L(4:6)\right\|$: 회전 크기&lt;/li&gt;
&lt;li&gt;$\omega =&amp;nbsp; $T_{(k+1, i)}^L(4:6)/\left\| T_{(k+1, i)}^L(4:6)\right\|$: 회전 방향의 단위 벡터&lt;/li&gt;
&lt;li&gt;$\hat{\omega}$: $\omega$의 skew symmetric matrix&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이 문제를 최적화 문제로 풀기 위해서 상관관계의 거리가 가까워진다는 것은 registration, 즉 motion estimation이 되었다는 의미이므로 아래와 같이 edge correspondence와 planar correspondence를 cost로 삼는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;392&quot; data-origin-height=&quot;51&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baDvA0/btsMvNZZgiG/ZfEFnob718SRz3AaHsdC40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baDvA0/btsMvNZZgiG/ZfEFnob718SRz3AaHsdC40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baDvA0/btsMvNZZgiG/ZfEFnob718SRz3AaHsdC40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaDvA0%2FbtsMvNZZgiG%2FZfEFnob718SRz3AaHsdC40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;392&quot; height=&quot;51&quot; data-origin-width=&quot;392&quot; data-origin-height=&quot;51&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;34&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEuNEO/btsMtunxgGx/E1GCK5IQ8rUIcEDwP82HGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEuNEO/btsMtunxgGx/E1GCK5IQ8rUIcEDwP82HGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEuNEO/btsMtunxgGx/E1GCK5IQ8rUIcEDwP82HGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEuNEO%2FbtsMtunxgGx%2FE1GCK5IQ8rUIcEDwP82HGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;34&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;34&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이 두 공식을 벡터화하여 표현하면 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;155&quot; data-origin-height=&quot;50&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buJW5q/btsMtPZdSXj/SLq1M9mUihJooSYiWUgYY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buJW5q/btsMtPZdSXj/SLq1M9mUihJooSYiWUgYY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buJW5q/btsMtPZdSXj/SLq1M9mUihJooSYiWUgYY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuJW5q%2FbtsMtPZdSXj%2FSLq1M9mUihJooSYiWUgYY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;155&quot; height=&quot;50&quot; data-origin-width=&quot;155&quot; data-origin-height=&quot;50&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;정리하면, d가 최소가 되는 $T_{k+1}^L$를 비선형 최적화 방식으로 구한다. $J= \partial f/\partial T_{k+1}^L$를 구한 다음 Levenberg-Marquardt 방식으로 최적화를 수행한다. 이때 error 값은 bisquare weight 함수를 통해 outlier에 강건하도록 설정된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;41&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cD8jJ1/btsMvPKi5ew/haRByAhxnHqFCoDvrUWo40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cD8jJ1/btsMvPKi5ew/haRByAhxnHqFCoDvrUWo40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cD8jJ1/btsMvPKi5ew/haRByAhxnHqFCoDvrUWo40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcD8jJ1%2FbtsMvPKi5ew%2FhaRByAhxnHqFCoDvrUWo40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;484&quot; height=&quot;41&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;41&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그래서 지금까지의 과정을 요약하면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$t_{k+1}$초에서 $\bar{P}_{k}$, $P_{k+1}$, $T_{k+1}^L$ 값을 입력으로 받는다.&lt;/li&gt;
&lt;li&gt;Sweeping을 시작할 때 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;$T_{k+1}^L$를 초기화 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;Feature point를 추출한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;이전 sweep에서 featur point에 상응하는 점들을 찾고 distance를 구한다.&lt;/li&gt;
&lt;li&gt;거리 오차를 최적화하는 최적의 $T_{k+1}^L$를 찾기 위해서 비선형 최적화를 수행한다.&lt;/li&gt;
&lt;li&gt;sweep이 끝나는 순간 $P_{k+1}$에서 $\bar{P}_{k+1}$로 reprojection하고 값을 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LiDAR Mapping&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;LiDAR Mapping 과정은 Odometry보다 더 낮은 빈도로 실행되고 sweep이 끝난 순간 한 번만 수행된다. k+1번째 sweep이 끝나면 Odometry는 왜곡되지 않은 point cloud인 $\bar{P}_{k+1}$과 동시에 sweep 동안의 라이다 움직임을 포함한 pose transform인 $T_{k+1}^L$을 생성한다. mapping 알고리즘은 World 좌표계 {$W$}와 $\bar{P}_{k+1}$를 매칭하는 것이 핵심이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;290&quot; data-origin-height=&quot;116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F2u0e/btsMuhBe1yv/rOHjeEIRc5KwBecI6lrH1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F2u0e/btsMuhBe1yv/rOHjeEIRc5KwBecI6lrH1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F2u0e/btsMuhBe1yv/rOHjeEIRc5KwBecI6lrH1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF2u0e%2FbtsMuhBe1yv%2FrOHjeEIRc5KwBecI6lrH1K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;290&quot; height=&quot;116&quot; data-origin-width=&quot;290&quot; data-origin-height=&quot;116&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$Q_{k}$: k번째 sweep에서 맵을 구성하는 Point Cloud&lt;/li&gt;
&lt;li&gt;$T_k^W$: k번째 sweep에서 World 좌표계 {$W$}를 기준으로 본 LiDAR의 Pose Transform&lt;/li&gt;
&lt;li&gt;$\bar{Q}_{k}$: $\bar{P}_k$에서 World 좌표계 {$W$}로 reprojection된 Point Cloud&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Odometry에서 한 것처럼 feature extraction, finding correspondence, motion estimation을 통해서 $\bar{Q}_{k+1}$을 $Q_k$에 registration한다. mapping에서 $\bar{Q}_{k+1}$에 대한 feature extraction을 이미 odometry에서 수행했기 때문에 그대로 사용한다. Odometry는 10Hz, Mapping은 1Hz이므로 10배의 feature를 사용하게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;correspondence 생성은 $\bar{Q}_{k+1}$의 각 feature point마다 반경 10m 이내의 Point Cloud만을 사용한다. 그리고 $\bar{Q}_k$와 $Q_{k-1}$ 사이의 correspondence를 찾고 최적의 $T_k^W$를 찾기 위해 최적화를 수행해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$S'$: $Q_{k-1}$에서 임의의 점들의 집합&lt;/li&gt;
&lt;li&gt;$M$: $S'$의 공분산 행렬&lt;/li&gt;
&lt;li&gt;$V$, $E$: M의 eigenvalue와 eigenvector&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이렇게 정의하고서는 eigen decomposition을 수행하여 최적화한다. correspondence를 찾은 후에는 Odometry 방식과 같은 공식을 사용해 transformation을 구한다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Paper</category>
      <author>소-은</author>
      <guid isPermaLink="true">https://2nnsv.tistory.com/221</guid>
      <comments>https://2nnsv.tistory.com/221#entry221comment</comments>
      <pubDate>Mon, 24 Feb 2025 15:46:40 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스: 점프와 순간 이동</title>
      <link>https://2nnsv.tistory.com/218</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/12980&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/12980&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1739256635618&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/12980&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fYAWz/hyYfRP7qTn/h6zYf4o8WgLFzO9P2woBqK/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960,https://scrap.kakaocdn.net/dn/bRp85S/hyYcinSzSS/LKhaUpw166IjvC4QIb76nk/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/12980&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/12980&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fYAWz/hyYfRP7qTn/h6zYf4o8WgLFzO9P2woBqK/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960,https://scrap.kakaocdn.net/dn/bRp85S/hyYcinSzSS/LKhaUpw166IjvC4QIb76nk/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 문제 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이 문제는 점프와 순간 이동 중에 선택해서 에너지 사용량을 최소가 되도록 하여 최솟값을 반환해야 한다. 점프를 하면 한 번에 원하는 좌표로 이동할 수 있지만 에너지 사용량이 점프한 칸 수만큼 추가되어야 한다. 순간이동은 현재 좌표에서 2배에 해당하는 좌표로 이동할 수 있는데 에너지 사용량은 변화가 없다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;처음에는 0에서 4, 0에서 3, 0에서 2, 0에서 1까지 점프한 다음, 순간이동이 가능한지 확인했다. (현재 좌표) * 2 == N(목표 지점), (현재 좌표) * 2 &amp;lt; N, (현재 좌표) * 2 &amp;gt; N으로 나누었다. 그래서 (현재 좌표) * 2이면 break하여 min 값을 비교하고, (현재 좌표) * 2 &amp;lt; N이면 이 과정을 반복하는 것으로 생각했다. 그러나 너무 따질 게 많고 복잡해서 다시 한 번 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;일단, 순간이동을 할 수 있으려면, 당연히 (현재 좌표) * 2 가 목표 지점인 N보다 작아야 한다. 반대로 생각해서 N을 2로 나누었을 때 홀수이면 순간이동을 할 수 없기 때문에 에너지 사용량을 늘리고 현재 위치 N에 1을 뺀다. 2로 나누었을 때 짝수이면 순간이동이 가능하기 때문에 N을 2로 나누어 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 코드&lt;/h2&gt;
&lt;pre id=&quot;code_1739257105519&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

int solution(int n)
{
    int ans = 0;
    while (0 &amp;lt; n) {
        if (n % 2 == 0) {
            n /= 2;
        }
        else {
            n -= 1;
            ans++;
        }
    }

    return ans;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>PS/Programmers</category>
      <author>소-은</author>
      <guid isPermaLink="true">https://2nnsv.tistory.com/218</guid>
      <comments>https://2nnsv.tistory.com/218#entry218comment</comments>
      <pubDate>Tue, 11 Feb 2025 15:58:32 +0900</pubDate>
    </item>
    <item>
      <title>[ROS2] ROS 기초(Node, Package, Launch, Parameter)</title>
      <link>https://2nnsv.tistory.com/214</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;ROS란&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;ROS(Robot Operating System)은 로봇 애플리케이션 구축에 필요한 SW 라이브러리와 툴 모음을 말한다. 로봇이 움직일 때를 생각해보면, 통신, 제어, 계측 등 다양한 분야의 지식이 필요하고 이것을 유기적으로 연결해야 자연스럽게 로봇의 움직임을 만들 수 있다. 이 많은 것을 모두 공부하고 로봇을 만드려면 적어도 1년이 걸릴 텐데, 이를 쉽게 하도록 도와주는 것이 ROS이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Node&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;ROS에서 하나 또는 여러 목적을 달성하기 위한 기능 단위를 말한다. 바퀴 구동을 위한 모터를 조작하는 노드, 레이저의 센서 데이터를 전달하는 노드를 예로 들 수 있다. 각 노드들은 앞으로 나올 topic, service, action, parameter 들을 전달하거나 받을 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;783&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kyzzu/btsMaNACYOJ/k6u2UFDGhIgmWhZhPmmSf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kyzzu/btsMaNACYOJ/k6u2UFDGhIgmWhZhPmmSf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kyzzu/btsMaNACYOJ/k6u2UFDGhIgmWhZhPmmSf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fkyzzu%2FbtsMaNACYOJ%2Fk6u2UFDGhIgmWhZhPmmSf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;326&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;783&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Node 확인을 위해서 turtlesim 을 켜보자.&lt;/p&gt;
&lt;pre id=&quot;code_1738912613688&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 run turtlesim turtlesim_node
ros2 run turtlesim turtle_teleop_key&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2121&quot; data-origin-height=&quot;1137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1EfSZ/btsMa9b5NU9/H1rm6VZBdYWASGF1VlAAr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1EfSZ/btsMa9b5NU9/H1rm6VZBdYWASGF1VlAAr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1EfSZ/btsMa9b5NU9/H1rm6VZBdYWASGF1VlAAr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1EfSZ%2FbtsMa9b5NU9%2FH1rm6VZBdYWASGF1VlAAr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;322&quot; data-origin-width=&quot;2121&quot; data-origin-height=&quot;1137&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이제 아래 명령을 통해서 노드가 실행중인지 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1738912804584&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 node list&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1050&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UKc2e/btsL9Rp7Jfj/ELtzDBsYYlqsNti2hKgao1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UKc2e/btsL9Rp7Jfj/ELtzDBsYYlqsNti2hKgao1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UKc2e/btsL9Rp7Jfj/ELtzDBsYYlqsNti2hKgao1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUKc2e%2FbtsL9Rp7Jfj%2FELtzDBsYYlqsNti2hKgao1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;210&quot; data-origin-width=&quot;1050&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;/turtlesim 노드에 대한 정보를 알고 싶다면 아래 명령을 통해서 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1738912870268&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 node info /turtlesim&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;1152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A3daB/btsL9RKvVzr/AqGTftbKbAUsnz3WYqtHVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A3daB/btsL9RKvVzr/AqGTftbKbAUsnz3WYqtHVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A3daB/btsL9RKvVzr/AqGTftbKbAUsnz3WYqtHVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA3daB%2FbtsL9RKvVzr%2FAqGTftbKbAUsnz3WYqtHVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;475&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;1152&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;이제 두 노드가 어떻게 정보를 주고 받는지 확인해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1738912973476&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rqt_graph&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2410&quot; data-origin-height=&quot;1336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LWpZ6/btsMafKXKY3/nTIysQ1wLkLngozkkrHbb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LWpZ6/btsMafKXKY3/nTIysQ1wLkLngozkkrHbb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LWpZ6/btsMafKXKY3/nTIysQ1wLkLngozkkrHbb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLWpZ6%2FbtsMafKXKY3%2FnTIysQ1wLkLngozkkrHbb1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;333&quot; data-origin-width=&quot;2410&quot; data-origin-height=&quot;1336&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Package&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;패키지는 특정 기능을 가진 노드들을 그룹으로 만들어 관리하기 쉽게 만들어 놓은 단위이다. 그래서 보통은 패키지를 직접 만들어 이 워크스페이스 안에서 작업한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;워크스페이스인 ros2_ws를 생성하고 안에 src 폴더도 함께 만들어준다.&lt;/p&gt;
&lt;pre id=&quot;code_1738913229441&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mkdir -p ~/ros2-study/src
cd ~/ros2-study/src&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;파이썬을 기준으로 하기 때문에 아래와 같은 명령을 통해 패키지를 생성한다. C++을 기준으로 할 때는 명령어가 달라지므로 주의해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1738913284556&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 pkg create --build-type ament_python --node-name my_first_node my_first_package&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이제 패키지가 생성되었기 때문에 실행해보자. 항상 build는 워크 스페이스인 ros2_ws에서 해야 한다. 빌드가 완료되었다면, setup.bash를 설정해주어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1738913352764&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd ~/ros2-study
colcon build
source install/local_setup.bash&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이제 생성된 노드를 실행해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1738914706982&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 run my_first_package my_first_node&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XvdUh/btsMbsJogTy/9HXnfNsYs1u70uKJMmLn61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XvdUh/btsMbsJogTy/9HXnfNsYs1u70uKJMmLn61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XvdUh/btsMbsJogTy/9HXnfNsYs1u70uKJMmLn61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXvdUh%2FbtsMbsJogTy%2F9HXnfNsYs1u70uKJMmLn61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;75&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;참고로, ros를 실행하고 패키지를 빌드, 생성할 때 ros 환경 세팅과 setup을 해줘야 하는데 alias를 설정하면 복잡하게 하지 않아도 된다. bashrc 파일을 아래와 같이 수정하면 된다. 참고로 ros_domain_id 설정은 유의해야 하는데, 여러 사람들과 같은 네트워크에서 ROS를 실행해야 할 때 같은 domain id를 같은&amp;nbsp; 걸 쓰면 다른 사람이 실행하는 것과 겹칠 수 있으므로 유의해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1738915723709&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo vim ~/.bashrc&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1738915696780&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# .bashrc
alias humble = &quot;source /opt/ros/humble/setup.bash; ros_domain; echo \&quot;ROS2 humble is activated!\&quot;&quot;
alias ros_domain = &quot;export ROS_DOMAIN=13&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Launch&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;로봇의 기능을 수행하기 위해서는 여러 개의 노드를 실행해야 한다. 매번 많은 노드를 실행하고 중지시키지 않고 한 번의 명령으로 노드를 실행하고 중지시킬 수 있다. 그것이 바로 Launch이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;먼저, src/ros2-study/setup.py 파일을 아래 내용으로 수정한다. 이는 my_first_package 패키지 아래 launch 폴더 내 '.launch.py'로 끝나는 파일을 모두 패키지에 포함한다는 내용이다.&lt;/p&gt;
&lt;pre id=&quot;code_1738916117457&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from setuptools import find_packages, setup
import os
import glob

package_name = 'my_first_package'

setup(
    name=package_name,
    version='0.0.0',
    packages=find_packages(exclude=['test']),
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
        ('share/' + package_name + '/launch', glob.glob(os.path.join('launch', '*.launch.*')))
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='soeun',
    maintainer_email='soeun@todo.todo',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
        ],
    },
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이렇게 script를 수정한 후에 빌드를 한다. 참고로 빌드 명령에 --symlink-install이라는 옵션을 추가하면 실행에 필요한 파일을 install 폴더에 복사하지 않고 symlink를 생성한다. 즉, 원본 파일을 직접 사용한다는 뜻이다.&lt;/p&gt;
&lt;pre id=&quot;code_1738916660313&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd ~/ros2-study
colcon build --symlink-install&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Parameter&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;파라미터는 노드의 Configuration 정보다. 모든 노드들은 자신의 파라미터를 가지고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;먼저 파라미터 리스트를 확인하려면 다음과 같은 명령을 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1738916794763&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 param list&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1473&quot; data-origin-height=&quot;701&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqcbc7/btsMbZs9hX3/h9qDOkL48xKdT1zZlk3SHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqcbc7/btsMbZs9hX3/h9qDOkL48xKdT1zZlk3SHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqcbc7/btsMbZs9hX3/h9qDOkL48xKdT1zZlk3SHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcqcbc7%2FbtsMbZs9hX3%2Fh9qDOkL48xKdT1zZlk3SHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;286&quot; data-origin-width=&quot;1473&quot; data-origin-height=&quot;701&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;파라미터 값을 조회하거나 수정하려면 아래와 같은 명령을 사용하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1738916882344&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 param get /turtlesim background_g # 조회
ros2 param set /turtlesim background_g 100 # 변경&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;현재 설정된 파라미터 값을 저장하고 싶다면 아래 명령을 통해서 저장할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1738916946672&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 param dump /turtlesim &amp;gt; turtlesim.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이렇게 설정된 turtlesim.yaml 값을 /turtlesim 노드에 반영하려면 아래 명령어를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1738916992382&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ros2 param load /turtlesim turtlesim.yaml&lt;/code&gt;&lt;/pre&gt;</description>
      <category>ROS</category>
      <author>소-은</author>
      <guid isPermaLink="true">https://2nnsv.tistory.com/214</guid>
      <comments>https://2nnsv.tistory.com/214#entry214comment</comments>
      <pubDate>Fri, 7 Feb 2025 17:30:16 +0900</pubDate>
    </item>
    <item>
      <title>[컴퓨터구조] 2-5. Data Structure</title>
      <link>https://2nnsv.tistory.com/213</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;메모리에 각각의 자료구조가 어떻게 올라가는지 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;배열은 항상 메모리의 연속적인 공간에 할당된다.&lt;/b&gt; 메모리에서 데이터를 가져오는 시간이 CPU 연산 시간보다 길기 때문에 메인 메모리에 접근하는 시간은 전체 성능에 영향을 준다. 이때, 연속 공간에 배열을 할당하면 locality가 향상되어 메모리에서 데이터를 가져오는 시간을 줄일 수 있다. locality에 대해서는 이후 캐시 메모리에서 자세하게 배울 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2204&quot; data-origin-height=&quot;999&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eumha2/btsLFJxMRY6/9QyKg4wZTkDqA9kXSZIYAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eumha2/btsLFJxMRY6/9QyKg4wZTkDqA9kXSZIYAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eumha2/btsLFJxMRY6/9QyKg4wZTkDqA9kXSZIYAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feumha2%2FbtsLFJxMRY6%2F9QyKg4wZTkDqA9kXSZIYAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;272&quot; data-origin-width=&quot;2204&quot; data-origin-height=&quot;999&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그렇다면, 한 코드에서 &lt;b&gt;여러 개 배열을 선언&lt;/b&gt;하면 어떻게 할당될까? 일반적으로 하나의 배열을 연속적인 공간에 할당되지만 &lt;u&gt;&lt;b&gt;서로 다른 배열은 연속적으로 할당되지 않는다.&lt;/b&gt; &lt;/u&gt;즉, 처음 할당된 배열 다음에 항상 두번째 할당된 배열이 놓이지 않는다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;아래처럼 배열을 선언한다고 했을 때, 각각 하나의 배열은 연속적인 공간에 할당되지만 배열과 배열은 연속적으로 할당되지 않을 수도 있다. 만약에 a[5] 값을 접근한다고 하면, 아래 그림에서는 9가 출력될 것이다. 왜냐하면 아래 그림에서는 각각의 배열이 연속적으로 할당되어 있어서 a의 주소를 넘어버려도 큰 문제는 없다. 그러나 아래와 같이 할당되지 않을 확률이 높기 때문에 일반적인 경우라면 쓰레기값이 출력될 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1736158973446&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typedef int zip_dig[5];

zip_dig a = { 1, 5, 2, 1, 3 };
zip_dig b = { 9, 8, 1, 9, 5 };
zip_dig c = { 9, 4, 7, 2, 0 };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3016&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KiAxB/btsLGaVXMHV/ItFaUOCUkiFJWbmeBZuFWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KiAxB/btsLGaVXMHV/ItFaUOCUkiFJWbmeBZuFWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KiAxB/btsLGaVXMHV/ItFaUOCUkiFJWbmeBZuFWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKiAxB%2FbtsLGaVXMHV%2FItFaUOCUkiFJWbmeBZuFWk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;199&quot; data-origin-width=&quot;3016&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이제 2차원 배열(multi dimensional array)도 살펴보자. 기본적으로 메모리에서 모든 요소들은 행 우선 순서(Row-Major Ordering)이기 때문에 아래 그림처럼 배치된다. 그래서 정수형 A[i][j]이 선언되어있다고 할 때, A[R][C]에 접근하고 싶다면,&amp;nbsp; A +(i*C + j)*K가 메모리 주소가 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1737003770318&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;zip_dig d[4] = 
	{{9, 8, 1, 9, 5},
    	{9, 8, 1, 0, 5},
    	{9, 8, 1, 0, 3},
    	{9, 8, 1, 1, 5}};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2차원 배열과 유사하게, Multi-level Array에 대해서도 알아보자. Multi level Array는 배열이긴 하나, 각 요소에 다른 배열의 시작주소를 담고 있다. 이 경우에는 multi dimensional array와 동일하게 다차원 배열이지만, 연속된 공간을 차지 하지 않아서 메모리가 부족한 상황에서 유용하다. 즉, 꼭 메모리의 연속된 공간에 넣지 않아도 되기 때문에 편리하지만, 그만큼 메모리 접근 횟수가 늘어나 성능을 저하시킬 수 있다.&lt;/p&gt;</description>
      <category>CS/컴퓨터구조</category>
      <author>소-은</author>
      <guid isPermaLink="true">https://2nnsv.tistory.com/213</guid>
      <comments>https://2nnsv.tistory.com/213#entry213comment</comments>
      <pubDate>Thu, 16 Jan 2025 18:20:11 +0900</pubDate>
    </item>
    <item>
      <title>[컴퓨터구조] 2-4. Linking</title>
      <link>https://2nnsv.tistory.com/212</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;프로그램이 실행되려면 소스 코드가 컴파일러에서 컴파일되고, 어셈블러가 기계어로 바꾼 다음 링커에서 실행 가능한 파일로 만들어야 한다. 그 중에서 링킹 과정에 대해서 자세히 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;링커는 하나의 실행 가능한 파일로 만들어주는 일이다. 아래 명령어를 통해 컴파일을 하면 아래 그림과 같은 과정으로 실행 가능한 형태로 만들어 준다. 링커에는 relocatable object file이 input으로 들어가고 링커에서 executable object file로 변경되어 나온다.&lt;/p&gt;
&lt;pre id=&quot;code_1734496448719&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;gcc -Og -o prog main.c sum.c
./prog&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pzfYq/btsLmUGG6SA/anDb1xNFgUgKkGcsZbtzUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pzfYq/btsLmUGG6SA/anDb1xNFgUgKkGcsZbtzUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pzfYq/btsLmUGG6SA/anDb1xNFgUgKkGcsZbtzUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpzfYq%2FbtsLmUGG6SA%2FanDb1xNFgUgKkGcsZbtzUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;502&quot; height=&quot;363&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;링커에서 나온 executable object file은 메모리에 올라간다. 즉 메모리의 일부에 locate되고 실행할 수 있는 형태인 것이다. linking 과정은 linking time에 따라 나눌 수 있는데, static linking은 executable object file을 만드는 compile time에 모든 과정이 이루어진다. symbol resolution, relocation 과정을 거치는데, symbol resolution은 하나의 symbol을 인식하는 과정이고, relocation은 text section, data section으로 분류된 symbol들을 메모리에 재배치하여 excutable object file을 만드는 과정이다. 반대로 dynamic linking은 Load time, run time에 linking이 모두 이루어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjGA2I/btsLmmRhJ2w/k4ulGldKsMmzzqfvtcFnDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjGA2I/btsLmmRhJ2w/k4ulGldKsMmzzqfvtcFnDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjGA2I/btsLmmRhJ2w/k4ulGldKsMmzzqfvtcFnDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjGA2I%2FbtsLmmRhJ2w%2Fk4ulGldKsMmzzqfvtcFnDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;410&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;링커를 사용하면 좋은 점이 무엇일까? 먼저, Modularity를 보장해준다. 프로그램은 큰 모듈 덩어리가 아니라 작은 소스 파일들의 결합으로 만들어지는데, 일반적인 함수로 만들어 쉽게 사용할 수 있다. 예를 들어 C언어의 수학 함수를 사용할 수 있는 라이브러리나 standard C library를 말한다. 두번째로 Efficiency이다. 여러 개의 소스 파일들을 동시에 컴파일해서 시간을 줄일 수 있고 일반적으로 많이 사용하는 함수들은 라이브러리처럼 끌어당겨 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;앞서 설명한 것처럼 링커는 symbol resolution, relocation 과정을 통해 executable object file을 만든다. symbol resolution에는 함수와 변수를 구분해서 symbol table에 저장한다. relocation 단계에서 코드와 데이터 섹션을 분리해 하나의 섹션으로 넣는다. 그래서 이 symbol들은 .o file에서 상대적 주소를 가지고 있다가 가상 메모리에서 절대 주소를 갖게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그래서 이 과정 중의 파일들은 세 가지로 분류할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Relocatable Object File(.o) : linking 전의 파일 형식으로 코드와 데이터 영역이 포함된다. .c 파일은 하나의 .o 파일들을 하나씩 가진다.&lt;/li&gt;
&lt;li&gt;Executable Object File(.out) : linking 후 형식으로 메모리에 복사되어 실행될 코드와 데이터 영역이 포함된다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Shared Object File(.so)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그렇다면 Variable과 Symbol에 대해서 알아보자. 먼저, variable의 life와 scope에 대해서 알아야 한다. &lt;b&gt;life&lt;/b&gt;는 해당 오브젝트가 메모리에 있는지 없는지로 결정되고, &lt;b&gt;scope&lt;/b&gt;는 어떤 모듈에서 access할 수 있는지에 대한 것이다. 다른 곳에서 정의되어서 live 상태인데 visible 상태가 아닐 수 있다. 그러나 not live인데 visible 상태일 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;local variable : 함수 내부에서 선언된 변수로 스택 프레임에 저장된다. life는 함수가 호출되고 return 때까지 이고 함수 안에서만 access되기 때문에 function scope을 가진다. &lt;b&gt;static local variable&lt;/b&gt;은 함수 내부에 선언되어도 stack에 올라가는게 아니라 전역 영역에 선언된다. 그래서 함수가 시작될 때 1번만 초기화되어 종료될 때까지 메모리에 상주하고 있다. 그래서 첫번째로 함수를 호출하고 return 되고 한번더 함수를 호출하면 초기화되지 않고 이전에 사용한 그대로 해당 변수를 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;global variable : 함수 외부에서 정의된 변수들이다. 모든 프로그램에서 scope를 가지고 &amp;nbsp;프로그램이 종료되면 not live 상태가 된다.&lt;/li&gt;
&lt;li&gt;static variable : life는 해당 함수가 호출되고 return될 때까지이지만 scope은 특정 함수, 파일에 한정된다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Symbol은 정의된 variable이나 function의 이름&lt;/b&gt;을 말한다. relocatable object file의 variable이 링커에 들어가면 이름이 고정되어 symbol table에 올라가 참조된다. 링커의 입장에서는 각각의 symbol들을 아래와 같이 분류한다. 이때 variable과 다르다는 것을 기억해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;global symbol : 현재 module에서 정의되고 다른 모듈에서 참조될 수 있는 symbol들을 말한다. 예를 들어서 C 함수 중 static이 아닌 함수들, static이 아닌 global variable을 말한다.&lt;/li&gt;
&lt;li&gt;external symbol : global symbol이긴 한데 현재 module에서 정의되지 않고 다른 module에서 정의된 symbol을 말한다. 다른 소스 파일에서 정의된 symbol인 경우 relocatable file에서는 external symbol은 '?'로 기록된다.&lt;/li&gt;
&lt;li&gt;local symbol : 현재 module에 정의되어 있으면서 현재 module에서만 참조될 수 있는 symbol이다. &lt;b&gt;static 키워드와 함께 정의된 c function과 variable을 말한다.&lt;/b&gt; &lt;u&gt;&lt;b&gt;local linker symbol들은 local variable과 전혀 관계가 없다는 것을 주의해야 한다.&lt;/b&gt;&lt;/u&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;main 관점에서 보면, buf와 main symbol은 다른 파일에서도 access 할 수 있기 때문에 Global Symbol이다. swap()은 visible한 상태이지만 swap.c에 정의되어 있기 때문에 External Symbol이다. swap.c에서 보면, buf는 외부에서 정의되었기 때문에 external symbol이고 링킹이 되기 전까지는 '???' 상태로 유지된다. temp는 local variable로 linker가 관리하는 것이 아니라 메모리의 스택 영역에서 관리되는 변수다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1755&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ct4GvG/btsLnAnRVq9/IlOZ4LgSU2nKvtouHV5Z20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ct4GvG/btsLnAnRVq9/IlOZ4LgSU2nKvtouHV5Z20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ct4GvG/btsLnAnRVq9/IlOZ4LgSU2nKvtouHV5Z20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fct4GvG%2FbtsLnAnRVq9%2FIlOZ4LgSU2nKvtouHV5Z20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;342&quot; data-origin-width=&quot;1755&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;실제로 relocatable object files은 각각 text와 data 영역을 가지며 linker에 의해 가상 메모리에 할당된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1657&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rURaA/btsLmpgzygu/38La10Vc7Xv41SkVMDnyhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rURaA/btsLmpgzygu/38La10Vc7Xv41SkVMDnyhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rURaA/btsLmpgzygu/38La10Vc7Xv41SkVMDnyhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrURaA%2FbtsLmpgzygu%2F38La10Vc7Xv41SkVMDnyhK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;362&quot; data-origin-width=&quot;1657&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;주의해야 할 것은 바로 local symbol이다. Local Non-static C variable은 일반적인 지역변수를 말하는데 메모리의 스택 영역에 올라간다. 반면에 Local Static C variable은 .bss 혹은 .data 영역에 저장된다. Lifetime은 다르지만 Global variable과 동일하게 취급되는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;만약에 symbol이 중복된다면 symbol table에 어떻게 저장될까? 모든 symbol들은 strong과 weak로 구분할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;strong : Procedure과 초기화된 global symbols&lt;/li&gt;
&lt;li&gt;weak : 초기화되지 않은 global symbols&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;아래 그림처럼 같은 이름을 가진 symbol이 있다. 서로 다른 메모리 공간을 하지 않고, p2()에서 foo를 출력하면 5가 나올 것이다. 왜냐하면 초기화된 foo가 strong symbol이기 때문에 이를 기준으로 할당된다. weak symbol에 대해서는 메모리를 할당하지 않는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;926&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baMm5x/btsLmHBmHNC/14d5uLGHxyYduiWX6wtCK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baMm5x/btsLmHBmHNC/14d5uLGHxyYduiWX6wtCK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baMm5x/btsLmHBmHNC/14d5uLGHxyYduiWX6wtCK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaMm5x%2FbtsLmHBmHNC%2F14d5uLGHxyYduiWX6wtCK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;139&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;926&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그래서 linker의 symbol rule이 있다. 먼저, 이름이 같은 strong symbol이 여러 개 잇는 경우, linker에서 에러가 발생한다. 만약에 strong symbol 하나, weak symbol 여러 개가 있다면, strong symbol을 참조하게 된다. 마지막으로 weak symbol이 여러 개 있다면 임의의 symbol이 선택된다. 첫번째 규칙 같은 경우에는 Linker의 오류가 발생하는데, 나머지 두개는 오류가 발생하지 않아서 의도치 않은 결과를 출력할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;예를 통해서 확인해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;첫 번째 예시의 경우, p1() {}이 동일하게 선언되어 오류가 발생한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bF2NAU/btsLnWRPoOn/OlvxOqR40bU5hJyGqn0wx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bF2NAU/btsLnWRPoOn/OlvxOqR40bU5hJyGqn0wx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bF2NAU/btsLnWRPoOn/OlvxOqR40bU5hJyGqn0wx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbF2NAU%2FbtsLnWRPoOn%2FOlvxOqR40bU5hJyGqn0wx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;358&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;100&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;두 번째의 경우 둘 다 weak symbol이므로 임의의 변수를 기준으로 메모리를 할당한다. 만약에 p1()에서 x = 5를 선언하고 p2()에서 x = x + 3을 했다면 p2()에서 x 값은 8이 된다. 이때 linker에 에러는 발생하지 않는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;117&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5THVE/btsLmiPANM3/C2kmss5vHUhckpugHzUjwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5THVE/btsLmiPANM3/C2kmss5vHUhckpugHzUjwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5THVE/btsLmiPANM3/C2kmss5vHUhckpugHzUjwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5THVE%2FbtsLmiPANM3%2FC2kmss5vHUhckpugHzUjwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;61&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;117&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;세 번째, 정수형 변수 2개와 double 형 변수가 선언되었다. 정수형 변수와 double형 변수가 이름이 동일하고 모두 weak symbol이다. 만약에 int x와 int y를 기준으로 할당했다고 하자. p2()에서 double x의 값을 업데이트를 하면, 8 byte가 업데이트되면서 y 값에 overwrite될 가능성이 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;136&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRKB21/btsLoe5v7VL/4FrbHGH9u7eBBARQGjbjJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRKB21/btsLoe5v7VL/4FrbHGH9u7eBBARQGjbjJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRKB21/btsLoe5v7VL/4FrbHGH9u7eBBARQGjbjJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRKB21%2FbtsLoe5v7VL%2F4FrbHGH9u7eBBARQGjbjJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;71&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;136&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;네 번째, 정수형으로 선언된 x, y가 strong symbol이므로 p2()에서 x를 읽어오면 7을 출력하게 된다. 만약에 p2()에서 x 값을 수정하게 되면 y 값을 100% overwrite 하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;131&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chADT1/btsLmhC9GCN/UZ5Ev7csKAXWqgLFkup62K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chADT1/btsLmhC9GCN/UZ5Ev7csKAXWqgLFkup62K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chADT1/btsLmhC9GCN/UZ5Ev7csKAXWqgLFkup62K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchADT1%2FbtsLmhC9GCN%2FUZ5Ev7csKAXWqgLFkup62K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;68&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;131&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;마지막으로 strong과 weak이 각각 정의된 경우라면 strong symbol이 메모리에 올라가서 p2()에서는 7 값을 읽게 된다. 큰 문제는 발생하지 않는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;125&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQnB5A/btsLopzden8/LpiAqEkhxDquVFuU3leoS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQnB5A/btsLopzden8/LpiAqEkhxDquVFuU3leoS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQnB5A/btsLopzden8/LpiAqEkhxDquVFuU3leoS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQnB5A%2FbtsLopzden8%2FLpiAqEkhxDquVFuU3leoS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;65&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;125&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>CS/컴퓨터구조</category>
      <author>소-은</author>
      <guid isPermaLink="true">https://2nnsv.tistory.com/212</guid>
      <comments>https://2nnsv.tistory.com/212#entry212comment</comments>
      <pubDate>Wed, 18 Dec 2024 17:09:16 +0900</pubDate>
    </item>
    <item>
      <title>[컴퓨터구조] 2-3. Calling Convention</title>
      <link>https://2nnsv.tistory.com/211</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;RISC-V에서 함수를 호출하는 규약이 존재한다. 꼭 지켜야 하는 규칙은 아니지만 대부분 이렇게 사용한다고 보면 된다. 함수 호출 과정은 약간 복잡하다. 아래와 같은 함수가 있을 때 main()에서 f1, f2로 argument x와 y를 적절하게 전달해야 한다. 메모리 공간을 할당해 함수를 실행하고, 실행 후에는 기존의 PC 값으로 돌아와야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;일반 분기문과 달리, 명령어의 진행 순서가 연속적이지 않고 함수를 수행하고 나서는 꼭 원래 위치로 돌아가야 한다. 또한 아래 예시처럼 함수 수행 후 return 값이 main 함수에서 다시 사용될 수 있다. 이러한 복잡한 과정에서 아키텍쳐에서는 어떻게 구현되어 있는지 알아보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QwQka/btsLlPe6pDC/6RgAPtMxM6iZPX2Z7Ggq7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QwQka/btsLlPe6pDC/6RgAPtMxM6iZPX2Z7Ggq7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QwQka/btsLlPe6pDC/6RgAPtMxM6iZPX2Z7Ggq7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQwQka%2FbtsLlPe6pDC%2F6RgAPtMxM6iZPX2Z7Ggq7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;551&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1377&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;아래는 main에서 j 명령어를 사용해 myfn으로 이동하는 과정이다. myfn에서는 main 이후에 실행되어야 할 함수인 after1의 시작 주소로 j하는 것으로 마무리하고 있다. 이런 경우에는 우리가 의도한 대로 프로그램이 흘러간다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2271&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brrTZD/btsLnj6UVN7/m2oIO9Zqo6UdEqy0vfdwU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brrTZD/btsLnj6UVN7/m2oIO9Zqo6UdEqy0vfdwU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brrTZD/btsLnj6UVN7/m2oIO9Zqo6UdEqy0vfdwU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrrTZD%2FbtsLnj6UVN7%2Fm2oIO9Zqo6UdEqy0vfdwU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;264&quot; data-origin-width=&quot;2271&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그러나 myfn을 여러 번 호출하면 어떻게 될까? main에서 myfn을 호출하고 after1으로 돌아간다. 다시 after1에서 myfn을 호출하면 j after1으로 인해서 다시 after1으로 돌아가 무한 반복된다. 원래라면 after1에서 myfn을 호출하고 나면 after2로 이동하는게 맞다. 그러나 myfn을 여러 번 사용하게 되면 올바르게 작동하지 않는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2209&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJWqKY/btsLlHajNCm/YmVvrfpiInLDRcSRvoKh6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJWqKY/btsLlHajNCm/YmVvrfpiInLDRcSRvoKh6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJWqKY/btsLlHajNCm/YmVvrfpiInLDRcSRvoKh6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJWqKY%2FbtsLlHajNCm%2FYmVvrfpiInLDRcSRvoKh6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;272&quot; data-origin-width=&quot;2209&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그래서 jal, jalr 명령어를 사용해 돌아올 메모리 주소 값을 저장하고 확인한다. jal myfn을 호출하면 x1에 돌아올 주소, 즉 다음 실행할 명령어 주소 PC+4 값을 x1 레지스터에 저장한 후에 myfn으로 이동한다. myfn을 수행하고 나서 다시 돌아올 때는 jalr이라는 명령어로 x1에 저장된 주소 값을 확인해 해당 주소로 이동한다. 이렇게 하면 여러 번 myfn을 호출해도 아무 문제가 없다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2287&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brdA3n/btsLndTdNE6/Fhj3f0UPEOqQY3FBqJVDqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brdA3n/btsLndTdNE6/Fhj3f0UPEOqQY3FBqJVDqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brdA3n/btsLndTdNE6/Fhj3f0UPEOqQY3FBqJVDqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrdA3n%2FbtsLndTdNE6%2FFhj3f0UPEOqQY3FBqJVDqk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;262&quot; data-origin-width=&quot;2287&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;여기에서 또 문제가 하나 있다. 만약에 myfn에서 자기 자신을 호출하게 되면 어떻게 될까? main에서 jal로 이동한 후에 myfn에서 자신을 호출했다. x1에 after2 주소가 쓰여지고 after2에서 jalr을 하면 x1에 after2 주소가 쓰여있어 무한 반복된다. 이러한 문제를 해결하려면 레지스터에 값을 저장하는 것과 더불어 메모리 영역에 다음 실행할 주소 값을 저장해야 하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2066&quot; data-origin-height=&quot;929&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6HYSH/btsLnH7xSJR/VenqsbVTUz6YUKALWL4B50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6HYSH/btsLnH7xSJR/VenqsbVTUz6YUKALWL4B50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6HYSH/btsLnH7xSJR/VenqsbVTUz6YUKALWL4B50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6HYSH%2FbtsLnH7xSJR%2FVenqsbVTUz6YUKALWL4B50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;290&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2066&quot; data-origin-height=&quot;929&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;메모리 영역 중 Stack 영역에 return address를 담아두면 된다. addi sp, sp, -8을 통해 스택의 일부 공간을 확보한 다음 sd t5, 0(sp)을 통해 레지스터 값을 메모리에 저장하면 된다. 만약에 스택에서 pop 하고 싶으면 ld t4, 0(sp)으로 메모리에 저장된 값을 레지스터에 올리고, addi sp, sp, 8로 스택 포인터를 올려주면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1545&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bW0eUr/btsLmk6ESJH/Trfztsy7Jkoyb1zxsWgkW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bW0eUr/btsLmk6ESJH/Trfztsy7Jkoyb1zxsWgkW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bW0eUr/btsLmk6ESJH/Trfztsy7Jkoyb1zxsWgkW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbW0eUr%2FbtsLmk6ESJH%2FTrfztsy7Jkoyb1zxsWgkW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;618&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1545&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;아래 예시처럼 myfn에서 스택 공간을 확보하고 after2에서 확보된 스택 공간을 free 시킨다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1797&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZPeqn/btsLmG2G1wV/xHv5FP1pFKsM5ctbDYTkF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZPeqn/btsLmG2G1wV/xHv5FP1pFKsM5ctbDYTkF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZPeqn/btsLmG2G1wV/xHv5FP1pFKsM5ctbDYTkF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZPeqn%2FbtsLmG2G1wV%2FxHv5FP1pFKsM5ctbDYTkF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;334&quot; data-origin-width=&quot;1797&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그렇다면 main 함수에서 서로 다른 함수로 arguments는 어떻게 전달하는 것일까? 레지스터에 저장해두고 필요할 때 접근해 사용한다. 그런데, 서로 다른 함수에서 같은 레지스터의 값을 바꾸면 원치 않은 결과가 발생한다. caller는 함수를 호출하는 것을 뜻하고 callee는 호출당한 함수를 말하는데, caller와 callee는 서로 다른 레지스터를 사용해야 한다. 그래서 caller는 한번에 8개의 레지스터를 사용해 callee로 argument를 전달한다. 만약에 8개보다 많은 argument를 전달해야 하면 나머지 argument는 메모리의 &lt;b&gt;스택 영역&lt;/b&gt;에 올려 전달하고 사용하고 싶다면 메모리에 접근해야 한다. 참고로 작은 숫자부터 스택에 넣어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1590&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dRrIMO/btsLlTIEqlQ/VU1j93owC3vFfSzZ3oeEZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dRrIMO/btsLlTIEqlQ/VU1j93owC3vFfSzZ3oeEZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dRrIMO/btsLlTIEqlQ/VU1j93owC3vFfSzZ3oeEZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdRrIMO%2FbtsLlTIEqlQ%2FVU1j93owC3vFfSzZ3oeEZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;377&quot; data-origin-width=&quot;1590&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그런데 space for x17 부분은 왜 존재할까? 재귀가 아닌 이상 크게 필요가 없다. 그러나 재귀 함수를 호출하면 같은 레지스터를 사용해야 하는데, 값이 바뀔 수 있기 때문이다. 그래서 메모리에 레지스터의 원래 값을 저장해둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;정리하면 아래와 같은 blue, pink 함수가 있을 때, pink의 argument 6개를 레지스터로 전달하고 재귀적으로 사용하거나 필요한 경우에는 스택에 레지스터 값을 저장해두고 사용할 수 있다. 만약에 argument가 8개보다 많은 경우는 성능이 좋을까? 그렇지 않다. 해당 변수를 사용하기 위해서 메모리에 주기적으로 접근해야 하기 때문에 성능에 큰 영향을 미친다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734494831713&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;blue() {
	pink(0,1,2,3,4,5);
}

pink(int a, int b, int c, int d, int e, int f) {
	int*g;
	long A[2];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1401&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3dHb1/btsLnkx92sr/N1d02h5UURgHTu0MIlJhW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3dHb1/btsLnkx92sr/N1d02h5UURgHTu0MIlJhW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3dHb1/btsLnkx92sr/N1d02h5UURgHTu0MIlJhW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3dHb1%2FbtsLnkx92sr%2FN1d02h5UURgHTu0MIlJhW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;560&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1401&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;실제로 메모리에 올라갈 때는 이러한 방식으로 올라간다. 프로그램의 가상 메모리 상에서 stack에는 local variable, Heap에는 malloc으로 할당된 변수들, data 영역에는 static variable, global variable이 저장된다. 마지막으로 text 영역에는 실행할 프로그램 코드가 저장된다. data와 text 영역은 프로그램 실행 동안에 내용이 바뀌지는 않는다. heap과 stack은 프로그램의 실행 중에 크기와 내용이 바뀌는 영역이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1538&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TjtfP/btsLm34AxF9/qQPJeksKWwYY65xL7RWc70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TjtfP/btsLm34AxF9/qQPJeksKWwYY65xL7RWc70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TjtfP/btsLm34AxF9/qQPJeksKWwYY65xL7RWc70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTjtfP%2FbtsLm34AxF9%2FqQPJeksKWwYY65xL7RWc70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;390&quot; data-origin-width=&quot;1538&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1538&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBGRzT/btsLmT8O1Xs/WPkBGpJwqxAMyd2gxEjuA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBGRzT/btsLmT8O1Xs/WPkBGpJwqxAMyd2gxEjuA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBGRzT/btsLmT8O1Xs/WPkBGpJwqxAMyd2gxEjuA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBGRzT%2FbtsLmT8O1Xs%2FWPkBGpJwqxAMyd2gxEjuA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1538&quot; height=&quot;1000&quot; data-origin-width=&quot;1538&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;정리하면, Calling Convention에서 중요한 것은 조건문에서 적절하게 분기되어 해당 주소로 잘 이동하고 다시 돌아와야 한다. 또 함수에 필요한 argument를 잘 전달하기 위해서 레지스터를 사용하고 레지스터의 오염을 방지하기 위해서 메모리의 stack 영역에 저장해두고 필요할 때 load해서 사용한다.&lt;/p&gt;</description>
      <category>CS/컴퓨터구조</category>
      <author>소-은</author>
      <guid isPermaLink="true">https://2nnsv.tistory.com/211</guid>
      <comments>https://2nnsv.tistory.com/211#entry211comment</comments>
      <pubDate>Wed, 18 Dec 2024 13:25:06 +0900</pubDate>
    </item>
  </channel>
</rss>