PART 5 ROS 2 연동과 시뮬레이션 · 약 45분

18ROS 2 연동과 MoveIt 2

robot_state_publisher·RViz2 로 모델을 띄우고, 공식 ROS 2 패키지로 궤적 계획·중력 보상·MoveIt 2 를 다룹니다.

ROS 2 를 왜 쓰나요#

지금까지는 Python SDK(reBotArm_control_py)로 팔을 직접 움직였습니다. 프로그램 하나가 팔 하나를 다룰 때는 이걸로 충분합니다. 그런데 카메라·경로 계획기·시각화 도구·음성 비서가 동시에 팔을 들여다보고 명령해야 한다면 어떨까요? 서로 약속된 방식으로 이야기를 주고받을 "공용 우체국"이 필요합니다. 그것이 ROS 2(Robot Operating System 2) 입니다.

용어비유reBot 예
노드(node)일하는 사람 한 명reBotArmController, robot_state_publisher, move_group
토픽(topic)계속 흘러나오는 방송/rebotarm/joint_states — 관절 각도를 100 Hz 로 방송
서비스(service)한 번 묻고 한 번 답하기/rebotarm/enable — "모터 켜 줘" → "켰어요"
액션(action)오래 걸리는 심부름 + 중간 보고 + 취소 가능/rebotarm/follow_joint_trajectory — 궤적 따라가기
TF모든 좌표계의 족보base_link → link1 → … → gripper_end

이 장에서는 Seeed 가 공개한 공식 ROS 2 작업공간으로 reBot 을 띄우고, RViz2 로 보고, MoveIt 2 로 경로를 계획합니다. 근거는 공식 위키 B601-DM ROS 2 연동 · B601-RS ROS 2 연동 과 저장소 reBotArmController_ROS2 입니다.

실물이 없어도 괜찮습니다

이 장의 명령은 Ubuntu + ROS 2 가 설치된 PC 가 필요합니다. 없다면 마지막의 시뮬레이터 ROS 2 탭 실습으로 메시지 구조와 TF 를 먼저 익히세요. MoveIt 2 의 demo.launch.py 는 실물 없이 가상 하드웨어로도 돌아갑니다.


공식 작업공간 rebotarm_ros2 구성#

저장소를 받으면 rebotarm_ros2 라는 작업공간이 됩니다. 하나의 작업공간이 DM 과 RS 를 모두 지원합니다. 핵심 아이디어는 "Python SDK 를 ROS 2 의 토픽·서비스·액션으로 감싼다"입니다. 그래서 SDK 가 third_party/ 아래에 함께 들어갑니다.

패키지역할
rebotarm_msgs전용 메시지·서비스·액션 정의 (ArmStatus, MoveToPoseIK, SetGripper …)
rebotarmcontroller드라이버 노드 reBotArmController 와 예제 실행 파일 GravityCompensation, GripperControl, MoveTo, MoveToPose
rebotarm_bringup런치 파일, 하드웨어 설정(rebotarm_hardware.yaml), URDF·메시, RViz 설정
rebotarm_moveit_configMoveIt 2 설정: SRDF, 기구학, 관절 한계, OMPL, 컨트롤러
rebotarm_moveit_demosMoveIt 2 데모 draw_square(사각형 그리기), pick_place(집어 옮기기)
권장 환경참고 환경
Ubuntu 24.04 + ROS 2 Jazzy + Python 3.12Ubuntu 22.04 + ROS 2 Humble + Python 3.10 (명령의 jazzy 를 humble 로)

설치와 빌드#

위키의 순서를 그대로 옮깁니다. ${ROS_DISTRO} 는 ROS 2 환경을 불러오면 jazzy 또는 humble 로 채워집니다.

bash
sudo apt update
sudo apt install -y python3-colcon-common-extensions python3-pip git
sudo apt install -y \
  ros-${ROS_DISTRO}-control-msgs \
  ros-${ROS_DISTRO}-trajectory-msgs \
  ros-${ROS_DISTRO}-tf-transformations \
  ros-${ROS_DISTRO}-robot-state-publisher \
  ros-${ROS_DISTRO}-rviz2 \
  ros-${ROS_DISTRO}-pinocchio

git clone https://github.com/Seeed-Projects/reBotArmController_ROS2.git rebotarm_ros2
cd rebotarm_ros2
python3 -m pip install --user --break-system-packages --index-url https://pypi.org/simple motorbridge
mkdir -p third_party
git clone https://github.com/Seeed-Projects/reBotArm_control_py.git third_party/reBotArm_control_py

source /opt/ros/${ROS_DISTRO}/setup.bash
colcon build --symlink-install
source install/setup.bash
ros2 pkg executables rebotarmcontroller     # 실행 파일 5개가 보이면 성공

MoveIt 2 까지 쓰려면 추가 패키지가 필요합니다.

bash
sudo apt install -y \
  ros-${ROS_DISTRO}-moveit ros-${ROS_DISTRO}-moveit-configs-utils \
  ros-${ROS_DISTRO}-moveit-kinematics ros-${ROS_DISTRO}-moveit-planners-ompl \
  ros-${ROS_DISTRO}-moveit-simple-controller-manager \
  ros-${ROS_DISTRO}-ros2-control ros-${ROS_DISTRO}-ros2-controllers ros-${ROS_DISTRO}-xacro
새 터미널마다

ROS 2 명령이 "찾을 수 없음"이 나오면 거의 항상 source install/setup.bash 를 빠뜨린 것입니다. 새 터미널을 열 때마다 작업공간 폴더에서 실행하세요.


팔 연결하고 띄우기#

두 모델은 버스 연결 방법이 다릅니다. 기본 모델은 rebotarm_hardware.yaml 의 default_model: dm 입니다.

B601-DMB601-RS
연결USB2CAN 시리얼 브리지 → /dev/ttyACM0SocketCAN → can0 (1 Mbps)
준비sudo usermod -a -G dialout $USER 후 재로그인sudo ip link set can0 type can bitrate 1000000 → sudo ip link set can0 up
기동ros2 launch rebotarm_bringup bringup.launch.py channel:=/dev/ttyACM0ros2 launch rebotarm_bringup bringup.launch.py model:=rs channel:=can0

RViz2 까지 함께 띄우려면 끝에 use_rviz:=true 를 붙입니다. 팔을 두 대 쓰면 arm_namespace:=left_arm 처럼 이름공간을 나눕니다.

전원과 작업 공간을 먼저 확인

DM 은 24V, RS 는 48V 입니다. 전원 어댑터를 바꿔 끼우지 마세요. 위키는 "컨트롤러를 켜거나 팔에 전원을 넣기 전에 작업 공간에 사람과 장애물이 없게 하라"고 강조합니다. 비상 시 전원을 끊을 수 있도록 손 닿는 곳에 두세요.

robot_state_publisher 와 TF#

bringup.launch.py 는 드라이버 노드와 함께 **robot_state_publisher** 를 띄웁니다. 이 노드가 하는 일은 단순합니다.

  1. 시작할 때 URDF(robot_description)를 받아 링크·관절 구조를 기억합니다.
  2. /rebotarm/joint_states 로 관절 각도가 들어올 때마다 10장의 정기구학과 같은 계산을 해서
  3. 링크 사이 변환을 /tf 로 방송합니다.
reBot 실물 팔 모터 6 + 그리퍼 (CAN) reBotArmController 드라이버 노드 IK·궤적·중력 보상 내장 can0 / ttyACM0 ReBot_Arm_*.urdf robot_description robot_state_publisher 관절각 + URDF → 좌표 변환 /rebotarm/joint_states RViz2 Fixed Frame: base_link /tf move_group MoveIt 2 경로 계획 follow_joint_trajectory (액션) 목표 지정
드라이버가 관절 각도를 방송하면 robot_state_publisher 가 TF 로 바꾸고, RViz2 는 그 TF 로 메시를 그립니다. MoveIt 2 는 액션으로 궤적을 보냅니다.

런치 파일 안을 보면 robot_state_publisher 의 기본 토픽 /joint_states 를 /rebotarm/joint_states 로 이름 바꾸기(remap) 해 연결해 둔 것을 볼 수 있습니다. 또 URDF 를 그대로 넘기지 않고 17장의 rviz_urdf_compat.py 를 거친 결과를 robot_description 으로 씁니다. Jazzy RViz 의 다중 재질 색 문제 때문입니다.

RViz2 에서 팔이 안 보이면 위키 안내대로 Fixed Frame 이 base_link 인지 확인하고, 마우스 휠로 거리를 조절하세요.

bash
ros2 topic echo /rebotarm/joint_states --once   # 관절 이름·위치·속도·토크
ros2 run tf2_ros tf2_echo base_link gripper_end # RS 의 TCP 위치 (DM 은 end_link)

토픽 · 서비스 · 액션으로 움직이기#

기본 이름공간은 /rebotarm 입니다.

종류이름하는 일
토픽/rebotarm/joint_states관절 상태 (sensor_msgs/msg/JointState)
토픽/rebotarm/arm_status팔 전체 상태
서비스/rebotarm/enable · /disable · /safe_home모터 켜기·끄기·안전 복귀
서비스/rebotarm/set_zero영점 설정 (7장)
서비스/rebotarm/move_to_pose_ik목표 자세로 IK 이동
서비스/rebotarm/gravity_compensation/start · /stop중력 보상 켜기·끄기
서비스/rebotarm/gripper/set그리퍼 위치
액션/rebotarm/move_to_pose손끝 목표 자세로 이동
액션/rebotarm/follow_joint_trajectory표준 관절 궤적 따라가기
액션/rebotarm/gripper/command표준 그리퍼 명령

RS 위키의 예시 흐름입니다(DM 도 같은 인터페이스).

bash
ros2 service call /rebotarm/enable std_srvs/srv/Trigger

ros2 action send_goal /rebotarm/move_to_pose rebotarm_msgs/action/MoveToPose \
  "{target_pose: {position: {x: 0.30, y: 0.0, z: 0.30}, orientation: {x: 0.0, y: 0.0, z: 0.0, w: 1.0}}, duration: 2.0}"

ros2 action send_goal /rebotarm/follow_joint_trajectory \
  control_msgs/action/FollowJointTrajectory \
  "{trajectory: {joint_names: ['joint1','joint2','joint3','joint4','joint5','joint6'],
    points: [{positions: [0.1,0,0,0,0,0], time_from_start: {sec: 5}}]}}"

ros2 service call /rebotarm/safe_home std_srvs/srv/Trigger
ros2 service call /rebotarm/disable std_srvs/srv/Trigger

미리 만들어 둔 실행 파일로도 같은 일을 할 수 있습니다.

bash
ros2 run rebotarmcontroller MoveTo -- 0.20 -0.20 -0.20 -0.20 0.10 -0.10 --duration 8.0
ros2 run rebotarmcontroller MoveTo -- --joint joint3 --position -0.20 --duration 5.0
ros2 run rebotarmcontroller MoveToPose -- --x 0.30 --y 0.0 --z 0.30 --qw 1.0 --duration 2.0
ros2 run rebotarmcontroller GravityCompensation
ros2 run rebotarmcontroller GripperControl
위 관절 숫자는 DM 기준입니다

MoveTo 예시의 -0.20 같은 값은 위키에 그대로 실린 것입니다. 17장에서 봤듯 RS 와 DM 은 축 부호가 다릅니다. 모델에 맞는 부호인지, 한계 안인지 먼저 확인하고 작은 값과 긴 --duration 으로 시작하세요.

중력 보상(GravityCompensation)은 팔이 제 무게로 처지지 않도록 모터가 딱 중력만큼만 버티게 하는 모드입니다. 켜면 손으로 팔을 가볍게 밀어 원하는 자세로 옮길 수 있습니다. 위키에 따르면 이 스크립트는 먼저 /rebotarm/enable 을 부르고 보상을 시작하며, Ctrl+C 를 누르면 보상 정지 → safe_home → disable 순서로 끝냅니다. 원리는 19장에서 Pinocchio 로 직접 계산해 봅니다.

저수준 cmd 토픽은 디버그용

/rebotarm/joints/<관절>/cmd/mit 같은 저수준 토픽은 IK·궤적 계획·URDF 한계 검사를 하지 않습니다. 응용 동작은 반드시 위 표의 서비스·액션을 쓰라는 것이 위키의 안내입니다.


MoveIt 2 — 목표만 주면 경로를 만들어 줍니다#

MoveToPose 는 "여기로 가"만 처리합니다. 가는 길에 책상이나 자기 몸에 부딪히는지는 따지지 않습니다. MoveIt 2 는 URDF 의 collision 형상으로 충돌 없는 경로를 찾아 줍니다.

rebotarm_moveit_config/config/ 에 들어 있는 주요 설정은 이렇습니다.

파일내용
rebotarm.srdf (DM) · rebotarm_rs.srdf (RS)계획 그룹 arm = base_link → gripper_tcp 사슬, home 자세(모두 0), 인접 링크 충돌 무시 목록
rebotarm_rs.urdf.xacro 등공식 URDF 를 include 하고, gripper_end(RS) 원점에 빈 링크 gripper_tcp 를 고정으로 추가
kinematics.yamlIK 플러그인 kdl_kinematics_plugin/KDLKinematicsPlugin
joint_limits_rs.yaml 등계획용 속도·가속도 한계
ros2_controllers_rs.yaml가상 하드웨어용 rebotarm_controller(6관절)·gripper_controller
moveit_hardware_controllers.yaml실물용: 컨트롤러 rebotarm 의 follow_joint_trajectory 액션 사용

SRDF 의 핵심 몇 줄을 보면 MoveIt 이 무엇을 "팔"로 보는지 알 수 있습니다.

xml
<group name="arm">
  <chain base_link="base_link" tip_link="gripper_tcp"/>
</group>
<virtual_joint name="FixedBase" type="fixed" parent_frame="world" child_link="base_link"/>
<disable_collisions link1="link1" link2="link2" reason="Adjacent"/>

실행 순서#

상황명령
가상 하드웨어로 연습 (실물 불필요)ros2 launch rebotarm_moveit_config demo.launch.py (RS 는 model:=rs)
실물: ① 드라이버 먼저ros2 launch rebotarm_bringup driver.launch.py channel:=/dev/ttyACM0 (RS: model:=rs channel:=can0)
실물: ② MoveIt 연결ros2 launch rebotarm_moveit_config hardware.launch.py (RS 는 model:=rs)
데모ros2 launch rebotarm_moveit_demos draw_square.launch.py / pick_place.launch.py

RViz2 의 MotionPlanning 패널에서 주황색 목표 팔을 끌어 놓고 Plan 을 누르면 경로가 미리 재생되고, Execute 를 누르면 실제로 움직입니다.

실물 실행 전 반드시

위키는 하드웨어에서 MoveIt 데모를 돌리기 전에 RViz 에서 계획된 경로를 먼저 확인하고, 언제든 컨트롤러를 멈출 준비를 하라고 합니다. 처음에는 Plan 만 눌러 경로를 눈으로 본 뒤 Execute 하세요. 토크가 켜진 팔의 움직임 범위 안에 손을 넣지 마세요.


커뮤니티 ROS 2 패키지#

공식 저장소 README 의 기여자 표에는 @danieldoradotalaveron-rb 의 ROS 2(Humble) 작업이 올라 있습니다.

저장소내용 (README 기준)
rebotarm_monitor_ros2수동(passive) 진단 모니터 — rqt_robot_monitor 용 /diagnostics, 시리얼·CAN 을 아는 집계기
reBotArmController_ROS2 (포크)접속 시 휴식 자세 기억·종료 시 천천히 복귀(/rebotarm/park), MIT 램프아웃으로 부드럽게 멈추는 중력 보상, IK/FK 게임패드 텔레오퍼(시뮬레이션 검증), end_link 아래 D405 손목 카메라 TF

공식 작업공간과 이름이 같은 포크이니 혼동하지 마세요. 공식 저장소에는 이 밖에도 RS MuJoCo 래퍼(rebotarm_mujoco_rs)와 자연어 에이전트(rebotarm_agent) 패키지가 함께 들어 있습니다(20장, 21장).


시뮬레이터 ROS 2 탭으로 미리 보기#

강좌 시뮬레이터의 ROS 2 탭은 ROS 를 설치하지 않아도, 지금 팔 자세가 ROS 2 메시지로 어떻게 생겼는지 보여 줍니다.

ROS 2 탭 열기

실습 — JointState 와 TF 읽기
  1. ROS 2 탭을 열고 조인트 탭에서 joint2 를 움직여 보세요. JointState 메시지의 name 순서와 position 값(라디안!)이 어떻게 바뀌는지 확인합니다.
  2. 같은 자세에서 TF 체인을 따라가 base_link 부터 TCP 링크(RS gripper_end / DM end_link)까지 몇 단계인지 세어 봅니다. 고정 관절도 TF 에 나타나나요?
  3. IK 탭에서 목표를 바꾼 뒤 ROS 2 탭의 JointTrajectory 메시지를 읽어 보세요. points 마다 positions 와 time_from_start 가 있는 구조가 위의 follow_joint_trajectory 예시와 같은지 비교합니다.
  4. 모델을 DM 으로 바꿔 같은 모양을 만들고 joint2·joint3 의 부호를 비교하세요.

정리#

  • ROS 2 는 노드가 토픽(방송)·서비스(문답)·액션(긴 심부름)으로 이야기하는 구조이고, TF 는 좌표계 족보입니다.
  • 공식 작업공간 rebotarm_ros2 는 Python SDK 를 감싸 DM·RS 를 모두 지원합니다. DM 은 /dev/ttyACM0, RS 는 model:=rs channel:=can0 으로 띄웁니다.
  • robot_state_publisher 는 URDF + /rebotarm/joint_states 로 /tf 를 만들고, RViz2 는 그 TF 로 팔을 그립니다.
  • /rebotarm/move_to_pose, /rebotarm/follow_joint_trajectory, GravityCompensation 등으로 기구학·궤적·중력 보상을 씁니다. 저수준 cmd 토픽은 안전 검사가 없습니다.
  • MoveIt 2 설정은 계획 그룹 arm(base_link→gripper_tcp), KDL IK 를 쓰며, 실물은 드라이버 → hardware.launch.py 순서로, 반드시 Plan 으로 경로를 확인한 뒤 실행합니다.

확인 문제#

  1. 관절 각도를 계속 방송하는 것은 토픽·서비스·액션 중 무엇인가요? 궤적처럼 오래 걸리고 취소가 필요한 명령은요?
  2. robot_state_publisher 가 /tf 를 만들려면 어떤 두 가지 입력이 필요한가요?
  3. RViz2 에 팔이 보이지 않을 때 위키가 먼저 확인하라는 설정은?
  4. 실물로 MoveIt 2 를 쓸 때 driver.launch.py 와 hardware.launch.py 중 무엇을 먼저 실행하나요?
  5. /rebotarm/joints/joint2/cmd/mit 토픽으로 응용 동작을 보내면 안 되는 이유는?
정답
  1. 관절 각도는 토픽(/rebotarm/joint_states), 궤적은 액션(/rebotarm/follow_joint_trajectory)입니다.
  2. URDF(robot_description)와 관절 상태(/rebotarm/joint_states)입니다.
  3. Fixed Frame 이 base_link 인지 확인합니다.
  4. driver.launch.py 로 드라이버를 먼저 띄우고, 그다음 hardware.launch.py 로 MoveIt 을 연결합니다.
  5. 저수준 cmd 토픽은 IK·궤적 계획·URDF 한계 검사를 하지 않아 갑작스럽거나 한계를 넘는 동작이 날 수 있기 때문입니다.