@aibee/crc-bmap 0.8.44 → 0.8.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/lib/bmap.cjs.min.js +408 -401
  2. package/lib/bmap.esm.js +7644 -1526
  3. package/lib/bmap.esm.min.js +408 -401
  4. package/lib/bmap.min.js +408 -401
  5. package/lib/src/bmap.js +60 -54
  6. package/lib/src/context/OrbitControls.js +4 -1
  7. package/lib/src/context/context.js +54 -37
  8. package/lib/src/context/control.js +85 -83
  9. package/lib/src/context/scene.js +1 -0
  10. package/lib/src/elements/base-svg.js +5 -4
  11. package/lib/src/elements/floor.js +8 -0
  12. package/lib/src/elements/glb-model.js +14 -18
  13. package/lib/src/elements/graphic.js +18 -8
  14. package/lib/src/elements/ground-texture.js +41 -44
  15. package/lib/src/elements/heatmap.js +2 -1
  16. package/lib/src/elements/lane.js +5 -1
  17. package/lib/src/elements/merge-graphic.js +3 -28
  18. package/lib/src/elements/model.js +5 -9
  19. package/lib/src/elements/overlay.js +9 -7
  20. package/lib/src/elements/poi.js +55 -49
  21. package/lib/src/elements/poi2.js +52 -53
  22. package/lib/src/elements/shadow.js +3 -1
  23. package/lib/src/elements/svg-line.js +2 -0
  24. package/lib/src/elements/svg-polygon.js +1 -0
  25. package/lib/src/elements/text-texture.js +32 -36
  26. package/lib/src/elements/wall.js +3 -41
  27. package/lib/src/external/meshLine.js +17 -0
  28. package/lib/src/factory/img-texture.js +1 -0
  29. package/lib/src/factory/material.js +21 -51
  30. package/lib/src/factory/model.js +31 -33
  31. package/lib/src/factory/text-texture.js +17 -0
  32. package/lib/src/factory/unique-key.js +8 -0
  33. package/lib/src/layer/graphic-layer.js +1 -0
  34. package/lib/src/layer/parking-layer.d.ts +6 -0
  35. package/lib/src/layer/parking-layer.js +8 -0
  36. package/lib/src/layer/poi-layer.js +4 -1
  37. package/lib/src/layer/poi-layer2.js +2 -0
  38. package/lib/src/loader/AibeeLoader/index.js +230 -276
  39. package/lib/src/loader/AibeeLoader/layer.js +8 -6
  40. package/lib/src/loader/CrLoader/api/floor.js +45 -73
  41. package/lib/src/loader/CrLoader/index.js +88 -106
  42. package/lib/src/operations/hover/hover-helper.js +12 -2
  43. package/lib/src/operations/selection/selection.js +11 -1
  44. package/lib/src/plugins/car-inertial-position/car-inertial-position.d.ts +42 -10
  45. package/lib/src/plugins/car-inertial-position/car-inertial-position.js +285 -67
  46. package/lib/src/plugins/car-inertial-position/compass.d.ts +4 -5
  47. package/lib/src/plugins/car-inertial-position/compass.js +43 -38
  48. package/lib/src/plugins/car-inertial-position/kalman-filter.d.ts +14 -0
  49. package/lib/src/plugins/car-inertial-position/kalman-filter.js +30 -0
  50. package/lib/src/plugins/car-inertial-position/utils.d.ts +1 -1
  51. package/lib/src/plugins/car-inertial-position/utils.js +8 -5
  52. package/lib/src/plugins/cr-nav-path/cr-nav-path.js +47 -55
  53. package/lib/src/plugins/cr-nav-path/cr-path.worker.js +4 -2
  54. package/lib/src/plugins/equipment/equipment.js +20 -22
  55. package/lib/src/plugins/mul-floor-navigation/mul-floor-navigation.js +63 -61
  56. package/lib/src/plugins/mul-floor-navigation/path.js +34 -30
  57. package/lib/src/plugins/mul-floor-navigation/start-model.js +2 -1
  58. package/lib/src/plugins/mul-floor-select/mul-floor-select.js +2 -1
  59. package/lib/src/plugins/mul-floors/mul-floors.js +1 -0
  60. package/lib/src/plugins/nav-path/nav-path.js +51 -59
  61. package/lib/src/plugins/nav-path/path.worker.js +4 -2
  62. package/lib/src/plugins/navigation/navigation.d.ts +5 -0
  63. package/lib/src/plugins/navigation/navigation.js +240 -217
  64. package/lib/src/plugins/navigation/path.js +34 -30
  65. package/lib/src/plugins/navigation/position-navigation.d.ts +4 -0
  66. package/lib/src/plugins/navigation/position-navigation.js +96 -79
  67. package/lib/src/plugins/navigation/start-rotate-helper-poi.js +18 -6
  68. package/lib/src/plugins/pdr-position/imu-position.js +13 -9
  69. package/lib/src/plugins/pdr-position/particle.js +4 -2
  70. package/lib/src/plugins/pdr-position/pdr.js +5 -4
  71. package/lib/src/plugins/pdr-position/position.js +5 -2
  72. package/lib/src/plugins/pdr-position/sensor.js +20 -25
  73. package/lib/src/plugins/select/select.js +11 -1
  74. package/lib/src/utils/camera-bound.js +3 -1
  75. package/lib/src/utils/color.js +8 -4
  76. package/lib/src/utils/coordinate.js +1 -0
  77. package/lib/src/utils/create.js +4 -2
  78. package/lib/src/utils/events.js +15 -4
  79. package/lib/src/utils/index-db.js +18 -11
  80. package/lib/src/utils/init-helper.js +7 -2
  81. package/lib/src/utils/obj-utils.js +3 -2
  82. package/lib/src/utils/os.js +1 -0
  83. package/lib/src/utils/path.js +15 -4
  84. package/lib/src/utils/promise.js +3 -1
  85. package/lib/src/utils/proxy.js +2 -1
  86. package/lib/src/utils/road.js +20 -14
  87. package/lib/src/utils/road2.js +60 -39
  88. package/lib/src/utils/rules.js +1 -0
  89. package/lib/src/utils/string.js +3 -1
  90. package/lib/src/utils/svg.js +12 -11
  91. package/lib/src/utils/taskQueue.js +29 -29
  92. package/lib/src/utils/timer.js +8 -1
  93. package/lib/src/utils/translate.js +3 -1
  94. package/lib/src/utils/tween.js +8 -0
  95. package/lib/src/utils/webworker.js +10 -9
  96. package/package.json +2 -1
@@ -1,69 +1,298 @@
1
1
  // 车辆惯性导航
2
+ /**
3
+ * 定位结果有三种:视觉定位结果、beacon定位结果、pdr定位结果
4
+ * 以 pdr 定位为主要流程,以视觉结果和beacon结果为校准,其中主视觉定位,beacon是在长期无视觉情况下才会采用的
5
+ * 以视觉定位结果计算角度、速度,通过卡尔曼滤波平滑速度和角度的变化
6
+ * 什么时候通过视觉、蓝牙校准pdr的结果???
7
+ */ import { _ as _extends } from "@swc/helpers/_/_extends";
8
+ import "core-js/modules/es.array.find-last-index.js";
9
+ import "core-js/modules/es.array.find-last.js";
10
+ import "core-js/modules/es.array.push.js";
2
11
  import { EventDispatcher } from "three";
3
- import { Compass } from "./compass";
4
- import { calculateLineDirection, predictFuturePosition, predictFutureSpeed, transformSpeed } from "./utils";
12
+ import { calculateLineDirection, predictFuturePosition, transformSpeed } from "./utils";
5
13
  import { getLength, Timer } from "../../utils";
14
+ import { KalmanFilter } from "./kalman-filter";
15
+ import { isNil } from "lodash";
6
16
  export class CarInertialPosition extends EventDispatcher {
7
- startCompass() {
8
- this.compass.start();
17
+ setPathAngle(angle) {
18
+ this.pathAngle = angle;
19
+ this.dispatchEvent({
20
+ type: "change-compass",
21
+ value: this.pathAngle
22
+ });
23
+ if (isNil(this.angle)) {
24
+ this.angle = angle;
25
+ }
26
+ }
27
+ changeSpeed() {
28
+ const serverHistory = this.history.filter((item)=>[
29
+ "vision"
30
+ ].includes(item.type));
31
+ if (serverHistory.length < 2) {
32
+ this.speedFilter.filter(0);
33
+ this.speed = 0;
34
+ return;
35
+ }
36
+ ;
37
+ // 计算速度:位置变化 / 时间差
38
+ // const speeds = serverHistory.slice(1).map((current, index) => {
39
+ // const previous = serverHistory[index];
40
+ // const timeDiff = current.time - previous.time;
41
+ // const distanceDiff = getLength(current.position, previous.position);
42
+ // // 防止除零
43
+ // return timeDiff > 0 ? (distanceDiff / timeDiff) : 0;
44
+ // });
45
+ // // 计算平均速度
46
+ // const speed = speeds.reduce((a, b) => a + b, 0) / speeds.length;
47
+ const first = serverHistory[0];
48
+ const last = serverHistory.slice(-1)[0];
49
+ const distance = getLength(last.position, first.position);
50
+ const time = last.time - first.time;
51
+ const speed = time > 0 ? distance / time : 0;
52
+ // 使用卡尔曼滤波器平滑速度
53
+ const smoothedSpeed = this.speedFilter.filter(speed);
54
+ this.speed = smoothedSpeed;
55
+ }
56
+ changeAngle(histories) {
57
+ if (histories === void 0) histories = this.visionHistory;
58
+ if (histories.length < 2) {
59
+ this.angle = null;
60
+ }
61
+ ;
62
+ const angle = calculateLineDirection(histories.map((item)=>item.position));
63
+ if (angle !== null) {
64
+ this.setAngle(angle);
65
+ }
9
66
  }
10
- setPosition(position, time, duration) {
11
- this.history.push({
67
+ setAngle(angle, dispatch) {
68
+ if (dispatch === void 0) dispatch = true;
69
+ this.angle = angle;
70
+ if (dispatch) {
71
+ this.dispatchEvent({
72
+ type: "change-position-compass",
73
+ value: angle
74
+ });
75
+ }
76
+ }
77
+ /**
78
+ * 添加视觉结果
79
+ * @param position
80
+ * @param time
81
+ * @param duration
82
+ * @returns
83
+ */ setPosition(position, time, duration) {
84
+ const item = {
12
85
  position,
13
86
  time,
14
87
  clientTime: Date.now() - duration,
15
88
  type: "vision"
16
- });
17
- const MAX_DISTANCE = 5; // 保留最近20米的点
18
- for(let i = 0; i < this.history.length; i++){
19
- const item = this.history[i];
20
- const distance = getLength(item.position, position);
21
- if (distance > MAX_DISTANCE) {
22
- this.history.shift();
23
- i--;
89
+ };
90
+ this._setVisionHistoryForAngle(item);
91
+ if (isNil(this.angle) || !this.speed) {
92
+ this.addHistory(item);
93
+ return;
94
+ }
95
+ const lastIndex = this.history.findLastIndex((item)=>item.type === "vision");
96
+ const last = this.history[lastIndex];
97
+ if (last) {
98
+ if (last.time > time) {
99
+ // 这一桢的时间比本地最后一桢的时间还早,是个已经走过的点,废弃
100
+ return;
101
+ }
102
+ // 如果这个在pdr的点前面,说明pdr滞后了,采用视觉点
103
+ const angle = calculateLineDirection([
104
+ this.history.slice(-1)[0].position,
105
+ position
106
+ ]);
107
+ if (angle && Math.abs(angle - this.angle) < 60) {
108
+ this.addHistory(item);
109
+ return;
110
+ }
111
+ // 判断视觉点连续三次超出最后的点5米,就采用视觉点
112
+ // 预估这个视觉点的正确位置
113
+ const predictPos = predictFuturePosition(position, this.speed, 360 - this.pathAngle, duration);
114
+ const distance = getLength(predictPos, this.history.slice(-1)[0].position);
115
+ // 超过10米就把位置拉过来
116
+ if (distance > 10) {
117
+ this.visionExcessesCount++;
24
118
  } else {
25
- break;
119
+ this.visionExcessesCount = 0;
120
+ }
121
+ if (this.visionExcessesCount > 3) {
122
+ console.warn("连续三次视觉和pdr差距大于5米,使用视觉校准");
123
+ // 采用视觉结果
124
+ this.addHistory(item);
125
+ this.visionExcessesCount = 0;
26
126
  }
27
127
  }
28
- const posInfo = this.getPosition("vision");
29
- this.dispatchEvent({
30
- type: "change-pos",
31
- value: posInfo
32
- });
33
- this.startPositionTimer();
128
+ }
129
+ resetPdrPosition() {
130
+ const lastVision = this.history.findLast((item)=>item.type === "vision");
131
+ if (lastVision && this.speed) {
132
+ // 预估这个视觉点的正确位置
133
+ const predictPos = predictFuturePosition(lastVision.position, this.speed, 360 - this.pathAngle, Date.now() - lastVision.clientTime);
134
+ if (predictPos) {
135
+ this.history.push({
136
+ position: predictPos,
137
+ time: Date.now(),
138
+ clientTime: Date.now(),
139
+ type: "pdr"
140
+ });
141
+ }
142
+ }
143
+ }
144
+ /**
145
+ * 获取 最后一个视觉坐标在加了pdr惯性的坐标
146
+ */ getLastVisionPdrPos() {
147
+ const lastIndex = this.history.findLastIndex((item)=>item.type === "vision");
148
+ if (lastIndex !== -1) {
149
+ var _this_history_;
150
+ return (_this_history_ = this.history[lastIndex + 1]) != null ? _this_history_ : this.history[lastIndex];
151
+ }
152
+ return null;
153
+ }
154
+ _setVisionHistoryForAngle(history) {
155
+ if (!this.visionHistory.length) {
156
+ this.visionHistory.push(history);
157
+ return;
158
+ }
159
+ const angle = calculateLineDirection([
160
+ this.visionHistory.slice(-1)[0].position,
161
+ history.position
162
+ ]);
163
+ if (angle !== null) {
164
+ // console.error("角度差", this.angle, angle, Math.abs(this.angle! - angle))
165
+ if (this.angle && Math.abs(this.angle - angle) > 60) {
166
+ this.angleExcessesCount++;
167
+ } else {
168
+ this.angleExcessesCount = 0;
169
+ this.visionHistory.push(history);
170
+ }
171
+ }
172
+ // 连续三次角度大于90度就修改角度
173
+ if (this.angleExcessesCount > 3) {
174
+ console.warn("连续三次角度比较歪,重新矫正");
175
+ // this.angle = angle!;
176
+ this.visionHistory = [
177
+ history
178
+ ];
179
+ this.angleExcessesCount = 0;
180
+ } else {
181
+ // 保留两米的点
182
+ let distance = getLength(this.visionHistory[0].position, this.visionHistory.slice(-1)[0].position);
183
+ while(distance > 5 && this.visionHistory.length > 15){
184
+ this.visionHistory.shift();
185
+ distance = getLength(this.visionHistory[0].position, this.visionHistory.slice(-1)[0].position);
186
+ }
187
+ }
188
+ // 更新角度
189
+ this.changeAngle();
190
+ }
191
+ addHistory(data) {
192
+ this.history.push(data);
193
+ // 处理history超出
194
+ let visionHistory = this.history.filter((item)=>item.type === 'vision');
195
+ // 最后一个视觉点的时间离当前时间的差值
196
+ const lastVisionTimeDelta = visionHistory.length ? Date.now() - visionHistory.slice(-1)[0].clientTime : 0;
197
+ let maxDistance = getLength(this.history[0].position, this.history.slice(-1)[0].position);
198
+ // 如果有5s内的视觉点,保留3m内的点,如果没有5s内的视觉点,保留10米的蓝牙点
199
+ const MAX_DISTANCE = lastVisionTimeDelta > 5000 ? 10 : 3;
200
+ // 最少保留5个点 但是如果已经5s没有视觉点了就全部抛弃,采用蓝牙点
201
+ const MIN_POSITION_HISTORY = lastVisionTimeDelta > 5000 ? 0 : 5;
202
+ if (lastVisionTimeDelta > 5000) {
203
+ console.warn("视觉结果超出5s不可用,全部清空");
204
+ }
205
+ while(visionHistory.length > MIN_POSITION_HISTORY && maxDistance > MAX_DISTANCE){
206
+ this.history.shift();
207
+ visionHistory = this.history.filter((item)=>item.type === 'vision');
208
+ maxDistance = getLength(this.history[0].position, this.history.slice(-1)[0].position);
209
+ }
210
+ // 如果视觉都失效了用蓝牙更新速度和角度
211
+ if (MIN_POSITION_HISTORY === 0) {
212
+ // this.changeAngle(this.history.filter(item => item.type === "beacon"))
213
+ this.changeSpeed();
214
+ } else if (data.type === "vision") {
215
+ // 如果是视觉点位就更新速度
216
+ this.changeSpeed();
217
+ }
218
+ this.changePosition(data.type);
34
219
  }
35
220
  setBeaconPosition(position, time, duration) {
36
- this.history.push({
221
+ const clientTime = Date.now() - duration;
222
+ const item = {
37
223
  position,
38
224
  time,
39
- clientTime: Date.now() - duration,
225
+ clientTime,
40
226
  type: "beacon"
41
- });
42
- const posInfo = this.getPosition("beacon");
43
- this.dispatchEvent({
44
- type: "change-pos",
45
- value: posInfo
46
- });
47
- this.startPositionTimer();
227
+ };
228
+ const lastServerIndex = this.history.findLastIndex((item)=>item.type !== "pdr");
229
+ const lastServerItem = this.history[lastServerIndex];
230
+ if (!lastServerItem) {
231
+ // 之前没有定位结果采用蓝牙结果
232
+ this.addHistory(item);
233
+ return;
234
+ }
235
+ if (isNil(this.pathAngle) || !this.speed) {
236
+ // 定位结果还不足以支持pdr 采用后端的结果
237
+ this.addHistory(item);
238
+ return;
239
+ }
240
+ // 收到了一个已经过去了的点,弃用
241
+ if (lastServerItem && lastServerItem.time > time) {
242
+ return;
243
+ }
244
+ // 预估这个视觉点的正确位置
245
+ const predictPos = predictFuturePosition(position, this.speed, 360 - this.pathAngle, duration);
246
+ const distance = getLength(predictPos, this.history.slice(-1)[0].position);
247
+ if (distance > 5) {
248
+ this.beaconExcessesCount++;
249
+ } else {
250
+ this.beaconExcessesCount = 0;
251
+ }
252
+ if (this.beaconExcessesCount > 3) {
253
+ console.warn("连续三次beacon和pdr差距大于5米,使用beacon校准");
254
+ // 采用beacon结果
255
+ this.addHistory(item);
256
+ this.beaconExcessesCount = 0;
257
+ }
48
258
  }
49
259
  startPositionTimer() {
50
260
  if (this.positionTimer) {
51
261
  this.timer.clearInterval(this.positionTimer);
52
262
  }
53
- if (this.autoCleanTimer) {
54
- this.timer.clearTimeout(this.autoCleanTimer);
55
- }
56
263
  this.positionTimer = this.timer.setInterval(()=>{
57
- const posInfo = this.getPosition("pdr");
58
- this.dispatchEvent({
59
- type: "change-pos",
60
- value: posInfo
61
- });
264
+ this.changePosition();
62
265
  }, 20);
63
- this.autoCleanTimer = this.timer.setTimeout(()=>{
64
- this.positionTimer && this.timer.clearInterval(this.positionTimer);
65
- this.positionTimer = null;
66
- }, 1000);
266
+ }
267
+ changePosition(from) {
268
+ if (from === void 0) from = 'pdr';
269
+ const posInfo = this.getPosition(from);
270
+ if (posInfo.success) {
271
+ this.history.push({
272
+ position: posInfo.pos,
273
+ time: Date.now(),
274
+ clientTime: Date.now(),
275
+ type: "pdr"
276
+ });
277
+ if (this.speed) {
278
+ // 再往前补2米的车头的距离
279
+ // const headPos = predictFuturePosition(
280
+ // posInfo.pos,
281
+ // this.speed,
282
+ // 360 - this.pathAngle!,
283
+ // 2 / this.speed
284
+ // )
285
+ this.dispatchEvent({
286
+ type: "change-pos",
287
+ value: _extends({}, posInfo)
288
+ });
289
+ } else {
290
+ this.dispatchEvent({
291
+ type: "change-pos",
292
+ value: posInfo
293
+ });
294
+ }
295
+ }
67
296
  }
68
297
  getPosition(from) {
69
298
  if (this.history.length === 0) {
@@ -82,45 +311,34 @@ export class CarInertialPosition extends EventDispatcher {
82
311
  return {
83
312
  success: true,
84
313
  pos: lastHistory.position,
85
- compass: null,
86
- speed: 0
314
+ compass: this.pathAngle,
315
+ speed: this.speed
87
316
  };
88
317
  }
89
- // 计算角度只用视觉的结果
90
- const visionHistory = this.history.filter((item)=>item.type === "vision");
91
- // 推导到当前时间距离最后一个定位点的时间
92
- const angle = calculateLineDirection(visionHistory.map((history)=>history.position));
93
- if (angle !== null) {
94
- this.compassAngle = angle;
95
- this.dispatchEvent({
96
- type: "change-compass",
97
- value: angle
98
- });
99
- // this.compass.setAbsoluteCompass(angle, lastHistory.time);
318
+ if (isNil(this.pathAngle) || !this.speed) {
319
+ return {
320
+ success: true,
321
+ pos: lastHistory.position,
322
+ compass: this.pathAngle,
323
+ speed: this.speed
324
+ };
100
325
  }
101
326
  const lastTime = lastHistory.clientTime;
102
327
  const deltaTime = Date.now() - lastTime;
103
- // 预估后的速度
104
- const speed = predictFutureSpeed(this.history, deltaTime);
105
- // console.log("speed", from, speed, transformSpeed(speed), deltaTime, lastTime)
106
328
  // 根据角度和速度时间计算预估后的位置
107
- const pos = this.compassAngle ? predictFuturePosition(lastHistory.position, speed, 360 - this.compassAngle, deltaTime) : lastHistory.position;
329
+ const pos = this.pathAngle ? predictFuturePosition(lastHistory.position, this.speed, 360 - this.pathAngle, deltaTime) : lastHistory.position;
108
330
  return {
109
331
  success: true,
110
332
  pos,
111
- compass: this.compassAngle,
112
- speed: transformSpeed(speed)
333
+ compass: this.pathAngle,
334
+ speed: transformSpeed(this.speed)
113
335
  };
114
336
  }
115
337
  dispose() {
116
- this.compass.stop();
117
338
  this.timer.dispose();
118
339
  }
119
340
  constructor(){
120
- super(), this.history = [], this.compass = new Compass(), this.compassAngle = 0, this.timer = new Timer(), this.positionTimer = null, this.autoCleanTimer = null;
121
- // this.compass.addEventListener("compass", ({ value }) => {
122
- // this.compassAngle = value;
123
- // this.dispatchEvent({ type: "change-compass", value })
124
- // })
341
+ super(), this.history = [], this.visionHistory = [], this.speed = 0, this.angle = null, this.timer = new Timer(), this.positionTimer = null, this.speedFilter = new KalmanFilter(), this.visionExcessesCount = 0, this.beaconExcessesCount = 0, this.angleExcessesCount = 0, this.pathAngle = 0;
342
+ this.startPositionTimer();
125
343
  }
126
344
  }
@@ -1,4 +1,5 @@
1
1
  import { EventDispatcher } from "three";
2
+ import { KalmanFilter } from "./kalman-filter";
2
3
  interface SensorEventMap {
3
4
  start: {};
4
5
  stop: {};
@@ -15,12 +16,10 @@ interface CompassData {
15
16
  */
16
17
  export declare class Compass extends EventDispatcher<SensorEventMap> {
17
18
  compassData: CompassData[];
18
- absoluteCompass: null | {
19
- time: number;
20
- compass: number;
21
- };
22
- delta: number;
19
+ delta: number | null;
23
20
  deltas: number[];
21
+ kalmanFilter: KalmanFilter;
22
+ deltaExcessesCount: number;
24
23
  constructor();
25
24
  start(): void;
26
25
  deviceOrientationAbsHandler: (e: DeviceOrientationEvent) => void;
@@ -1,8 +1,8 @@
1
- import { _ as _async_to_generator } from "@swc/helpers/_/_async_to_generator";
1
+ import "core-js/modules/es.array.push.js";
2
2
  import { isNil } from "lodash";
3
3
  import { isIphone } from "../../utils";
4
4
  import { EventDispatcher } from "three";
5
- import { removeOutliers } from "./utils";
5
+ import { KalmanFilter } from "./kalman-filter";
6
6
  /**
7
7
  * 监听compass 在获取到视觉定位的角度之后,计算视觉和compass的误差,保存一下这个误差,在后续定位中不断的调整这个误差
8
8
  */ export class Compass extends EventDispatcher {
@@ -17,16 +17,12 @@ import { removeOutliers } from "./utils";
17
17
  * @param compass
18
18
  * @param time
19
19
  */ setAbsoluteCompass(compass, time) {
20
- if (!this.delta) {
20
+ if (isNil(this.delta)) {
21
21
  this.emitCompass(compass);
22
22
  }
23
23
  if (!this.compassData.length) {
24
24
  return;
25
25
  }
26
- this.absoluteCompass = {
27
- compass: compass,
28
- time
29
- };
30
26
  let cutTimeCompassIndex = this.compassData.findIndex((item)=>item.timestamp >= time);
31
27
  if (cutTimeCompassIndex === -1) {
32
28
  cutTimeCompassIndex = this.compassData.length - 1;
@@ -42,18 +38,32 @@ import { removeOutliers } from "./utils";
42
38
  curCompass = nextCompass.timestamp - time > prevCompass.timestamp - time ? prevCompass : nextCompass;
43
39
  }
44
40
  const delta = compass - curCompass.res;
45
- this.deltas.push(delta);
46
- const deltas = removeOutliers(this.deltas);
47
- if (deltas.length) {
48
- this.delta = deltas.reduce((sum, cur)=>sum + cur, 0) / deltas.length;
41
+ const filteredDelta = this.kalmanFilter.filter(delta);
42
+ this.deltas.push(filteredDelta);
43
+ if (isNil(this.delta)) {
44
+ this.delta = filteredDelta;
45
+ } else {
46
+ console.error(Math.abs(delta - this.delta), Math.abs(filteredDelta - this.delta), compass, curCompass.res);
47
+ if (Math.abs(filteredDelta - this.delta) > 10) {
48
+ this.deltaExcessesCount++;
49
+ } else {
50
+ this.deltaExcessesCount = 0;
51
+ this.delta = filteredDelta;
52
+ }
53
+ if (this.deltaExcessesCount > 10) {
54
+ console.warn("连续10次角度变化比较大", this.delta, filteredDelta);
55
+ this.delta = filteredDelta;
56
+ this.deltaExcessesCount = 0;
57
+ }
49
58
  }
50
59
  }
51
60
  emitCompass(compass) {
61
+ var _this_delta;
52
62
  // 把 compass 限制在 0 ~ 360
53
- console.log("compass delta", this.delta, compass);
63
+ const delta = (_this_delta = this.delta) != null ? _this_delta : 0;
54
64
  this.dispatchEvent({
55
65
  type: "compass",
56
- value: (compass + this.delta + 360) % 360
66
+ value: (compass + delta + 360) % 360
57
67
  });
58
68
  }
59
69
  /**
@@ -67,32 +77,27 @@ import { removeOutliers } from "./utils";
67
77
  });
68
78
  }
69
79
  }
70
- checkSensor() {
71
- var _this = this;
72
- return _async_to_generator(function*() {
73
- const deviceOrientation = yield _this.checkDeviceOrientation();
74
- return {
75
- deviceOrientation
76
- };
77
- })();
80
+ async checkSensor() {
81
+ const deviceOrientation = await this.checkDeviceOrientation();
82
+ return {
83
+ deviceOrientation
84
+ };
78
85
  }
79
- checkDeviceOrientation() {
80
- return _async_to_generator(function*() {
81
- var _window_DeviceOrientationEvent;
82
- if (!isIphone) {
83
- return true;
84
- }
85
- if (typeof window.DeviceOrientationEvent !== "undefined" && typeof ((_window_DeviceOrientationEvent = window.DeviceOrientationEvent) == null ? void 0 : _window_DeviceOrientationEvent.requestPermission) === "function") {
86
- try {
87
- var _window_DeviceOrientationEvent1;
88
- const permission = yield (_window_DeviceOrientationEvent1 = window.DeviceOrientationEvent) == null ? void 0 : _window_DeviceOrientationEvent1.requestPermission();
89
- return permission === "granted";
90
- } catch (e) {
91
- return false;
92
- }
86
+ async checkDeviceOrientation() {
87
+ var _window_DeviceOrientationEvent;
88
+ if (!isIphone) {
89
+ return true;
90
+ }
91
+ if (typeof window.DeviceOrientationEvent !== "undefined" && typeof ((_window_DeviceOrientationEvent = window.DeviceOrientationEvent) == null ? void 0 : _window_DeviceOrientationEvent.requestPermission) === "function") {
92
+ try {
93
+ var _window_DeviceOrientationEvent1;
94
+ const permission = await ((_window_DeviceOrientationEvent1 = window.DeviceOrientationEvent) == null ? void 0 : _window_DeviceOrientationEvent1.requestPermission());
95
+ return permission === "granted";
96
+ } catch (e) {
97
+ return false;
93
98
  }
94
- return false;
95
- })();
99
+ }
100
+ return false;
96
101
  }
97
102
  stop() {
98
103
  if (isIphone) {
@@ -108,7 +113,7 @@ import { removeOutliers } from "./utils";
108
113
  });
109
114
  }
110
115
  constructor(){
111
- super(), this.compassData = [], this.absoluteCompass = null, this.delta = 0, this.deltas = [], this.deviceOrientationAbsHandler = (e)=>{
116
+ super(), this.compassData = [], this.delta = null, this.deltas = [], this.kalmanFilter = new KalmanFilter(), this.deltaExcessesCount = 0, this.deviceOrientationAbsHandler = (e)=>{
112
117
  const currTime = Date.now();
113
118
  const { alpha, beta, gamma } = e;
114
119
  if (isNil(alpha) || isNil(beta) || isNil(gamma)) {
@@ -0,0 +1,14 @@
1
+ interface KalmanFilterOptions {
2
+ measurementNoise: number;
3
+ processNoise: number;
4
+ errorEstimate: number;
5
+ }
6
+ export declare class KalmanFilter {
7
+ estimate: number;
8
+ errorEstimate: number;
9
+ processNoise: number;
10
+ measurementNoise: number;
11
+ constructor(options?: Partial<KalmanFilterOptions>);
12
+ filter(measurement: number): number;
13
+ }
14
+ export {};
@@ -0,0 +1,30 @@
1
+ // 卡尔曼滤波器实现
2
+ export class KalmanFilter {
3
+ // 核心滤波方法
4
+ filter(measurement) {
5
+ // 1. 预测阶段
6
+ const prediction = this.estimate;
7
+ const predictionError = this.errorEstimate + this.processNoise;
8
+ // 2. 计算卡尔曼增益
9
+ const kalmanGain = predictionError / (predictionError + this.measurementNoise);
10
+ // 3. 更新估计
11
+ this.estimate = prediction + kalmanGain * (measurement - prediction);
12
+ // 4. 更新误差
13
+ this.errorEstimate = (1 - kalmanGain) * predictionError;
14
+ return this.estimate;
15
+ }
16
+ constructor(options = {}){
17
+ // 当前状态估计
18
+ this.estimate = 0;
19
+ // 估计误差
20
+ this.errorEstimate = 1;
21
+ // 过程噪声(系统内在变化)
22
+ this.processNoise = 0.1;
23
+ // 测量噪声(传感器不确定性)
24
+ this.measurementNoise = 1.0;
25
+ this.measurementNoise = options.measurementNoise || 1.0;
26
+ // 过程噪声(系统内在变化)
27
+ this.processNoise = options.processNoise || 0.1;
28
+ this.errorEstimate = options.errorEstimate || 1;
29
+ }
30
+ }
@@ -17,7 +17,7 @@ export declare function calculateInstantaneousSpeed(positions: CarPosition[]): n
17
17
  * @param timeIntervalMs
18
18
  * @returns
19
19
  */
20
- export declare function predictFutureSpeed(positions: CarPosition[], timeIntervalMs: number): number;
20
+ export declare function predictFutureSpeed(positions: CarPosition[], timeIntervalMs: number): number | null;
21
21
  /**
22
22
  * 转换速度从 m/ms 转成 km/h
23
23
  * @param speed
@@ -1,3 +1,6 @@
1
+ import "core-js/modules/web.dom-collections.iterator.js";
2
+ import "core-js/modules/es.array.push.js";
3
+ import "core-js/modules/es.array.sort.js";
1
4
  import { Vector2 } from "three";
2
5
  import { getLength } from "../../utils";
3
6
  // 计算最小二乘法的斜率 (m) 和截距 (b)
@@ -113,11 +116,11 @@ function getLineEndpoints(m, b, points) {
113
116
  // const n = speeds.length;
114
117
  const first = positions[0];
115
118
  const last = positions.slice(-1)[0];
116
- const distance = positions.slice(1).reduce((sum, cur, index)=>{
117
- const l = getLength(cur.position, positions[index].position);
118
- return sum + l;
119
- }, 0);
119
+ const distance = getLength(last.position, first.position);
120
120
  const time = last.time - first.time;
121
+ if (time < 100 || distance < 3) {
122
+ return null;
123
+ }
121
124
  return distance / time;
122
125
  // if (n === 0) {
123
126
  // return 0; // 如果没有速度数据,返回0
@@ -165,7 +168,7 @@ function getLineEndpoints(m, b, points) {
165
168
  const vY = Math.sin(angleInRadians);
166
169
  const dir = new Vector2(vX, vY);
167
170
  // 一般车头到四轮中间的位置是2米左右
168
- vec.add(dir.normalize().multiplyScalar(distance + 2));
171
+ vec.add(dir.normalize().multiplyScalar(distance));
169
172
  return [
170
173
  vec.x,
171
174
  vec.y