CIVIC(シビック)FL1でのOBD2データ取得(Arduino使用)応用編

目次

このページの目的

いまどきの車では、OBD(OnBoadrdDialog)によりいろんなデータが取得できる。それを利用した各種インジケーターも販売されているが、自分の欲しいデータを数値で取得できれば、いろんな使い道がある、はずなんだよね。「CIVIC(シビック)FL1でのOBD2データ取得(Arduino使用)基礎編」で基本的な動作は確認できたので応用していこう。

データロギング機能を追加(2022-09-24)

ところで、CAN-BUSシールドにはマイクロSDカードスロットが付いている。これを利用しない手はないだろう。スイッチ入切によるデータロギング機能を追加してみよう。

データロギングスイッチを追加
データロギングスイッチを追加
回路図
回路図

Arduinoスケッチ(データロギング追加)

#include <SPI.h>
#include <mcp2515_can.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <SD.h>

//CAN-BUS
const int CAN_CS_PIN = 9;
const int CAN_INT_PIN = 2;
unsigned char stmp[] = {0x02, 0x01, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned char cmnd[] = {0x0B, 0x0C, 0x11, 0x15};
unsigned long trgt[] = {0x18DA10F1, 0x18DAEFF1, 0x18DAEFF1, 0x18DA10F1};
String unit;
float data;
unsigned char fct;
unsigned char len = 0;
unsigned char buf[8];
mcp2515_can CAN(CAN_CS_PIN);

//LCD
int Col;
int Row;
LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display

//SD
const int SWITCH1 = 6;     //D6ピンを使用
const int SWITCH2 = 7;     //D7ピンを使用
const int chipSelect = 4;  //D4ピンがSD制御用(Arduinoの標準的な設定。CAN-BUSシールドもそうなっている)
int LogStat = 0;
int FileNum = 0;
String FileTemp;
File dir;
File LogData;

unsigned long ti;

void setup() {
  //SD Init
  pinMode(SWITCH1, OUTPUT);         //D6ピンを出力で使用
  pinMode(SWITCH2, INPUT_PULLUP);   //D7ピンを入力で使用。プルアップしておく(スイッチOFFの場合にHIGHになるように)
  digitalWrite(SWITCH1, LOW);       //D6ピンはLOWにしておく
  if (!SD.begin(chipSelect)) {
    Serial.println("initialization failed!");
    while (1);
  }
  dir = SD.open("/");
  while(1){
    File entry =  dir.openNextFile();
    if(!entry){break;}
    if (!entry.isDirectory()) {
      FileTemp = entry.name();
    }
    entry.close();
    FileNum = FileTemp.toInt()+1;
  }
  
  //LCD Init
  lcd.init();
  lcd.backlight();
  SERIAL_PORT_MONITOR.begin(115200);
  while(!Serial);
  if (CAN.begin(CAN_500KBPS) != 0) {
    lcd.setCursor(1,0);
    lcd.print("CAN-BUS init error!");
    while(1);
  }
  lcd.setCursor(1,0);
  lcd.print("CAN-BAS init ok!");

  CAN.init_Mask(0 , 1 , 0x00000000);  //0x1FFFFFFF);
  CAN.init_Filt(0 , 1 , 0x18DAF110);
  CAN.init_Filt(1 , 1 , 0x18DAF1EF);
  CAN.init_Mask(1 , 1 , 0xFFFFFFFF);
  CAN.init_Filt(2 , 1 , 0x00000000);
  CAN.init_Filt(3 , 1 , 0x00000000);
  CAN.init_Filt(4 , 1 , 0x00000000);
  CAN.init_Filt(5 , 1 , 0x00000000);

  delay(1000);
  lcd.clear();
}

void loop() {
  for (int i=0; i<4; i++) {
    ti = millis();
    unit = "";
    stmp[2] = cmnd[i];
    CAN.sendMsgBuf(trgt[i], 1,8, stmp);
    if(CAN.checkReceive()) {
      CAN.readMsgBuf(&len, buf);
      switch (buf[2]) {
        //Intake Pressure
        case 0x0B:
          data=buf[3];
          fct = 0;
          unit = "kPa";
          Col = 0; Row=0;
          break;
        //rpm
        case 0x0C:
          data=((float)256*buf[3]+buf[4])/4;
          fct = 0;
          unit = "rpm";
          Col = 8;Row=0;
          break;
        //Throttle
        case 0x11:
          data=(float)100*buf[3]/255;
          fct = 1;
          unit = "%";
          Col=0;Row=1;
          break;
        //Air-Fuel
        case 0x15:
          data=((float)256*buf[3]+buf[4])/32768;
          fct = 2;
          unit = "AFR";
          Col=8;Row=1;
          break;
      }
      
      //LCD
      lcd.setCursor(Col,Row);lcd.print(data,fct);lcd.print(unit);
      
      //Data Logging
      if (digitalRead(SWITCH2) == LOW && LogStat == 0) {     //ロギングしていないとき(LogStat=0)、スイッチがON(D7がLOW)になったら
        Serial.println("Logging...");
        LogData = SD.open(String(FileNum), FILE_WRITE);      //設定したファイルを作成
        LogStat = 1;                                         //ロギング動作中(LogStat=1)に
      }
      if (LogStat == 1) {                                    //ロギング中ならデータ書き込み
        LogData.print(buf[2],HEX);LogData.print(",");LogData.print(ti);LogData.print(",");LogData.println(data,fct);
        if (digitalRead(SWITCH2) == 1) {                     //スイッチOFFになったら終了
          LogData.close();
          LogStat = 0;
          FileNum++;
        }
      }
    }
  }
  delay(100);
}

データを取ってみた

早速、データを取ってみよう。取得するのは、上記スケッチにより、時間・アクセル開度・空燃比・インマニ吸気圧力・エンジン回転数、だ。実際に取得したデータでは、ところどころ取得ミス(値がゼロ)があったのでそれを削除してある。

データロギングサンプルグラフの項目説明
項目データの説明
横軸時間経過[msec]
縦軸1(左)アクセル開度[%]
縦軸2(左)空燃比(取得したデータに理論空燃比14.7をかけて実際の空燃比にした、つもり)
縦軸3(左)インマニ吸気圧力(絶対圧)[kPa]
縦軸4(右)エンジン回転数[rpm]
データロギングサンプル1
データロギングサンプル1
データロギングサンプル2
データロギングサンプル2

一応、解説を。

なんちゃって馬力計算

データが取れればやってみたくなるのはいつものこと(前回参照『CBR1000RR(SC36)フルパワー化:なんちゃって馬力計算』)だ。今回はトルクも計算してみよう。

馬力・トルクの計算に必要な公式
項目公式
運動方程式[N]=[kg]×α[m/s2]
動力[W]=[N]×[m/s]
トルク[Nm]=[N]×[m]

まず、質量だが、これは、

1,440kg=1,340kg(車両重量:6MT@EXグレード、ガソリン満タン)+100kg(運転手+フロアマット等オプション品+デッドニング材料+その他カスタム部品、のおおよそ)

とする。次に加速度αだが,加速度とは単位時間当たりの速度の増加分なので,データサンプリング毎の計測間隔(Arduinoの時間計測精度による)とそのときの回転数差・減速比・タイヤ径から求めることが可能だ。

α[m/s^2]=(rpm2-rpm1)[rpm]÷60[rpm->1/sec]÷4.105[減速比]÷X.XX[計測時のギヤの変速比]×π[円周率]×0.645[235/40R18タイヤ直径 m]
※CVTだとギヤ比可変で計算できないのでOBD2車速データを取って計算する必要あり

速度は、その瞬間のタイヤ回転数×タイヤ円周長で算出する。

[m/s]=rpm[rpm]÷60[rpm->1/sec]÷4.105[減速比]÷X.XX[計測時のギヤの変速比]×π[円周率]×0.645[235/40R18タイヤの直径:m]
※CVTだとギヤ比可変で計算できないのでOBD2車速データを取って計算する必要あり

トルク計算に必要な長さはタイヤ直径より

[m]=0.3225=0.645[タイヤ直径:m]÷2[半径にする]

となる。ただし、このでの計算はタイヤに作用するトルクなので、エンジン単体のトルクにするためには、変速比で割ってやる必要がある。

Te[Nm]=[Nm]÷4.105[減速比]÷X.XX[計測時のギヤの変速比]

さて、その計算結果は...おぉっと、結構衝撃的な値となったぞ。

馬力計算結果
馬力計算結果
カタログスペックと計算結果
項目カタログ値計算結果
出力[kw]134(6000rpm)97(6000rpm) ※近似曲線
トルク[Nm]240(1700-4500rpm)170(5000rpm) ※近似曲線

まぁこの計算は理論的には正しいかもしれないが、データには雑なところ(近似曲線の引き方もそうだ)があるのでやむを得ないところだ。例えば、5400rpmあたりの外れたデータを1個無視するだけでも5kwくらいは増えそうだし。さらには、車両内部で、安全(エコも?)方向の制御が働いている可能性もあるしね。

さらに続く...

さて、これをどう応用していくか...続く...