ESP32のADC分布

概要

ESP32のADC特性測定
概要 ESP32と電源の自動計測環境とが構築できたので、ADCの特性を計測してみたいと思います。 手元環境で計測してみたので、個体差がありますし電源自体もキャリブレーションされているものではありませんのでご注意ください。 ESP32のADC...

前回に電圧を変更しながら特性を計測してみました。今回は電圧を固定して複数回計測をしてどのような分布になっているのかを確認してみました。

実験手順

  1. 電源を1Vに設定してESP32に接続
  2. SCPI経由でADC値を取得(analogReadMilliVolts)
  3. 分布を確認

電源側の分布も含まれてしまうのでそこまで厳密なものではありませんが、どのように補正をすればよいのかを確認になります。

実験で利用したコード

Jupyter notebookをVSCode上で動かして検証しました。通常のPythonでもよかったのですが、変数の中身が見えるのと何度か実行しなおすのに便利でした。

上記みたいな画面でして、Pythonのコードを実行した結果がすぐ下にでて、一番下にPython内部の変数一覧が確認できます。

import time
import pyvisa
import numpy as np
import matplotlib.pyplot as plt
import statistics
import math

rm = pyvisa.ResourceManager("@py")
inst = rm.open_resource(
    "ASRL5::INSTR", baud_rate=115200, read_termination="\n", write_termination="\n"
)

print(inst.query("*IDN?"))

v = []

for setma in range(0, 1000):
    valv = int(inst.query("AVIn32?").strip())
    v.append(valv)
    print("%d, %d" % (setma, valv))
    time.sleep(0)

data = np.array(v)
n, bins, patches = plt.hist(v, bins=100, range=(950, 1050))
plt.title("n = %d" % (len(data)))
plt.show()

print("合計 : %d" % (sum(data)))
print("個数 : %d" % (len(data)))
print("最小 : %d" % (np.min(data)))
print("最大 : %d" % (np.max(data)))
print("平均 : %.2f" % (data.mean()))
print("中央値 : %d" % (statistics.median(data)))
print("最頻値 : %d" % (statistics.mode(data)))
print("標準偏差 : %.2f" % (np.std(data)))

上記のようなコードをJupyterで細切れにして実行しています。グラフとかがコピーしやすいのでJupyterは便利でした。

結果

n = 1000

合計 : 996999
個数 : 1000
最小 : 864
最大 : 1153
平均 : 997.00
中央値 : 998
最頻値 : 1011
標準偏差 : 16.12

とりあえず何も考えずに1000回実行して分布を確認します。最頻値はちょっと山からずれていますね。もう少し中心によっていると思っていました。中央値か平均値を使うのが良さそうです。

n = 100

合計 : 99860
個数 : 100
最小 : 971
最大 : 1068
平均 : 998.60
中央値 : 999
最頻値 : 1004
標準偏差 : 12.50

100個にデータを減らしてみました。それなりに分布していますが中央値か平均値でよさそうなデータです。

n = 10

合計 : 10030
個数 : 10
最小 : 980
最大 : 1013
平均 : 1003.00
中央値 : 1008
最頻値 : 1011
標準偏差 : 11.17

さすがに10個だと頼りないデータです。10個だとちょっと補正をするのには足りない感じですね。

n = 10000

合計 : 9974017
個数 : 10000
最小 : 838
最大 : 1142
平均 : 997.40
中央値 : 997
最頻値 : 985
標準偏差 : 14.96

1000個のときとあまり変わらない感じです。

処理時間

SCPIでシリアル経由でアナログ入力を実行しているので、ある程度時間がかかっています。

データ数時間
10個0.0秒
100個0.3秒
1000個3.1秒
10000個33.4秒

誤差がありますが、100個のデータを取得するのに0.3秒かかっています。安いDMMと考えるとこれぐらいのデータ取得時間がかかってもいいような気がします。

ESP32側で平均処理を追加

// これはグローバル変数として定義
uint16_t meanCount = 100;

平均する回数をセット。


  // Set mean count
  scpi.RegisterCommand("MEANcount",
                       [](SCPI_C commands, SCPI_P parameters, Stream& interface) {
                         uint16_t value = String(parameters.First()).toInt();
                         if (1 < value) {
                           meanCount = value;
                         }
                       });

  // Analog Mean In(mV)
  scpi.RegisterCommand("AVMIn#?",
                       [](SCPI_C commands, SCPI_P parameters, Stream& interface) {
                         int GpioPin = -1;
                         String command = String(commands.Last());
                         command.toUpperCase();
                         sscanf(command.c_str(), "%*[AVMIN]%u?", &GpioPin);
                         uint32_t sum = 0;
                         for (int i = 0; i < meanCount; i++) {
                           sum += analogReadMilliVolts(GpioPin);
                         }
                         interface.println(sum / meanCount);
                       });

平均回数分だけ取得するように修正。

再度取得

v = []
inst.write("MEANcount 100")
for setma in range(0, 100):
    valv = int(inst.query("AVMIn32?").strip())
    v.append(valv)
    print("%d, %d" % (setma, valv))
    time.sleep(0)

上記で100回の平均データを100個取得。

合計 : 99749
個数 : 100
最小 : 994
最大 : 1001
平均 : 997.49
中央値 : 997
最頻値 : 997
標準偏差 : 1.50

データもまとまりました。処理時間は1.2秒でした。実際には100回の平均を取得しているので、Python側で100個も取得する必要がありません。

10000回の平均

10000回のデータ平均を1個取得するのに0.9秒かかっています。

データはもちろん1個です。

合計 : 997
個数 : 1
最小 : 997
最大 : 997
平均 : 997.00
中央値 : 997
最頻値 : 997
標準偏差 : 0.00

データ的には大丈夫そうですがさすにが1秒ぐらいデータ取得にかかるのは問題がありそうです。

1000回の平均

0.9秒から0.1秒に減りました。これぐらいなら許容できるかな?

合計 : 997
個数 : 1
最小 : 997
最大 : 997
平均 : 997.00
中央値 : 997
最頻値 : 997
標準偏差 : 0.00

データ的にも問題ありません。

100回の平均

100回の平均だと0.0秒になりました。有効桁数以下なので実際には0.01秒ぐらいかかっているはずです。

合計 : 995
個数 : 1
最小 : 995
最大 : 995
平均 : 995.00
中央値 : 995
最頻値 : 995
標準偏差 : 0.00

これぐらいでも問題ないかもですね。

平均数別分布確認

迷走していますが、平均数を変えながらどらぐらい分布しているか再度確認します。

100回データの平均を取ったものを1000個分布したものです。これぐらいでいいかなと思ったのですが。。。

1000回の平均はあきらかにデータが違いますね。100回平均だと標準偏差が1.51だったものが1000回平均だと0.58まで減っています。

10000回の平均だと100個しか取得していませんでしたが、なんとすべて同じ997になりました。これはこれで嘘くさいですが、1秒計測に時間かかっているのは伊達ではないみたいです。

もう一度実行したらちょびっと他の値もでていました。こちらの方がリアルっぽいデータ。。。

ちなみに2000回平均にしたらこんな感じでした。約0.2秒の計測時間に相当します。

3000回平均でこのデータです。約0.3秒。

まとめ

ESP32のADCは平均をとって利用したほうが精度は良さそうです。1000回測定をして0.1秒ぐらいかかるので、100回から10000回の間ぐらいで要求時間に応じて平均を取ってみてください。

今回はanalogReadMilliVolts()を単純に複数回呼び出していますが、連続モードで取得することでもう少し計測時間を減らすことはできるかもしれません。

あと測定対象のノイズの影響も受けるので、注意してください。そして統計処理は雰囲気で行っているので仕事とか研究の場合にはもう少し厳密にやる必要があると思います!

コメント