10) Here Document를 이용한 XML 작성
cat > "${CYCLONEDDS_FILE}" <<XML
<CycloneDDS>
<Domain id="any">
<General>
<NetworkInterfaceAddress>${interface}</NetworkInterfaceAddress>
<AllowMulticast>default</AllowMulticast>
</General>
</Domain>
</CycloneDDS>
XML
<<XML은 Bash의 Here Document 문법입니다.
여러 줄의 텍스트를 파일에 기록할 때 사용합니다.
기존 파일 덮어쓰기
cat > "${CYCLONEDDS_FILE}"
>는 파일을 새로 작성합니다.
같은 이름의 파일이 이미 있다면 기존 내용을 지우고 새 내용으로 덮어씁니다.
변수 치환
Here Document 시작 부분이 다음과 같습니다.
<<XML
구분자인 XML에 작은따옴표가 없으므로 내부 변수는 실제 값으로 치환됩니다.
다음 코드에서:
<NetworkInterfaceAddress>${interface}</NetworkInterfaceAddress>
interface가 wlan0이면 다음 내용으로 저장됩니다.
<NetworkInterfaceAddress>wlan0</NetworkInterfaceAddress>
생성되는 실제 파일
최종 파일은 대략 다음과 같습니다.
<CycloneDDS>
<Domain id="any">
<General>
<NetworkInterfaceAddress>wlan0</NetworkInterfaceAddress>
<AllowMulticast>default</AllowMulticast>
</General>
</Domain>
</CycloneDDS>
Domain 설정
<Domain id="any">
모든 DDS Domain에 이 설정을 적용한다는 의미입니다.
ROS 2 Domain ID 자체는 별도로 다음 환경 변수에서 지정합니다.
ROS_DOMAIN_ID=200
네트워크 인터페이스 지정
<NetworkInterfaceAddress>wlan0</NetworkInterfaceAddress>
Cyclone DDS가 어떤 네트워크 장치를 사용하여 통신할지 지정합니다.
TurtleBot3가 유선과 무선 네트워크를 동시에 사용하거나 Docker, VPN 인터페이스가 존재하는 경우 잘못된 장치를 선택하는 문제를 줄일 수 있습니다.
Multicast 설정
<AllowMulticast>default</AllowMulticast>
Cyclone DDS의 기본 multicast 정책을 사용합니다.
ROS 2 노드 검색은 일반적으로 multicast 환경의 영향을 받습니다.
같은 공유기에 연결되어 있어도 공유기의 AP isolation 또는 client isolation 기능이 활성화되어 있으면 원격 PC에서 TurtleBot3를 찾지 못할 수 있습니다.
11) ROS 2 실행 명령 생성 함수
ros_shell()
{
local ros_command="$1"
local payload
ros_shell() 함수는 tmux window 안에서 실행할 완전한 Bash 명령 문자열을 생성합니다.
단순히 다음 명령만 실행하는 것이 아닙니다.
ros2 launch ...
각 tmux window에서 다음 작업을 모두 수행하는 문자열을 만듭니다.
- ROS 2 Humble 환경 source
- TurtleBot3 작업공간 source
- RGB LED 작업공간 source
- ROS Domain ID 설정
- TurtleBot3 모델 설정
- LiDAR 모델 설정
- Cyclone DDS RMW 설정
- 네트워크 인터페이스 설정
- Cyclone DDS XML 경로 설정
- 실제 ROS 2 명령 실행
tmux의 각 window는 별도의 셸 프로세스로 실행되므로 모든 window에 환경을 개별적으로 적용해야 합니다.
실행할 ROS 명령 전달
local ros_command="$1"
함수에 전달된 첫 번째 인자를 ros_command라는 지역 변수에 저장합니다.
예를 들어 다음과 같이 호출하면:
ros_shell 'exec ros2 launch turtlebot3_bringup robot.launch.py'
ros_command에는 다음 문자열이 저장됩니다.
exec ros2 launch turtlebot3_bringup robot.launch.py
payload 문자열 생성
printf -v payload \
'source %q && source %q && source %q && export ROS_DOMAIN_ID=200 LDS_MODEL=LDS-03 TURTLEBOT3_MODEL=burger RMW_IMPLEMENTATION=rmw_cyclonedds_cpp ROS_NETWORK_INTERFACE=%q CYCLONEDDS_URI=%q && %s' \
"${ROS_SETUP}" \
"${TB3_SETUP}" \
"${RGB_LED_SETUP}" \
"${ROS_NETWORK_INTERFACE}" \
"file://${CYCLONEDDS_FILE}" \
"${ros_command}"
이 부분은 스크립트에서 가장 복잡하지만 가장 중요한 부분입니다.
printf -v의 의미
printf -v payload
일반 printf는 결과를 화면에 출력합니다.
printf '%s\n' "hello"
-v를 사용하면 화면에 출력하지 않고 지정한 변수에 저장합니다.
printf -v payload '형식' 값
결과적으로 완성된 명령 문자열이 payload 변수에 저장됩니다.
%q의 의미
%q
Bash의 printf에서 %q는 문자열을 셸에서 안전하게 다시 사용할 수 있도록 escape합니다.
예를 들어 경로에 공백이나 특수문자가 있더라도 하나의 안전한 인자로 전달할 수 있습니다.
다음 변수들이 %q로 처리됩니다.
"${ROS_SETUP}"
"${TB3_SETUP}"
"${RGB_LED_SETUP}"
"${ROS_NETWORK_INTERFACE}"
"file://${CYCLONEDDS_FILE}"
일반적인 환경에서는 경로에 공백이 없지만, 자동 생성되는 셸 명령 문자열에서는 %q를 사용하는 것이 더 안전합니다.
&&로 명령 연결
완성되는 payload는 대략 다음 구조입니다.
source ROS_SETUP &&
source TB3_SETUP &&
source RGB_LED_SETUP &&
export 환경변수들 &&
실제 ROS 2 명령
&&는 앞 명령이 성공했을 때만 다음 명령을 실행합니다.
예를 들어 TurtleBot3 setup 파일 source에 실패하면 카메라나 bringup 명령은 실행되지 않습니다.
이는 오류 상태에서 불완전한 환경으로 ROS 2 노드를 실행하는 것을 방지합니다.
ROS 2 환경 변수 설정
export ROS_DOMAIN_ID=200
ROS 2 통신 Domain을 200으로 지정합니다.
원격 PC도 같은 값을 사용해야 합니다.
export ROS_DOMAIN_ID=200
다음 설정은 LiDAR 모델을 LDS-03으로 지정합니다.
export LDS_MODEL=LDS-03
TurtleBot3 모델은 Burger로 설정합니다.
export TURTLEBOT3_MODEL=burger
RMW 구현체는 Cyclone DDS를 사용합니다.
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
자동 감지한 인터페이스도 환경 변수로 전달합니다.
export ROS_NETWORK_INTERFACE=wlan0
Cyclone DDS 설정 파일은 URI 형태로 전달합니다.
export CYCLONEDDS_URI=file:///tmp/turtlebot3_cyclonedds_sjyong.xml
로컬 파일 URI는 file:// 뒤에 절대 경로가 붙으므로 /tmp 경로에서는 슬래시가 세 개가 됩니다.
file:///tmp/파일.xml
%s로 실제 명령 삽입
&& %s
마지막 %s 위치에 ros_command가 그대로 들어갑니다.
bringup 명령을 전달했다면 최종 payload 끝부분은 다음과 같습니다.
&& exec ros2 launch turtlebot3_bringup robot.launch.py
/bin/bash -lc 명령 생성
printf '/bin/bash -lc %q' "${payload}"
이 줄은 최종적으로 tmux가 실행할 명령을 출력합니다.
결과는 대략 다음 형태가 됩니다.
/bin/bash -lc 'source ... && export ... && ros2 launch ...'
/bin/bash
tmux window 안에서 Bash를 실행합니다.
-l
login shell로 실행합니다.
로그인 셸의 환경 초기화 규칙을 적용할 수 있습니다.
-c
뒤에 전달된 문자열을 명령으로 실행합니다.
bash -c "명령 문자열"
최종 출력
ros_shell() 함수는 직접 ROS 2 명령을 실행하지 않습니다.
tmux에 전달할 완성된 명령 문자열을 출력합니다.
뒤에서는 다음과 같이 명령 치환으로 사용됩니다.
"$(ros_shell 'exec ros2 launch ...')"
exec ros2를 사용하는 이유
bringup, camera, audio 명령 앞에는 exec가 들어 있습니다.
exec ros2 launch turtlebot3_bringup robot.launch.py
exec는 현재 Bash 프로세스를 ROS 2 프로세스로 교체합니다.
일반 실행 구조는 다음과 같습니다.
tmux
└── bash
└── ros2
exec를 사용하면 다음처럼 됩니다.
tmux
└── ros2
중간 Bash 프로세스가 없어지므로 다음 장점이 있습니다.
- tmux에서 종료 신호가 ROS 2 프로세스에 직접 전달됩니다.
- 불필요한 중간 셸 프로세스가 남지 않습니다.
- pane 종료와 ROS 2 프로세스 종료 관계가 명확해집니다.
12) 기존 tmux session 접속 함수
attach_session()
{
if [[ -n "${TMUX:-}" ]]; then
tmux switch-client -t "${SESSION_NAME}"
else
exec tmux attach-session -t "${SESSION_NAME}"
fi
}
이 함수는 현재 사용자가 이미 tmux 안에 있는지 확인한 후 적절한 접속 명령을 선택합니다.
TMUX 환경 변수 확인
[[ -n "${TMUX:-}" ]]
tmux 안에서 실행 중이면 보통 TMUX 환경 변수가 설정되어 있습니다.
다음 명령으로 확인할 수 있습니다.
echo "$TMUX"
tmux 내부에서는 대략 다음과 같은 값이 표시될 수 있습니다.
/tmp/tmux-1000/default,1234,0
tmux 외부에서는 일반적으로 빈 값입니다.
${TMUX:-}를 사용한 이유는 set -u 상태에서도 안전하게 확인하기 위해서입니다.
tmux 내부에서 session 변경
tmux switch-client -t "${SESSION_NAME}"
이미 tmux 안에 있는 상태에서 다른 session으로 이동할 때 사용합니다.
tmux 내부에서 다시 attach-session을 실행하면 중첩 tmux 문제가 발생할 수 있습니다.
따라서 tmux 안에서는 switch-client를 사용합니다.
실제 대상은 다음과 같습니다.
tmux switch-client -t turtlebot3
tmux 외부에서 session 접속
exec tmux attach-session -t "${SESSION_NAME}"
현재 일반 SSH 터미널이라면 기존 tmux session에 접속합니다.
실제 명령은 다음과 같습니다.
tmux attach-session -t turtlebot3
앞의 exec는 현재 스크립트 프로세스를 tmux attach 프로세스로 교체합니다.
사용자가 tmux에서 빠져나오면 불필요한 Bash 스크립트 프로세스가 남지 않습니다.
13) tmux session 생성 함수
create_session()
{
이 함수는 TurtleBot3용 tmux session 전체를 생성합니다.
실행 순서는 다음과 같습니다.
→ setup 파일 검사
→ 네트워크 인터페이스 감지
→ Cyclone DDS 설정 파일 생성
→ bringup window 생성
→ camera window 생성
→ audio window 생성
→ debug window 생성
→ debug pane 분할
→ window 유지 옵션 설정
→ debug pane 정렬
→ bringup window 선택
→ 설정 결과 출력
setup 파일 확인
check_file "${ROS_SETUP}"
check_file "${TB3_SETUP}"
check_file "${RGB_LED_SETUP}"
필요한 세 개의 setup 파일이 모두 존재하는지 검사합니다.
검사 대상은 다음과 같습니다.
/opt/ros/humble/setup.bash
~/turtlebot3_ws/install/setup.bash
~/rgb_led_ws/install/setup.bash
하나라도 없으면 session 생성을 중단합니다.
예를 들어 RGB LED 작업공간이 빌드되지 않았다면 다음 오류가 출력됩니다.
[ERROR] setup 파일을 찾을 수 없습니다: /home/ubuntu/rgb_led_ws/install/setup.bash
네트워크 인터페이스 변수 export
export ROS_NETWORK_INTERFACE
ROS_NETWORK_INTERFACE="$(detect_network_interface)"
첫 번째 줄은 ROS_NETWORK_INTERFACE를 환경 변수로 export합니다.
export ROS_NETWORK_INTERFACE
두 번째 줄에서 자동 감지 함수의 결과를 저장합니다.
ROS_NETWORK_INTERFACE="$(detect_network_interface)"
감지 결과가 wlan0이면 다음과 같은 상태가 됩니다.
export ROS_NETWORK_INTERFACE=wlan0
이 변수는 현재 스크립트뿐 아니라 자식 프로세스에서도 사용할 수 있습니다.
Cyclone DDS 설정 파일 생성
write_cyclonedds_config "${ROS_NETWORK_INTERFACE}"
감지된 인터페이스 이름을 전달하여 Cyclone DDS XML 파일을 생성합니다.
예를 들어 인터페이스가 wlan0이라면 XML 내부에 다음 값이 들어갑니다.
<NetworkInterfaceAddress>wlan0</NetworkInterfaceAddress>
bringup session 생성
tmux new-session -d \
-s "${SESSION_NAME}" \
-n bringup \
"$(ros_shell 'exec ros2 launch turtlebot3_bringup robot.launch.py')"
이 명령은 새로운 tmux session과 첫 번째 window를 동시에 생성합니다.
백그라운드 생성
-d
detached 상태로 session을 생성합니다.
즉, session을 만들지만 즉시 화면에 접속하지는 않습니다.
나머지 window와 pane을 모두 구성한 후 별도로 접속합니다.
session 이름
-s "${SESSION_NAME}"
session 이름을 turtlebot3으로 지정합니다.
첫 번째 window 이름
-n bringup
첫 번째 window 이름을 bringup으로 지정합니다.
실행 명령
"$(ros_shell 'exec ros2 launch turtlebot3_bringup robot.launch.py')"
ros_shell() 함수가 ROS 환경 설정이 포함된 완전한 명령을 생성합니다.
bringup window에서 최종적으로 실행되는 핵심 명령은 다음과 같습니다.
ros2 launch turtlebot3_bringup robot.launch.py
이 window에서는 일반적으로 다음 기능이 실행됩니다.
- OpenCR 통신
- 모터 제어
- 엔코더 데이터
- Odometry
- TF
- LDS-03 LiDAR
- 배터리 및 센서 상태
tmux 스크롤 기록 크기 설정
tmux set-option -t "${SESSION_NAME}" history-limit 50000
tmux pane에 저장할 과거 출력 줄 수를 50,000줄로 설정합니다.
ROS 2 launch 로그가 길게 출력되더라도 이전 내용을 더 많이 확인할 수 있습니다.
tmux 스크롤 모드는 다음 키로 들어갑니다.
Ctrl+b
[
방향키나 Page Up, Page Down으로 이동할 수 있습니다.
스크롤 모드 종료는 다음 키입니다.
q
tmux 버전이나 설정 범위에 따라 history-limit 관련 오류가 발생한다면 window 옵션으로 명시할 수 있습니다.
tmux set-window-option -t "${SESSION_NAME}" history-limit 50000
마우스 기능 활성화
tmux set-option -t "${SESSION_NAME}" mouse on
tmux에서 마우스를 사용할 수 있도록 설정합니다.
활성화되면 다음 기능을 사용할 수 있습니다.
- 마우스로 pane 선택
- 마우스 휠로 로그 스크롤
- window 선택
- pane 경계 크기 조절
SSH 터미널 프로그램의 설정에 따라 마우스 선택 동작이 달라질 수 있습니다.
camera window 생성
tmux new-window \
-t "${SESSION_NAME}" \
-n camera \
"$(ros_shell 'exec ros2 launch turtlebot3_bringup camera.launch.py format:=BGR888 width:=320 height:=240')"
기존 turtlebot3 session에 새로운 window를 추가합니다.
대상 session
-t "${SESSION_NAME}"
새 window를 추가할 session을 지정합니다.
window 이름
-n camera
window 이름을 camera로 지정합니다.
카메라 launch 실행
ros2 launch turtlebot3_bringup camera.launch.py \
format:=BGR888 \
width:=320 \
height:=240
카메라 출력 형식은 BGR888로 설정합니다.
영상 크기는 다음과 같습니다.
가로: 320
세로: 240
낮은 해상도를 사용하면 TurtleBot3의 CPU 사용량과 네트워크 대역폭을 줄일 수 있습니다.
audio window 생성
tmux new-window \
-t "${SESSION_NAME}" \
-n audio \
"$(ros_shell 'exec ros2 run robot_audio_output audio_output_node')"
세 번째 window를 생성하고 오디오 출력 노드를 실행합니다.
실행되는 핵심 명령은 다음과 같습니다.
ros2 run robot_audio_output audio_output_node
이 명령을 실행하기 전에 ros_shell() 함수가 다음 환경을 모두 source합니다.
source /opt/ros/humble/setup.bash
source ~/turtlebot3_ws/install/setup.bash
source ~/rgb_led_ws/install/setup.bash
따라서 robot_audio_output 패키지를 찾기 위해 사용자가 audio window에서 수동으로 source할 필요가 없습니다.
debug window 생성
tmux new-window \
-t "${SESSION_NAME}" \
-n debug \
"$(ros_shell "watch -n 2 -t 'ros2 node list 2>/dev/null'")"
네 번째 window를 만들고 첫 번째 디버깅 pane을 실행합니다.
watch 명령
watch -n 2 -t 'ros2 node list 2>/dev/null'
watch는 지정한 명령을 반복 실행하고 화면을 갱신합니다.
-n 2
2초 간격으로 실행합니다.
-t
watch의 제목 줄을 표시하지 않습니다.
실제로 반복 실행되는 명령은 다음과 같습니다.
ros2 node list
ROS 2 노드 목록을 2초마다 갱신하여 보여줍니다.
2>/dev/null
ROS 2 discovery 과정에서 발생할 수 있는 오류 메시지를 숨깁니다.
다만 오류 원인을 분석할 때는 오류 메시지가 숨겨져 있다는 점을 고려해야 합니다.
debug 두 번째 pane 생성
tmux split-window \
-t "${SESSION_NAME}:debug" \
"$(ros_shell "watch -n 2 -t 'ros2 topic list 2>/dev/null'")"
debug window를 분할하여 두 번째 pane을 만듭니다.
대상은 다음과 같습니다.
turtlebot3:debug
두 번째 pane에서 실행되는 명령은 다음과 같습니다.
watch -n 2 -t 'ros2 topic list 2>/dev/null'
ROS 2 토픽 목록을 2초마다 갱신합니다.
TurtleBot3가 정상적으로 실행되면 일반적으로 다음 토픽이 표시됩니다.
/scan
/odom
/tf
/tf_static
/joint_states
/cmd_vel
/battery_state
카메라가 정상이라면 영상 관련 토픽도 표시됩니다.
/camera/image_raw
실제 토픽 이름은 카메라 패키지 설정에 따라 다를 수 있습니다.
debug 세 번째 pane 생성
tmux split-window \
-t "${SESSION_NAME}:debug" \
"$(ros_shell 'ros2 topic hz /scan; exec bash -i')"
세 번째 pane에서는 LiDAR의 /scan 토픽 발행 주기를 확인합니다.
ros2 topic hz /scan
정상적으로 LiDAR 데이터가 발행되면 다음과 비슷한 결과가 반복 출력됩니다.
average rate: 10.012
min: 0.098s max: 0.102s std dev: 0.001
세미콜론의 의미
ros2 topic hz /scan; exec bash -i
세미콜론은 앞 명령이 성공하거나 실패하더라도 뒤 명령을 실행합니다.
ros2 topic hz /scan은 일반적으로 사용자가 Ctrl+C를 누를 때까지 계속 실행됩니다.
사용자가 측정을 중단하면 다음 명령이 실행됩니다.
exec bash -i
대화형 Bash 유지
exec bash -i
-i는 interactive shell을 의미합니다.
따라서 /scan 주기 측정을 중단한 뒤 pane이 바로 닫히지 않고 사용자가 다른 ROS 2 명령을 입력할 수 있습니다.
예를 들어 다음 명령을 실행할 수 있습니다.
ros2 topic echo /scan --once
ros2 topic info /scan -v
ros2 topic hz /odom
debug 네 번째 pane 생성
tmux split-window \
-t "${SESSION_NAME}:debug" \
"$(ros_shell "printf '\nTurtleBot3 debug shell\nROS_DOMAIN_ID=%s\nTURTLEBOT3_MODEL=%s\nLDS_MODEL=%s\nRMW_IMPLEMENTATION=%s\nROS_NETWORK_INTERFACE=%s\nCYCLONEDDS_URI=%s\n\n' \"\${ROS_DOMAIN_ID}\" \"\${TURTLEBOT3_MODEL}\" \"\${LDS_MODEL}\" \"\${RMW_IMPLEMENTATION}\" \"\${ROS_NETWORK_INTERFACE}\" \"\${CYCLONEDDS_URI}\"; exec bash -i")"
네 번째 pane은 현재 ROS 2 환경 변수 값을 출력한 후 대화형 Bash를 실행합니다.
출력 형식은 다음과 같습니다.
TurtleBot3 debug shell
ROS_DOMAIN_ID=200
TURTLEBOT3_MODEL=burger
LDS_MODEL=LDS-03
RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
ROS_NETWORK_INTERFACE=wlan0
CYCLONEDDS_URI=file:///tmp/turtlebot3_cyclonedds_ubuntu.xml
변수 앞의 역슬래시
다음 부분에는 변수 앞에 역슬래시가 들어 있습니다.
\"\${ROS_DOMAIN_ID}\"
역슬래시가 없으면 바깥쪽 스크립트가 ros_shell() 함수를 호출할 때 변수가 너무 일찍 확장될 수 있습니다.
\${ROS_DOMAIN_ID}
형태로 작성하면 최종적으로 tmux 내부의 Bash가 실행될 때 변수가 확장됩니다.
즉, 변수 평가 시점을 안쪽 셸로 미루는 역할을 합니다.
이 부분은 여러 단계의 셸 문자열을 생성할 때 중요합니다.
실행 구조는 다음과 같습니다.
현재 Bash 스크립트
→ ros_shell 함수에서 명령 문자열 생성
→ tmux가 문자열 전달
→ /bin/bash -lc가 문자열 실행
→ 내부 환경 변수 평가
디버깅 셸 유지
환경 변수를 출력한 후 다음 명령이 실행됩니다.
exec bash -i
따라서 사용자는 네 번째 pane에서 자유롭게 명령을 입력할 수 있습니다.
대표적인 디버깅 명령은 다음과 같습니다.
ros2 node list
ros2 topic list
ros2 service list
ros2 action list
ros2 topic echo /odom --once
ros2 topic info /scan -v
ros2 topic hz /camera/image_raw
모든 window에 remain-on-exit 적용
for window_name in bringup camera audio debug; do
tmux set-window-option \
-t "${SESSION_NAME}:${window_name}" \
remain-on-exit on
done
for 반복문으로 네 개의 window에 동일한 옵션을 적용합니다.
반복되는 window 이름은 다음과 같습니다.
bringup
camera
audio
debug
실제로는 다음 네 명령과 같습니다.
tmux set-window-option -t turtlebot3:bringup remain-on-exit on
tmux set-window-option -t turtlebot3:camera remain-on-exit on
tmux set-window-option -t turtlebot3:audio remain-on-exit on
tmux set-window-option -t turtlebot3:debug remain-on-exit on
remain-on-exit on은 window 안의 프로그램이 종료되어도 pane을 없애지 않습니다.
카메라 노드가 장치 오류로 종료되었다면 해당 pane이 남아 있기 때문에 마지막 오류 메시지를 확인할 수 있습니다.
이 옵션이 없으면 프로그램 종료와 함께 pane 또는 window가 사라져 문제를 확인하기 어려울 수 있습니다.
debug pane 자동 정렬
tmux select-layout -t "${SESSION_NAME}:debug" tiled
debug window에 생성된 네 개 pane을 균등하게 배치합니다.
최종 형태는 대략 다음과 같습니다.
┌────────┬────────┐
│ ros2 node list │ ros2 topic list │
│ 2초마다 갱신 │ 2초마다 갱신 │
├────────┼────────┤
│ ros2 topic hz │ debug shell │
│ /scan │환경 변수 및 명령│
└────────┴────────┘
split-window를 연속해서 실행하면 pane 크기가 불균형할 수 있습니다.
tiled 레이아웃은 전체 pane을 가능한 한 균등한 크기로 재배치합니다.
기본 표시 window 선택
tmux select-window -t "${SESSION_NAME}:bringup"
session 구성 완료 후 bringup window를 현재 활성 window로 선택합니다.
따라서 사용자가 session에 접속하면 가장 먼저 TurtleBot3 본체 실행 로그를 확인할 수 있습니다.
bringup 로그는 다음 문제를 확인하는 데 중요합니다.
- OpenCR 연결 실패
- LiDAR 연결 실패
- 시리얼 포트 권한 문제
- 잘못된 TurtleBot3 모델 설정
- 센서 초기화 오류
- 모터 통신 오류
생성 완료 정보 출력
echo "[OK] tmux session: ${SESSION_NAME}"
echo "[OK] network interface: ${ROS_NETWORK_INTERFACE}"
echo "[OK] Cyclone DDS config: ${CYCLONEDDS_FILE}"
session 생성 후 핵심 설정값을 출력합니다.
출력 예시는 다음과 같습니다.
[OK] tmux session: turtlebot3
[OK] network interface: wlan0
[OK] Cyclone DDS config: /tmp/turtlebot3_cyclonedds_ubuntu.xml
첫 번째 줄은 생성한 tmux session 이름입니다.
두 번째 줄은 Cyclone DDS가 사용할 네트워크 인터페이스입니다.
세 번째 줄은 생성한 Cyclone DDS XML 파일 경로입니다.
이 정보는 원격 PC에서 ROS 2 노드가 검색되지 않을 때 중요한 점검 자료가 됩니다.
14) create_session 함수가 생성하는 최종 구조
create_session() 함수 실행이 완료되면 다음 구조가 만들어집니다.
Session: turtlebot3
Window 0: bringup
└── ros2 launch turtlebot3_bringup robot.launch.py
Window 1: camera
└── ros2 launch turtlebot3_bringup camera.launch.py
format:=BGR888 width:=320 height:=240
Window 2: audio
└── ros2 run robot_audio_output audio_output_node
Window 3: debug
├── Pane 1: ros2 node list
├── Pane 2: ros2 topic list
├── Pane 3: ros2 topic hz /scan
└── Pane 4: 수동 디버깅 Bash
15) show_status 함수 정의
show_status()
{
show_status() 함수는 현재 TurtleBot3 tmux session이 실행 중인지 확인하고, 실행 중이라면 window 정보를 출력합니다.
함수는 다음과 같이 호출합니다.
show_status
이 함수는 스크립트의 status 명령에서 사용됩니다.
./turtlebot3_tmux.sh status
tmux session 존재 여부 확인
if tmux has-session -t "${SESSION_NAME}" 2>/dev/null; then
tmux has-session 명령은 지정한 session이 존재하는지 확인합니다.
tmux has-session -t "${SESSION_NAME}"
SESSION_NAME이 turtlebot3이면 다음 session의 존재 여부를 검사합니다.
tmux has-session -t turtlebot3
session이 존재하면 명령은 성공 종료 코드인 0을 반환합니다.
session이 존재하지 않으면 실패 종료 코드를 반환합니다.
이 종료 코드를 if 조건문에서 사용합니다.
if tmux has-session ...; then
session이 존재할 때는 then 이후 코드가 실행됩니다.
2>/dev/null은 오류 메시지를 화면에 표시하지 않도록 합니다.
2>/dev/null
여기서 숫자 2는 표준 오류 출력을 의미합니다.
/dev/null은 들어온 데이터를 버리는 특수 파일입니다.
따라서 session이 존재하지 않을 때 tmux가 출력하는 오류 메시지를 숨길 수 있습니다.
현재 window 목록 출력
tmux list-windows \
-t "${SESSION_NAME}" \
-F '#{window_index}:#{window_name} panes=#{window_panes} active=#{window_active}'
이 명령은 지정한 tmux session에 생성된 모든 window 정보를 출력합니다.
기본 명령은 다음과 같습니다.
tmux list-windows
-t 옵션으로 대상 session을 지정합니다.
-t "${SESSION_NAME}"
-F 옵션은 출력 형식을 지정합니다.
-F '출력 형식'
사용된 형식은 다음과 같습니다.
'#{window_index}:#{window_name} panes=#{window_panes} active=#{window_active}'
각 변수의 의미는 다음과 같습니다.
#{window_index} window 번호
#{window_name} window 이름
#{window_panes} 해당 window의 pane 개수
#{window_active} 현재 선택된 window 여부
출력 예시는 다음과 같습니다.
0:bringup panes=1 active=1
1:camera panes=1 active=0
2:audio panes=1 active=0
3:debug panes=4 active=0
첫 번째 줄은 다음 의미입니다.
0:bringup panes=1 active=1
- window 번호는 0
- window 이름은 bringup
- pane은 1개
- 현재 활성 window
debug window는 다음과 같이 출력될 수 있습니다.
3:debug panes=4 active=0
- window 번호는 3
- window 이름은 debug
- pane은 4개
- 현재 활성 window는 아님
session이 없을 때 메시지 출력
else
echo "실행 중인 ${SESSION_NAME} 세션이 없습니다."
fi
tmux has-session 검사 결과 session이 없으면 else 부분이 실행됩니다.
출력 예시는 다음과 같습니다.
실행 중인 turtlebot3 세션이 없습니다.
마지막의 fi는 Bash의 if 조건문이 끝났다는 의미입니다.
Bash 조건문 구조는 다음과 같습니다.
if 조건; then
조건이 참일 때 실행
else
조건이 거짓일 때 실행
fi
16) 기본 명령값 설정
COMMAND="${1:-start}"
이 코드는 스크립트 실행 시 전달된 첫 번째 인자를 COMMAND 변수에 저장합니다.
Bash에서 $1은 첫 번째 명령행 인자를 의미합니다.
예를 들어 다음과 같이 실행하면:
./turtlebot3_tmux.sh stop
$1에는 stop이 들어갑니다.
따라서 다음과 같이 설정됩니다.
COMMAND=stop
다음과 같이 실행하면:
./turtlebot3_tmux.sh status
COMMAND에는 status가 들어갑니다.
COMMAND=status
:-start는 첫 번째 인자가 없을 경우 사용할 기본값입니다.
"${1:-start}"
따라서 아무 인자 없이 실행하면:
./turtlebot3_tmux.sh
자동으로 다음과 같은 의미가 됩니다.
./turtlebot3_tmux.sh start
즉, 기본 동작은 start입니다.
17) tmux 설치 여부 확인
command -v tmux >/dev/null 2>&1 ||
fail "tmux가 설치되어 있지 않습니다: sudo apt install -y tmux"
command -v는 특정 명령이 시스템에 설치되어 있는지 확인하는 Bash 명령입니다.
command -v tmux
tmux가 설치되어 있다면 실행 파일 경로가 출력됩니다.
/usr/bin/tmux
스크립트에서는 이 출력이 필요하지 않으므로 다음과 같이 버립니다.
>/dev/null 2>&1
각 부분의 의미는 다음과 같습니다.
>/dev/null 표준 출력 버림
2>&1 표준 오류를 표준 출력과 같은 곳으로 보냄
결국 정상 출력과 오류 출력을 모두 화면에 표시하지 않습니다.
|| 연산자는 앞 명령이 실패했을 때 뒤 명령을 실행한다는 의미입니다.
앞의 명령 || 실패했을 때 실행할 명령
따라서 tmux가 설치되어 있지 않으면 다음 함수가 실행됩니다.
fail "tmux가 설치되어 있지 않습니다: sudo apt install -y tmux"
출력 예시는 다음과 같습니다.
[ERROR] tmux가 설치되어 있지 않습니다: sudo apt install -y tmux
그 후 스크립트가 종료됩니다.
18) case 문으로 사용자 명령 처리
case "${COMMAND}" in
case 문은 COMMAND 변수의 값에 따라 다른 코드를 실행합니다.
처리 가능한 명령은 다음과 같습니다.
start
attach
stop
restart
status
Bash의 case 문 기본 구조는 다음과 같습니다.
case "${변수}" in
값1)
명령
;;
값2)
명령
;;
esac
각 항목의 마지막에 있는 ;;는 해당 조건 처리가 끝났다는 뜻입니다.
마지막의 esac는 case를 거꾸로 쓴 단어이며, case 문 종료를 의미합니다.
19) start 명령 처리
start)
if ! tmux has-session -t "${SESSION_NAME}" 2>/dev/null; then
create_session
fi
attach_session
;;
사용자가 다음과 같이 실행하면 이 부분이 실행됩니다.
./turtlebot3_tmux.sh start
먼저 기존 session이 있는지 확인합니다.
tmux has-session -t "${SESSION_NAME}"
앞에 있는 !는 조건을 반대로 바꿉니다.
if ! 명령; then
즉, session이 없을 때 조건이 참이 됩니다.
session이 없다면 다음 함수가 실행됩니다.
create_session
이 함수는 bringup, camera, audio, debug window를 새로 생성합니다.
session이 이미 있다면 create_session은 실행하지 않습니다.
이 방식으로 같은 이름의 tmux session이 중복 생성되는 것을 방지합니다.
마지막에는 다음 함수가 실행됩니다.
attach_session
따라서 session이 새로 생성되었든 이미 존재하든 관계없이 최종적으로 해당 session에 접속합니다.
start 명령의 전체 동작은 다음과 같습니다.
session 존재 여부 확인
→ 없으면 새 session 생성
→ 있으면 기존 session 사용
→ session에 접속
20) attach 명령 처리
attach)
tmux has-session -t "${SESSION_NAME}" 2>/dev/null ||
fail "실행 중인 ${SESSION_NAME} 세션이 없습니다."
attach_session
;;
사용자가 다음과 같이 실행하면 이 부분이 실행됩니다.
./turtlebot3_tmux.sh attach
먼저 session이 존재하는지 확인합니다.
tmux has-session -t "${SESSION_NAME}"
session이 존재하지 않으면 || 뒤의 fail 함수가 실행됩니다.
fail "실행 중인 ${SESSION_NAME} 세션이 없습니다."
출력 예시는 다음과 같습니다.
[ERROR] 실행 중인 turtlebot3 세션이 없습니다.
session이 존재하면 다음 함수가 실행됩니다.
attach_session
attach는 기존 session에 접속만 하며 새로운 session을 생성하지 않습니다.
따라서 다음 차이가 있습니다.
start session이 없으면 생성한 후 접속
attach session이 있을 때만 접속