【Python/Pandas】パルス信号の時定数、立ち上がり、立ち下がりを計算

PythonモジュールPandasでパルス信号の立ち上がり、立ち下がりを計算する方法についてソースコード付きでまとめました。

【Pandas】時間信号データの実効値を計算

パルス信号データ(CSV)を読み込んで、波形の時定数、立上り時間、立下り時間、ON(HIGH)の時間を計算します。

種別 概要
立ち上がり時間 時間信号が10%から90%に上がるまでの時間。
立ち下がり時間 時間信号が90%から10%に下がるまでの時間。
時定数 時間信号が10%から63.2%に上がるまでの時間。
RMS 二乗平均平方根(Root Mean Square)。二乗した値の平均を求めその平方根を計算する。
【信号処理】実効値の計算式・計算例(Excel、Pythonなど)
読み込んだデータ
CSV current.csv

current.csv

time,current
-6,0
-5.8,1
-5.6,0
-5.4,0
︙
0,0
0.2,1
0.4,2
︙
2.4,13
2.6,12
2.8,16
︙ 

サンプルコード

# -*- coding: utf-8 -*-
import os
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import math


class Pulse():
    # 波形の各種時間(立上り、下がり、ON)を計算
    def calc_time(self, ts, ys, Ymax=None, Ymin=None, upper_threshold=0.9, lower_threshold=0.1, Tr_start_min=None, Ton_min=None):
        dst = {}

        # 最小時間が指定されていなければ、最も小さい時間をセット
        if Tr_start_min == None:
            Tr_start_min = ts.min()

        # 最小時間が指定されていなければ、最も小さい時間をセット
        if Ton_min == None:
            Ton_min = 0

        if Ymax == None:
            Ymax = ys.max()

        if Ymin == None:
            Ymin = ys.min()

        dst["Ymax"] = Ymax
        dst["Ymin"] = Ymin
        dst["Yamp"] = Yamp = Ymax - Ymin

        dst["upper"] = upper_threshold
        dst["lower"] = lower_threshold

        # 立上り開始時間(信号がしきい値と最小値を超えた直後の時間)を取得
        Trs_start = ts[ys > Yamp * lower_threshold]
        dst["tr_start"] = Tr_start = Trs_start[Trs_start > Tr_start_min].min()

        # 立上り終了時間を取得
        Trs_end = ts[ys > Yamp * upper_threshold]
        dst["tr_end"] = Tr_end = Trs_end[Trs_end > Tr_start].min()

        # 立上り時間を計算
        dst["tr"] = Tr = Tr_end - Tr_start

        # 立下り開始時間を取得( > 立上り終了時間 + ON最小時間)
        try:
            Tfs_start = ts[ys < Yamp * upper_threshold]
            dst["tf_start"] = Tf_start = Tfs_start[Tfs_start >
                                                   Tr_end + Ton_min].min()
        except:
            dst["tf_start"] = Tf_start = ts[-1]

        # ONの時間を計算(立上り終了時間~立下り開始時間)
        dst["ton"] = Ton = Tf_start - Tr_end

        # 立下り終了時間を取得
        try:
            Tfs_end = ts[ys < Yamp * 0.1]
            dst["tf_end"] = Tf_end = Tfs_end[Tfs_end > Tf_start].min()
        except:
            dst["tf_end"] = Tf_end = ts[-1]

        # 立下り時間を計算
        dst["tf"] = Tf = Tf_end - Tf_start

        # 時定数を計算
        taus = ts[ys >= Yamp * 0.632]
        dst["tau"] = tau = taus[taus > Tr_start_min].min() - Tr_start
        self.pulse_times = dst

        return dst

    # 波形の各種時間(立上り、下がり、ON)を計算
    def show_time(self):
        dst = {}
        tr_start = self.pulse_times["tr_start"]
        tr_end = self.pulse_times["tr_end"]
        tr = self.pulse_times["tr"]
        ton = self.pulse_times["ton"]
        tf_start = self.pulse_times["tf_start"]
        tf_end = self.pulse_times["tf_end"]
        tf = self.pulse_times["tf"]
        tau = self.pulse_times["tau"]
        Ymax = self.pulse_times["Ymax"]
        Ymin = self.pulse_times["Ymin"]
        Yamp = self.pulse_times["Yamp"]

        print("Tr start:", tr_start)
        print("Tr end:", tr_end)
        print("Tr:", tr)
        print("Ton:", ton)
        print("Tf start:", tf_start)
        print("Tr end:", tf_end)
        print("Tf:", tf)
        print("Tau:", tau)

    def save_graph(self, x, y, xlabel, ylabel, save_path, label_name="Y"):
        tr_start = self.pulse_times["tr_start"]
        tr_end = self.pulse_times["tr_end"]
        tr = self.pulse_times["tr"]
        ton = self.pulse_times["ton"]
        tf_start = self.pulse_times["tf_start"]
        tf_end = self.pulse_times["tf_end"]
        tf = self.pulse_times["tf"]
        tau = self.pulse_times["tau"]
        Ymax = self.pulse_times["Ymax"]
        Ymin = self.pulse_times["Ymin"]
        Yamp = self.pulse_times["Yamp"]
        upper_threshold = self.pulse_times["upper"]
        lower_threshold = self.pulse_times["lower"]

        # 保存先のディレクトリパスが存在しなければ作成
        dir_path = os.path.dirname(save_path)
        if not os.path.exists(dir_path):
            os.mkdir(dir_path)

        # グラフ化
        ax = plt.axes()
        plt.rcParams['font.family'] = 'Times New Roman'  # 全体のフォント
        plt.rcParams['axes.linewidth'] = 1.0  # 軸の太さ

        # 電流値をプロット
        plt.plot(x, y, lw=1, c="r", alpha=0.7, ms=2, label=label_name)

        # 立上り・下りの開始、終了時間、時定数に垂線をプロット
        plt.vlines(tr_start, min(y), max(y), ls='--',
                   color="b", lw=1, label="Tr start")
        plt.vlines(tr_end, min(y), max(y), ls='--',
                   color="g", lw=1, label="Tr end")
        plt.vlines(tf_start, min(y), max(y),
                   ls='--', lw=1, label="Tf start")
        plt.vlines(tf_end, min(y), max(y), ls='--',
                   color="m", lw=1, label="Tf end")
        plt.vlines(tau, min(y), max(y), ls='-', lw=1, label="Tau")

        # 電流最大値の10%、90%に水平線をプロット
        plt.hlines(Yamp * 0.9, min(x), max(x), ls='--',
                   color="r", lw=1, label="Amp " + str(upper_threshold*100)+"%")
        plt.hlines(Yamp * 0.1, min(x), max(x), ls='--',
                   color="y", lw=1, label="Amp " + str(lower_threshold*100)+"%")

        # グラフの保存
        plt.legend(loc="best")     # 凡例の表示(2:位置は第二象限)
        plt.xlabel('Time[msec]', fontsize=12)  # x軸ラベル
        plt.ylabel('Current[A]', fontsize=12)  # y軸ラベル
        plt.grid()  # グリッドの表示
        plt.legend(loc="best")  # 凡例の表示
        plt.savefig(save_path)
        plt.clf()

    # 実効値の計算
    def calc_rms(self, y):
        return math.sqrt(np.sum(y ** 2) / y.size)  

def main():
    pulse = Pulse()

    # 読み込むCSVファイルのパス
    csv_path = "C:/prog/python/auto/current.csv"
    save_path = "C:/prog/python/auto/"

    # 空のデータフレームを作成
    df = pd.DataFrame({})

    # CSVファイルのロードし、データフレームへ格納
    df = pd.read_csv(csv_path, encoding="UTF-8", skiprows=0)

    # 電流値の列データを取り出し
    Its = df.loc[:, "current"]

    # 経過時間の列データを取り出し
    ts = df.loc[:, "time"]

    # 各種時間を計算(上限90%、下限10%)
    pulse.calc_time(ts, Its, Ymin=0, Ton_min=50)
    times = pulse.show_time()
    pulse.save_graph(
        ts, Its, xlabel="Time[msec]", ylabel="Current[A]", save_path=save_path+"a.png", label_name="I(t)")

    """
        Tr start: 8.4
        Tr end: 328.2
        Tr: 319.8
        Ton: 278.8
        Tf start: 607.0
        Tr end: 620.8
        Tf: 13.799999999999955
        Tau: 114.39999999999999
        RMS: 249.061976770524
    """
    print("RMS:", pulse.calc_rms(Its))

if __name__ == "__main__":
    main()

関連記事
1 【Python】Pandasで信号処理入門
2 【Pandas入門】データ分析のサンプル集
3 【Python入門】サンプル集・使い方
Python
技術雑記

コメント

タイトルとURLをコピーしました