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

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

ただいまの
回答率

88.93%

ジャイロセンサが出力した角度をGUI上に出力したい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 319

noooooooob

score 4

環境

Raspberry Pi 3 B+
python 3.7
ジャイロセンサ MPU-6050

やりたいこと

ジャイロセンサの出力をGUI上に出力させたいです。
また、出力した角度を元にサーボモータを動かしたいです。

作成したコード

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import subprocess
import time
import numpy as np
import RPi.GPIO as GPIO

import smbus
import math
from time import sleep

Servo_pin = 18



class Tab1Widget(QWidget):

    def __init__(self):
        super().__init__() 
        self.title = "ジャイロテスト"
        self.left = 50
        self.top = 50
        self.width = 1300
        self.height = 1200
        self.initUI()
        self.counter = 0

    def initUI(self):

        btn1 = QPushButton("スタート", self)
        btn2 = QPushButton("ストップ", self)

        btn1.clicked.connect(self.start )
        btn2.clicked.connect(self.stop )

        self.textbox3 = QLineEdit(self)
        self.textbox4 = QLineEdit(self)
        self.textbox5 = QLineEdit(self)


        label2 = QLabel("ジャイロセンサ")
        label3 = QLabel("ピッチ角")
        label4 = QLabel("ロール角")
        #label5 = QLabel("ヨー角")


        layoutA = QGridLayout()
        layoutA.addWidget(btn1,0,0)
        layoutA.addWidget(btn2,0,1)
        layoutA.addWidget(label3,1,0)
        layoutA.addWidget(self.textbox3,1,1)
        layoutA.addWidget(label4,2,0)
        layoutA.addWidget(self.textbox4,2,1)
        #layoutA.addWidget(label5,3,0)
        #layoutA.addWidget(self.textbox5,3,1)


        layoutB = QVBoxLayout()
        layoutB.addWidget(label2)
        layoutB.addLayout(layoutA)


        self.setLayout(layoutB)

        self.show()


    def start(self):

        #servo-setup
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(Servo_pin, GPIO.OUT)


        self.startGyro()

        timer = QTimer()
        timer.timeout.connect(self.startGyro)
        timer.start(100)


    def startGyro(self):
        DEV_ADDR = 0x68

        ACCEL_XOUT = 0x3b
        ACCEL_YOUT = 0x3d
        ACCEL_ZOUT = 0x3f
        TEMP_OUT = 0x41
        GYRO_XOUT = 0x43
        GYRO_YOUT = 0x45
        GYRO_ZOUT = 0x47

        PWR_MGMT_1 = 0x6b
        PWR_MGMT_2 = 0x6c

        bus = smbus.SMBus(1)
        bus.write_byte_data(DEV_ADDR, PWR_MGMT_1, 0)


        def read_word(adr):
            high = bus.read_byte_data(DEV_ADDR, adr)
            low = bus.read_byte_data(DEV_ADDR, adr+1)
            val = (high << 8) + low
            return val

            # Sensor data read
        def read_word_sensor(adr):
            val = read_word(adr)
            if (val >= 0x8000):         # minus
                return -((65535 - val) + 1)
            else:                       # plus
                return val


        def get_temp():
            temp = read_word_sensor(TEMP_OUT)
            x = temp / 340 + 36.53      # data sheet(register map)記載の計算式.
            return x


        def getGyro():
            x = read_word_sensor(GYRO_XOUT)/ 131.0
            y = read_word_sensor(GYRO_YOUT)/ 131.0
            z = read_word_sensor(GYRO_ZOUT)/ 131.0
            return [x, y, z]


        def getAccel():
            x = read_word_sensor(ACCEL_XOUT)/ 16384.0
            y= read_word_sensor(ACCEL_YOUT)/ 16384.0
            z= read_word_sensor(ACCEL_ZOUT)/ 16384.0
            return [x, y, z]

        ax, ay, az = getAccel()
        gx, gy, gz = getGyro()

        #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))
        Roll = math.atan(ay/az) * 57.324 #57.324 = 360/2π であり、rad から ° に変換
        Pitch = math.atan(-ax / math.sqrt( ay* ay+ az*az ) ) * 57.324

        #pitch = math.atan(-ax / (ay*math.sin(roll) + az*math.cos(roll)))

        #print('{0:4.3f},   {0:4.3f},' .format(pitch, roll))

        #ピッチ角によるサーボモーター駆動


        #角度からデューティ比を求める関数
        def servo_angle(angle):
            duty = 2.5 + (12.0 - 2.5) * (angle + 90) / 180   #角度からデューティ比を求める
            Servo.ChangeDutyCycle(duty)     #デューティ比を変更

            #ピッチ角によるサーボモーター駆動
        Servo = GPIO.PWM(Servo_pin, 50)
        Servo.start(0)
        servo_angle(round(Pitch)*(-1))


        pitch = str(round(self.Pitch))
        self.textbox3.setText(pitch + "°")

        roll = str(round(self.Roll))
        self.textbox4.setText(roll + "°")

    def stop(self):
        Servo.stop()
        GPIO.cleanup()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    ex = Tab1Widget()
    sys.exit(app.exec_())

状況

作成したGUGIで「スタート」ボタンを押すと、
161行目の

pitch = str(round(self.Pitch))


AttributeError: 'Tab1Widget' object has no attribute 'Pitch'

と出てきます。
しかし、141行目で

Pitch = math.atan(-ax / math.sqrt( ay* ay+ az*az ) ) * 57.324

とPitchを出していると思うのですが、何がいけないのでしょうか?

また、その他気になる点などあれば、教えていただけますと幸いです。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

  • Pitch はローカル変数
  • self.Pitch はインスタンス変数

「self」は、他言語の「this」の様に省略可能ではありません。


エラーの解決策: self.Pitch -> Pitch
代入しているのはローカル変数のPitch なので、
self.Pitch ではなく、Pitch を使いましょう。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/09 20:23

    本当にありがとうございました。

    >read_byte_data が処理をブロッキングする(入力がない場合に止まる)ことはありませんか?

    に関してですが、ジャイロセンサのサンプルコードをコピペしただけですので、私自身、正直何をしているのか理解しきれていません。ですが、今のところ、特に止まるようなことはありません。

    キャンセル

  • 2020/07/09 20:34

    タイマー処理 -> GUIの描画/マウス等の入力イベント処理 -> タイマー処理

    と、繰り返し処理されるので、入力があるまで待つようなブロッキング操作があると
    GUI のフリーズに繋がります。

    問題なければ現状のまま大丈夫ですが、もし止まることがあれば、
    「スレッド」を用いて、別スレッドで GPIO の入出力を行うような対策が必要になります。
    (対策は、問題が起きた時に、新たに質問するくらいの優先度で良いと思います。)

    キャンセル

  • 2020/07/10 11:00

    丁寧な回答及び解説ありがとうございます!!
    とても助かりました!!

    キャンセル

+1

タイマーの動作確認用のコードです

from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication, QLabel


class View(QLabel):
    def __init__(self):
        super().__init__()

        self.count = 0

        timer = QTimer()
        timer.timeout.connect(self.countUp)
        timer.start(1000)

        # `timer` はローカル変数なので、この後直ぐに破棄される

    def countUp(self):
        # print("Count")

        self.count += 1
        self.setText("Count: {}".format(self.count))



def main():
    import sys
    app = QApplication(sys.argv)

    view = View()
    view.show()

    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

解決策A: QObject の親を指定する

        timer = QTimer(self)
        timer.timeout.connect(self.countUp)
        timer.start(1000)

親オブジェクトが破棄されるまで生存します。


解決策B: インスタンス変数へ保存

        timer = QTimer()
        timer.timeout.connect(self.countUp)
        timer.start(1000)

        self.timer = timer

インスタンスが有効な間、timer も生存します。


詳細は微妙に違います。現状ではそれ程気にする必要はありませんが、

例えば、timer をストップしたい場合、後からどのように timer を参照するか考えて見て下さい。
後から再度参照する場合はインスタンス変数にした方が便利です。
A の方法の場合は、objectName という引数で名前を付けて、後から探索可能にしたりします。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/09 20:09

    すごく丁寧な回答ありがとうございます。
    今後QTimerを活用する際、ぜひ参考にさせていただきます!

    キャンセル

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

  • ただいまの回答率 88.93%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る