基于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. 起始点选择:选择在较大自由区域的中心
相关推荐
cui_win2 分钟前
【网络】Linux 内核优化实战 - net.ipv4.tcp_congestion_control
linux·网络·tcp/ip
笑稀了的野生俊1 小时前
ImportError: /lib/x86_64-linux-gnu/libc.so.6: version GLIBC_2.32‘ not found
linux·人工智能·ubuntu·大模型·glibc·flash-attn
千帐灯无此声1 小时前
Linux 测开:日志分析 + 定位 Bug
linux·c语言·c++·bug
誰能久伴不乏1 小时前
深入了解 Vim 编辑器:从入门到精通
linux·编辑器·vim
ghie90902 小时前
在Centos系统上如何有效删除文件和目录的指令汇总
linux·运维·centos
Linux-palpitate2 小时前
Keepalived+LVS实现LNMP网站的高可用部署
linux·运维·服务器·mysql·lvs
潇-xiao2 小时前
进程状态 + 进程优先级切换调度-进程概念(5)
linux·笔记
江公望3 小时前
Linux kernel regcache_cache_only()函数详解
linux
liuyunluoxiao4 小时前
基于生产者消费者模型的线程池【Linux操作系统】
linux
程序员黄老师5 小时前
Ubuntu 24.04上安装 Intelligent Pinyin 中文输入法
linux·运维·ubuntu