로봇 제어를 위한 작업 공간과 패키지 구성

The Elder Node·2026년 9월 5일

UR 로봇 제어용 컨트롤러 작성

이전과 마찬가지로 전체 코드는 https://github.com/hwjeon0123/robot-arm-study/ 를 참고한다.

1. 새로운 워크스페이스 생성

기존 ur_ws 환경과 새 코드를 분리하기 위해 robot-arm-study 워크스페이스를 생성하고 오버레이 환경을 구성한다.

  • 컨테이너 볼륨 마운트 추가
-v "${UR_WS}:/home/${CTR_USERNAME}/ur_ws:rw,z"
-v "${ARM_WS}:/home/${CTR_USERNAME}/robot-arm-study:rw,z"
  • 컨테이너 파일에서 환경 변수 소싱 (.bashrc)
RUN echo '[ -f ~/ur_ws/install/setup.bash ] && source ~/ur_ws/install/setup.bash' >> /home/$USERNAME/.bashrc && \
    echo '[ -f ~/robot-arm-study/install/setup.bash ] && source ~/robot-arm-study/install/setup.bash' >> /home/$USERNAME/.bashrc

jazzy → ur_ws(underlay) → robot-arm-study(overlay) 순서로 덮어씌운다. -f 옵션으로 빌드 결과물이 존재할 때만 소싱되도록 처리했다.

2. 패키지 분리 생성

ROS 2 매니퓰레이터 패키지 관례에 따라 형상(Description)과 실행(Bringup)을 분리하여 생성한다.

  • arm_description: 로봇 모델 형상 정의 (xacro/URDF). 시뮬레이션 및 실물 환경에서 재사용.
  • arm_bringup: 실행 구성 (launch, config yaml). 시나리오별로 분리.
mkdir -p ~/robot-arm-study/src
cd ~/robot-arm-study/src
ros2 pkg create arm_description --build-type ament_cmake --dependencies xacro ur_description
ros2 pkg create arm_bringup --build-type ament_cmake --dependencies arm_description robot_state_publisher ros_gz_sim xacro

두 패키지 모두 실행 노드가 아닌 리소스 파일 설치가 목적이므로 --build-type ament_cmake를 사용한다.

패키지 생성 때 의존성 패키지를 지정하면 src/package.xml파일에 추가된다. 각 패키지의 패키지 의존성에 대한 내용은 아래와 같다.

  • ur_description: ur_ws의 UR 매크로/메시/파라미터를 $(find ur_description)로 참조하기 위해 필요
  • arm_description: 내가 만들 형상 패키지
  • robot_state_publisher: URDF 파일을 읽어서 TF(Transform, 좌표계 변환)를 계산하고 발행(Publish)해 주는 ROS 2의 공식 패키지
  • ros_gz_sim: ROS 2와 Gazebo를 연결해주는 브릿지 패키지
  • xacro → launch에서 xacro 실행

3. URDF 및 Xacro 작성 (arm_description)

ur_description의 매크로를 가져와 최상위 ur5e.urdf.xacro를 작성한다.

~/robot-arm-study/src/arm_description/urdf/ur5e.urdf.xacro

<?xml version="1.0"?>
<robot xmlns:xacro="http://wiki.ros.org/xacro" name="ur5e">
  <xacro:arg name="name"      default="ur5e"/>
  <xacro:arg name="ur_type"   default="ur5e"/>
  <xacro:arg name="tf_prefix" default=""/>

  <xacro:include filename="$(find ur_description)/urdf/ur_macro.xacro"/>
  
  <xacro:arg name="joint_limit_params" default="$(find ur_description)/config/$(arg ur_type)/joint_limits.yaml"/>
  <xacro:arg name="kinematics_params"  default="$(find ur_description)/config/$(arg ur_type)/default_kinematics.yaml"/>
  <xacro:arg name="physical_params"    default="$(find ur_description)/config/$(arg ur_type)/physical_parameters.yaml"/>
  <xacro:arg name="visual_params"      default="$(find ur_description)/config/$(arg ur_type)/visual_parameters.yaml"/>

  <!-- Gazebo 고정 기준 링크 -->
  <link name="world"/>

  <xacro:ur_robot
    name="$(arg name)"
    tf_prefix="$(arg tf_prefix)"
    parent="world"
    joint_limits_parameters_file="$(arg joint_limit_params)"
    kinematics_parameters_file="$(arg kinematics_params)"
    physical_parameters_file="$(arg physical_params)"
    visual_parameters_file="$(arg visual_params)"
    force_abs_paths="true">
    <origin xyz="0 0 0" rpy="0 0 0"/>
  </xacro:ur_robot>
</robot>
  • parent="world": Gazebo 환경에서 로봇 베이스를 바닥에 고정(fixed joint)하기 위해 추가.
  • force_abs_paths="true": Gazebo가 메시 파일을 정상적으로 로드하도록 절대 경로(file:///) 출력을 강제함.

작성 후 CMakeLists.txt에 install(DIRECTORY urdf DESTINATION share/${PROJECT_NAME}) 규칙을 추가하고, check_urdf 명령으로 파싱 결과를 검증한다.

4. Gazebo 실행 및 ros2_control 설정 (arm_bringup)

4.1 제어 인터페이스 Xacro 작성

컨트롤러 태그를 분리하여 arm_study.ros2_control.xacro를 생성하고, 최상위 URDF 파일에서 include 한다.

<?xml version="1.0"?>
<robot xmlns:xacro="http://wiki.ros.org/xacro">
 <xacro:arg name="controllers_yaml" default=""/>
 <xacro:include filename="$(find ur_description)/urdf/inc/ur_joint_control.xacro"/>

 <ros2_control name="$(arg name)" type="system">
   <hardware>
     <plugin>gz_ros2_control/GazeboSimSystem</plugin>
   </hardware>
   <xacro:ur_joint_control_description tf_prefix="$(arg tf_prefix)"/>
 </ros2_control>

 <gazebo>
   <plugin filename="gz_ros2_control-system" name="gz_ros2_control::GazeboSimROS2ControlPlugin">
     <parameters>$(arg controllers_yaml)</parameters>
   </plugin>
 </gazebo>
</robot>

4.2 컨트롤러 YAML 설정

arm_bringup/config 디렉토리에 arm_controllers.yaml을 생성하여 joint_state_broadcaster와 joint_trajectory_controller를 정의한다. 궤적 컨트롤러의 joints 목록은 ur_joint_control.xacro에 정의된 관절 이름과 동일해야 한다.

4.3 런치 파일 구성

arm_bringup/launch 디렉토리에 arm_study_bringup.launch.py를 만들고 다음 구성 요소를 포함한다:
1. xacro 실행 결과 파싱: robot_description_content를 xacro를 이용해서 생성하고 파싱할 때 yaml 파일로 가정하지 않고 단순 문자열로 취급하도록(ParameterValue(..., value_type=str))하여 'Unable to parse the value of parameter robot_description as yaml. 문제를 회피
2. robot_state_publisher 노드 실행: use_sim_time 매개 변수를 true로 설정
3. ros_gz_sim 패키지에서 제공하는 기본 런치 파일(gz_sim.launch.py)을 이용하여 Gazebo 실행
4. ros_gz_sim 패키지의 create 실행 파일을 이용하여 앞서 생성한 robot_description_content를 읽어서 로봇팔이 생성되도록 노드를 실행한다.
5. controller_manager 패키지의 spawner를 실행하여 joint_state_broadcaster(로봇 관절 각도를 보고)와 joint_trajectory_controller(궤적 명령을 받아서 관절 제어) 제어기를 로드한다.

런치 실행 후 ros2 control list_controllers를 통해 2개의 컨트롤러가 active 상태인지 확인한다.

5. MoveIt 2 연동

경로를 계획하고 생성하는 MoveIt이 제어기(Gazebo/ros2_control)로 제어 명령을 전달할 수 있도록 Action 통신 채널을 설정하는 과정이다.

참고할 소스:
~/ur_ws/src/ur_driver/ur_moveit_config/config/moveit_controllers.yaml

패키지 의존성 추가 (arm_bringup/package.xml)

MoveIt 연동을 위한 런치 파일 작성 및 노드 실행을 위해, arm_bringup 패키지의 package.xml에 다음 의존성을 추가해야 한다.

<depend>moveit_configs_utils</depend>
<depend>ament_index_python</depend>
<depend>moveit_ros_move_group</depend>

• moveit_configs_utils: 복잡한 MoveIt 파라미터를 조립해 주는 MoveItConfigsBuilder 모듈 사용
• ament_index_python: 런치 파일 내에서 타 패키지의 절대 경로를 찾기 위한 get_package_share_directory 함수 사용
• moveit_ros_move_group: 최종적으로 구동할 move_group 노드 패키지

5.1 SRDF 및 컨트롤러 주소록 작성

  • arm_study.srdf.xacro: ur_moveit_config의 기본 ur_macro.srdf.xacro를 가져와서 기존 로봇의 SRDF 설정을 그대로 사용한다.
  • moveit_controllers.yaml: MoveIt 내부에서 궤적 데이터를 던질 Action Client의 정보를 명세한다. Gazebo의 joint_trajectory_controller로 정의한다.

5.2 move_group 런치 파일 작성

MoveItConfigsBuilder를 활용하여 move_group.launch.py를 구성한다. 의존성 파일들의 위치가 각기 다르므로 패키지 기준점을 명확히 지정한다.

    arm_description_share = Path(get_package_share_directory("arm_description"))
    ur_moveit_config_share = Path(get_package_share_directory("ur_moveit_config"))

    moveit_config = (
        MoveItConfigsBuilder(robot_name="ur5e", package_name="arm_bringup")
        .robot_description_semantic(
            arm_description_share / "srdf" / "arm_study.srdf.xacro", {"name": "ur5e"},
        )
        .robot_description_kinematics(ur_moveit_config_share / "config" / "kinematics.yaml")
        .joint_limits(ur_moveit_config_share / "config" / "joint_limits.yaml")
        .planning_pipelines(pipelines=["ompl"])
        .trajectory_execution()
        .to_moveit_configs()
    )
  • URDF를 포함하지 않는 이유: MoveItConfigsBuilder에 URDF 경로를 지정하지 않으면, move_group 노드는 자동으로 /robot_description 토픽을 구독하여 값을 채운다.
  • trajectory_execution(): 파일 경로를 명시하지 않으면 arm_bringup/config 디렉토리 내의 moveit_controllers.yaml을 자동 탐색한다.

이전과 마찬가지로 전체 코드는 https://github.com/hwjeon0123/robot-arm-study/ 를 참고한다.

6. 최종 실행 및 검증

터미널 2개를 열어 구축한 하위 계층(Gazebo)과 상위 계층(MoveIt 2)을 차례로 기동하여 연동을 확인한다. (하위 제어기가 켜지기 전에 MoveIt을 먼저 실행하면 통신 대상을 찾지 못해 에러가 발생한다.)

  • 터미널 1
vglrun ros2 launch arm_bringup arm_study_bringup.launch.py
  • 터미널 2
vglrun ros2 launch arm_bringup move_group.launch.py

(vglrun은 앞의 글에서 언급했듯이 컨테이너에서 3차원 가속을 사용하기 위해 설치해 둔 VirtualGL을 사용하기 위해 추가하였다)

터미널 로그에 [move_group-1] You can start planning now! 메시지가 출력된다.

profile
무선/임베디드 엔지니어의 ROS2 & AI 개척기

0개의 댓글