bitterharvest’s diary

A Bitter Harvestは小説の題名。作者は豪州のPeter Yeldham。苦闘の末に勝ちえた偏見からの解放は命との引換になったという悲しい物語

HaskellでLegoのモータとセンサをRaspberry Piを利用して制御する

1.GitHubに掲載

BrickPiをHaskellから利用するための外部関数インタフェースを完成させると、HaskellからLEGO社のモータやセンサが使えるようになる。外部関数インタフェースとC言語でのプログラムはこの記事で掲載するのには無理なほど大きなサイズになった。そこで、GitHubにアップロードしたので、適宜参照して欲しい。github.com


プログラムの中で、BrickPi.cとBrickPi.hは、Dexter Industry社が提供していたBrickPi.hを修正したものである。これらは、モータやセンサへの制御データを設定するのに利用する。tick.hとtick.cは、tick.hを修正したもので、時間の制御に利用する。

また、BrickHS.hsは、上記のプログラムへの外部関数インタフェースを定義したものである。Haskellでプログラムを作成したときは、次のようにコンパイルして実行プログラムを作成する。

ghc --make 応用プログラム.hs BrickPi.o tick.o

なお、BrickPi.o, tick.oのかわりにBrickPi.c, tick.cを用いてもよい。

GitHubには、c言語用のライブラリに含まれていたテストプログラムも一緒にアップロードしてある。

これらは次のようになっている。

プログラム名 機能
Motor-test.hs 二つのモータを利用した車を前進、後退、左折、右折させる
LEGO-Motor-Test.h 二つのモータを使い前進と交代を繰り返す
Lego-TouchSensor-Test.hs タッチセンサに触れたかどうかを返す
Lego-UltrasonicSensor-Test.hs センサと障害物の間がどれだけ離れているかを計測する。出てくる数値は実際の長さ(cm)よりも少し大きめである
Lego-ColorSensor-Test.hs 6種類に分けて色情報を返す
simplebot-simple.hs 二つのモータを利用した車が、w,a,s,d,xのキーを押すと前進、左折、後退、右折、停止する

2.プログラムの紹介

GitHubにアップロードしてあるプログラムの二つだけ、即ち、LEGO-Motor-Test.hsとLEGO-TouchSesor-Test.hsを紹介する。他は同じような構造なので、これから推察することができる。プログラムは次のようになっている。

import BrickHs
import Control.Concurrent.Thread.Delay

main = do 
  clearTick
  result <- brickPiSetup
  print "BrickPiSetup: "
  print (show result)
  if result /= 0 then 
      return () 
    else do
      setAddress 0 1
      setAddress 1 2
      setMotorEnable port_a 1
      setMotorEnable port_b 1
      result1 <- brickPiSetupSensors
      print "BrickPiSetupSensors:" 
      print (show result1)
      delay 10000
      motor 0 1
      return ()

motor time rot = do
  let time1 = if rot == 1 then time + 1 else time - 1 
  if rot == 1 then do
      setMotorSpeed port_a 200
      setMotorSpeed port_b 200
    else do
      setMotorSpeed port_a (-200)
      setMotorSpeed port_b (-200)
  brickPiUpdateValues
  delay 10000
  if time1 > 300 then 
      motor time1 0
    else if time1 < 0 then 
             motor time1 1
           else 
             motor time1 rot

このプログラムは前進と交代を繰り返す。それを実際に行っているのは関数motorの部分である。この関数で、回転方向を示しているのが、rotである。rotが1の時は前進で、そうでないときは後退である。また、その持ち時間はtimeで表している。前進しているときには、timeは増えていくが300に達したとき終わりで、今度は、後ろ向きに回転する。また、timeは減っていく。減っていくことの限界は0である。ここに達すると、前進となり、再び増えることになる。

モータやセンサへの制御データの伝達の仕方には一定のパターンがあって、最初に、刻んでいる時間をclearTickで初期化する。次に、BrickPiが使える状態にあるかどうかをbrickPiSetupで調べる。もしよければ、BrickPiのアドレスを設定する。ハードウェアのところで述べたが、BrickPiは分離した二つの部分(一つはモータA,B,ポート1,2、他の一つはモータC,D,ポート3,4)から成り立っているので、これらのアドレスである。次の、モータであれば、稼働させるための情報をsetMotorEnableで与える。この制御情報が伝達されたかどうかをbrickPiSetupSensorsで調べる。大丈夫のようならモータの回転速度をsetMotorSpeedで指定し、この情報をbrickPiUpdateValuesで伝達する。後は、この部分を繰り返す。

次は、タッチセンサである。このプログラムは以下のようになっている。モータのプログラムとの相違は、モータを駆動していた部分で、このセンサのタイプをsetSensorTypeで指定していることである。センサの場合にはタイプを指定することでどのセンサを使うかを特定する。このセンサが触られているかどうかの情報はgetSensorで得る。触っていれば1が返ってくる。そうでなければ0である。

import BrickHs
import Control.Concurrent.Thread.Delay

main = do 
  clearTick
  result <- brickPiSetup
  print "BrickPiSetup: "
  print (show result)
  if result /= 0 then 
      return () 
    else do
      setAddress 0 1
      setAddress 1 2
      setSensorType port_1 type_sensor_touch
      result1 <- brickPiSetupSensors
      print "BrickPiSetupSensors:" 
      print (show result1)
      if result1 /= 0 then
          return ()
      else do
          delay 10000
          touch 
          return ()


touch = do
  result <- brickPiUpdateValues
  if result == 0 then do
      print "Results: " 
      value <- getSensor port_1
      print (show value)
      delay 10000
    else
      delay 10000
  touch
  return ()

なお、両方のプログラムで時間間隔を作るために、delayを用いた。これはマイクロ秒を単位としているが、この関数を利用するためにはconcurrent-extraのモジュールを必要とする。