欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

用铯进行无人机飞行模拟的纯前端实施

最编程 2024-04-15 17:28:55
...

纯前端用cesium实现无人机模拟飞行

1.效果预览

index (1).gif

2.项目搭建

项目采用vite + vue3 + ts框架搭建,使用vite-plugin-cesium插件引入cesium

vite.config.ts 配置

import { defineConfig } from "vite"
import vue from "@vitejs/plugin-vue"
import cesium from "vite-plugin-cesium"

export default {
    plugins: [
      vue(),
      cesium()
    ]
}

3.搭建 cesium

<template>
    <div style="width:100vw; height:100vh" ref="container"></div>
</templete>
<script setup lang="ts">
    import * as cesium from "cesium"
    import { onMounted, ref } from "vue"
    
    const container = ref<HTMLElement>()
    onMounted(()=>{
        const configs = {
          infoBox: false,
          selectionIndicator: false,
          animation: false,
          baseLayerPicker: false,
          geocoder: false,
          navigationHelpButton: false,
          fullscreenButton: false,
          homeButton: false,
          sceneModePicker: false,
          timeline: false,
          shadows: true,
          shouldAnimate: true,
        }
        const viewer = new cesium.Viewer(container.value, {
            ...configs,
            terrainProvider: cesium.createWorldTerrain()
        })
    })
</script>

4.导入无人机模型

无人机模型文件放置在:项目根目录下 public/models/uav.glb

<template>
    <div style="width:100vw; height:100vh" ref="container"></div>
</templete>
<script setup lang="ts">
    import * as cesium from "cesium"
    import { onMounted, ref } from "vue"
    
    const container = ref<HTMLElement>()
    onMounted(()=>{
        const configs = {...}
        const viewer = new cesium.Viewer(container.value, {...})
        
        //模型的初始位置(经度,维度,高度)
        const position = cesium.Cartesian3.fromDegrees(120, 30, 2000)
        const hpr = new cesium.HeadingPitchRoll()
        const orientation = cesium.Transforms.headingPitchRollQuaternion(position, hpr)
        const model = viewer.entities.add({
            name: "uax",
            position,
            orientation,
            model: {
                uri: "/models/uav.glb",
                minimumPixelSize: 128,
                maximumScale: 20000,
                runAnimations: true
            }
        })
        //摄像头跟随模型
        viewer.trackedEntity = model
        
    })
</script>

5. 完成无人机的基本动作

<template>
    <div style="width:100vw; height:100vh" ref="container"></div>
</templete>
<script setup lang="ts">
    import * as cesium from "cesium"
    import { onMounted, ref } from "vue"
    
    const container = ref<HTMLElement>()
    //飞行姿态参数
    const flightParams = reactive({
        at: 30,
        lng: 120,
        altitude: 2000,
        heading: 0,
        pitch: 0,
        roll: 0,
        correction: 1,
        speed: 1224
    })
    onMounted(()=>{
        //...
        const model = viewer.entities.add({...})
        viewer.trackedEntity = model
        
        //根据flightParams参数调整无人机的飞行姿态
        const adjustFlightAttitude = () => {
            // 在requestAnimationFrame中调用, 每秒更新60次
            // temp 计算的是每次更新无人机移动的经纬度度数
            const temp = flightParams.speed / 60 / 60 / 60 / 110
            flightParams.lng += temp * Math.cos(flightParams.heading)
            flightParams.lat -= temp * Math.sin(flightParams.heading)
            const { lng, lat, altitude, heading, pitch, roll } = flightParams
            flightParams.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)
            model.orientation = orientation
            model.position = position
        }
        const renderer = () => {
            adjustFlightAttitude()
            requestAnimationFrame(renderer)
        }
        renderer()
    })
</script>

6.完成无人机的按键监听

没有通过用户按键事件直接调整飞行参数, 是为了实现按键无冲同时姿态调整过渡更加顺畅

<template>
    <div style="width:100vw; height:100vh" ref="container"></div>
</templete>
<script setup lang="ts">
    import * as cesium from "cesium"
    import { onMounted, ref } from "vue"
    
    const container = ref<HTMLElement>()
    const flightParams = reactive({...})
    const DIRECTION_ENUM = {
        UP: "w",
        DOWN: "s",
        LEFT: "a",
        RIGHT: "d",
        SPEED_UP: "q",
        SPEED_DOWN: "e",
    }
    const keysMap = {
        [DIRECTION_ENUM.UP]: false,
        [DIRECTION_ENUM.DOWN]: false,
        [DIRECTION_ENUM.LEFT]: false,
        [DIRECTION_ENUM.RIGHT]: false,
        [DIRECTION_ENUM.SPEED_UP]: false,
        [DIRECTION_ENUM.SPEED_DOWN]: false,
    }
    onMounted(()=>{
        //...
        const model = viewer.entities.add({...})
        viewer.trackedEntity = model
        const adjustFlightAttitude = () => {...}
        //开启按键监听
        const openKeysListener = () => {
            document.addEventListener("keydown", (e: KeyboardEvent) => {
              if (Object.keys(keysMap).includes(e.key)){ 
                  keysMap[e.key] = true
              }
            })
            document.addEventListener("keyup", (e: KeyboardEvent) => {
              if (Object.keys(keysMap).includes(e.key)){
                  keysMap[e.key] = false
              }
            })
        }
        const renderer = () => {
            adjustFlightAttitude()
            requestAnimationFrame(renderer)
        }
        renderer()
        openKeysListener()
    })
    
</script>

7.按键监听后调整飞行参数

<template>
    <div style="width:100vw; height:100vh" ref="container"></div>
</templete>
<script setup lang="ts">
    import * as cesium from "cesium"
    import { onMounted, ref } from "vue"
    
    const container = ref<HTMLElement>()
    const flightParams = reactive({...})
    const DIRECTION_ENUM = {...}
    const keysMap = {...}
    onMounted(()=>{
        //...
        const adjustFlightAttitude = () => {...}
        const openKeysListener = () => {...}
        const adjustFlightParams = () => {
            //无人机加速
            if (keysMap[DIRECTION_ENUM.SPEED_UP]) {
              flightParams.speed += 100
            }
            //无人机减速
            if (keysMap[DIRECTION_ENUM.SPEED_DOWN]) {
              flightParams.speed -= 100
            }
            //机体爬升
            if (keysMap[DIRECTION_ENUM.UP] && flightParams.pitch <= 0.3) {
                flightParams.pitch += 0.005
                const { speed, pitch } = flightParams
                const temp = (flightParams.speed / 60 / 60 / 60) * 110
                flightParams.altitude += temp * Math.sin(pitch)
            }
            //机体俯冲
            if (keysMap[DIRECTION_ENUM.DOWN] && flightParams.pitch >= -0.3) {
                flightParams.pitch -= 0.01
                const { speed, pitch } = flightParams
                const temp = (flightParams.speed / 60 / 60 / 60) * 110
                flightParams.altitude += temp * Math.sin(pitch)
            }
            //机体左转
            if (keysMap[DIRECTION_ENUM.LEFT]) {
                flightParams.heading -= 0.005
                if (flightParams.roll > -0.785) flightParams.roll -= 0.005
            }
            //机体右转
            if (keysMap[DIRECTION_ENUM.RIGHT]) {
                flightParams.heading += 0.005
                if (flightParams.roll < 0.785) flightParams.roll += 0.005
            }
            //方向自动回正
            const { heading, pitch, roll } = flightParams
            const { abs, cos } = Math
            flightParams.correction = abs(cos(heading) * cos(pitch))
            if (abs(heading) < 0.001) flightParams.heading = 0
            if (abs(roll) < 0.001) flightParams.roll = 0
            if (abs(pitch) < 0.001) flightParams.pitch = 0
            if (flightParams.roll > 0) flightParams.roll -= 0.003
            if (flightParams.roll < 0) flightParams.roll += 0.003
            if (flightParams.pitch < 0) flightParams.pitch += 0.005
            if (flightParams.pitch > 0) flightParams.pitch -= 0.003
        }
        const renderer = () => {
            adjustFlightParams()
            adjustFlightAttitude()
            requestAnimationFrame(renderer)
        }
        renderer()
        openKeysListener()
    })
    
</script>

8.完整代码和DEMO实例

  • 如果需要阅读完整代码,可以访问fengtianxi001/MF-UAV仓库, 顺便再帮我点个star吧!
  • 在线案例可以访问MF-UAV, 在线预览效果

推荐阅读