18
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

このWebカメラを使ってな、このWebカメラを使ってな、ビジュアルフィードバックしようと思うたのじゃ

Last updated at Posted at 2021-12-17

1. 概要

通常、汎用OS上で動作するSimulinkはリアルタイム性が保証されていません。しかしSimulinkのSimulation Pacing機能やSimulink Desktop Real-Timeを用いることで、専用機器がなくとも、100 Hz程度ならばそこそこの(ソフト)リアルタイム性が得られる(可能性がある)そうです。低速とはいえ、実機実験の際にシリアルI/FのAD/DA変換機器さえ用意すれば、I/O以外の全機能をPC上のSimulinkのみに集中して実装できマイコンへの頻繁な書き込みが不要になったり、PC用のアクセサリが使用できたりするのは魅力的ですよね。

そこで今回はSimulink Desktop Real-Timeと手持ちのWebカメラを用いて簡易的なビジュアルフィードバック制御にトライした事例をレポートします。
rect12071.png

用語たち

  • Simulation Pacing: Simulinkモデルの実行時間を実時間と同期するため、サンプリング周期に合わせたウェイトを設けるSimulinkの実行方式です。kHzオーダーの同期は厳しいですが、100 Hzぐらいまでならそこそこの性能が出ます。
  • Simulink Desktop Real-Time: 専用I/Oデバイスとの連携により、Simulation Pacingより高精度なリアルタイム性を提供するアドオンです。シリアルポートなどの汎用I/Oを用いた場合はSimulation Pacingと同じ性能しか発揮できませんが、通信系のブロックが便利だったりデッドライン超過エラーが出せたりするので今回はこちらを使用しました。詳細はMATLABブログの記事が大変参考になります。
  • ビジュアルフィードバック制御: フィードバック制御系のセンサとして、カメラ画像を用いた制御のことです。

2. 結果

s1.gif
こんな感じのアームの位置決め制御ができました。フィードバック制御周期は40 msです。

3. 実装

3.1 ハードウェア構成

実験系のハードウェア構成は以下の写真の通り、DCモータに直結されたアームの角度をWebカメラで撮影しPCに取得、PCから送信されたPWM Dutyに従いモータドライバを駆動してアーム角度を制御します。
image.png

3.1.1 Webカメラ

Logicool C270nを使用しています。最高フレームレートは30 fpsです。
image.png

3.1.2 アーム

拾ったDCモータ(型式不明)に養生テープを巻いたアームを自称する何かを接着した、人類史に残るクソプラントです。
image.png

3.1.3 モータドライバ

Arduino Uno Rev3 + Motor Shield Rev3の組み合わせです。モータ駆動用電源はDC 5V/2.3 Aを使用しています。
image.png

3.1.4 PC

PCのスペックは以下の通りです。

  • CPU: Intel(R) Core(TM) i7-8700
  • RAM: 16 GB
  • OS: Windows 10 Home

3.2 ソフトウェア構成

3.2.1 Arduinoスケッチ

モータドライバたるArduino Unoには以下のスケッチを書き込んでいます。ポーリングループ内のparseInt関数でSimulinkからシリアル通信を待機し、受信後Motor Shieldの回転方向とPWM Dutyの設定を行います。

Arduinoスケッチ
#define PIN_DIRECTION 12
#define PIN_PWM 3
#define PIN_BRAKE 9

void setup() {
  /* for higher pwm frequency */
  TCCR2B = TCCR2B & B11111000 | B00000001;
  Serial.begin(115200);

  pinMode(PIN_DIRECTION, OUTPUT);
  pinMode(PIN_BRAKE, OUTPUT);

  /* disengage brake */
  digitalWrite(PIN_BRAKE, LOW);
}

void loop() {
  long v_out;

  /* receive control input from simulink */
  v_out = Serial.parseInt();

  if (v_out >= 0) { /* forward direction */
    digitalWrite(PIN_DIRECTION, HIGH);
    analogWrite(PIN_PWM, v_out);
  }
  
  else { /* reverse direction */
    digitalWrite(PIN_DIRECTION, LOW);
    analogWrite(PIN_PWM, -v_out);
  }

  /* flush buffer */
  while (Serial.available() > 0) {
    Serial.read();
  }
}

3.2.2 Simulinkモデル

使用したSimulinkモデルの全体は下図の通りです。以下、各部分について解説します。
image.png

Image Acquisition Toolbox/From Video Deviceブロック

image.png
Webカメラなどの映像デバイスとのI/Fを行うブロックです。
image.png
主要な設定項目は以下の通りです。

  • Video Format: 処理の高速化のため、フレームサイズを最小の160x120に変更しています。
  • Block sample time: フレームレートは25 fpsで設定しています。
image.png また、***Edit properties***ボタンからカメラの撮影設定を行います。撮影の高速化のため、***Backlight Compensation***、***Exposure Mode***、***White Balance Mode***はすべてManualに設定し、さらに***Exposure***をやや下げています。

アーム領域のバイナリマスク作成

image.png
カメラ画像からのアーム角度の取得に先立ち、画像中のアーム領域を抽出します。まずANDブロック以前の部分でアーム(というか養生テープ)の蛍光グリーンを抽出するため、HSV色空間に変換してから閾値処理を行っています。この閾値の決定のため、1枚画像を撮影し、「色のしきい値」アプリを使用しました。
image.png
続いてノイズ除去とアーム領域を細くして角度推定精度を向上するため、Erodeブロックで収縮処理を実施しています。
image.png
パラメータは以下の通りです。
image.png

Hough変換によるライン角度検出

image.png
前処理されたバイナリマスクをもとに、Hough変換を用いてラインの角度を検出します。Hough変換のパラメータは以下の通りです。
image.png
通常、このあとは出力HoughをHough Peaksブロックに入力し、検出されたライン角度Thetaに対応する局所ピークのインデックスを取得します。しかし今回は唯一のラインを検出すればよいため、単にMaximumブロックで全体最大インデックスを取得しています。

画像処理全体の様子を以下の動画に示します。
feawg.gif

コントローラ

image.png
コントローラはPD制御と不感帯補償のためのディザで構成し、目標値は0°、つまり画像上下軸として設定しました。ただしモータの配線の都合により、極性を反転させています。PD制御器のパラメータは以下の通りで、制御周期はカメラサンプリング周期を引き継ぎ40 msで駆動されます。
image.png
一方ディザのパラメータは以下の通りで、こちらはさらに短いサンプリング周期10 msで駆動しています。
image.png

出力リミット

image.png
Saturationブロックを用いて制御入力の大きさをArduino UnoのPWM範囲0~255(+符号)に制限します。
image.png

Simulink Desktop Real-Timeによる同期I/O

image.png

最後に決定された制御入力を、制御周期10 msごとにシリアルI/F経由でArduino Unoに送信します。設定は以下の通りです。
image.png

  • Data acquisition board: Arduino Unoを接続したシリアルポートを指定します。
  • Sample time: 10 msに設定します。
  • Maximum missed ticks: リアルタイム処理のデッドライン超過が指定数を超えたときにエラー終了させる設定です。長すぎるとリアルタイム処理の失敗に気づきませんが、短すぎるとパラメータ調整のためにブロックパラメータを開いたりシリアル通信の開始時のディレイなどで容易にエラー終了してしまいます。Show "Missed Ticks" portにチェックを行えばデッドライン超過数を出力できるため、Maximum missed ticksはやや大きめに取り、Missed Ticks portの出力を監視するのも手です。
  • Format String: printf関数のフォーマット指定と同じ要領です。Arduino UnoのparseInt関数は"\r\n"を終端子とするためこれを付与しています。
  • Initial string/Final string: 初回及び終了時の送信内容です。両者とも入力0を指定しています。

4. 結果

4.1 初期値応答

冒頭のデモ映像(再掲)と、PD制御入力とアーム角の時系列を示します。
s1.gif
image.png

4.2 インパルス応答

位置決め制御系を見たらしばけ、古事記にもそう書かれている。

ということで下図のインパルス応答試験装置を用いた応答試験を実施しました。
image.png
(クソエイムに伴う20回ほどのリテイクののち)以下のアームの挙動が得られました。
imp3.gif
image.png

5. まとめ

ということで、Simulink Desktop Real-Timeを用いた簡易的なリアルタイム制御のデモでした。前述の通り、あまり高速には対応できず、同期を取ってなおOSの機嫌次第で数ティック落ちるなどの問題がありますが、低速用途であればなかなかに使いでがありそうです。

これ以上の高速化を求める場合、Simulink Desktop Real-Timeの専用のI/Oボードを用いるほか、今回のようにArduinoをI/Oボードとして機能させる構成だと@motorcontrolmanさんの記事がとても参考になります。

おまけ

カメラ映像のリアルタイム性について

Webカメラの映像が所定の周期でサンプルされているかについては情報がなかったので、簡易的に検証を行いました。マイコンの多色LEDをサンプリング周期で赤青に点滅させ、各取得画像で点滅を検出できるかを調べます。以下が各フレームの画像ですが、少なくともこの速度帯では、正常に取得できているようです。
image.png

その他の例

初めてこの機能を知った時にテンションが上がりすぎていろいろ制御していたので、そのログをば。

プラントはすべて、以下で作成した電気回路です。

18
5
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
18
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?