AEGLOSLAB ROS 2 Jazzy 대전

제5부 좌표·모델

5.4 xacro — 매크로와 조립

5.4.1 이 장에서 배우는 것 / 선수 지식

URDF를 직접 쓰다 보면 곧 한계에 부딪힌다. 바퀴 네 개를 다는 데 같은 블록을 네 번 복사하고, 치수를 바꾸면 여러 곳을 고쳐야 하고, 관성 텐서를 직접 계산해 넣고, 시뮬용 설정과 실기용 설정을 두 파일로 유지한다. URDF는 기계가 읽기 좋은 형식이지 사람이 쓰기 좋은 형식이 아니다.

xacro(XML Macros)가 그 층을 추가한다. 속성·수식·매크로·조건부 블록을 제공하고, 최종적으로 평범한 URDF를 출력한다.

배우는 것:

  • 속성과 수식 — 치수를 한 곳에서 관리하기.
  • 매크로 — 반복 구조를 함수로.
  • 파일 분할과 include — 모듈 조립.
  • 조건부 블록 — 시뮬·실기·변형 모델 분기.
  • 인자(arg) — 명령줄에서 모델 변형.
  • 처리 방법 — CLI·launch·CMake 각각.
  • 디버깅 — 전개 결과를 보는 법.

선수 지식: 5.3(URDF 구조), 4.1.4(설치 규칙), 5.3.5(관성 계산).

정직: xacro 문법은 오랫동안 안정적이다. Jazzy의 xacro로 지시자 목록·조건 평가·인자 형식을 직접 실행해 확인했고(5.4.12), 그 결과 이 장의 함정 서술 하나가 실제와 반대였음을 발견해 정정했다. 나머지 예시는 통용되는 형태다(목차 3).

5.4.2 속성과 수식

<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="my_robot">

  <!-- 속성 정의 -->
  <xacro:property name="base_length" value="0.4"/>
  <xacro:property name="base_width"  value="0.3"/>
  <xacro:property name="base_height" value="0.1"/>
  <xacro:property name="base_mass"   value="5.0"/>

  <xacro:property name="wheel_radius" value="0.06"/>
  <xacro:property name="wheel_width"  value="0.03"/>
  <xacro:property name="wheel_mass"   value="0.5"/>

  <link name="base_link">
    <visual>
      <geometry>
        <box size="${base_length} ${base_width} ${base_height}"/>
      </geometry>
    </visual>
    <inertial>
      <mass value="${base_mass}"/>
      <!-- 직육면체 관성 공식 (5.3.5) -->
      <inertia
        ixx="${base_mass/12.0 * (base_width*base_width + base_height*base_height)}"
        iyy="${base_mass/12.0 * (base_length*base_length + base_height*base_height)}"
        izz="${base_mass/12.0 * (base_length*base_length + base_width*base_width)}"
        ixy="0" ixz="0" iyz="0"/>
    </inertial>
  </link>

</robot>

${...} 안에서 파이썬 수식이 평가된다.

기능예
사칙연산${a + b * 2}
상수${pi}, ${pi/2}
함수${sin(pi/4)}, ${sqrt(2)}, ${radians(90)}
속성 참조${wheel_radius}
조건식${'a' if cond else 'b'}

견해: 관성을 수식으로 계산하는 것이 xacro의 가장 큰 실무 이득 중 하나다. 5.3.5에서 본 "관성을 부정확하게 넣는 실수"가 구조적으로 방지된다 — 치수와 질량만 바꾸면 관성이 따라온다. 관성 매크로를 공용 파일로 만들어 두면 팀 전체가 재사용한다.

속성을 파일로 분리:

<!-- properties.xacro -->
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
  <xacro:property name="base_length" value="0.4"/>
  <xacro:property name="wheel_radius" value="0.06"/>
</robot>
<!-- robot.urdf.xacro -->
<xacro:include filename="$(find my_robot_description)/urdf/properties.xacro"/>

속성 딕셔너리(구조화):

<xacro:property name="wheel" value="${dict(radius=0.06, width=0.03, mass=0.5)}"/>
<!-- 사용 -->
<cylinder radius="${wheel['radius']}" length="${wheel['width']}"/>

(확인 필요: 딕셔너리 문법 지원 범위는 버전 의존)

5.4.3 매크로

반복 구조를 함수로 만든다.

<!-- 관성 매크로 (공용) -->
<xacro:macro name="cylinder_inertia" params="m r h">
  <inertial>
    <mass value="${m}"/>
    <inertia ixx="${m*(3*r*r + h*h)/12}"
             iyy="${m*(3*r*r + h*h)/12}"
             izz="${m*r*r/2}"
             ixy="0" ixz="0" iyz="0"/>
  </inertial>
</xacro:macro>

<xacro:macro name="box_inertia" params="m x y z">
  <inertial>
    <mass value="${m}"/>
    <inertia ixx="${m*(y*y+z*z)/12}"
             iyy="${m*(x*x+z*z)/12}"
             izz="${m*(x*x+y*y)/12}"
             ixy="0" ixz="0" iyz="0"/>
  </inertial>
</xacro:macro>

바퀴 매크로:

<xacro:macro name="wheel" params="prefix reflect_y reflect_x">
  <link name="${prefix}_wheel">
    <visual>
      <origin rpy="${pi/2} 0 0"/>
      <geometry>
        <cylinder radius="${wheel_radius}" length="${wheel_width}"/>
      </geometry>
      <material name="black"/>
    </visual>
    <collision>
      <origin rpy="${pi/2} 0 0"/>
      <geometry>
        <cylinder radius="${wheel_radius}" length="${wheel_width}"/>
      </geometry>
    </collision>
    <xacro:cylinder_inertia m="${wheel_mass}" r="${wheel_radius}" h="${wheel_width}"/>
  </link>

  <joint name="${prefix}_wheel_joint" type="continuous">
    <origin xyz="${reflect_x * base_length/2 * 0.7}
                 ${reflect_y * (base_width/2 + wheel_width/2)}
                 0"/>
    <parent link="base_link"/>
    <child link="${prefix}_wheel"/>
    <axis xyz="0 1 0"/>
  </joint>
</xacro:macro>

<!-- 호출 — 네 줄로 바퀴 넷 -->
<xacro:wheel prefix="front_left"  reflect_x="1"  reflect_y="1"/>
<xacro:wheel prefix="front_right" reflect_x="1"  reflect_y="-1"/>
<xacro:wheel prefix="rear_left"   reflect_x="-1" reflect_y="1"/>
<xacro:wheel prefix="rear_right"  reflect_x="-1" reflect_y="-1"/>

매크로 파라미터의 종류:

형식의미
params="a b c"필수 인자
params="a b:=default"기본값 있는 인자
params="*origin"XML 블록 인자
params="**props"딕셔너리 인자 — 실행 확인(Jazzy)

블록 인자가 유용하다:

<xacro:macro name="sensor_mount" params="name parent *origin">
  <link name="${name}_link">
    <visual><geometry><box size="0.05 0.05 0.02"/></geometry></visual>
  </link>
  <joint name="${name}_joint" type="fixed">
    <xacro:insert_block name="origin"/>     <!-- 호출부의 블록 삽입 -->
    <parent link="${parent}"/>
    <child link="${name}_link"/>
  </joint>
</xacro:macro>

<!-- 호출 시 origin 블록을 넘긴다 -->
<xacro:sensor_mount name="laser" parent="base_link">
  <origin xyz="0.15 0 0.12" rpy="0 0 0"/>
</xacro:sensor_mount>

함정: 매크로 안의 이름에 ${prefix}를 빠뜨리면 같은 이름의 링크가 여러 개 생긴다. URDF는 이를 오류로 검출할 수도, 조용히 덮어쓸 수도 있다(확인 필요). 매크로를 만들 때 모든 이름에 접두어를 붙였는지 확인하는 것이 규칙이다.

5.4.4 파일 분할과 조립

로봇이 커지면 파일을 나눈다.

파일내용
properties.xacro치수·질량 상수
inertia_macros.xacro관성 계산 매크로(공용)
materials.xacro색상 정의
base.xacro본체
wheels.xacro구동부
sensors.xacro센서 장착
arm.xacro매니퓰레이터
gazebo.xacro시뮬 전용 확장(5.3.7)
ros2_control.xacro제어 인터페이스(6.1)
robot.urdf.xacro최상위 — 전부 include
<!-- robot.urdf.xacro -->
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="my_robot">

  <xacro:arg name="use_sim" default="false"/>
  <xacro:arg name="prefix"  default=""/>

  <xacro:include filename="$(find my_robot_description)/urdf/properties.xacro"/>
  <xacro:include filename="$(find my_robot_description)/urdf/inertia_macros.xacro"/>
  <xacro:include filename="$(find my_robot_description)/urdf/materials.xacro"/>
  <xacro:include filename="$(find my_robot_description)/urdf/base.xacro"/>
  <xacro:include filename="$(find my_robot_description)/urdf/wheels.xacro"/>
  <xacro:include filename="$(find my_robot_description)/urdf/sensors.xacro"/>

  <xacro:base prefix="$(arg prefix)"/>
  <xacro:wheel prefix="$(arg prefix)front_left"  reflect_x="1"  reflect_y="1"/>
  <!-- ... -->

  <xacro:if value="$(arg use_sim)">
    <xacro:include filename="$(find my_robot_description)/urdf/gazebo.xacro"/>
  </xacro:if>

</robot>

견해: $(find pkg) 경로를 쓰라. 상대 경로는 실행 위치에 따라 깨지고, 설치본에서 특히 그렇다(4.1.4). $(find ...)는 ament 인덱스로 해석되므로 소스·설치본 어디서든 동작한다.

5.4.5 조건부 블록과 인자

<!-- 인자 선언 -->
<xacro:arg name="use_sim"       default="false"/>
<xacro:arg name="use_gpu_lidar" default="false"/>
<xacro:arg name="robot_variant" default="standard"/>

<!-- arg를 property로 변환해야 수식에서 쓸 수 있다 -->
<xacro:property name="use_sim" value="$(arg use_sim)"/>

<!-- 조건 -->
<xacro:if value="${use_sim}">
  <xacro:include filename="gazebo.xacro"/>
</xacro:if>

<xacro:unless value="${use_sim}">
  <xacro:include filename="real_hardware.xacro"/>
</xacro:unless>

<!-- 값 비교 -->
<xacro:if value="${robot_variant == 'extended'}">
  <xacro:arm prefix="arm_"/>
</xacro:if>

정정(실행 확인, Jazzy): 이 자리에 원래 "$(arg x)는 문자열이라 xacro:if에 직접 쓰면 "false"가 참으로 평가될 수 있다"고 적혀 있었다. 실제로는 그렇지 않다 — 직접 써도 property를 경유한 것과 결과가 같고, "false"는 정확히 거짓으로 평가된다. 자세한 실측 표는 5.4.12에 있다. 불필요한 우회였으므로 걷어낸다.

활용 사례:

인자용도
use_sim시뮬 전용 플러그인·센서 포함 여부
prefix다중 로봇 프레임 접두어(5.2.6)
variant하드웨어 구성 변형(센서 유무·팔 유무)
use_fake_hardwareros2_control 모의 하드웨어(6.2)

다중 로봇 예:

xacro robot.urdf.xacro prefix:=robot1/ > robot1.urdf
xacro robot.urdf.xacro prefix:=robot2/ > robot2.urdf

견해: 하나의 xacro로 여러 구성을 만드는 것이 두 파일을 유지하는 것보다 낫다. 시뮬용·실기용 URDF를 따로 두면 반드시 어긋난다 — 한쪽만 고치는 일이 생긴다. 단일 출처 원칙(5.2.5·5.7)을 지키는 방법이 조건부 블록이다.

5.4.6 반복

<!-- 재귀 매크로로 반복 구현 -->
<xacro:macro name="link_chain" params="n prefix parent">
  <xacro:if value="${n > 0}">
    <link name="${prefix}${n}"/>
    <joint name="${prefix}${n}_joint" type="revolute">
      <parent link="${parent}"/>
      <child link="${prefix}${n}"/>
      <origin xyz="0 0 0.1"/>
      <axis xyz="0 0 1"/>
      <limit lower="-1.57" upper="1.57" effort="10" velocity="1"/>
    </joint>
    <xacro:link_chain n="${n-1}" prefix="${prefix}" parent="${prefix}${n}"/>
  </xacro:if>
</xacro:macro>

<xacro:link_chain n="5" prefix="seg" parent="base_link"/>

정직: xacro에는 명시적 반복문이 없고 재귀 매크로가 관용적 방법이다. 가독성이 떨어지므로, 개수가 적으면(바퀴 4개) 단순히 네 줄 쓰는 편이 낫다. 재귀는 개수가 파라미터로 변해야 할 때만 쓴다.

리스트 순회 지시자는 Jazzy의 xacro에 없다(실행 확인 — 지시자 전체 목록은 5.4.12). 아래처럼 쓰는 코드를 보게 되면 그것은 파이썬 표현식과 재귀 매크로의 조합이지, 전용 반복 문법이 아니다:

<xacro:property name="wheel_list" value="${['fl','fr','rl','rr']}"/>
<xacro:macro name="all_wheels" params="names:=^">
  <!-- ... -->
</xacro:macro>

5.4.7 처리 방법

① CLI:

xacro robot.urdf.xacro > robot.urdf
xacro robot.urdf.xacro use_sim:=true prefix:=robot1/ > robot1.urdf

② launch에서(권장):

import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import Command, LaunchConfiguration, PathJoinSubstitution
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare
from launch_ros.parameter_descriptions import ParameterValue


def generate_launch_description():
    use_sim = LaunchConfiguration('use_sim')

    xacro_file = PathJoinSubstitution([
        FindPackageShare('my_robot_description'), 'urdf', 'robot.urdf.xacro'])

    # 실행 시점에 xacro 처리
    robot_description = ParameterValue(
        Command(['xacro ', xacro_file, ' use_sim:=', use_sim]),
        value_type=str)

    return LaunchDescription([
        DeclareLaunchArgument('use_sim', default_value='false'),
        Node(package='robot_state_publisher', executable='robot_state_publisher',
             parameters=[{'robot_description': robot_description,
                          'use_sim_time': use_sim}]),
    ])

ParameterValue(..., value_type=str)가 중요하다. 이것을 빠뜨리면 launch가 XML 문자열을 다른 타입으로 추론하려다 실패할 수 있다(확인 필요: 배포판별 동작).

③ Python으로 직접:

import xacro
doc = xacro.process_file(xacro_path, mappings={'use_sim': 'true'})
robot_desc = doc.toprettyxml(indent='  ')

④ 빌드 시점에 URDF 생성:

find_package(xacro REQUIRED)
xacro_add_files(urdf/robot.urdf.xacro TARGET media_files)
# (확인 필요: 매크로 이름·사용법은 버전 의존)
방식장점단점
launch에서 처리인자로 유연, 소스가 단일 출처실행마다 처리 비용
빌드 시점 생성실행이 빠름, 결과를 검증 가능인자 조합마다 파일
CLI 수동디버깅에 유용자동화 안 됨

견해: launch에서 처리하는 것이 기본이고, 빌드 시점 생성은 검증·배포용으로 병행하면 좋다. CI에서 xacro + check_urdf를 실행해 전개 결과를 검증하면(5.7), 문법 오류가 로봇 부팅 시점이 아니라 CI에서 검출된다.

5.4.8 디버깅

xacro 문제의 대부분은 "전개 결과를 안 보고 추측하는 것"에서 온다.

# 전개 결과 확인 — 가장 중요한 습관
xacro robot.urdf.xacro > /tmp/expanded.urdf
less /tmp/expanded.urdf

# 인자 조합별로
xacro robot.urdf.xacro use_sim:=true > /tmp/sim.urdf
xacro robot.urdf.xacro use_sim:=false > /tmp/real.urdf
diff /tmp/sim.urdf /tmp/real.urdf     # 조건부가 의도대로 동작하나

# 전개 후 검증 (5.3.9)
xacro robot.urdf.xacro | check_urdf /dev/stdin
# 또는
xacro robot.urdf.xacro > /tmp/r.urdf && check_urdf /tmp/r.urdf

# 현재 로봇의 실제 기술 확인 (5.5)
ros2 topic echo /robot_description --qos-durability transient_local --once

흔한 오류와 원인:

오류원인
Undefined substitution argument<xacro:arg> 선언 누락
name 'x' is not defined속성 미정의·오타·include 순서
unknown macro매크로 정의 전에 호출·include 누락
조건부가 항상 참문자열 "false" 평가 문제(5.4.5)
링크 이름 중복매크로에 prefix 누락
값이 문자열로 남음${} 빠뜨림

함정: include 순서가 중요하다. 매크로·속성은 정의 후에 사용해야 한다. 파일을 나눌 때 properties·macros를 먼저 include하는 것이 규칙이다.

함정: ${} 없이 값을 쓰면 문자열 그대로 들어간다. <mass value="base_mass"/>처럼 쓰면 파싱 오류가 나거나 비정상적인 값이 된다. 숫자가 들어갈 자리에는 항상 ${} 를 확인한다.

5.4.9 조립 가능한 로봇 설계

xacro의 실제 가치는 모듈 조립에 있다.

<!-- 재사용 가능한 센서 모듈 -->
<xacro:macro name="lidar_2d" params="prefix parent update_rate:=10 *origin">
  <link name="${prefix}_link">
    <visual>
      <geometry><cylinder radius="0.04" length="0.05"/></geometry>
    </visual>
    <collision>
      <geometry><cylinder radius="0.04" length="0.05"/></geometry>
    </collision>
    <xacro:cylinder_inertia m="0.15" r="0.04" h="0.05"/>
  </link>

  <joint name="${prefix}_joint" type="fixed">
    <xacro:insert_block name="origin"/>
    <parent link="${parent}"/>
    <child link="${prefix}_link"/>
  </joint>

  <!-- 시뮬 전용 부분은 조건부로 (10.3) -->
  <xacro:if value="${use_sim}">
    <gazebo reference="${prefix}_link">
      <sensor name="${prefix}" type="gpu_lidar">
        <update_rate>${update_rate}</update_rate>
        <!-- ... -->
      </sensor>
    </gazebo>
  </xacro:if>
</xacro:macro>
<!-- 로봇마다 다른 위치에 같은 센서 -->
<xacro:lidar_2d prefix="front_laser" parent="base_link" update_rate="15">
  <origin xyz="0.2 0 0.15" rpy="0 0 0"/>
</xacro:lidar_2d>

<xacro:lidar_2d prefix="rear_laser" parent="base_link">
  <origin xyz="-0.2 0 0.15" rpy="0 0 ${pi}"/>
</xacro:lidar_2d>

이 패턴의 이점:

이점설명
재사용센서 모듈을 여러 로봇에서
일관성관성·충돌 기하가 항상 같게
유지보수센서 모델 수정이 한 곳에서
조립로봇 변형을 구성으로 표현

견해: 센서·구동부 모듈을 별도 패키지로 만들어 공유하는 것이 여러 로봇을 다루는 조직에서 효과적이다. my_sensors_description 패키지에 매크로를 모아 두고, 각 로봇 패키지가 include한다. 벤더가 제공하는 기술 패키지들이 이 구조를 쓴다.

함정: 매크로가 외부 속성에 의존하면(위 예의 ${use_sim}) 재사용성이 떨어진다. 호출하는 쪽에서 그 속성을 정의해야 하고, 안 하면 오류가 난다. 파라미터로 받는 것이 더 견고하다 — 다만 파라미터가 늘어나 장황해지는 트레이드오프가 있다.

5.4.12 xacro가 실제로 아는 것 — 실측

이 장이 "버전에 따라 다를 수 있다"고 유보해 둔 항목들을 Jazzy의 xacro로 직접 실행해 확인했다(실행 확인).

5.4.12.1 지시자는 열한 개다

xacro가 인식하는 xacro: 지시자 전체다:

지시자하는 일
xacro:macro · xacro:insert_block매크로 정의 / 블록 인자 삽입
xacro:property · xacro:arg속성 / 명령행 인자
xacro:if · xacro:unless조건
xacro:include파일 포함
xacro:eval표현식 결과를 텍스트로
xacro:call매크로를 이름으로 동적 호출
xacro:attribute · xacro:element속성·요소를 계산해서 생성

뒤의 셋은 이 장이 다루지 않던 것들이다. 태그 이름·속성 이름·매크로 이름 자체를 계산해야 할 때 쓴다. 아래는 실제로 실행해 본 것이다(실행 확인):

<xacro:property name="is_link" value="true"/>
<xacro:property name="attr_name" value="mass"/>
<xacro:property name="mname" value="hello"/>

<xacro:element xacro:name="${'link' if is_link else 'joint'}" name="generated"/>
<inertial><xacro:attribute name="${attr_name}" value="1"/></inertial>
<xacro:call macro="${mname}" who="world"/>

결과:

<link name="generated"/>
<inertial mass="1"/>
<link name="hi_world"/>

읽는 법 세 가지: xacro:element는 이름을 xacro:name 속성으로 받고(단순한 name은 생성될 요소의 속성으로 넘어간다), xacro:attribute는 부모 요소에 속성을 추가하며, xacro:call은 매크로 이름을 macro=로 받고 나머지 속성을 그 매크로의 인자로 넘긴다.

견해: 셋 다 마지막 수단이다. 태그나 속성 이름이 변수인 URDF는 읽기 어렵고 편집기의 자동완성·검증도 받지 못한다. xacro:if 두 갈래로 풀 수 있으면 그쪽이 낫다. 이것들이 값어치를 하는 곳은 센서 종류를 목록으로 받아 서로 다른 태그를 뿜어야 하는 생성기류 매크로 정도다.

여기 없는 것도 중요하다 — xacro:for·xacro:for_each 같은 반복 지시자는 존재하지 않는다. 5.4.8의 재귀 매크로가 관용적 방법인 이유가 이것이다.

5.4.12.2 xacro:if의 조건 평가 — 문자열은 안전하다

이 장은 원래 $(arg x)를 조건에 직접 쓰는 것을 위험하다고 적었다. 실제로 값을 바꿔 가며 실행해 봤다:

<xacro:arg name="use_sim" default="false"/>
<xacro:if value="$(arg use_sim)">            <!-- 직접 -->
<xacro:property name="us" value="$(arg use_sim)"/>
<xacro:if value="${us}">                     <!-- property 경유 -->
넘긴 값직접 쓴 조건property 경유
(기본 false)거짓거짓
true참참
false거짓거짓
1참참
0거짓거짓
True참참
yes오류로 중단오류로 중단
(빈 값)거짓거짓

두 방식의 결과가 완전히 같다. 그리고 결정적으로, 조건으로 해석할 수 없는 값(yes)은 조용히 참이 되는 것이 아니라 오류를 낸다.

그래서 이 장의 원래 함정 서술 — "조용한 버그를 만드는 지점" — 은 방향이 반대였다. 실제 거동은 오히려 안전한 쪽이다. property를 한 번 거치는 것은 이름을 붙여 읽기 좋게 하는 이유로는 여전히 좋지만, 정확성 때문에 필요한 것은 아니다.

함정: 실제 조심할 값은 yes·on·Y 같은 "사람이 참이라고 여기는 단어"다. 셸이나 YAML에서 오던 습관으로 use_sim:=yes를 넘기면 빌드가 멈춘다. 다행히 조용하지 않으니, 오류 메시지를 만나면 값을 true/false로 바꾸면 된다.

5.4.12.3 인자 형식 네 가지 전부 동작한다

params의 네 형태를 한 매크로에 모아 실제로 처리시켰다(실행 확인):

<xacro:macro name="leg" params="prefix *origin **extra">
  <link name="${prefix}_leg"><xacro:insert_block name="origin"/></link>
</xacro:macro>

<xacro:leg prefix="left">
  <origin xyz="1 2 3"/>
  <extra><foo bar="1"/></extra>
</xacro:leg>

출력은 <link name="left_leg"><origin xyz="1 2 3"/></link>로, *블록과 **딕셔너리가 함께 있어도 정상 처리된다.

5.4.10 함정 카탈로그

#함정증상대처
1전개 결과 확인 안 함추측으로 디버깅xacro > /tmp/x.urdf
2${} 누락문자열이 그대로숫자 자리 확인
3include 순서 오류매크로·속성 미정의properties·macros 먼저
4매크로에 prefix 누락링크 이름 중복모든 이름에 접두어
5조건 인자에 yes·on 사용오류로 중단(참/거짓 아님)true/false·1/0 만 쓴다(5.4.12.2)
6상대 경로 include설치본에서 실패$(find pkg)
7시뮬·실기 URDF 별도 유지어긋남조건부 블록
8ParameterValue value_type 누락launch 실패value_type=str
9재귀 매크로 남용가독성 저하개수가 적으면 직접
10매크로가 외부 속성 의존재사용성 저하파라미터로
11메시·xacro 설치 누락배포본 실패install(DIRECTORY urdf meshes)
12CI 검증 없음로봇 부팅 시 발견CI에서 전개+검증

5.4.11 체크포인트

  • xacro가 푸는 URDF의 한계 네 가지를 말할 수 있는가.
  • ${} 안에서 쓸 수 있는 것 다섯 가지를 들 수 있는가. 관성을 수식으로 계산하는 것이 왜 큰 이득인가.
  • 매크로 파라미터의 네 형식과, 블록 인자가 유용한 경우를 말할 수 있는가.
  • 매크로 안의 이름에 접두어를 빠뜨리면 무슨 일이 생기는가.
  • 파일 분할 시 include 순서 규칙은 무엇이며 왜인가.
  • xacro:if가 참·거짓으로 받는 값은 정확히 무엇인가. yes를 넘기면 무슨 일이 생기나?
  • 시뮬용·실기용 URDF를 따로 두면 안 되는 이유는? 어떤 원칙과 연결되는가.
  • xacro 처리 네 가지 방식의 장단을 비교하고, 권장 조합을 말할 수 있는가.
  • ParameterValue(..., value_type=str)가 필요한 이유는?
  • xacro 디버깅의 가장 중요한 습관은 무엇인가. 조건부가 의도대로 동작하는지 확인하는 방법은?
  • 조립 가능한 센서 모듈 설계의 이점 네 가지와, 매크로가 외부 속성에 의존할 때의 문제는?
  • xacro가 인식하는 지시자는 몇 개이며 반복문은 있는가. params의 네 형태를 들 수 있는가(5.4.12).

다음 장 5.5는 이 모델을 실제 tf로 바꾸는 노드 — robot_state_publisher와 관절 상태를 다룬다. URDF가 어떻게 /tf가 되고, /robot_description이 어떻게 배포되며, 무엇이 빠지면 트리가 끊기는가.