Como construir os olhos de um autopiloto para drone de combate FPV

Aprenda a criar um módulo de vídeo para um drone de combate FPV capaz de capturar transmissões de vídeo de uma câmera FPV analógica, utilizá-las para tarefas de visão computacional e transmitir as imagens para o controlador de voo para processamento adicional (OSD) e transmissão de vídeo via VTX.

Este artigo é uma continuação natural da P&D que venho realizando por mais de um ano, focada no desenvolvimento de um autopiloto para drones de combate FPV. Você pode ler os artigos anteriores sobre este assunto, como Como Construir um Drone de Combate FPV para Defender Seu País e Voo autônomo FPV com MAVLink e Raspberry Pi. Parte II.

Quando comecei a explorar maneiras de notificar o operador FPV sobre o estado atual do autopiloto e parâmetros de voo, percebi que não havia soluções de software disponíveis dentro dos controladores de voo no firmware Betaflight. Isso me levou a considerar uma solução hardware-software.

Avaliando diferentes opções, dois fatores principais se destacaram: Primeiro, era importante manter a compatibilidade com o firmware Betaflight e a configuração popular de drones FPV sem alterá-las. Segundo, a solução precisava ter a capacidade de modificar o feed de vídeo FPV e o OSD (On-Screen Display) para mostrar o alvo reconhecido pelo modelo de visão computacional, juntamente com outros estados do autopiloto e parâmetros de voo.

Neste artigo, compartilho o que descobri ao longo do caminho.

Esquema

A ideia inicial era criar um intermediário entre a câmera FPV e o controlador de voo (FC) para sobrepor controles e mensagens adicionais na tela FPV, enquanto também processava a imagem para tarefas de visão computacional orientadas por IA. Para esta função, escolhi um Raspberry Pi junto com dois dongles: um dongle AV2USB (para recuperar imagens digitais MJPEG da câmera FPV) e um dongle HDMI2AV (para transmitir as imagens alteradas para o FC em formato analógico PAL/NTSC).

1*uRGKhojq1oDqJikjWVGjbw Como construir os olhos de um autopiloto para drone de combate FPV

Para implementar um intermediário entre a câmera FPV e o controlador de voo, decidi suportar interfaces comumente usadas, especificamente o cabo JST de 3 pinos. Nesta configuração, um cabo JST conecta o dongle AV2USB à câmera FPV, enquanto outro cabo JST conecta o controlador de voo ao dongle HDMI2AV. Ambos os dongles estão conectados ao computador acompanhante (Raspberry Pi 3 no nosso caso), permitindo que ele recupere/transmita a imagem da câmera FPV com sobreposição de vídeo específica para tarefas de CV.

1*QGmNycRP-0nM5-ox1sTxbQ Como construir os olhos de um autopiloto para drone de combate FPV

Como mostrado na imagem acima, soldei o fio vermelho (5V) do cabo USB no pad de 5V do dongle, juntamente com o wiro vermelho do cabo JST. Esta configuração permite que o cabo USB forneça energia simultaneamente tanto ao dongle quanto à câmera FPV.

Os fios ground (GND) (preto) e o wiro de sinal de vídeo (amarelo) do cabo JST estão soldados nos pads AV e G do dongle. Além disso, o wiro ground (GND) (cinza) do cabo USB também está conectado ao pad G do dongle. O wiro azul do USB é soldado no pad DP (Data+), enquanto o wiro branco do USB é soldado no pad DM (Data–) do dongle.

Uma soldagem adequada garante o funcionamento confiável do circuito.

1*kvDd2Nrq4-v0gunJiyJclw Como construir os olhos de um autopiloto para drone de combate FPV

Para conectar o dongle HDMI2AV ao FC, usei o wiro CVBS (o fio amarelo da saída PAL) e conectei-o a um cabo JST, que é conectado diretamente à placa do FC usando o mesmo cabo JST de 3 pinos dedicado para conexão de câmera FPV. Consulte o diagrama de fiação acima para mais detalhes.

Apenas dois fios do cabo CVBS estão conectados ao conector JST de 3 pinos. O fio amarelo é o fio de sinal (CAM) e conecta-se ao fio amarelo do outro lado. O fio preto é o terra (GND) e conecta-se ao terra correspondente do outro lado. O fio vermelho (5V) permanece desconectado, pois é normalmente usado para alimentar a câmera FPV. Porém, em nossa configuração, alimentamos a câmera FPV de forma diferente.

Vamos prosseguir com a parte de software.

Imports

Para implementar o recurso de sobreposição de vídeo, precisaremos importar apenas dois pacotes que serão usados ao longo do projeto: cv2 para tarefas de visão computacional, como captura de quadros da câmera FPV, e numpy para realizar operações com arrays e manipulação de dados.

import cv2
import numpy as np

Se você ainda não os instalou, agora é a hora.

Definições e Estado

Extraído dos arquivos apropriados, definitions.py e autopilot.py no repositório Autopilot “BEE” com seguimento de alvo para Drone de Combate FPV (versão Simulador), esta seção fornece as configurações e o estado atual do autopiloto que devem ser exibidos na tela FPV.

# definições
video_source = 0
video_full = 1
video_message_limit = 60
camera_width = 720
camera_height = 480
camera_fps = 1

# estado
state = {
    'bee_state' : 'OFF', # OFF, READY, ...
    'rssi':0,
    'rssi_msg':'Sinal forte',
    'frame': {},
    'video_msg': '[O controle manual está ATIVO]',
    'video_msg_countdown':0
}

Definições – configurações para o módulo de sobreposição de vídeo:

  • video_source = 0, refere-se à câmera conectada através da porta USB no RPi, como não há câmera instalada por padrão, esta é a primeira câmera com índice 0.
  • video_full = 1, significa que a sobreposição de vídeo será exibida em modo tela cheia. Defina como 0 para depuração.
  • video_message_limit = 60, limita a exibição de video_msg na tela FPV a 60 quadros.
  • camera_width e camera_height, definem a resolução para 720×480 pixels.
  • camera_fps = 1, captura 1 quadro por segundo usando OpenCV (cv2).

Estado do Autopiloto (status atual):

  • rssi_msg, pode ser definido como Sinal forte ou Sem sinal. Com base nesta mensagem, o rótulo RC e o símbolo de círculo serão exibidos na tela FPV em vermelho ou verde, indicando a força do sinal.
  • video_msg, pode conter qualquer mensagem a ser exibida na tela FPV até o número limitado de quadros, contado em video_msg_countdown.
  • frame, mantém o quadro atual, que pode ser processado pelo modelo de visão computacional para operação autônoma do drone FPV.

O estado atual e todos os estados de campo são atualizados durante a operação do autopiloto por outros componentes da aplicação.

Sobreposição de vídeo

O núcleo deste subsistema são as funções de sobreposição de vídeo, que adicionam componentes e mensagens adicionais sobre o status do autopiloto, como força do sinal RC, mensagem personalizada, alvo escalado e uma cruz no centro para direcionamento, todas exibidas na tela FPV (óculos FPV ou display FPV).

Veja o código e sua descrição sob a listagem.

# Sobreposição do autopiloto
def draw_rc_auto_status(frame):
    color_green = (0, 255, 0)
    color_red = (0, 0, 255)
    
    color = color_green if state['rssi_msg'] == 'Strong signal' else color_red
    cv2.circle(frame, (50, 50), 7, color, -1)
    
    font = cv2.FONT_HERSHEY_SIMPLEX
    cv2.putText(frame, "RC", (65, 55), font, 0.5, color, 2)

    if state['bee_state'] == 'OFF':
        cv2.putText(frame, 'MANUAL', (110, 55), font, 0.5, color_red, 1)
    else:
        cv2.putText(frame, 'AUTO', (110, 55), font, 0.5, color_green, 2)

def draw_dotted_line(frame, start, end, color, thickness, gap):
    x1, y1 = start
    x2, y2 = end
    length = int(np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2))
    for i in range(0, length, gap * 2):
        start_x = int(x1 + (x2 - x1) * i / length)
        start_y = int(y1 + (y2 - y1) * i / length)
        end_x = int(x1 + (x2 - x1) * (i + gap) / length)
        end_y = int(y1 + (y2 - y1) * (i + gap) / length)
        cv2.line(frame, (start_x, start_y), (end_x, end_y), color, thickness)

def draw_cross_target(frame):
    color_white = (255, 255, 255)
    height, width, _ = frame.shape
    center_x, center_y = width // 2, height // 2

    draw_dotted_line(frame, (center_x - 50, center_y), 
                     (center_x + 50, center_y), color_white, 2, 5)

    draw_dotted_line(frame, (center_x, center_y - 50), 
                     (center_x, center_y + 50), color_white, 2, 5)

def draw_scaled_target(frame):
    color_white = (255, 255, 255)
    rect_size = 50
    height, width, _ = frame.shape
    center_x, center_y = width // 2, height // 2
    top_left_x = center_x - rect_size // 2
    top_left_y = center_y - rect_size // 2

    center_region = frame[top_left_y:top_left_y + rect_size, 
                          top_left_x:top_left_x + rect_size]
    scaled_region = cv2.resize(center_region, (rect_size * 2, rect_size * 2), 
                               interpolation=cv2.INTER_LINEAR)

    overlay_x_start = width - rect_size * 2 - 20
    overlay_y_start = 20
    frame[overlay_y_start:overlay_y_start + rect_size * 2, 
          overlay_x_start:overlay_x_start + rect_size * 2] = scaled_region

    cv2.rectangle(frame, (overlay_x_start, overlay_y_start),
                (overlay_x_start + rect_size * 2, overlay_y_start + rect_size * 2), color_white, 1)

def draw_video_message(frame):
    font = cv2.FONT_HERSHEY_SIMPLEX
    color_white = (256, 256, 256)
    
    if state['video_msg'] != '':
        cv2.putText(frame, state['video_msg'], (43, 80), font, 0.5, color_white, 1)
        
        countdown = int(state['video_msg_countdown'])
        if countdown < video_message_limit:
            state['video_msg_countdown'] = countdown + 1
        else:
            state['video_msg'] = ''
            state['video_msg_countdown'] = 0

A função draw_rc_auto_status(frame) exibe a força do sinal RC e o modo do autopiloto na sobreposição de vídeo. Ela usa um círculo verde ou vermelho para indicar se o sinal é forte ou fraco. Também mostra o modo de voo: MANUAL em vermelho se o autopiloto estiver desligado, e AUTO em verde se estiver ativo.

A função draw_dotted_line(frame, start, end, color, thickness, gap) desenha uma linha pontilhada entre dois pontos no quadro. Ela calcula segmentos de linha com base no espaço e na espessura especificados, desenhando pequenos segmentos de linha para criar um efeito pontilhado.

Outra função, draw_cross_target(frame), desenha uma mira no centro do quadro de vídeo. Utilizando draw_dotted_line, cria um par de linhas pontilhadas horizontais e verticais que se cruzam no centro. Este auxílio visual ajuda a rastrear o ponto central da tela FPV (direção do alvo) durante a operação do drone FPV.

A função draw_scaled_target(frame) extrai e aumenta uma região central do feed de vídeo FPV, exibindo-a como um inset no canto superior direito. Captura uma seção quadrada, amplia e sobrepõe ao quadro. Isso fornece uma visão mais detalhada do alvo.

A função draw_video_message(frame) é responsável por exibir mensagens de texto na sobreposição de vídeo. Se uma mensagem existir no estado do sistema sob o campo video_msg, ela é mostrada abaixo da força do sinal RC. Isso inclui um temporizador de contagem regressiva para controlar a duração da mensagem e limpá-la uma vez que o temporizador exceda um limite predefinido (padrão: 60). Esta mensagem pode exibir qualquer texto personalizado recuperado pelo recurso de sobreposição de vídeo para a tela dos óculos FPV. É útil para exibir várias etapas da operação do Autopiloto.

Ponto de execução

A função main() implementa todas as operações necessárias para o recurso de sobreposição de vídeo, manipulando a inicialização e execução de outras funções para desenhar controles e mensagens adicionais na tela FPV.

# Função principal
def main():
    cap = cv2.VideoCapture(video_source)

    if not cap.isOpened():
        print("Erro: Não foi possível acessar a câmera.")
        exit()

    cap.set(cv2.CAP_PROP_FRAME_WIDTH, camera_width)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, camera_height)
    cap.set(cv2.CAP_PROP_FPS, camera_fps)

    if video_full:
        cv2.namedWindow("BEE", cv2.WND_PROP_FULLSCREEN)
        cv2.setWindowProperty("BEE", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)

    print("Pressione 'q' para sair.")

    while True:
        ret, frame = cap.read()

        if not ret:
            print("Erro: Não foi possível ler o quadro.")
            break

        # Salvar o quadro atual no estado para
        # Tarefas de Visão Computacional
        state['frame'] = frame

        draw_rc_auto_status(frame)
        draw_scaled_target(frame)
        draw_cross_target(frame)
        draw_video_message(frame)

        cv2.imshow('BEE', frame)

        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    cap.release()
    cv2.destroyAllWindows()

A função main() inicializa um dispositivo de captura de vídeo (por exemplo, uma câmera FPV que conectamos anteriormente) usando OpenCV, configurando sua resolução e taxa de quadros. Opicionalmente, configura a janela de exibição para o modo de tela cheia, se video_full estiver ativado (definido como 1). A função entra em um loop para ler continuamente quadros de vídeo, atualizando um dicionário compartilhado state com o quadro atual para tarefas de visão computacional.

Aplicando diversas funções de Sobreposição de Vídeo (draw_rc_auto_status, draw_scaled_target, draw_cross_target, draw_video_message) para exibir controles e mensagens na tela FPV.

A função main(), como implementada acima, é adequada apenas para fins experimentais. Para integração no Autopiloto, este código seria movido para uma thread separada e gerenciado pelo processo principal do Autopiloto. Porém, isso é um tópico para outra história.

Lançamento e uso

Uma vez que toda a fiação e soldagem estão concluídas, liguei o drone FPV com o computador acompanhante instalado nele. Em seguida, executei um script Python para verificar se o módulo de vídeo está funcionando corretamente, transmitindo com sucesso o feed de vídeo FPV da câmara FPV para o controlador de voo e subsequentemente para os óculos FPV via VTX.

1*96SnJqsug0r42kUi5IZMXg Como construir os olhos de um autopiloto para drone de combate FPV

Como mostrado na foto acima, tudo, incluindo o OSD, feed de vídeo, e sobreposição de vídeo, é exibido corretamente na tela FPV. A resolução padrão PAL de 720×480 pixels resulta em uma qualidade de imagem menos que ideal. No entanto, este nível de resolução é bastante comum para drones FPV com câmera FPV analógica a bordo.

DESCLASSIFICADO_20250109_OP_TI_FPV_VIDEO_OVERLAY_v1.0

Às vezes, gosto de adicionar um toque de mistério aos meus artigos, e este não é exceção. Para uma reviravolta criativa, usei notação militar no estilo NATO para destacar a natureza pública desses arquivos. Aproveite a jornada!

  1. Script completo compartilhado como uma gist no GitHub – Sobreposição de vídeo para drone FPV com autopiloto baseado em visão computacional.
  2. Repositório onde este módulo de vídeo pode ser utilizado e seria útil – Autopiloto para Drone de Combate FPV no Betaflight (versão vazia).

Deixe seus pensamentos nos comentários sobre o que este código NATO (acima) pode conter. Intrigante, não é? Considere isso como um desafio!

Apoie o exército ucraniano

É o terceiro ano da invasão em grande escala da Rússia na Ukraina – uma luta desafiadora e árdua pela independência e pela preservação de vidas. Como muitos de vocês ouviram das notícias internacionais, essa luta continua pesada.

Muitos de nós continuamos a apoiar defensores ucranianos através de contribuições financeiras, doações e fornecimento de componentes essenciais. Eu encorajo você a fazer o mesmo para combater esse mal global.

Use este artigo como uma das fontes para ajudá-lo a construir seu próprio sistema de autopiloto para um Drone de Combate FPV e fornecer seus dispositivos finalizados a um contato de confiança dentro das Forças Armadas Ucranianas.

Compartilhe

No Guia da Internet, simplificamos o que parece complicado! Compartilhamos conteúdos sobre tecnologia, finanças, criptomoedas e as tendências do momento de forma clara e objetiva. Seja para aprender sobre investimentos, explorar novas tecnologias ou descobrir curiosidades incríveis, aqui você sempre encontra informação confiável e acessível!

Publicar comentário