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_config | MoveIt 2 설정: SRDF, 기구학, 관절 한계, OMPL, 컨트롤러 |
rebotarm_moveit_demos | MoveIt 2 데모 draw_square(사각형 그리기), pick_place(집어 옮기기) |
| 권장 환경 | 참고 환경 |
|---|---|
| Ubuntu 24.04 + ROS 2 Jazzy + Python 3.12 | Ubuntu 22.04 + ROS 2 Humble + Python 3.10 (명령의 jazzy 를 humble 로) |
설치와 빌드#
위키의 순서를 그대로 옮깁니다. ${ROS_DISTRO} 는 ROS 2 환경을 불러오면 jazzy 또는 humble 로 채워집니다.
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 까지 쓰려면 추가 패키지가 필요합니다.
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}-xacroROS 2 명령이 "찾을 수 없음"이 나오면 거의 항상 source install/setup.bash 를 빠뜨린 것입니다. 새 터미널을 열 때마다 작업공간 폴더에서 실행하세요.
팔 연결하고 띄우기#
두 모델은 버스 연결 방법이 다릅니다. 기본 모델은 rebotarm_hardware.yaml 의 default_model: dm 입니다.
| B601-DM | B601-RS | |
|---|---|---|
| 연결 | USB2CAN 시리얼 브리지 → /dev/ttyACM0 | SocketCAN → 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/ttyACM0 | ros2 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** 를 띄웁니다. 이 노드가 하는 일은 단순합니다.
- 시작할 때 URDF(
robot_description)를 받아 링크·관절 구조를 기억합니다. /rebotarm/joint_states로 관절 각도가 들어올 때마다 10장의 정기구학과 같은 계산을 해서- 링크 사이 변환을
/tf로 방송합니다.
런치 파일 안을 보면 robot_state_publisher 의 기본 토픽 /joint_states 를 /rebotarm/joint_states 로 이름 바꾸기(remap) 해 연결해 둔 것을 볼 수 있습니다. 또 URDF 를 그대로 넘기지 않고 17장의 rviz_urdf_compat.py 를 거친 결과를 robot_description 으로 씁니다. Jazzy RViz 의 다중 재질 색 문제 때문입니다.
RViz2 에서 팔이 안 보이면 위키 안내대로 Fixed Frame 이 base_link 인지 확인하고, 마우스 휠로 거리를 조절하세요.
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 도 같은 인터페이스).
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미리 만들어 둔 실행 파일로도 같은 일을 할 수 있습니다.
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 GripperControlMoveTo 예시의 -0.20 같은 값은 위키에 그대로 실린 것입니다. 17장에서 봤듯 RS 와 DM 은 축 부호가 다릅니다. 모델에 맞는 부호인지, 한계 안인지 먼저 확인하고 작은 값과 긴 --duration 으로 시작하세요.
중력 보상(GravityCompensation)은 팔이 제 무게로 처지지 않도록 모터가 딱 중력만큼만 버티게 하는 모드입니다. 켜면 손으로 팔을 가볍게 밀어 원하는 자세로 옮길 수 있습니다. 위키에 따르면 이 스크립트는 먼저 /rebotarm/enable 을 부르고 보상을 시작하며, Ctrl+C 를 누르면 보상 정지 → safe_home → disable 순서로 끝냅니다. 원리는 19장에서 Pinocchio 로 직접 계산해 봅니다.
/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.yaml | IK 플러그인 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 이 무엇을 "팔"로 보는지 알 수 있습니다.
<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 탭을 열고
조인트탭에서 joint2 를 움직여 보세요.JointState메시지의name순서와position값(라디안!)이 어떻게 바뀌는지 확인합니다. - 같은 자세에서 TF 체인을 따라가
base_link부터 TCP 링크(RSgripper_end/ DMend_link)까지 몇 단계인지 세어 봅니다. 고정 관절도 TF 에 나타나나요? IK탭에서 목표를 바꾼 뒤 ROS 2 탭의JointTrajectory메시지를 읽어 보세요.points마다positions와time_from_start가 있는 구조가 위의follow_joint_trajectory예시와 같은지 비교합니다.- 모델을 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 으로 경로를 확인한 뒤 실행합니다.
확인 문제#
- 관절 각도를 계속 방송하는 것은 토픽·서비스·액션 중 무엇인가요? 궤적처럼 오래 걸리고 취소가 필요한 명령은요?
robot_state_publisher가/tf를 만들려면 어떤 두 가지 입력이 필요한가요?- RViz2 에 팔이 보이지 않을 때 위키가 먼저 확인하라는 설정은?
- 실물로 MoveIt 2 를 쓸 때
driver.launch.py와hardware.launch.py중 무엇을 먼저 실행하나요? /rebotarm/joints/joint2/cmd/mit토픽으로 응용 동작을 보내면 안 되는 이유는?
- 관절 각도는 토픽(
/rebotarm/joint_states), 궤적은 액션(/rebotarm/follow_joint_trajectory)입니다. - URDF(
robot_description)와 관절 상태(/rebotarm/joint_states)입니다. - Fixed Frame 이
base_link인지 확인합니다. driver.launch.py로 드라이버를 먼저 띄우고, 그다음hardware.launch.py로 MoveIt 을 연결합니다.- 저수준 cmd 토픽은 IK·궤적 계획·URDF 한계 검사를 하지 않아 갑작스럽거나 한계를 넘는 동작이 날 수 있기 때문입니다.