基于Isaac Sim场景的Coverage Path Planning 项目部署完整指南

Coverage Path Planning 项目部署与场景转换完整指南

项目概述

rodriguesrenato/coverage-path-planning 是一个覆盖路径规划算法,使用A*搜索算法的变体来为机器人生成最优的全覆盖轨迹。项目要求输入格式为NxM的numpy数组,其中:

  • 0 = 自由区域
  • 1 = 障碍物
  • 2 = 起始点

环境准备

激活Isaac Lab环境

bash 复制代码
cd ~/Project
conda activate isaaclab_4_5_0

验证依赖包

scss 复制代码
# 检查必要的Python包
python -c "import numpy; print('NumPy version:', numpy.__version__)"
python -c "import matplotlib; print('Matplotlib available')"
python -c "import enum; print('Enum available')"

第一步:克隆项目

bash 复制代码
cd ~/Project
git clone https://github.com/rodriguesrenato/coverage-path-planning.git
cd coverage-path-planning

第二步:安装项目依赖

根据项目要求,安装必要的Python包:

bash 复制代码
# 如果缺少matplotlib
pip install matplotlib

# 如果缺少其他依赖包
pip install numpy matplotlib scipy

第三步:理解项目结构

根据实际克隆的项目结构:

bash 复制代码
coverage-path-planning/
├── docs/                    # 文档文件夹(包含PDF论文)
├── images/                  # 算法说明图片
├── maps/                    # 示例地图文件(map0.npy 到 map4.npy)
├── output_images/           # 输出结果图片(map0.png 到 map4.png)
├── coverage_planner.py      # 主要的规划器类
├── coverage_test.py         # 测试脚本
├── generate_np_maps.py      # 地图生成脚本
├── maps_orig               # 原始地图数据
├── README.md               # 项目说明
└── LICENSE                 # 许可证

第四步:从IsaacSim USD场景生成occupancy map

使用Isaac Sim内置的Occupancy Map生成器

  1. 启动Isaac Sim
bash 复制代码
cd ~/isaacsim
./isaac-sim.sh
  1. 加载你的USD场景
markdown 复制代码
*   File → Open → 选择你的 `.usd` 场景文件
  1. 确保所有几何体有碰撞属性
markdown 复制代码
*   选中场景中的对象
*   右键 → Add → Physics → Collider Preset
  1. 生成Occupancy Map
markdown 复制代码
*   顶部菜单:tools → robotics → Occupancy Map

*   设置参数:

    *   Origin: 起始位置 (确保是空闲区域)
    *   Lower Bound: 地图下边界
    *   Upper Bound: 地图上边界
    *   Cell Size: 网格大小 (建议0.1米)
    *   Origin Z: 扫描高度 (如0.1米)

*   点击 **CALCULATE**

*   点击 **VISUALIZE IMAGE**

*   点击 **SAVE IMAGE** 保存为PNG文件

如下图所示:

理解Isaac Sim输出信息

从输出信息示例:

yaml 复制代码
Top Left: (-29.975, -39.975) 
Top Right: (-29.975, 64.975) 
Bottom Left: (9.975, -39.975) 
Bottom Right: (9.975, 64.975) 
Coordinates of top left of image (pixel 0,0) as origin, + X down, + Y right: (39.975, 9.975)
Image size in pixels: 2100, 800

分析

  • 实际世界坐标范围:X轴从-29.975到9.975米(约40米),Y轴从-39.975到64.975米(约105米)
  • 图像尺寸:2100x800像素
  • 分辨率:X方向约0.019m/pixel,Y方向约0.131m/pixel

第五步:创建PNG到NumPy转换脚本

创建 convert_isaac_png_to_coverage.py

python 复制代码
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import os

def convert_isaac_png_to_coverage_format(png_path, cell_size_info, start_position=None, output_name="isaac_scene"):
    """
    将Isaac Sim生成的PNG占用栅格转换为coverage-path-planning格式
    
    Args:
        png_path: PNG文件路径
        cell_size_info: Isaac Sim输出的坐标信息字典
        start_position: 起始点位置 (x, y) 在numpy数组坐标系中,如果为None则自动选择
        output_name: 输出文件名前缀
    """
    
    print("=== Isaac Sim PNG 转 Coverage-Path-Planning 格式 ===")
    print(f"处理文件: {png_path}")
    
    # 1. 读取PNG图像
    if not os.path.exists(png_path):
        print(f"错误: 找不到文件 {png_path}")
        return None
        
    img = Image.open(png_path)
    if img.mode != 'L':  # 如果不是灰度图,转换为灰度
        img = img.convert('L')
    
    img_array = np.array(img)
    print(f"原始图像尺寸: {img_array.shape}")
    print(f"像素值范围: {img_array.min()} - {img_array.max()}")
    
    # 2. 显示原始图像
    plt.figure(figsize=(12, 5))
    plt.subplot(1, 3, 1)
    plt.imshow(img_array, cmap='gray', origin='upper')
    plt.title('原始Isaac Sim图像')
    plt.colorbar()
    
    # 3. 转换为二值图像 (0=自由区域, 1=障碍物)
    # Isaac Sim通常: 黑色(低值)=障碍物, 白色(高值)=自由区域
    # 使用阈值方法转换
    threshold = 128  # 可以根据实际情况调整
    binary_map = (img_array < threshold).astype(int)
    
    print(f"二值化后统计:")
    print(f"  自由区域(0): {np.sum(binary_map == 0)} 像素")
    print(f"  障碍物(1): {np.sum(binary_map == 1)} 像素")
    
    # 4. 显示二值化结果
    plt.subplot(1, 3, 2)
    plt.imshow(binary_map, cmap='viridis', origin='upper')
    plt.title('二值化结果\n0=Free, 1=Obstacle')
    plt.colorbar()
    
    # 5. 选择起始点
    if start_position is None:
        # 自动选择起始点:在自由区域中心附近
        free_positions = np.where(binary_map == 0)
        if len(free_positions[0]) > 0:
            center_x, center_y = binary_map.shape[0] // 2, binary_map.shape[1] // 2
            distances = (free_positions[0] - center_x)**2 + (free_positions[1] - center_y)**2
            closest_idx = np.argmin(distances)
            start_x = free_positions[0][closest_idx]
            start_y = free_positions[1][closest_idx]
            start_position = (start_x, start_y)
        else:
            print("警告: 没有找到自由区域,使用中心点作为起始点")
            start_position = (binary_map.shape[0] // 2, binary_map.shape[1] // 2)
    
    # 6. 创建最终的coverage map
    coverage_map = binary_map.copy()
    start_x, start_y = start_position
    coverage_map[start_x, start_y] = 2  # 设置起始点
    
    print(f"起始点设置在: ({start_x}, {start_y})")
    print(f"最终地图统计:")
    print(f"  自由区域(0): {np.sum(coverage_map == 0)}")
    print(f"  障碍物(1): {np.sum(coverage_map == 1)}")
    print(f"  起始点(2): {np.sum(coverage_map == 2)}")
    
    # 7. 显示最终结果
    plt.subplot(1, 3, 3)
    plt.imshow(coverage_map, cmap='viridis', origin='upper')
    plt.title('Coverage Map\n0=Free, 1=Obstacle, 2=Start')
    plt.colorbar()
    
    # 标记起始点
    plt.plot(start_y, start_x, 'r*', markersize=15, label='Start Point')
    plt.legend()
    
    plt.tight_layout()
    plt.show()
    
    # 8. 保存结果
    output_dir = '/home/lwb/Project/coverage-path-planning/maps/'
    os.makedirs(output_dir, exist_ok=True)
    
    npy_path = os.path.join(output_dir, f'{output_name}.npy')
    png_output_path = os.path.join(output_dir, f'{output_name}.png')
    
    np.save(npy_path, coverage_map)
    
    # 保存可视化图像
    plt.figure(figsize=(8, 6))
    plt.imshow(coverage_map, cmap='viridis', origin='upper')
    plt.colorbar(label='0=Free, 1=Obstacle, 2=Start')
    plt.title(f'Coverage Map: {output_name}')
    plt.plot(start_y, start_x, 'r*', markersize=15, label='Start Point')
    plt.legend()
    plt.savefig(png_output_path, dpi=150, bbox_inches='tight')
    plt.show()
    
    print(f"\n转换完成!")
    print(f"  NumPy文件: {npy_path}")
    print(f"  图像文件: {png_output_path}")
    
    return coverage_map, npy_path

def resize_map_for_coverage_planning(coverage_map, target_max_size=100):
    """
    将地图调整到适合coverage planning的尺寸
    coverage-path-planning算法对于大地图可能会很慢
    """
    original_shape = coverage_map.shape
    
    # 如果地图太大,进行下采样
    if max(original_shape) > target_max_size:
        from scipy import ndimage
        
        scale_factor = target_max_size / max(original_shape)
        new_shape = (int(original_shape[0] * scale_factor), 
                    int(original_shape[1] * scale_factor))
        
        print(f"地图尺寸从 {original_shape} 调整到 {new_shape}")
        
        # 下采样
        resized_map = ndimage.zoom(coverage_map, scale_factor, order=0)  # order=0 保持整数值
        
        # 确保值仍然是 0, 1, 2
        resized_map = np.round(resized_map).astype(int)
        
        # 确保至少有一个起始点
        if np.sum(resized_map == 2) == 0:
            free_positions = np.where(resized_map == 0)
            if len(free_positions[0]) > 0:
                center_idx = len(free_positions[0]) // 2
                start_x = free_positions[0][center_idx]
                start_y = free_positions[1][center_idx]
                resized_map[start_x, start_y] = 2
        
        return resized_map
    
    return coverage_map

# 使用示例
if __name__ == "__main__":
    # 你需要修改这个路径为你的PNG文件路径
    png_file_path = "/path/to/your/isaac_sim_occupancy_map.png"
    
    # Isaac Sim输出的坐标信息(根据你的实际输出调整)
    cell_size_info = {
        "top_left": (-29.975, -39.975),
        "top_right": (-29.975, 64.975),
        "bottom_left": (9.975, -39.975),
        "bottom_right": (9.975, 64.975),
        "image_size": (2100, 800),
        "origin_offset": (39.975, 9.975)
    }
    
    # 转换地图
    coverage_map, npy_path = convert_isaac_png_to_coverage_format(
        png_file_path, 
        cell_size_info, 
        start_position=None,  # 自动选择起始点
        output_name="kitchen_scene"
    )
    
    if coverage_map is not None:
        # 如果地图太大,调整尺寸
        if max(coverage_map.shape) > 50:  # coverage-path-planning对大地图可能很慢
            print("\n地图较大,建议调整尺寸...")
            resized_map = resize_map_for_coverage_planning(coverage_map, target_max_size=50)
            
            # 保存调整尺寸后的地图
            resized_npy_path = npy_path.replace('.npy', '_resized.npy')
            np.save(resized_npy_path, resized_map)
            print(f"调整尺寸后的地图保存到: {resized_npy_path}")
            
            # 可视化调整后的地图
            plt.figure(figsize=(8, 6))
            plt.imshow(resized_map, cmap='viridis', origin='upper')
            plt.colorbar(label='0=Free, 1=Obstacle, 2=Start')
            plt.title('Resized Coverage Map')
            start_pos = np.where(resized_map == 2)
            if len(start_pos[0]) > 0:
                plt.plot(start_pos[1][0], start_pos[0][0], 'r*', markersize=15, label='Start Point')
                plt.legend()
            plt.show()

第六步:运行转换脚本

bash 复制代码
cd ~/Project/coverage-path-planning

# 创建转换脚本
nano convert_isaac_png_to_coverage.py
# 粘贴上面的代码,修改png_file_path为你的PNG文件路径,并且修改Isaac Sim输出的坐标信息

# 运行转换
python convert_isaac_png_to_coverage.py

运行转换效果图:

第七步:测试转换后的地图

复制代码
python coverage_test.py

未缩放大小的结果

压缩大小的结果

输出的不同启发式算法对比

覆盖路径规划结果分析

性能排名(按代价Cost)

  1. HORIZONTAL + < : 4944步,542.70 最优
  2. HORIZONTAL + v: 4944步,542.80
  3. HORIZONTAL + > : 4980步,545.60
  4. HORIZONTAL + ^ : 4980步,545.70
  5. VERTICAL系列: 4979-5004步,560.30-562.50
  6. CHEBYSHEV系列: 4987-5027步,587.60-594.00
  7. MANHATTAN系列: 5027-5176步,562.70-586.90

启发式算法效果对比

算法 平均代价 性能评级
HORIZONTAL ~544 优秀
VERTICAL ~561 良好
MANHATTAN ~573 一般
CHEBYSHEV ~591 较差

实用建议

推荐: 厨房环境使用HORIZONTAL算法,初始方向选择西向或南向

参数调优指南

如果coverage planning失败

  1. 地图太大

    ini 复制代码
    # 在convert脚本中调整target_max_size
    resized_map = resize_map_for_coverage_planning(coverage_map, target_max_size=30)
  2. 起始点位置不好

    ini 复制代码
    # 手动指定起始点
    start_position = (map_height//4, map_width//4)  # 左上角1/4位置

性能优化建议

  1. 对于大地图:将尺寸控制在50x50以内
  2. 对于复杂地图:可以先简化,移除小障碍物
  3. 起始点选择:选择在较大自由区域的中心
相关推荐
AlfredZhao4 小时前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户97183563346610 小时前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪11 小时前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠1 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush41 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5201 天前
Linux 11 动态监控指令top
linux
不会C语言的男孩1 天前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
古城小栈1 天前
Unix 与 Linux 异同小叙
linux·服务器·unix
凡人叶枫1 天前
Effective C++ 条款42:了解 typename 的双重意义
java·linux·服务器·c++
2601_961875241 天前
决战申论100题2026|最新|范文
linux·容器·centos·debian·ssh·fabric·vagrant