diff --git a/.gitignore b/.gitignore index 2aa3aa6c..2e4afe99 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,10 @@ Thumbs.db dist/ build/ +# 前端构建输出 +frontend/src/renderer/dist/ +frontend/src/renderer/dist-electron/ + # 临时文件 *.tmp *.temp diff --git a/backend/app.spec b/backend/app.spec index 0997eb73..9d7bb7fe 100644 --- a/backend/app.spec +++ b/backend/app.spec @@ -14,7 +14,8 @@ a = Analysis( ('dll/femtobolt/bin/ob_usb.dll', 'dll/femtobolt/bin'), # Orbbec USB库 ('dll/femtobolt/bin/live555.dll', 'dll/femtobolt/bin'), # Live555库 ('dll/femtobolt/bin/OrbbecSDKConfig_v1.0.xml', 'dll/femtobolt/bin'), # Orbbec配置文件 ('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # SMiTSense传感器库 - ('dll/smitsense/SMiTSenseUsbWrapper.dll', 'dll/smitsense'), # SMiTSense传感器库包装类 + ('dll/smitsense/Wrapper.dll', 'dll/smitsense'), # Wrapper + ('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # Wrapper ], hiddenimports=[ 'flask', diff --git a/backend/build_app.py b/backend/build_app.py index 4bda99e1..d6864832 100644 --- a/backend/build_app.py +++ b/backend/build_app.py @@ -42,7 +42,8 @@ a = Analysis( ('dll/femtobolt/bin/ob_usb.dll', 'dll/femtobolt/bin'), # Orbbec USB库 ('dll/femtobolt/bin/live555.dll', 'dll/femtobolt/bin'), # Live555库 ('dll/femtobolt/bin/OrbbecSDKConfig_v1.0.xml', 'dll/femtobolt/bin'), # Orbbec配置文件 ('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # SMiTSense传感器库 - ('dll/smitsense/SMiTSenseUsbWrapper.dll', 'dll/smitsense'), # SMiTSense传感器库包装类 + ('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # SMiTSenseUsb库 + ('dll/smitsense/Wrapper.dll', 'dll/smitsense'), # SMiTSense传感器库包装类 ], hiddenimports=[ 'flask', diff --git a/backend/devices/utils/config.ini b/backend/devices/utils/config.ini index 29a6fdba..4e1df162 100644 --- a/backend/devices/utils/config.ini +++ b/backend/devices/utils/config.ini @@ -29,7 +29,7 @@ depth_range_max = 1700 [DEVICES] imu_device_type = real -imu_port = COM3 +imu_port = COM7 imu_baudrate = 9600 pressure_device_type = real pressure_use_mock = False diff --git a/backend/devices/utils/config_manager.py b/backend/devices/utils/config_manager.py index 28f8a211..fef59656 100644 --- a/backend/devices/utils/config_manager.py +++ b/backend/devices/utils/config_manager.py @@ -11,7 +11,7 @@ import json import logging from typing import Dict, Any, Optional, Union from pathlib import Path - +import sys class ConfigManager: """配置管理器""" @@ -43,19 +43,38 @@ class ConfigManager: Returns: str: 配置文件路径 """ + import sys + # 可能的配置文件路径 - possible_paths = [ - os.path.join(os.path.dirname(__file__), 'config.ini') - ] + possible_paths = [] + + # 如果是打包后的exe文件,从exe文件同级目录获取 + if getattr(sys, 'frozen', False): + # 打包后的exe文件路径 + exe_dir = os.path.dirname(sys.executable) + possible_paths.append(os.path.join(exe_dir, 'config.ini')) + + # 开发环境下的路径 + possible_paths.extend([ + os.path.join(os.path.dirname(__file__), 'config.ini'), + os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'config.ini'), # backend/config.ini + os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))), 'config.ini') # 项目根目录/config.ini + ]) for path in possible_paths: abs_path = os.path.abspath(path) if os.path.exists(abs_path): self.logger.info(f"找到配置文件: {abs_path}") return abs_path - - + # 如果都找不到,返回默认路径(exe同级目录或backend目录) + if getattr(sys, 'frozen', False): + default_path = os.path.join(os.path.dirname(sys.executable), 'config.ini') + else: + default_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'config.ini') + + self.logger.warning(f"未找到配置文件,使用默认路径: {default_path}") + return default_path def _load_config(self): """ 加载配置文件 diff --git a/backend/dll/smitsense/SMiTSenseUsbWrapper.dll b/backend/dll/smitsense/SMiTSenseUsbWrapper.dll deleted file mode 100644 index cfa259cc..00000000 Binary files a/backend/dll/smitsense/SMiTSenseUsbWrapper.dll and /dev/null differ diff --git a/backend/main.py b/backend/main.py index c34cefbc..8f7a4155 100644 --- a/backend/main.py +++ b/backend/main.py @@ -147,10 +147,10 @@ class AppServer: engineio_logger=False, ping_timeout=60, ping_interval=25, - manage_session=True, # 启用会话管理,解决打包环境会话问题 + manage_session=False, always_connect=False, - transports=['polling'], # 只使用polling,避免打包环境websocket问题 - allow_upgrades=False, # 禁用升级到websocket + transports=['polling', 'websocket'], # 优先使用polling + allow_upgrades=True, # 允许升级到websocket cookie=None # 禁用cookie ) self.logger.info('SocketIO初始化成功') @@ -205,9 +205,9 @@ class AppServer: self.logger.info('正在初始化设备管理器...') self.device_managers = { 'camera': CameraManager(self.socketio, self.config_manager), - 'femtobolt': FemtoBoltManager(self.socketio, self.config_manager), - 'imu': IMUManager(self.socketio, self.config_manager), - 'pressure': PressureManager(self.socketio, self.config_manager) + # 'femtobolt': FemtoBoltManager(self.socketio, self.config_manager), + # 'imu': IMUManager(self.socketio, self.config_manager), + # 'pressure': PressureManager(self.socketio, self.config_manager) } # 为每个设备添加状态变化回调 diff --git a/backend/tests/moniter_windows.py b/backend/tests/moniter_windows.py new file mode 100644 index 00000000..4198c2b1 --- /dev/null +++ b/backend/tests/moniter_windows.py @@ -0,0 +1,66 @@ +import psutil +import time +from datetime import datetime +from pynvml import * + +def get_memory_usage(): + mem = psutil.virtual_memory() + return mem.percent, mem.used / (1024 ** 3), mem.total / (1024 ** 3) + +def get_cpu_usage(): + return psutil.cpu_percent(interval=1) # 获取CPU利用率(百分比) + +def get_gpu_usage(): + try: + nvmlInit() + device_count = nvmlDeviceGetCount() + gpu_stats = [] + for i in range(device_count): + handle = nvmlDeviceGetHandleByIndex(i) + name = nvmlDeviceGetName(handle).decode("utf-8") + mem_info = nvmlDeviceGetMemoryInfo(handle) + util = nvmlDeviceGetUtilizationRates(handle) + gpu_stats.append({ + "name": name, + "gpu_util": util.gpu, + "mem_util": util.memory, + "mem_used": mem_info.used / (1024 ** 3), + "mem_total": mem_info.total / (1024 ** 3) + }) + nvmlShutdown() + return gpu_stats + except Exception: + return [{"name": "N/A", "gpu_util": 0, "mem_util": 0, "mem_used": 0, "mem_total": 0}] + +def main(): + log_file = "monitor_log.txt" + with open(log_file, "a", encoding="utf-8") as f: + while True: + now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + # CPU + cpu_percent = get_cpu_usage() + + # 内存 + mem_percent, mem_used, mem_total = get_memory_usage() + + # GPU + gpu_stats = get_gpu_usage() + + # 拼接日志内容 + log_line = (f"[{now}] CPU: {cpu_percent:.1f}% | " + f"Memory: {mem_percent:.1f}% ({mem_used:.2f} GB / {mem_total:.2f} GB)") + + for gpu in gpu_stats: + log_line += (f" | GPU: {gpu['name']} {gpu['gpu_util']}% " + f"(Mem {gpu['mem_used']:.2f} GB / {gpu['mem_total']:.2f} GB)") + + # 输出 + print(log_line) + f.write(log_line + "\n") + f.flush() + + time.sleep(9) # 这里+CPU统计的1秒,总体大约10秒一个周期 + +if __name__ == "__main__": + main() diff --git a/backend/tests/testfemtobolt.py b/backend/tests/testfemtobolt.py new file mode 100644 index 00000000..1645f585 --- /dev/null +++ b/backend/tests/testfemtobolt.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +import time +import numpy as np +import matplotlib.pyplot as plt +from matplotlib.colors import LinearSegmentedColormap + +class FemtoBoltViewer: + def __init__(self, depth_min=900, depth_max=1300): + self.depth_min = depth_min + self.depth_max = depth_max + + # 自定义colormap + colors = ['fuchsia', 'red', 'yellow', 'lime', 'cyan', 'blue', + 'fuchsia', 'red', 'yellow', 'lime', 'cyan', 'blue'] + self.cmap = LinearSegmentedColormap.from_list("custom_cmap", colors) + + # SDK设备句柄 + self.device_handle = None + self.pykinect = None + self.config = None + + # 初始化matplotlib figure + plt.ion() + self.fig, self.ax = plt.subplots(figsize=(7, 7)) + + def _load_sdk(self): + """加载FemtoBolt SDK""" + try: + import pykinect_azure as pykinect + self.pykinect = pykinect + # 配置DLL路径 + base_dir = os.path.dirname(os.path.abspath(__file__)) + dll_path = os.path.join(base_dir, "..", "dll", "femtobolt", "bin", "k4a.dll") + self.pykinect.initialize_libraries(track_body=False, module_k4a_path=dll_path) + return True + except Exception as e: + print(f"加载SDK失败: {e}") + return False + + def _configure_device(self): + """配置FemtoBolt设备""" + self.config = self.pykinect.default_configuration + self.config.depth_mode = self.pykinect.K4A_DEPTH_MODE_NFOV_UNBINNED + self.config.camera_fps = self.pykinect.K4A_FRAMES_PER_SECOND_15 + self.config.synchronized_images_only = False + self.config.color_resolution = 0 + self.device_handle = self.pykinect.start_device(config=self.config) + + def _render_depth(self, depth_image: np.ndarray): + """使用matplotlib绘制深度图,带背景和自定义colormap""" + # 过滤深度范围 + depth = np.where((depth_image >= self.depth_min) & (depth_image <= self.depth_max), + depth_image, 0) + + # 屏蔽深度为0的部分 + depth = np.ma.masked_equal(depth, 0) + + # 背景图(灰色) + background = np.ones_like(depth) * 0.5 + + self.ax.clear() + # 绘制背景 + self.ax.imshow(background, origin='lower', cmap='gray', alpha=0.3) + # 绘制白色栅格线 + self.ax.grid(True, which='both', axis='both', color='white', linestyle='-', linewidth=1, zorder=0) + # 绘制深度等高线 + self.ax.contourf(depth, levels=200, cmap=self.cmap, vmin=self.depth_min, vmax=self.depth_max, origin='upper', zorder=2) + plt.pause(0.001) + plt.draw() + + def run(self): + if not self._load_sdk(): + print("SDK加载失败,退出") + return + + self._configure_device() + print("FemtoBolt深度相机启动成功,按 Ctrl+C 退出") + + try: + while True: + capture = self.device_handle.update() + if capture is None: + continue + + ret, depth_image = capture.get_depth_image() + if not ret or depth_image is None: + continue + + self._render_depth(depth_image) + + except KeyboardInterrupt: + print("退出程序") + finally: + self.device_handle.stop() + self.device_handle.close() + plt.close(self.fig) + + +if __name__ == "__main__": + viewer = FemtoBoltViewer(depth_min=900, depth_max=1300) + viewer.run() diff --git a/frontend/src/renderer/dist-electron/win-unpacked/resources/backend/Log/OrbbecSDK.log.txt b/frontend/src/renderer/dist-electron/win-unpacked/resources/backend/Log/OrbbecSDK.log.txt deleted file mode 100644 index 56f41a96..00000000 --- a/frontend/src/renderer/dist-electron/win-unpacked/resources/backend/Log/OrbbecSDK.log.txt +++ /dev/null @@ -1,40 +0,0 @@ -[08/18 20:41:43.488456][debug][24904][Context.cpp:30] Context creating, work_dir=D:\Trae_space\BodyBalanceEvaluation\frontend\src\renderer\dist-electron\win-unpacked\resources\backend -[08/18 20:41:43.488655][debug][24904][Context.cpp:49] Config file version=1.1 -[08/18 20:41:43.488739][debug][24904][FrameBufferManager.cpp:23] Max global frame buffer size updated! size=2048.000MB -[08/18 20:41:43.488782][info][24904][Context.cpp:68] Context created with config: default config! -[08/18 20:41:43.488823][info][24904][Context.cpp:73] Work directory=D:\Trae_space\BodyBalanceEvaluation\frontend\src\renderer\dist-electron\win-unpacked\resources\backend, SDK version=v1.10.11-20240724-aeaa107e5 -[08/18 20:41:43.489074][debug][24904][DeviceManager.cpp:30] DeviceManager init ... -[08/18 20:41:43.489100][info][24904][MfPal.cpp:105] createObPal: create WinPal! -[08/18 20:41:43.489124][debug][24904][MfPal.cpp:110] WmfPal init ... -[08/18 20:41:43.520661][debug][24904][MfPal.cpp:117] WmfPal created! -[08/18 20:41:43.520721][debug][24904][DeviceManager.cpp:34] Enable USB Device Enumerator ... -[08/18 20:41:43.553521][debug][24904][EnumeratorLibusb.cpp:321] queryDevicesInfo done! -[08/18 20:41:43.553940][debug][24904][MfPal.cpp:216] Create WinEventDeviceWatcher! -[08/18 20:41:43.554220][debug][24904][UsbDeviceEnumerator.cpp:78] No matched usb device found! -[08/18 20:41:43.554276][info][24904][DeviceManager.cpp:15] Current found device(s): (0) -[08/18 20:41:43.554305][debug][24904][DeviceManager.cpp:52] DeviceManager construct done! -[08/18 20:41:43.554347][debug][24904][Context.cpp:81] Context destroying ... -[08/18 20:41:43.554360][debug][24904][DeviceManager.cpp:56] DeviceManager destroy ... -[08/18 20:41:43.554371][debug][24904][DeviceManager.cpp:64] DeviceManager Destructors done -[08/18 20:41:43.558317][debug][24904][MfPal.cpp:128] WmfPal destroyed! -[08/18 20:41:43.558735][info][24904][Context.cpp:84] Context destroyed -[08/18 20:43:03.299275][debug][29780][Context.cpp:30] Context creating, work_dir=D:\Trae_space\BodyBalanceEvaluation\frontend\src\renderer\dist-electron\win-unpacked\resources\backend -[08/18 20:43:03.299338][debug][29780][Context.cpp:49] Config file version=1.1 -[08/18 20:43:03.299358][debug][29780][FrameBufferManager.cpp:23] Max global frame buffer size updated! size=2048.000MB -[08/18 20:43:03.299375][info][29780][Context.cpp:68] Context created with config: default config! -[08/18 20:43:03.299399][info][29780][Context.cpp:73] Work directory=D:\Trae_space\BodyBalanceEvaluation\frontend\src\renderer\dist-electron\win-unpacked\resources\backend, SDK version=v1.10.11-20240724-aeaa107e5 -[08/18 20:43:03.299416][debug][29780][DeviceManager.cpp:30] DeviceManager init ... -[08/18 20:43:03.299428][info][29780][MfPal.cpp:105] createObPal: create WinPal! -[08/18 20:43:03.299441][debug][29780][MfPal.cpp:110] WmfPal init ... -[08/18 20:43:03.340690][debug][29780][MfPal.cpp:117] WmfPal created! -[08/18 20:43:03.340727][debug][29780][DeviceManager.cpp:34] Enable USB Device Enumerator ... -[08/18 20:43:03.367003][debug][29780][EnumeratorLibusb.cpp:321] queryDevicesInfo done! -[08/18 20:43:03.367322][debug][29780][MfPal.cpp:216] Create WinEventDeviceWatcher! -[08/18 20:43:03.367577][debug][29780][UsbDeviceEnumerator.cpp:78] No matched usb device found! -[08/18 20:43:03.367605][info][29780][DeviceManager.cpp:15] Current found device(s): (0) -[08/18 20:43:03.367629][debug][29780][DeviceManager.cpp:52] DeviceManager construct done! -[08/18 20:43:03.367646][debug][29780][Context.cpp:81] Context destroying ... -[08/18 20:43:03.367657][debug][29780][DeviceManager.cpp:56] DeviceManager destroy ... -[08/18 20:43:03.367667][debug][29780][DeviceManager.cpp:64] DeviceManager Destructors done -[08/18 20:43:03.369012][debug][29780][MfPal.cpp:128] WmfPal destroyed! -[08/18 20:43:03.369386][info][29780][Context.cpp:84] Context destroyed diff --git a/frontend/src/renderer/src/views/Detection.vue b/frontend/src/renderer/src/views/Detection.vue index 5df7c0aa..7544c5d3 100644 --- a/frontend/src/renderer/src/views/Detection.vue +++ b/frontend/src/renderer/src/views/Detection.vue @@ -927,7 +927,7 @@ function connectWebSocket() { // 创建主Socket.IO连接 socket = io(BACKEND_URL, { - transports: ['polling'], // 只使用polling,与后端保持一致 + transports: ['websocket', 'polling'], // 只使用polling,与后端保持一致 timeout: 10000, forceNew: true, reconnection: true, @@ -937,7 +937,7 @@ function connectWebSocket() { // 创建统一设备命名空间连接 devicesSocket = io(BACKEND_URL + '/devices', { - transports: ['polling'], // 只使用polling,与后端保持一致 + transports: ['websocket', 'polling'], // 只使用polling,与后端保持一致 timeout: 10000, forceNew: true })