From 62e638c4d28e07bf7df12f9e28eb29c0ccddc823 Mon Sep 17 00:00:00 2001 From: huangfu <3045324663@qq.com> Date: Fri, 24 Oct 2025 21:39:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=9E=E9=80=80=E4=BA=86=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=86=85=E5=AE=B9,=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=AF=B9?= =?UTF-8?q?=E4=BA=8E=E7=8E=AF=E5=A2=83=E6=96=B9=E6=B3=95=E7=9A=84=E6=8F=8F?= =?UTF-8?q?=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Env/DEBUG_GUIDE.md | 339 --------- Env/ENVIRONMENT_INFO.md | 279 +++++++ Env/GPU_ACCELERATION.md | 221 ------ Env/PERFORMANCE_OPTIMIZATION.md | 131 ---- Env/QUICK_START.md | 241 ------ Env/__pycache__/logger_utils.cpython-310.pyc | Bin 4877 -> 4877 bytes Env/__pycache__/scenario_env.cpython-310.pyc | Bin 13234 -> 21683 bytes Env/__pycache__/scenario_env.cpython-313.pyc | Bin 24323 -> 34721 bytes .../simple_idm_policy.cpython-310.pyc | Bin 770 -> 770 bytes Env/run_multiagent_env.py | 13 +- Env/run_multiagent_env_fast.py | 115 --- Env/run_multiagent_env_parallel.py | 156 ---- Env/run_multiagent_env_visual.py | 60 -- Env/scenario_env.py | 698 ++++++++++++------ Env/test_lane_filter.py | 219 ------ Env/test_no_traffic_lights.py | 76 ++ 16 files changed, 844 insertions(+), 1704 deletions(-) delete mode 100644 Env/DEBUG_GUIDE.md create mode 100644 Env/ENVIRONMENT_INFO.md delete mode 100644 Env/GPU_ACCELERATION.md delete mode 100644 Env/PERFORMANCE_OPTIMIZATION.md delete mode 100644 Env/QUICK_START.md delete mode 100644 Env/run_multiagent_env_fast.py delete mode 100644 Env/run_multiagent_env_parallel.py delete mode 100644 Env/run_multiagent_env_visual.py delete mode 100644 Env/test_lane_filter.py create mode 100644 Env/test_no_traffic_lights.py diff --git a/Env/DEBUG_GUIDE.md b/Env/DEBUG_GUIDE.md deleted file mode 100644 index 5204651..0000000 --- a/Env/DEBUG_GUIDE.md +++ /dev/null @@ -1,339 +0,0 @@ -# 调试功能使用指南 - -## 📋 概述 - -已为车道过滤和红绿灯检测功能添加了详细的调试输出,帮助您诊断和理解代码行为。 - ---- - -## 🎛️ 调试开关 - -### 1. 配置参数 - -在创建环境时,可以通过 `config` 参数启用调试模式: - -```python -env = MultiAgentScenarioEnv( - config={ - # ... 其他配置 ... - - # 🔥 调试开关 - "debug_lane_filter": True, # 启用车道过滤调试 - "debug_traffic_light": True, # 启用红绿灯检测调试 - }, - agent2policy=your_policy -) -``` - -### 2. 默认值 - -两个调试开关默认都是 `False`(关闭),避免正常运行时产生大量日志。 - ---- - -## 📊 车道过滤调试 (`debug_lane_filter=True`) - -### 输出内容 - -``` -📍 场景信息统计: - - 总车道数: 123 - -🔍 开始车道过滤: 共 51 辆车待检测 - -车辆 1/51: ID=128 - 🔍 检测位置 (-4.11, 46.76), 容差=3.0m - ✅ 在车道上 (车道184, 检查了32条) - ✅ 保留 - -车辆 7/51: ID=134 - 🔍 检测位置 (-51.34, -3.77), 容差=3.0m - ❌ 不在任何车道上 (检查了123条车道) - ❌ 过滤 (原因: 不在车道上) - -... (所有车辆) - -📊 过滤结果: 保留 45 辆, 过滤 6 辆 -``` - -### 调试信息说明 - -| 信息 | 含义 | -|------|------| -| 📍 场景信息统计 | 场景的基本信息(车道数、红绿灯数) | -| 🔍 开始车道过滤 | 开始过滤,显示待检测车辆总数 | -| 🔍 检测位置 | 车辆的坐标和使用的容差值 | -| ✅ 在车道上 | 找到了车辆所在的车道,显示车道ID和检查次数 | -| ❌ 不在任何车道上 | 所有车道都检查完了,未找到匹配的车道 | -| 📊 过滤结果 | 最终统计:保留多少辆,过滤多少辆 | - -### 典型输出案例 - -**情况1:车辆在正常车道上** -``` -车辆 1/51: ID=128 - 🔍 检测位置 (-4.11, 46.76), 容差=3.0m - ✅ 在车道上 (车道184, 检查了32条) - ✅ 保留 -``` -→ 检查了32条车道后找到匹配的车道184 - -**情况2:车辆在草坪/停车场** -``` -车辆 7/51: ID=134 - 🔍 检测位置 (-51.34, -3.77), 容差=3.0m - ❌ 不在任何车道上 (检查了123条车道) - ❌ 过滤 (原因: 不在车道上) -``` -→ 检查了所有123条车道都不匹配,该车辆被过滤 - ---- - -## 🚦 红绿灯检测调试 (`debug_traffic_light=True`) - -### 输出内容 - -``` -📍 场景信息统计: - - 总车道数: 123 - - 有红绿灯的车道数: 0 - ⚠️ 场景中没有红绿灯! - -🚦 检测车辆红绿灯 - 位置: (-4.1, 46.8) - 方法1-导航模块: - current_lane = - lane_index = 184 - has_traffic_light = False - 该车道没有红绿灯 - 方法2-遍历车道: 开始遍历 123 条车道 - ✓ 找到车辆所在车道: 184 (检查了32条) - has_traffic_light = False - 该车道没有红绿灯 - 结果: 返回 0 (无红绿灯/未知) -``` - -### 调试信息说明 - -| 信息 | 含义 | -|------|------| -| 有红绿灯的车道数 | 统计场景中有多少个红绿灯 | -| ⚠️ 场景中没有红绿灯 | 如果数量为0,会特别提示 | -| 方法1-导航模块 | 尝试从导航系统获取 | -| current_lane | 导航系统返回的当前车道对象 | -| lane_index | 车道的唯一标识符 | -| has_traffic_light | 该车道是否有红绿灯 | -| status | 红绿灯的状态(GREEN/YELLOW/RED/None) | -| 方法2-遍历车道 | 兜底方案,遍历所有车道查找 | -| ✓ 找到车辆所在车道 | 遍历找到了匹配的车道 | - -### 典型输出案例 - -**情况1:场景没有红绿灯** -``` -📍 场景信息统计: - - 有红绿灯的车道数: 0 - ⚠️ 场景中没有红绿灯! - -🚦 检测车辆红绿灯 - 位置: (-4.1, 46.8) - 方法1-导航模块: - ... - has_traffic_light = False - 该车道没有红绿灯 - 结果: 返回 0 (无红绿灯/未知) -``` -→ 所有车辆都会返回0,这是正常的 - -**情况2:有红绿灯且状态正常** -``` -🚦 检测车辆红绿灯 - 位置: (10.5, 20.3) - 方法1-导航模块: - current_lane = <...> - lane_index = 205 - has_traffic_light = True - status = TRAFFIC_LIGHT_GREEN - ✅ 方法1成功: 绿灯 -``` -→ 方法1直接成功,返回1(绿灯) - -**情况3:红绿灯状态为None** -``` -🚦 检测车辆红绿灯 - 位置: (10.5, 20.3) - 方法1-导航模块: - current_lane = <...> - lane_index = 205 - has_traffic_light = True - status = None - ⚠️ 方法1: 红绿灯状态为None -``` -→ 有红绿灯,但状态异常,返回0 - -**情况4:导航失败,方法2兜底** -``` -🚦 检测车辆红绿灯 - 位置: (15.2, 30.5) - 方法1-导航模块: 不可用 (hasattr=True, not_none=False) - 方法2-遍历车道: 开始遍历 123 条车道 - ✓ 找到车辆所在车道: 210 (检查了45条) - has_traffic_light = True - status = TRAFFIC_LIGHT_RED - ✅ 方法2成功: 红灯 -``` -→ 方法1失败,方法2兜底成功,返回3(红灯) - ---- - -## 🧪 测试方法 - -### 方式1:使用测试脚本 - -```bash -# 标准测试(无详细调试) -python Env/test_lane_filter.py - -# 调试模式(详细输出) -python Env/test_lane_filter.py --debug -``` - -### 方式2:在代码中直接启用 - -```python -from scenario_env import MultiAgentScenarioEnv -from simple_idm_policy import ConstantVelocityPolicy - -env = MultiAgentScenarioEnv( - config={ - "data_directory": "...", - "use_render": False, - - # 启用调试 - "debug_lane_filter": True, - "debug_traffic_light": True, - }, - agent2policy=ConstantVelocityPolicy(target_speed=50) -) - -obs = env.reset(0) -# 调试信息会自动输出 -``` - ---- - -## 📝 调试输出控制 - -### 场景1:只想看车道过滤 - -```python -config = { - "debug_lane_filter": True, - "debug_traffic_light": False, # 关闭红绿灯调试 -} -``` - -### 场景2:只想看红绿灯检测 - -```python -config = { - "debug_lane_filter": False, - "debug_traffic_light": True, # 只看红绿灯 -} -``` - -### 场景3:生产环境(关闭所有调试) - -```python -config = { - "debug_lane_filter": False, - "debug_traffic_light": False, -} -# 或者直接不设置这两个参数,默认就是False -``` - ---- - -## 💡 常见问题诊断 - -### 问题1:所有红绿灯状态都是0 - -**检查调试输出:** -``` -📍 场景信息统计: - - 有红绿灯的车道数: 0 - ⚠️ 场景中没有红绿灯! -``` - -**结论:** 场景本身没有红绿灯,返回0是正常的 - ---- - -### 问题2:车辆被过滤但不应该过滤 - -**检查调试输出:** -``` -车辆 X: ID=XXX - 🔍 检测位置 (x, y), 容差=3.0m - ❌ 不在任何车道上 (检查了123条车道) - ❌ 过滤 (原因: 不在车道上) -``` - -**可能原因:** -1. 车辆确实在非车道区域(草坪/停车场) -2. 容差值太小,可以尝试增大 `lane_tolerance` -3. 车道数据有问题 - -**解决方案:** -```python -config = { - "lane_tolerance": 5.0, # 增大容差到5米 -} -``` - ---- - -### 问题3:性能下降 - -启用调试模式会有大量输出,影响性能: - -**解决方案:** -- 只在开发/调试时启用 -- 生产环境关闭所有调试开关 -- 或者只测试少量车辆: - ```python - config = { - "max_controlled_vehicles": 5, # 只测试5辆车 - "debug_traffic_light": True, - } - ``` - ---- - -## 📌 最佳实践 - -1. **开发阶段**:启用调试,理解代码行为 -2. **调试问题**:根据需要选择性启用调试 -3. **性能测试**:关闭所有调试 -4. **生产运行**:永久关闭调试 - ---- - -## 🔧 调试输出示例 - -完整的调试运行示例: - -```bash -cd /home/huangfukk/MAGAIL4AutoDrive -python Env/test_lane_filter.py --debug -``` - -输出会包含: -- 场景统计信息 -- 每辆车的详细检测过程 -- 最终的过滤/检测结果 -- 性能统计 - ---- - -## 📖 相关文档 - -- `README.md` - 项目总览和问题解决 -- `CHANGELOG.md` - 更新日志 -- `PERFORMANCE_OPTIMIZATION.md` - 性能优化指南 - diff --git a/Env/ENVIRONMENT_INFO.md b/Env/ENVIRONMENT_INFO.md new file mode 100644 index 0000000..72ca3e3 --- /dev/null +++ b/Env/ENVIRONMENT_INFO.md @@ -0,0 +1,279 @@ +# 多智能体场景环境详细说明 + +## 1. 观测信息详解 + +### 观测向量结构(总维度:107维) + +每个智能体的观测向量包含以下信息: + +```python +观测向量 = [ + # 1. 车辆状态信息 (5维) + position_x, # 车辆X坐标 + position_y, # 车辆Y坐标 + velocity_x, # X方向速度 + velocity_y, # Y方向速度 + heading_theta, # 朝向角度 + + # 2. 前向激光雷达 (80维) + lidar_1, # 第1个激光束的距离 + lidar_2, # 第2个激光束的距离 + ... + lidar_80, # 第80个激光束的距离 + # 范围:30米,用于前方障碍物检测 + + # 3. 侧向激光雷达 (10维) + side_lidar_1, # 第1个侧向激光束的距离 + ... + side_lidar_10, # 第10个侧向激光束的距离 + # 范围:8米,用于侧方障碍物检测 + + # 4. 车道线检测 (10维) + lane_line_1, # 第1个车道线检测距离 + ... + lane_line_10, # 第10个车道线检测距离 + # 范围:3米,用于车道线识别 + + # 5. 导航信息 (2维) + destination_x, # 目标点X坐标 + destination_y, # 目标点Y坐标 +] +``` + +### 观测信息说明 + +1. **车辆状态 (5维)** + - 位置:全局坐标系下的(x, y) + - 速度:车辆在全局坐标系下的速度分量 + - 朝向:车辆的航向角(弧度) + +2. **激光雷达 (100维)** + - 前向80束:覆盖前方视野,检测动态和静态障碍物 + - 侧向10束:检测侧方物体,用于变道等操作 + - 车道线10束:专门检测车道线位置 + +3. **导航信息 (2维)** + - 目标位置:从专家数据中提取的车辆最终位置 + +**总维度:5 + 80 + 10 + 10 + 2 = 107维** + +--- + +## 2. 多场景加载逻辑 + +### 2.1 场景加载机制 + +MetaDrive的ScenarioEnv通过以下配置管理多场景: + +```python +config = { + "data_directory": "path/to/dataset", # 包含dataset_mapping.pkl的目录 + "num_scenarios": 3, # 场景总数(从mapping文件读取) + "start_scenario_index": 0, # 起始场景索引 + "sequential_seed": True, # 是否顺序切换场景 +} +``` + +### 2.2 场景切换逻辑 + +#### 场景索引管理 +- **初始化**:环境启动时从`start_scenario_index`开始 +- **顺序模式**(`sequential_seed=True`): + - 每次`env.reset()`后自动切换到下一个场景 + - 循环顺序:场景0 → 场景1 → 场景2 → 场景0 ... + +#### 示例流程 +```python +env = MultiAgentScenarioEnv(config={ + "data_directory": "path/to/dataset", # 假设有3个场景 + "start_scenario_index": 0, + "sequential_seed": True, +}) + +obs = env.reset(0) # 使用场景0 +# ... 运行场景0 ... + +obs = env.reset() # 自动切换到场景1 +# ... 运行场景1 ... + +obs = env.reset() # 自动切换到场景2 +# ... 运行场景2 ... + +obs = env.reset() # 循环回到场景0 +``` + +### 2.3 当前场景信息获取 + +可以通过以下方式查看当前场景信息: + +```python +# 获取当前场景索引 +current_scenario = env.engine.current_seed + +# 获取场景总数 +total_scenarios = env.config["num_scenarios"] + +# 查看场景ID +scenario_id = env.engine.traffic_manager.current_scenario_id +``` + +### 2.4 手动指定场景 + +如果需要固定使用某个场景: + +```python +# 方法1:在reset时指定 +obs = env.reset(seed=1) # 使用场景1 + +# 方法2:配置固定场景 +config = { + "start_scenario_index": 2, # 从场景2开始 + "sequential_seed": False, # 禁用自动切换 +} +``` + +--- + +## 3. 车辆观测获取机制 + +### 3.1 观测获取流程 + +``` +step() 被调用 + ↓ +更新所有车辆物理状态 + ↓ +_spawn_controlled_agents() # 生成新车辆(按时间步) + ↓ +_get_all_obs() # 获取所有车辆观测 + ↓ + 遍历 controlled_agents: + ├─ 获取车辆状态 (position, velocity, heading) + ├─ 调用激光雷达传感器 + ├─ 组装观测向量 + └─ 添加到 obs_list + ↓ +返回 obs_list(包含所有车辆的观测) +``` + +### 3.2 观测返回格式 + +```python +obs = env.reset() +# obs 是一个列表,每个元素是一个车辆的观测向量 + +obs = [ + [obs_vehicle_0], # 第1辆车的107维观测 + [obs_vehicle_1], # 第2辆车的107维观测 + ... +] + +# 在step中 +obs, rewards, dones, infos = env.step(actions) +# obs格式相同,但只包含当前存活的车辆 +``` + +### 3.3 观测一致性保证 + +1. **物理状态同步** + - 所有车辆在同一物理时间步获取观测 + - 保证观测的时间一致性 + +2. **传感器独立性** + - 每个车辆有独立的传感器 + - 激光雷达从各自位置发射 + +3. **动态车辆管理** + - 新车辆在生成时立即获取观测 + - 观测列表动态更新 + +--- + +## 4. 常见问题解答 + +### Q1: 为什么观测维度是107而不是其他? +**A**: +- 车辆状态: 5维 (x, y, vx, vy, heading) +- 前向激光雷达: 80维 +- 侧向激光雷达: 10维 +- 车道线检测: 10维 +- 目标位置: 2维 +- **总计: 5 + 80 + 10 + 10 + 2 = 107维** + +### Q2: 如何确保多场景都被使用? +**A**: 设置`sequential_seed=True`,环境会自动循环遍历所有场景。 + +### Q3: 车辆在不同时间步生成,如何获取观测? +**A**: 每次调用`step()`时: +1. 先检查是否有新车辆需要生成(`_spawn_controlled_agents`) +2. 为所有现存车辆(包括新生成的)获取观测 +3. 返回的obs_list包含所有当前存活车辆的观测 + +### Q4: 如果场景中车辆数量不同怎么办? +**A**: +- 观测列表长度动态调整 +- 使用`max_controlled_vehicles`可限制最大车辆数 +- 使用`filter_offroad_vehicles`可过滤无效车辆 + +### Q5: 观测数据的坐标系是什么? +**A**: +- 位置/速度/目标:**全局坐标系**(世界坐标) +- 激光雷达:**车辆局部坐标系**(以车辆为中心) + +--- + +## 5. 配置建议 + +### 5.1 训练配置 +```python +config = { + "data_directory": "path/to/dataset", + "sequential_seed": True, # 循环使用所有场景 + "filter_offroad_vehicles": True, # 过滤无效车辆 + "max_controlled_vehicles": 20, # 限制车辆数防止过载 + "inherit_expert_velocity": False, # 训练时不继承速度 + "horizon": 300, # 每场景运行300步 +} +``` + +### 5.2 评估配置 +```python +config = { + "data_directory": "path/to/dataset", + "sequential_seed": False, # 固定场景 + "start_scenario_index": 0, # 指定场景 + "filter_offroad_vehicles": True, + "max_controlled_vehicles": None, # 不限制车辆数 + "inherit_expert_velocity": True, # 评估时继承速度 + "verbose_reset": True, # 输出详细信息 +} +``` + +--- + +## 6. 调试技巧 + +### 6.1 查看当前场景信息 +```python +# 在reset后查看 +print(f"当前场景: {env.engine.current_seed}") +print(f"总场景数: {env.config['num_scenarios']}") +print(f"可控车辆数: {len(env.controlled_agents)}") +``` + +### 6.2 查看观测维度 +```python +obs = env.reset() +print(f"车辆数量: {len(obs)}") +if len(obs) > 0: + print(f"单车辆观测维度: {len(obs[0])}") +``` + +### 6.3 启用详细日志 +```python +config = { + "verbose_reset": True, # 重置时详细统计 + "debug_lane_filter": True, # 车道过滤调试 +} +``` + diff --git a/Env/GPU_ACCELERATION.md b/Env/GPU_ACCELERATION.md deleted file mode 100644 index 17768b4..0000000 --- a/Env/GPU_ACCELERATION.md +++ /dev/null @@ -1,221 +0,0 @@ -# GPU加速指南 - -## 当前性能瓶颈分析 - -从测试结果看,即使关闭渲染,FPS仍然只有15-20左右,主要瓶颈是: - -### 计算量分析(51辆车) -``` -激光雷达计算: -- 前向雷达:80束 × 51车 = 4,080次射线检测 -- 侧向雷达:10束 × 51车 = 510次射线检测 -- 车道线雷达:10束 × 51车 = 510次射线检测 -合计:5,100次射线检测/帧 - -红绿灯检测: -- 遍历所有车道 × 51车 = 数千次几何计算 -``` - -**关键问题**:这些计算都是CPU单线程串行的,无法利用多核和GPU! - ---- - -## GPU加速方案 - -### 方案1:优化激光雷达计算(已实现)✅ - -**优化内容:** -1. 减少激光束数量:100束 → 52束(减少48%) -2. 优化红绿灯检测:避免遍历所有车道 -3. 激光雷达缓存:每N帧才重新计算一次 - -**预期提升:** 2-4倍(30-60 FPS) - -**使用方法:** -```bash -python Env/run_multiagent_env_fast.py -``` - ---- - -### 方案2:MetaDrive GPU渲染(有限支持) - -**说明:** -MetaDrive基于Panda3D引擎,理论上支持GPU渲染,但: -- GPU主要用于**图形渲染**,不是物理计算 -- 激光雷达的射线检测仍在CPU上 -- GPU渲染主要加速可视化,不加速训练 - -**启用方法:** -```python -config = { - "use_render": True, - "render_mode": "onscreen", # 或 "offscreen" - # Panda3D会自动尝试使用GPU -} -``` - -**限制:** -- 需要显示器或虚拟显示(Xvfb) -- WSL2环境需要配置X11转发 -- 对无渲染训练无帮助 - ---- - -### 方案3:使用GPU加速的物理引擎(推荐但需要迁移) - -**选项A:Isaac Gym (NVIDIA)** -- 完全在GPU上运行物理模拟和渲染 -- 可同时模拟数千个环境 -- **缺点**:需要完全重写环境代码,迁移成本高 - -**选项B:IsaacSim/Omniverse** -- NVIDIA的高级仿真平台 -- 支持GPU加速的激光雷达 -- **缺点**:学习曲线陡峭,环境配置复杂 - -**选项C:Brax (Google)** -- JAX驱动,完全在GPU/TPU上运行 -- **缺点**:功能有限,不支持复杂场景 - ---- - -### 方案4:策略网络GPU加速(推荐)✅ - -虽然环境仿真在CPU,但可以让**策略网络在GPU上运行**: - -```python -import torch - -# 创建GPU上的策略模型 -device = torch.device("cuda" if torch.cuda.is_available() else "cpu") -policy = PolicyNetwork().to(device) - -# 批量处理观测 -obs_batch = torch.tensor(obs_list).to(device) -with torch.no_grad(): - actions = policy(obs_batch) -actions = actions.cpu().numpy() -``` - -**优势:** -- 51辆车的推理可以并行 -- 如果使用RL训练,GPU加速训练过程 -- 不需要修改环境代码 - ---- - -### 方案5:多进程并行(最实用)✅ - -既然单个环境受限于CPU单线程,可以**并行运行多个环境**: - -```python -from multiprocessing import Pool -import os - -def run_single_env(seed): - """运行单个环境实例""" - env = MultiAgentScenarioEnv(config=...) - obs = env.reset(seed) - - for step in range(1000): - actions = {...} - obs, rewards, dones, infos = env.step(actions) - if dones["__all__"]: - break - - env.close() - return results - -# 使用进程池并行运行 -if __name__ == "__main__": - num_processes = os.cpu_count() # 12600KF有10核20线程 - seeds = list(range(num_processes)) - - with Pool(processes=num_processes) as pool: - results = pool.map(run_single_env, seeds) -``` - -**预期提升:** 接近线性(10核 ≈ 10倍吞吐量) - -**CPU利用率:** 可达80-100% - ---- - -## 推荐的完整优化方案 - -### 1. 立即可用(已实现) -```bash -# 使用优化版本,激光束减少+缓存 -python Env/run_multiagent_env_fast.py -``` -**预期:** 30-60 FPS(2-4倍提升) - -### 2. 短期优化(1-2小时) -- 实现多进程并行 -- 策略网络迁移到GPU - -**预期:** 300-600 FPS(总吞吐量) - -### 3. 中期优化(1-2天) -- 使用NumPy矢量化批量处理观测 -- 优化Python代码热点(用Cython/Numba) - -**预期:** 额外20-30%提升 - -### 4. 长期方案(1-2周) -- 迁移到Isaac Gym等GPU加速仿真器 -- 或使用分布式训练框架(Ray/RLlib) - -**预期:** 10-100倍提升 - ---- - -## 为什么MetaDrive无法直接使用GPU? - -### 架构限制: -1. **物理引擎**:使用Bullet/Panda3D的CPU物理引擎 -2. **射线检测**:串行CPU计算,无法并行 -3. **Python GIL**:全局解释器锁限制多线程 -4. **设计目标**:MetaDrive设计时主要考虑灵活性而非极致性能 - -### GPU在仿真中的作用: -- ✅ **图形渲染**:绘制画面(但我们训练时不需要) -- ✅ **神经网络推理/训练**:策略模型计算 -- ❌ **物理计算**:MetaDrive的物理引擎在CPU -- ❌ **传感器模拟**:激光雷达等在CPU - ---- - -## 检查GPU是否可用 - -```bash -# 检查NVIDIA GPU -nvidia-smi - -# 检查PyTorch GPU支持 -python -c "import torch; print(f'CUDA available: {torch.cuda.is_available()}')" - -# 检查MetaDrive渲染设备 -python -c "from panda3d.core import GraphicsPipeSelection; print(GraphicsPipeSelection.get_global_ptr().get_default_pipe())" -``` - ---- - -## 总结 - -| 方案 | 实现难度 | 性能提升 | GPU使用 | 推荐度 | -|------|----------|----------|---------|--------| -| 减少激光束 | ⭐ | 2-4x | ❌ | ⭐⭐⭐⭐⭐ | -| 激光雷达缓存 | ⭐ | 1.5-3x | ❌ | ⭐⭐⭐⭐⭐ | -| 多进程并行 | ⭐⭐ | 5-10x | ❌ | ⭐⭐⭐⭐⭐ | -| 策略GPU加速 | ⭐⭐ | 2-5x | ✅ | ⭐⭐⭐⭐ | -| GPU渲染 | ⭐⭐⭐ | 1.2x | ✅ | ⭐⭐ | -| 迁移Isaac Gym | ⭐⭐⭐⭐⭐ | 10-100x | ✅ | ⭐⭐⭐ | - -**结论:** -1. 先用已实现的优化(减少激光束+缓存) -2. 再实现多进程并行 -3. 策略网络用GPU训练 -4. 如果还不够,考虑迁移到GPU仿真器 - diff --git a/Env/PERFORMANCE_OPTIMIZATION.md b/Env/PERFORMANCE_OPTIMIZATION.md deleted file mode 100644 index a07dc24..0000000 --- a/Env/PERFORMANCE_OPTIMIZATION.md +++ /dev/null @@ -1,131 +0,0 @@ -# MetaDrive 性能优化指南 - -## 为什么帧率只有15FPS且CPU利用率不高? - -### 主要原因: - -1. **渲染瓶颈(最主要)** - - `use_render: True` + 每帧调用 `env.render()` 会严重限制帧率 - - MetaDrive 使用 Panda3D 渲染引擎,渲染是**同步阻塞**的 - - 即使CPU有余力,也要等待渲染完成才能继续下一步 - - 这就是为什么CPU利用率低但帧率也低的原因 - -2. **激光雷达计算开销** - - 每帧对每辆车进行3次激光雷达扫描(100个激光束) - - 需要进行物理射线检测,计算量较大 - -3. **物理引擎同步** - - 默认物理步长很小(0.02s),需要频繁计算 - -4. **Python GIL限制** - - Python全局解释器锁限制了多核并行 - - 即使是多核CPU,Python单线程性能才是瓶颈 - -## 性能优化方案 - -### 方案1:关闭渲染(推荐用于训练) -**预期提升:10-20倍(150-300+ FPS)** - -```python -config = { - "use_render": False, # 关闭渲染 - "render_pipeline": False, - "image_observation": False, - "interface_panel": [], - "manual_control": False, -} -``` - -### 方案2:降低物理计算频率 -**预期提升:2-3倍** - -```python -config = { - "physics_world_step_size": 0.05, # 默认0.02,增大步长 - "decision_repeat": 5, # 每5个物理步执行一次决策 -} -``` - -### 方案3:优化激光雷达 -**预期提升:1.5-2倍** - -修改 `scenario_env.py` 中的 `_get_all_obs()` 函数: - -```python -# 减少激光束数量 -lidar = self.engine.get_sensor("lidar").perceive( - num_lasers=40, # 从80减到40 - distance=30, - base_vehicle=vehicle, - physics_world=self.engine.physics_world.dynamic_world -) - -# 或者降低扫描频率(每N步才扫描一次) -if self.round % 5 == 0: - lidar = self.engine.get_sensor("lidar").perceive(...) -else: - lidar = self.last_lidar[agent_id] # 使用缓存 -``` - -### 方案4:间歇性渲染 -**适用场景:既需要可视化又想提升性能** - -```python -# 每10步渲染一次,而不是每步都渲染 -if step % 10 == 0: - env.render(mode="topdown") -``` - -### 方案5:使用多进程并行(高级) -**预期提升:接近线性(取决于进程数)** - -```python -from multiprocessing import Pool - -def run_env(seed): - env = MultiAgentScenarioEnv(config=...) - # 运行仿真 - return results - -# 使用进程池并行运行多个环境 -with Pool(processes=8) as pool: - results = pool.map(run_env, range(8)) -``` - -## 文件说明 - -- `run_multiagent_env.py` - **标准版本**(无渲染,基础优化) -- `run_multiagent_env_fast.py` - **极速版本**(激光雷达优化+缓存)⭐推荐 -- `run_multiagent_env_parallel.py` - **并行版本**(多进程,最高吞吐量)⭐⭐推荐 -- `run_multiagent_env_visual.py` - **可视化版本**(有渲染,适合调试) - -## 性能对比 - -| 配置 | 单环境FPS | 总吞吐量 | CPU利用率 | 文件 | 适用场景 | -|------|-----------|----------|-----------|------|----------| -| 原始配置(有渲染) | 15-20 | 15-20 | 15-20% | visual | 实时可视化调试 | -| 关闭渲染 | 20-25 | 20-25 | 20-30% | 标准版 | 基础训练 | -| 激光雷达优化+缓存 | 30-60 | 30-60 | 30-50% | fast | 快速训练⭐ | -| 多进程并行(10核) | 30-60 | 300-600 | 90-100% | parallel | 大规模训练⭐⭐ | - -**说明:** -- **单环境FPS**:单个环境实例的帧率 -- **总吞吐量**:所有进程合计的 steps/second -- 12600KF(10核20线程)推荐使用并行版本 - -## 建议 - -1. **训练时**:使用高性能版本(关闭渲染) -2. **调试时**:使用可视化版本,或间歇性渲染 -3. **大规模实验**:使用多进程并行 -4. **如果需要GPU加速**:考虑使用GPU渲染或将策略网络部署到GPU上 - -## 为什么CPU利用率低? - -- **渲染阻塞**:CPU在等待渲染完成 -- **Python GIL**:限制了多核利用 -- **I/O等待**:可能在等待磁盘读取数据 -- **单线程瓶颈**:MetaDrive主循环是单线程的 - -解决方法:关闭渲染 + 多进程并行 - diff --git a/Env/QUICK_START.md b/Env/QUICK_START.md deleted file mode 100644 index f9b03ca..0000000 --- a/Env/QUICK_START.md +++ /dev/null @@ -1,241 +0,0 @@ -# 快速使用指南 - -## 🚀 已实现的性能优化 - -根据您的测试结果,原始版本FPS只有15左右,现已进行了全面优化。 - ---- - -## 📊 性能瓶颈分析 - -您的CPU是12600KF(10核20线程),但利用率不到20%,原因是: - -1. **激光雷达计算瓶颈**:51辆车 × 100个激光束 = 每帧5100次射线检测 -2. **红绿灯检测低效**:遍历所有车道进行几何计算 -3. **Python GIL限制**:单线程执行,无法利用多核 -4. **计算串行化**:所有车辆依次处理,没有并行 - ---- - -## 🎯 推荐使用方案 - -### 方案1:极速单环境(推荐新手)⭐ -```bash -python Env/run_multiagent_env_fast.py -``` - -**优化内容:** -- ✅ 激光束:100束 → 52束(减少48%计算量) -- ✅ 激光雷达缓存:每3帧才重新计算 -- ✅ 红绿灯检测优化:避免遍历所有车道 -- ✅ 关闭所有渲染和调试 - -**预期性能:** 30-60 FPS(2-4倍提升) - ---- - -### 方案2:多进程并行(推荐训练)⭐⭐ -```bash -python Env/run_multiagent_env_parallel.py -``` - -**优化内容:** -- ✅ 同时运行10个独立环境(充分利用10核CPU) -- ✅ 每个环境应用所有单环境优化 -- ✅ CPU利用率可达90-100% - -**预期性能:** 300-600 steps/s(20-40倍总吞吐量) - ---- - -### 方案3:可视化调试 -```bash -python Env/run_multiagent_env_visual.py -``` - -**说明:** 保留渲染功能,FPS约15,仅用于调试 - ---- - -## 🔧 关于GPU加速 - -### GPU能否加速MetaDrive? - -**简短回答:有限支持,主要瓶颈不在GPU** - -**详细说明:** - -1. **物理计算(主要瓶颈)** ❌ 不支持GPU - - MetaDrive使用Bullet物理引擎,只在CPU运行 - - 激光雷达射线检测也在CPU - - 这是FPS低的主要原因 - -2. **图形渲染** ✅ 支持GPU - - Panda3D会自动使用GPU渲染 - - 但我们训练时关闭了渲染,所以GPU无用武之地 - -3. **策略网络** ✅ 支持GPU - - 可以把Policy模型放到GPU上 - - 但环境本身仍在CPU - -### GPU渲染配置(可选) -```python -config = { - "use_render": True, - # GPU会自动用于渲染 -} -``` - -### 策略网络GPU加速(推荐) -```python -import torch - -device = torch.device("cuda" if torch.cuda.is_available() else "cpu") -policy_model = PolicyNet().to(device) - -# 批量推理 -obs_tensor = torch.tensor(obs_list).to(device) -actions = policy_model(obs_tensor) -``` - -**详细说明请看:** `GPU_ACCELERATION.md` - ---- - -## 📈 性能对比 - -| 版本 | FPS | CPU利用率 | 改进 | -|------|-----|-----------|------| -| 原始版本 | 15 | 20% | - | -| 极速版本 | 30-60 | 30-50% | 2-4x | -| 并行版本 | 30-60/env | 90-100% | 总吞吐20-40x | - ---- - -## 💡 使用建议 - -### 场景1:快速测试环境 -```bash -python Env/run_multiagent_env_fast.py -``` -单环境,快速验证功能 - -### 场景2:大规模数据收集 -```bash -python Env/run_multiagent_env_parallel.py -``` -多进程,最大化数据收集速度 - -### 场景3:RL训练 -```bash -# 推荐使用Ray RLlib等框架,它们内置了并行环境管理 -# 或者修改parallel版本,保存经验到replay buffer -``` - -### 场景4:调试/可视化 -```bash -python Env/run_multiagent_env_visual.py -``` -带渲染,可以看到车辆运行 - ---- - -## 🔍 性能监控 - -所有版本都内置了性能统计,运行时会显示: -``` -Step 100: FPS = 45.23, 车辆数 = 51, 平均步时间 = 22.10ms -``` - ---- - -## ⚙️ 高级优化选项 - -### 调整激光雷达缓存频率 - -编辑 `run_multiagent_env_fast.py`: -```python -env.lidar_cache_interval = 3 # 改为5可进一步提速(但观测会更旧) -``` - -### 调整并行进程数 - -编辑 `run_multiagent_env_parallel.py`: -```python -num_workers = 10 # 改为更少的进程数(如果内存不足) -``` - -### 进一步减少激光束 - -编辑 `scenario_env.py` 的 `_get_all_obs()` 函数: -```python -lidar = self.engine.get_sensor("lidar").perceive( - num_lasers=20, # 从40进一步减少到20 - distance=20, # 从30减少到20米 - ... -) -``` - ---- - -## 🎓 为什么CPU利用率低? - -### 原因分析: - -1. **单线程瓶颈** - - Python GIL限制 - - MetaDrive主循环是单线程的 - - 即使有10个核心,也只用1个 - -2. **I/O等待** - - 等待渲染完成(如果开启) - - 等待磁盘读取数据 - -3. **计算不均衡** - - 某些计算很重(激光雷达),某些很轻 - - CPU在重计算之间有空闲 - -### 解决方案: - -✅ **已实现:** 多进程并行(`run_multiagent_env_parallel.py`) -- 每个进程占用1个核心 -- 10个进程可充分利用10核CPU -- CPU利用率可达90-100% - ---- - -## 📚 相关文档 - -- `PERFORMANCE_OPTIMIZATION.md` - 详细的性能优化指南 -- `GPU_ACCELERATION.md` - GPU加速的完整说明 - ---- - -## ❓ 常见问题 - -### Q: 为什么关闭渲染后FPS还是只有20? -A: 主要瓶颈是激光雷达计算,不是渲染。请使用 `run_multiagent_env_fast.py`。 - -### Q: GPU能加速训练吗? -A: 环境模拟在CPU,但策略网络可以在GPU上训练。 - -### Q: 如何最大化CPU利用率? -A: 使用 `run_multiagent_env_parallel.py` 多进程版本。 - -### Q: 会影响观测精度吗? -A: 激光束减少会略微降低精度,但实践中影响很小。缓存会让观测滞后1-2帧。 - -### Q: 如何恢复原始配置? -A: 使用 `run_multiagent_env_visual.py` 或修改配置文件中的参数。 - ---- - -## 🚦 下一步 - -1. 先测试 `run_multiagent_env_fast.py`,验证性能提升 -2. 如果满意,用于日常训练 -3. 需要大规模训练时,使用 `run_multiagent_env_parallel.py` -4. 考虑将策略网络迁移到GPU - -祝训练顺利!🎉 - diff --git a/Env/__pycache__/logger_utils.cpython-310.pyc b/Env/__pycache__/logger_utils.cpython-310.pyc index cecc6a8f275b25fd1d4780f456665b4f01f49f8f..d6591ccad64b74b9ad7d31a5971576c70ae458a6 100644 GIT binary patch delta 20 ZcmeBG>s8~<=jG*M00M>h?;E+9gaIx~1WW(` delta 20 ZcmeBG>s8~<=jG*M00QsKZyULpgaI$F1b_el diff --git a/Env/__pycache__/scenario_env.cpython-310.pyc b/Env/__pycache__/scenario_env.cpython-310.pyc index 3929cf598963b1789ab1f4956c1ab9797d287449..0ceae8b502ae0f4e5615aea5b3b1744474db2424 100644 GIT binary patch literal 21683 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 N(^c^o2LBT9{|~@CNaX+k delta 6528 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 diff --git a/Env/__pycache__/simple_idm_policy.cpython-310.pyc b/Env/__pycache__/simple_idm_policy.cpython-310.pyc index 9fdf69f5c89970831a4e149c341c2123c880603d..fed572963ea952caaca8010e490c614eacc41a38 100644 GIT binary patch delta 20 acmZo-YhvTh=jG*M00NIiKQ?lIX955$u?0o| delta 20 acmZo-YhvTh=jG*M00JkkZyUM4GXVf9Z3N~3 diff --git a/Env/run_multiagent_env.py b/Env/run_multiagent_env.py index 9175432..9c28a4e 100644 --- a/Env/run_multiagent_env.py +++ b/Env/run_multiagent_env.py @@ -5,7 +5,7 @@ from logger_utils import setup_logger import sys import os -WAYMO_DATA_DIR = r"/home/huangfukk/MAGAIL4AutoDrive/Env" +WAYMO_DATA_DIR = r"/home/huangfukk/mdsn" def main(enable_logging=False, log_file=None): """ @@ -18,7 +18,7 @@ def main(enable_logging=False, log_file=None): env = MultiAgentScenarioEnv( config={ # "data_directory": AssetLoader.file_path(AssetLoader.asset_path, "waymo", unix_style=False), - "data_directory": AssetLoader.file_path(WAYMO_DATA_DIR, "exp_converted", unix_style=False), + "data_directory": AssetLoader.file_path(WAYMO_DATA_DIR, "exp_filtered", unix_style=False), "is_multi_agent": True, "num_controlled_agents": 3, "horizon": 300, @@ -30,9 +30,12 @@ def main(enable_logging=False, log_file=None): # 车道检测与过滤配置 "filter_offroad_vehicles": True, # 启用车道区域过滤,过滤草坪等非车道区域的车辆 "lane_tolerance": 3.0, # 车道检测容差(米),可根据需要调整 - "max_controlled_vehicles": 2, # 限制最大车辆数(可选,None表示不限制) - "debug_lane_filter": True, - "debug_traffic_light": True, + "max_controlled_vehicles": None, # 限制最大车辆数(可选,None表示不限制) + + # 调试配置(可选) + # "debug_lane_filter": True, # 启用车道过滤详细调试 + # "verbose_reset": True, # 启用重置详细统计 + # "inherit_expert_velocity": True, # 继承专家速度 }, agent2policy=ConstantVelocityPolicy(target_speed=50) ) diff --git a/Env/run_multiagent_env_fast.py b/Env/run_multiagent_env_fast.py deleted file mode 100644 index 5942cd8..0000000 --- a/Env/run_multiagent_env_fast.py +++ /dev/null @@ -1,115 +0,0 @@ -from scenario_env import MultiAgentScenarioEnv -from simple_idm_policy import ConstantVelocityPolicy -from metadrive.engine.asset_loader import AssetLoader -from logger_utils import setup_logger -import time -import sys -import os - -WAYMO_DATA_DIR = r"/home/huangfukk/MAGAIL4AutoDrive/Env" - -def main(enable_logging=False): - """极致性能优化版本 - 启用所有优化选项""" - env = MultiAgentScenarioEnv( - config={ - "data_directory": AssetLoader.file_path(WAYMO_DATA_DIR, "exp_converted", unix_style=False), - "is_multi_agent": True, - "num_controlled_agents": 3, - "horizon": 300, - - # 关闭所有渲染 - "use_render": False, - "render_pipeline": False, - "image_observation": False, - "interface_panel": [], - "manual_control": False, - "show_fps": False, - "debug": False, - - # 物理引擎优化 - "physics_world_step_size": 0.02, - "decision_repeat": 5, - - "sequential_seed": True, - "reactive_traffic": True, - - # 车道检测与过滤配置 - "filter_offroad_vehicles": True, # 过滤非车道区域的车辆 - "lane_tolerance": 3.0, - "max_controlled_vehicles": 15, # 限制车辆数以提升性能 - }, - agent2policy=ConstantVelocityPolicy(target_speed=50) - ) - - # 【关键优化】启用激光雷达缓存 - # 每3帧才重新计算激光雷达,其余帧使用缓存 - # 可将激光雷达计算量减少到原来的1/3 - env.lidar_cache_interval = 3 - - obs = env.reset(0) - - # 性能统计 - start_time = time.time() - total_steps = 0 - - print("=" * 60) - print("极致性能模式") - print("激光雷达优化:80→40束 (前向), 10→6束 (侧向+车道线)") - print("激光雷达缓存:每3帧计算一次,中间帧使用缓存") - print("预期性能提升:3-5倍") - print("=" * 60) - - for step in range(10000): - actions = { - aid: env.controlled_agents[aid].policy.act() - for aid in env.controlled_agents - } - - obs, rewards, dones, infos = env.step(actions) - total_steps += 1 - - # 每100步输出一次性能统计 - if step % 100 == 0 and step > 0: - elapsed = time.time() - start_time - fps = total_steps / elapsed - print(f"Step {step:4d}: FPS = {fps:6.2f}, 车辆数 = {len(env.controlled_agents):3d}, " - f"平均步时间 = {1000/fps:.2f}ms") - - if dones["__all__"]: - break - - # 最终统计 - elapsed = time.time() - start_time - fps = total_steps / elapsed - print("\n" + "=" * 60) - print(f"总计: {total_steps} 步") - print(f"耗时: {elapsed:.2f}s") - print(f"平均FPS: {fps:.2f}") - print(f"单步平均耗时: {1000/fps:.2f}ms") - print("=" * 60) - - env.close() - - -if __name__ == "__main__": - # 解析命令行参数 - enable_logging = "--log" in sys.argv or "-l" in sys.argv - - # 提取自定义日志文件名 - log_file = None - for arg in sys.argv: - if arg.startswith("--log-file="): - log_file = arg.split("=")[1] - break - - if enable_logging: - # 使用日志记录 - log_dir = os.path.join(os.path.dirname(__file__), "logs") - with setup_logger(log_file=log_file or "run_fast.log", log_dir=log_dir): - main(enable_logging=True) - else: - # 普通运行(只输出到终端) - print("💡 提示: 使用 --log 或 -l 参数启用日志记录") - print("-" * 60) - main(enable_logging=False) - diff --git a/Env/run_multiagent_env_parallel.py b/Env/run_multiagent_env_parallel.py deleted file mode 100644 index d3eaebd..0000000 --- a/Env/run_multiagent_env_parallel.py +++ /dev/null @@ -1,156 +0,0 @@ -""" -多进程并行版本 - 充分利用多核CPU -适合大规模数据收集和训练 -""" -from scenario_env import MultiAgentScenarioEnv -from simple_idm_policy import ConstantVelocityPolicy -from metadrive.engine.asset_loader import AssetLoader -import time -import os -from multiprocessing import Pool, cpu_count - -WAYMO_DATA_DIR = r"/home/huangfukk/MAGAIL4AutoDrive/Env" - - -def run_single_env(args): - """在单个进程中运行一个环境实例""" - seed, num_steps, worker_id = args - - # 创建环境(每个进程独立) - env = MultiAgentScenarioEnv( - config={ - "data_directory": AssetLoader.file_path(WAYMO_DATA_DIR, "exp_converted", unix_style=False), - "is_multi_agent": True, - "num_controlled_agents": 3, - "horizon": 300, - - # 性能优化 - "use_render": False, - "render_pipeline": False, - "image_observation": False, - "interface_panel": [], - "manual_control": False, - "show_fps": False, - "debug": False, - - "physics_world_step_size": 0.02, - "decision_repeat": 5, - "sequential_seed": True, - "reactive_traffic": True, - - # 车道检测与过滤配置 - "filter_offroad_vehicles": True, - "lane_tolerance": 3.0, - "max_controlled_vehicles": 15, - }, - agent2policy=ConstantVelocityPolicy(target_speed=50) - ) - - # 启用激光雷达缓存 - env.lidar_cache_interval = 3 - - # 运行仿真 - start_time = time.time() - obs = env.reset(seed) - total_steps = 0 - total_agents = 0 - - for step in range(num_steps): - actions = { - aid: env.controlled_agents[aid].policy.act() - for aid in env.controlled_agents - } - - obs, rewards, dones, infos = env.step(actions) - total_steps += 1 - total_agents += len(env.controlled_agents) - - if dones["__all__"]: - break - - elapsed = time.time() - start_time - fps = total_steps / elapsed if elapsed > 0 else 0 - avg_agents = total_agents / total_steps if total_steps > 0 else 0 - - env.close() - - return { - 'worker_id': worker_id, - 'seed': seed, - 'steps': total_steps, - 'elapsed': elapsed, - 'fps': fps, - 'avg_agents': avg_agents, - } - - -def main(): - """主函数:协调多个并行环境""" - # 获取CPU核心数 - num_cores = cpu_count() - # 建议使用物理核心数(12600KF是10核20线程,使用10个进程) - num_workers = min(10, num_cores) - - print("=" * 80) - print(f"多进程并行模式") - print(f"CPU核心数: {num_cores}") - print(f"并行进程数: {num_workers}") - print(f"每个环境运行: 1000步") - print("=" * 80) - - # 准备任务参数 - num_steps_per_env = 1000 - tasks = [(seed, num_steps_per_env, worker_id) - for worker_id, seed in enumerate(range(num_workers))] - - # 启动多进程池 - start_time = time.time() - - with Pool(processes=num_workers) as pool: - results = pool.map(run_single_env, tasks) - - total_elapsed = time.time() - start_time - - # 统计结果 - print("\n" + "=" * 80) - print("各进程执行结果:") - print("-" * 80) - print(f"{'Worker':<8} {'Seed':<6} {'Steps':<8} {'Time(s)':<10} {'FPS':<8} {'平均车辆数':<12}") - print("-" * 80) - - total_steps = 0 - total_fps = 0 - - for result in results: - print(f"{result['worker_id']:<8} " - f"{result['seed']:<6} " - f"{result['steps']:<8} " - f"{result['elapsed']:<10.2f} " - f"{result['fps']:<8.2f} " - f"{result['avg_agents']:<12.1f}") - total_steps += result['steps'] - total_fps += result['fps'] - - print("-" * 80) - avg_fps_per_env = total_fps / len(results) - total_throughput = total_steps / total_elapsed - - print(f"\n总体统计:") - print(f" 总步数: {total_steps}") - print(f" 总耗时: {total_elapsed:.2f}s") - print(f" 单环境平均FPS: {avg_fps_per_env:.2f}") - print(f" 总吞吐量: {total_throughput:.2f} steps/s") - print(f" 并行效率: {total_throughput / avg_fps_per_env:.1f}x") - print("=" * 80) - - # 与单进程对比 - print(f"\n性能对比:") - print(f" 单进程FPS (预估): ~30 FPS") - print(f" 多进程吞吐量: {total_throughput:.2f} steps/s") - print(f" 性能提升: {total_throughput / 30:.1f}x") - print("=" * 80) - - -if __name__ == "__main__": - main() - diff --git a/Env/run_multiagent_env_visual.py b/Env/run_multiagent_env_visual.py deleted file mode 100644 index bf1e366..0000000 --- a/Env/run_multiagent_env_visual.py +++ /dev/null @@ -1,60 +0,0 @@ -from scenario_env import MultiAgentScenarioEnv -from simple_idm_policy import ConstantVelocityPolicy -from metadrive.engine.asset_loader import AssetLoader -import time - -WAYMO_DATA_DIR = r"/home/huangfukk/MAGAIL4AutoDrive/Env" - -def main(): - """带可视化的版本(低FPS,约15帧)""" - env = MultiAgentScenarioEnv( - config={ - "data_directory": AssetLoader.file_path(WAYMO_DATA_DIR, "exp_converted", unix_style=False), - "is_multi_agent": True, - "num_controlled_agents": 3, - "horizon": 300, - - # 可视化设置(牺牲性能) - "use_render": True, - "manual_control": False, - - "sequential_seed": True, - "reactive_traffic": True, - }, - agent2policy=ConstantVelocityPolicy(target_speed=50) - ) - - obs = env.reset(0) - - start_time = time.time() - total_steps = 0 - - for step in range(10000): - actions = { - aid: env.controlled_agents[aid].policy.act() - for aid in env.controlled_agents - } - - obs, rewards, dones, infos = env.step(actions) - env.render(mode="topdown") # 实时渲染 - - total_steps += 1 - - if step % 100 == 0 and step > 0: - elapsed = time.time() - start_time - fps = total_steps / elapsed - print(f"Step {step}: FPS = {fps:.2f}, 车辆数 = {len(env.controlled_agents)}") - - if dones["__all__"]: - break - - elapsed = time.time() - start_time - fps = total_steps / elapsed - print(f"\n总计: {total_steps} 步,耗时 {elapsed:.2f}s,平均FPS = {fps:.2f}") - - env.close() - - -if __name__ == "__main__": - main() - diff --git a/Env/scenario_env.py b/Env/scenario_env.py index f05726d..dd1e701 100644 --- a/Env/scenario_env.py +++ b/Env/scenario_env.py @@ -1,3 +1,91 @@ +""" +多智能体场景环境 (MultiAgentScenarioEnv) + +================================== +配置参数说明 (写在最前面) +================================== + +基础配置: + data_directory (str): 专家数据目录路径 + num_controlled_agents (int): 默认可控智能体数量,默认3 + horizon (int): 每个回合的最大步数,默认1000 + +车道检测与过滤配置: + filter_offroad_vehicles (bool): 是否过滤非车道区域的车辆,默认True + - True: 过滤掉在草坪、停车场等非车道区域生成的车辆 + - False: 保留所有车辆 + lane_tolerance (float): 车道检测容差(米),默认3.0 + - 用于放宽车道检测的边界条件 + max_controlled_vehicles (int|None): 最大可控车辆数限制,默认None + - None: 不限制车辆数量 + - int: 限制最多生成的车辆数 + +场景对象配置: + no_traffic_lights (bool): 是否禁用红绿灯渲染和逻辑,默认False + - True: 完全移除场景中的红绿灯 + - False: 保留红绿灯(按数据集原样生成) + +专家数据继承配置: + inherit_expert_velocity (bool): 是否继承专家数据中车辆的初始速度,默认False + - True: 车辆生成时使用专家数据中的速度 + - False: 车辆生成时速度为0(由策略控制) + +调试模式配置: + debug_lane_filter (bool): 车道过滤详细调试输出,默认False + - 输出每个车辆位置的车道检测详细过程 + verbose_reset (bool): 重置时输出详细统计信息,默认False + - 输出场景统计、过滤详情等 + +使用示例: + env = MultiAgentScenarioEnv( + config={ + "data_directory": "path/to/data", + "max_controlled_vehicles": 10, + "inherit_expert_velocity": True, # 继承专家速度 + "no_traffic_lights": True, # 禁用红绿灯 + "verbose_reset": True, # 详细输出 + }, + agent2policy=your_policy + ) + +================================== +整体逻辑和处理流程 +================================== + +1. 初始化阶段: + - 继承MetaDrive的ScenarioEnv基类 + - 配置多智能体参数(车辆数量、调试模式等) + - 接收策略映射字典(agent2policy) + +2. 环境重置阶段 (reset方法): + - 解析专家数据,提取车辆生成信息(car_birth_info_list) + - 清理原始交通数据,只保留车辆位置、朝向、目的地、速度(可选) + - 禁用红绿灯(如果配置) + - 初始化地图和车道信息 + - 执行车道过滤(_filter_valid_spawn_positions),移除非车道区域的车辆 + - 限制最大车辆数量 + - 生成可控智能体(_spawn_controlled_agents) + +3. 观测获取阶段 (_get_all_obs方法): + - 遍历所有可控车辆 + - 获取车辆状态(位置、速度、朝向) + - 检测红绿灯状态(_get_traffic_light_state) + - 获取激光雷达数据(前向、侧向、车道线检测) + - 组装完整观测向量 + +4. 环境步进阶段 (step方法): + - 执行所有智能体的动作 + - 更新物理引擎状态 + - 生成新的智能体(按时间步) + - 返回新的观测、奖励、完成状态 + +核心功能模块: +- PolicyVehicle: 可控制策略的车辆类 +- 车道检测与过滤: 确保车辆只在有效车道上生成 +- 多智能体管理: 动态生成和管理可控车辆 +- 专家速度继承: 可选地继承专家数据中的初始速度 +""" + import numpy as np from metadrive.component.navigation_module.node_network_navigation import NodeNetworkNavigation from metadrive.envs.scenario_env import ScenarioEnv @@ -11,172 +99,347 @@ from metadrive.type import MetaDriveType class PolicyVehicle(DefaultVehicle): + """ + 可控制策略的车辆类 + + 继承自MetaDrive的DefaultVehicle,增加了策略控制和目标设置功能。 + 用于多智能体环境中的可控车辆,支持自定义策略和目的地。 + """ + def __init__(self, *args, **kwargs): + """ + 初始化策略车辆 + + Args: + *args: 传递给父类的位置参数 + **kwargs: 传递给父类的关键字参数 + """ super().__init__(*args, **kwargs) - self.policy = None - self.destination = None + self.policy = None # 车辆的控制策略 + self.destination = None # 车辆的目标目的地 def set_policy(self, policy): + """ + 设置车辆的控制策略 + + Args: + policy: 控制策略对象,必须实现act(observation)方法 + """ self.policy = policy def set_destination(self, des): + """ + 设置车辆的目标目的地 + + Args: + des: 目标位置坐标 (x, y) + """ self.destination = des def act(self, observation, policy=None): + """ + 根据观测获取动作 + + Args: + observation: 环境观测数据 + policy: 可选的外部策略,如果提供则优先使用 + + Returns: + 动作向量,如果无策略则返回随机动作 + """ if self.policy is not None: return self.policy.act(observation) else: return self.action_space.sample() def before_step(self, action): - self.last_position = self.position # 2D vector - self.last_velocity = self.velocity # 2D vector - self.last_speed = self.speed # Scalar - self.last_heading_dir = self.heading + """ + 执行动作前的状态记录 + + 在每步执行前记录当前状态,用于后续的状态追踪和分析。 + + Args: + action: 即将执行的动作 + """ + self.last_position = self.position # 记录当前位置 (2D向量) + self.last_velocity = self.velocity # 记录当前速度 (2D向量) + self.last_speed = self.speed # 记录当前速度大小 (标量) + self.last_heading_dir = self.heading # 记录当前朝向 if action is not None: - self.last_current_action.append(action) - self._set_action(action) + self.last_current_action.append(action) # 记录动作历史 + self._set_action(action) # 设置动作到车辆 def is_done(self): + """ + 检查车辆是否完成任务 + + 目前为空实现,可根据需要添加到达目的地或碰撞检测逻辑 + + Returns: + bool: True表示任务完成,False表示继续执行 + """ # arrive or crash pass +# 将PolicyVehicle注册为默认车辆类型 vehicle_class_to_type[PolicyVehicle] = "default" class MultiAgentScenarioEnv(ScenarioEnv): + """ + 多智能体场景环境 + + 基于MetaDrive的ScenarioEnv扩展,支持多智能体强化学习训练。 + 主要功能包括: + 1. 从专家数据中提取车辆信息并生成可控智能体 + 2. 车道检测与过滤,确保车辆在有效区域生成 + 3. 红绿灯状态检测,为智能体提供交通信号信息 + 4. 多智能体观测、动作和奖励管理 + """ + @classmethod def default_config(cls): + """ + 获取环境的默认配置 + + 继承父类配置并添加多智能体相关的配置参数 + + 配置参数说明: + - data_directory: 专家数据目录路径 + - num_controlled_agents: 默认可控智能体数量 + - horizon: 每个回合的最大步数 + + 车道检测与过滤配置: + - filter_offroad_vehicles: 是否过滤非车道区域的车辆 + - lane_tolerance: 车道检测容差(米) + - max_controlled_vehicles: 最大可控车辆数限制(None表示不限制) + + 场景对象配置: + - no_traffic_lights: 禁用红绿灯渲染和逻辑 + + 专家数据继承配置: + - inherit_expert_velocity: 是否继承专家数据中车辆的初始速度(默认False) + + 调试模式配置: + - debug_traffic_light: 红绿灯检测详细调试输出 + - debug_lane_filter: 车道过滤详细调试输出 + - verbose_reset: 重置时输出详细统计信息 + + Returns: + dict: 包含所有配置参数的字典 + """ config = super().default_config() config.update(dict( + # 基础配置 data_directory=None, num_controlled_agents=3, horizon=1000, + # 车道检测与过滤配置 - filter_offroad_vehicles=True, # 是否过滤非车道区域的车辆 - lane_tolerance=3.0, # 车道检测容差(米),用于放宽边界条件 - max_controlled_vehicles=None, # 最大可控车辆数限制(None表示不限制) + filter_offroad_vehicles=True, + lane_tolerance=3.0, + max_controlled_vehicles=None, + + # 场景对象配置 + no_traffic_lights=False, + + # 专家数据继承配置 + inherit_expert_velocity=False, + # 调试模式配置 - debug_traffic_light=False, # 是否启用红绿灯检测调试输出 - debug_lane_filter=False, # 是否启用车道过滤调试输出 + debug_lane_filter=False, + verbose_reset=False, )) return config def __init__(self, config, agent2policy): - self.policy = agent2policy - self.controlled_agents = {} - self.controlled_agent_ids = [] - self.obs_list = [] - self.round = 0 + """ + 初始化多智能体场景环境 + + Args: + config: 环境配置字典,包含各种参数设置 + agent2policy: 智能体ID到策略的映射字典 + """ + self.policy = agent2policy # 智能体策略映射 + self.controlled_agents = {} # 可控智能体字典 {agent_id: vehicle} + self.controlled_agent_ids = [] # 可控智能体ID列表 + self.obs_list = [] # 观测数据列表 + self.round = 0 # 当前时间步 + # 调试模式配置 - self.debug_traffic_light = config.get("debug_traffic_light", False) self.debug_lane_filter = config.get("debug_lane_filter", False) + + # 调用父类初始化 super().__init__(config) def reset(self, seed: Union[None, int] = None): - self.round = 0 + """ + 重置环境到初始状态 + + 这是环境的核心重置方法,执行以下步骤: + 1. 初始化日志系统 + 2. 解析专家数据,提取车辆生成信息 + 3. 清理原始交通数据 + 4. 初始化地图和车道信息 + 5. 执行车道过滤和车辆数量限制 + 6. 生成可控智能体 + 7. 返回初始观测 + + Args: + seed: 随机种子,用于环境重置时的随机性控制 + + Returns: + list: 所有智能体的初始观测数据 + """ + self.round = 0 # 重置时间步计数器 + + # 初始化日志系统 if self.logger is None: self.logger = get_logger() log_level = self.config.get("log_level", logging.DEBUG if self.config.get("debug", False) else logging.INFO) set_log_level(log_level) + # 延迟初始化MetaDrive引擎 self.lazy_init() self._reset_global_seed(seed) if self.engine is None: raise ValueError("Broken MetaDrive instance.") + + # 在场景加载前禁用红绿灯(如果配置中启用了no_traffic_lights选项) + if self.config.get("no_traffic_lights", False): + # 重写红绿灯管理器的方法,阻止创建和使用红绿灯 + if hasattr(self.engine, 'light_manager') and self.engine.light_manager is not None: + self.engine.light_manager.before_reset = lambda *args, **kwargs: None + self.engine.light_manager.after_reset = lambda *args, **kwargs: None + self.engine.light_manager.before_step = lambda *args, **kwargs: None + self.engine.light_manager.get_traffic_light = lambda *args, **kwargs: None + self.logger.info("已禁用红绿灯管理器") + # 步骤1:解析专家数据,提取车辆生成信息 # 记录专家数据中每辆车的位置,接着全部清除,只保留位置等信息,用于后续生成 - _obj_to_clean_this_frame = [] - self.car_birth_info_list = [] + _obj_to_clean_this_frame = [] # 需要清理的对象ID列表 + self.car_birth_info_list = [] # 车辆生成信息列表 + for scenario_id, track in self.engine.traffic_manager.current_traffic_data.items(): + # 跳过自车(SDC - Self Driving Car) if scenario_id == self.engine.traffic_manager.sdc_scenario_id: continue - else: - if track["type"] == MetaDriveType.VEHICLE: - _obj_to_clean_this_frame.append(scenario_id) - valid = track['state']['valid'] - first_show = np.argmax(valid) if valid.any() else -1 - last_show = len(valid) - 1 - np.argmax(valid[::-1]) if valid.any() else -1 - # id,出现时间,出生点坐标,出生朝向,目的地 - self.car_birth_info_list.append({ - 'id': track['metadata']['object_id'], - 'show_time': first_show, - 'begin': (track['state']['position'][first_show, 0], track['state']['position'][first_show, 1]), - 'heading': track['state']['heading'][first_show], - 'end': (track['state']['position'][last_show, 0], track['state']['position'][last_show, 1]) - }) + + # 只处理车辆类型的对象 + if track["type"] == MetaDriveType.VEHICLE: + _obj_to_clean_this_frame.append(scenario_id) + valid = track['state']['valid'] # 车辆有效性标记 + # 找到车辆首次出现和最后出现的时间步 + first_show = np.argmax(valid) if valid.any() else -1 + last_show = len(valid) - 1 - np.argmax(valid[::-1]) if valid.any() else -1 + + # 提取车辆关键信息 + car_info = { + 'id': track['metadata']['object_id'], + 'show_time': first_show, + 'begin': (track['state']['position'][first_show, 0], track['state']['position'][first_show, 1]), + 'heading': track['state']['heading'][first_show], + 'end': (track['state']['position'][last_show, 0], track['state']['position'][last_show, 1]) + } + + # 如果配置要求继承专家速度,则提取初始速度 + if self.config.get("inherit_expert_velocity", False): + velocity = track['state']['velocity'][first_show] + car_info['velocity'] = (velocity[0], velocity[1]) + + self.car_birth_info_list.append(car_info) + # 非车辆对象(如红绿灯、行人等)保留,不清理 + # 清理车辆原始交通数据,释放内存(保留红绿灯等其他对象) for scenario_id in _obj_to_clean_this_frame: self.engine.traffic_manager.current_traffic_data.pop(scenario_id) + # 步骤2:重置MetaDrive引擎和传感器 self.engine.reset() self.reset_sensors() self.engine.taskMgr.step() + # 步骤3:获取地图车道信息 self.lanes = self.engine.map_manager.current_map.road_network.graph - # 调试:场景信息统计 - if self.debug_lane_filter or self.debug_traffic_light: + # 调试:场景信息统计(仅在verbose_reset模式下输出) + if self.config.get("verbose_reset", False): print(f"\n📍 场景信息统计:") print(f" - 总车道数: {len(self.lanes)}") - # 统计红绿灯数量 - if self.debug_traffic_light: + # 统计红绿灯数量(如果未禁用红绿灯) + if not self.config.get("no_traffic_lights", False): traffic_light_lanes = [] for lane in self.lanes.values(): if self.engine.light_manager.has_traffic_light(lane.lane.index): traffic_light_lanes.append(lane.lane.index) print(f" - 有红绿灯的车道数: {len(traffic_light_lanes)}") - if len(traffic_light_lanes) > 0: - print(f" 车道索引: {traffic_light_lanes[:5]}" + - (f" ... 共{len(traffic_light_lanes)}个" if len(traffic_light_lanes) > 5 else "")) - else: - print(f" ⚠️ 场景中没有红绿灯!") + if len(traffic_light_lanes) > 5: + print(f" 车道索引示例: {traffic_light_lanes[:5]} ...") + else: + print(f" - 红绿灯: 已禁用") - # 在获取车道信息后,进行车道区域过滤 + # 步骤4:执行车道区域过滤 total_cars_before = len(self.car_birth_info_list) valid_count, filtered_count, filtered_list = self._filter_valid_spawn_positions() - # 输出过滤信息 + # 输出过滤信息(仅在有过滤时输出) if filtered_count > 0: - self.logger.warning(f"车辆生成位置过滤: 原始 {total_cars_before} 辆, " - f"有效 {valid_count} 辆, 过滤 {filtered_count} 辆") - for filtered_car in filtered_list[:5]: # 只显示前5个 - self.logger.debug(f" - 过滤车辆 ID={filtered_car['id']}, " - f"位置={filtered_car['position']}, " - f"原因={filtered_car['reason']}") - if filtered_count > 5: - self.logger.debug(f" - ... 还有 {filtered_count - 5} 辆车被过滤") + if self.config.get("verbose_reset", False): + self.logger.warning(f"车辆生成位置过滤: 原始 {total_cars_before} 辆, " + f"有效 {valid_count} 辆, 过滤 {filtered_count} 辆") + for filtered_car in filtered_list[:3]: + self.logger.debug(f" 过滤车辆 ID={filtered_car['id']}, " + f"位置={filtered_car['position']}, " + f"原因={filtered_car['reason']}") + if filtered_count > 3: + self.logger.debug(f" ... 还有 {filtered_count - 3} 辆车被过滤") + else: + self.logger.info(f"车辆过滤: {total_cars_before} 辆 -> {valid_count} 辆 (过滤 {filtered_count} 辆)") - # 限制最大车辆数(在过滤后应用) + # 步骤5:限制最大车辆数(在过滤后应用) max_vehicles = self.config.get("max_controlled_vehicles", None) if max_vehicles is not None and len(self.car_birth_info_list) > max_vehicles: + original_count = len(self.car_birth_info_list) self.car_birth_info_list = self.car_birth_info_list[:max_vehicles] - self.logger.info(f"限制最大车辆数为 {max_vehicles} 辆") + if self.config.get("verbose_reset", False): + self.logger.info(f"限制最大车辆数: {original_count} 辆 -> {max_vehicles} 辆") - self.logger.info(f"最终生成 {len(self.car_birth_info_list)} 辆可控车辆") + # 最终统计 + if self.config.get("verbose_reset", False): + self.logger.info(f"✓ 最终生成 {len(self.car_birth_info_list)} 辆可控车辆") + # 清理渲染器 if self.top_down_renderer is not None: self.top_down_renderer.clear() self.engine.top_down_renderer = None - self.dones = {} - self.episode_rewards = defaultdict(float) - self.episode_lengths = defaultdict(int) + # 初始化回合相关变量 + self.dones = {} # 智能体完成状态 + self.episode_rewards = defaultdict(float) # 回合奖励累积 + self.episode_lengths = defaultdict(int) # 回合长度累积 + # 清空可控智能体 self.controlled_agents.clear() self.controlled_agent_ids.clear() + # 步骤6:调用父类重置并生成可控智能体 super().reset(seed) # 初始化场景 self._spawn_controlled_agents() + # 步骤7:返回初始观测 return self._get_all_obs() def _is_position_on_lane(self, position, tolerance=None): """ 检测给定位置是否在有效车道范围内 + 这个函数用于验证车辆生成位置是否在合法的车道上,避免在草坪、停车场等 + 非车道区域生成车辆。支持两种检测方法: + 1. 严格检测:直接使用MetaDrive的point_on_lane方法 + 2. 容差检测:考虑车道边缘的容差范围(当前已禁用) + Args: position: (x, y) 车辆位置坐标 tolerance: 容差范围(米),用于放宽检测条件。None时使用配置中的默认值 @@ -184,11 +447,13 @@ class MultiAgentScenarioEnv(ScenarioEnv): Returns: bool: True表示在车道上,False表示在非车道区域(如草坪、停车场等) """ + # 检查车道信息是否已初始化 if not hasattr(self, 'lanes') or self.lanes is None: if self.debug_lane_filter: print(f" ⚠️ 车道信息未初始化,默认允许") return True # 如果车道信息未初始化,默认允许生成 + # 设置容差参数 if tolerance is None: tolerance = self.config.get("lane_tolerance", 3.0) @@ -198,6 +463,7 @@ class MultiAgentScenarioEnv(ScenarioEnv): print(f" 🔍 检测位置 ({position_2d[0]:.2f}, {position_2d[1]:.2f}), 容差={tolerance}m") # 方法1:直接检测是否在任一车道上 + # 遍历所有车道,使用MetaDrive的point_on_lane方法进行精确检测 checked_lanes = 0 for lane in self.lanes.values(): try: @@ -207,6 +473,7 @@ class MultiAgentScenarioEnv(ScenarioEnv): print(f" ✅ 在车道上 (车道{lane.lane.index}, 检查了{checked_lanes}条)") return True except: + # 如果检测过程中出现异常,继续检查下一条车道 continue if self.debug_lane_filter: @@ -214,6 +481,7 @@ class MultiAgentScenarioEnv(ScenarioEnv): # 方法2:如果严格检测失败,使用容差范围检测(考虑车道边缘) # 注释:此方法已被禁用,如需启用请取消注释 + # 该方法通过计算点到车道中心线的横向距离来判断是否在容差范围内 # if tolerance > 0: # for lane in self.lanes.values(): # try: @@ -233,12 +501,22 @@ class MultiAgentScenarioEnv(ScenarioEnv): def _filter_valid_spawn_positions(self): """ 过滤掉生成位置不在有效车道上的车辆信息 - 根据配置决定是否执行过滤 + + 这个函数是车道过滤的核心实现,用于确保所有生成的车辆都在合法的车道上。 + 它会遍历所有车辆生成信息,使用_is_position_on_lane方法检测每个位置, + 过滤掉在草坪、停车场等非车道区域的车辆。 + + 过滤过程包括: + 1. 检查配置是否启用过滤 + 2. 遍历所有车辆生成信息 + 3. 对每个车辆位置进行车道检测 + 4. 分离有效和无效的车辆 + 5. 更新车辆生成列表 Returns: - tuple: (有效车辆数量, 被过滤车辆数量, 被过滤车辆ID列表) + tuple: (有效车辆数量, 被过滤车辆数量, 被过滤车辆详细信息列表) """ - # 如果配置中禁用了过滤,直接返回 + # 检查配置是否启用车道过滤 if not self.config.get("filter_offroad_vehicles", True): if self.debug_lane_filter: print(f"🚫 车道过滤已禁用") @@ -247,19 +525,23 @@ class MultiAgentScenarioEnv(ScenarioEnv): if self.debug_lane_filter: print(f"\n🔍 开始车道过滤: 共 {len(self.car_birth_info_list)} 辆车待检测") - valid_cars = [] - filtered_cars = [] + # 初始化过滤结果 + valid_cars = [] # 有效车辆列表 + filtered_cars = [] # 被过滤车辆列表 tolerance = self.config.get("lane_tolerance", 3.0) + # 遍历所有车辆生成信息进行检测 for idx, car in enumerate(self.car_birth_info_list): if self.debug_lane_filter: print(f"\n车辆 {idx+1}/{len(self.car_birth_info_list)}: ID={car['id']}") + # 检测车辆生成位置是否在有效车道上 if self._is_position_on_lane(car['begin'], tolerance=tolerance): valid_cars.append(car) if self.debug_lane_filter: print(f" ✅ 保留") else: + # 记录被过滤的车辆信息 filtered_cars.append({ 'id': car['id'], 'position': car['begin'], @@ -268,6 +550,7 @@ class MultiAgentScenarioEnv(ScenarioEnv): if self.debug_lane_filter: print(f" ❌ 过滤 (原因: 不在车道上)") + # 更新车辆生成列表为过滤后的结果 self.car_birth_info_list = valid_cars if self.debug_lane_filter: @@ -276,204 +559,185 @@ class MultiAgentScenarioEnv(ScenarioEnv): return len(valid_cars), len(filtered_cars), filtered_cars def _spawn_controlled_agents(self): + """ + 生成可控智能体车辆 + + 根据当前时间步和车辆生成信息,动态生成需要出现的可控车辆。 + 每个车辆都会被分配策略和目标目的地,并注册到MetaDrive引擎中 + 参与物理仿真。 + + 生成过程: + 1. 遍历所有车辆生成信息 + 2. 检查车辆是否应该在当前时间步出现 + 3. 创建PolicyVehicle实例 + 4. 设置车辆策略和目的地 + 5. 注册到环境管理和引擎中 + """ + # 注释:可以获取自车位置用于相对位置计算(当前未使用) # ego_vehicle = self.engine.agent_manager.active_agents.get("default_agent") # ego_position = ego_vehicle.position if ego_vehicle else np.array([0, 0]) + + # 遍历所有车辆生成信息 for car in self.car_birth_info_list: + # 检查车辆是否应该在当前时间步出现 if car['show_time'] == self.round: + # 生成智能体ID agent_id = f"controlled_{car['id']}" + # 在MetaDrive引擎中生成车辆对象 vehicle = self.engine.spawn_object( - PolicyVehicle, - vehicle_config={}, - position=car['begin'], - heading=car['heading'] + PolicyVehicle, # 使用自定义的策略车辆类 + vehicle_config={}, # 车辆配置(使用默认) + position=car['begin'], # 车辆生成位置 + heading=car['heading'] # 车辆生成朝向 ) + # 重置车辆状态到指定位置和朝向 vehicle.reset(position=car['begin'], heading=car['heading']) + + # 如果配置要求继承专家速度,则设置初始速度 + if 'velocity' in car and self.config.get("inherit_expert_velocity", False): + vehicle.set_velocity(car['velocity']) - vehicle.set_policy(self.policy) - vehicle.set_destination(car['end']) + # 设置车辆的控制策略和目标 + vehicle.set_policy(self.policy) # 设置策略 + vehicle.set_destination(car['end']) # 设置目的地 + # 注册到环境管理 self.controlled_agents[agent_id] = vehicle self.controlled_agent_ids.append(agent_id) # ✅ 关键:注册到引擎的 active_agents,才能参与物理更新 + # 这是MetaDrive引擎识别和管理智能体的关键步骤 self.engine.agent_manager.active_agents[agent_id] = vehicle - def _get_traffic_light_state(self, vehicle): + def _get_all_obs(self): """ - 获取车辆当前位置的红绿灯状态(优化版) + 获取所有可控智能体的观测数据 - 解决问题: - 1. 部分红绿灯状态为None的问题 - 添加异常处理和默认值 - 2. 车道分段导致无法获取红绿灯的问题 - 优先使用导航模块,失败时回退到遍历 + 这是环境的核心观测函数,为每个可控智能体组装完整的观测向量。 + 观测数据包括: + 1. 车辆状态信息:位置、速度、朝向 + 2. 传感器数据:激光雷达(前向、侧向、车道线检测) + 3. 导航信息:目标目的地 + + 观测向量结构: + - position[2]: 车辆位置 (x, y) + - velocity[2]: 车辆速度 (vx, vy) + - heading[1]: 车辆朝向角度 + - lidar[80]: 前向激光雷达数据 (80个激光束,30米范围) + - side_lidar[10]: 侧向激光雷达数据 (10个激光束,8米范围) + - lane_line_lidar[10]: 车道线检测数据 (10个激光束,3米范围) + - destination[2]: 目标目的地 (x, y) Returns: - int: 0=无红绿灯, 1=绿灯, 2=黄灯, 3=红灯 + list: 所有智能体的观测数据列表 """ - traffic_light = 0 - state = vehicle.get_state() - position_2d = state['position'][:2] + self.obs_list = [] # 清空观测列表 - if self.debug_traffic_light: - print(f"\n🚦 检测车辆红绿灯 - 位置: ({position_2d[0]:.1f}, {position_2d[1]:.1f})") - - try: - # 方法1:优先尝试从车辆导航模块获取当前车道(更高效) - if hasattr(vehicle, 'navigation') and vehicle.navigation is not None: - current_lane = vehicle.navigation.current_lane - - if self.debug_traffic_light: - print(f" 方法1-导航模块:") - print(f" current_lane = {current_lane}") - print(f" lane_index = {current_lane.index if current_lane else 'None'}") - - if current_lane: - has_light = self.engine.light_manager.has_traffic_light(current_lane.index) - - if self.debug_traffic_light: - print(f" has_traffic_light = {has_light}") - - if has_light: - status = self.engine.light_manager._lane_index_to_obj[current_lane.index].status - - if self.debug_traffic_light: - print(f" status = {status}") - - if status == 'TRAFFIC_LIGHT_GREEN': - if self.debug_traffic_light: - print(f" ✅ 方法1成功: 绿灯") - return 1 - elif status == 'TRAFFIC_LIGHT_YELLOW': - if self.debug_traffic_light: - print(f" ✅ 方法1成功: 黄灯") - return 2 - elif status == 'TRAFFIC_LIGHT_RED': - if self.debug_traffic_light: - print(f" ✅ 方法1成功: 红灯") - return 3 - elif status is None: - if self.debug_traffic_light: - print(f" ⚠️ 方法1: 红绿灯状态为None") - return 0 - else: - if self.debug_traffic_light: - print(f" 该车道没有红绿灯") - else: - if self.debug_traffic_light: - print(f" 导航模块current_lane为None") - else: - if self.debug_traffic_light: - has_nav = hasattr(vehicle, 'navigation') - nav_not_none = vehicle.navigation is not None if has_nav else False - print(f" 方法1-导航模块: 不可用 (hasattr={has_nav}, not_none={nav_not_none})") - - except Exception as e: - if self.debug_traffic_light: - print(f" ❌ 方法1异常: {type(e).__name__}: {e}") - pass - - try: - # 方法2:遍历所有车道查找(兜底方案,处理车道分段问题) - if self.debug_traffic_light: - print(f" 方法2-遍历车道: 开始遍历 {len(self.lanes)} 条车道") - - found_lane = False - checked_lanes = 0 - - for lane in self.lanes.values(): - try: - checked_lanes += 1 - if lane.lane.point_on_lane(position_2d): - found_lane = True - if self.debug_traffic_light: - print(f" ✓ 找到车辆所在车道: {lane.lane.index} (检查了{checked_lanes}条)") - - has_light = self.engine.light_manager.has_traffic_light(lane.lane.index) - if self.debug_traffic_light: - print(f" has_traffic_light = {has_light}") - - if has_light: - status = self.engine.light_manager._lane_index_to_obj[lane.lane.index].status - if self.debug_traffic_light: - print(f" status = {status}") - - if status == 'TRAFFIC_LIGHT_GREEN': - if self.debug_traffic_light: - print(f" ✅ 方法2成功: 绿灯") - return 1 - elif status == 'TRAFFIC_LIGHT_YELLOW': - if self.debug_traffic_light: - print(f" ✅ 方法2成功: 黄灯") - return 2 - elif status == 'TRAFFIC_LIGHT_RED': - if self.debug_traffic_light: - print(f" ✅ 方法2成功: 红灯") - return 3 - elif status is None: - if self.debug_traffic_light: - print(f" ⚠️ 方法2: 红绿灯状态为None") - return 0 - else: - if self.debug_traffic_light: - print(f" 该车道没有红绿灯") - break - except: - continue - - if self.debug_traffic_light and not found_lane: - print(f" ⚠️ 未找到车辆所在车道 (检查了{checked_lanes}条)") - - except Exception as e: - if self.debug_traffic_light: - print(f" ❌ 方法2异常: {type(e).__name__}: {e}") - pass - - if self.debug_traffic_light: - print(f" 结果: 返回 {traffic_light} (无红绿灯/未知)") - - return traffic_light - - def _get_all_obs(self): - # position, velocity, heading, lidar, navigation, TODO: trafficlight -> list - self.obs_list = [] + # 遍历所有可控智能体 for agent_id, vehicle in self.controlled_agents.items(): + # 获取车辆基本状态信息 state = vehicle.get_state() - # 使用优化后的红绿灯检测方法 - traffic_light = self._get_traffic_light_state(vehicle) + # 获取激光雷达传感器数据 + # 前向激光雷达:80个激光束,30米检测距离,用于障碍物检测 + lidar = self.engine.get_sensor("lidar").perceive( + num_lasers=80, + distance=30, + base_vehicle=vehicle, + physics_world=self.engine.physics_world.dynamic_world + ) + + # 侧向激光雷达:10个激光束,8米检测距离,用于侧向障碍物检测 + side_lidar = self.engine.get_sensor("side_detector").perceive( + num_lasers=10, + distance=8, + base_vehicle=vehicle, + physics_world=self.engine.physics_world.static_world + ) + + # 车道线检测激光雷达:10个激光束,3米检测距离,用于车道线识别 + lane_line_lidar = self.engine.get_sensor("lane_line_detector").perceive( + num_lasers=10, + distance=3, + base_vehicle=vehicle, + physics_world=self.engine.physics_world.static_world + ) - lidar = self.engine.get_sensor("lidar").perceive(num_lasers=80, distance=30, base_vehicle=vehicle, - physics_world=self.engine.physics_world.dynamic_world) - side_lidar = self.engine.get_sensor("side_detector").perceive(num_lasers=10, distance=8, - base_vehicle=vehicle, - physics_world=self.engine.physics_world.static_world) - lane_line_lidar = self.engine.get_sensor("lane_line_detector").perceive(num_lasers=10, distance=3, - base_vehicle=vehicle, - physics_world=self.engine.physics_world.static_world) - - obs = (state['position'][:2] + list(state['velocity']) + [state['heading_theta']] - + lidar[0] + side_lidar[0] + lane_line_lidar[0] + [traffic_light] - + list(vehicle.destination)) + # 组装完整的观测向量 + obs = (state['position'][:2] + # 位置 (x, y) + list(state['velocity']) + # 速度 (vx, vy) + [state['heading_theta']] + # 朝向角度 + lidar[0] + # 前向激光雷达数据 + side_lidar[0] + # 侧向激光雷达数据 + lane_line_lidar[0] + # 车道线检测数据 + list(vehicle.destination)) # 目标目的地 (x, y) + self.obs_list.append(obs) + return self.obs_list def step(self, action_dict: Dict[AnyStr, Union[list, np.ndarray]]): + """ + 执行环境的一个时间步 + + 这是环境的核心步进函数,执行以下操作序列: + 1. 更新时间步计数器 + 2. 执行所有智能体的动作(before_step) + 3. 更新MetaDrive物理引擎状态 + 4. 执行智能体动作后的处理(after_step) + 5. 生成新的智能体(按时间步) + 6. 获取新的观测数据 + 7. 计算奖励和完成状态 + 8. 返回环境状态 + + Args: + action_dict: 智能体动作字典 {agent_id: action} + + Returns: + tuple: (观测数据, 奖励字典, 完成状态字典, 信息字典) + - obs: 所有智能体的观测数据列表 + - rewards: 每个智能体的奖励 {agent_id: reward} + - dones: 每个智能体的完成状态 {agent_id: done, "__all__": episode_done} + - infos: 每个智能体的额外信息 {agent_id: info} + """ + # 步骤1:更新时间步计数器 self.round += 1 + # 步骤2:执行所有智能体的动作(动作执行前处理) for agent_id, action in action_dict.items(): if agent_id in self.controlled_agents: + # 记录车辆状态并设置动作 self.controlled_agents[agent_id].before_step(action) + # 步骤3:更新MetaDrive物理引擎状态 + # 这是核心的物理仿真步骤,所有车辆状态都会根据动作更新 self.engine.step() + # 步骤4:执行智能体动作后的处理 for agent_id in action_dict: if agent_id in self.controlled_agents: + # 执行动作后的状态更新(如果有after_step方法) self.controlled_agents[agent_id].after_step() + # 步骤5:生成新的智能体(按时间步动态生成) self._spawn_controlled_agents() + + # 步骤6:获取新的观测数据 obs = self._get_all_obs() + + # 步骤7:计算奖励和完成状态 + # 初始化所有智能体的奖励为0(可根据需要实现奖励计算逻辑) rewards = {aid: 0.0 for aid in self.controlled_agents} + + # 初始化所有智能体的完成状态为False(可根据需要实现完成条件) dones = {aid: False for aid in self.controlled_agents} + + # 检查整个回合是否结束(达到最大步数) dones["__all__"] = self.episode_step >= self.config["horizon"] + + # 初始化所有智能体的额外信息为空字典 infos = {aid: {} for aid in self.controlled_agents} + + # 步骤8:返回环境状态 return obs, rewards, dones, infos diff --git a/Env/test_lane_filter.py b/Env/test_lane_filter.py deleted file mode 100644 index 9a9298a..0000000 --- a/Env/test_lane_filter.py +++ /dev/null @@ -1,219 +0,0 @@ -""" -测试车道过滤和红绿灯检测功能 -""" -from scenario_env import MultiAgentScenarioEnv -from simple_idm_policy import ConstantVelocityPolicy -from metadrive.engine.asset_loader import AssetLoader -from logger_utils import setup_logger -import os - -WAYMO_DATA_DIR = r"/home/huangfukk/MAGAIL4AutoDrive/Env" - -def test_lane_filter(): - """测试车道过滤功能(基础版)""" - print("=" * 60) - print("测试1:车道过滤功能(基础)") - print("=" * 60) - - # 创建启用过滤的环境 - env = MultiAgentScenarioEnv( - config={ - "data_directory": AssetLoader.file_path(WAYMO_DATA_DIR, "exp_converted", unix_style=False), - "is_multi_agent": True, - "num_controlled_agents": 3, - "horizon": 100, - "use_render": False, - - # 车道过滤配置 - "filter_offroad_vehicles": True, - "lane_tolerance": 3.0, - "max_controlled_vehicles": 10, - }, - agent2policy=ConstantVelocityPolicy(target_speed=50) - ) - - print("\n启用车道过滤...") - obs = env.reset(0) - print(f"生成车辆数: {len(env.controlled_agents)}") - print(f"观测数据长度: {len(obs)}") - - # 运行几步 - for step in range(5): - actions = {aid: env.controlled_agents[aid].policy.act() - for aid in env.controlled_agents} - obs, rewards, dones, infos = env.step(actions) - - env.close() - print("✓ 车道过滤测试通过\n") - - -def test_lane_filter_debug(): - """测试车道过滤功能(详细调试)""" - print("=" * 60) - print("测试1b:车道过滤功能(详细调试模式)") - print("=" * 60) - - env = MultiAgentScenarioEnv( - config={ - "data_directory": AssetLoader.file_path(WAYMO_DATA_DIR, "exp_converted", unix_style=False), - "is_multi_agent": True, - "num_controlled_agents": 3, - "horizon": 100, - "use_render": False, - - # 车道过滤配置 - "filter_offroad_vehicles": True, - "lane_tolerance": 3.0, - "max_controlled_vehicles": 5, # 只看前5辆车 - - # 🔥 启用调试模式 - "debug_lane_filter": True, # 启用车道过滤调试 - }, - agent2policy=ConstantVelocityPolicy(target_speed=50) - ) - - print("\n启用车道过滤调试...") - obs = env.reset(0) - - env.close() - print("\n✓ 车道过滤调试测试完成\n") - - -def test_traffic_light(): - """测试红绿灯检测功能""" - print("=" * 60) - print("测试2:红绿灯检测功能(启用详细调试)") - print("=" * 60) - - env = MultiAgentScenarioEnv( - config={ - "data_directory": AssetLoader.file_path(WAYMO_DATA_DIR, "exp_converted", unix_style=False), - "is_multi_agent": True, - "num_controlled_agents": 3, - "horizon": 100, - "use_render": False, - "filter_offroad_vehicles": True, - "max_controlled_vehicles": 3, # 只测试3辆车 - - # 🔥 启用调试模式 - "debug_traffic_light": True, # 启用红绿灯调试 - }, - agent2policy=ConstantVelocityPolicy(target_speed=50) - ) - - obs = env.reset(0) - - # 测试红绿灯检测(调试模式会自动输出详细信息) - print(f"\n" + "="*60) - print(f"开始逐车检测红绿灯状态(共 {len(env.controlled_agents)} 辆车)") - print("="*60) - - for idx, (aid, vehicle) in enumerate(list(env.controlled_agents.items())[:3]): # 只测试前3辆 - print(f"\n【车辆 {idx+1}/3】 ID={aid}") - traffic_light = env._get_traffic_light_state(vehicle) - state = vehicle.get_state() - - status_text = {0: '无/未知', 1: '绿灯', 2: '黄灯', 3: '红灯'}[traffic_light] - print(f"最终结果: 红绿灯状态={traffic_light} ({status_text})\n") - - env.close() - print("="*60) - print("✓ 红绿灯检测测试完成") - print("="*60 + "\n") - - -def test_without_filter(): - """测试禁用过滤的情况""" - print("=" * 60) - print("测试3:禁用过滤(对比测试)") - print("=" * 60) - - env = MultiAgentScenarioEnv( - config={ - "data_directory": AssetLoader.file_path(WAYMO_DATA_DIR, "exp_converted", unix_style=False), - "is_multi_agent": True, - "num_controlled_agents": 3, - "horizon": 100, - "use_render": False, - - # 禁用过滤 - "filter_offroad_vehicles": False, - "max_controlled_vehicles": None, - }, - agent2policy=ConstantVelocityPolicy(target_speed=50) - ) - - print("\n禁用车道过滤...") - obs = env.reset(0) - print(f"生成车辆数(未过滤): {len(env.controlled_agents)}") - - env.close() - print("✓ 禁用过滤测试通过\n") - - -def run_tests(debug_mode=False): - """运行测试的主函数""" - try: - if debug_mode: - print("🐛 调试模式启用") - print("=" * 60 + "\n") - test_lane_filter_debug() - test_traffic_light() - else: - print("⚡ 标准测试模式(使用 --debug 参数启用详细调试)") - print("=" * 60 + "\n") - test_lane_filter() - test_traffic_light() - test_without_filter() - - print("\n" + "=" * 60) - print("✅ 所有测试通过!") - print("=" * 60) - print("\n功能说明:") - print("1. 车道过滤功能已启用,自动过滤非车道区域车辆") - print("2. 红绿灯检测采用双重策略,确保稳定获取状态") - print("3. 可通过配置参数灵活启用/禁用功能") - print("\n使用方法:") - print(" python Env/test_lane_filter.py # 标准测试") - print(" python Env/test_lane_filter.py --debug # 详细调试") - print(" python Env/test_lane_filter.py --log # 保存日志") - print(" python Env/test_lane_filter.py --debug --log # 调试+日志") - print("\n请运行 run_multiagent_env.py 查看完整效果") - - except Exception as e: - print(f"\n❌ 测试失败: {e}") - import traceback - traceback.print_exc() - - -if __name__ == "__main__": - import sys - - # 解析命令行参数 - debug_mode = "--debug" in sys.argv or "-d" in sys.argv - enable_logging = "--log" in sys.argv or "-l" in sys.argv - - # 提取自定义日志文件名 - log_file = None - for arg in sys.argv: - if arg.startswith("--log-file="): - log_file = arg.split("=")[1] - break - - if enable_logging: - # 启用日志记录 - log_dir = os.path.join(os.path.dirname(__file__), "logs") - - # 生成默认日志文件名 - if log_file is None: - mode_suffix = "debug" if debug_mode else "standard" - from datetime import datetime - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - log_file = f"test_{mode_suffix}_{timestamp}.log" - - with setup_logger(log_file=log_file, log_dir=log_dir): - run_tests(debug_mode=debug_mode) - else: - # 不启用日志,直接运行 - run_tests(debug_mode=debug_mode) - diff --git a/Env/test_no_traffic_lights.py b/Env/test_no_traffic_lights.py new file mode 100644 index 0000000..97d5358 --- /dev/null +++ b/Env/test_no_traffic_lights.py @@ -0,0 +1,76 @@ +"""测试禁用红绿灯功能""" +from scenario_env import MultiAgentScenarioEnv +from simple_idm_policy import ConstantVelocityPolicy +from metadrive.engine.asset_loader import AssetLoader + +WAYMO_DATA_DIR = r"/home/huangfukk/MAGAIL4AutoDrive/Env" + +def test_no_traffic_lights(): + """测试禁用红绿灯""" + print("=" * 60) + print("测试:禁用红绿灯功能") + print("=" * 60) + + env = MultiAgentScenarioEnv( + config={ + "data_directory": AssetLoader.file_path(WAYMO_DATA_DIR, "exp_converted", unix_style=False), + "is_multi_agent": True, + "num_controlled_agents": 3, + "horizon": 300, + "use_render": True, + "sequential_seed": True, + "reactive_traffic": True, + "manual_control": True, + + # 车道检测与过滤配置 + "filter_offroad_vehicles": True, + "lane_tolerance": 3.0, + "max_controlled_vehicles": 2, + + # 禁用红绿灯 + "no_traffic_lights": True, # 关键配置 + + # 调试模式 + "debug_lane_filter": False, + "verbose_reset": False, + }, + agent2policy=ConstantVelocityPolicy(target_speed=50) + ) + + print("\n重置环境...") + obs = env.reset(0) + + # 检查红绿灯管理器状态 + if hasattr(env.engine, 'light_manager') and env.engine.light_manager is not None: + num_lights = len(env.engine.light_manager._lane_index_to_obj) + print(f"✓ 红绿灯管理器中的红绿灯数量: {num_lights}") + if num_lights == 0: + print("✅ 成功:所有红绿灯已被移除!") + else: + print(f"⚠️ 警告:仍有 {num_lights} 个红绿灯") + + print("\n运行几步测试...") + for step in range(100): + actions = { + aid: env.controlled_agents[aid].policy.act() + for aid in env.controlled_agents + } + + obs, rewards, dones, infos = env.step(actions) + env.render(mode="topdown") + + if step == 0: + print(f"步骤 {step}: 环境运行正常") + + if dones["__all__"]: + break + + print(f"\n测试完成,共运行 {step+1} 步") + print("请检查渲染窗口中是否还有红绿灯显示") + + env.close() + print("=" * 60) + +if __name__ == "__main__": + test_no_traffic_lights() +