QMC5883Lをpythonで使うための忘備録

巷で混同されているHMC5883LとQMC5883LですがQMC5883Lが主流になりつつあるので,
とりあえずHMC5883Lと思って間違えて買っちゃった方向けに,QMC5883Lの動作確認用に作りました.
最後のコードをこのままコピペして走らせると,CSVに保存されます.想定している環境はラズベリーパイです.

2024/05/21　追記
説明書見つけたので追記します
0x0Aに0x80を書き込むとリセットされるそうです.スケールは8Gに設定してるので,
出てきた値を3000で割ります.

参考記事
https://gujorobo.blogspot.com/2017/09/tj3bhmc5883lqmc5883l.html
https://github.com/RigacciOrg/py-qmc5883l/blob/master/py_qmc5883l/__init__.py

# 第一関門
そもそも認識されるか
```
 sudo i2cdetect -y 1
 ```
 認識されたら画像のように0x0dが表示されます.
 
![スクリーンショット 2024-05-16 235346.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3647460/70d8bbc8-633c-195d-6263-45103b449b97.png)

# いざ動作確認
```
import smbus2
import time
import csv

bus = smbus2.SMBus(1)
bus.write_byte_data(QMC5883L_ADDR, 0x0A, 0x80) #reset　#5/21追記

bus.write_byte_data(QMC5883L_ADDR, 0x09, 0x1D)  # define OSR=512,full scale range 8g odr 200hz
time.sleep(0.01)　#5/21追記

def get_mag():
    data = bus.read_i2c_block_data(QMC5883L_ADDR, QMC5883L_XOUT_L, 6)
    x = data[0] | (data[1] << 8)
    y = data[2] | (data[3] << 8)
    z = data[4] | (data[5] << 8)
    if x >= 32768:
        x -= 65536
    if y >= 32768:
        y -= 65536
    if z >= 32768:
        z -= 65536
    data=np.array([x,y,z])/3000   #8G: 3000LSB/G 2G:12000LSB/G　#5/21追記
    return data

with open('QMC5883test.csv', 'w', newline='') as file:
    writer = csv.writer(file)
    writer.writerow(['x','y','z'])
    while True:
        QMC5883_mag=QMC5883.get_mag()
        print(QMC5883_mag)
        writer.writerow([QMC5883_mag[0],QMC5883_mag[1],QMC5883_mag[2]])
```

平置きで一回転すると,xyでグラフを作ったときに円になるはずです.
![xy_graph.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3647460/5913b791-21a1-d63f-aeaf-ec026d6254ac.png)

atan2を使えばheading角が出ます.
![heading_graph.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3647460/41d7c246-fd3a-9e21-f288-1a8b5767a4bb.png)


キャリブレーションの１例として,最大値と最小値でオフセットする方法があります.
オフセットさせるとより正確なheading角が計算できると思います.
![xy_shifted_graph.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3647460/39c1b6e6-41ab-9611-6892-56e9ec353ed9.png)
![heading_shifted_graph.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3647460/3c3f1c69-51a3-87ec-7124-e1bb1ad1f3cf.png)
# 付録
グラフ作成は以下でやりました.
```
import csv
import matplotlib.pyplot as plt
import math
import numpy as np
# データを格納するリストを初期化
xx=[]
x = []
y = []
heading=[]
# CSVファイルを読み込む
with open('QMC5883test.csv', 'r') as file:
    reader = csv.reader(file)
    next(reader)  # 1行目を無視
    for row in reader:
        x.append(float(row[0]))
        y.append(float(row[1]))
        heading.append(math.atan2(float(row[1]),float(row[0])))
x=np.array(x)
y=np.array(y)

maxx=max(x)
minx=min(x)
maxy=max(y)
miny=min(y)
shiftedx=[]
shiftedy=[]
shifted_heading=[]
for i in range(len(x)):
    shiftedx.append(x[i]-(maxx+minx)/2)
    shiftedy.append(y[i]-(maxy+miny)/2)
    shifted_heading.append(math.atan2(shiftedy[i],shiftedx[i]))
heading = np.array(heading)
xx = range(len(heading))

# グラフを作成
plt.figure()
plt.plot(x, y, marker='o', linestyle='-',label='original')
plt.plot(shiftedx, shiftedy, marker='o', linestyle='-',label='shifted')

# タイトルとラベルの設定
plt.title('xy')
plt.xlabel('x(G)')
plt.ylabel('y(G)')
# 凡例を表示
plt.legend()
# 軸のスケールを等しくする
plt.axis('equal')
plt.grid(True)

# グラフを保存
plt.savefig('xy_shifted_graph.png')

# グラフを表示（オプション）
plt.show()

plt.figure()
plt.plot(xx, heading, marker='o', linestyle='-',label='original')
plt.plot(xx, shifted_heading, marker='o', linestyle='-',label='shifted')

# タイトルとラベルの設定
plt.title('heading')
plt.xlabel('row')
plt.ylabel('heading(rad)')
# 凡例を表示
plt.legend()


# グラフを保存
plt.savefig('heading_shifted_graph.png')

# グラフを表示（オプション）
plt.show()
```