Módulo de Interface¶
Interface gráfica baseada em Qt6/PySide6 para controle de drones, visão computacional e ferramentas de sistema do ROS2.
Início rápido¶
Ou pela linha de comando:
Funcionalidades¶
Aba Control¶
Interface de controle de drones com controle de velocidade por teclado e navegação por posição.
Fluxo de conexão:
- Select Firmware + Link: Escolha o firmware (ArduPilot, PX4, Bebop, Crazyflie) e, para ArduPilot/PX4, o transporte (MAVROS, MAVLink, ou DDS). O painel de configuração se adapta à seleção.
- Connect Driver: Inicia o processo de driver em segundo plano — MAVROS (
apm.launch/px4.launch), o servidor do Crazyflie, o driver do Bebop, ouMicroXRCEAgentpara PX4 DDS. Links MAVLink diretos (ArduPilot/PX4) abrem o link do FCU dentro da própria instância, então este passo é pulado. - Initialize Instance: Cria o objeto do drone com a configuração selecionada no painel.
- Ready: Controles de voo habilitados.
Indicadores de status:
- Driver: processo de driver / agent do ROS2 em execução
- Instance: objeto do drone inicializado
- FCU: controlador de voo conectado (veículos com FCU)
- Armed: estado de armamento dos motores (veículos com FCU / Crazyflie)
Controle de velocidade:
- Teclado: W/S (cima/baixo), A/D (yaw), setas (frente/trás/esquerda/direita)
- Sliders: Ajustam a velocidade máxima por eixo (Vx, Vy, Vz, Vyaw)
- Reference Frame: Body, World, ou Takeoff
Controle de posição (veículos com FCU e Crazyflie):
- Navega até a posição alvo com offsets de X, Y, Z, Yaw
- Frames de referência: Body ou Takeoff
- Precisão e timeout configuráveis
Backends (Firmware + Link):
| Firmware / Link | Chave | Notas |
|---|---|---|
| ArduPilot / MAVROS | mavros |
Controle completo do ArduPilot (arm, takeoff, land, velocidade, posição, telemetria) pela ponte MAVROS |
| ArduPilot / MAVLink | mavlink |
Mesmo controle por um link pymavlink direto (sem MAVROS); defina a connection string e, para voo indoor, o preset do tópico de vision-pose (/visual_slam/tracking/vo_pose_covariance) |
| PX4 / MAVROS | px4 |
Streaming de setpoint OFFBOARD; a conexão usa por padrão o endpoint offboard do PX4 (udp://:14540@127.0.0.1:14580 para SITL) |
| PX4 / MAVLink | px4_mavlink |
Link pymavlink direto (sem MAVROS); padrão udp:0.0.0.0:14540 |
| PX4 / DDS | px4_dds |
uXRCE-DDS nativo. Connect Driver inicia o MicroXRCEAgent na porta UDP configurada (padrão 8888); um agent iniciado em outro lugar (por exemplo, make sim-bridge FIRMWARE=px4 PROTOCOL=dds) é detectado automaticamente. A prontidão é o aparecimento dos tópicos /fmu/* do PX4, não um processo ou nome de sessão |
| Bebop | bebop |
Controle básico (takeoff, land, velocidade, flips) |
| Crazyflie | crazyflie |
Takeoff, land, velocidade e posição a bordo (goTo) |
Os painéis de MAVROS e MAVLink são compartilhados entre ArduPilot e PX4 e se adaptam ao firmware: o padrão de conexão muda para o endpoint offboard do PX4, e as listas de presets de PID e setpoint são carregadas do config do PX4 em vez do config do ArduPilot (ambos incluem os presets SITL *_sim_*). O config de setpoint expõe os limites de velocidade/aceleração de cada firmware — WPNAV/GUID_OPTIONS do ArduPilot ou MPC_* do PX4 — com Apply setpoint params to FCU aplicando-os no momento do arm. O painel uXRCE-DDS (px4_dds) expõe a fonte de pose, a porta UDP do agent (padrão 8888), o namespace e o preset de PID — mas sem controles de setpoint/apply-setpoint, já que os parâmetros do PX4 não são transportados pelo uXRCE-DDS.
Aba Vision¶
Processamento de visão computacional em tempo real com múltiplas fontes de câmera e filtros.
Fontes de câmera: Webcam, RealSense, T265, OAK-D, C920, IMX219, tópico ROS, profundidade ROS, arquivo
Categorias de filtro:
- Color: Filtragem de cor HSV com calibração
- Edge: Detecção de bordas Canny, contornos
- Blur/Transform: Blur gaussiano, sharpen, rotação, resize
- Morphology: Erosão, dilatação, threshold adaptativo, equalização de histograma
- Effects: Pencil sketch, estilização, cartoonify, linhas/círculos de Hough, optical flow
- AI: Tracking de mãos (MediaPipe), face mesh (MediaPipe)
- Markers: Detecção ArUco (17 tipos de dicionário)
Estimativa de profundidade (câmeras de profundidade):
- Medição de distância em tempo real
- Visualização de profundidade colorizada
- Medição por clique (click-to-measure)
Aba ROS¶
Ferramentas de introspecção e interação com o sistema ROS2.
Topics:
- Navegar, subscrever e publicar mensagens
- Visualização de mensagens em tempo real
- Detecção automática de configurações de QoS
Services:
- Navegar e chamar services
- Tratamento customizado de request/response
Parameters:
- Visualizar e modificar parâmetros de nó
- Atualizações em tempo real
Plot:
- Plotagem em tempo real de campos numéricos
- Múltiplos plots, pause/resume
- Exportação para CSV
Arquitetura¶
Estrutura de alto nível¶
classDiagram
class NectarApp {
QMainWindow root
owns tabs + ROSExecutor
}
class ROSExecutor {
+node Node
+start(node_name) bool
+shutdown()
}
class ControlTab {
+set_node(node)
+cleanup()
}
class VisionTab {
+set_node(node)
+cleanup()
}
class ROSTab {
+set_node(node)
+cleanup()
}
NectarApp *-- ROSExecutor
NectarApp *-- ControlTab
NectarApp *-- VisionTab
NectarApp *-- ROSTab
Modelo de threads¶
flowchart TB
subgraph MainThread["Main Thread (Qt Event Loop)"]
UI[UI Updates]
Timers[QTimers]
end
subgraph ROSThread["ROS2 Thread"]
Executor[MultiThreadedExecutor]
Callbacks[ROS Callbacks]
end
subgraph WorkerThreads["Worker Threads"]
Workers[DriverWorker, MoveToWorker, etc.]
end
UI --> |"Start"| WorkerThreads
WorkerThreads --> |"Signals"| UI
ROSThread --> |"Signals"| UI
Pontos-chave:
- O executor do ROS2 roda em uma thread separada
- Operações bloqueantes (start do driver, navegação) usam worker threads
- Comandos de velocidade são enviados via QTimer (intervalo de 50ms)
- Todas as atualizações de UI acontecem na thread principal
Para desenvolvedores¶
Widgets¶
Componentes de UI reutilizáveis disponíveis em nectar.interface.widgets:
| Widget | Finalidade |
|---|---|
Card |
Container elevado com cantos arredondados |
StatusIndicator |
Ponto de status com label (active/inactive/warning/error) |
LabeledSlider |
Slider vertical com label e exibição de valor |
CollapsibleSection |
Seção expansível/recolhível |
VideoDisplay |
Exibição de frame OpenCV com suporte a clique |
ImageViewer |
Exibição de vídeo com label de informação |
DualVideoDisplay |
Exibição de RGB e profundidade |
DroneConfigPanel |
UI de configuração do drone |
DetectionConfigPanel |
Configuração do modelo de detecção de objetos |
MessageFieldEditor |
Editor de campos de mensagem ROS |
ParameterReconfigureWidget |
Editor de parâmetros do ROS2 |
Exemplo:
from nectar.interface import Card, StatusIndicator, LabeledSlider
card = Card()
card.add_widget(StatusIndicator("Status", "active"))
card.add_widget(LabeledSlider("Speed", 0.0, 1.0, 0.5))
ROSExecutor¶
Gerencia o nó e o executor do ROS2 em uma thread de fundo:
from nectar.interface import ROSExecutor
executor = ROSExecutor()
executor.start("my_node")
node = executor.node # Access ROS2 node
executor.shutdown()
Signals:
status_changed(bool): Status de conexão do ROS2error_occurred(str): Erros do ROS2
Worker Threads¶
Operações de longa duração usam workers QThread para evitar congelamento da UI:
- DriverWorker: Inicia/para processos de driver do ROS2
- DroneInstanceWorker: Inicializa objetos de drone
- MoveToWorker: Navegação por posição (loop PID bloqueante)
- FlightActionWorker: Chamadas de serviço (arm, takeoff, land)
- CameraInitWorker: Inicialização de câmera
Padrão:
worker = MyWorker()
worker_thread = QThread()
worker.moveToThread(worker_thread)
worker.finished.connect(worker_thread.quit)
worker_thread.started.connect(worker.run)
worker_thread.start()
Chamadas de serviço¶
As chamadas de serviço do ROS 2 usam um padrão assíncrono para evitar deadlocks. BaseDrone._call_service dispara call_async, e então bloqueia a thread chamadora em um threading.Event que é sinalizado pelo done-callback do future — o executor compartilhado (a thread de spin de fundo do SDK ou o ROSExecutor da GUI) leva o future à conclusão em sua própria thread:
future = client.call_async(request)
done = threading.Event()
future.add_done_callback(lambda _f: done.set())
done.wait(timeout=...) # calling thread blocks; the executor thread completes the future
result = future.result()
Chamadas de voo bloqueantes (takeoff, move_to, …) fazem polling da própria conclusão com _wait_until(predicate, timeout) (um loop de time.sleep(0.05)), contando com esse mesmo executor de fundo para continuar entregando os callbacks de telemetria.
Layout¶
app.py— janela principalNectarApp;ros_executor.py— integração ROS 2/Qt;theme.py— estilo e corestabs/—control_tab.py(controle de drone),vision_tab.py(câmera e filtros),ros_tab.py(ferramentas ROS 2)widgets/— componentes reutilizáveis:drone_config.py,detection_panel.py,message_editor.py,param_reconfigure.py
Temas¶
Cores definidas em theme.py:
from nectar.interface import COLORS
COLORS.background # #0A0E14
COLORS.surface # #12171E
COLORS.accent # #F5A623 (amber)
COLORS.success # #34C759 (green)
COLORS.error # #FF453A (red)
# ... see theme.py for full list
Solução de problemas¶
UI congela: Garanta que operações bloqueantes usem worker threads (não a thread principal)
Timeouts de serviço: Verifique se o driver está em execução e se o FCU está conectado
Sem telemetria: Verifique a conexão do driver e se a configuração do sensor corresponde à configuração do FCU
Câmera não funciona: Verifique as permissões do dispositivo e os nomes dos tópicos ROS
