电能质量工具

免费下载Matlab 电能质量仿真工具,带有GUI界面

预览截图

应用介绍

免费下载Matlab 电能质量仿真工具,带有GUI界面

%% Power Quality Tool
% A lightweight application for investigating common power quality measures of
% arbitrary power system measurement waveforms and groups of such waveforms.

function PowerQualityTool
    % Create invisible figure window with no title, menus, or toolbar.
    f = figure('Visible', 'off', ...
               'NumberTitle', 'off', ...
               'MenuBar', 'none', ...
               'ToolBar', 'none', ...
               'Position', [360, 500, 460, 315]);
    
    % Create tabs for time and frequency domain plots for easy switching.
    hPlotTabGroup = uitabgroup('Parent', f, ...
                               'Units', 'Pixels', ...
                               'Position', [0, 0, 305, 315], ...
                               'SelectionChangedFcn', {@tabSelectCallback});
    hTimePlotTab = uitab('Parent', hPlotTabGroup, ...
                         'Title', 'Time Domain');
    hFrequencyPlotTab = uitab('Parent', hPlotTabGroup, ...
                              'Title', 'Frequency Domain');
    
    % Create buttons, label, and drop-down, and then align them.
    hLoad = uicontrol('Style', 'pushbutton', ...
                      'String', 'Load', ...
                      'Position', [345, 250, 70, 25], ...
                      'Callback', {@loadButtonCallback});
    hLabel = uicontrol('Style', 'text', ...
                       'String', 'Select Data', ...
                       'Position', [355, 225, 60, 15]);
    hPopup = uicontrol('Style', 'popupmenu', ...
                       'String', {'No Data'}, ...
                       'Position', [330, 195, 100, 25], ...
                       'Callback', {@popupMenuCallback});
    hPanel = uipanel('Parent', f, ...
                     'Title', 'Results', ...
                     'Units', 'Pixels', ...
                     'Position', [315, 10, 125, 170]);
    hTable = uitable('Parent', hPanel, ...
                     'Data', {'<HTML>f<sub>0</sub></HTML>', 0; ...
                              '<HTML>V<sub>RMS</sub></HTML>', 0; ...
                              '<HTML>I<sub>RMS</sub></HTML>', 0; ...
                              '<HTML>THD<sub>V</sub></HTML>', 0; ...
                              '<HTML>THD<sub>I</sub></HTML>', 0; ...
                              'PF', 0; ...
                              'DF', 0}, ...
                     'RowName', {}, ...
                     'ColumnName', {'', 'Value'}, ...
                     'ColumnWidth', {35, 'auto'}, ...
                     'Position', [5, 5, 115, 150]);
    align([hLoad, hLabel, hPopup, hPanel, hTable], 'Center', 'None');
    
    % Create axes for both time and frequency plots within respective tabs.
    hTimeAxes = axes('Parent', hTimePlotTab, ...
                     'Units', 'Pixels', ...
                     'Position', [50, 60, 200, 200]);
    hFrequencyAxes = axes('Parent', hFrequencyPlotTab, ...
                          'Units', 'Pixels', ...
                          'Position', [50, 60, 200, 200]);
    
    % Change to normalized units for relative positioning (vs. absolute).
    f.Units = 'normalized';
    hPlotTabGroup.Units = 'normalized';
    hTimePlotTab.Units = 'normalized';
    hFrequencyPlotTab.Units = 'normalized';
    hTimeAxes.Units = 'normalized';
    hFrequencyAxes.Units = 'normalized';
    hLoad.Units = 'normalized';
    hLabel.Units = 'normalized';
    hPopup.Units = 'normalized';
    hPanel.Units = 'normalized';
    hTable.Units = 'normalized';
    
    % Assign a name to the tool's title bar.
    f.Name = 'Power Quality Tool';
    
    % Center the window on the screen.
    movegui(f, 'center')
    
    % Show window with axes and controls.
    f.Visible = 'on';
    
    % Create data and results structures.
    Data = struct(); % for user input data
    Results = struct(); % for calculated values
    currentData = [];
    currentName = '';
    currentTab = hPlotTabGroup.SelectedTab.Title;
    
    % Callback for the tab group tab selection.
    function tabSelectCallback(source, eventdata)
        currentTab = eventdata.NewValue.Title;
        if ~strcmp(currentName, '')
            updatePlot(currentData, currentName, currentTab, Results);
        end
    end
    
    % Callback for the drop-down menu.
    function popupMenuCallback(source, eventdata)
        % Determine if single or multiple data sets (buses) and assign a set.
        [~, numData] = size(Data);
        currentName = source.String{source.Value};
        
        if numData == 1
            currentData = [Data.Time, Data.Voltage, Data.Current];
        else
            index = find(strcmp(currentName, {Data.Name}));
            currentData = [Data(index).Time, Data(index).Voltage, Data(index).Current];
        end
        
        % Update results data and plot of selected data set.
        [currentData, Results] = updateResults(currentData, Results);
        updatePlot(currentData, currentName, currentTab, Results);
    end
    
    % Callback for 'Load' button.  Requires data in CSV format, with headers
    % and arranged in columns: Time, Voltage, Current.
    function loadButtonCallback(source, eventdata)
        % Let the user pick a (few) file(s).
        fileTypesCell = {'*.csv;*.txt;*.dat', ...
                         'Comma-Separated Values (*.csv,*.txt,*.dat)'; ...
                         '*.*', ...
                         'All Files (*.*)'};
        [fileName, pathName] = uigetfile(fileTypesCell, ...
                                         'Select waveform data', ...
                                         'MultiSelect', 'on');
        
        % Read and prepare the data.
        if ~(isequal(fileName, 0) || isequal(pathName, 0)) % user didn't cancel
            if iscell(fileName)
                [~, cellSize] = size(fileName);
                for i = 1:cellSize
                    tempData = csvread([pathName, fileName{i}], 1, 0);
                    Data(i).Name = fileName{i}(1:end - 4);
                    Data(i).Time = tempData(:, 1) - tempData(1, 1); % remove offset
                    Data(i).Voltage = tempData(:, 2);
                    Data(i).Current = tempData(:, 3);
                end
                currentData = [Data(1).Time, Data(1).Voltage, Data(1).Current];
            else
                tempData = csvread([pathName, fileName], 1, 0);
                Data.Name = fileName(1:end - 4);
                Data.Time = tempData(:, 1) - tempData(1, 1); % remove offset
                Data.Voltage = tempData(:, 2);
                Data.Current = tempData(:, 3);
                currentData = [Data.Time, Data.Voltage, Data.Current];
            end
            
            % Inject the data and its identifiers into the appropriate places.
            hPopup.String = {Data.Name};

            % Automatically select the first file.
            hPopup.Value = 1;
            currentName = hPopup.String{1};

            % Update result data and plot with (first) loaded data.
            [currentData, Results] = updateResults(currentData, Results);
            updatePlot(currentData, currentName, currentTab, Results);
        end
    end

    % Result update function.  Updates RMS, THD, PF and DF.  Updates frequency
    % domain values for both voltage and current.
    function [currentData, Results] = updateResults(currentData, Results)
        % Gather time information.
        period60 = 1/60; % static 60 Hz cycle definition
        Results.Tm = currentData(end, 1); % measurement period
        
        % Create vertical line t parameters to indicate periods of 60 Hz.
        Results.timeVerts = struct();
        Results.nPeriods = floor(Results.Tm/period60);
        
        for iVerts = 1:Results.nPeriods
            Results.timeVerts(iVerts).t = [iVerts*period60, iVerts*period60];
        end
        
        [currentData, Results] = cleanData(currentData, Results);
        
        % Calculate RMS voltage and current of composite waveform.
        Results.RMSv = rms(currentData(:, 2));
        Results.RMSi = rms(currentData(:, 3));
        
        % Calculate and store peaks for time domain annotations.
        [Results.vtPeaks, Results.vtPeakIndices] = findpeaks(currentData(:, 2));
        [Results.itPeaks, Results.itPeakIndices] = findpeaks(currentData(:, 3));
        
        % Find fundamental frequency allowing search of entirety of waveforms.
        Results = findFundamentalFrequency(currentData, Results);
        
        fftVoltageResult = abs(fft(Results.correctedVoltage)/(Results.nTimePoints + 1));
        fftVoltageResult((Results.nTimePoints - 1)/2 + 1:end) = [];
        fftVoltageResult(2:end - 1) = 2*fftVoltageResult(2:end - 1);
        
        fftCurrentResult = abs(fft(Results.correctedCurrent)/(Results.nTimePoints + 1));
        fftCurrentResult((Results.nTimePoints - 1)/2 + 1:end) = [];
        fftCurrentResult(2:end - 1) = 2*fftCurrentResult(2:end - 1);
        
        % Truncate all results to 5 kHz.        
        Results.frequency = Results.sampleFrequency*(0:(Results.nTimePoints/2 + 1))/Results.nTimePoints;
        Results.frequency(Results.frequency > 5000) = [];
        Results.frequency = Results.frequency';
        numFrequencies = length(Results.frequency);
        Results.fVoltage = fftVoltageResult(1:numFrequencies);
        Results.fCurrent = fftCurrentResult(1:numFrequencies);
        
        % Calculate power factor.
        dt = 1/Results.sampleFrequency;
        Pavg = Results.correctedVoltage'*Results.correctedCurrent*dt/(Results.nPeriods/60);
        Results.powerFactor = Pavg/(Results.RMSv*Results.RMSi);
        
        % Calculate harmonic component amplitudes.
        harmonicFrequencies = 60*(1:floor(5e3/60)); % harmonics to 5 kHz
        Results.harmonicVoltage = [];
        Results.harmonicCurrent = [];
        
        for harmonic = 1:length(harmonicFrequencies)
            [frequencyIndex, ~] = find(Results.frequency == harmonicFrequencies(harmonic));
            Results.harmonicVoltage = [Results.harmonicVoltage; Results.fVoltage(frequencyIndex)];
            Results.harmonicCurrent = [Results.harmonicCurrent; Results.fCurrent(frequencyIndex)];
        end
        
        % Calculate THDv and THDi.
        Results.THDv = norm(Results.harmonicVoltage(2:end), 2)/Results.harmonicVoltage(1);
        Results.THDi = norm(Results.harmonicCurrent(2:end), 2)/Results.harmonicCurrent(1);
        
        % Calculate displacement factor using PF and THDi/v.
        Results.displacementFactor = sqrt(1 + Results.THDv^2)*sqrt(1 + Results.THDi^2)*Results.powerFactor;
        
        % Calculate and store peaks for frequency domain annotations.
        prominenceV = 0.01*max(Results.fVoltage);
        prominenceI = 0.01*max(Results.fCurrent);
        [Results.vfPeaks, Results.vfPeakIndices] = findpeaks(Results.fVoltage, ...
                                                             'MinPeakProminence', prominenceV);
        [Results.ifPeaks, Results.ifPeakIndices] = findpeaks(Results.fCurrent, ...
                                                             'MinPeakProminence', prominenceI);
        
        
        % Assign data to table for display.
        tableData = tablePrep(Results);
        hTable.Data(:, 2) = tableData;
    end

    % Cleans data for processing by smoothing and extending length to a power of
    % two number of data points via spline interpolation.
    function [cleanedData, Results] = cleanData(currentData, Results)
        % Define final measurement time point.
        Tm = currentData(end, 1);
        
        % Pick out last periodic point and set aside periodic segment.
        sampleTimeGoal = Results.nPeriods/60;
        [~, timeIndex] = min(abs(currentData(:, 1) - sampleTimeGoal));
        Tp = currentData(timeIndex, 1);
        truncationIndices = 1:timeIndex - 1;
        timeVector = currentData(truncationIndices, 1);
        nTimePoints = length(timeVector);
        
        % Interpolate
        nFullTimePoints = ceil(2^(nextpow2(nTimePoints))*Tm/Tp);
        cleanedData(:, 1) = linspace(0, Tm, nFullTimePoints);
        cleanedData(:, 2) = spline(currentData(:, 1), currentData(:, 2), cleanedData(:, 1));
        cleanedData(:, 3) = spline(currentData(:, 1), currentData(:, 3), cleanedData(:, 1));
        
        sampledVoltage = currentData(truncationIndices, 2);
        sampledCurrent = currentData(truncationIndices, 3);
        
        % Prepare for FFT.
        nTimePointsGoal = 2^nextpow2(nTimePoints);
        timeVectorGoal = linspace(0, sampleTimeGoal, nTimePointsGoal);
        correctedVoltage = spline(timeVector, sampledVoltage, timeVectorGoal)';
        correctedCurrent = spline(timeVector, sampledCurrent, timeVectorGoal)';
        
        % Assign values for next segment
        Results.timeVector = timeVectorGoal(1:end - 1);
        Results.nTimePoints = length(Results.timeVector);
        Results.sampledVoltage = sampledVoltage;
        Results.correctedVoltage = correctedVoltage(1:end - 1);
        Results.sampledCurrent = sampledCurrent;
        Results.correctedCurrent = correctedCurrent(1:end - 1);
        Results.sampleFrequency = Results.nTimePoints/timeVectorGoal(end);
    end

    % Fundamental frequency detection function.  Finds fundamental frequency by
    % averaging all distances to peaks within 0.1% magnitude of each other.
    function Results = findFundamentalFrequency(currentData, Results)
        time = currentData(:, 1);
        dt = mean(diff(time));
        freq = [];
        
        vpks = Results.vtPeaks;
        vpkt = Results.vtPeakIndices;
        ipks = Results.itPeaks;
        ipkt = Results.itPeakIndices;
        
        % Periodic testing parameters.
        runningAverage = 1/60; % initialize to line frequency
        identified = 0;
        
        % Find time gaps between peaks of similar voltage magnitude.
        for vPeak = 1:length(vpks)
            tempIdx = vpkt;
            tempIdx(vPeak) = []; % exclude test point
            vField = vpks;
            vField(vPeak) = [];
            vDiff = vpks(vPeak) - vField; % field points
            for idx = 1:length(vDiff)
                if vDiff(idx) <= 0.0001*vpks(vPeak) % difference is <= 0.01%
                    timeDiff = abs(time(tempIdx(idx)) - time(vpkt(vPeak)));
                    count1 = 1;
                    count2 = 1;
                    
                    % Test for multiple/fractional period.
                    while ~identified
                        ratio = timeDiff/runningAverage;
                        
                        if ratio > 1.01 % more than one period +/- 1%
                            count2 = count2 + 1;
                            timeDiff = timeDiff*count1/count2;
                        elseif ratio < 0.99 % less than one period +/- 1%
                            count1 = count1 + 1;
                            timeDiff = timeDiff*count1/count2;
                        else % about one period +/- 10%
                            if rem(count1, count2) == 0 % near integer period, keep
                                freq = [freq; 1/timeDiff];
                                runningAverage = mean([1./freq; runningAverage]);
                                identified = 1;
                            else % we have a fraction of a period, discard
                                identified = 1;
                            end
                        end
                    end
                end
                identified = 0;% prepare for next iteration
            end
        end
        
        % Find time gaps between peaks of similar current magnitude.
        for iPeak = 1:length(ipks)
            tempIdx = ipkt;
            tempIdx(iPeak) = []; % exclude test point
            iField = ipks;
            iField(iPeak) = [];
            iDiff = ipks(iPeak) - iField; % field points
            for idx = 1:length(iDiff)
                if iDiff(idx) <= 0.0001*ipks(iPeak) % difference is <= 0.01%
                    timeDiff = abs(time(tempIdx(idx)) - time(ipkt(iPeak)));
                    count1 = 1;
                    count2 = 1;
                    
                    % Test for multiple/fractional period.
                    while ~identified
                        ratio = timeDiff/runningAverage;
                        
                        if ratio > 1.01 % more than one period + 1%
                            count2 = count2 + 1;
                            timeDiff = timeDiff*count1/count2;
                        elseif ratio < 0.99 % less than one period - 1%
                            count1 = count1 + 1;
                            timeDiff = timeDiff*count1/count2;
                        else % about one period +/- 1%
                            if rem(count1, count2) == 0 % near integer period, keep
                                freq = [freq; 1/timeDiff];
                                runningAverage = mean([1./freq; runningAverage]);
                                identified = 1;
                            else % we have a fraction of a period, discard
                                identified = 1;
                            end
                        end
                    end
                    identified = 0;% prepare for next iteration
                end
            end
        end
        
        Results.f0 = mean(freq);
        
    end

    % Table data preparation function.  Formats data according to type,
    % magnitude, and precision.
    function tableData = tablePrep(Results)
        tableData = {Results.f0; ...
                     Results.RMSv; ...
                     Results.RMSi; ...
                     Results.THDv; ...
                     Results.THDi; ...
                     Results.powerFactor; ...
                     Results.displacementFactor};
        
        % Format fundamental frequency for precision.
        tableData{1} = sprintf('%3.1f Hz', tableData{1});
        
        % Format RMS voltage for expected ranges.
        if Results.RMSv >= 1e6
            tableData{2} = sprintf('%5.3f MV', tableData{2}/1e6);
        elseif Results.RMSv >= 1e3
            tableData{2} = sprintf('%5.3f kV', tableData{2}/1e3);            
        else
            tableData{2} = sprintf('%5.3f V', tableData{2});
        end
        
        % Format RMS current for expected ranges.
        if Results.RMSi >= 1e3
            tableData{3} = sprintf('%5.3f kA', tableData{3}/1e3);
        elseif Results.RMSi >= 1
            tableData{3} = sprintf('%5.3f A', tableData{3});
        else
            tableData{3} = sprintf('%5.3f mA', tableData{3}/1e-3);
        end
        
        % Format voltage THD as a percent.
        tableData{4} = sprintf('%4.4f %%', tableData{4}*100);
        
        % Format current THD as a percent.
        tableData{5} = sprintf('%4.4f %%', tableData{5}*100);
        
        % Format power factor, keeping up to 4 decimal places.
        tableData{6} = sprintf('%.4f', tableData{6});
        
        % Format displacement factor, keeping only two decimal places.
        tableData{7} = sprintf('%.2f', tableData{7});
            
    end

    % Plot update function.  Updates appropriate plot with fresh data after load
    % or on drop-down selection.
    function updatePlot(currentData, currentName, selectedTab, Results)
        switch selectedTab
            case 'Time Domain'
                axes(hTimeAxes);
                cla reset;
                yyaxis left
                plot(currentData(:, 1), currentData(:, 2));
                title([currentName, ' Waveforms'])
                xlabel('Time')
                ylabel('Voltage')
                grid on;
                yyaxis right
                plot(currentData(:, 1), currentData(:, 3));
                ylabel('Current')

                % Include period indicator lines.
                for iVerts = 1:Results.nPeriods
                    Results.timeVerts(iVerts).y = hTimeAxes.YLim;
                    x = Results.timeVerts(iVerts).t;
                    y = Results.timeVerts(iVerts).y;
                    line(x, y, 'Color', 'black');
                end
                
                % Annotate peaks.
                ivVals = [max(Results.vtPeaks), max(Results.itPeaks)];
                tVals = [currentData(Results.vtPeakIndices(Results.vtPeaks == ivVals(1)), 1), ...
                         currentData(Results.itPeakIndices(Results.itPeaks == ivVals(2)), 1)];
                labels = {sprintf('V_{pk} = %.2g V', ivVals(1)), ...
                          sprintf('I_{pk} = %.2g A', ivVals(2))};
                
                % Assign text location based on peak time location.
                if tVals(1) <= currentData(end, 1)/2
                    directionV = 'left';
                else
                    directionV = 'right';
                end
                
                if tVals(2) <= currentData(end, 1)/2
                    directionI = 'left';
                else
                    directionI = 'right';
                end
                
                % Place voltage peak annotation.
                yyaxis left
                vPeakLabel = text(tVals(1), ivVals(1), labels{1});
                vPeakLabel.VerticalAlignment = 'top';
                vPeakLabel.HorizontalAlignment = directionV;
                vPeakLabel.Color = hTimeAxes.YAxis(1).Color;
                
                % Place current peak annotation.
                yyaxis right
                iPeakLabel = text(tVals(2), ivVals(2), labels{2});
                iPeakLabel.VerticalAlignment = 'bottom';
                iPeakLabel.HorizontalAlignment = directionI;
                iPeakLabel.Color = hTimeAxes.YAxis(2).Color;
            
            case 'Frequency Domain'
                axes(hFrequencyAxes);
                cla reset;
                yyaxis left
                bar(Results.frequency/60, Results.fVoltage, 6)
                title([currentName, ' Spectrum'])
                xlabel('Harmonic')
                ylabel('Voltage')
                grid on
                yyaxis right
                bar(Results.frequency/60, Results.fCurrent, 3)
                ylabel('Current')
                
                % Create significant (1%) harmonic annotations.
                vVals = Results.vfPeaks;
                iVals = Results.ifPeaks;
                fValsV = Results.frequency(Results.vfPeakIndices)/60;
                fValsI = Results.frequency(Results.ifPeakIndices)/60;
                vLabels = [];
                iLabels = [];
                
                for peak = 1:length(vVals)
                    vLabels = [vLabels, sprintf('V_{%d} = %3.4g V\n', fValsV(peak), vVals(peak))];
                end
                
                for peak = 1:length(iVals)
                    iLabels = [iLabels, sprintf('I_{%d} = %3.4g A\n', fValsI(peak), iVals(peak))];
                end
                      
                % Place peak annotations in legend.
                legend(vLabels, iLabels)
                
            otherwise
                
        end
    end
end

文件列表(部分)

名称 大小 修改日期
license.txt0.70 KB2017-11-21
PowerQualityTool.m5.10 KB2017-11-21
PowerQuality_powermatlab0.00 KB2018-03-22

立即下载

相关下载

[各种电能质量扰动的MATLAB模型] 此文档的函数提供了各种电能质量扰动的MATLAB模型。
[DSTATCOM 改善分布式系统电能质量] DSTATCOM 改善分布式系统电能质量
[电能质量工具] 免费下载Matlab 电能质量仿真工具,带有GUI界面

评论列表 共有 0 条评论

暂无评论

微信捐赠

微信扫一扫体验

立即
上传
发表
评论
返回
顶部