スマートフォンから操作できるブルドーザーロボットです。前進、後退、回転が可能です。Qoosky WebSocket API を利用しています。制作費は Raspberry Pi が既に手元にあれば追加でおおよそ 1 万円です。センサー機能が搭載されていないため正確な操作はできませんが、学校や職場から自宅のロボットを動かすこともできます。
パーツを購入して大まかな部品を組み立てる (1日目)
必要な部品リスト
Raspberry Pi 一式が既に手元にある場合、工作に必要な部品リストは以下のとおりです。
部品名 | 参考価格 | 個数 |
---|---|---|
2チャンネルリモコン ブルドーザー工作基本セット | 1,800 円 | 1個 |
Grove I2C モータードライバモジュール | 1,740 円 | 1個 |
I2C 接続キャラクタ LCD モジュール ACM1602NI | 1,280 円 | 1個 |
cheero Canvas 3200mAh IoT 機器対応 モバイルバッテリー | 3,240 円 | 1個 |
9V形 乾電池 | 300 円 (使い捨ての場合) | 1個 |
10k 半固定抵抗 | 30 円 | 1個 |
1k 抵抗 | 20 円 | 1個 |
ユニバーサル基板 | 200 円 | 1個 |
画像ネットプリント | 30 円 (写真サイズ) | 5個 |
1.0mm プラ板 二枚入り | 418 円 | 1個 |
5.0mm プラ棒 | 515 円 | 1個 |
セメダイン ホビー用 20ml CA-221 | 182 円 | 1個 |
セメダイン 貼ってはがせる粘着材 | 448 円 | 1個 |
合計 | 10,323 円 | - |
手元に届いたので早速工作キットを組み立ててみます。
付属の説明書を読みながら組み立てます。後にプラ板で作るボックスがやや重たいため、モーターの負荷を考慮してブルドーザーの部品が上下に稼動しないモードで組み立てました。8 コマのベルトが不要であったりする等、数カ所つまずきポイントがありました。なお、コントローラはスマートフォンで代用するため、組み立て不要です。
細かいパーツを除くと、おおよそこれらのモジュールを組み合わせれば完成します。
ブレッドボードを用いて I2C デバイスの動作設定を行う (2日目)
ブレッドボードで回路を組む
以下のように回路を組みます。動作検証ですので、ブレッドボードを利用します。
ACM1602NI と Grove I2C モータードライバモジュールの動作電圧が 3.3v および 5v と異なるため、厳密に作るならば I2C の電圧レベルを変換する必要がありますが、今回のような簡単な回路ではあまり気にせずにそのまま接続しても動きます。
Connecting the 5V Arduino directly to a single 3.3V-powered I2C chip usually works, even though it violates official specifications in multiple ways.
http://playground.arduino.cc/Main/I2CBi-directionalLevelShifter
ピン配置の参考資料
I2C デバイスの動作確認
事前設定
制御プログラムを動作させるための Raspberry Pi 設定を事前に行います。
- IP 固定: 無線 LAN 接続の設定を行ったら IP を固定しておきます。
- パッケージの管理情報を最新にする: apt-get` コマンドでパッケージの管理情報を最新にしておきます。
sudo apt-get update
- I2C 設定を有効にしておきます。
I2C デバイスの動作検証
ACM1602NI の動作検証を行っておきます。以下のコマンドで、モータードライバの出力電圧が制御できることを確認します。
出力電圧 0V
i2cset -y 1 0x0f 0x82 0x00 0x00 i
出力電圧 2.5V (9V 電源の場合。M1、M2、両方)
i2cset -y 1 0x0f 0x82 0x46 0x00 i
i2cset -y 1 0x0f 0x82 0x00 0x46 i
i2cset -y 1 0x0f 0x82 0x46 0x46 i
電圧の向きが左右同じ (前進、後退)
i2cset -y 1 0x0f 0xaa 0x0a 0x01 i
i2cset -y 1 0x0f 0xaa 0x05 0x01 i
電圧の向きが左右逆 (右回転、左回転)
i2cset -y 1 0x0f 0xaa 0x09 0x01 i
i2cset -y 1 0x0f 0xaa 0x06 0x01 i
参考資料
注意事項
- モータードライバモジュールの上部にある、リセットボタンを押してから実行してください (モータードライバの電源を入れてから Raspberry Pi の電源を入れてください)
- モーターの耐電圧は 3.0V です。誤って高電圧をかけないように電圧レベルの設定値に注意してください。
- 電圧レベルと向きの両方を指定する必要があります。片方だけでは出力に失敗します。
制御プログラムを作る (3日目)
制御プログラム
websocket_clients のうち、Ruby のサンプルコードをもとにしました。ruby はバージョンが 2.1.3 のものを使用しました。依存 Gem が一つだけありますので、事前にインストールしておきます。
sudo gem install websocket-client-simple -v 0.3.0
bulldozer.rb
#!/usr/bin/ruby
# -*- coding: utf-8 -*-
require 'json'
require 'logger'
require 'websocket-client-simple'
API_TOKEN = 'XXXX-XXXX-XXXX-XXXX'
API_ENDPOINT = 'wss://api.qoosky.io/v1/controller/actuator/ws'
LCD_PIN = 4
$logger = Logger.new STDOUT
$last_time = Time.now
$speed = 70 # 9V 電源で 2.5V 程度の計算になります。
def lcd_update(key)
line1 = ["0x00 0x01", # クリア
"0x00 0x02", # HOME
"0x80 0x6b", "0x80 0x65", "0x80 0x79", # key
"0x80 0x#{(48+key).to_s(16)}",
"0x80 0x20", "0x80 0x#{($speed/70.0*2.5 + 48).round.to_s(16)}", "0x80 0x76", # 0-9v
"0x00 0xc0"] # 改行
case key
when 3
line2 = %w(73 70 65 65 64 20 75 70) # speed up
when 4
line2 = %w(73 70 65 65 64 20 64 6f 77 6e) # speed down
when 5
line2 = %w(66 6f 72 77 61 72 64) # forward
when 8
line2 = %w(62 61 63 6b 77 61 72 64) # backward
when 6
line2 = %w(6c 65 66 74) # left
when 7
line2 = %w(72 69 67 68 74) # right
else
line2 = %w(75 6e 64 65 66 69 6e 65 64) # undefined
end
line1.each{|ch| system "i2cset -y 1 0x50 #{ch}"}
line2.each{|ch| system "i2cset -y 1 0x50 0x80 0x#{ch}"}
end
def motor_drive(key)
hex = $speed.to_s(16)
case key
when 3 # speed up
$speed += 10
hex = '00'
when 4 # speed down
$speed -= 10
hex = '00'
when 5 # forward
system "i2cset -y 1 0x0f 0xaa 0x05 0x01 i"
when 8 # backward
system "i2cset -y 1 0x0f 0xaa 0x0a 0x01 i"
when 6 # left
system "i2cset -y 1 0x0f 0xaa 0x09 0x01 i"
when 7 # right
system "i2cset -y 1 0x0f 0xaa 0x06 0x01 i"
else
hex = '00'
end
system "i2cset -y 1 0x0f 0x82 0x#{hex} 0x#{hex} i"
end
begin
# WebSocket SSL クライアント
ws = WebSocket::Client::Simple.connect API_ENDPOINT
# コネクション確立時のイベント
ws.on :open do
if ws.handshake.valid?
$logger.info 'Successfully connected to the API server.'
ws.send({ token: API_TOKEN }.to_json)
# LCD バックライト ON
system "gpio -g mode #{LCD_PIN} out"
system "gpio -g write #{LCD_PIN} 1"
# LCD 初期化
system "i2cset -y 1 0x50 0x00 0x01"
system "i2cset -y 1 0x50 0x00 0x38"
system "i2cset -y 1 0x50 0x00 0x0f"
system "i2cset -y 1 0x50 0x00 0x06"
else
ws.emit :error, 'websocket handshake failed.'
end
end
# エラー時のイベント
ws.on :error do |err|
$logger.error "An unexpected error has occurred: #{err}"
ws.emit :close
end
# コネクション切断時のイベント
ws.on :close do |e|
raise "Connection closed."
end
# メッセージ受信時のイベント
ws.on :message do |msg|
$logger.info "received: #{msg}"
json = JSON.parse msg.to_s
if key = json['pushedKey']
lcd_update(key)
motor_drive(key)
end
$last_time = Time.now
end
# メインスレッド待機
timer = 100
loop do
if (timer == 0)
timer = 100
msg = "keep-alive message."
ws.send msg
$logger.info "sent: #{msg}"
else
timer -= 1
if Time.now - $last_time > 0.5
$logger.info("motor stop")
system "i2cset -y 1 0x0f 0x82 0x00 0x00 i" # モーター停止
end
end
sleep 0.1
end
rescue => e
$logger.warn "An error occurred: #{e}\nretrying..."
sleep 5
retry # 再試行
ensure
# 終了時に消灯して初期化
system "gpio -g write #{LCD_PIN} 0"
system "i2cset -y 1 0x50 0x00 0x01"
end
実行方法
ruby bulldozer.rb
動作概要: ボタン番号に応じて以下の動作をします。
ボタン番号 | 動作 |
---|---|
1 | 未定義 |
2 | 未定義 |
3 | 加速 (初期値は 9V 電源でモーター電圧 2.5V) |
4 | 減速 (初期値は 9V 電源でモーター電圧 2.5V) |
5 | 前進 |
6 | 左回転 |
7 | 右回転 |
8 | 後退 |
10 秒毎に keep-alive メッセージを送信してサーバーからの切断を防ぎます。ネットワーク障害などが原因でコネクションが切断された時は自動で再接続します。加速と減速は電源電圧が低下してきた場合に利用します。加速しすぎるとモーターが故障する原因になりますので注意してください。
試験的に操作してみます。
前進、後退、右回転、左回転の操作ができることを確認します。回線速度に依存する部分もありますが、反応もリアルタイムです。
バッテリーや回路を格納するボックスを作る (4日目)
プラ板でボックスを作る
1.0mm 厚のプラ版で Raspberry Pi やバッテリーを格納するボックスを作ります。プラ版はハサミやカッターで簡単に加工できます。
骨組みを作る
洗濯挟みなどで固定してセメダインが乾くのを待ちます。LCD 用の窓を切り取っておきます。
プラ版ボックスを乗せる柱を二本用意しておく
プラ棒を利用して、ボックスを乗せる柱を二本立てておきます。こちらもセメダインで固定します。
ネットプリントしたフィルムで装飾
ボックスの骨組みができたら、セブンイレブン等でプリントアウトしたフィルムで装飾します。フリー素材の迷彩柄を利用しました。
洗濯挟みで固定してセメダインが固まるのを待ちます。
LCD 用の窓を切り取ります。
本体に乗せる
便利な商品「貼って剥がせるセメダイン」を利用してボックスを本体に乗せます。モーターが故障したときにも取り外せるので安心です。
側面から
別の角度から
回路作り (5日目)
はんだづけ
LCD をのせるための回路を作ります。ブレッドボードで動作検証した回路です。
裏面です。配線の固定方法が少し残念なことになっていますが、簡単な回路なのでこのままで。
完成 (6日目)
ボックスの中にモジュールを設置する
個別に動作検証してきたモジュールを、ボックスの中に設置します。
別の角度から
ふたを閉じて完成です。
Raspberry Pi 起動時の設定
好みに応じて rc.local に nohupを用いたコマンドを登録すれば Raspberry Pi 起動時に自動で WebSocket 通信が開始できます。または tmux を用いて、ターミナルを終了してもプログラムの実行が継続されるようにしておきます。
$ tmux new
$ ruby bulldozer.rb ←実行後にターミナルを閉じる
記事内で用いられたハードウェア
記事内で用いられたソフトウェア (電子回路図、CAD、ソースコード等)
- ファイル名種別サイズアップロード日時
- bulldozer.rbソースコード4KB2024/07/04 21:03
- bulldozer-schematic.jpg電子回路図133KB2024/07/04 21:21
関連記事
- 任意の言語で Raspberry Pi デジタル入出力、アナログ出力 (PWM)python を用いた例 書籍や公式ドキュメントなどを見ると、Raspberry Pi は python を (発音が似ているからでしょうか) 標準的な言語としてサポートしているように思われます。python には GPIO を操作するライブラリが標準で導入されています。 デジタル出力 #!/usr/bi
- Android Things による LED 点灯 (Raspberry Pi 3)電子工作や製品のプロトタイピング (例『地球規模で遠隔操作できるブルドーザー』) で利用される Raspberry Pi 3 について、 Android Things アプリケーションを開発できます。 本ページでは、簡単な例として LED を点灯させるアプリケーションを扱います。より実用的なアプリケーションを開発する際には [GitHub (Android Thin
- Raspberry Pi 無線接続時の IP 固定wlan0 の IP 固定方法 Raspberry Pi の SSID とパスワード設定は終わっている前提で以下のファイルを編集します。 $ sudo vi /etc/network/interfaces 以下の 4 行を一番最後に追加します。適宜自分の環境用に IP を変えてください。 iface wlan0 inet static address 192.168.179.1
- Raspberry Pi アナログ入力 (I2C A/D コンバータ)ADS1015 について Raspberry Pi には A/D コンバータが内蔵されていません。デジタル入出力、アナログ出力に加えてアナログ入力を行うためには外部 IC を利用する必要があります。ここでは I2C デバイスの ADS1015 を利用します。事前に I2C 設定を行っておいてください。 ![](
- Raspberry Pi ピン配置などを公式ドキュメントで調べる公式ドキュメント Raspberry Pi 公式ドキュメントにはインストール方法や初期設定などが記載されています。 Raspberry Pi Documentation ピン配置 / GPIO [GPIO: Models A+, B+, Raspberry Pi 2 B and Raspberry Pi