2014年6月19日 星期四

讀取 GY-80 模組:加速度計(ADXL345) 陀螺儀(L3G4200) 電子羅盤(HMC5883) 氣壓計(BMP085)

這是一個很久以前買的模組

一直沒空搞它, 由於它上了四顆感測器, 這要翻不少文件
還要找一堆轉換公式, 麻煩極了, 所以就先丟著, 直到我又收到單子(?)才重啟
這貨是飛行玩具在用的, 像是四槳直升機, 用來感測機體的位置與方向
如上圖標示, 它的四顆感測器分別為:

加速度計 ADXL345
陀螺儀 L3G4200
電子羅盤 HMC5883
氣壓計 BMP085

搜尋 datasheet 時建議搜尋模組型號 GY-80, 這樣可以找到網頁一次給足所有資料
一個一個貼下去找反而麻煩, 而且還不會有範例程式
模組背面是長這樣

它有一堆接腳, 但我只會用四根, 也就是 i2c 的界面, 剩下是狀態腳和中斷腳
我這裡一律都用 polling mode, 目前我的應用還不需要中斷
這裡將依序紀錄各感測器的操作方法
i2c 界面將不做介紹, 這個資料到處都有, 不再重複, 文末會附上源碼供參考
所有的 i2c 通信都是 stop 後再 start, 沒有做 re-start (或稱 no-stop)
使用的板子是本實驗室自製的 WT-09, 對外連接的 D 形正反器已移除改為直連




加速度計 ADXL345
這貨的 i2c 位址是 0x53, 向左 shift 一位後加上 r/w bit 就可丟上 bus
它有多種量測模式, 這顆和別家的有點不同
別家的我看過的多是固定資料寬度, 透過暫存器切範圍
例如 10-bit 時, ±2g 範圍就是把 ±2g 切割成 10-bit
量測結果每多 1 就是多了 2g / 1024 = 0.0019g
而 ±4g 範圍就是把 ±4g 切割成 10-bit, 以此類推
這顆 ADXL345 可以進這種模式, 但也可以變成固定每個 bit 的 g 值增加量
也就是每多 1 就是多 4mg, 設定範圍越大值就越大, 資料寬度就越寬
最寬可以到 13-bit, 這樣比較好, 反正就設最大範圍, 資料收下就當 16-bit 整數算
我們就不需要去管現在到底要除多少, 只要把它都乘 4mg 就對了
初始化流程丟在這函數裡

void adxl345_init(void){
  i2c_set_reg(ADXL345_SLA, 0x2D, 0x00); // normal mode, standby
  i2c_set_reg(ADXL345_SLA, 0x31, 0x0B); // full resolution, +/- 16G
  i2c_set_reg(ADXL345_SLA, 0x38, 0x00); // bypass mode
  i2c_set_reg(ADXL345_SLA, 0x2C, 0x0A); // 100Hz
  i2c_set_reg(ADXL345_SLA, 0x2D, 0x08); // start measure
}

量測出來結果大概像這樣

當裝置平躺在桌上, 會量得向下的 1g 加速度
Z = 257, 257 * 4mg = 1028mg = 1.028g
而 X, Y 則接近 0
當感測器呈現自由落體時則是 X, Y, Z 都會接近 0
當感測器固定的板子被觸碰時則會在該軸上產生一個小的加速度
這些在 datasheet 裡都有寫, 主要用於特殊應用




陀螺儀 L3G4200
這貨的 i2c 位址是 0x69, 向左 shift 一位後加上 r/w bit 就可丟上 bus
官方說明文件 :

Technical article TA0343
Everything about STMicroelectronics’ 3-axis digital MEMS gyroscopes

它的初始化流程很少, 只要兩行

void l3g4200_init(void){
  // 200Hz, Cutoff 12.5, enable 3 axis, and start
  i2c_set_reg(L3G4200_SLA, 0x20, 0x4F);
  // block data update, 250dps 
  i2c_set_reg(L3G4200_SLA, 0x23, 0x80);
}

設定資料輸出速度, 然後啟動, 就這樣
第二行是設定只有讀完 LSB 和 MSB 才更新資料
避免分段讀取 16-bit 整數時讀到一半被更新導致資料錯亂
這顆的 datasheet 上寫了一堆功能, 可是有些好像不能用...
我的程式裡有留註解, 如果把 FIFO 打開, 資料還是會進去沒錯
但是會有一些奇怪的資料被塞進去, 看起來像是轉換出錯的
一開始以為是我系統電不穩, 電不穩會直接影響轉換結果
不過 google 一下發現這個問題老外的論壇也有提到
所以最後就是不要用 FIFO, 改成直接輸出, 輸出前先讀狀態暫存器
自己親自確認當前資料是否有效, 有效才讀
陀螺儀的資料是角速度 (Angular Velocity)
datasheet 看了老半天沒發現到底怎麼轉換單位
它只說有三種範圍, 250 / 500 / 2000 dps, 但沒說資料寬度
(16-bit 是資料格式, 不是最大範圍)
後來從老外的範例碼抽出, 每個 bit 的精度如下:
250dps : 0.00875
500dps : 0.0175
2000dps : 0.07
假設讀出值為 200, 範圍設 250dps, 那麼當前角速度就是 200 * 0.00875 = 1.75dps
dps = degree per second
degree 即是度數, 360 度 = 一圈那個, 不是數學常用的 radian (弧度)
如果需要三角函數計算會需要轉換一下
角速度要轉成方向需要乘上時間, d = V * t, d:距離 V:速度 t:時間, 簡單的國中數學
時間以感測器設 100 Hz 來說, 就是乘上 0.01 秒就可以獲得轉動量, 也就是轉了幾度
不過由於上面寫的, 有值可讀不代表值正確, 而等確認值後就不一定是 100 Hz 了
所以比較正確的算法應是量測時間後乘上去
量測時間可以設定 AVR 上的硬體 timer 來計算, 目前我沒這麼做, 只是概略的乘一個固定值
所以只要轉動太快就會失去方向, 這是執行結果:

顯示當前 X, Y, Z 軸所轉到的角度, 0-359 度
這貨是需要校正的, 需設定一個 0 值
類比轉數位難免會有雜訊, 就算都完全不動, 它仍然會量測到不規則漂移的極小值
廠商建議把這些值濾掉, 作法就是讓裝置不動, 然後讀取數十個樣本求平均
接著之後所有從感測器讀到的數都和這平均值比較, 低於平均的就丟棄, 當作裝置沒有動

由於陀螺儀不受磁場影響, 對於飛行設備或船來說是最理想的指示裝備
不過目前我還不需要它, 所以只是簡單讀取計算後丟出
如果要正確知道方位最好加上時間量測, 老外的範例碼幾乎都有做
一個老外的範例
Parallax Gyroscope Module 3-Axis L3G4200D sample code




電子羅盤 HMC5883
這貨的 i2c 位址是 0x1E, 向左 shift 一位後加上 r/w bit 就可丟上 bus
初始化流程也是少少的, 只要兩行

void hmc5883_init(void){
  i2c_set_reg(HMC5883_SLA, 0, 0x60); // 8 sample average
  i2c_set_reg(HMC5883_SLA, 1, 0x20); // +/-1.3 Ga
}

設定每次量測都是量 8 個樣本做平均才輸出
量測範圍為 ±1.3Ga (高斯, 磁力單位)
如果你和我一樣只是想知道方位, 那麼如何轉換成磁力值就不是那麼重要了
我的程式裡並沒有求磁力值, 若有需要可以查 datasheet
裡面有寫當範圍多少時對應的每個 bit 是幾個微高斯 (0.001Ga)
磁力感應器比較麻煩的是它一定要校正, 因為電能產生磁
而電路板上尤其電源部份一定會有磁力, 也就是有電感的地方
如果有外殼還會再受到影響, 所以組裝成系統後一定要校正
否則板上的磁場會和地球磁場混在一起, 難以區別方向
量測出的磁力值意義是這樣的 :
如下圖, 中間那顆球代表地球

隨便塗一下, 不要太計較XD
地磁會讓鐵分子呈南北向排列, 我們可以想像好像是由北流向南
這說法並不正確, 不過對我來說這樣比較容易想像, 尤其當考慮正負時
當感測器的 Y 軸指向磁北時, 可以量測到最大的正磁力值, 此時 X 軸的值為 0
如果把 X 軸指向磁北, 則 X 軸可以量測到最大的正磁力值, 此時 Y 軸的值為 0
如果把 X 軸背對磁北, 則 X 軸可以量測到最小的負磁力值, 此時 Y 軸的值為 0
如果我們把 X, Y 的值全丟出來畫成圖, 它會長得像這樣 :

就是一個圓
接著我們如果想知道方位, 就用國中數學算一下

這應該不用說明了吧XD
X^2 + Y^2 開根號就取得邊長, 然後用反餘弦函數 acos(y / L) 求得弧度
然後再把弧度換成角度就完成了
以往國中數學常常把 X 軸那裡當 0 度, 這裡要改一下
因為 Y 軸向北, 所以拿 Y 軸去求 acos
我們只是要知道 X, Y 的 "量", 並不需要知道它到底是幾高斯
所以直接把感測器出來的數字丟進去即可, 就不需要先算高斯值

以上是考慮理想狀態, 可惜的是如果加上電源磁力, 事情就不會那麼美好了
如果我們把 XY, YZ, ZX 的值全丟出來畫成圖, 它會像這樣

它還是圓沒錯, 但是不會在正中間
如果我們把上面的國中數學丟進去算, 那結果就是亂七八糟
以上圖藍色圈為例, 它將永遠不會有向南的角度值出現
因此我們會需要校正, 把他們都拉回中間, 要變成像這樣 :

這樣才有辦法知道方位
校正的方法就是找出每個軸的最大最小值, 然後求出中點值
每次讀取感測器值後都減去中點值, 它就會在中間了
校正時裝置要水平轉一圈, 垂直轉一圈, 以利找出最大最小值
校正的地點避免在室內, 以免受到鐵的家具影響

另外, 這是 X, Y 平面, 如果總是把三軸的值都丟出來
會發現 Z 方向還會有一個磁場, 由上往地心, 這個磁場我不知道怎麼解釋XD
如果有需要讓裝置可以站立著量測方向, 這個磁場要注意, 不要用它去計算方向
這需搭配加速度計來協同計算, 這裡我就沒做了

這是執行的畫面

-259 那是 Z 軸的值, 純參考用

校正畫面





氣壓計 BMP085
這貨的 i2c 位址是 0x77, 向左 shift 一位後加上 r/w bit 就可丟上 bus 
它不用初始化設定, 要量測時下命令即可
第一次量測前需從感測器讀出校正值, 它是一堆係數
這是工廠出廠時放進去的, 然後依照官方文件用一堆公式算一算就有了
各種感測器的值是否正確幾乎都和電壓以及溫度有關
有的感測器會把溫度補償自己做掉, 有的則要用戶自己算, 這顆就是屬於要自己算的
為什麼要這樣算就是他們的商業機密, 通常和他們的量測材料有關
我們搞系統所能做的就只有照著抄, 沒別的了
程式碼中所有 bmp085_ 開頭的函數自己翻一翻, 蠻多的, 所以就不貼出來了
算出來的單位是帕 (Pa), 氣象常用百帕 (hPa) 做單位, 也就是 1 hPa = 100 Pa
當高度每增加 8.43 公尺, 氣壓就會少 1 hPa, 我們可以用這來計算高度
由於這感測器可以量測到 Pa, 所以我的程式是用 Pa 去算
氣壓少 1 Pa 就假設是增加 0.0843 公尺, 發現它還真可以量得準
坐電梯就可以看到高度逐漸變化, 相當有趣
不過氣壓會隨氣候改變, 所以如果要知道當前高度, 得先知道地面氣壓
所以我的程式有做一個功能, 就是按個按鈕就存下當前氣壓
然後開始移動, 我就可以知道高度增加了多少


另外還提供標準資料來計算, 通常海平面氣壓會以 1013 hPa 作為基準
按個按鈕會出現 Std 圖示表示以 1013 hPa 為基準計算


如果不想移動而想看值變化, 可以試試將裝置丟進透明夾鍊袋, 充滿氣後封起來
然後按壓袋子, 氣壓值應該要會上升

附上源碼供參考

mcounter.zip

這包源碼是用於下篇 自製退伍計時器

17 則留言:

  1. 你好 想請問電子羅盤用於室內定位 其校正的問題 可以如何解決

    回覆刪除
    回覆
    1. 目前我無解, 請拿去室外吧

      除非把家具電器全移走, 牆壁裡鋼筋都抽掉!XD

      刪除
  2. 作者已經移除這則留言。

    回覆刪除
  3. 如果電子羅盤傾斜,值是不是就會改變?要用z軸來算補償對嗎

    回覆刪除
    回覆
    1. 是的, 但是要怎麼用 Z 補償, 得依賴別的感測器
      只從三軸磁力是沒法判斷是否傾斜的

      刪除
  4. 請問照hmc5883的datasheet來說,磁力計最小最大值是從0xF800–0x07FF(-2048~2047),那如果把磁力計轉了一圈 最大最小值卻是300~-700之類的奇怪數值,會不會本身電子羅盤有問題,還是在室內會有影響? 但感覺用atan2算出來的角度,轉一圈好像也正確?

    回覆刪除
    回覆
    1. 最大最小值是指可以量測的範圍, 地球磁場不一定會剛好到可量測的範圍
      這和你對感測器下的參數有關, 也就是 "每個 bit 是幾個微高斯" 那項
      每個 bit 代表的微高斯越少, 量測範圍就越小, 就有可能量到最大最小值
      但這樣遇到強磁鐵等物品時數值很容易就爆表, 這樣也不太好用
      設定值須視需求而定, 如果預期要量強磁, 範圍就要設寬些
      室內會影響的是磁性物品, 會影響到什麼程度和環境有關, 無法預測

      刪除
  5. 請問室外校正完 帶回室內還可以使用嗎?

    回覆刪除
    回覆
    1. 要看室內的影響狀況, 如果沒有磁性物就沒問題
      無磁性但金屬屏蔽好像也不行, 這我沒有實驗過
      如果有磁性物也不一定不行, 要看室內磁場情況
      如果要到未知狀況的室內運作, 那我會建議你假設它不能用

      刪除
    2. 所以版主使用compass都是在室外?
      因為是想裝在自走車上面,在室內自走用(轉90度),看來是很多困難。

      刪除
    3. 是的, 都在室外
      自走車如果是雙馬達, 也就是兩輪兩馬達 + 一萬向輪
      這種的只要一顆馬達不動, 另一顆計算轉的圈數
      這樣就能 90 度了, 這裡有一篇報告寫很詳細:
      http://www.86duino.com/wp-includes/file/Chapter07-TC.pdf

      刪除
  6. 請問版主有時候各種角度都轉很久了,最大最小值都沒在改變,結果不小心可能轉到某個角度值又改變很多,這樣要考慮新值嗎?明明先前怎麼轉都沒變。
    另外三軸的間距都不同(最大最小值相減的值),這樣有沒有可能是錯的,就算三軸有偏移,但是在同個環境總間距不是要相等嗎?還是說我可能沒轉出實際的間距。

    謝謝

    回覆刪除
    回覆
    1. ***因工作延後回覆請見諒***
      值突然改變的狀況請嘗試讀出轉圈時所有軸的值然後畫成圖
      如果確定沒有其他磁力影響, 畫出來的形狀若不是圓而是十字星形
      那有可能這感測器已經損壞, 不良的焊接溫度曲線有可能導致這樣
      三軸間距不同這我沒注意過, 不確定是否異常
      但是沒有準確的已知磁場很難說同個環境會相同, 這我沒法回答

      刪除
  7. 如果只是拿來做電梯的水平震動測量是否可行?

    回覆刪除
    回覆
    1. 水平震動測量用重力感測器即可, 一片一兩百就有, 不需這種
      如果只是要在電梯裡短暫測一下, 手機裝個 app 在電梯中平放觀測即可

      刪除