
【突入電流測定編】Pythonによるオシロスコープ波形データ解析の秘訣
2021.12.20
Pythonで突入電流波形解析してみた!
今回は、オシロスコープで得られた csv の突入電流波形データに対して、Python で自動評価するテクニックを紹介します。
電子部品ヒューズの選定手順に含まれる、I2t-T カーブへの適用確認をジュール積分計算とグラフ描画によって行います。
電源回路の評価で必要不可欠な突入電流波形解析を Python で行います。
記事の最後にコピペで使えるサンプルコードも付属しています!
突入電流波形について
突入電流とは、電気回路の電源オン時に瞬間的に流れる電流となっており、短い時間で急速に増加したあと急速に減衰し、定常値に収束するような挙動をとります。
主に回路内のコンデンサ部品の合計容量によってその大きさは変わりますが、一般的には机上計算が難しい複雑な振る舞いとなります。
そのため、オシロスコープを使って実測値を測定しなければ評価することはできません。
突入電流を評価する意義
では、突入電流を評価する意義は何なのでしょうか。
最もポピュラーなケースが電子部品ヒューズの選定です。
過電流を検出し、安全のため自動で溶断するヒューズは、瞬間的な突入電流で誤って溶断してしまう可能性があります。
そのリスクを判断するためには突入電流の波形評価が必要不可欠なのです。
突入電流波形の評価方法
評価の具体的な方法としては、各社ヒューズメーカーが詳しい説明資料を用意しています。
ここでは、評価の中でも少々ややこしい I2t-T 特性の評価について簡単に説明していきます。
下図は Python で生成した大雑把な突入電流波形の例です。

まず、得られた突入電流波形の始めから時間単位でジュール積分値を計算します。
積分値は各時間ごとに累積させていき、横軸を時間、縦軸を累積積分値としたグラフを描きます。
このグラフは I2t-T 曲線と呼ばれ、この曲線がメーカー側が提示する範囲に収まっていれば合格となります。
各ヒューズのモデルごとに I2t-T 曲線は用意されていますので、エンジニアはヒューズ本来の溶断電流値を満たしつつ、合格範囲内となるモデルを選定していくことになります。
I2t-T 曲線は例えば、下記のメーカーのヒューズモデルのデータシートを閲覧することで確認できます。
SOC 株式会社:DC35V 3216サイズ 速断 表面実装型プロテクター
https://www.socfuse.com/ja/products/detail/dc35vp11cf/
本記事での実施内容
本記事では、上記で述べた I2t-T 特性の評価に注目し、Python で突入電流波形から I2t-T 曲線を描くことを行います。
突入電流波形は今までのように、オシロスコープから Pyvisa などを使って得られたことを想定し、csv 形式のものを用意します。
今回は、Python を使って突入電流を擬似的に再現した波形データを取り扱います。
コード内では、csv の読み出しに始まり、逐次処理方式で突入電流の始まりを自動検出し、ジュール積分値を累積する形で計算します。
最後に matplotlib を使い、グラフを作成する形です。
準備
今回のコード実行に必要な環境は下記になります。
インストールが必要なもの
- Python の開発環境(Spyderなど)
- Pandas モジュール(波形データ生成に必要)
- math モジュール(波形データ生成に必要)
- random モジュール(波形データ生成に必要
筆者の実行環境
- Python(3.6)
- Spyder(3.2.6)
解析する波形データ
波形データは Python で簡単に生成できますので、下記のコードを実行してみてください。
上の方にある突入電流波形の例通りのものが生成されるはずです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | import pandas as pd import random import math import matplotlib.pyplot as plt X_SAMPLE = 3000 NOISE_SEED_OFF = 123 #rise_wave RISE_TIME=300 PEAK_TIME = 400 DAMP_TIME = 1200 PEAK_CUR = 3.2 STABLE_CUR =1.5 NOISE_AMP_2=0.05 RC=300 PEAK_INT = (PEAK_TIME-RISE_TIME)*4 DAMP_INT = (DAMP_TIME-PEAK_TIME)*4 xx = [ i for i in range(3000)] analog_wave_rush=[] noise_rush=[] for i in range(len(xx)): random.seed(NOISE_SEED_OFF+i) noise_rush.append(NOISE_AMP_2*random.randint(-100,100)/100) if i<RISE_TIME: analog_wave_rush.append(noise_rush[i]) elif i<PEAK_TIME: rush_dt = PEAK_CUR*math.sin(2/PEAK_INT*xx[i-RISE_TIME]*math.pi) analog_wave_rush.append(rush_dt+noise_rush[i]) elif i<DAMP_TIME: damp_dt = PEAK_CUR - (PEAK_CUR-STABLE_CUR)*math.cos(2/DAMP_INT*xx[i-PEAK_TIME]*math.pi + math.pi/2*3) analog_wave_rush.append(damp_dt+noise_rush[i]) else: rise_dt = STABLE_CUR*(1 - math.exp(-1/RC*xx[i-RISE_TIME])) analog_wave_rush.append(STABLE_CUR+noise_rush[i]) plt.figure(figsize=(10,8)) plt.plot(xx,analog_wave_rush) plt.savefig("./analog_wave_rush.jpg") plt.show() wave_data= pd.DataFrame({"wave_rush":analog_wave_rush}) wave_data.to_csv("analog_wave_rush.csv") |
コード本文
それでは、解析をスタートしましょう!
今回も波形データに特化した時系列逐次処理で処理を行っていきます。
詳細については前回記事(記事ID26995へのリンク)を参照してみてください。
①モジュールのインポートとcsvデータの読み込み
まずは下記のようにモジュールのインポートと csv の読み込みを行います。
ここは前回記事とほとんど変わりません。
1 2 3 4 5 6 7 8 9 10 | #変数の定義 ch1_rise_st=[] ch1_rise_st_buf =[ 0 for i in range(10)] TimeCnt=[] JouleIntg=[] prevIntg=0 #立ち上がり検出を1回だけ実施するためのフラグ rise_st_f = True start_measure_f = False |
②使用する変数の定義とフラグの初期化
次に変数とフラグを定義して初期化します。
使われ方については後述のコードで説明していきます。
1 2 3 4 5 6 7 8 9 10 | #変数の定義 ch1_rise_st=[] ch1_rise_st_buf =[ 0 for i in range(10)] TimeCnt=[] JouleIntg=[] prevIntg=0 #立ち上がり検出を1回だけ実施するためのフラグ rise_st_f = True start_measure_f = False |
③波形データの時系列逐次処理
前回から使っている時系列逐次処理の部分です。
こちらも従来通りですので、説明は割愛します。
メインは次から説明する while 文の中身です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #データの読み込み開始 ##最初の1行目はループ前に読み込む row_data = wave_file.readline() split_data = row_data.split(",") dt_cnt=0 while row_data: index_dt = float(split_data[0]) ch1_dt = float(split_data[1]) ###########この間に解析処理を入れる########## ~~~~~ ####################################### ##次の行を読み込む。ここで最終行なら空白が返ってくるためループ終了 dt_cnt+=1 row_data = wave_file.readline() split_data = row_data.split(",") wave_file.close() |
④突入電流開始時間の検出
メインの処理の前に突入電流の検出を行います。
あらかじめ設定した閾値を超過した立ち上がりタイミングから検出処理を開始します。
コードを見ていきましょう!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #測定開始の判定 #バッファを使って閾値の超過タイミングを検出する ##サンプル間隔を開けてノイズの影響を軽減する if index_dt % DET_SMP_INT == 0 and rise_st_f: #リングバッファへの保存 for i in range(len(ch1_rise_st_buf)-1): ch1_rise_st_buf[len(ch1_rise_st_buf)-1-i] = ch1_rise_st_buf[len(ch1_rise_st_buf)-1-i-1] ch1_rise_st_buf[0]=ch1_dt #5回連続で値が閾値を超えた場合に閾値超過と判定する for i in range(len(ch1_rise_st_buf)): if ch1_rise_st_buf[i] > START_TH: if i >4 : start_measure_f = True rise_st_f=False StartPnt = index_dt break else: break |
今までと同様に、リングバッファを使って、連続して閾値を超えたタイミングを検出しています。
検出時には StartPnt に開始時のサンプル位置を記録し、グラフ描画のスタート地点としています。
⑤ジュール積分値の計算とグラフ描画用の配列作成
メインとなるジュール積分値の計算です。
基本的には突入電流の検出タイミングから指定したサンプル間隔ごとに計算していく形になります。
コードを見ていきましょう!
1 2 3 4 5 6 7 8 9 | #ジュール積分の計算をする処理 if index_dt % DET_SMP_INT == 0 and start_measure_f: #検出タイミングを0secとしてサンプル間隔毎にグラフ用の配列に追加する TimeCnt.append((index_dt - StartPnt)*SMP_TIME) #現在の時間でI2tを計算し、前回の値を加算することでジュール積分値を算出し、グラフ用の配列に追加する integ_calc = ch1_dt*ch1_dt*(SMP_TIME*DET_SMP_INT) + prevIntg JouleIntg.append(integ_calc) #ジュール積分値は前回の値を累積するので今回の値を保持しておく prevIntg=integ_calc |
グラフを描画するのに必要なリストデータは時間 TimeCnt と積分値 JouleIntg なのでこの2つに要素を追加していく形になります。
まず、
1 | TimeCnt.append((index_dt - StartPnt)*SMP_TIME |
で実時間を検出タイミングを起点に算出します。
ここで、波形データ1点ごとのサンプリング時間 SMP_TIME を正しく把握しておくことが重要です。
次に
1 | integ_calc = ch1_dt*ch1_dt*(SMP_TIME*DET_SMP_INT) + prevIntg |
前回時間におけるジュール積分値に現在時間における積分値を加算して累積していきます。
積分値の計算は、離散データでは
時間間隔 × 電流値の2乗
で計算ができるため、データのサンプリング時間(SMP_TIME)と処理のサンプリング間隔(DET_SMP_INT)から正しい値を算出しましょう。
上記を繰り返し処理していくことでグラフ描画用のデータが生成されます。
⑥ I2t-t グラフの描画
最後に波形データ処理の結果得られたジュール積分値を時間軸にプロットします。
ポイントとしては、比較するメーカーのグラフと描画方法と範囲を揃えることです。
メーカーのグラフは図データのみになるので、最終的には目視で比較することとなります。
1 2 3 4 5 6 7 8 9 | #I2t-tグラフを描画する plt.xscale("log") plt.yscale("log") plt.grid(which='major') plt.grid(which='minor') plt.xlim(GRAPH_XLIM) plt.ylim(GRAPH_YLIM) plt.scatter(TimeCnt, JouleIntg,s=3) plt.show() |
下図のグラフが生成されました。

このグラフとメーカーのグラフを見比べてみましょう。
こちらのリンクの pdf 形式のデータシートからグラフを参照することができます。
プロットしたデータよりマージンがある曲線は3150のようです。
したがって、型番 DC35VP11CF では定格電流3.15A を選べばよいことが分かりました。
サンプルスクリプト
最後に今回紹介した解析コードの全文を下記に記載します。
今までの説明で省かれていた閾値などの設定値も記載しており、解析したい対象に応じて値を変えれば、同様の解析が手持ちの波形データでも行えるはずです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | import matplotlib.pyplot as plt wave_path = "./analog_wave_rush.csv" wave_file = open(wave_path,'r') skip_row_count = 1 #設定値関連 SMP_TIME = 100 * 10**(-6) #取得した波形データのデータごとのサンプリング時間(今回は100us) START_TH = 0.1 #突入電流の検出を開始する電流閾値[A] DET_SMP_INT = 2 #電圧閾値を検出する際にデータ比較をするサンプル間隔 GRAPH_XLIM = [100 * 10**(-6),1.0] #I2t-tグラフのX軸の描画範囲(比較するメーカーのグラフに合わせる) GRAPH_YLIM = [10**(-2),10**2] #I2t-tグラフのY軸の描画範囲(比較するメーカーのグラフに合わせる) #変数の定義 ch1_rise_st=[] ch1_rise_st_buf =[ 0 for i in range(10)] TimeCnt=[] JouleIntg=[] prevIntg=0 #立ち上がり検出を1回だけ実施するためのフラグ rise_st_f = True start_measure_f = False #ヘッダ行等のスキップ while skip_row_count !=0: row_data = wave_file.readline() skip_row_count-=1 #データの読み込み開始 ##最初の1行目はループ前に読み込む row_data = wave_file.readline() split_data = row_data.split(",") dt_cnt=0 while row_data: index_dt = float(split_data[0]) ch1_dt = float(split_data[1]) ###########この間に解析処理を入れる########## #測定開始の判定 #バッファを使って閾値の超過タイミングを検出する ##サンプル間隔を開けてノイズの影響を軽減する if index_dt % DET_SMP_INT == 0 and rise_st_f: #リングバッファへの保存 for i in range(len(ch1_rise_st_buf)-1): ch1_rise_st_buf[len(ch1_rise_st_buf)-1-i] = ch1_rise_st_buf[len(ch1_rise_st_buf)-1-i-1] ch1_rise_st_buf[0]=ch1_dt #5回連続で値が閾値を超えた場合に閾値超過と判定する for i in range(len(ch1_rise_st_buf)): if ch1_rise_st_buf[i] > START_TH: if i >4 : start_measure_f = True rise_st_f=False StartPnt = index_dt break else: break #ジュール積分の計算をする処理 if index_dt % DET_SMP_INT == 0 and start_measure_f: #検出タイミングを0secとしてサンプル間隔毎にグラフ用の配列に追加する TimeCnt.append((index_dt - StartPnt)*SMP_TIME) #現在の時間でI2tを計算し、前回の値を加算することでジュール積分値を算出し、グラフ用の配列に追加する integ_calc = ch1_dt*ch1_dt*(SMP_TIME*DET_SMP_INT) + prevIntg JouleIntg.append(integ_calc) #ジュール積分値は前回の値を累積するので今回の値を保持しておく prevIntg=integ_calc ####################################### ##次の行を読み込む。ここで最終行なら空白が返ってくるためループ終了 dt_cnt+=1 row_data = wave_file.readline() split_data = row_data.split(",") wave_file.close() #I2t-tグラフを描画する plt.xscale("log") plt.yscale("log") plt.grid(which='major') plt.grid(which='minor') plt.xlim(GRAPH_XLIM) plt.ylim(GRAPH_YLIM) plt.scatter(TimeCnt, JouleIntg,s=3) plt.savefig("./I2t-t_graph.jpg") plt.show() |
こちらの記事もオススメ!
書いた人はこんな人

- 「好きを仕事にするエンジニア集団」の(株)ライトコードです!
ライトコードは、福岡、東京、大阪の3拠点で事業展開するIT企業です。
現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。
いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。
システム開発依頼・お見積もり大歓迎!
また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」「WEBディレクター」を積極採用中です!
インターンや新卒採用も行っております。
以下よりご応募をお待ちしております!
https://rightcode.co.jp/recruit
ライトコードの日常12月 1, 2023ライトコードクエスト〜東京オフィス歴史編〜
ITエンタメ10月 13, 2023Netflixの成功はレコメンドエンジン?
ライトコードの日常8月 30, 2023退職者の最終出社日に密着してみた!
ITエンタメ8月 3, 2023世界初の量産型ポータブルコンピュータを開発したのに倒産!?アダム・オズボーン