移动面积算法——捕捉移动波峰

博客 动态
0 301
优雅殿下
优雅殿下 2022-02-09 15:55:11
悬赏:0 积分 收藏

移动面积算法——捕捉移动波峰

一、为什么使用移动面积算法

 

 

 解:常规波峰判定是采用高低阈值的方法进行筛除,但会出现如图情况。左边噪声高于实际波峰(绿色)高度,甚至高于阈值(红色),会造成波峰高度的误判等。

 

二、移动面积算法的雏形与原理

 

 

 选定矩形(mask),此处我设其宽为波峰的1/2,高为波峰最高,面积为S2。通过mask在I-V图中,从左到右移动,计算出每一次移动后mask捕捉到的波峰面积(黄色),黄色面积为S1。

  通过ratio = S1 / S2,将得到每一个 mask 位置不一样的黄色面积占比,我称之为 ratio。如图,明显可见最右边的 mask 中,黄色占比大于50%(或0.5)。则阈值考量可以设置为0.5~1之间,具体须看实际数据。

  ratio > 0.5 时,则 mask 捕捉到了波峰位置。

  而且值得注意的是,图二和图一的峰的横坐标位置不一致,由此可见移动面积算法存在高灵活性的巨大优势。

 

三、代码注释0~7的细解:

0. 限制范围0.1V~1.1V (排除0.1V附近的干扰噪声)

 

1. 计算峰最高点位置,坐标(V, Imax)

 

2. 确定移动矩形(称之为mask) 的长和宽,delta_I 和delta_V

 

3. 确定 mask 移动的初始电压位置(X轴)为0.1V, 限制范围:0A<I<Imax(峰最高点纵坐标)
面积:
area = (Vn - Vn-1) * (In + In-1)/2 ;

比值: # mask从横坐标左往右移动,分别计算并保存每次的ratio值
ratio = 峰波形在mask 所占面积 / mask 总面积 ;

 

4. 从保存的所有ratio 值,求最大ratio(最大ratio值大于0.5,则可说明波峰存在)

 

5.五点法确定峰顶点位置

(1)从保存的所有ratio 值,求ratio>0.7 的数量 # 标准峰型通常为30个,此数量体现峰的宽度,筛除脉冲形状的峰
(2)五点法,前三点上升沿与后三点下降沿,判断峰是否为峰,代码中满足①②时皆为波峰顶点,③存在情况过多且不符。

 

 

 

6. 计算单峰面积(不包括其余噪声,即从峰宽两侧算起) # 面积算法(包括噪声)

此计算公式存在些许不足,有一定偏差,可忽视不用。

 

7. fitter 滤波。

拟合波形,多项式拟合法。作参考。 # 目前屏蔽,暂时不用

 

 

 

四、全篇代码:

def get_moverectangleratiovalue(volts, diffcrnts, weno, movevspos=0.1, detwidth=0.1, noise_line=0, ratiothreshold=0.40, ratiocntlimit=0):     #--Mei    """Get moving rectangle mask ratio value.(Area of interest is called mask.)    The area occupancy ratio    Args:        volts (list) -- List of voltages (float) in volts        diffcrnts (list) -- List of hybrid-to-base current differences (float) in amps        weno(int) -- we number        detwidth (float) -- The width of the interval used to detect the calculated mask voltage range(Usually 0~0.2V)        movevspos (float) -- The starting voltage value used to move the mask (Usually 0~0.5V)        noise_line (float) -- Below the apparent noise line is noise, and the ampere value of the noise line is used as                              the baseline for mask calculation        ratiothreshold (float) -- In the mask, the threshold of the ratio of the waveform area to the total area of the mask                             rectangle (threshold size: 0~1) If there is a peak, usually 0.5~1        ratiocntlimit (int) -- Determines the peak width.If the ratio count(>0.5)is greater than the limit,    Returns: result (bool) -- Determine whether there is a peak by this ratio and return the result.                                  If the ratio is higher than "ratiothreshold" return True,                                  and lower than "ratiothreshold" return False.             ratio (float*1.00) -- The ratio of the area of the waveform to the area of the mask             ampere_max (float) -- Maximum current value             peakmainarea (float) -- Peak area except for noise range(not the peak range)    """    # init parameter    result = False    result_code = 0        # Tracing the cause of NEG results (0 ~ 5) 0 means POS result. Other means NEG.    limit_volts = []    # V>0    limit_amps = []     # when V>0    peak_pos = (0, 0)           # max peak pos (V, A)    mask_wavearea = 0    mask_waveareamovelist = []    ratio_list = []    list_area = []    ratio_cnt = 0    peakposlis = []    def find_cmax(volts, diffcrnts, detv1=0.1, detv2=1.0):        # Find max diff current value        maxcrnt = -1e20        maxv = 9  # Impossibly large value        for x, volt in enumerate(volts):            if volt < detv1:                continue            if volt > detv2:                break            if diffcrnts[x] > maxcrnt:                maxcrnt = diffcrnts[x]                maxv = volts[x]        return (maxcrnt, maxv)    # Normally we will set the noise to 0 or to 2.5e-8 A(real noise) in order to count.    for index, volt in enumerate(volts):        if volt >= movevspos:   # Usually the point before 0.1V is not used as a reference.            limit_volts.append(volt)            limit_amps.append(diffcrnts[index])    if len(limit_amps) <= 0:        limit_amps.append(0)    # current_max = max(limit_amps)    # current_max = get_peakdetectionvalue    # 0. Ensure that 'limit_volts' and 'ratio_list' list have the same number of indexes    current_max, volt_max  = find_cmax(volts, diffcrnts)    if current_max < noise_line:     # noise line is 0 to 2.5e-8(A)        print("return False: Max ampere is lower than noise line")        result_code = 1        return False, 0.05, 0, 0    # print("===================================================================")    # 1.The first "for" loop is for finding the peak pos    for x, volt in enumerate(volts):        # Now I limit the first voltage start value (volt > 0).        if diffcrnts[x] == current_max and volt >= 0:            # find max peak point pos (volt, ampere)            peak_volt = volts[x]            peak_pos = (peak_volt, current_max)    # 2.The second "for" loop is for finding the valley pos.    # Now that the valley pos is not used, if there is a better way to judge the valley pos ??    for x, volt in enumerate(volts):        if volt > 0 and volt < peak_pos[0] and diffcrnts[x-1] < noise_line and diffcrnts[x] >= noise_line:            valley_pos = (volt, diffcrnts[x])    # Mask is the valley pos(x_min, y_min) between peak pos(x_max,y_max) of rectangle area.    delta_V = detwidth #(peak_pos[0] - valley_pos[0])    # Don't pay attention to the impact of noise line for now.(Reserved for subsequent development.)    delta_A = (peak_pos[1] - noise_line)        # eliminate noise about 0.25e-7 or lower.    mask_totalarea = delta_V * delta_A    # print("mask total area = %s" % mask_totalarea)    # 3. The third "for" loop is for calculating every moving wave area on mask.    def mask_algorithm(volts, diffcrnts, noise_line, movevspos):        for x, volt in enumerate(volts):            # Determine the starting point pos of the mask            # checking mask range            mask_Vmax = volt + delta_V      # new range from max volt of mask            mask_rect = (volt, noise_line, mask_Vmax, peak_pos[1])     # (Vmin,Amin,Vmax,Amax)            if volt >= movevspos:    # movevspos or the first volt.This is defined mask moving start pos.                # count the wave area on mask                mask_wavearea = 0       # reset new wave area is 0                for index, v in enumerate(volts):                    # Use this FOR loop to count waveform surface base in rectangular area.                    # Only count the waveform area of the rectangular area.                    if v >= volt and v <= mask_Vmax:                        # Each cycle calculates a slice in the mask,                        # and each slice is determined by two adjacent points of VOLT.                        ampere2 = diffcrnts[index]                        ampere1 = diffcrnts[index - 1]                        # Ensure that diffcrnts are larger than the noise line.Otherwise equal to the noise line.                        if diffcrnts[index] < noise_line:                            ampere2 = noise_line                        if diffcrnts[index - 1] < noise_line:                            ampere1 = noise_line                        if diffcrnts[index] > peak_pos[1]:                            ampere2 = peak_pos[1]                        if diffcrnts[index - 1] > peak_pos[1]:                            ampere1 = peak_pos[1]                        # Only count ampere more than noise_line 0.25(e-7)                        # area = (V1-V2)*(A2+A1-3*A0)/2;  A0 = 0                        area = (v - volts[index - 1]) * (ampere2 + ampere1 - (3 * noise_line)) / 2                        # print("area = (%s - %s) * (%s + %s -3(%s)) / 2 = %s" % (volts[index], volts[index-1], ampere2, ampere1, noise_line, area))                        mask_wavearea += area                        list_area.append(area)                # print("every mask area = %s" % mask_wavearea)                # print("********************* every wave area is equaled all slice area ************************")                mask_waveareamovelist.append(mask_wavearea)                # print("mask wave area = %s" % mask_wavearea)        # 3.1 Save the waveform to the mask area ratio        for singlearea in mask_waveareamovelist:            ratio_list.append(round(singlearea / mask_totalarea, 5))        # print(ratio_list)        # print("++++++++++++++++++++++++++ all for loop end +++++++++++++++++++++++++++++++++ ")    # 3. Calculating every moving wave area on mask.    mask_algorithm(volts, diffcrnts, noise_line, movevspos)    # 4.When there is more than one peak or no peak, the judgment result is False.    ratio = max(ratio_list)    def multipeaks_screen(ratio, ratio_cnt, result, result_code):        maxratio = 0        if ratio >= ratiothreshold:            # if someone scale more than ratio threshold(usually 0.5)    计数:比值大于阈值的情况            for rat in ratio_list:                if rat >= ratiothreshold:                    ratio_cnt += 1            if ratio_cnt > 30:          # ratio_cnt 体现的是峰的宽度,ratio_cnt 越大峰越宽。                result_code = 3                result = False                maxratio = 0            # print("weno = %s,  ratio count = %s, max-ratio = %s" % (weno, ratio_cnt, ratio))            # Normally it is 30. You can also use the PRINT above to check the ratio count of the normal peak shape.            if ratio_cnt > ratiocntlimit and ratio_cnt <= 30:                # If the ratio count is greater than the limit, the waveform is too wide.                result = False                maxratio = ratio                # If the waveform is too wide, we need confirm the waveform is whether the waveform is multiple peaks.                if ratio_cnt <= 25 and ratio_cnt > 0:                    peak_cnt = 0                    volts_xlist = []                    for i, v in enumerate(volts):                        volts_xlist.append(i)                    for x, ratio_x in enumerate(ratio_list):                        # If there is not noly one peak(maybe more), I set a limit for cheaking a thing called'AS A PEAK'.                        if ratio_x > ratiothreshold:        # 五点法确定峰顶点位置                            if x > 2 and x+2 <= len(ratio_list)-1 :#and x % 2 == 0:                                if (ratio_x - ratio_list[x-1]) >= 0 and (ratio_x - ratio_list[x+1]) >= 0:                                    if (ratio_list[x-2] - ratio_list[x-1]) < 0 and (ratio_list[x+1] - ratio_list[x+2]) > 0:                                        # Limit the peak appearing before 0.2V, which is regarded as invalid.                                        if limit_volts[x] > 0.4 and limit_volts[x] < 1.1:                                            peak_cnt += 1                                            maxratio = ratio_x                                            # peakposlis.append((volts[x], diffcrnts[x]))     # log peak pos(Shaped like a peak)                                            # print("Peak pos = (%s, %s); index = %s" % (volts[x], diffcrnts[x], x))                                            # print("Peak volt pos= %s" % (limit_volts[x]))                    # print("peak cnt = %s" % peak_cnt)                    if peak_cnt > 1 or peak_cnt == 0:                        result = False                        maxratio = 0                        result_code = 4                    if peak_cnt == 1:                        result = True            # else:            #     result = True        else:            result_code = 2        return  maxratio, ratio_cnt, result, result_code    # 5.  Multi peaks screening    ratio, ratio_cnt, result, result_code = multipeaks_screen(ratio, ratio_cnt, result, result_code)    def calculate_area(x, noiseline, volts):        # Area calculation formula        a2 = diffcrnts[x]        a1 = diffcrnts[x - 1]        area = (volts[x] - volts[x - 1]) * (a2 + a1 - (3 * noiseline)) / 2        return area    # 6. Calculate the only one peak area    if result:      # 多峰筛除之后,Ture结果进行        # calculate peak area 计算单峰面积        peakposlis.append(peak_pos)        # print(peak_pos)        peakarealis = []        temp_min = peak_pos[1]/2        log_index = 0        if peakposlis != []:            for m, (x, y) in enumerate(peakposlis):                for n, volt in enumerate(volts):                    if volt > 0.3:                        value = abs(diffcrnts[n] - (y/2))   # half peak high 半峰高度与                        if value <= temp_min:                            temp_min = value                            log_index = n                # print(peakposlis[m][0])                # print(volts[log_index])                min_halfwidth = round(abs(peakposlis[m][0] - volts[log_index]), 4)                # print("min_halfwidth=%s" % min_halfwidth)                x_min = peakposlis[m][0] - (min_halfwidth * 2)                x_max = peakposlis[m][0] + (min_halfwidth * 2)                # print("x_min=%s, x_max=%s" % (x_min,x_max))                peakarea = 0                for index, v in enumerate(volts):                    if v >= x_min and v <= x_max:                        peakarea += calculate_area(index, noise_line, volts)                # print("peak area = %s" % peakarea)                peakarealis.append(peakarea)        peakmainarea = 0        maxv = 0        for peakarea in peakarealis:            if maxv < peakarea:                maxv = peakarea        peakmainarea = maxv        if peakmainarea == 0:            result = False            result_code = 4        ra_index = ratio_list.index(max(ratio_list, key=abs))        # print("ratio max index = %s " % ra_index)        # print("===========================================================================")    else:        peakmainarea = 0    # 7.    if not result and dglobs.fiter_enable and False:  # debug fitting and sum res        # Before fitting debug        befo_maxratio = ratio        befo_res = result        calculate_y = fitter.polynomial_fittodraw(volts, diffcrnts, 12, weno)    # only a fiter and draw to show        # Fit the original data and output fitted current data        diffs_fited = fitter.polynomial_fitting(volts, diffcrnts, 15)   # complex degree = 15        mask_algorithm(volts, diffs_fited, noise_line, movevspos)        after_maxratio = max(ratio_list)        after_res = None        if after_maxratio > ratiothreshold:            after_res = True        else:            after_res = False            result_code = 5        result = after_res        # print("enable fitter and adjust result")    # getamps_leastsquarefitting(volts, diffcrnts)    # print("-----------------Area-Mei----------------")    # print("we: %s, res: %s, ratio: %s, A max: %s, peak area: %s"    #       % (weno, result, ratio, current_max, peakmainarea))    # debug+    # calculate_y = fitter.polynomial_fittodraw(volts, diffcrnts, 12, weno)  # only a fiter and draw to show    # time.sleep(1)    # Save volt (value/pos) of the max ampere    # for index, volt in enumerate(volts):    #     if diffcrnts[index] == current_max:    #         dglobs.allwes_peakpos.append(volt)    # debug+    # print("weno = %s,  ratio count = %s, max-ratio = %s" % (weno, ratio_cnt, ratio))    # print("result code = %s" % result_code)    return result, ratio, current_max, peakmainarea, result_code

 

posted @ 2022-02-09 15:02 Lorzen 阅读(52) 评论(0) 编辑 收藏 举报
回帖
    优雅殿下

    优雅殿下 (王者 段位)

    2018 积分 (2)粉丝 (47)源码

    小小码农,大大世界

     

    温馨提示

    亦奇源码

    最新会员