4. 振动模式设计:连续振动、脉冲振动、渐变振动、复合振动模式算法
好,咱们进入第四章。振动模式设计,说白了就是让按摩仪「怎么动」的问题。你想想看,用户买按摩仪回去,不是要一个只会嗡嗡响的电机,而是想要不同的体感——有人喜欢持续按压,有人喜欢一阵一阵的敲打,还有人喜欢由弱到强的渐进感。
我个人习惯把振动模式分成四大类:连续、脉冲、渐变、复合。这四种模式基本覆盖了市面上 90% 的按摩仪需求。咱们一个一个来拆。
4.1 连续振动模式
连续振动是最基础的。电机一直转,频率和幅度保持不变。我刚开始做嵌入式开发时,觉得这玩意儿太简单了,不就是给个 PWM 固定占空比嘛。但后来发现,连续振动也有讲究——频率的选择直接影响用户体验。
举个例子,低频(比如 30Hz)适合深层肌肉放松,高频(比如 150Hz)适合表层按摩。我建议你在代码里把频率做成可配置的参数,而不是写死。
核心要点:连续振动 = 固定 PWM 占空比 + 固定频率。适合「持续按压」场景。
// 连续振动示例(伪代码)
void continuous_vibration(int duty_cycle, int frequency_hz) {
int period_ms = 1000 / frequency_hz;
while (1) {
set_pwm(duty_cycle); // 开
delay_ms(period_ms); // 保持
// 注意:这里不需要关,因为是连续输出
}
}
嗯,这里要注意一点:连续振动时间长了,电机容易发热。我在项目中遇到过用户连续使用 2 小时,结果电机外壳烫手。所以建议加一个「自动暂停」机制,比如每 30 分钟强制停 5 秒。
4.2 脉冲振动模式
脉冲振动,就是一阵一阵的。像敲背的感觉。这种模式对算法要求不高,但对时序控制要求很细。
脉冲的核心参数有三个:脉冲宽度(on time)、间隔时间(off time)、脉冲次数。我一般用「占空比」来描述脉冲的疏密程度——注意,这里的占空比和 PWM 的占空比是两码事。
| 参数 | 说明 | 推荐范围 |
|---|---|---|
| 脉冲宽度 | 每次振动持续的时间 | 100ms - 2000ms |
| 间隔时间 | 两次振动之间的停顿 | 200ms - 3000ms |
| 脉冲次数 | 一组脉冲包含多少个振动 | 3 - 20 次 |
// 脉冲振动示例
void pulse_vibration(int on_ms, int off_ms, int count) {
for (int i = 0; i < count; i++) {
set_pwm(80); // 80% 占空比,全速振动
delay_ms(on_ms);
set_pwm(0); // 关闭
delay_ms(off_ms);
}
}
我的小技巧:脉冲的「起停」要加软过渡。直接 0% 到 80% 会有冲击感。我习惯在启动时用 50ms 从 0 渐变到目标值,停止时同理。这样手感更柔和。
4.3 渐变振动模式
渐变振动,说白了就是让振动强度像潮水一样涨落。这种模式在放松类按摩仪上特别受欢迎。
渐变算法有两种常见实现:线性渐变和指数渐变。线性渐变好理解,就是占空比匀速变化。指数渐变呢?一开始变化慢,后面变化快,更符合人体感知的「韦伯-费希纳定律」。
我个人更推荐指数渐变。为什么?因为人对振动的感知不是线性的。你从 10% 加到 20%,感觉变化很大;但从 80% 加到 90%,感觉变化很小。指数渐变正好补偿了这种感知差异。
// 指数渐变示例(从 10% 到 90%,用时 3 秒)
void exponential_ramp_up(int duration_ms) {
int steps = 100; // 分成 100 步
int step_time = duration_ms / steps;
for (int i = 0; i <= steps; i++) {
float t = (float)i / steps; // 0.0 ~ 1.0
float duty = 10 + 80 * (t * t); // 指数曲线
set_pwm((int)duty);
delay_ms(step_time);
}
}
我曾经踩过的坑:渐变过程中不要用 delay() 阻塞主循环。否则用户想中途停止都停不了。一定要用定时器或状态机来实现,让渐变过程可以被中断。
4.4 复合振动模式
复合振动,就是把上面几种模式组合起来。比如「先脉冲 10 秒,再渐变 5 秒,最后连续振动 30 秒」。这种模式最接近真实按摩师的手法——先敲打放松,再按压深层。
复合模式的算法设计,核心在于「状态机」。我习惯把每个子模式定义成一个状态,状态之间通过时间或用户输入来切换。
// 复合振动状态机示例
typedef enum {
STATE_PULSE,
STATE_RAMP,
STATE_CONTINUOUS,
STATE_IDLE
} vibration_state_t;
void composite_vibration() {
vibration_state_t state = STATE_PULSE;
int timer = 0;
while (state != STATE_IDLE) {
switch (state) {
case STATE_PULSE:
pulse_vibration(500, 300, 10); // 脉冲 10 次
state = STATE_RAMP;
break;
case STATE_RAMP:
exponential_ramp_up(5000); // 渐变 5 秒
state = STATE_CONTINUOUS;
break;
case STATE_CONTINUOUS:
continuous_vibration(70, 100); // 连续 30 秒
delay_ms(30000);
state = STATE_IDLE;
break;
default:
break;
}
}
}
设计原则:复合模式要「可打断、可跳过」。用户按一下按键,应该能立即切换到下一个模式或停止。千万别让用户干等。
4.5 模式切换与用户体验
最后聊几句用户体验。模式切换时,我建议加一个「淡入淡出」效果。比如从脉冲切换到渐变,中间不要直接跳变,而是用 200ms 做一个平滑过渡。
你想想看,用户正享受着脉冲的敲打感,突然变成连续振动——如果没有过渡,那种突兀感会让人吓一跳。我在早期产品中就犯过这个错,后来被用户吐槽「像触电一样」。
嗯,总结一下这章的核心:
- 连续振动:固定参数,适合持续按压
- 脉冲振动:有节奏的敲打,注意起停软过渡
- 渐变振动:指数曲线更符合人体感知
- 复合振动:状态机实现,可打断可跳过
下一章咱们会聊振动反馈的实时调参,到时候会用到这些模式做实验。记得把代码跑一遍,感受一下不同参数带来的手感差异。