UE4中使用飞控仿真插件——AirSim

1. 简述

项目中使用UE4开发无人机飞控的仿真,所以了解并学习了部分AirSim的机制,微软开源了基于虚幻引擎的一款用于模拟无人机飞行的工具AirSim。通过AirSim平台可以理解自动驾驶仪在真实世界中是如何行驶的,同时可以在该平台使用深度学习技术来理解这些运输工具在各种不同的环境下是如何反应的。可以通过飞行控制板或模拟器进行硬件模拟输入,这里我使用了纯软件仿真。
微软官方开源项目地址
官方文档说明

2.简单配置
根据官方说明或网络上其他安装步骤,将项目clone并编译,将AirSim应用到你自己的UE4工程:具体操作如下:

  1. 复制Unreal\Plugins文件夹从你在上面部分做的构建到你的虚幻项目的文件夹的根
  2. 在虚幻项目的.uproject文件中,向“Modules”对象添加关键的额外依赖项
    "AdditionalDependencies": [ "AirSim" ]
    -Plugins部分
    "Plugins": [ { "Name": "AirSim", "Enabled": true } ]
    添加完后类似这样:
{
	"FileVersion": 3,
	"EngineAssociation": "4.24",
	"Category": "Samples",
	"Description": "",
	"Modules": [
		{
			"Name": "LandscapeMountains",
			"Type": "Runtime",
			"LoadingPhase": "Default",
			"AdditionalDependencies": [
				"AirSim"
			]
		}
	],
	"TargetPlatforms": [
		"MacNoEditor",
		"WindowsNoEditor"
	],
	"Plugins": [
		{
			"Name": "AirSim",
			"Enabled": true
		}
	]
}

3.修改setting.json文件

设置文件默认都在C:\Users\Admin\Documents\AirSim\settings.json文件下,完整的用法参考如下:一般不需要全部使用,需要的再做添加:

{
  "SimMode": "",
  "ClockType": "",
  "ClockSpeed": 1,
  "LocalHostIp": "127.0.0.1",
  "RecordUIVisible": true,
  "LogMessagesVisible": true,
  "ViewMode": "",
  "RpcEnabled": true,
  "EngineSound": true,
  "PhysicsEngineName": "",
  "SpeedUnitFactor": 1.0,
    "SpeedUnitLabel": "m/s",
  "Recording": {
    "RecordOnMove": false,
    "RecordInterval": 0.05,
    "Cameras": [
        { "CameraName": "0", "ImageType": 0, "PixelsAsFloat": false, "Compress": true }
    ]
  },
  "CameraDefaults": {
    "CaptureSettings": [
      {
        "ImageType": 0,
        "Width": 256,
        "Height": 144,
        "FOV_Degrees": 90,
        "AutoExposureSpeed": 100,
        "AutoExposureBias": 0,
        "AutoExposureMaxBrightness": 0.64,
        "AutoExposureMinBrightness": 0.03,
        "MotionBlurAmount": 0,
        "TargetGamma": 1.0,
        "ProjectionMode": "",
        "OrthoWidth": 5.12
      }
    ],
    "NoiseSettings": [
      {
        "Enabled": false,
        "ImageType": 0,

        "RandContrib": 0.2,
        "RandSpeed": 100000.0,
        "RandSize": 500.0,
        "RandDensity": 2,

        "HorzWaveContrib":0.03,         
        "HorzWaveStrength": 0.08,
        "HorzWaveVertSize": 1.0,
        "HorzWaveScreenSize": 1.0,

        "HorzNoiseLinesContrib": 1.0,
        "HorzNoiseLinesDensityY": 0.01,
        "HorzNoiseLinesDensityXY": 0.5,

        "HorzDistortionContrib": 1.0,
        "HorzDistortionStrength": 0.002
      }
    ],
    "Gimbal": {
      "Stabilization": 0,
      "Pitch": NaN, "Roll": NaN, "Yaw": NaN
    }
    "X": NaN, "Y": NaN, "Z": NaN,
    "Pitch": NaN, "Roll": NaN, "Yaw": NaN    
  },
  "OriginGeopoint": {//初始生成点的相对于世界的经纬度
    "Latitude": 47.641468,
    "Longitude": -122.140165,
    "Altitude": 122
  },
  "TimeOfDay": {
    "Enabled": false,
    "StartDateTime": "",
    "CelestialClockSpeed": 1,
    "StartDateTimeDst": false,
    "UpdateIntervalSecs": 60
  },
  "SubWindows": [//需要几个子窗口
    {"WindowID": 0, "CameraName": "0", "ImageType": 3, "Visible": false},
    {"WindowID": 1, "CameraName": "0", "ImageType": 5, "Visible": false},
    {"WindowID": 2, "CameraName": "0", "ImageType": 0, "Visible": false}    
  ],
  "SegmentationSettings": {
    "InitMethod": "",
    "MeshNamingMethod": "",
    "OverrideExisting": false
  },
  "PawnPaths": {
    "BareboneCar": {"PawnBP": "Class'/AirSim/VehicleAdv/Vehicle/VehicleAdvPawn.VehicleAdvPawn_C'"},
    "DefaultCar": {"PawnBP": "Class'/AirSim/VehicleAdv/SUV/SuvCarPawn.SuvCarPawn_C'"},
    "DefaultQuadrotor": {"PawnBP": "Class'/AirSim/Blueprints/BP_FlyingPawn.BP_FlyingPawn_C'"},
    "DefaultComputerVision": {"PawnBP": "Class'/AirSim/Blueprints/BP_ComputerVisionPawn.BP_ComputerVisionPawn_C'"}
  },
  "Vehicles": {//这里配置多机的状态
    "SimpleFlight": {
      "VehicleType": "SimpleFlight",
      "DefaultVehicleState": "Armed",
      //Armed螺旋桨处于活跃状态,取消为Inactive
      "AutoCreate": true,
      "PawnPath": "",
      "EnableCollisionPassthrogh": false,
      "EnableCollisions": true,
      "AllowAPIAlways": true,//允许api控制,重要
      "RC": {//远程控制 switch PX4
        "RemoteControlID": 0,
        "AllowAPIWhenDisconnected": false
      },
      "Cameras": {   
        //相同的元素,作为元素内部的相机参考上面
      },
      "X": NaN, "Y": NaN, "Z": NaN,
      "Pitch": NaN, "Roll": NaN, "Yaw": NaN
    },
    "PhysXCar": {
      "VehicleType": "PhysXCar",
      "DefaultVehicleState": "",
      "AutoCreate": true,
      "PawnPath": "",
      "EnableCollisionPassthrogh": false,
      "EnableCollisions": true,
      "RC": {
        "RemoteControlID": -1
      },
      "Cameras": {   
        "MyCamera1": {
          //相同的元素,作为元素内部的相机参考上面
        },
        "MyCamera2": {
          //相同的元素,作为元素内部的相机参考上面
        },        
      },
      "X": NaN, "Y": NaN, "Z": NaN,
      "Pitch": NaN, "Roll": NaN, "Yaw": NaN      
    }
  }
}

特别地,

   "Lidar1": { 
             
             "DrawDebugPoints": true   //绘制激光雷达
        }

4.开发示例
仿照ShellDemo,我们来创建一个自定义的类,通过键盘输入来调用它:这里我需要实现的目的是——多机同时飞行,按多航路点,阵型等,所以这里用到了线程来保证每一个飞机及独立飞行没有延迟,这里作为示例开两个线程。

	class MultiCommand : public DroneCommand {
		public:
			MultiCommand() : DroneCommand("Multi", "Test Muti fly?")
			{
			}

			bool execute(const DroneCommandParameters& params)
			{
				DWORD dwThreadId1;//线程ID
				DWORD dwThreadId2;

				HANDLE hThread1 = CreateThread(NULL, 0, TakeProc1, NULL, 0, &dwThreadId1);
				HANDLE hThread2 = CreateThread(NULL, 0, TakeProc2, NULL, 0, &dwThreadId2);
				
				params.context->client.enableApiControl(true,"Drone1");


				params.context->client.armDisarm(true, "Drone1");
			  
				float takeoffTimeout = 5;
				params.context->client.takeoffAsync(takeoffTimeout,"Drone1")->waitOnLastTask();//起飞
			
				auto position = params.context->client.getMultirotorState("Drone1").getPosition();
				float z = position.z(); // 当前位置 (NED coordinate system).
				const float speed = 3.0f;
				const float size = 10.0f;//以3米/秒的速度以10米阈值飞行
				const float duration = size / speed;//持续时间
				DrivetrainType driveTrain = DrivetrainType::ForwardOnly;
				YawMode yaw_mode(true, 0);
				std::cout << "moveByVelocityZ(" << speed << ", 0, " << z << "," << duration << ")" << std::endl;
				params.context->client.moveByVelocityZAsync(speed, 0, z, duration, driveTrain, yaw_mode);
				std::this_thread::sleep_for(std::chrono::duration<double>(duration));

				std::cout << "moveByVelocityZ(0, " << speed << "," << z << "," << duration << ")" << std::endl;
				params.context->client.moveByVelocityZAsync(0, speed, z, duration, driveTrain, yaw_mode);
				std::this_thread::sleep_for(std::chrono::duration<double>(duration));

				params.context->client.hoverAsync()->waitOnLastTask();
				std::this_thread::sleep_for(std::chrono::duration<double>(10));

				CloseHandle(hThread1);
				printf("线程1关闭\n");
				CloseHandle(hThread2);
				printf("线程2关闭\n");

				printf("FINISH-DONE\r\n");
				return false;
			}
		};

线程写法:这里演示线程2——在四个点绕圈飞行

DWORD WINAPI TakeProc2(LPVOID lpParam)
{
	MultirotorRpcLibClient client;

	client.enableApiControl(true, "Drone3");
	client.armDisarm(true, "Drone3");

	float takeoffTimeout = 5;
	client.takeoffAsync(takeoffTimeout, "Drone3")->waitOnLastTask();

	auto position = client.getMultirotorState("Drone3").getPosition();
	float z = position.z(); //当前位置(NED coordinate system).  
	const float speed = 3.0f;
	const float size = 10.0f;
	const float duration = size / speed;
	DrivetrainType driveTrain = DrivetrainType::ForwardOnly;
	YawMode yaw_mode(true, 0);
	std::cout << "Drone3(" << speed << ", 0, " << z << "," << duration << ")" << std::endl;
	client.moveByVelocityZAsync(speed, 0, z, duration, driveTrain, yaw_mode, "Drone3");
	std::this_thread::sleep_for(std::chrono::duration<double>(duration));
	std::cout << "Drone3(0, " << speed << "," << z << "," << duration << ")" << std::endl;
	client.moveByVelocityZAsync(0, speed, z, duration, driveTrain, yaw_mode, "Drone3");
	std::this_thread::sleep_for(std::chrono::duration<double>(duration));
	std::cout << "Drone3(" << -speed << ", 0, " << z << "," << duration << ")" << std::endl;
	client.moveByVelocityZAsync(-speed, 0, z, duration, driveTrain, yaw_mode, "Drone3");
	std::this_thread::sleep_for(std::chrono::duration<double>(duration));
	std::cout << "Drone3(0, " << -speed << "," << z << "," << duration << ")" << std::endl;
	client.moveByVelocityZAsync(0, -speed, z, duration, driveTrain, yaw_mode, "Drone3");
	std::this_thread::sleep_for(std::chrono::duration<double>(duration));

	client.hoverAsync()->waitOnLastTask();
	return 0;
}

当然,不要忘记更改settings.json的内容哦,多机的示例配置如下:

{
  "SettingsVersion": 1.2,
  "SimMode": "Multirotor",
  "OriginGeopoint": {
    "Latitude": 47.641468,
    "Longitude": -122.140165,
    "Altitude": 122
  },
  "SubWindows": [
    {"WindowID": 0, "ImageType": 0, "CameraName": "3", "Visible": true},
    {"WindowID": 1, "ImageType": 3, "CameraName": "0", "Visible": true},
    {"WindowID": 2, "ImageType": 6, "CameraName": "4", "Visible": true}
  ],
  "SettingsVersion": 1.2,
    "SimMode": "Multirotor",

    "Vehicles": {
        "Drone1": {
          "VehicleType": "SimpleFlight",
          "X": 4, "Y": 0, "Z": -2,
      "Yaw": -180
        },
        "Drone2": {
          "VehicleType": "SimpleFlight",
          "AutoCreate": true,
          "X": 8, "Y": 0, "Z": -2
        },
		 "Drone3": {
          "VehicleType": "SimpleFlight",
          "AutoCreate": true,
          "X": 12, "Y": 0, "Z": 0
        },
         "Drone4": {
          "VehicleType": "SimpleFlight",
          "AutoCreate": true,
          "X": 16, "Y": 0, "Z": 0
        },
     	 "Drone5": {
          "VehicleType": "SimpleFlight",
          "AutoCreate": true,
          "X": 20, "Y": 0, "Z": 0
        }
    
    }
    
}

至此,我们完成了插件对无人机最基本的操作,通过后台代码控制完成想要的效果。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐