无人机控制修改为手柄遥感

main
许宏杰 3 days ago
parent c044001c43
commit b2992edf5b

@ -1 +1 @@
[{"D:\\project\\vue\\MF-UavMonitor\\src\\assets\\fonts\\SarasaMonoSC\\result.css":"1","D:\\project\\vue\\MF-UavMonitor\\src\\pages\\home\\src\\Params\\params.vue":"2","D:\\project\\vue\\MF-UavMonitor\\src\\assets\\fonts\\Furore\\result.css":"3","D:\\project\\vue\\MF-UavMonitor\\src\\pages\\home\\src\\Params\\altimeter.vue":"4","D:\\project\\vue\\MF-UavMonitor\\src\\assets\\fonts\\DincorosBlack\\result.css":"5","D:\\project\\vue\\MF-UavMonitor\\src\\pages\\home\\index.vue":"6","D:\\project\\vue\\MF-UavMonitor\\src\\pages\\home\\src\\Location\\index.vue":"7","D:\\project\\vue\\MF-UavMonitor\\src\\pages\\home\\src\\UavProfile\\index.vue":"8","D:\\project\\vue\\MF-UavMonitor\\src\\pages\\home\\src\\Compass\\index.vue":"9","D:\\project\\vue\\MF-UavMonitor\\src\\pages\\home\\src\\PlayerProfile\\index.vue":"10","D:\\project\\vue\\MF-UavMonitor\\src\\pages\\home\\src\\Gradienter\\index.vue":"11","D:\\project\\vue\\MF-UavMonitor\\src\\layout\\Layout.vue":"12"},{"size":171709,"mtime":1743044652000,"hashOfConfig":"13"},{"size":2275,"mtime":1743044652000,"hashOfConfig":"14"},{"size":2866,"mtime":1743044650000,"hashOfConfig":"13"},{"size":2545,"mtime":1743044652000,"hashOfConfig":"14"},{"size":1967,"mtime":1743044650000,"hashOfConfig":"13"},{"size":1083,"mtime":1743066182134,"hashOfConfig":"14"},{"size":833,"mtime":1743064170106,"hashOfConfig":"14"},{"size":2872,"mtime":1743044652000,"hashOfConfig":"14"},{"size":3593,"mtime":1743044652000,"hashOfConfig":"14"},{"size":2237,"mtime":1743066054862,"hashOfConfig":"14"},{"size":4566,"mtime":1743044652000,"hashOfConfig":"14"},{"size":1388,"mtime":1743066173175,"hashOfConfig":"14"},"umlgel","18uby9b"]
[{"D:\\project\\vue\\MF-UavMonitor\\src\\assets\\fonts\\SarasaMonoSC\\result.css":"1","D:\\project\\vue\\MF-UavMonitor\\src\\pages\\home\\src\\Params\\params.vue":"2","D:\\project\\vue\\MF-UavMonitor\\src\\assets\\fonts\\Furore\\result.css":"3","D:\\project\\vue\\MF-UavMonitor\\src\\pages\\home\\src\\Params\\altimeter.vue":"4","D:\\project\\vue\\MF-UavMonitor\\src\\assets\\fonts\\DincorosBlack\\result.css":"5","D:\\project\\vue\\MF-UavMonitor\\src\\pages\\home\\index.vue":"6","D:\\project\\vue\\MF-UavMonitor\\src\\pages\\home\\src\\Location\\index.vue":"7","D:\\project\\vue\\MF-UavMonitor\\src\\pages\\home\\src\\UavProfile\\index.vue":"8","D:\\project\\vue\\MF-UavMonitor\\src\\pages\\home\\src\\Compass\\index.vue":"9","D:\\project\\vue\\MF-UavMonitor\\src\\pages\\home\\src\\PlayerProfile\\index.vue":"10","D:\\project\\vue\\MF-UavMonitor\\src\\pages\\home\\src\\Gradienter\\index.vue":"11","D:\\project\\vue\\MF-UavMonitor\\src\\layout\\Layout.vue":"12","D:\\project\\vue\\MF-UavMonitor\\src\\pages\\home\\src\\Params\\index.vue":"13","D:\\project\\vue\\MF-UavMonitor\\src\\pages\\demo\\index.vue":"14"},{"size":171709,"mtime":1743044652000,"hashOfConfig":"15"},{"size":2275,"mtime":1743044652000,"hashOfConfig":"16"},{"size":2866,"mtime":1743044650000,"hashOfConfig":"15"},{"size":2545,"mtime":1743044652000,"hashOfConfig":"16"},{"size":1967,"mtime":1743044650000,"hashOfConfig":"15"},{"size":560,"mtime":1743406213199,"hashOfConfig":"16"},{"size":833,"mtime":1743064170106,"hashOfConfig":"16"},{"size":2872,"mtime":1743044652000,"hashOfConfig":"16"},{"size":3593,"mtime":1743044652000,"hashOfConfig":"16"},{"size":2237,"mtime":1743066054862,"hashOfConfig":"16"},{"size":4566,"mtime":1743044652000,"hashOfConfig":"16"},{"size":1362,"mtime":1743406146053,"hashOfConfig":"16"},{"size":416,"mtime":1743044652000,"hashOfConfig":"16"},{"size":4129,"mtime":1743387234241,"hashOfConfig":"16"},"umlgel","18uby9b"]

@ -4,8 +4,6 @@
> 模型加载较慢,如果长时间未加载成功请刷新后重试
> 服务地址 :https://mf-uav-monitor.vercel.app/#/home
> 服务地址 :http://39.101.188.84:9999/demo/uav-demo/#/home
<h1>

@ -4,16 +4,20 @@ import * as Cesium from 'cesium'
import 'cesium/Build/Cesium/Widgets/widgets.css'
export const params = reactive({
lat: 29.83,
lng: 119.86,
altitude: 2000,
heading: 4.7,
pitch: 0,
roll: 0,
lat: 31.162232,
lng: 120.734077,
altitude: 3000,
heading: -15, //当前朝向
pitch: 0, //保当前俯仰角
roll: 0, //当前翻滚角
correction: 1,
speed: 700,
gamepad: false,
gamepadKey: null,
})
let interval: any
export function useUav() {
const container = ref<HTMLElement>()
const viewer = shallowRef<Cesium.Viewer>()
@ -58,15 +62,17 @@ export function useUav() {
// @ts-ignore 清除版权信息
viewer.value.cesiumWidget.creditContainer.style.display = 'none'
}
const boostrapUav = (url: string) => {
onLoadModel(url)
onAddKeyboardListener()
onLoadModel('//data.mars3d.cn/gltf/mars/wrj.glb')
// onAddKeyboardListener()
const renderer = () => {
onAdjustParams()
//开启手柄实时监听
startgamepad()
// onAdjustParams()
onAdjustAttitude()
requestAnimationFrame(renderer)
}
renderer()
}
@ -79,14 +85,138 @@ export function useUav() {
model: {
uri: url,
runAnimations: true,
// minimumPixelSize: 128,
// maximumScale: 20000,
// scale: 0.05,
minimumPixelSize: 128,
maximumScale: 20000,
scale: 0.05,
},
})
viewer.value!.trackedEntity = entity
uav.value = entity
}
//开启监听游戏手柄
const onAddGamepadboardListener = () => {
window.addEventListener('gamepadconnected', function (e) {
var gp = navigator.getGamepads()[e.gamepad.index]
console.log(gp, '链接')
})
// 监听游戏手柄拔出
window.addEventListener('gamepaddisconnected', function (e) {
clearInterval(interval)
})
}
const startgamepad = () => {
var gamepad = navigator.getGamepads()[0]
if (gamepad) {
//手柄方向按键
// pressKey(gamepad!.buttons)
// 手柄方向遥感
rocker(gamepad!.axes)
}
}
const rocker = (axes: any) => {
const deadZone = 0.5
const x = axes[0]
const y = axes[1]
// //机体左转
if (x < -deadZone) {
//机体左转
params.heading -= 0.005
if (params.roll > -0.785) {
params.roll -= 0.005
}
} else if (x > deadZone) {
//机体右转
params.heading += 0.005
if (params.roll < 0.785) {
params.roll += 0.005
}
}
// 检测垂直方向机体爬升
if (y < -deadZone && params.pitch <= 0.3) {
params.pitch += 0.005
if (params.pitch > 0) {
const { speed, pitch } = params
const temp = (params.speed / 60 / 60 / 60) * 110
//1经纬度约等于110km
params.altitude += temp * Math.sin(pitch)
}
} else if (y > deadZone && params.pitch >= -0.3) {
//机体俯冲
params.pitch -= 0.006
if (params.pitch < 0) {
const { speed, pitch } = params
//1经纬度约等于110km
const temp = (params.speed / 60 / 60 / 60) * 110
params.altitude += temp * Math.sin(pitch)
}
}
uavCommit()
}
const uavCommit = () => {
const { heading, pitch, roll } = params
const { abs, cos } = Math
params.correction = abs(cos(heading) * cos(pitch))
if (abs(heading) < 0.001) params.heading = 0
if (abs(roll) < 0.001) params.roll = 0
if (abs(pitch) < 0.001) params.pitch = 0
//方向自动回正
// if (params.heading > 0) params.heading -= 0.0025
// if (params.heading < 0) params.heading += 0.0025
if (params.roll > 0) params.roll -= 0.003
if (params.roll < 0) params.roll += 0.003
if (params.pitch < 0) params.pitch += 0.005
if (params.pitch > 0) params.pitch -= 0.003
}
//按键
const pressKey = (arr: any) => {
// 通常情况下,方向键对应的按钮索引
const upButton = arr[12]
const downButton = arr[13]
const leftButton = arr[14]
const rightButton = arr[15]
//机体爬升
if (upButton.pressed && params.pitch <= 0.3) {
params.pitch += 0.005
if (params.pitch > 0) {
const { speed, pitch } = params
const temp = (params.speed / 60 / 60 / 60) * 110
//1经纬度约等于110km
params.altitude += temp * Math.sin(pitch)
}
} else if (downButton.pressed && params.pitch >= -0.3) {
//机体俯冲
params.pitch -= 0.006
if (params.pitch < 0) {
const { speed, pitch } = params
//1经纬度约等于110km
const temp = (params.speed / 60 / 60 / 60) * 110
params.altitude += temp * Math.sin(pitch)
}
} else if (leftButton.pressed) {
//机体左转
params.heading -= 0.005
if (params.roll > -0.785) {
params.roll -= 0.005
}
} else if (rightButton.pressed) {
//机体右转
params.heading += 0.005
if (params.roll < 0.785) {
params.roll += 0.005
}
}
uavCommit()
}
//开启按键监听
const onAddKeyboardListener = () => {
document.addEventListener('keydown', (e: KeyboardEvent) => {
@ -158,19 +288,22 @@ export function useUav() {
if (params.pitch < 0) params.pitch += 0.005
if (params.pitch > 0) params.pitch -= 0.003
}
//开启飞行姿态调整
// 开启飞行姿态调整/
const onAdjustAttitude = () => {
const temp = params.speed / 60 / 60 / 60 / 110
params.lng += temp * Math.cos(params.heading)
params.lat -= temp * Math.sin(params.heading)
const { lng, lat, altitude, heading, pitch, roll } = params
params.altitude += temp * Math.sin(pitch) * 110 * 1000 * 10
const position = Cesium.Cartesian3.fromDegrees(lng, lat, altitude)
const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll)
const orientation = Cesium.Transforms.headingPitchRollQuaternion(
position,
hpr
)
uav.value.orientation = orientation
uav.value.position = position
@ -193,6 +326,7 @@ export function useUav() {
}
onMounted(async () => {
onAddGamepadboardListener()
await boostrapViewer()
boostrapUav(`${import.meta.env.VITE_API_DOMAIN}/models/feiji.glb`)
})

@ -1,14 +1,14 @@
<template>
<div class="layout">
<div class="layout-header">
<!-- <div class="layout-header">
<div style="display: flex">
<!-- <LayoutBrand /> -->
<!-- <LayoutMenu /> -->
<LayoutBrand />
<LayoutMenu />
</div>
<div style="display: flex">
<!-- <LayoutActions /> -->
<LayoutActions />
</div>
</div>
</div> -->
<div class="layout-body">
<router-view v-slot="{ Component }">
<transition name="slide">
@ -40,8 +40,9 @@ import LayoutActions from './LayoutActions.vue'
}
.layout-body {
box-sizing: border-box;
height: calc(100% - 50px);
padding: 20px;
height: 100%;
// padding: 20px;
background-color: var(--color-neutral-2);
// background-image: url('@/assets/images/pattern_light.svg');

@ -0,0 +1,158 @@
<template>
<div class="gamepad-container">
<h1>游戏手柄监听</h1>
<div v-if="gamepadConnected">
<div class="status">
<h2>方向键状态</h2>
<p>: {{ buttons.dpadUp ? '按下' : '释放' }}</p>
<p>: {{ buttons.dpadDown ? '按下' : '释放' }}</p>
<p>: {{ buttons.dpadLeft ? '按下' : '释放' }}</p>
<p>: {{ buttons.dpadRight ? '按下' : '释放' }}</p>
<h2>左摇杆状态</h2>
<p>X轴: {{ axes.leftStickX.toFixed(2) }}</p>
<p>Y轴: {{ axes.leftStickY.toFixed(2) }}</p>
<h2>右摇杆状态</h2>
<p>X轴: {{ axes.rightStickX.toFixed(2) }}</p>
<p>Y轴: {{ axes.rightStickY.toFixed(2) }}</p>
</div>
</div>
<div v-else>
<p>未检测到游戏手柄请连接手柄后按任意按钮激活</p>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
const gamepadConnected = ref(false)
const animationFrameId = ref(null)
//
const buttons = ref({
dpadUp: false,
dpadDown: false,
dpadLeft: false,
dpadRight: false,
})
//
const axes = ref({
leftStickX: 0,
leftStickY: 0,
rightStickX: 0,
rightStickY: 0,
})
//
const STANDARD_MAPPING = {
buttons: {
dpadUp: 12,
dpadDown: 13,
dpadLeft: 14,
dpadRight: 15,
},
axes: {
leftStickX: 0,
leftStickY: 1,
rightStickX: 2,
rightStickY: 3,
},
}
//
const updateGamepadStatus = () => {
const gamepads = navigator.getGamepads()
const gamepad = gamepads[0] //
if (!gamepad) {
gamepadConnected.value = false
return
}
gamepadConnected.value = true
//
buttons.value.dpadUp =
gamepad.buttons[STANDARD_MAPPING.buttons.dpadUp].pressed
buttons.value.dpadDown =
gamepad.buttons[STANDARD_MAPPING.buttons.dpadDown].pressed
buttons.value.dpadLeft =
gamepad.buttons[STANDARD_MAPPING.buttons.dpadLeft].pressed
buttons.value.dpadRight =
gamepad.buttons[STANDARD_MAPPING.buttons.dpadRight].pressed
//
axes.value.leftStickX = gamepad.axes[STANDARD_MAPPING.axes.leftStickX]
axes.value.leftStickY = gamepad.axes[STANDARD_MAPPING.axes.leftStickY]
axes.value.rightStickX = gamepad.axes[STANDARD_MAPPING.axes.rightStickX]
axes.value.rightStickY = gamepad.axes[STANDARD_MAPPING.axes.rightStickY]
//
animationFrameId.value = requestAnimationFrame(updateGamepadStatus)
}
//
const handleGamepadConnected = (event) => {
console.log('手柄已连接:', event.gamepad)
if (!animationFrameId.value) {
animationFrameId.value = requestAnimationFrame(updateGamepadStatus)
}
}
//
const handleGamepadDisconnected = (event) => {
console.log('手柄已断开:', event.gamepad)
gamepadConnected.value = false
if (animationFrameId.value) {
cancelAnimationFrame(animationFrameId.value)
animationFrameId.value = null
}
}
onMounted(() => {
window.addEventListener('gamepadconnected', handleGamepadConnected)
window.addEventListener('gamepaddisconnected', handleGamepadDisconnected)
//
if (navigator.getGamepads()[0]) {
animationFrameId.value = requestAnimationFrame(updateGamepadStatus)
}
})
onBeforeUnmount(() => {
window.removeEventListener('gamepadconnected', handleGamepadConnected)
window.removeEventListener('gamepaddisconnected', handleGamepadDisconnected)
if (animationFrameId.value) {
cancelAnimationFrame(animationFrameId.value)
}
})
</script>
<style>
.gamepad-container {
max-width: 600px;
padding: 20px;
margin: 0 auto;
font-family: Arial, sans-serif;
}
.status {
padding: 20px;
margin-top: 20px;
color: #333;
background-color: #f5f5f5;
border-radius: 8px;
}
h1,
h2 {
color: #333;
}
p {
margin: 8px 0;
}
</style>

@ -1,28 +1,13 @@
<template>
<div class="container">
<BasePanel :width="20" :height="18">
<div style="width: 100%; height: 100%" ref="container"></div>
</BasePanel>
<UavProfile :width="4" :height="9" />
<PlayerProfile :width="4" :height="9" />
<Location :width="4" :height="6" />
<Params :width="12" :height="6" />
<Gradienter :width="4" :height="6" />
<Compass :width="4" :height="6" />
<div style="width: 100%; height: 100%" ref="container"></div>
<!-- <div class="no-gamepad" v-show="params.gamepad"></div> -->
</div>
</template>
<script setup lang="ts">
import { BasePanel } from '@/components'
import {
UavProfile,
PlayerProfile,
Compass,
Params,
Gradienter,
Location,
} from './src'
import { onMounted, nextTick } from 'vue'
import { useUav } from '@/hooks/useUav'
import { params } from '@/hooks'
const { container, loadModel, cesiumBoostrap } = useUav()
@ -32,12 +17,6 @@ onMounted(() => {
</script>
<style lang="scss" scoped>
.container {
box-sizing: border-box;
display: grid;
grid-template-rows: repeat(24, 1fr);
grid-template-columns: repeat(24, 1fr);
grid-auto-flow: row dense;
grid-gap: 10px;
width: 100%;
height: 100%;
}

@ -7,6 +7,11 @@ export const routes: any[] = [
name: 'home',
component: () => import('@/pages/home/index.vue'),
},
{
path: '/demo',
name: 'demo',
component: () => import('@/pages/demo/index.vue'),
},
]
export const router = createRouter({

Loading…
Cancel
Save