×

使用arduino/genuino 101的板载惯性测量单元(IMU)教程

消耗积分:0 | 格式:zip | 大小:0.16 MB | 2022-12-19

杨勇

分享资料个

描述

 

简介和动机:

有很多关于如何在您的项目中使用 arduino/genuino 101 的板载惯性测量单元 (IMU) 的优秀教程。例如:,但缺少有关如何使用 Arduino/Genuino 101 的板载低功耗蓝牙 (BLE) 传输此数据的信息。

本教程项目将演示一种使用 Evothings Workbench 通过 BLE 将原始 IMU 数据传输到移动设备的方法。本教程还将展示如何使用Smoothie.js以图形方式实时显示 IMU 数据

pYYBAGOYdd-APbeYAADSZ-ZTrtQ305.jpg
标题(可选)
 

你需要什么

  • Arduino/Genuino 101 板
  • USB电缆
  • Arduino IDE 1.6.8
  • 我们定制的 arduino 草图,imuble01.ino
  • Evothings 工作台和 Evothings 查看器 
  • Smoothie.js来自 SmoothieCharts 
  • 我们修改后的应用文件index.htmlapp.js

软件

该项目的代码已注释,我鼓励您阅读它。我不会提供所有代码如何工作的逐行细分,而是强调我认为最重要的领域。

对于 BLE 连接的 Arduino/Genuino 101 项目,了解所需软件的整体布局非常重要。除了电路板上的 Arduino 草图之外,您还必须编写软件才能在移动设备上运行。为了编写草图,我们使用熟悉的 Arduino IDE 以及我们项目所需的库 CurieIMU 和 CurieBLE。

要为移动设备开发我们的软件,有两大类选项:

  • 直接为我们希望与之交互的移动设备的操作系统编写本机应用程序。
  • 使用 JavaScript/HTML/CSS 三重奏编写一个 Web 应用程序,它基本上可以在任何平台上运行。

在我与 BLE 合作的最初努力中,我面临的挑战不仅是学习 BLE,而且还必须了解这些选项。这就是 Evothings 的用武之地。Evothings 平台是一个完整的“生态系统”。借助 EvothingsStudio,我们可以使用 JavaScript/HTML/CSS 编写我们软件的移动应用程序组件。通过在我们的移动设备上运行 EvothngsViewer 应用程序,我们可以访问设备的原生功能,例如 BLE。Evothings 平台包括一个名为 easyble.js 的库,该库具有通过 BLE 连接与您的 Arduino/Genuino 101 项目建立和通信的所有必要功能。

 

如果您担心不了解 HTML/CSS/JavaScript 会阻碍您,我可以保证在开始这些项目和使用 Evothings 之前,我没有任何网络编程经验。我确实有 C/Arduino C 和 Python 方面的经验,但没有 HTML/CSS/JavaScript 方面的经验。evothings 网站包含大量优秀的教程和文章 可帮助您入门。如果您有一些基本的编程知识,并且愿意通读并学习一些有趣的代码示例,那么请立即投入并使用这个非凡的工具!

Arduino草图

我为 arduino 项目编写软件的首要原则是建立在其他人的工作之上,这个项目也不例外。我使用加速度计和陀螺仪原始数据访问示例草图作为我项目草图的基础。这些位于 CurieIMU 库 ( http://www.arduino.cc/en/Reference/CurieIMU )参考页面的底部。我从加速度计草图开始,然后将相关的陀螺仪草图元素剪切并粘贴到其中。

低功耗蓝牙

当我第一次开始使用 BLE 时,我对所有新术语感到不知所措。我希望它会像通过串行端口发送消息一样简单。如果您感到不知所措,请不要担心,只需开始尝试即可。您使用它的次数越多,您就越有信心在您的项目中使用 BLE!有许多很好的教程可以帮助您入门。开始学习的几个好地方是 CurieBLE 库参考和 Adafruit BLE 教程:

BLE 标准规定了服务,这些服务是相关数据值的集合或集合。在 BLE 的世界中,这些值被称为特性。每个服务和特征都由一个通用唯一标识符 (uuid) 指定。对于官方接受的服务,这些 uuid 是为您指定的。信不信由你,我找不到惯性测量的标准服务!所以对于这个应用程序,我必须为我的“imu 服务”生成一个自定义 uuid。对于自定义服务,您必须创建自己的 128 位自定义 uuid。这很容易做到,并且有很多资源可以生成这些 uuid。我使用的是一个在线 uuid 生成器:

完成后,您可以使用 CurieBLE 库创建 BLE 服务:

BLEService imuService("917649A0-D98E-11E5-9EEC-0002A5D5C51B"); // 自定义 UUID

BLECharacteristic imuAccCharacteristic("917649A1-D98E-11E5-9EEC-0002A5D5C51B", BLERead | BLENotify, 12 );

BLECharacteristic imuGyroCharacteristic("917649A2-D98E-11E5-9EEC-0002A5D5C51B", BLERead | BLENotify, 12 );

BLEUnsignedCharCharacteristic appButtonCharacteristic("917649A7-D98E-11E5-9EEC-0002A5D5C51B", BLERead | BLEWrite );

请阅读 Arduino/Genuino 101 网站上的 BLE 代码示例(https://www.arduino.cc/en/Tutorial/Genuino101CurieBLEHeartRateMonitor) 和此代码中的注释以进一步了解如何设置您的 BLE 服务和特性。通过 BLE 发送浮点数:所有 BLE 数据都以字节形式传输。因此,无论您的数据是来自整数、字符还是浮点数,都必须将其转换为字节才能通过 BLE 传输。无需将 IMU 数据作为浮点数传输,CurieIMU 接口允许您以整数形式访问原始 IMU 数据。然而,这两个示例应用程序都将这些原始数据转换为具有物理意义的浮点数。这些转换基于 CurieIMU 参考中推荐的公式。对于加速度计,建议使用以下公式将原始加速度计读数转换为 mg:

     浮动 g = (gRaw/32768.0)*getAccelerometerRange()

我们有什么选择来处理这个问题?

  • 我们可以通过 BLE 发送原始数据整数,并在我们软件的移动应用程序端实现推荐的方程。这将问题转移到移动应用程序上。
  • 我们可以将浮点数转换为字符串。字符串只是 char 数组,或者另一种看待它们的方式是字节数组。然后我们可以在移动应用程序方面随意转换或解码这些字节。这是 CurieBLE 库 ( http://www.arduino.cc/en/Reference/CurieBLE ) 的教程/参考所暗示的解决方案。
  • 我们可以使用一些 C 编程魔法将我们的浮点数转换为它们的 char 数组或字节等价物,然后通过 BLE 将它们发送给移动应用程序“解码”。

选项 1 很好,但没有教给我们任何新东西。此外,当我们最终获得只能作为浮点数使用的数据时,我们将回到我们开始的地方。英特尔居里模块非常强大,那么为什么不使用它来执行计算并让移动应用程序来处理其他琐事呢?选项 2 是一种非常常用且可接受的方法,但涉及处理字符串和字符串转换的所有开销。

选项 3 可能是最复杂的,但也是最有效的。我们跳过了处理字符串的所有开销。在这个选项中,我们还将学习如何使用代表我们数据的实际字节。我们将更深入地了解我们的计算机如何表示和存储数字。我们还将获得以 Arduino C 为核心的信心。最重要的是,这个选项也将为我们赢得一些严肃的“极客信誉”,因此作为优秀的制造者和黑客,我们当然会选择选项 #3!

选项#3:

所有数据类型在内存中都有一个底层表示。了解浮点数的存储和表示方式是我们解决方案的关键。Arduino C 使用 32 位或 4 个字节来表示和存储一个浮点数。char 和 unsigned char 数据类型都使用 4 位或 1 字节来表示和存储 char 数据。

unsigned char 是 CurieBLE 库中数据交换的“货币”:

/**

* 设置 Characteristic 的当前值

* @param value 要设置的新值,作为字节数组。数据存储在内部副本中。

* @param length 数组中要写入的有效数据的长度,以字节为单位。

* 不得超过为此特征设置的 maxLength。

* @return bool true 设置值成功,错误为 false

*/

bool setValue(const unsigned char value[], unsigned short length);

如您所见,为了使用setValue()函数更改特征的值,我们需要将数据作为 char 数组传递(unsigned char 数组与字节数组相同)。

所以现在的问题变成了,我们如何传递一个浮点数和一个无符号字符数组?我们可以通过使用 C 语言union来做到这一点。union关键字允许我们让不同数据类型的变量在内存中共享相同的空间由于 C 中的浮点数占用 4 个字节,我们希望与 4 个无符号字符的数组共享 4 个字节的内存空间。

从某种意义上说,编译器并不真正关心我们如何解释字节,它只是保留空间并允许我们使用这个空间,就好像它是我们联合中的一种数据类型一样。但是,当我们在程序中使用变量时,我们必须非常小心,以确保正确考虑变量的大小和类型。

实际上,在我们的例子中,我们需要从加速度计和陀螺仪中分别考虑 3 个浮点数。每个轴一个浮点数。我们可以为每个变量考虑一个单独的变量、联合和 BLE 特征。但是,知道每个浮点数的长度为 4 个字节,并且每次 BLE 传输最多可以发送 20 个字节,我们可以使用 2 个联合、2 个 3 个浮点数的数组发送完整的加速度计和陀螺仪数据集,并且只需2 个特性,每个特性 12 个字节。这不仅会使我们的代码更具可读性和可管理性,而且效率更高。

为此,我们将使用联合来共享 3 个浮点数数组(需要 12 个字节)和一个 12 个无符号字符数组之间的空间。这样做是这样的:

联盟

{

     浮动一个[3];

    无符号字符字节[12];

} 数据;

当从 IMU 读取数据并转换为浮点数时,我们将其分配给联合体的浮点元素:

   CurieIMU.readAccelerometer(axRaw, ayRaw, azRaw);

   accData.a[0] = convertRawAcceleration(axRaw);

   accData.a[1] = convertRawAcceleration(ayRaw);

   accData.a[2] = convertRawAcceleration(azRaw);

不幸的是,我们还没有完成。如果我们尝试将联合传递给setValue()函数,我们的编译器会抱怨并且不让我们继续。完成这项工作还需要一个步骤。我们必须让编译器相信我们的并集实际上是一个无符号字符数组,方法是这样转换。演员表是一种编程魔法,它允许我们将一种数据类型转换为另一种数据类型(https://www.arduino.cc/en/Reference/Cast)。在某些情况下,这可能会导致数据丢失,但在我们的例子中,由于我们只是处理字节,我们根本不会丢失任何信息。然而,为了执行这个转换,我们确实需要使用一些指针魔法:

   unsigned char *acc = (unsigned char *)&accData;

让我们翻译一下:

首先,&accData获取我们 12 个字节的内存位置的地址。然后,(unsigned char *)&accData告诉编译器我们认为这个内存位置被视为一个无符号数组字符。实际上,这是一个指向该内存位置第一个字节的指针。最后,unsigned char *acc将变量acc分配给这个指针。要处理的内容很多,但多读几遍就会明白。如果没有,只需按原样使用模式发送数据。对陀螺仪数据重复完全相同的命令模式。 现在我们可以继续使用移动应用程序了。

JavaScript/HTML/CSS 移动应用程序:

为了开发我们的移动应用程序,必须学习 JavaScript/HTML/CSS 似乎是一件苦差事。但是,时间和精力将是值得的!它将为您的项目打开许多机会,并允许您利用许多令人兴奋的库进行编程。一个这样的库,smoothie.js将在此处讨论。为了帮助您学习如何使用 JavaScript/HTML/CSS,Evothings 网站和 Evothings 工作台提供了大量示例和教程来帮助您。就本项目而言,我发现最简单的方法是修改 TI SensorTag CC2650 示例。此示例代码遵循功能强大的结构,易于阅读,并且已经具有我们项目所需的大部分功能。每个示例都有一个包含许多文件的目录树,乍一看可能看起来很复杂,但出于我们的目的,我们只需要修改示例根目录中的两个文件 index.html 和app.js ,并将smoothie.js库添加到我们的项目目录。下面的项目配方涵盖了这些步骤

移动应用程序的亮点:

没有什么可以替代阅读代码,但我将在这里介绍一些亮点。请记住,我们的 IMU 数据将作为无符号字符或字节数组通过 BLE 发送。幸运的是,JavaScript 提供了易于使用的函数来将这些字节转换回任何形式的数据。这是一个代码片段,它将我们的加速度计字节转换回 JavaScript 中的浮点数:

   var ax = new DataView(data).getFloat32(0, true);

   var ay = new DataView(data).getFloat32(4, true);

   var az = new DataView(data).getFloat32(8, true);

字节通过 data 变量传递到我们的函数中。此数据用于实例化 DataView。一旦我们有了这个,我们就可以调用 DataView 对象的许多方法之一来将数据转换为我们想要的表示形式。在 这种情况下,我们要将其转换回浮点数。还记得我们从 Arduino/Genuino 101 IMU 创建的浮点数是 4 字节还是 32 位长?getFloat32 ()函数专门用于处理 32 位浮点表示。我们发送到getFloat32()的参数功能非常重要。第一个是一个数字,这个数字在我们的例子中是 0、4 或 8,表示我们的 4 字节浮点数的第一个字节的字节偏移或索引。第二个参数 true 表示我们的浮点数是小端格式。就是这样,很简单。我们现在可以将所有的 分配给一个变量axayaz来表示沿每个轴的加速度。

现在,拥有这些数据的实时滚动图形表示不是很巧妙吗?当然会!为了实现这一点,我们将使用Smoothie.js库。我相信这说明了使用 JavaScript/HTML/CSS 为连接设备开发移动应用程序的力量。几乎所有您能想到的用途都有免费且易于使用的库和框架。

Smoothie.js库在 http://smoothiecharts.org/  网站上 有完整的描述。为了学习如何使用该库,作者创建了一个 10 分钟的教程(http://smoothiecharts.org/tutorial.html)。在查看示例并阅读他们的教程后,您应该能够轻松地使用他们的代码来创建自己的绘图。作者还创建了一个构建器页面 ( http://smoothiecharts.org/builder/ ),允许您以图形方式自定义绘图。根据您的意愿对绘图进行格式化后,您可以从构建器页面底部的文本框中剪切代码并将其直接粘贴到您的应用程序中。

我们将从smoothiecharts.org 主页下载smoothie.js库到我们的移动应用程序目录中以使用它。请按照以下项目配方中的说明执行此操作。index.html 中的以下行允许我们在应用程序中使用该库:

这就是教学的内容。让我们做这个项目吧!

硬件:

Arduino/Genuino 101。

因为 Arduino/Genuino 101 带有板载 BLE 和 IMU 功能,所以无需制作电路!只需通过 USB 将您的计算机连接到您的计算机以上传草图并访问 Arduino 软件 (IDE) 的串行监视器窗口以进行调试。

项目配方:

我假设您已经安装了 Arduino IDE、对英特尔居里的板级支持以及所有必要的库。如果没有,请在此处获取说明:https://www.arduino.cc/en/Guide/Arduino101。

1) 下载并安装 EvothingsStudio 和 EvothingsViewer ( http://evothings.com/download/ )。按照此处特定于您的操作系统和移动平台的说明进行操作。

2) 启动 Evothings Workbench 并单击示例选项卡。

pYYBAGOYdeGAR9laAAGPwP0Pez8298.png
Evothings Workbench 在桌面上打开,单击顶部的示例选项卡。
 

 

3) 复制 TI SensorTag CC2650 演示应用程序并为其命名。

4) 使用文件浏览器,进入 EvothingsStudio/MyApps 目录,找到您在步骤 3 中重命名的项目的主目录。

poYBAGOYdeSAKfa8AADMUqkVZcg685.png
项目主目录,将 index.html 和 app.js 替换为项目 index.html 和 app.js 文件。
 

5) 选择 index.html 文件并将其重命名为 index_old.html。

6) 选择 app.js 文件并将其重命名为 app_old.js。

7) 现在将本项目教程中的 index.html 和 app.js 文件复制到新项目的主目录中。

8) 创建一个名为 Smoothie 的目录。

9) 转到 smoothiecharts.org 并单击标有下载 Smoothie.js 的按钮。Smoothie.js 库的文本应显示在您的浏览器中。

10) 单击浏览器的文件选项卡,然后选择将页面另存为。在步骤 8 中创建的 Smoothie 目录中将此页面另存为 Smoothie.js。

11) 使用 USB 电缆将您的 Arduino/Genuino 101 连接到您的计算机。

12) 使用您的 Arduino IDE 1.6.8,将 imu_ble.ino 草图上传到 Arduino/Genuino 101。

13) 打开串口连接。在建立 BLE 连接之前,不会从 arduino/genuino101 imu 发送数据,因此此时您不应期望看到任何串行数据。

14) 返回 EvothingsWorkbench 并从 Connect 选项卡中获取您的连接密钥。

15) 在您的移动设备上启动 EvothingsViewer 并输入连接密钥。

16) 点击移动应用主页上的开始按钮。

17) 建立连接后,您应该会在移动设备上看到以下消息,状态:数据流活动 - 加速度计。值应该在 Arduino IDE 串行窗口上流式传输,并且线条应该在加速度计和陀螺仪图上滚动。

18) 摇晃、扭转和转动 Arduino/Genuino101!

结论:

您可以选择多种方法来收集、传输和显示 IMU 数据,但我相信这里介绍的方法是最有教益的方法。仔细阅读代码并对其进行试验确实没有替代品。你的努力会得到回报。作为创客,我们越是真正了解我们的项目是如何运作的,我们就越能完成:

“投入劳动力的最佳红利总是来自寻求更多的知识,而不是更多的权力。” 1906 年 3 月 12 日签署威尔伯和奥维尔赖特。”

 


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !

'+ '

'+ '

'+ ''+ '
'+ ''+ ''+ '
'+ ''+ '' ); $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code ==5){ $(pop_this).attr('href',"/login/index.html"); return false } if(data.code == 2){ //跳转到VIP升级页面 window.location.href="//m.lene-v.com/vip/index?aid=" + webid return false } //是会员 if (data.code > 0) { $('body').append(htmlSetNormalDownload); var getWidth=$("#poplayer").width(); $("#poplayer").css("margin-left","-"+getWidth/2+"px"); $('#tips').html(data.msg) $('.download_confirm').click(function(){ $('#dialog').remove(); }) } else { var down_url = $('#vipdownload').attr('data-url'); isBindAnalysisForm(pop_this, down_url, 1) } }); }); //是否开通VIP $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code == 2 || data.code ==5){ //跳转到VIP升级页面 $('#vipdownload>span').text("开通VIP 免费下载") return false }else{ // 待续费 if(data.code == 3) { vipExpiredInfo.ifVipExpired = true vipExpiredInfo.vipExpiredDate = data.data.endoftime } $('#vipdownload .icon-vip-tips').remove() $('#vipdownload>span').text("VIP免积分下载") } }); }).on("click",".download_cancel",function(){ $('#dialog').remove(); }) var setWeixinShare={};//定义默认的微信分享信息,页面如果要自定义分享,直接更改此变量即可 if(window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){ var d={ title:'使用arduino/genuino 101的板载惯性测量单元(IMU)教程',//标题 desc:$('[name=description]').attr("content"), //描述 imgUrl:'https://'+location.host+'/static/images/ele-logo.png',// 分享图标,默认是logo link:'',//链接 type:'',// 分享类型,music、video或link,不填默认为link dataUrl:'',//如果type是music或video,则要提供数据链接,默认为空 success:'', // 用户确认分享后执行的回调函数 cancel:''// 用户取消分享后执行的回调函数 } setWeixinShare=$.extend(d,setWeixinShare); $.ajax({ url:"//www.lene-v.com/app/wechat/index.php?s=Home/ShareConfig/index", data:"share_url="+encodeURIComponent(location.href)+"&format=jsonp&domain=m", type:'get', dataType:'jsonp', success:function(res){ if(res.status!="successed"){ return false; } $.getScript('https://res.wx.qq.com/open/js/jweixin-1.0.0.js',function(result,status){ if(status!="success"){ return false; } var getWxCfg=res.data; wx.config({ //debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId:getWxCfg.appId, // 必填,公众号的唯一标识 timestamp:getWxCfg.timestamp, // 必填,生成签名的时间戳 nonceStr:getWxCfg.nonceStr, // 必填,生成签名的随机串 signature:getWxCfg.signature,// 必填,签名,见附录1 jsApiList:['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 wx.onMenuShareTimeline({ title: setWeixinShare.title, // 分享标题 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 type: setWeixinShare.type, // 分享类型,music、video或link,不填默认为link dataUrl: setWeixinShare.dataUrl, // 如果type是music或video,则要提供数据链接,默认为空 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); }); }); } }); } function openX_ad(posterid, htmlid, width, height) { if ($(htmlid).length > 0) { var randomnumber = Math.random(); var now_url = encodeURIComponent(window.location.href); var ga = document.createElement('iframe'); ga.src = 'https://www1.elecfans.com/www/delivery/myafr.php?target=_blank&cb=' + randomnumber + '&zoneid=' + posterid+'&prefer='+now_url; ga.width = width; ga.height = height; ga.frameBorder = 0; ga.scrolling = 'no'; var s = $(htmlid).append(ga); } } openX_ad(828, '#berry-300', 300, 250);