ROS 2 · 로봇 모델링

URDF로
로봇을 그린다

링크와 조인트를 XML로 적어 RViz에 띄우기까지
— 매니퓰레이터 한 대를 처음부터 만들어 본다

URDF link · joint RViz xacro
testbot.urdf 링크 5 · 조인트 4
<robot name="testbot"> ├─ <material> green · orange ├─ <link> base 기저 ├─ <link> link1 … link4 │ └─ visual · collision · inertial └─ <joint> base_joint └─ link1_link2 · link2_link3 · …
URDF는 결국 링크 목록과 조인트 목록이다
ROS 2에서 로봇을 다루려면 먼저 "이 로봇이 어떻게 생겼는가"를 기계가 읽을 수 있게 적어야 한다. 그 표준 형식이 URDF다. 오늘은 매니퓰레이터(로봇 팔) 한 대를 XML로 적고, RViz에 띄워 관절을 직접 움직여 보는 데까지 간다. 전체 40분 기준으로 앞 절반은 태그 문법, 뒤 절반은 실행과 도구에 쓴다. 청중이 ROS 초심자라면 워크스페이스(~/robot_ws) 개념을 여기서 한 번 확인하고 넘어간다.
01

URDF란 무엇인가

로봇의 생김새를 기계가 읽을 수 있는 형식으로 적어두는 파일. 비슷한 형식이 여럿 있는데 무엇이 어디에 쓰이는지, 그리고 URDF가 표현하지 못하는 것은 무엇인지부터 정리한다.

이 장의 목표는 "URDF가 뭐 하는 물건인지"에 대한 공통 인식을 만드는 것. 시간이 부족하면 SDF·SRDF 비교(04)를 한 줄로 넘기고 한계(05)로 바로 간다.
정의Unified Robot Description Format

URDF는 로봇을 XML로 적어둔 명세서다

  • 로봇의 기하학적 모델·관절·센서 정보를 담는 XML 파일
  • 부품은 <link>, 움직임은 <joint>로 기술한다
  • 이 둘을 이어 붙인 것이 곧 로봇 한 대의 정의다
  • 완성된 모델은 RViz·Gazebo에서 바로 확인하고 시뮬레이션할 수 있다
<?xml version="1.0" ?>
<robot name="testbot">

  <link name="base"/>
  <link name="link1"> ... </link>

  <joint name="base_joint" type="fixed">
    <parent link="base"/>
    <child  link="link1"/>
  </joint>

</robot>
한 줄 요약 링크는 무엇이 있는가, 조인트는 어떻게 붙어서 어떻게 움직이는가.
여기서 XML을 무서워할 필요 없다는 걸 짚는다. 태그 종류가 열 개 남짓이고, 그중 절반은 안 써도 RViz에 뜬다. 오른쪽 코드는 앞으로 계속 확장해 나갈 뼈대이므로 한 번 소리내어 읽어준다: "로봇이 있고, 링크가 있고, 링크를 잇는 조인트가 있다."
비교URDF · SDF · SRDF

이름이 비슷한 세 형식, 쓰는 도구가 다르다

형식무엇을 기술하나주로 쓰는 도구
URDF링크·조인트 — 로봇의 형상과 움직임RViz
SDF물리와 환경까지 포함한 시뮬레이션 정보Gazebo
SRDF그룹·경로 계획·충돌 검사MoveIt
출발점은 언제나 URDF gz sdf -p model.urdf > model.sdf 로 SDF를, Setup Assistant로 SRDF를 만들어 낸다.
표를 한 줄씩 읽지 말고 기준만 말한다: "보여주기만 하면 URDF, 물리까지 돌리면 SDF, 팔을 계획적으로 움직이면 SRDF." 실무에서 SDF·SRDF를 직접 손으로 쓰는 일은 거의 없고 대부분 URDF에서 변환한다는 점이 핵심.
한계모든 로봇을 담지는 못한다

URDF는 트리만 그릴 수 있다

트리 구조만 표현된다

링크 하나에 부모는 하나뿐이다. 고리처럼 닫히는 폐루프 구조 — 델타 로봇 같은 병렬 기구 — 는 URDF로 표현할 수 없다.

check_urdf가 트리를 출력하는 것도 이 때문

링크는 강체로 가정한다

조인트로 이어진 링크가 휘거나 늘어나지 않는다고 본다. 케이블·스프링 같은 유연한 요소는 지원하지 않는다.

필요하면 Gazebo 플러그인 쪽에서 해결

알고 시작하자 최대한 일반적으로 설계된 명세지만 모든 로봇을 설명하지는 못한다. 대부분의 팔·모바일 로봇은 트리로 충분하다.
한계를 먼저 말하는 이유는, 나중에 "왜 이게 안 되지"에서 시간을 낭비하지 않기 위해서다. 청중 중 병렬 기구를 다루는 사람이 있으면 SDF나 MuJoCo 쪽을 언급해 준다. 여기서 오래 끌지 말 것 — 30초면 충분하다.
02

무엇을 모델링하나 — 매니퓰레이터

사람의 팔과 비슷하게 움직이는 로봇을 매니퓰레이터라 부른다. XML을 쓰기 전에 이 로봇이 어떤 부분으로 나뉘는지, 그중 URDF가 신경 쓰는 것은 무엇인지 본다.

용어 정리 장이다. 로봇 배경이 없는 청중이 많으면 여기에 시간을 조금 더 쓰고, 로봇 하는 사람이 많으면 두 장 다 빠르게 넘긴다.
구조manipulator

매니퓰레이터는 네 부분으로 나뉜다

기저 (base)로봇이 고정되는 곳 — 바닥이거나 모바일 로봇
링크 (link)기저·조인트·말단을 잇는 강체 프레임
조인트 (joint)회전·병진 — 움직임을 만드는 부분
말단 장치 (end effector)사람의 손 — 집게·흡착판·분사 노즐

화살표는 부모 → 자식 방향이다. 기저에 가까운 쪽이 항상 부모이고, 링크와 조인트가 번갈아 반복되며 팔이 길어진다.

가운데 두 칸이 hero로 강조된 이유를 말한다 — URDF가 실제로 기술하는 것은 링크와 조인트뿐이고, 기저와 말단 장치도 결국 링크로 표현된다. 즉 네 부분은 개념적 구분이고 파일에서는 두 종류의 태그로 수렴한다.
핵심link vs joint

결국 링크와 조인트가 전부다

<link> — 무엇이 있는가

이름 · 외형 · 무게(kg) · 관성 모멘트(kg·m²)를 적는다.

외형은 원통·원뿔·직육면체 같은 단순 도형이 기본. 복잡한 구조는 stl·dae 메시 파일로.

<joint> — 어떻게 붙어 움직이는가

이름 · 종류 · 운동의 기준 축 · 최소/최대 값 · 힘과 속도를 적는다.

언제나 앞뒤 링크의 관계를 기술한다. 조인트 홀로는 의미가 없다.

나머지는 곁가지 이 두 태그만 정확하면 RViz에 로봇이 뜬다. 나머지 태그는 전부 이 둘의 세부 사항이다.
이 슬라이드가 발표 전체의 지도 역할을 한다. 뒤에 나오는 visual·collision·inertial·origin·material은 전부 link의 하위, axis·limit·parent·child는 전부 joint의 하위라는 걸 미리 못 박아 두면 청중이 길을 잃지 않는다.
03

패키지를 만들고 뼈대를 세운다

ROS에서 로봇 모델은 로봇명_description 패키지에 담는 것이 관례다. testbot_description을 만들고, 그 안에 URDF의 가장 바깥 뼈대를 세운다.

여기부터 손이 움직이는 구간이다. 라이브 데모를 할 거면 이 장에서 터미널로 전환한다. 데모가 없다면 명령어를 그대로 읽어주기만 해도 충분하다.
순서ros2 pkg create

관례는 로봇명_description

1
cd ~/robot_ws/src
워크스페이스의 소스 폴더로 이동한다
2
ros2 pkg create testbot_description --build-type ament_cmake --dependencies urdf
모델링 정보만 담을 패키지를 만든다
3
mkdir urdf && vim urdf/testbot.urdf
URDF 파일이 놓일 자리를 만든다
4
index.ros.org 에서 "description" 검색
잘 만들어진 남의 URDF를 예제로 열어 본다
왜 이 이름인가 커뮤니티 관례라서, 남의 로봇 패키지를 찾을 때도 같은 규칙이 그대로 통한다.
4번을 특히 강조한다. URDF는 처음부터 쓰기보다 비슷한 로봇의 description 패키지를 열어 고쳐 쓰는 편이 훨씬 빠르다. 시간이 있으면 실제로 ROS Index를 열어 turtlebot이나 UR 로봇의 URDF를 한 번 보여준다.
뼈대<robot>

<robot>은 링크 목록과 조인트 목록이다

<?xml version="1.0" ?>
<robot name="testbot">

  <!-- 부품: 무엇이 있는가 -->
  <link name="base"/>
  <link name="link1"> ... </link>
  <link name="link2"> ... </link>

  <!-- 연결: 어떻게 붙어 움직이는가 -->
  <joint name="base_joint"  type="fixed">    ... </joint>
  <joint name="link1_link2" type="revolute"> ... </joint>

</robot>
루트는 하나 <robot>이 문서의 root 요소로 제일 먼저 선언된다. 링크끼리는 조인트를 통해서만 연결된다.
순서에 대한 질문이 자주 나온다 — 링크와 조인트를 섞어 써도 파싱은 되지만, 링크를 모두 적고 조인트를 모아 적는 편이 읽기 좋다. 실제 testbot.urdf도 material → link 4개 → joint 4개 순서다.
04

<link> — 로봇의 부품

링크 하나를 제대로 적는 법. 보이는 모양, 부딪히는 모양, 물리 계산에 쓰는 질량과 관성 — 이 세 가지가 각각 무엇이고 왜 나뉘어 있는지 본다.

발표에서 가장 밀도가 높은 장이다. 5장이 이어지므로 청중이 지치지 않게 중간중간 "이건 RViz에 띄우는 데는 없어도 된다"는 식으로 우선순위를 알려준다.
구성visual · collision · inertial

링크 하나가 세 얼굴을 가진다

  • <visual> — 화면에 보이는 모양
  • <collision> — 충돌 판정에 쓰는 간섭 범위
  • <inertial> — 질량과 관성 텐서, 물리 계산용
  • 셋 다 각자의 <origin><geometry>를 따로 가진다
<link name="link1">
  <visual>
    <origin xyz="0 0 0.25" rpy="0 0 0"/>
    <geometry><box size="0.1 0.1 0.5"/></geometry>
    <material name="green"/>
  </visual>
  <collision> ... </collision>
  <inertial> ... </inertial>
</link>
셋은 독립이다 보이는 모양과 부딪히는 모양이 같을 필요가 없다. RViz만 띄울 거라면 <visual>만 있어도 된다.
"RViz만 띄울 거면 visual만"이라는 말이 초심자에게는 큰 안도가 된다. inertial은 Gazebo로 물리를 돌릴 때 필요하고, 그때 값이 이상하면 로봇이 폭발하듯 튀어 나가는 현상이 생긴다는 일화를 덧붙이면 기억에 남는다.
개념왜 나눠 놓았나

보이는 몸과 부딪히는 몸은 따로다

<visual> — 표시 범위

box·cylinder·sphere가 기본. 표현하기 어려운 형상은 STL·DAE 같은 CAD 파일을 불러온다.

예쁘게 보이는 것이 목적

<collision> — 간섭 범위

태그 구성은 같지만 목적이 다르다. 표시 범위보다 크게 잡아 안전 여유를 두기도 한다.

빠르고 안전하게 계산하는 것이 목적

실무 팁 정교한 메시는 <visual>에, 단순한 박스·원통은 <collision>에. 보기도 좋고 충돌 계산도 가벼워진다.
이 팁은 실제로 로봇 시뮬레이션 성능을 크게 좌우한다. 삼각형 수만 개짜리 메시로 충돌 판정을 돌리면 시뮬레이션이 실시간을 못 따라간다. 청중 중 Gazebo 경험자가 있으면 여기서 공감이 나온다.
좌표<origin>

위치는 xyz(미터), 방향은 rpy(라디안)

  • xyz — 3차원 공간에서의 위치, 단위는 미터(m)
  • rpy — roll·pitch·yaw 오일러 각, 단위는 라디안(rad)
  • roll은 x축, pitch는 y축, yaw는 z축 둘레의 회전
  • 언제나 부모 좌표계 기준의 상대 좌표
<!-- 길이 0.5 박스를 z로 절반만큼 올린다 -->
<origin xyz="0.0 0.0 0.25" rpy="0.0 0.0 0.0"/>
<geometry>
  <box size="0.1 0.1 0.5"/>
</geometry>

<!-- yaw 90도 = pi/2 = 1.5708 rad -->
<origin xyz="0 0 0" rpy="0 0 1.5708"/>
자주 하는 실수 rpy에 도(degree)를 그대로 넣는 것. 90을 넣으면 90 라디안, 약 14바퀴 반이 돌아간다.
박스를 0.25만큼 올리는 이유를 설명한다 — geometry의 원점은 도형의 중심이라, 길이 0.5짜리 박스를 바닥에서 시작하게 하려면 절반인 0.25를 올려야 한다. 이걸 모르면 링크가 절반씩 땅에 묻힌 채로 렌더링된다. RViz에서 실제로 보여줄 수 있으면 가장 좋다.
물리<inertial>

관성 텐서는 대칭이라 6개만 적는다

3×3 관성 행렬xyz
xixxixyixz
yixyiyyiyz
zixziyzizz
대칭이라 위 삼각만 ixy = iyx이므로 ixx · ixy · ixz · iyy · iyz · izz 여섯 개면 충분하다. <mass>는 kg, <inertia>는 kg·m².
값을 어디서 구하냐는 질문이 반드시 나온다. 답: 위키백과의 "List of 3D inertia tensors"에 도형별 공식이 정리돼 있고, CAD 툴에서 계산해 주기도 한다. 예제처럼 전부 1.0으로 두고 RViz만 띄우는 것도 초반에는 흔한 선택이다.
외형<material>

색은 0~1 사이의 rgba로 준다

<!-- 로봇 상단에 한 번 정의하고 -->
<material name="green">
  <color rgba="0 0.6 0 1" />
</material>
<material name="orange">
  <color rgba="1.0 0.4 0.0 1.0"/>
</material>

<!-- 링크에서는 이름으로 참조한다 -->
<visual>
  ...
  <material name="green"/>
</visual>
마지막 숫자는 투명도 빨강·초록·파랑·알파를 각각 0.0~1.0으로. 알파가 1.0이면 불투명이다. <texture>로 png 이미지를 입힐 수도 있다.
0~255가 아니라 0~1이라는 점을 짚는다. 웹 색상에 익숙한 사람이 255를 넣어 하얗게 뜨는 일이 흔하다. 색을 링크마다 다르게 주면 RViz에서 어느 링크가 어떻게 움직이는지 눈으로 구분하기 쉬워진다 — 예제가 green/orange를 번갈아 쓰는 이유다.
05

<joint> — 움직임을 만드는 것

조인트는 두 링크를 잇고, 그 연결이 어떻게 움직일 수 있는지를 정한다. 여섯 가지 종류와, 실제 동작을 결정하는 축·제한 값을 본다.

여기가 URDF의 절정이다. 앞의 link 장이 정적인 이야기였다면 이 장은 동적인 이야기. RViz 데모가 있다면 이 장 끝에 배치하는 것이 극적이다.
종류type="..."

조인트는 여섯 종류가 있다

type움직임비유
fixed움직이지 않는다기저와 첫 링크의 결합
revolute각도 제한이 있는 회전선풍기의 좌우 회전
continuous제한 없는 연속 회전자동차 바퀴
prismatic한 축을 따라 직선 이동미끄러지는 서랍
floating6자유도 이동 + 회전자유 부유체
planar한 평면 위 이동 + 회전평면 위를 도는 물체
실무에서 쓰는 건 사실상 fixed · revolute · continuous · prismatic 넷이다. floating과 planar는 표준에는 있지만 실제 로봇 URDF에서 보기 드물다고 말해 주면 청중이 외울 부담을 덜어낸다. revolute와 continuous의 차이(제한의 유무)는 퀴즈로도 나온다.
연결parent · child

조인트가 부모와 자식을 이어 트리를 만든다

  • <parent>·<child>기저에 가까운 쪽이 부모
  • 트리는 여기서 만들어진다: base → link1 → link2 → …
  • <origin> — 부모 링크 기준 조인트의 상대 위치
  • type이 이 연결의 움직임 방식을 정한다
<joint name="link1_link2" type="revolute">
  <origin xyz="0.0 0.0 0.5" rpy="0.0 0.0 0.0"/>
  <parent link="link1"/>
  <child  link="link2"/>
  <axis xyz="0 0 1"/>
  <limit lower="-2.617" upper="2.617"
         effort="30.0" velocity="1.571"/>
</joint>
이름 규칙 link1_link2처럼 잇는 두 링크를 이름에 담아 두면 트리를 눈으로 읽을 수 있다.
origin의 z가 0.5인 이유를 짚는다 — link1의 길이가 0.5이므로 그 끝에 다음 조인트가 온다. 즉 조인트의 origin은 "부모 링크의 어디에 이 관절을 달 것인가"를 말한다. 이 감각이 잡히면 URDF에서 좌표를 헤매는 시간이 크게 줄어든다.
동작axis · limit

<axis><limit>이 실제 동작을 정한다

  • axis xyz="0 0 1"z축 회전. 선풍기 목처럼 좌우로 돈다
  • axis xyz="0 1 0"y축 회전. 팔꿈치처럼 위아래로 꺾인다
  • lower·upper — 회전 범위, 단위는 라디안
  • effort — 조인트에 주는 힘(N), velocity — 속도(rad/s)
<!-- 목처럼 도는 관절 -->
<axis xyz="0.0 0.0 1.0"/>

<!-- 팔꿈치처럼 꺾이는 관절 -->
<axis xyz="0.0 1.0 0.0"/>

<limit lower="-2.617" upper="2.617"
       effort="30.0" velocity="1.571"/>
2.617 rad ≈ 150° 값을 바꿔가며 RViz에서 직접 돌려보는 것이 URDF를 이해하는 가장 빠른 길이다.
축은 로봇의 기준 좌표계를 따른다는 점이 중요하다. 예제에서 link1_link2는 z축, link2_link3와 link3_link4는 y축을 쓰는데, 그래서 첫 관절은 팔 전체를 좌우로 돌리고 나머지 둘은 팔을 접었다 펴는 동작이 된다. RViz의 joint_state_publisher GUI 슬라이더로 하나씩 움직여 보면 즉시 이해된다.
06

RViz에 띄우고 관절을 움직인다

파일만 봐서는 알 수 없다. 런치 파일로 노드 세 개를 띄우면 화면에 로봇이 나타나고 슬라이더로 관절을 직접 돌려볼 수 있다.

여기서부터 결과물이 나온다. 데모 영상이나 스크린샷을 준비했다면 이 장에서 쓴다. 노드 구조(23)를 이해시키는 데 시간을 가장 많이 쓰고, 런치 파일 코드(24)는 빠르게 넘긴다.
구조node & topic

런치 파일은 노드 세 개를 띄운다

joint_state_publisher_gui슬라이더로 관절 값을 만들어 낸다
robot_state_publisherURDF + 관절 값 → 좌표 변환(TF) 계산
rviz2받은 TF로 로봇을 3D로 그린다

화살표는 토픽이 흐르는 방향이다. URDF 자체는 robot_description 파라미터로 robot_state_publisher에 미리 넣어 둔다.

이 그림 하나가 5장 분량을 대신한다. 핵심은 "관절 값을 만드는 쪽"과 "그 값으로 좌표를 계산하는 쪽"이 분리돼 있다는 것. 실제 로봇에서는 왼쪽의 GUI 자리에 엔코더 값을 읽는 드라이버 노드가 들어간다는 점을 꼭 덧붙인다 — 그래야 이 구조를 왜 이렇게 나눴는지 납득이 된다.
실전testbot.launch.py

런치 파일이 하는 일은 사실 한 줄뿐이다

def generate_launch_description():
    urdf_file = os.path.join(
        get_package_share_directory('testbot_description'),
        'urdf', 'testbot.urdf')
    with open(urdf_file, 'r') as infp:
        robot_description_file = infp.read()

    robot_state_publisher = Node(
        package='robot_state_publisher', executable='robot_state_publisher',
        parameters=[{'robot_description': robot_description_file}])
    joint_state_publisher_gui = Node(
        package='joint_state_publisher_gui', executable='joint_state_publisher_gui')
    rviz2 = Node(
        package='rviz2', executable='rviz2',
        arguments=['-d', rviz_display_config_file])

    return LaunchDescription([robot_state_publisher, joint_state_publisher_gui, rviz2])
핵심은 이것 URDF 파일을 읽어 robot_description 파라미터로 넘기는 것. 나머지는 노드를 띄우는 상용구다.
지면에 맞춰 축약한 코드다(원본은 output='screen', use_sim_time 등이 더 붙는다). 여기서 오래 머물지 말고 한 줄만 강조하고 넘어간다. rviz_display_config_file은 RViz의 화면 배치를 저장해 둔 .rviz 파일 경로로, 매번 디스플레이를 다시 추가하지 않으려고 쓴다.
역할누가 무엇을 주고받나

두 publisher가 각자 다른 일을 한다

노드구독발행
joint_state_publisher/joint_states
robot_state_publisher/joint_states/tf · /tf_static · /robot_description
rviz2/tf · /robot_description
흐름 한 줄 관절 값이 들어오면 URDF와 합쳐 좌표 변환을 계산해 내보낸다. RViz는 그 결과를 그릴 뿐이다.
/joint_states는 sensor_msgs/msg/JointState, /tf는 tf2_msgs/msg/TFMessage 타입이다. 메시지 타입까지 외울 필요는 없지만, ros2 topic echo로 직접 들여다보면 URDF가 어떻게 숫자로 변하는지 눈으로 볼 수 있다고 안내한다. /tf_static은 fixed 조인트처럼 변하지 않는 변환만 담는다.
순서install → build → launch

설치하고, 빌드하고, 띄운다

1
sudo apt install ros-humble-joint-state-publisher-gui ros-humble-robot-state-publisher
런치 파일이 띄우는 노드들의 패키지를 먼저 깐다
2
cd ~/robot_ws && colcon build --symlink-install
워크스페이스를 빌드한다
3
ros2 launch testbot_description testbot.launch.py
RViz와 관절 슬라이더 GUI가 함께 뜬다
alias를 걸어두자 alias cba='colcon build --symlink-install' 처럼 줄여두면 이 반복이 훨씬 가벼워진다.
--symlink-install이 중요하다. 이 옵션이 있으면 urdf나 launch 파일을 고쳤을 때 다시 빌드하지 않아도 반영된다. URDF는 값을 바꿔가며 확인하는 일이 잦아서 이 옵션 하나로 반복 주기가 크게 짧아진다. 빌드 후 source install/local_setup.bash를 잊지 말라는 것도 함께.
지름길urdf_tutorial

런치 파일 없이도 바로 볼 수 있다

# urdf_tutorial 패키지를 설치하고
$ sudo apt install ros-humble-urdf-tutorial

# 모델 경로만 넘기면 RViz가 뜬다
$ ros2 launch urdf_tutorial display.launch.py \
    model:=/home/parallels/robot_ws/src/testbot_description/urdf/testbot.urdf
언제 쓰나 태그 값을 바꿔가며 확인하는 초반 단계. 런치 파일과 .rviz 설정을 만들기 전까지 이걸로 버틴다.
런치 파일로 띄운 것과 똑같은 화면이 나온다. 결국 urdf_tutorial 패키지 안에 앞에서 본 것과 같은 런치 파일이 들어 있을 뿐이다. 처음 URDF를 배울 때는 이걸로 시작하고, 로봇이 어느 정도 완성된 뒤에 자기 패키지의 런치 파일을 만드는 순서를 권한다.
07

검증하는 법과 도구들

RViz를 띄우기 전에 문법과 트리를 확인하는 명령어들, 그리고 URDF가 길어졌을 때 반복을 줄여주는 xacro를 본다.

마무리 장. 시간이 없으면 check_urdf(29) 하나만 보여주고 xacro(31)로 건너뛴다. 도구 표(30)는 자료로 남기고 넘겨도 된다.
검증$ check_urdf testbot.urdf

check_urdf — 문법과 링크 트리를 한 번에

robot name is: testbot ---------- Successfully Parsed XML --------------- root Link: base has 1 child(ren) child(1): link1 child(1): link2 child(1): link3 child(1): link4
여기서 걸리는 것 오타 난 링크 이름, 부모가 없는 링크, 트리가 끊긴 곳. RViz를 띄우기 전에 먼저 돌린다.
출력이 계단처럼 들여쓰기되는 게 곧 트리 구조다. 의도한 것과 다르게 나오면 조인트의 parent/child를 잘못 적은 것. RViz는 URDF가 깨져 있어도 조용히 아무것도 안 그리는 경우가 있어서, 문제를 빨리 찾으려면 이 명령어를 먼저 쓰는 습관이 좋다.
도구무엇을 보여주나

막힐 때 꺼내 쓰는 도구들

도구무엇을 보여주나
check_urdf문법 오류와 링크 트리
urdf_to_graphiz링크·조인트 관계와 상대 좌표 변환을 PDF 다이어그램으로
ros2 run tf2_tools view_frames실행 중인 TF 트리를 PDF로
rqt노드와 토픽이 실제로 어떻게 연결됐는지
URDF for VSCode.urdf·.xacro 문법 강조와 스니펫
rviz2최종 렌더링 — 결국 여기서 확인한다
JetBrains PyCharm에는 쓸 만한 URDF 플러그인이 없어서 VSCode + URDF 확장이 사실상 표준이다. VSCode의 "ROS: Preview URDF"는 ROS 1에서는 되는데 ROS 2에서는 빈 화면이 나오는 경우가 있으니 기대하지 말 것. urdf_to_graphiz는 .gv와 .pdf를 함께 만들어 준다.
확장XML Macro

xacro는 반복되는 XML을 매크로로 줄인다

  • XML Macro의 줄임말. URDF를 만들어 주는 매크로 언어
  • <xacro:property> — 값을 이름으로 관리
  • pi·radians(x)·sqrt(x) 같은 수식을 쓸 수 있다
  • <xacro:macro> — 인자를 받는 링크·조인트 템플릿
  • <xacro:if> — 조건에 따라 다른 블록을 넣는다
<xacro:property name="the_radius" value="2.1" />
<geometry type="cylinder" radius="${the_radius}" />

<!-- 라디안을 직접 계산하지 않아도 된다 -->
<xacro:property name="upper" value="${radians(150)}"/>

<xacro:macro name="link_box" params="suffix">
  <link name="link_${suffix}"> ... </link>
</xacro:macro>

<xacro:link_box suffix="1" />
언제 넘어가나 링크 네댓 개까지는 URDF로 충분하다. 같은 블록을 복사하기 시작하면 그때가 xacro로 옮길 때다.
오늘 만든 testbot도 link1~link4가 거의 같은 내용을 네 번 반복한다 — 딱 xacro가 필요한 지점이다. radians(150)을 쓸 수 있다는 것만으로도 옮길 이유가 된다고 말하면 공감이 크다. 실제 로봇 패키지는 대부분 .urdf가 아니라 .urdf.xacro를 쓴다.
정리오늘 가져갈 것

두 가지만 기억하면 된다

모델은 링크와 조인트다

<link>로 부품을, <joint>로 연결과 움직임을 적는다.

visual·collision·inertial·origin·axis·limit은 전부 이 둘의 세부 사항

확인은 RViz에서 한다

값을 바꾸고 다시 띄워보는 반복이 URDF를 이해하는 가장 빠른 길이다.

그 전에 check_urdf로 트리부터 확인

다음 단계 모델링한 로봇 제어해 보기 · Gazebo로 물리 시뮬레이션 · Navigation.
여기서 발표를 닫아도 되고, 퀴즈로 이어가도 된다. 다음 단계 세 가지는 이 글의 "다음 스터디 주제"에서 그대로 가져온 것이니, 후속 발표를 예고하는 자리로 쓰면 좋다. 질문 시간은 이 슬라이드에 머문 채로 받는다.
확인 ①클릭하면 답이 열립니다

스스로 답해보기 — 형식과 링크

URDF는 RViz(모델링), SDF는 Gazebo(시뮬레이션), SRDF는 MoveIt(경로 계획·충돌 검사). 셋 다 XML이고 출발점은 URDF다. → 슬라이드 04
고리처럼 닫히는 폐루프(병렬) 구조. URDF는 링크마다 부모가 하나인 트리만 표현한다. 휘거나 늘어나는 유연한 요소도 담지 못한다. → 슬라이드 05
보여줄 모양과 충돌 판정 범위가 달라도 되기 때문이다. collision을 크게 잡아 안전 여유를 두거나, 무거운 메시 대신 단순 도형을 써서 계산을 가볍게 한다. → 슬라이드 13 · 14
z축(yaw)으로 1.5708 라디안, 약 90도 회전. rpy의 단위는 라디안이고 xyz는 미터다. 도 단위를 그대로 넣으면 엉뚱한 곳을 본다. → 슬라이드 15
3×3 관성 행렬이 대칭이라 ixy=iyx, ixz=izx, iyz=izy이기 때문이다. 위 삼각의 ixx·ixy·ixz·iyy·iyz·izz만 있으면 나머지가 결정된다. → 슬라이드 16
한 문제를 읽고 5~10초 기다린 뒤 답을 연다. Q3과 Q5가 특히 잘 갈린다. 시간이 없으면 Q2와 Q4만 다뤄도 앞 장의 핵심은 짚힌다.
확인 ②클릭하면 답이 열립니다

스스로 답해보기 — 조인트와 실행

둘 다 회전이지만 revolute는 lower·upper 각도 제한이 있고, continuous는 제한 없이 계속 돈다. 자동차 바퀴는 continuous. → 슬라이드 19
기저에 가까운 쪽이 부모다. 이렇게 base → link1 → link2 → … 순으로 트리가 만들어지고, check_urdf가 그 트리를 그대로 출력해 준다. → 슬라이드 20
약 ±150도. limit의 각도 단위는 라디안이고, 함께 쓰는 effort는 뉴턴(N), velocity는 rad/s다. → 슬라이드 21
/joint_states를 구독하고, URDF와 합쳐 좌표 변환을 계산해 /tf·/tf_static으로 발행한다. 관절 값 자체를 만드는 건 joint_state_publisher다. → 슬라이드 23 · 25
check_urdf로 문법과 트리를 보고, ros2 launch urdf_tutorial display.launch.py model:=... 로 RViz에 바로 띄운다. → 슬라이드 27 · 29
마지막 슬라이드다. Q5는 실제로 가장 자주 쓰게 될 습관이라 꼭 짚고 마무리한다. 답을 다 연 상태로 두고 질문을 받으면 화면이 요약 역할을 한다.
발표자 노트
← 글 보기 URDF 로봇 모델링
01 / 34

단축키

→ · Space · PgDn
다음 슬라이드
← · PgUp
이전 슬라이드
Home · End
처음 · 마지막
O
전체 슬라이드 개요
N
발표자 노트
T
밝은 테마 / 어두운 테마
F
전체 화면
Esc
닫기