diff --git a/frontend/src/renderer/src/services/api.js b/frontend/src/renderer/src/services/api.js
index 8d2c1896..22c23e23 100644
--- a/frontend/src/renderer/src/services/api.js
+++ b/frontend/src/renderer/src/services/api.js
@@ -112,6 +112,11 @@ export const deviceAPI = {
return api.post('/api/devices/calibrate')
},
+ // 校准IMU头部姿态传感器
+ calibrateIMU() {
+ return api.post('/api/devices/calibrate/imu')
+ },
+
// 测试设备
testDevice() {
return api.post('/api/devices/test')
diff --git a/frontend/src/renderer/src/views/Detection.vue b/frontend/src/renderer/src/views/Detection.vue
index f80c01b1..27aa45a9 100644
--- a/frontend/src/renderer/src/views/Detection.vue
+++ b/frontend/src/renderer/src/views/Detection.vue
@@ -83,11 +83,7 @@
{{ imuStatus }}
-
清零并保存
@@ -99,40 +95,36 @@
-
-
+
-
旋转
-
左:{{ headPoseMaxValues.rotationLeftMax.toFixed(1) }}°
-
右:{{ headPoseMaxValues.rotationRightMax.toFixed(1) }}°
+
左:{{
+ headPoseMaxValues.rotationLeftMax.toFixed(1) }}°
+
右:{{
+ headPoseMaxValues.rotationRightMax.toFixed(1) }}°
-

+
-
倾斜
-
左:{{ headPoseMaxValues.tiltLeftMax.toFixed(1) }}°
-
右:{{ headPoseMaxValues.tiltRightMax.toFixed(1) }}°
+
左:{{
+ headPoseMaxValues.tiltLeftMax.toFixed(1) }}°
+
右:{{
+ headPoseMaxValues.tiltRightMax.toFixed(1) }}°
-

+
-
俯仰
-
下:{{ headPoseMaxValues.pitchDownMax.toFixed(1) }}°
-
上:{{ headPoseMaxValues.pitchUpMax.toFixed(1) }}°
+
下:{{
+ headPoseMaxValues.pitchDownMax.toFixed(1) }}°
+
上:{{
+ headPoseMaxValues.pitchUpMax.toFixed(1) }}°
-
- 历史数据..
-
-
-
+
+
@@ -167,11 +159,11 @@
-
+
@@ -404,9 +398,9 @@ const handleClose = () => {
const diagnosticForm = ref({})
// 模拟历史数据
const historyData = ref([
- { id: 3, rotLeft: '-55.2°', rotRight: '54.2°', tiltLeft: '-17.7°', tiltRight: '18.2°', pitchDown: '-20.2°', pitchUp: '10.5°' },
- { id: 2, rotLeft: '-55.8°', rotRight: '56.2°', tiltLeft: '-17.5°', tiltRight: '17.9°', pitchDown: '-21.2°', pitchUp: '12.1°' },
- { id: 1, rotLeft: '-56.1°', rotRight: '55.7°', tiltLeft: '-17.5°', tiltRight: '18.5°', pitchDown: '-22.2°', pitchUp: '11.5°' }
+ // { id: 3, rotLeft: '-55.2°', rotRight: '54.2°', tiltLeft: '-17.7°', tiltRight: '18.2°', pitchDown: '-20.2°', pitchUp: '10.5°' },
+ // { id: 2, rotLeft: '-55.8°', rotRight: '56.2°', tiltLeft: '-17.5°', tiltRight: '17.9°', pitchDown: '-21.2°', pitchUp: '12.1°' },
+ // { id: 1, rotLeft: '-56.1°', rotRight: '55.7°', tiltLeft: '-17.5°', tiltRight: '18.5°', pitchDown: '-22.2°', pitchUp: '11.5°' }
])
const chartoption = ref({
backgroundColor: '#000000',
@@ -435,26 +429,26 @@ const chartoption = ref({
length: '85%',
width: 2,
offsetCenter: [0, '5%'],
- itemStyle:{
- color:'#ff8d00'
+ itemStyle: {
+ color: '#ff8d00'
},
},
anchor: {
show: true,
showAbove: true,
size: 18,
- icon:'circle',
+ icon: 'circle',
itemStyle: {
borderWidth: 3,
- color:'#ff8d00',
- borderColor:'#ff8d00',
+ color: '#ff8d00',
+ borderColor: '#ff8d00',
}
},
axisLine: {
roundCap: true,
lineStyle: {
width: 3,
- color:[[1, '#0089ff']]
+ color: [[1, '#0089ff']]
}
},
axisTick: {
@@ -523,6 +517,8 @@ function connectWebSocket() {
startVideoStream()
startIMUStreaming();
startPressureStreaming();
+ //绘制头部仪表盘
+ initchart()
})
// 连接失败事件
@@ -694,6 +690,7 @@ const headPoseMaxValues = ref({
// 头部姿态历史最值记录数组
const headPoseHistory = ref([])
+const headPoseData = ref({})
// 最值跟踪状态
const isTrackingMaxValues = ref(false)
@@ -703,22 +700,48 @@ function handleIMUData(data) {
try {
if (data && data.head_pose) {
const headPose = data.head_pose
-
+
// 更新头部姿态数据
console.log('🎯 更新IMU头部姿态数据:', {
rotation: headPose.rotation, // 旋转角:左旋(-), 右旋(+)
tilt: headPose.tilt, // 倾斜角:左倾(-), 右倾(+)
pitch: headPose.pitch // 俯仰角:俯角(-), 仰角(+)
})
-
- // 显示角度值(保留一位小数)
- console.log(`📐 头部姿态角度 - 旋转: ${headPose.rotation.toFixed(1)}°, 倾斜: ${headPose.tilt.toFixed(1)}°, 俯仰: ${headPose.pitch.toFixed(1)}°`)
-
- // 如果正在跟踪最值,则更新最值数据
- if (isTrackingMaxValues.value) {
- updateHeadPoseMaxValues(headPose)
+ if (rotationCharts) {
+ rotationCharts.setOption({
+ series: [{
+ data: [{
+ value:headPose.rotation.toFixed(1)
+ }]
+ }]
+ })
}
-
+ if (pitchCharts) {
+ pitchCharts.setOption({
+ series: [{
+ data: [{
+ value:headPose.pitch.toFixed(1)
+ }]
+ }]
+ })
+ }
+ if (tiltCharts) {
+ tiltCharts.setOption({
+ series: [{
+ data: [{
+ value:headPose.tilt.toFixed(1)
+ }]
+ }]
+ })
+ }
+ // 显示角度值(保留一位小数)
+ // console.log(`📐 头部姿态角度 - 旋转: ${headPose.rotation.toFixed(1)}°, 倾斜: ${headPose.tilt.toFixed(1)}°, 俯仰: ${headPose.pitch.toFixed(1)}°`)
+
+ // 如果正在跟踪最值,则更新最值数据
+ // if (isTrackingMaxValues.value) {
+ updateHeadPoseMaxValues(headPose)
+ // }
+
// 这里可以添加数据可视化逻辑
// 例如更新图表或显示数值
@@ -751,7 +774,7 @@ function updateHeadPoseMaxValues(headPose) {
headPose.rotation
)
}
-
+
// 更新倾斜角最值
if (headPose.tilt < 0) {
// 左倾(负值),取绝对值的最大值
@@ -766,7 +789,7 @@ function updateHeadPoseMaxValues(headPose) {
headPose.tilt
)
}
-
+
// 更新俯仰角最值
if (headPose.pitch < 0) {
// 下俯(负值),取绝对值的最大值
@@ -781,16 +804,16 @@ function updateHeadPoseMaxValues(headPose) {
headPose.pitch
)
}
-
- // 输出当前最值(用于调试)
- console.log('📊 当前头部姿态最值:', {
- rotationLeft: headPoseMaxValues.value.rotationLeftMax.toFixed(1),
- rotationRight: headPoseMaxValues.value.rotationRightMax.toFixed(1),
- tiltLeft: headPoseMaxValues.value.tiltLeftMax.toFixed(1),
- tiltRight: headPoseMaxValues.value.tiltRightMax.toFixed(1),
- pitchUp: headPoseMaxValues.value.pitchUpMax.toFixed(1),
- pitchDown: headPoseMaxValues.value.pitchDownMax.toFixed(1)
- })
+
+ // // 输出当前最值(用于调试)
+ // console.log('📊 当前头部姿态最值:', {
+ // rotationLeft: headPoseMaxValues.value.rotationLeftMax.toFixed(1),
+ // rotationRight: headPoseMaxValues.value.rotationRightMax.toFixed(1),
+ // tiltLeft: headPoseMaxValues.value.tiltLeftMax.toFixed(1),
+ // tiltRight: headPoseMaxValues.value.tiltRightMax.toFixed(1),
+ // pitchUp: headPoseMaxValues.value.pitchUpMax.toFixed(1),
+ // pitchDown: headPoseMaxValues.value.pitchDownMax.toFixed(1)
+ // })
} catch (error) {
console.error('❌ 更新头部姿态最值失败:', error)
}
@@ -800,7 +823,7 @@ function updateHeadPoseMaxValues(headPose) {
function clearAndStartTracking() {
try {
saveMaxValuesToHistory()
-
+
// 重置所有最值为0
headPoseMaxValues.value = {
rotationLeftMax: 0,
@@ -810,26 +833,21 @@ function clearAndStartTracking() {
pitchUpMax: 0,
pitchDownMax: 0
}
-
+
// 开始跟踪
isTrackingMaxValues.value = true
-
- console.log('🔄 头部姿态最值已清零,开始跟踪')
- ElMessage.success('头部姿态最值已清零,开始跟踪')
+
+ // console.log('🔄 头部姿态最值已清零,开始跟踪')
+ // ElMessage.success('头部姿态最值已清零,开始跟踪')
} catch (error) {
- console.error('❌ 清零最值失败:', error)
- ElMessage.error('清零最值失败')
+ // console.error('❌ 清零最值失败:', error)
+ ElMessage.error('清零失败')
}
}
// 保存当前最值到历史记录
function saveMaxValuesToHistory() {
try {
- if (!isTrackingMaxValues.value) {
- ElMessage.warning('请先点击清零开始跟踪')
- return
- }
-
// 创建当前最值的副本
const currentMaxValues = {
id: headPoseHistory.value.length + 1,
@@ -841,20 +859,20 @@ function saveMaxValuesToHistory() {
pitchDownMax: Number(headPoseMaxValues.value.pitchDownMax.toFixed(1)),
timestamp: new Date().toLocaleString()
}
-
+
// 添加到历史记录
headPoseHistory.value.push(currentMaxValues)
-
+
// 停止跟踪
isTrackingMaxValues.value = false
-
- console.log('💾 头部姿态最值已保存:', currentMaxValues)
- ElMessage.success(`头部姿态最值已保存(第${currentMaxValues.id}组)`)
-
+
+ // console.log('💾 头部姿态最值已保存:', currentMaxValues)
+ // ElMessage.success(`头部姿态最值已保存(第${currentMaxValues.id}组)`)
+
// 更新历史数据表格(如果存在的话)
updateHistoryTable()
} catch (error) {
- console.error('❌ 保存最值失败:', error)
+ // console.error('❌ 保存最值失败:', error)
ElMessage.error('保存最值失败')
}
}
@@ -865,7 +883,7 @@ function updateHistoryTable() {
// 将头部姿态最值数据合并到现有的历史数据中
if (headPoseHistory.value.length > 0) {
const latestData = headPoseHistory.value[headPoseHistory.value.length - 1]
-
+
// 更新historyData数组,添加头部姿态最值数据
const newHistoryItem = {
id: latestData.id,
@@ -877,66 +895,64 @@ function updateHistoryTable() {
pitchUp: latestData.pitchUpMax,
timestamp: latestData.timestamp
}
-
// 如果historyData数组存在,则添加新数据
if (historyData.value) {
historyData.value.push(newHistoryItem)
}
-
+
console.log('📋 历史数据表格已更新')
}
} catch (error) {
console.error('❌ 更新历史数据表格失败:', error)
}
}
-
+const footPressure = ref({
+ left_front: '',
+ left_rear: '',
+ right_front: '',
+ right_rear: '',
+ left_total: '',
+ right_total: '',
+ total_pressure: ''
+})
+const footImgSrc = ref('')
// 处理压力传感器足部压力数据
function handlePressureData(data) {
try {
if (data && data.foot_pressure) {
const pressureData = data.foot_pressure
-
+
// 更新足部压力数据
- console.log('👣 接收到压力传感器数据:')
-
// 显示分区压力值
if (pressureData.pressure_zones) {
- const zones = pressureData.pressure_zones
- console.log(' 分区压力值:')
- console.log(` 左前足: ${zones.left_front} N`)
- console.log(` 左后足: ${zones.left_rear} N`)
- console.log(` 右前足: ${zones.right_front} N`)
- console.log(` 右后足: ${zones.right_rear} N`)
- console.log(` 左足总压力: ${zones.left_total} N`)
- console.log(` 右足总压力: ${zones.right_total} N`)
- console.log(` 总压力: ${zones.total_pressure} N`)
+ footPressure.value = pressureData.pressure_zones
}
-
// 显示平衡分析
- if (pressureData.balance_analysis) {
- const balance = pressureData.balance_analysis
- console.log(' 平衡分析:')
- console.log(` 平衡比例: ${(balance.balance_ratio * 100).toFixed(1)}%`)
- console.log(` 压力中心偏移: ${balance.pressure_center_offset}%`)
- console.log(` 平衡状态: ${balance.balance_status}`)
- console.log(` 左足前后比: ${(balance.left_front_ratio * 100).toFixed(1)}%`)
- console.log(` 右足前后比: ${(balance.right_front_ratio * 100).toFixed(1)}%`)
- }
-
+ // if (pressureData.balance_analysis) {
+ // const balance = pressureData.balance_analysis
+ // console.log(' 平衡分析:')
+ // console.log(` 平衡比例: ${(balance.balance_ratio * 100).toFixed(1)}%`)
+ // console.log(` 压力中心偏移: ${balance.pressure_center_offset}%`)
+ // console.log(` 平衡状态: ${balance.balance_status}`)
+ // console.log(` 左足前后比: ${(balance.left_front_ratio * 100).toFixed(1)}%`)
+ // console.log(` 右足前后比: ${(balance.right_front_ratio * 100).toFixed(1)}%`)
+ // }
+
// 处理压力图片
if (pressureData.pressure_image) {
- console.log(' 📊 接收到压力分布图片 (base64格式)')
+ // console.log(' 📊 接收到压力分布图片 (base64格式)')
// 这里可以将图片显示在界面上
- updatePressureImage(pressureData.pressure_image)
+ if (pressureData.pressure_image && pressureData.pressure_image.length > 0) {
+ footImgSrc.value = pressureData.pressure_image
+ } else {
+ console.warn('⚠️ 收到空的压力传感器数据图')
+ }
}
-
-
}
} catch (error) {
console.error('❌ 处理压力传感器数据失败:', error)
}
}
-
// 启动IMU头部姿态数据推流
function startIMUStreaming() {
if (socket && socket.connected) {
@@ -1429,21 +1445,19 @@ async function saveRecording() {
mimeType: currentMimeType || 'video/webm;codecs=vp9'
})
})
-
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
-
const result = await response.json()
-
if (result.success) {
+ //诊断信息说明
+ dialogVisible.value = true
console.log('🎬 录像保存成功:', result.filepath)
ElMessage.success({
message: `录像保存成功!文件路径: ${result.filepath}`,
duration: 5000
})
isRecording.value = false
- dialogVisible.value = true
// 更新会话的视频路径
if (patientInfo.value.sessionId) {
try {
@@ -1460,7 +1474,6 @@ async function saveRecording() {
// patientInfo.value.sessionId = null
console.log('✅ 会话正式结束,会话ID已清空')
} else {
- dialogVisible.value = false
throw new Error(result.message || '保存失败')
}
@@ -1470,7 +1483,6 @@ async function saveRecording() {
message: `保存录像失败: ${error.message}`,
duration: 5000
})
- dialogVisible.value = false
// 即使保存失败,也要清空会话ID,避免状态混乱
patientInfo.value.sessionId = null
console.log('⚠️ 录像保存失败,但会话已结束,会话ID已清空')
@@ -1639,27 +1651,51 @@ const handleBeforeUnload = () => {
}
}
const creatorId = ref('')
-let headCharts = null;
+let rotationCharts = null;
+let pitchCharts = null;
+let tiltCharts = null;
const initchart = () => {
// 确保 DOM 元素已经渲染
nextTick(() => {
- const chartDom = document.getElementById('headChart1');
+ const chartDom = document.getElementById('rotationChartId');
if (chartDom) {
// 如果图表已经存在,先销毁
- if (headCharts) {
- headCharts.dispose();
+ if (rotationCharts) {
+ rotationCharts.dispose();
}
- headCharts = echarts.init(chartDom);
- headCharts.setOption(chartoption.value);
+ rotationCharts = echarts.init(chartDom);
+ rotationCharts.setOption(chartoption.value);
// 添加窗口大小调整监听器
window.addEventListener('resize', () => {
- if (headCharts) {
- headCharts.resize();
+ if (rotationCharts) {
+ rotationCharts.resize();
}
});
} else {
- console.warn('找不到 ID 为 headChart1 的 DOM 元素');
+ console.warn('找不到 ID 为 的 DOM 元素');
+ }
+ const chartDom2 = document.getElementById('pitchChartId');
+ if (chartDom2) {
+ // 如果图表已经存在,先销毁
+ if (pitchCharts) {
+ pitchCharts.dispose();
+ }
+ pitchCharts = echarts.init(chartDom2);
+ pitchCharts.setOption(chartoption.value);
+ } else {
+ console.warn('找不到 ID 为 的 DOM 元素');
+ }
+ const chartDom3 = document.getElementById('tiltChartId');
+ if (chartDom3) {
+ // 如果图表已经存在,先销毁
+ if (tiltCharts) {
+ tiltCharts.dispose();
+ }
+ tiltCharts = echarts.init(chartDom3);
+ tiltCharts.setOption(chartoption.value);
+ } else {
+ console.warn('找不到 ID 为 的 DOM 元素');
}
});
}
@@ -1669,7 +1705,7 @@ onMounted(() => {
loadPatientInfo()
// 组件挂载时连接WebSocket并自动开始推流
- // connectWebSocket()
+ connectWebSocket()
// 监听页面关闭或刷新事件
window.addEventListener('beforeunload', handleBeforeUnload)
@@ -1677,7 +1713,6 @@ onMounted(() => {
console.log(authStore.currentUser)
creatorId.value = authStore.currentUser.id
}
- initchart()
})
onUnmounted(() => {
@@ -2175,6 +2210,9 @@ onUnmounted(() => {
:deep(.el-table .el-table__cell) {
padding: 4px 0px;
}
+:deep(.el-table__body-wrapper) {
+ border-bottom: 1px solid #ffffff !important;
+}