• 로컬 네트워트에 여러 PC들이 연결되어 있고, 이 PC들 간에 데이터를 주고 받을 때, Time Stamp가 동기화 될 필요가 있습니다. 특히 ROS2와 같은 경우, Topic 등의 데이터들이 수집되는 상황에서 각 메시지들의 시간 값이 틀리게 되면, 에러를 발생하는 주요 원인이 됩니다.

    이를 위해서 시간 동기화를 하여야 되는데, 각 PC들이 인터넷 망에 연결된 상황이라면, 주기적으로 외부 NTP 서버를 통해 인터넷 시각을 기준으로 각 PC의 시간이 맞춰지게 됩니다. 하지만, 로컬 네트워크 (폐쇠망)의 경우 이렇게 외부 NTP 서버에 접속이 불가능하므로, 특정PC 하나를 기준으로 시간을 동기화할 수 있도록 하여, 연결된 PC의 시간을 동기화 시킬 수 있습니다.

    chrony는 기존 사용되었던 ntp의 개선버전(?)입니다.


    먼저 기준이 될 PC에서 작업을 시작합니다.

    chrony를 설치합니다.

    $ sudo apt install chrony

    다음으로 chrony.conf 파일을 수정합니다.

    $ sudo vi /etc/chrony/chrony.conf

    설정 파일 내에 다음의 구문을 추가합니다.

    pool ntp.ubuntu.com        iburst maxsources 4
    pool <기준-PC-IP-주소>      iburst maxsources 1
    pool 0.ubuntu.pool.ntp.org iburst maxsources 1
    pool 1.ubuntu.pool.ntp.org iburst maxsources 1
    pool 2.ubuntu.pool.ntp.org iburst maxsources 2
    
    ...
    
    local stratum 10
    allow 10.0.0.0/24   # 10.0.0.xx IP들에게만 동기화 기능 허용

    저장하고 나와서, chronyd, chrony 서비스를 재시작 해줍니다.

    $ sudo systemctl restart chronyd
    $ sudo systemctl restart chrony

    기준 PC에서 작업은 끝났습니다.


    다음으로 동기화 대상 PC들에서 다음과 같이 작업을 수행합니다.

    먼저 chrony 설치

    $ sudo apt install chrony

    설정파일 수정

    $ sudo vi /etc/chrony/chrony.conf

    다음과 같이 레퍼런스 주소에 기준 PC의 주소를 입력해 줍니다.

    #pool ntp.ubuntu.com        iburst maxsources 4
    #pool 0.ubuntu.pool.ntp.org iburst maxsources 1
    #pool 1.ubuntu.pool.ntp.org iburst maxsources 1
    #pool 2.ubuntu.pool.ntp.org iburst maxsources 2
    pool  10.0.0.10             iburst maxsources 1

    저장하고, chrony를 재시작해줍니다.

    $ sudo systemctl restart chrony

    강제로 동기화 명령 수행 – 보통은 기준 PC와 시간을 체크해서 특정 범위만큼 차이가 벌어지면 동기화 작업을 수행하게 됩니다만, 일단은 강제로 한번 동기화 작업을 진행해봅니다.

    $ sudo chronyc -a makestep
    200 OK

    레퍼런스를 확인해봅니다.

    $ chronyc sources
    MS Name/IP address         Stratum Poll Reach LastRx Last sample
    ===============================================================================
    ^* _gateway                      4   6    77    32    -27us[ -384us] +/-   11ms

    저의 경우엔, 기준PC의 주소가 gateway로 사용되고 있어 저렇게 IP 주소대신 gateway라고 표시되고 있습니다.

    동기화 상태로 확인 가능합니다.

    $ watch -n 0.5 chronyc tracking
    
    Reference ID    : 0A00000A (_gateway)
    Stratum         : 5
    Ref time (UTC)  : Fri Feb 14 00:26:30 2025
    System time     : 0.000031521 seconds slow of NTP time
    Last offset     : -0.000079722 seconds
    RMS offset      : 0.000119501 seconds
    Frequency       : 23.396 ppm slow
    Residual freq   : -0.139 ppm
    Skew            : 3.385 ppm
    Root delay      : 0.013619509 seconds
    Root dispersion : 0.004857286 seconds
    Update interval : 64.5 seconds
    Leap status     : Normal

    System time을 확인해보면 약 0.000031521 느린것을 확인할 수 있습니다.

    끝.

  • 로봇 시스템의 경우, 내부망과 외부로 연결하기 위한 인터넷망을 분리하여 설계하는 경우가 있습니다. 내부 기기들은 로컬망으로, 로봇을 원격으로 접속하기 위한 인터넷망을 따로 두는 경우인데, 보통 2개 이상의 디바이스 or PC가 들어가는 경우입니다. 대략 그림으로 설명해보면,

    이렇게 되어 있을 경우, PC2에서는 인터넷망에 접속이 불가능하므로 외부망으로 연결이 되지 않습니다. 패키지를 설치하거나 할 경우에 약간 불편한 경우가 발생하는데, iptables를 이용해서 PC2에서 PC1을 통해 인터넷망이 접속 가능하도록 설정할 수 있습니다.

    일단 PC1에서 필요한 패키지를 설치합니다.

    $ sudo apt install iptables iptables-persistent

    또, IP 포워드 기능이 활성화되어 있는지 확인합니다.

    $ sudo vi /etc/sysctrl.conf
    
    # 28th line uncomment
    net.ipv4.ip_forward=1

    다음으로 iptables를 설정합니다. 네트워크 인터페이스 이름은 위 그림을 기준으로 하겠습니다. (사용자 환경에 따라 변경하여 쓰시면 됩니다)

    $ sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
    $ sudo iptables -A FORWARD -i wlan0 -o eth0 -j ACCEPT
    $ sudo iptables -A FORWARD -i eth0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT

    이제 이걸 영구적으로 저장하고, 다음 부팅 후에도 사용하도록 설정합니다.

    $ sudo netfilter-persistent save
    $ sudo netfilter-persistent reload

    이제 PC1에서의 작업은 끝났습니다.


    다음으로 PC2에서는 IP 설정만 다시 해주면 됩니다. 먼저 현재 저장된 설정 이름을 확인해봅니다.

    $ nmcli connection show
    NAME                UUID                                  TYPE      DEVICE  
    Wired connection 1  fb960c33-6436-3331-b11b-174bfebacc62  ethernet  eth0    
    docker0             311a9d66-a3fe-4aeb-ac9c-665beb0a7e1f  bridge    docker0 
    

    저의 경우엔 “Wired connection 1″으로 되어 있습니다.

    이제 IP 설정을 해줍니다.

    $ sudo nmcli con modify "Wired connection 1"  ipv4.addresses 10.0.0.11/24
    $ sudo nmcli con modify "Wired connection 1"  ipv4.gateway 10.0.0.10
    $ sudo nmcli con modify "Wired connection 1"  ipv4.dns 8.8.8.8
    $ sudo nmcli con modify "Wired connection 1"  ipv4.method manual
    $ sudo nmcli con up "Wired connection 1"

    이제 연결을 테스트 해보면…

    $ ping 8.8.8.8
    PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
    64 bytes from 8.8.8.8: icmp_seq=1 ttl=51 time=43.7 ms
    64 bytes from 8.8.8.8: icmp_seq=2 ttl=51 time=41.3 ms

    외부망으로 연결이 잘 되는 것을 확인할 수 있습니다.

    끝.

  • 하여간 이 Jetson 개발자 놈들은 뭐하나 쉽게 넘어가는 꼴을 못보는듯 합니다. Jetpack5에서 Jetpack6으로 넘어가는데도 시간이 한참을 걸리더만, Jetpack6에 올라가서는 Realsense 카메라들이 인신이 안되는 버그가 있습니다. 원인은 해당 드라이버를 포함하지 않아서이고, 이를 해결하기 위해선 커널 소스를 받아 해당 드라이버를 재빌딩해서 복사해 줘야 합니다.

    특히 D435i의 경우 IMU가 내장되어 있는데, 이 IMU의 센서데이터를 USB HID 프로토콜을 이용해서 받도록 되어 있습니다. 이를 담당하는 디바이스 드라이버가 hidraw라는 녀석입니다. (아마 IMU가 내장되어 있지 않는 시리즈들은 잘 인식될 수도 있습니다. ^^)

    다행히 librealsense 레포지토리에 커널 및 모듈을 편하게 빌드할 수 있도록 스크립트를 잘 만들어놓아서 빌드까지는 쉽게 해결이 가능합니다.

    https://github.com/IntelRealSense/realsense_mipi_platform_driver/blob/dev/README_JP6.md

    $ git clone https://github.com/IntelRealSense/realsense_mipi_platform_driver.git -b dev
    $ cd realsense_mipi_platform_driver
    $ ./setup_workspace.sh 6.0
    $ ./apply_patches.sh apply 6.0
    $ ./build_all.sh 6.0

    유의해야 할 점은 현재까지는 Jetpack 6.0 (rev2)만 지원하고 있어서, 현재 최신버전인 6.2에서는 위 방법을 사용할 수 없고, 아직까지 지원 방안이 없습니다. 따라서 Jetson Orin NX 보드에 SDK Manager를 이용해 설치할 때, Jetpack 버전을 6.0 (rev2)로 선택해야 합니다.

    이렇게 빌드를 완료하고,


    그 다음, 빌드된 커널 모듈을 Jetson 보드에 복사하여 적용해야 하는데, 위 레포지토리의 문서를 따라서 진행하면, 드라이버의 문제인지 부팅이 제대로 되지 않거나, USB가 먹통이 되어버립니다. 따라서 다음의 과정만 진행하면 됩니다.

    $ cd realsense_mipi_platform_driver
    $ scp -r images/6.0/rootfs/lib/modules/5.15.136-tegra/extra <jetson-user-id>@<jetson-board-ip-address>:~/

    다음으로 Jetson 보드에서는

    $ sudo cp -r ~/extra /lib/modules/$(uname -r)/
    $ cd /lib/modules/$(uname -r)/
    $ sudo rm -rf videodev.ko

    다음으로 depmod 설정파일에 경로를 추가합니다.

    $ sudo vi /etc/depmod.d/ubuntu.conf
    
    search updates boot built-in
    이 부분을
    search extra updates boot built-in
    으로 수정

    다시 터미널에서 다음과 같이 실행하고

    $ sudo depmod

    재부팅한 다음, Realsense 카메라를 꼽아주면 제대로 인식이 되고, 정상적으로 사용이 가능합니다.

    끝.

  • Ubuntu 24.04를 설치하면 자동으로 Nvidia 그래픽카드 드라이버가 설치된다. Nvidia에서 직접 제공하는 Repository를 사용하면, 쉽게 최신 버전의 그래픽 카드 드라이버를 설치할 수 있다.

    먼저 현재 설치되어 있는 드라이버를 삭제한다.

    $ sudo apt purge *nvidia*

    다음으로, Nvidia Repository의 키를 등록 및 Repository 추가를 위한 패키지 설치한다.

    $ wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/x86_64/cuda-keyring_1.1-1_all.deb
    $ sudo dpkg -i cuda-keyring_1.1-1_all.deb

    이제, apt를 업데이트하고, 드라이버를 설치한다. 이때 cuda를 설치하면 해당 CUDA의 버전에 맞게 안정된 드라이버를 같이 설치해준다.

    $ sudo apt update
    $ sudo apt install cuda

    마지막으로 노트북의 경우, prime-select 기능을 이용해 intel 대신 nvidia 드라이버가 항상 사용할 수 있도록 설정해준다.

    $ sudo prime-select nvidia

    재부팅하면 완료.

  • 어디에선가의 요청에 의해서 JAVA 환경에서 MODBUS를 이용해 디바이스의 레지스터를 읽어오는 예제코드를 작성하였습니다. JAVA는 Hello World 출력 정도 기억만 남아 있어서..^^ 일단 대충 테스트 코드를 작성하여 동작시켜보니.. 역시나 잘 동작합니다.

    검색해보니 jlibmodbus가 유용해보이기도 하고, 실제 MODBUS-RTU over TCP/IP도 지원하고 있길래 사용하였습니다.

    package com.unknown.modbusmonitorexam;
    
    import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;
    import com.intelligt.modbus.jlibmodbus.exception.ModbusNumberException;
    import com.intelligt.modbus.jlibmodbus.exception.ModbusProtocolException;
    import com.intelligt.modbus.jlibmodbus.master.ModbusMaster;
    import com.intelligt.modbus.jlibmodbus.master.ModbusMasterFactory;
    import com.intelligt.modbus.jlibmodbus.serial.*;
    import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters;
    
    import java.net.InetAddress;
    import java.net.UnknownHostException;
    
    public class ModbusMonitorExam {
        public static void main(String[] args) throws UnknownHostException, ModbusIOException, SerialPortException, ModbusProtocolException, ModbusNumberException {
            TcpParameters tcpParameters = new TcpParameters();
            tcpParameters.setHost(InetAddress.getByName("192.168.10.11"));
            tcpParameters.setPort(9200);
            tcpParameters.setKeepAlive(true);
    
            SerialUtils.setSerialPortFactory(new SerialPortFactoryTcpClient(tcpParameters));
            ModbusMaster master = ModbusMasterFactory.createModbusMasterRTU(new SerialParameters());
    
            master.connect();
    
            int[] registerValues = master.readHoldingRegisters(1, 0, 5);
            for(int values : registerValues)
            {
                System.out.println("Value: " + values);
            }
        }
    }

    여담으로 Intellij IDEA를 이용해서 개발하니… 개발환경이 꽤나 편하네요..^^

  • 설치

    $ sudo curl https://packages.osrfoundation.org/gazebo.gpg --output /usr/share/keyrings/pkgs-osrf-archive-keyring.gpg
    $ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] http://packages.osrfoundation.org/gazebo/ubuntu-stable $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/gazebo-stable.list > /dev/null
    $ sudo apt-get update
    $ sudo apt-get install gz-harmonic

    실행

    $ gz sim

    ROS2 패키지 설치

    $ sudo apt install ros-humble-ros-gzharmonic

    설치 시 ros-humble-desktop-full 패키지 의존성이 깨지기 때문에, 기존 설치된 패키지들이 더이상 필요없다고 뜰텐데, 이건 무시해도 됨. ign-gazebo 관련 패키지들이 전부 지워지고 새버전으로 설치됨.

    패키지 실행

    $ ros2 launch ros_gz_sim gz_sim.launch.py

    설치 완료.

  • 원래는 노션에 적어놓고 매번 반복하던 내용인데, 아예 블로그에 올려 놓으면 귀찮게 로그인 안해도 볼수 있기에 옮겨 놓음.

    설치 환경: Ubuntu 22.04

    Ubuntu 설치 시 영문환경으로 하는 것을 강력히 추천. 한글 입력은 https://ahnbk.dev/?p=368를 참고하면 됨.

    ROS2 Humble 설치에 대한 공식 문서는 https://docs.ros.org/en/humble/Installation.html 를 참고하길 바람.


    Repository 추가 및 Key 등록

    sudo apt install software-properties-common
    sudo add-apt-repository universe
    $ sudo apt update
    $ sudo apt install curl -y
    $ sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
    $ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null

    ROS2 설치

    $ sudo apt update
    $ sudo apt install ros-humble-desktop-full


    개발환경 셋업

    $ echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc
    $ echo "export ROS_DOMAIN_ID=<your_domain_id>" >> ~/.bashrc
    $ echo "export ROS_LOCALHOST_ONLY=0" >> ~/.bashrc
    $ source ~/.bashrc

    개발툴 (colcon) 설치

    $ sudo apt install python3-colcon-common-extensions

    워크스페이스 생성 및 빌드

    $ mkdir -p ~/dev_ws/src
    $ cd ~/dev_ws
    $ colcon build --symlink-install

    생성한 워크스페이스 등록

    $ echo "source ~/dev_ws/install/setup.bash" >> ~/.bashrc

    colcon_cd, colcon 매개변수 자동완성 기능 활성화

    $ echo "source /usr/share/colcon_cd/function/colcon_cd.sh" >> ~/.bashrc
    $ echo "export _colcon_cd_root=/opt/ros/humble/" >> ~/.bashrc
    $ echo "source /usr/share/colcon_argcomplete/hook/colcon-argcomplete.bash"

    DDS 설치 (fastRTPS -> CycloneDDS)

    $ sudo apt install ros-humble-rmw-cyclonedds-cpp
    $ echo "export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp" >> ~/.bashrc

    cyclonedds 설정 파일 생성 및 등록

    $ vi ~/cyclonedds.xml
    <?xml version="1.0" encoding="UTF-8" ?>
    <CycloneDDS xmlns="https://cdds.io/config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://cdds.io/config https://raw.githubusercontent.com/eclipse-cyclonedds/cyclonedds/master/etc/cyclonedds.xsd">
        <Domain id="any">
            <General>
                <Interfaces>
                    <!-- <NetworkInterface autodetermine="false" name="enp2s0`" priority="default" multicast="default" /> -->
                    <NetworkInterface autodetermine="true" priority="default" multicast="default" />
                </Interfaces>
                <AllowMulticast>default</AllowMulticast>
                <MaxMessageSize>65500B</MaxMessageSize>
            </General>
        </Domain>
    </CycloneDDS>
    $ echo "export CYCLONEDDS_URI=file:///home/$(whoami)/cyclonedds.xml" >> ~/.bashrc

    rosdep 및 vcstool 설치

    $ sudo apt install python3-rosdep
    $ sudo rosdep init
    $ rosdep update
    $ sudo apt install python3-vcstool

    이것으로 기본 설치 완료. 대략 20분 정도 소요됨.

  • cmd.exe나 PowerShell에서 Python3를 실행하는 경우, Tab키를 이용한 자동 완성 기능이 안된다. 이를 해결하기 위해선, 간단히 다음의 패키지를 설치하면 됨.

    > pip install -U pyreadline

    pyreadline은 Python3 실행 시 윈도우에서 자동 완성기능을 담당하는 패키지임.

    Ubuntu에서는 당연히 되던 기능인데, 윈도우 환경에선 안되길래 검색해보니 해결 방법이 바로 보임.

  • 설치환경, Ubuntu 24.04

    설치할 때, 언어는 영어(English)로 하는 것을 추천. 한글 입력은 https://ahnbk.dev/?p=368 참고.

    ROS2 설치에 대한 자세한 설명은 공식 문서 페이지를 참조. (https://docs.ros.org/en/jazzy/Installation/Ubuntu-Install-Debians.html)


    Repository 설정

    $ sudo apt install software-properties-common
    $ sudo add-apt-repository universe
    $ sudo apt update && sudo apt install curl -y
    $ sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
    $ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null

    ROS2 개발에 필요한 툴들을 모아놓은 메타 패키지 설치 (옵션)

    $ sudo apt install ros-dev-tools


    ROS2 설치 시작

    $ sudo apt update
    $ sudo apt install ros-jazzy-desktop

    DDS 설치. 기본으론 FastRTPS가 설치되어 있으나, CycloneDDS을 사용하는 것을 권장함

    $ sudo apt install ros-jazzy-rmw-cyclonedds-cpp


    환경설정

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

    도메인 아이디 설정

    $ echo "export ROS_DOMAIN_ID=<your_domain_id>" >> ~/.bashrc

    Jazzy 버전부터는 DDS를 이용한 자동 연결 기능에 대한 범위를 환경변수로 설정 가능

    $ echo "export ROS_AUTOMATIC_DISCOVERY_RANGE=<discover option>" >> ~/.bashrc

    옵션값은 다음의 한 종류를 선택

    SUBNET : 현재 서브넷 설정 (예: 255.255.255.0)에 따라 Node 발견(Discover) 및 연결 (Default)
    LOCALHOST : 현재 머신 내에서만 Node 발견(Discover) 및 연결 
    OFF : 다른 노드와 연결하지 않음, 단독 실행
    SYSTEM_DEFAULT : 초기값 사용 (SUBNET)

    설정이 끝난 다음, source나 터미널 재실행으로 환경변수 셋업 적용

    $ source ~/.bashrc


    개발툴(colcon) 설치 및 설정

    $ sudo apt install python3-colcon-common-extensions

    colcon_cd 명령 사용을 위한 환경변수 셋업

    $ echo "source /usr/share/colcon_cd/function/colcon_cd.sh" >> ~/.bashrc
    $ echo "export _colcon_cd_root=/opt/ros/jazzy/" >> ~/.bashrc

    workspace 디렉토리 생성

    $ mkdir -p ~/dev_ws/src
    $ cd ~/dev_ws

    빌드

    $ colcon build --symlink-install

    현재 workspace 사용하도록 환경변수 설정

    $ echo "source ~/dev_ws/install/setup.bash" >> ~/.bashrc

    rosdep 설치

    $ sudo apt install python3-rosdep
    $ sudo rosdep init
    $ rosdep update

    vcstool 설치

    $ sudo apt install python3-vcstool

    이것으로 일단 기본 설치 및 환경 셋업 완료.

  • 설치는 간단히 다음과 같이 입력하여 설치

    $ sudo apt install neovim

    다음으로, 기본 vi, vim 커맨드를 입력시 자동으로 nvim으로 연결하도록 설정한다.

    $ sudo update-alternatives --config vim
    There are 2 choices for the alternative vim (providing /usr/bin/vim).
    
      Selection    Path                Priority   Status
    ------------------------------------------------------------
      0            /usr/bin/nvim        30        auto mode
    * 1            /usr/bin/nvim        30        manual mode
      2            /usr/bin/vim.basic   30        manual mode
    
    Press <enter> to keep the current choice[*], or type selection number:

    여기서 /user/bin/nvim을 선택하면, vim 입력시 nvim이 실행이 됨. 마찬가지로 vi에 대해서도 설정.

    $ sudo update-alternatives --config vi
    There are 2 choices for the alternative vi (providing /usr/bin/vi).
    
      Selection    Path                Priority   Status
    ------------------------------------------------------------
      0            /usr/bin/nvim        30        auto mode
    * 1            /usr/bin/nvim        30        manual mode
      2            /usr/bin/vim.basic   30        manual mode
    
    Press <enter> to keep the current choice[*], or type selection number:

    마찬가지로 1번 선택.

    alias나 심볼릭 링크를 번거롭게 설정하는 대신, 위와 같이 설정하면 간단히 해결됨.