骑行头盔碰撞检测 —— 基于 nRF52840 与 LSM6DS3 的低成本智能安全方案

💡 划重点

  • 主控芯片:nRF52840(支持 BLE 5.0,内置 NFC,Cortex-M4F,性价比高)
  • 核心传感器:LSM6DS3 六轴加速度计/陀螺仪(I2C 接口,低功耗)
  • 执行单元:线性振动马达(PWM 驱动,提供分级触觉反馈)
  • 通信方式:BLE 5.0(连接手机 App,实时上报碰撞事件与位置信息)
  • 目标成本:整机物料低于 140 元人民币,适合小批量众筹或学生创客项目
  • 核心价值:碰撞后 1 秒内触发手机报警,支持手动“我没事”确认,避免误报

需求洞察

目标用户:城市通勤骑行者、公路车运动爱好者、外卖配送骑手。

真实痛点

为什么值得做

方案设计

感知层:LSM6DS3 三轴加速度计+三轴陀螺仪,采样率 416Hz(可配置),通过 I2C 与主控通信。持续采集加速度数据,计算合成矢量幅度(SVM),用于检测瞬时冲击。

控制层:nRF52840 主控运行实时检测算法。使用滑动窗口(窗口长度 200ms)识别冲击峰值,当 SVM 超过阈值(默认 4g)且持续时间大于 5ms,判定为一次碰撞,进入“疑似碰撞状态”。若 3 秒内加速度恢复正常且未检测到二次冲击,则触发确认流程;若存在连续冲击,判定为严重碰撞,直接报警。

执行层:震动马达 PWM 驱动,分三级反馈:

日志/联网层:通过 BLE 5.0 与手机 App(iOS/Android)配对。设备本地缓存最近 50 条事件日志(时间戳+碰撞强度),手机连接后自动同步。App 内置紧急联系人设置、SOS 短信模板、墨迹天气与骑行路线记录(可选)。

物料清单

序号 物料名称 数量 用途 估算单价(元) 小计(元)
1 nRF52840 核心板(如 Arduino Nano 33 BLE Sense 兼容版) 1 主控 45.0 45.0
2 LSM6DS3 六轴传感器模块(I2C 接口) 1 碰撞检测 18.0 18.0
3 线性振动马达(扁平贴片式) 1 震动反馈 6.0 6.0
4 锂电池 3.7V 500mAh 带保护板 1 供电 15.0 15.0
5 TP4056 充电模块 1 充电管理 3.0 3.0
6 3D 打印头盔卡扣外壳(ABS+TPU) 1 结构固定 12.0 12.0
7 轻触按键(6x6x5mm) 2 取消报警/手动测试 1.0 2.0
8 100Ω电阻、0.1uF电容、接插件等 1套 外围电路 5.0 5.0
9 RGB LED(0815 贴片) 1 状态指示 2.0 2.0
10 尼龙扎带、魔术贴、导线 1套 安装辅料 2.0 2.0
11 PCB 打样(5x5cm 四层板) 1 整合所有器件 25.0(均摊10片样板则约15元) 25.0
合计 135.0 元

(总预算 140 元内,预留约 5 元走线/焊接耗材。)

接线与结构

GPIO 分配表(基于 Arduino nRF52840)

微调说明:指定 PWM 引脚 P0.05 在 nRF52840 上支持定时器通道(如 TIMER0 的 CC0 通道),可输出高频 PWM(20kHz)驱动马达,优于普通 GPIO 模拟 PWM(会有明显噪声)。其他引脚均避开 nRF52840 上 USB、NFC 等占用引脚,确保烧录调试无冲突。

物理结构

核心代码

以下为 PlatformIO + Arduino 框架下的可编译代码骨架(针对 nRF52840,使用 Adafruit BSP 或 Arduino Core)。


// 骑行头盔碰撞检测 - 核心骨架
// 需安装库:Adafruit_LSM6DS3, Adafruit Unified Sensor

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_LSM6DS3.h>

// 引脚定义
#define MOTOR_PWM_PIN      (P0_05)   // 指定PWM引脚
#define BTN_CANCEL_PIN     (P0_11)
#define BTN_TEST_PIN       (P0_12)
#define LED_R_PIN          (P0_08)
#define LED_G_PIN          (P0_09)
#define LED_B_PIN          (P0_10)

// 碰撞阈值(单位:g)
#define THRESHOLD_LIGHT    2.5f
#define THRESHOLD_MEDIUM   4.0f
#define THRESHOLD_SEVERE   8.0f

Adafruit_LSM6DS3 lsm;

// 状态机
enum CollisionState {
    IDLE,
    SUSPECT,      // 疑似碰撞,等待确认
    CONFIRMED_LIGHT,
    CONFIRMED_MEDIUM,
    CONFIRMED_SEVERE
};
CollisionState state = IDLE;

uint32_t lastShockTime = 0;
float maxSVM = 0.0f;

void setup() {
    Serial.begin(115200);
    pinMode(LED_R_PIN, OUTPUT);
    pinMode(LED_G_PIN, OUTPUT);
    pinMode(LED_B_PIN, OUTPUT);

    // 初始化 I2C 及传感器
    if (!lsm.begin_I2C(LSM6DS3_ADDRESS)) {
        // 错误指示:红灯常亮
        digitalWrite(LED_R_PIN, HIGH);
        while (1) delay(100);
    }

    // 初始化 LED(绿色闪烁一次表示启动成功)
    digitalWrite(LED_G_PIN, HIGH);
    delay(200);
    digitalWrite(LED_G_PIN, LOW);

    // 定时器 PWM 初始化
    // (具体 PWM 频率/占空比设置由 Board 支持,可简化为 analogWrite)
    analogWrite(MOTOR_PWM_PIN, 0);
}

void loop() {
    lsm.getEvent(&accel;_event, &gyro;_event, nullptr);
    
    // 计算合成矢量幅度 SVM = sqrt(x^2 + y^2 + z^2) - 1g 偏置
    float ax = accel_event.acceleration.x / 9.8f;   // 单位转换为 g
    float ay = accel_event.acceleration.y / 9.8f;
    float az = accel_event.acceleration.z / 9.8f;
    float svm = sqrt(ax*ax + ay*ay + az*az);

    // 刷新最大 SVM
    uint32_t now = millis();
    if (svm > maxSVM) maxSVM = svm;

    // 状态机处理
    switch (state) {
        case IDLE:
            if (svm > THRESHOLD_LIGHT) {
                // 进入疑似碰撞状态,记录时间
                state = SUSPECT;
                lastShockTime = now;
                // 点亮黄色 LED
                digitalWrite(LED_R_PIN, HIGH);
                digitalWrite(LED_G_PIN, HIGH);
            }
            break;

        case SUSPECT:
            // 检测二次冲击(严重碰撞)
            if (svm > THRESHOLD_SEVERE) {
                state = CONFIRMED_SEVERE;
                triggerMotor(255, 3000);   // 全速震动 3 秒
                sendSOS();                // BLE 发送 SOS
            } 
            // 3 秒内无二次冲击,进入中等碰撞判定
            else if (now - lastShockTime > 3000) {
                if (maxSVM >= THRESHOLD_MEDIUM) {
                    state = CONFIRMED_MEDIUM;
                    triggerMotor(180, 1000);   // 中速震动
                    // BLE 通知手机,等待用户取消
                } else if (maxSVM >= THRESHOLD_LIGHT) {
                    state = CONFIRMED_LIGHT;
                    triggerMotor(100, 500);    // 短震动提醒
                }
                state = IDLE;   // 复位
                maxSVM = 0.0f;
            }
            // 取消按键:认定为人为误触(如取下头盔)
            if (digitalRead(BTN_CANCEL_PIN) == LOW) {
                state = IDLE;
                maxSVM = 0.0f;
                digitalWrite(LED_R_PIN, LOW);
                digitalWrite(LED_G_PIN, LOW);
            }
            break;

        default:
            // 冷却 10 秒后回 IDLE
            if (now > lastShockTime + 10000) {
                state = IDLE;
                maxSVM = 0.0f;
                digitalWrite(LED_R_PIN, LOW);
                digitalWrite(LED_G_PIN, LOW);
                analogWrite(MOTOR_PWM_PIN, 0);
            }
            break;
    }
}

// 震动马达控制(PWM 占空比 duty=0-255, 持续时间 ms)
void triggerMotor(uint8_t duty, uint16_t duration) {
    delay(duration);
    analogWrite(MOTOR_PWM_PIN, 0);
}

// BLE SOS 发送(骨架占位,需结合 BLE UART 或自定义服务)
void sendSOS() {
    // 串口输出 SOS 信息(调试用)
    Serial.println("SOS: Collision detected!");
    // 实际项目:通过 BLE 发送带有时间戳与碰撞强度的通知
}

调试步骤

步骤 操作 预期结果
1 编译并烧录固件,打开串口监视器 上电后绿色 LED 闪烁一次,串口打印启动信息
2 水平放置头盔,观察串口打印 SVM 值 静止时 SVM 约为 1.0g(即 1g 重力),波动 < 0.1g
3 用手掌轻拍头盔(约 2.5g 冲击) 红色+绿色 LED 同时亮起,马达短震动 1 次,串口显示碰撞事件
4 用力拍打头盔(>4g) 马达震动 5 次(中速),手机 App 收到通知并可取消
5 模拟严重碰撞(将头盔从 50cm 高处自由落到软垫上) 马达全速震动 3 秒,BLE 发送 SOS 消息
6 按下取消按钮(P0.11) 任何报警状态立即退出,LED 熄灭
7 手动测试按钮(P0.12) 触发一次假碰撞事件,用于演示
8 持续运行 24 小时 电池电压从 4.2V 下降不超过 0.5V,无异常重启

设计图

骑行头盔碰撞检测结构flow
1
感知输入
2
控制处理
3
执行反馈
4
数据上报