OBD2とPythonでシフトポジションを取る

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1990652/da6db102-28a4-c52c-fcbb-9278b88068dd.png)
# まえおき
[前回](https://qiita.com/nonono_copen/items/264fcba0b08cb0abcc05)のあらすじを3行で〜
● ドライブ中にコペンのシフト位置が不安だからシフトインジケータ作ろ!
● OBD2でシフト位置を含めたクルマの情報取れるかチェックや！
● 車速とかは取れたけど，シフト位置は取れないや〜ん（泣）どうにかせな・・・

ということで今回はどうにかしてシフトポジションを取る方法を考える．
シフトインジケータ作成のロードマップで言えば，以下の部分を実施していく！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1990652/df45f645-dea3-92da-f516-59eb386bc32f.png)

# シフトポジションの取り方
コペンの場合，OBD2からPID164から直接取ることは不可能であった．（[前回記事参照](https://qiita.com/nonono_copen/items/264fcba0b08cb0abcc05)）
だが！車速とエンジン回転数，その他諸々の情報があれば，シフトポジションが取れることがわかった！[参考にした記事](https://kurumashikou.com/gear-ratio-and-velocity/)


ギアポジションの特定に必要な情報は以下5つである．
※"[  ]"に後述する数式やコード内の変数名を記載する．

1. **対象車の各ギアの変速比**　 [_LA400K_GEAR_RATION_]
今回はコペン LA400KのMTが対象である．
[公式のコペン主要諸元表(PDF)-ダイハツ工業](https://www.daihatsu.co.jp/lineup/copen/pdf/spec.pdf)の”トランスミッションー変速比”から取得可能ある．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1990652/9831e603-0b9f-dad4-c060-17f1efc9e65c.png)

1. **最終減速比** [_F_]
[上記資料](https://www.daihatsu.co.jp/lineup/copen/pdf/spec.pdf)の主要諸元表にある最終減速比が対象の値である．
車種が異なる場合は各車種の主要諸元表をぐぐって拾って欲しい．

1. **タイヤ周径** [_R_]
ホイールを変えている場合は各自クルマのスペックから計算する．
コペンのノーマルタイヤは”165/50R16”（断面層/扁平率/インチ）である．
[本計算サイト](https://newcars.jp/tools/tire-wheel-size/)からタイヤの周径を計算可能である．

1. **車速** [_speed_]
OBD2からリアルタイムで取得する
1. **エンジン回転数** [_rpm_]
OBD2からリアルタイムで取得する

ギアポジションの特定方法として，その時の車速とエンジン回転数からギア比 [_GearRation_]を計算し，そのギア比に最も近い値のギアポジションを返す．
計算式としては以下の通りである．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1990652/dffaa95a-5100-e562-b8f2-993908e6119f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1990652/4c5e273b-0459-6f7a-7c76-6bf4c3aa64d7.png)

上記のGearRationに最も近いギアポジションを返す！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1990652/5b7d98fe-8b09-1f83-61d4-bfc3d9e5c8b6.png)

難しいようなことを言っているようで全くムズかしくない．．．
（言語化するとムずくなる．．．）

ヨウスル二，ギア比を計算して，それがどのギアポジションの値なのか？を見てあげるだけである！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1990652/3e22044d-a096-0c02-064d-d0a9ba498394.png)

実装したコードを次章にて説明する．

# ギアポジションの取得
上記の計算式をPythonに落とし込む．
コードは以下の通りである

```Python:gearPosition.py
###############################################
# 車速とエンジン回転数からギアポジションを取得
###############################################
import time
import numpy as np
import obd
from   obd import OBDStatus

###############################################
# 定数 CONST
# コペン LA400K（MT）の値
# 車種等が違う場合は値を変更する必要あり
###############################################
LA400K_GEAR_RATION = [ 3.181, # 1速
					   1.842, # 2速
					   1.250, # 3速
					   0.916, # 4速
					   0.750  # 5速
					 ]
R = 1.795                     # タイヤ周径
F = 5.545                     # 最終減速比

###############################################
# 関数 define
###############################################
def cal_gearPosition(gearRation):
	"""
	現在ギア比（gearRation）と変速比表（LA400K_GEAR_RATION）から
	現在ギア位置（gearPosition）を返す（変速比表から一番近いギア比のidxを返す）
	Input	: ギア比（gearRation）
	Output	: ギア位置（gearPosition）
	"""
	gearPosition = np.argmin(np.abs(np.array(LA400K_GEAR_RATION) - gearRation)) 
	gearPosition = gearPosition + 1 # idxが0から始まっているので+1
	return gearPosition

def cal_gearRation(rpm, speed):
	"""
	回転数（rpm）と車速（speed）からギア比（gearRation）を求める
	Input	: 回転数（rpm）, 車速（speed）
	Output	: ギア比（gearRation）
	"""
	gearRation = R * rpm / (speed * F) * 60 / 1000
	return gearRation

###############################################
# メイン main
###############################################
if __name__ == '__main__':
	connection = obd.OBD()      # OBD2とコネクションを確立
	print (connection.status())

	if connection.status() == OBDStatus.CAR_CONNECTED:
		while(True):
			try:
				rpm   = connection.query(obd.commands.RPM).value.magnitude
				speed = connection.query(obd.commands.SPEED).value.magnitude

				if speed != 0: # speed=0だと除算でエラーが発生するため
					gearRation   = cal_gearRation(rpm, speed)        # ギア比を計算
					gearPosition = cal_gearPosition(gearRation)      # ギアポジションを計算
					print("GearPosition is {}".format(gearPosition)) # ギアポジションを表示
				else:
					gearRation   = 0
					gearPosition = 0
					print("speed is 0, cannot get GearPosition")
				time.sleep(.2) # 0.2s 停止

			except KeyboardInterrupt:
				pass

	connection.close()

```

案外短いでしょ？
実行結果は以下の通りである．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1990652/be9d124a-4e63-ba8b-7ab5-6d0563942067.png)

クルマが動いていない（車速が0km/h）の時は，0除算になってしまうため，
シフトポジションが取得できないようになっている点は要注意である．

リアルタイム性はどの程度なのか，動画をいずれ出す．
[ToDO：実行画面とシフト操作を移した動画をここに載せる]

精度は．．．．結果を記載
ギア飛ばしは．．．．結果を記載
（この辺は後ほど評価，今はコードを共有することを優先）

残念ながら，今回のこの手法ではバックギアは取れないため，
バックギアは直接バックギアの信号を電気的に取得する必要があるかも．
（カーナビのバックカメラ信号などから取れるかと考えている）

# まとめ
OBD2から直接シフトポジションが取れないことから，
車速とエンジン回転数とその他諸々の情報からシフトポジションを取得する方法を記載した．

結果として，バックギア以外のギア情報は取得できることが確認でき，
精度としては〇〇であった．またギア飛ばしの対応も〇〇である．

次回以降の作業として，シフトインジケータのデザイン決めとRasberryPiへの実装を行うこととする．



# 参考
[Pythonのリスト要素からある値と最も近い値を取り出す](https://qiita.com/icchi_h/items/fc0df3abb02b51f81657)

[ギア比と回転数、速度の関係のグラフ](https://kurumashikou.com/gear-ratio-and-velocity/)

[コペン主要諸元表(PDF)](https://www.daihatsu.co.jp/lineup/copen/pdf/spec.pdf)