import notifyCenter from 'global'; var ActionJump = require('jump-0.3'); import { KalmanModel, KalmanObservation } from "../js/kalman/kalman"; import { mVector, mMatrix } from "../js/kalman/sylvester" cc.Class({ extends: cc.Component, properties: { playerContro: { default: null, type: cc.Node, serializable: true, }, playerControScript: { default: null, visible: false, serializable: true, }, mass: { default: 50, type: cc.Integer, tooltip: "物体质量/kg", serializable: true, }, xLCount: { default: 0, type: cc.Float, tooltip: "x轴负向", visible: false, serializable: false, }, xRCount: { default: 0, type: cc.Float, tooltip: "x轴正向", visible: false, serializable: false, }, yLCount: { default: 0, type: cc.Float, tooltip: "y轴负向", visible: false, serializable: false, }, yRCount: { default: 0, type: cc.Float, tooltip: "y轴正向", visible: false, serializable: false, }, zLCount: { default: 0, type: cc.Float, tooltip: "z轴负向", visible: false, serializable: false, }, zRCount: { default: 0, type: cc.Float, tooltip: "z轴正向", visible: false, serializable: false, }, //记录一次打击,如果通方向就更新最大值 hitFirst: { default: null, visible: false, serializable: true, }, //锁住hit update 更新情况 bLock: { default: false, visible: false, serializable: false, }, //定义一个状态字典 hitState: { default: null, serializable: false, visible: false, }, staticTime: { default: 1, type: cc.Float, serializable: true, tooltip: "立柱静止的检测时间" }, bSwing: { default: false, serializable: true, tooltip: "是否摆动" }, showCalorieLabel: { default: null, type: cc.Label, serializable: true }, LCount: { default: 0, type: cc.Integer, tooltip: "左勾拳打击次数", visible: false, serializable: false, }, RCount: { default: 0, type: cc.Integer, tooltip: "右勾拳打击次数", visible: false, serializable: false, }, ZCount: { default: 0, type: cc.Integer, tooltip: "直拳打击次数", visible: false, serializable: false, }, AllCalorie: { default: 0, type: cc.Integer, tooltip: "总共的卡路里", visible: false, serializable: false, }, AllPower: { default: 0, type: cc.Integer, tooltip: "三次下来的总力量", visible: false, serializable: false, }, bRAnimation: false, bLAnimation: false, bMAnimation: false, currentLimitValue: { default: 8, type: cc.Integer, tooltip: "限制值", visible: false, serializable: false, }, currentLimitDireValue: { default: 1, type: cc.Float, tooltip: "角度限制值", visible: false, serializable: false, }, drawNodeX: { default: null, type: cc.Node, tooltip: "绘制节点", }, drawNodeZ: { default: null, type: cc.Node, tooltip: "绘制Z节点", }, drawNodeY: { default: null, type: cc.Node, tooltip: "绘制Y节点", }, drawNodeLinearX: { default: null, type: cc.Node, tooltip: "绘制节点", }, drawNodeLinearZ: { default: null, type: cc.Node, tooltip: "绘制Z节点", }, drawNodeLinearY: { default: null, type: cc.Node, tooltip: "绘制Y节点", }, drawNodeSqrt: { default: null, type: cc.Node, tooltip: "绘制开平方的节点", }, drawNodeLinearSqrt: { default: null, type: cc.Node, tooltip: "绘制开平方的节点", }, drawNodeOriSqrt: { default: null, type: cc.Node, tooltip: "绘制开平方的节点", }, drawTip: { default: null, type: cc.Node, tooltip: "提示点", }, //X轴的变化数组值 xAccArray: [], xMax: 0, maxTimeoutId: null, bMaxPause: false, xMin: 0, minTimeoutId: null, bMinPause: false, bCalculation: false, calTimeout: null, //波动判断打击部分 //记录打击的x 轴数据 xArray: [], //z轴的变化数组值 zArray: [], //更新的下标 updateIndex: 0, //是否正在计算对应的值 bCalX: false, bCalZ: false, xDifference: 0, zDifference: 0, xMaxDifference: 0, zMaxDifference: 0, xMaxAddCount: 0, xAddDifferenceCount: 0, zMaxAddCount: 0, zAddDifferenceCount: 0, hitCountL: 0, hitCountZ: 0, //是否判断 bJudging: false, //z和x系数转换 ratio: 0.5, //合力计算 oldResultantDifference: 0, resultantDifference: 0, bCalResultant: false, //最大的差值 resultantMaxDifference: 0, //最大的加速度 resultantMaxX: 0, resultantMaxZ: 0, resultantMaxAddCount: 0, resultAddDifferenceCount: 0, //传感器部分 sensorHit: false, phoneAccIndex: 0, BLEAccIndex: 0, oldPhoneIndex: 0, oldBLEAccIndex: 0, alpha: 0, beta: 0, gamma: 0, oldOriAlpha: 0, oldOriBeta: 0, oldOriGamma: 0, oldOriCount: 0, jump: false,//是否是跳 jumpCount: 0, runCount: 0, bGyroDraw: true, bAccDraw: true, gyroDrawLabel: { default: null, type: cc.Label, serializable: true, }, accDrawLabel: { default: null, type: cc.Label, serializable: true, }, bGyroLog: false, bAccLog: false, gyroLogLabel: { default: null, type: cc.Label, serializable: true, }, accLogLabel: { default: null, type: cc.Label, serializable: true, }, rotate: { default: null, type: cc.Node }, //记录当前数据 currentGameDataArray: [], currentInterval: null, currentDataArray: [], //是否先手原始数据 bShowOri: true, bShowIndex: 0, //游戏节点 gameNode: { default: null, type: cc.Node, tooltip: "游戏节点", }, axisLabel: { default: null, type: cc.Label, serializable: true, }, isY: true, LastMS: 0, LastTime: 0, /** * 融合算法部分使用的参数 */ //记录时间 timestamp: 0, //ms转s MS2S: 0.001, //开始初始化bool initState: true, // angular speeds from gyro gyro: new Array(3), // rotation matrix from gyro data gyroMatrix: new Array(9), // orientation angles from gyro matrix gyroOrientation: new Array(3), // magnetic field vector magnet: new Array(3), // accelerometer vector accel: new Array(3), // orientation angles from accel and magnet accMagOrientation: new Array(3), // final orientation angles from sensor fusion fusedOrientation: new Array(3), // accelerometer and magnetometer based rotation matrix rotationMatrix: new Array(9), /** * 波形 */ drawX: { default: null, type: cc.Node, tooltip: "绘制X节点", }, }, onChangeAxis(event) { // console.log(event); this.isY = event.isChecked this.axisLabel.string = this.isY ? 'y' : 'x'; }, // LIFE-CYCLE CALLBACKS: onLoad() { //关闭渲染 // this.bAccDraw = false; window.webView = this; this.playerControScript = this.playerContro.getComponent("PlayerController"); //游戏节点 if (this.gameNode != null) { this.gameScript = this.gameNode.getComponent("game"); } this.hitState = { "xLCount": 0, "xRCount": 0, "zLCount": 0, "zRCount": 0 } this.oldxA = 0; this.oldzA = 0; this.bUpdateOnce = false; this.bDelayOnce = false; this.lineX = this.drawNodeX.getComponent("line"); this.lineZ = this.drawNodeZ.getComponent("line"); this.lineY = this.drawNodeY.getComponent("line"); this.lineLinearX = this.drawNodeLinearX.getComponent("line"); this.lineLinearZ = this.drawNodeLinearZ.getComponent("line"); this.lineLinearY = this.drawNodeLinearY.getComponent("line"); this.drawXScript = this.drawX.getComponent("heartBeat"); this.lineSqrt = this.drawNodeSqrt.getComponent("line"); this.lineSqrtLinear = this.drawNodeLinearSqrt.getComponent("line"); this.lineOriSqrt = this.drawNodeOriSqrt.getComponent("line"); this.lineDrawTip = this.drawTip.getComponent("line"); //判断左右勾拳 z this.oldAlpha = 0; this.oldGamma = 0; this.oldBeta = 0; this.xA = 0; this.yA = 0; this.zA = 0; this.bJump = false; this.linear_acceleration_z = 0; // this.isY = true; /** * 算法初始参数 */ this.gyroOrientation[0] = 0.0; this.gyroOrientation[1] = 0.0; this.gyroOrientation[2] = 0.0; // initialise gyroMatrix with identity matrix this.gyroMatrix[0] = 1.0; this.gyroMatrix[1] = 0.0; this.gyroMatrix[2] = 0.0; this.gyroMatrix[3] = 0.0; this.gyroMatrix[4] = 1.0; this.gyroMatrix[5] = 0.0; this.gyroMatrix[6] = 0.0; this.gyroMatrix[7] = 0.0; this.gyroMatrix[8] = 1.0; this.schedule(() => { this.calculateFusedOrientationTask(); }, 0.03, 0, 1) }, start() { // 卡曼尔滤波 this.x_0 = mVector([0]); this.P_0 = mMatrix([[1]]); this.F_k = mMatrix([[1]]); this.Q_k = mMatrix([[0.1]]); this.KM = new KalmanModel(this.x_0, this.P_0, this.F_k, this.Q_k); // console.log(this.KM); this.z_k = mVector([1]); this.H_k = mMatrix([[1]]); this.R_k = mMatrix([[4]]); this.KO = new KalmanObservation(this.z_k, this.H_k, this.R_k); // for (var i = 0; i < 100; i++) { // let _temp = 0.5 + Math.random(); // _temp *= 9.8; // if (i == 30) // _temp = 60; // if(i == 31) // _temp = -70; // if (i > 50) { // _temp = -100; // } // this.z_k = mVector([_temp]); // this.KO.z_k = this.z_k; // this.KM.update(this.KO); // console.log(this.KM.x_k.elements[0], i, _temp); // this.lineZ.onUpdateIndex(); // this.lineZ.onUpdateDraw(this.KM.x_k.elements[0]); // } this.rotate.angle = 90; this.actionJump = new ActionJump(); this.actionJump.addEventListener('resultant', (e) => { // console.log(e.type); if (e.type == 'jump') { this.jumpCount++; this.playerControScript.onJumpCount(this.jumpCount); // this.lineSqrt.onUpdateDrawFromColor(e.value, cc.Color.RED, 5); this.bJump = true; } else if (e.type == 'peakOfWave') { if (this.bAccDraw) { this.lineSqrt.onUpdateDrawLastValueFromColor(e.oldValue, cc.Color.WHITE, 5, e.lastIndex); } } else if (e.type == 'valleyOfWave') { if (this.bAccDraw) { this.lineSqrt.onUpdateDrawLastValueFromColor(e.oldValue, cc.Color.BLUE, 5, e.lastIndex); } } else if (e.type == 'curAngle') { // this.playerControScript.onHandleState(e.value); if (e.value > 0) { this.rotate.angle = 180; this.playerControScript.leftJump(); } else { this.rotate.angle = 0; this.playerControScript.rightJump(); } } else if (e.type == 'rotate') { // if (allOGyroValue > 0) { // console.log('right:', allOGyroValue); // } else { // console.log('left:', allOGyroValue); // } } else if (e.type == 'stateDataOfJump') { console.log('stateDataOfJump:', JSON.stringify(e)); // this.event.trigger('resultant', { // type: "stateDataOfJump", // currentMaxValue: _frameMaxValue, // currentMinValue: _frameMinValue, // peakOfWaveMaxValue: this.peakOfWaveMaxValue, // valleyOfWaveMinValue: this.valleyOfWaveMinValue, // oGyroValue: _frameGyroValue, // resultant: resultant, // tempIndex: i, //触发了多少次 // name: "highestCountEnd" // }); console.log(Math.abs(e.currentMaxValue) > Math.abs(e.currentMinValue) ? "左方向" : "右方向"); console.log(Math.abs(e.oGyroValue) > Math.abs(e.oGyroMinValue) ? "右旋转" : "左旋转"); if (e.oGyroValue > 1000) { this.rotate.angle = 0; } else if (e.oGyroMinValue < -1000) { this.rotate.angle = 180; } //发送给game,在game里面处理判断 // if (this.gameScript) // this.gameScript.listenStateDataOfJump(e, this.isY); } else if (e.type == 'bUpdateDraw') { if (this.bAccDraw) { let { lAccX, lAccY, lAccZ } = e.data.linearAcc; let { oAccX, oAccY, oAccZ } = e.data.oriAcc; // if (lAccX > 0) // this.lineX.onUpdateDraw(lAccX); // else // this.lineX.onUpdateDrawFromColor(lAccX, cc.Color.WHITE, 2); // this.lineLinearX.onUpdateDrawFromColor(oAccX, "#00F0FF", 2); // this.z_k = mVector([e.linearZ]); // this.KO.z_k = this.z_k; // this.KM.update(this.KO); // // console.log(this.KM.x_k.elements[0], e.linearZ); // if (this.KM.x_k.elements[0] > 0) // this.lineZ.onUpdateDraw(this.KM.x_k.elements[0]); // else // this.lineZ.onUpdateDrawFromColor(this.KM.x_k.elements[0], cc.Color.WHITE, 2); // if (lAccZ > 0) // this.lineZ.onUpdateDraw(lAccZ); // else // this.lineZ.onUpdateDrawFromColor(lAccZ, cc.Color.WHITE, 2); this.lineZ.onUpdateDraw(oAccZ); this.lineLinearZ.onUpdateDrawFromColor(lAccZ, "#00F0FF", 2); } } else if (e.type == 'stop') { console.log('stop'); this.onSaveData(this.currentGameDataArray); this.lineX.clear(); this.lineY.clear(); this.lineLinearY.clear(); // let tempArray = e.tempDataArray[0]; for (let i = 0; i < tempArray.length; i++) { //绘制读取的数据 this.lineY.onUpdateIndex(); this.lineY.onUpdateDraw(tempArray[i].oriAcc.oAccZ); this.lineLinearY.onUpdateIndex(); this.lineLinearY.onUpdateDraw(tempArray[i].linearAcc.lAccZ); if (i < 30) { this.lineX.onUpdateIndex(); this.lineX.onUpdateDraw(tempArray[i].oriAcc.oAccZ); } } } }) this.onBind(); setInterval(() => { let ave = this.phoneAccIndex - this.oldPhoneIndex; this.playerControScript.onAvePhoneIndex(ave); this.oldPhoneIndex = this.phoneAccIndex; let ave2 = this.BLEAccIndex - this.oldBLEAccIndex; this.playerControScript.onAveBLEIndex(ave2); this.oldBLEAccIndex = this.BLEAccIndex; }, 1000) // for (let i = 0; i < 300; i++) { // let box = {}; // box["acc"] = { // ax: -9 * Math.random() + 0.1, // ay: -9 * Math.random() + 0.1, // az: 9 * Math.random() + 0.1 // }; // box["gyro"] = { // gx: -9 * Math.random() + 0.1, // gy: -9 * Math.random() + 0.1, // gz: 9 * Math.random() + 0.1 // }; // box["min"] = 0; // box["s"] = 0; // box["ms"] = 10 * i + 10; // this.onBLEBoxUpdate(box); // } // let index = 0; // this.schedule(() => { // let box = {}; // box["acc"] = { // ax: -9 * Math.random() + 0.1, // ay: -9 * Math.random() + 0.1, // az: 9 * Math.random() + 0.1 // }; // box["gyro"] = { // gx: -9 * Math.random() + 0.1, // gy: -9 * Math.random() + 0.1, // gz: 9 * Math.random() + 0.1 // }; // box["min"] = 0; // box["s"] = 0; // box["ms"] = 10 * index + 10; // index ++ ; // this.onBLEBoxUpdate(box); // }, 0.03) }, //重置一下,记录的数据 onResetAccState() { console.log("重置 onResetAccState"); this.hitFirst = null; this.bLock = false; this.hitState = { "xLCount": 0, "xRCount": 0, "zLCount": 0, "zRCount": 0 } }, onBind() { notifyCenter.on('webViewMessage', (data) => { let name = data.funName; if (name == "onWatchAccelerometer") { /** * 返回加速计的数据 * { * xAxis * yAxis * zAxis * } */ webView.onUpdateAcc(data); } else if (name == "onWatchOrientation") { } else if (name == "onDeviceUpdateData") { webView.sensorHit = true; let gameData = data.gameData; // console.log(gameData.data); if (gameData.dataType == "Box") { webView.onBLEBoxUpdate(gameData.data, true); } } else if (name == "onDeviceUpdateJson") { let gameData = data.gameData; if (gameData.dataType == "Json") { webView.onBLEJsonUpdate(gameData.data); } } else if (name == 'saveData') { let gameData = data.gameData; // console.log("saveData:", gameData.length); //停止硬件更新 webView.onSendWriteBLEDataValue(null, 4); let saveArrayData = gameData; // for (let i = 0; i < saveArrayData.length; i++) { // console.log(saveArrayData[i]); // //绘制读取的数据 // } if (this.currentInterval) { this.currentInterval = null; clearInterval(this.currentInterval); } let _index = 60; let _length = saveArrayData.length - 370; // this.currentInterval = setInterval(() => { // webView.onBLEBoxUpdate(saveArrayData[_index]); // _index++; // if (_index >= _length) { // clearInterval(this.currentInterval); // } // }, 0, 0.006) for (let i = _index; i < _length; i++) { //绘制读取的数据 webView.onBLEBoxUpdate(saveArrayData[i]); } this.actionJump.setEndUpdate(); } }); // 监听蓝牙设备刷新 uni.postMessage({ data: { funName: "addDeviceUpdateListener", gameData: {} } }) //监听蓝牙设备刷新 uni.postMessage({ data: { funName: "addDeviceJsonUpdateListener", gameData: {} } }) }, onUnBind() { notifyCenter.off('webViewMessage'); uni.postMessage({ data: { funName: "closeAccelerometer", gameData: {} } }) uni.postMessage({ data: { funName: "closeOrientation", gameData: {} } }) }, //返回一个json对象 onBLEJsonUpdate(gameData) { // console.log("json:", gameData); }, //手柄盒子数据处理 /** * 计算数据不能为 0, gx, gy, gz * @param {*} gameData * @returns */ onBLEBoxUpdate(gameData, bAdd) { //记录最近的500帧原始设备数据 if (bAdd) { if (this.currentGameDataArray.length <= 500) { this.currentGameDataArray.push(gameData); } else { this.currentGameDataArray.shift(); this.currentGameDataArray.push(gameData); } } // console.log(gameData); //********传感器数值******** let { ax, ay, az } = gameData.acc; let { gx, gy, gz } = gameData.gyro; let { min, s, ms } = gameData; // //更新 // //更新Accl // this.accel = [ax, ay, az]; // this.calculateAccMagOrientation(); // //更新计算gyro // this.gyroFunction([gx, gy, gz], ms); //更新index if (this.bAccDraw) { // this.lineX.onUpdateIndex(); this.lineZ.onUpdateIndex(); // this.lineY.onUpdateIndex(); this.lineLinearX.onUpdateIndex(); // this.lineLinearY.onUpdateIndex(); this.lineLinearZ.onUpdateIndex(); this.lineSqrt.onUpdateIndex(); this.lineOriSqrt.onUpdateIndex(); // if (this.bGyroDraw) { // this.lineX.onUpdateDrawOriBeta(gx); // this.lineX.onUpdateDrawOriGamma(gy); // this.lineX.onUpdateDrawOriAlpha(gz); // this.lineOriSqrt.onUpdateDraw(oriNew); // } // this.playerControScript.updateOriSqrt(oriNew); } // var msGap = ms - this.LastMS; // if (msGap < 0) { // msGap += 1000; // } // this.LastMS = ms; // let _ax = gameData.acc.ax * 10; // let _ay = gameData.acc.ay * 10; // let _az = gameData.acc.az * 10; // //低通滤波分离重力 // let curTime = new Date().getTime(); // let alpha = 1000 / (1000 + msGap); // 0.8; // this.LastTime = curTime; // this.xA = alpha * this.xA + (1 - alpha) * _ax; // this.yA = alpha * this.yA + (1 - alpha) * _ay; // this.zA = alpha * this.zA + (1 - alpha) * _az; //高通滤波获取线性速度 // let linear_acceleration_x = _ax - this.xA; // let linear_acceleration_y = _ay - this.yA; // let linear_acceleration_z = _az - this.zA; // let _temp = { // oriAcc: { // oAccX: _ax, // oAccY: _ay, // oAccZ: _az // }, // linearAcc: { // lAccX: linear_acceleration_x, // lAccY: linear_acceleration_y, // lAccZ: linear_acceleration_z // }, // gravityAcc: { // gravityX: this.xA, // gravityY: this.yA, // gravityZ: this.zA // }, // bLimitRebound: false, // resultant: Math.sqrt(_ax * _ax // + _ay * _ay + _az * _az), // runIndex: this.BLEAccIndex, // //陀螺仪 // oriGyro: { // oGyroX: gx, // oGyroY: gy, // oGyroZ: gz // }, // //输入当前轴 // bYAxis: this.isY, // }; gameData.BLEAccIndex = this.BLEAccIndex; gameData.bYAxis = this.isY; this.actionJump.updateJump(gameData); // this.currentDataArray.push(_temp); // this.drawXScript.addPoint(_temp.oriAcc.oAccX); if (this.bAccDraw) { //显示 // this.playerControScript.updateAcc({ xAxis: _ax, yAxis: _ay, zAxis: _az }); // this.playerControScript.updateAcc({ xAxis: deltaVector[0], yAxis: deltaVector[1], zAxis: deltaVector[2] }); // this.playerControScript.updateStr(deltaVector[3]); // this.playerControScript.updateAcc({ xAxis: this.gyroOrientation[0], yAxis: this.gyroOrientation[1], zAxis: this.gyroOrientation[2] }); // this.playerControScript.updateSqrt(_temp.resultant); this.lineOriSqrt.onUpdateDraw(gy); } // if (this.bAccDraw) { // this.lineSqrt.onUpdateDraw(_temp.resultant); // } // if (this.bAccLog) { // console.log("acc:" + JSON.stringify(gameData.acc)); // } this.playerControScript.updateBLEIndex(this.BLEAccIndex); if (this.BLEAccIndex > 150) { this.onClear(); this.bJump = false; } this.BLEAccIndex++; this.oldxA = this.xA; }, onClear() { // this.lineX.clear(); this.lineZ.clear(); // this.lineY.clear(); this.lineLinearX.clear(); // this.lineLinearY.clear(); this.lineLinearZ.clear(); this.lineSqrt.clear(); this.lineOriSqrt.clear(); this.BLEAccIndex = 0; this.oldBLEAccIndex = 0; this.currentDataArray = []; this.rotate.angle = 90; }, onSendWriteBLEData() { uni.postMessage({ data: { funName: "writeBLEConnectionValue", gameData: { value: this.playerControScript.writeValue } } }) }, onSendWriteBLEDataValue(event, data) { console.log(data); this.onClear(); this.bJump = false; uni.postMessage({ data: { funName: "writeBLEConnectionValue", gameData: { value: data } } }) console.log(this.gyroMatrix); console.log(this.gyroMatrix.length); }, onSaveData(data) { uni.postMessage({ data: { funName: "saveData", gameData: { value: data } } }) }, onGetData() { uni.postMessage({ data: { funName: "getData", gameData: {} } }) }, onOnlySendWriteBLEDataValue(event, data) { console.log(data); uni.postMessage({ data: { funName: "writeBLEConnectionValue", gameData: { value: data } } }) }, //在app 端输出log onLog(str) { uni.postMessage({ data: { funName: "log", gameData: str } }) }, onChangeAccDraw() { console.log("this.bAccDraw", this.bAccDraw); this.bAccDraw = !this.bAccDraw; this.accDrawLabel.string = this.bAccDraw ? "关闭渲染加速计" : "开启渲染加速计"; }, onChangeGyroDraw() { this.bGyroDraw = !this.bGyroDraw; this.gyroDrawLabel.string = this.bGyroDraw ? "关闭渲染陀螺仪" : "开启渲染陀螺仪"; }, onShowAccLog() { this.bAccLog = !this.bAccLog; this.accLogLabel.string = this.bAccLog ? "关闭加速计日志" : "开启加速计日志"; }, onShowGyroLog() { this.bGyroLog = !this.bGyroLog; this.gyroLogLabel.string = this.bGyroLog ? "关闭陀螺仪日志" : "开启陀螺仪日志"; }, onSlideEvent(data) { this.bShowIndex = data.progress; this.onUpdateShowData(this.bShowIndex); }, onSwitchData(data) { // console.log(data.node); this.bShowOri = data.isChecked; data.node.getChildByName('label').getComponent(cc.Label).string = this.bShowOri ? '原始数据' : '线性加速度'; this.onUpdateShowData(this.bShowIndex); }, onUpdateShowData(_progress) { let _length = this.currentDataArray.length; let index = Math.ceil(_progress * _length); let _data = this.currentDataArray[index - 1 < 0 ? 0 : index - 1]; let currentX = _data.oriAcc.x; let currentZ = _data.oriAcc.z; let currentY = _data.oriAcc.y; if (!this.bShowOri) { currentX = _data.linearAcc.ax; currentZ = _data.linearAcc.az; currentY = _data.linearAcc.ay; } //显示 this.playerControScript.updateAcc({ xAxis: currentX, yAxis: currentY, zAxis: currentZ }); this.playerControScript.updateSqrt(_data.resultant); this.lineDrawTip.drawCicleFromIndex(index); }, /** * * 处理陀螺仪数据 * */ getRotationVectorFromGyro(gyroValues, deltaRotationVector, timeFactor) { // console.log("gyroValues:", gyroValues); let EPSILON = 0.001; let normValues = new Array(3);; // Calculate the angular speed of the sample let omegaMagnitude = Math.sqrt(gyroValues[0] * gyroValues[0] + gyroValues[1] * gyroValues[1] + gyroValues[2] * gyroValues[2]); // Normalize the rotation vector if it's big enough to get the axis if (omegaMagnitude > EPSILON) { normValues[0] = gyroValues[0] / omegaMagnitude; normValues[1] = gyroValues[1] / omegaMagnitude; normValues[2] = gyroValues[2] / omegaMagnitude; } // Integrate around this axis with the angular speed by the timestep // in order to get a delta rotation from this sample over the timestep // We will convert this axis-angle representation of the delta rotation // into a quaternion before turning it into the rotation matrix. let thetaOverTwo = omegaMagnitude * timeFactor; let sinThetaOverTwo = Math.sin(thetaOverTwo); let cosThetaOverTwo = Math.cos(thetaOverTwo); // console.log(sinThetaOverTwo, "==", normValues[0]); deltaRotationVector[0] = sinThetaOverTwo * normValues[0]; deltaRotationVector[1] = sinThetaOverTwo * normValues[1]; deltaRotationVector[2] = sinThetaOverTwo * normValues[2]; deltaRotationVector[3] = cosThetaOverTwo; }, /** Helper function to convert a rotation vector to a rotation matrix. * Given a rotation vector (presumably from a ROTATION_VECTOR sensor), returns a * 9 or 16 element rotation matrix in the array R. R must have length 9 or 16. * If R.length == 9, the following matrix is returned: *
     *   /  R[ 0]   R[ 1]   R[ 2]   \
     *   |  R[ 3]   R[ 4]   R[ 5]   |
     *   \  R[ 6]   R[ 7]   R[ 8]   /
     *
* If R.length == 16, the following matrix is returned: *
     *   /  R[ 0]   R[ 1]   R[ 2]   0  \
     *   |  R[ 4]   R[ 5]   R[ 6]   0  |
     *   |  R[ 8]   R[ 9]   R[10]   0  |
     *   \  0       0       0       1  /
     *
* @param rotationVector the rotation vector to convert * @param R an array of floats in which to store the rotation matrix */ getRotationMatrixFromVector(R, rotationVector) { let q0; let q1 = rotationVector[0]; let q2 = rotationVector[1]; let q3 = rotationVector[2]; if (rotationVector.length >= 4) { q0 = rotationVector[3]; } else { q0 = 1 - q1 * q1 - q2 * q2 - q3 * q3; q0 = (q0 > 0) ? Math.sqrt(q0) : 0; } let sq_q1 = 2 * q1 * q1; let sq_q2 = 2 * q2 * q2; let sq_q3 = 2 * q3 * q3; let q1_q2 = 2 * q1 * q2; let q3_q0 = 2 * q3 * q0; let q1_q3 = 2 * q1 * q3; let q2_q0 = 2 * q2 * q0; let q2_q3 = 2 * q2 * q3; let q1_q0 = 2 * q1 * q0; if (R.length == 9) { R[0] = 1 - sq_q2 - sq_q3; R[1] = q1_q2 - q3_q0; R[2] = q1_q3 + q2_q0; R[3] = q1_q2 + q3_q0; R[4] = 1 - sq_q1 - sq_q3; R[5] = q2_q3 - q1_q0; R[6] = q1_q3 - q2_q0; R[7] = q2_q3 + q1_q0; R[8] = 1 - sq_q1 - sq_q2; } else if (R.length == 16) { R[0] = 1 - sq_q2 - sq_q3; R[1] = q1_q2 - q3_q0; R[2] = q1_q3 + q2_q0; R[3] = 0.0; R[4] = q1_q2 + q3_q0; R[5] = 1 - sq_q1 - sq_q3; R[6] = q2_q3 - q1_q0; R[7] = 0.0; R[8] = q1_q3 - q2_q0; R[9] = q2_q3 + q1_q0; R[10] = 1 - sq_q1 - sq_q2; R[11] = 0.0; R[12] = R[13] = R[14] = 0.0; R[15] = 1.0; } }, matrixMultiplication(A, B) { let result = new Array(9); result[0] = A[0] * B[0] + A[1] * B[3] + A[2] * B[6]; result[1] = A[0] * B[1] + A[1] * B[4] + A[2] * B[7]; result[2] = A[0] * B[2] + A[1] * B[5] + A[2] * B[8]; result[3] = A[3] * B[0] + A[4] * B[3] + A[5] * B[6]; result[4] = A[3] * B[1] + A[4] * B[4] + A[5] * B[7]; result[5] = A[3] * B[2] + A[4] * B[5] + A[5] * B[8]; result[6] = A[6] * B[0] + A[7] * B[3] + A[8] * B[6]; result[7] = A[6] * B[1] + A[7] * B[4] + A[8] * B[7]; result[8] = A[6] * B[2] + A[7] * B[5] + A[8] * B[8]; return result; }, /** * Computes the device's orientation based on the rotation matrix. *

* When it returns, the array values are as follows: *

*

* Applying these three rotations in the azimuth, pitch, roll order * transforms an identity matrix to the rotation matrix passed into this * method. Also, note that all three orientation angles are expressed in * radians. * * @param R * rotation matrix see {@link #getRotationMatrix}. * * @param values * an array of 3 floats to hold the result. * * @return The array values passed as argument. * * @see #getRotationMatrix(float[], float[], float[], float[]) * @see GeomagneticField */ getOrientation(R, values) { /* * 4x4 (length=16) case: * / R[ 0] R[ 1] R[ 2] 0 \ * | R[ 4] R[ 5] R[ 6] 0 | * | R[ 8] R[ 9] R[10] 0 | * \ 0 0 0 1 / * * 3x3 (length=9) case: * / R[ 0] R[ 1] R[ 2] \ * | R[ 3] R[ 4] R[ 5] | * \ R[ 6] R[ 7] R[ 8] / * */ if (R.length == 9) { values[0] = Math.atan2(R[1], R[4]); values[1] = Math.asin(-R[7]); values[2] = Math.atan2(-R[6], R[8]); } else { values[0] = Math.atan2(R[1], R[5]); values[1] = Math.asin(-R[9]); values[2] = Math.atan2(-R[8], R[10]); } return values; }, getRotationMatrixFromOrientation(o) { let xM = new Array(9); let yM = new Array(9); let zM = new Array(9); let sinX = Math.sin(o[1]); let cosX = Math.cos(o[1]); let sinY = Math.sin(o[2]); let cosY = Math.cos(o[2]); let sinZ = Math.sin(o[0]); let cosZ = Math.cos(o[0]); // rotation about x-axis (pitch) xM[0] = 1.0; xM[1] = 0.0; xM[2] = 0.0; xM[3] = 0.0; xM[4] = cosX; xM[5] = sinX; xM[6] = 0.0; xM[7] = -sinX; xM[8] = cosX; // rotation about y-axis (roll) yM[0] = cosY; yM[1] = 0.0; yM[2] = sinY; yM[3] = 0.0; yM[4] = 1.0; yM[5] = 0.0; yM[6] = -sinY; yM[7] = 0.0; yM[8] = cosY; // rotation about z-axis (azimuth) zM[0] = cosZ; zM[1] = sinZ; zM[2] = 0.0; zM[3] = -sinZ; zM[4] = cosZ; zM[5] = 0.0; zM[6] = 0.0; zM[7] = 0.0; zM[8] = 1.0; // rotation order is y, x, z (roll, pitch, azimuth) let resultMatrix = this.matrixMultiplication(xM, yM); resultMatrix = this.matrixMultiplication(zM, resultMatrix); return resultMatrix; }, calculateAccMagOrientation() { // magnet if (this.getRotationMatrix(this.rotationMatrix, null, this.accel, [1, 1, 1])) { this.getOrientation(this.rotationMatrix, this.accMagOrientation); } // this.getOrientation(this.rotationMatrix, this.accel); }, getRotationMatrix(R, I, gravity, geomagnetic) { // TODO: move this to native code for efficiency let Ax = gravity[0]; let Ay = gravity[1]; let Az = gravity[2]; let normsqA = (Ax * Ax + Ay * Ay + Az * Az); let g = 9.81; let freeFallGravitySquared = 0.01 * g * g; if (normsqA < freeFallGravitySquared) { // gravity less than 10% of normal value return false; } let Ex = geomagnetic[0]; let Ey = geomagnetic[1]; let Ez = geomagnetic[2]; let Hx = Ey * Az - Ez * Ay; let Hy = Ez * Ax - Ex * Az; let Hz = Ex * Ay - Ey * Ax; let normH = Math.sqrt(Hx * Hx + Hy * Hy + Hz * Hz); if (normH < 0.1) { // device is close to free fall (or in space?), or close to // magnetic north pole. Typical values are > 100. return false; } let invH = 1.0 / normH; Hx *= invH; Hy *= invH; Hz *= invH; let invA = 1.0 / Math.sqrt(Ax * Ax + Ay * Ay + Az * Az); Ax *= invA; Ay *= invA; Az *= invA; let Mx = Ay * Hz - Az * Hy; let My = Az * Hx - Ax * Hz; let Mz = Ax * Hy - Ay * Hx; if (R != null) { if (R.length == 9) { R[0] = Hx; R[1] = Hy; R[2] = Hz; R[3] = Mx; R[4] = My; R[5] = Mz; R[6] = Ax; R[7] = Ay; R[8] = Az; } else if (R.length == 16) { R[0] = Hx; R[1] = Hy; R[2] = Hz; R[3] = 0; R[4] = Mx; R[5] = My; R[6] = Mz; R[7] = 0; R[8] = Ax; R[9] = Ay; R[10] = Az; R[11] = 0; R[12] = 0; R[13] = 0; R[14] = 0; R[15] = 1; } } if (I != null) { // compute the inclination matrix by projecting the geomagnetic // vector onto the Z (gravity) and X (horizontal component // of geomagnetic vector) axes. let invE = 1.0 / Math.sqrt(Ex * Ex + Ey * Ey + Ez * Ez); let c = (Ex * Mx + Ey * My + Ez * Mz) * invE; let s = (Ex * Ax + Ey * Ay + Ez * Az) * invE; if (I.length == 9) { I[0] = 1; I[1] = 0; I[2] = 0; I[3] = 0; I[4] = c; I[5] = s; I[6] = 0; I[7] = -s; I[8] = c; } else if (I.length == 16) { I[0] = 1; I[1] = 0; I[2] = 0; I[4] = 0; I[5] = c; I[6] = s; I[8] = 0; I[9] = -s; I[10] = c; I[3] = I[7] = I[11] = I[12] = I[13] = I[14] = 0; I[15] = 1; } } return true; }, gyroFunction(gyroValues, timeMs) { // don't start until first accelerometer/magnetometer orientation has been acquired if (this.accMagOrientation == null) return; // initialisation of the gyroscope based rotation matrix if (this.initState) { let initMatrix = new Array(9); initMatrix = this.getRotationMatrixFromOrientation(this.accMagOrientation); let test = new Array(3); this.getOrientation(initMatrix, test); this.gyroMatrix = this.matrixMultiplication(this.gyroMatrix, initMatrix); this.initState = false; } // copy the new gyro values into the gyro array // convert the raw gyro data into a rotation vector let deltaVector = new Array(4); // if (this.timestamp != 0) { // } let dT = (timeMs - this.timestamp) * this.MS2S; this.gyro = []; gyroValues.forEach((item) => this.gyro.push(item)); this.getRotationVectorFromGyro(this.gyro, deltaVector, dT / 2.0); // measurement done, save current time for next interval this.timestamp = timeMs; // convert rotation vector into rotation matrix let deltaMatrix = new Array(9); // console.log("deltaVector1:" + deltaVector); // console.log("deltaMatrix1:" + deltaMatrix); this.getRotationMatrixFromVector(deltaMatrix, deltaVector); // console.log("deltaVector2:" + deltaVector); // console.log("deltaMatrix2:" + deltaMatrix); // apply the new rotation interval on the gyroscope based rotation matrix this.gyroMatrix = this.matrixMultiplication(this.gyroMatrix, deltaMatrix); // console.log(this.gyroMatrix); // console.log(this.gyroMatrix.length); // get the gyroscope based orientation from the rotation matrix this.getOrientation(this.gyroMatrix, this.gyroOrientation); }, /** * 主要计算 */ calculateFusedOrientationTask() { let FILTER_COEFFICIENT = 0.98; let oneMinusCoeff = 1.0 - FILTER_COEFFICIENT; this.fusedOrientation[0] = FILTER_COEFFICIENT * this.gyroOrientation[0] + oneMinusCoeff * this.accMagOrientation[0]; this.fusedOrientation[1] = FILTER_COEFFICIENT * this.gyroOrientation[1] + oneMinusCoeff * this.accMagOrientation[1]; this.fusedOrientation[2] = FILTER_COEFFICIENT * this.gyroOrientation[2] + oneMinusCoeff * this.accMagOrientation[2]; // overwrite gyro matrix and orientation with fused orientation // to comensate gyro drift this.gyroMatrix = this.getRotationMatrixFromOrientation(this.fusedOrientation); // System.arraycopy(fusedOrientation, 0, gyroOrientation, 0, 3); this.gyroOrientation = []; this.fusedOrientation.forEach((item) => this.gyroOrientation.push(item)); } });