无人机视觉语言导航从入门到精通(二十三):真机部署实践指南
·
无人机视觉语言导航从入门到精通(二十三):真机部署实践指南
摘要
将视觉语言导航系统部署到真实机器人或无人机平台是研究走向应用的关键一步。本文将详细介绍真机部署的完整流程,包括硬件选型、软件架构设计、安全机制、实时性优化,以及常见问题的解决方案。通过本文的学习,读者将掌握 VLN 系统真机部署的实践技能。
关键词:真机部署、硬件选型、ROS、安全机制、实时系统
一、硬件平台选型
1.1 地面机器人平台
| 平台类型 | 适用场景 | 代表产品 | 价格区间 |
|---|---|---|---|
| 差速驱动 | 室内平坦地面 | TurtleBot, Create3 | ¥3K-8K |
| 麦克纳姆轮 | 灵活移动 | SCUTTLE, Agility | ¥5K-15K |
| 四足机器人 | 复杂地形 | Unitree Go1 | ¥16K-50K |
| 轮腿复合 | 全地形 | ANYmal | ¥200K+ |
推荐配置(室内导航):
# 地面机器人配置
platform:
type: differential_drive
model: TurtleBot4
max_speed: 0.5 m/s
wheel_base: 0.287 m
sensors:
camera:
type: Intel RealSense D435i
resolution: 640x480
fps: 30
depth_range: 0.3-10m
lidar:
type: RPLidar A2
range: 12m
scan_rate: 10Hz
imu:
type: built-in (D435i)
rate: 200Hz
compute:
main:
type: Jetson Orin Nano
memory: 8GB
storage: 256GB NVMe
edge:
type: Raspberry Pi 4
role: sensor_interface
1.2 无人机平台
| 平台类型 | 载重 | 续航 | 适用场景 |
|---|---|---|---|
| 轻型多旋翼 | <0.5kg | 15-25min | 室内、近距离 |
| 中型多旋翼 | 0.5-2kg | 20-35min | 室外、中距离 |
| 重型多旋翼 | 2-5kg | 25-45min | 专业应用 |
| 固定翼 | 1-3kg | 1-2h | 长距离巡航 |
推荐配置(室外导航):
# 无人机配置
platform:
type: quadcopter
model: DJI M300 / Custom F450
weight: 3.5kg (含载荷)
max_payload: 2kg
max_speed: 15 m/s
flight_time: 35min
flight_controller:
type: Pixhawk 6X
firmware: PX4 v1.14
features:
- obstacle_avoidance
- return_to_home
- geofencing
sensors:
camera_front:
type: Intel RealSense D455
resolution: 1280x720
fps: 30
fov: 87°
camera_down:
type: Global Shutter Camera
resolution: 1920x1080
fps: 60
gps:
type: u-blox F9P RTK
accuracy: 2cm (RTK)
rangefinder:
type: TFmini Plus
range: 0.1-12m
rate: 100Hz
compute:
main:
type: Jetson AGX Orin
memory: 32GB
power: 15-60W
communication:
data_link:
type: 5.8GHz WiFi
range: 2km
bandwidth: 50Mbps
telemetry:
type: 915MHz radio
range: 5km
1.3 传感器选型
深度相机对比:
| 型号 | 深度范围 | 精度 | FOV | 特点 |
|---|---|---|---|---|
| RealSense D435i | 0.3-10m | ±2% | 87° | 带 IMU |
| RealSense D455 | 0.6-20m | ±2% | 87° | 长距离 |
| Azure Kinect | 0.5-5.5m | ±1% | 120° | 高精度 |
| ZED 2 | 0.5-20m | ±1% | 110° | 双目立体 |
| OAK-D | 0.2-35m | ±2% | 73° | 边缘 AI |
计算平台对比:
| 平台 | AI 性能 | 功耗 | 内存 | 价格 |
|---|---|---|---|---|
| Jetson Nano | 472 GFLOPS | 5-10W | 4GB | ¥1K |
| Jetson Orin Nano | 40 TOPS | 7-15W | 8GB | ¥2K |
| Jetson AGX Orin | 275 TOPS | 15-60W | 32-64GB | ¥12K |
| Intel NUC | CPU only | 28-65W | 16-64GB | ¥4K |
| Raspberry Pi 5 | CPU only | 5-12W | 4-8GB | ¥0.5K |
二、软件架构设计
2.1 系统架构
2.2 ROS 2 节点设计
# vln_navigation/vln_node.py
import rclpy
from rclpy.node import Node
from rclpy.qos import QoSProfile, ReliabilityPolicy
from sensor_msgs.msg import Image, CameraInfo
from geometry_msgs.msg import Twist, PoseStamped
from nav_msgs.msg import Odometry
from std_msgs.msg import String
from cv_bridge import CvBridge
import numpy as np
class VLNNavigationNode(Node):
"""VLN 导航节点"""
def __init__(self):
super().__init__('vln_navigation')
# 参数
self.declare_parameters(
namespace='',
parameters=[
('model_path', 'models/vln_model.pt'),
('max_speed', 0.5),
('control_rate', 10.0),
('safety_distance', 0.5),
]
)
# 初始化组件
self.bridge = CvBridge()
self.model = self._load_model()
# 状态
self.current_image = None
self.current_depth = None
self.current_pose = None
self.instruction = None
self.navigation_active = False
# QoS 配置
sensor_qos = QoSProfile(
reliability=ReliabilityPolicy.BEST_EFFORT,
depth=1
)
# 订阅器
self.image_sub = self.create_subscription(
Image, '/camera/color/image_raw',
self.image_callback, sensor_qos
)
self.depth_sub = self.create_subscription(
Image, '/camera/depth/image_rect_raw',
self.depth_callback, sensor_qos
)
self.odom_sub = self.create_subscription(
Odometry, '/odom',
self.odom_callback, 10
)
self.instruction_sub = self.create_subscription(
String, '/vln/instruction',
self.instruction_callback, 10
)
# 发布器
self.cmd_vel_pub = self.create_publisher(Twist, '/cmd_vel', 10)
self.status_pub = self.create_publisher(String, '/vln/status', 10)
# 控制定时器
control_rate = self.get_parameter('control_rate').value
self.control_timer = self.create_timer(
1.0 / control_rate, self.control_loop
)
self.get_logger().info('VLN Navigation Node initialized')
def _load_model(self):
"""加载 VLN 模型"""
import torch
model_path = self.get_parameter('model_path').value
# 加载模型
model = torch.jit.load(model_path)
model.eval()
# 移动到 GPU(如果可用)
if torch.cuda.is_available():
model = model.cuda()
self.get_logger().info('Model loaded on GPU')
else:
self.get_logger().info('Model loaded on CPU')
return model
def image_callback(self, msg: Image):
"""RGB 图像回调"""
self.current_image = self.bridge.imgmsg_to_cv2(msg, 'rgb8')
def depth_callback(self, msg: Image):
"""深度图像回调"""
self.current_depth = self.bridge.imgmsg_to_cv2(msg, 'passthrough')
def odom_callback(self, msg: Odometry):
"""里程计回调"""
self.current_pose = msg.pose.pose
def instruction_callback(self, msg: String):
"""指令回调"""
self.instruction = msg.data
self.navigation_active = True
self.get_logger().info(f'Received instruction: {self.instruction}')
def control_loop(self):
"""控制循环"""
if not self.navigation_active:
return
if self.current_image is None or self.instruction is None:
return
# 安全检查
if not self._safety_check():
self._emergency_stop()
return
# VLN 推理
action = self._vln_inference()
# 转换为控制命令
cmd = self._action_to_cmd(action)
# 发布控制命令
self.cmd_vel_pub.publish(cmd)
# 检查是否完成
if action == 'stop':
self.navigation_active = False
self._publish_status('Navigation completed')
def _safety_check(self) -> bool:
"""安全检查"""
if self.current_depth is None:
return True
safety_distance = self.get_parameter('safety_distance').value
# 检查前方障碍物
center_region = self.current_depth[
200:280, 280:360
]
min_distance = np.nanmin(center_region) / 1000.0 # 转换为米
if min_distance < safety_distance:
self.get_logger().warn(f'Obstacle detected at {min_distance:.2f}m')
return False
return True
def _vln_inference(self) -> str:
"""VLN 模型推理"""
import torch
# 预处理图像
image_tensor = self._preprocess_image(self.current_image)
# 编码指令
instruction_encoding = self._encode_instruction(self.instruction)
# 模型推理
with torch.no_grad():
output = self.model(image_tensor, instruction_encoding)
action_idx = output.argmax().item()
actions = ['forward', 'left', 'right', 'stop']
return actions[action_idx]
def _action_to_cmd(self, action: str) -> Twist:
"""将动作转换为控制命令"""
cmd = Twist()
max_speed = self.get_parameter('max_speed').value
if action == 'forward':
cmd.linear.x = max_speed
elif action == 'left':
cmd.angular.z = 0.5
elif action == 'right':
cmd.angular.z = -0.5
elif action == 'stop':
pass # 全零
return cmd
def _emergency_stop(self):
"""紧急停止"""
cmd = Twist() # 全零
self.cmd_vel_pub.publish(cmd)
self._publish_status('Emergency stop')
def _publish_status(self, status: str):
"""发布状态"""
msg = String()
msg.data = status
self.status_pub.publish(msg)
def _preprocess_image(self, image: np.ndarray):
"""预处理图像"""
import torch
import torchvision.transforms as T
transform = T.Compose([
T.ToPILImage(),
T.Resize((224, 224)),
T.ToTensor(),
T.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
tensor = transform(image).unsqueeze(0)
if torch.cuda.is_available():
tensor = tensor.cuda()
return tensor
def _encode_instruction(self, instruction: str):
"""编码指令"""
# 使用预加载的 tokenizer
# 这里简化处理
import torch
encoding = torch.zeros(1, 80).long()
if torch.cuda.is_available():
encoding = encoding.cuda()
return encoding
def main(args=None):
rclpy.init(args=args)
node = VLNNavigationNode()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
2.3 Launch 文件
# launch/vln_navigation.launch.py
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
def generate_launch_description():
return LaunchDescription([
# 参数声明
DeclareLaunchArgument(
'model_path',
default_value='models/vln_model.pt',
description='Path to VLN model'
),
DeclareLaunchArgument(
'max_speed',
default_value='0.5',
description='Maximum linear speed'
),
# 相机驱动
Node(
package='realsense2_camera',
executable='realsense2_camera_node',
name='camera',
parameters=[{
'enable_color': True,
'enable_depth': True,
'depth_module.profile': '640x480x30',
'rgb_camera.profile': '640x480x30',
}]
),
# VLN 导航节点
Node(
package='vln_navigation',
executable='vln_node',
name='vln_navigation',
parameters=[{
'model_path': LaunchConfiguration('model_path'),
'max_speed': LaunchConfiguration('max_speed'),
'control_rate': 10.0,
'safety_distance': 0.5,
}],
output='screen'
),
# 安全监控节点
Node(
package='vln_navigation',
executable='safety_monitor',
name='safety_monitor',
parameters=[{
'emergency_stop_distance': 0.3,
'warning_distance': 1.0,
}]
),
# 可视化
Node(
package='rviz2',
executable='rviz2',
name='rviz',
arguments=['-d', 'config/vln_navigation.rviz']
),
])
三、安全机制
3.1 多层安全架构
3.2 安全监控节点
# vln_navigation/safety_monitor.py
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image, LaserScan, BatteryState
from geometry_msgs.msg import Twist, PoseStamped
from std_msgs.msg import Bool
from cv_bridge import CvBridge
import numpy as np
from enum import Enum
class SafetyLevel(Enum):
NORMAL = 0
WARNING = 1
CRITICAL = 2
EMERGENCY = 3
class SafetyMonitor(Node):
"""安全监控节点"""
def __init__(self):
super().__init__('safety_monitor')
# 参数
self.declare_parameters(
namespace='',
parameters=[
('emergency_stop_distance', 0.3),
('warning_distance', 1.0),
('critical_distance', 0.5),
('min_battery_voltage', 10.5),
('max_speed', 1.0),
('watchdog_timeout', 1.0),
('geofence_radius', 50.0),
]
)
self.bridge = CvBridge()
self.safety_level = SafetyLevel.NORMAL
self.last_cmd_time = self.get_clock().now()
self.home_position = None
# 订阅器
self.depth_sub = self.create_subscription(
Image, '/camera/depth/image_rect_raw',
self.depth_callback, 10
)
self.lidar_sub = self.create_subscription(
LaserScan, '/scan',
self.lidar_callback, 10
)
self.battery_sub = self.create_subscription(
BatteryState, '/battery_state',
self.battery_callback, 10
)
self.cmd_vel_sub = self.create_subscription(
Twist, '/cmd_vel',
self.cmd_vel_callback, 10
)
self.pose_sub = self.create_subscription(
PoseStamped, '/robot_pose',
self.pose_callback, 10
)
# 发布器
self.safe_cmd_pub = self.create_publisher(Twist, '/safe_cmd_vel', 10)
self.emergency_pub = self.create_publisher(Bool, '/emergency_stop', 10)
self.safety_status_pub = self.create_publisher(String, '/safety_status', 10)
# 监控定时器
self.monitor_timer = self.create_timer(0.1, self.monitor_loop)
self.watchdog_timer = self.create_timer(0.1, self.watchdog_loop)
self.get_logger().info('Safety Monitor initialized')
def depth_callback(self, msg: Image):
"""深度图像安全检查"""
depth = self.bridge.imgmsg_to_cv2(msg, 'passthrough')
depth_meters = depth.astype(np.float32) / 1000.0
# 前方区域检测
h, w = depth_meters.shape
front_region = depth_meters[h//3:2*h//3, w//3:2*w//3]
# 过滤无效值
valid_depths = front_region[np.isfinite(front_region) & (front_region > 0)]
if len(valid_depths) == 0:
return
min_depth = np.min(valid_depths)
self._check_obstacle_distance(min_depth, 'depth_camera')
def lidar_callback(self, msg: LaserScan):
"""激光雷达安全检查"""
ranges = np.array(msg.ranges)
# 过滤无效值
valid_ranges = ranges[np.isfinite(ranges) & (ranges > msg.range_min)]
if len(valid_ranges) == 0:
return
min_range = np.min(valid_ranges)
self._check_obstacle_distance(min_range, 'lidar')
def battery_callback(self, msg: BatteryState):
"""电池状态检查"""
min_voltage = self.get_parameter('min_battery_voltage').value
if msg.voltage < min_voltage:
self.get_logger().warn(f'Low battery: {msg.voltage:.2f}V')
self._update_safety_level(SafetyLevel.WARNING)
if msg.voltage < min_voltage - 1.0:
self.get_logger().error('Critical battery level!')
self._update_safety_level(SafetyLevel.CRITICAL)
def cmd_vel_callback(self, msg: Twist):
"""命令速度检查"""
self.last_cmd_time = self.get_clock().now()
max_speed = self.get_parameter('max_speed').value
# 限制速度
safe_cmd = Twist()
safe_cmd.linear.x = np.clip(msg.linear.x, -max_speed, max_speed)
safe_cmd.linear.y = np.clip(msg.linear.y, -max_speed, max_speed)
safe_cmd.angular.z = np.clip(msg.angular.z, -2.0, 2.0)
# 根据安全级别调整
if self.safety_level == SafetyLevel.WARNING:
safe_cmd.linear.x *= 0.5
safe_cmd.linear.y *= 0.5
elif self.safety_level == SafetyLevel.CRITICAL:
safe_cmd.linear.x *= 0.2
safe_cmd.linear.y *= 0.2
elif self.safety_level == SafetyLevel.EMERGENCY:
safe_cmd = Twist() # 全零
self.safe_cmd_pub.publish(safe_cmd)
def pose_callback(self, msg: PoseStamped):
"""位置检查(地理围栏)"""
if self.home_position is None:
self.home_position = msg.pose.position
return
# 计算距离家的距离
distance = np.sqrt(
(msg.pose.position.x - self.home_position.x)**2 +
(msg.pose.position.y - self.home_position.y)**2
)
geofence_radius = self.get_parameter('geofence_radius').value
if distance > geofence_radius:
self.get_logger().warn(f'Approaching geofence boundary: {distance:.1f}m')
self._update_safety_level(SafetyLevel.WARNING)
if distance > geofence_radius * 1.1:
self.get_logger().error('Geofence violation!')
self._update_safety_level(SafetyLevel.CRITICAL)
def _check_obstacle_distance(self, distance: float, source: str):
"""检查障碍物距离"""
emergency_dist = self.get_parameter('emergency_stop_distance').value
critical_dist = self.get_parameter('critical_distance').value
warning_dist = self.get_parameter('warning_distance').value
if distance < emergency_dist:
self.get_logger().error(
f'Emergency: Obstacle at {distance:.2f}m ({source})'
)
self._update_safety_level(SafetyLevel.EMERGENCY)
elif distance < critical_dist:
self.get_logger().warn(
f'Critical: Obstacle at {distance:.2f}m ({source})'
)
self._update_safety_level(SafetyLevel.CRITICAL)
elif distance < warning_dist:
self._update_safety_level(SafetyLevel.WARNING)
def _update_safety_level(self, level: SafetyLevel):
"""更新安全级别"""
if level.value > self.safety_level.value:
self.safety_level = level
if level == SafetyLevel.EMERGENCY:
self._trigger_emergency_stop()
def _trigger_emergency_stop(self):
"""触发紧急停止"""
msg = Bool()
msg.data = True
self.emergency_pub.publish(msg)
# 发送零速度
self.safe_cmd_pub.publish(Twist())
self.get_logger().error('EMERGENCY STOP TRIGGERED')
def monitor_loop(self):
"""监控循环"""
# 发布安全状态
status_msg = String()
status_msg.data = f"Safety Level: {self.safety_level.name}"
self.safety_status_pub.publish(status_msg)
# 逐渐恢复安全级别
if self.safety_level != SafetyLevel.NORMAL:
# 简化:每秒降一级(实际应更复杂)
pass
def watchdog_loop(self):
"""看门狗检查"""
timeout = self.get_parameter('watchdog_timeout').value
elapsed = (self.get_clock().now() - self.last_cmd_time).nanoseconds / 1e9
if elapsed > timeout:
# 超时,停止机器人
self.safe_cmd_pub.publish(Twist())
def main(args=None):
rclpy.init(args=args)
node = SafetyMonitor()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
四、模型优化与部署
4.1 模型量化
import torch
import torch.quantization
def quantize_model(model, calibration_data):
"""量化模型"""
# 准备量化
model.eval()
model.qconfig = torch.quantization.get_default_qconfig('fbgemm')
# 准备模型
model_prepared = torch.quantization.prepare(model)
# 校准
with torch.no_grad():
for data in calibration_data:
model_prepared(data)
# 转换
model_quantized = torch.quantization.convert(model_prepared)
return model_quantized
def export_to_tensorrt(model, input_shape, output_path):
"""导出到 TensorRT"""
import torch_tensorrt
# 创建示例输入
example_input = torch.randn(*input_shape).cuda()
# 编译
trt_model = torch_tensorrt.compile(
model,
inputs=[
torch_tensorrt.Input(
min_shape=input_shape,
opt_shape=input_shape,
max_shape=input_shape,
dtype=torch.float32
)
],
enabled_precisions={torch.float16, torch.float32},
workspace_size=1 << 30, # 1GB
)
# 保存
torch.jit.save(trt_model, output_path)
print(f'TensorRT model saved to {output_path}')
def export_to_onnx(model, input_shape, output_path):
"""导出到 ONNX"""
model.eval()
dummy_input = torch.randn(*input_shape)
torch.onnx.export(
model,
dummy_input,
output_path,
export_params=True,
opset_version=13,
do_constant_folding=True,
input_names=['image', 'instruction'],
output_names=['action'],
dynamic_axes={
'image': {0: 'batch_size'},
'instruction': {0: 'batch_size'},
'action': {0: 'batch_size'}
}
)
print(f'ONNX model saved to {output_path}')
4.2 推理优化
class OptimizedInference:
"""优化的推理引擎"""
def __init__(self, model_path: str, device: str = 'cuda'):
self.device = device
# 加载 TensorRT 模型
if model_path.endswith('.ts'):
self.model = torch.jit.load(model_path)
else:
self.model = self._load_onnx(model_path)
self.model.eval()
# 预分配内存
self._warmup()
def _load_onnx(self, model_path: str):
"""加载 ONNX 模型"""
import onnxruntime as ort
providers = ['CUDAExecutionProvider', 'CPUExecutionProvider']
self.session = ort.InferenceSession(model_path, providers=providers)
return None # 使用 ONNX Runtime
def _warmup(self):
"""预热(预分配内存)"""
dummy_image = torch.randn(1, 3, 224, 224).to(self.device)
dummy_instruction = torch.zeros(1, 80).long().to(self.device)
for _ in range(10):
with torch.no_grad():
_ = self.infer(dummy_image, dummy_instruction)
print('Inference engine warmed up')
@torch.no_grad()
def infer(self, image: torch.Tensor, instruction: torch.Tensor) -> torch.Tensor:
"""执行推理"""
if self.model is not None:
return self.model(image, instruction)
else:
# ONNX Runtime
outputs = self.session.run(
None,
{
'image': image.cpu().numpy(),
'instruction': instruction.cpu().numpy()
}
)
return torch.from_numpy(outputs[0])
def benchmark(self, num_iterations: int = 100):
"""性能基准测试"""
import time
dummy_image = torch.randn(1, 3, 224, 224).to(self.device)
dummy_instruction = torch.zeros(1, 80).long().to(self.device)
# 同步 GPU
if self.device == 'cuda':
torch.cuda.synchronize()
start_time = time.time()
for _ in range(num_iterations):
_ = self.infer(dummy_image, dummy_instruction)
if self.device == 'cuda':
torch.cuda.synchronize()
elapsed = time.time() - start_time
fps = num_iterations / elapsed
print(f'Inference FPS: {fps:.2f}')
print(f'Latency: {1000/fps:.2f} ms')
return fps
五、测试与验证
5.1 分级测试流程
5.2 测试脚本
# tests/test_vln_system.py
import unittest
import rclpy
from rclpy.node import Node
import time
class VLNSystemTest(unittest.TestCase):
"""VLN 系统测试"""
@classmethod
def setUpClass(cls):
rclpy.init()
@classmethod
def tearDownClass(cls):
rclpy.shutdown()
def test_model_inference(self):
"""测试模型推理"""
from vln_navigation.vln_node import VLNNavigationNode
node = VLNNavigationNode()
# 创建测试输入
import numpy as np
test_image = np.random.randint(0, 255, (480, 640, 3), dtype=np.uint8)
test_instruction = "Go to the kitchen"
node.current_image = test_image
node.instruction = test_instruction
# 测试推理
action = node._vln_inference()
self.assertIn(action, ['forward', 'left', 'right', 'stop'])
node.destroy_node()
def test_safety_check(self):
"""测试安全检查"""
from vln_navigation.safety_monitor import SafetyMonitor
node = SafetyMonitor()
# 测试障碍物检测
node._check_obstacle_distance(0.2, 'test')
self.assertEqual(node.safety_level.name, 'EMERGENCY')
node.destroy_node()
def test_cmd_vel_limiting(self):
"""测试速度限制"""
from geometry_msgs.msg import Twist
# 创建过大的速度命令
cmd = Twist()
cmd.linear.x = 10.0 # 过大
# 应该被限制
max_speed = 0.5
limited = min(cmd.linear.x, max_speed)
self.assertEqual(limited, max_speed)
def main():
unittest.main()
if __name__ == '__main__':
main()
六、常见问题与解决
6.1 问题排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 推理延迟高 | 模型过大 | 量化、剪枝、TensorRT |
| 定位漂移 | IMU 漂移 | 视觉里程计融合 |
| 控制抖动 | 命令频率不一致 | 低通滤波 |
| 电池续航短 | 计算功耗高 | 模型优化、动态频率 |
6.2 调试工具
# 查看话题
ros2 topic list
ros2 topic echo /vln/status
# 查看节点
ros2 node list
ros2 node info /vln_navigation
# 记录数据
ros2 bag record -a -o navigation_test
# 性能分析
ros2 run rqt_graph rqt_graph
七、小结
本文详细介绍了 VLN 系统真机部署的完整流程:
- 硬件选型:平台、传感器、计算单元
- 软件架构:ROS 2 节点设计、Launch 文件
- 安全机制:多层安全、监控节点
- 模型优化:量化、TensorRT、ONNX
- 测试验证:分级测试流程
真机部署需要在性能、安全、可靠性之间取得平衡,建议采用渐进式部署策略。
参考文献
[1] Macenski S, et al. Robot Operating System 2: Design, architecture, and uses in the wild. Science Robotics, 2022.
[2] NVIDIA. TensorRT Developer Guide. 2024.
[3] PX4 Development Team. PX4 Autopilot User Guide. 2024.
下篇预告
下一篇文章《前沿研究方向与热点》将介绍 VLN 领域的最新研究进展,包括持续学习、多智能体协作、对话导航等前沿方向。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)