Unreal Engine/언리얼-ROS-Physical 통합 프로젝트

[UnrealRobotics: SO-101] (8) 로봇 상태 퍼블리셔(robot_status_publisher) 셋업, Rviz 연결

테크앤아트 2026. 4. 21. 23:45
728x90
반응형

 

 

 


 

 

rclpy 환경 충돌 문제 해결

 

  • LeRobot은 conda env lerobot (Python 3.12, miniforge)에서 동작
  • ROS2 Humble의 rclpy는 시스템 Python 3.10 (Ubuntu 22.04)에서 동작
  • 두 환경은 PATH 충돌을 일으켜 같은 터미널에서 못 씀

 

먼저 확인할 내용

 

  1. 시스템 Python 버전 (Ubuntu 22.04 기본 = 3.10 예상)
  2. conda env lerobot의 Python 버전 (3.12 — PROGRESS.md 기재)
  3. ros-humble-rclpy가 apt로 설치되어 있는지, 어느 Python을 타겟으로 빌드되었는지
  4. rclpy의 .so 파일이 어느 CPython ABI를 요구하는지
  5. LeRobot의 주요 dependency (특히 torch)가 system Python venv에 설치 가능한 크기/복잡도인지

 

# ═══════════════════════════════════════════════════════════
# 1. 시스템 Python 확인 (conda 영향 제거한 상태에서)
# ═══════════════════════════════════════════════════════════
echo "===== [1] System Python ====="
# conda PATH 제거
export PATH=$(echo $PATH | tr ':' '\n' | grep -v miniforge | tr '\n' ':' | sed 's/:$//')
which python3
python3 --version
which python3.10
python3.10 --version 2>/dev/null || echo "python3.10 not found"

# ═══════════════════════════════════════════════════════════
# 2. ROS2 rclpy 설치 상태 및 Python ABI 타겟 확인
# ═══════════════════════════════════════════════════════════
echo ""
echo "===== [2] rclpy installation ====="
dpkg -l | grep ros-humble-rclpy || echo "ros-humble-rclpy not installed via apt"
echo ""
echo "--- rclpy Python package location ---"
ls -la /opt/ros/humble/lib/python3.10/site-packages/rclpy/ 2>/dev/null | head -20 || echo "not found at python3.10 path"
echo ""
echo "--- rclpy .so file (ABI check) ---"
find /opt/ros/humble -name "_rclpy_pybind11*.so" 2>/dev/null

# ═══════════════════════════════════════════════════════════
# 3. ROS2 source → rclpy import 검증 (시스템 Python)
# ═══════════════════════════════════════════════════════════
echo ""
echo "===== [3] rclpy import test (system python3) ====="
source /opt/ros/humble/setup.bash
python3 -c "import rclpy; print('rclpy OK:', rclpy.__file__)" 2>&1

# ═══════════════════════════════════════════════════════════
# 4. conda env 'lerobot' Python 버전 + LeRobot import 확인
# ═══════════════════════════════════════════════════════════
echo ""
echo "===== [4] conda env 'lerobot' ====="
# miniforge PATH 복원
source ~/miniforge3/etc/profile.d/conda.sh 2>/dev/null || source ~/miniconda3/etc/profile.d/conda.sh 2>/dev/null
conda activate lerobot
which python
python --version
python -c "import lerobot; print('lerobot version:', getattr(lerobot, '__version__', 'unknown')); print('lerobot path:', lerobot.__file__)" 2>&1
conda deactivate

# ═══════════════════════════════════════════════════════════
# 5. LeRobot 주요 dependency 크기 확인 (venv 방식 feasibility)
# ═══════════════════════════════════════════════════════════
echo ""
echo "===== [5] LeRobot deps ====="
source ~/miniforge3/etc/profile.d/conda.sh 2>/dev/null || source ~/miniconda3/etc/profile.d/conda.sh 2>/dev/null
conda activate lerobot
pip list 2>/dev/null | grep -iE "^(torch|numpy|scipy|feetech|scservo)" || echo "deps check failed"
conda deactivate

# ═══════════════════════════════════════════════════════════
# 6. 시스템 Python 3.10에서 LeRobot dependency 설치 가능성 프리뷰
# ═══════════════════════════════════════════════════════════
echo ""
echo "===== [6] system python3.10 - pip availability ====="
python3.10 -m pip --version 2>&1 || echo "pip not available for python3.10"
python3.10 -m venv --help > /dev/null 2>&1 && echo "venv module: OK" || echo "venv module: MISSING (need: sudo apt install python3.10-venv)"

 

위의 명령어 실행으로, 다음의 정보를 알아낼 수 있다.

시스템 Python 3.10.12
ros-humble-rclpy 3.3.21 설치됨, python3.10 ABI 바이너리 (_rclpy_pybind11.cpython-310-*.so)
rclpy import (시스템 py3) OK
conda lerobot env Python 3.12.13, lerobot 0.5.2
LeRobot deps torch 2.10, numpy 2.2, feetech-servo-sdk
python3.10-venv OK
python3.10 -m pip 없음 (apt로 python3-pip 설치 필요)

 

 

위의 정보를 바탕으로, rclpy는 python 3.10 ABI 바이너리로 lock-in 되어 있어서 conda Python 3.12에서는 사용할 수 없고, 시스템에 3.10+venv가 있으므로 venv를 쓰는 방향으로 갈 수 있다. venv는 격리되므로 ROS2 Humble apt 패키지를 건드리지 않는다. 또한, 3.10 바이너리를 그대로 쓰기 때문에 rclpy의 .so와 ABI 호환이 된다.

ROS2 bridge 용으로 별도의 Python 3.10 venv를 만들어서 LeRobot을 한 번 더 설치한다.

 

 


 

 

1단계: 실행 계획 (에러 발생)

# ═══════════════════════════════════════════════════════════
# Step 1.1: python3.10-pip 설치 (apt)
# ═══════════════════════════════════════════════════════════
sudo apt update
sudo apt install -y python3-pip python3.10-venv

# ═══════════════════════════════════════════════════════════
# Step 1.2: conda 영향 배제하고 순수 시스템 환경 확보
# ═══════════════════════════════════════════════════════════
export PATH=$(echo $PATH | tr ':' '\n' | grep -v miniforge | tr '\n' ':' | sed 's/:$//')
# conda auto-activate 방지
conda config --set auto_activate_base false 2>/dev/null || true

# ═══════════════════════════════════════════════════════════
# Step 1.3: venv 생성 (프로젝트 workspace 안에 배치)
# ═══════════════════════════════════════════════════════════
cd ~/UnrealRobotics
python3.10 -m venv .venv-ros-bridge
source .venv-ros-bridge/bin/activate

# 확인
which python        # 반드시 ~/UnrealRobotics/.venv-ros-bridge/bin/python
python --version    # 3.10.12

# ═══════════════════════════════════════════════════════════
# Step 1.4: pip 업그레이드 + LeRobot editable install
# ═══════════════════════════════════════════════════════════
pip install --upgrade pip setuptools wheel

# LeRobot은 이미 ~/lerobot에 클론되어 있음 (conda env와 공유)
# editable 설치이므로 같은 소스를 양쪽 env에서 사용 가능
cd ~/lerobot
pip install -e ".[feetech]"

# ═══════════════════════════════════════════════════════════
# Step 1.5: rclpy + lerobot 동시 import 검증 (핵심!)
# ═══════════════════════════════════════════════════════════
cd ~/UnrealRobotics
source /opt/ros/humble/setup.bash
# venv는 이미 activate 상태여야 함 (which python으로 재확인)
which python

python <<'EOF'
import sys
print("Python:", sys.version)
print("Prefix:", sys.prefix)

import rclpy
print("rclpy OK:", rclpy.__file__)

from lerobot.robots.so_follower import SO101Follower, SO101FollowerConfig
print("SO101Follower OK")

from lerobot.teleoperators.so_leader import SOLeader, SO101LeaderConfig
print("SOLeader OK")

# Bridge 노드에서 쓸 메시지 타입도 미리 검증
from sensor_msgs.msg import JointState
print("JointState OK:", JointState)

print("\n=== All imports successful. Ready for Phase 3.3 bridge node. ===")
EOF

 

Step 1.4 진행 중 " ERROR: Package 'lerobot' requires a different Python: 3.10.12 not in '>=3.12'" 에러가 발생하여 다른 방법을 사용해야 한다. rclpy는 3.10, lerobot은 3.12 이상 버전을 필수로 한다...

 

 

1단계 대안: 두 개의 프로세스 + ZeroMQ 방안

기존 계획을 변경한다. single-process rclpy+LeRobot 래핑 → two-process + ZeroMQ IPC

더보기

┌─────────────────────────┐               ┌──────────────────────────┐
│ lerobot_worker.py                                   │               │ ros_bridge_node.py                                  │
│ (conda env, Python 3.12)                       │<─IPC─>│ (system py3.10 + rclpy)                            │
│ - LeRobot API 호출                                 │               │ - rclpy publish/subscribe                           │
│ - /dev/ttyACM* 점유                                 │               │ - ROS2 <-> IPC 번역                                │
└─────────────────────────┘               └──────────────────────────┘

위에서 만든 .venv-ros-bridge venv는 그대로 살려둔다.

pyzmq는 두 곳에 설치한다.

- conda env lerobot에 pip install pyzmq

- venv .venv-ros-bridge에 pip install pyzmq

 

 

1-A. venv (.venv-ros-bridge)에 pyzmq + rclpy 공존 검증

# 깨끗한 환경 확보 — conda PATH 제거
export PATH=$(echo $PATH | tr ':' '\n' | grep -v miniforge | tr '\n' ':' | sed 's/:$//')

# venv 활성화
cd ~/UnrealRobotics
source .venv-ros-bridge/bin/activate

# 확인
which python   # ~/UnrealRobotics/.venv-ros-bridge/bin/python
python --version   # 3.10.12

# pyzmq 설치
pip install pyzmq

# ROS2 source + rclpy + pyzmq 동시 import 검증
source /opt/ros/humble/setup.bash
pip install numpy #python 실행 전 numpy 설치

python <<'EOF'
import sys
print("Python:", sys.version.split()[0])
print("Prefix:", sys.prefix)

import rclpy
print("rclpy OK:", rclpy.__file__)

import zmq
print("pyzmq OK:", zmq.__version__)

from sensor_msgs.msg import JointState
print("JointState OK")

# ZMQ 기본 socket 생성 smoke test
ctx = zmq.Context()
pub = ctx.socket(zmq.PUB)
pub.bind("tcp://127.0.0.1:5555")
pub.close()
ctx.term()
print("ZMQ socket smoke test OK")

print("\n=== venv side: rclpy + pyzmq coexistence verified ===")
EOF

deactivate

ok로 잘 나오면 성공.

 

 

1-B. conda env (lerobot)에 pyzmq 추가 + LeRobot 공존 검증

# conda 활성화 (새 터미널 열었다면 source 필요할 수 있음)
source ~/miniforge3/etc/profile.d/conda.sh
conda activate lerobot

which python   # ~/miniforge3/envs/lerobot/bin/python
python --version   # 3.12.13

# pyzmq 설치
pip install pyzmq

# LeRobot + pyzmq 공존 import 검증
python <<'EOF'
import sys
print("Python:", sys.version.split()[0])
print("Prefix:", sys.prefix)

import zmq
print("pyzmq OK:", zmq.__version__)

from lerobot.robots.so_follower import SO101Follower, SO101FollowerConfig
print("SO101Follower OK")

from lerobot.teleoperators.so_leader import SOLeader, SO101LeaderConfig
print("SOLeader OK")

# ZMQ 기본 socket smoke test (bridge 쪽과 port 겹치지 않게 5557 사용)
ctx = zmq.Context()
pub = ctx.socket(zmq.PUB)
pub.bind("tcp://127.0.0.1:5557")
pub.close()
ctx.term()
print("ZMQ socket smoke test OK")

print("\n=== worker side: lerobot + pyzmq coexistence verified ===")
EOF

conda deactivate

OK가 잘 나오면 성공.

 

 

1-C. Cross-process ZMQ loopback 검증 (두 env 간 실제 메시지 전달)

두 env의 pyzmq 버전이 호환되는지, 그리고 localhost TCP로 데이터가 오가는지 실제로 확인한다. 두 터미널이 필요하다.

 

 

터미널 1 (worker 역할, conda env):

source ~/miniforge3/etc/profile.d/conda.sh
conda activate lerobot

python <<'EOF'
import zmq, time, json
ctx = zmq.Context()
pub = ctx.socket(zmq.PUB)
pub.bind("tcp://127.0.0.1:5555")
print("PUB bound on 5555. Sending 20 messages at 10Hz...")
time.sleep(0.5)  # SUB이 연결할 시간
for i in range(20):
    msg = {"seq": i, "ts": time.time(), "dummy_joint_pos": -5.89 + i * 0.1}
    pub.send_string(json.dumps(msg))
    print(f"  sent: {msg}")
    time.sleep(0.1)
pub.close()
ctx.term()
print("PUB done.")
EOF

 

 

터미널 2 (bridge 역할, venv + ROS2 source) — 터미널 1 실행 직전에 먼저 SUB을 띄워놓아야 초기 메시지를 놓치지 않는다.

export PATH=$(echo $PATH | tr ':' '\n' | grep -v miniforge | tr '\n' ':' | sed 's/:$//')
cd ~/UnrealRobotics
source .venv-ros-bridge/bin/activate
source /opt/ros/humble/setup.bash

python <<'EOF'
import zmq, json
ctx = zmq.Context()
sub = ctx.socket(zmq.SUB)
sub.connect("tcp://127.0.0.1:5555")
sub.setsockopt_string(zmq.SUBSCRIBE, "")  # 모든 메시지 구독
sub.setsockopt(zmq.RCVTIMEO, 5000)  # 5초 idle이면 종료
print("SUB connected to 5555. Waiting for messages...")

count = 0
try:
    while True:
        raw = sub.recv_string()
        msg = json.loads(raw)
        print(f"  recv: {msg}")
        count += 1
except zmq.Again:
    pass
print(f"\nTotal received: {count}")
sub.close()
ctx.term()
EOF

 

 

총 17개의 수신으로 성공적으로 동작하는 것을 확인할 수 있었다. ZMQ PUB/SUB 정상 동작 확인.

 

 


 

 

2단계: 파일 구조 생성

이제 실제 코드를 작성하며 두 파일을 만들어야 한다.

 

  1. lerobot_worker.py — conda env에서 실행, LeRobot API + ZMQ PUB/REP
  2. ros_bridge_node.py — venv + ROS2에서 실행, ZMQ SUB/REQ + rclpy

 

 

파일 디렉토리 계획

더보기

~/UnrealRobotics/src/lerobot_ros2_bridge/
├── package.xml
├── setup.py
├── setup.cfg
├── resource/
│   └── lerobot_ros2_bridge
├── lerobot_ros2_bridge/
│   ├── __init__.py
│   └── ros_bridge_node.py      ← rclpy 노드 (venv에서 실행)
└── scripts/
    └── lerobot_worker.py        ← LeRobot + ZMQ (conda에서 실행)

 

 

파일 생성

# 아래 구조를 수동 생성:
mkdir -p ~/UnrealRobotics/src/lerobot_ros2_bridge/{lerobot_ros2_bridge,scripts,resource}
touch ~/UnrealRobotics/src/lerobot_ros2_bridge/resource/lerobot_ros2_bridge
touch ~/UnrealRobotics/src/lerobot_ros2_bridge/lerobot_ros2_bridge/__init__.py
# 그 다음 5개 파일을 맞게 배치

files.zip
0.01MB

5개 파일을 디렉토리 계획에 맞게 배치한다.

 

 

# 2. ROS2 빌드 (miniforge PATH 제거 필수!)
export PATH=$(echo $PATH | tr ':' '\n' | grep -v miniforge | tr '\n' ':' | sed 's/:$//')
source /opt/ros/humble/setup.bash
cd ~/UnrealRobotics
colcon build --packages-select lerobot_ros2_bridge --symlink-install
source install/setup.bash

# 3. 빌드 성공 확인
ros2 pkg list | grep lerobot

빌드 성공과 패키지 등록이 확인되면 성공.

 

 


 

 

3단계: 로봇 없이 end-to-end 파이프 검증

실물 로봇 연결 전에 dummy 데이터로 전체 파이프라인을 테스트한다. worker를 가짜 joint 값으로 돌려서 ZMQ → ROS2 → ros2 topic echo까지 데이터가 흐르는지 확인.

 

 

터미널이 3개 필요하다.

터미널 1 — dummy worker (conda env):

source ~/miniforge3/etc/profile.d/conda.sh
conda activate lerobot

python3 <<'WORKER_EOF'
import zmq, json, time, math, signal, sys

ctx = zmq.Context()
pub = ctx.socket(zmq.PUB)
pub.bind("tcp://127.0.0.1:5555")

rep = ctx.socket(zmq.REP)
rep.bind("tcp://127.0.0.1:5556")

poller = zmq.Poller()
poller.register(rep, zmq.POLLIN)

print("[dummy-worker] PUB on 5555, REP on 5556. Sending fake joints at 30Hz...")

joints = ["shoulder_pan","shoulder_lift","elbow_flex","wrist_flex","wrist_roll","gripper"]
seq = 0
running = True
signal.signal(signal.SIGINT, lambda *a: sys.exit(0))

while running:
    t = time.time()
    # Sine wave in degrees (±30°) so values look realistic
    pos = {j: 30.0 * math.sin(t + i * 0.5) for i, j in enumerate(joints)}
    msg = {"seq": seq, "ts": t, "follower": pos, "leader": pos}
    pub.send_string(json.dumps(msg))
    seq += 1

    # Check commands (non-blocking)
    evts = dict(poller.poll(timeout=0))
    if rep in evts:
        raw = rep.recv_string()
        print(f"  cmd recv: {raw[:80]}")
        rep.send_string(json.dumps({"status": "ok"}))

    time.sleep(1/30)
WORKER_EOF

 

 

터미널 2 — bridge node (venv + ROS2):  에러 터미널3 에러로 아래 다시 작성

export PATH=$(echo $PATH | tr ':' '\n' | grep -v miniforge | tr '\n' ':' | sed 's/:$//')
cd ~/UnrealRobotics
source .venv-ros-bridge/bin/activate
source /opt/ros/humble/setup.bash
source install/setup.bash

ros2 run lerobot_ros2_bridge bridge_node

 

 

터미널 3 — 검증 (ROS2):

export PATH=$(echo $PATH | tr ':' '\n' | grep -v miniforge | tr '\n' ':' | sed 's/:$//')
source /opt/ros/humble/setup.bash

# joint_states 토픽이 보이는지 확인
ros2 topic list | grep joint

# 데이터 흐름 확인 (5개만 보고 종료)
ros2 topic echo /joint_states --once

 

 

*터미널2에서 오류 발생 "ModuleNotFoundError: No module named 'zmq'"

venv의 pyzmq가 ros2 run에서 보이지 않는다고 한다. ros2 run은 venv의 site-packages를 자동으로 참조하지 않기 때문.

export PATH=$(echo $PATH | tr ':' '\n' | grep -v miniforge | tr '\n' ':' | sed 's/:$//')
sudo pip3 install pyzmq numpy --break-system-packages 2>/dev/null || sudo pip3 install pyzmq numpy

-> 수정 후 터미널2에서 메시지 개수가 늘어나는 것 확인.

 

 

*터미널3에서 아무것도 출력되지 않는 오류 발생. FastDDS discovery가 WSL2 mirrored mode에서 완전히 막혀있는 상황. ros2 topic list 자체가 hang하는 건 daemon과의 통신도 안 되는 것.

# 터미널 3에서 먼저 테스트
export PATH=$(echo $PATH | tr ':' '\n' | grep -v miniforge | tr '\n' ':' | sed 's/:$//')
source /opt/ros/humble/setup.bash

# CycloneDDS 설치 (이미 있을 수도 있음)
sudo apt install -y ros-humble-rmw-cyclonedds-cpp

# RMW 전환
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export ROS_LOCALHOST_ONLY=1

# daemon 재시작 (중요!)
ros2 daemon stop
ros2 daemon start

# 테스트
ros2 topic list

/rosout 등을 출력하면 성공.

 

 

터미널2 - (bridge node)

export PATH=$(echo $PATH | tr ':' '\n' | grep -v miniforge | tr '\n' ':' | sed 's/:$//')
cd ~/UnrealRobotics
source .venv-ros-bridge/bin/activate
source /opt/ros/humble/setup.bash
source install/setup.bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export ROS_LOCALHOST_ONLY=1
ros2 run lerobot_ros2_bridge bridge_node

 

 

터미널3 - 출력

ros2 topic list
ros2 topic echo /joint_states --once

맨 오른쪽 터미널3에서 6개의 joint 이름, position 값이 잘 표시되는 것을 확인했다!

결론적으로 ROS2 노드 간 DDS discovery는 CycloneDDS가 필요하다고 결정되었다.

 

 

전체 데이터 흐름

dummy worker (ZMQ PUB) → bridge node (ZMQ SUB → rclpy publish) → /joint_states (ROS2 topic)

 

 


 

 

4단계: robot_state_publisher 연동

bridge node가 퍼블리시하는 /joint_states를 robot_state_publisher가 받아서 TF 트리를 생성하면, RViz에서 실시간으로 로봇 모델이 움직이는 걸 볼 수 있다. 이미 URDF + display.launch.py가 검증됐으니 바로 연결 가능하다.

 

 

터미널1, 2가 계속 돌아가는 상황에서

터미널4 - robot_state_publisher

export PATH=$(echo $PATH | tr ':' '\n' | grep -v miniforge | tr '\n' ':' | sed 's/:$//')
source /opt/ros/humble/setup.bash
source ~/UnrealRobotics/install/setup.bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export ROS_LOCALHOST_ONLY=1

# xacro → URDF 변환 후 robot_state_publisher 실행
xacro ~/UnrealRobotics/src/so101_description/urdf/so101_arm.urdf.xacro variant:=follower > /tmp/so101_follower.urdf

ros2 run robot_state_publisher robot_state_publisher --ros-args -p robot_description:="$(cat /tmp/so101_follower.urdf)"

 

*URDF 내용이 너무 길어서 커맨드라인 인자로 전달이 안되는 오류 발생

# 파일로 파라미터 전달
cat > /tmp/rsp_params.yaml << 'YAML'
robot_state_publisher:
  ros__parameters:
    robot_description: ""
YAML

# URDF 내용을 yaml에 삽입
python3 -c "
import yaml
with open('/tmp/so101_follower.urdf') as f:
    urdf = f.read()
params = {'robot_state_publisher': {'ros__parameters': {'robot_description': urdf}}}
with open('/tmp/rsp_params.yaml', 'w') as f:
    yaml.dump(params, f, default_flow_style=False)
print('Done')
"

export PATH=$(echo $PATH | tr ':' '\n' | grep -v miniforge | tr '\n' ':' | sed 's/:$//')
source /opt/ros/humble/setup.bash
source ~/UnrealRobotics/install/setup.bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export ROS_LOCALHOST_ONLY=1

ros2 run robot_state_publisher robot_state_publisher --ros-args --params-file /tmp/rsp_params.yaml

링크 출력 확인.

 

 

터미널5 - rviz2

export PATH=$(echo $PATH | tr ':' '\n' | grep -v miniforge | tr '\n' ':' | sed 's/:$//')
source /opt/ros/humble/setup.bash
source ~/UnrealRobotics/install/setup.bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export ROS_LOCALHOST_ONLY=1
rviz2

 

 

rviz2 화면에서 [Add] - RobotModel로 로봇을 추가하고,

[Global Options] - [Fixed Frame] - base_link로 설정하고,

[RobotModel] - [Description Topic] 오른쪽이 비어있다면 /robot_description을 직접 지정해야 한다.

로봇이 원을 그리듯 움직이면 성공.

 

 


 

 

*터미널1~5 접은글로 전체 코드 정리

더보기

터미널1 (dummy worker)

source ~/miniforge3/etc/profile.d/conda.sh
conda activate lerobot

python3 <<'WORKER_EOF'
import zmq, json, time, math, signal, sys

ctx = zmq.Context()
pub = ctx.socket(zmq.PUB)
pub.bind("tcp://127.0.0.1:5555")

rep = ctx.socket(zmq.REP)
rep.bind("tcp://127.0.0.1:5556")

poller = zmq.Poller()
poller.register(rep, zmq.POLLIN)

print("[dummy-worker] PUB on 5555, REP on 5556. Sending fake joints at 30Hz...")

joints = ["shoulder_pan","shoulder_lift","elbow_flex","wrist_flex","wrist_roll","gripper"]
seq = 0
signal.signal(signal.SIGINT, lambda *a: sys.exit(0))

while True:
    t = time.time()
    pos = {j: 30.0 * math.sin(t + i * 0.5) for i, j in enumerate(joints)}
    msg = {"seq": seq, "ts": t, "follower": pos, "leader": pos}
    pub.send_string(json.dumps(msg))
    seq += 1
    evts = dict(poller.poll(timeout=0))
    if rep in evts:
        raw = rep.recv_string()
        rep.send_string(json.dumps({"status": "ok"}))
    time.sleep(1/30)
WORKER_EOF

 

터미널 2 (bridge node):

export PATH=$(echo $PATH | tr ':' '\n' | grep -v miniforge | tr '\n' ':' | sed 's/:$//')
cd ~/UnrealRobotics
source /opt/ros/humble/setup.bash
source install/setup.bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export ROS_LOCALHOST_ONLY=1
ros2 run lerobot_ros2_bridge bridge_node

 

터미널 3 (robot_state_publisher):

export PATH=$(echo $PATH | tr ':' '\n' | grep -v miniforge | tr '\n' ':' | sed 's/:$//')
source /opt/ros/humble/setup.bash
source ~/UnrealRobotics/install/setup.bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export ROS_LOCALHOST_ONLY=1
ros2 run robot_state_publisher robot_state_publisher --ros-args --params-file /tmp/rsp_params.yaml

 

터미널 4 (검증):

export PATH=$(echo $PATH | tr ':' '\n' | grep -v miniforge | tr '\n' ':' | sed 's/:$//')
source /opt/ros/humble/setup.bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export ROS_LOCALHOST_ONLY=1
ros2 node list
ros2 topic list
ros2 topic echo /joint_states --once

 

터미널 5 (RViz):

export PATH=$(echo $PATH | tr ':' '\n' | grep -v miniforge | tr '\n' ':' | sed 's/:$//')
source /opt/ros/humble/setup.bash
source ~/UnrealRobotics/install/setup.bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export ROS_LOCALHOST_ONLY=1
rviz2

 

 

완성된 부분

  • 환경 조사 (rclpy 3.10 vs lerobot 3.12 비호환 확인)
  • Two-process + ZeroMQ IPC 아키텍처 결정 ( LeRobot ≥0.5.2는 Python ≥3.12 요구 → rclpy(3.10)와 한 프로세스 공존 불가 → two-process ZMQ 구조 채택)
  • pyzmq 양쪽 env 설치 + cross-process 검증
  • lerobot_ros2_bridge ROS2 패키지 생성
  • CycloneDDS 필수 발견 및 적용 ( ROS2 노드 간 DDS discovery에 FastDDS가 WSL2 mirrored mode에서 동작 안 함. export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp + export ROS_LOCALHOST_ONLY=1 필수. (CycloneDDS 불필요 기술은 rosbridge WebSocket 경로에만 해당)
  • dummy worker → ZMQ → bridge node → /joint_states → robot_state_publisher → TF → RViz 3D 모델 실시간 움직임

 

 


 

 

 

728x90
반응형