ソフトウェア開発者がハードウェア/IoTをやる意味

12月の .NET ラボ勉強会では「これから始める Windows IoT Core」ということで発表してきました。

[slideshare id=56308093&doc=windowsiotcore-151220031915]

その中で、さらっと流していきましたが「ソフトウェア開発者がIoTをやる意味」のところをもう少し掘り下げて解説しておきます。

垣根を超えるために

私がプログラミングを始めたのは30年以上前ですが、既にソフトウェア開発というジャンルは確立されていました。まだ N88Basic の頃だったり、MS-DOS や DR-DOS だったり、したものの直接「ハードウェア」に触るためには、ハードウェアにアクセスするための馬鹿高いインターフェースボードが必要でした。逆に言えば、ハードウェアとソフトウェアの垣根があって、その馬鹿高いインターフェースを買わない限り直接ハードを触ることはなかったのです。当然、一方では組込みシステムの開発者(当時はアセンブラ)が居たわけで、基板設計とあわせてハードの I/O を地道にたたくハードなソフトウェア屋さんもいらっしゃったわけです。

ともあれ、大学の頃は主に炉心のシミュレーションをやっていたので、ほとんどハードウェアに触ることがありませんでした。学生実験で計測器で測定するぐらいですね。おそらく工学系でも機械工学の方は、もっとハードウェア寄りのことを知っていると思います。

コンピュータ上で何でもできる。インターネットや仮想空間も含めて、コンピュータ内のソフトウェアの世界でかなりのことができます。昨今の人工知能や機械学習、VR なども含めればソフトウェアの世界で突っ切ることも可能な時代になってきています。

逆に言えば、「ハードウェアの世界」と「ソフトウェアの世界」という形で、垣根が現前に存在します。子供の科学などで、少しでも電子回路を組んだことがあれば、半田付けとかコンデンサの使い方とか計測誤差とか、そういう手を使ってアナログ的なところをうまく補完していく方法もあることが分かったのでしょうが、こう 30 年間もソフトウェア内だけでやっていると、ここのところがうまくわからなかったのですよ。

ハードウェアは誤差を許容する世界

ソフトウェアも小数点以下は正確に表せないので、誤差を許容するように作りますが。ハードウェア≒現実な世界では、誤差は範囲を以て対応させます。抵抗値にしても、教科書には 330Ω と書かれていても、だいたい 1kΩ ~ 100Ω ぐらいのを持ってきても大丈夫です。更に半田や銅線の内部抵抗があるので、ぴっちりと計算で決まるわけではありません。抵抗値では2,3倍違ってもだいたい動きます。また、有効桁数は1桁か2桁ぐらいしかありません。ソフトウェアの計算では有効桁数は8桁ぐらい簡単に取れるのですが(doubleの間ならば)のですが、そのあたりもハードウェアとソフトウェアの世界では値の扱いが異なります。

このあたり、即値でも幅があるし、合計値にも幅があります。しかし、RC回路を組んで振動させると分かるのですが、Δt の要素が入ってきて時間軸のある物理現象がかかわってくると、理想状態(定常状態)とは違ったものを見えてきます。また、細かいこところでは誤差が入っていても、全体の値は目の前にある物理現象が絶対なのですがから、合計値が計算で違っていたとしても「誤差」として考えなければいけません。これは、設計をいくらうまくやってもコードの動きが絶対だ、というところと似ています。理論上、あれこれと言い訳をしても、現実で動いているものが絶対的な「現実」ですからね(ああ、量子力学的な説明はまた別なので、これは別途)。

垣根が低くなっている

巷の IoT, Maker Faire などの展覧会に行ってみると、ソフトウェアとは一変した世界があります。到底、ソフトウェア開発者としては到底追いつけないような個人的な出展があって、ちょっと尻込みしてしまうぐらいです。

しかし、一部の高度なロボット技術者の方は別として、ソフトウェア的にはあまり複雑なことをやっている訳ではないことが分かります。データシートというハードウェアの仕様を読み込まないとうまく作れないものがたくさんありますが、ある意味 .NET Framework のような大量なクラスライブラリと同じような形で、ソフトウェア設計の知識を導入すればもう少し扱いやすくなるのではないか?と思われるものもあります。そのあたりは、どちらが主という訳でもなく、相互に乗り入れが可能ではないか、と思っています。

私は Mesh のようなソフトウェアから手軽に扱えるもの、をあえて持っていません。これは、去年の夏から、ロボット戦車みたいなものを作りたかったので、もっとハードウェア寄りな考え方が必要だったので「ソフトウェアから扱う手軽さ」よりも「ハードウェアを扱う手軽さ」が自分にとって必要だったからです。このために、PIC やユニバーサル基板での半田付けなどを試していきました。

そこで、ちょうどよく、ハードウェアの扱いとソフトウェアの扱いとして自分の中で手足になっていきそうなものが、

  • Arduino Nano/Mini
  • Arduino Nano 3.1 (ATmega328P搭載)--販売終了

  • Raspberry Pi 2

商品画像1

  • mbed

です。組み立てのブロック周りとしては LEGO Mindstorms EV3 も良いのですが、いかんせん個人で買うには値段が高いので、あくまで教育用ですね。私の考えている「数を揃える」のが難しいのでちょっと後回しです。

 

これらの 3つは、ある程度値段も安く情報も手に入りやすいものです。通信の有線LAN/WiFi/Bluetooth も扱いやすくなっています。

かつては、PIC とユニバーサル基板で電子工作をしなければいけなかったことを考えると(参考用に、昔の電子工作用の本は数冊買いました)、だんぜん楽です。半田付けをしなくても、ブレッドボードで十分だし、固定したいのであればグルーガンで接着する方法もあります。ブレッドボードも安いものであれば200円前後ですから、試作ならばこれで十分です。かつ、ソフトウェア開発者からハードを触るだけだったら試作で十分です。

Lチカとモーターを回した後に進む場所

最初は、マイコンだけ買って Lチカする訳ですが、それも1時間ほどチカチカさせれば飽きてしまいます(クリスマスツリーとか、LED 制御も色々あるので実は奥が深いのですが)。じゃあ、その後は何か動かすものが必要かな、と思うと機械工学の知識が必要になってきます。アルミ板を曲げたり、切ったり、ドリルで穴をあけたりと専用の工具が必要になってきます。ソフトウェア開発の場合は、パソコンとスマートフォンぐらいで十分だったのですが、色々取り揃えないといけないわけです。

これが垣根になっています。

まあ、昨今の3Dプリンタを買って一気に工作環境を整えてしまうという方法もありますが、初期投資が大変ですよね。それに 3D ソフトウェアを覚えないといけません。

その前にお勧めなのが、既存のおもちゃを作ったり、改造したりすることです。イスペット社のグリッパーアームは5,000円程度と低価格なロボットアームです。関節にはサーボモーターじゃなくて普通のブラシモーターが使われていますが、試しに動かしてみるにはこれで十分でしょう。PC で動かせるバージョンもありますが、あえてコントローラだけ安価なものを買って Arduino などで動かしてみるとよいです。

http://ecx.images-amazon.com/images/I/51NmTEqRgEL.jpg

http://pds.exblog.jp/pds/1/201301/21/06/b0158406_134562.jpg

他にも、ダイソーの100円電車とか、電池で動くおもちゃは大抵改造が可能です。タミヤのギアボックスを買えばプラモデルの戦車を Bluetooth でラジコン化することもできます(Bluetooth には安い HC0-05 のシリアル通信を使えばよいのです)。

そういう安くて簡単なところから入るとよいでしょう。IoT というと、クラウドとセンサーの組み合わせがほとんどで、センサーの類も結構高価なものだったり、サーバーが月額で有料だったりしますが、いえいえ、そういうものを使う必要はありません。センサーの類は中華 Arduino と安価なセンサーの組み合わせで十分だし、サーバーは安価なホスティングサービスを借りて PHP で組む、というパターンでも良いわけです。

その先にあるもの

私のこれからになりますが、ハードウェアの世界を垣間見たら、ソフトウェアの世界に戻ってきましょう。ハードウェアの誤差やΔtの世界を、うまくモデル化してやるのがソフトウェアの役目です。ロボットを扱うための ROS でもよいし、組込みの RTOS を使ってもいいし、スマートフォンやタブレットからうまく制御できる仕組みを使ってもよいでしょう。カメラ機能と画像認識、各種センサーを組み合わせて人工知能的に動く iRobot のような制御も自作できると思います。これを数台作って相互通信させるところが目標です。

ハードウエア屋さんの良いところを持ってきて、うまくソフトウェアでモデル化して、それでハードウェアをうまく制御できるのが目標ですよね。来年こそ暇を見つけて蓮根に行きます。

カテゴリー: 開発 | ソフトウェア開発者がハードウェア/IoTをやる意味 はコメントを受け付けていません

大規模プロジェクトで MVVM を導入するときの注意

先日の JXUG の MVVM の補足 を書いたので、どうせだからもうちょっと突っ込んだ話題を残しておきます。Xamarin.Forms などを使う場合は、そんなに多くの人は関わらないでしょう。勉強会でもちらっと話しましたが、ひとりで MVVM すべてを作ったり、数名で MVVM を分け合ったりするのがほとんです。

せっかく V-VM-M と分かれているのだから、ある程度規模が大きくなれば分担しないといけません。最初に2つのパターンが考えられて、

  • 縦割りにして、画面ごとに MVVM を一貫して作る。
  • 横割りにして、V-VM-M を別々の人に割り当てる。

ことが考えられます。理想的には View をデザイナが受け持ち、VM をプログラマが、M をデータベーススペシャリストが担当するのが良いのですが、人数的な問題や規模的な問題、開発者のスキルのばらつき、遠地なのか近地なのか、同じ会社なのか別会社なのか、という様々な「政治的な」問題が絡み合って、理想的にはできません。となれば、プロジェクトマネジメントも含めて、何らかの妥協点を見つけて、ソフトウェア開発を進めていくことになります。まあ、それこそが「マネジメント」なのですが。

20160114_02

かれこれ5年前の話から

NDA 的には5年経っているので話しても大丈夫なのですが、まあ細かい話は無しにして、Silverlight + MVVM モデルで DataGrid をバインドの落とし穴 をやっていたころです。Silverlight が流行っていて、ActiveX コントロールではなくてブラウザ上に高機能なインターフェースを乗せようとしていた時期ですね。折しも、ちょうど MVVM が発表されたあたりで初期のブームだったのです。

経理系のシステムで、結構規模が大きくかかわっていたのはマネージャ等を除いても30名ぐらいいます。銀行系の大規模とは違うのですが、最近のスマートフォンの開発や、WEB開発に比べれば十分に大きく、そして混乱しやすい人数でもあります。そこに新技術としての Sliverlight と MVVM を同時にぶち込んだところに悲劇があったわけですが…まあ、仕事なのであれこれのトラブルがあってもそれなりに収めるわけなんですがね。

コンサルティング的な立場で入ったはずなのですが、複数の画面を担当することになりました。これもよくありがちで、技術的な点よりも人手でなんとかしようとする人海戦術パターンです。初期の頃から入っていれば設計的になんとかできたのでしょうが、結果的にみれば火消的な時期に入ってしまったため既存の設計に引きずられています。

MVVM パターンのライブラリが別会社だった

ある程度の画面数を超えると、人海戦術的にモノ作りをするパターンにおちいります。まあ、その手のプログラミング自体、労働集約的なところから抜け出せないので、仕方がないといえば仕方ないところです。いまだと、画面数をぐっと減らしてしまうか、共通化することで手数を減らしますよね。あと画面生成を自動化するとか。

そういうものはなくて(今でもそういうものがない会社はたくさんあります)、ひたすら手作業であ画面を作っていきます。Silverlight で画面を作るのですが、画面の部品は別の会社が作っていました。どうやら、ほかのプロジェクトで使ったらしい Sliverlight の画面部品を今回のプロジェクトに流用しようとしたらしく、そこに MVVM のバインドを入れ込んだという訳です。

いやいや、この別会社の部品がひどくて、そもそも Silverlight の Binding は元から揃っているにも関わらず、さらにそれをラップするような同じコントロールをひとつひとつ提供するという技をやってきました。当時、私も MVVM をやり始めたばかりなので、なんでこんな変な形になっているのかわからなかったのですが、あとから考えれば根本的に設計を間違えていたのでしょう。なんだかよくわからない Binding を付加したなんだかよくわからないラップされただけのコントロールで画面を組み合わせると、なんだかよくわからない形で画面部品にアクセスしないといけなくなります。だから、余計混乱するし、別会社のコントロールに不具合が発生するたびにあれこれと時間をロスしていました。

まあ、この手の共通部品は社内で作るのがベストですよね。あるいは、綿密なサンプルと設計をしたうえで社内に持ち込むのがベターです。

画面ごとに縦割りの MVVM は効率が悪い

画面部品を作っている別会社の MVVM の知識も足りないし、まして使う側の社内のメンバの MVVM の知識も足りません。そういう足りない上に、従来の画面ごとに担当を分けていったので更に混乱しています。よくわからない MVVM の知識は、それぞれ V-VM-M に分けて考えてインターフェースをきちっと作ると作業分担をして効率よく進めることができるのですが、従来通り縦割りに MVVM を作りました。このため V-VM-M のすべての知識がないと画面がうまく作れないわけです。

MVVM の社内講習会などはありませんでした。理由としては、MVVM が「解らなくても使いやすい」とされていた画面の部品を別会社から購入したことにより、それをクリアする話だったのですが、実際には VM の Binding が必須なコードになっているので、全然ラップされていません。というか、MVVM の構造を理解せずに画面を作るのであれば、MVVM パターン自体を導入する必要はありませんよね。本末転倒です。

  • MVVM を完全にラップする → 従来のイベントベースでコーディングできる
  • MVVM を使いやすくラップする → View の Binding 記述などを減らす

のどちらかの方法に進むべきだったのですが、結果的にどちらの悪いところを取ってしまって、MVVM をラップしているけども Binding がむき出しなコントロールを使う羽目に陥りました。そんなややこしいことをするならば、そのまま標準コントロールを使ったほうが楽です。

なので、MVVM をある程度の多人数で作る場合には、

  • 全員が MVVM の構造を理解して、画面ごとに縦割りで作る
  • MVVM の概要だけ理解して、インターフェースを守って V-VM-M で担当を分ける

というパターンになります。一見、V-VM-M で分けたほうが効率が良いように見えますが、現実は異なります。というのも、構造的に V-VM-M と分けえるとそれぞれの層に特化することはできるのですが、画面ごとに異なるドメインの知識が必要になってしまいます。このため、プロジェクト全体の業務知識がないと、VM が作れないというところに陥ります。

5名ぐらいで分担している場合は、プロジェクト全体の業務知識はさほどではないのですが、30名レベルになると結構なボリュームになります。分業するならば、業務知識はそれぞれで分担できるほうが効率がよいので、MVVM パターンを画面単位で作るのが良いのですが、これ先に言った通りに MVVM パターン全体の知識が必要となってしまいます。そういうジレンマが MVVM パターンにはあるということを理解して MVVM パターンを導入してください。

テスト可能な VM, M を作る

xUnit を使ってテスト可能なコードを書くことにより、コードの品質は上がるしリファクタリングがやりやすくなります。これも数名で書いている分にはちょこちょこと変えられる程度なのでしょうが、30名レベルになるとかなり混雑してきます。

先のプロジェクトでは Model の部分は WCF を通じて SQL Server にアクセスしていました。このため DBA チームがあって、SQL Server の構造にあった Model を返すのですが、これが曲者です。当然、View は Model に合うような構造になっていないので、VM あたりで構造を変えないといけません。いわゆるインピーダンスミスマッチというやつですね。

MVVM パターンのひとつの回避策としては、画面に即した Model をもうひとつ作り、データベースの Model をうまく組み合わせることです。多少冗長にはなりますが、VM が比較的に簡単になるので、これは有効な手段です。ただし、実装中にコロコロとデータベースのテーブル構造が変わるたびに画面用の Model も変更しないといけないためインテグレーション的に不利なところがあります。

もうひとつのパターンは、VM 内で変換する方法です。もとも View と Model のつなぎを担当する VM なのですから、業務ロジック(画面の操作ロジック)も含めて ViewModel をコーディングしていくことも可能です。ただし、これもデータベースのテーブル構造が変わると VM に手を入れなければならず、さらに VM 自体が画面と密着しているために、テーブル構造の変化が VM –> View にまで伝播してしまうことが多く再テストが大変です。

このようなことから、Model の自動テストだけでなく、ViewModel の自動テストも行えると、上記の2つのパターンの複雑さが多少改善されます。Model だけのテストだと VM の再テストが画面操作からしかできなくなって膨大な打鍵試験が発生します。この打鍵試験を減らすことが xUnit の目的なので、このままは本末転倒です。そこで、自動テストがしやすいように VM を作っているスタイルが必須になります。

まあ、担当した画面だけは自動テストっぽい VM の作りをしていたのですが、もともとそういう文化がない会社だったので、そのあたりはダメダメでした。Excel な設計書をきっちりと書いて、なぜか別の人がコーディングして、なぜか別な人がテストするという従来の方法でやっていたので炎上は必須です、といいますか、炎上していました。設計した会社とコーディングをしていた会社がかなり険悪な状態になっていたんですよね。設計する会社が親会社で、コーディングが子会社だったので猶さらその無茶ぶりな圧力は異常なところがありました。別会社ではありますがパワハラに近いものです。

で、オチはどうなったか?

この話、実はオチがあります。

設計するのが親会社、下請けが子会社だったのですが、ある日突然、子会社が親会社を買収することになってですね、プロジェクト間の立場が一転したのです。へこへことコーディングしていた会社が一夜明けて、設計してた会社の上に立つことになって、進捗会議なども一遍しました。なんだかなー、笑えます。会議のパワハラも、いままでのストレスも相まって、えらいことになってました。

で、私はどうなったかというと、元親会社の契約更新ができなくなって2か月程度でお払い箱ですよ。それ以来、その手の技術支援っぽい派遣には手を付けないことにしています スマイル 安い派遣で働くよりも、うちで地道に勉強していたほうが将来的に有利ってことですね。

カテゴリー: 開発 | 大規模プロジェクトで MVVM を導入するときの注意 はコメントを受け付けていません

JXUG で話した MVVM の活用の解説を

JXUGC #9 Xamarin.Forms Mvvm 実装方法 Teachathon – connpass
http://jxug.connpass.com/event/22840/

にて、田淵さんコードに「マサカリ」を投げまくって来ました。実は、ピアレビューという手法があって、できるだけきめ細かくコードをレビューしていくという手法があります。本来ならばインスペクションの形式を使うのですが、「人を攻撃する」のではなくて、コードのみをバシバシと叩いて向上させる方法ですね。コードを個人の成果物ではなくて、共同の成果物として仕立てあげることが最終目標です。

ひとまず、MVVM とは何ぞ?なり、Xamarin.Forms とは?という話はすっ飛ばして直接コードから入ったのは良かったと思います。ペアプロとか、M-VM-M の分業体制なんかはあんな感じで進めるとうまくいくでしょう。

私の発表したサンプルコードは以下にあるので、ざっと解説をつけておきます。

http://github.com/moonmile/JXUG

プロジェクトの構造

コードにはちょっとだけでも単体テスト用のコードを付けるようにしています。私が TDD を使う目的としては、テストの効率化の他に、「オブジェクト/ライブラリの使い方」を示すためにも作っています。そのクラスをどのように使うのか、かつ、クラスを使う時にどのようなインターフェースにしたら使いづらくはなならないか、の検討用に単体テストコードを使っています。
また、実際にクラスを動かすときのサンプルとして、いきなり Xamarin のコードでは大変(実機でしか動作しないパターンなど)なので、最初に WPF や Windows フォームを使った小さなサンプルを用意します。これでいくつか実験した後で、実際のモバイルコードに直す、あるいは組み合わせていいきます。こうすると、プロジェクトの最後のほうになって実機コードが複雑になっても、実験アプリによって少しテストをしながら、という手法が取れます。

  • /Test/MStopWatch.Test — 単体テストコード
  • /Test/MStopWatch.WPF — WPF による実験コード
  • /ViewModel/MStopWatch.VM — WPF/Xamarin.Forms の共通の VM
  • /ViewModel/MStopWatchFsharp.VM — 試しに F# で書き直した VM
  • MStopWatch — Xamarin.Froms の共通 PCL
  • MStopWatch.Droid — Android 用
  • MStopWatch.iOS — iOS 用
  • MStopWatch.WinPhone — Windows Phone 用

最後の、Droid/iOS/WinPhone は Xamarin.Forms を使うと手を入れずに済みます。シミュレータの場合は、Windows Phone が Hyper-V を使って一番早く動きます。

これで、MVVM パターンを形作るわけですが、いくつかの仕掛けが入っています。ストップウオッチのタイマの場所を何処に置くのかによって VM の書き方が変わります。勉強会のときにも強調しましたが、特に正解があるわけではありません。とあるコードやプロジェクトによって、「そこが最適であろう」という推測はできますが、実際に置かなければならないということではありません。ふさわしい場所がある、というだけです。

ストップウォッチタイマを ViewModel に置く

StopWatchVM.cs

public class StopWatchVM : BindableBase
{
...
    public void Start()
    {
        _now = DateTime.Now;
        _startTime = _now;
        _items.Clear();
        _loop = true;
        Mode = 1;
        _task = new Task(async () => {
            while (_loop)
            {
                await Task.Delay(100);
                _now = DateTime.Now;
                if (OnTimer != null)
                    OnTimer();
                else
                {
                    this.NowSpan = _now - _startTime;   // 画面を更新
                }
            }
        });
        _task.Start();
    }

タイマは、100 msec で動かしています。非常に遅いように見えますが、画面に表示させるときは 100 msec で十分で、Lap ボタンを押したときには改めて DateTime.Now から取っているので正確な時刻が取得できます。このあたりが、表示用の VM とデータとしての Model の違いになりますね。

ここではスレッド越えを許すためにコールバック関数 OnTimer を定義させていますが、Android でもコールバック関数は必要ではありませんでした。NowSpan プロパティと更新すると、INotifyPropertyChanged で画面に通知されます。

ストップウォッチタイマを Model に置く

StopWatchVM2.cs

public class StopWatchModel
{
...    public DateTime StartTime { get; set; }
    public void Start()
    {
        StartTime = Now = DateTime.Now;
        Items.Clear();
        _loop = true;
        _task = new Task(async () => {
            while (_loop)
            {
                await Task.Delay(100);
                Now = DateTime.Now;
                if (OnTimer != null)
                    OnTimer();
            }
        });
        _task.Start();
    }

Model のほうにタイマを用意した例です。この意図としては、計測機器の割り込みイベントや、外部から定期的に割り込みが入るようなパターンを想定しています。この場合、イベントが Model -> VM -> View へと数珠つなぎになるので、MVVM のまま使うよりも Rx のような方法を取ったほうが楽です。

ストップウォッチタイマを View に置く

ちょっとサンプルには書き忘れましたが、View 自身にタイマーを持たせることもできます。ストップウォッチの場合には、

  • 定期的に人の目に触れる View の時刻を切り替える
  • 内部で持つ時刻データを正確に持つ

の2つに分離できることがわかります。このため、内部データは Lap ボタンを押したタイミングで DateTime.Now を取得すればよいわけで、何も定期的に内部データを更新する必要はありません。なので、画面の表示させる View だけタイマー更新を使うという方法が考えられます。これはちょうどゲームの画面更新(スプライト機能など)を行う場合に、描画はキャラの更新タイミングに合わせるのではなくて、垂直同期にあわせるという方法ですね。たいていのゲームは 50fps 程度あれば十分なので、20 msec 程度で更新させれば十分です。
なので、Lap タイムは msec 単位で持っていても、画面更新は 20 msec 単位程度で十分ということです。

View 単体の更新では、WPF の場合は Storyboard の更新タイミングを使う方法もあります。これらは機会を見てサンプルに付け加えていきましょう。

VM を F# で書く

VM や Model に単体テストが入れば開発効率は非常に上がります。画面であれこれテストしたり、インタプリタで一時的なテストを繰り返すよりも、自動テストができる作り方にするのです。

F# で書いた VM の全文が次になります。これらは、単体テスト MStopWatch.Test でテストが可能です。

type StopWatchVM() = 
    let ev = new Event<_,_>()
    let mutable _mode = 0
    let mutable _startTime = DateTime()
    let mutable _now = DateTime()
    let mutable _nowSpan = TimeSpan()
    let mutable _items = new ObservableCollection<LapTime>()
    let mutable _loop = false
    let mutable _task:Task = null

    member this.StartButtonText 
        with get() = 
            match _mode with
            | 0 -> "Start"
            | 1 -> "Stop"
            | 2 -> "Restart"
            | _ -> ""
    member this.Mode 
        with get() = _mode
        and set(value) = 
            if ( _mode <> value ) then
                _mode <- value
                ev.Trigger(this, PropertyChangedEventArgs("StartButtonText"))
                ev.Trigger(this, PropertyChangedEventArgs("Mode"))
    member this.Items 
        with get() = _items
        and set(value) = 
            _items <- value
            ev.Trigger(this, PropertyChangedEventArgs("Items"))
    member this.NowSpan
        with get() = _nowSpan
        and set(value) = 
            _nowSpan <- value
            ev.Trigger(this, PropertyChangedEventArgs("NowSpan"))


    member this.ClickStart() =
        match _mode with
        | 0 -> this.Start()
        | 1 -> this.Stop()
        | 2 -> this.Reset()
        | _ -> ()
    member this.ClickLap() = this.Lap()

    member this.Start() = 
        _now <- DateTime.Now
        _startTime <- _now
        _items.Clear()
        _loop <- true
        this.Mode <- 1
        _task <- new Task( fun () ->
                while ( _loop ) do
                    ( Async.Sleep(100) |> Async.StartAsTask ).Wait()
                    _now <- DateTime.Now
                    this.NowSpan <- _now - _startTime
            )
        _task.Start()

    member this.Stop() = 
        _now <- DateTime.Now
        this.Items.Add( LapTime( this.Items.Count+1, _now, _now-_startTime))
        _loop <- false
        this.Mode <- 2

    member this.Reset() = 
        _now <- DateTime.Now
        _startTime <- _now
        this.NowSpan <- TimeSpan(0,0,0)
        this.Items.Clear()
        this.Mode <- 0

    member this.Lap() =
        _now <- DateTime.Now
        this.Items.Add( LapTime( this.Items.Count+1, _now, _now-_startTime))

    interface INotifyPropertyChanged with
        [<CLIEvent>]
        member this.PropertyChanged = ev.Publish

VM を単体テストする

Model を自動テスト化すると頑丈なコードが書けます。さらに画面に近い VM をテストするコードを書くことも可能です。

/// <summary>
/// ラップを実行する
/// </summary>
 [TestMethod]
public void TestOneLap()
{
    var vm = new StopWatchVM();
    vm.Start();
    Assert.AreEqual("Stop", vm.StartButtonText);
    System.Threading.Thread.Sleep(1000);

    vm.Lap();
    Assert.AreEqual("Stop", vm.StartButtonText);
    // ひとつだけ追加されている
    Assert.AreEqual(1, vm.Items.Count);

    System.Threading.Thread.Sleep(1000);
    vm.Stop();
    Assert.AreEqual("Restart", vm.StartButtonText);
}

VM の構造を、UI/View から触るメソッドにうまく対応させてやれば、このようにユーザーのアクションをエミュレートできます。最近では Test Cloud のように実機/エミュレータを使って UI ベースのテストをすることも可能です。全ての UI イベントをエミュレートする必要はありませんが、おまかな動作がテストできると、実機を使った打鍵チェックを減らすことができます。

カスタムコントロールの利用

MVVM パターンを使うと、何にでも Binding を使って表そうとしてしまいますが、その分 View が冗長になってしまいます。勉強会でも話しましたが、本来は XAML をデザイナが記述し、コードビハイドをプログラマが記述するという分業ができる、というのが当時の売りでした。ですが、最初の頃に XAML をデザインするにはすべてをコードでみるしかないという状態に陥っていたため、XAML 自体もプログラマが書くようなスタイルになってしまいました。

Xamarin.Forms で、Button クラスを継承して Mode プロパティで表示が変えられるようなカスタムコーントロールを作ります。こうすることで、コントロール自体をより高機能な部品にすることができます。ここではボタンの表示を Mode プロパティで切り替えているだけですが、画像ファイルを張り付けたり、アニメーションをしたりすることができます。これらの動きを全て XAML で書くような Setter な方法もありますが、カスタムコントロールを作ってしまったほうが XAML の View が複雑にならなくて済みます。

public class CustomButton : Button
{
    public static BindableProperty ModeProperty =
        BindableProperty.Create<CustomButton, int>(
            p => p.Mode,
            0,
            defaultBindingMode: BindingMode.TwoWay,
            propertyChanged: (bindable, oldValue, newValue) =>
            {
                var uc = bindable as CustomButton;
                switch (newValue)
                {
                    case 0: uc.Text = "開始"; break;
                    case 1: uc.Text = "停止"; break;
                    case 2: uc.Text = "リセット"; break;
                }
                ((CustomButton)bindable).Mode = newValue;
            });
    public int Mode
    {
        get { return (int)GetValue(ModeProperty); }
        set { SetValue(ModeProperty, value); }
    }
}

WPF の場合は、DependencyProperty を使うため、若干 Xamarin.Forms と書き方が違うので注意が必要です。

class CustomButton : Button 
{
    /// <summary>
    /// モードを指定
    /// </summary>
    public static readonly DependencyProperty ModeProperty =
        DependencyProperty.Register(
            "Mode",     // プロパティ名
            typeof(int),    // プロパティの型
            typeof(CustomButton),  // コントロールの型
            new FrameworkPropertyMetadata(   // メタデータ                   
                0,
                new PropertyChangedCallback((o, e) =>
                {
                    var uc = o as CustomButton;
                    if (uc != null)
                    {
                        int v = (int)e.NewValue;
                        switch ( v )
                        {
                            case 0: uc.Content = "開始"; break;
                            case 1: uc.Content = "停止"; break;
                            case 2: uc.Content = "リセット"; break;
                        }
                    }
                })));
    // 依存プロパティのラッパー
    public int Mode
    {
        get { return (int)GetValue(ModeProperty); }
        set { SetValue(ModeProperty, value); }
    }
}

ひとつの VM に複数の View を割り当てる

本来ならば、View と VM はきれいに分離するはずなので、動的に View をロードすることも可能です。インターネット経由で View(XAML)をロードすることも可能なのですが、これは結構難しいです。しかし、一定の View のパターンを持っていて、場合によって XAML 全体を切り替えるということができます。
この方法は、権限の違うユーザ(管理ユーザ、一般ユーザー)では画面をダイナミックに切り替える、ということができます。

Xamarin.Forms の MStopWatch プロジェクトには MyPage.xaml と MyPageV.xaml という2つの View があります。VM が同じであっても、インスタンスを生成するときに Page クラスを切り替えることができます。
ちなみに、MyPageV.xaml は、すべて View のコードビハイドにロジックを入れてしまった例です。

こんな感じで、ちょこちょこと業務ノウハウっぽいものも入れてある Xamarin.Forms の MVVM サンプルコードですので、ぜひ活用してください。

カテゴリー: Xamarin | JXUG で話した MVVM の活用の解説を はコメントを受け付けていません

Windows IoT Core ver.10.0.10586 のデバッグを有効にする

土曜日の .NET ラボ勉強会のために、やっとこさ Windows IoT Core の新しいバージョン 10586 を入れて Visual Studio 2015 からデバッグ実行しようと思ったのだけど「できない」。なぜか?と思いつつ探しつつ、結構ややこしい状態になっていたのでメモ書きしておきます。

必要な環境

Windows IoT – Console App Sample
http://ms-iot.github.io/content/en-US/win10/samples/ConsoleApp.htm

を見ると、Windows 10 の 10586 と Visual Studio 20156 Update 1 と Windows IoT Core の 10586 を揃えないといけないようです。結果的には揃えてバージョンが違ったときにはどうなるのかわかりませんが、揃えておきます。

が、揃えたもののデバッグ実行ができません。

新しい環境ではリモートデバッグ(msvsmon.exe)が実行されていない

デバッグメニューの中に「Start」ボタンが増えています。どうやら、普段はリモートデバッグができない環境で、この Start ボタンを押すとリモートデバッグできるようになるらしいのですが…

image

これを押しても、エラーがでます。

Failed to start the Visual Studio Remote Debugger. Make sure the remote debugger is copied to the device using Visual Studio For more information please visit www.windowsondevices.com

image

リンク先を見ても解決しないので、なんだかなー、という感じなのですが、あれこれと調べていくと、そもそもリモートデバッグ環境の /RDBG フォルダがないことが解ります。どうやら、それ以前の 10556 版にはあるのですが、なぜか最新版から落ちている。

解決策は、10556 から RDBG をコピーする

Getting Visual Studio Debugger running on latest build (10586) of Windows 10 IoT – TechNet Articles – United States (English) – TechNet Wiki
http://social.technet.microsoft.com/wiki/contents/articles/32718.getting-visual-studio-debugger-running-on-latest-build-10586-of-windows-10-iot.aspx

あれこれ探した挙句、TechNet に手順が書いてありました。元の 10556 版を入れて、RDBG をローカルにコピーして、最新の 10586 にコピーしなおして、再起動すれば ok。っていう手順なのですが、元のファイルなんかないよー、ってことで、http://1drv.ms/1QqAKz4 に 10556 版の RDBG を置いておきますので、USB メモリにコピーして Windows IoT Core にコピーしてください。直接、SD カードに書き込むと Win IoT から見えないので、RPi に USB メモリを差し込んでコマンドラインでコピーします。

このあと、7. Use the Web UI -> process -> Run command and run the below command:
c:\RDBG\RegisterOneCoreRdbg.cmd と c:\RDBG\StartOneCoreRdbg.cmd を動かします。以前、やったときは「Start」そのままで大丈夫な気がしたのですが、このレジストが必要な模様。

再起動すれば、プロセスに msvsmon.exe が現れて従来通りリモート実行できます。

image

 

MemoryStatus Console Application Sample のサンプルは C++ なのでリモート実行の手順がややこしくなっていますが、ユニバーサルアプリを C# で作れば、「プロジェクト」→「デバッグ」で、対象のリモートコンピュータを指定すれば ok です(デバイスのほうは Windows Phone になるので使いません)。

image

デバッグ実行

こんな風にミニ液晶を使って動かすことができます。

image

土曜日までに、サーボモーターと Lチカを準備する予定。

カテゴリー: Win IoT | 2件のコメント

LEGO Mindstorms EV3+MonoBrick+F# で倒立振子ロボット

F# Advent Calendar 2015 – connpass の 12日目です。

中身的には 倒立振子ロボットを MonoBrick で作成する(F#編) | Moonmile Solutions Blog の延長線上で、何故 F#(関数型言語)でロボット制御をやろうとするのか?ってのをつらつらと書いていきます。

C# 版は、EV3 + MonoBrick + C# なスライドを参照して貰うということで、計算のところを少し突っ込んで行きます。

物理方程式を解く

実は、制御工学を習ったことが無かったので知らなかったのですが、倒立振子ロボットのように「状態の変化に対して少しずつ対応していく」という方式は単純な線形の式で表せます。線形と非線形の違いは、直線近似とそれ以外の複雑な変化を解くという違いにもなりますが、ある意味で物理現象のような「とある時刻 t の状態から、少し異なった t+Δt の状態を予測する」という場合には、線形に近似をしても問題はありません。Δt のサンプリング周期にも依りますが、とある複雑な予測解析を行う場合は、未来の t’ の値を直接計算するよりも Δt の積み重ねのほうが簡単に計算できたりします。

image

これは、よくやる重力の物理方程式で、惑星の動きとかをシミュレーションするときによく使われる方法ですね。太陽と地球の場合は、楕円として計算できるけど、三体問題として太陽・地球・月の場合は複雑に絡み合っているので直接計算はできません。別途 Δt で計算するというアレです。

当然、Δt は近似であるので、その積み重ねによって計算された未来予測値は正しい値とは限らないし、むしろずれています。誤差が累積されますからね。しかし、制御工学のように、その時々の時刻 t’ の値を観測することによって、Δt による線形近似のずれは、その t’ 毎によって補正されるため直近の値のみ正確であればそれでよいのです。このあたりを踏まえると、倒立振子ロボットのように一見、実現不可能ようなもの(私自身が倒立振子ロボットを見たのは、つくば博覧会のときですから30年以上前です。そのときに出品していた、一本足ロボットが現在の iRobo の前身なのですから驚きですね)であっても、意外と簡単な近似だけで行けるものです。

上記の運動方程式をあれこれラグランジュ方程式で解いて、変換すると、

角度θ、φに対するゲインの式

image

予測値に円滑に近づけるための PID 制御

image

の 2つだけで十分なのです。

それぞれの定数(K1, k2 等)を決めるためには複雑な方程式を解かないければいけませんが、最終的に倒立振子ロボットを制御するためのロジックは、上記のような単純な式であらわせます。これが組込みで動くので、Arduino のようなメモリが少ないマイコンでも動くという仕組みです。

科学計算の式をプログラム言語で解く難しさ

ここで注目したのは、最終的な式は簡単に見えるけども、途中の計算は複雑だということです。それぞれの K1 のような定数は、既知の数値のように見えますが、ロボットの重心位置や質量、タイヤの半径や質量、モーターの角速度や逆電流などに関わってくる定数になります。このあたり、科学計算の式をプログラム言語に直すときのむずかしさがあり、その間はギャップがあります。

  • 数式として解いたときの式
  • プログラムコードで実現するコード

の間には結構な隔たりがあります。

image

このような式を、プログラム言語(C言語や、C++、C#,Fortran など)に直していくわけですが、これがなかなか大変です。書き直すときのコツもあるのですが、問題は「式」の表現と「コード」の表現に大きな隔たりがあることです。

仕事上、数式を Fortran、C++ で書き直すことをやっていますが、よく間違えます。ただし、数式として抱えている様々な条件(前提条件なども含む)を写し間違えるあわけですね。その間違い、実際もとの数式に当てはめないとわからなかったり、複数の式が絡み合っているとなかなか見つかりません。

そんな仕事の中でよく使っているのは、F# による検算です。

  1. 数式を書く(与えられる)
  2. F# で書く
  3. C++、Fortran で書き直す。
  4. 3 と 2 の答え合わせを xUnit で行う。

という方法をよくやります。これをやると数式の写し間違えが激減しました。理由は F# が関数型言語で、式の取り扱いが得意だからというのもあるのでしょうが、検算することにより不具合率が減ります。また、この F# で書くコードは、巷の関数型言語特有の複雑さ(モナドとかオプショナルとか)をまったく使わず、ごく普通に式をプログラム言語に直す、というスタイルでしか使っていません。ですが、不具合が激減します。

束縛(バインド)と関数内関数の活用

ここ 2 年間の経験上ではありますが、この不具合率の低さは、

  • 束縛(let)の利用により、できるだけ値を不変にしようとする努力をする
    → 影響範囲が少なくなる
  • その場でしか使わない関数は、関数内関数として定義する。
    → 関数名が短くて済む。影響範囲が狭い。
  • if文、switch/match 文が値を持つ
    → 型システムの利用

なところです。

image

値の束縛は、C言語であれば define 定義すればよいし、C# であれば const にすれば ok です。ですが、F# の場合(関数型言語一般)は、デフォルトが束縛(let)であって、値を変えたいときはわざわざ、mutable にしないといけないという面倒臭さがあります。これが「面倒くさい」ことによって、より値が変化しない束縛(let)のほうにコードが寄ってきて、影響範囲の少ないコードが書きやすくなるという利点が出てきます。

逆に言えば、オブジェクトのプロパティをがりがりと変化させるようなオブジェクト指向とは別の向きを関数型言語は示してくれます。それは相反するものではなくて、その場その場でより使いやすい方向に向ければよいので、オブジェクト指向も関数型言語も相補的な形で使えるでしょう。

そういった場合、倒立振子ロボットのような物理的な制御をする場合には、物理量が不変であるからこそ数式的に関数型言語を使う法が適しているといえます。逆にロボット制御であっても、リモートコントローラーでアクチュエーター(サーボモーターやアームなど)を適切に動かすような主導的な制御はオブジェクト指向/UML を使ったほうがうまく作れるとも言えます。そういうパターンを踏まえて、物理値を逐次観測していてアジャイル的に制御を加えていく制御工学な自立コントロールはなかなか面白い分野になります。

サンプルコード

倒立振子ロボットを F# で書いたコードです。

moonmile/MonoBalancer
https://github.com/moonmile/MonoBalancer

C# と見比べると面白いかも…というか、今は C# から F# に書き直しただけなので、そのうち F# らしいスタイルに直していきましょう。

カテゴリー: F# | LEGO Mindstorms EV3+MonoBrick+F# で倒立振子ロボット はコメントを受け付けていません

Raspberry Pi で Xamarin.Forms を動かそう(Advent編)

この記事は、Xamarin Advent Calendar 2015 の 4日目の記事です。以前、ざっと書き流していたのですが、もうちょっと詳しく書いていきます。

Xamarin が動作するプラットフォームで、相当異色な感じの Android on Raspberry Pi なので、読み物的にお楽しみください。

Raspberry Pi を用意する

そもそも、なぜ Raspberry Pi で Android なんて動かすのか…は、さておき(苦笑)、Raspberry Pi を用意します。Android のインストールに BerryBoot というインストーラー ディストリビューションを使うのですが、バイナリを見る限り最新の RasPi 2 でも、それ以前のものでも大丈夫なようです。

image

動作確認は、Raspberry Pi 2 と5inch ディスプレイで確認しています。Raspberry Pi には HDMI 出力がついているので、普通の HD の液晶ディスプレイでも動きます。が、GPU まわりが遅いらしく 1920×1080 のサイズでは非常に遅くて使い物になりません。この 5inch 液晶ディスプレイだと 800×480 でちょうどいい感じに動きます。ちなみに、あとで設定しますが、config.txt を弄ってディスプレイ解像度を変えた後は、据え置きの液晶ディスプレイでも 800×480 の解像度になるので、800×600 などの適当な荒い解像度にして起動しなおせばよいでしょう。

BerryBoot をダウンロードする

BerryBoot v2.0 – bootloader / universal operating system installer から、RasPi 2 ならば berryboot-20150916-pi2-only.zip を、それ以前の RasPi ならば berryboot-20140814.zip をダウンロードします。

image

最新のファイルは berryboot download | SourceForge.net からダウンロードができます。

micro SD カードに焼きこむ

ダウンロードした zip ファイルを解凍して、micro SD カードに焼きこみます。焼きこむといはいえ、解凍したファイルを micro SD カードにコピーするだけです。micro SD カードは 8GB 程度で十分です。

image

途中で、起動に失敗したり、micro SD カードが見えなくなった&容量が減った場合は、SDFormatter でフォーマットしてください。オプションで、論理サイズ調節を「ON」にしてフォーマットすれば大抵回復します。

image

Android のフォーマットは、Windows から読み込めないので micro SD カードが読み込めなくなった(Windows 標準でフォーマットできなくなった)、あるいは Linux で使っていたら 50MB ぐらいしか容量が無くなった(本来は8GBあるはずなのに)、ときにフォーマットするとよいです。

BerryBoot を起動する

BerryBoot は、最初に起動すると SD カードをフォーマットします。

image

USB キーボードをつなげて、OK を押してください。

RasPi 2 の初期ロットの制限かもしれませんが、HDMI ケーブルをつなげて RasPi に電源を入れるとうまく立ち上がりません。多分、起動時の力不足で立ち上がらなくなってしまうのでしょう。USB キーボード/マウス、有線LAN をつなげっぱなしでも立ち上がらないことが多いので、電源を入れる前は外して、起動しているときに繋げるという技を使っています。

image

こんな感じでフォーマットをします。

Android をインストールする

“Add OS” ボタンで、OS を追加するのですが、OS イメージはインターネットからダウンロードする方式になっているので、有線LAN をつなげておきます。Android は 4.4(KitKat)がダウンロードできます。最新版はないですね…

image

Android 4.4 を選択するとダウンロードが始まります。

image

ダウンロードが終わると、メニューに追加されます。

image

ここでメニューの「Exit」を選択すると、Android が起動されます。

image

いちおう、FullHD 解像度で使えるのですが非常に重いです。これでは実験用には使いづらいので、もうすこし低解像度にします。

config.txt で解像度を調節する

再び、RasPi に電源を入れなおして起動すると、BerryBoot → Android の順に立ち上がります。このときに、BerryBoot メニューで Edit Menu ボタンを押します。このボタンを押さないと、10秒後に自動的に Android が立ち上がるので注意してください。

image

メニューから「>>」に隠れている「Edit config」をクリックして、config.txt を書き換えます。

image

hdmi_group=2
hdmi_mode=1
hdmi_mode=87
hdmi_cvt 800 480 60 6 0 0 0

のように 800×480 で書き換えています。config.txt を書き間違えると画面がでなくなってしまうので、その時は micro SD カードを焼くところから再スタートしてください。

液晶ディスプレイで 800×480 で動かしたときんは、こんな感じになってスムーズに動くようになります。動かすにはマウスが必要ですが。

image

Android on Raspberry Pi を起動する

5 inch ディスプレイをつなげて動かすとこんな感じになります。

image

5 inch 液晶の場合は、電力が十分足りるのかつなぎっぱなしで起動しても大丈夫なのです。Bluetooth は使えないので有線マウスが必須なのが難点ですが。無線マウスは反応がにぶいですが一応動くようです。

adb でデバッグ接続する

やっとこさ、Android の領域に入りました。Android on RasPi の場合、USB デバッグはできるかどうかわからないのですが、既に 5555 ポートが開いていてデバッグモードで動くようになっています。

adb connect <IPアドレス>

でデバッグ接続をします。IP アドレスがどれに割り振られているかを調べるのが大変ですが、Android 起動時の表示にちらっと出てくるので覚えておいてください。

image

これで Visual Studio や Xamarin Studio からデバッグができるようになります。

Visual Studio で Android on RasPi を動かす

Xamarin Studio 2015 で Android プロジェクトを作ってデバッグ起動をします。

image

起動メニューのところに「ARM vexpress」と出るので、これを選択します。

image

Android テンプレートの Hello World プロジェクトが起動できます。

Xamarin.Forms を Android on RasPi で動かす

Xamarin.Forms のサンプルとして My Shoppe をダウンロードして動かしてみましょう。

Visual Studio 2015 Update 1 ではエラーが出てビルドができないので、Xamarin Studio でビルドしています。

image

ビルド実行は、ARM vexpress(API 19) を選択すれば ok です。

image

image

いくつか動かしてみると、画像のロードやテーブルもうまく動いています。これで Android のデバッグも順調にできますね。いや、普通に Android を使ったほうが便利なのですがw

Android on RasPi を何に使うか?

さて、Android が Raspberry Pi で動くことが分かったのですが、これを何につかうか?です。もともと、組込み系の展示会に行ったときに Android 試験ボードを見て、結構高い(2,3万円程度)ので何かで代用できないかな、と思ったところがスタートです。Android 拡張ボードには、ネットワークの他にも Bluetooth や各種ピンが配置されています。Android on RasPi の場合には、Bluetooth ドングルが有効にならないのですが、ピンのほうはなんとかできるのでは?と考えています。UI 部分を Xamarin で作って、内部的には Linux で GPIO 等を操作できればいいですよね。

まあ、5 インチ液晶を載せてしまうとピンが隠れてしまうので駄目なんですが、ピンがつながるようにして、戦車やロボットアームが動かないかな、と考えているところです。Rasbian と同じように /sys/class/gpio/export にアクセスすることで制御が可能だと思うんですけどね。

カテゴリー: Android, RaspberryPi, Xamarin | Raspberry Pi で Xamarin.Forms を動かそう(Advent編) はコメントを受け付けていません

EV3 + MonoBrick + C# で倒立振子ロボットを動かそう、とプロジェクト管理の話

11月の.NETラボ勉強会で発表した倒立振子ロボットの補足です。

EV3 + MonoBrick + C#
http://www.slideshare.net/moonmile/ev3-monobrick-c

実は、制御工学の話からプロジェクト管理とかアジャイルとかテスト技法に繋げてみたかったのですが、話し忘れました。私自身は工学部出身なのですが、制御工学は習わずじまいだったんですよね。電磁気学とか量子力学のところは通っているので座標変換まわりは大丈夫なのですが、一番の驚きは時間軸の t の項を式に加えるというところです。

以下は雑多な話として、かつ既に制御工学/品質工学の専門の方には当たり前のことかもしませんが、ちょっと比較として面白かったのでメモ的に残しておきます。

制御工学はハンドリングの技術

ハンドリング、フィードバックは、アジャイルプロセスやプロジェクト管理の世界ではよく聞かれる用語です。その元ネタは、制御工学にあると知ってはいたものの、そのままの言葉とそのままの手法が制御工学の中にある(当たり前といえば当たり前)のちょっと吃驚です。制御工学の教科書として「はじめての制御工学」「はじめての現代制御理論」の本を使っているわけですが、一番わかりやすい制御のたとえとしては、車のハンドリングがあります。人が車を運転するときに少し左にずれれば少し右に、少し右にずれれば左にと小刻みにハンドルを動かします。この「ハンドリング」は当たり前といえば当たり前ですが、初期値のハンドルを決めた後、ずっとそのままではなくて、途中でハンドルをちょこちょこと動かすわけです。たとえ、直線の道であっても道の凸凹や車の揺れなをを修正しながらまっすく走るように制御をします。

ちょうど同じ例えば、アジャイル技法にもあって、プロジェクトを進めるときには途中途中でハンドリングをするたとえ話が出てきます。最初に決めた仕様や設計に従って、まっすくぐに進むのではなくて、途中途中の動きに合わせてちょっとずつ目的に向かうように修正していく手法です。ウォーターフォールの管理(本当のウォーターフォール技法ではなく、一般的に知られている/嫌われている「ウォーターフォール」の方)では、最初の仕様/設計に従って突き進みます。つまり、最初の角度に従って真っすぐに進むという方法ですね。そうなると、ウォーターフォール管理に「制御」があるかというと、制御はありません。途中途中でみられるものは単なる「観測」であって、観測を本線の制御にフィードバックする仕組みがありません。また、アーンドバリューは予測にはなりますが、予測をした後にどうするか?の基準がありません(現在の価値の傾きからどのくらいの遅れが発生するのか?という予測は立てられます)。どちらにせよ、出発時点で方向を変えないのであれば、制御ではありません。厳密にいえば、初期値=時間が0 のときだけ考慮するので、制御工学の大切な t の項が出てこないためです。

そんなわけで、制御工学を IT のプロセス管理/改善に含めるためには、必ず時間軸の t 項が必要になってきます。

理想状態では制御がいらない

image

これは、倒立振子の座標軸を示したものですが、完全な倒立状態の場合には、θとφは0になります。倒立状態のときに、これまた理想的な状態の場合は、完全な倒立が成立した後は永遠に倒立します。理想的な棒と理想的なタイヤが、理想的な倒立をした場合には永遠に倒立するであろうことは容易に想像できますが、それは「理想」でしかなくて、現実的なところは必ずすぐに倒れます。何故でしょう?

倒立のために理想的な初期値を与えたときは、時間軸の t の項に対する変数は常に変化が 0 になります。永遠に倒立しているのだから、θもφも 0 のままですよね。しかし、現実には時間 t によって変化をするので、θ(t) と φ(t) になります。t に依存するわけですね。ここで面白いのは、

  • t = 0 のときに初期値が 0 ではない。
  • t > 0 のときに初期値とは異なる値になる。

という 2点があります。t = 0 の場合は、まさしく初期状態で、倒立振子ロボットを最初に手で立たせようというところです。また、壁に倒立振子ロボットを寄りかからせて立っているところを想像してください。どちらにせよ、θ = 0 に近い値がいいのですが、現実問題として 0 にはなりません。なんらかの揺れが発生するわけです。かつ、時間が進んで t > 0 の状態では、なんらかの要因で θ の値は刻々と変わります。それは倒れる瞬間かもしないし、ゆらゆらと制御しながら立っている瞬間かもしれません。少なくとも t > 0 の時点では、θ と φ は一定ではない状態が続きます。

これもプロジェクト管理やアジャイルプロセスと同じで、初期の状態はどうやっても理想形にはなりません。むしろ理想形にはならないという前提に立つことが、制御工学からわかります。また、時間が進みに従って、いろいろな要因がかかわってプロジェクト内の設計や要件が揺れるわけですが、これも t > 0 の条件にあたります。初期値は理想的ではないし、途中の進捗率も理想的にはならないのだから、なんらかの制御が必要になりますよね。逆に言えば、理想的なプロジェクトでは制御なり管理(マネージメント)は必要ありません。ですが、現実は理想ではないので、なんらかのマネージメントが必須になります。

フィードバック制御の効果は遅れてやってくる

image

制御工学ではブロック線図が使われます。音響関係やコイル/コンデンサを使った発振も、このブロック線図を書いてフィードバックの向きを入れます。

このフィードバックですが、オブジェクト指向的には一瞬で終わる( t の項目がない)ように見えますが、実はフィードバックは遅れてやってきます。この図はラプラス変換されたものですが、目標値とのずれをC(s)で制御として記述し、それに外乱要因が D(s) として追加された後、操作を P(s) で行います。これが時間の項目がないと、オブジェクト指向的に一瞬で計算が収束する Δt = 0 となるわけですが、実際にはΔt > 0 になります。このために時間軸が発生して、

image

こんな感じで、時間軸のそれぞれの出力値 y(t) が変わってくるわけです。オブジェクト指向やアスペクト指向自体も、遠地のメッセージングやネットワーク環境などを考えると t の項目が出てきます。それが複数のスレッドの場合は、遅延を考えたり、デットロックを考えたりする基準になるのですが、このあたりもオブジェクト指向と時間 t の関係を考えると色々と面白いです。

さて、最終的な出力からのフィードバックは、次の t にマイナス制御として働きます(なんらかの値へと収束させようとする力)。これをより安定的に制御するためには、グラフのように上に飛びぬけた「オーバーシュート」の状態では困る場合があります。また、なかなか収束せずにゆらゆらと振動してしまう場合も困ります。

これを安定稼働させるために PID 制御があって、それぞれ差分と積分と微分の項があります。

image

この Kp,Ki,Kd をうまく決めてやることによって(行列の状態方程式を解くことで値を計算できます…まだ私は計算できていませんが)、オーバーシュートをせず、振動が起こらずに目標値に着地させることができます。それぞれの項目が何を意味するのかは、最初の車のハンドリングの話に戻ります。地面の揺れや車の揺れに従って、時間 t に沿ってちょこちょことハンドルを動かして真っすぐ動かすようにします。しかし、大きくハンドルが外れた場合、大きく戻すと簡単にオーバーシュートしてしまいます。ハンドルが切りすぎの状態になって蛇行してしまいますよね。大きくハンドルが外れた場合、あるいは大きく曲がる場合は、少しずつハンドルの向きを調節して、最終的に大きなずれをなくすようなハンドリングの仕方をします。当たり前といえば当たり前の動作ですが、これをプロジェクト管理に当てはめてみましょう。

何らかの大きなトラブルがあって、プロジェクトの進捗が大きく遅れたとします。1日がっつりと進捗が遅れた場合はどうすればいいのか?ってことですね。1日遅れたのだから、プロジェクトメンバを休日出勤させて1日取り戻してはどうか?というのが、よく使われる進捗管理の方法だと思うのですが、どうでしょうか。制御工学的には、大きなずれは少しずつの制御を積み重ねたほうが、オーバーシュートや振動が少なくなるわけです。ならば、プロジェクトの進捗が大きく遅れた場合に、1日の休日出勤を行ってはダメですよね。オーバーシュートが起こりそうです。そうなると、遅れはちょっとずつ取り戻したほうがよいのではないか、というのが制御工学的な考え方です。

制御がうまくいかなくて、元の値の上下を振動してしまうのは、元に戻す力が早すぎる(たいていは加速度のゲインが大きすぎる)ことです。これを、ゆるゆるとやるのが制御の基本です。何故、少しゆるくやるのかというと、フィードバックの効果は遅れてやってくるからです。これは、RC 回路の高周波のフィルタでも同じで、少し遅れるために振幅が相殺されてしまう現象と同じです。となれば、物理現象をプロジェクト管理に応用すれば、なんらかの対策を打ったとしてもその効果があらわれるのは少しあとになりますよね。大抵の場合は、すぐに効果が表れると思って(期待してしまって)なんらかの効果が出るものに執着したり、効果がでないとすぐに別の対策を立てたりしますが、実はフィードバック効果自体は少し遅れてくるのが物理現象なのです。ならば、ならかの対策を打ったときには対策の効果がでるまで、時間が掛かることを知って対策を打ちましょうということです。

フィードフォワードで予見する

倒立振子ロボットのプレゼン資料には「フィードフォワード」の話はできません。フィードフォワードは、フィードバックの逆で、先に手を打つことです。先に書いた通り、フィードバック制御だけでは、効果が遅れて発生するので常に後手後手の制御になってしまいます。ならば、目標値に沿うように、外乱の要因をあらかじめ低減するのがフィードフォワード制御の役目です。外乱ってのは何かというと、予期しない揺れみたいなものですね。ですが、外部からの揺れであっても一定の法則があってり、一定の範囲で測定できたりするわけで、その項目が過敏に働かないように制御を入れることができます。

まだ学習途中なのですが、フィードフォワード制御を使って外乱に過敏にしないようにするためには、多変量解析の仕組みを利用して、加味する値の影響度(ゲイン)を調べるといいでしょう。余分な雑音を雑音とみなすために、影響度を小さくします。これはちょうど、数式の中に sin θ を θ が微小のときには sin θ ≒ θ とみなす、ことと似ています。より小さくなる自乗の項目を減らしたり、局値を取ったりして式を簡単にして解きやすくします。

じゃあ、これがプロジェクト管理の場合にはどれに対応するのだろうと考えてみると、ちょうどスクラムプロセスのスクラムマスターの役割に似ています。プロジェクト内のエンジニアが外部の雑音(金額交渉やスケジュール交渉など)に惑わされないよに防壁に役割をします。雑音を雑音と扱い、重要なものだけ通すフィルターの役割をする分けですね。

また、常に対策が後手後手になってしまうフィードバック制御(「進捗どうですか?」など)ではなくて、何らかの予測のものに先手を打つことが必要です。マイルストーンを置いたり、バーンダウンチャートで不具合の収束度を見極めたり、増員をしたりという早期対策ですね。

このあたり、色々考えながら制御工学の式を解いています。最終的には、いくつかの固定値と簡単な足し算の式だけで、倒立振子ロボットが倒れないで済むのが驚きです。簡単な式だからこそ、マイコンとしてメモリの少ないチップでも十分制御ができるしアセンブラで書くこともできる(まあ、そのほかの動作がややこしいんですが)のですね。このあたり、サーボモータも PID 制御をしていて面白いです(というか、サーボというのがその制御そのもの名前だった)。

このあたり、本格的な制御工学とプロジェクト管理/アジャイルプロセスが、どう組み合わさるのかは考えてみたいところです。少なくとも、私にとって制御工学というジャンルはそういう目でも見ています。

カテゴリー: 開発 | EV3 + MonoBrick + C# で倒立振子ロボットを動かそう、とプロジェクト管理の話 はコメントを受け付けていません

倒立振子ロボットを MonoBrick で作成する(F#編)

つらつらと、はじめての制御工学 を読んで、やっとこさ PID 制御のところを過ぎて講座12のボード線図のところに来たところですが、C#版をF#版に直します。F# なのだから、関数型っぽい式の作り方とか制御工学で使われている変数をそのまま流用するとかしたいところなのですが、そのあたりはまだ理解&コードの読み進めが足りず…ひとまず、C#からF#にコンバートして倒立振子できるところまでいきました。

コード

moonmile/MonoBalancer
https://github.com/moonmile/MonoBalancer

ここの MonoBalancerF が F# のコードです。

Xamarin Studio で F# プロジェクトを作る

MonoBrick を使う場合、Xamarin Stduio を使う方法と Visual Studio + WinSCP を使う方法の2種類がありますが、Xamarin Studio で最初のプロジェクトを作ります。

image

プロジェクトができあがったら、参照設定で「MonoBrickFirmware」がきちんと参照されていることを確認して(なぜか時々エラーになるので、そのときはいったん削除してもう一度参照すればokです)、おもむろに F# でコーディングをします。

image

Xamarin Studio で F# プロジェクトをビルドする

なぜか、ビルドをするときは「有効なランタイム」を「Microsoft .NET」にしておきます。Mono のほうだとビルドできないんですよね。環境の違いかもしれませんが。

image

でもって、デバッグ実行するときは「Mono 4.0.3…」に直してデプロイをします。このあたりが面倒なのですが、仕方がない。

デバッグ実行する

Xamarin Studio でデバッグ実行をすると MonoBrick にアセンブリがアップロードされて、そのまま実行されます。このときには、「有効なランタイム」を「Mono 4.0.3…」にしておきます。

image

そうすると暫く時間が立つと EV3 + MonoBrick で F# のコードが実行されます…が、この立ち上がりが結構を遅くて閉口するので、アップロードだけ Xamarin Studio に任せて、その後は Tera Term で MonoBrick に接続して直接立ち上げます。このほうが、コンソールを使えるのでログ出力とかできて便利です。

デバッグ実行で Upload program to brick … が終わったあたりで、ぶちっと止めてしまいます。止めるぐらいだから、WinSCP でアップロードしても同じなんですが、まあ、Xamarin Studio だけでできるのでこれはこれで便利。

image

その後で、適当なターミナルで接続して、apps フォルダ内にあるアセンブリを直接起動します。

image

mono MonoBalancerF.exe

のようにして起動します。ターミナルを使うと Console.WriteLine(“MonoBalancer start…”) のようなコンソール出力が見れるので、EV3 のディスプレイに出さなくてもデバッグがやりやすくなります。まあ、TCP/IP 通信しているので、転送スピードを考えてログの出力は控えめにしないといけませんが。

mono が 4.0 ベースだからなのか、非常に F# の立ち上がりが遅いです。ロボットの倒立が始まるまで5秒以上待たされるのは、Thread 生成が遅いのかコードが悪いのかわかりませんが、まあ、C# 版よりもスタートに時間がかかります。時間はかかりますが、20msec でジャイロセンサーの値を読み取って、倒立できるぐらいまでのスピードは出ているので実用的でしょう。

何故 F# で作るのか?

ロボット制御の場合、たいていの制御プログラムは C/C++ で書かれているのでオブジェクト指向的に書かれているものが多いのですが、関数型言語で書いたらどうなるのかなというのと、制御の式あたりは関数型で書いたほうが色々とデバッグ/試行錯誤しやすいだろう、という発想がもとです。

SketchBot doing Turtle Graphics! – YouTube

https://www.youtube.com/watch?v=w_4HxiHnwAM&feature=youtu.be
も MonoBrick + F# で書かれていたりします。

カテゴリー: EV3, F#, MonoBrick | 倒立振子ロボットを MonoBrick で作成する(F#編) はコメントを受け付けていません

倒立振子ロボットを MonoBrick で作成する(C#編)

ここ2週間ほど倒立振子ロボットにハマっていたのがようやく形になって来たので、メモ代わりに残しておきます。初手は ET ロボコンの EV3Way用のサンプルプログラム なのですが、そのままだとうまく動かかなったので(今考えると車輪の径の設定等が必要だった)、もうちょっと簡単に組み立てて動くところから始められるように、BALANC3R にある LabVIEW のコードから C# に移植しています。

moonmile/MonoBalancer
https://github.com/moonmile/MonoBalancer

移植先

Robot Square – Tutorial: Building BALANC3R – Robot Square のコードを忠実に C# に移植しているつもりです。「つもり」というのは、コードの意味をいまいち把握していないからですね。いくつかパラメータの意味もわかっていないので、そのまま C# に移してパラメータもそのまま持ってきています。

よく知らなかったのですが、倒立振子ロボットは制御工学では定番の教材で色々なものがあります。ジャイロセンサや、モーターの特性(角速度、ギアボックスの比率、トルクなど)、重心位置、車輪の半径など、いろいろな要素が絡み合って倒立するように(倒れないように)する訳ですが、それらをモデル化してフィードバック回路にするところがミソです。複雑な要素をうまくモデル化して時間軸で定常値に近づけます…と今のところ理解しています(まだ、教科書でPID制御のあたりまでしか行ってないので)。

MonoBrick

LEGO Mindstorms EV3 は主に LabVIEW を使ってコードブロックを組み合わせてプログラミングをしていきます。が、そのままでは普通のプログラミングがしずらいので、MonoBrick.DK | Home of MonoBrick を使います。Linux に Mono を入れて C# でも動かそう(F#/VB等でも動きます)という方法です。EV3 への制御は MonoBrickFirmware ライブラリを通して使います。

MonoBrickFirmware のコードは、Larsjep/monoev3 にあります。豊富なサンプルコードがあるので色々な機能を動かすには苦労しないでしょう。MonoBrickFirmware を見ていくと、中間コード生成して EV3 に渡していることがわかります。ここの中間コードの形式などは、ダウンロード – レゴ®マインドストーム LEGO.com にある EV3 Firmware Developer Kit 等で公開されています。

ちょっとしたコードの解説

制御工学的な話は勉強中なので、ちょっとだけコードの解説をしておきます。

センサーとしては「ジャイロセンサー」と「モーターの角速度」だけを使います。倒立振子ロボットを作る際のセンサーは「ジャイロ」「ロータリーエンコーダ」「トルクセンサ」が必要なことが多いらしいのですが、EV3 の倒立振子ロボットは2つだけです。

double GAIN_ANGLE_VELOCITY = 1.3;
int GAIN_ANGLE = 25;
int GAIN_WHEEL_SPEED = 75 ;
int GAIN_WHEEL_POSITION = 350;

ba.Initialize(
    0,  // LEGO Jyro 
    42, // wheel 42 mm
    22);// sample time 22msec
ba.SetConstants(
    0.6,    // Kp
    14,     // Ki
    0.005,  // Kd
    GAIN_ANGLE_VELOCITY,    // Gain Angular Velocity 
    GAIN_ANGLE,     // Gain Angle    
    GAIN_WHEEL_SPEED,     // Gain Wheel speed 
    GAIN_WHEEL_POSITION);   // Gain Wheel position 

ba.Initialize で指定するのはタイヤの直径と、サンプリングタイムです。重心位置は寄与していないのかもしれません。計測間隔は 2,3msec と短いものが多いのですが、Baranc3R では 22msec(内部的に補正して 20msec)になっています。これでもバランスが取れているので、C# などで制御するときには非常に楽です。ちなみに .NET Framwork 等では Timer クラスを使うと 30msec 以上になってしまうので、ちょっと手を入れています。元の LabVIEW のコードで 2msec 補正しているのは、計算時間(2msec)+ 待ち間隔(20msec)という感じなんだと思います。

ba.SetConstants で指定しているのは PID パラメータと、ゲインです。この値は制御工学を紐解くのと実測値を調節しないと駄目なので、そのまま使います。通常は、モデル化→ ブロック線図 → 伝達回路 → ラプラス変換 → PID 制御 に直して定常値を計算する(らしい)のですが、このあたりはまだ勉強中。

double motorPosition = Position();
double robotPosition = 0.0;
double robotSpeed = 0.0;
ReadEncoders(out robotPosition, out robotSpeed);
double angle = 0.0;
double angleVelocity = 0.0;
ReadGyro(out angle, out angleVelocity);
double inputVal = CombineSensorValues(angleVelocity, angle, robotSpeed, robotPosition, motorPosition);
double pid = PID(_kp, _ki, _kd, _dt, 0.0, inputVal);
Errors(pid); // エラーの場合は例外が発生する
SetMotorPower(_steering, pid);

制御ループの中で、CombineSensorValues 関数が入力値を計算しているところです。参照値は、倒立させるために常に 0.0 です。ただし、リファレンス値を 0.0 にしてしまうと、ステアリングとロボットのスピードも殺されてしまうような気がするんですけど、どうなんでしょうね。私のコードの移植が変なのかもしれません。

PID 関数でモーターに与えるパワー(PWD値、-100%から100%の間)を計算します。このあたりの定数値は Arduino や Raspberry Pi で倒立振子ロボットを作ったときに異なってくるところです。まあ、そのあたりも理解して作りたいなと思っているので、やっぱり制御工学の知識は必要ですね。

あと、平均スピードの計算方法とかジャイロセンサーの取得方法とかが少し工夫してあります。ジャイロセンサーの揺れに過敏にならないように近傍の平均値を使っていますね。

数式に直すのと、モデル化に戻すのは後で。

参考先

姿勢制御のコードを理解してプログラムを書きたかったので、既存のプログラムを読み直している途中です。結局のところ、制御工学の教科書 はじめての制御工学 (KS理工学専門書) [Kindle版] に行きついています。

etroboEV3 / Wiki / Home
http://sourceforge.net/p/etroboev3/wiki/Home/
LEGO Mindstorms EV3でGyro Boyを動かしてみた。(その1) – robo8080のブログ
http://blog.goo.ne.jp/roboz80/e/455074cc58e99474d4daf6a24d5ebcc0
2輪倒立振子ロボット2号機作成。 – robo8080のブログ
http://blog.goo.ne.jp/roboz80/e/e485f7efa259e44baaa298fd6989c556
Robot Square – Tutorial: Building BALANC3R – Robot Square
http://robotsquare.com/2014/06/23/tutorial-building-balanc3r/
NXTway-GS (Self-Balancing Two-Wheeled Robot) Controller Design – File Exchange – MATLAB Central
http://www.mathworks.com/matlabcentral/fileexchange/19147-nxtway-gs–self-balancing-two-wheeled-robot–controller-design
倒立振子の研究
http://www.instructables.com/id/%E5%80%92%E7%AB%8B%E6%8C%AF%E5%AD%90%E3%81%AE%E7%A0%94%E7%A9%B6/
みのくらロボット
http://minokura.net/works/wheelpendulum.html

カテゴリー: C#, EV3, MonoBrick | 1件のコメント

[C#] ミリ秒単位のタイマーを作成する

MonoBrick を使い倒立振子ロボットを C# に移植したところですが、ジャイロやサーボの回転数のサンプリングレートに問題があります。もともとのコードがだいたい 20 msec 単位(1秒間に50回)ぐらいのサンプリングを行っているものの、途中で C# で Timer クラスを使うと 30 msec ぐらいの精度しかないんですよね。しかも、だんだんと値がずれていきます。

ちょっと考えて、きちんと 20 msec 単位でサンプリングできる TickTimer クラスを作ったので公開しておきます。

TickTimer クラス

System.Diagnostics.Stopwatch と Thread を使って正確に msec 単位の割り込みを発生させます。使い方は、Timer クラスと同じようにコールバック関数を指定して使います。
最初は Task で作ったのですが、mono の Task 生成が遅いらしく Thread に切り替えています。MonoBrick が .NET 4.0 ベースなので async/await が使えないし、まあ Task である必要もないので。

public class TickTimer
{
	TimerCallback _cb;
	Stopwatch _sw;
	int _dueTime;
	int _period;
	bool _loop = true;
	Thread _task;

	public TickTimer(TimerCallback callback, object state, int dueTime, int period)
	{
		_cb = callback;
		_dueTime = dueTime;
		_period = period;
		_sw = new Stopwatch();
		_task = new Thread(onTimer);
		_task.Start( state );
	}
	public TickTimer(TimerCallback callback, int period)
	{
		_cb = callback;
		_period = period;
		_sw = new Stopwatch();
		_task = new Thread(onTimer);
	}
	public void Start( object state = null )
	{
		_dueTime = 0;
		_task.Start( state );
	}

	public void Stop()
	{
		_loop = false;
	}
	private void onTimer(object state)
	{
		Thread.Sleep(_dueTime);
		_sw.Restart();
		while (_loop)
		{
			long msec = _sw.ElapsedMilliseconds;
			int rest = _period - (int)(msec % _period);
			// 200msecだけ余らせてスリープ
			if (rest > 200)
			{
				Thread.Sleep(rest - 200);
			}
			// 200msecの間、ちょうどになるまでループで待つ
			while (true)
			{
				if (_sw.ElapsedMilliseconds >= msec + rest)
				{
					break;
				}
			}
			if (_cb != null)
			{
				_cb(state);
			}
		}
		_sw.Stop();
	}
}

利用コード

class Program
{
	static void Main(string[] args)
	{
		// 1秒毎にタイマーを発生させる
		// System.Threading.Timer で間隔 20msec を指定すると 30msec 程度になる.
		// 1.0sec を指定しても完全に1.0にはならない。1msecぐらいずれていく
		Timer tm = new Timer(timerCB,null,0,1000);
		Console.WriteLine("Timer start.");
		Console.ReadKey();
		tm.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);

		// System.Diagnostics.Stopwatch を利用して、正確なタイマーを作る 
		TickTimer tm2 = new TickTimer(timerCB, null, 0, 1000);
		Console.WriteLine("TickTimer start.");
		Console.ReadKey();
		tm2.Stop();
		// 20msecも正確に測れる
		// TickTimer tm3 = new TickTimer(timerCB, null, 0, 20);
		TickTimer tm3 = new TickTimer(timerCB, 20);
		tm3.Start();
		Console.WriteLine("TickTimer start at 20msec.");
		Console.ReadKey();
		tm3.Stop();
	}
	static void timerCB(object obj)
	{
		var msec = DateTime.Now.Millisecond;
		Console.WriteLine("msec: {0}", msec);
	}
}

実行すると、こんな感じ。

Timer クラスを使うと 1msec ずつずれていくけど、TickTimer の場合は、1msec 程度ずれても元に戻るので正確な 1000msec や 20msec で割り込みを入れられます。

この割り込みを使って、PID 制御の積分成分を出せば良いはずで、これで平均値を出すときのずれが減るかなと。いまのところ、こんな感じで倒立できています。

20151106_01

カテゴリー: 開発, C# | [C#] ミリ秒単位のタイマーを作成する はコメントを受け付けていません