CarlaAir 详细技术架构

系统架构图

┌─────────────────────────────────────────────────────────────────┐
│                    Unreal Engine 4.26                            │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │              ASimWorldGameMode (核心)                      │  │
│  │  ┌─────────────────────┐  ┌────────────────────────────┐  │  │
│  │  │  Carla 子系统        │  │  AirSim 子系统              │  │  │
│  │  │  (继承自父类)        │  │  (BeginPlay 中引导)         │  │  │
│  │  │                     │  │                            │  │  │
│  │  │  ■ CarlaEpisode     │  │  ■ ASimModeBase (Actor)    │  │  │
│  │  │  ■ Weather          │  │  ■ SimHUDWidget            │  │  │
│  │  │  ■ Traffic Manager  │  │  ■ AirSim Settings         │  │  │
│  │  │  ■ ActorDispatcher  │  │  ■ API Server (:41451)     │  │  │
│  │  │  ■ SensorManager    │  │  ■ PIPCamera System        │  │  │
│  │  │  ■ ActorFactories   │  │  ■ Physics Engine          │  │  │
│  │  │  ■ CarlaRecorder    │  │  ■ NedTransform            │  │  │
│  │  │  ■ RPC Server(:2000)│  │                            │  │  │
│  │  └─────────────────────┘  └────────────────────────────┘  │  │
│  └───────────────────────────────────────────────────────────┘  │
│                                                                 │
│  ┌──────────────────┐  ┌──────────────────┐                     │
│  │  Town10HD Map     │  │  AirSim Assets   │                     │
│  │  (Roads, Buildings│  │  (Drone model,   │                     │
│  │   Traffic Lights) │  │   Weather, HUD)  │                     │
│  └──────────────────┘  └──────────────────┘                     │
└─────────────────────────────────────────────────────────────────┘
         │                              │
         ▼                              ▼
  ┌──────────────┐              ┌──────────────┐
  │  Carla Python │              │ AirSim Python │
  │  Client API   │              │ Client API    │
  │  (port 2000)  │              │ (port 41451)  │
  └──────────────┘              └──────────────┘

初始化序列

UE4 引擎开始
  │
  ├── 1. ASimWorldGameMode 构造函数
  │     ├── Super() → 父类 ACarlaGameModeBase 初始化轮次, 记录器, 标签代理(Delegates)
  │     ├── 将默认棋子类(DefaultPawnClass)设置为空(nullptr),防止虚幻引擎自动生成棋子(会打断无人机控制)
  │     ├── 使用天气蓝图(BP_Weather)构建天气类(WeatherClass)
  │     ├── 注册 8 个动作者工厂(ActorFactory)
  │     ├── 初始化 AirSim 记录器
  │     └── 加载 AirSim 头显小工具蓝图
  │
  ├── 2. ASimWorldGameMode::BeginPlay()
  │     ├── Super::BeginPlay()
  │     │     ├── Carla 轮次(Episode)初始化
  │     │     ├── 天气(Weather)动作者生成
  │     │     ├── 动作者工厂(ActorFactory)实例化
  │     │     ├── 交通管理器启动
  │     │     └── Carla RPC 服务器启动 (port 2000)
  │     │
  │     ├── 创建观察者棋子(SpectatorPawn, 不持有)
  │     │     └── 注册到 Carla 轮次中的观察者(可通过 Carla API 移动观察者)
  │     │
  │     ├── AirSim 引导
  │     │     ├── InitializeAirSimSettings() — 读取 settings.json (地图由 Carla 加载,会忽略 AirSim 中的关卡名设置)
  │     │     ├── SetUnrealEngineSettings() — 关闭运动模糊(MotionBlur), 启用自定义景深(CustomDepth)
  │     │     ├── CreateSimMode() — SpawnActor<ASimModeWorldMultiRotor>
  │     │     │     └── SimModeBase::BeginPlay()
  │     │     │           ├── 获取 PlayerStart Transform
  │     │     │           ├── 初始化北东地变换 NedTransform(不移动世界原点!)
  │     │     │           ├── 创建 WorldSimApi
  │     │     │           └── 创建无人机棋子
  │     │     ├── CreateAirSimWidget() — 头显小工具添加到视窗(比如传感器数据显示)
  │     │     ├── SetupAirSimInputBindings() — R/T/1/2/3/0 键绑定
  │     │     └── SimMode->startApiServer() — AirSim RPC Server 启动 (port 41451)
  │     │
  │     └── 完成!两个 API 服务器同时运行
  │
  ├── 3. Tick Loop (每帧)
  │     ├── Super::Tick() — Carla Recorder tick
  │     └── AirSim Debug Report Widget 更新
  │
  └── 4. EndPlay()
        ├── SimMode->stopApiServer()
        ├── 销毁 Widget, SimMode
        └── Super::EndPlay() — Carla 清理

关键数据流

车辆生成流程

Python: world.spawn_actor(vehicle_bp, transform)
  → Carla RPC Server
    → CarlaEpisode::SpawnActor()
      → ActorDispatcher → VehicleFactory
        → UE4 World SpawnActor()
          → 物理引擎接管 (Chaos/PhysX)

无人机控制流程

Python: client.moveToPositionAsync(x, y, z, speed)
  → AirSim RPC Server
    → SimModeWorldMultiRotor
      → SimpleFlight Controller
        → NedTransform 坐标转换 (NED → UE4)
          → Drone Pawn 移动

传感器数据流

Carla 传感器:
  Camera/Lidar/Radar → SensorManager → Raw Data → RPC → Python numpy array

AirSim 传感器:
  PIPCamera → RenderTarget → simGetImages() → RPC → Python numpy array

文件修改详情

修改清单(共 3 个文件修改,2 个新文件)

文件 修改类型 修改行数 目的
SimWorldGameMode.h 新建 75 行 统一 GameMode 类声明
SimWorldGameMode.cpp 新建 548 行 统一 GameMode 完整实现
CarlaGameModeBase.h 修改 2 处 ~4 行 WeatherClass/ActorFactories → protected
CarlaEpisode.h 修改 1 处 1 行 添加 friend class ASimWorldGameMode
SimModeBase.cpp 修改 1 处 ~6 行 注释掉 SetNewWorldOrigin()

每个修改的精确位置

CarlaGameModeBase.h:

// 原始: private 区域
// 修改: 移到 protected 区域
protected:
    UPROPERTY(EditAnywhere)
    TSubclassOf<AWeather> WeatherClass;

    UPROPERTY(EditAnywhere)
    TArray<TSubclassOf<ACarlaActorFactory>> ActorFactories;

CarlaEpisode.h (~第 338 行):

// 添加:
friend class ASimWorldGameMode;

SimModeBase.cpp (~第 119 行):

// 原始:
// this->GetWorld()->SetNewWorldOrigin(FIntVector(player_loc) + FIntVector(0, 0, 0));

// 修改为注释 + 说明:
// NOTE: Do NOT call SetNewWorldOrigin() here. In CarlaAir...
global_ned_transform_.reset(new NedTransform(player_start_transform,
                                             UAirBlueprintLib::GetWorldToMetersScale(this)));

Town10HD 地图布局

                    Y=-69
                     │
    Ocean (x<20)     │    Inland City (x>55)
                     │
  ┌──────────────────┼──────────────────────────┐
  │                  │                           │
  │   Coast/Beach    │    ┌──────────────────┐   │
  │                  │    │  City Center     │   │
  │   PlayerStart    │    │  (80, 30)        │   │
  │   (x≈0, NED     │    │                  │   │
  │    origin)       │    │  Buildings,      │   │  Y=141
  │                  │    │  Roads,          │   │
  │                  │    │  Intersections   │   │
  │                  │    └──────────────────┘   │
  │                  │                           │
  └──────────────────┼──────────────────────────┘
  X=-115             │                     X=110

  NED 原点在 PlayerStart (x≈0)
  城市中心在 NED (80, 0, -30) = Carla (80, 0, 30m高)

已知限制和约束

约束 原因 解决方案
Walker 不能使用 AI Controller go_to_location() 触发四元数错误导致 segfault 使用静态 Walker
simSetCameraPose 在 Shipping 包中崩溃 C++ abort in Shipping build 不调用,使用无人机轨道 + yaw tracking
AirSim 活动时最多 ~8 个 autopilot 车辆 概率性四元数崩溃 批量启用 autopilot,延迟 0.5s
纯 CARLA 脚本可安全处理 50+ 车辆 无 AirSim 干扰 展示交通时不启动无人机