feat:增加人工头
This commit is contained in:
parent
85be4085b8
commit
ba3acc5da8
@ -39,6 +39,10 @@ export const controlModuleList = [
|
||||
{
|
||||
label: "灵巧手",
|
||||
value: "hand"
|
||||
},
|
||||
{
|
||||
label: "人工头",
|
||||
value: "head"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@ -77,6 +77,14 @@ export const constantRoutes = [
|
||||
name: "摄像头示教",
|
||||
meta: { title: "摄像头详情" },
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
path: "head/:id",
|
||||
component: () =>
|
||||
import("@/views/device/register/components/Head/index.vue"),
|
||||
name: "人工头示教",
|
||||
meta: { title: "人工头详情" },
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
path: "speaker/:id",
|
||||
|
||||
314
src/views/device/register/components/Head/index.vue
Normal file
314
src/views/device/register/components/Head/index.vue
Normal file
@ -0,0 +1,314 @@
|
||||
<template>
|
||||
<div class="head-control">
|
||||
<h2 class="title">人工头舵机控制</h2>
|
||||
<div class="main-content">
|
||||
<!-- 左侧:10个Yaw预设输入框 -->
|
||||
<div class="preset-panel left-panel">
|
||||
<h3>Yaw 预设位(左右转头)</h3>
|
||||
<div v-for="(preset, index) in leftPresets" :key="index" class="preset-item">
|
||||
<span class="label">位 {{ index + 1 }}</span>
|
||||
<el-input-number
|
||||
v-model="preset.value"
|
||||
:min="0"
|
||||
:max="1"
|
||||
:step="0.01"
|
||||
:precision="2"
|
||||
size="small"
|
||||
controls-position="right"
|
||||
@change="onPresetChange"
|
||||
/>
|
||||
</div>
|
||||
<el-button type="primary" size="small" @click="applyYawFromPresets">应用当前 Yaw</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 中央图片 + 20个控制点 -->
|
||||
<div class="image-container">
|
||||
<img
|
||||
src="https://cdn.head-acoustics.com/_processed_/3/5/csm_HMS_V_frontal_seitlich_67c86d4fad.jpg"
|
||||
alt="人工头"
|
||||
class="head-image"
|
||||
/>
|
||||
<!-- 20 个控制点 -->
|
||||
<div
|
||||
v-for="(point, index) in points"
|
||||
:key="index"
|
||||
class="control-point"
|
||||
:style="{ left: point.x + '%', top: point.y + '%' }"
|
||||
:class="{ active: point.active }"
|
||||
@click="applyPreset(index)"
|
||||
:title="`点 ${index + 1}: Yaw=${point.yaw.toFixed(2)}, Pitch=${point.pitch.toFixed(2)}`"
|
||||
>
|
||||
{{ index + 1 }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:10个Pitch预设输入框 -->
|
||||
<div class="preset-panel right-panel">
|
||||
<h3>Pitch 预设位(上下点头)</h3>
|
||||
<div v-for="(preset, index) in rightPresets" :key="index" class="preset-item">
|
||||
<span class="label">位 {{ index + 1 }}</span>
|
||||
<el-input-number
|
||||
v-model="preset.value"
|
||||
:min="0"
|
||||
:max="1"
|
||||
:step="0.01"
|
||||
:precision="2"
|
||||
size="small"
|
||||
controls-position="right"
|
||||
@change="onPresetChange"
|
||||
/>
|
||||
</div>
|
||||
<el-button type="primary" size="small" @click="applyPitchFromPresets">应用当前 Pitch</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 当前值显示与下发 -->
|
||||
<div class="current-display">
|
||||
<div class="current-item">
|
||||
<span>当前 Yaw:</span>
|
||||
<span class="value">{{ servo1.toFixed(2) }}</span>
|
||||
</div>
|
||||
<div class="current-item">
|
||||
<span>当前 Pitch:</span>
|
||||
<span class="value">{{ servo2.toFixed(2) }}</span>
|
||||
</div>
|
||||
<el-button type="primary" @click="sendServoValues">立即下发舵机值</el-button>
|
||||
<el-button @click="resetToCenter">复位到中心 (0.50, 0.50)</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
// import { setServo } from '@/api/device/head'; // 假设接口
|
||||
|
||||
const terminalId = ref('25449ff3fcc7b27da5f69462c5efcec2');
|
||||
const deviceId = ref('head1');
|
||||
|
||||
const servo1 = ref(0.5); // 当前 Yaw
|
||||
const servo2 = ref(0.5); // 当前 Pitch
|
||||
|
||||
// 左侧10个Yaw预设(对应左侧10个点)
|
||||
const leftPresets = ref([
|
||||
{ value: 0.10 }, { value: 0.15 }, { value: 0.08 }, { value: 0.12 }, { value: 0.18 },
|
||||
{ value: 0.05 }, { value: 0.10 }, { value: 0.15 }, { value: 0.08 }, { value: 0.12 }
|
||||
]);
|
||||
|
||||
// 右侧10个Pitch预设(对应右侧10个点,但Pitch值可独立调整)
|
||||
const rightPresets = ref([
|
||||
{ value: 0.20 }, { value: 0.30 }, { value: 0.40 }, { value: 0.50 }, { value: 0.60 },
|
||||
{ value: 0.70 }, { value: 0.80 }, { value: 0.90 }, { value: 0.95 }, { value: 1.00 }
|
||||
]);
|
||||
|
||||
// 当预设值改变时,自动同步到对应的点(可选)
|
||||
const onPresetChange = () => {
|
||||
// 可选:同步到 points 数组的 yaw/pitch
|
||||
leftPresets.value.forEach((p, i) => {
|
||||
points.value[i].yaw = p.value;
|
||||
});
|
||||
rightPresets.value.forEach((p, i) => {
|
||||
points.value[i + 10].pitch = p.value;
|
||||
});
|
||||
};
|
||||
|
||||
// 点击图片上的点 → 应用对应预设,并同步到输入框
|
||||
const applyPreset = (index) => {
|
||||
points.value.forEach(p => p.active = false);
|
||||
points.value[index].active = true;
|
||||
|
||||
const yaw = points.value[index].yaw;
|
||||
const pitch = points.value[index].pitch;
|
||||
|
||||
servo1.value = yaw;
|
||||
servo2.value = pitch;
|
||||
|
||||
// 同步到左侧/右侧输入框(高亮当前对应的预设)
|
||||
if (index < 10) {
|
||||
leftPresets.value.forEach((p, i) => p.value = i === index ? yaw : p.value);
|
||||
} else {
|
||||
rightPresets.value.forEach((p, i) => p.value = i === (index - 10) ? pitch : p.value);
|
||||
}
|
||||
|
||||
sendServoValues();
|
||||
};
|
||||
|
||||
// 从左侧预设应用当前选中的Yaw(假设用户先在输入框选中一个,这里用最后一个修改的近似,或添加选中逻辑)
|
||||
let lastLeftIndex = null;
|
||||
let lastRightIndex = null;
|
||||
|
||||
// 简化:提供按钮直接应用当前所有输入框的平均或手动选择。这里提供“应用当前Yaw”按钮,使用第一个非默认的值或手动。
|
||||
// 为简化,这里按钮应用“第一个输入框的值”作为Yaw,你可以改为添加选中高亮。
|
||||
const applyYawFromPresets = () => {
|
||||
servo1.value = leftPresets.value[0].value; // 示例:使用第一个,你可改为选中机制
|
||||
sendServoValues();
|
||||
};
|
||||
|
||||
const applyPitchFromPresets = () => {
|
||||
servo2.value = rightPresets.value[0].value; // 示例:使用第一个
|
||||
sendServoValues();
|
||||
};
|
||||
|
||||
const sendServoValues = async () => {
|
||||
try {
|
||||
// await setServo({
|
||||
// terminalId: terminalId.value,
|
||||
// deviceId: deviceId.value,
|
||||
// servo1: servo1.value,
|
||||
// servo2: servo2.value,
|
||||
// });
|
||||
} catch (error) {
|
||||
console.error('舵机下发失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const resetToCenter = () => {
|
||||
servo1.value = 0.5;
|
||||
servo2.value = 0.5;
|
||||
points.value.forEach(p => p.active = false);
|
||||
sendServoValues();
|
||||
};
|
||||
|
||||
// 20个点坐标(已调整适应新图片)
|
||||
const points = ref([
|
||||
// 左侧10点
|
||||
{ x: 18, y: 20, yaw: 0.10, pitch: 0.20, active: false },
|
||||
{ x: 22, y: 30, yaw: 0.15, pitch: 0.30, active: false },
|
||||
{ x: 15, y: 40, yaw: 0.08, pitch: 0.40, active: false },
|
||||
{ x: 20, y: 50, yaw: 0.12, pitch: 0.50, active: false },
|
||||
{ x: 25, y: 60, yaw: 0.18, pitch: 0.60, active: false },
|
||||
{ x: 12, y: 70, yaw: 0.05, pitch: 0.70, active: false },
|
||||
{ x: 18, y: 80, yaw: 0.10, pitch: 0.80, active: false },
|
||||
{ x: 22, y: 90, yaw: 0.15, pitch: 0.90, active: false },
|
||||
{ x: 15, y: 95, yaw: 0.08, pitch: 0.95, active: false },
|
||||
{ x: 20, y: 98, yaw: 0.12, pitch: 1.00, active: false },
|
||||
|
||||
// 右侧10点(Yaw偏大)
|
||||
{ x: 82, y: 20, yaw: 0.90, pitch: 0.20, active: false },
|
||||
{ x: 78, y: 30, yaw: 0.85, pitch: 0.30, active: false },
|
||||
{ x: 85, y: 40, yaw: 0.92, pitch: 0.40, active: false },
|
||||
{ x: 80, y: 50, yaw: 0.88, pitch: 0.50, active: false },
|
||||
{ x: 75, y: 60, yaw: 0.82, pitch: 0.60, active: false },
|
||||
{ x: 88, y: 70, yaw: 0.95, pitch: 0.70, active: false },
|
||||
{ x: 82, y: 80, yaw: 0.90, pitch: 0.80, active: false },
|
||||
{ x: 78, y: 90, yaw: 0.85, pitch: 0.90, active: false },
|
||||
{ x: 85, y: 95, yaw: 0.92, pitch: 0.95, active: false },
|
||||
{ x: 80, y: 98, yaw: 0.88, pitch: 1.00, active: false },
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.head-control {
|
||||
padding: 20px;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
border-radius: 20px;
|
||||
height: calc(100vh - 125px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 40px;
|
||||
}
|
||||
|
||||
.preset-panel {
|
||||
width: 220px;
|
||||
background: #f5f7fa;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.preset-panel h3 {
|
||||
margin: 0 0 10px 0;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.preset-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 50px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.image-container {
|
||||
position: relative;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.head-image {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.control-point {
|
||||
position: absolute;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: rgba(64, 158, 255, 0.8);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transform: translate(-50%, -50%);
|
||||
transition: all 0.2s;
|
||||
border: 3px solid #fff;
|
||||
}
|
||||
|
||||
.control-point:hover {
|
||||
background: #409eff;
|
||||
transform: translate(-50%, -50%) scale(1.2);
|
||||
}
|
||||
|
||||
.control-point.active {
|
||||
background: #f56c6c;
|
||||
transform: translate(-50%, -50%) scale(1.4);
|
||||
}
|
||||
|
||||
.current-display {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 40px;
|
||||
margin-top: 30px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.current-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-weight: bold;
|
||||
color: #409eff;
|
||||
min-width: 60px;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user