feat: 增加机器人扬声器和麦克风音量的控制

This commit is contained in:
zhanghao 2026-07-22 14:33:02 +08:00
parent b1186e5922
commit 43ca0b0122
5 changed files with 215 additions and 19 deletions

View File

@ -12,5 +12,8 @@ VITE_WS_URL = ws://192.168.0.222:13080/ws
# explanation
VITE_INSPECTION_TYPE = "inspection"
VITE_INSPECTION_ARM_DEVICEID = "huayan_arm"
VITE_INSPECTION_AGV_DEVICEID = "huayan_arm"
VITE_INSPECTION_ARM_DEVICE_ID = "huayan_arm"
VITE_INSPECTION_AGV_DEVICE_ID = "src1100"
VITE_AUDIO_SERVER_URL = "ws://192.168.0.222:13080/ws/audio"
VITE_SPEAKER_DEVICE_ID = "spk1"
VITE_MIC_DEVICE_ID = "mic1"

View File

@ -54,4 +54,44 @@ export function speedJApi(data) {
method: 'post',
data: data
})
}
export function clearFault(params) {
return request({
url: '/api/arm/clearFault',
method: 'get',
params: params
})
}
export function getMicVolume(params) {
return request({
url: '/api/Microphone/volume',
method: 'get',
params: params
})
}
export function setMicVolume(data) {
return request({
url: '/api/Microphone/volume',
method: 'post',
data: data
})
}
export function getSpeakerVolume(params) {
return request({
url: '/api/Speaker/volume',
method: 'get',
params: params
})
}
export function setSpeakerVolume(data) {
return request({
url: '/api/Speaker/volume',
method: 'post',
data: data
})
}

View File

@ -135,8 +135,8 @@
<div class="speed-container">
<div class="label">速度</div>
<el-slider v-model="speed" :min="0.1" :step="0.1" :max="2" />
<div class="proportion">{{( speed / 2 * 100).toFixed(0) }} %</div>
<el-slider v-model="speed" :min="0.01" :step="0.01" :max="1" />
<div class="proportion">{{( speed / 1 * 100).toFixed(0) }} %</div>
</div>
</div>
</template>
@ -174,7 +174,7 @@ const errorNum = ref(0)
const getPose = async (robot) => {
try {
const res = await getPoseApi({
deviceId: import.meta.env.VITE_INSPECTION_ARM_DEVICEID,
deviceId: import.meta.env.VITE_INSPECTION_ARM_DEVICE_ID,
terminalId: robot.terminalId
})
if (res.code === 200) {
@ -249,7 +249,7 @@ const move = async (axis, direction) => {
}
speedLApi({
deviceId: import.meta.env.VITE_INSPECTION_ARM_DEVICEID,
deviceId: import.meta.env.VITE_INSPECTION_ARM_DEVICE_ID,
terminalId: props.robot.terminalId,
duration: 60,
acceleration: speed.value + 0.1,
@ -262,7 +262,7 @@ const move = async (axis, direction) => {
*/
const stop = async () => {
await stopMotionApi({
deviceId: import.meta.env.VITE_INSPECTION_ARM_DEVICEID,
deviceId: import.meta.env.VITE_INSPECTION_ARM_DEVICE_ID,
terminalId: props.robot.terminalId
})
}

View File

@ -52,6 +52,34 @@
</el-button>
</div>
<div class="robot-volume">
<span class="robot-volume__label">机器人麦克风音量</span>
<el-slider
v-model="microphoneVolume"
class="robot-volume__slider"
:min="0"
:max="100"
:disabled="isMicrophoneVolumeLoading"
:show-tooltip="true"
@change="updateMicrophoneVolume"
/>
<span class="robot-volume__value">{{ microphoneVolume }}%</span>
</div>
<div class="robot-volume">
<span class="robot-volume__label">机器人扬声器音量</span>
<el-slider
v-model="speakerVolume"
class="robot-volume__slider"
:min="0"
:max="100"
:disabled="isSpeakerVolumeLoading"
:show-tooltip="true"
@change="updateSpeakerVolume"
/>
<span class="robot-volume__value">{{ speakerVolume }}%</span>
</div>
<section class="status-card">
<div class="status-copy">
<p>当前状态<strong>{{ statusMeta.label }}</strong></p>
@ -72,12 +100,13 @@
<script setup>
import { computed, onBeforeUnmount, ref } from 'vue'
import { ElMessage, ElNotification } from 'element-plus'
import { setMicVolume, getMicVolume, setSpeakerVolume, getSpeakerVolume } from '@/api/inspection/cockpit'
// WebSocket JSON PCM使 WebRTCSDP ICE
const AUDIO_SERVER_URL = 'ws://192.168.1.222:13080/ws/audio'
const AUDIO_SERVER_URL = import.meta.env.VITE_AUDIO_SERVER_URL
const TERMINAL_ID = 'c2f8a06826baf03843925c1a2a13bcfd'
const DEVICE_ID = '1234'
const OPERATOR_ID = 'user001'
const speakerDeviceId = import.meta.env.VITE_SPEAKER_DEVICE_ID
const micDeviceId = import.meta.env.VITE_MIC_DEVICE_ID
// PCM S16LE48kHz10ms
const PCM_SAMPLE_RATE = 48000
@ -296,8 +325,8 @@ function connectAudioServer(sessionToken) {
sendControlMessage({
type: 'join',
terminalId: TERMINAL_ID,
deviceId: DEVICE_ID,
operatorId: OPERATOR_ID
speakerDeviceId: speakerDeviceId,
micDeviceId: micDeviceId
})
}
@ -613,6 +642,96 @@ function endCall() {
ElMessage.success('通话已结束')
}
const microphoneVolume = ref(50)
const speakerVolume = ref(50)
const isMicrophoneVolumeLoading = ref(false)
const isSpeakerVolumeLoading = ref(false)
let confirmedMicrophoneVolume = 50
let confirmedSpeakerVolume = 50
async function updateMicrophoneVolume(volume) {
const previousVolume = confirmedMicrophoneVolume
isMicrophoneVolumeLoading.value = true
try {
const response = await setMicVolume({
deviceId: micDeviceId,
terminalId: TERMINAL_ID,
volume: Math.round(volume)
})
if (response.code !== 200) throw new Error(response.message || '设置机器人音量失败')
microphoneVolume.value = volume
confirmedMicrophoneVolume = volume
ElMessage.success(`机器人麦克风音量已设置为 ${volume}%`)
} catch (error) {
microphoneVolume.value = previousVolume
ElMessage.error(error.message || '设置机器人音量失败')
} finally {
isMicrophoneVolumeLoading.value = false
}
}
async function updateSpeakerVolume(volume) {
const previousVolume = confirmedSpeakerVolume
isSpeakerVolumeLoading.value = true
try {
const response = await setSpeakerVolume({
deviceId: speakerDeviceId,
terminalId: TERMINAL_ID,
volume: Math.round(volume)
})
if (response.code !== 200) throw new Error(response.message || '设置扬声器音量失败')
speakerVolume.value = volume
confirmedSpeakerVolume = volume
ElMessage.success(`机器人扬声器音量已设置为 ${volume}%`)
} catch (error) {
speakerVolume.value = previousVolume
ElMessage.error(error.message || '设置扬声器音量失败')
} finally {
isSpeakerVolumeLoading.value = false
}
}
async function loadMicrophoneVolume({ silent = false } = {}) {
isMicrophoneVolumeLoading.value = true
try {
const response = await getMicVolume({
deviceId: micDeviceId,
terminalId: TERMINAL_ID
})
if (response.code !== 200) throw new Error(response.message || '获取机器人音量失败')
const volume = Math.max(0, Math.min(100, Number(response.data) || 0))
microphoneVolume.value = volume
confirmedMicrophoneVolume = volume
} catch (error) {
if (!silent) ElMessage.error(error.message || '获取机器人音量失败')
} finally {
isMicrophoneVolumeLoading.value = false
}
}
async function loadSpeakerVolume({ silent = false } = {}) {
isSpeakerVolumeLoading.value = true
try {
const response = await getSpeakerVolume({
deviceId: speakerDeviceId,
terminalId: TERMINAL_ID
})
if (response.code !== 200) throw new Error(response.message || '获取扬声器音量失败')
const volume = Math.max(0, Math.min(100, Number(response.data) || 0))
speakerVolume.value = volume
confirmedSpeakerVolume = volume
} catch (error) {
if (!silent) ElMessage.error(error.message || '获取扬声器音量失败')
} finally {
isSpeakerVolumeLoading.value = false
}
}
onMounted(() => {
loadMicrophoneVolume()
loadSpeakerVolume()
})
// AudioWorklet WebSocket
onBeforeUnmount(cleanupResources)
</script>
@ -843,6 +962,30 @@ $panel-border: rgb(112 192 255 / 24%);
:deep(.el-button + .el-button) { margin-left: 0; }
}
.robot-volume {
display: grid;
grid-template-columns: auto minmax(120px, 210px) 42px;
align-items: center;
gap: 12px;
width: min(100%, 430px);
margin-top: 10px;
color: #badeff;;
font-size: 11px;
&__label { white-space: nowrap; }
&__value { color: #fff; text-align: right; font-variant-numeric: tabular-nums; }
:deep(.el-slider__runway) { background-color: #dff7ff; }
:deep(.el-slider__bar) { background: linear-gradient(90deg, #46c8ff, #936cff); }
:deep(.el-slider__button) {
width: 14px;
height: 14px;
border-color: #46c8ff;
background: #dff7ff;
box-shadow: 0 0 10px rgb(70 200 255 / 65%);
}
}
:deep(.call-button) {
width: 178px;
height: 34px;

View File

@ -82,8 +82,8 @@
</div>
<div class="speed-container">
<div class="label">速度</div>
<el-slider v-model="jointSpeed" :min="0.1" :step="0.1" :max="3" />
<div class="proportion">{{( jointSpeed / 3 * 100).toFixed(0) }} %</div>
<el-slider v-model="jointSpeed" :min="0.01" :step="0.01" :max="1" />
<div class="proportion">{{( jointSpeed / 1 * 100).toFixed(0) }} %</div>
</div>
</div>
</div>
@ -119,7 +119,7 @@ import UrdfViewer from './UrdfView.vue'
import DirectionControl from './DirectionControl.vue'
import RoboticArm from "./RoboticArm.vue";
import { getRobotList } from '@/api/inspection/robot'
import { getJointStateApi, moveJApi, torqueOnApi, speedJApi, stopMotionApi } from '@/api/inspection/cockpit'
import { getJointStateApi, moveJApi, torqueOnApi, speedJApi, stopMotionApi, clearFault } from '@/api/inspection/cockpit'
import { Remove, CirclePlus } from '@element-plus/icons-vue'
import VoiceConversation from "./VoiceConversation.vue";
import IPlayer from "@/components/IPlayer/index.vue";
@ -178,8 +178,14 @@ const handlerController = (value) => {
if (value === 2) {
// 使
torqueOnApi({
deviceId: import.meta.env.VITE_INSPECTION_ARM_DEVICEID,
deviceId: import.meta.env.VITE_INSPECTION_ARM_DEVICE_ID,
terminalId: activeBotData.value.terminalId
}).catch(error => {
console.error('开启机械臂使能失败:', error)
clearFault({
deviceId: import.meta.env.VITE_INSPECTION_ARM_DEVICE_ID,
terminalId: activeBotData.value.terminalId
})
})
jointControls.value = urdfViewerRef.value.jointControls
if (animationId.value) {
@ -188,6 +194,10 @@ const handlerController = (value) => {
animationId.value = setInterval(() => {
getJointState(activeBotData.value)
}, 100)
} else {
if (animationId.value) {
clearInterval(animationId.value)
}
}
}
@ -209,7 +219,7 @@ const errCount = ref(0)
const getJointState = async (robot) => {
try {
const res = await getJointStateApi({
deviceId: import.meta.env.VITE_INSPECTION_ARM_DEVICEID,
deviceId: import.meta.env.VITE_INSPECTION_ARM_DEVICE_ID,
terminalId: robot.terminalId
})
if (res.code === 200) {
@ -246,7 +256,7 @@ const updateJoint = async (jointName, _dir) => {
const index = jointControls.value.findIndex(joint => joint.name === jointName)
velocities[index] = jointSpeed.value * (_dir === '+' ? 1 : -1)
const res = await speedJApi({
deviceId: import.meta.env.VITE_INSPECTION_ARM_DEVICEID,
deviceId: import.meta.env.VITE_INSPECTION_ARM_DEVICE_ID,
terminalId: activeBotData.value.terminalId,
acceleration: jointSpeed.value + 0.1,
duration: 60,
@ -259,7 +269,7 @@ const updateJoint = async (jointName, _dir) => {
*/
const stop = async () => {
await stopMotionApi({
deviceId: import.meta.env.VITE_INSPECTION_ARM_DEVICEID,
deviceId: import.meta.env.VITE_INSPECTION_ARM_DEVICE_ID,
terminalId: activeBotData.value.terminalId
})
}