URDF 적용
이제 ROS2에 사용될 URDF를 가져와서 적용해야 한다. 원래는 로봇의 CAD 파일을 이용하거나 그마저도 없으면 길이나 치수 등을 측정하여 형상, 길이, joint 종류와 이동 범위, 위치를 XML 형식으로 작성해야 한다. 다행히 내가 진행하는 SO-ARM-101은 이미 공식적으로 URDF를 제공하며, 다른 유저에 의해서 패키지 형태로 제공되고 있는 것을 찾을 수 있었다.
https://github.com/legalaspro/so101-ros-physical-ai/tree/main/so101_description/urdf
so101-ros-physical-ai/so101_description/urdf at main · legalaspro/so101-ros-physical-ai
ROS 2 stack for SO-101 leader/follower: Feetech driver, bringup, teleop, MoveIt2, episode recording, Rerun, policy inference. - legalaspro/so101-ros-physical-ai
github.com
이미 이전에 ROS2 Workspace를 colcon build 명령어를 통해서 만들어 놨고,
[UnrealRobotics: SO-101] (2) 프로젝트 환경 세팅, 워크스페이스 구축, 언리얼 프로젝트 생성
[UnrealRobotics: SO-101] (2) 프로젝트 환경 세팅, 워크스페이스 구축, 언리얼 프로젝트 생성
ROS2 Humble 환경 세팅Windows11 WSL2 환경이기 때문에 ROS2 Humble 공식 문서의 Ubuntu 설치 환경을 따라간다. Jazzy가 ROS2의 최신버전이지만 활용 예시 등을 참고하기에는 Humble의 검색 결과가 많이 나오기 때
lightbakery.tistory.com
만들어진 워크스페이스 폴더 하위에 github로부터 필요한 파일만 가져와서 넣으면 된다.
이미 joint 이름이 같게 사용되고 있고 joint limit 숫자도 소수점까지 일치한다.
해당 Repo는 Ubuntu 24.04 + Jazzy이긴 한데 현재 우리가 설치한 Ubuntu 22.04 + Humble 에서도 사용 가능한 부분들을 가져오면 된다. xacro, urdf, robot_state_publisher, joint_state_publisher_gui, rviz2 모두 Humble에서 제공하는 표준 패키지이다.
so101_description/ 폴더만 가져와서 사용하는 것이 유리하다.
1단계: 비어있는 Workspace 상태 확인
cd ~/UnrealRobotics
ls -la
ls src/

2단계: sparse-checkout으로 so101_description만 가져오기
cd ~/UnrealRobotics/src
# so101_description 폴더만 선택적으로 받을 sparse 저장소 초기화
git clone --filter=blob:none --no-checkout https://github.com/legalaspro/so101-ros-physical-ai.git _legalaspro_sparse
cd _legalaspro_sparse
git sparse-checkout init --cone
git sparse-checkout set so101_description
git checkout main
# so101_description만 workspace/src로 꺼내기
mv so101_description ../so101_description
# sparse 저장소는 필요 없으니 삭제 (업데이트 다시 받으려면 이 명령을 다시 실행)
cd ..
rm -rf _legalaspro_sparse
# 확인
ls -la ~/UnrealRobotics/src/so101_description/

위와 같은 리스트가 나오면 성공.
sparse-checkout을 사용하는 이유
- 업데이트 추적이 쉬움: legalaspro가 URDF 버그 고치거나 메시 개선하면 git pull 한 번으로 반영. tar 다운로드는 이런 추적이 안 됨.
- 깔끔함: 전체 클론(45MB)과 달리 필요한 36MB 패키지만 받고, 불필요한 so101_bringup, so101_inference 같은 Jazzy 전용 패키지는 아예 가져오지 않음.
- 프로젝트 분리: workspace의 src/so101_description/이 upstream과 연결된 git 체크아웃이라는 사실을 나중에 프로젝트 git에 포함시킬지 결정할 때 유연함.
3단계: 빌드 의존성 설치 (rosdep)
source /opt/ros/humble/setup.bash
cd ~/UnrealRobotics
# 의존성 자동 설치 (xacro, joint_state_publisher_gui 등)
sudo rosdep init 2>/dev/null || true
rosdep update
rosdep install --from-paths src --ignore-src -r -y
* sudo rosdep init은 이미 한 적 있으면 에러가 나는데, 2>/dev/null || true로 무시한다.
4단계: colcon build
cd ~/UnrealRobotics
# colcon이 없으면 설치
which colcon || sudo apt install -y python3-colcon-common-extensions
colcon build --packages-select so101_description --symlink-install
진행 중 에러 발생, conda deactivate로 (base) 지워서 했는데도 같은 에러 발생.

miniforge PATH를 수동으로 제거하고 빌드 -해결
export PATH=$(echo $PATH | tr ':' '\n' | grep -v miniforge | tr '\n' ':' | sed 's/:$//')
source /opt/ros/humble/setup.bash
# 확인 — 시스템 python3을 가리켜야 함
which python3
# 기대: /usr/bin/python3
# 이전 빌드 캐시 정리 후 재빌드
rm -rf build/ install/ log/
colcon build --packages-select so101_description --symlink-install

5단계: 빌드 검증
source ~/UnrealRobotics/install/setup.bash
# 패키지 인식 여부
ros2 pkg list | grep so101_description
# xacro가 URDF로 정상 변환되는지 (leader)
ros2 run xacro xacro \
$(ros2 pkg prefix so101_description)/share/so101_description/urdf/so101_arm.urdf.xacro \
variant:=leader > /tmp/so101_leader.urdf
echo "Leader URDF generated: $(wc -l < /tmp/so101_leader.urdf) lines"
# follower도
ros2 run xacro xacro \
$(ros2 pkg prefix so101_description)/share/so101_description/urdf/so101_arm.urdf.xacro \
variant:=follower > /tmp/so101_follower.urdf
echo "Follower URDF generated: $(wc -l < /tmp/so101_follower.urdf) lines"
# joint 이름 확인
grep 'joint name=' /tmp/so101_follower.urdf | grep -v fixed

6단계: RViz 시각화 테스트 (WSL2에서 GUI)
source ~/UnrealRobotics/install/setup.bash
# Follower 모델을 RViz에 띄움 (joint_state_publisher_gui로 슬라이더 조작)
ros2 launch so101_description display.launch.py
에러 발생

display.launch.py가 xacro가 아닌 plain URDF(so101_new_calib.urdf)를 직접 참조하고 있는데, 이 파일은 legalaspro 레포에 포함되어 있지 않다(TheRobotStudio 공식 레포에만 있는 파일). xacro를 사용하도록 수정하면 됨.
cd ~/UnrealRobotics/src/so101_description/launch
cp display.launch.py display.launch.py.bak
cat > display.launch.py << 'EOF'
import os
import xacro
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.conditions import IfCondition, UnlessCondition
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
def generate_launch_description():
so101_description_share_dir = get_package_share_directory('so101_description')
# Declare arguments
gui_arg = DeclareLaunchArgument(
'gui',
default_value='true',
description='Use joint_state_publisher_gui instead of joint_state_publisher',
)
display_config_arg = DeclareLaunchArgument(
'display_config',
default_value=os.path.join(so101_description_share_dir, 'rviz', 'display.rviz'),
description='Path to the RViz display config file',
)
variant_arg = DeclareLaunchArgument(
'variant',
default_value='follower',
description='Robot variant: leader or follower',
)
gui = LaunchConfiguration('gui')
display_config = LaunchConfiguration('display_config')
variant = LaunchConfiguration('variant')
# Use xacro instead of plain URDF
robot_desc_path = os.path.join(
so101_description_share_dir, "urdf", "so101_arm.urdf.xacro"
)
# xacro.process_file needs the variant value at launch-file parse time,
# so we use OpaqueFunction to resolve it.
from launch.actions import OpaqueFunction
def launch_setup(context):
variant_str = context.launch_configurations['variant']
robot_desc = xacro.process_file(robot_desc_path, mappings={'variant': variant_str})
robot_description_xml = robot_desc.toxml()
robot_state_publisher_node = Node(
package="robot_state_publisher",
executable="robot_state_publisher",
parameters=[{"robot_description": robot_description_xml}],
output="screen",
)
joint_state_publisher_node = Node(
package='joint_state_publisher',
executable='joint_state_publisher',
condition=UnlessCondition(gui),
)
joint_state_publisher_gui_node = Node(
package='joint_state_publisher_gui',
executable='joint_state_publisher_gui',
condition=IfCondition(gui),
)
rviz_node = Node(
package='rviz2',
executable='rviz2',
name='rviz2',
output='screen',
arguments=['-d', display_config],
)
return [
robot_state_publisher_node,
joint_state_publisher_node,
joint_state_publisher_gui_node,
rviz_node,
]
return LaunchDescription(
[
gui_arg,
display_config_arg,
variant_arg,
OpaqueFunction(function=launch_setup),
]
)
EOF
오류 해결.
다시 실행하면 GUI가 뜬다.
source ~/UnrealRobotics/install/setup.bash
ros2 launch so101_description display.launch.py variant:=follower

'Unreal Engine > 언리얼-ROS-Physical 통합 프로젝트' 카테고리의 다른 글
| [UnrealRobotics: SO-101] (9) 실물 로봇 + RViz 실시간 동기화 (0) | 2026.04.22 |
|---|---|
| [UnrealRobotics: SO-101] (8) 로봇 상태 퍼블리셔(robot_status_publisher) 셋업, Rviz 연결 (0) | 2026.04.21 |
| [UnrealRobotics: SO-101] (6) SO-ARM-101 조립 (0) | 2026.04.15 |
| [UnrealRobotics: SO-101] (5) SO-ARM-101 부품 검증, 모터 연결 확인 (2) | 2026.04.10 |
| [UnrealRobotics: SO-101] (4) 언리얼 Output Log에 로그 출력 (C++ 파일 생성 및 디버깅) (0) | 2026.04.09 |