ロードセルブレーキを作ろう | まこさんのRC

まこさんのRC

まこさんのRCのブログです。

ちょいちょい出てきたロードセルブレーキの作り方。めちゃ簡単です。

 
用意するもの
[材料]
20kgのロードセル
HX711
Arduino
M4皿ネジ25mm2本
M4皿ネジ20mm1本
 
 
手順
1 ペダルを取ります
2 裏のビスを取ります
滑り止めの裏にも隠れてますので注意
 
 
3 配線をはずします。外した配線は絶縁して放置で多分大丈夫。
 
4 台座からペダル本体を外します
 
5 ペダルの裏のねじを取って台座を外します
 
6 ロードセルをM4ネジで元々あるネジ穴に一旦固定し下の穴の目印にポンチを打ちます
 
7 3.2mmのドリルで穴を開けます
 
8 M4のタップを切ります
 
9 ワッシャーで嵩上げしてM4x25mmの皿ネジで固定します。ロードセルはこのように隙間が必要です。
取り付けた様子
 
10 ペダル取り付けのアダプタを3Dプリンタで自作します。M4x20mmの皿ネジで固定します。
 
 
11 元通り組み立てて完成
 
 
ArduinoとHX711について。
HX711は基盤上の10Hzに付いている小さい抵抗を隣の80Hzに移設。
配線は
Arduino - HX711
10 - DT
14 - SCK
VCC - VCC
GND - GND
 
HX711 - ロードセル
E+ - 赤
E- - 黒
A- - 白
A+ - 緑
 
 
 
ブレーキだけでなくアクセルも分離してますので余計な配線(黄色)もあります。
アクセルは、アナログ入力の0番ピンにポテンショメータをつないで読み取っています。
 
ケースも3Dプリンタで自作
 
 
こんな感じ
 
あとはArduino IDE、Joystickライブラリをインストールして書き込みます。
他の方が作られたスケッチを一部改変
 
/**** ここから ****/
#include <EEPROM.h>
#include <Joystick.h>

// HX711接続pin
#define DT_BRAKE   10
#define SCK_BRAKE  14
#define A_PIN 0

// EEPROM
#define EEPAD_HL    0               // HardLimit
#define EEPAD_DZ    2               // DeadZone
#define EEPAD_SL    4               // SoftLimit
#define EEPAD_RESERVE1 6            // 
#define EEPAD_RESERVE2 8            // 
#define EEPAD_NEXT  10

// common
#define READ_AVERAGE    30
int isDebug = 0;

#define TYPE_THROTTLE 0
#define TYPE_BRAKE    1

typedef struct _loadcell {
  int type;                   // 0:スロットル 1:ブレーキ
  int axis;                   // 解像度
  int dt;                     // Arduino PIN(HX711のDT)
  int sck;                    // Arduino PIN(HX711のSCK)
  int HardLimit;              // ロードセルの最大荷重(g)
  int DeadZone;               // デッドゾーン
  int SoftLimit;              // ソフト側最大荷重
  float offset;               // 無荷重時の値
  float val;
  float raw;
  int data;
} LOADCELL;

// Throttle
#define THROTTLE_GAIN 4
#define THROTTLE_AXIS (1024*THROTTLE_GAIN-1)          // スロットル解像度 default:1023
int throttle_direction = 0;         // スロットル向き 0:増加(Normal) 1:減少(Reverce)
LOADCELL  th = {
  TYPE_THROTTLE, THROTTLE_AXIS, 0, 0, THROTTLE_AXIS, 160 * THROTTLE_GAIN , 780 * THROTTLE_GAIN, 0, 0, 0, 0
};

// Brake
#define BRAKE_AXIS  8191            // ブレーキの解像度 default:1023
LOADCELL  br = {
  TYPE_BRAKE, BRAKE_AXIS, DT_BRAKE, SCK_BRAKE, 20000, 2500, 12800, 0, 0, 0, 0
};

/* Joystick */
Joystick_ Joystick = Joystick_(
                       0x03,                    // reportid
                       JOYSTICK_TYPE_GAMEPAD,   // type
                       0,                       // button count
                       0,                       // hat switch count
                       false,                   // x axis disable
                       false,                   // y axis disable
                       false,                   // z axis disable
                       false,                   // right x axis disable
                       false,                   // right y axis disable
                       false,                   // right z axis disable
                       false,                   // rudder disable
                       true,                    // throttle enable
                       false,                   // accelerator disable
                       true,                    // brake enable
                       false                    // steering disable
                     );

void Caribration_throttle(LOADCELL *lc) {
  lc->offset = 0;
  throttle_direction = 0;
  ReadThrottle(lc);

  // 無負荷時の値によって向きを自動判別
  if (lc->raw > (512*THROTTLE_GAIN)) {
    throttle_direction = 1;
    lc->raw = THROTTLE_AXIS - lc->raw;
  } else {
    throttle_direction = 0;
  }
  ReadThrottle(lc);
  lc->offset = lc->raw;
}

void  Caribration_brake(LOADCELL *lc) {
  // HX711との通信
  pinMode(lc->sck, OUTPUT);
  pinMode(lc->dt, INPUT);
  lc->offset = 0;
  ReadLoadcell(true, lc);
  lc->offset = lc->raw;
}

void info() {
  Serial.println("Hard Limit: "  + String(th.HardLimit)  + "/" + String(br.HardLimit));
  Serial.println("Soft Limit: "  + String(th.SoftLimit)  + "/" + String(br.SoftLimit));
  Serial.println("Dead Zone : "  + String(th.DeadZone)   + "/" + String(br.DeadZone));
  Serial.println("Offset: "     + String(th.offset)            + "/" + String(br.offset));
  Serial.print("Throttle Direction: ");
  if (throttle_direction) Serial.println("REV");
  else                    Serial.println("NOR");
}

int ReadThrottle(LOADCELL *lc) {
  float dt;

  dt = 0;
  for (int t = 0; t < THROTTLE_GAIN; t++) {
    dt += analogRead(A_PIN);
    delayMicroseconds( 50 );
  }
  if (throttle_direction) dt = THROTTLE_AXIS - dt;

  lc->raw = dt - lc->offset;
  if (lc->raw < 0)  lc->raw = 0;
  return false;
}

int ReadLoadcell(int flg, LOADCELL * lc) {
  long sum = 0;
  for (int i = 0; i < (flg ? READ_AVERAGE : 1); i++) {
    long data = 0;
    // 24bit分のデータ読み出し
    while (digitalRead(lc->dt) != 0) if (!flg) return true;
    for (char bit = 0; bit < 24; bit++) {
      digitalWrite(lc->sck, 1);
      delayMicroseconds(5);
      digitalWrite(lc->sck, 0);
      delayMicroseconds(5);
      data = (data << 1) | (digitalRead(lc->dt));
    }
    digitalWrite(lc->sck, 1); //gain=128(A)
    delayMicroseconds(5);
    digitalWrite(lc->sck, 0);
    delayMicroseconds(5);
    data = data ^ 0x800000;
    sum += data;
  }
  float ave = sum / (flg ? READ_AVERAGE : 1);
  float volt, gram;
  volt = ave * (4.2987 / 16777216.0 / 128);
  gram = volt / (0.001 * 4.2987 / lc->HardLimit);
  if ( lc->offset > 0 && (gram - lc->offset) > lc->HardLimit ) return true;
  if ( (gram - lc->offset) > (lc->HardLimit / 100)) {
    lc->raw = gram - lc->offset;
    return false;
  }
  else {
    lc->raw = 0;
    return false;
  }
}

void joystick(int data, LOADCELL * lc) {
  switch (lc->type) {
    case TYPE_THROTTLE:
      Joystick.setThrottle(data);
      break;
    case TYPE_BRAKE:
      Joystick.setBrake(data);
      break;
  }
}

int setJoystick(float load, LOADCELL * lc) {
  int val;

  if ( load < lc->DeadZone) {
    val = 0;
  }
  else{
    val = map(load, lc->DeadZone, lc->SoftLimit, 0, lc->axis);
  }

  if (val > lc->axis) {
    val  = lc->axis;
  }

  joystick(val, lc);
  return val;
}

void getEEPROM(LOADCELL *lc) {
  int tmp;

  // Hard Limit
  EEPROM.get(lc->type * EEPAD_NEXT + EEPAD_HL, tmp);
  if (tmp > 0) {
    lc->HardLimit = tmp;
  }

  EEPROM.get(lc->type * EEPAD_NEXT + EEPAD_SL, tmp);
  if (tmp > 0) {
    lc->SoftLimit = tmp;
  }
  // Dead Zone
  EEPROM.get(lc->type * EEPAD_NEXT + EEPAD_DZ, tmp);
  if (tmp >= 0) {
    lc->DeadZone = tmp;
  }
}

void putEEPROM(char *subcom, int val, LOADCELL * lc) {
  switch (*subcom) {
    case 'h': // Hard Limit
      if (val > 0) {
        lc->HardLimit = val;
        EEPROM.put(EEPAD_NEXT * lc->type + EEPAD_HL, lc->HardLimit);
        switch (lc->type) {
          case TYPE_THROTTLE:
            Serial.print("TH ");
            break;
          case TYPE_BRAKE:
            Serial.print("BR ");
            break;
        }
        Serial.println("Hard Limit -> " + String(val));
        info();
        break;
      }
      break;
    case 'd': // Dead Zone
      if (val >= 0) {
        lc->DeadZone = val;
        EEPROM.put(EEPAD_NEXT * lc->type + EEPAD_DZ, lc->DeadZone);
        switch (lc->type) {
          case TYPE_THROTTLE:
            Serial.print("TH ");
            break;
          case TYPE_BRAKE:
            Serial.print("BR ");
            break;
        }
        Serial.println("DeadZone -> " + String(val));
        info();
        break;
      }
      break;
    case 's': // Soft Limit
      if ( val > 0) {
        lc->SoftLimit = val;
        EEPROM.put(EEPAD_NEXT * lc->type + EEPAD_SL, lc->SoftLimit);
        switch (lc->type) {
          case TYPE_THROTTLE:
            Serial.print("TH ");
            break;
          case TYPE_BRAKE:
            Serial.print("BR ");
            break;
        }
        Serial.println("SoftLimit -> " + String(val));
        info();
        break;
      }
      break;
  }
}

int Command() {
  if (Serial.available() > 0) {
    String buf = Serial.readString();
    char com = buf.charAt(0);
    char subcom[2];
    int val;

    subcom[0] = buf.charAt(1);
    
    val = buf.substring(2).toInt();

    switch (com) {
      case 'i':
        info();
        break;
      case 'D': // Debug
        isDebug = !isDebug;
        break;
      case 't':
        putEEPROM(subcom, val, &th);
        break;
      case 'b':
        putEEPROM(subcom, val, &br);
        break;
      case 'r': // reset
        Caribration_throttle(&th);
        Caribration_brake(&br);
        info();
        break;
      default:
        info();
        break;
    }
  }
}

void setup() {
  Serial.begin(9600);     // COM通信速度

  // ブレーキ/スロットルの分解能(Defaultは1023)
  Joystick.setThrottleRange(0, THROTTLE_AXIS);
  Joystick.setBrakeRange(0, BRAKE_AXIS);
  Joystick.begin();

  // 初期設定
  getEEPROM(&th);
  Caribration_throttle(&th);
  setJoystick(0, &th);

  getEEPROM(&br);
  Caribration_brake(&br);
  setJoystick(0, &br);
}

void loop() {
  int flg = 0;
  // データの読み込み
  if (!ReadThrottle(&th)) {
    th.data = setJoystick(th.raw, &th);
    flg = 1;
  }

  if (!ReadLoadcell(false, &br)) {
    br.data = setJoystick(br.raw, &br);
    flg = 1;
  }

  if (isDebug && flg) {
    Serial.print("T"); Serial.print(th.data); Serial.print("("); Serial.print(th.raw); Serial.print(") / ");
    Serial.print("B");
    {
      if (br.raw >= 0) {
        Serial.print(br.data); Serial.print("("); Serial.print(br.raw); Serial.println(")");
      }
      else  Serial.println("---");
    }
  }
  Command();
}
/**** ここまで ****/

 

Arduino IDE のシリアルモニタで微調整ができます。

コマンド 解説
i 現在の設定値を表示
r リセット
D デバッグモード
この数値をもとに下記のパラメータを設定する
bhパラメータ ロードセルの最大値(単位g)
bsパラメータ ブレーキペダルの最大値(単位g)
bdパラメータ ブレーキペダルのあそび(単位g)
tsパラメータ スロットルペダルの最大値
tdパラメータ スロットルペダルのあそび