はじめに
最近、Arduinoやラズパイ等でいろいろな時計を作成するのにはまっています。
サーボを動かしたり、7SEGを表示させたりと方法は様々ですが、その処理命令をsleep関数やdelay関数等を使って繰り返し実行していました。
複数の時計を作成して並べてながめていてようやく気がついたのですが、それぞれの秒の桁のカウント(もしくは秒針の運針)の間隔が微妙にずれていました。
よく考えるとあたりまえなのですが、sleepやdelayで次の実行まで待っても、実際の処理にかかる時間が加算されるため、実行間隔は
sleepやdelayで処理を待つ時間 + 実際の処理にかかる時間
ということになります。
当初は実際の処理にかかる時間を計測してsleep,delayで待つ時間を減算していましたが、処理時間も常に一定ではなかったりと限界を感じたため、根本的な対処を考えました。
テンプレートプログラム
シェルスクリプト
#!/bin/sh
interval=0.5 # 秒単位で実行周期を設定
while :
do
next_time=$(echo "scale=3; `date +%s.%3N` + $interval" | bc ) # 次回実行時刻を取得
# 処理(interval以下で終わるように設定する必要あり)
sleep $(echo "scale=3; $next_time - `date +%s.%3N`" | bc ) # 次回実行時刻まで待つ
done
python
from time import time
from time import sleep
interval = 0.5 # 秒単位で実行周期を設定
while True:
next_time = round((time() + interval),1) # 次回実行時刻を取得
# 処理(interval以下で終わるように設定する必要あり)
sleep( next_time - time() ) # 次回実行時刻まで待つ
Arduino IDE
void setup() {
last_time = 0; // 前回実行時刻を初期化
interval = 500; // ミリ秒単位で実行周期を設定
}
void loop() {
unsigned long current_time = millis(); // 現在時刻を取得
if ((current_time - last_time) >= interval) { // 前回実行時刻から実行周期以上経過していたら
// 処理(interval以下で終わるように設定する必要あり)
last_time = current_time; // 前回実行時刻を現在時刻で更新
}
}
課題
pythonとArduino IDEではそこそこ正確なのですが、シェルスクリプトがどうしても指定した実行周期よりも15/1000秒程度間隔が長くなってしまいます。
bcやdateの処理時間が原因なのかなと思いいろいろなスペックのLinuxで試してみましたが状況は同じでした。
基本pythonと同じ考え方で作っているつもりなのですが・・・。
解決方法が見つかったら更新します。