"""测量详细日志记录器 - 记录测量过程的所有详细信息""" import sys from pathlib import Path from datetime import datetime from typing import Optional class MeasurementLogger: """测量过程详细日志记录器""" def __init__(self, output_dir: str = "results"): """ 初始化测量日志记录器 Args: output_dir: 输出目录路径 """ self.output_dir = Path(output_dir) self.output_dir.mkdir(parents=True, exist_ok=True) # 生成日志文件名 timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') self.log_file = self.output_dir / f"measurement_detail_{timestamp}.log" # 打开日志文件 self.file_handle = open(self.log_file, 'w', encoding='utf-8') # 写入头部 self._write_header() def _write_header(self): """写入日志头部""" self.log("=" * 80) self.log("点云测量系统 - 详细测量日志") self.log("=" * 80) self.log(f"开始时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") self.log("=" * 80) self.log("") def log(self, message: str, also_print: bool = True): """ 记录日志信息 Args: message: 日志消息 also_print: 是否同时输出到控制台 """ # 写入文件 self.file_handle.write(message + "\n") self.file_handle.flush() # 立即刷新到文件 # 输出到控制台 if also_print: print(message) def log_task_start(self, task_index: int, total_tasks: int, task_name: str): """记录任务开始""" self.log("") self.log("=" * 80) self.log(f"[{task_index}/{total_tasks}] 开始执行任务: {task_name}") self.log("=" * 80) def log_roi_extraction(self, roi_name: str, point_count: int, bbox_min: list, bbox_max: list): """ 记录ROI提取信息 Args: roi_name: ROI名称(如"源平面ROI"、"目标平面ROI") point_count: 提取到的点数 bbox_min: ROI最小边界 [x, y, z] bbox_max: ROI最大边界 [x, y, z] """ self.log(f" {roi_name}:") self.log(f" 边界: X[{bbox_min[0]:.2f}, {bbox_max[0]:.2f}], " f"Y[{bbox_min[1]:.2f}, {bbox_max[1]:.2f}], " f"Z[{bbox_min[2]:.2f}, {bbox_max[2]:.2f}]") self.log(f" 提取点数: {point_count:,} 个点") def log_ransac_fitting(self, plane_name: str, total_points: int, inlier_count: int, inlier_ratio: float, plane_equation: Optional[tuple] = None): """ 记录RANSAC平面拟合信息 Args: plane_name: 平面名称(如"源平面"、"目标平面") total_points: 参与拟合的总点数 inlier_count: 内点数量 inlier_ratio: 内点率(0-1之间的浮点数) plane_equation: 平面方程 (a, b, c, d),满足 ax + by + cz + d = 0 """ self.log(f" {plane_name} RANSAC拟合:") self.log(f" 拟合点数: {total_points:,} 个点") self.log(f" 内点数量: {inlier_count:,} 个点") self.log(f" 内点率: {inlier_ratio:.2%}") if plane_equation: a, b, c, d = plane_equation self.log(f" 平面方程: {a:.6f}x + {b:.6f}y + {c:.6f}z + {d:.6f} = 0") # 计算法向量 import numpy as np normal = np.array([a, b, c]) norm = np.linalg.norm(normal) if norm > 0: normal = normal / norm self.log(f" 法向量: ({normal[0]:.6f}, {normal[1]:.6f}, {normal[2]:.6f})") def log_sampling(self, sample_count: int, method: str = "均匀采样"): """ 记录采样信息 Args: sample_count: 采样点数 method: 采样方法 """ self.log(f" 采样策略: {method}") self.log(f" 采样点数: {sample_count} 个点") def log_distance_result(self, mean_distance: float, std_distance: float, min_distance: float, max_distance: float): """ 记录距离测量结果 Args: mean_distance: 平均距离 std_distance: 标准差 min_distance: 最小距离 max_distance: 最大距离 """ self.log(f" 测量结果:") self.log(f" 平均距离: {mean_distance:.4f} mm") self.log(f" 标准差: {std_distance:.4f} mm") self.log(f" 最小距离: {min_distance:.4f} mm") self.log(f" 最大距离: {max_distance:.4f} mm") self.log(f" 距离范围: [{min_distance:.4f}, {max_distance:.4f}] mm") def log_task_success(self, execution_time: float): """记录任务成功""" self.log(f" 任务状态: ✅ 成功") self.log(f" 执行时间: {execution_time:.2f} 秒") def log_task_failure(self, error_message: str, execution_time: float): """记录任务失败""" self.log(f" 任务状态: ❌ 失败") self.log(f" 错误信息: {error_message}") self.log(f" 执行时间: {execution_time:.2f} 秒") def log_circle_fitting(self, circle_name: str, center: tuple, radius: float, point_count: int): """ 记录圆拟合信息 Args: circle_name: 圆的名称 center: 圆心坐标 (x, y, z) radius: 半径 point_count: 拟合点数 """ self.log(f" {circle_name}:") self.log(f" 圆心: ({center[0]:.4f}, {center[1]:.4f}, {center[2]:.4f})") self.log(f" 半径: {radius:.4f} mm") self.log(f" 拟合点数: {point_count:,} 个点") def log_kdtree_optimization(self, original_points: int, filtered_points: int, percentage: float): """ 记录KDTree优化信息 Args: original_points: 原始点数 filtered_points: 筛选后点数 percentage: 保留百分比 """ self.log(f" KDTree优化:") self.log(f" 原始点数: {original_points:,} 个点") self.log(f" 筛选后: {filtered_points:,} 个点 ({percentage:.2f}%)") def close(self): """关闭日志文件""" self.log("") self.log("=" * 80) self.log(f"结束时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") self.log("=" * 80) if self.file_handle: self.file_handle.close() print(f"\n详细测量日志已保存到: {self.log_file}") def __del__(self): """析构函数,确保文件被关闭""" if hasattr(self, 'file_handle') and self.file_handle: self.file_handle.close()