質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

1679閲覧

Raspberry Pi上でリアルタイムで指文字を認識させた後に2つのサーボモータを動作させようとしています。

nyororo

総合スコア3

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/07/01 10:24

編集2021/07/02 04:36

前提・実現したいこと

Raspberry Pi上でカメラにリアルタイムで指の本数を数えさせ、それに合わせてサーボモータを動かそうとしています。
指文字の本数を数えさせるプログラム、サーボモータの動作のプログラムはそれぞれうまくいっているのですが、これらを組み合わせるところで苦戦しています。
指文字の認識のプログラムは下に貼っています。
while文がうまく回っていないのではないかと考えていますが、ここ一週間くらい調べてもどうにもならずここへ質問に来ました。

発生している問題・エラーメッセージ

一瞬でtry-except文を抜けてしまい、動作しません。
print(l)によって最初に読み込まれた画像の指の本数を出し続けてしまいます。

Traceback (most recent call last): File "/usr/lib/python3.7/ast.py", line 35, in parse return compile(source, filename, mode, PyCF_ONLY_AST) File "/home/pi/Desktop/HandServo2.py", line 177 except KeyboardInterrupt: #Ctrl+Cキーが押された ^

本来指文字を数えるプログラムではframeというカメラに入力されている映像とmaskという入力映像をハイ、ローレベルに置き換えた映像の二つが映るはずなのですがどちらも動作しません。tryとwhileを入れ替えて実行してみたところカメラが起動した瞬間のmaskの画像だけ映ります。

また、このプログラムを走らせるとサーボモータがとても痙攣したような動きをします。
何度も制御信号(l)が送られてくるためだと考えているので、lが10回入ったらモータを動かすというようなプログラムに書き換える予定です。

該当のソースコード

python

1import cv2 2import numpy as np 3import math #必要なモジュールをインポート 4import RPi.GPIO as GPIO #GPIO用のモジュールをインポート 5import time #時間制御用のモジュールをインポート 6import sys #sysモジュールをインポート 7cap = cv2.VideoCapture(0) 8 9 10servo_pin1 = 17 #変数"Servo_pin1"に17を格納 11GPIO.setmode(GPIO.BCM) #GPIOのモードを"GPIO.BCM"に設定 12GPIO.setup(servo_pin1, GPIO.OUT) #GPIO17を出力モードに設定 13 14servo_pin2 = 27 #変数"Servo_pin2"に27を格納 15GPIO.setup(servo_pin2, GPIO.OUT) #GPIO27を出力モードに設定 16 17#PWMの設定 18servo1 = GPIO.PWM(servo_pin1, 50) #GPIO.PWM1(ポート番号, 周波数[Hz]) 19servo2 = GPIO.PWM(servo_pin2, 50) #GPIO.PWM2(ポート番号, 周波数[Hz]) 20 21servo1.start(0) 22print('servo1_setup') 23servo2.start(0) 24print('servo2_setup') 25 26while(1): 27 try: 28 29 #an error comes if it does not find anything in window as it cannot find contour of max area 30 #therefore this try error statement 31 ret, frame = cap.read() 32 frame=cv2.flip(frame,1) 33 kernel = np.ones((3,3),np.uint8) 34 35 #define roi which is a small square on screen 36 roi=frame[100:300, 100:300] 37 38 39 cv2.rectangle(frame,(100,100),(300,300),(0,255,0),0) 40 hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) 41 42 43 44 # range of the skin colour is defined 45 lower_skin = np.array([0,20,70], dtype=np.uint8) 46 upper_skin = np.array([20,255,255], dtype=np.uint8) 47 48 #extract skin colur image 49 mask = cv2.inRange(hsv, lower_skin, upper_skin) 50 51 52 53 #extrapolate the hand to fill dark spots within 54 mask = cv2.dilate(mask,kernel,iterations = 4) 55 56 #image is blurred using GBlur 57 mask = cv2.GaussianBlur(mask,(5,5),100) 58 59 60 61 #find contours 62 63 #If the version is "open cv4", the number of returns is 2. image,contents..." I changed it from 64 contours,hierarchy= cv2.findContours(mask,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) 65 66 #find contour of max area(hand) 67 cnt = max(contours, key = lambda x: cv2.contourArea(x)) 68 69 #approx the contour a little 70 epsilon = 0.0005*cv2.arcLength(cnt,True) 71 approx= cv2.approxPolyDP(cnt,epsilon,True) 72 73 74 #make convex hull around hand 75 hull = cv2.convexHull(cnt) 76 77 #define area of hull and area of hand 78 areahull = cv2.contourArea(hull) 79 areacnt = cv2.contourArea(cnt) 80 81 #find the percentage of area not covered by hand in convex hull 82 arearatio=((areahull-areacnt)/areacnt)*100 83 84 #find the defects in convex hull with respect to hand 85 hull = cv2.convexHull(approx, returnPoints=False) 86 defects = cv2.convexityDefects(approx, hull) 87 88 # l = no. of defects 89 l=0 90 91 #code for finding no. of defects due to fingers 92 for i in range(defects.shape[0]): 93 s,e,f,d = defects[i,0] 94 start = tuple(approx[s][0]) 95 end = tuple(approx[e][0]) 96 far = tuple(approx[f][0]) 97 pt= (100,180) 98 99 100 # find length of all sides of triangle 101 a = math.sqrt((end[0] - start[0])**2 + (end[1] - start[1])**2) 102 b = math.sqrt((far[0] - start[0])**2 + (far[1] - start[1])**2) 103 c = math.sqrt((end[0] - far[0])**2 + (end[1] - far[1])**2) 104 s = (a+b+c)/2 105 ar = math.sqrt(s*(s-a)*(s-b)*(s-c)) 106 107 #distance between point and convex hull 108 d=(2*ar)/a 109 110 # apply cosine rule here 111 angle = math.acos((b**2 + c**2 - a**2)/(2*b*c)) * 57 112 113 114 # ignore angles > 90 and ignore points very close to convex hull(they generally come due to noise) 115 if angle <= 90 and d>30: 116 l += 1 117 cv2.circle(roi, far, 3, [255,0,0], -1) 118 119 #draw lines around hand 120 cv2.line(roi,start, end, [0,255,0], 2) 121 122 123 l+=1 124 125 #display corresponding gestures which are in their ranges 126 font = cv2.FONT_HERSHEY_SIMPLEX 127 128 if l==1: 129 if areacnt<2000: 130 cv2.putText(frame,'Put hand in the box',(0,50), font, 2, (0,0,255), 3, cv2.LINE_AA) 131 else: 132 if arearatio<12: 133 cv2.putText(frame,'0',(0,50), font, 2, (0,0,255), 3, cv2.LINE_AA) 134 135 else: 136 cv2.putText(frame,'1',(0,50), font, 2, (0,0,255), 3, cv2.LINE_AA) 137 servo1.ChangeDutyCycle(2.0) 138 time.sleep(0.5) 139 servo1.ChangeDutyCycle(7.0) #水平方向になる 140 time.sleep(3) 141 142 elif l==2: 143 cv2.putText(frame,'2',(0,50), font, 2, (0,0,255), 3, cv2.LINE_AA) 144 servo1.ChangeDutyCycle(12.0) 145 time.sleep(0.5) 146 servo1.ChangeDutyCycle(7.0) 147 time.sleep(0.5) 148 149 150 elif l==3: 151 if arearatio<27: 152 cv2.putText(frame,'3',(0,50), font, 2, (0,0,255), 3, cv2.LINE_AA) 153 servo2.ChangeDutyCycle(2.0) 154 time.sleep(0.5) 155 servo2.ChangeDutyCycle(7.0) 156 time.sleep(0.5) 157 else: 158 cv2.putText(frame,'ok',(0,50), font, 2, (0,0,255), 3, cv2.LINE_AA) 159 160 elif l==4: 161 cv2.putText(frame,'4',(0,50), font, 2, (0,0,255), 3, cv2.LINE_AA) 162 servo2.ChangeDutyCycle(12.0) 163 time.sleep(0.5) 164 servo2.ChangeDutyCycle(7.0) 165 time.sleep(0.5) 166 167 elif l==5: 168 cv2.putText(frame,'5',(0,50), font, 2, (0,0,255), 3, cv2.LINE_AA) 169 170 elif l==6: 171 cv2.putText(frame,'reposition',(0,50), font, 2, (0,0,255), 3, cv2.LINE_AA) 172 173 else : 174 cv2.putText(frame,'reposition',(10,50), font, 2, (0,0,255), 3, cv2.LINE_AA) 175 176 cv2.imshow('mask',mask) 177 cv2.imshow('frame',frame) 178 179 except KeyboardInterrupt: #Ctrl+Cキーが押された 180 servo1.stop() #サーボモータ1を止める 181 servo2.stop() #サーボモータ2を止める 182 GPIO.cleanup() #GPIOをクリーンアップ 183 sys.exit() #プログラムを終了 184 cv2.destroyAllWindows() 185 cap.release() 186 187 188 189 190 191

試したこと

while文の手前でGPIOのセットアップ、try文の終盤のif, elif分の中にサーボモータの動作設定を行うと動かなくなります。
servo1.start の下にprintを入れてみたところ、startは一度のみしか適用されていないことが分かりました。
やはりカメラの映像が連続で取り込まれず最初の一瞬のみしか入っていないのが問題です。
whileがうまくループできていないのではないかと考えています。

字数制限にかかったので必要でしたらコメントの返信で指の本数を数えるプログラムを記載します。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

except KeyboardInterrupt: #Ctrl+Cキーが押された

^

IndentationError: unexpected unindent

このエラーは、内容にあるとおり、except文のインデントがtryと合っていないために起きているエラーです。
少なくとも文法的に正しくなければ動作しません。

これを直しても、なお動作しないということであれば、修正後のソースと問題内容で質問を修正しましょう。


「指文字の本数を数えさせるプログラム、サーボモータの動作のプログラムはそれぞれうまくいっている」ということですが、それらを混ぜてしまっていることでうまく動かなくなってしまっているのではないでしょうか。
自分でやるのであれば、

  • 指を数える処理を関数化する。
    例えば、本数が判定できたら本数を返すようにする
  • 引数によってサーボの位置を変更する関数を作る。
  • それらを適切につなぎ合わせる処理をつくる

のようにします。こうすることでどこに問題があるのかわかりやすくなります。
また、サーボのような外部のデバイスの制御では、適切にスリープを入れることも必要です。高速にON/OFFをしてしまうと結局動いていないように見えるなどです。
コードにはスリープが入っているようですがらそのあたりは考慮しているのかもしれませんが、原因かもしれません。

投稿2021/07/01 10:49

編集2021/07/02 05:48
TakaiY

総合スコア13847

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nyororo

2021/07/01 10:56

本当に情けないミスをしていましたね、、ありがとうございます。 ただいま帰宅中で学校にラズパイを置いてきてしまったので、明日インデントを修正し直して実行します。
nyororo

2021/07/02 04:39

インデントがずれていた部分を直してきました。プログラムも修正しています。 動作はするのですが、カメラの映像が連続で読み込まれず、最初に一瞬入力された画像の処理しか行ってくれません。
nyororo

2021/07/02 06:01

指を数える処理を関数化、してみようと思います。 本数が判定できると指の本数がlに格納され、print(l)で出力されるようにプログラムを書いています。因数(l)によりサーボの位置を変更するプログラムを入れたところ正常に動作しなくなったので今困っているところでした。 サーボモータはあまりスリープを長くすると動作が不安定になるので、因数が一回で動くのではなく、10回入力されたら動かすというような変更を加えたいと考えています。ご指摘いただいたようにこれを改善すればサーボモータが痙攣したような動きをしているのは解決できるのではないかと考えています。 ですが、やはりそれ以前にカメラの映像が入力されなくなったことが一番気になっています。。。
TakaiY

2021/07/02 06:21

> サーボモータはあまりスリープを長くすると動作が不安定になる そうですね。 サーボは入力値を常に参照してそれに合せて位置を決めるようにできているので、制御側は定期的に参照位置を送信している必要がありますね。 ということを考えると、サーボの制御以外のことをするのであれば、サーボの制御は別スレッドなどにして、定常的に情報を流せるようにする必要があるかもしれません。 上手く情報が取れるようになってからの話ではありますが。
nyororo

2021/07/11 07:56 編集

すいません、送信したつもりができていませんでした、、、 TakaiY様がおっしゃったとおりサーボの制御を別枠にする方法でうまくいきました。具体的には今までCPUでPWM制御を行うソフトウェアPWMで制御していましたがCPUから独立したクロックで制御するハードウェアPWMを利用したところかなり動作が安定するようになりました。これに加えて因数が複数回入力されたのちにモーターを動かす、というようにカウンタを設けてプログラムを変更しました。 また、プログラムが一瞬で抜けてしまう理由として、モータの制御をプログラムに組み込んだ際にwaitkeyをつけ忘れていたことが問題でした。 この二点で問題が解決しました。アドバイスありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問