ArduinoとCAN通信で制御する振動アクセルペダル(試作)

S660やアルトワークスでサーキットを走行すると、ついついアツくなってしまい、レブリミッターを作動させてしまう事があった。対策として、CANから車両の回転数を取得しインジケータLEDを点灯させることに成功した(こちらの記事)。今回はこの回路を利用して、指定した回転数になるとアクセルペダルを振動させてドライバーにお知らせする機能を作成してみた。

目次

きっかけ

レブリミット付近になると、レブインジケータが点灯(視覚)したり、あるいはブザーが鳴動(音)するという手段でドライバーにお知らせする機能がある。CAN信号を受信してインジケータを点灯させるガジェットを作成してみた。

あわせて読みたい
ArduinoとCAN通信で作るレブインジケータ S660やアルトワークスでサーキットを走行すると、ついついアツくなってしまい、レブリミッターを作動させてしまう事があった。レブリミッターはその通り、上限回転数に...

しかし、レブインジケータは昼間はそれほど目立たない。また、ブザーは爆音+ヘルメットだと聞こえづらい。

そういえば、実家のメルセデスW205には、車線を跨ぎそうになるとステアリングに「コツッ、コツッ」という振動を与えて教えてくれている機能がついている事を思い出した。修正操作すべきものを直感的にお知らせするために振動を使うのは面白いと思い、いろいろ調べてみた。

振動アクセルペダル

アクセルペダルを振動させるという構想は古くからあるようだ。アクセルペダルの裏側に「振動器」を取り付け、車間が近いときに警報するという特許が出願されている。

【請求項1】 アクセルペダルの裏側に取り付けられアクセルペダルに機械的振動を与える励振器と、この励振器を車間距離警報装置の警報出力信号にしたがって制御する制御回路とを備えたことを特徴とするアクセルペダルの振動装置。

【請求項2】 前記励振器は、アクセルペダルの裏側に取付けられた電動機と、この電動機により回転駆動される回転軸にその重心をずらして取付けられた重りとを含む請求項1記載のアクセルペダルの振動装置。

特許第2906101号(特許権者:日野自動車)より

今回は、車両の回転信号に従ってこの「振動器」を制御するアクセルペダルを作ってみることにした。

モーターの選定とトランジスタ

今回使用した振動モーターはこちら。小型振動モーターを選定。

モーターの動作確認と電力計測

テスト用の電源5Vを繋いで見たところ、結構な振動が。小型だけどパワフルだ。電流計で計測すると、590mAの電流を消費することがわかった。

600mAの電流出力をArduinoのデジタルPIN(40mA程度)では駆動することは不可能。Arduinoの5Vピンは5V1Aの出力が可能である。よって、今回はArduinoの5Vピンから電源を取得し、トランジスタ経由で振動モーターを動作させることにした。

回路

前回作成のLEDインジケータの回路の点灯用LEDの部分を振動モーターに置き換えただけ。

トランジスタの選定

コレクタ電流800mAまで対応可能な2SC2120-YというNPN型トランジスタを選定した。トラジスタとは何?という方はこちらのページが参考になると思う。

コレクタ電流にArduinoの5Vピンの1A出力を接続し、ベースにモーターへの出力信号、エミッタ接続先はArduinoのGND。モーターの隣に保護用ダイオードをつけている。こちらのページが参考になる。

プログラム設計

振動モーターによる回転数超過のお知らせをする機能を実装する。ここで、指定回転数を超過している状態が続く場合にずっと振動モーターがONだと、大変に煩わしい。そこで、回転数が指定した値を超過した時にモーターを短時間で振動した後は、回転数が指定値以下になるまでモーターは振動しない設計とした。動作概要は以下の通り。

上記となるような状態遷移を整理。

これを実装する。モーターON後に一定時間経過したら、指定回転数を超過していてもモーターはOFFする。この機能を、タイマ関数を用いて実装する。

ソースコード作成

前回作成のレブインジケータを改修して作成した。まずはポイントのみ解説し、プログラムソースコードは、この記事の最後に掲載しておく。

タイマ割り込み

Arduinoは、一定時間が経過したら処理を割り込ませるという「タイマ割り込み」機能を使う。タイマ割り込み関数を定義し、一定時間(MAX_TIME_MOTOR_ON)経過したら、モーターはOFFする。

タイマ割り込みのソースコード例

タイマ割り込み部分のみ、概要で表すと以下のようなコードとなる。MsTimer2関数を用いる。

---------------------中略-------------------------------
// MsTimer2をincludeする
#include "MsTimer2.h"
--------------------------------------------------------
// Motor ONの最大時間(msec)
#define MAX_TIME_MOTOR_ON 600
--------------------------------------------------------
void setup(){ 
  // タイマー割り込みの設定
  MsTimer2::set(MAX_TIME_MOTOR_ON, timer_interrapt);
}
--------------------------------------------------------
void loop(){
  if(モーター駆動条件を満たしたら){
    MsTimer2::start(); //計測タイマスタート
  }
}
--------------------------------------------------------
void timer_interrapt(void){
  // 時間経過フラグしたら、Motor動作準備フラグをOFFする
  flag_Motor_Ready = false;
  // 時間計測タイマを停止する(カウントストップ)
  MsTimer2::stop();
}

クルマに装着してテスト

準備が整ったので車両に装着してみる。

エンジンをかけて走行する前に、モーターを短時間振動させるプログラムでどのようなフィーリングになるのかを確かめてみた。以下のプログラムでは、Arduni Nanoのリセットボタンを押すとsetup()関数が呼ばれるので一度だけモーターを指定した時間分だけ動作させる事ができる。

/*
 * File:   motor_test.ino
 * Author: 岡本一車
 * https://kurumashikou.com/
*/
// Motor ONの最大時間(msec)
#define MAX_TIME_MOTOR_ON 600
void setup() {
  // シリアルモニタの設定
  SERIAL_PORT_MONITOR.begin(115200);
  // シリアルポートの準備が整うまで待つ
  while(!Serial);
  // 出力ポート設定
  pinMode(10, OUTPUT);
  delay(2000);
  digitalWrite(10, HIGH);
  delay(MAX_TIME_MOTOR_ON);  
  digitalWrite(10, LOW);
  digitalWrite(INDICATOR_PIN, LOW);  
}
// メイン処理 ////////////////////////////////////////////////
void loop() {
}

これを使って、アクセルペダルのどこにつければよいかを色々探した結果、アクセルペダル裏に貼り付けられるスペースがありそうだったので暫定で養生テープで固定し、振動を感じることができるかをテスト。

ちゃんと固定されていないので振動音がひどいが、足の裏にしっかりと振動が伝わってくる。作動時間は今回600msで設定しているものの、400ms程度でも十分かもしれない。

まとめ

今回はここまで。振動モーターを用いてArduni Nanoで制御を行い、指定した回転数になるとアクセルペダルを振動させてドライバーにお知らせする機能を作成してみた。結果、アクセルペダルの裏面に貼り付けることで効果的にドライバに情報通知ができることがわかった。

今後の取り組み

今回は車両への取り付けのみ

  • アクセルペダルへの固定方法の工夫(防水性能含む)
  • サーキットなどの限界走行時の有効性確認
  • アンジュレーション(道路路面の振動)入力時の確認
  • Noromal・Sportsモードの設定
  • 振動時間のチューニング

などが例として挙げられる。

特に、街乗りなどでも振動によるお知らせをするNormalモードを作るような場合を考えてみる。街乗りなので、お知らせする回転数は低く設定されると考えられるが、設定回転数が低いとヒール&トゥの際にムダに振動してしまう。そこで、クラッチ踏込状態を監視し、振動させないなどの配慮が必要となると思われる(アルトワークスではCANの回転数信号が出力されているCAN IDにクラッチのON/OFF情報も入っているので楽)。

次回、完成したデバイスを用いて全開走行してみようと思う。

今回使用した機材

振動モーター

Arduni Nano

ユニバーサル基板にハンダ付けできるピッチのピンがついているArduino。そしてめちゃくちゃ小さい。

Arduino
¥4,670 (2024/05/18 14:54:43時点 Amazon調べ-詳細)

ただし少し高いので、試作品の動作確認を行った後は、互換品を使って作成している。そのまま置き換え可能。

Arduni Nano(及び互換品)は、PCとの接続がUSB-TypeBなので、ケーブルを持っていなければ購入しておこう。

Amazonベーシック(Amazon Basics)
¥508 (2024/05/19 01:31:16時点 Amazon調べ-詳細)

ArduinoとCANバスモジュールや、Arduino同士の接続に必要なのがジャンパーケーブル。オス-メス、オス-オス、メス-メスが揃っている。Arduinoのピンにそのまま差せる。

関連記事

あわせて読みたい
ArduinoNanoとMCP2515でCANデータを受信する 以前、Arduino MEGAとCAN-BUS Shieldを用いて車両のCAN情報を取得する事に成功した。→こちら https://kurumashikou.com/gets660can_via_arduino-and-canbus-shield/ Ard...
あわせて読みたい
ArduinoでCANデータを送信する CANデータを読み取ることができる受信モジュールについてはすでに作成したものの、今後のCANを使った車載ガジェットの開発のために、CANを送信できるプログラムを作成し...
あわせて読みたい
Arduino NanoとArduino Mega間でCAN通信する Arduino Megaに実装したCAN送信プログラムをArduino Nanoに実装したCAN受信プログラムが受信して、PC上のシリアルモニタに表示するテストを行った。ArduinoでCAN通信ガ...
あわせて読みたい
CAN通信のシミュレーション用の模擬ECUを作成 CAN送信モジュールを作成し、Arduino同士の通信が可能になったので、今回はArduino Megaに、実際の車両ECUのデータを模したものを送信させるようにプログラムの改修を行...
あわせて読みたい
ArduinoとCAN通信で作るレブインジケータ S660やアルトワークスでサーキットを走行すると、ついついアツくなってしまい、レブリミッターを作動させてしまう事があった。レブリミッターはその通り、上限回転数に...

プログラムソースコード

/*
 * File:   Haptic_feedback_REV_guidance.ino
 * Author: 岡本一車
 * https://kurumashikou.com/
*/
#include <SPI.h>
#include <mcp_can.h>
#include "string.h"
#include "MsTimer2.h"
// S660用にコンパイルするなら1、アルトワークス用なら0を指定
#define S660 1
// S660用の設定 //////////////////////////////////
#if S660
  // S660用LEDを点灯するヒステリシスを設定する
  // LED ONする回転数
  #define RPM_LED_ON 7550
  // LED OFFできる回転数
  #define RPM_LED_OFF 7350
  
  // S660用CANフィルタ設定
  #define MASK0 0x7FF
  // S660 engine_Rev CAN ID 0x1DC
  #define Filter0 0x1DC
  // S660 Transmission_Vel CAN ID 0x158
  #define Filter1 0x158
  #define MASK1 0x7FF
  #define Filter2 0x000
  #define Filter3 0x000
  #define Filter4 0x000
  #define Filter5 0x000
// アルトワークス用の設定 ///////////////////////////
#else
  // HA36S用LEDを点灯するヒステリシスを設定する
  // LED ONする回転数
  #define RPM_LED_ON 7200
  // LED OFFできる回転数
  #define RPM_LED_OFF 7100
  // HA36Sアルトワークス用CANフィルタ設定
  #define MASK0 0x7FF
  // S660 engine_Rev CAN ID 0x124
  #define Filter0 0x124
  // S660 Transmission_Vel CAN ID 0x314
  #define Filter1 0x314
  #define MASK1 0x7FF
  #define Filter2 0x000
  #define Filter3 0x000
  #define Filter4 0x000
  #define Filter5 0x000
// 車種別設定ここまで
#endif
// HapticデバイスをONする回転数とOFFする回転数
// Motor ONする回転数
#define RPM_MOTOR_ON 7550
// Motor を再びReadyにできる回転数
#define RPM_MOTOR_ON_READY 7100
// Motor ONの最大時間(msec)
#define MAX_TIME_MOTOR_ON 600
// ピンの設定
// SS_defaultPin Arduino unoは10, megaは53
// ここではCAN通信用のCSピンとして、digital 9を使用する
const int SPI_CAN_CS_PIN = 9;
// Motor出力ピン
const int MOTOR_PIN = 10;
// 表示用LEDピン
const int INDICATOR_PIN = 8;
// グローバル変数 ///////////////////////////////////////////
// タイムスタンプ用変数
unsigned long timestamp = 0;
unsigned long timestamp_old = 0;
// 受信メッセージ格納用
unsigned long can_ID = 0;
unsigned char len = 0;
unsigned char buf[8] = {0, 0, 0, 0, 0, 0, 0, 0};
// 送信メッセージ格納用
//unsigned char sendMsg[8] = {0, 0, 0, 0, 0, 0, 0, 1};
// 回転数格納用変数
unsigned int rpm = 0;
// 速度格納用変数
unsigned int vel = 0;
// Hapticデバイス作動フラグ
bool motor_ON = false;
// Motor動作準備フラグ
bool flag_Motor_Ready = false;
// CANデータ書き込み値を保持する
String logDataStr; 
// CANトランシーバにCSピン設定をセットし、CAN通信インスタンスを生成
MCP_CAN CAN(SPI_CAN_CS_PIN);
// 割り込み関数の定義
void timer_interrapt(void);
// 初期設定 ////////////////////////////////////////////////
void setup() {
  // シリアルモニタの設定
  SERIAL_PORT_MONITOR.begin(115200);
  // シリアルポートの準備が整うまで待つ
  while(!Serial);
  
  // 出力ポート設定
  pinMode(MOTOR_PIN, OUTPUT);
  pinMode(INDICATOR_PIN, OUTPUT);
  // タイマー割り込みの設定
  MsTimer2::set(MAX_TIME_MOTOR_ON, timer_interrapt);
  // CANの初期設定 ///////////////////////////////////////////////
  if (CAN.begin(MCP_ANY, CAN_500KBPS, MCP_8MHZ) != CAN_OK) {
    SERIAL_PORT_MONITOR.println("CAN-BUS initiliased error ..............");
    while(1);
  }else{
    CAN.setMode(MCP_NORMAL);
    SERIAL_PORT_MONITOR.println("CAN-BUS initiliased OK !!");
  }
  
  // フィルターとマスクの設定。Mask0/Mask2両方の設定が必要
  CAN.init_Mask(0, 0, MASK0);
  CAN.init_Mask(1, 0, MASK1);
  CAN.init_Filt(0, 0, Filter0);
  CAN.init_Filt(1, 0, Filter1);
  CAN.init_Filt(2, 0, Filter2); 
  CAN.init_Filt(3, 0, Filter3);
  CAN.init_Filt(4, 0, Filter4);
  CAN.init_Filt(5, 0, Filter5);
  // Motor動作準備OK
  flag_Motor_Ready = true;
}
// メイン処理 ////////////////////////////////////////////////
void loop() {
  // CANメッセージ受信したときの動作
  if (CAN_MSGAVAIL == CAN.checkReceive()) {
    
    // 受信したデータをシリアルモニタに送信 ////////////////////////////////////
    if(timestamp_old == 0){
      timestamp_old = millis();
    }
    // CANデータを取得
    //CAN.readMsgBufID(&can_ID, &len, buf);
    CAN.readMsgBuf(&can_ID, &len, buf);
    // タイムスタンプの取得
    timestamp = millis() - timestamp_old;
    
// S660の場合の処理
#if S660
    // 回転数を取得(CAN ID 0x1DC)
    if(can_ID==476){
      rpm = buf[1]*256 + buf[2];
      
    // 速度を取得(CAN ID 0x158)
    }else if(can_ID==344){
      vel = buf[4]*256+buf[5];
    }
// アルトワークスの場合の処理
#else
    // 回転数を取得(CAN ID 0x124)
    if(can_ID==292){
      rpm = buf[1]*256 + buf[2];
      rpm = rpm/4;
      
    // 速度を取得(CAN ID 0x314)
    }else if(can_ID==788){
      vel = buf[1]*256+buf[2];
      vel = vel*3/400;
    }
#endif
    // デバッグ用出力
    logDataStr  = "";
    logDataStr += String(rpm);
    logDataStr += String("rpm / ");
    logDataStr += String(vel);
    logDataStr += String("kph");
    logDataStr += "\r\n";
    if(motor_ON){
      logDataStr += String("*********************************************");  
    }
    SERIAL_PORT_MONITOR.print(logDataStr); 
    
  }
  // ON条件 ////////////////////////////////////////////////////////
  // モータがOFF、かつ回転数を超過した、かつ、 Motor動作準備フラグが立っている
  if(!motor_ON && rpm>=RPM_MOTOR_ON && flag_Motor_Ready){
    // モータONする
    motor_ON = true;
    // 時間計測タイマをセットする
    MsTimer2::start();
  }
  
  // OFF条件 ////////////////////////////////////////////////////////
  // ONのまま所定時間が過ぎたらMotor動作準備フラグがOFFされるのでモーターをOFF  
  if(!flag_Motor_Ready){
    // モータOFFする
    motor_ON = false;
  }
  // 再度ON許可する条件 //////////////////////////////////////////////
  // 回転数が指定回転数を下回ったらMotor動作準備フラグを立てることにより、
  // 次回指定回転数超過時に再びMotorをONできる
  if(rpm<=RPM_MOTOR_ON_READY){
    // 時間計測タイマを停止する(カウントストップ)
    MsTimer2::stop();
    // Motor動作準備フラグを立てる
    flag_Motor_Ready = true;
  }
  // 動作フラグが立っているとき、MotorとLEDを表示 /////
  if(motor_ON){
    digitalWrite(MOTOR_PIN, HIGH);
    digitalWrite(INDICATOR_PIN, HIGH);
  }else{
    digitalWrite(MOTOR_PIN, LOW);
    digitalWrite(INDICATOR_PIN, LOW);
  }
}
// タイマ割り込み
void timer_interrapt(void){
  // 時間経過フラグしたら、Motor動作準備フラグをOFFする
  flag_Motor_Ready = false;
  // 時間計測タイマを停止する(カウントストップ)
  MsTimer2::stop();
}
この記事をSNSでシェアする
  • URLをコピーしました!

コメント

コメント一覧 (3件)

  • はじめまして。
    すごいなー。と興味深く読ませていただいてます。
    自分でも参考にさせていただきたく。
    ひとつ教えてほしいのですが、MsTimer2.h が見つからないのですが、
    こちらはどちらにあるのでしょうか?

  • あ すいません。検索したら出てきました。
    また、不明点ありましたらすいませんが、よろしくお願いします。

  • 毎日勉強 様

    はじめまして。
    MsTimer2,hが見つかったようで良かったです。念のため。
    https://www.arduino.cc/reference/en/libraries/mstimer2/
    マニュアルにありますように、このライブラリを使用するためには、ArduinoのIDE(統合開発環境)からインストールする必要があります。
    インストール方法は、一般的なものですが、このサイト様が参考になるかと思います。
    https://nobita-rx7.hatenablog.com/entry/28494140
    Arduinoは色々な実験ができて楽しいですよね。今後もよろしくお願いします。

コメントする

目次