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 做一个平滑过渡。

你想想看,用户正享受着脉冲的敲打感,突然变成连续振动——如果没有过渡,那种突兀感会让人吓一跳。我在早期产品中就犯过这个错,后来被用户吐槽「像触电一样」。

嗯,总结一下这章的核心:

  • 连续振动:固定参数,适合持续按压
  • 脉冲振动:有节奏的敲打,注意起停软过渡
  • 渐变振动:指数曲线更符合人体感知
  • 复合振动:状态机实现,可打断可跳过

下一章咱们会聊振动反馈的实时调参,到时候会用到这些模式做实验。记得把代码跑一遍,感受一下不同参数带来的手感差异。