基于不规则坐标生成占用地图时遭遇SIGKILL内存问题求助
大尺度点云占用地图生成的内存优化方案
核心内存瓶颈分析
你的问题本质是大尺寸包围盒(Bounding Box)对应的布尔网格占用过多内存,加上全量加载CSV数据、不必要的数组拷贝,触发OOM导致SIGKILL。140万点如果对应的bbox范围很大(比如x/y跨数千米),直接生成全量布尔网格会瞬间耗尽内存。
分步优化措施
1. 数据读取:分批加载,只保留必要字段
不要一次性加载整个CSV文件到内存,逐行/分批读取,仅提取x、y坐标(z字段无用可直接跳过),同时同步计算全局边界极值,避免单独存储全量点数据。
2. 占用网格:用集合去重存储,延迟创建数组
多个点可能落在同一个网格内,用集合存储已占用的网格坐标,避免重复标记;等边界确定后,再创建最小尺寸的布尔数组,仅标记占用位置。
3. 形态学操作:原地修改减少临时内存
腐蚀操作时指定output参数为原数组,避免生成额外的临时数组占用内存。
4. 极端场景:稀疏矩阵替代稠密数组
如果bbox范围极大(比如跨数万公里),稠密布尔数组仍会爆内存,改用稀疏矩阵仅存储占用的网格位置,内存占用与实际占用网格数成正比。
优化后代码示例
基础版(稠密网格,适合大多数场景)
import csv import numpy as np from scipy.ndimage import binary_erosion class GridBuilder: def __init__(self, resolution=1.0): self.resolution = resolution # 初始化边界极值 self.x_min, self.x_max = float('inf'), -float('inf') self.y_min, self.y_max = float('inf'), -float('inf') # 用集合存储已占用的网格坐标(自动去重) self.occupied_grids = set() def process_csv(self, csv_path): """分批处理CSV,同步更新边界和占用网格""" with open(csv_path, 'r') as f: reader = csv.DictReader(f) for row in reader: x, y = float(row['x']), float(row['y']) # 更新全局边界 self.x_min = min(self.x_min, x) self.x_max = max(self.x_max, x) self.y_min = min(self.y_min, y) self.y_max = max(self.y_max, y) # 计算网格坐标(向下取整,可根据需求调整为向零取整) grid_x = int(np.floor(x / self.resolution)) grid_y = int(np.floor(y / self.resolution)) self.occupied_grids.add((grid_x, grid_y)) def build_occupied_grid(self, erosion_kernel_size=2): """生成占用网格并执行腐蚀操作""" # 计算网格尺寸 grid_width = int(np.ceil((self.x_max - self.x_min) / self.resolution)) + 1 grid_height = int(np.ceil((self.y_max - self.y_min) / self.resolution)) + 1 # 创建初始全False布尔网格(1字节/元素,内存最优) occupied_grid = np.zeros((grid_height, grid_width), dtype=np.bool_) # 计算网格坐标到数组索引的偏移量 x_offset = -int(np.floor(self.x_min / self.resolution)) y_offset = -int(np.floor(self.y_min / self.resolution)) # 标记占用网格 for gx, gy in self.occupied_grids: arr_x = gx + x_offset arr_y = gy + y_offset if 0 <= arr_x < grid_width and 0 <= arr_y < grid_height: occupied_grid[arr_y, arr_x] = True # 形态学腐蚀:原地操作减少临时内存 if erosion_kernel_size > 0: kernel = np.ones((erosion_kernel_size, erosion_kernel_size), dtype=np.bool_) binary_erosion(occupied_grid, structure=kernel, output=occupied_grid) return occupied_grid, (self.x_min, self.x_max, self.y_min, self.y_max) def is_point_occupied(self, point, grid, bbox): """检查点是否在占用区域内""" x, y = point x_min, x_max, y_min, y_max = bbox # 先判断是否在bbox内,避免越界 if not (x_min <= x <= x_max and y_min <= y <= y_max): return False # 计算点对应的网格索引 grid_x = int(np.floor(x / self.resolution)) grid_y = int(np.floor(y / self.resolution)) x_offset = -int(np.floor(x_min / self.resolution)) y_offset = -int(np.floor(y_min / self.resolution)) arr_x = grid_x + x_offset arr_y = grid_y + y_offset return grid[arr_y, arr_x]
极端场景版(稀疏矩阵)
如果bbox范围极大,稠密数组仍无法容纳,改用稀疏矩阵存储:
from scipy.sparse import csr_matrix def build_sparse_occupied_grid(self, erosion_kernel_size=2): grid_width = int(np.ceil((self.x_max - self.x_min) / self.resolution)) + 1 grid_height = int(np.ceil((self.y_max - self.y_min) / self.resolution)) + 1 x_offset = -int(np.floor(self.x_min / self.resolution)) y_offset = -int(np.floor(self.y_min / self.resolution)) rows, cols = [], [] for gx, gy in self.occupied_grids: arr_x = gx + x_offset arr_y = gy + y_offset if 0 <= arr_x < grid_width and 0 <= arr_y < grid_height: rows.append(arr_y) cols.append(arr_x) # 创建稀疏布尔矩阵 sparse_grid = csr_matrix((np.ones(len(rows), dtype=np.bool_), (rows, cols)), shape=(grid_height, grid_width)) # 稀疏矩阵腐蚀操作可根据需求选择:若内存允许,转稠密后处理;否则用稀疏形态学工具 return sparse_grid, (self.x_min, self.x_max, self.y_min, self.y_max)
额外调优建议
内存分析:用
memory_profiler模块定位内存峰值点,精准优化:pip install memory-profiler在代码中添加
@profile装饰器后运行python -m memory_profiler your_script.py。矢量化加速:如果分批读取后仍有性能问题,可改用pandas分批读取并矢量化处理坐标:
import pandas as pd chunk_size = 10000 for chunk in pd.read_csv(csv_path, usecols=['x', 'y'], chunksize=chunk_size): # 矢量化更新边界 self.x_min = min(self.x_min, chunk['x'].min()) self.x_max = max(self.x_max, chunk['x'].max()) # 矢量化计算网格坐标并去重 grid_x = np.floor(chunk['x'] / self.resolution).astype(int) grid_y = np.floor(chunk['y'] / self.resolution).astype(int) self.occupied_grids.update(zip(grid_x, grid_y))边界裁剪:如果bbox边缘存在大量无点区域,可适当缩小bbox范围(比如在极值基础上加减1米),进一步减小网格尺寸。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

