自分の車両の馬力がどれくらいで、どのような出力特性をしているかは、車好きならば気になるところ。例えば、ECUを交換するなどのパワー系チューニングを実施した結果、どれくらい出力が変化したのか、効果測定をしてみたいと思うハズだ。
そんなニーズを満たしてくれるのがシャシダイナモ(=シャシダイ)だが、計測は専門店に行かないとできないし、費用もそれなりにかかってしまう。そこで、大掛かりな設備を必要とせず、サーキットなどの安全な直線路があればお気軽に馬力を計測する手法が無いかを考えてみた。
時系列速度データと車両パラメータから馬力推定
ArduinoでS660のCAN情報を取得し、車速と思われる情報を時系列でサンプリングして馬力を計測する。この手法では、シャシダイに比べて絶対精度はかなわない一方、ギア固定(例えば2速)で全開加速した時に各回転数でどれくらいの加速度が得られるかの実測データを算出することができる。
また、部品を交換した前後での車両の相対的な変化を簡単に計測できるため、効果測定用のデータとしては役に立つと考えている。更に、他車両でもCANの速度データを解析できれば流用できる方法であるので汎用性も高い。
実際に、この記事で作成したプログラムを使って、こちらの記事ではアルトワークスの馬力計測を行っている。
馬力計測の原理
馬力=トルク×回転数で算出が可能なことを利用し、車両からCAN経由で取得した速度の時系列データから加速度を計算。加速度からトルクと馬力を算出する。
馬力とトルクの関係はこちらの記事を参照
トルクの算出
詳細の物理については知見が深いサイトに説明を譲るとして、トルクは、
で算出することができる。
トルクを算出する情報のうち、変速比・ファイナルギア比・車重はMT車ならカタログに載っている値で計算できそう(ただし、CVT車の場合にはギア比が不明なためこの手法は使えない)。
タイヤ半径については、S660の場合は駆動輪が後輪なので、後輪のサイズを用いることとした。加速度は時間あたりの速度増加量なので、速度の時系列データさえあれば算出できる。
馬力を計算する為に必要となる回転数は、ギアが固定(例えば2速)であれば速度から回転数は計算できる。
速度から回転数を算出する手法についてはこちらの記事を参照
ということで、速度の時系列データを取得して、トルクを算出。速度から回転数を計算し、トルク×回転数で馬力を算出してみた。
速度データの取得(Arduino+CAN-BUS Shield V2.0)
ArduinoでCANデータを取得するモジュールは以前作成済み。
Arduinoで取得するCANデータIDの選定
今回最も時間がかかったのがCANデータの解析。先行で、以下のサイトで解析が進んでいたため、ありがたくその情報を参考にさせていただいた。
この情報をもとに彷徨っているとCANのIDとデータ内容を.dbc形式でgithubにUploadしているプロジェクトがあった。以下リンク先はFITのCANデータ一覧だが、上記リンク内で解析されている内容と共通部分も多く見られたので、こちらも参考にしてみた。
Fit用としてUpされているdbc形式のファイルを見てみると、次のような記載になっている。
BO_ 464 WHEEL_SPEEDS: 8 VSA
SG_ WHEEL_SPEED_FL : 7|15@0+ (0.01,0) [0|250] "kph" EON
SG_ WHEEL_SPEED_FR : 8|15@0+ (0.01,0) [0|250] "kph" EON
SG_ WHEEL_SPEED_RL : 25|15@0+ (0.01,0) [0|250] "kph" EON
SG_ WHEEL_SPEED_RR : 42|15@0+ (0.01,0) [0|250] "kph" EON
SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON
こちらを参考に.dbcファイルを読み解いてみると、この記載は以下のように解釈できる。
BO_ 464 WHEEL_SPEEDS: 8 VSA の意味
ID 464(16進数で0x1D0)は8バイトのデータを持っていて、送信しているのはVSA。名前はWHEEL_SPEEDとする。
SG_ WHEEL_SPEED_RL : 25|15@0+ (0.01,0) [0|250] “kph” EON の意味
WHEEL_SPEEDに含まれるシグナル、WHEEL_SPEED_RLを取得。
25|15@0+ ⇒ ID_464(0x1D0)の25ビット目からスタートして、15ビット分を使う。情報はビッグエンディアンで、符号なし。
(0.01,0) ⇒ ×0.01する。加算(+)する値は0。
[0|250] ⇒最小値は0、最大値は250まで。
“kph” ⇒単位はkph(km/h)
ということで、ArduinoとCAN-BUS Shieldを用いればタイムスタンプ付きのデータが取得できるので、車速データと思われるID464(0x1D0)を取得することに決定。Arduinoのスケッチ(プログラム)を作成した。
こちらの記事に記載しているスケッチを改修し、フィルターでID464のみ取得してSDカードに保存している。
実走でのデータ取得
最大出力を計算するには、平坦な場所でアクセル全開のフル加速時のデータを取得する必要がある。S660の2速で7700回転時の速度は78km/hとなるため、フル加速データを安全に計測できる場所は自ずと限られてくる。
実際に取得を考えている場合は法的にも、安全にも配慮して計測して欲しい。
今回は2速固定でアクセルをベタ踏みフル加速~レブリミッタ付近まで回した後、1200回転程度まで減速する走行を5回行って、CANログデータを取得した。サンプリングタイムにもよるが、データ数を増やすとバラつきを減らす効果がある。
ArduinoのSDカードに保存されるデータフォーマット
取得したデータは、時系列のCSV形式とした。Arduino側で、以下フォーマットでSDカードに格納されるようにプログラムを作成。Timestamp[ms], CAN ID(dec), DataLength, Byte0(dec), Byte1(dec), …. Byte7(dec) でSDカードに保存される。実際にArduinoで取得したデータの抜粋を下記に示す。
8017,464,8,0,0,0,0,0,0,0,10
8038,464,8,0,0,0,0,0,0,0,10
8059,464,8,0,0,0,0,0,0,0,10
8077,464,8,0,0,0,0,0,0,12,224
8097,464,8,1,166,3,68,6,160,12,228
8120,464,8,1,166,3,68,6,160,13,167
8137,464,8,1,190,3,116,6,248,13,174
8158,464,8,1,212,3,160,7,88,14,99
8179,464,8,1,212,3,160,7,88,15,23
8199,464,8,1,230,3,200,7,168,15,21
8220,464,8,1,250,3,240,8,0,15,188
8241,464,8,1,250,4,24,8,0,16,85
8261,464,8,2,16,4,24,8,80,16,253
CANデータの解析と、グラフ表示
CANデータ解析は、Excelでもできるが、配列データを処理するにはちょっと面倒。以前はMatlabのオープンソース互換のOctaveを利用していたが、今回は流行りの(乗り遅れてるww)Pythonを学習してみたかった。そこで、Pythonでの解析からグラフの描画までをプログラミングしてみた。C言語やMatlabを使ったことがあれば、ググりながら数時間イジるとクセがつかめると思う。
SDカードに取得した速度データを解析したのがこちら。
この速度データから
・サンプリング時間あたりの速度差を計算し、加速度を算出する。
・速度に対応した回転数を算出する。
そして、加速度・車重・ギア比・タイヤ半径からトルクを計算し、対応する回転数から馬力を計算するプログラムを作成した。Pythonのプログラム実行結果で表示される以下グラフは、横軸回転数、縦軸はトルクと馬力を示している。S660のトルク特性として、低回転からトルクがモリモリ立ち上がる特性と、高回転で漸減している様子が見て取れる(解析用ソースコードはこの記事の最後に添付)。
これは、感覚的にもその通りで、低回転でアクセル全開すると、2000~3000rpmからガツンと立ち上がるフィーリング。さらに、今回のデータは、馬力は最大55馬力、トルクは80N・m程度と言う結果に。しかし、S660のプレスリリース公式資料にあるような特性にはなっていない。
S660のカタログSPECを確認してみると、最高出力64馬力(6,000rpm) 、最大トルク104N・m (2,600rpm)となっているので、今回作成したグラフは最大トルク・最大出力の値もズレが有る。この違いはおそらく、空気抵抗・タイヤの抵抗・トランスミッションのロス、などがが影響していると思われる(Hondaの公式データはエンジン単体?)。
補正パラメータで追い込むことはできそうだが、目的はパーツを変えた際の変化した特性の計測であるため、今回はこのままで良しとした。なぜなら、外的要因を含めて車両が実際に出力できた馬力のはずで、シャシダイナモでは表現されない実走時の空気抵抗などを含めて表現できていると考えるからだ。今後例えばECUのチューニングを行ったり、部品をセッティングしていく際にとても頼りになるツールになるだろう。
今回使用したデバイス
Arduino
Arduino Unoでも良いと思うが、色々遊んでみたかったので、Arduino megaを選定。
CAN-BUS Shield V2.0
OBDコネクタ
D-SUB9ピン メスコネクタ
CAN-BUS Shield V2.0と車両を接続する際に必要なコネクタ。はんだ付けして使う。
データ取得プログラム(Arduino)
ArduinoとCAN-BUS Shield を組み合わせて車両からCANデータを取得し、実装されているmicroSDカードスロットルを利用して、microSDカードに取得したCANデータを保存するプログラムを作成した(こちら)。
データ解析プログラム(Python)
プロのレビュアーや解析のエキスパートの皆様、算出の考え方や計算方法等、アドバイスがありましたらコメントお願いします。
"""
File: power_analysis_s660.py
Author: 岡本一車
https://kurumashikou.com/
"""
#必要なライブラリのimport
import numpy as np;
import csv
import math;
from matplotlib import pyplot as plt;
# ログファイルの定義 ----------------------------------------------------------------------------------------
# プログラムディレクトリへのパス
dirPath = "D:\power_analysis";
# ログディレクトリへのパス
logFileDir = dirPath + "\\s660_candata";
# ログファイルへのパス
logFilePath = logFileDir + "\\" + DATALOG_.CSV";
#車両
car_name = "S660";
""" 車両個別設定項目 ----------------------------------------------------------------------------------------
車重
タイヤ
ギア比
を設定する。
------------------------------------------------------------------------------------------------------------ """
# 車重[kg]
weight = 830 + 100;
# タイヤの設定 -----------------------------------------
# タイヤ太さ[mm] ex:195/45R16なら195
tire_width_mm = 195;
# 扁平率[%] ex:195/45R16なら45
tire_aspect_ratio = 45;
# ホイールサイズ[inch] ex:195/45R16なら16
wheel_size = 16;
# ギアレシオ -------------------------------------------
"""
S660の場合
final: 4.875
1st: 3.571
2nd: 2.227
3rd: 1.529
4th: 1.15
5th: 0.869
6th: 0.686
"""
gear_ratio_arr = [
4.875,
3.571,
2.227,
1.529,
1.15,
0.869,
0.686,
];
# 計測に使用したギア
gear_select = 2;
# 走行するギアレシオを設定する
gear_ratio = gear_ratio_arr[gear_select];
final_gear_ratio = gear_ratio_arr[0];
# データ取得の最低回転数
min_rpm = 1500;
max_rpm = 7700;
""" ----------------------------------------------------------------------------------------------------------
移動平均を算出する関数
arg1:データのlist
num:移動平均算出の個数
return 移動平均算出後のLIST
データ個数が不足している場合、0が入る
例えば… moving_Average([2, 4, 9, 17],3) の結果、
returnは[0, 0, 5.0, 10.0]
(2+4+9)/3 ↑ ↑(4+9+17)/3
---------------------------------------------------------------------------------------------------------- """
def moving_Average(in_list, num):
cumsum = [0];
ret_mvingAve = [];
for i, x in enumerate(in_list, 1):
cumsum.append(cumsum[i-1] + x);
if i>=num:
elem_movingAve = (cumsum[i] - cumsum[i-num])/num;
ret_mvingAve.append(elem_movingAve);
for j in range(num-1):
ret_mvingAve.insert(0,0);
return ret_mvingAve;
""" ----------------------------------------------------------------------------------------------------------
ログデータの読み込み処理
読み込むCSVファイルフォーマット
TimeStamp[ms],CAN-ID,DLC, ...
DateByte1, DateByte2, DateByte3, DateByte4, DateByte5, DateByte6, DateByte7, DateByte8
---------------------------------------------------------------------------------------------------------- """
# CSV形式のログファイルから、データを読み込む
csv_file = open( logFilePath, "r", encoding="ms932", errors="", newline="");
# CSVファイルからデータを読み込むためのReaderオブジェクトを設定
CanLogReader = csv.reader(csv_file);
# 生のログ全データを抽出し、保持
CANLogRawData = [row for row in CanLogReader];
# NumPyのArray型に変更
CANLogRawData = np.array(CANLogRawData);
# CSVファイルのClose
csv_file.close();
""" ----------------------------------------------------------------------------------------------------------
Listデータの型を数値型に変換する
-------------------------------------------------------------------------------------------------------- """
for i in range(len(CANLogRawData)):
for j in range(len(CANLogRawData[i])):
# 格納されている値が10進数かどうかを確認して、10進数に変換できないデータは-1に変換する
if(not CANLogRawData[i][j].isdecimal()):
CANLogRawData[i][j] = -1;
# 全体をキャストしてint型に変更
CANLogRawData = CANLogRawData.astype(np.int_);
""" ----------------------------------------------------------------------------------------------------------
結果算出用に、時間およびΔtを算出する
-------------------------------------------------------------------------------------------------------- """
# 時間の取得
log_time = np.array(CANLogRawData[:,0]);
# 差分取得用に、1サンプル進んだt1を作成
delta_t1 = np.array(log_time[1:]);
# log_time
delta_t = delta_t1 - log_time[0:-1];
# 結果の先頭に0を追加
delta_t = np.append([0], delta_t);
"""----------------------------------------------------------------------------------------------------------
CANデータ変換による車速の算出
https://github.com/commaai/opendbc/blob/master/honda_fit_ex_2018_can_generated.dbc
を参照。
BO_ 464 WHEEL_SPEEDS: 8 VSA
SG_ WHEEL_SPEED_FL : 7|15@0+ (0.01,0) [0|250] "kph" EON
SG_ WHEEL_SPEED_FR : 8|15@0+ (0.01,0) [0|250] "kph" EON
SG_ WHEEL_SPEED_RL : 25|15@0+ (0.01,0) [0|250] "kph" EON
SG_ WHEEL_SPEED_RR : 42|15@0+ (0.01,0) [0|250] "kph" EON
SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON
----------------------------------------------------------------------------------------------------------"""
# CANデータ部だけを抽出する
canData = np.array(CANLogRawData[:,3:11]);
#FL車速の取得 --------------------------------------------------------------------------------------------
# |byte0 |byte1 |byte2 |byte3 |byte4 |byte5 |byte6 |byte7 |
# |11111111|11111110|00000000|00000000|00000000|00000000|00000000|00000000|
# |<-----data----->|
# --------------------------------------------------------------------------------------------------------
# byte0,byte1を抜き出す
WHEEL_SPEED_FL = canData[:,0:2];
# 抜き出したbyteに対して、対象のビットのみ取得する演算
WHEEL_SPEED_FL = (WHEEL_SPEED_FL & [0b11111111,0b11111110]);
# byte1の最下位ビットが1桁目となるようにビットシフト、byte0の最下位ビットが8桁目となるようにシフト
WHEEL_SPEED_FL = ([WHEEL_SPEED_FL[:,0] << 7 , WHEEL_SPEED_FL[:,1] >>1]);
# byte1とbyte2を足し合わせて、Factor0.01をかける
WHEEL_SPEED_FL = np.sum(WHEEL_SPEED_FL, axis=0)*0.01;
#FR車速の取得 --------------------------------------------------------------------------------------------
# |byte0 |byte1 |byte2 |byte3 |byte4 |byte5 |byte6 |byte7 |
# |00000000|00000001|11111111|11111100|00000000|00000000|00000000|00000000|
# |<-----data ----->|
# --------------------------------------------------------------------------------------------------------
# byte1,byte2,byte3を抜き出す
WHEEL_SPEED_FR = canData[:,1:4];
# 抜き出したbyteに対して、対象のビットのみ取得する演算
WHEEL_SPEED_FR = (WHEEL_SPEED_FR & [0b00000001,0b11111111,0b11111100]);
# byte1の最下位ビットが1桁目となるようにビットシフト、byte0の最下位ビットが8桁目となるようにシフト
WHEEL_SPEED_FR = ([WHEEL_SPEED_FR[:,0] << 14 , WHEEL_SPEED_FR[:,1] << 6, WHEEL_SPEED_FR[:,2] >>2]);
# byte1とbyte2を足し合わせて、Factor0.01をかける
WHEEL_SPEED_FR = np.sum(WHEEL_SPEED_FR, axis=0)*0.01;
#RL車速の取得 --------------------------------------------------------------------------------------------
# |byte0 |byte1 |byte2 |byte3 |byte4 |byte5 |byte6 |byte7 |
# |00000000|00000000|00000000|00000011|11111111|11111000|00000000|00000000|
# | |<-----data ----->|
# --------------------------------------------------------------------------------------------------------
# 先頭からbyte3,byte4,byte5を抜き出す
WHEEL_SPEED_RL = canData[:,3:6];
# 抜き出したbyteに対して、対象のビットのみ取得する演算
WHEEL_SPEED_RL = (WHEEL_SPEED_RL & [0b00000011,0b11111111,0b11111000]);
# byte1の最下位ビットが1桁目となるようにビットシフト、byte0の最下位ビットが8桁目となるようにシフト
WHEEL_SPEED_RL = ([WHEEL_SPEED_RL[:,0] << 13 , WHEEL_SPEED_RL[:,1] << 5, WHEEL_SPEED_RL[:,2] >>3]);
# byte1とbyte2を足し合わせて、Factor0.01をかける
WHEEL_SPEED_RL = np.sum(WHEEL_SPEED_RL, axis=0)*0.01;
#RL車速の取得 --------------------------------------------------------------------------------------------
# |byte0 |byte1 |byte2 |byte3 |byte4 |byte5 |byte6 |byte7 |
# |00000000|00000000|00000000|00000000|00000000|00000111|11111111|11110000|
# | |<-----data ----->|
# --------------------------------------------------------------------------------------------------------
# 先頭からbyte5,byte6,byte7を抜き出す
WHEEL_SPEED_RR = canData[:,5:8];
# 抜き出したbyteに対して、対象のビットのみ取得する演算
WHEEL_SPEED_RR = (WHEEL_SPEED_RR & [0b00000111,0b11111111,0b11110000]);
# byte1の最下位ビットが1桁目となるようにビットシフト、byte0の最下位ビットが8桁目となるようにシフト
WHEEL_SPEED_RR = ([WHEEL_SPEED_RR[:,0] << 12 , WHEEL_SPEED_RR[:,1] << 4, WHEEL_SPEED_RR[:,2] >>4]);
# byte1とbyte2を足し合わせて、Factor0.01をかける
WHEEL_SPEED_RR = np.sum(WHEEL_SPEED_RR, axis=0)*0.01;
# リア車速の平均を求め、馬力算出用の速度とする
"""
vel_kph = np.array([[WHEEL_SPEED_RL],[WHEEL_SPEED_RR]]);
vel_kph = vel_kph.mean(axis=(0,1));
"""
vel_kph = np.array(WHEEL_SPEED_RL);
# 算出された速度に移動平均フィルタをかける
vel_kph = np.array(moving_Average(vel_kph,4));
"""----------------------------------------------------------------------------------------------------------
速度[km/h]から加速度[m/s2]を算出
----------------------------------------------------------------------------------------------------------"""
# 速度の差分を計算
delta_v1 = np.array(vel_kph[1:]);
# 1データ前の速度との差分を取得
delta_v = delta_v1 - vel_kph[0:-1];
# 1データあたりの加速度を算出し、サンプリングタイムからm/s2に変換
accel_mps2 = delta_v/3.6*1000/delta_t[1:];
# 結果の先頭に0を追加(1番目のデータは0とする)
accel_mps2 = np.append([0], accel_mps2);
# 加速度に移動平均フィルタをかける。加速度は重めのフィルタが必要
accel_mps2 = np.array(moving_Average(accel_mps2,15));
"""----------------------------------------------------------------------------------------------------------
速度[km/h]から回転数[rpm]を算出
----------------------------------------------------------------------------------------------------------"""
# タイヤ直径[m] = ((2 × タイヤ太さ[mm] × 扁平率[%]/100) + (ホイールサイズ[inch] × 25.4(インチ変換定数))/1000
tire_diameter = ((2*tire_width_mm*tire_aspect_ratio/100)+(wheel_size*25.4))/1000
# タイヤ外周[m] = タイヤ直径 × π
tire_circumference = tire_diameter*math.pi;
# 回転数[rpm] = (速度[km/h]×ギア比×ファイナルギア比)/タイヤ外周[m]×1000/60
tacho_rpm = np.array((vel_kph*gear_ratio*final_gear_ratio)/tire_circumference*1000/60);
"""----------------------------------------------------------------------------------------------------------
加速度[m/s2]からトルク[N・m]を算出
----------------------------------------------------------------------------------------------------------"""
# トルク[N・m] = 加速度[m/s2]/(ギア比×ファイナル) × タイヤ直径/2 × 車重[kg]
torq_nm = np.array(accel_mps2/(gear_ratio*final_gear_ratio)*(tire_diameter/2)*weight);
"""----------------------------------------------------------------------------------------------------------
トルク[N・m]と回転数からパワー[kw]と馬力[ps]を算出
----------------------------------------------------------------------------------------------------------"""
# パワー[Kw] = 2π x トルク(N・m) x 回転数(rpm) / 60 / 1000
power_kw = np.array(2*math.pi*torq_nm*tacho_rpm/60/1000);
# トルク、パワーの計算結果を格納(回転数、トルク、パワー、馬力)
power_data = np.array([tacho_rpm, torq_nm, power_kw, power_kw*1.35962]);
"""----------------------------------------------------------------------------------------------------------
取得したデータを整形する。不要なデータの削除およびマイナス値の削除、並び替え
----------------------------------------------------------------------------------------------------------"""
# 数値マイナスのものを削除
power_data = np.delete(power_data, np.where(power_data<0)[1], axis=1);
# 回転数がmin_rpm~max_rpmで指定した範囲に絞り込み
power_data = power_data[:,(power_data[0]>min_rpm)&(power_data[0]<max_rpm)];
# 馬力データを回転数で並び替え
sorted_index = np.argsort(power_data[0]);
power_data = power_data[:,sorted_index];
# ここまでの処理で、以下の配列が得られた
# power_data =
# [
# [昇順にソートされた回転数rpmの配列],
# [回転数に対応したトルクN・m],
# [回転数に対応したパワーkw],
# [回転数に対応した馬力ps]
# ]
# ----------------------------------------------------------------------------------------------------------
"""----------------------------------------------------------------------------------------------------------
近似曲線の作成
----------------------------------------------------------------------------------------------------------"""
# 近似曲線の始点、終点の定義。最高回転数のレブリミッターが効くので、そのより少し低めに設定
poly_x_ax = list(range(min_rpm,max_rpm-200,10));
poly_y_axTorq = np.polyfit(power_data[0,:], power_data[1,:], 10);
poly_y_axPS = np.polyfit(power_data[0,:], power_data[3,:], 10);
"""----------------------------------------------------------------------------------------------------------
グラフの描写
----------------------------------------------------------------------------------------------------------"""
# Figureの作成
velfig = plt.figure();
velfig_ax1 = velfig.add_subplot(111);
velfig_ax1.set_title("vel kph " + car_name);
velfig_ax1.set_xlabel("time[ms]");
velfig_ax1.set_ylabel("vel[kph]");
plt.plot(log_time,vel_kph);
# Figureの作成
fig = plt.figure();
# 軸を追加
ax1 = fig.add_subplot(111);
# 軸の設定(回転数、トルク)
ax1.set_xlim(min_rpm-100, max_rpm+100);
ax1.set_ylim(0, int(max(power_data[1,:])*1.2));
ax1.set_title("Power and Torque curve " + car_name);
ax1.set_xlabel("EnginSpeed[rpm]");
ax1.set_ylabel("torque[N.m]");
# トルクの散布図を追加
plt.scatter(power_data[0,:], power_data[1,:], s=1, color="cornflowerblue");
# トルクの近似曲線の追加
ax1.plot(poly_x_ax, np.poly1d(poly_y_axTorq)(poly_x_ax), color="blue", label="Torque");
# 軸の設定(馬力側)
ax2 = ax1.twinx();
ax2.set_ylim(0, int(max(power_data[3,:])*1.2));
ax2.set_ylabel("Power[PS]");
# 馬力の散布図を追加
ax2.scatter(power_data[0,:], power_data[3,:], s=1, color="lightsalmon");
# 馬力の近似曲線の追加
ax2.plot(poly_x_ax, np.poly1d(poly_y_axPS)(poly_x_ax), color="red", label="Power[PS]");
# グリッド線の追加
ax1.grid(b=None, which="both", axis="x");
ax2.grid(b=None, which="both", axis="both");
# ラベルの追加
h1, l1 = ax1.get_legend_handles_labels();
h2, l2 = ax2.get_legend_handles_labels();
ax2.legend(h1+h2, l1+l2, loc="lower right");
# グラフを描画
plt.show();
コメント
コメント一覧 (2件)
ども、ACTY Type-Rです。拙者のCAN-ID解析結果を参照していただきありがとうございます。
AruduioでのCAN-BUSデータ抽出、ソースとも、とても参考になりました。
僕は、Arudinoはとっかかり始めたまま、ここ数か月、放置しっぱなしになってたので、本記事がいい刺激になりました。
今後のコンテンツ増加、楽しみにしてます。
ACTY Type-R様
大変有用なCANデータ情報、ありがとうございました。
御本人様からコメントいただきまして大変恐縮です。みんカラのIDを持っていないため、ACTY Type-R様のサイトにコメントできず、失礼しました。
こちらこそ、ACTY Type-R様の記事更新、楽しみにしております。
今後とも、よろしくお願いいたします。