2025年2月9日日曜日

ロータリーエンコーダの読み取り改善を目指して

ロータリーエンコーダは、回したときに状態誤検知が発生することがある。
原因としては以下が考えられる。
①:読み込み間隔が長いため状態変化の取りこぼしが発生していること
②:状態変化時のバタつきを検知していること

前回は、以下のように状態読み取り間隔を十分長くとる(0.01秒間隔)ことにより、②の誤検知頻度を低くすることにしてみた。

import digitalio
import board
import time

s1 = digitalio.DigitalInOut(board.GP17)
s1.direction = digitalio.Direction.INPUT
s1.pull = digitalio.Pull.UP
s2 = digitalio.DigitalInOut(board.GP16)
s2.direction = digitalio.Direction.INPUT
s2.pull = digitalio.Pull.UP
n_s1=o_s1=s1.value
n_s2=o_s2=s2.value
while True:
    n_s1 , n_s2= s1.value , s2.value
    if(n_s1 != o_s1 or n_s2 != o_s2) :
        if n_s1 ^ o_s2 == 0 :
	        print("UP")
        else:
	        print("DOWN")
        o_s1,o_s2=n_s1,n_s2
    time.sleep(0.01)
    
このとき、低速で動かすとほとんど誤検知が発生しないが、高速で動かすと誤検知が複数回連続で見られる。
これは、高速操作時の誤検知は①で低速時の誤検知は②で、②は読み取り間隔が長いためほとんど遭遇しないのだと思う。

上のプログラムのtime.sleep行をコメントアウトすると、ゆっくり動かしても高速に動かしても誤検知が見られる。
連続した誤検知はたまにしか発生しないが、3行同時に変化が表示されることがある。
こちらは、①は発生していないが、たまに②が3回連続で発生していると思う。


まずは、timesleepを小さい量から徐々に大きくし、低速回転時にたましか誤検知が発生せず連続誤検知が1回のみのtimesleep値を探す。

0.00001とした場合、操作途中に12回連続でUP/DOWNを繰り返すことがある。→この間隔ではバタつきが12回分以上は発生していることがある。
 0.0001とした場合、操作途中に 4回連続でUP/DOWNを繰り返すことがある。→この間隔ではバタつきが4回以上は発生していることがある。
  0.001とした場合、操作途中に 2回連続でUP/DOWNを繰り返すことがある。→この間隔ではバタつきが2回以上は発生していることがある。
  0.002とした場合、操作途中に 1回連続でUP/DOWNを繰り返すことがある。→この間隔ではバタつきが1回以上は発生していることがある。

というわけで、0.002秒を採用し、運悪く誤検知のタイミングで読み取ってしまった場合に備え、最初のプログラムを以下のように少し加工してみた。


import digitalio
import board
import time

s1 = digitalio.DigitalInOut(board.GP17)
s1.direction = digitalio.Direction.INPUT
s1.pull = digitalio.Pull.UP
s2 = digitalio.DigitalInOut(board.GP16)
s2.direction = digitalio.Direction.INPUT
s2.pull = digitalio.Pull.UP
n_s1=o_s1=s1.value
n_s2=o_s2=s2.value
while True:
    nn_s1 , nn_s2= s1.value , s2.value
    if (nn_s1 != n_s1 or nn_s2 != n_s2) :
        n_s1,n_s2 =nn_s1,nn_s2
    elif(n_s1 != o_s1 or n_s2 != o_s2) :
        if n_s1 ^ o_s2 == 0 :
	        print("UP")
        else:
	        print("DOWN")
        o_s1,o_s2=n_s1,n_s2
    time.sleep(0.002)


加工した部分time.sleepの値の他に、以下の部分を
    n_s1 , n_s2= s1.value , s2.value
    if(n_s1 != o_s1 or n_s2 != o_s2) :
を以下に変更したこと。
    nn_s1 , nn_s2= s1.value , s2.value
    if (nn_s1 != n_s1 or nn_s2 != n_s2) :
        n_s1,n_s2 =nn_s1,nn_s2
    elif(n_s1 != o_s1 or n_s2 != o_s2) :


変更の狙いは以下が成り立つとして、
・0.002秒の間隔なら2回目の読み取り時には正しい読み取りができているはず。
・人の操作は、0.002x2よりも十分遅い。(間隔の2回分の時間が開いたとしても、人の次の操作と誤検知期間よりは短い。)
変更内容は、
0.002秒間隔でロータリーエンコーダを読み取るが、状態変更後の初回の読み取り時には状態変更を受け入れず、
次の読み取りで初回読み取りと同じ値であれば、先の読み取りは正常として状態変更を受け入れる。
次の読み取りで初回読み取りと違う値であれば、先の読み取りは誤検知として読み飛ばしたうえで今回の読み取りを初回読み取りとする。

ただ、ごくたまに誤検知をする。
原因はわからないが、以下かなぁ。
・状態変更後の誤検知期間の時間長は一定(0.002秒)ではないのか
・ダイヤルを次の目盛りまで回す操作が0.004秒よりも速いのか
・読み取り頻度が短すぎるのか
・そのほかの要因なのか

やってないのでわからないのだけど、0.01μFのセラミックコンデンサを並列つなぎすると劇的に改善するものなのかな?

0 件のコメント: