2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Raspberry Pi 3 B+ で、キーボードを押していない状態を、どーしても取りたかった 1

Last updated at Posted at 2019-04-30

キーを押していないのが取りたい!!

作成中のカムプログラムロボット(以降カムロボ)を、キーボード操作したくて試行錯誤してました。
なんとか、カーソルキーの上下左右を押したときに、前進、更新、左旋回、右旋回ができるようにできたのですが、何も押していないときはストップさせたかった。

でもこれ、な~んにも押していないという状態が取れず、スペースキーを押したらストップさせるようにしました。
これでもいいはいいんですが、どうしてもキーを離したらストップさせたかった!!

やりたかったこと

キー入力についてこんなことをしたかった。

No. 動作 取りたい状態
1 どこかのキーを押した 押したキーのコード
2 そのキーを押し続けた 押し続けているという状態
3 キーを離した 何も押していないという状態

キーコードの取得は readchar モジュールを使いました。
結果、1と2については、入力されたキーコードと、過去に入力されたキーを比較することで実現できたのですが、3の状態が取れなかった。。:sob:

3の状態が取れないのは readcharモジュールのreadchar.readkey() は、キー入力があるまで待ち状態になるためで(当たり前なので悪いわけでは無い)、押していないという状態を取ることができませんでした。

色々と試してできそうな目処が立ったのでまとめてみました。

OS環境

Raspberry Piの OSはRaspbian 9.8になります。

pi@raspberrypi:~ $ lsb_release -a
No LSB modules are available.
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux 9.8 (stretch)
Release:        9.8
Codename:       stretch

readchar のインストール

sudo pip3 install readchar

python3に対応した readcharがほしかったので、上記のようにインストールしました。

キー入力プログラム(テスト版)

テストで利用したキー入力プログラムです。
実行を停止する場合は、'q' を押してください。Ctrl+c でも止まりません。
結局、readchar.readkey() の部分をThread化して対応しました。

thread_readkey_00.py
import time
import threading

from readchar import readkey, key as inchar

class CheckKey(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.Key = ''
        self.KeyTime = 0.0

    def run(self):
        while(True):
            self.Key = readkey()
            self.KeyTime = time.time()

            if self.Key == 'q':
                break

InputKey = CheckKey() 
InputKey.start()

BeforeKey = ''
BeforeTime = 0.0

while(True):
    if(InputKey.Key == 'q'):
        break
    elif(InputKey.Key == BeforeKey and InputKey.KeyTime != BeforeTime):
        print("Same the key",end="\r\n")
        BeforeTime = InputKey.KeyTime
    elif(InputKey.Key == BeforeKey and InputKey.KeyTime == BeforeTime):
        print("No press the key",end="\r\n")
    elif(InputKey.Key != BeforeKey):
        print("Change the key",end="\r\n")
        BeforeKey = InputKey.Key
        BeforeTime = InputKey.KeyTime

    time.sleep(1)

class CheckKey()で、入力されたキーコードと入力時間を取得して、保存します。
入力された時間を保持するのが重要。

    def run(self):
        while(True):
            self.Key = readkey()
            self.KeyTime = time.time()

キーボード入力用のクラスを生成して、スレッドとして入力待ちの状態を作ります。

InputKey = CheckKey() 
InputKey.start()

前回取得したキーコードと、その入力時間を、メイン側で保持して、キー入力スレッド内の入力キーコードと入力時間を比較して、状態を特定します。

BeforeKey = ''
BeforeTime = 0.0

while(True):
    if(InputKey.Key == 'q'):
        break
    elif(InputKey.Key == BeforeKey and InputKey.KeyTime != BeforeTime):
        print("Same the key",end="\r\n")
        BeforeTime = InputKey.KeyTime
    elif(InputKey.Key == BeforeKey and InputKey.KeyTime == BeforeTime):
        print("No press the key",end="\r\n")
    elif(InputKey.Key != BeforeKey):
        print("Change the key",end="\r\n")
        BeforeKey = InputKey.Key
        BeforeTime = InputKey.KeyTime

    time.sleep(1)

こんな感じで状態を判断しています。

No. 動作 取りたい状態
1 どこかのキーを押した 入力されたキーと前回入力されたキーが違う(判断式:InputKey.Key != BeforeKey
2 そのキーを押し続けた 入力されたキーと前回入力されたキーが同じで、それぞれの入力時間が違う(判断式:InputKey.Key == BeforeKey and InputKey.KeyTime != BeforeTime
3 何も押していない 入力されたキーと前回入力されたキーが同じで、それぞれの入力時間が同じ(判断式:InputKey.Key == BeforeKey and InputKey.KeyTime == BeforeTime

readchar.readkey() が入力待ちのときは、キーが押された時間も変わらない。この状態を利用してキーが押されていない、という状態を判断しています。

まだ修正の余地あり。。

実行結果は下記のとおりです。

実行結果
$ python3 ./thread_readkey_00.py 
No press the key
No press the key
No press the key
Change the key
Same the key
Same the key
Same the key
No press the key
Change the key
Same the key
Same the key
Change the key
Same the key
Same the key
No press the key
No press the key
Change the key
Same the key
Same the key
No press the key
No press the key

カーソルキーを押し続けると、"Same the key" で、何も押していないと、"No press the key" になりました。

想定通りの動作になったのですが、最終行のtime.sleep(1) を、time.sleep(0.1) にすると微妙な動作をすることが判明。

time.sleep(0.1)にしたあとの実行結果
Same the key
Same the key
Same the key
Same the key
Change the key
No press the key
No press the key
No press the key
No press the key
Same the key
Same the key
No press the key
No press the key
No press the key
No press the key

別のカーソルキーを押して、そのまま押し続けた後に、"No press the key" と何も押していないという状態が続き、その後 "Same the key" となり、離すと "No press the key"となります。

このままだと、変更後に停止し、同じキーが押されていると認識されて、停止状態のままになってしまいます。残念ながらまだ修正の余地がある状態です。

カムロボは動作が遅いので、それほどシビアな判定はいらないのですが、せめて0.5秒程度のタイムラグでキー状態が取れるよう修正を行っています。
修正が終わりましたら、また改めてアップしたいと思います。

余談

キー入力プログラムの下の print() 不思議に思った方もいるかもしれません。

print("Same the key",end="\r\n")

この部分の end="\r\n" を除くいたり、end="\n" にすると、ターミナルへの出力が改行されないで、謎の空白を伴って出力されます。

$ python3 ./thread_readkey.py
No press the key
                No press the key
                                No press the key
                                                No press the key
                                                                Change the key
                                                                              No press the key
                                                                                              No press the key
        Same the key
                    Same the key
                                Same the key
                                            Same the key
                                                        No press the key
                                                                        Change the key
                                                                                      Same the key
                                                                                                  Same the key
        No press the key
                        No press the key

リダイレクトして、ファイル出力すると崩れていない。

$ python3 ./thread_readkey.py > aaa
$ cat aaa
No press the key
No press the key
No press the key
No press the key
Change the key
Same the key
Change the key
Same the key
Change the key
Change the key
Same the key
No press the key

キャレッジリターンで行頭に戻していない感じに見えたので、end="\r\n" を追加したら問題なくなりました。

$ python3 ./thread_readkey.py
No press the key
No press the key
No press the key
Change the key
Same the key
Same the key
Same the key
Same the key
No press the key
Change the key
Same the key
Same the key
Same the key
No press the key
No press the key
No press the key
No press the key
No press the key

どこのバグなのかわかりませんが、とりあえず治ったので良しとして作業を進めます。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?