@ziztechnology/dial-library 0.0.9 → 0.0.11

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.
package/README.md CHANGED
@@ -166,31 +166,34 @@ type SensorMetric<T> =
166
166
  | `batteryCapacity` | `{ levelPercent, chargeCounterUah }` | 无 |
167
167
  | `battery` | 电池是否存在、充电状态、健康度和电压等 | 无 |
168
168
 
169
- 如果只需要根据单次快照做无状态判断,可以使用纯函数 `classifyDrivingSnapshot()`:
169
+ 如果需要检查单次快照的车辆坐标投影和瞬时候选,可以使用 `classifyDrivingSnapshot()`:
170
170
 
171
171
  ```ts
172
172
  import { classifyDrivingSnapshot, unifiedSensorInfo } from '@ziztechnology/dial-library';
173
173
 
174
- const status = classifyDrivingSnapshot(await unifiedSensorInfo());
174
+ const analysis = classifyDrivingSnapshot(await unifiedSensorInfo());
175
175
 
176
- if (status === null) {
176
+ if (analysis === null) {
177
177
  console.log('传感器数据不可用、非法或已经过期');
178
178
  } else {
179
- console.log('当前瞬时状态:', status);
179
+ console.log('纵向加速度:', analysis.metrics.longitudinal);
180
+ console.log('偏航角速度:', analysis.metrics.yaw);
181
+ console.log('瞬时候选:', analysis.candidateStatus);
182
+ console.log('拒绝原因:', analysis.rejectionReason);
180
183
  }
181
184
  ```
182
185
 
183
- `checkRunningStatus()` 是同一函数的兼容别名。单次分类不会去抖,也不会保存上一次状态;它使用 Runtime 的 `motionIntensity >= 0.4 m/s²` 判断瞬时行驶。面向 UI 时,通常应该使用下一节带 accelerometer 滑窗的状态控制器。
186
+ 该函数不返回最终的 `STOPPED` `STEADY_DRIVING`,也不保存时间窗口。`candidateStatus` 只能用于诊断,面向 UI 必须使用下一节的状态控制器。
184
187
 
185
- 设备的固定安装方向为:
188
+ 设备的正反面和车头朝向需要固定,但允许安装倾角变化。SDK 对重力向量做低通滤波,并动态建立车辆坐标系:
186
189
 
187
190
  ```text
188
- +X = 车身右侧
189
- +Y = 上方
190
- -Z = 车辆前进方向
191
+ right = 将设备 +X 投影到水平面
192
+ up = 归一化后的 TYPE_GRAVITY
193
+ forward = right × up
191
194
  ```
192
195
 
193
- 因此纵向加速度取 `-linearAcceleration.z`,横向加速度取 `linearAcceleration.x`,偏航角速度取 `gyroscope.y`。
196
+ 线性加速度和陀螺仪随后投影到该坐标系。标准化后的正偏航表示左转,负偏航表示右转,因此同一安装朝向下的俯仰角偏差不会改变前后、左右和转向符号。
194
197
 
195
198
  ## 行车状态基本使用
196
199
 
@@ -220,9 +223,11 @@ unsubscribe();
220
223
  controller.destroy();
221
224
  ```
222
225
 
223
- 控制器默认每 200ms 读取一次,并保证上一次读取结束后才开始下一次。它会对普通动作平滑、要求候选状态连续出现、设置状态最短展示时间,并允许急刹车等高优先级状态抢占。候选状态不会触发 UI,只有提交后的稳定状态才会通知订阅者。
226
+ 控制器默认每 200ms 读取一次,并保证上一次读取结束后才开始下一次。所有确认都使用传感器时间而不是轮询次数;相同 `sampledAtMs` 的重复数据不会推进窗口。
224
227
 
225
- 静止和匀速使用 accelerometer 的 1 秒三轴波动窗口判断。窗口按 `sampledAtMs` 去重,至少积累 5 个有效样本;总体标准差大于 `0.4 m/s²` 时进入 `STEADY_DRIVING`,小于 `0.25 m/s²` 时进入 `STOPPED`,中间区间保持原基础状态。滑窗预热不影响加速、刹车或转弯识别。accelerometer 不可用或过期时,整次行车快照进入不可用流程。
228
+ 静止和匀速使用 accelerometer 的 1 秒三轴 MAD 稳健波动窗口,至少需要 5 个唯一样本。运动量不高于 `0.12 m/s²` 且陀螺仪足够稳定,连续 5 秒才进入 `STOPPED`;运动量达到 `0.18 m/s²` 或陀螺仪出现持续运动,连续 0.6 秒进入 `STEADY_DRIVING`。中间区间保持原基础状态。
229
+
230
+ 普通加速和刹车要求 0.8 秒内至少 4/5 的样本方向一致;急加速和急刹要求峰值、0.6 秒持续均值及方向一致性同时成立,并拒绝垂直能量占主导或短时间反号的颠簸。转向由偏航持续值或累计转角触发并锁存到回正结束。最终优先级为:急加减速、已锁存转向、普通加减速、基础状态。
226
231
 
227
232
  ### 读取当前状态
228
233
 
@@ -233,9 +238,11 @@ const snapshot = controller.getSnapshot();
233
238
 
234
239
  console.log(snapshot.status); // 已提交状态
235
240
  console.log(snapshot.candidate); // 正在确认的候选状态,可能为 null
236
- console.log(snapshot.candidateConfirmationCount);
241
+ console.log(snapshot.candidateSinceMs); // 候选首次出现的传感器时间
242
+ console.log(snapshot.activeTurn); // 已锁存的转向事件
243
+ console.log(snapshot.baseStatus); // STOPPED 或 STEADY_DRIVING
237
244
  console.log(snapshot.lifecycle); // stopped、running 或 destroyed
238
- console.log(snapshot.tuningVersion); // driving-status-v1
245
+ console.log(snapshot.tuningVersion);
239
246
  ```
240
247
 
241
248
  ### 调整识别参数
@@ -245,22 +252,20 @@ console.log(snapshot.tuningVersion); // driving-status-v1
245
252
  ```ts
246
253
  const controller = createDrivingStatusController({
247
254
  tuning: {
248
- turnYawThreshold: 0.6,
255
+ turnYawThreshold: 0.13,
249
256
  motionWindowMs: 1_200,
250
257
  motionMinimumSamples: 6,
251
- drivingMotionThreshold: 0.45,
252
- stoppedMotionThreshold: 0.28,
253
- confirmationSamples: {
254
- LEFT_TURN: 3,
255
- RIGHT_TURN: 3,
256
- },
258
+ drivingMotionThreshold: 0.2,
259
+ stoppedMotionThreshold: 0.11,
260
+ stoppedConfirmationMs: 5_000,
261
+ longitudinalThreshold: 0.75,
257
262
  },
258
263
  });
259
264
  ```
260
265
 
261
- `drivingMotionThreshold` 必须大于 `stoppedMotionThreshold`,形成进入行驶和退出行驶的迟滞区间。`motionMinimumSamples` 必须是正整数,其余窗口和阈值必须是正有限数值。
266
+ `drivingMotionThreshold` 必须大于 `stoppedMotionThreshold`,进入阈值也必须大于相应退出阈值。样本数必须是正整数,方向一致率必须大于 `0.5` 且不大于 `1`,其余窗口和阈值必须是正有限数值。
262
267
 
263
- 固定安装的惯性传感器无法严格区分“完全静止”和“理想匀速直线运动”。默认算法使用三轴加速度波动和陀螺仪扰动近似判断,修改阈值后应在真机上路测。
268
+ 惯性传感器无法严格区分“完全静止”和“没有任何振动的理想匀速直线运动”。默认算法使用稳健振动统计、陀螺仪扰动和 5 秒确认近似判断,修改阈值后应使用完整路测过程回放验证。
264
269
 
265
270
  ### 驱动行车表情
266
271
 
package/dist/index.d.mts CHANGED
@@ -69,38 +69,86 @@ declare const unifiedSensorInfo: () => Promise<UnifiedSensorInfoPayload>;
69
69
  interface DrivingStatusTuning {
70
70
  version: string;
71
71
  maxSampleAgeMs: number;
72
- rapidLongitudinalThreshold: number;
73
- longitudinalThreshold: number;
74
- turnYawThreshold: number;
75
- turnLateralThreshold: number;
72
+ gravityFilterTimeConstantMs: number;
73
+ biasFilterTimeConstantMs: number;
74
+ vehicleRightMinimumNorm: number;
76
75
  motionWindowMs: number;
77
76
  motionMinimumSamples: number;
78
- drivingMotionThreshold: number;
79
77
  stoppedMotionThreshold: number;
78
+ drivingMotionThreshold: number;
80
79
  stoppedGyroThreshold: number;
81
- hysteresisRatio: number;
82
- smoothingWindowSize: number;
83
- confirmationSamples: Readonly<Record<CarRunningStatus, number>>;
84
- minimumDisplayMs: Readonly<Record<CarRunningStatus, number>>;
85
- }
86
- type DrivingStatusTuningOverrides = Partial<Omit<DrivingStatusTuning, 'confirmationSamples' | 'minimumDisplayMs'>> & {
87
- confirmationSamples?: Partial<Record<CarRunningStatus, number>>;
88
- minimumDisplayMs?: Partial<Record<CarRunningStatus, number>>;
89
- };
80
+ stoppedRawGyroThreshold: number;
81
+ drivingGyroThreshold: number;
82
+ drivingRawGyroThreshold: number;
83
+ stoppedConfirmationMs: number;
84
+ drivingConfirmationMs: number;
85
+ longitudinalWindowMs: number;
86
+ longitudinalMinimumSamples: number;
87
+ longitudinalThreshold: number;
88
+ longitudinalExitThreshold: number;
89
+ longitudinalSignCoherence: number;
90
+ longitudinalReleaseMs: number;
91
+ rapidLongitudinalWindowMs: number;
92
+ rapidLongitudinalMinimumSamples: number;
93
+ rapidLongitudinalPeakThreshold: number;
94
+ rapidLongitudinalMeanThreshold: number;
95
+ rapidLongitudinalSignCoherence: number;
96
+ rapidMinimumDisplayMs: number;
97
+ verticalBumpRatio: number;
98
+ longitudinalReversalWindowMs: number;
99
+ turnWindowMs: number;
100
+ turnMinimumSamples: number;
101
+ turnYawThreshold: number;
102
+ turnConfirmationMs: number;
103
+ turnIntegratedAngleThreshold: number;
104
+ turnSignCoherence: number;
105
+ turnExitYawThreshold: number;
106
+ turnExitConfirmationMs: number;
107
+ turnClosureWindowMs: number;
108
+ }
109
+ type DrivingStatusTuningOverrides = Partial<DrivingStatusTuning>;
90
110
  declare const DEFAULT_DRIVING_STATUS_TUNING: Readonly<DrivingStatusTuning>;
111
+ interface DrivingVehicleFrame {
112
+ up: Vector3Value;
113
+ right: Vector3Value;
114
+ forward: Vector3Value;
115
+ }
116
+ interface DrivingMetricsCorrection {
117
+ filteredGravity?: Vector3Value;
118
+ linearAccelerationBias?: Vector3Value;
119
+ gyroscopeBias?: Vector3Value;
120
+ }
91
121
  interface DrivingSnapshotMetrics {
92
122
  capturedAtMs: number;
123
+ linearAccelerationSampledAtMs: number;
124
+ gyroscopeSampledAtMs: number;
125
+ gravitySampledAtMs: number;
93
126
  longitudinal: number;
94
127
  lateral: number;
128
+ vertical: number;
95
129
  yaw: number;
96
- motionIntensity: number;
97
130
  gyroMagnitude: number;
131
+ rawGyroMagnitude: number;
132
+ rawLinearAcceleration: Vector3Value;
133
+ rawGyroscope: Vector3Value;
134
+ frame: DrivingVehicleFrame;
98
135
  }
99
- declare const classifyDrivingSnapshot: (info: UnifiedSensorInfoPayload, tuningOverrides?: DrivingStatusTuningOverrides) => CarRunningStatus | null;
100
- declare const checkRunningStatus: (info: UnifiedSensorInfoPayload, tuningOverrides?: DrivingStatusTuningOverrides) => CarRunningStatus | null;
136
+ type DrivingSnapshotCandidate = Exclude<CarRunningStatus, 'STOPPED' | 'STEADY_DRIVING'>;
137
+ type DrivingSnapshotRejectionReason = 'vertical_bump' | null;
138
+ interface DrivingSnapshotAnalysis {
139
+ capturedAtMs: number;
140
+ metrics: DrivingSnapshotMetrics;
141
+ candidateStatus: DrivingSnapshotCandidate | null;
142
+ rejectionReason: DrivingSnapshotRejectionReason;
143
+ quality: {
144
+ frameValid: true;
145
+ maximumSampleAgeMs: number;
146
+ };
147
+ }
148
+ declare const classifyDrivingSnapshot: (info: UnifiedSensorInfoPayload, tuningOverrides?: DrivingStatusTuningOverrides) => DrivingSnapshotAnalysis | null;
101
149
  declare const resolveDrivingStatusTuning: (overrides?: DrivingStatusTuningOverrides) => DrivingStatusTuning;
102
- declare const extractDrivingSnapshotMetrics: (info: UnifiedSensorInfoPayload, tuning?: Pick<DrivingStatusTuning, "maxSampleAgeMs">) => DrivingSnapshotMetrics | null;
103
- declare const classifyDrivingMetrics: (metrics: DrivingSnapshotMetrics, tuning?: DrivingStatusTuning) => CarRunningStatus;
150
+ declare const extractDrivingSnapshotMetrics: (info: UnifiedSensorInfoPayload, tuning?: Pick<DrivingStatusTuning, "maxSampleAgeMs" | "vehicleRightMinimumNorm">, correction?: DrivingMetricsCorrection) => DrivingSnapshotMetrics | null;
151
+ declare const buildVehicleFrame: (gravity: Vector3Value, rightMinimumNorm?: number) => DrivingVehicleFrame | null;
104
152
  //#endregion
105
153
  //#region src/sensor/driving_expressions.d.ts
106
154
  interface MediaLibraryRef {
@@ -210,6 +258,16 @@ type DrivingStatusDiagnosticEvent = {
210
258
  unavailableForMs: number;
211
259
  category: 'provider_error' | 'invalid_or_stale_snapshot';
212
260
  tuningVersion: string;
261
+ } | {
262
+ type: 'detector_evidence';
263
+ atMs: number;
264
+ motionMad: number | null;
265
+ baseCondition: 'warming_up' | 'stationary' | 'moving' | 'hysteresis';
266
+ rapidCandidate: 'RAPID_ACCELERATION' | 'SUDDEN_BRAKING' | null;
267
+ longitudinalCandidate: 'ACCELERATION' | 'BRAKING' | null;
268
+ turnCandidate: 'LEFT_TURN' | 'RIGHT_TURN' | null;
269
+ rejectionReason: 'vertical_bump' | 'longitudinal_reversal' | null;
270
+ tuningVersion: string;
213
271
  } | {
214
272
  type: 'candidate_changed';
215
273
  atMs: number;
@@ -223,13 +281,6 @@ type DrivingStatusDiagnosticEvent = {
223
281
  previousStatus: CarRunningStatus;
224
282
  reason: DrivingStatusChangeReason;
225
283
  tuningVersion: string;
226
- } | {
227
- type: 'status_held_by_min_duration';
228
- atMs: number;
229
- status: CarRunningStatus;
230
- candidate: CarRunningStatus;
231
- remainingMs: number;
232
- tuningVersion: string;
233
284
  } | {
234
285
  type: 'fallback_to_stopped';
235
286
  atMs: number;
@@ -239,8 +290,10 @@ type DrivingStatusDiagnosticEvent = {
239
290
  interface DrivingControllerSnapshot {
240
291
  lifecycle: 'stopped' | 'running' | 'destroyed';
241
292
  status: CarRunningStatus;
293
+ baseStatus: 'STOPPED' | 'STEADY_DRIVING';
294
+ activeTurn: 'LEFT_TURN' | 'RIGHT_TURN' | null;
242
295
  candidate: CarRunningStatus | null;
243
- candidateConfirmationCount: number;
296
+ candidateSinceMs: number | null;
244
297
  statusCommittedAtMs: number;
245
298
  lastAvailableAtMs: number | null;
246
299
  unavailableSinceMs: number | null;
@@ -320,4 +373,4 @@ interface DrivingExpressionPlayer {
320
373
  }
321
374
  declare const createDrivingExpressionPlayer: (container: HTMLElement, options: DrivingExpressionPlayerOptions) => DrivingExpressionPlayer;
322
375
  //#endregion
323
- export { AvailableSensorMetric, BATTERY_HEALTH_LABELS, BatteryCapacityValue, BatteryHealth, BatteryPlugged, BatteryStateValue, BatteryStatus, CAR_RUNNING_LABELS, CAR_RUNNING_STATUSES, CAR_RUNNING_STATUS_PRIORITY, CarRunningStatus, CompleteDrivingExpressionsConfig, DEFAULT_DRIVING_STATUS_TUNING, DrivingControllerSnapshot, DrivingExpressionMedia, DrivingExpressionPlayer, DrivingExpressionPlayerDiagnosticEvent, DrivingExpressionPlayerErrorCategory, DrivingExpressionPlayerOptions, DrivingExpressionPlayerSnapshot, DrivingExpressionResolutionError, DrivingExpressionsConfig, DrivingExpressionsConfigError, DrivingExpressionsConfigErrorCode, DrivingExpressionsConfigInput, DrivingSensorProvider, DrivingSnapshotMetrics, DrivingStatusChangeReason, DrivingStatusClock, DrivingStatusController, DrivingStatusControllerOptions, DrivingStatusDiagnosticEvent, DrivingStatusEvent, DrivingStatusTuning, DrivingStatusTuningOverrides, EmojiDrivingExpressionMedia, ImageDrivingExpressionMedia, LivePhotoDrivingExpressionMedia, MediaLibraryRef, OFFICIAL_DRIVING_EXPRESSIONS_CONFIG, OFFICIAL_DRIVING_EXPRESSION_FALLBACKS, OrientationValue, ParseDrivingExpressionsOptions, StructuredDrivingExpressionMedia, TgsDrivingExpressionMedia, UnavailableSensorMetric, UnifiedSensorInfoPayload, UnifiedSensorMetric, UnifiedSensorUnavailableReason, Vector3Value, VideoDrivingExpressionMedia, WaitForDrivingExpressionsConfigOptions, checkRunningStatus, classifyDrivingMetrics, classifyDrivingSnapshot, createDrivingExpressionPlayer, createDrivingStatusController, extractDrivingSnapshotMetrics, isPublicDrivingMediaUrl, parseDrivingExpressionsConfig, readDrivingExpressionsConfig, resolveDrivingExpression, resolveDrivingExpressionStrict, resolveDrivingStatusTuning, tryParseDrivingExpressionsConfig, unifiedSensorInfo, waitForDrivingExpressionsConfig };
376
+ export { AvailableSensorMetric, BATTERY_HEALTH_LABELS, BatteryCapacityValue, BatteryHealth, BatteryPlugged, BatteryStateValue, BatteryStatus, CAR_RUNNING_LABELS, CAR_RUNNING_STATUSES, CAR_RUNNING_STATUS_PRIORITY, CarRunningStatus, CompleteDrivingExpressionsConfig, DEFAULT_DRIVING_STATUS_TUNING, DrivingControllerSnapshot, DrivingExpressionMedia, DrivingExpressionPlayer, DrivingExpressionPlayerDiagnosticEvent, DrivingExpressionPlayerErrorCategory, DrivingExpressionPlayerOptions, DrivingExpressionPlayerSnapshot, DrivingExpressionResolutionError, DrivingExpressionsConfig, DrivingExpressionsConfigError, DrivingExpressionsConfigErrorCode, DrivingExpressionsConfigInput, DrivingMetricsCorrection, DrivingSensorProvider, DrivingSnapshotAnalysis, DrivingSnapshotCandidate, DrivingSnapshotMetrics, DrivingSnapshotRejectionReason, DrivingStatusChangeReason, DrivingStatusClock, DrivingStatusController, DrivingStatusControllerOptions, DrivingStatusDiagnosticEvent, DrivingStatusEvent, DrivingStatusTuning, DrivingStatusTuningOverrides, DrivingVehicleFrame, EmojiDrivingExpressionMedia, ImageDrivingExpressionMedia, LivePhotoDrivingExpressionMedia, MediaLibraryRef, OFFICIAL_DRIVING_EXPRESSIONS_CONFIG, OFFICIAL_DRIVING_EXPRESSION_FALLBACKS, OrientationValue, ParseDrivingExpressionsOptions, StructuredDrivingExpressionMedia, TgsDrivingExpressionMedia, UnavailableSensorMetric, UnifiedSensorInfoPayload, UnifiedSensorMetric, UnifiedSensorUnavailableReason, Vector3Value, VideoDrivingExpressionMedia, WaitForDrivingExpressionsConfigOptions, buildVehicleFrame, classifyDrivingSnapshot, createDrivingExpressionPlayer, createDrivingStatusController, extractDrivingSnapshotMetrics, isPublicDrivingMediaUrl, parseDrivingExpressionsConfig, readDrivingExpressionsConfig, resolveDrivingExpression, resolveDrivingExpressionStrict, resolveDrivingStatusTuning, tryParseDrivingExpressionsConfig, unifiedSensorInfo, waitForDrivingExpressionsConfig };