This commit is contained in:
limengnan 2025-08-19 15:28:35 +08:00
commit d39a2c7695
11 changed files with 212 additions and 57 deletions

4
.gitignore vendored
View File

@ -43,6 +43,10 @@ Thumbs.db
dist/
build/
# 前端构建输出
frontend/src/renderer/dist/
frontend/src/renderer/dist-electron/
# 临时文件
*.tmp
*.temp

View File

@ -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',

View File

@ -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',

View File

@ -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

View File

@ -11,7 +11,7 @@ import json
import logging
from typing import Dict, Any, Optional, Union
from pathlib import Path
import sys
class ConfigManager:
"""配置管理器"""
@ -43,10 +43,23 @@ 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)
@ -54,8 +67,14 @@ class ConfigManager:
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):
"""
加载配置文件

View File

@ -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)
}
# 为每个设备添加状态变化回调

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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
})