分析振动波形

Android 设备上最常见的振动致动器是线性共振致动器 (LRA)。LRA 可在无响应的玻璃表面上模拟按钮点击的感觉。清晰明快的点击反馈信号通常持续 10 到 20 毫秒。这种感觉让用户互动更加自然。对于虚拟键盘,这种点击反馈可以提高输入速度并减少错误。

LRA 有几个常见的共振频率

  • 有些 LRA 的共振频率在 200 到 300 Hz 范围内,这与人皮肤对振动最敏感的频率相符。此频率范围内的振动感通常被描述为平滑、尖锐和穿透。
  • 其他型号的 LRA 的共振频率较低,约为 150 Hz。这种触感在质量上更柔和、更饱满(在维度上)。
组件包括(从上到下):盖子、板、中间磁铁、2 个侧面磁铁、质量块、2 个弹簧、线圈、柔性电路、底座和粘合剂。 线性共振致动器 (LRA) 的组件。

在两种不同频率下,如果输入电压相同,振动输出幅度可能会有所不同。频次与 LRA 的谐振频率相差越远,其振动幅度就越小。

给定设备的触感效果同时使用振动致动器及其驱动程序。包含过驱动和主动制动功能的触感驱动器可以缩短 LRA 的上升时间和振铃时间,从而实现更灵敏、更清晰的振动。

振动器输出加速度

频次与输出加速度的映射关系 (FOAM) 描述了在给定振动频率(以赫兹为单位)下可实现的最大输出加速度(以 G 峰值为单位)。从 Android 16(API 级别 36)开始,平台通过 VibratorFrequencyProfile 为此映射提供内置支持。您可以将此类与基本高级波封 API 搭配使用,以创建触感反馈效果。

大多数 LRA 电机在其 FOAM 中只有一个峰值,通常接近其共振频率。一般来说,随着频率偏离此范围,加速度会呈指数级下降。该曲线可能不对称,并且在共振频率附近可能呈现平台状,以保护电机免受损坏。

邻近的图显示了 LRA 电机的一个 FOAM 示例。

当频率增加到约 120 Hz 时,加速度呈指数级增加。然后,加速度在 180 Hz 左右保持稳定,之后逐渐减小。
LRA 电机的 FOAM 示例。

人类感知检测阈值

人类感知检测阈值是指人能够可靠检测到的振动的最小加速度。此级别因振动频率而异。

邻近的图显示了人类触觉感知检测阈值(以加速度表示)与时间频率的关系。阈值数据根据 Bolanowski Jr. 等人 2014 年论文(见图 1)中的位移阈值转换而来,韩J. 等人 1988 年发表的文章《Four channels mediate the mechanical aspects of touch》

Android 会在 BasicEnvelopeBuilder 中自动处理此阈值,该阈值用于验证所有效果是否都使用可产生振动幅度的频率范围,且该振动幅度至少比人类感知检测阈值高 10 dB。

当频率增加到大约 20 Hz 时,人类的检测阈值会以对数方式上升到大约 -35 dB。在 200 Hz 左右,阈值保持稳定,之后一直到 -20 dB 呈大致线性增加。
人类触觉感知检测阈值。

在线教程进一步说明了加速度振幅与位移振幅之间的转换

振动加速度级别

人类对振动强度的感知(一种感知度量)不会随着振动幅度(一种物理参数)线性增长。感知强度以感觉级 (SL) 为特征,定义为在相同频率下高于检测阈值的 dB 量。

相应的振动加速度幅度(以 G 峰值表示)可按如下方式计算:

$$ Amplitude(G) = 10^{Amplitude(db)/20} $$

...其中,振幅(分贝)是声源强度 (SL) 和检测阈值的总和,即相邻图中特定频率下的纵轴值。

相邻的图显示了在 10、20、30、40 和 50 dB SL 下的振动加速度水平,以及人类触觉感知检测阈值(0 dB SL),作为时间频率的函数。数据是根据 Verrillo, R. T. 等人 1969 年的文章《振动触觉刺激的感觉强度》。

随着所需感觉级别的增加,所需的加速度(以分贝为单位)大致会增加相同的量。例如,100 Hz 振动的 10 dB 感觉级约为 -20 dB,而不是 -30 dB。
振动加速度级别。

Android 会在 BasicEnvelopeBuilder 中自动处理此转换,该函数以感觉级空间 (dB SL) 中的归一化强度作为值,并将其转换为输出加速度。另一方面,WaveformEnvelopeBuilder 不会应用此转换,而是将值视为加速度空间 (Gs) 中归一化的输出加速度振幅。波封 API 假设,当设计师或开发者考虑振动强度的变化时,他们希望感知到的强度遵循分段线性波封。

设备上的默认波形平滑处理

为便于说明,我们来考虑一下自定义波形模式在通用设备上的行为:

Kotlin

val timings: LongArray = longArrayOf(50, 50, 50, 50, 50, 100, 350, 250)
val amplitudes: IntArray = intArrayOf(77, 79, 84, 99, 143, 255, 0, 255)
val repeatIndex = -1 // Don't repeat.

vibrator.vibrate(VibrationEffect.createWaveform(timings, amplitudes, repeatIndex))

Java

long[] timings = new long[] { 50, 50, 50, 50, 50, 100, 350, 250 };
int[] amplitudes = new int[] { 77, 79, 84, 99, 143, 255, 0, 255 };
int repeatIndex = -1 // Don't repeat.

vibrator.vibrate(VibrationEffect.createWaveform(timings, amplitudes, repeatIndex));

以下图表显示了与上述代码段对应的输入波形和输出加速度。请注意,每当模式中的振幅发生阶跃变化时(即在 0 毫秒、150 毫秒、200 毫秒、250 毫秒和 700 毫秒时),加速度都会逐渐增加,而不是突然增加。在每次振幅阶跃变化时,也会出现过冲;当输入振幅突然降至 0 时,会出现明显的振铃,持续时间至少为 50 毫秒。

阶跃函数输入波形的图。
实际测量的波形图,显示了更多级别之间的自然过渡。

改进了触感模式

为了避免过冲并缩短振铃时间,请更缓慢地更改振幅。以下显示了修订版波形图和加速度图:

Kotlin

val timings: LongArray = longArrayOf(
    25, 25, 50, 25, 25, 25, 25, 25, 25, 25, 75, 25, 25,
    300, 25, 25, 150, 25, 25, 25
)
val amplitudes: IntArray = intArrayOf(
    38, 77, 79, 84, 92, 99, 121, 143, 180, 217, 255, 170, 85,
    0, 85, 170, 255, 170, 85, 0
)
val repeatIndex = -1 // Do not repeat.

vibrator.vibrate(VibrationEffect.createWaveform(timings, amplitudes, repeatIndex))

Java

long[] timings = new long[] {
        25, 25, 50, 25, 25, 25, 25, 25, 25, 25, 75, 25, 25,
        300, 25, 25, 150, 25, 25, 25
    };
int[] amplitudes = new int[] {
        38, 77, 79, 84, 92, 99, 121, 143, 180, 217, 255, 170, 85,
        0, 85, 170, 255, 170, 85, 0
    };
int repeatIndex = -1; // Do not repeat.

vibrator.vibrate(VibrationEffect.createWaveform(timings, amplitudes, repeatIndex));

包含额外步数的输入波形图。
显示平滑过渡的实测波形图。

创建更复杂的触感反馈效果

令人满意的点击响应中的其他元素则更为复杂,需要了解设备中使用的 LRA。为获得最佳效果,请使用设备预先构建的波形和平台提供的常量,这样您便可以执行以下操作:

  • 执行清晰效果和基元
  • 将它们串联起来以组成新的触感效果。

这些预定义的触感常量和基元可以大大加快您创建优质触感效果的速度。