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

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

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

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

Python

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

Q&A

1回答

393閲覧

Python,HC-SR04,一定時間の測定

jserojgmv53

総合スコア0

Raspberry Pi

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

Python

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

0グッド

0クリップ

投稿2023/03/29 09:27

編集2023/03/30 04:06

実現したいこと

Raspberrypi4 を使用しています。
一定時間HC-SR04で距離を測定したいです。

36,000秒の間、毎秒csvに時間と距離を記録

上記の動作を目的としております。

以下のプログラムを起動中、2時間程でエラーメッセージもなく動作しなくなりました。

数秒~1時間程度は問題なく動作するのですが、
2時間程度経つと何の表示もなく動かない状態になります。

原因と対策をご教授いただければ幸いです。

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

エラーメッセージ
なし

該当のソースコード

import time import RPi.GPIO as GPIO import datetime import csv PIN_TRIG = 24 PIN_ECHO = 25 SPEED_OF_SOUND = 33145 def init_GPIO(): GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(PIN_TRIG, GPIO.OUT) GPIO.setup(PIN_ECHO, GPIO.IN) def calc_distance(): GPIO.output(PIN_TRIG, GPIO.LOW) time.sleep(0.5) GPIO.output(PIN_TRIG, True) time.sleep(0.5) GPIO.output(PIN_TRIG, False) while GPIO.input(PIN_ECHO) == 0: signaloff = time.time() while GPIO.input(PIN_ECHO) == 1: signalon = time.time() time_passed = signalon - signaloff distance = SPEED_OF_SOUND * time_passed / 2 return distance init_GPIO() for measure in range(32400): distance = f'{calc_distance():.3f}' today = datetime.date.today() today = str(today).replace('-','') with open(today + 'data.csv','a') as f: now = datetime.datetime.now() recordtime = '{0:%H:%M:%S}'.format(now) writer = csv.writer(f) writer.writerow([recordtime,distance]) f.close() print(recordtime,distance) GPIO.cleanup()

Python3

1 2### 補足情報(FW/ツールのバージョンなど) 3 4・Raspberrypi4 B 5・HC-SR04

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

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

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

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

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

y_waiwai

2023/03/29 09:35

このままではコードが読めないので、質問を編集し、</>(コードの挿入)ボタンを押し、出てくる’’’の枠の中にコードを貼り付けてください
thkana

2023/03/30 22:35

そこで「丸投げ」するんじゃなくて、自分で「測距系が悪いのか」「記録系が悪いのか」「組み合わせで症状が出るのか」みたいな切り分けをしてみてはいかがですか?
jserojgmv53

2023/03/31 03:53

測定距離については、センサー推奨の範囲で移動し適切に測れていました。 時間については、動かさず距離が一定で1~2時間、不定期で対象物を移動し1~2時間は適切に 測定できています。そのため、距離系が原因とは考えておりません。再起動しても変わりありません。 記録系は数秒~1時間程度は問題なく動作するのですが、やはり2時間程度経過するとエラー表示もなく、 動作しなくなります。最初から測定ができていないわけでなく、途中までは計測できていることから、 記録系ではないかと思います。しかし調べ方が悪いのか似た事例を確認することができず、上記のように 再起動なり時間や距離を変更しながらやってみたのですが、解決に至らなかったため、ご質問させて 頂きました。
thkana

2023/03/31 12:17

いやまぁ、私も記録系だとはおもってますけどね。 例えば測定している行をコメントアウトして # distance = f'{calc_distance():.3f}' time.sleep(1) distance=12.34 とか置き換えてそれでも症状がでれば、測距の影響は「全く」なく、ファイル操作だけに絞れるわけでしょう? そうやって「確か」にしていくことがまず最初だと思うのですよ。 (考えてるネタはあるけれど、まずはそれを確認してから...)
jserojgmv53

2023/04/01 08:23

ご指南を頂きありがとうございます。頂いた切り分けについて、そこまで考えつくこと自体ができておりませんでした。とても勉強になります。一度、検証させて頂きます。ありがとうございます。
thkana

2023/04/01 13:42

ファイルを毎回open/closeしているのが良くないのでは、という予想を立てていたのですが、とりあえずそうではなさそう... 手元で再現実験してみましたが、とりあえず2.5時間回したところで特に停止しませんでした。 そこで反射が受信できない状態とかを試そうといじっているときに仮組みの線が外れたか、 time_passed = signalon - signaloff の行でsignaloffに値が設定されていないとのことでエラー終了しました。 再度実行して一晩放置してみましょうか... 質問者さんの環境では事象についての再現性はあるのでしょうか。もしかして、メモリ量とかOSのバージョンだとかPythonのバージョンだとかを徹底的に合わせないと再現しない、なんてこともあるかも知れません。
thkana

2023/04/01 14:16

あれ? なんか5分ぐらい流したら止まったな... トリガパルス発生直後のビミョーなタイミングでLinuxのタスク切り替えがあって反射待ちパルスを捕まえられなかった(他タスクにとられている間に反射待ちのパルスが終わってしまった)とかいうことがあったりしないかしら?そうなるとパルス待ちのwhileループから抜けられなくなりますよね?
jserojgmv53

2023/04/02 07:08

thkana様 ご返信と回答を頂き有難うございます。 現在前回ご指南いただいた以下の内容で置き換え検証しております。 まだ1回目ですが6時間経過し症状は発生しておりません。 念のため現状の環境のまま再度検証してみたいと思います。 ファイルを毎回開いていたのは回答でおっしゃられた通りファイルの破損を懸念しておりました。 「2時間程度」の検証は、回数として6回になります。配線が外れたり物理的なイレギュラーは目視確認状ないと思います。ご回答いただいた内容を調べさせていただき、切り分けしつつ考えられる原因とその対策について確認していきたいと思います。 自分の視野が狭く、課題抽出の甘さや、質問を投稿するうえで事前に検証した気になっていた浅さをとても痛感しました。反省点とし先ずは頂いた内容を検証し考え方から今後に活かしていきたいと思います。ありがとうございます。 私事により検証に時間がかかるかもしれませんが、進捗分かり次第再度コメントさせて頂きます。
guest

回答1

0

Python

1def calc_distance(): 2 GPIO.output(PIN_TRIG, GPIO.LOW) 3 time.sleep(0.5) 4 GPIO.output(PIN_TRIG, True) 5 time.sleep(0.5) 6 GPIO.output(PIN_TRIG, False) 7 t0=time.time() #追加 8 while GPIO.input(PIN_ECHO) == 0: 9 signaloff = time.time() 10 if signaloff-t0 > 1 : #1秒以上Echoパルスが来なかった 11 print('TOUT') # 表示して 12 while True: # 止める(どうせ止まるのだけれど、表示を一回で止めたい) 13 pass # 14 while GPIO.input(PIN_ECHO) == 1: 15 signalon = time.time() 16 time_passed = signalon - signaloff 17 distance = SPEED_OF_SOUND * time_passed / 2 18 return distance

と変更して試行したところ(その回では)40分程でTOUTが出てきました(寝る前に仕掛けて寝た後に出たのでN=1)。
センサー側でパルスが出ないことがある、という可能性はありますが、そうであれば、よく使われるセンサーですからネット上でもう少しそういう話が拾えるでしょう。しかし、そういう話は聞きません。

質問者さんの観測で「2時間程で」というのが何回ぐらいの試行の結果でしょうか。例外はなかったのでしょうか。回数の多い試行で毎回2時間であったのなら、私の手元とは別の現象なのかも知れませんが、とりあえず私の手元の現象についていうならばLinuxのタスクスイッチによって距離のパルスを取りこぼした、という想定が現実味を帯びてきます。(本来現象についての情報は質問に書かれているべきと思います)

タスクスイッチであれば、プログラム上どの場所でも起こりうるので、試行中に起きた別の現象

time_passed = signalon - signaloff
の行でsignaloffに値が設定されていないとのことでエラー終了

も「トリガ発行後にタスクスイッチが入り、echoのパルスのタイミングによってwhile GPIO.input(PIN_ECHO)==0:に到達した時点でecho=1になっていてwhile文の中に入らず通過した」として説明ができます。
他に、パルス終端の検出中にタスクスイッチがあれば「取得した距離データが実際より大きい」という現象も起こり得るでしょうが、データのばらつきに紛れて見つけられていないだけかも知れません。

原因をこれだと仮定するなら、対策は
・試行のプログラムと同様の仕組みでタイムアウトを設けてループを脱出させる (止まる対策)
・signaloff/signalonには予め(結果が確実に異常値になるような)何かしらの値を与えておく(値が設定されていなことによるエラー対策)
・毎秒何回かの測定を行い、最大値/最小値を外して平均を取る(異常値除外)
あたりということになるかと思います。
Linuxマシンである=タスクスイッチを制御できないRaspberry Piをこの用途に使うことが適切でない、と思ってもしまうのですが。


以下、余談になりますが
私が自分で作るなら(この用途にはそもそもRaspberry Piは使わないだろうというのはおいといて)
・ファイルのopen/close、appendのためのデータ終端検出はコストが大きいので、プログラムの開始時にファイルをオープン、各回では書き込みのみを行い、終了時にクローズとするかと思います。まぁ、破綻はしないようなので現状でも間違っているということではありませんし、現状のプログラムには、途中でプログラムの異常停止などがあってもファイルが壊れにくいという利点があるのも事実です。(最初はこの部分での破綻を疑っていた...)
・各々の処理には、ゼロでない時間がかかっているはずです。time.sleep()で1秒周期を作っていると、処理の時間が測定間隔の誤差として累積していくことになります。もちろん、目的に対して十分な精度が得られているのなら現状で構いませんが、
取得した時刻の「秒」の値が変わったら測定を行うというようにすればより安定した測定周期が得られるでしょう。

Python

1prevSec=-1 #直前の「秒」データを保持 2measure=0 3while measure< 32400: 4 now = datetime.datetime.now() 5 if now.time().second != prevSec : #時刻の「秒」が変化した? 6 # 測定処理をここに記述 7 measure+=1 8 prevSec=now.time().second #更新された「秒」を保持

投稿2023/04/01 23:00

編集2023/04/01 23:13
thkana

総合スコア7629

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問