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

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

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

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

Raspberry Pi

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

Q&A

解決済

3回答

3920閲覧

不定期に「cv2.error:~~~~cvtColor」が生じます。

pythonnoob1

総合スコア18

OpenCV

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

Raspberry Pi

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

0グッド

0クリップ

投稿2020/11/12 08:42

編集2020/11/17 07:38

環境

raspberry pi 3 B+
python
open cv (バージョンは忘れてしまいましたが、新しめのやつです)
pyqt5 (バージョンは忘れてしまいましたが、新しめのやつです)

###目標
自律移動ロボットを製作しています。
GUIを作成し、GUI上でボタンを押すことでロボットを動かす仕組みです。
ロボットは搭載されたカメラから環境認識を行い自律的にモーターの出力を変えます。

###状態
カメラを起動し、モータを動かすことが出来ています。
しかし、ちょくちょく「cv2.error」が出てきます。

エラー文は以下になります。

cv2.error: OpenCV(3.4.3) /Users/travis/build/skvark/opencv-python/opencv/modules/imgproc/src/color.cpp:181: error: (-215:Assertion failed) !_src.empty() in function 'cvtColor'

これはよくあるエラーだそうで、画像が認識できていないということだそうです。
しかし、分からないことに、同じプログラムでも、調子が良いと出てこず、調子が悪いとすぐに出てきます。何が原因かもわかりません。
ラズパイの動作が重くなると画像処理が出来なくなり、画像が読み込めなくなるのでしょうか?
どうかご教授を頂けますと幸いです。

また、現在の私の環境上返信に時間を要してしまいます。質問をしている身分で大変申し訳ありませんがよろしくお願いします。

以下に作成したコード(必要そうなもの)を載せます。
###作成したコード(必要そうなもの)

python

1#GUI.py 2import sys 3from PyQt5.QtWidgets import * 4from PyQt5.QtGui import * 5from PyQt5.QtCore import * 6import subprocess 7import time 8import numpy as np 9import cv2 10import math 11import RPi.GPIO as GPIO 12 13import motor 14import Gyro 15import Image_Processing 16 17 18class Tab1Widget(QWidget): 19 20 def __init__(self): 21 super().__init__() 22 self.title = "自律移動" 23 self.left = 50 24 self.top = 50 25 self.width = 1300 26 self.height = 1200 27 self.initUI() 28 self.counter = 0 29 30 def initUI(self): 31 32 super(Tab1Widget, self).__init__() 33 self.cap = cv2.VideoCapture(0) 34 35 self.cap.set(cv2.CAP_PROP_FRAME_WIDTH,320) 36 self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT,240) 37 self.cap.set(cv2.CAP_PROP_FPS,10) 38 39 self.camera = 0 40 self.auto = 0 41 42 self.Pitch=0 43 self.Roll=0 44 45 46 self.view1 = QGraphicsView() 47 self.scene1 = QGraphicsScene() 48 49 btn1 = QPushButton("カメラSTART", self) 50 btn2 = QPushButton("カメラSTOP", self) 51 btn3 = QPushButton("自律移動START", self) 52 btn4 = QPushButton("自律移動STOP", self) 53 54 btn1.clicked.connect(self.camera_on) 55 btn2.clicked.connect(self.camera_off) 56 btn3.clicked.connect(self.auto_move_on) 57 btn4.clicked.connect(self.auto_move_off) 58 59 #GUIレイアウト(省略) 60 61 self.show() 62 63 64 65 def camera_on(self): 66 self.camera = 1 67 68 #サーボモータ(正面カメラ)のsetup 69 Servo_pin=18 70 GPIO.setmode(GPIO.BCM) 71 GPIO.setup(Servo_pin, GPIO.OUT) 72 self.Servo = GPIO.PWM(Servo_pin, 50) 73 self.Servo.start(0) 74 75 self.camera_set() 76 77 timer = QTimer(self.view1) 78 timer.timeout.connect(self.camera_set) 79 timer.start(300) 80 81 82 def camera_off(self): 83 self.camera = 0 84 85 def camera_set(self): 86 #Gyro.pyからデータ取得 87 self.Pitch, self.Roll = Gyro.GyroSensor(self.Pitch, self.Roll) 88 89 #テキストに出力 90 pitch = str(round(self.Pitch)) 91 self.textbox7.setText(pitch + "°") 92 93 roll = str(round(self.Roll)) 94 self.textbox8.setText(roll + "°") 95 96 ret, cv_img = self.cap.read() 97 98 if ret == False: 99 return 100 101 def servo_angle(angle): 102 duty = 2.5 + (12.0 - 2.5) * (angle + 90) / 180 103 self.Servo.ChangeDutyCycle(duty) 104 105 servo_angle(round(self.Pitch)) 106 self.Servo.stop() 107 108 height = cv_img.shape[0] 109 width = cv_img.shape[1] 110 center = (int(width/2),int(height/2)) 111 angle = round(self.Roll*(-1)) 112 scale = 1.0 113 trans = cv2.getRotationMatrix2D(center,angle,scale) 114 self.cv_img = cv2.warpAffine(cv_img,trans,(width,height)) 115 116 self.img_src = self.cv_img 117 118 119 cv_img_RGB = cv2.cvtColor(self.cv_img,cv2.COLOR_BGR2RGB) 120 121 122 cv_img_RGB = cv2.resize(cv_img_RGB,(320,240)) 123 124 height, width, dim = cv_img_RGB.shape 125 126 bytesPerLine = dim * width 127 128 self.image = QImage(cv_img_RGB.data, width, height, bytesPerLine, QImage.Format_RGB888) 129 130 self.item = QGraphicsPixmapItem(QPixmap.fromImage(self.image)) 131 132 self.scene.addItem(self.item) 133 134 self.view.setScene(self.scene) 135 136 137 def auto_move_on(self): 138 139 self.auto = 1 140 GPIO.cleanup() 141 motor.init_GPIO() 142 self.pin1 , self.pin2 , self.pin3 , self.pin4 , self.pin5 , self.pin6 = motor.init_GPIO() 143 self.auto_move_set() 144 repeatTime = 10 145 timer = QTimer(self.view1) 146 timer.timeout.connect(self.auto_move_set) 147 timer.start(repeatTime) 148 149 def auto_move_off(self): 150 151 self.auto = 0 152 self.motor_S() 153 GPIO.cleanup() 154 print("cleanup") 155 156 157 def auto_move_set(self): 158 if self.auto == 0 or self.camera == 0: 159 return 160 variable, img_src0, elapsed_time = Image_Processing.I_P(self.auto,self.img_src,self.Roll) 161 162 #モーターを動かすプログラム。画像処理(Image_Processing.py)にて得られた変数(variable)を基にモータの出力を変更 163 self.Duty_R , self.Duty_L = motor.motor_PWM(p_vaiue3,self.pin1 , self.pin2 , self.pin3 , self.pin4 , self.pin5 , self.pin6 ) 164 165 variable = str(round(variable,3)) 166 self.textbox6.setText(variable) 167 elapsed_time = str(round(elapsed_time,3)) 168 self.textbox1.setText(elapsed_time) 169 Duty_R = str(round(self.Duty_R ,1)) 170 self.textbox7.setText(Duty_R) 171 Duty_L = str(round(self.Duty_L,1)) 172 self.textbox8.setText(Duty_L) 173 174if __name__ == "__main__": 175 app = QApplication(sys.argv) 176 ex = Tab1Widget() 177 sys.exit(app.exec_())

python

1#Image_Processing.py 2import numpy as np 3import math 4import cv2 5import time 6import Value 7 8def I_P(auto,img_src,Roll): 9 t1 = time.time() 10 if auto == 0: 11 return 12 F= 0 13 14 15 img_src=cv2.resize(img_src,(180,60)) 16 img_src=img_src[20:60, 20-round(75*math.cos(math.radians(90-Roll))):140-round(75*math.cos(math.radians(90-Roll)))] 17 #HSV変換 18 hsv = cv2.cvtColor(img_src, cv2.COLOR_BGR2HSV) 19 img_src0 = cv2.cvtColor(img_src, cv2.COLOR_BGR2RGB) 20 GREEN_min = np.array([30, 64, 30]) 21 GREEN_max = np.array([90, 255,255]) 22 img_mask = cv2.inRange(hsv,GREEN_min ,GREEN_max) 23 24 neiborhood8 = np.array([[1, 1, 1],[1, 1, 1],[1, 1, 1]],np.uint8) 25 img_erosion = cv2.erode(img_mask,neiborhood8,iterations=2) 26 img_erosion = cv2.erode(img_erosion,neiborhood8,iterations=2) 27 img_dilation = cv2.dilate(img_erosion,neiborhood8,iterations=2) 28 img_dilation = cv2.dilate(img_dilation,neiborhood8,iterations=2) 29 img_dst = cv2.bitwise_and(img_src, img_src, mask=img_dilation) 30 img_dst2 = cv2.bitwise_and(img_src, img_src, mask=img_mask) 31 32 #Value.pyで変数(variable)を計算 33 row_L , row_R ,variable = Value.Get_MotorParameter(img_mask,1 ,F) 34 35 36 t2 = time.time() 37 elapsed_time = t2 - t1 38 39 return variable ,img_src0 ,elapsed_time

ちなみに、エラーはImage_Processing.pyのHSV変換するところ

hsv = cv2.cvtColor(img_src, cv2.COLOR_BGR2HSV)

で毎回生じます。

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2020/11/12 09:53

確かめられないのでコメント欄で対応します。 恐らく ret, cv_img = self.cap.read()の処理で何らかの原因で(過負荷でフレームが落ちて?カメラがタイムアウトして?)画像が拾えていない、其のあとに拾えていない画像はHSVにできていない、だと思います。 if ret is True:のような感じで画像が拾えたときだけ処理をするような安全策を仕込んで様子を見られますか?
pythonnoob1

2020/11/12 10:21

fourteenlength様、返信ありがとうございます。 実際に実行できるのは明日になりそうです。明日実行し、また結果を報告させて頂きます。よろしくお願いします。
pythonnoob1

2020/11/13 06:35

fourteenlength様、本日、様々な可能性を考え実際に実行してみました。 しかし、改善されなかったことをご報告させていただきます。 返信してくださり、誠に有難うございます。
退会済みユーザー

退会済みユーザー

2020/11/14 07:21 編集

少なくともRollの値を0-360で振った時に212~328になった時'img_src.shape (40, 0, 3)'が発生してエラーが起きて止まるとこまで確認しました。回答にはならないのでここに参考まで。 ```Python import numpy as np import cv2 for Roll in range(360): ____print("------------") ____print("Roll:", Roll ) ____img_src = img_src = np.zeros((180,60,3),np.uint8) ____try: ________img_src = img_src[20:60, 20-round(75*math.cos(math.radians(90-Roll))):140-round(75*math.cos(math.radians(90-Roll)))] ________# print("img_src.shape", img_src.shape) ________img_src = cv2.cvtColor(img_src ,cv2.COLOR_BGR2RGB) ____except Exception as err: ________print(err) ```
退会済みユーザー

退会済みユーザー

2020/11/14 07:31 編集

variable, img_src0, elapsed_time = Image_Processing.I_P(self.auto,self.img_src,self.Roll) の後にimg_src0を使う予定はあるのでしょうか?少なくとも今はなさそうですし予定がないのであればimg_src0関連の処理を全部消すのもありかと…。 画像をどう使うのか分かりませんが、 1.クラッシュしなければいい(深追いしない、ただのデバグ用) エラーが起きていたような条件のimg_src0は使う予定はそもそもない、であればゼロにならないように画像の辺の最小値を1にするような前処理(Rollの範囲を制限する前処理)を挟んだらいいと思います。もしくはダミー画像で差し替える等でしょうか。 2.クラッシュも困るし、img_src0をいかなる時も使いたい Rollの値が212~328に収まる条件を探るべきです。
pythonnoob1

2020/11/14 07:39

fourteenlength様、ありがとうございます!!大変参考になりました。今回のエラーの原因であるということを考慮し、参考にさせて頂きます!! img_src0は使わないので、処理を消しときます。ありがとうございます!!
退会済みユーザー

退会済みユーザー

2020/11/14 07:42 編集

(私なら間違いなく1を選びますが o_0 )yuki23さんの考え方は非常に重要なので、個人的には2の方を進んでほしいです。
pythonnoob1

2020/11/14 07:48

fourteenlength様、返信ありがとうございます。どちらの考え方もとても参考にさせて頂いております。色々なことを視野に入れて考えていきたいと思います。ありがとうございます!!
退会済みユーザー

退会済みユーザー

2020/11/17 10:54

このコード(回答欄込み)でエラーは発生しますか?
pythonnoob1

2020/11/17 12:06

fourteenlength様、返信ありがとうございます。 先程、一部修正(yuki23様とのやりとり参照)させて頂きました。モーターは繋いでいませんが、その場合はエラーが出ませんでした?これが、偶然なのかは分かりませんが後日モーターを繋げて実験したいと思います。
guest

回答3

0

ベストアンサー

ret をチェックする処理は既に入っているのでそこじゃないですね

python

1 ret, cv_img = self.cap.read() 2 3 if ret == False: 4 return

おそらく cv2.warpAffine で失敗しているんじゃないかと思いますが、特定はできないので、ログを全部取り、失敗するときとそうでないときの条件を調べるべきです。
当てずっぽうですが、ジャイロセンサーの値が不正なのではと思います。


追記

数値計算プログラミングでは極めて初歩的なことですが、atan 関数は分母が0になると計算できないので、基本的にプログラミングでは使いません。atan2 関数を使います。
次のようにしてみてください。

roll = math.atan(ay/az) * 57.324 ↓ roll = math.atan2(ay, az) * 57.324

追記2

やっと証拠が揃いましたね。
実際に Roll に値を当てはめてみると、
16 < Roll で 20-round(75*math.cos(math.radians(90-Roll))) < 0
となります。
img_src[20:60, 20-round(75*math.cos(math.radians(90-Roll))):140-round(75*math.cos(math.radians(90-Roll)))] の部分に具体的に値の例を当てはめると、img_src[20:60, -1:119] のようなことになります。

ここで、python の配列の範囲指定の文法では、負の数-xを指定すると「後ろからx番目」という意味になります。
img_srcの第2次元のサイズは180なので、-1:119ならば179:119と同じです。
ところが左のインデックスが右のインデックスより大きいとき、python ではエラーにならずに大きさ0の範囲となります。よってimg_srcは空の配列となります。これが問題の直接の原因です。

より根本的な原因としては、上のコードで、Rollの値によっては範囲指定が画像サイズをはみ出してしまうことを設計で考慮していないことが原因です。
はみ出した時にどうすればいいのかは、Value.Get_MotorParameter のあたりを含めたアルゴリズムの設計の問題なので、私からはなんとも言えません。
既にやったように、try-exceptなどで単にスキップするというのも一つの手ではあるかもしれません。

投稿2020/11/12 11:28

編集2020/12/01 12:08
yuki23

総合スコア1448

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

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

pythonnoob1

2020/11/12 11:43

yuki23様、回答ありがとうございます。 初心者で大変申し訳ありませんが、「ログを取る」というのは、具体的にどうすれば良いのでしょうか? ファイルを作成し、書き込みを行うのでしょうか?それとも、print関数でターミナル上に出力するのでしょうか? あと、「ジャイロセンサーの値が不正」というのはどういった時、どういった時に不正の値となるのでしょうか? これに関しては、ジャイロセンサーのプログラムを見ないと分からないと思いますので、使わせて頂いたプログラムのURLを貼らせて頂きます。 https://shizenkarasuzon.hatenablog.com/entry/2019/03/06/163906 こちらのサイト様から、ピッチ角、ロール角を出力させて頂きました。
yuki23

2020/11/12 12:04

> 「ログを取る」というのは、具体的にどうすれば良いのでしょうか? ファイルに保存するのが基本です。 > 「ジャイロセンサーの値が不正」というのはどういった時、どういった時に不正の値となるのでしょうか? 原因を考えようとする姿勢は結構ですが、ジャイロセンサーが原因かそうでないかは、ジャイロセンサーのログを取っていないので、まだ何もわかっていません。可能性の1つとして挙げたまでです。予断を持つのはかえって危険なこともあります。 ロボットはプログラムだけで動いているのではありません。プログラム以外のことは、いつでもどこでも不正な値が入ってくる可能性があるとみなしてプログラミングしなければなりません。センサーの出力は、「プログラム以外のこと」の代表ですので、真っ先に疑いの目を向けました。 他にはカメラもそうですが、これはチェックをしているので可能性は低いだろうと判断しました。しかし本当にカメラが原因でないかどうかは、ログを取らなければ証明はできません。
pythonnoob1

2020/11/13 06:44

yuki23様、返信いただきありがとうございます。 「ログを取る」「ジャイロセンサーの値が不正」の件、大変勉強になりました。 本日、実際にジャイロセンサのみを動かして実験してみました。その際に、上コメントでも貼らせていただきました、ジャイロセンサを使う上で参考にさせていただいたサイト様(https://shizenkarasuzon.hatenablog.com/entry/2019/03/06/163906)のコードの下から4行目である 「roll = math.atan(ay/az) * 57.324」 にて、『分母が「0」ですよ』というエラーが出てしまいました。 ちなみにエラー文は ZeroDivisionError: float division by zero です。これについて、なにか対処法はあるのでしょうか?
pythonnoob1

2020/11/13 06:49

上のコメントの続きを書かせていただきます。 自律移動ロボットの実験もジャイロセンサ単体の実験もGUIを作成し、ファイルにロール角、ピッチ角を書き込ませてみたものの、エラーが起こると、GUIが強制的に閉じられてしまい、エラーした際のデータをとることが出来ません。 しかし、ジャイロセンサ単体でGUIが強制終了するということは、少なくとも今回の問題に影響していると考えてもよろしいのでしょうか?
yuki23

2020/11/13 07:45

atan について追記しました。 GUIが強制的に閉じられたからデータが取れないとは? ファイルにログを保存していないのですか?
pythonnoob1

2020/11/13 11:57

atan2の件、ありがとうございます。 > GUIが強制的に閉じられたからデータが取れないとは? open()とclose()で、ジャイロセンサで得られたピッチ角およびロール角をファイルに書き込みしているのですが、ジャイロセンサでエラーが出た瞬間にプログラムが強制終了(GUI)が停止してしまうので、エラーが発生した時のデータがファイルに書き込まれない、ということです。 > ファイルにログを保存していないのですか? 私の解釈したログの取り方は上記のように、open()、close()でファイルに書き込む方法だったのですが、間違いだったのでしょうか?
yuki23

2020/11/14 02:12

> 私の解釈したログの取り方は上記のように、open()、close()でファイルに書き込む方法だったのですが、間違いだったのでしょうか? ソースコードを見ないとわかりませんが、ジャイロセンサーの読み取りと cvtColor の間にログ出力を入れたが、ログ出力が通る前に強制終了したということですか? (Gyro.py も「ジャイロセンサー単体のGUI」とやらのソースコードも見せてもらってないですが) 0除算でエラーが発生して cvtColor までたどり着いていないなら、直す必要はあるがそれで質問の問題が改善するとは断定できないです。
pythonnoob1

2020/11/14 07:45

yuki23様、返信ありがとうございます。 私は馬鹿でした…cvtColorの後にロール角とピッチ角のログを取っていました…また改めて、やりなして見ます。しかし、次実験できるのが、少し日を跨いでしまいそうです…大変申し訳ございませんが、少し結果をお待ち下さい。 > Gyro.py も「ジャイロセンサー単体のGUI」とやらのソースコードも見せてもらってないですが こちらについても、大変申し訳ございませんが、今手元にPCがない為、送れません。(現在はiPhoneで返信させて頂いております)今しばらくお待ちください。
pythonnoob1

2020/11/16 04:05 編集

yuki23様、 まだ、動作確認はできていないのですが、回答のほうに変更したコード及びジャイロセンサ単体のGUIのコードを載せさせていただきました。 動作確認は、早くて明日になってしまいそうです。質問している身分で大変申し訳ございませんが、少しお待ちください。
pythonnoob1

2020/11/17 07:27

yuki23様、お待たせいたしました。 動作実験を行ってみました。 atan2に変更して行うと、カメラを起動させた時点で、 「TypeError: atan2 expected 2 arguments, got 1」 とエラーが出てしまいます。 また、今回モータにつなぐコードが断線してしまいモータを動かすことが出来ませんでした。モータの電源は入れず、モータの電源が入っている体で「自律移動スタート」ボタンを押し、ロボットを手で押して動かしてみたのですが、特にエラー(cv2.error)は出ませんでした。(単に調子が良かっただけ?) モータが直り次第、再度実験をしたいと思います。
yuki23

2020/11/17 07:46

私の回答のコードをよく読んでください。関数名をatan2に変えるだけではだめですよ。
yuki23

2020/11/17 10:21

モーターを繋がなかった時は発生しなかったということから思うに、モーターから発生するノイズや電源の容量不足といったハードウェア面の問題も、もしかするとあるのかもしれませんね。
pythonnoob1

2020/11/17 11:59

yuki23様、返信ありがとうございます。 > 関数名をatan2に変えるだけではだめ 今、気付きました、、、それが原因であると思います。 > モーターから発生するノイズや電源の容量不足といったハードウェア面の問題も、もしかするとあるのかもしれません また後日、モーターを繋げて実験してみます。その時の挙動をまた、報告させて頂きます。
pythonnoob1

2020/11/19 09:43

yuki23様、大変申し訳ございません。 モーターの不調でもう少し動作確認に時間がかかりそうです。 また、ジャイロセンサの件に関しまして、atan2での動作確認ができ、何度か実験してみたところエラーは出ませんでした。
yuki23

2020/11/20 09:54

私は別に急いでませんので、ゆっくり確実にやっていただいたらいいと思います
pythonnoob1

2020/11/24 07:59

yuki23様、お優しいコメントありがとうございます。 モーターの修復が出来ましたので、動作確認をいたしました。 結果として、atan2に変えてもエラーが発生してしまいましたので、Gyroが原因ではないということでしょうか?
pythonnoob1

2020/11/24 08:08

def camera_set(): の中のコードですが、少し多くのコードが詰まっているように思えるのですが、「これらのコードを処理するのにQTimerの0.3秒じゃ処理できず、結果としてカメラ画像の取得が出来なくなる」ということはあるのでしょうか?
yuki23

2020/11/25 01:09

atan2 の件 時間が経ったので忘れてしまったのでしょうが、atan の問題は cvtColor の問題の前に発生している別の問題でしたよね? だから、atan を直しても cvtColor の問題が直らないのは当たり前です。 > 0除算でエラーが発生して cvtColor までたどり着いていないなら、直す必要はあるがそれで質問の問題が改善するとは断定できないです。
yuki23

2020/11/25 01:13

> 「これらのコードを処理するのにQTimerの0.3秒じゃ処理できず、結果としてカメラ画像の取得が出来なくなる」ということはあるのでしょうか? もちろん考えられますが、だとするとなぜ発生する時としない時があるのか、モーターを駆動した時としない時の違いを説明できませんね? そこを確かめるためにログを取ってもらったと思いますが、ログはどうでしたか?
pythonnoob1

2020/11/26 06:34 編集

yuki23様、ありがとうございます。 ログをジャイロセンサの読み取りとcvtColorの間に入れて見たのですが、特に変わった様子はなく、普通にピッチ角、ロール角を出力していました。 今日行った実験では、Image_Processing.py内のHSV変換する前のコード (img_src=img_src[20:60, 20-round(75*math.cos(math.radians(90-Roll))):140-round(75*math.cos(math.radians(90-Roll)))]) を img_src=img_src[20:60, 20:140] と、計算をしないコードに変えた時、エラーは発生しませんでした。 もしかしたら、ラズパイの計算速度がどのくらいかは分かりませんが、HSV変換直前の画像切り抜きの時点で、計算が間に合わずエラーが出てしまっている可能性があるかも知れません? それか、ロール角が大きすぎる場合、画像切り抜きが画像のサイズからはみ出してしまうため、上手く画像として認識されない可能性があるかも知れません?
yuki23

2020/11/26 12:03

ログを取る処理が動いたかどうかではなく、取ったログにどんな値が記録できたかを聞いてるんですが……
pythonnoob1

2020/11/30 05:29

yuki23様、返信が遅れてしまい大変申し訳ございませんでした。 木曜日に実験を行い、データを手元に保存するのを忘れてしまいました。。。 大変申し訳ございませんが、確認できるのが明日になってしまいます。
pythonnoob1

2020/11/30 05:44 編集

また、yuki23様にご教授いただきたいことがあります。 「毎回、 hsv = cv2.cvtColor(img_src, cv2.COLOR_BGR2HSV) でエラーが生じてしまう。」 ということをお話しさせていただいていると思うのですが、このコードの前に「画像の読み込み判定」を設け、読み込めなければモーターを止め、 def auto_move_set(self):   if self.auto == 0 or self.camera == 0:     return から、また始める。 という対処法は考えられますか? また、考えられる場合、「画像の読み込み判定」はどのように行えば良いのでしょうか? カメラのように ret, cv_img = self.cap.read()   if ret == False:     break で行えるのでしょうか?自分なりに調べてみたのですが、画像の読み込み、表示、保存についてしか出てこず分かりませんでした。
yuki23

2020/12/01 02:31

読み込みで失敗しているのではなくて変換で失敗していると思われるので、同じ値でauto_move_setの先頭に戻っても結果は同じだと思います。ジャイロの新しい値を取るところまで戻らないといけません。つまり、I_Pもauto_move_setも全部飛ばして次のループを待つということです。 具体的には try-catch 文でI_Pを囲み、エラー時にはreturnすれば一応できることはできます。 「エラーが起きたら理由はわからないけどとりあえずスキップさせておけ」というのは、ロバスト性の観点では考慮する価値はありますが、エラーが起きる原因を放置していいのかどうかはご自身の判断です
pythonnoob1

2020/12/01 06:32

yuki23様 try-catch 文の件ありがとうございます。実際に使ったのは try-except文ですが、とりあえずはエラーを起こしながらでもモーターは動きました。一応、私の回答のほうに変更点を書き加えておきます。
pythonnoob1

2020/12/01 07:01

また、今回の実験においてエラーの原因がなんとなくわかったような気がします。 ログを見てみるとエラー発生時のピッチ角またはロール角の値が跳ね上がっていました。 普段ピッチ角は絶対値5前後、ロール角は絶対値10前後なのですが、エラー発生時は絶対値が21や22といった具合になっていました。
yuki23

2020/12/01 07:56

出先からうろおぼえで書いたので間違えました。ごめんなさい >try-except
yuki23

2020/12/01 12:12

回答追記しました。 結局の所、fourteenlength さんが既に指摘した部分ですが、 pythonnoob1 さんが実際にその問題が起こる時に問題が起こる値が Roll に入っていて、問題が起こらないときには入っていないという証拠をログを取ることで確認しない限り、 何か修正をしたとしても「理屈を理解しない当てずっぽうでたまたま発生しなくなった」のではないという根拠は得られませんでした。 無駄に遠回りをさせられたと思われたかもしれませんが、結果的にどういう風に直すにしろ、遠回りをしたおかげで「ジャイロセンサーの不具合では」「モーターがおかしいのでは」「計算時間がかかりすぎているのでは」という懐疑の声に対して、自信を持って「私は確かに原因を特定し適切な方法で修正しました」と答えられるようになりました。
pythonnoob1

2020/12/01 13:19

yuki23様、fourteenlength様、永きに渡り私の質問に対応してくださり大変ありがとうございます。 今回の質問で、エラーをただ解決するだけでなく、ログを取ることの大切さ、様々なことに疑問を持つ大切さなど、様々なことを学ぶことができました。 これも、yuki23様、fourteenlength様のお陰でございます。 本当にありがとうございました。
guest

0

※これは回答ではありません(質問の延長です。)

次に、ジャイロセンサ単体のGUIのコードを示します。

python

1import sys 2from PyQt5.QtWidgets import * 3from PyQt5.QtGui import * 4from PyQt5.QtCore import * 5import subprocess 6import time 7import numpy as np 8import cv2 9import RPi.GPIO as GPIO 10 11import smbus 12import math 13from time import sleep 14 15Servo_pin = 18 16 17 18 19class Tab1Widget(QWidget): 20 21 def __init__(self): 22 super().__init__() ##スーパークラスの"QWidget"を呼び出す 23 self.title = "ジャイロテスト" 24 self.left = 50 25 self.top = 50 26 self.width = 1300 27 self.height = 1200 28 self.initUI() 29 self.counter = 0 30 31 def initUI(self): 32 self.a=0 33 34 btn1 = QPushButton("スタート", self) 35 btn2 = QPushButton("ストップ", self) 36 37 btn1.clicked.connect(self.start ) 38 btn2.clicked.connect(self.stop ) 39 40 self.textbox3 = QLineEdit(self) 41 self.textbox4 = QLineEdit(self) 42 43 44 label2 = QLabel("ジャイロセンサ") 45 label3 = QLabel("ピッチ角") 46 label4 = QLabel("ロール角") 47 48 49 layoutA = QGridLayout() 50 layoutA.addWidget(btn1,0,0) 51 layoutA.addWidget(btn2,0,1) 52 layoutA.addWidget(label3,1,0) 53 layoutA.addWidget(self.textbox3,1,1) 54 layoutA.addWidget(label4,2,0) 55 layoutA.addWidget(self.textbox4,2,1) 56 57 58 layoutB = QVBoxLayout() 59 layoutB.addWidget(label2) 60 layoutB.addLayout(layoutA) 61 62 63 self.setLayout(layoutB) 64 65 self.show() 66 67 68 def start(self): 69 70 self.a = 1 71 72 self.startGyro() 73 74 timer = QTimer(self) 75 timer.timeout.connect(self.startGyro) 76 timer.start(100) 77 78 79 def startGyro(self): 80 if self.a == 0: 81 return 82 83 DEV_ADDR = 0x68 84 85 ACCEL_XOUT = 0x3b 86 ACCEL_YOUT = 0x3d 87 ACCEL_ZOUT = 0x3f 88 TEMP_OUT = 0x41 89 GYRO_XOUT = 0x43 90 GYRO_YOUT = 0x45 91 GYRO_ZOUT = 0x47 92 93 PWR_MGMT_1 = 0x6b 94 PWR_MGMT_2 = 0x6c 95 96 bus = smbus.SMBus(1) 97 bus.write_byte_data(DEV_ADDR, PWR_MGMT_1, 0) 98 99 100 def read_word(adr): 101 high = bus.read_byte_data(DEV_ADDR, adr) 102 low = bus.read_byte_data(DEV_ADDR, adr+1) 103 val = (high << 8) + low 104 return val 105 106 # Sensor data read 107 def read_word_sensor(adr): 108 val = read_word(adr) 109 if (val >= 0x8000): # minus 110 return -((65535 - val) + 1) 111 else: # plus 112 return val 113 114 115 def get_temp(): 116 temp = read_word_sensor(TEMP_OUT) 117 x = temp / 340 + 36.53 # data sheet(register map)記載の計算式. 118 return x 119 120 121 def getGyro(): 122 x = read_word_sensor(GYRO_XOUT)/ 131.0 123 y = read_word_sensor(GYRO_YOUT)/ 131.0 124 z = read_word_sensor(GYRO_ZOUT)/ 131.0 125 return [x, y, z] 126 127 128 def getAccel(): 129 x = read_word_sensor(ACCEL_XOUT)/ 16384.0 130 y= read_word_sensor(ACCEL_YOUT)/ 16384.0 131 z= read_word_sensor(ACCEL_ZOUT)/ 16384.0 132 return [x, y, z] 133 134 ax, ay, az = getAccel() 135 gx, gy, gz = getGyro() 136 137 #print ('{0:4.3f}, {0:4.3f}, {0:4.3f}, {0:4.3f}, {0:4.3f}, {0:4.3f},' .format(gx, gy, gz, ax, ay, az)) 138 Roll = math.atan2(ay,az) * 57.324 #57.324 = 360/2π であり、rad から ° に変換 139 Pitch = math.atan2(-ax , math.sqrt( ay* ay+ az*az ) ) * 57.324 140 141 #pitch = math.atan(-ax / (ay*math.sin(roll) + az*math.cos(roll))) 142 143 #print('{0:4.3f}, {0:4.3f},' .format(pitch, roll)) 144 roll = str(round(self.Roll)) 145 pitch = str(round(self.Pitch)) 146 147 data= open('/home/pi/Desktop/Gyro_GUI/GyroData.txt','w') #open(ファイル名指定, どんな仕事をさせるか) 'a'→同じファイル名のファイルが存在していた時、既存ファイルに追記 148 # 'w'→同じファイル名のファイルが存在していた時、上書き保存 149 data.write( "ロール角:" + roll + "、ピッチ角:" + pitch + "\n") #文字列(str)p_vaiue3を書き込み+改行 150 data.close #ファイルを閉じる 151 152 153 pitch = str(round(Pitch)) 154 self.textbox3.setText(pitch + "°") 155 156 roll = str(round(Roll)) 157 self.textbox4.setText(roll + "°") 158 159 def stop(self): 160 self.a = 0 161 Servo = GPIO.PWM(Servo_pin, 50) 162 Servo.stop() 163 GPIO.cleanup(Servo_pin) 164 print("CLEAN") 165 166if __name__ == "__main__": 167 app = QApplication(sys.argv) 168 ex = Tab1Widget() 169 sys.exit(app.exec_())

投稿2020/11/16 04:01

編集2020/11/17 12:03
pythonnoob1

総合スコア18

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

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

0

※これは回答ではありません(質問の延長です。)

まず、Gyro.pyを含めたコードを示します。
変更1回目
GUI.pyとImage_Processing.pyについて img_src0関係を削除しました。
変更2回目
Gyro.pyに関しましては消させていただきます。(文字数確保のため)
Image_Processing.pyに関しまして、try-except文の追加、及びhsv変換前にロール角ピッチ角のログをとるようにしました。
なお、すべてのコードにおいて関係のなさそうなところを省略させていただきます。

python

1#GUI.py 2import sys 3from PyQt5.QtWidgets import * 4from PyQt5.QtGui import * 5from PyQt5.QtCore import * 6import subprocess 7import time 8import numpy as np 9import cv2 10import math 11import RPi.GPIO as GPIO 12 13import motor 14import Gyro 15import Image_Processing 16 17 18class Tab1Widget(QWidget): 19 20 def __init__(self): 21 super().__init__() 22 self.title = "自律移動" 23 self.left = 50 24 self.top = 50 25 self.width = 1300 26 self.height = 1200 27 self.initUI() 28 self.counter = 0 29 30 def initUI(self): 31 32 super(Tab1Widget, self).__init__() 33 self.cap = cv2.VideoCapture(0) 34 35 self.cap.set(cv2.CAP_PROP_FRAME_WIDTH,320) 36 self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT,240) 37 self.cap.set(cv2.CAP_PROP_FPS,10) 38 39 self.camera = 0 40 self.auto = 0 41 42 self.Pitch=0 43 self.Roll=0 44 45 46 self.view1 = QGraphicsView() 47 self.scene1 = QGraphicsScene() 48 49 #GUIレイアウト(省略) 50 51 self.show() 52 53 def camera_on(self): 54 self.camera = 1 55 56 self.camera_set() 57 timer = QTimer(self.view1) 58 timer.timeout.connect(self.camera_set) 59 timer.start(300) 60 61 62 def camera_off(self): 63 self.camera = 0 64 65 def camera_set(self): 66 #Gyro.pyからデータ取得 67 self.Pitch, self.Roll = Gyro.GyroSensor(self.Pitch, self.Roll) 68 69 #テキストに出力 70 pitch = str(round(self.Pitch)) 71 self.textbox7.setText(pitch + "°") 72 73 roll = str(round(self.Roll)) 74 self.textbox8.setText(roll + "°") 75 76 ret, cv_img = self.cap.read() 77 78 if ret == False: 79 return 80 81 height = cv_img.shape[0] 82 width = cv_img.shape[1] 83 center = (int(width/2),int(height/2)) 84 angle = round(self.Roll*(-1)) 85 scale = 1.0 86 trans = cv2.getRotationMatrix2D(center,angle,scale) 87 self.cv_img = cv2.warpAffine(cv_img,trans,(width,height)) 88 89 self.img_src = self.cv_img 90 91 #RGB変換 92 cv_img_RGB = cv2.cvtColor(self.cv_img,cv2.COLOR_BGR2RGB) 93 94 #リサイズ 95 cv_img_RGB = cv2.resize(cv_img_RGB,(320,240)) 96 97 height, width, dim = cv_img_RGB.shape 98 99 bytesPerLine = dim * width 100 101 self.image = QImage(cv_img_RGB.data, width, height, bytesPerLine, QImage.Format_RGB888) 102 103 self.item = QGraphicsPixmapItem(QPixmap.fromImage(self.image)) 104 105 self.scene.addItem(self.item) 106 107 self.view.setScene(self.scene) 108 109 110 def auto_move_on(self): 111 112 self.auto = 1 113 GPIO.cleanup() 114 motor.init_GPIO() 115 self.pin1 , self.pin2 , self.pin3 , self.pin4 , self.pin5 , self.pin6 = motor.init_GPIO() 116 self.auto_move_set() 117 repeatTime = 10 118 timer = QTimer(self.view1) 119 timer.timeout.connect(self.auto_move_set) 120 timer.start(repeatTime) 121 122 def auto_move_off(self): 123 124 self.auto = 0 125 self.motor_S() 126 GPIO.cleanup() 127 print("cleanup") 128 129 130 def auto_move_set(self): 131 if self.auto == 0 or self.camera == 0: 132 return 133 134 variable, elapsed_time = Image_Processing.I_P(self.auto,self.img_src,self.Roll) 135 136 #モーターを動かすプログラム。画像処理(Image_Processing.py)にて得られた変数(variable)を基にモータの出力を変更 137 self.Duty_R , self.Duty_L = motor.motor_start(p_vaiue3,self.pin1 , self.pin2 , self.pin3 , self.pin4 , self.pin5 , self.pin6 ) 138 139 variable = str(round(variable,3)) 140 self.textbox6.setText(variable) 141 elapsed_time = str(round(elapsed_time,3)) 142 self.textbox1.setText(elapsed_time) 143 Duty_R = str(round(self.Duty_R ,1)) 144 self.textbox7.setText(Duty_R) 145 Duty_L = str(round(self.Duty_L,1)) 146 self.textbox8.setText(Duty_L) 147 148if __name__ == "__main__": 149 app = QApplication(sys.argv) 150 ex = Tab1Widget() 151 sys.exit(app.exec_())

python

1#Image_Processing.py 2import numpy as np 3import math 4import cv2 5import time 6import Value 7 8def I_P(auto,img_src,Roll,Pitch): 9 try: 10 t1 = time.time() 11 if auto == 0: 12 return 13 F=0 14 15 r=str(round(Roll)) 16 p=str(round(Pitch)) 17 18 img_src=cv2.resize(img_src,(180,60)) 19 img_src=img_src[20:60, 20-round(75*math.cos(math.radians(90-Roll))):140-round(75*math.cos(math.radians(90-Roll)))] 20 21 WRIGHT=open('/home/pi/Desktop/PAR.txt','a') 22 WRIGHT=("P:"+p+"、R:"+r+"\n") 23 WRIGHT.close 24 25 #HSV変換 26 hsv = cv2.cvtColor(img_src, cv2.COLOR_BGR2HSV) 27 GREEN_min = np.array([30, 64, 30]) 28 GREEN_max = np.array([90, 255,255]) 29 img_mask = cv2.inRange(hsv,GREEN_min ,GREEN_max) 30 31 neiborhood8 = np.array([[1, 1, 1],[1, 1, 1],[1, 1, 1]],np.uint8) 32 img_erosion = cv2.erode(img_mask,neiborhood8,iterations=2) 33 img_erosion = cv2.erode(img_erosion,neiborhood8,iterations=2) 34 img_dilation = cv2.dilate(img_erosion,neiborhood8,iterations=2) 35 img_dilation = cv2.dilate(img_dilation,neiborhood8,iterations=2) 36 img_dst = cv2.bitwise_and(img_src, img_src, mask=img_dilation) 37 img_dst2 = cv2.bitwise_and(img_src, img_src, mask=img_mask) 38 39 #Value.pyで変数(variable)を計算 40 row_L , row_R ,variable = Value.Get_MotorParameter(img_mask,1 ,F) 41 42 43 44 t2 = time.time() 45 elapsed_time = t2 - t1 46 47 return variable ,elapsed_time 48 49 except: 50 print("Error") 51 WRIGHT=open('/home/pi/Desktop/PAR.txt','a') 52 WRIGHT=("ERROR"+"\n") 53 WRIGHT.close 54 variable=0 55 elapsed_time=0 56 return variable ,elapsed_time

投稿2020/11/16 03:56

編集2020/12/01 06:55
pythonnoob1

総合スコア18

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問