AirSim封装了一些API接口,使用这些API接口,可以用程序跟仿真进行交互。例如可以使用API来获取图片、无人机状态、控制无人机/车辆的运动等。
AirSim的 API非常丰富,有很多可以调用的功能,可以将这些功能分成以下几类:
- 图像类API :获取各种类型的图像、控制云台等;
- 控制仿真运行 :可以控制仿真暂停或继续;
- 碰撞API :获取碰撞信息,包括碰撞次数、位置、表面信息、渗透深度等;
- 环境时间API :主要是控制太阳在天空中的位置;
- 环境天气API :控制环境中的天气:下雨、雪、灰尘、雾气、道路湿滑程度等;
- 环境风API :控制环境中的风速和风向等;
- 雷达API :添加和控制环境中的雷达传感器;
- 无人机或车辆的API :控制运动、获取状态等,
python环境
需要安装msgpack-rpc-python
与airsim
两个库,python版本为3.8。
AirSim坐标系
虚幻引擎中的坐标系与 AirSim 定义的坐标系是不同的,但AirSim已经非常好的处理了这个问题,只需要按照AirSim的坐标系设置即可,AirSim会自动转换。
全局坐标系是固连到大地的,x,y,z三个坐标轴的指向分别是北,东,地,也就是朝北是x轴的正方向,朝南就是x轴的负方向。全局坐标系的原点位置是大地的某一点(可以在settings文件中设置)。
机体坐标系是固连到四旋翼机身的,x,y,z三个坐标轴的指向分别是前,右,下,也就是飞机的前方是x轴的正方向,飞机后方是x轴的负方向。机体坐标系的原点位置是机体的重心位置。
如果将 playerStart的旋转都设为0,那么仿真刚开始的时候,四旋翼的机体坐标系与全局坐标系是重合的。
起飞降落
控制无人机起飞降落的代码如下:
import airsim
# connect to the AirSim simulator
client = airsim.MultirotorClient()
client.confirmConnection()
# get control
client.enableApiControl(True)
# unlock
client.armDisarm(True)
# takeoff and land
client.takeoffAsync().join()
client.landAsync().join()
# lock
client.armDisarm(False)
# release control
client.enableApiControl(False)
下面解释这段代码:
import airsim
使用python做AirSim仿真,必须导入airsim包
client = airsim.MultirotorClient()
与AirSim建立连接,并且返回句柄(client),后面的每次操作需要使用这个句柄
如果是汽车仿真,代码是:client = airsim.CarClient()
client.confirmConnection()
检查通信是否建立成功,并且会在命令行中打印连接情况,如果连接正常会在命令行中打印如下:
Connected!
Client Ver:1 (Min Req: 1), Server Ver:1 (Min Req: 1)
client.enableApiControl(True)
因为安全问题,API控制默认是不开启的,遥控器有全部的控制权限。所以必须要在程序中使用这个函数来获取控制权
遥控器的操作会抢夺API的控制权,同时让API获取的控制权失效。使用isApiControlEnabled
可以检查API是否具有控制权
client.armDisarm(True)
使用这个函数可以让无人机的旋翼启动和停止旋转
client.takeoffAsync().join() # 起飞
client.landAsync().join() # 降落
takeoffAsync
开始起飞过程,而join()
方法等待起飞过程完成。
很多无人机或者汽车控制的函数都有Async作为后缀,这些函数在执行的时候会立即返回。这样的话,虽然任务还没有执行完,但是程序可以继续执行下去,而不用等待这个函数的任务在仿真中有没有执行完。如果想让程序在这里等待任务执行完,则只需要在后面加上.join()
。本例就是让程序在这里等待无人机起飞任务完成,然后再执行降落任务。
新的任务会打断上一个没有执行完的任务,所以如果takeoff函数没有加.join()
,则最后的表现是无人机还没有起飞就降落了,无人机是不会起飞的。
本质上说,这是异步起飞降落过程,是一个阻塞调用,代码会在此处等待,直到起飞完成。
client.armDisarm(False)
让无人机的旋翼停止旋转
client.enableApiControl(False)
释放API的控制权
位置控制
通过位置坐标控制无人机飞行轨迹代码如下:
import airsim
import time
# connect to the AirSim simulator
client = airsim.MultirotorClient()
client.enableApiControl(True) # get control
client.armDisarm(True) # unlock
client.takeoffAsync().join() # takeoff
# square flight
client.moveToZAsync(-3, 1).join() # 上升到3m高度
client.moveToPositionAsync(5, 0, -3, 1).join() # 飞到(5,0)点坐标
client.moveToPositionAsync(5, 5, -3, 1).join() # 飞到(5,5)点坐标
client.moveToPositionAsync(0, 5, -3, 1).join() # 飞到(0,5)点坐标
client.moveToPositionAsync(0, 0, -3, 1).join() # 回到(0,0)点坐标
client.landAsync().join() # land
client.armDisarm(False) # lock
client.enableApiControl(False) # release control
此代码能够实现的效果是:
- 第一阶段:起飞
- 第二阶段:上升到3米高度
- 第三阶段:飞正方形
- 向前飞 5 米
- 向右飞 5 米
- 向后飞 5 米
- 向左飞 5 米,回到起飞点
- 第四阶段:降落
轨迹如下:
client.moveToZAsync(-3, 1).join() # 上升到3m高度
moveToZAsync(z, velocity)
是高度控制API,第一个参数是高度,第二个参数是速度
以1m/s的速度飞到3米高(AirSim中,Z轴向下为正)
.join()
后缀的意思是程序在这里等待直到任务完成,也就是四旋翼达到3米的高度
client.moveToPositionAsync(5, 0, -3, 1).join() # 飞到(5,0)点坐标
client.moveToPositionAsync(5, 5, -3, 1).join() # 飞到(5,5)点坐标
client.moveToPositionAsync(0, 5, -3, 1).join() # 飞到(0,5)点坐标
client.moveToPositionAsync(0, 0, -3, 1).join() # 回到(0,0)点坐标
moveToPositionAsync(x, y, z, velocity)
是水平位置控制API,x,y,z是全局坐标位置,velocity是速度
水平位置控制API
def moveToPositionAsync(
self,
x, # 位置坐标(北东地坐标系)
y,
z,
velocity, # 飞行速度
timeout_sec=3e38, # 如果没有响应,超时时间,一般不会超时
drivetrain=DrivetrainType.MaxDegreeOfFreedom, # 设置飞行朝向模式和yaw角控制模式
yaw_mode=YawMode(),
lookahead=-1, # 设置路径飞行的时候的yaw角控制模式
adaptive_lookahead=1,
vehicle_name="",
)
偏航角控制
drivetrain 参数可以设置为两个量:
airsim.DrivetrainType.ForwardOnly
: 始终朝向速度方向airsim.DrivetrainType.MaxDegreeOfFreedom
:手动设置yaw角度
yaw_mode
必须设置为 YawMode()
类型的变量,这个结构体类型包含两个属性:
YawMode().is_rate
:True - 设置角速度;False - 设置角度YawMode().yaw_or_rate
:可以是任意浮点数
下面分几种情况讨论:
情况1 (不允许):
drivetrain = airsim.DrivetrainType.ForwardOnly
yaw_mode = airsim.YawMode(True, 0)
client.moveToPositionAsync(x, y, z, velocity, drivetrain=drivetrain, yaw_mode=yaw_mode).join()
当drivetrain = airsim.DrivetrainType.ForwardOnly
时,四旋翼始终朝向其飞行的方向,这时 yaw_mode 不允许设置为 YawMode().is_rate = True
。因为前面的参数要求四旋翼朝向运动方向,而 yaw_mode 要求四旋翼以一定的角速度旋转,这是矛盾的。
情况2:
drivetrain = airsim.DrivetrainType.ForwardOnly
yaw_mode = airsim.YawMode(False, 90)
client.moveToPositionAsync(x, y, z, velocity, drivetrain=drivetrain, yaw_mode=yaw_mode).join()
这种情况下,四旋翼的朝向始终与前进方向相差90度,也就是四旋翼始终向左侧方向运动。例如:当四旋翼在绕着一个圆心转圈时,其朝向始终指向圆心。这里的90度可以任意设置。
下图中,蓝色代表无人机朝向,红色代表无人机速度方向。在飞行至3m高度后(即图中白点),无人机顺时针旋转90°,而运动速度方向不变,也就导致了四旋翼向左侧方向运动的现象。
情况3:
drivetrain = airsim.DrivetrainType.MaxDegreeOfFreedom
yaw_mode = airsim.YawMode(False, 0)
client.moveToPositionAsync(x, y, z, velocity, drivetrain=drivetrain, yaw_mode=yaw_mode).join()
在全局坐标系下,不管速度方向是什么,四旋翼的yaw角始终等于0, 也就是其朝向始终指向正北方向。如果是90度,则始终指向正东方向,而-90度,则始终指向正西方向。
在机体坐标系下,结果如下所示:
速度方向(北东地) | yaw_or_rate | 结果 |
---|---|---|
(5, 0)正北方向 | 0 | 速度方向与无人机朝向一致 |
(5, 0)正北方向 | 90 | 无人机朝向沿速度方向顺时针旋转90度 |
(5, 0)正北方向 | -90 | 无人机朝向沿速度方向逆时针旋转90度 |
(-5, 0)正南方向 | 0 | 速度方向与无人机朝向一致 |
(-5, 0)正南方向 | 90 | 无人机朝向沿速度方向顺时针旋转90度 |
(-5, 0)正南方向 | -90 | 无人机朝向沿速度方向逆时针旋转90度 |
(0, 5)正东方向 | 90 | 无人机朝向沿速度方向顺时针旋转90度 |
(0, 5)正东方向 | -90 | 无人机朝向沿速度方向逆时针旋转90度 |
(0, -5)正西方向 | 90 | 无人机朝向沿速度方向顺时针旋转90度 |
(0, -5)正西方向 | -90 | 无人机朝向沿速度方向逆时针旋转90度 |
情况4:
drivetrain = airsim.DrivetrainType.MaxDegreeOfFreedom
yaw_mode = airsim.YawMode(True, 10)
client.moveToPositionAsync(x, y, z, velocity, drivetrain=drivetrain, yaw_mode=yaw_mode).join()
这种情况下,四旋翼不管速度方向是什么,yaw角以10度/秒的速度旋转。
上图中,白色为无人机朝向,蓝色为无人机速度方向,在无人机沿速度方向前进的过程中,无人机一直顺时针旋转,直至到达终点。
下面总结一下这两个参数的设置对效果的影响:
ForwardOnly | MaxDegreeOfFreedom | |
---|---|---|
is_rate=True | 不允许 | yaw角以yaw_or_rate度/秒旋转 |
is_rate=False | yaw角相对于速度方向偏差yaw_or_rate度 | yaw角相对正北方向偏差yaw_or_rate度 |
速度控制
import airsim
import time
client = airsim.MultirotorClient() # connect to the AirSim simulator
client.enableApiControl(True) # 获取控制权
client.armDisarm(True) # 解锁
client.takeoffAsync().join() # 第一阶段:起飞
client.moveToZAsync(-2, 1).join() # 第二阶段:上升到2米高度
# 飞正方形
client.moveByVelocityZAsync(1, 0, -2, 8).join() # 第三阶段:以1m/s速度向前飞8秒钟
client.moveByVelocityZAsync(0, 1, -2, 8).join() # 第三阶段:以1m/s速度向右飞8秒钟
client.moveByVelocityZAsync(-1, 0, -2, 8).join() # 第三阶段:以1m/s速度向后飞8秒钟
client.moveByVelocityZAsync(0, -1, -2, 8).join() # 第三阶段:以1m/s速度向左飞8秒钟
# 悬停 6 秒钟
client.hoverAsync().join() # 第四阶段:悬停6秒钟
time.sleep(6)
client.landAsync().join() # 第五阶段:降落
client.armDisarm(False) # 上锁
client.enableApiControl(False) # 释放控制权
飞行轨迹如下:
可以观察到起飞点和降落点并不重合。这是因为四旋翼是一个非线性系统,给予一个速度指令,它是不可能瞬时达到的,而且这个速度指令与当前的速度之差越大,到达这个速度指令的调节时间就越长。所以在上面的程序中,最后的四旋翼并没有回到起点位置。
client.moveByVelocityZAsync(vx, vy, z, duration).join()
速度控制函数,让四旋翼在z的高度,以vx, vy的速度,飞行duration秒。.join()是程序在这里等待任务执行完成。
client.hoverAsync().join()
让四旋翼在当前位置悬停
速度控制API
def moveByVelocityZAsync(
self,
vx, # 全局坐标系下x轴方向上的速度
vy, # 全局坐标系下y轴方向上的速度
z, # 全局坐标系下的高度
duration, # 持续的时间,单位:秒
drivetrain=DrivetrainType.MaxDegreeOfFreedom, # 用来设置偏航控制
yaw_mode=YawMode(), # 用来设置偏航控制
vehicle_name="", # 用于多机协同
)
通信机制
AirSim API 使用的是 TCP/IP 中的 msgpack-rpc 协议,这是一种网络通信协议,所以如果设置正确,其实可以用两台不同的电脑来做仿真,一台跑 AirSim 和 Unreal,另一台跑 python程序。
当 AirSim 开始仿真的时候,会打开 41451 端口,并监听这个端口的需求。python 程序使用 msgpack serialization 格式向这个端口发送 RPC 包,就可以与AirSim进行通信了。
如果计算机的41451 端口已经被其他程序使用了,那么可以通过 settings 文件改变AirSim使用的端口号。使用这种网络通信协议的方式,可以将 AirSim 和 python程序隔离,互不干扰。所以不需要修改 AirSim 的任何源代码,就可以满足非常多的仿真需求;而且即使python程序中断了,AirSim 的仿真也可以继续进行。
首先保证运行AirSim的电脑和运行python的电脑可以互相ping通。在运行AirSim的电脑上修改settings.json
文件为:
{
"RpcEnabled": true,
"RpcAddress": "该电脑的IP地址",
"RpcPort": 41451,
"SimMode": "Multirotor", // 或其他模式
// 其他设置...
}
在运行python的电脑上创建AirSim对象并通过AirSim端的ip和端口号访问。
import airsim
client = airsim.MultirotorClient(ip="AirSim电脑的IP地址", port=41451)
client.confirmConnection()
client.enableApiControl(True)
client.armDisarm(True)
# 其他 AirSim 操作...
都设置完成后,首先在运行 AirSim 的电脑上启动 Unreal 环境,然后在运行 Python 的电脑上执行 Python 脚本,即可完成通信。
标签:控制,join,yaw,AirSim,client,飞行,无人机,airsim From: https://www.cnblogs.com/xushengxiang/p/17995196