文章内容
AirSim学习笔记汇总
- AirSim学习(1)安装Unreal Engine和AirSim
- AirSim学习(2)创建UE4项目并添加AirSim插件
- AirSim学习(3)AirSim的PythonAPI基本操作——VehicleClient类
- AirSim学习(4)AirSim的PythonAPI基本操作——MultirotorClient类
- AirSim学习(5)AirSim的C++接口、AirSim与ROS的联合仿真
引言
在上一篇博客中,VehicleClient
类已经被详细解读了,这篇博客将会介绍它用于无人机仿真的一个子类MultirotorClient
。
AirSim中的机体坐标系定义
机体系X轴指向机体正前,Y轴指向机体正左,Z轴指向机体正下方,与ROS和MAVROS中常用的RFU机体坐标系不同。
class MultirotorClient
1. 构造函数
调用父类的构造函数
输入参数
- ip:字符串,表示连接的IP地址,默认为空字符串。
- port:整数,表示端口号,默认为41451。
- timeout_value:整数,表示超时时间,默认为3600秒。
2. 起飞、降落与返航
takeoffAsync
使无人机起飞至起点上方3m的位置
输入参数
timeout_sec
:整数,起飞至指定高度的超时时间,默认为20秒。vehicle_name
:字符串,无人机的名称,默认为空字符串。
输出参数
msgpackrpc.future.Future
对象,表示异步操作的未来结果,调用 .join()
等待操作完成。
landAsync
使无人机降落,输入输出与takeoffAsync
相同
goHomeAsync
使无人机返航,输入输出与takeoffAsync
相同
3. 上层控制接口
moveByVelocityBodyFrameAsync
在无人机局部NED坐标系中根据给定的速度(vx
, vy
, vz
)移动指定时间 duration
moveByVelocityZBodyFrameAsync
在无人机局部NED坐标系中根据给定的速度 (vx, vy) 和z轴位置移动指定时间 duration

moveByVelocityAsync
在世界NED坐标系中根据给定的速度(vx
, vy
, vz
)移动指定时间 duration
moveByVelocityZAsync
在世界NED坐标系中根据给定的速度 (vx, vy) 和z轴位置移动指定时间 duration
moveOnPathAsync
沿着给定的路径path
按给定速度velocity
移动无人机。
moveToPositionAsync
按给定速度velocity
移动到(x
, y
, z
)
moveToGPSAsync
按给定速度velocity
移动到GPS坐标(latitude
, longitude
, altitude
)
moveToZAsync
按给定速度velocity
移动到指定高度z
moveByManualAsync
按手动控制移动无人机
输入参数
vx_max
:x方向速度上限
vy_max
:y方向速度上限
vz_max
:z方向速度上限
z_min
:世界NED坐标系下允许的无人机最小飞行高度
duration
:执行时间,结束后切换回非手动模式
rotateToYawAsync
旋转无人机到指定的偏航角
rotateByYawRateAsync
以指定的偏航角速度旋转无人机,持续duration
时间
hoverAsync
使无人机悬停
moveByRC
使用遥控器数据移动无人机,遥控器数据类型如下
class RCData(MsgpackMixin):
timestamp = 0
pitch, roll, throttle, yaw = (0.0,)*4 #init 4 variable to 0.0
switch1, switch2, switch3, switch4 = (0,)*4
switch5, switch6, switch7, switch8 = (0,)*4
is_initialized = False
is_valid = False
def __init__(self, timestamp = 0, pitch = 0.0, roll = 0.0, throttle = 0.0, yaw = 0.0, switch1 = 0,
switch2 = 0, switch3 = 0, switch4 = 0, switch5 = 0, switch6 = 0, switch7 = 0, switch8 = 0, is_initialized = False, is_valid = False):
self.timestamp = timestamp
self.pitch = pitch
self.roll = roll
self.throttle = throttle
self.yaw = yaw
self.switch1 = switch1
self.switch2 = switch2
self.switch3 = switch3
self.switch4 = switch4
self.switch5 = switch5
self.switch6 = switch6
self.switch7 = switch7
self.switch8 = switch8
self.is_initialized = is_initialized
self.is_valid = is_valid
4. 底层控制接口
moveByMotorPWMsAsync
电机PWM控制
moveByRollPitchYawZAsync
姿态角-高度控制
moveByRollPitchYawThrottleAsync
姿态角-推力控制
moveByRollPitchYawrateThrottleAsync
姿态角速度-推力控制
setAngleRateControllerGains
设置角速度控制器增益,输入参数angle_rate_gains
类型:
class AngleRateControllerGains():
"""
Struct to contain controller gains used by angle level PID controller
Attributes:
roll_gains (PIDGains): kP, kI, kD for roll axis
pitch_gains (PIDGains): kP, kI, kD for pitch axis
yaw_gains (PIDGains): kP, kI, kD for yaw axis
"""
def __init__(self, roll_gains = PIDGains(0.25, 0, 0),
pitch_gains = PIDGains(0.25, 0, 0),
yaw_gains = PIDGains(0.25, 0, 0)):
self.roll_gains = roll_gains
self.pitch_gains = pitch_gains
self.yaw_gains = yaw_gains
def to_lists(self):
return [self.roll_gains.kp, self.pitch_gains.kp, self.yaw_gains.kp], [self.roll_gains.ki, self.pitch_gains.ki, self.yaw_gains.ki], [self.roll_gains.kd, self.pitch_gains.kd, self.yaw_gains.kd]
setAngleLevelControllerGains
设置姿态控制器增益,该增益用于含有姿态控制的上层控制API,例如moveByRollPitchYawZAsync
、moveByRollPitchYawThrottleAsync
等。输入参数angle_level_gains
类型
class AngleLevelControllerGains():
"""
Struct to contain controller gains used by angle rate PID controller
Attributes:
roll_gains (PIDGains): kP, kI, kD for roll axis
pitch_gains (PIDGains): kP, kI, kD for pitch axis
yaw_gains (PIDGains): kP, kI, kD for yaw axis
"""
def __init__(self, roll_gains = PIDGains(2.5, 0, 0),
pitch_gains = PIDGains(2.5, 0, 0),
yaw_gains = PIDGains(2.5, 0, 0)):
self.roll_gains = roll_gains
self.pitch_gains = pitch_gains
self.yaw_gains = yaw_gains
def to_lists(self):
return [self.roll_gains.kp, self.pitch_gains.kp, self.yaw_gains.kp], [self.roll_gains.ki, self.pitch_gains.ki, self.yaw_gains.ki], [self.roll_gains.kd, self.pitch_gains.kd, self.yaw_gains.kd]
setVelocityControllerGains
设置速度控制器增益,用于上层APImoveByVelocityAsync
,输入参数velocity_gains
类型:
class VelocityControllerGains():
"""
Struct to contain controller gains used by velocity PID controller
Attributes:
x_gains (PIDGains): kP, kI, kD for X axis
y_gains (PIDGains): kP, kI, kD for Y axis
z_gains (PIDGains): kP, kI, kD for Z axis
"""
def __init__(self, x_gains = PIDGains(0.2, 0, 0),
y_gains = PIDGains(0.2, 0, 0),
z_gains = PIDGains(2.0, 2.0, 0)):
self.x_gains = x_gains
self.y_gains = y_gains
self.z_gains = z_gains
def to_lists(self):
return [self.x_gains.kp, self.y_gains.kp, self.z_gains.kp], [self.x_gains.ki, self.y_gains.ki, self.z_gains.ki], [self.x_gains.kd, self.y_gains.kd, self.z_gains.kd]
设置位置控制器增益,用于上层APImoveByPositionAsync
,输入参数position_gains
类型:
class PositionControllerGains():
"""
Struct to contain controller gains used by position PID controller
Attributes:
x_gains (PIDGains): kP, kI, kD for X axis
y_gains (PIDGains): kP, kI, kD for Y axis
z_gains (PIDGains): kP, kI, kD for Z axis
"""
def __init__(self, x_gains = PIDGains(0.25, 0, 0),
y_gains = PIDGains(0.25, 0, 0),
z_gains = PIDGains(0.25, 0, 0)):
self.x_gains = x_gains
self.y_gains = y_gains
self.z_gains = z_gains
def to_lists(self):
return [self.x_gains.kp, self.y_gains.kp, self.z_gains.kp], [self.x_gains.ki, self.y_gains.ki, self.z_gains.ki], [self.x_gains.kd, self.y_gains.kd, self.z_gains.kd]
5. 查询状态
getMultirotorState
查询无人机状态,返回类型:
class MultirotorState(MsgpackMixin):
collision = CollisionInfo()
kinematics_estimated = KinematicsState()
gps_location = GeoPoint()
timestamp = np.uint64(0)
landed_state = LandedState.Landed
rc_data = RCData()
ready = False
ready_message = ""
can_arm = False
getRotorStates
返回电机状态
class RotorStates(MsgpackMixin):
timestamp = np.uint64(0)
rotors = []
官方示例程序
导入需要使用的包
import setup_path #unused
import airsim
import numpy as np
import os
import tempfile
import pprint
import cv2
初始化
# connect to the AirSim simulator
client = airsim.MultirotorClient() # 初始化无人机客户端
client.confirmConnection() # 确认与UE4的连接
client.enableApiControl(True) # 使能API控制
读取传感器数据
state = client.getMultirotorState() # 获取无人机状态
s = pprint.pformat(state)
print("state: %s" % s)
imu_data = client.getImuData() # 获取IMU数据
s = pprint.pformat(imu_data)
print("imu_data: %s" % s)
barometer_data = client.getBarometerData() # 获取气压计数据
s = pprint.pformat(barometer_data)
print("barometer_data: %s" % s)
magnetometer_data = client.getMagnetometerData() # 获取磁力计数据
s = pprint.pformat(magnetometer_data)
print("magnetometer_data: %s" % s)
gps_data = client.getGpsData() # 获取GPS数据
s = pprint.pformat(gps_data)
print("gps_data: %s" % s)
控制
airsim.wait_key('Press any key to takeoff')
print("Taking off...")
client.armDisarm(True) # 无人机解锁
client.takeoffAsync().join() # 无人机起飞
state = client.getMultirotorState() # 查询无人机状态
print("state: %s" % pprint.pformat(state))
airsim.wait_key('Press any key to move vehicle to (-10, 10, -10) at 5 m/s')
client.moveToPositionAsync(-10, 10, -10, 5).join() # 位置控制
client.hoverAsync().join()
state = client.getMultirotorState()
print("state: %s" % pprint.pformat(state))
拍照并保存
airsim.wait_key('Press any key to take images')
# get camera images from the car
responses = client.simGetImages([
airsim.ImageRequest("0", airsim.ImageType.DepthVis), #depth visualization image
airsim.ImageRequest("1", airsim.ImageType.DepthPerspective, True), #depth in perspective projection
airsim.ImageRequest("1", airsim.ImageType.Scene), #scene vision image in png format
airsim.ImageRequest("1", airsim.ImageType.Scene, False, False)]) #scene vision image in uncompressed RGBA array
print('Retrieved images: %d' % len(responses))
tmp_dir = os.path.join(tempfile.gettempdir(), "airsim_drone")
print ("Saving images to %s" % tmp_dir)
try:
os.makedirs(tmp_dir)
except OSError:
if not os.path.isdir(tmp_dir):
raise
for idx, response in enumerate(responses):
filename = os.path.join(tmp_dir, str(idx))
if response.pixels_as_float:
print("Type %d, size %d" % (response.image_type, len(response.image_data_float)))
airsim.write_pfm(os.path.normpath(filename + '.pfm'), airsim.get_pfm_array(response))
elif response.compress: #png format
print("Type %d, size %d" % (response.image_type, len(response.image_data_uint8)))
airsim.write_file(os.path.normpath(filename + '.png'), response.image_data_uint8)
else: #uncompressed array
print("Type %d, size %d" % (response.image_type, len(response.image_data_uint8)))
img1d = np.fromstring(response.image_data_uint8, dtype=np.uint8) # get numpy array
img_rgb = img1d.reshape(response.height, response.width, 3) # reshape array to 4 channel image array H X W X 3
cv2.imwrite(os.path.normpath(filename + '.png'), img_rgb) # write to png
重置、上锁、关闭API控制
airsim.wait_key('Press any key to reset to original state')
client.reset() # 重置无人机
client.armDisarm(False) # 无人机上锁
# that's enough fun for now. let's quit cleanly
client.enableApiControl(False) # 关闭API控制
例程运行效果
AirSim官方例程 hello_drone.py