天天看點

[體感遊戲] 1、MPU6050資料采集傳輸與可視化

最近在研究體感遊戲,到目前為止實作了基于51單片機的MPU6050資料采集、利用藍牙子產品将資料傳輸到上位機,并利用C#自制序列槽資料高速采集軟體,并且将資料通過自制的折線圖繪制子產品可視化地展示出來等功能。本文将主要對實作這意見單系統中遇到的問題做一個小結——其中包括:

<a href="http://www.cnblogs.com/zjutlitao/p/4396653.html#a">1、基于51的MPU6050子產品通信簡介(入門級)</a>

<a href="http://www.cnblogs.com/zjutlitao/p/4396653.html#b">2、陀螺儀資料采集與傳輸及幀格式介紹(小技巧)</a>

<a href="http://www.cnblogs.com/zjutlitao/p/4396653.html#c">3、基于C#的序列槽接收函數(C#基本知識)</a>

<a href="http://www.cnblogs.com/zjutlitao/p/4396653.html#d">4、多線程資料池解決高速序列槽實時性問題(難點)</a>

<a href="http://www.cnblogs.com/zjutlitao/p/4396653.html#e">5、折線圖可視化子產品(程式員基本功)</a>

關鍵詞:MPU6050 藍牙 C#序列槽 多線程 高速序列槽 折線圖繪制

因為是入門級,就先最簡單的介紹如何利用51從MPU6050中讀取資料吧(對于想知道卡爾曼濾波、俯角仰角、距離測量、摔倒檢測、記步等算法的可能要在接下來介紹)。既然要和MPU6050通信,那麼必不可少的是閱讀晶片手冊,如果您覺得親自去看又長又多而且都是英文的手冊很費時,不仿看看我找的簡要版:

[體感遊戲] 1、MPU6050資料采集傳輸與可視化

MPU-60X0是全球首例9軸運動處理器。它內建了3軸MEMS陀螺儀,3軸MEMS加速計,以及1個可擴充的數字運動處理器DMP(Digital Motion Processor),可用I2C接口連接配接一個第三方的數字傳感器,比如磁力計。擴充之後就可以通過其I2C或SPI接口輸出一個9軸的信号。MPU-60X0也可以通過其I2C接口連接配接非慣性的數字傳感器,比如壓力傳感器。

[體感遊戲] 1、MPU6050資料采集傳輸與可視化

MPU-60X0對陀螺儀和加速計分别用了三個16位的ADC,将其測量的模拟量轉化為可輸出的數字量。為了精确跟蹤快速和慢速運動,傳感器的測量範圍是可控的,陀螺儀可測範圍為±250,±500,±1000,±2000°/秒(dps),加速計可測範圍為±2,±4,±8,±16g(重力加速度)。

注:下圖是采用序列槽助手将MPU6050采集的資料顯示在上位機上,其中前三列輸出為三維的加速度(這裡的加速度包括地球本身的重力加速度),後三列為三維的角速度。

[體感遊戲] 1、MPU6050資料采集傳輸與可視化

但是這裡的輸出值并不是真正的加速度和角速度的值,上面說過,MPU是一個16位AD量程可程控的裝置,這裡設定的加速度傳感器的測量量程為正負2g(這裡的g為重力加速度),陀螺儀的量程為正負2000°/s。是以要用下面的公式進行轉化:

[體感遊戲] 1、MPU6050資料采集傳輸與可視化

好了,有了上面的基礎知識之後咱們就能嘗試用51的I2C總線從MPU6050讀取實時的3軸加速度和3軸角速度了。由于51本身不帶有I2C總線通信協定,是以我們要自己實作一個I2C通信協定,下面是我從網上找的并稍加修改的一個I2C總線通信的代碼:

<a></a>

如果你沒搞過硬體又從未聽說過I2C,那麼想想socket的握手再看看上面36~43行的有關ACK、Send、Write的函數大概能明白I2C的功能。當我們實作I2C的通信函數之後就可以與帶有I2C通信接口的晶片進行通信,那麼怎樣通信呢?其實很簡單——你可以把每個晶片比做為一個巨大的儲物櫃,儲物櫃裡每個抽屜裡存着相應的東西,你想讓傭人幫你去拿個東西,隻要告訴傭人對應的抽屜号就行了。這裡I2C總線相當于這個傭人,每個抽屜相當于晶片中的寄存器,抽屜号相當于寄存器位址。當你想設定晶片的某些屬性時是向對應的寄存器内寫資料,當想從晶片内擷取相關資料時,就要通過I2C向對應的位址寫資料然後接收晶片傳回的資料。這裡的8~31行為MPU-6050晶片内幾個常用的寄存器位址,前四個常用來作為設定晶片工作屬性,15~28共14個寄存器位址用來擷取傳感器的3軸加速度、3軸角速度和溫度的資料(這裡每一種資訊都包括H和L兩位,是由于8位表示不完該資料,于是分高低兩部分)

[體感遊戲] 1、MPU6050資料采集傳輸與可視化

這樣我們便不難了解InitMPU6050()和GetData(uchar REG_Address)函數:初始化函數是向相應的位址寫初始化配置資料(關于0x00\0x07等意思請參看MPU6050寄存器版說明書),而GetData則是傳入想獲得資料項的低位址,然後連續讀取目前位址資料和下一位址資料合成為想要的項目資料(上面講了資料分高低部分)。

[體感遊戲] 1、MPU6050資料采集傳輸與可視化

其實,利用序列槽藍牙子產品單片機要做的工作和對序列槽進行的操作一樣,對序列槽寫資料則送至藍牙子產品将資料發出,當外部有資料傳送過來時,單片機可以用相應的中斷捕獲該事件,然後接收消息。是以主函數中初始化序列槽和MPU6050之後就進入循環資料發送狀态,在循環中GetData是上面介紹的獲得3軸加速度、3軸角速度或溫度的值的函數,SendData則是将int類型的值轉換為字元串然後一位一位的發送出去,而最開始和最後分别發送一個#和$作為該幀的開始和結束标志位,具體格式如下:

#

1

2

3

5

4

-

$

注:符号位要麼為'-',要麼為空。

上面講到下位機通過序列槽藍牙将資料發送給上位機,那麼上位機如何接收藍牙信号呢?其實以我的筆記本為例,因為筆記本内置藍牙子產品,是以無需在上位機上獨立安裝一個USB-藍牙子產品。而上位機操作藍牙子產品和操作序列槽幾乎一模一樣。如下面的C#程式,當點選連接配接按鈕時執行個體化SerialPort,設定端口号、讀逾時、然後執行個體化一個序列槽資料接收事件句柄(這裡PortDataReceived作為資料接收的回調函數)。

在PortDataReceived中,隻要簡單調用Connection.Read(data, 0, length);就能從序列槽緩沖區讀取資料到data中。

注:本來是每次讀取1byte放入資料池,結果出現程式運作速度越來越慢,本以為是上面的資料池設計的有問題,結果把資料池裡的線程注釋掉改為ask函數來每次需要資料時才獲得,但是問題并不在于此;于是想到可能是繪制折線圖的函數有問題,但是重查了一遍發現問題不在于此;于是仔細測量每個過程耗時,發現每個子產品耗時正常,最後發現是由于序列槽緩沖區資料積累造成程式變慢,(因為下位機每20ms發送一次20byte的資料給上位機,上位機若一次不接收完所有資料,将會造成每次都有剩餘而逐漸變慢),于是直接改成每次接收20byte,問題得到解決。

由于下位機10ms發送一次20byte的資料,上位機一方面要做好接收工作,保證資料不擁擠在序列槽接收緩沖區;另一方面也要實時擷取目前從序列槽讀到的最新資料。如果采用傳統多線程+鎖的機制是可以的,但是當多線程中加入鎖勢必會影響程式執行效率,通過綜合分析該問題最終抽象出一個特殊的資料模型——自動更新的環形棧:

[體感遊戲] 1、MPU6050資料采集傳輸與可視化

這樣,當采用多線程時,用一個類似于棧的環狀棧結構體(實時從序列槽讀資料放入資料池,資料池用p_write标記最新資料存儲位置,當外部程式想得到最新資料時,調用ask程式,ask程式從目前p_write向前取40個資料(因為有效資料長度為20,一次取40保證至少有一個有效資料),然後從這40個資料中找出有效資訊,指派給X,Y,Z;然後外部程式可以直接用對象通路X,Y,Z),通過适當調節環的容量達到自我覆寫的效果,同時根據p_write指針可以實時取得最新資料。

通過上面幾步我們已經可以将下位機的陀螺儀3軸的加速度收集過來了,但是如果先将資料收集好,然後再用matlab繪制,我們很難知道哪個動作對應哪個資料,不利于我們觀察效果(雖然matlab上自帶序列槽接口,但是LZ就是任性!有一張好看的臉,還是想着靠實力赢得地位,哈哈哈~)。

如本節小标題括号内所示,在C#裡寫一個繪制折線圖的程式應該屬于我們的基本功(我可不是調用相應的繪圖接口哦!),其大緻思想就是用一個List存儲num個資料,當list中的資料少于num個時則不斷添加,當list内的資料大于num個時,則從尾部進來一個的同時從頭部删除一個(這樣才能實作perfect的效果)。

[體感遊戲] 1、MPU6050資料采集傳輸與可視化

注:其實中間還出現了一個邏輯錯誤性小插曲:原初寫好之後,本以為能夠實作高效資料采集顯示,但是仔細觀察發現還是有很大延時,但是旁邊的資料顯示卻非常實時。這是為什麼呢?查找了一會最終發現問題出在折線圖繪制上——本來采用固定的模式(一張圖能存放多少資料點就用vector&lt;int&gt;P/Q/R在初始化的時候存放這麼多點,然後每次有一個新的資料過來時就會将新資料加到vector後面,同時删除最前面的一個資料,這樣做是為了友善初始vector裡沒有資料繪制折線圖錯誤的問題),可是問題就出在這!咋一看這種思路很好,初始化vector中放num個點,每次新的來到将最前面一個資料沖掉,這樣這個vector始終保持着num個點,且最新的在最後面,整個折線圖能反應實時情況。但是由于我為了“安全”起見,在vector初始化時多Add幾個資料,這樣導緻vector中的資料量N&gt;折線圖一次能呈現的資料量num,是以最新的資料總會在之後出現!當時沒有想到是這個原因,就直接改了下DateLineChar函數,實作根據vector大小自動繪制的算法(這樣就不用預先在vector中裝入一定量的值了)

6、預告與小結(預知後事如何,請聽下回分解)

上面我隻是簡單收集了MPU6050的3軸加速度數值,當MPU6050位置固定好之後,我們就能根據資料推測其具體的姿态。例如:

[體感遊戲] 1、MPU6050資料采集傳輸與可視化

綠色的z軸方向的加速度先高後低,紅色y軸方向加速度先低後高,藍色x軸方向加速度和y軸類似,但是比y軸幅度變化小,而後半周期數值正負正好相反。那麼MPU6050運動過程大緻為:在y軸方向上做往返運動,同時在x軸和z軸方向有稍微的偏轉。(水準靜止放置時z軸為重力加速度,x,y為0)

[體感遊戲] 1、MPU6050資料采集傳輸與可視化

綠色的z軸變化不大,紅色的y和藍色的x同步類正弦變化。呵呵,這個運動狀态分析起來就不太容易了~不過沒關系,接下來我們要進一步擷取并計算MPU6050的傾角,甚至是利用卡爾曼濾波計算MPU6050的運動距離,最終達到perfect的運動跟蹤效果~

連結

本文轉自beautifulzzzz部落格園部落格,原文連結:http://www.cnblogs.com/zjutlitao/p/4396653.html,如需轉載請自行聯系原作者

繼續閱讀