#强化学习 #bevy #rl #gym #ai

bevy_rl

使用bevy构建强化学习环境

30个版本

0.14.0 2024年7月5日
0.13.0 2024年2月20日
0.12.0 2023年11月5日
0.11.0-beta2023年7月17日
0.0.5 2022年5月11日

#179 in 游戏开发

Download history 3/week @ 2024-06-02 203/week @ 2024-06-30 60/week @ 2024-07-07 3/week @ 2024-07-14 231/week @ 2024-07-28 8/week @ 2024-08-04

每月239次下载
用于bevy_rl_shooter

MIT/Apache

1MB
595

🏋️‍♀️ bevy_rl

Crates.io MIT/Apache 2.0 Crates.io Rust

image

为Bevy引擎提供强化学习

使用Bevy引擎构建OpenAI Gym环境,以训练能够从屏幕像素或定义的观察状态中学习的AI代理。

📝功能

  • 一套API用于实现OpenAI Gym接口,例如 resetsteprender 及相关模拟器状态
  • 多代理支持
  • 将屏幕像素渲染到RAM缓冲区 — 用于训练使用原始像素的代理
  • REST API用于控制代理

👩‍💻用法

1. 定义动作和观察空间

观察空间需要是 Serializable 以便REST API能够工作。

// Action space
#[derive(Default)]
pub struct Actions {
    // actuator_signals: [f32; 3],
}

// Observation space
#[derive(Default, Serialize, Clone)]
pub struct Observations {
    // agent_coords: [(f32, f32); 16],
}

2. 启用AI Gym插件

宽度和高度应超过256,否则wgpu会panic。

// Setup bevy_rl
let ai_gym_state = AIGymState::<Actions, State>::new(AIGymSettings {
    width: u32,              // Width and height of the screen
    height: u32,             // ...
    num_agents: 1,           // Number of agents — each will get a camera handle
    render_to_buffer: false, // You can disable rendering to buffer
    pause_interval: 0.01,    // 100 Hz
    ..default()
});
app.insert_resource(ai_gym_state)
    .add_plugins(AIGymPlugin::<Actions, Observations>::default());

2.1(可选)启用缓冲区渲染

如果您的环境需要导出原始像素,您需要将渲染目标附加到您想要从中导出它们的每个相机。渲染目标会在每一帧从GPU内存复制到RAM缓冲区,以便可以使用REST API访问。

pub(crate) fn spawn_cameras(
    ai_gym_state: Res<AIGymState<Actions, Observations>>,
) {
    let mut ai_gym_state = ai_gym_state.lock().unwrap();
    let ai_gym_settings = ai_gym_state.settings.clone();

    for i in 0..ai_gym_settings.num_agents {
        let render_image_handle = ai_gym_state.render_image_handles[i as usize].clone();
        let render_target = RenderTarget::Image(render_image_handle);
        let camera_bundle = Camera3dBundle {
            camera: Camera {
                target: render_target,  // Render target is baked in bevy_rl and used to export pixels
                priority: -1,           // set to -1 to render at the firstmost pass
                ..default()
            },
            ..default()
        };
        commands.spawn(camera_bundle);
    }
}

4. 处理bevy_rl事件

bevy_rl将通过事件与您的环境通信。这些事件由REST API或 bevy_rl.SimulationPauseTimer(以给定的间隔暂停模拟 AIGymSettings.pause_interval)启动。

事件 描述 用法
EventReset 将环境重置到初始状态 您应该在这里重新构建您的环境
EventControl 切换到控制状态 您应该在这里接收动作并将它们应用到您的环境中(并恢复模拟)
EventPause 暂停环境执行 暂停物理引擎或游戏时钟并快照您的游戏状态

以下是如何处理这些事件的示例

// EventPauseResume
fn bevy_rl_pause_request(
    mut pause_event_reader: EventReader<EventPauseResume>,
    ai_gym_state: Res<AIGymState<Actions, State>>,
) {
    for _ in pause_event_reader.iter() {
        // Pause simulation (physics engine)
        // ...
        // Collect state into serializable struct
        let env_state = Observations(...);
        // Set bevy_rl gym state
        let mut ai_gym_state = ai_gym_state.lock().unwrap();
        ai_gym_state.set_env_state(env_state);
    }
}

// EventControl
fn bevy_rl_control_request(
    mut pause_event_reader: EventReader<EventControl>,
    mut simulation_state: ResMut<State<SimulationState>>,
) {
    for control in pause_event_reader.iter() {
        let unparsed_actions = &control.0;
        for i in 0..unparsed_actions.len() {
            if let Some(unparsed_action) = unparsed_actions[i].clone() {
                let action: Vec<f64> = serde_json::from_str(&unparsed_action).unwrap();
                // Pass control inputs to your agents
                // ...
            }
        }
        // Resume simulation (physics engine)
        // ...
        // Return to running state; note that it uses pop/push to avoid
        // entering `SystemSet::on_enter(SimulationState::Running)` which initialized game world anew
        simulation_state.pop().unwrap();
    }
}

/// Handle bevy_rl::EventReset
pub(crate) fn bevy_rl_reset_request(
    mut reset_event_reader: EventReader<EventReset>,
    mut commands: Commands,
    mut walls: Query<Entity, &Wall>,
    mut players: Query<(Entity, &Actor)>,
    mut simulation_state: ResMut<State<SimulationState>>,
    ai_gym_state: Res<AIGymState<Actions, Observations>>,
) {
    if reset_event_reader.iter().count() == 0 {
        return;
    }

    // Reset envrionment state here

    // Return simulation in Running state
    simulation_state.set(SimulationState::Running).unwrap();

    // tell bevy_rl that environment is reset and return response to REST API
    let ai_gym_state = ai_gym_state.lock().unwrap();
    ai_gym_state.send_reset_result(true);
}

注册系统以处理bevy_rl事件。

app.add_systems(
    Update,
    (
        .with_system(bevy_rl_control_request)
        .with_system(bevy_rl_reset_request)
        .with_system(bevy_rl_pause_request),
    ).in_set(SimulationState::PausedForControl)
);

💻 AIGymState API

这些方法在 AIGymState 资源上可用。您应该使用它们来更改bevy_rl的内部状态。

方法 描述 用法
set_reward(agent_index: usize,score: f32) 为代理设置奖励 当某个事件发生时,您可以设置代理的奖励。
set_terminated(agent_index: usize,result: bool) 设置代理的终止状态 一旦您的代理被击败,您应将其状态设置为 true。适用于多代理。
reset() 重置 bevy_rl 状态 当您重置环境以清除导出的状态历史时,应调用此方法。
set_env_state(state:状态) 设置当前环境状态 当您序列化环境状态时,应在此处设置。
send_reset_result(result: bool) 将重置结果发送到 REST API 当您重置环境以与 REST API 同步时,应调用此方法。

🌐 REST API

可以通过 REST API 访问 bevy_rl 启用的环境。以下是可用端点的列表

方法 动词 bevy_rl 版本
相机像素 GET https://127.0.0.1:7878/visual_observations
状态 GET https://127.0.0.1:7878/state
重置环境 GET https://127.0.0.1:7878/reset
步骤 GET https://127.0.0.1:7878/step?payload=ACTION

bevy_rl_shooter 实现了一个 Python 包装器的示例。

✍️ 示例

依赖关系

~30–65MB
~1M SLoC