使用 Betaflight 和树莓派实现 FPV 无人机自主飞行!

描述

开始利用Betaflight飞行控制器、简单的10个Python文件和树莓派,为你的FPV战斗无人机构建自己的自动驾驶仪。

FPV

FPV无人机搭载树莓派在“Kill House”机库中飞行

基于Betaflight的FPV无人机因其经济实惠和操作简便。然而,与基于ArduPilot的无人机和飞机不同,它们在设计时并未考虑自主飞行功能。这并不意味着自主飞行无法实现——只是使用Betaflight固件实现起来要复杂得多。

 

为何要在Betaflight上追求FPV自动驾驶?自主飞行具有诸多优势,例如在飞行过程中避免电子系统的干扰、在无FPV操作员控制的情况下穿越干扰区域等。然而,在Betaflight上实现这一自主飞行功能绝非易事。

作为一名热衷于推进FPV无人机技术的软件开发者,我接受了这一挑战,致力于研究和开发一种基于Betaflight的简单FPV自动驾驶仪。我的目标是开发一个初始模板,让他们能够从零开始,基于Betaflight构建自己的FPV战斗无人机自动驾驶仪。

在本文中,我将分享我的研究成果——一份详细的、逐步指导如何在Betaflight上使用树莓派构建FPV自动驾驶仪的指南。无论你是业余爱好者、发烧友还是面临类似挑战的开发者,这份指南都将为你节省数月的时间。

那么,让我们开始吧,一起创造这些令人惊叹的事物!

自动驾驶仪

与目前战场上使用的“作战就绪”自动驾驶仪不同,这款“空载驾驶仪”(自动驾驶仪)缺乏在战场上识别或跟踪目标的能力。相反,它专为基本自主操作而设计,能够向前飞行2秒并释放其有效载荷(炸弹)。

这款自动驾驶仪可作为开发者的模板,为那些希望启动自己的研发计划的人提供一个起点。我们的目标是推动技术发展,创造出能够自主进行目标轰炸,同时避免战场上电子战系统干扰的作战级自动驾驶仪。

该概念涉及使用一款在战场战场上广泛使用的标准FPV战斗无人机,并配备一台预装了基于Python的代码的伴侣计算机(如树莓派)。此设置实现了所宣称的功能,使无人机能够在没有FPV操作员参与的情况下自主飞行并释放其有效载荷。

接线图

让我们先从接线图开始。该设置采用了一款围绕Aocoda F460堆栈构建的FPV无人机,这是一款高性能系统,包括Aocoda F405 v2飞行控制器(FC)和3060S(60A)电子调速器(ESC)。下图展示了各种组件(包括舵机模块、摄像机、ELRS接收器、VTX和树莓派)如何与飞行控制器焊接在一起。

FPV

F405 v2 FC和FPV战斗无人机组件

以下是每个组件的接线详情:

摄像机:黄色(CAM)、红色(5V)、黑色(GND)

投放系统:红色(5V)、黑色(GND)、粉色(S5),对应飞行控制器(FC)上的SERVO1。

VTX(视频发射器):红色(9V)、黑色(GND)、绿色(VTX)、蓝色T4(UART4上的TX)连接到VTX上对应的RX引脚。

ELRS接收器:红色(4.5V)、黑色(GND)、蓝色(T2)、蓝色(R2)。FC上的T2和R2与UART2对齐,其中T2连接到接收器上的RX,R2连接到TX。

树莓派:使用Type-C转USB线将飞行控制器连接到树莓派,通过MSP协议进行通信。

与往常一样,ESC使用堆栈捆绑包中包含的8针线连接到FC,无需额外解释。

 

Betaflight配置

如前所述,配置FPV无人机是解决方案的关键部分。然而,我不会详细介绍所有所需的设置,而是仅关注支持自主飞行基本场景所需的关键配置。

要配置FPV无人机以进行自主飞行,我们需要将以下模式分配给无线电控制器上的适当AUX通道:

FPV

Betaflight配置器中的模式部分

ARM:用于启动和关闭电机。

ANGLE:激活ANGLE模式,使自动驾驶仪能够在稳定飞行模式下控制无人机。

ALTHOLD:保持当前高度,防止飞行过程中发生任何意外的高度变化。

BEEPER:激活无人机的蜂鸣器,在紧急情况下(如在树林中丢失无人机)可用于定位无人机。

MSP OVERRIDE:最关键的模式,允许自动驾驶仪通过在msp_override_channels_mask变量中定义的切换来接管控制权。

MSP OVERRIDE的详细配置说明将在下文的“MSP OVERRIDE”部分提供。

还需要正确配置舵机模块。如下图所示,SERVO1已设置为响应AUX2的切换。请确保在你的配置中复制此设置。

FPV

舵机部分,AUX2设置为控制SERVO1舵机设备

最后,为了完成配置,我们需要启用并设置MSP OVERRIDE模式。这需要在CLI中配置msp_override_channels_mask变量。为了我们的自动驾驶仪的目的,掩码值已计算为47,对应于位掩码00101111。

FPV

在CLI中设置msp_override_channels_mask

要设置它,请执行以下脚本。

  •  
  •  
  •  

set msp_override_channels_mask = 47saveget msp_override_channels_mask

每次更改后,请务必通过在CLI中输入save命令来保存配置。

MSP OVERRIDE

现在,你将明白为什么我们在上一节中为msp_override_channels_mask设置了位掩码00101111(47)。

Betaflight中的MSP OVERRIDE模式允许自动驾驶仪(树莓派)覆盖特定的AUX值,从而直接控制无人机。激活后,此模式允许自动驾驶仪调整ROLL、PITCH、YAW、THROTTLE和AUX值等参数。在此设置中,我们使用MSP OVERRIDE来管理无人机的方向、速度、整体飞行,并在必要时触发与SERVO1(AUX2)链接的舵机模块以释放炸弹。

Betaflight中的msp_override_channels_mask变量决定了自动驾驶仪将覆盖哪些发射机(无线电控制器)通道。在我们的场景中,通道保留用于飞行控制的ROLL、PITCH、YAW和THROTTLE,以及用于激活SERVO1的AUX2,以便在必要时释放炸弹。

需要注意的是,掩码逻辑遵循反向位顺序。为了清晰起见,请参考下图,该图解释了掩码值如何计算为47(0x00101111)以及为何选择这些通道。

FPV

msp_override_channels_mask的掩码

在msp_override_channels_mask中,值为1表示覆盖已启用,而0表示已禁用。如所示,我们为前四个专用于飞行控制的通道(ROLL、PITCH、YAW、THROTTLE)以及控制有效载荷释放的AUX2启用了覆盖。

树莓派配置

根据最初的设计,树莓派作为伴侣计算机,通过MSP协议与飞行控制器(FC)上的Betaflight固件进行通信。

虽然Betaflight最初并非为自主飞行而设计,但最近的更新引入了强大的MSP OVERRIDE模式,使我们能够开发自动驾驶仪,正如我们之前所讨论的。

FPV

安装在FPV战斗无人机电池组上的树莓派

为了开始探索自动驾驶仪,我们应按照GitHub仓库中“Betaflight上的FPV战斗无人机自动驾驶仪(空载版本)”的“树莓派配置”部分中概述的步骤进行操作,包括复制文件、设置适当的服务以及创建日志文件夹以允许收集遥测数据和一般日志。

https://github.com/under0tech/autopilot_bee_ept

架构

自动驾驶仪具有多线程架构。其核心是一个由命令路由器管理的命令队列,该路由器处理所有传入的命令。路由器在其专用线程(称为路由器线程)中运行,而另外两个线程——遥测线程和空载驾驶仪线程——则专注于将命令追加到队列中。

FPV

Betaflight的简单自动驾驶仪

遥测线程处理系统监控和遥测请求,将命令排队以向自动驾驶仪提供关键数据,如当前高度、飞机速度和实时RC通道值。而空载驾驶仪线程则管理与自主飞行和有效载荷投放相关的命令,如炸弹投放。

有关自动驾驶仪架构的更详细解释,请参阅GitHub仓库中提供的README_DEV.md文件。

https://github.com/under0tech/autopilot_bee_ept/blob/main/README_DEV.md

模式

与我之前为实验目的克隆的原始自动驾驶仪不同,我们现在拥有了一个真正的遥控器——RadioMaster Pocket M2,它允许我们根据需要切换AUX开关和相关模式。

FPV

RadioMaster Pocket M2无线电控制器

在此自动驾驶仪的实现中,我将其设计为仅具有两种模式:

1.OFF——自动驾驶仪保持非活动状态,等待操作员输入以切换到另一种模式。在此状态下,只有遥测监控和日志记录功能可用。

2.READY——激活基本功能,无人机向前飞行两秒,然后投放其有效载荷(例如,炸弹)。

模式由command_telemetry_mode_change(telemetry)函数管理,该函数依赖于AUX3的值。低值(1000)对应于OFF模式,中间值(1503)未分配,而高值(2000)则激活READY模式。

该函数在router.py文件中实现。

  •  
  •  
  •  
  •  
  •  

def command_telemetry_mode_change(telemetry):    previous_throttle = autopilot.state['throttle']    rc_chs = struct.unpack('<' + 'H' * (len(telemetry) // 2), telemetry)    autopilot.state['roll'] = rc_chs[0]    autopilot.state['pitch'] = rc_chs[1]    autopilot.state['yaw'] = rc_chs[2]    autopilot.state['throttle'] = rc_chs[3]    autopilot.state['aux1'] = rc_chs[4]    autopilot.state['aux2'] = rc_chs[5]    autopilot.state['aux3'] = rc_chs[6]    autopilot.state['aux4'] = rc_chs[7]    aux3_raw = int(autopilot.state['aux3'])    autopilot_mode = autopilot.state['bee_state']    if aux3_raw == 1000:        autopilot_mode = 'OFF'    elif aux3_raw == 1503:        mavs.prepare_go_forward(previous_throttle)        time.sleep(0.1)    elif aux3_raw == 2000:        autopilot_mode = 'READY'    if autopilot_mode != autopilot.state['bee_state']:        autopilot.state['bee_state'] = autopilot_mode        messages.display(                    messages.bee_state_changed_to, [autopilot_mode])        command_queue.queue.clear()

你可能已经注意到,中间值(1503)会触发自动驾驶仪执行其主要场景的准备步骤,尽管它并未分配一个独特的模式名称。我做出这一选择是有意的,并且完全清楚它偏离了预期的架构。然而,我认为代码中至少有一处不完美是可以接受的——细心的开发者可能会很快发现并纠正这一点。

遥测

如前所述,遥测线程负责处理系统监控和遥测请求的命令排队。它为自动驾驶仪提供关键数据,如当前高度、飞机速度和实时RC通道值。

  •  
  •  

import timeimport autopilotimport messagesimport routerimport definitions as varsdef telemetry_requestor(stop_command):    while autopilot.state['connection'] == False and not stop_command.is_set():        try:            time.sleep(5)            messages.display(messages.telemetry_process_connecting, [vars.companion_computer])        except Exception as e:            messages.display(messages.fatal_error, [e])            pass    if not stop_command.is_set():        messages.display(messages.telemetry_process_connected, [vars.companion_computer])    while not stop_command.is_set():        try:                        router.put_command(router.Command(2,'MONITOR',{'target':'MSP_ANALOG'}))            router.put_command(router.Command(2,'TELEMETRY',{'target':'MSP_ALTITUDE'}))            router.put_command(router.Command(1,'TELEMETRY',{'target':'MSP_RC'}))            time.sleep(4)        except:            pass    stopped_time = time.strftime("%H:%M:%S, %Y, %d %B", time.localtime())    messages.display(messages.telemetry_requestor_done, [stopped_time])

遥测请求以2秒的间隔添加到队列中。

如上所示,MSP_ALTITUDE和MSP_ANALOG请求每2秒发送一次,以为自动驾驶仪提供当前高度和飞机速度数据。同时,MSP_RC值对于模式切换和整体控制至关重要,因此每秒请求一次,以确保对自主飞行器的更响应和高效控制。

空载驾驶仪

空载驾驶仪线程管理与向前飞行和有效载荷投放相关的命令排队,如炸弹投放。这些命令每1秒添加到队列中。

  •  
  •  

def go_forward():    set_row_rc(        vars.default_roll,        vars.default_pitch + 20,         vars.default_yaw,         int(autopilot.state['throttle']),         vars.default_servo_aux2)     wait_for_execution('go_forward')    set_row_rc(        vars.default_roll,        vars.default_pitch,         vars.default_yaw,         int(autopilot.state['throttle']),         vars.default_servo_aux2)     wait_for_execution('go_forward')    return Truedef deliver():     set_row_rc(        vars.default_roll,        vars.default_pitch,         vars.default_yaw,         int(autopilot.state['throttle']),         2000) # 2000 to open the servo-device (to deliver the bomb)    wait_for_execution('deliver')    return True

当FPV操作员将飞行器切换到READY模式时,它会向前飞行2秒,然后投放炸弹。这一逻辑在commands.py文件中的go_forward()和deliver()函数中实现。

MSP命令

在逻辑层面,命令路由器(router.py)通过执行commands.py中的命令来管理驾驶仪逻辑。这些命令在更低的逻辑层面上封装了更精确的FPV无人机控制。

  •  
  •  

import serialimport timeimport autopilotimport definitions as varsimport msp_helper as mspcommand_delays =  {    'go_forward': 2,    'deliver': 1}command_target_ids = {    'MSP_ANALOG': msp.MSP_ANALOG,    'MSP_ALTITUDE': msp.MSP_ALTITUDE,    'MSP_RC': msp.MSP_RC}serial_port = {}def wait_for_execution(target, delay=0):    if delay == 0:        delay = command_delays.get(target)    time.sleep(delay)def get_target_id(target):    return int(command_target_ids.get(target))def connect():    global serial_port    serial_port = serial.Serial(        vars.companion_computer,         vars.companion_baud_rate,         timeout=1)def disconnect():    serial_port.close()def reboot():    disconnect()    time.sleep(1)    connect()def set_row_rc(roll, pitch, yaw, throttle, servo_aux):    # ROLL/PITCH/THROTTLE/YAW/AUX1/AUX2/AUX3/AUX4    data = [roll,             pitch,             throttle,             yaw, 0,             servo_aux, 0, 0]    msp.send_msp_command(serial_port, msp.MSP_SET_RAW_RC, data)    msp_command_id, payload = msp.read_msp_response(serial_port)    if msp_command_id != msp.MSP_SET_RAW_RC:        return False    return Truedef copter_init():    # connect()    return set_row_rc(        vars.default_roll,        vars.default_pitch,         vars.default_yaw,         vars.default_throttle,         vars.default_servo_aux2)def telemetry(target):    msp_target_command_id = get_target_id(target)    msp.send_msp_request(serial_port, msp_target_command_id)    time.sleep(0.1)    msp_command_id, payload = msp.read_msp_response(serial_port)    if msp_command_id == msp_target_command_id:        return payload    return {}def prepare_go_forward(throttle):    set_row_rc(        vars.default_roll,        vars.default_pitch,         vars.default_yaw,         int(throttle),         vars.default_servo_aux2) 

如上所示,它处理主要场景的准备prepare_go_forward(throttle)、遥测请求telemetry(target)和系统监控。此外,它还管理四轴飞行器的初始化connect(),并将AUX通道配置为某些特定值set_row_rc(roll, pitch, yaw, throttle, servo_aux),从而在无人机自动飞行期间实现自主控制和操作。

MSP助手

在自主飞行过程中,无人机由Betaflight固件通过伴侣计算机(树莓派)使用MSP协议进行控制,如前所述。该协议提供有关无人机当前状态的遥测数据,并启用飞行控制(偏航、俯仰、横滚、油门)以及通过切换AUX通道来控制伺服机构。

为了封装所有与低级或协议特定通信相关的功能,我实现了msp_helper.py模块。

  •  
  •  
  •  

import struct# MSP command IDsMSP_ANALOG = 110MSP_ALTITUDE = 109MSP_RC = 105MSP_SET_RAW_RC = 200def get_checksum(msp_command_id, payload):    checksum = 0    length = len(payload)    for byte in bytes([length, msp_command_id]) + payload:        checksum ^= byte    checksum &= 0xFF    return checksumdef send_msp_command(serial_port, msp_command_id, data):    payload = bytearray()    for value in data:        payload += struct.pack('<1H', value)    header = b'$M<'    length = len(payload)    checksum = get_checksum(msp_command_id, payload)    msp_package = header + bytes([length, msp_command_id]) + payload + bytes([checksum])    serial_port.write(msp_package)def send_msp_request(serial_port, msp_command_id):    header = b'$M<'    length = 0    checksum = get_checksum(msp_command_id, bytes([]))    msp_package = header + struct.pack(', length, msp_command_id) + bytes([checksum])    serial_port.write(msp_package)def read_msp_response(serial_port):    response = serial_port.readline()    if response.startswith(b'$M>'):        length = response[3]        msp_command_id = response[4]        payload = response[5:5 + length]        return msp_command_id, payload    else:        raise ValueError("Invalid MSP response")

如上所示,msp_helper.py封装了发送MSP命令、发送MSP请求、计算校验和以及读取MSP响应等命令。为了我们的自动驾驶仪的目的,我只包含了四个MSP命令:MSP_ANALOG、MSP_ALTITUDE、MSP_RC和MSP_SET_RAW_RC。然而,MSP协议还支持许多其他命令。

消息和日志

在运行过程中,自动驾驶仪会生成并向各种目标(包括应用程序控制台)发送消息,同时还会将消息和日志存储在日志文件中。每次自动驾驶仪启动时都会创建一个新的日志文件。

FPV

FPV无人机的飞行记录

日志文件保存在Logs文件夹中,可以在无人机飞行后的任何时间进行查看。与飞机上的“黑匣子”类似,这些文件有助于分析飞行情况并调查可能发生的任何紧急情况。

我强烈建议将logger.py设置为DEBUG模式以捕获详细信息。这一做法将最大限度地从日志中学习,并促进你的FPV战斗无人机版本的自动驾驶仪系统随着每次飞行而不断改进。

下一步是什么?

本指南为构建与Betaflight固件兼容的自动驾驶仪提供了一个基础模板。如果你正在考虑开发自动驾驶仪,那么本指南将是一个很好的起点。你可以通过融入计算机视觉、目标识别、目标跟踪、目标跟随、巡航控制等高级功能来增强它。

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分