心辰·Dev

基于 Arduino 的穿戴式计步器

功能介绍

完成了步态感知系统初步功能的实现,成品能较好地感知人体运动过程中产生的加速度,并将其数值的变化转换为人体移动的步数。在此基础上可以进行跌倒检测、移动距离感知等进一步的开发。

硬件组成

  1. Arduino 开源平台:提供数据处理功能
  2. 六轴加速度传感器 6Dof Shield :提供数据采集功能
  3. Xbee 、蓝牙等无线传输模块:提供终端监控功能(可选)
  4. 电源模块:供电

相关库文件

  1. FreeSixIMU.cpp
    该库文件整合了三轴加速度计 ADXL345 和陀螺仪 ITG3200 所测得的初始数据。常用库函数如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 这三个函数可以使 6Dof Shield 按不同的模式初始化
    void FreeSixIMU::init()
    void FreeSixIMU::init(bool fastmode)
    void FreeSixIMU::init(int acc_addr, int gyro_addr, bool fastmode)

    void FreeSixIMU::getValues(float * values) {
    // 该函数可以在主程序中直接调用以获取加速度的原始值。
    int accval[3];
    acc.readAccel(&accval[0], &accval[1], &accval[2]);
    values[0] = ((float) accval[0]);
    values[1] = ((float) accval[1]);
    values[2] = ((float) accval[2]);

    gyro.readGyro(&values[3]);
    }
  2. FIMU_ADXL345.cpp
    FreeSixIMU.hgetValues(float * values)中使用的acc.readAccel()来自于FIMU_ADXL345.cpp库文件。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    void ADXL345::readAccel(int *x, int *y, int *z) {
    readFrom(ADXL345_DATAX0, TO_READ, _buff); // 从 ADXL345 读取数据

    *x = (((int)_buff[1]) << 8) | _buff[0];
    *y = (((int)_buff[3]) << 8) | _buff[2];
    *z = (((int)_buff[5]) << 8) | _buff[4];
    }

    void ADXL345::readFrom(byte address, int num, byte _buff[]) {
    Wire.beginTransmission(_dev_address); // 开始传送
    Wire.write(address); // 传送的是读取数据的地址
    Wire.endTransmission(); // 结束传送

    Wire.beginTransmission(_dev_address);
    Wire.requestFrom(_dev_address, num); // 从设备请求 6 bytes

    int i = 0;
    while(Wire.available()) {
    // 设备可能发送少于请求的 bytes
    _buff[i] = Wire.read(); // 接收一个 byte
    i++;
    }

    if(i != num){
    status = ADXL345_ERROR;
    error_code = ADXL345_READ_ERROR;
    }
    Wire.endTransmission();
    }
  3. FIMU_ITG3200.cpp
    FreeSixIMU.hgetValues(float * values)中使用的gyro.readGyro()来自于FIMU_ITG3200.cpp库文件。

  4. Wire.cpp
    Wire 库就是针对 I2C 串行通信的应用,使用这个库能够很方便地进行 I2C 通信。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    /* 这三个函数作用是初始化I2C通信,如果不带参数,则作为主机加入到总线当中,如果带参数,则以参数为地址,作为从机加入到总线当中。*/
    void TwoWire::begin(void)
    void TwoWire::begin(uint8_t address)
    void TwoWire::begin(int address)

    // 开始发送数据给以参数为地址的从机。
    void TwoWire::beginTransmission(uint8_t address)
    void TwoWire::beginTransmission(int address)

    // 发送数据结束。
    uint8_t TwoWire::endTransmission(uint8_t sendStop)
    uint8_t TwoWire::endTransmission(void)

    // 作为主机向从机请求数据,其中参数1为从机地址,参数2为请求的数据大小。
    uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop)
    uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity)
    uint8_t TwoWire::requestFrom(int address, int quantity)
    uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop)

    // 通过I2C发送一个数据或一串数据。
    size_t TwoWire::write(uint8_t data)
    size_t TwoWire::write(const uint8_t *data, size_t quantity)

    int TwoWire::available(void) // 返回接收数据的个数
    int TwoWire::read(void) // 接受从机返回的数据

取得原始数据的示例程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <FreeSixIMU.h>
#include <FIMU_ADXL345.h>
#include <FIMU_ITG3200.h>

#include <Wire.h>

// 初始化 FreeSixIMU 实例
FreeSixIMU sixDOF = FreeSixIMU();
int rawSixDof[6];
void setup() {
Serial.begin(9600);
Wire.begin();

delay(5);
sixDOF.init();
delay(5);
}

void loop() {

sixDOF.getRawValues(rawSixDof);
for (int i=0; i<6; i++) {
// 输出原始数据

switch (i) {
case 0:
Serial.print("Acc.x :");
break;
case 1:
Serial.print("Acc.y :");
break;
case 2:
Serial.print("Acc.z :");
break;
case 3:
Serial.print("gyro.x :");
break;
case 4:
Serial.print("gyro.y :");
break;
case 5:
Serial.print("gyro.z :");
break;
default:
Serial.print("Err");
}
Serial.println(rawSixDof[i]);
}

delay(1000);
}

核心数据处理程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <FreeSixIMU.h>
#include <FIMU_ADXL345.h>
#include <FIMU_ITG3200.h>
#include <Wire.h>

#define FILTER_N 12

FreeSixIMU sixDOF = FreeSixIMU();
int rawSixDof[6];
float oldAccx, oldAccy, oldAccz, oldDot;
int stepNumber = 0;

void setup() {
Serial.begin(9600);
Wire.begin();

delay(5);
sixDOF.init();
delay(5);
}

void loop() {
float accx = Filter(0); // 读取滤波后的加速度 x 分量
float accy = Filter(1);
float accz = Filter(2);
float dot = (oldAccx * accx)+(oldAccy * accy)+(oldAccz * accz);
float oldAcc = abs(sqrt(oldAccx * oldAccx + oldAccy * oldAccy + oldAccz * oldAccz));
float newAcc = abs(sqrt(accx * accx + accy * accy + accz * accz));
dot /= (oldAcc * newAcc); // 计算加速度变化程度

if(abs(dot - oldDot) >= 0.05) {
// 变化程度超过阈值,则判定步数增加
// 并打印
stepNumber += 1;
Serial.println(stepNumber);
}

oldAccx = accx;
oldAccy = accy;
oldAccz = accz;
oldDot = dot;

delay(100);
}

int filterBuf[FILTER_N + 1];

float Filter(int s) {
// 平均滤波法
float filterSum = 0.0;

for(int i = 0; i < FILTER_N; i++) {
sixDOF.getRawValues(rawSixDof);
filterBuf[i] = rawSixDof[s];
filterSum += filterBuf[i];
delay(5);
}
return (float)(filterSum / FILTER_N);
}