Transporte MAVROS¶
MavrosTransport conecta o núcleo do veículo compartilhado a um mavros_node em execução. Ele apoia o mesmo veículo sobre MAVROS para os dois firmwares (MavrosDrone para ArduPilot, Px4MavrosDrone para PX4) — o comportamento de voo agnóstico a firmware, a navegação, o takeoff/land, o GPS, o RTL e o PID são compartilhados e documentados no núcleo do veículo (as especificidades de setpoint/parâmetro do ArduPilot estão em ArduPilot).
Para a API pública do
Drone, os métodos de navegação, as referências, as fontes de altitude, a detecção de takeoff/land e a configuração de PID, veja o README do núcleo do veículo.
Papel¶
MavrosTransport implementa a interface VehicleTransport contra o MAVROS:
- Telemetria ← subscriptions do ROS. Cada callback converte uma mensagem
mavros_msgs/geometry_msgs/sensor_msgsem um valor simples devehicle/typese o armazena atomicamente. - Comandos → service clients do ROS (
/mavros/set_mode,/mavros/cmd/*,/mavros/param/set_parameters). - Setpoints → publishers do ROS (
/mavros/setpoint_raw/local,/mavros/setpoint_position/global). - A conversão de frame é feita pelo próprio MAVROS (ENU↔NED), então o transporte publica valores na convenção ROS diretamente.
classDiagram
class VehicleTransport {
<<abstract>>
+state local_pose vision_pose gps heading rel_alt rangefinder distance_sensors
+arm() set_mode() command_takeoff() command_land() set_param()
+send_velocity_target() send_local_target() send_global_target()
}
class MavrosTransport {
-_vehicle_state VehicleState
-_local_plain _vision_plain _gps_plain LocalPose/GeoPoint
+start() close() driver_command()
+send_command_long(command, *params) bool
}
VehicleTransport <|.. MavrosTransport
MavrosTransport ..> mavros_node : ROS topics/services
MavrosDrone é simplesmente ArduPilotDrone construído com um MavrosTransport, e Px4MavrosDrone é Px4Drone construído com o mesmo transporte (veja drone.py e ../px4/mavros_drone.py).
Requisitos¶
O MAVROS é uma dependência opcional (não faz parte da instalação padrão do SDK). Instale-o uma vez com make drone-mavros (ou ./scripts/setup.sh drone mavros), que adiciona ros-<distro>-mavros/-mavros-extras e os datasets do GeographicLib.
Um mavros_node precisa então estar fazendo a ponte com o FCU. O transporte pode iniciá-lo para você quando MavrosConfig.start_driver=True:
MavrosConfig.connection_string é um fcu_url do MAVROS (por exemplo, serial:///dev/ttyUSB0:921600, tcp://127.0.0.1:5760), não uma string do pymavlink. Veja o transporte MAVLink para a alternativa de conexão direta.
Configuração¶
import nectar
from nectar.control import DroneFactory, MavrosConfig, PoseSource
nectar.init()
config = MavrosConfig(
pose_source=PoseSource.VISION, # VISION (indoor) or GPS (outdoor)
expect_lidar=True, # wait for rangefinder at startup
connection_string="serial:///dev/ttyUSB0:921600",
apply_setpoint_params=False, # True to push WPNAV/GUID params to the FCU on arm
)
drone = DroneFactory.create("mavros", config)
MavrosConfig expõe um nome de tópico para cada subscription (state_topic, lidar_topic, local_position_topic, vision_topic, gps_topic, heading_topic, rel_alt_topic), uma tupla opcional distance_sensors (veja Sensores de Distância), além de pid_config_file / setpoint_config_file / apply_setpoint_params (consumidos pelo núcleo compartilhado). A pose_source seleciona o conjunto de subscriptions indoor vs. outdoor.
Presets de SITL (em config.py): SITL_CONFIG, SITL_GPS_CONFIG, SITL_GAZEBO_CONFIG, SITL_VISION_CONFIG.
Mapeamento de Telemetria¶
Os callbacks de subscriber convertem mensagens ROS para os tipos simples do núcleo:
| Tópico ROS | Tipo ROS | Tipo no núcleo | Armazenado como |
|---|---|---|---|
/mavros/state |
mavros_msgs/State |
VehicleState |
state |
/mavros/local_position/pose |
geometry_msgs/PoseStamped |
LocalPose |
local_pose |
/mavros/vision_pose/pose_cov |
geometry_msgs/PoseWithCovarianceStamped |
LocalPose |
vision_pose |
/mavros/vision_pose/pose |
geometry_msgs/PoseStamped |
LocalPose |
vision_pose |
/mavros/global_position/global |
sensor_msgs/NavSatFix |
GeoPoint |
gps |
/mavros/global_position/rel_alt |
std_msgs/Float64 |
float |
rel_alt |
/mavros/global_position/compass_hdg |
std_msgs/Float64 |
float |
heading |
/mavros/rangefinder/rangefinder |
sensor_msgs/Range |
float |
rangefinder |
/mavros/state é assinado com durabilidade TRANSIENT_LOCAL, de modo que o último estado em cache seja entregue na subscription (capturando de forma confiável mudanças de arm/modo). A subscription de visão é escolhida em runtime: um callback PoseWithCovarianceStamped quando o nome do tópico contém pose_cov, caso contrário um PoseStamped simples. Indoor (pose_source=VISION) assina o tópico de visão; outdoor assina GPS, rel-alt e heading da bússola.
Os tópicos de comando (publishers) e os serviços do MAVROS estão listados abaixo.
Sensores de Distância¶
lidar_topic alimenta o rangefinder voltado para baixo, usado para altitude. Para expor rangefinders adicionais ou setores de proximidade (distance_sensors / get_distance(orientation) no drone), dois lados precisam estar alinhados:
- O MAVROS precisa publicá-los. Seu plugin
distance_sensormapeia cada idDISTANCE_SENSORdo FCU para um tópicosensor_msgs/Range, por meio dos parâmetros por sensor do plugin. - O SDK precisa assinar esses tópicos.
Rangenão carrega id nem orientação, então declare cada um emMavrosConfig.distance_sensorspara marcar as leituras recebidas:
from nectar.control import MavrosConfig, DistanceSensorTopic, SensorOrientation
config = MavrosConfig(
distance_sensors=(
DistanceSensorTopic("/mavros/distance_sensor/rangefinder_fwd", SensorOrientation.FORWARD, sensor_id=1),
DistanceSensorTopic("/mavros/distance_sensor/rangefinder_left", SensorOrientation.LEFT, sensor_id=2),
),
)
sensor_type é derivado de Range.radiation_type; signal_quality não está disponível via MAVROS e permanece None. O transporte MAVLink direto não precisa de nada disso, lendo id e orientação diretamente de DISTANCE_SENSOR. Veja o README do núcleo do veículo para o modelo de dados.
Tópicos e Serviços ROS2¶
Publishers¶
| Tópico | Tipo | Finalidade |
|---|---|---|
/mavros/setpoint_raw/local |
PositionTarget | Setpoints de velocidade e posição local |
/mavros/setpoint_position/global |
GeoPoseStamped | Setpoints de posição GPS (outdoor) |
/mavros/setpoint_raw/global |
GlobalPositionTarget | Setpoints brutos de GPS (outdoor) |
Setpoints locais usam uma bitmask de velocidade (velocidade + yaw-rate ativos) para move_velocity, e uma bitmask de posição (posição + yaw ativos) para alvos de posição local. O MAVROS traduz PositionTarget para SET_POSITION_TARGET_LOCAL_NED (veja setpoint_mixin.hpp).
Serviços¶
| Serviço | Tipo | Finalidade |
|---|---|---|
/mavros/set_mode |
SetMode | Muda o modo de voo |
/mavros/cmd/arming |
CommandBool | Arma/desarma os motores |
/mavros/cmd/takeoff |
CommandTOL | Comando de takeoff |
/mavros/cmd/land |
CommandTOL | Comando de land |
/mavros/cmd/set_home |
CommandHome | Define a posição de home |
/mavros/cmd/command |
CommandLong | Comandos MAVLink genéricos (set_speed, do_servo, disarm) |
/mavros/param/set_parameters |
SetParameters | Define parâmetros do FCU |
Comportamento das Chamadas de Serviço¶
Todas as chamadas de serviço passam por BaseDrone._call_service, que é segura contra deadlock: seguindo a orientação de sync/async do ROS 2, ela chama call_async e bloqueia na future enquanto o executor do SDK continua girando (spinning) os callbacks em uma thread de fundo. Ela retorna o objeto de resposta (ou None em timeout/erro); só valida campos da resposta quando um validator é passado.
O transporte interpreta as respostas assim:
| Comando | Método | Condição de sucesso |
|---|---|---|
| set_mode, arm, takeoff, land, set_home, disarm | bool(res) |
Heurística de ACK de serviço — uma resposta retornada conta como sucesso |
COMMAND_LONG genérico (set_speed, do_servo) |
send_command_long |
bool(res) and res.success |
set_param |
— | todos os results[].successful |
Por que a heurística de ACK de serviço para arm/takeoff/land: validar res.success / res.result nesses serviços produzia falsos negativos e retentativas desnecessárias, então o transporte trata uma resposta bem-formada do MAVROS como aceitação e deixa o núcleo do veículo confirmar o resultado pela telemetria: arm() faz poll de is_armed/modo, e takeoff/land usam a detecção de estabilização do FlightSequencer. O COMMAND_LONG genérico não tem estado de acompanhamento para fazer poll, então ainda verifica res.success.
Códigos MAV_RESULT¶
Os serviços de comando do MAVROS reportam o MAV_RESULT do MAVLink no campo result: 0 ACCEPTED, 1 TEMPORARILY_REJECTED, 2 DENIED, 3 UNSUPPORTED, 4 FAILED, 5 IN_PROGRESS.
Visão Indoor¶
Com o MAVROS, o EKF do FCU (EKF3 do ArduPilot / EKF2 do PX4) recebe navegação externa pelo próprio plugin de visão do MAVROS: uma fonte de pose externa publica em /mavros/vision_pose/pose_cov (ou /pose), e o MAVROS converte ENU→NED e envia VISION_POSITION_ESTIMATE para o FCU. O transporte apenas assina o mesmo tópico para expor vision_pose para a navegação PID do lado do computador de bordo.
- D435i + Isaac ROS Visual SLAM (atual):
D435i → isaac_ros_visual_slam → relay → /mavros/vision_pose/pose_cov— Localization - T265 + vision_to_mavros (legado/fallback):
T265 VIO → /tf → vision_to_mavros (ENU alignment) → /mavros/vision_pose/pose— Legacy T265
Os parâmetros de FCU por firmware (fontes, taxa, fonte de altura) e a origem do EKF estão
documentados em Localization → FCU setup /
EKF origin. Teoria:
Concepts. O
transporte MAVLink direto substitui esse relay do MAVROS por uma
VisionPoseBridge embutida.
Referências¶
- MAVROS GitHub · MAVROS ROS2 API · MAVROS Wiki
- SET_POSITION_TARGET_LOCAL_NED · MAV_RESULT · VISION_POSITION_ESTIMATE
- ROS 2 Sync vs Async Service Clients · ROS 2 Executors
- vision_to_mavros · Isaac ROS cuVSLAM with RealSense
- Lógica de voo compartilhada: Núcleo do veículo · Especificidades do ArduPilot: ArduPilot