init commit

This commit is contained in:
叶志超
2023-10-13 16:43:53 +08:00
parent c1fc36b44c
commit d184776cb3
275 changed files with 8305 additions and 0 deletions

View File

@@ -0,0 +1,218 @@
<template>
<div ref="satelliteRef" id="satellite" />
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import * as THREE from 'three'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js'
import { useSerialPortStore, SerialPortEventType } from '@/stores/serialPort'
import { LineBreakParser } from '@/utils/parser'
const accRate = 16384.0
const gyrRate = 131.072
const satelliteRef = ref(null)
let renderer = null
let scene = null
let camera = null
let satellite = null
const initSatellite = async () => {
// 创建场景对象Scene
scene = new THREE.Scene()
// 添加相机
const w = satelliteRef.value.clientWidth, h = satelliteRef.value.clientHeight
const aspect = w / h
const frustumSize = 15
camera = new THREE.OrthographicCamera(
frustumSize * aspect / -2, frustumSize * aspect / 2,
frustumSize / 2, frustumSize / -2)
camera.position.z = 100
scene.add(camera)
// 添加renderer
renderer = new THREE.WebGLRenderer()
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(w, h)
// 加载卫星模型
const modelDirPath = '../models/geo/'
const rgbeLoader = new RGBELoader().setPath(modelDirPath)
const environment = await rgbeLoader.loadAsync('env.hdr')
environment.mapping = THREE.EquirectangularReflectionMapping
scene.environment = environment
const gltfLoader = new GLTFLoader().setPath(modelDirPath)
const geoModel = await gltfLoader.loadAsync('geo.gltf')
geoModel.scene.traverse( function ( child ) {
if ( child.isMesh ) {
child.material.emissive = child.material.color
child.material.emissiveMap = child.material.map
}
})
satellite = geoModel.scene
scene.add(satellite)
camera.lookAt(satellite.position)
satelliteRef.value.appendChild(renderer.domElement)
}
let acc = new Array(3), gyr = new Array(3)
let firstSample = true
let lastTime = 0
let RwGyro = [0, 0, 0] // 重新读取陀螺仪
let Awz = [0, 0] // XZ/ YZ平面和Z轴(度)R的投影之间的角度
let RwEst = [0, 0, 0]
const wGyro = 10.0
const HALF_PI = Math.PI / 2
const render = () => {
if (acc && gyr) {
updateRwExt(acc, gyr)
}
if (satellite) {
satellite.rotation.x = HALF_PI * -RwEst[0]
satellite.rotation.z = HALF_PI * RwEst[1]
// satellite.rotation.y = HALF_PI * RwEst[2]
}
renderer?.render(scene, camera)
}
const updateRwExt = (acc, gyr) => {
normalize3DVec(acc)
let currentTime = Date.now()
let interval = currentTime - lastTime
lastTime = currentTime
if (firstSample || isNaN(RwEst[0])) {
for (let index = 0; index < 3; index++) {
RwEst[index] = acc[index]// 初始化加速度传感器读数
}
} else {
// 对RwGyro进行评估
if (Math.abs(RwEst[2]) < 0.1) {
// Rz值非常的小,它的作用是作为Axz与Ayz的计算参照值,防止放大的波动产生错误的结果。
// 这种情况下就跳过当前的陀螺仪数据,使用以前的。
for (let index = 0; index < 3; index++) {
RwGyro[index] = RwEst[index]
}
} else {
// ZX/ZY平面和Z轴R的投影之间的角度,基于最近一次的RwEst值
for (let index = 0; index < 2; index++) {
let tmpf = gyr[index] // 获取当前陀螺仪的deg/s
tmpf *= interval / 1000.0 // 得到角度变化值
Awz[index] = Math.atan2(RwEst[index], RwEst[2]) * 180 / Math.PI // 得到角度并转换为度
Awz[index] += tmpf // 根据陀螺仪的运动得到更新后的角度
}
// 判断RzGyro是多少,主要看Axz的弧度是多少
// 当Axz在-90 ..90 => cos(Awz) >= 0这个范围内的时候RzGyro是准确的
let signRzGyro = (Math.cos(Awz[0] * Math.PI / 180) >= 0) ? 1 : -1
// 从Awz的角度值反向计算RwGyro的公式请查看网页 http://starlino.com/imu_guide.html
for (let index = 0; index < 2; index++) {
RwGyro[0] = Math.sin(Awz[0] * Math.PI / 180)
RwGyro[0] /= Math.sqrt(1 + squared(Math.cos(Awz[0] * Math.PI / 180)) * squared(Math.tan(Awz[1] * Math.PI / 180)))
RwGyro[1] = Math.sin(Awz[1] * Math.PI / 180)
RwGyro[1] /= Math.sqrt(1 + squared(Math.cos(Awz[1] * Math.PI / 180)) * squared(Math.tan(Awz[0] * Math.PI / 180)))
}
RwGyro[2] = signRzGyro * Math.sqrt(1 - squared(RwGyro[0]) - squared(RwGyro[1]))
// 把陀螺仪与加速度传感器的值进行结合
for (let index = 0; index < 3; index++) {
RwEst[index] = (acc[index] + wGyro * RwGyro[index]) / (1 + wGyro)
}
normalize3DVec(RwEst)
}
}
}
const normalize3DVec = (vector) => {
const R = Math.sqrt(squared(vector[0]) + squared(vector[1]) + squared(vector[2]))
vector[0] /= R
vector[1] /= R
vector[2] /= R
}
const squared = x => Math.pow(x, 2)
const animate = () => {
render()
requestAnimationFrame(animate)
}
// 判断字符串是否满足协议格式
const isValidFormat = data => {
if (!data) {
return false
}
if (data.indexOf('@POSE') < 0) {
return false
}
const splitIndex = data.indexOf('*') + 1
const dataStr = data.substring(0, splitIndex)
const csStr = data.substring(splitIndex)
if (!dataStr || !csStr) {
return false
}
if (dataStr.length !== parseInt(csStr.trim())) {
return false
}
return true
}
// 将data转换成加速度和角速度
const data2AccGyr = data => {
const startIndex = data.indexOf(',') + 1
const endIndex = data.indexOf('*')
const dataArr = data.substring(startIndex, endIndex).split(',').map(parseFloat)
acc = new Array(3), gyr = new Array(3)
acc[0] = dataArr[0] / accRate
acc[1] = dataArr[1] / accRate
acc[2] = dataArr[2] / accRate
gyr[0] = dataArr[3] / gyrRate
gyr[1] = dataArr[4] / gyrRate
gyr[2] = dataArr[5] / gyrRate
}
const decoder = new TextDecoder()
const parser = new LineBreakParser()
const onSerialPortData = (data: Uint8Array) => {
const lineArr = parser.parse(decoder.decode(data))
lineArr.forEach(line => {
if (!isValidFormat(line)) return
data2AccGyr(line)
})
}
const serialPort = useSerialPortStore()
serialPort.addEventListener(SerialPortEventType.data, onSerialPortData)
onMounted(() => {
initSatellite()
animate()
})
</script>