LoginSignup
1
1

More than 3 years have passed since last update.

OctaveとArduinoを使ってモータを制御してみる

Posted at

はじめに

Octaveには、Arduino と通信して GPIO を HIGH / LOW したりするための Octave Arduino Toolkit が用意されています。この記事では、OctaveとArduinoを使ってモータを制御するための、Octave Arduino Toolkit パッケージの使い方を取り扱います。

大まかな流れは以下の通りです。

題目 内容 参考
導入編 パッケージをインストールする。
Arduinoに接続する。
Wiki
基本編 LEDを光らせる。
電圧を読む。
PWMでモータを動かす。
Wiki
関数リファレンス
応用編 割り込みで単相型エンコーダを読む。 マニュアル
関数リファレンス
実践編 モータを制御する。

環境

  • DELL Vostro 1000
  • Bodhi Linux 5.0.0
  • Octave 4.2.2
  • Octave Arduino Toolkit 0.6.0
  • Arduino IDE 1.8.8
  • Arduino Nano
  • 制御対象物(LED、エンコーダ、モータ等)

導入編

使用する前に、パッケージを読み込みます。

octave
>> pkg load arduino

パッケージがない場合は、インストールしてからパッケージを読み込みます。
(少々時間がかかります)

octave
>> pkg install -forge arduino
>> pkg load arduino

Arduinoに、octave通信用のスケッチを書き込みます。
以下のコマンドを実行するとArduino IDEが立ち上がるので、IDE上で書き込みを実行します。
(一度書き込めばOK)

octave
>> % スケッチ書き込み
>> arduinosetup
>> % IDEが起動しない場合は、IDEのインストールパスを指定して書き込み
>> arduinosetup('arduinobinary', '{InstallDir}/arduino')

arduinoをPCにUSB接続したのち、octaveからarduinoへ接続します。

octave
>> % Arduinoと接続
>> a = arduino;
>> % 接続できない場合は、デバイスポートを指定してArduinoと接続
>> a = arduino("/dev/ttyUSB0");

arduinoが接続されているポートを以下の関数で調べることができます。(環境によっては、通信が上手くいかずに見つけられないことがあるようです。scanForArduinos関数の受信処理にpauseを挟むと上手くいきました。)

octave
>> scanForArduinos

基本編(Lチカ、電圧計測、モータ駆動)

LEDを光らせます。

octave
>> pkg load arduino % パッケージ読み込み
>> ar = arduino;    % arduino 接続
>> led_pin="d13";   % LED pin の設定
>> writeDigitalPin (ar, led_pin, 1); % LED点灯
>> writeDigitalPin (ar, led_pin, 0); % LED消灯

電圧を読みます。

octave
>> pkg load arduino % パッケージ読み込み
>> ar = arduino;    % arduino 接続
>> sensor_pin="a4"; % sensor pin の設定
>> readAnalogPin(ar, sensor_pin) % 電圧の読み込み

モータドライバとモータを接続し、モータを動かします。(回路設計は割愛)

octave
>> pkg load arduino % パッケージ読み込み
>> ar = arduino;    % arduino 接続
>> motor_pin="d5";  % motor pin の設定 ※PWMに対応していること
>> writePWMDutyCycle(ar, motor_pin, 1.0); % モータ作動
>> writePWMDutyCycle(ar, motor_pin, 0.0); % モータ停止

この他にも便利な関数がたくさんあるため、いろいろ試してみてください。

主なライブラリ/アドオン 主な機能
i2cdev I2C通信ライブラリ
servo RCサーボモータ用ライブラリ
ultrasonic 超音波センサ用ライブラリ
rotaryEncoder 2相型ロータリエンコーダ用ライブラリ
ExampleAddon アドオンのサンプル
adafruit Adafruit Motor Shild v2向けアドオン

応用編(割り込みによるエンコーダカウンタ)

割り込みで単相型エンコーダを読みこみます。

Octave Arduino Toolkit には、任意のスケッチをArduinoに仕込んで、Octaveから呼び出す「アドオン」の仕組みが用意されています。今回はそれを利用して、エンコーダに接続したポートを監視して、変化毎にカウントアップする関数を割り込ませるようにします。

はじめに、新たにアドオンを作成します。

shell
$ cd {InstallDir}/+arduinoaddons    # アドオンのフォルダに移動
$ mkdir +UserAddon                  # 新たなアドオンを作成
$ touch +UserAddon/SingleEncoder.h  # 新たなアドオンのArduino側プログラム作成
$ touch +UserAddon/SingleEncoder.m  # 新たなアドオンのOctave側プログラム作成

Arduino側プログラムを実装します。

SingleEncoder.h

#include "LibraryBase.h"

#define ENC  2                  // エンコーダを接続するピン
#define CMDID_READ_COUNT 0x01   // Octaveと通信でやり取りするコマンドID

// 割り込み時に操作する変数
volatile bool pin = false;
volatile bool pin_old = false;
volatile unsigned long count = 0;

// 割り込み時に実行する関数
void readEncoder() {
  pin = digitalRead(ENC);
  if( pin == !pin_old ) count++;  // チャタリング防止
  pin_old = pin;
}

const char NO_NEED_VALUE[] PROGMEM = "No Needs a value to readCount";

class SingleEncoder : public LibraryBase
{
public:
  SingleEncoder(OctaveArduinoClass& a)
  {
    // アドオンの設定
    libName = "UserAddon/SingleEncoder"; // libNameは{フォルダ名}/{クラス名}
    a.registerLibrary(this);
    // 割り込みの設定
    attachInterrupt(digitalPinToInterrupt(ENC), readEncoder, CHANGE);
  }

  void commandHandler(uint8_t cmdID, uint8_t* data, uint8_t datasz)
  {
    unsigned long tmp;
    switch (cmdID)
    {
      case CMDID_READ_COUNT:
        // カウンタの読み込み
        if(datasz == 0){
          tmp = count; count = 0; // カウンタのリセット
          data[0] = (tmp>>24)&0xff;
          data[1] = (tmp>>16)&0xff;
          data[2] = (tmp>>8)&0xff;
          data[3] = (tmp)&0xff;
          datasz = 4;
          sendResponseMsg(cmdID, data, datasz); // カウンタの送信
        }else{
          sendErrorMsg_P(NO_NEED_VALUE); // エラーメッセージの送信
        }
        break;

      default:
        sendUnknownCmdIDMsg(); // エラーメッセージの送信
    }
  }
};

Octave側プログラムを実装します。

SingleEncoder.m

classdef SingleEncoder < arduinoio.LibraryBase
  % プライベート変数
  properties(Access = private, Constant = true)
      CMDID_READ_COUNT = hex2dec("01");
  end
  % パブリック変数
  properties(Access = protected, Constant = true)
    % LibraryName は{フォルダ名}/{クラス名}
    % CppHeaderFile は{クラスヘッダファイル名}
    % CppClassname は{クラス名}
    LibraryName = 'UserAddon/SingleEncoder';
    DependentLibraries = {};
    ArduinoLibraryHeaderFiles = {};
    CppHeaderFile = fullfile(arduinoio.FilePath(mfilename('fullpath')), 'SingleEncoder.h');
    CppClassName = 'SingleEncoder';
  end
  % クラスメソッド
  methods
    % コンストラクタ
    function obj = SingleEncoder(parentObj)
      obj.Parent = parentObj;
      obj.Pins = [];
    end
    % カウンタの読み込み
    function count = readEncCount(obj)
      cmdID = obj.CMDID_READ_COUNT;
      [tmp, sz] = sendCommand(obj.Parent, obj.LibraryName, cmdID, []);
      count = uint32(tmp(1))*(256*256*256) + uint32(tmp(2))*(256*256) + uint32(tmp(3))*256 + uint32(tmp(4));
    end

  end
end

Arduinoに、パッケージ+アドオンのスケッチを書き込みます。
(Arduino側プログラムを変更するたびに実行します)

octave
>> % パッケージ読み込み
>> pkg load arduino
>> % アドオンを指定してスケッチ書き込み
>> arduinosetup('libraries', 'UserAddon/SingleEncoder')
>> % IDEが起動しない場合は、IDEのインストールパスとアドオンを指定して書き込み
>> arduinosetup('arduinobinary', '{InstallDir}/arduino', 'libraries', 'UserAddon/SingleEncoder')

エンコーダを接続し、適当に回した後、カウンタを読み込みます。(回路設計は割愛)

octave

>> ar = arduino([], [], 'libraries', 'UserAddon/SingleEncoder', 'forcebuild', true); % arduino 接続
>> en = addon(ar, "UserAddon/SingleEncoder");                                        % addon   接続
>> readEncCount(en)                                                                  % カウンタ読み込み

実践編(モータの制御)

モータを制御します。

単なるループだと、一定間隔でコマンドを送ることができないため、pauseを利用します。

main.m
function main
    % パッケージをロード
    pkg load arduino
    % ArduinoにOctave通信用のコアライブラリと自作ライブラリを書き込み
    arduinosetup('libraries', 'UserAddon/SingleEncoder')
    % Arduinoと接続
    ar=arduino([], [], 'libraries', 'UserAddon/SingleEncoder', 'forcebuild', true);
    % Addonと接続
    en=addon(ar, "UserAddon/SingleEncoder");

    % 演算周期
    dt = 0.1;

    % 制御対象を初期化
    ENC = "d5";
    writePWMDutyCycle(ar, ENC, 0);

    unwind_protect
        % 制御ループ
        while(true)
            tic
            % エンコーダのカウンタを読み込み
            cnt = readEncCount(en);
            % カウンタよりモータへの入力値を演算
            u   = control(cnt);
            % モータへの入力値をコマンド送信
            writePWMDutyCycle(ar, ENC, u);
            % 残り時間は待機
            pause(dt-toc)
        end
    unwind_protect_cleanup
        % 終了処理
        writePWMDutyCycle(ar, ENC, 0);
        clear en
        clear ar
    end_unwind_protect
end

function u = control(cnt)
    u=1.0;
    % ここに制御プログラムを実装する
end

おわりに

Octave Arduino Toolkit により、容易にArduinoを操作できるため、制御設計が非常にやりやすくなります。

Octave Arduino Toolkit と同様なパッケージはMATLABにもあり、(というかMATLABにはだいぶ前からありました)そちらの方が容易に同じことができるかもしれません。Octaveの良いところは、オープンソースなため拡張性に優れるところです。上記のプログラムも、パッケージのソースコードを参考にしながら作ることができました。

一方で、当然のことながら、OctaveにしろMATLABにしろ、Arduinoに無駄な処理負荷及び通信負荷をかけることになるため、短い演算周期で制御ループを回すことはできません。(筆者の環境で100msが限界でした。)開発初期の簡単なテストなどに使うのが良いと思いました。

参考文献

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1