🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Raspbian

Raspbianは、DebianベースのRaspberry Pi用ディストリビューション。ハードウェア浮動小数点演算を有効にすることが可能で、Webブラウズなどの速度を向上できます。

Matplotlib

MatplotlibはPythonのおよび、NumPy用のグラフ描画ライブラリです。多くの場合、IPythonと連携して使われます。

NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Raspberry Pi

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

Q&A

解決済

3回答

1776閲覧

ラズパイで電圧測定を行う上での計測時間のズレ

masotail

総合スコア6

Raspbian

Raspbianは、DebianベースのRaspberry Pi用ディストリビューション。ハードウェア浮動小数点演算を有効にすることが可能で、Webブラウズなどの速度を向上できます。

Matplotlib

MatplotlibはPythonのおよび、NumPy用のグラフ描画ライブラリです。多くの場合、IPythonと連携して使われます。

NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Raspberry Pi

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

0グッド

0クリップ

投稿2019/11/05 09:22

以下の写真の回路を組んでch0に接続したものの電圧をラズパイによって以下のプログラム(ADC.py)を使ってサンプリング周波数1000hz データ数15000 計測時間15秒 で電圧を測定したところ計測時間15秒のはずが約22秒になってしまいます。このプログラムからサンプリング10000hz 計測時間10秒にすると35秒に差が広がります。データ数は指定した通りの数だけ取れていたのでサンプリング周波数が指定したものよりも小さくなっているようです。指定したサンプリング周波数通りに計測したいのですがどのようにすればよいでしょうか?

ADC.py

import wiringpi as pi
import time
import mcp_adc
import matplotlib.pyplot as plt
import numpy as np
from scipy import signal
import openpyxl as excel

SPI_CE = 0
SPI_SPEED = 1000000
READ_CH = 0
VREF = 5.0
V = []
Vi = []
i = 0

adc = mcp_adc.mcp3208( SPI_CE, SPI_SPEED, VREF )

x = np.arange(0,15, 0.001)
y = x/10
lines, = plt.plot(x, y)

while True:
value = adc.get_value( READ_CH )
volt = adc.get_volt( value )
print ("Value:", value, "Volt:", volt )
V.append(volt)
Vi.append(volt)
i = i+1

time.sleep( 0.001 )

if i==15000:

T = (15/i) fs = 1/T i =0 wb=excel.Workbook() ws=wb.active n=15000 for i in range(1,n): T=Vi[i-1] ws.cell(row=i,column=1,value=T) wb.save("denatsu.xlsx") Vi.clear()

またモジュールmcp_adcは以下の通りです

import wiringpi as pi
import struct

class mcp3002:
def init( self, ss, speed, vref ):
self.ss = ss
self.speed = speed
self.vref = vref

pi.wiringPiSPISetup( self.ss, self.speed )

def get_value( self, ch ):
senddata = 0x6800 | ( 0x1800 * ch )
buffer = struct.pack( '>h', senddata )
pi.wiringPiSPIDataRW( self.ss , buffer )
value = ( buffer[0] * 256 + buffer[1] ) &0x3ff
return value

def get_volt( self, value ):
return value * self.vref / float( 1023 )

class mcp3008:
def init( self, ss, speed, vref ):
self.ss = ss
self.speed = speed
self.vref = vref

pi.wiringPiSPISetup( self.ss, self.speed )

def get_value( self, ch ):
cmd = 0xc0 | ( ch <<3 )
buffer = cmd <<16
buffer = buffer.to_bytes( 3, byteorder='big' )
pi.wiringPiSPIDataRW( self.ss, buffer )
value = ( buffer[0] <<16 | buffer[1] <<8 | buffer[2] ) >>7
return value

def get_volt( self, value ):
return value * self.vref / float( 1023 )

class mcp3204:
def init( self, ss, speed, vref ):
self.ss = ss
self.speed = speed
self.vref = vref

pi.wiringPiSPISetup( self.ss, self.speed )

def get_value( self, ch ):
cmd = 0xc0 | ( ch <<3 )
buffer = cmd <<24
buffer = buffer.to_bytes( 4, byteorder='big' )
pi.wiringPiSPIDataRW( self.ss, buffer )
value = ( buffer[0] <<24 | buffer[1] <<16 | buffer[2] <<8 | buffer[2] ) >>13
return value

def get_volt( self, value ):
return value * self.vref / float( 4095 )

class mcp3208:
def init( self, ss, speed, vref ):
self.ss = ss
self.speed = speed
self.vref = vref

pi.wiringPiSPISetup( self.ss, self.speed )

def get_value( self, ch ):
cmd = 0xc0 | ( ch <<3 )
buffer = cmd <<24
buffer = buffer.to_bytes( 4, byteorder='big' )
pi.wiringPiSPIDataRW( self.ss, buffer )
value = ( buffer[0] <<24 | buffer[1] <<16 | buffer[2] <<8 | buffer[2] ) >>13
return value

def get_volt( self, value ):
return value * self.vref / float( 4095 )

イメージ説明
イメージ説明
イメージ説明

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

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

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

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

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

guest

回答3

0

ベストアンサー

masotailさん、

↑このプログラムでは、計測ごとに、sleep 0.001してますが、計測時間は0秒ではないので、その分が誤差になります。

参考書(?)では、time.sleep(0.1)とかいてあるので、10Hz程度であれば、
計測時間(+その他)はsleepに対して比較的短いと考えられるので、無視できる(?)誤差程度で、と済ましているんだとおもいます。
が、サンプリング周波数を上げて、計測回数が増えれば、誤差も無視できないようになってくると思います。

なので、Sleep使わずに、開始時刻と現在の時刻の差をみてポーリングして待って、計測をするというのはどうでしょう。
以下のコードdo_samplingの部分をADC読み出しにかえてみてください。
注意点としては、計測(+その他)の時間 > サンプリング間隔時間、と**!! ならないように !!**、ですね(笑

import time import numpy as np import matplotlib.pyplot as plt def do_sampling(i): y[i] = time.time() - time_start sampling_num = 15000 sampling_rate = 1000 sampling_interval = 1 / sampling_rate x = np.arange(0, sampling_num / sampling_rate, sampling_interval) y = np.zeros(sampling_num) count=0 time_start = time.time() time_next = time_start while count < sampling_num : do_sampling(count) time_next += sampling_interval while (time.time() < time_next): # 次の時間まで待つ。 pass count = count + 1 time_end = time.time() time_diff = time_end - time_start plt.plot(x, y) plt.show()

10kHzやそれ以上は、どうでしょうかね?結果出たら教えてください。

投稿2019/11/07 18:55

mt08

総合スコア1825

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

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

masotail

2019/11/08 10:19

ご回答ありがとうございます。mt08さんが想定しているコードは以下のものでしょうか? 以下のコードで測定した結果多少誤差はありますがほぼサンプリング周波数1000Hzで測定できました。 import wiringpi as pi import time import mcp_adc3208 import matplotlib.pyplot as plt import numpy as np from scipy import signal import openpyxl as excel SPI_CE = 0 SPI_SPEED = 1000000 READ_CH = 0 VREF = 5.0 Vi = [] i = 0 adc = mcp_adc3208.mcp3208( SPI_CE, SPI_SPEED, VREF ) def do_sampling(i): y[i] = time.time() - time_start sampling_num = 15000 sampling_rate = 1000 sampling_interval = 1 / sampling_rate x = np.arange(0, sampling_num / sampling_rate, sampling_interval) y = np.zeros(sampling_num) count=0 time_start = time.time() time_next = time_start while count < sampling_num : do_sampling(count) time_next += sampling_interval value = adc.get_value( READ_CH ) volt = adc.get_volt( value ) Vi.append(volt) while (time.time() < time_next): # 次の時間まで待つ。 pass count = count + 1 time_end = time.time() time_diff = time_end - time_start wb=excel.Workbook() ws=wb.active for i in range(1,sampling_num): s=y[i] T=Vi[i-1] ws.cell(row=i,column=1,value=s) ws.cell(row=i,column=2,value=T) wb.save("denatsu.xlsx")
mt08

2019/11/08 17:47

masotailさん、 "多少誤差"が、どの程度を許容するかですが、精度を要求するなら、ラズパイ(Linux) + Python使って、計測ってのは、考え直した方がいいかもしれません。 ハードウェアで、所定の周波数でサンプリングできて、DMAで転送してくれるとか、割り込みがかかるとか、そのようなのに対応している環境が必要かもですね。
masotail

2019/11/09 18:30

多少誤差があると言っても1000hzで計測した場合100万分の1秒単位の誤差しかないのでとても満足しています。ありがとうございました!
masotail

2019/11/09 18:39

問題点としてはやはりサンプリング周波数の上限が約10khzであることです。理想としては100khzくらいで計測したいと考えています。
guest

0

value = adc.get_value( READ_CH ) volt = adc.get_volt( value )

というコードを100回ぐらい繰り返して(ループではなく、コードを100回繰り返して書いて(200行))、実行にかかる時間を測定してみてください。
それを100で割れば1回データを取得する(A/Dコンバータから値を取り出して、それを電圧の値に変える)のに必要な時間が判ります。

ループすることで計測時間が伸びているのか否かが、測定にかかる時間を正確に把握する事で判断できる場合が有ります。

print ("Value:", value, "Volt:", volt )

なんていうのは無駄(計測時間を延ばすだけ)なので省きましょう。
画面に表示しなくても、後でExcelに入ったデータを見れば良いのですから。

投稿2019/11/07 07:53

coco_bauer

総合スコア6915

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

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

masotail

2019/11/08 09:37

ご回答ありがとうございます。測定してみます。
guest

0

  • sleep関数の時間精度はそんなに良くありません
  • sleep関数以外のコードの実行時間というのもあります。特にAD変換時には、変換完了まで内部で待ってます

というとこらへんが原因だと思われます。
まあ、計測時間15秒なら、15秒経過したかどうかでループを抜けるようにすればいいんではないかと。


ああ、この場合サンプリング周波数が重要なのね。
それならプログラムループでするのは諦めたほうがよろしいかと。

タイマ割り込みを使うようにするとか考える必要があります

#pythonでできるのかどうかは知らない

投稿2019/11/05 09:41

編集2019/11/05 09:45
y_waiwai

総合スコア88040

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

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

masotail

2019/11/08 09:36

ご回答ありがとうございました。色々勉強してみます。
Q71

2019/11/09 23:44

時間の精度が良くないというか、「最低でも指定された時間は当該スレッドに処理をさせない」という命令です。その時間以上“寝ている”ことは保証されていますが、寝過ごす事については規定されていません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問