🌍 Environment API¶
Gymnasium 环境接口,提供标准的强化学习环境抽象。
主要类¶
| 类 | 说明 |
|---|---|
OrcaGymEulerEnv |
推荐的环境基类 |
OrcaGymVectorEnv |
向量化并行环境 |
OrcaGymEulerEnv¶
OrcaGymEulerEnv 是你编写自定义环境的推荐基类。它封装了仿真核心,让你专注于任务逻辑。
构造参数¶
class OrcaGymEulerEnv:
def __init__(
self,
frame_skip: int, # 每次 step() 对应的物理仿真步数
orcagym_addr: str, # gRPC 服务端地址(如 "localhost:50051")
agent_names: list[str], # 智能体名称列表
time_step: float, # 物理仿真时间步长(秒)
*,
model_xml_path: str | None = None, # 本地 XML 路径(离线模式)
skip_grpc_load: bool = False, # True → 离线模式
render_mode: str = "human", # "human" / "none"
sync_render: bool = False,
**kwargs,
)
公共属性¶
data: OrcaGymDataView # 完整状态只读视图
model: OrcaGymModel # 模型结构信息
sim_config: SimConfig # 求解器参数配置
ctrl: np.ndarray # getter 返回当前 actuator_force;setter 设置控制输入
init_qpos: np.ndarray # 缓存的初始广义坐标
init_qvel: np.ndarray # 缓存的初始广义速度
frame_skip: int # step() 对应的物理步数
seed: int # 随机种子
@property
dt: float # 环境时间步长 = sim_config.timestep × frame_skip
仿真控制¶
核心步进方法。设置控制 → 步进 n_frames → 自动同步状态。调用后self.data 已为最新状态。ctrl 形状必须为 (nu,)。
set_ctrl 设置控制输入但不步进;mj_step / mj_forward 为低级仿真控制,通常不需要直接调用。
状态设置¶
def set_joint_qpos(qpos: np.ndarray) -> None # 设置广义坐标(全量)
def set_joint_qvel(qvel: np.ndarray) -> None # 设置广义速度(全量)
mj_forward() 更新派生量。
力应用¶
def apply_body_force(body_name: str, force: np.ndarray, torque: np.ndarray) -> None
def clear_body_force(body_name: str) -> None
def clear_all_forces() -> None
def mj_apply_force_at_site(site_name: str, force: np.ndarray, torque: np.ndarray) -> None
def mj_clear_xfrc_applied_for_site(site_name: str) -> None
Mocap 与几何体设置¶
def set_mocap_pos_and_quat(mocap_pos_and_quat_dict: dict) -> None
def set_geom_friction(geom_friction_dict: dict) -> None
def add_extra_weight(weight_load_dict: dict) -> None
状态查询(按名称,无需 ID)¶
# 关节查询
def query_joint_qpos(joint_names: list[str]) -> dict[str, np.ndarray]
def query_joint_qvel(joint_names: list[str]) -> dict[str, np.ndarray]
def query_joint_qacc(joint_names: list[str]) -> dict[str, np.ndarray]
def query_joint_offsets(joint_names: list[str]) -> dict[str, np.ndarray] # 关节偏移
def query_joint_lengths(joint_names: list[str]) -> dict[str, np.ndarray] # 关节长度
def query_joint_dofadrs(joint_names: list[str]) -> dict[str, int] # 关节 dof 起始地址
def jnt_qposadr(joint_name: str) -> int
def jnt_dofadr(joint_name: str) -> int
# Body 位姿
def get_body_xpos_xmat_xquat(body_name_list: list[str]) -> dict
def get_body_xpos_xmat_xquat_xvel(body_name_list: list[str]) -> dict
# Site 查询
def query_site_pos_and_mat(site_names: list[str]) -> dict
def query_site_size(site_names: list[str]) -> dict[str, np.ndarray]
# 传感器/执行器/接触
def query_sensor_data(sensor_names: list[str]) -> dict[str, np.ndarray]
def query_actuator_torques(actuator_names: list[str]) -> dict[str, np.ndarray]
def query_contact_simple() -> list[dict]
def query_contact_force(contact_ids: list[int]) -> dict[int, np.ndarray]
def get_cfrc_ext() -> np.ndarray
def get_goal_bounding_box(geom_name: str) -> np.ndarray # geom 包围盒半尺寸 (3,)
def body_subtree_mass(body_name: str) -> float
基座坐标系变换¶
def query_site_pos_and_quat_B(site_names, base_body_list) -> dict
def query_site_xvalp_xvalr(site_names) -> tuple[dict, dict]
def query_site_xvalp_xvalr_B(site_names, base_body_list) -> tuple[dict, dict]
def query_velocity_body_B(ee_body, base_body) -> np.ndarray # 6D 速度(基座系)
def query_position_body_B(ee_body, base_body) -> np.ndarray # 3D 位置(基座系)
def query_orientation_body_B(ee_body, base_body) -> np.ndarray # 四元数(基座系)
def query_joint_axes_B(joint_names, base_body) -> dict # 关节轴方向(基座系)
里程计查询¶
def query_robot_velocity_odom(base_body, initial_base_pos, initial_base_quat) -> tuple
def query_robot_position_odom(base_body, initial_base_pos, initial_base_quat) -> np.ndarray
def query_robot_orientation_odom(base_body, initial_base_pos, initial_base_quat) -> np.ndarray
雅可比¶
def mj_jacBody(jacp: np.ndarray, jacr: np.ndarray, body_name: str) -> None
def mj_jacSite(jacp: np.ndarray, jacr: np.ndarray, site_name: str) -> None
def mj_jac_site(site_names: list[str]) -> dict[str, dict]
等式约束与体操作¶
Env 层公共原语(程序化操作应使用以下方法编排):
def equality_find_slot_by_body(body_name: str) -> int # 查找含指定 body 的等式约束槽位,未找到返回 -1
def equality_constraint(slot: int) -> dict # 读取单个等式约束完整数据
def equality_update(
slot: int,
*,
eq_type: int | None = None, # mjtEq 类型常量
obj1_name: str | None = None, # 新的 obj1 body 名称
obj2_name: str | None = None, # 新的 obj2 body 名称
data: np.ndarray | None = None, # 约束数据 (mjNEQDATA,)
active: bool | None = None, # 是否激活
solref: np.ndarray | None = None, # 求解器参考参数 (2,)
solimp: np.ndarray | None = None, # 求解器 impedance 参数 (5,)
forward: bool = True, # 写入后是否调用 mj_forward
) -> None
⚠️ 注意:以下方法在 Env 层不存在: -
update_equality_constraints/modify_equality_objects:仅存在于OrcaGymEuler(gym 层)与MuJoCoSimCore(sim 层)。modify_equality_objects签名为modify_equality_objects(eq_ids: list[int], obj1_ids=None, obj2_ids=None), 参数为 int 列表而非 names。 -update_anchor_equality_constraints/anchor_actor/release_body_anchored/do_body_manipulation:为 Env 层内部_前缀 方法(_anchor_actor/_release_body_anchored/_do_body_manipulation), 由render()内部驱动的 UI 抓取状态机使用,不应直接调用。 程序化操作应使用equality_find_slot_by_body+equality_constraint+equality_update+set_mocap_pos_and_quat原语编排。
只读查询¶
Studio 交互¶
def render() -> np.ndarray | None # 渲染到 Studio
def begin_save_video(file_path, capture_mode=0) -> None
def stop_save_video() -> None
def get_current_frame() -> int
def get_next_frame() -> int
def get_camera_time_stamp(last_frame_index) -> dict
def get_frame_png(image_path) -> None
def load_content_file(content_file_name, **kwargs) -> None
摄像头传感器配置¶
def set_camera_sensor_info(
actor_name: str,
capture_rgb: bool,
capture_depth: bool,
save_mp4_file: bool = False,
use_dds: bool = False,
**kwargs, # 可选扩展参数:capture_normal, width, height, vertical_fov, near_clip, far_clip, gamma, color_port, depth_port, ...
) -> None
def make_camera_viewport_active(actor_name: str, entity_name: str) -> None
Studio 桥接¶
名称空间解析(多智能体)¶
继承自 OrcaGymEnvMixin,用于在多智能体环境中自动为实体名称添加智能体前缀。
@property
agent_num: int # 智能体数量
def body(name: str, agent_id: int | None = None) -> str # body 名称 → "agent_name_body_name"
def joint(name: str, agent_id: int | None = None) -> str # 关节名称
def actuator(name: str, agent_id: int | None = None) -> str # 执行器名称
def site(name: str, agent_id: int | None = None) -> str # site 名称
def mocap(name: str, agent_id: int | None = None) -> str # mocap 名称
def sensor(name: str, agent_id: int | None = None) -> str # 传感器名称
agent_id=None时默认使用第一个智能体(索引 0)。
示例:
# 单智能体环境(agent_names=["robot_1"])
env.body("pelvis") # → "robot_1_pelvis"
env.joint("leg_l_1") # → "robot_1_leg_l_1"
# 多智能体环境(agent_names=["robot_1", "robot_2"])
env.body("pelvis", agent_id=1) # → "robot_2_pelvis"
动作/观测空间生成¶
继承自 OrcaGymEnvMixin,用于便捷地生成符合 Gymnasium 规范的动作空间和观测空间。
def generate_action_space(bounds: np.ndarray) -> gym.Space # (nu, 2) → Box 动作空间
def generate_observation_space(obs: np.ndarray | dict) -> gym.Space # 从观测样例生成观测空间
generate_action_space 会自动处理 ±inf 边界(裁剪到 float32 可表示范围),避免 gymnasium 的溢出警告。
示例:
# 根据执行器控制范围生成动作空间
ctrlrange = self.model.get_actuator_ctrlrange() # (nu, 2)
self.action_space = self.generate_action_space(ctrlrange)
# 根据观测样例生成观测空间
obs_sample = self._get_obs()
self.observation_space = self.generate_observation_space(obs_sample)
随机种子¶
设置后可通过 self.np_random 使用 RandomState 实例。
重置(Gymnasium 标准接口)¶
由 OrcaGymEnvMixin 提供,编排顺序:set_seed_value → reset_simulation → reset_model → render。
子类应复写
reset_model()而非直接复写reset()。
抽象方法(子类必须实现)¶
def step(action: np.ndarray) -> tuple[ObsType, float, bool, bool, dict]
def reset_model() -> tuple[dict, dict]
def _get_obs() -> np.ndarray | dict
生命周期方法¶
def initialize_grpc()
def initialize_simulation() # 加载模型
def reset_simulation() # 重置状态
def init_qpos_qvel() # 缓存初始状态
def set_time_step(time_step) # 设置时间步长
def pause_simulation()
def close() # 关闭连接
使用示例¶
import numpy as np
from gymnasium import spaces
from orca_gym.environment.euler.orca_gym_euler_env import OrcaGymEulerEnv
class MyRobotEnv(OrcaGymEulerEnv):
"""最简环境:Box 观测 + Box 动作,离线模式。"""
def __init__(self, model_xml_path: str):
super().__init__(
frame_skip=5,
orcagym_addr="localhost:50051",
agent_names=["robot_1"],
time_step=0.001,
model_xml_path=model_xml_path,
skip_grpc_load=True, # 离线模式
)
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.Box(
low=-np.inf, high=np.inf, shape=obs_sample.shape, dtype=np.float32
)
def _get_obs(self) -> np.ndarray:
return np.concatenate([
self.data.qpos.copy(),
self.data.qvel.copy(),
]).astype(np.float32)
def step(self, action: np.ndarray):
action = np.asarray(action, dtype=np.float32).reshape(self.model.nu)
self.do_simulation(action, self.frame_skip)
obs = self._get_obs()
reward = self._compute_reward(obs)
terminated = False
truncated = False
info = {"time": float(self.data.time)}
return obs, reward, terminated, truncated, info
def _compute_reward(self, obs: np.ndarray) -> float:
return 0.0 # 替换为你的奖励函数
def reset_model(self):
qpos = self.init_qpos + self.np_random.uniform(-0.1, 0.1, self.model.nq)
qvel = self.init_qvel + self.np_random.uniform(-0.1, 0.1, self.model.nv)
self.set_joint_qpos(qpos)
self.set_joint_qvel(qvel)
self.mj_forward()
self._sync_view()
return self._get_obs(), {}
# 使用
if __name__ == "__main__":
env = MyRobotEnv(model_xml_path="/path/to/scene.xml")
obs, _ = env.reset()
for _ in range(100):
action = env.action_space.sample()
obs, reward, terminated, truncated, info = env.step(action)
env.close()
OrcaGymVectorEnv¶
向量化环境,并行执行多个环境。继承自 Gymnasium VectorEnv。
class OrcaGymVectorEnv(VectorEnv):
def __init__(self, num_envs: int, worker_index: int, entry_point: str, **kwargs)
def step(actions) -> tuple[obs, rewards, terminations, truncations, infos]
def reset(*, seed=None, options=None) -> tuple[obs, infos]
公共属性¶
num_envs: int # 并行环境数量
observation_space: gym.Space # 批量观测空间
single_observation_space: gym.Space # 单环境观测空间
action_space: gym.Space # 批量动作空间
single_action_space: gym.Space # 单环境动作空间
主要方法¶
def reset(*, seed=None, options=None) -> tuple[ObsType, list[dict]]
def step(actions: ActType) -> tuple[ObsType, np.ndarray, np.ndarray, np.ndarray, list[dict]]
def render() -> None
def close() -> None
返回值说明¶
| 返回值 | 形状/类型 | 说明 |
|---|---|---|
observations |
(num_envs, *obs_shape) |
所有环境的观测 |
rewards |
(num_envs,) |
所有环境的奖励 |
terminated |
(num_envs,) bool |
是否终止 |
truncated |
(num_envs,) bool |
是否截断 |
infos |
list[dict] |
每个环境的 info 字典 |
RewardType¶
模块路径:from orca_gym.environment.orca_gym_env import RewardType