SWEST 17

mrubyの現状と事例紹介

九州工業大学 情報工学部
田中 和明
RubyAssociation
軽量Rubyフォーラム自己紹介
田中 和明（たなか かずあき）● 九州工業大学 情報工学部 機械情報工学科●●●

出身は情報工学で，力学は不得意
機械らしいことを研究テーマに．．．
組込みシステム（ハード＋ソフト）の研究

Rubyとmruby●Ruby– オブジェクト志向プログラム言語

ISO30170 / JIS X 3017
– Webアプリケーション開発では標準的な言語

●

mruby

– Rubyの仕様に従って，軽量化した言語– 実行時に必要なリソースを少なくする

組込みシステム開発の課題●

組込みシステムの開発費内訳– (1) (2) (3) に入るのは？

A：ハードウェア（電子系）

B：ハードウェア（機構系）

C：ソフトウェア

その他
共通費用
システム
開発費
（1）
（3）
（2）

IPA 「組込みソフトウェア産業の現状と課題」
より抜粋

組込みシステム開発の課題●

組込みシステムの開発費内訳
その他
共通費用
システム
開発費
ハードウェア（機構系）ソフトウェアハードウェア（電子系）
IPA 「組込みソフトウェア産業の現状と課題」
より抜粋

開発費内訳について●

開発費はソフト ＞ ハード（電子） ＞ ハード（機構）

ハード（機構）の開発は，相当のノウハウが蓄積されてきている● 物理的な制約から，自由度は少ない●（組込みシステムにおいて）
ソフトウェアの開発は，あまり進歩していない● 自由度が大きい（→ 簡単に複雑化してしまう）●

Rubyが使われる理由●

ソフトウェア開発がしやすい– 開発者のためのプログラム言語である（まつもとゆきひろ氏）●

プログラムの可読性が高い– 自分のプログラム，他人のプログラムを理解しやすい●

多くのライブラリが用意されている

Rubyのプログラム●

次のプログラムの振る舞いを推測してみてください
Raspi.digitalWrite 5, Raspi::HIGH
Raspi.digitalWrite 13, Raspi::LOW
Raspi.digitalWrite 26, Raspi::HIGH●（補足説明）– digitalWrite：信号（HIGH/LOW）を出力する– 信号5：青色– 信号13：黄色– 信号26：赤色

Rubyプログラムの特徴
プログラムを読みやすい↓● プログラムを書きやすい
保守しやすい不具合を減らせる↓● ソフトウェアのコストを減らせる
開発期間を短縮できる● ソフトゥエア開発を試行錯誤できる●

Rubyで組込みソフトを開発

かなり無理
●

Rubyインタープリタが多くの資源を必要とする– 数十MBのメモリ– 数百msの初期化

mruby開発へ●

Rubyの特徴（開発者に優しい）を組込みソフト開発へ導入する

経済産業省
地域イノベーション創出研究開発事業「軽量Rubyを用いた組込みプラットフォームの研究・開発」● 2010年〜2012年●

Rubyプログラムの実行
Ruby
ソースコードコードの解析
中間コードの生成実行mrubyの動作
Ruby
ソースコードコードの解析
中間コードの生成
実行

開発時に済ませておく

この状態を保存する組込み機器で動かす

コンパイル●

C言語やJavaのようなコンパイル動作と同じ？

C言語やJavaでは，コンパイルにより動作を事前に最適化して機械語に変換している（コンパイル処理）．
● コンパイル後の機械語を実行する．
●●

アイディアとしてはCやJavaと同じしかし．．．

mruby●Rubyコンパイラを作れば良い！

Rubyは動的な言語である●

CやJavaのようなコンパイルでは不十分
Rubyの良さが失われてしまう

（参考）動的とは？
a = 15p a * 2
a = 1.2
p a * 2a = "abc"
p a * 2a = [1,2]
p a * 2「a」の内容によって「a * 2」の動作が変わる．
機械語を生成できない

（参考）C言語では#include <stdio.h>a = 15p a * 2

int main(void){
int a1 = 15;
double a2 = 1.2;

a = 1.2
p a * 2

printf("%d\n", a1 * 2);printf("%f\n", a2 * 2);return 0;}a = "abc"p a * 2a = [1,2]
p a * 2

（参考）動的とは？
先の例は， C++の仮想関数でも実現できる● では以下のようなプログラムは？
●def func(n)return n+1
end
if rand(2)==1 thendef func(n)return n*2endend
p func(5)mruby●Rubyに特化したコンパイラと実行環境

さらに・・・●

さまざまな実行環境に対応する– 他機種への対応：– 実行環境に依存する部分だけを実装するだけで良い

VMで実行することで可能となること

VMさえ移植すれば，どのような環境でも動作する！
● VMのビルドに必要なのは，Cコンパイラだけ●

マイコン用のVM● PC用のVM●

「mrubyで実現する」
組込みソフトの開発
Ruby
ソースコードコンパイル
中間コードの生成PCである程度の動作をチェックしたらPC版のVMで動かす挙動を検証できるマイコンに書き込んでVMで動かす
ターゲット環境「mrubyで実現する」
組込みソフトの開発
Ruby
ソースコードCPU，OS，メモリなど全てが異なっていてもOK

コンパイル
中間コードの生成PCである程度の動作をチェックしたらPC版のVMで動かす挙動を検証できるマイコンに書き込んでVMで動かす
ターゲット環境

DEMO●

エミュレーションと実行
loop doRaspi.digitalWrite 26, Raspi::LOW
Raspi.digitalWrite 13, Raspi::HIGH
Raspi.sleep 1
Raspi.digitalWrite 13, Raspi::LOW
Raspi.digitalWrite 5, Raspi::HIGH
Raspi.sleep 1
Raspi.digitalWrite 5, Raspi::LOW
Raspi.digitalWrite 26, Raspi::HIGH
Raspi.sleep 1endどうしてこのような実行が可能なのか？

●

VMがコードを実行する仕組み

中間コードは環境に依存しない形式● ハードウェアに依存する機能（関数など）は，名前で呼び出す●

Raspi.digitalWrite 26, Raspi::LOW

VMのライブラリからdigitalWriteの機能を探してその機能を実行する

mruby関連情報●

mruby本体
https://github.com/mruby/mruby
http://forum.mruby.org/●「Ruby・mruby活用ガイドブック」
中国経済産業局のホームページからダウンロー
ドできます．

付録：
EV3でmrubyを動かしてみる
資料作成協力者
安積 卓也（大阪大学）
石川 拓也（名古屋大学）
長原 裕希（立命館大学）
長谷川 涼（大阪大学）

EV3用プラットフォーム••
その他のOSS...

LMS2012

名古屋大学により開発
デバドラなどをLinuxなどから移植(LinuxベースのVM)

MINDSTORMS EV3用
TOPPERSプラットフォーム

leJOS(Java VM)

EV3RT

MonoBrick

今回のプラットフォーム(.Net framework)
ev3dev(Linux)TOPPERS/HRP2
TOPPERS/ASPTECS仕様••

TECS WGにより開発
mrubyの動作環境

mruby，TECSを利用可能なPF• mruby on ev3rt+tecs– http://www.toppers.jp/tecs.html#mruby_ev3rt– TECSとはTOPPERSプロジェクトで開発しているコンポーネントシステム(アプリケーションをコンポーネントベース開発するためのシステム)– ev3rt+tecsはEV3RT上でのアプリケーションのコンポーネントベース開発が可能な環境• C++APIのようにEV3のモジュールをTECSコンポーネントとして扱うことができる– TECSの提供するmrubyブリッジ機能により，mrubyからev3rt+tecsが提供するモジュール機能を利用できる• mrubyからCコードを呼ぶためのI/Fを自動生成

最新版を入手• パッケージをダウンロードして解凍– http://www.toppers.jp/tecs.html#mruby_ev3rt– $ tar xvzf mruby-on-ev3rt+tecs_package-alpha1.0.2.tar.gz•

ダウンロードしたパッケージ名

環境の構築••Windows7、 Windows８、Windows８.1
Cygwinインストール– ruby– GNU Make– bison•

クロスコンパイラ– arm-none-eabi-gcc.exe (GNU Tools for ARM Embedded Processors)
https://launchpad.net/gcc-arm-embedded/4.8/4.8-2014-q3-update/+download/gccarm-none-eabi-4_8-2014q3-20140805-win32.exe•

mkimage– Windows用バイナリはパッケージに同梱• Cygwin及びクロスコンパイラのインストールはEV3RTの開発環境構築を参照してください。

– http://dev.toppers.jp/trac_user/ev3pf/wiki/DevEnvWin

パスの通し方• クロスコンパイラをインストールしたディレクト
リにPATHを通す– C:¥Program Files (x86)¥GNU Tools ARMEmbedded¥4.8 2014q1¥bin※フォルダ名は、クロスコンパイラのバージョンごとに変わります。

• arm-none-eabi-gccへパスが通っているかを確認• コンパイラバージョンが表示されない場合：Pathを確認ディレクトリ構造• bin– Windows向け開発環境のバイナリ– mkimageを含む• doc– mrubyリファレンス– ビルド手順– サンプルプログラムの説明• hr-tecs– TOPPERS/HRP2及びEV3プラットフォーム– サンプルプログラム（hrtecs/workspace/mruby_samples）• mruby– mruby ver1.1.0– build_config.rbでEV3用のクロスコンパイルを指定

コンパイル手順• mrubyのビルド（host用コンパイル→ARM用クロスコンパイル）• パッケージを展開したディレクトリで– $ cd mruby– $ make– mrubyディレクトリでmakeを実行すると、mrubyがビルドされる。
※ビルドには、ruby及びbisonのインストールが必要下記の出力がされればビルド成功

コンパイル手順• EV3RT+TECS・EV3ドライバ・アプリケーションのビルド準備– SDカードへコピー• デフォルトでは、cygwinが使用されていることを想定の上で、Eドライブにコピーされる。
• ドライブを変更するには、サンプルコードのMakefileを編集。
hr-tecs/workspace/mruby_samples/Makefile• Makefile内を "SD_DIR" で検索して、変数にSDカードドライブを指定• SDカードドライブのドライブレターに合わせてください• ここで指定したディレクトリにEV3用イメージファイルがコピーされます

ここの名前をSDカードのドライブ名に変更

実行したいプログラムを選択
hr-tecs/workspace/mruby_samples/Makefile

実行したいアプリアプリケーション
を一つ選ぶ

コンパイル手順• EV3RT+TECS・EV3ドライバ・アプリケーションのビルド– パッケージを展開してディレクトリで– $ cd hr-tecs/workspace/mruby_samples/– $ make tecs
• GNU Makeがtecsgenを実行してくれます– $ make depend• ファイルの依存関係を抽出します• ヘッダファイルなど、読み込まれるファイルを更新していなければ、実行する必要ありません。

– $ make– カレントディレクトリをサンプルアプリケーションのディレクトリに移動し、makeを行うことでコンパイルが行える。

コンパイル手順：①make tecs

tecs.timestampが出力されれば成功

コンパイル手順：②make depend

touch omit_svc.hが生成されてからしばらく待つ（処理中）：長い場合数分

コンパイル手順：③make

このような出力が出れば成功

下記のようなエラーになる場合は、SD_DIRの指定が間違っている
もしくは、SDカードを認識していない（ささってない）コンパイル手順：uImage• ビルドの確認– mkimageが生成され、uImageがコピーされていれば、ビルド成功です– SDカードのルートディレクトリに、uImageというファイルができます– SDカードをEV3本体に入れる

本体操作基本操作中央（Enter）ボタンで電源オン

OS起動中

中央（Enter）ボタンでｍrubyプログラム開始

LEDが赤の間はOSの起動中

LEDが緑になれ
ばOS起動完了

戻る（Back）ボタン
長押しで電源オフ

コンパイル手順：サンプルプログラムの変更の場合
サンプルプログラムの変更後はmakeだけで良い（make tecs,make dependは一度だけ）
サンプルプ
ログラムの変更makeev3way_sample• ETロボコン用のサンプル– 倒立制御しながら、ライントレースを行う• タッチセンサ（:port_1）• カラーセンサ（:port_2）• ジャイロセンサ（:port_3）• 超音波センサ（:port_4）• しっぽモータ（:port_a）• 右モータ（:port_b）• 左モータ（:port_c）ev3way_sample• 操作手順– 電源を入れる– 黒色のライン上にカラーセンサを移動– タッチセンサを押す：黒色の値を取得– 白色の上にカラーセンサを移動– タッチセンサを押す：白色の値を取得：しっぽを下ろす– ライン上移動– タッチセンサを押す：ライントレーススタート

ev3way_sample.rb：初期化
begin
LCD.puts "ev3way_sample.rb"
ひとつしかないもの（ポート番号指定不要）は、
LCD.puts "--- mruby version ---"
クラスメソッドとして直接呼び出す
Speaker.volume = 1
forward = turn = 0# initialize sensors$sonar = UltrasonicSensor.new(SONAR_SENSOR)$color = ColorSensor.new(COLOR_SENSOR)
ポート番号を指定して初期化$color.reflect（インスタンス化）$touch = TouchSensor.new(TOUCH_SENSOR)$gyro = GyroSensor.new(GYRO_SENSOR)# initialize motors$motor_l = Motor.new(LEFT_MOTOR)$motor_r = Motor.new(RIGHT_MOTOR)$motor_t = Motor.new(TAIL_MOTOR)$motor_t.reset_count# Signal calibration
LED.color = :orange…

ev3way_sample.rb：黒色、白色の取得# Calibration$black_value = color_calibration
LCD.puts "black::#{$black_value}"$white_value = color_calibration
LCD.puts "white::#{$white_value}"

ライントレースの基準値を計算

threshold = (($black_value + $white_value) / 2).round# wait startdef color_calibration(n=10)
LCD.puts "Ready to start"
loop {
break if $touch.pressed?
タッチセンサが押されるまで待つ
RTOS.delay(10)}col = 0n.times { col += $color.reflect}
col = (col / n).round
カラーセンサn回取得し、Speaker.tone(:a4, 200)
平均値を取得RTOS.delay(500)colendev3way_sample.rb：スタート準備# wait start
LCD.puts "Ready to start"
loop {#initialize tailtail_control(TAIL_ANGLE_STAND_UP)
RTOS.delay(10)
しっぽの位置を指定された角度に保つ# Touch sensor start（フィードバック制御）
break if $touch.pressed?
}# reset motor encoder
def tail_control(angle)$motor_l.reset_count
目標値
現在の値$motor_r.reset_count# reset Gyro sensor
pwm = ((angle - $motor_t.count) * P_GAIN).to_i$gyro.reset
pwm = (pwm > PWM_ABS_MAX) ? PWM_ABS_MAX :(pwm < -PWM_ABS_MAX) ? -PWM_ABS_MAX : pwm# Signal start status$motor_t.power = pwm
LED.color = :green$motor_t.stop(true) if pwm == 0end

ev3way_sample.rb：ライントレース
障害物まで一定の距離以下になると止まる

def sonar_alert$sonar_counter += 1
if $sonar_counter == 10
distance = $sonar.distance$sonar_alert = distance <=SONAR_ALERT_DISTANCE&& distance >= 0$sonar_counter = 0
end$sonar_alert
end# main loop
forward = turn = 0loop {start = RTOS.msec# up tailtail_control(TAIL_ANGLE_DRIVE)
if sonar_alert
forward = turn = 0else# Line trace
turn = $color.reflect >= threshold ? 20 : -20
forward = 30
end
カラーセンサと閾値と比較し…
サンプルでは、30に固定どちらかに曲がる}
ここを変更すると、自前のライントレースが可能ev3way_sample.rb：倒立制御# main loop
loop {
start = RTOS.msec…
# call balance_control API
pwm_l, pwm_r = Balancer.control(forward.to_f,turn.to_f,$gyro.rate.to_f,
GYRO_OFFSET,$motor_l.count.to_f,$motor_r.count.to_f,
Battery.mV.to_f)$motor_l.stop(true) if pwm_l == 0$motor_l.power = pwm_l$motor_r.stop(true) if pwm_r == 0$motor_r.power = pwm_r
wait = 4 - (RTOS.msec - start)
RTOS.delay(wait) if wait > 0}

バランサの返り値が2つC言語で実装されたバランサを呼び出す

4ミリ秒周期で実行
現状1ミリ秒程度で処理完了
mubyでも十分制御可能

パッケージ内のドキュメント• doc/EV3RT_mruby_API_Reference.pdf– 提供しているサンプル• doc/mruby_on_ev3rt+tecs_build.pdf– 環境設定• doc/mruby_sample.pdf– サンプルプログラムの説明

問い合わせ先・リンク• 質問等は下記のMLに– users＠toppers.jp• モニタ募集中• EV3RT– http://dev.toppers.jp/trac_user/ev3pf/wiki/WhatsEV3RT