Raspberry Pi(ロボット)

目次Raspberry Piロボット|センサー(温度・気圧・湿度人感)|赤外線リモコン読み上げMQTT連携IFTTT連携

ここでは、簡単なロボットを作って、LEDランプのON/OFF、DCモータの制御、サーボモータの制御、Webカメラのストリーミング配信の基本機能を試して見ます。
また、Webとの連携ソフトを使ってスマートフォンで操作出来るようにします。

ロボットの構成

  1. Raspberry Pi B を使って、DCモーター、サーボモーター、LEDランプの制御と、
    Webカメラの動画配信が出来る様にします。
  2. ロボットの操作はPCやスマートフォンで操作出来る様にWebブラウザを使います。
  3. Raspberry Pi OSの設定でモニター、キーボード、マウスを一次的に使いますが、
    OSの基本設定完了後はPCでリモートコントロール操作出来る様にします。
  4. 配線はブレッドボードを使います。電源はRaspberry Piとモーター用電源に分けて
    モバイルバッテリーを使います。

画像をクリックすると拡大表示できます。

Webiopiを設定する

Webiopiインストールしていない場合は「Webブラウザからの遠隔制御(Webiopi)の設定」でインストールして下さい。

webiopi/configの設定

設定デイレクトリーは下記の通りとします。

ユーザディレクトリ/xxx
          ┣ script.py(pythonプログラムの処理)
          ┗ zzzz.html (HTMLプログラム)  

個別プログラムと連動する場合は、Webiopi開始時のPythonスクリプトとHTMLドキュメントのディレクトリ及び
省略時のHTMLファイル名を「/etc/webiopi/config」ファイルに追記して指定します。

・・・・省略・・・・
[SCRIPTS]
# Load custom scripts syntax :
# name = sourcefile
# each sourcefile may have setup, loop and destroy functions and macros
#myscript = /home/pi/webiopi/examples/scripts/macros/script.py
myscript = /home/pi/xxx/script.py
#------------------------------------------------------------------------#

[HTTP]
# HTTP Server configuration
enabled = true
port = 8000

# File containing sha256(base64("user:password"))
# Use webiopi-passwd command to generate it
passwd-file = /etc/webiopi/passwd

# Change login prompt message
prompt = "WebIOPi"

# Use doc-root to change default HTML and resource files location
#doc-root = /home/pi/webiopi/examples/scripts/macros
doc-root = /home/pi/xxx
# Use welcome-file to change the default "Welcome" file
#welcome-file = index.html
welcome-file = zzzz.html
#------------------------------------------------------------------------#
・・・・省略・・・・

LEDをon/ofする

Webiopi開始時にLEDをオンにして、終了時にオフにします。また、HTMLプログラムでオン/オフで出来る様にします。

結線図


Pythonスクリプト(script-led.py

import webiopi
###################################################
# LED制御
###################################################
webiopi.setDebug()
GPIO = webiopi.GPIO

LIGHT = 23 # GPIO pin using BCM numbering
GPIO.setFunction(LIGHT, GPIO.OUT)
GPIO.digitalWrite(LIGHT, GPIO.HIGH)

@webiopi.macro
def lighton():
        GPIO.digitalWrite(LIGHT, GPIO.HIGH)

@webiopi.macro
def lightoff():
        GPIO.digitalWrite(LIGHT, GPIO.LOW)

# destroy function is called at WebIOPi shutdown
def destroy():
    GPIO.digitalWrite(LIGHT, GPIO.LOW)

HTMLプログラム(html-led.html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>RaspberryPi</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<script type="text/javascript" src="/webiopi.js"></script>
</head>
<body>
<H1 style="text-align:center;">RaspberryPi</H1>
<form name="form">
<table style="margin:0 auto;"><tr><td style="text-align:center;">
<input class="bt" type="button" value="ライトオン" onclick="webiopi().callMacro( 'lighton' );"></td><td>
<input class="bt" type="button" value="ライトオフ" onclick="webiopi().callMacro( 'lightoff' );"></td></tr>
</table>
</form>
<script>
webiopi().ready( initialize_webiopi );
function initialize_webiopi(){
    // GPIOの状態を監視しない
    webiopi().refreshGPIO(false);
}
</script>
</body>
</html>

DCモーターを動かす

タミヤ ツインモーター ギヤボックス
モータドライバ(TA7291P)

モータドライバ(TA7291P)について

PIN端子について
PIN
端子
名称 機能
1 GND GND
2 OUT1 モーター出力端子1
3 NC 未接続
4 VREF 制御電源端子(0〜20V)※
5 IN1 ロジック入力端子1
6 IN2 ロジック入力端子2
7 VM モーター用電源
8 RS モーター出力電流検出用端子
9 NC 未接続
10 OUT2 モーター出力端子2
ロジック入力端子について
IN1 IN2 モーター動作
0 0 ストップ
PWM 0 正転(PWM値に応じて可変)
0 PWM 逆転(PWM値に応じて可変)
1 1 ブレーキ

結線図

※ 制御電源端子「VREF」の接続は、モーター駆動用バッテリーから
抵抗(3k〜10kΩ)を介して配線します。

Pythonスクリプト(script-motor.py

import webiopi
import time
#############################################################
#モータ制御
#############################################################
PIN_L1 = 27
PIN_L2 = 17
PIN_R1 = 11
PIN_R2 = 9

g_mode = 0
g_percentage = 50

GPIO.setFunction( PIN_L1, GPIO.PWM )
GPIO.setFunction( PIN_L2, GPIO.PWM )
GPIO.setFunction( PIN_R1, GPIO.PWM )
GPIO.setFunction( PIN_R2, GPIO.PWM )

def MotorDrive( iIn1Pin, iIn2Pin, percentage ):
  if 100 < percentage:
    percentage = 100
  if -100 > percentage:
    percentage = -100
  if 10 > percentage and -10 < percentage:
    GPIO.pwmWrite( iIn1Pin, 0.0 )
    GPIO.pwmWrite( iIn2Pin, 0.0 )
  elif 0 < percentage:
    GPIO.pwmWrite( iIn1Pin, percentage * 0.01 )
    GPIO.pwmWrite( iIn2Pin, 0.0 )
  else:
      GPIO.pwmWrite( iIn1Pin, 0.0 )
      GPIO.pwmWrite( iIn2Pin, -percentage * 0.01 )

@webiopi.macro
def ChangeDriveMode( mode ):
  if mode == "0":
    webiopi.debug("ChangeDriveMode : Stop")
    MotorDrive( PIN_L1, PIN_L2, 0 );
    MotorDrive( PIN_R1, PIN_R2, 0 );
  elif mode == "1":
    webiopi.debug("ChangeDriveMode : Forward")
    MotorDrive( PIN_L1, PIN_L2, g_percentage );
    MotorDrive( PIN_R1, PIN_R2, g_percentage );
  elif mode == "2":
    webiopi.debug("ChangeDriveMode : Backward")
    MotorDrive( PIN_L1, PIN_L2, -g_percentage );
    MotorDrive( PIN_R1, PIN_R2, -g_percentage );
  elif mode == "3":
    webiopi.debug("ChangeDriveMode : CW")
    MotorDrive( PIN_L1, PIN_L2, g_percentage );
    MotorDrive( PIN_R1, PIN_R2, -g_percentage );
  elif mode == "4":
    webiopi.debug("ChangeDriveMode : CCW")
    MotorDrive( PIN_L1, PIN_L2, -g_percentage );
    MotorDrive( PIN_R1, PIN_R2, g_percentage );
  global g_mode
  g_mode = mode

@webiopi.macro
def ChangeVoltageLevel( level ):
  webiopi.debug("ChangeVoltageLevel : %s" % (level))
  global g_percentage
  g_percentage = 1 * int(level)
  ChangeDriveMode( g_mode )

HTMLプログラム(html-motor.html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>RaspberryPi</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<script type="text/javascript" src="/webiopi.js"></script>
</head>
<body>
<H1 style="text-align:center;">RaspberryPi</H1>
<form name="form">
<table style="margin:0 auto;"><tr><td style="text-align:center;">
<input class="bt" type="button" value="前進" onclick="webiopi().callMacro( 'ChangeDriveMode',1 );"><br>
<input class="bt" type="button" value="左" onclick="webiopi().callMacro( 'ChangeDriveMode',4 );">
<input class="bt" type="button" value="停止" onclick="webiopi().callMacro( 'ChangeDriveMode',0 );">
<input class="bt" type="button" value="右" onclick="webiopi().callMacro( 'ChangeDriveMode',3 );"><br>
<input class="bt" type="button" value="後退" onclick="webiopi().callMacro( 'ChangeDriveMode',2 );"><br>レベル<br>
<input type="range" name="range1" min="1" max="100" step="1" value="1" onchange="vlevel(form.range1.value)"> <span id="inp">1</span>
</td></tr></table>
</form>
<script>
//****************************************
//モータ制御用
//****************************************
webiopi().ready( function() {
webiopi().callMacro( 'ChangeDriveMode',0 );
vlevel(1) ;
} ) ;
function vlevel(level) { inp.innerHTML = level ; webiopi().callMacro( "ChangeVoltageLevel", level); }
</script>
</body>
</html>

サーボモーターを動かす

PWM信号生成ライブラリ(WiringPi)の設定

サーボを精度良く制御するために必要な、精度の高いPWM信号を生成するためのライブラリである”wiringPi”をインストールします。
GitHubからWiringPiをダウンロードします。

git clone https://github.com/WiringPi/WiringPi.git

ダウンロードしたWiringPiディレクトリに移動してビルドします。

cd WiringPi
sudo ./build

PythonからWiringPiを操作するための”WiringPi2-Python”をインストールします。

$ cd
$ sudo apt-get update
$ sudo apt-get install python-dev python-setuptools
$ git clone https://github.com/Gadgetoid/WiringPi2-Python.git
$ cd WiringPi2-Python
$ sudo python setup.py install
$ sudo python3 setup.py install

WebIOPiと連携させるために、Raspbyerry Pi標準のPython2.xに加え、Python3.xもインストールしています。

サーボモーター(SG90)について

SG90の仕様
項目 仕様
PWMサイクル 20ms
制御パルス 0.5ms~2.4ms
制御角 ±約90°(180°)
動作速度 0.1秒/60°
動作電圧 4.8V(~5V)
Duty比とDuty値
角度 μs Duty% Duty Cycle ※
-90 500 2.5 25.6
-45 975 4.88 50.0
0 1450 7.25 74.2
45 1925 9.63 98.6
90 2400 12 122.9

※Duty Cycle = 100% のときに1024とした場合の値

結線図

Pythonスクリプト(script-servo.py

import webiopi
import time
########################################################
# サーボ制御
########################################################
import wiringpi
def getServoDutyForWebIOPi(val):
    val_min = 0.0
    val_max = 1.0
    servo_min = 25   # 50Hzで, 0.7ms
    servo_max = 123  # 50Hzで, 2.0ms

    duty = int((servo_max-servo_min)*(val-val_min)/(val_max-val_min) + servo_min)
    return duty

wiringpi.wiringPiSetupGpio() # GPIO名で番号指定

wiringpi.pinMode(18, wiringpi.GPIO.PWM_OUTPUT)
wiringpi.pinMode(19, wiringpi.GPIO.PWM_OUTPUT)

wiringpi.pwmSetMode(wiringpi.GPIO.PWM_MODE_MS) # 周波数固定
wiringpi.pwmSetClock(375) # 50 Hz

wiringpi.pwmWrite(18, getServoDutyForWebIOPi(0.5))
wiringpi.pwmWrite(19, getServoDutyForWebIOPi(0.5))

@webiopi.macro
def setHwPWM1(duty, commandID):
    wiringpi.pwmWrite(18, getServoDutyForWebIOPi(float(duty)))
@webiopi.macro
def setHwPWM2(duty, commandID):
    wiringpi.pwmWrite(19, getServoDutyForWebIOPi(float(duty)))

HTMLプログラム(html-servo.html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>RaspberryPi</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<script type="text/javascript" src="/webiopi.js"></script>
</head>
<body>
<H1 style="text-align:center;">RaspberryPi</H1>
<form name="form">
<table style="margin:0 auto;"><tr><td style="text-align:center;">
<table style="margin:0 auto;"><tr><td>
右<input type="range" name="range21" min="0" max="20" step="1" value="10" onchange="sv1(form.range21.value)"><br>
左<input type="range" name="range22" min="0" max="20" step="1" value="10" onchange="sv2(form.range22.value)"><br>
</td></tr></table>
</form>
<script>
//*******************************************
//サーボ用
//*******************************************
webiopi().ready( initialize_webiopi );
// 命令送信ごとに増加するIDを作成(iOSのSafariでPOSTがキャッシュされることの対策)
var commandID=0;
function initialize_webiopi(){
    // GPIOの状態を監視しない
//  webiopi().refreshGPIO(false);
}
function sv1(z) {
  ratio = z / 20 ;
  webiopi().callMacro("setHwPWM1", [ratio, commandID++]);
}
function sv2(z) {
  ratio = z / 20 ;
  ratio = 1.0 - ratio;
  webiopi().callMacro("setHwPWM2", [ratio, commandID++]);
}
</script>
</body>
</html>

Webカメラを使う

Logitech C270の仕様

120 万画素、ビデオ解像度(1280×720)、最大フレームレート30fps、内臓マイク付

Webカメラ ストリーミング(mjpeg-streamer)の設定

次のコマンドでインストールします。

# Update & Install Tools
sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get install build-essential libjpeg9-dev imagemagick libv4l-dev cmake -y

# Clone Repo in /tmp
cd /tmp
git clone https://github.com/jacksonliam/mjpg-streamer.git
cd mjpg-streamer/mjpg-streamer-experimental

# Make
make
sudo make install

Webサーバーの起動(インプットをRaspberry Pi用に修正)コマンド。
下記のコマンドで開始します。画像サイズ、フレームレート(fps)は任意に設定してください。

/usr/local/bin/mjpg_streamer -i "input_raspicam.so -x 640 -y 480 -fps 15 -q 80"
-o "output_http.so -p 8080 -w /usr/local/share/mjpg-streamer/www"

Webサーバーを起動しています。
http://RaspberryPiのIPアドレス:8080/へアクセスするとサンプルスクリプトとストリーミングされた映像を見ることができます。

映像表示スクリプト(html-camera.html

JavaScriptのサンプルスクリプトは必要に応じてストリーミングコンテンツの場所を変更します。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>RaspberryPi</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<script type="text/javascript" src="/webiopi.js"></script>
</head>
<body onload="createImageLayer();">
<H1 style="text-align:center;">RaspberryPi</H1>
<div id="webcam" style="text-align:center;">
<!--noscript--><img src="http://192.168.0.10:8080/?action=snapshot" /><!--/noscript-->
</div>
<script>
//*********************************************
// Webカメラ
//*********************************************
/* Copyright (C) 2007 Richard Atterer, richard息atterer.net
   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License, version 2. See the file
   COPYING for details. */
var imageNr = 0; // Serial number of current image
var finished = new Array(); // References to img objects which have finished downloading
var paused = false;

function createImageLayer() {
  var img = new Image();
  img.style.position = "absolute";
  img.style.zIndex = -1;
  img.onload = imageOnload;
  img.onclick = imageOnclick;
//img.src = "http://" + document.getElementById("ipa") + ":8080/?action=snapshot&n=" + (++imageNr);
  img.src = "http://192.168.0.10:8080/?action=snapshot&n=" + (++imageNr);
  var webcam = document.getElementById("webcam");
  webcam.insertBefore(img, webcam.firstChild);
}

// Two layers are always present (except at the very beginning), to avoid flicker
function imageOnload() {
  this.style.zIndex = imageNr; // Image finished, bring to front!
  while (1 < finished.length) {
    var del = finished.shift(); // Delete old image(s) from document
    del.parentNode.removeChild(del);
  }
  finished.push(this);
  if (!paused) createImageLayer();
}

function imageOnclick() { // Clicking on the image will pause the stream
  paused = !paused;
  if (!paused) createImageLayer();
}
</script>
</body>
</html>

ストリーミングの開始

下記のコマンドで開始します。画像サイズ、フレームレート(fps)は任意に設定してください。

$ /usr/local/bin/mjpg_streamer -i "input_uvc.so -r 320x240 -fps 15" 
    -o "output_http.so -p 8080 -w /usr/local/share/mjpg-streamer/www"

自動起動設定

自動起動設定をsystemd 「/etc/systemd/system/mjpg_streamer.service」ファイルに、下記の様に作成します。

# /etc/systemd/system/mjpg_streamer.service
    [Unit]
    Description=mjpg-streamer
    After=syslog.target
    
    [Service]
    Type=simple
    WorkingDirectory=/usr/local/bin/
    ExecStart=/usr/local/bin/mjpg_streamer -i "input_uvc.so -r 320x240
    -fps 15" 
    -o "output_http.so -p 8080 -w /usr/local/share/mjpg-streamer/www"
    
    TimeoutStopSec=5
    StandardOutput=null
    
    [Install]
    WantedBy = multi-user.target

自動起動コマンドの実行

作成したファイルを読み込みます。

$ sudo systemctl daemon-reload

以下コマンドでサービスの自動起動を設定します。

$ sudo systemctl enable mjpg_streamer

自動起動を停止したい場合は以下のコマンド

$ sudo systemctl disable mjpg_streamer

サービスの起動と停止は以下のコマンド

$ sudo systemctl start mjpg_streamer $ sudo systemctl stop mjpg_streamer

サービスの自動起動を設定したら再起動して確認してください。

ロボットの完成

結線


Pyshonスクリプト(script-python.py

HTMLプログラム(html-robot.html

完成動画

目次Raspberry Piロボット|センサー(温度・気圧・湿度人感)|赤外線リモコン読み上げMQTT連携IFTTT連携