首页 > 其他分享 >飞行控制

飞行控制

时间:2024-01-29 19:45:33浏览次数:22  
标签:控制 join yaw AirSim client 飞行 无人机 airsim

​ AirSim封装了一些API接口,使用这些API接口,可以用程序跟仿真进行交互。例如可以使用API来获取图片、无人机状态、控制无人机/车辆的运动等。

​ AirSim的 API非常丰富,有很多可以调用的功能,可以将这些功能分成以下几类:

  • 图像类API :获取各种类型的图像、控制云台等;
  • 控制仿真运行 :可以控制仿真暂停或继续;
  • 碰撞API :获取碰撞信息,包括碰撞次数、位置、表面信息、渗透深度等;
  • 环境时间API :主要是控制太阳在天空中的位置;
  • 环境天气API :控制环境中的天气:下雨、雪、灰尘、雾气、道路湿滑程度等;
  • 环境风API :控制环境中的风速和风向等;
  • 雷达API :添加和控制环境中的雷达传感器;
  • 无人机或车辆的API :控制运动、获取状态等,

python环境

​ 需要安装msgpack-rpc-pythonairsim两个库,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 米,回到起飞点
  • 第四阶段:降落

轨迹如下:

position.png
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°,而运动速度方向不变,也就导致了四旋翼向左侧方向运动的现象。

yawn.png

情况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度/秒的速度旋转。

rotate.png

上图中,白色为无人机朝向,蓝色为无人机速度方向,在无人机沿速度方向前进的过程中,无人机一直顺时针旋转,直至到达终点。

下面总结一下这两个参数的设置对效果的影响:

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)      # 释放控制权

​ 飞行轨迹如下:

velectory.png

​ 可以观察到起飞点和降落点并不重合。这是因为四旋翼是一个非线性系统,给予一个速度指令,它是不可能瞬时达到的,而且这个速度指令与当前的速度之差越大,到达这个速度指令的调节时间就越长。所以在上面的程序中,最后的四旋翼并没有回到起点位置。

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

相关文章

  • CSAPP学习笔记——chapter8 异常控制流
    CSAPP学习笔记——chapter8异常控制流简介异常控制流(ExceptionalControlFlow,ECF)是在计算机系统中处理不寻常或异常情况的一种机制。它允许系统跳出正常的顺序控制流,响应那些并不直接由程序的控制流逻辑触发的事件。ECF在硬件、操作系统和应用程序层面都有体现,并且是现代计算......
  • 通过LINUX驱动控制FPGA端PWM外设(LED) 通过应用程序命令传参随意修改寄存器的值(PWM波频
    用法:先下发下面的命令让kernel信息打印到串口:echo7417>/proc/sys/kernel/printk然后增加程序可执行性:chmod777pwmdriver_app  先执行./pwmdriver_app/dev/pwm400000200然后执行./pwm_driver_app/dev/pwm400000200,可以发现LED[1]......
  • QT Creator12.0.1运行普通C/C++程序时候没有控制台输出
    问题:QTCreator12.0.1运行普通C/C++程序时候没有控制台输出菜单栏选择:[编辑]->[设置],按下图依次设置。启用终端输出,还有去掉内部终端输出的选项运行后控制台窗口正常弹出......
  • Jmeter 之 forEach控制器
    1添加方法: 线程组右键->添加->逻辑控制器->ForEach控制器 2作用:可以更方便JMeter后置处理器提取出来的多组数据,也可以定义具有特定规则的数据,用ForEach读取3各个组件介绍:输入变量前缀:变量的前缀开始循环字段:要读取的变量后缀......
  • Hive参数调优:如何控制reduce个数与参数调优(合并小文件和拆分大文件)
    reduce的个数一般最后决定了输出文件的个数,如果想多输出文件的个数(这样文件变小,但有可能程序变慢),那么可以人为增加reduce个数。如果想减少文件个数,也可以手动较少reduce个数(同样可能程序变慢)。但实际开发中,reduce的个数一般通过程序自动推定,而不人为干涉,因为人为控制的话,如果使用......
  • SpringBoot + SpEL,轻松搞定复杂权限控制
    对于在Springboot中,利用自定义注解+切面来实现接口权限的控制这个大家应该都很熟悉,也有大量的博客来介绍整个的实现过程,整体来说思路如下:自定义一个权限校验的注解,包含参数value配置在对应的接口上定义一个切面类,指定切点在切入的方法体里写上权限判断的逻辑乍一看,没毛病,学......
  • C# 守护控制台进程(process)
    C#通过Process和list配合不断检测进程并重启该进程使用语言:C#环境:.netFramework4.6.1(当前使用)(貌似支持所有环境,我就不多说了)usingSystem;usingSystem.Collections.Generic;usingSystem.Diagnostics;usingSystem.Linq;usingSystem.Text;usingSystem.Threadi......
  • 跟着思兼学习Klipper(26): 大卸八块 Klipper 远程控制实验汇总
    又名《给创想三维K1找个"强力外援"》前言原创文章,转载引用请务必注明链接,水平有限,如有疏漏,欢迎交流指正。文章如有更新请访问DFRobot社区及cnblogs博客园,前者内容较全,后者排版及阅读体验更佳。我们约定:主板指MCU部分,上位机指运行Klippy的MPULinux部分。玩了好......
  • Jmeter:逻辑控制器(二)
    一前言环境:Jmeter5.3window10本篇继续记录逻辑控制器二逻辑控制器1吞吐控制器按照官方的说法,此控制器的命名并不正确,因为它并不直接控制吞吐量该控制器用来控制其子节点的执行的频率,有两种模式执行百分比执行次数未勾选peruser的情况下,假设线程组那里配置循......
  • Golang并发控制方式有几种?
    Go语言中的goroutine是一种轻量级的线程,其优点在于占用资源少、切换成本低,能够高效地实现并发操作。但如何对这些并发的goroutine进行控制呢?一提到并发控制,大家最先想到到的是锁。Go中同样提供了锁的相关机制,包括互斥锁sync.Mutex和读写锁sync.RWMutex;除此之外Go还提供了原子操作......