update csv export and added fixed mesh axis drawing

This commit is contained in:
ykcheungab
2026-07-15 14:25:03 +08:00
parent 3d1509df9d
commit 87ac152907
11 changed files with 578 additions and 35 deletions
+74
View File
@@ -2,6 +2,7 @@
from __future__ import annotations
import math
import sys
import time
@@ -17,6 +18,7 @@ from PyQt5.QtWidgets import (
)
from pipeline.config import PipelineConfig
from pipeline.csv_recorder import CsvRecorder
from pipeline.service import FingerPipeline
from .app_paths import resource_path
@@ -54,6 +56,7 @@ class FingerDashboardWindow(QMainWindow):
self.config = config
self.layout = layout
self.pipeline = FingerPipeline(config)
self.csv_recorder = CsvRecorder(config.csv_save_dir)
self._running = False
self._frame_index = 0
self._wall_fps = 0.0
@@ -103,6 +106,7 @@ class FingerDashboardWindow(QMainWindow):
self.settings_panel.set_config_path(self.config.config)
self.settings_panel.set_calibration_path(self.config.calib or "")
self.settings_panel.set_finger_export_dir(self.config.export_dir)
self.settings_panel.set_csv_save_dir(self.config.csv_save_dir)
self.settings_panel.log_visibility_changed.connect(self._set_log_panel_visible)
self.settings_panel.free_rotation_changed.connect(self.mesh_viewport.set_free_rotation)
@@ -112,9 +116,12 @@ class FingerDashboardWindow(QMainWindow):
self.settings_panel.browse_video_requested.connect(self._select_camera)
self.settings_panel.browse_config_requested.connect(self._select_config)
self.settings_panel.browse_finger_assets_requested.connect(self._select_finger_assets)
self.settings_panel.browse_csv_dir_requested.connect(self._select_csv_dir)
self.settings_panel.csv_record_toggled.connect(self._on_csv_record_toggled)
self.settings_panel.btn_play_pause.clicked.connect(self._on_play_pause_clicked)
self.settings_panel.btn_reset_tracker.clicked.connect(self._on_reset)
self.settings_panel.set_play_pause_state("run")
self.settings_panel.set_csv_controls_for_pipeline(False)
self._panel_widgets: dict[str, QWidget] = {}
self._panel_widgets["mesh"] = self.mesh_viewport
@@ -314,6 +321,7 @@ class FingerDashboardWindow(QMainWindow):
cal = self.settings_panel.calibration_path()
self.config.calib = cal or None
self.config.export_dir = self.settings_panel.finger_export_dir()
self.config.csv_save_dir = self.settings_panel.csv_save_dir() or "./outputs/csv"
try:
self.pipeline = FingerPipeline(self.config)
@@ -334,6 +342,7 @@ class FingerDashboardWindow(QMainWindow):
self.timer.start(self.layout.timer_interval_ms)
self._set_play_pause_button("pause")
self.settings_panel.btn_reset_tracker.setEnabled(True)
self.settings_panel.set_csv_controls_for_pipeline(True)
print(
f"▶ 启动 — 摄像机={self.config.video}, 配置={self.config.config}, "
f"标定={self.config.calib or '(none)'}, 资产={self.config.export_dir}"
@@ -342,6 +351,7 @@ class FingerDashboardWindow(QMainWindow):
def _on_stop(self) -> None:
if not self._running:
return
self._stop_csv_recording(announce=True)
self._running = False
self.timer.stop()
self.log_handler.clear_status_line()
@@ -354,6 +364,7 @@ class FingerDashboardWindow(QMainWindow):
self._set_play_pause_button("run")
self.settings_panel.btn_reset_tracker.setEnabled(False)
self.settings_panel.set_finger_export_dir_enabled(True)
self.settings_panel.set_csv_controls_for_pipeline(False)
def _on_reset(self) -> None:
if not self._running:
@@ -413,10 +424,26 @@ class FingerDashboardWindow(QMainWindow):
profile.record("total", total_ms)
self.pipeline.record_total_frame(total_ms)
measurement = self.pipeline.measure_for_csv(frame, self._frame_index)
if self.csv_recorder.is_recording:
self.csv_recorder.write(measurement)
flow_mag = 0.0
if frame.flow.size:
flow_mag = float((frame.flow[:, :, 0] ** 2 + frame.flow[:, :, 1] ** 2).mean() ** 0.5)
contact_xyz_mm = None
if (
math.isfinite(measurement.contact_x)
and math.isfinite(measurement.contact_y)
and math.isfinite(measurement.contact_z)
):
contact_xyz_mm = (
measurement.contact_x,
measurement.contact_y,
measurement.contact_z,
)
log_cfg = layout.log_config
if log_cfg.status_enabled:
self.log_handler.set_status_line(
@@ -428,6 +455,7 @@ class FingerDashboardWindow(QMainWindow):
flow_rms=flow_mag,
mask_coverage=frame.mask_coverage,
wall_fps=self._wall_fps,
contact_xyz_mm=contact_xyz_mm,
)
)
@@ -460,6 +488,50 @@ class FingerDashboardWindow(QMainWindow):
if path:
self.settings_panel.set_finger_export_dir(path)
def _select_csv_dir(self) -> None:
path = QFileDialog.getExistingDirectory(
self,
"选择 CSV 保存目录",
self.settings_panel.csv_save_dir() or "./outputs/csv",
)
if path:
self.settings_panel.set_csv_save_dir(path)
self.config.csv_save_dir = path
def _on_csv_record_toggled(self, start: bool) -> None:
if start:
self._start_csv_recording()
else:
self._stop_csv_recording(announce=True)
def _start_csv_recording(self) -> None:
if not self._running:
print("请先启动传感器后再记录 CSV。")
self.settings_panel.set_csv_recording(False)
return
save_dir = self.settings_panel.csv_save_dir() or "./outputs/csv"
self.config.csv_save_dir = save_dir
try:
path = self.csv_recorder.start(save_dir)
except OSError as exc:
print(f"无法开始 CSV 记录:{exc}")
self.settings_panel.set_csv_recording(False)
return
self.settings_panel.set_csv_recording(True)
print(f"开始 CSV 记录:{path}")
def _stop_csv_recording(self, *, announce: bool = False) -> None:
if not self.csv_recorder.is_recording:
self.settings_panel.set_csv_recording(False)
return
path, count = self.csv_recorder.stop()
self.settings_panel.set_csv_recording(False)
if announce:
if path is not None:
print(f"CSV 记录结束:{path},总行数:{count}")
else:
print(f"CSV 记录结束,总行数:{count}")
def keyPressEvent(self, event):
key = event.key()
if key == Qt.Key_Escape:
@@ -483,6 +555,7 @@ class FingerDashboardWindow(QMainWindow):
def closeEvent(self, event):
self.timer.stop()
self._stop_csv_recording(announce=True)
self.mesh_viewport.close_renderer()
self.pipeline.close()
event.accept()
@@ -493,6 +566,7 @@ def run_dashboard(config: PipelineConfig, *, layout: DashboardLayout = LAYOUT) -
from .app import get_or_create_qapp
print("\n按键说明:Q 退出 | R 重置追踪器 | S 开始录像 | E 结束录像 | ESC 全屏")
print("GUI:Record CSV / Stop CSV 写入接触点与力到所选目录。")
if config.profile:
print(f"Profiling enabled (report every {config.profile_every} frames).")
print("Log FPS= is end-to-end dashboard rate; sdk= is Sensor internal rate.")