[Day 23] 让tinyML感受你的律动

话说为了要作出吃了会考试100分、会变聪明、变漂亮的「爆浆濑尿虾牛丸」,我特别找来庙街最重情义的火鸡姐来帮忙。火鸡姐腕力惊人,平均每块牛肉需要二万六千八百多次不停敲打才能打出这麽好的牛丸。「城市睇真D」还特别派了记者来专访这个好吃、新奇又好玩,弹力十足到可以拿来打乒乓球的产品,结果她竟然问了一句,你如何证明这个牛肉真的被打了二万六千八百多次而不是一万六千八百多次呢?为了证明我所言不虚,於是我拿出了人称tinyML神器的「Arduino Nano 33 BLE Sense(以下简称BLE Sense)」(如图Fig. 23-1),用它上面的LSM9DS1九轴运动感测器(以下简称IMU)来计算火鸡姐双手挥动的次数和分析她精准的打击动作,才平息了这次抹黑的消息。

Arduino Nano 33 BLE Sense LMS9DS1九轴运动感测器(IMU)
Fig. 23-1 Arduino Nano 33 BLE Sense LMS9DS1九轴运动感测器(IMU)。(OmniXRI整理绘制, 2021/10/8)

什麽是IMU呢?全名为Inertial Measurement Unit,惯性量测单元,俗称运动感测器(Motion Sensor)。十多年前在微机电(MEMS)制程技术的帮助下,开始大量微形化,因此而大行其道,常见於各种车辆安全气囊、行动装置的掉落侦测、体感游戏机摇杆(如NDS Wii, PS Move等)、运动手环等装置上。IMU主要包括下列三种主要元件。

  • 加速度计(Accelerometer),又称重力计,又因重力单位为G,所以也常简称为G Sensor。
  • 陀螺仪(Gyroscope),又称角加速度计,主要用於测各轴向旋转速度变化,常被简称Gyro Sensor。
  • 地磁计(Magnetometer),主要用於量测地磁角度,相当於指南针作用,因此也常被称为电子罗盘。

刚开始三种元件多为独立产品,後来因不同应用需求、体积大小及价格考量,慢慢发展出整合两项或三项元件的产品。通常每种元件多半可以侦测XYZ三个轴向,所以常被简称三轴、六轴(G + Gyro)、九轴(G + Gyro + Magneto)运动感测器。後来又因卫星导航(GPS)常遇到高架桥、隧道等遮蔽时无法精准定位,於是就有人整合九轴资讯开发出惯性导航算法,可用於室内(或无GPS信号)导航使用,因此九轴运动感测器也被称为惯性量测元件(IMU)。

由上述介绍可知,想知道运动方向、速度就得搭配这些元件。所以BLE Sense上也搭载了ST LSM9DS1 IMU感测器,其主要规格如下所示。更完整内容可参考官方提供的技术手册

  • 3 acceleration channels, 3 angular rate channels, 3 magnetic field channels
  • ±2/±4/±8/±16 g linear acceleration full scale (多选一)
  • ±4/±8/±12/±16 gauss magnetic full scale (多选一)
  • ±245/±500/±2000 dps angular rate full scale (多选一)
  • 16-bit data output
  • SPI / I2C serial interfaces
  • Analog supply voltage 1.9 V to 3.6 V
  • “Always-On" eco power mode down to 1.9mA

在先前[Day 09] tinyML开胃菜Arduino IDE上桌(下)已经介绍过如何安装BLE Sense这块开发板的基本函式库,透过「Arduino_LSM9DS1」就可轻松读取三种感测元件的数值。接下来就简单介绍其本用法。

/*
  Arduino LSM9DS1 基本范例
  可由主选单[档案]-[范例]-[Arduino_LSM9DS1]下的三个范例产生
  Simple Accelerometer (加速度计)
  Simple Gyroscope (陀螺仪)
  Simple Magnetometer (地磁计)
  三个范例大同小异,以下以加速计为例,另两种则以注解方式补充在关键行数下方
*/

#include <Arduino_LSM9DS1.h> // 导入Arduino_LSM9DS1函式库头文件

// 设定脚位用途及模组初始化(只在电源启动或重置时执行一次)
void setup() {
  Serial.begin(9600); // 设定序列埠通讯速度为9,600bps
  while (!Serial);
  Serial.println("Started");

  if (!IMU.begin()) { // 初始化IMU
    Serial.println("Failed to initialize IMU!");
    while (1);
  }

  Serial.print("Accelerometer sample rate = "); // 加速度计用
  // Serial.print("Gyroscope sample rate = "); // 陀螺仪用
  // Serial.print("Magnetic field sample rate = "); // 地磁计用
  
  Serial.print(IMU.accelerationSampleRate()); // 加速度计用,设定取样速度
  // Serial.print(IMU.gyroscopeSampleRate()); // 陀螺仪用,设定取样速度
  // Serial.print(IMU.magneticFieldSampleRate()); // 地磁计用,设定取样速度
  
  Serial.println(" Hz"); // 加速度计用
  // Serial.println(" Hz"); // 陀螺仪用
  // Serial.println(" uT"); // 地磁计用
  
  Serial.println(); // 输出空白行
  
  Serial.println("Acceleration in G's"); // 加速度计用
  // Serial.println("Gyroscope in degrees/second"); // 陀螺仪用
  // Serial.println("Magnetic Field in uT"); // 地磁计用
  
  Serial.println("X\tY\tZ"); // 输出X Y Z 字串
}

// 设定无穷循环程序 (会一直依序重覆执行)
void loop() {
  float x, y, z; // 存放三轴值
  
  // 加速度计用,若加速度计备妥则读入三轴加速度(重力)值
  if (IMU.accelerationAvailable()) {
    IMU.readAcceleration(x, y, z);
  
  // 陀螺仪用,若陀螺仪备妥则读入三轴角加速值
  // if (IMU.gyroscopeAvailable()) {  
  //   IMU.readGyroscope(x, y, z);
  
  // 地磁计用,若地磁计备妥则读入三轴地磁值
  // if (IMU.magneticFieldAvailable()) {
  //   IMU.readMagneticField(x, y, z);
  
    // 透过序列埠将三轴数值送到电脑,可使用[工具]下的「序列埠监控视窗」观看数值,
    // 或透过「序列绘图家」直接观看三轴数值变化曲线图,更为方便。
    Serial.print(x);
    Serial.print('\t');
    Serial.print(y);
    Serial.print('\t');
    Serial.println(z);
  }
}

附上序列埠绘图家所得加速度变化,如图Fig. 23-2所示。

Arduino Nano 33 BLE Sense - LSM9DS1加速度计读值
Fig. 23-2 Arduino Nano 33 BLE Sense - LSM9DS1加速度计读值。(OmniXRI整理绘制, 2021/10/8)

如果想要保留这些值再写入其它外挂的Serial Flash或SD卡的人,可先开一个比较大的记忆区,例如float x_data[取样频率x取样秒数]来暂存,当满了就暂停取样,待写入完後,清空记忆区,再重新循环或结束取样动作,就像前面章节介绍在作声音取样一样。

这里补充一下,IMU.begin()初始化时其预设值如下所示。

  • 加速度计使用范围 [-4,+4]g -/+0.122 mg,输出频率119Hz。
  • 陀螺仪使用范围 [-2000, +2000] dps +/-70 mdps,输出频率119Hz。
  • 地磁计使用范围 [-400, +400] uT +/-0.014 uT,输出频率20Hz。

如果使用在特殊用途需要大一点或小一点的范围,Arduino_LSM9DS1函式库目前并没有提供,须自行手动修改下列程序,\libraries\Arduino_LSM9DS1\src\LSM9DS1.cpp,如果你不是很有把握,请不要随意更动这些值。更多LSM9DS1完整函式用法可参考官网说明文件

/*
  Arduino Nano 33 BLE Sense - LSM9DS1 IMU(九轴运动感测器)初始化函式
*/

int LSM9DS1Class::begin()
{
  _wire->begin();

  // reset
  writeRegister(LSM9DS1_ADDRESS, LSM9DS1_CTRL_REG8, 0x05);
  writeRegister(LSM9DS1_ADDRESS_M, LSM9DS1_CTRL_REG2_M, 0x0c);

  delay(10);

  if (readRegister(LSM9DS1_ADDRESS, LSM9DS1_WHO_AM_I) != 0x68) {
    end();

    return 0;
  }

  if (readRegister(LSM9DS1_ADDRESS_M, LSM9DS1_WHO_AM_I) != 0x3d) {
    end();

    return 0;
  }

  writeRegister(LSM9DS1_ADDRESS, LSM9DS1_CTRL_REG1_G, 0x78); // 119 Hz, 2000 dps, 16 Hz BW
  writeRegister(LSM9DS1_ADDRESS, LSM9DS1_CTRL_REG6_XL, 0x70); // 119 Hz, 4G

  writeRegister(LSM9DS1_ADDRESS_M, LSM9DS1_CTRL_REG1_M, 0xb4); // Temperature compensation enable, medium performance, 20 Hz
  writeRegister(LSM9DS1_ADDRESS_M, LSM9DS1_CTRL_REG2_M, 0x00); // 4 Gauss
  writeRegister(LSM9DS1_ADDRESS_M, LSM9DS1_CTRL_REG3_M, 0x00); // Continuous conversion mode

  return 1;
}

有了上面的基础,相信大家已经可以联想到,这和语音(唤醒词)辨识非常相似,都是连续信号,只是资料从一维变三维,所以应该很容易可以用Edge Impulse这样的工具才完成「动作辨识」。没错,这个部份就留待下回分解。

ps. 为让文章更活泼传达硬梆梆的技术内容,所以引用了经典电影「食神」的桥段,希望小弟戏剧性的二创不会引起电影公司的不悦,在此对星爷及电影公司致上崇高的敬意,敬请见谅。

参考连结

ST LSM9DS1 iNEMO inertial module:3D magnetometer, accelerometer, gyroscope
ST LSM9DS1 iNEMO inertial module 资料手册
Arduino LSM9DS1 library说明文件


<<:  拆掉 v-model + computed get/set 到 vuex

>>:  Day 23 : 插件篇 02 — 如何在 Obsidian 中自动汇整笔记?使用 Dataview 查询与呈现符合条件的笔记

菜鸟日记Day 29-用Chart.js制作图表

原本要使用C3.js搭配D3.js套件制作动态图表,但不知为何一直无法正常抓取D3.js的cdn档案...

NetSuite Order to Cash flow - Create Sales Order

Order to Cash 所有 ERP 最基础的功能, 主要用来表现从 订单 -> 收到款项...

<Day22>用Shioaji API模拟帐户做台股下单

● 这章会示范如何用Shioaji做台股下单 终於来到下单的环节啦~~~ 没有下单过,别说你进入过股...

Day07 建造APP(1)

昨天我们创造完Project後,我们今天要学的是怎麽打造App,你们可能会对这名子感到疑惑,想说这个...

什麽是统一建模语言 (UML)?

UML是统一建模语言的简称,是一种标准化建模语言,由一组集成的图表组成,旨在帮助系统和软件开发人员指...