
1. 从零开始为什么Arduino/Genuino 101是运动感知的绝佳起点如果你刚接触Arduino或者玩过Uno、Nano这类基础板子想尝试点更“智能”的东西那么Arduino/Genuino 101以下简称101绝对是一个被低估的宝藏。它不像ESP32那样功能繁多也不像树莓派那样像个微型电脑但它有一个独一无二的优势板载了六轴惯性测量单元IMU也就是集成了加速度计和陀螺仪。这意味着你不用再像用Arduino Uno那样为了读取MPU6050的数据而额外购买模块、连接杜邦线、处理I2C通信可能遇到的地址冲突和电平匹配问题。101把这一切都集成在了一块板子上开箱即用。这听起来可能只是省了点麻烦但实际意义远不止于此。对于初学者来说最大的障碍往往不是代码逻辑而是硬件连接和底层通信的调试。一个松动的杜邦线、一个错误的I2C地址、一个没接好的GND都可能让你在“为什么没数据”的坑里折腾半天极大地挫伤学习热情。101直接帮你绕过了这个“硬件劝退”阶段让你能立刻、专注地投入到传感器数据的获取、理解和应用这个核心环节。你可以把全部精力放在“数据意味着什么”以及“能用数据做什么”这些更有趣的问题上。从技术角度看101板载的这颗IMU芯片是博世Bosch的BMI160。它是一颗在消费电子领域比如智能手机、可穿戴设备非常成熟的芯片性能稳定功耗也控制得不错。加速度计能测量三个轴向X, Y, Z的线性加速度单位通常是重力加速度g9.8 m/s²。陀螺仪则测量三个轴向的角速度也就是旋转的快慢单位通常是度每秒°/s或弧度每秒rad/s。把这两者结合起来你就能知道板子当前的运动状态是静止、在移动、在旋转还是被摔了出去。所以这篇教程的目标非常直接手把手带你打通从101板子到电脑屏幕的数据通路让你亲眼看到、亲手处理这些原始的加速度和角速度数据。我们会从最基础的开发环境搭建开始一步步写代码、上传、打开串口监视器直到看到那一串串跳动的数字。这不仅是101的入门第二步更是你进入“物理计算”和“运动感知”世界的关键一步。无论你是想做自平衡小车、手势控制器、运动记录仪还是仅仅想理解手机计步器的工作原理从这里开始都是最平滑的路径。2. 环境准备IDE、驱动与核心库的精准配置在开始写代码之前我们需要一个稳固的“工作台”。对于101来说这个工作台主要由三部分组成Arduino IDE、板子USB驱动、以及处理IMU数据的专用库。任何一环出问题后续都会举步维艰。2.1 Arduino IDE的选择与板卡支持安装首先你需要Arduino IDE。这里我强烈建议新手直接使用Arduino IDE 2.x的最新稳定版例如2.3.10。相比经典的1.8.x版本2.x版本在代码自动补全、串口监视器、项目管理等方面都有巨大提升界面也更现代化。去Arduino官网下载对应你操作系统Windows, macOS, Linux的版本即可。安装好IDE后打开它。101的核心处理器是Intel的Curie模块所以它并不在默认的板卡列表里。我们需要手动添加对这块板子的支持。打开“文件” - “首选项”。在“附加开发板管理器网址”一栏你会看到一个或多个URL。我们需要添加Arduino 101的板卡支持地址。通常正确的地址是https://github.com/arduino/ArduinoCore-arc32/raw/master/package_arc32_index.json注意由于Arduino官方维护策略的变迁有时这个地址会失效或变更。一个更稳妥的方法是在首选项里已有的网址后面添加一个逗号然后加上http://downloads.arduino.cc/packages/package_arc32_index.json。如果都不行可以去Arduino官网的101板块查找最新的板卡支持URL。点击“好”保存首选项。接下来打开“工具” - “开发板” - “开发板管理器”。在顶部的搜索框中输入“Arduino 101”。在搜索结果中你应该会看到由“Intel”提供的“Arduino 101”包。点击它然后选择安装最新版本。这个过程会下载并安装101的所有编译工具链、核心库和定义文件需要一些时间请保持网络通畅。安装完成后关闭开发板管理器。现在你可以在“工具” - “开发板”列表中找到并选择“Arduino/Genuino 101”了。2.2 关键驱动安装与串口识别Windows用户重点这是Windows用户最容易踩坑的地方。当你第一次把101通过USB线连接到电脑时系统可能会尝试自动安装驱动但很多时候会失败导致在IDE的端口列表中看不到你的板子。为什么需要这个驱动101板载了一个USB转串口芯片通常是FTDI或类似方案它的作用是把101芯片通过串口发送的数据转换成电脑USB能识别的信号。驱动就是这个“翻译官”的工作证没有它电脑和板子就无法对话。解决方案如下让IDE帮你安装推荐首选在Arduino IDE中选择好“Arduino/Genuino 101”开发板后点击“工具” - “获取板卡信息”。有时IDE会自动触发驱动的查找和安装流程。如果成功你会看到端口中出现一个新的COM口如COM3, COM4。手动安装驱动如果上述方法不行你需要手动安装。打开设备管理器在Windows搜索框输入“设备管理器”。找到“端口 (COM和LPT)”或“其他设备”下面看到一个带有黄色感叹号的设备名字可能类似“Arduino 101”或“Genuino 101”或者是一个未知的USB设备。右键点击它选择“更新驱动程序软件” - “浏览我的计算机以查找驱动程序软件”。在接下来的界面中选择“让我从计算机上的可用驱动程序列表中选取”。在列表里尝试选择“通用串行总线设备”下的“USB Serial Device”或者如果有“FTDI”相关的驱动也可以选择。如果都不行你可能需要去FTDI官网下载最新的VCP虚拟串口驱动进行安装。验证成功驱动安装成功后在设备管理器的“端口 (COM和LPT)”下你会看到一个明确的串行端口例如“USB Serial Device (COM3)”。记住这个COM编号比如COM3。回到Arduino IDE在“工具” - “端口”菜单中选择对应的COM口。现在你的软件环境就准备好了。2.3 安装CurieIMU库与板载传感器对话的桥梁101的加速度计和陀螺仪BMI160并不是通过简单的模拟引脚或数字引脚来读取的。它通过一种更高效、更复杂的内部总线与主处理器Curie通信。为了简化我们的操作Arduino官方提供了一个名为CurieIMU的专用库。这个库封装了所有底层的通信细节提供了非常友好的函数接口让我们用几行代码就能读取原始数据。安装库有两种方法通过库管理器最方便在Arduino IDE中点击“工具” - “管理库...”。在库管理器的搜索框中输入“CurieIMU”。你应该能看到一个由“Intel”发布的“CurieIMU”库。点击它然后选择“安装”。这是最推荐的方式能确保版本兼容。手动安装备选如果库管理器里找不到你可以去GitHub上搜索“ArduinoCore-arc32”这个仓库在它的libraries文件夹下找到CurieIMU库下载ZIP文件。然后在IDE中点击“项目” - “加载库” - “添加.ZIP库...”选择你下载的ZIP文件。安装成功后你可以在代码文件的顶部通过#include CurieIMU.h来使用这个库了。至此硬件、驱动、软件、库所有准备工作全部就绪。3. 第一行代码初始化IMU与理解数据坐标系现在让我们打开一个新的Arduino草图Sketch开始写代码。我们首先要做的是和板载的IMU打个招呼告诉它“嗨我准备好读取数据了。”3.1 初始化流程与参数设置在setup()函数里我们需要完成IMU的初始化和基本配置。setup()函数只在板子上电或复位时运行一次。#include CurieIMU.h void setup() { // 初始化串口通信波特率设置为115200 // 这是电脑和板子之间数据传输的速度约定115200是Arduino常用的较高速度能保证数据流畅。 Serial.begin(115200); // 等待串口连接对于像Leonardo、101这类有原生USB的板子是个好习惯 while (!Serial) { ; // 等待串口端口连接。对于USB CDC通用串行总线通信设备类是必需的 } // 初始化CurieIMU库 CurieIMU.begin(); // 设置加速度计的测量范围单位是重力加速度g。这里设为±2g。 // 可选范围通常有±2g, ±4g, ±8g, ±16g。范围越小在低加速度下分辨率越高越灵敏。 // 对于手部晃动、设备倾斜这类应用±2g或±4g通常足够。 CurieIMU.setAccelerometerRange(2); // 设置陀螺仪的测量范围单位是度/秒(°/s)。这里设为±250°/s。 // 可选范围有±250, ±500, ±1000, ±2000 °/s。同样范围越小对小角度旋转越灵敏。 // 人体手势的角速度很少超过200°/s所以±250是个安全的起始值。 CurieIMU.setGyroRange(250); Serial.println(IMU初始化完成开始输出数据...); Serial.println(Accel X\tAccel Y\tAccel Z\tGyro X\tGyro Y\tGyro Z); // 制表符\t用于在串口监视器中让数据对齐形成表格。 }关键点解析CurieIMU.begin()这个函数至关重要。它建立了与BMI160芯片的通信链路。如果这步失败比如芯片损坏或连接异常后续所有读取数据的函数都会失效。在实际项目中你可以在这里添加一个判断如果begin()返回false则通过串口输出错误信息便于排查。setAccelerometerRange和setGyroRange这两个设置直接决定了你读到的原始数据的“满量程”和“灵敏度”。例如加速度计量程设为±2g意味着当传感器感受到2倍重力加速度约19.6 m/s²时输出的原始数据会达到其最大值对于16位ADC通常是±32768。量程越小每个数字单位LSB代表的实际加速度或角速度值就越小测量也就越精细。你可以根据应用场景调整。比如做碰撞检测可能需要±16g做精细的姿态估计±2g更合适。3.2 建立正确的空间坐标系概念在读取数据之前必须在脑海里建立一个清晰的坐标系。对于101板子以及绝大多数IMU遵循的是右手坐标系。X轴平行于板子的长边指向USB接口的反方向即远离USB口的一侧。Y轴平行于板子的短边指向板子正面丝印文字“Genuino 101”或“Arduino 101”的阅读方向通常是从左到右。Z轴垂直于板子平面指向板子的正面有元件的一面。为什么理解这个很重要当你把板子平放在桌面上时加速度计的Z轴读数应该接近1g或-1g取决于库对正方向的定义X和Y轴接近0g。如果你看到的数据不符合这个预期那可能是板子方向拿错了或者代码里对数据的正负理解有误。陀螺仪的数据也完全基于这个坐标系绕X轴旋转是“滚转”Roll绕Y轴旋转是“俯仰”Pitch绕Z轴旋转是“偏航”Yaw。建立一个正确的空间参照系是后续进行姿态解算、运动分析的基础。4. 核心数据读取循环获取并解读原始值初始化完成后我们进入loop()函数。这个函数会以最快的速度取决于代码执行时间和串口输出延迟循环执行。在这里我们将不断地读取传感器的原始数据并打印出来。4.1 读取原始数据的函数调用在loop()函数中添加以下代码void loop() { int ax, ay, az; // 用于存储加速度计原始值X, Y, Z int gx, gy, gz; // 用于存储陀螺仪原始值X, Y, Z // 读取加速度计三个轴的原始数据 CurieIMU.readAccelerometer(ax, ay, az); // 读取陀螺仪三个轴的原始数据 CurieIMU.readGyro(gx, gy, gz); // 将读取到的六个原始值通过串口打印出来 Serial.print(ax); Serial.print(\t); Serial.print(ay); Serial.print(\t); Serial.print(az); Serial.print(\t); Serial.print(gx); Serial.print(\t); Serial.print(gy); Serial.print(\t); Serial.println(gz); // 最后一个数据后用println换行 // 添加一个短暂的延迟控制数据输出速率避免串口监视器刷屏过快。 // 这里延迟50毫秒即每秒输出约20组数据便于观察。 delay(50); }4.2 原始数据的物理意义与单位换算现在将代码上传到你的101板子点击IDE左上角的“上传”按钮。上传成功后打开“工具” - “串口监视器”确保右下角的波特率设置为115200。你应该会看到一列列快速滚动的数字。这些数字就是原始数据Raw Data。它们直接来自传感器内部的模数转换器ADC通常是16位有符号整数范围在-32768到32767之间。如何理解这些数字关键在于之前设置的量程Range。原始值本身没有直接的物理意义必须结合量程进行换算。加速度计换算公式实际加速度 (g) 原始读数 / (32768 / 设置量程)因为我们设置了setAccelerometerRange(2)即量程为±2g所以分母是 32768 / 2 16384。因此实际加速度 (g) ax / 16384。 例如如果ax读数是8192那么X轴的实际加速度就是 8192 / 16384 0.5 g。陀螺仪换算公式实际角速度 (°/s) 原始读数 / (32768 / 设置量程)因为我们设置了setGyroRange(250)即量程为±250°/s所以分母是 32768 / 250 ≈ 131.072。因此实际角速度 (°/s) gx / 131.072。 例如如果gx读数是655那么绕X轴旋转的角速度大约是 655 / 131.072 ≈ 5 °/s。实操验证将101板子静止、水平放置在桌面上。观察串口数据。加速度计的Z轴读数az应该稳定在一个较大的正值或负值取决于库定义附近换算成g值应接近1或-1。X轴和Y轴读数应在0附近小幅波动。将板子绕X轴缓慢旋转就像翻书一样。你会看到陀螺仪的X轴读数gx出现明显变化正值或负值代表旋转方向。当你停止旋转读数应回到0附近。快速晃动一下板子你会看到所有六个数据都发生剧烈变化。这就是最原始的传感器数据。它们是所有高级应用如姿态估计、步数计数、手势识别的基石。5. 数据可视化与噪声观察使用串口绘图器看数字滚动固然直接但想要直观感受数据的变化趋势和噪声特性Arduino IDE自带的串口绘图器Serial Plotter是一个神器。它能把串口传来的数字实时绘制成曲线图。使用方法保持代码运行关闭串口监视器。点击“工具” - “串口绘图器”。同样确保波特率为115200。为了让绘图器能正确绘制多条曲线我们需要调整一下数据输出格式。绘图器默认将同一行中用空格或制表符隔开的数字识别为不同颜色的曲线。我们之前的代码已经用了\t制表符所以可以直接使用。但是为了更清晰我们可以稍微优化一下loop()中的打印部分去掉标签只打印纯数据void loop() { int ax, ay, az; int gx, gy, gz; CurieIMU.readAccelerometer(ax, ay, az); CurieIMU.readGyro(gx, gy, gz); // 为了绘图器打印用空格分隔的六个数据 Serial.print(ax); Serial.print( ); Serial.print(ay); Serial.print( ); Serial.print(az); Serial.print( ); Serial.print(gx); Serial.print( ); Serial.print(gy); Serial.print( ); Serial.println(gz); // 最后用println换行 delay(50); // 控制绘图更新频率 }重新上传代码并打开串口绘图器。你现在应该能看到6条不同颜色的曲线在跳动。通过绘图器观察什么静态噪声当板子完全静止时曲线并不是一条完美的直线而是在某个值上下波动。这个波动的幅度就是传感器的噪声。陀螺仪的噪声通常看起来比加速度计更“毛躁”。了解这个噪声水平很重要后续做数据滤波时就知道需要滤掉多大幅度的波动。动态响应快速移动板子观察曲线的峰值和跟随速度。这反映了传感器的动态响应特性。零点偏移零漂尤其是陀螺仪理想情况下静止时输出应为0。但你会发现静止时陀螺仪的曲线可能不在0线上而是有一个固定的偏移。这个就是零漂。对于要求不高的应用可以在程序开始时计算一段时间的平均值作为零漂值然后在后续读数中减去它。这就是简单的软件校准。串口绘图器是一个极其强大的调试和教学工具它能让你“看见”数据对传感器特性建立直觉。6. 进阶实践校准、滤波与实用代码优化获取原始数据只是第一步。在实际项目中直接使用这些原始数据往往效果不佳因为它们含有噪声和误差。我们需要进行一些处理。6.1 陀螺仪零漂的简易校准陀螺仪的零漂会随着时间和温度变化是导致姿态估计特别是偏航角Yaw随时间累积误差漂移的主要原因。虽然无法完全消除但上电时进行一次简单的静态校准可以大幅改善初始精度。我们可以在setup()函数中IMU初始化之后添加一个校准例程void calibrateGyro() { Serial.println(正在进行陀螺仪校准请保持板子绝对静止...); delay(2000); // 等待2秒让用户放好板子 int numSamples 100; long gxSum 0, gySum 0, gzSum 0; for (int i 0; i numSamples; i) { int gx, gy, gz; CurieIMU.readGyro(gx, gy, gz); gxSum gx; gySum gy; gzSum gz; delay(10); // 每隔10ms读一次 } // 计算平均值作为零漂偏移量 gxOffset gxSum / numSamples; gyOffset gySum / numSamples; gzOffset gzSum / numSamples; Serial.print(校准完成。偏移量 (X, Y, Z): ); Serial.print(gxOffset); Serial.print(, ); Serial.print(gyOffset); Serial.print(, ); Serial.println(gzOffset); } // 在文件顶部全局变量区定义偏移量 int gxOffset 0, gyOffset 0, gzOffset 0;然后在loop()中读取陀螺仪数据后进行修正CurieIMU.readGyro(gx, gy, gz); gx - gxOffset; gy - gyOffset; gz - gzOffset;6.2 引入低通滤波平滑数据传感器数据的高频噪声可以通过低通滤波来平滑。最简单的是一阶低通滤波也叫指数移动平均滤波。它的原理是本次输出值 α * 本次采样值 (1-α) * 上次输出值。其中α是滤波系数0α≤1α越小滤波效果越强越平滑但响应也越迟缓。我们可以为每个轴的数据实现一个滤波函数float lowPassFilter(float newValue, float oldValue, float alpha) { return alpha * newValue (1 - alpha) * oldValue; } // 全局变量存储滤波后的值 float filtered_ax 0, filtered_ay 0, filtered_az 0; float filtered_gx 0, filtered_gy 0, filtered_gz 0; float filterAlpha 0.2; // 滤波系数可根据需要调整0.1~0.5 void loop() { int raw_ax, raw_ay, raw_az; int raw_gx, raw_gy, raw_gz; CurieIMU.readAccelerometer(raw_ax, raw_ay, raw_az); CurieIMU.readGyro(raw_gx, raw_gy, raw_gz); // 换算为物理单位并应用滤波 filtered_ax lowPassFilter(raw_ax / 16384.0, filtered_ax, filterAlpha); filtered_ay lowPassFilter(raw_ay / 16384.0, filtered_ay, filterAlpha); filtered_az lowPassFilter(raw_az / 16384.0, filtered_az, filterAlpha); // 陀螺仪数据先校准零漂再换算并滤波 raw_gx - gxOffset; raw_gy - gyOffset; raw_gz - gzOffset; filtered_gx lowPassFilter(raw_gx / 131.072, filtered_gx, filterAlpha); filtered_gy lowPassFilter(raw_gy / 131.072, filtered_gy, filterAlpha); filtered_gz lowPassFilter(raw_gz / 131.072, filtered_gz, filterAlpha); // 打印滤波后的物理值 Serial.print(filtered_ax, 4); Serial.print( ); // ... 打印其他值 Serial.println(filtered_gz, 4); // 打印4位小数 delay(50); }现在你再观察串口绘图器会发现曲线变得平滑了许多但快速运动时的响应会有一点延迟。这就是滤波的权衡平滑度 vs. 响应速度。你需要根据具体应用调整filterAlpha的值。6.3 优化数据输出与解析效率当你需要把数据发送到电脑进行更复杂的处理比如用Processing或Python做实时可视化时频繁的Serial.print和文本格式转换会成为性能瓶颈且占用大量串口带宽。一个更高效的方法是使用二进制传输。将整数或浮点数直接以字节流的形式发送而不是转换成可读的文本。这能大幅减少数据量提高传输速度。void loop() { int ax, ay, az, gx, gy, gz; CurieIMU.readAccelerometer(ax, ay, az); CurieIMU.readGyro(gx, gy, gz); // 应用校准... // 应用滤波... // 假设我们发送滤波后的浮点数4字节每个 float data[6] {filtered_ax, filtered_ay, filtered_az, filtered_gx, filtered_gy, filtered_gz}; Serial.write((byte*)data, sizeof(data)); // 直接写入字节流 // 可以加一个分隔符或同步头方便接收端解析例如发送一个特定的字节 // Serial.write(0xAA); delay(10); // 可以设置更短的延迟因为传输效率高了 }在电脑端你需要用相应的程序如Processing的serial.readBytes()或Python的struct.unpack()来解析这个字节流。这种方式在需要高速、实时传输数据时是必备技能。7. 从数据到应用思维延伸与项目构想成功获取并处理了稳定的加速度和陀螺仪数据你已经掌握了运动感知的核心输入。接下来这些数据能用来做什么这里提供几个方向帮你打开思路1. 简易姿态估计倾角计仅使用加速度计在设备静止或缓慢移动时可以通过反三角函数估算出设备相对于水平面的倾斜角俯仰和滚转。pitch atan2(-ax, sqrt(ay*ay az*az)) * 180 / PIroll atan2(ay, az) * 180 / PI注意这个方法对线性加速度比如移动非常敏感一旦设备在移动计算出的角度就完全错误了。这就是为什么需要融合陀螺仪数据。2. 运动触发与手势识别通过分析加速度数据的幅度和模式可以实现简单的动作检测。敲击检测监测某个轴向的加速度在短时间内是否超过一个高阈值。晃动检测监测一段时间内加速度变化的频率和幅度。自由落体检测监测合加速度是否接近0g失重状态。 你可以设置一个“能量”窗口计算三轴加速度向量和的模在一定时间内的变化来识别不同的手势模式。3. 与陀螺仪融合——互补滤波这是入门级姿态解算的关键一步。加速度计在静态时测角度准但动态时差陀螺仪测角速度准短期积分得角度准但长期有漂移。互补滤波的核心思想是用高通滤波器滤掉加速度计数据中的低频噪声即动态误差用低通滤波器滤掉陀螺仪积分角度中的高频噪声即漂移然后将两者按一定权重融合。网上有大量关于“Arduino互补滤波”的教程和代码你的101板子正是实践它的绝佳平台。4. 数据记录器利用101的蓝牙功能是的101还板载了蓝牙低能耗BLE你可以将实时运动数据无线发送到手机App在手机上记录和分析。或者单纯用SD卡模块将数据以CSV格式存储下来用于事后分析比如分析一段跑步过程中的步频和冲击力。踩坑心得与最后叮嘱电源稳定性是关键IMU传感器对电源噪声敏感。如果使用不稳定的电源比如有些USB端口或劣质移动电源可能会在数据中引入额外的噪声。尽量使用电脑的USB端口或可靠的电源适配器。延迟不是敌人是工具delay()函数在教程中用于控制数据输出速率方便观察。但在真正的实时控制项目中如平衡车delay()会阻塞程序是致命的。你需要学会使用millis()函数进行非阻塞定时或者探索中断等更高级的用法。理解数据的局限性从IMU的原始数据到有意义的姿态信息中间有很长的路要走。零漂、温漂、尺度因子误差、轴间非正交误差……这些都会影响精度。对于业余项目简单的校准和滤波通常足够。但对于高要求应用可能需要更复杂的标定算法和传感器融合算法如卡尔曼滤波、Mahony滤波。善用官方示例Arduino IDE中在“文件” - “示例” - “CurieIMU”下官方提供了大量高级示例代码包括计步器、姿态识别、手势识别等。在掌握基础数据读取后深入研究这些示例是提升的最佳途径。现在你的101板子不再只是一块能点亮LED的微控制器它成了一个能感知自身运动的智能节点。数据的洪流已经打开下一步就看你如何驾驭它去构建那些能感知、响应物理世界的酷炫项目了。