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

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

ただいまの
回答率

87.77%

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

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 1,320

score 6

以下の写真の回路を組んで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 )

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

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 3

checkベストアンサー

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/08 19: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")

    キャンセル

  • 2019/11/09 02:47

    masotailさん、

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

    キャンセル

  • 2019/11/10 03:30

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

    キャンセル

  • 2019/11/10 03:39

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

    キャンセル

0

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

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


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

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

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

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/11/08 18:36

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

    キャンセル

  • 2019/11/10 08:44

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

    キャンセル

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/08 18:37

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

    キャンセル

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

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

関連した質問

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