제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_hardware | ros2_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 | ${} 누락 | 문자열이 그대로 | 숫자 자리 확인 |
| 3 | include 순서 오류 | 매크로·속성 미정의 | properties·macros 먼저 |
| 4 | 매크로에 prefix 누락 | 링크 이름 중복 | 모든 이름에 접두어 |
| 5 | 조건 인자에 yes·on 사용 | 오류로 중단(참/거짓 아님) | true/false·1/0 만 쓴다(5.4.12.2) |
| 6 | 상대 경로 include | 설치본에서 실패 | $(find pkg) |
| 7 | 시뮬·실기 URDF 별도 유지 | 어긋남 | 조건부 블록 |
| 8 | ParameterValue value_type 누락 | launch 실패 | value_type=str |
| 9 | 재귀 매크로 남용 | 가독성 저하 | 개수가 적으면 직접 |
| 10 | 매크로가 외부 속성 의존 | 재사용성 저하 | 파라미터로 |
| 11 | 메시·xacro 설치 누락 | 배포본 실패 | install(DIRECTORY urdf meshes) |
| 12 | CI 검증 없음 | 로봇 부팅 시 발견 | 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이 어떻게 배포되며, 무엇이 빠지면 트리가 끊기는가.