Unitree Go2 进阶:SLAM 建图、路径规划与视觉导航实战 2026
在宇树 Go2 机器狗上实现自主导航:SLAM 建图(Cartographer/RTAB-Map)、全局路径规划(Nav2)、视觉避障,附完整 ROS2 代码与参数调优
Unitree Go2 进阶:SLAM 建图、路径规划与视觉导航实战 2026
在《宇树 Go2 机器狗 ROS2 开发完全指南》(039 文章)中,我们完成了环境搭建和基础控制。本文将深入自主导航这一核心能力——让 Go2 在未知环境中建图、规划路径、避开障碍物,最终到达目标位置。
自主导航是机器人从”遥控玩具”进化为”真正机器人”的关键一步。宇树 Go2 凭借 LDS 激光雷达、深度摄像头和强大的机载算力,完全具备实现工业级自主导航的硬件基础。
你将学到:
- ✅ SLAM 建图原理与 Go2 实战(Cartographer + RTAB-Map)
- ✅ Nav2 导航框架配置与调优
- ✅ 全局路径规划与局部避障
- ✅ 视觉导航(深度相机 + YOLO 识别)
- ✅ 多机协同导航架构
1. 自主导航架构
1.1 导航系统组成
一个完整的自主导航系统包含以下模块:
┌─────────────────────────────────────────────────────────┐
│ 导航系统架构 │
├─────────────────────────────────────────────────────────┤
│ │
│ [传感器层] │
│ ├── LDS 激光雷达 → /scan (LaserScan) │
│ ├── 深度摄像头 → /depth_camera/image_raw (Image) │
│ ├── IMU → /go2/imu (Imu) │
│ └── 里程计 → /odom (Odometry) │
│ │
│ [感知层] │
│ ├── SLAM → /map (OccupancyGrid) │
│ └── 目标检测 → /detections (DetectionArray) │
│ │
│ [决策层] │
│ ├── 全局规划器 → 全局路径 (Path) │
│ └── 局部规划器 → 速度指令 (Twist) │
│ │
│ [执行层] │
│ └── Go2 运动控制器 → 关节指令 │
│ │
└─────────────────────────────────────────────────────────┘
1.2 坐标变换(TF)
导航系统依赖精确的坐标变换树:
map → odom → base_link → [sensors]
├── lidar_link
├── camera_link
└── imu_link
关键概念:
- map:全局地图坐标系(SLAM 输出)
- odom:里程计坐标系(会累积漂移)
- base_link:机器人本体坐标系
- 传感器坐标系:各传感器的安装位置
Go2 SDK 已提供 odom → base_link 的变换,我们需要补充 map → odom(由 SLAM 或定位节点提供)。
2. SLAM 建图
2.1 Cartographer 集成
Cartographer 是 Google 开源的 2D/3D SLAM 算法,在精度和鲁棒性上表现优异。
安装 Cartographer:
# 安装依赖
sudo apt install -y \
libceres-dev \
liblua5.3-dev \
libcairo2-dev \
libeigen3-dev
# 从源码编译(ROS2 Humble)
mkdir -p ~/cartographer_ws/src
cd ~/cartographer_ws/src
git clone https://github.com/cartographer-project/cartographer.git
git clone https://github.com/cartographer-project/cartographer_ros.git
cd ~/cartographer_ws
colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release
source install/setup.bash
Go2 专用配置文件:
-- cartographer_go2.lua
include "map_builder.lua"
include "trajectory_builder.lua"
options = {
map_builder = MAP_BUILDER,
trajectory_builder = TRAJECTORY_BUILDER,
map_frame = "map",
tracking_frame = "base_link",
published_frame = "odom",
odom_frame = "odom",
provide_odom_frame = false,
publish_frame_projected_to_2d = true,
use_odometry = true,
use_nav_sat = false,
use_landmarks = false,
num_laser_scans = 1,
num_multi_echo_laser_scans = 0,
num_subdivisions_per_laser_scan = 1,
num_point_clouds = 0,
lookup_transform_timeout_sec = 0.2,
submap_publish_period_sec = 0.3,
pose_publish_period_sec = 5e-3,
trajectory_publish_period_sec = 30e-3,
rangefinder_sampling_ratio = 1.,
odometry_sampling_ratio = 1.,
fixed_frame_pose_sampling_ratio = 1.,
imu_sampling_ratio = 1.,
landmarks_sampling_ratio = 1.,
}
MAP_BUILDER.use_trajectory_builder_2d = true
TRAJECTORY_BUILDER_2D.min_range = 0.15
TRAJECTORY_BUILDER_2D.max_range = 12.0
TRAJECTORY_BUILDER_2D.missing_data_ray_length = 3.0
TRAJECTORY_BUILDER_2D.use_imu_data = true
TRAJECTORY_BUILDER_2D.use_online_correlative_scan_matching = true
TRAJECTORY_BUILDER_2D.motion_filter.max_distance_meters = 0.1
TRAJECTORY_BUILDER_2D.motion_filter.max_angle_radians = math.rad(0.2)
POSE_GRAPH.constraint_builder.min_score = 0.65
POSE_GRAPH.constraint_builder.global_localization_min_score = 0.7
POSE_GRAPH.optimize_every_n_nodes = 90
POSE_GRAPH.constraint_builder.sampling_ratio = 0.3
return options
启动 SLAM 节点:
#!/usr/bin/env python3
# launch/go2_cartographer.launch.py
from launch import LaunchDescription
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
import os
def generate_launch_description():
cartographer_config_dir = os.path.join(
get_package_share_directory('go2_navigation'),
'config'
)
return LaunchDescription([
# Cartographer 节点
Node(
package='cartographer_ros',
executable='cartographer_node',
name='cartographer_node',
output='screen',
parameters=[{
'configuration_directory': cartographer_config_dir,
'configuration_basename': 'cartographer_go2.lua'
}],
remappings=[
('scan', '/go2/lidar'),
('imu', '/go2/imu'),
('odom', '/go2/odom'),
]
),
# 占据栅格发布
Node(
package='cartographer_ros',
executable='occupancy_grid_node',
name='cartographer_occupancy_grid_node',
parameters=[{'resolution': 0.05}]
),
# RViz 可视化
Node(
package='rviz2',
executable='rviz2',
name='rviz2',
arguments=['-d', os.path.join(
get_package_share_directory('go2_navigation'),
'rviz', 'slam.rviz'
)]
)
])
运行建图:
# 终端 1:启动 Go2 SDK
ros2 launch unitree_go2_sdk go2_bringup.launch.py
# 终端 2:启动 SLAM
ros2 launch go2_navigation cartographer.launch.py
# 终端 3:遥控 Go2 移动(建图需要运动)
ros2 run teleop_twist_keyboard teleop_twist_keyboard
# 终端 4:保存地图
ros2 run nav2_map_server map_saver_cli -f ~/go2_map
2.2 RTAB-Map 3D SLAM
如果需要 3D 建图(用于复杂地形),RTAB-Map 是更好的选择。
# 安装 RTAB-Map
sudo apt install ros-humble-rtabmap-ros
RTAB-Map 配置(融合激光雷达 + 深度相机):
# launch/go2_rtabmap.launch.py
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='rtabmap_slam',
executable='rtabmap',
name='rtabmap',
output='screen',
parameters=[{
'database_path': '~/.ros/rtabmap.db',
'frame_id': 'base_link',
'subscribe_depth': False,
'subscribe_scan': True,
'subscribe_rgb': False,
'approx_sync': True,
# ICP 参数
'Reg/Strategy': '1', # ICP
'Reg/Force3DoF': 'true', # 2D 模式
'ICP/MaxTranslation': '0.2',
'ICP/MaxRotation': '0.3',
# 回环检测
'RGBD/ProximityBySpace': 'true',
'RGBD/ProximityMaxGraphDepth': '30',
}],
remappings=[
('scan', '/go2/lidar'),
('odom', '/go2/odom'),
]
)
])
2.3 SLAM 算法对比
| 特性 | Cartographer | RTAB-Map | GMapping |
|---|---|---|---|
| 维度 | 2D/3D | 2D/3D | 2D |
| 精度 | 高 | 高 | 中 |
| 资源占用 | 中 | 高 | 低 |
| 回环检测 | ✅ 强 | ✅ 强 | ❌ 弱 |
| 适用场景 | 室内平面 | 复杂地形 | 小范围 |
| Go2 推荐度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ |
选择建议:
- 室内平坦环境 → Cartographer
- 需要 3D 地图或户外 → RTAB-Map
- 资源受限的嵌入式设备 → GMapping
2.4 建图质量评估
指标:
- 一致性:回环检测后地图是否对齐
- 完整性:是否覆盖所有可达区域
- 精度:重复行走同一路径,地图重合度
常见问题:
- 地图漂移:IMU 校准不准或里程计误差过大
- 解决:重新校准 IMU,检查里程计参数
- 回环失败:特征点不足(长廊环境)
- 解决:降低
min_score阈值,增加回环搜索范围
- 解决:降低
- 地图分层:地面不平整导致 2D 投影错误
- 解决:调整
base_link高度,使用 3D SLAM
- 解决:调整
3. Nav2 导航框架
3.1 Nav2 架构
Nav2 是 ROS2 的标准导航框架,包含:
- 全局规划器:A* / Dijkstra / Theta*
- 局部规划器:DWB / TEB / MPPI
- 代价地图:静态层 + 膨胀层 + 障碍层
- 恢复行为:旋转、后退、清除代价地图
3.2 安装与配置
sudo apt install ros-humble-navigation2
sudo apt install ros-humble-nav2-bringup
核心配置文件:
# config/nav2_params.yaml
amcl:
ros__parameters:
use_sim_time: False
alpha1: 0.2
alpha2: 0.2
alpha3: 0.2
alpha4: 0.2
alpha5: 0.2
base_frame_id: "base_link"
beam_skip_distance: 0.5
beam_skip_error_threshold: 0.9
beam_skip_threshold: 0.3
do_beams_skip: false
global_frame_id: "map"
lambda_short: 0.1
laser_likelihood_max_dist: 2.0
laser_max_range: 12.0
laser_min_range: 0.15
laser_model_type: "likelihood_field"
max_beams: 60
max_particles: 2000
min_particles: 500
odom_frame_id: "odom"
pf_err: 0.05
pf_z: 0.99
recovery_alpha_fast: 0.0
recovery_alpha_slow: 0.0
resample_interval: 1
robot_model_type: "nav2_amcl::DifferentialMotionModel"
save_pose_rate: 0.5
sigma_hit: 0.2
tf_broadcast: true
transform_tolerance: 1.0
update_min_a: 0.2
update_min_d: 0.25
z_hit: 0.5
z_max: 0.05
z_rand: 0.5
z_short: 0.05
bt_navigator:
ros__parameters:
use_sim_time: False
global_frame: map
robot_base_frame: base_link
odom_topic: /go2/odom
bt_loop_duration: 10
default_server_timeout: 20
default_nav_to_pose_bt_xml: "(path to nav2_bt_xml)/navigate_to_pose_w_replanning_and_recovery.xml"
plugin_lib_names:
- nav2_compute_path_to_pose_action_bt_node
- nav2_follow_path_action_bt_node
- nav2_spin_action_bt_node
- nav2_wait_action_bt_node
- nav2_clear_costmap_service_bt_node
- nav2_is_stuck_condition_bt_node
- nav2_goal_reached_condition_bt_node
- nav2_goal_updated_condition_bt_node
- nav2_initial_pose_received_condition_bt_node
- nav2_reinitialize_global_localization_service_bt_node
- nav2_rate_controller_bt_node
- nav2_distance_controller_bt_node
- nav2_speed_controller_bt_node
- nav2_truncate_path_action_bt_node
- nav2_goal_updater_node_bt_node
- nav2_recovery_node_bt_node
- nav2_pipeline_sequence_bt_node
- nav2_round_robin_node_bt_node
- nav2_transform_available_condition_bt_node
- nav2_time_expired_condition_bt_node
- nav2_distance_traveled_condition_bt_node
controller_server:
ros__parameters:
use_sim_time: False
controller_frequency: 20.0
min_x_velocity_threshold: 0.001
min_y_velocity_threshold: 0.001
min_theta_velocity_threshold: 0.001
progress_checker_plugin: "progress_checker"
goal_checker_plugins: ["general_goal_checker"]
controller_plugins: ["FollowPath"]
progress_checker:
plugin: "nav2_controller::SimpleProgressChecker"
required_movement_radius: 0.5
movement_time_allowance: 10.0
general_goal_checker:
plugin: "nav2_controller::SimpleGoalChecker"
stateful: True
xy_goal_tolerance: 0.25
yaw_goal_tolerance: 0.25
FollowPath:
plugin: "nav2_mppi_controller::MPPIController"
time_steps: 56
model_dt: 0.05
batch_size: 2000
ax_max: 3.0
ax_min: -3.0
ay_max: 3.0
az_max: 3.5
vx_std: 0.2
vy_std: 0.2
wz_std: 0.4
vx_max: 0.5
vx_min: -0.35
vy_max: 0.5
wz_max: 1.9
iteration_count: 1
prune_distance: 1.7
transform_tolerance: 0.1
temperature: 0.3
gamma: 0.015
motion_model: "DiffDrive"
visualize: false
reset_period: 1.0
TrajectoryVisualizer:
trajectory_step: 5
time_step: 3
critics: ["ConstraintCritic", "CostCritic", "GoalCritic", "GoalAngleCritic", "PathAlignCritic", "PathFollowCritic", "PathAngleCritic", "PreferForwardCritic"]
ConstraintCritic:
cost_weight: 4.0
cost_power: 1
GoalCritic:
cost_weight: 5.0
cost_power: 1
threshold_to_consider: 1.4
GoalAngleCritic:
cost_weight: 3.0
cost_power: 1
threshold_to_consider: 0.5
PreferForwardCritic:
cost_weight: 5.0
cost_power: 1
threshold_to_consider: 0.5
local_costmap:
local_costmap:
ros__parameters:
update_frequency: 5.0
publish_frequency: 2.0
global_frame: odom
robot_base_frame: base_link
use_sim_time: False
rolling_window: true
width: 3
height: 3
resolution: 0.05
robot_radius: 0.3
plugins: ["voxel_layer", "inflation_layer"]
inflation_layer:
plugin: "nav2_costmap_2d::InflationLayer"
cost_scaling_factor: 3.0
inflation_radius: 0.55
voxel_layer:
plugin: "nav2_costmap_2d::VoxelLayer"
enabled: True
footprint_clearing_enabled: true
max_obstacle_height: 2.0
publish_voxel_map: True
origin_z: 0.0
z_resolution: 0.05
z_voxels: 16
max_obstacle_height: 2.0
mark_threshold: 0
observation_sources: scan
scan:
topic: /go2/lidar
max_obstacle_height: 2.0
clearing: True
marking: True
data_type: "LaserScan"
raytrace_max_range: 3.0
raytrace_min_range: 0.0
obstacle_max_range: 2.5
obstacle_min_range: 0.0
static_layer:
map_subscribe_transient_local: True
always_send_full_costmap: True
global_costmap:
global_costmap:
ros__parameters:
update_frequency: 1.0
publish_frequency: 1.0
global_frame: map
robot_base_frame: base_link
use_sim_time: False
robot_radius: 0.3
resolution: 0.05
track_unknown_space: true
plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
obstacle_layer:
plugin: "nav2_costmap_2d::ObstacleLayer"
enabled: True
observation_sources: scan
scan:
topic: /go2/lidar
max_obstacle_height: 2.0
clearing: True
marking: True
data_type: "LaserScan"
raytrace_max_range: 3.0
raytrace_min_range: 0.0
obstacle_max_range: 2.5
obstacle_min_range: 0.0
static_layer:
plugin: "nav2_costmap_2d::StaticLayer"
map_subscribe_transient_local: True
inflation_layer:
plugin: "nav2_costmap_2d::InflationLayer"
cost_scaling_factor: 3.0
inflation_radius: 0.55
always_send_full_costmap: True
3.3 启动导航
# launch/go2_nav2.launch.py
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from ament_index_python.packages import get_package_share_directory
import os
def generate_launch_description():
nav2_bringup_dir = get_package_share_directory('nav2_bringup')
return LaunchDescription([
IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(nav2_bringup_dir, 'launch', 'bringup_launch.py')
),
launch_arguments={
'map': os.path.join(
get_package_share_directory('go2_navigation'),
'maps', 'go2_map.yaml'
),
'params_file': os.path.join(
get_package_share_directory('go2_navigation'),
'config', 'nav2_params.yaml'
),
'use_sim_time': 'False'
}.items()
)
])
# 终端 1:启动 Go2
ros2 launch unitree_go2_sdk go2_bringup.launch.py
# 终端 2:启动导航
ros2 launch go2_navigation nav2.launch.py
# 终端 3:RViz 可视化
ros2 run rviz2 rviz2
# 终端 4:发送导航目标
ros2 run nav2_simple_commander demo_security
3.4 Python 导航控制
#!/usr/bin/env python3
"""Nav2 导航控制示例"""
import rclpy
from rclpy.node import Node
from nav2_simple_commander.robot_navigator import BasicNavigator, TaskResult
from geometry_msgs.msg import PoseStamped
import tf_transformations
class Go2Navigator(Node):
def __init__(self):
super().__init__('go2_navigator')
self.navigator = BasicNavigator('go2_navigator')
# 设置初始位姿
initial_pose = PoseStamped()
initial_pose.header.frame_id = 'map'
initial_pose.header.stamp = self.get_clock().now().to_msg()
initial_pose.pose.position.x = 0.0
initial_pose.pose.position.y = 0.0
initial_pose.pose.orientation.w = 1.0
self.navigator.setInitialPose(initial_pose)
# 等待 Nav2 激活
self.navigator.waitUntilNav2Active()
self.get_logger().info('导航系统就绪')
def navigate_to_pose(self, x, y, theta):
"""导航到目标位姿"""
goal_pose = PoseStamped()
goal_pose.header.frame_id = 'map'
goal_pose.header.stamp = self.get_clock().now().to_msg()
goal_pose.pose.position.x = x
goal_pose.pose.position.y = y
q = tf_transformations.quaternion_from_euler(0, 0, theta)
goal_pose.pose.orientation.x = q[0]
goal_pose.pose.orientation.y = q[1]
goal_pose.pose.orientation.z = q[2]
goal_pose.pose.orientation.w = q[3]
self.get_logger().info(f'导航到 ({x}, {y}, {theta:.2f})')
self.navigator.goToPose(goal_pose)
while not self.navigator.isTaskComplete():
feedback = self.navigator.getFeedback()
if feedback:
self.get_logger().info(
f'距离目标: {feedback.distance_remaining:.2f}m'
)
result = self.navigator.getResult()
if result == TaskResult.SUCCEEDED:
self.get_logger().info('到达目标!')
return True
else:
self.get_logger().error('导航失败')
return False
def main():
rclpy.init()
navigator = Go2Navigator()
# 依次访问多个目标点
waypoints = [
(2.0, 0.0, 0.0),
(2.0, 2.0, 1.57),
(0.0, 2.0, 3.14),
(0.0, 0.0, -1.57),
]
for x, y, theta in waypoints:
success = navigator.navigate_to_pose(x, y, theta)
if not success:
break
rclpy.shutdown()
if __name__ == '__main__':
main()
4. 视觉导航
4.1 深度相机避障
Go2 前置深度相机可用于检测激光雷达盲区的障碍物。
#!/usr/bin/env python3
"""深度相机障碍物检测"""
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image, LaserScan
from cv_bridge import CvBridge
import cv2
import numpy as np
class DepthObstacleDetector(Node):
def __init__(self):
super().__init__('depth_obstacle_detector')
self.bridge = CvBridge()
self.subscription = self.create_subscription(
Image,
'/go2/camera/depth',
self.depth_callback,
10
)
self.publisher = self.create_publisher(
LaserScan,
'/go2/camera/scan',
10
)
self.get_logger().info('深度障碍物检测器已启动')
def depth_callback(self, msg):
# 转换为 OpenCV 格式
depth_image = self.bridge.imgmsg_to_cv2(msg, desired_encoding='32FC1')
# 参数
fx = 608.3 # 焦距(根据相机型号调整)
height, width = depth_image.shape
# 将深度图转换为伪激光雷达数据
scan = LaserScan()
scan.header = msg.header
scan.angle_min = -0.5 # 水平视角的一半
scan.angle_max = 0.5
scan.angle_increment = 0.01
scan.range_min = 0.3
scan.range_max = 5.0
# 采样深度图中间行
center_row = height // 2
ranges = []
for col in range(0, width, width // 100): # 采样 100 个点
depth = depth_image[center_row, col]
if np.isnan(depth) or depth < 0.3 or depth > 5.0:
ranges.append(float('inf'))
else:
# 计算水平角度
angle = np.arctan2(col - width // 2, fx)
ranges.append(depth)
scan.ranges = ranges
self.publisher.publish(scan)
def main():
rclpy.init()
node = DepthObstacleDetector()
rclpy.spin(node)
rclpy.shutdown()
if __name__ == '__main__':
main()
4.2 YOLO 目标跟随
结合 YOLOv8 实现目标识别与跟随:
#!/usr/bin/env python3
"""YOLO 目标跟随"""
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image
from geometry_msgs.msg import Twist
from cv_bridge import CvBridge
from ultralytics import YOLO
import cv2
import numpy as np
class TargetFollower(Node):
def __init__(self):
super().__init__('target_follower')
self.bridge = CvBridge()
# 加载 YOLO 模型
self.model = YOLO('yolov8n.pt')
self.target_class = 0 # 0=person
# 订阅相机
self.subscription = self.create_subscription(
Image,
'/go2/camera/front',
self.image_callback,
10
)
# 发布速度指令
self.publisher = self.create_publisher(Twist, '/go2/cmd_vel', 10)
# 控制参数
self.kp_linear = 0.5 # 距离比例系数
self.kp_angular = 0.003 # 角度比例系数
self.target_distance = 1.5 # 期望距离(米)
self.target_x = 320 # 图像中心 x
self.get_logger().info('目标跟随器已启动')
def image_callback(self, msg):
# 转换为 OpenCV 格式
cv_image = self.bridge.imgmsg_to_cv2(msg, 'bgr8')
# YOLO 检测
results = self.model(cv_image, verbose=False)
twist = Twist()
if len(results) > 0 and len(results[0].boxes) > 0:
# 找到最近的目标
boxes = results[0].boxes
target_idx = 0 # 简化:选第一个
box = boxes[target_idx]
x1, y1, x2, y2 = map(int, box.xyxy[0])
# 目标中心
cx = (x1 + x2) // 2
cy = (y1 + y2) // 2
# 估计距离(基于边界框大小)
box_height = y2 - y1
estimated_distance = 1000.0 / box_height # 经验公式
# 计算控制量
error_distance = estimated_distance - self.target_distance
error_angle = self.target_x - cx
twist.linear.x = self.kp_linear * error_distance
twist.angular.z = self.kp_angular * error_angle
# 限制速度
twist.linear.x = np.clip(twist.linear.x, -0.3, 0.5)
twist.angular.z = np.clip(twist.angular.z, -0.5, 0.5)
self.get_logger().info(
f'目标距离: {estimated_distance:.2f}m, '
f'角度误差: {error_angle}px, '
f'速度: ({twist.linear.x:.2f}, {twist.angular.z:.2f})'
)
self.publisher.publish(twist)
def main():
rclpy.init()
node = TargetFollower()
rclpy.spin(node)
rclpy.shutdown()
if __name__ == '__main__':
main()
5. 性能优化
5.1 计算资源分配
Go2 机载算力有限(通常为 Intel NUC 或类似),需要合理分配:
# 查看 CPU 使用
htop
# 限制节点 CPU 使用
ros2 run cartographer_ros cartographer_node \
--ros-args -p use_sim_time:=false \
--cpu-affinity 0,1 # 绑定到 CPU 0-1
# 降低频率
# cartographer: 10 Hz → 5 Hz
# 代价地图: 5 Hz → 2 Hz
5.2 内存优化
# 限制地图大小
# nav2_params.yaml
global_costmap:
global_costmap:
ros__parameters:
width: 20 # 限制为 20m x 20m
height: 20
# 定期清理
ros2 run nav2_map_server map_saver_cli -f ~/go2_map --ros-args -p save_map_timeout:=5.0
5.3 导航调优技巧
1. 局部规划器选择:
- DWB:经典稳定,适合大多数场景
- TEB:考虑运动学约束,适合狭窄空间
- MPPI:基于采样,适合复杂动态环境(推荐 Go2)
2. 代价地图膨胀半径:
inflation_layer:
inflation_radius: 0.55 # 机器人半径 + 安全距离
cost_scaling_factor: 3.0 # 越大,边缘代价下降越快
3. 目标容差:
general_goal_checker:
xy_goal_tolerance: 0.25 # 位置容差(米)
yaw_goal_tolerance: 0.25 # 角度容差(弧度)
6. 相关资源
如果你想深入了解机器狗开发,这些文章可能对你有帮助:
- 宇树 Go2 机器狗 ROS2 开发完全指南 - 环境搭建与基础控制
- 什么是具身智能? - 理解自主导航的理论基础
- 具身智能技术栈详解 - 感知、决策、执行全链路
- ROS2 入门:为机器人搭建通信骨架 - ROS2 核心概念
- 机器人开发环境搭建指南 - Ubuntu + ROS2 完整教程
总结
本文系统讲解了 Unitree Go2 的自主导航实现:
- ✅ SLAM 建图(Cartographer + RTAB-Map)
- ✅ Nav2 导航框架配置
- ✅ 全局路径规划与局部避障
- ✅ 视觉导航(深度相机 + YOLO)
- ✅ 性能优化与调优技巧
下一步:
- 尝试多机协同导航(见 037 文章)
- 集成强化学习优化局部规划器
- 探索户外导航(GPS + RTK)
FAQ
Go2 的 LDS 激光雷达有效范围是多少?
Go2 搭载的 LDS 激光雷达有效范围为 0.15m - 12m,扫描频率 10Hz,角度分辨率约 0.5°。在室内环境中表现良好,但在强阳光或反光表面场景下可能受影响。
Cartographer 和 RTAB-Map 应该选哪个?
Cartographer 适合 2D 平面导航,精度高、资源占用少;RTAB-Map 适合 3D 建图和复杂地形。如果你的 Go2 主要在平坦室内环境工作,推荐 Cartographer;如果需要处理楼梯、坡道等 3D 场景,选择 RTAB-Map。
Nav2 导航时机器人经常卡住怎么办?
常见原因:1) 膨胀半径设置过小,尝试增加 inflation_radius 到 0.6m;2) 局部规划器参数过于保守,调整 vx_max 和 wz_max;3) 代价地图更新频率太低,将 update_frequency 提升到 5Hz。
如何提高 SLAM 建图质量?
- 确保 IMU 校准准确;2) 建图时保持匀速运动(0.3-0.5m/s);3) 多次经过同一区域以触发回环检测;4) 避免长廊等特征稀疏环境;5) 使用
optimize_every_n_nodes参数控制优化频率。
Go2 可以在户外导航吗?
可以,但需要额外传感器。室内 LDS 在户外强光下失效,建议融合 GPS/RTK 提供全局定位,配合深度相机和 IMU 进行局部避障。参考 037 文章的多传感器融合方案。