Control robots with non-RT Linux

非RT Linux による制御法の実際


Linux による周期実行

一般にロボットなどの制御を行う場合には、数ミリ秒などの単位で、 特定の制御ルーチンを周期的に実行することになります。 Linux においては、周期的に実行する機能、というものはないため、 一定時間の休眠(休憩)をはさみつつ、処理を繰返し行うことになります。 すなわち、
  while(1)
   {
      usleep(5000);    // 5msec 休み
      DoControl();
   }
というような感じです。

さて、この方法がそのまま思い通り動くかというと No です。 その理由は通りです。

  1. Linux はマルチユーザ・マルチプロセスのOSです。一般に こういったOSでは「いかに平等に各プロセスにCPU時間を割り振るか」を CPU割り当ての原則にしているため、場合によってはうまく動くものの、 場合によっては他のプロセスに先にCPUを割り当てられてしまって、 待たされることがあります。
  2. Linux において、休眠解除はいつでも起きるわけではありません。 つまり、「5msec後」と指定しても、ピッタリその時間に起きるわけではなく、 5msec 後のあるタイミングで解除されることになります。いわば、 「10分単位でしか設定できない目覚まし時計」みたいなものです。 これでは5分後に起こしてほしくとも、うまくはいきません。 ふつうに構築したLinuxのばあい、この間隔が10msec で、しかも、 最低10msec より長い時間たたなければなりません。
    つまり、いくら頑張っても、この方法では20msec 単位でしか処理 できません。
しかし、ちょっとした工夫をするだけで、これらの問題はかなりの部分が 解決します。

Linux の動作

ここで、簡単に上のようなプログラムをつくった場合の動作について 触れておきます。

まず、usleep(sleep) を実行すると、Linux によって、そのプロセスは 休眠状態に入ります。休眠状態はそのプロセスがLinuxに対して CPUの割り当てを要求しない状態です。この状態にすることで、 時間を見ながら無限ループで無駄に時間を過ごして周期的な実行をする、 というMS-DOSでやることがあるような方法に比べて、ほかのプロセスに 迷惑をかけず、CPUを有効利用することができます。

Linuxでは、内部で一定周期でタイマ割り込みが発生しています。 これは一般的なLinuxの場合は 10msec 単位で起きます。 このとき、OS内部の時間に関する処理をいくつか行いますが、そのとき、 休眠しているプロセスを調べて、指定された時間が経過していた場合には、 休眠を解除して、実行可能状態にします。

Linux は現在実行可能状態のプロセスの中から、優先順位の高いものを 実行します。この優先順位は基本的には nice という数値で決定され ますが、CPUを使いっ放しのプロセスの優先順位は時間とともに下がって いきます。

以上のような仕掛けの元で、上のようなプログラムを実行すると、
 休眠→(タイマ割り込み×n)→休眠解除→CPU割り当て
ということになります。先ほどの2点の問題点は、起きる時間が タイマ割り込みのときのみ、かつ休眠解除しても優先順位の関係で CPUが割り当てられないことがある、ということに起因します。

優先順位の問題解決

まず、優先順位の問題を解決します。これは簡単で、
   # nice --20 program
と、プログラムの前に nice --20 をつけます。これはプログラムの 優先順位をあげてやるもので、標準で0、最低で20、最高で -20 です (--20 の一つ目の '-'はオプションの印でそのあとの'-20'が数値)。 こうすることで、休眠解除後に実行される確率が非常に高くなります。 なお、この nice は標準より優先度をあげるには root でなければ、なりません。 特定のプログラムを常に優先度を高く実行したい、という場合には、 別の方法があります。

とはいえ、休眠はほんの少しで、おおむねCPUを使いっ放しにするような プログラムをつくると、いくら起動時に優先度をあげても、優先順位は 時間とともに低下、思った通り実行されなくなりますので、要注意。

さらに、強制的に優先度をあげて、他のプロセスより必ず先に実行 させるという方法もありますが、詳細は他にゆずります。

休眠周期の分解能向上

次に、時間分解能を向上させます。先に述べたように、標準状態では 20msec 以上、10msec 単位でしか周期実行できず、非常に不便です。 しかも、他のプロセスにCPUをとられてしまった場合、最悪、この 10msec の間CPUを占有されてしまい、大幅な遅れが発生します。

解決案は非常に単純です。タイマ割り込みをもっと短くします。 10分単位の目覚まし時計を1分単位に改造するようなものです。

具体的には、カーネルのソースの /usr/src/linux/include/asm/param.h に

#ifndef HZ
#define HZ 100
#endif
という部分があります。この HZ を 1000 にすれば、1msec 単位で タイマ割り込みが、すなわち、休眠解除ができるようになります。 当然、タイマ割り込みの頻度が10倍になるので、その処理の分だけ 全体の処理速度は落ちますが、微々たるものです。現在のところ、 これによって生じている弊害は 'top' の時間表示がおかしくなった ことのみで、安定性には変化はみられていません。

なお、当然、ソースを変えただけではだめで、カーネルの再コンパイルが 必要です。


実験・考察

さて、どのくらいきれいに周期実行ができるかどうかを、ここで 検証しておきましょう。上で「確率が非常に高い」などと曖昧な 表現だったのは、この方法はあくまで「簡単にそこそこの性能」を 目的にしたものであって「完璧」な性能は出ない、ということにあります。 それでも、私としてはロボット制御には十分だとおもっています。 実際にデータをご覧の上、御評価下さい。

実験は周期測定用のダミー処理入りプログラムを つかっています。パソコンは Celeron 462MHzで、X を立ち上げて、 kterm や mule がいくつか起動された状態、Linux は 2.2.10 を 上記 HZ を 1000 にしたものです。
処理は適当に三角関数を計算し、ランダムに処理量が変動します。 周期性を保つ部分には、処理にかかった時間から適切な休眠時間を 割り出すような処理をいれてあります。目標周期は5msecで1万周期の データを示します。

このように、nice の設定やコンピュータで稼働中のプログラムにも 大きく依存しますが、1万回に数回レベルで1タイマ割り込み周期くらい 遅延が発生します(10msec周期の標準的なLinuxの場合、20msecを指定 しても、30になることがあります)。これがどう影響するかは処理内容次第と言えると おもいます。PID制御で時間間隔が変わると困る、ということもあると 思いますが、現在時間を細かく知る方法もあるので、それをもとに サンプリング間隔に依存するパラメータ(D,Iゲイン)を修正すれば、 この程度ならそれほど影響はないとおもいます。また、よほどきれいな 環境で理想的な制御をするのでなければ、むしろ、外乱の方が大きな 影響になると、私は考えています。


補足:cyclic.c

非RT Linux によるロボット制御
熊谷正朗/くまがいまさあき/Masaaki Kumagai
kumagai@emura.mech.tohoku.ac.jp