📷 相机与视觉 — 获取 RGB-D 图像¶
除了读取关节角度和 body 位姿,你还可以从仿真中获取图像——就像给机器人装上了眼睛。
OrcaGym 通过 WebSocket 从 OrcaStudio 获取实时渲染的相机画面。
快速开始:获取第一张图像¶
"""
first_camera.py — 获取仿真相机的第一张图像
"""
import time
import numpy as np
from orca_gym.sensor.rgbd_camera import CameraWrapper
# 1. 创建相机包装器
# name: 相机名称(任意)
# port: WebSocket 端口(在 OrcaStudio 中配置)
camera = CameraWrapper(name="front_camera", port=8765)
# 2. 启动相机流(后台线程自动接收和解码)
camera.start()
print("相机已启动,等待第一帧...")
# 3. 等待第一帧到达
while not camera.is_first_frame_received():
time.sleep(0.1)
print(f"✅ 收到第一帧!")
# 4. 获取图像
image = camera.image # NumPy 数组,形状 (H, W, 3),格式 BGR
print(f" 分辨率: {image.shape[1]}×{image.shape[0]}")
print(f" 数据类型: {image.dtype}")
print(f" 像素范围: [{image.min()}, {image.max()}]")
print(f" 帧序号: {camera.image_index}")
# 5. 保存图像(用 OpenCV)
import cv2
cv2.imwrite("first_frame.png", image)
print("✅ 图像已保存到 first_frame.png")
# 6. 停止相机
camera.stop()
在环境类中集成相机¶
将相机作为环境的一部分,每步获取图像:
"""
vision_env.py — 带相机观测的环境
"""
import time
import numpy as np
from gymnasium import spaces
from orca_gym.environment.euler.orca_gym_euler_env import OrcaGymEulerEnv
from orca_gym.sensor.rgbd_camera import CameraWrapper
class VisionEnv(OrcaGymEulerEnv):
"""在观测中加入相机图像的环境"""
def __init__(self, frame_skip, orcagym_addr, agent_names, time_step,
camera_port: int = 8765, **kwargs):
super().__init__(
frame_skip=frame_skip,
orcagym_addr=orcagym_addr,
agent_names=agent_names,
time_step=time_step,
**kwargs,
)
# ── 设置相机 ──
self._camera = CameraWrapper(name="agent_view", port=camera_port)
self._camera.start()
# 等待第一帧
print("等待相机就绪...")
while not self._camera.is_first_frame_received():
time.sleep(0.1)
print(f"✅ 相机就绪: {self._camera.image.shape}")
# 动作空间
self.action_space = spaces.Box(
low=-1.0, high=1.0, shape=(self.model.nu,), dtype=np.float32
)
obs_sample = self._get_obs()
self.observation_space = spaces.Dict({
key: spaces.Box(low=-np.inf, high=np.inf, shape=v.shape, dtype=v.dtype)
if isinstance(v, np.ndarray) and v.dtype != np.uint8
else spaces.Box(low=0, high=255, shape=v.shape, dtype=np.uint8)
for key, v in obs_sample.items()
})
def _get_obs(self):
"""
观测 = 本体感知(关节角度)+ 视觉(相机图像)
本体感知 (proprioception): 机器人"感觉"到的自身状态
视觉 (vision): 相机看到的画面
"""
return {
# 本体感知
"joint_pos": self.data.qpos.copy().astype(np.float32),
"joint_vel": self.data.qvel.copy().astype(np.float32),
# 视觉
"image": self._camera.image.copy(), # (H, W, 3) uint8
}
def step(self, action):
action = np.asarray(action, dtype=np.float32).reshape(self.model.nu)
self.do_simulation(action, self.frame_skip)
obs = self._get_obs()
reward = 0.0
terminated = False
truncated = False
return obs, reward, terminated, truncated, {}
def reset_model(self):
self.set_joint_qpos(self.init_qpos)
self.set_joint_qvel(self.init_qvel)
self.mj_forward()
self._sync_view()
return self._get_obs(), {}
def close(self):
self._camera.stop()
super().close()
显示相机画面¶
用 Matplotlib 实时显示:
import matplotlib.pyplot as plt
def show_camera_live(camera: CameraWrapper, duration: float = 10.0):
"""
实时显示相机画面(duration 秒)。
注意:这只是一个简单的显示示例。
实际 RL 训练中不需要这样逐帧显示——直接用 image 数组即可。
"""
plt.ion() # 交互模式
fig, ax = plt.subplots()
img_display = ax.imshow(np.zeros((480, 640, 3), dtype=np.uint8))
ax.set_title("Camera Feed")
ax.axis('off')
start = time.time()
while time.time() - start < duration:
frame = camera.image.copy()
# OpenCV 的 BGR → Matplotlib 的 RGB
frame_rgb = frame[..., ::-1]
img_display.set_data(frame_rgb)
fig.canvas.flush_events()
plt.pause(0.03) # ~30 FPS
plt.ioff()
plt.close()
多相机设置¶
需要多个视角?创建多个 CameraWrapper:
def setup_multi_camera():
"""同时启动多个相机"""
cameras = {
"front": CameraWrapper("front", port=8765),
"side": CameraWrapper("side", port=8766),
"top": CameraWrapper("top", port=8767),
}
# 全部启动
for cam in cameras.values():
cam.start()
# 等待所有相机就绪
for name, cam in cameras.items():
while not cam.is_first_frame_received():
time.sleep(0.1)
print(f"✅ {name}: {cam.image.shape}")
# 同步获取所有画面
def get_all_views():
return {name: cam.image.copy() for name, cam in cameras.items()}
return cameras, get_all_views
# 用法
cameras, get_views = setup_multi_camera()
views = get_views()
print(f"可用视角: {list(views.keys())}")
相机参数配置¶
在 OrcaStudio 中可以配置每个相机的参数:
| 参数 | 说明 | 典型值 |
|---|---|---|
| 分辨率 | 图像宽×高 | 640×480, 1280×720 |
| 帧率 | 每秒帧数 | 15, 30, 60 |
| RGB | 是否输出彩色图 | True |
| Depth | 是否输出深度图 | True(需要时) |
在 Python 侧通过 CameraSensorInfo 配置(需先创建 OrcaGymScene 实例):
from orca_gym.scene.orca_gym_scene import OrcaGymScene, CameraSensorInfo
# 先创建场景管理器
scene = OrcaGymScene(grpc_addr="localhost:50051")
# 配置某个相机的传感器参数
camera_config = CameraSensorInfo(
capture_rgb=True, # 输出 RGB 图像
capture_depth=True, # 输出深度图
save_mp4_file=False, # 不保存到文件
use_dds=False, # 不使用 DDS 压缩
)
scene.set_camera_sensor_info("camera_actor_name", camera_config)
性能建议¶
- 不要在主循环中
imshow— 图像显示很吃 CPU。训练时直接处理数组即可 - 缩小图像 — 如果不需要全分辨率,在
_get_obs()中 resize - 降低帧率 — 30 FPS 通常足够,更高的帧率浪费带宽
- 异步渲染 — 相机在后台线程解码,不阻塞主仿真线程
def _get_obs(self):
image = self._camera.image.copy()
# 缩小到 128×128 以减少计算量
small_image = cv2.resize(image, (128, 128))
return {..., "image": small_image}
下一步¶
现在你既能"感觉"(读状态),也能"看"(相机)。接下来学习如何精确地控制机器人:🎮 简单控制器。