Guides(으)로 돌아가기

스페이스마우스 텔레오퍼레이션 가이드: 로봇 학습의 설정, 캘리브레이션 및 데이터 수집

로봇 원격 조작을 위해 3D 연결 스페이스마우스를 사용하는 방법: ROS2 드라이버 설정, 축 지도, 죽은 영역 캘리브레이션, 그리고 모방 학습을 위해 HDF5 시범을 녹음. OpenArm 및 ViperX 호환성.

[← 가이드]

3Dconnexion SpaceMouse를 6DOF 로봇 텔레오퍼레이션에 사용하는 방법 드라이버 설치, 축 지도, 죽은 구역 캘리브레이션 및 HDF5 형식으로 모방 학습을 위한 시범 촬영.

왜 스페이스마우스가 원격 조작을 위해?

3Dconnexion SpaceMouse는 6DOF 입력 장치로 손 동작을 동시 번역 (X, Y, Z) 및 회전 (roll, pitch, yaw) 명령으로 변환합니다. 로봇 텔레오퍼레이션에 있어서, 그것은 특유의 장점을 제공합니다:

  • 6-DOF 한 손: 단일 스페이스마우스는 로봇 최종 효과자의 모든 6 가지 자유도를 동시에 제어합니다. 모드 스위치, 버튼 조합이 없습니다. 이것은 대부분의 운영자에게 가장 직관적인 단일 장치 텔레오퍼레이션 인터페이스로 만듭니다.
  • ** 입구 비용 낮:** SpaceMouse Compact ($129) 은 10-100배 더 많은 시스템과 같은 6DOF 입력량을 제공합니다. 리더-따라자 팔 ($2,000-5,000), VR 헤드셋 ($500++ 컨트롤러), 촉각 장치 ($3,000+) 와 비교하십시오.
  • ** 연구에서 널리 사용된다:** SpaceMouse 텔레오퍼레이션은 robomimic, MimicGen, 그리고 robosuite 코드베이스에서 사용되며, DROID 데이터 세트 (500K+ 에피소드) 의 기본 텔레오퍼레이션 방법이다. [VLA 미세 조정] (T14) 를 위해 데이터를 수집하는 경우, SpaceMouse 데이터는 기존 사전 훈련 데이터 세트와 가장 호환적입니다.
  • ** 기밀 유도:** 방 규모 추적을 필요로 하는 VR 컨트롤러와 달리, SpaceMouse는 방출되면 중앙으로 돌아가는 USB 장치입니다. IMU 유도, 추적 손실, 세션 사이 재 기밀이 없습니다.
  • 운동자 피로감 낮: 기기는 책상 위에 앉아 있다.운동자의 손이 자연스럽게 버튼에 있다. VR 컨트롤러를 팔높이에 들고 있거나 리더 팔을 물리적으로 움직일 때, 스페이스마우스 텔레오퍼레이션은 긴 데이터 수집 세션에 있어서 피로감이 훨씬 적습니다.

Tradeoffs: SpaceMouse는 힘 피드백을 제공하지 않습니다 (운전자는 로봇이 무엇을 만지는 것을 느낄 수 없습니다) 그리고 리더-후보 팔의 위치 직관성이 부족합니다 ( 리더 팔이 로봇의 자세를 반영합니다). 미세한 힘 제어 (insertion, polishing) 또는 복잡한 양동 조정이 필요한 작업에 있어서 리더-후보 팔은 더 높은 품질의 시범을 제공합니다. 일반 픽앤플레이, 객체 재편, 그리고 대부분의 테이블톱 조작 작업에 있어서 SpaceMouse는 비용과 품질의 가장 좋은 타협입니다.

2· 하드웨어 설정

스페이스마우스 모델

Model Price DOF Buttons Connection Recommendation
SpaceMouse Compact $129 6 2 USB Best value. Use left button for gripper toggle, right for episode save.
SpaceMouse Pro $299 6 15 USB Extra buttons useful for multi-function teleop (speed presets, mode switching).
SpaceMouse Pro Wireless $399 6 15 USB/BT Wireless adds 2-5ms latency. USB mode recommended for data collection.
SpaceMouse Enterprise $529 6 31 USB Overkill for teleoperation. The extra buttons go unused.

** 우리의 추천:** 단일 팔의 텔레오퍼레이션에 대한 SpaceMouse Compact ($129); 쌍방향 설정에 대한 두 개의 SpaceMouse Compact ($258 총) 을 제공합니다. 프로는 4 시간 이상의 세션에 편안함을 추가하지만 대부분의 팀에 대한 가격의 2배가 되지 않습니다. RCSV는 모든 컬렉션 스테이션에서 SpaceMouse Compact 단위를 사용합니다.

신체적 설정

  • 스페이스마우스를 테이블 높이에서 안정적인 표면에 배치하고, 바로 운영자의 앞에
  • 로봇 팔을 운영자에게 가시하게 배치 (카메라만 보는 것보다 직선 시야를 선호한다)
  • USB를 통해 연결하십시오. USB 허브를 피하십시오.
  • 리눅스에서 장치가 T10로 표시되는 것을 확인합니다. T11은 하나 이상의 장치가 표시되어야 합니다.

3. ROS2 드라이버 설치

T12 라이브러리는 모든 3D 연결 장치에 순수한 파이썬 인터페이스를 제공합니다. 우리는 로봇 제어 스택과 통합하기 위해 ROS2 노드에 포장합니다.

단계 1: 피스파크마우스 설치

# Install the library and its hidapi dependency
pip install pyspacemouse hidapi

# On Ubuntu, you also need the udev rules for non-root access
sudo tee /etc/udev/rules.d/99-spacemouse.rules <<'EOF'
SUBSYSTEM=="usb", ATTR{idVendor}=="256f", MODE="0666"
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", MODE="0666"
EOF
sudo udevadm control --reload-rules && sudo udevadm trigger

# Verify the device is detected
python3 -c "import pyspacemouse; print(pyspacemouse.list_devices())"

단계 2: ROS2 텔레오퍼레이션 노드를 생성

#!/usr/bin/env python3
"""spacemouse_teleop_node.py - ROS2 node for SpaceMouse teleoperation."""
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
import pyspacemouse
import numpy as np

class SpaceMouseTeleop(Node):
    def __init__(self):
        super().__init__('spacemouse_teleop')

        # Parameters (tune these per-robot)
        self.declare_parameter('linear_scale', 0.15)   # m/s per unit
        self.declare_parameter('angular_scale', 0.3)    # rad/s per unit
        self.declare_parameter('dead_zone', 0.08)       # ignore below this
        self.declare_parameter('publish_rate', 50.0)     # Hz

        self.linear_scale = self.get_parameter('linear_scale').value
        self.angular_scale = self.get_parameter('angular_scale').value
        self.dead_zone = self.get_parameter('dead_zone').value
        rate = self.get_parameter('publish_rate').value

        # Publisher
        self.pub = self.create_publisher(Twist, 'spacemouse/twist', 10)
        self.timer = self.create_timer(1.0 / rate, self.timer_callback)

        # Open device
        success = pyspacemouse.open()
        if not success:
            self.get_logger().error('Failed to open SpaceMouse device')
            raise RuntimeError('SpaceMouse not found')
        self.get_logger().info('SpaceMouse connected successfully')

    def apply_dead_zone(self, value):
        """Apply dead zone and normalize."""
        if abs(value) < self.dead_zone:
            return 0.0
        sign = 1.0 if value > 0 else -1.0
        return sign * (abs(value) - self.dead_zone) / (1.0 - self.dead_zone)

    def timer_callback(self):
        state = pyspacemouse.read()

        msg = Twist()
        # SpaceMouse axes -> robot EEF frame
        # NOTE: axis mapping depends on your robot's base frame convention.
        # These defaults work for OpenArm 1 and ViperX with Z-up, X-forward.
        msg.linear.x = self.apply_dead_zone(state.y) * self.linear_scale
        msg.linear.y = self.apply_dead_zone(state.x) * -self.linear_scale
        msg.linear.z = self.apply_dead_zone(state.z) * self.linear_scale
        msg.angular.x = self.apply_dead_zone(state.roll) * self.angular_scale
        msg.angular.y = self.apply_dead_zone(state.pitch) * self.angular_scale
        msg.angular.z = self.apply_dead_zone(state.yaw) * self.angular_scale

        self.pub.publish(msg)

def main():
    rclpy.init()
    node = SpaceMouseTeleop()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

3단계: 발사

# Terminal 1: Start the SpaceMouse node
ros2 run your_package spacemouse_teleop_node \
  --ros-args -p linear_scale:=0.15 -p angular_scale:=0.3 -p dead_zone:=0.08

# Terminal 2: Verify output
ros2 topic echo /spacemouse/twist

4. 축 지도 및 구성

스페이스마우스는 자신의 좌표 프레임에서 6개의 축 값을 내보내는데, 로봇은 다른 프레임을 사용합니다. 그 사이의 지도는 로봇이 기본 좌표 시스템을 어떻게 정의하느냐에 달려 있습니다.

공통축 지도

SpaceMouse Axis OpenArm 1 ViperX-300 / ALOHA Franka Research 3
X (push/pull) +Y (forward) +X (forward) +X (forward)
Y (left/right) -X (right) -Y (right) +Y (left)
Z (up/down) +Z (up) +Z (up) +Z (up)
Roll (tilt left/right) +Roll +Roll +Roll
Pitch (tilt fwd/back) +Pitch +Pitch -Pitch
Yaw (twist) +Yaw +Yaw +Yaw

** 지도를 어떻게 결정하는지를:** 로봇을 관찰하는 동안 스페이스마우스 버튼을 한 번에 한 방향으로 이동하십시오. 로봇이 잘못된 방향으로 움직이면 그 축을 부정하십시오. 잘못된 축으로 움직이면, 축을 교환하십시오. 이 과정은 5-10 분 동안 수행되며 로봇 구성에 한 번만 수행해야합니다.

5· 죽은 구역 조율 및 감수성

스페이스마우스는 매우 민감합니다. 죽은 구역 필터링이 없으면, 로봇은 운영자의 손이 버튼에 누워있을 때 이동합니다. 죽은 구역은 텔레오퍼레이션 데이터 품질에 가장 중요한 캘리브레이션 매개 변수입니다.

# spacemouse_config.yaml - Per-axis configuration
spacemouse_teleop:
  ros__parameters:
    # Dead zone: ignore input below this threshold (0.0-1.0)
    # Higher = less drift, but less responsive to small movements
    dead_zone_translation: 0.08   # Good default for most operators
    dead_zone_rotation: 0.12      # Rotation is more sensitive; higher DZ needed

    # Scaling: how fast the robot moves per unit of SpaceMouse input
    # Start conservative (0.1), increase as operator gains confidence
    linear_scale_x: 0.15    # m/s - forward/backward
    linear_scale_y: 0.15    # m/s - left/right
    linear_scale_z: 0.10    # m/s - up/down (slower to prevent table crashes)
    angular_scale_roll: 0.25   # rad/s
    angular_scale_pitch: 0.25  # rad/s
    angular_scale_yaw: 0.30    # rad/s - wrist yaw is the most-used rotation

    # Smoothing: exponential moving average (0.0 = no smoothing, 0.95 = very smooth)
    # Smoothing reduces jitter but adds latency. 0.3-0.5 is a good range.
    smoothing_factor: 0.4

조정 절차:

  1. 죽은 구역을 0.15 (보유) 로 설정하세요. 버튼을 풀어주세요. 로봇이 여전히 움직이지 않으면, 죽은 구역을 늘려주세요. 그렇지 않으면, 움직이지 않는 최소 값을 찾을 때까지 0.02 단계로 줄여주세요.
  2. 선형 스케일을 0.10로 설정하세요. 로봇을 10cm로 움직여보도록 노력하세요. 1초 이상 걸린다면, 증가하세요. 로봇이 제어할 수 없을 정도로 빠르게 움직인다면 감소하세요. 목표: 10cm의 움직임에 0.5-1.0초.
  3. 각도 스케일을 0.20로 설정하세요. 최종 효과자를 45도로 돌려보도록 시도하세요. 선형 스케일과 같은 조율 논리입니다.
  4. 가벼운 움직임이 0.4로 설정해 보세요.

6. OpenArm 1에 통합

[OpenArm 1]T15) 는 CAN-bus 인터페이스의 다미아오 액추이터를 사용합니다. 스페이스마우스 트윈트 명령어는 최종 효과자 속도 목표물로 변환되며, 역 운동학 (IK) 용기가 공동 속도 명령으로 변환합니다.

# openarm_spacemouse_teleop.py - Tested configuration for OpenArm 1
# Requires: openarm_sdk, pyspacemouse, numpy

import numpy as np
from openarm_sdk import OpenArm
from openarm_sdk.ik import DampedLeastSquaresIK
import pyspacemouse

# OpenArm-specific settings (tested at RCSV San Francisco lab)
OPENARM_CONFIG = {
    'axis_map': {
        'x': ('y', 1.0),      # SpaceMouse Y -> OpenArm +X (forward)
        'y': ('x', -1.0),     # SpaceMouse X -> OpenArm -Y (right)
        'z': ('z', 1.0),      # SpaceMouse Z -> OpenArm +Z (up)
        'roll': ('roll', 1.0),
        'pitch': ('pitch', 1.0),
        'yaw': ('yaw', 1.0),
    },
    'linear_scale': 0.12,     # m/s - OpenArm is precise; keep speed moderate
    'angular_scale': 0.25,    # rad/s
    'dead_zone': 0.08,
    'ik_damping': 0.05,       # Damped least-squares regularization
    'control_rate': 50,       # Hz - matches OpenArm servo rate
}

arm = OpenArm(port='/dev/ttyUSB0')
ik = DampedLeastSquaresIK(arm.urdf_path, damping=OPENARM_CONFIG['ik_damping'])
pyspacemouse.open()

print("SpaceMouse teleop active. Left button = toggle gripper. Right button = e-stop.")

try:
    while True:
        state = pyspacemouse.read()

        # Apply dead zone and scaling
        twist = np.zeros(6)
        for i, axis in enumerate(['x', 'y', 'z', 'roll', 'pitch', 'yaw']):
            raw = getattr(state, OPENARM_CONFIG['axis_map'][axis][0])
            raw *= OPENARM_CONFIG['axis_map'][axis][1]
            if abs(raw) < OPENARM_CONFIG['dead_zone']:
                raw = 0.0
            scale = OPENARM_CONFIG['linear_scale'] if i < 3 else OPENARM_CONFIG['angular_scale']
            twist[i] = raw * scale

        # IK solve: twist -> joint velocities
        current_joints = arm.get_joint_positions()
        joint_velocities = ik.solve(current_joints, twist)
        arm.set_joint_velocities(joint_velocities)

        # Button handling
        if state.buttons[0]:  # Left button -> toggle gripper
            arm.toggle_gripper()
        if state.buttons[1]:  # Right button -> emergency stop
            arm.stop()
            break

except KeyboardInterrupt:
    arm.stop()

7. ViperX/ ALOHA 설정과 통합

ViperX-300 및 ALOHA 시스템에서는 SpaceMouse가 Interbotix SDK 또는 ALOHA 텔레오퍼레이션 스택을 통해 통합됩니다. ALOHA 코드베이스에는 이미 리더-후보 텔레오퍼레이션의 대안으로 SpaceMouse 지원이 포함되어 있습니다.

# Using the ALOHA codebase with SpaceMouse
cd ~/aloha
python3 scripts/teleop_spacemouse.py \
  --robot_config configs/viperx300s.yaml \
  --spacemouse_config configs/spacemouse_compact.yaml \
  --linear_scale 0.15 \
  --angular_scale 0.30 \
  --dead_zone 0.08

# For bimanual ALOHA with two SpaceMouse devices:
python3 scripts/teleop_spacemouse_bimanual.py \
  --left_device_id 0 \
  --right_device_id 1

두 개의 SpaceMouse 장치를 사용하다가 두 개의 수동 원격 이동을 위해, 물리적으로 라벨을 붙이고 (테이프, 스티커) 일관된 장치 ID를 할당하십시오. 장치 ID 할당은 USB 재 연결 중 변경될 수 있습니다.

# List connected SpaceMouse devices with serial numbers
python3 -c "
import pyspacemouse
devices = pyspacemouse.list_devices()
for d in devices:
    print(f'Name: {d.name}, Serial: {d.serial}, ID: {d.id}')
"

8\ HDF5에 시연을 녹음

녹음 루프는 동기화된 카메라 프레임, 공동 상태, 액션 및 언어 해설을 HDF5 파일로 캡처합니다.

#!/usr/bin/env python3
"""record_episode.py - Record a teleoperation episode to HDF5."""
import h5py
import numpy as np
import time
from datetime import datetime

class EpisodeRecorder:
    def __init__(self, arm, cameras, save_dir='./episodes'):
        self.arm = arm
        self.cameras = cameras  # dict of {name: camera_object}
        self.save_dir = save_dir
        self.recording = False
        self.episode_data = None

    def start_episode(self, task_description=""):
        """Begin recording a new episode."""
        self.recording = True
        self.episode_data = {
            'joint_positions': [],
            'joint_velocities': [],
            'eef_pos': [],
            'eef_quat': [],
            'gripper_state': [],
            'actions': [],          # The twist commands sent to the robot
            'timestamps': [],
            'task_description': task_description,
        }
        # Add camera frame lists
        for cam_name in self.cameras:
            self.episode_data[f'camera_{cam_name}'] = []
        print(f"Recording started: '{task_description}'")

    def record_step(self, action_twist):
        """Record one timestep of data."""
        if not self.recording:
            return
        t = time.time()
        self.episode_data['joint_positions'].append(self.arm.get_joint_positions())
        self.episode_data['joint_velocities'].append(self.arm.get_joint_velocities())
        self.episode_data['eef_pos'].append(self.arm.get_eef_position())
        self.episode_data['eef_quat'].append(self.arm.get_eef_quaternion())
        self.episode_data['gripper_state'].append(self.arm.get_gripper_state())
        self.episode_data['actions'].append(action_twist)
        self.episode_data['timestamps'].append(t)
        for cam_name, cam in self.cameras.items():
            self.episode_data[f'camera_{cam_name}'].append(cam.get_frame())

    def save_episode(self):
        """Save the recorded episode to HDF5."""
        if not self.recording:
            return None
        self.recording = False
        n_steps = len(self.episode_data['timestamps'])
        if n_steps < 10:
            print(f"Episode too short ({n_steps} steps). Discarding.")
            return None

        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        filepath = f"{self.save_dir}/episode_{timestamp}.hdf5"

        with h5py.File(filepath, 'w') as f:
            # Metadata
            f.attrs['task_description'] = self.episode_data['task_description']
            f.attrs['n_steps'] = n_steps
            f.attrs['timestamp'] = timestamp
            f.attrs['control_freq_hz'] = 50

            # State data
            f.create_dataset('joint_positions',
                data=np.array(self.episode_data['joint_positions'], dtype=np.float32))
            f.create_dataset('joint_velocities',
                data=np.array(self.episode_data['joint_velocities'], dtype=np.float32))
            f.create_dataset('eef_pos',
                data=np.array(self.episode_data['eef_pos'], dtype=np.float32))
            f.create_dataset('eef_quat',
                data=np.array(self.episode_data['eef_quat'], dtype=np.float32))
            f.create_dataset('gripper_state',
                data=np.array(self.episode_data['gripper_state'], dtype=np.float32))
            f.create_dataset('actions',
                data=np.array(self.episode_data['actions'], dtype=np.float32))
            f.create_dataset('timestamps',
                data=np.array(self.episode_data['timestamps'], dtype=np.float64))

            # Camera data (compressed)
            for cam_name in self.cameras:
                frames = np.array(self.episode_data[f'camera_{cam_name}'],
                                  dtype=np.uint8)
                f.create_dataset(f'camera_{cam_name}', data=frames,
                    compression='gzip', compression_opts=4)

        print(f"Saved episode: {filepath} ({n_steps} steps, "
              f"{n_steps/50:.1f}s)")
        return filepath

9· 데이터 품질 팁

고품질의 스페이스마우스 시범은 보다 원활한 정책을 만들어 더 잘 일반화합니다.

  • 녹음하기 전에 5분 동안 따뜻해 주세요. 세션의 첫 몇 번의 시연은 항상 후기보다 더 나쁘습니다.
  • ** 느리고 부드럽게 움직여.** 어색한 움직임은 잡음적인 행동 표지를 만듭니다. 빠른 움직임을 해야 한다면 먼저 버튼을 풀 (허속) 하고, 다시 부드럽게 움직입니다. 선택과 장소 작업에 15-30초의 에피소드를 목표로 합니다.
  • **** 에피소드 사이에는 객체 위치 ( 작업 공간 내에서), 객체 지향 및 로봇 시작 구성을 무작위로 조정한다. 이 변이는 일반화에서 에피소드 수보다 더 중요하다.
  • 일반적으로 재설정. 로봇을 에피소드 사이 고정된 시작 자세로 되돌리는 스크립트 재설정 사용. 일반적 재설정으로 인해 초기 상태 분포에서 잡음이 발생한다.
  • ** 즉시 해설.** 기록 전에 또는 기록 중에 작업 설명을 작성하고, 그 후에 작성하지 마십시오. 포스트하크 해설은 오류가 발생하고 수집 작업 흐름을 느리게 합니다.
  • ** 세션 길이는:** 회수 세션 을 2 시간 까지 중단 없이 제한 합니다. 2 시간 후, 운영자 피로 는 시범 품질을 측정 할 수 있게 저하 합니다 (RCSV 내부 기준에 따라 행동 은 20%~40% 증가 합니다).

10. RLDS/레로봇 형식으로 변환

HDF5 에피소드를 녹음한 후 RLDS 또는 LeRobot 형식으로 변환하여 VLA 훈련:

# Convert HDF5 episodes to LeRobot format (for SmolVLA, ACT training)
# Requires: pip install lerobot

from lerobot.common.datasets.push_dataset_to_hub import hdf5_to_lerobot

hdf5_to_lerobot(
    raw_dir="./episodes/",           # Directory of HDF5 files
    repo_id="your-org/your-dataset", # Hugging Face Hub repo
    fps=50,
    video=True,                      # Encode camera frames as mp4
    push_to_hub=True,                # Upload to Hugging Face Hub
)

# Convert HDF5 to RLDS format (for OpenVLA fine-tuning)
# Requires: pip install tensorflow-datasets

python3 -m rlds_tools.hdf5_to_rlds \
  --input_dir ./episodes/ \
  --output_dir ./rlds_dataset/ \
  --dataset_name my_spacemouse_data \
  --action_key actions \
  --state_key joint_positions

11· 공통 문제 및 해결 방법

스페이스마우스 드리프 (로봇이 버튼을 풀 때 움직인다)

유해: 죽은 구역이 너무 작거나 장치가 약간의 기계적 편향을 가지고 있습니다. ** 수정:** 죽은 구역을 0.10-0.15로 증가하십시오.

축 반전 (로봇은 반대 방향으로 움직인다)

** 원인:** 스페이스마우스 프레임과 로봇 프레임 사이에 축이 일치하지 않는 신호를 표시합니다. ** 수정:** 설정에서 침해하는 축을 부정합니다. 각 축을 한 번에 이동하고 방향을 확인합니다.

녹음 중에 USB 연결을 끊는다

** 원인:** USB 연결, USB 허브 전력 관리, 또는 OS 기기를 중지. ** 수정:**

# Disable USB autosuspend for 3Dconnexion devices
echo -1 | sudo tee /sys/bus/usb/devices/*/power/autosuspend_delay_ms

# Or permanently in /etc/udev/rules.d/99-spacemouse.rules:
ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="256f", \
  TEST=="power/autosuspend_delay_ms", \
  ATTR{power/autosuspend_delay_ms}="-1"

녹음에서 높은 액션 좆

** 원인:** 연소 요소가 너무 낮거나 운영자 경험이 부족합니다. ** 수정:** 연소 요소를 0.5-0.6로 증가시켜야 합니다.

스페이스마우스 2개의 기기가 교환되고 있습니다.

**유스버 장치의 명목 순서는 결정적이지 않습니다. 정치: 장치 식별을 위해 일련 번호를 사용하십시오. (부 7의 코드를 참조하십시오.) 일련 번호에 따라 일관된 증상 링크를 할당하는 udev 규칙을 생성하십시오.

관련 독서

  • [로봇 훈련 데이터를 수집하는 방법 (완전 안내서) ]
  • [전체 수술을 시작]
  • [중동전화기계 설치]
  • [VLA 모델 비교 2026]
  • [2026년 물리 인공지능]
  • [오픈아름 1]
  • [RCSV 데이터 서비스]

RCSV에서 텔레오퍼레이션 데이터를 수집

스페이스마우스 기구들은 이미 오픈아름, 바이퍼X, DK1 플랫폼에서 구성 및 캘리브레이션되어 있습니다. 훈련된 운영자, 표준화된 HDF5 출력, $2,500 파일럿.

데이터 서비스를 탐구 우리와 연락하세요