2012年12月15日 (土)

Hello world for Roomba Hack!!

やる気が戻ってきたので、Roomba Hackを始めました。

手始めに、ここを参考にさせてもらいながら通信ケーブルを作りました。材料はすでに揃えてあったのですが、仕事が忙しかったり疲れていたりでやる気が出なかったので今まで放置していました。

続いてプログラムです。まずはシリアル通信のループバックテストをしてから、プログラミング言語を学ぶと最初にやる”Hello world”の表示プログラム的な意味合いで、Roombaにドレミというメロディを演奏させるプログラムを作ってみました。

これがそのプログラムのプロジェクトワークスペースを固めたファイルです。「HelloRoomba.zip」をダウンロード

開発環境はVisual Studio 2010で、シリアル通信をするためにBoostライブラリを使いました。

このプログラムは full mode を使用しているので、一度このプログラムを走らせるとRoombaのボタンをいくら押しても何も変化しなくなります。元に戻したい場合は、

char command[] = { 128, 130, 132, (以下省略)

の部分を

char command[] = { 128, 130, 131, (以下省略)

にしてプログラムを再実行してください。132がFull Modのopcodeで、131がSafe Modeのopcodeです。Safe ModeになってからRoombaを持ち上げてやると、wheel drop (脱輪)を検出してPassive Modeに遷移します。Passive Modeになれば、ボタン操作等を受け付けるようになります。

作ってみてわかったことなのですが、Roomba Open Interface (ROI)にはバージョン情報を取得するコマンドなどがないので、Roombaとの通信が確立したのかどうか確認しずらいのですね。なので、ボーレートが合っているのかどうかわかりにくい。仕方がないのでメロディを鳴らして通信が確立したことを確認するのがよさそうです。

とりあえずこれでメロディがなることまで確認できたので、シリアル通信の無線化を含めていろいろ遊んでみる予定です。

Visual Studio 2010でBoostを使う

久しぶりにBlogを更新しますが、これは完全に自分のためのメモです。

Roomba Hack をするのに、PC上のプログラムからシリアル通信でRoobaにコマンドを送りたい。開発環境はVisual Studio 2010を使うという前提で、シリアル通信するにはWin32 APIを叩くか、.NET FrameworkのSerialPortクラスを使うか、Boostライブラリのserial_portクラスを使うか。迷った結果、移植性を考えてBoostライブラリに決めました。で、これはその作業メモです。

まずBoostをダウンロードして、インストールする。配布もとはここです。バージョンは最新版である1.52.0を選びました。インストール方法は、基本的にインストーラをダブルクリックするだけなのですが、オプションとしてマルチスレッドライブラリにするか、デバッグをサポートするかなどがありました。私はCドライブにOSを、Dドライブにプログラムをインストールするようにしているので、Boostのインストール先はD:\Program Files\boost\にしました。ライブラリのサイズが大きい(私が選んだオプションで1.48GB程度ある)ので、インストールにも多少時間がかかります。

続いてVisual Studio 2010側の設定。とりあえず適当なプロジェクトを作り、Boostのインクルードディレクトリとライブラリのパスを設定します。Visual Studio 2010では「ユーザープロパティシート」なるものでコンパイラやリンカのスイッチ、ユーザマクロ定義を行うようです。プロパティシートは、[表示]メニューの[プロパティマネージャ]から作成します。そうするとプロパティマネージャがソリューションエクスプローラのあった場所に表示されるので、プロパティマネージャで先ほど作成したプロジェクトを右クリックすると、[新しいプロジェクトプロパティシートの追加]が表示されます。これを選んで、[構成プロパティ] → [VC++ディレクトリ]で、インクルードディレクトリに D:\Program Files\boost\boost_1_51; を、ライブラリディレクトリに D:\Program Files\boost\boost_1_51\lib; を追加しました。とりあえずこれでBoostを使う準備が完了しました。

2012年4月19日 (木)

Visual Studio 2010 で Open Dynamics Engine を動かす

春の情報処理技術者試験も終わったので、そろそろロボット開発を加速させて行きたいところです。それでですね、マイクロマウスとは直接関係ないのですが、フリーの物理計算エンジンであるOpen Dynamics Engineに前から興味がありまして、今日はその ODE を Visual Studio 2010で動かすことにチャレンジしました。

やり方は Visual C++ 2008 Express Edition と一緒なのでココを見ていただければインストール方法が書いてあります。私が付け加えることは何もありません。ODEの最新版であるODE v0.12 の premake では VS2008 までしか対応していないので、premake では vs2008 用のプロジェクトファイルを生成しておいて、Visual Studio 2010 で開けば変換を勝手にやってくれます。ODEについて来たデモプログラムのビルドと実行は何の問題もなく出来ました。

デモプログラムの次は、hello world 的な「リンゴの落下」プログラムをに挑戦してみます。

2011年1月 9日 (日)

Android開発環境をインストールしてみた

普段はあまり流行りものに手を出さないのですが、ちょっとばかりAndroidの開発環境をインストールしてみました。私がAndroidに興味を持った理由は幾つかありますが、そのあたりは説明を省略。

Javaの開発環境、Android SDK、Eclipseをインストールして、HelloAndroidな最も初歩のアプリケーションを作ってみました。インストールに関しては全く問題なし。EclipseからAndroidのエミュレータ上でアプリを実行しようとすると、おそろしく時間がかかるようです。これには参りました。でも、EclipseからAndroid SDK and AVD Managerを開いて、エミュレータを起動しておけばそれも問題なしということがわかりました。

Androidのアプリ開発は基本的にJavaなんですね。はじめて知りました。これまでプログラミング言語はCを使ってきて、C++, Java, C#は入門書読んで簡単なアプリを作ってみたくらいのレベルでしか理解していません。(VB6.0は入門書も読まずいきなりソースコードを書いて動いてしまいました。) 習うより慣れろというくらいですから、いじっているうちにいろいろ覚えると思います。

まだ実際のAndroid端末を持っていないので、e-mobile か au で端末を探すつもりです。

2010年10月26日 (火)

問題はシリアル通信だけではなかった

昨日の夜中にUpした日記で、UARTの送信処理が遅いので別タスクに切り出したほうがよいと書きました。しかし、問題はそこだけではありませんでした。

結露を先に書くと、問題は自作のタスクスケジューラにありました。細かいことを書くのが面倒になってきたので説明は省略。

シリアル通信が遅いことにいまさら気付いた

台車を制御しているファームウェアの処理周期が微妙に遅いような気がしていたので測って見たところ、100ms周期で動いていると思っていた処理が104ms周期になっていることがわかりました。いろいろ調べてみた結果、SH2用に自作したSCI_print関数の処理が遅いことがわかりました。この関数はSH2でもprintfが使えるように、%d、%s、%c だけをサポートした軽量級のprintfもどきです。試しにこの関数で32文字程度を送信すると、処理時間が8ms程度かかっています。

なぜそうなるのか。結論を先に書くと、それが非同期UART通信の処理速度だということです。計算してみた結果、気付きました。以下、その計算です。

【設問】

ボーレートがB[bps]のとき、n 文字送信するのに必要な時間 t を求めよ。

【解答】

スタートビット 1bit、データビット 8bit、ストップビット 1bit、パリティ無しと仮定すると、1文字送信するために必要なビット数は 1 + 8 + 1 = 10 [bit]である。したがって、

t = 10 * n / B

となる。

この式に B = 38400, n = 32 を代入すると、 t = 10 * 32 / 38400 ≒ 8.33 * 10^-3 [s] ということで、約8.3msかかることがわかります。だから、UARTで文字列を送信しているだけでms単位の時間があっという間に過ぎていくわけです。

さて対策です。こういう時は、送信処理だけ別タスクにしてしまうのが定石です。SCI関数には手をつけたくないので、Host I/F関連で送信タスクを一つ作ってやることにします。この台車制御用のファームはRTOSを使用していませんが、自作の簡易スケジューラがあるので、そちらにタスクを切り出せばマルチタスクもどきを実現できます。

この対策は、今日のこの記事を書きながら思いつきました。

2010年10月11日 (月)

センサデータログにはまだ未解決の部分が残っている

先日の日記で、センサデータのログ問題に関して「ほぼ」解決した、と書きました。ほぼ、と書いたのは一抹の不安があったからです。そして、動くことは動くけれど、まだ未解決の問題が残っていることがわかりました。

先日の反省を踏まえて、タイマイベントにより呼び出されるイベントハンドラ処理が(タイマのインターバルとして設定した)100msを超えてしまった場合に備えて、イベントハンドラの先頭と末尾にInterlocked.Exchangeを仕掛けました。これを仕掛けることでイベントハンドラの再入を検出して、再入しているようなら何もしないで処理を抜けさせます。この時、再入を検出した時点でシステムログに再入検出を記録することにしました。

ここからが問題です。まず、開発用PCのHDDでプログラムを実行すると、システムログにイベントハンドラの再入記録が残っていないので問題無いのですが、同じ開発用PCでもSDカード上でプログラムを実行する(すなわち、SDカード上にセンサログが記録される)と、システムログにイベントハンドラの再入検出が記録されてしまいます。ということは、SDカードに対するファイルの書き込みに100ms以上かかっているということなのでしょうか。

ということで、SDカードに書かれるデータのサイズと書き込み速度を見積もってみます。

  • (LRSのデータログ1行当たりのデータサイズ)
  • =((データ1点あたりのバイト数)+(カンマのバイト数))×(データ点数)
  • =((5 × 2) + 2)×1081
  • = 12972 [Byte] = 13 [KB]

これが1秒間に10回書き込まれるとすると、必要な書き込み速度は 130KB/sです。一方、私が使っているSDカードはClass4と書かれているので、書き込み速度は4MB/s。書き込まれるデータが小刻みなのか大きなブロックなのかでFlash ROMの書き込み速度は大きく変わりますが、それにしても桁が違うので書き込み速度には余裕がありそうです。

そこで、実際に書き込み時間がどれくらいかかっているのか測ってみました。その結果、HDDにセンサログデータを書き込んだ場合は1回あたり0~17ms(最頻値は 1ms)で、SDカードの場合は16~776ms(最頻値不明)でした。SDカードの場合、100ms以下の回数が少なく、120~200ms以上かかるケースが大多数でした。この結果からみて、SDカードへの頻繁な書き込みがボトルネックであることは間違いありません。

対策をいくつか考えてみます。

  • A案: RAM上にセンサログデータを貯めて、ログ収集を終了した時点でファイルに落とす。
  • B案: SDカード上でのプログラム実行を止めて、制御用PCのSSD上でプログラムを実行し、センサデータログもそこに落とす。
  • C案: log4netやsqliteなどの既存のコンポーネントにセンサログ収集を任せる。

A案は、処理速度的には速そうですが、仮に1時間分データを貯めるとすると475MBになるのでできれば避けたい。制御用PCのRAMは1GBしかないので、スワップが沢山発生するのもいやですから。

B案は、一番手っ取り早い方法です。元々、プログラムの書き換えやセンサログデータの移動を簡単にするためにSDカード上でプログラムを走らせて、データもそこに落としていました。でも、その手間はクリティカルな問題ではないので、処理速度的にNGなら手間をとったほうがいいかもしれません。なにより、一番確実ですから。

C案は、log4netやsqliteならそこら辺をうまくやってくれるんじゃないかなぁという期待だけで、根拠はありません。なので、やってみても解決しないかもしれません。

ということで、時間が無いので安全確実なB案がよさそうですね。

2010年10月 9日 (土)

スレッドプールに溜まっていたことが判明

先日の日記で書いたセンサデータのログがおかしい件、ほぼ解決しました。

本来ならば一つのスレッドからしかセンサデータのログを書き込んでいないはずなのに、システムログのエラーの出始めるところをみると、複数のスレッドが同じタイミングでエラーを吐いていますね。ここがポイントでした。

理由は、ログ収集開始時にプログラムの処理が遅くなるところがあったからでした。今、マニュアル走行モードのタイマには、System.Timers.Timerを使用して、Elapsedイベントを使って 100ms 毎にセンサデータをログファイルに書き込んでいます。ここで、Elapsedイベントのイベントハンドラの処理が100msを超えてしまうと、別のThreadPoolスレッドで同じイベントが再発生することがあるそうです。

ここでプログラム中でレーザーレンジスキャナのセンサデータをログファイルに保存する部分を見てみると、約1000点のデータを保存するために毎回 scannedData = new int[lrsMaxData]; のようにメモリ領域を確保する処理が入っていることに気付きました。C言語でいうところの malloc / calloc のようにメモリ領域確保は処理時間を要する部類なので、C#でもおそらくそうなのだと思います。これのメモリ領域確保が1回で済むように書き直したところ、平均 20~40msかかっていたセンサログ書き込みが平均 5~6msにまで短くなり、先日の日記で出ていたようなエラーも発生しなくなりました。

2010年9月28日 (火)

DLLを読み込めないという問題(3)

さらに続きです。ちょっとだけ進展がありました。

まず、UrgLibDll.dllがReleaseモードでなぜうまく動かなかったのか解明できました。それは、Releaseモードのリンカの入力設定で、モジュール定義ファイルが空欄になっていたことが原因でした。Debugモードではちゃんとモジュール定義ファイルを指定していたのにReleaseモードでは指定していなかった。だから、ReleaseモードでビルドしてできたDLLは、公開すべき関数をExportできていなかった、というわけです。

上記の修正で作りなおしたUrgLibDll.dllを制御用PC上にもっていき、Dependency Walkerで依存関係を洗ってみたところ、また別の問題を見つけました。それは、制御用PCにVisual C++ 2005再配布可能パッケージをインストールしたはずなのに、MSVCR80.DLLが見つからないという問題です。今思えば、インストールが始まる感じはあったのに完了したという通知がないままダイアログボックスが消えてしまっていたので、インストールがうまくいっていなかったようです。そこで、こんどは再配布可能パッケージ(SP1)をインストールしてみました。はい、今度はインストールが終わった後にPCの再起動を求めてきました。おそらくインストールできたのだと思います。

制御用PCを再起動したのち、Depedency WalkerでUrgLibDll.dllの依存関係を解析しなおしてみたところ、不足しているDLLからMSVCR80.DLLが消えました。

これで解決できていることを願って、新しいUrgLibDll.dllを制御プログラムのディレクトリに入れてからプログラムを起動してみたところ、今まで出ていた例外がでなくなりました。そこから先は未確認ですが、とりあえず一歩前進です。(EFSADU.DLL、IESHIMS.DLL、WER.DLLは無くても動きそうですね。)

2010年9月27日 (月)

DLLを読み込めないという問題(2)

昨日の続きです。そもそも何がおかしいのか、丹念にエラーメッセージを追うことにしました。

制御用PCでプログラムが吐きだした例外のメッセージは次のようなものでした。

System.DllNotFoundException: DLL 'UrgLibDll.dll' を読み込めません: このアプリケーションの構成が正しくないため、アプリケーションを開始できませんでした。アプリケーションを再度インストールすることにより問題が解決する場合があります。 (HRESULT からの例外: 0x800736B1)
   場所 UrgLib4CS.UrgLib._urg_initialize(urg_t& urg)
   場所 AIModule.Device.LaserRangeScanner..ctor()
   場所 AIModuleApp.Form1.Form1_Shown(Object sender, EventArgs e) 

(以下省略。)

プログラムのカレントディレクトリにUrgLibDll.dllを配置しているのにそのDLLを読み込めない、というのが問題点なわけです。ここでの議論から、必要なDLLが足りないとDllNotFoundExceptionが発生するということなので、昨日は「開発用PC上で」Depenency WalkerでDLLの依存関係を調べて、IESHIMS.DLLが足りないという結論に達しました。

しかし、ここに落とし穴を発見。開発用PC上でDLLの不足を探しても意味ねーじゃん? そう気づいたので、制御用PC上でDependency Walkerを実行してみました。ちなみに、ここから手に入ります

その結果、次のDLLが見つからないことがわかりました。

ここで注目すべきは、MSVCR80D.DLLです。”D”が入っていますね。これはUrgLibDll.dllがDebugモードでビルドされたことを示しています。Relseaseモードでビルドしたらうまく動かなかったのは昨日の日記に書いたとおりです。デバッグ用のMSVCR80D.DLLを必要としているのであれば、VC++ 2005の再配布可能パッケージをインストールしても動かないのは納得できます。

ちなみに、EFSADU.DLLはNTFSのファイル暗号化に関するDLLのようです。IESHIMS.DLLとWER.DLLは、インターネットで検索するとその二つのファイルが見つからないという書き込みが山ほどでてきます。

上記を踏まえて試してみるべきこと。

  1. UrgLibDll.dllをReleaseモードでちゃんと動くようにする。(Releaseモードで動けば、依存関係がMSVCR80D.DLLからMSVCR80.DLLになるはず。)
  2. 制御用PCにVisual Studio 2005を入れる。

後者のほうが手っ取り早いかな。

より以前の記事一覧

最近の写真