| name | ros2-bridge |
| description | PX4 AirSim 与 ROS2 桥接技能 - SITL/HITL 集成、无人机控制、传感器数据同步、多机协同 |
| user-invocable | true |
| argument-hint | PX4 AirSim桥接 OR PX4 ros2桥接 OR 无人机仿真 OR airsim多机 OR SITL HITL |
PX4 AirSim ROS2 Bridge Skill
PX4 Autopilot + AirSim 仿真器与 ROS2 之间的通讯桥接完整指南
何时使用
当需要以下帮助时使用此技能:
- 配置 PX4 SITL/HITL 与 AirSim 仿真
- 桥接无人机状态到 ROS2
- 发布 AirSim 传感器数据到 ROS2
- 订阅 ROS2 命令控制无人机
- 多无人机协同仿真
- 视觉/激光雷达仿真集成
快速参考
系统架构
┌─────────────────────────────────────────────────────────────┐
│ PX4 Autopilot │
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────┐ │
│ │ Commander │───▶│ Navigator │───▶│ Actuator │ │
│ └─────────────┘ └──────────────┘ └───────────────┘ │
└───────────┬─────────────────┬─────────────────────────────┘
│ │
▼ ▼
┌───────────────┐ ┌──────────────┐
│ AirSim API │◄─│ uORB Topics │
│ │ │ (mavlink) │
└───────┬───────┘ └──────────────┘
│ │
▼ ▼
┌───────────────┐ ┌──────────────┐
│ AirSim Sim │ │ MAVLink │
│ │ │ (ROS2) │
└───────┬───────┘ └──────┬───────┘
│ │
▼ ▼
┌───────────────┐ ┌──────────────┐
│ Sensors │ │ ROS2 │
│ (Cam/Lidar) │ │ Bridge │
└───────────────┘ └──────────────┘
PX4 AirSim 安装
git clone https://github.com/microsoft/AirSim.git
cd AirSim
./setup.sh
./build.sh
git clone --recursive https://github.com/PX4/PX4-Autopilot.git
cd PX4-Autopilot
make px4_sitl_default
export PX4_SIMULATOR=AirSim
export PX4_GAZEBO_HOSTNAME=127.0.0.1
mavros 桥接配置
mavros 安装
sudo apt install -y ros-humble-mavros ros-humble-mavlink
sudo apt install -y geographiclib-tools
sudo /opt/ros/humble/lib/mavros/install_geographiclib_datasets.sh
cd ~/ws/src
git clone -b humble https://github.com/mavlink/mavros.git
git clone -b humble https://github.com/mavlink/mavlink-ros2.git
cd ~/ws && colcon build --packages-select mavros mavros_extras
mavros 基础配置
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='mavros',
executable='mavros_node',
name='mavros',
parameters=[{
'pluginlibs': ['mavros'],
'plugin_lock_key': '',
'system_id': 1,
'component_id': 1,
'mavlink_system': 1,
'fcu_url': 'udp://:14540@127.0.0.1:14557',
'fcu_protocol': 'v2.0',
'sensor_bitrate': 0,
'conn_timeout': 5.0,
'timeout': 5.0,
'target_system_id': 1,
'target_component_id': 1,
: ,
}],
remappings=[
(, ),
(, ),
(, ),
(, ),
(, ),
(, ),
(, ),
(, ),
(, ),
(, ),
(, ),
(, ),
(, ),
(, ),
],
output=,
emulate_tty=
),
Node(
package=,
executable=,
parameters=[{
:
}]
)
])
AirSim 传感器桥接
AirSim 图像发布
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='airsim_ros_pkgs',
executable='img_pub_node',
name='front_camera',
parameters=[{
'camera_name': 'front_center',
'publish_rate': 30,
'publish_via_ros2': True,
'ros2_namespace': '/drone0',
'topic_id': 'front_camera/image_raw'
}]
),
Node(
package='airsim_ros_pkgs',
executable='img_pub_node',
name='depth_camera',
parameters=[{
'camera_name': 'depth_center',
'publish_rate': 15,
'publish_via_ros2': True,
'ros2_namespace': '/drone0',
'topic_id': 'depth_camera/image_raw'
}]
),
Node(
package='airsim_ros_pkgs',
executable='img_pub_node',
name='seg_camera',
parameters=[{
'camera_name': ,
: ,
: ,
: ,
:
}]
),
])
AirSim 激光雷达桥接
def generate_launch_description():
return LaunchDescription([
Node(
package='airsim_ros_pkgs',
executable='lidar_pub_node',
name='lidar',
parameters=[{
'lidar_name': 'Lidar1',
'publish_rate': 10,
'publish_via_ros2': True,
'ros2_namespace': '/drone0',
'topic_id': 'lidar/scan',
'frame_id': 'lidar_link',
'points_per_second': 100000,
'angle_min': -3.14159,
'angle_max': 3.14159,
'range_min': 0.5,
'range_max': 100.0,
}]
)
])
AirSim GPS/IMU 桥接
def generate_launch_description():
return LaunchDescription([
Node(
package='airsim_ros_pkgs',
executable='gps_pub_node',
name='gps',
parameters=[{
'gps_name': 'Gps1',
'publish_rate': 10,
'publish_via_ros2': True,
'ros2_namespace': '/drone0',
'topic_id': 'gps/fix'
}]
),
Node(
package='airsim_ros_pkgs',
executable='imu_pub_node',
name='imu',
parameters=[{
'imu_name': 'Imu1',
'publish_rate': 100,
'publish_via_ros2': True,
'ros2_namespace': '/drone0',
'topic_id': 'imu/data'
}]
),
])
无人机控制桥接
起飞/降落服务
def generate_launch_description():
return LaunchDescription([
Node(
package='mavros',
executable='mavros_node',
name='mavros_takeoff',
parameters=[{
'fcu_url': 'udp://:14540@127.0.0.1:14557',
}],
remappings=[
('/mavros/cmd/arming', '/drone0/mavros/cmd/arming'),
('/mavros/cmd/takeoff', '/drone0/mavros/cmd/takeoff'),
('/mavros/cmd/land', '/drone0/mavros/cmd/land'),
]
),
])
Python 控制脚本
import rclpy
from rclpy.node import Node
from mavros_msgs.srv import CommandBool, CommandTOL, SetMode
from mavros_msgs.msg import State, GlobalPosition, LocalPosition
from geometry_msgs.msg import PoseStamped, Twist
from geographic_msgs.msg import GeoPoseStamped
class DroneController(Node):
def __init__(self, drone_name='drone0'):
super().__init__(f'{drone_name}_controller')
self.drone_name = drone_name
self.arming_client = self.create_client(CommandBool, f'/{drone_name}/mavros/cmd/arming')
self.takeoff_client = self.create_client(CommandTOL, f'/{drone_name}/mavros/cmd/takeoff')
self.land_client = self.create_client(CommandTOL, f'/{drone_name}/mavros/cmd/land')
self.set_mode_client = self.create_client(SetMode, f'/{drone_name}/mavros/set_mode')
self.state_sub = self.create_subscription(
State, f'//mavros/state', .state_callback, )
.local_pos_sub = .create_subscription(
PoseStamped, , .pos_callback, )
.cmd_vel_pub = .create_publisher(
Twist, , )
.local_pos_pub = .create_publisher(
PoseStamped, , )
.current_state =
.current_pos =
():
.current_state = msg
():
.current_pos = msg
():
client.wait_for_service(timeout_sec=):
.get_logger().info()
():
.wait_for_service(.arming_client)
req = CommandBool.Request()
req.value =
future = .arming_client.call_async(req)
rclpy.spin_until_future_complete(, future)
future.result().success
():
.wait_for_service(.takeoff_client)
req = CommandTOL.Request()
req.altitude = altitude
req.latitude =
req.longitude =
req.min_pitch =
req.yaw =
future = .takeoff_client.call_async(req)
rclpy.spin_until_future_complete(, future)
future.result().success
():
.wait_for_service(.land_client)
req = CommandTOL.Request()
future = .land_client.call_async(req)
rclpy.spin_until_future_complete(, future)
future.result().success
():
.wait_for_service(.set_mode_client)
req = SetMode.Request()
req.custom_mode = mode
future = .set_mode_client.call_async(req)
rclpy.spin_until_future_complete(, future)
future.result().mode_sent
():
cmd = Twist()
cmd.linear.x = linear[]
cmd.linear.y = linear[]
cmd.linear.z = linear[]
cmd.angular.x = angular[]
cmd.angular.y = angular[]
cmd.angular.z = angular[]
.cmd_vel_pub.publish(cmd)
():
pos = PoseStamped()
pos.header.stamp = .get_clock().now().to_msg()
pos.header.frame_id =
pos.pose.position.x = x
pos.pose.position.y = y
pos.pose.position.z = z
.local_pos_pub.publish(pos)
():
rclpy.init()
controller = DroneController()
controller.get_logger().info()
controller.arm():
controller.get_logger().info()
:
controller.get_logger().error()
controller.get_logger().info()
controller.takeoff(altitude=):
controller.get_logger().info()
:
controller.get_logger().error()
rate = controller.create_rate()
_ ():
controller.publish_cmd_vel(linear=(, , ), angular=(, , ))
rclpy.spin_once(controller)
rate.sleep()
controller.get_logger().info()
controller.land()
rclpy.shutdown()
多无人机配置
AirSim 多无人机设置
{
"SeeDocsAt": "https://github.com/Microsoft/AirSim/blob/main/docs/settings.md",
"SettingsVersion": 1.2,
"SimMode": "Multirotor",
"Vehicles": {
"Drone0": {
"VehicleType": "SimpleFlight",
"X": 0, "Y": 0, "Z": 0,
"Yaw": 0,
"Cameras": {
"front_center": {
"CaptureSettings": [
{
"ImageType": 0,
"Width": 640,
"Height": 480
}
]
}
}
},
"Drone1": {
"VehicleType": "SimpleFlight",
"X": 10, "Y": 0, "Z": 0,
"Yaw": 0,
"Cameras": {...}
}
}
}
ROS2 多无人机桥接
def generate_launch_description():
nodes = []
for i, port in enumerate([14540, 14541, 14542]):
drone_ns = f'drone{i}'
nodes.append(
Node(
package='mavros',
executable='mavros_node',
name='mavros',
namespace=drone_ns,
parameters=[{
'system_id': i + 1,
'component_id': 1,
'fcu_url': f'udp://:14540@{127.0.0.1}:{14557 + i}',
'target_system_id': i + 1,
}],
remappings=[
('/mavros/state', f'/{drone_ns}/mavros/state'),
('/mavros/local_position/pose', f'/{drone_ns}/mavros/local_position/pose'),
('/mavros/setpoint_velocity/cmd_vel_unstamped', f'/{drone_ns}/cmd_vel'),
]
)
)
return LaunchDescription(nodes)
视觉/激光雷达仿真
深度感知示例
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image, CameraInfo
from geometry_msgs.msg import TransformStamped
from cv_bridge import CvBridge
import cv2
import numpy as np
class DepthPerception(Node):
def __init__(self):
super().__init__('depth_perception')
self.bridge = CvBridge()
self.depth_sub = self.create_subscription(
Image, '/drone0/depth_camera/image_raw', self.depth_callback, 10)
self.rgb_sub = self.create_subscription(
Image, '/drone0/front_camera/image_raw', self.rgb_callback, 10)
self.detection_pub = self.create_publisher(
Image, '/drone0/detections', 10)
self.latest_depth = None
self.latest_rgb = None
def depth_callback():
.latest_depth = .bridge.imgmsg_to_cv2(msg)
():
.latest_rgb = .bridge.imgmsg_to_cv2(msg)
.latest_depth :
depth_m = np.array(.latest_depth, dtype=np.float32)
valid_depth = depth_m[depth_m > ]
(valid_depth) > :
min_dist = valid_depth.()
max_dist = valid_depth.()
avg_dist = valid_depth.mean()
.get_logger().info(
)
depth_colored = cv2.applyColorMap(
cv2.convertScaleAbs(depth_m, alpha=), cv2.COLORMAP_JET)
out_msg = .bridge.cv2_to_imgmsg(depth_colored, )
.detection_pub.publish(out_msg)
调试和诊断
检查连接
ros2 service call /drone0/mavros/get_log_info mavros_msgs/srv/FileClose
ros2 topic echo /drone0/mavros/state
ros2 topic echo /drone0/mavros/extended_state
AirSim API 测试
import airsim
client = airsim.MultirotorClient()
client.confirmConnection()
state = client.getMultirotorState()
print(f"State: {state}")
client.armDisarm(True)
client.takeoff()
client.moveToPositionAsync(0, 0, -10, 5).join()
client.land()
最佳实践
-
端口配置:确保 PX4 SITL 和 mavros 使用正确的 UDP 端口
-
时钟同步:PX4 使用仿真时钟,确保 use_sim_time:=true
-
多机ID:每架无人机使用不同的 system_id
-
安全检查:飞行前确认无人机状态
-
GPS原点:AirSim 中设置与 ROS2 地图一致的 GPS 原点
故障排查
| 问题 | 原因 | 解决方案 |
|---|
| mavros 连接失败 | 端口被占用 | 检查 PX4 是否启动 |
| 无人机不响应 | 未解锁 | 调用 arming 服务 |
| 位置漂移 | GPS 未同步 | 设置一致的 GPS 原点 |
| 相机无数据 | AirSim 插件未加载 | 检查 settings.json |
相关技能