Matrix Converter - Cyclic Behaviour

Good afternoon,

I am currently working on a simulation of a three-phase to three-phase direct matrix converter using the 4-step commutation technique. I am having some difficult with an error that I cannot find the solution to.

The commutation control is implemented via a Simulink block generated from VHDL code (which I have not been able to attach due to restrictions on acceptable file extensions), which interfaces with a PLECS Blockset model of the matrix converter. The overall simulation is executed through a MATLAB script that sets parameters, selects the commutation strategy (I have both 3-step and 4-step implemented, though only the 4-step method is causing issues), and handles data collection and plotting.

The issue arises when I introduce an offset in the current measurement to emulate measurement error. When running the attached MATLAB script under these conditions, I encounter the error included below.

From my understanding, this may be related to the use of near-ideal switches in the converter and clamp circuit, potentially requiring additional non-idealities to ensure numerical stability. I have attempted several approaches, including adjusting on-resistances and forward voltages, as well as adding small parasitic capacitances across switches, but the issue persists.

I would greatly appreciate it if anyone could take a look at the models and advise on what might be causing this behaviour and how it could be resolved. Also please feel free to point me in the right direction of an existing forum post that already covers this if that’s the case, or ask for misisng info!

My software versions are included below.

Kind regards,
Matthew Lloyd


Error message:
Error using PointOfCommutationTesting (line 82)
Error occurred in ‘CurrentCommutationComparison/Circuit’.
Click here to open the PLECS diagnostics viewer.

At t = 0.00212758156000002804: Cyclic behavior during switching.

Switches that bounced:

  • ‘CurrentCommutationComparison/Circuit/Direct Matrix Converter1/With Commutation Scheme/Bidirectional Switch6/MOSFET/D1’
  • ‘CurrentCommutationComparison/Circuit/Direct Matrix Converter1/With Commutation Scheme/Bidirectional Switch6/MOSFET/D2’
  • ‘CurrentCommutationComparison/Circuit/Matrix Converter Clamp/D8’
  • ‘CurrentCommutationComparison/Circuit/Matrix Converter Clamp/D10’
  • ‘CurrentCommutationComparison/Circuit/Matrix Converter Clamp/D11’
  • ‘CurrentCommutationComparison/Circuit/Matrix Converter Clamp/D14’
  • ‘CurrentCommutationComparison/Circuit/Matrix Converter Clamp/D15’
  • ‘CurrentCommutationComparison/Circuit/Matrix Converter Clamp/D17’

Software Versions:
MATLAB & Simulink: 25.2 (25.2.0.3055257)
PLECS Blockset: 4.9.8

CurrentCommutationComparison.slx (3.0 MB)

Matlab script is below as I couldn’t attach it for some reason

clc;
close all;
clear all;

simulation_time = 0.005;

fsw = 50e3;
Ts = 1/fsw;

td_param = Ts * 1e-4;

% Input and output parameters
Vi = 210;
fI = 50;
fo = 200;
q = 0.2;
phase_i = 0;
phase = 0;


% Load Parameters
Po = 3000;
eff = 1;
%R = 3*(q*q*1.005*1.005*Vi*Vi*eff)/(2*Po);
R = 1;
L = 0;

% Input filter parameter calculation
PF = 1;
Pi = Po/(eff*PF);
Cf = Pi/(40*2*pi*fI*Vi*Vi*0.5);
fio = fsw*0.05;
Lf = 1/(2*pi*2*pi*fio*fio*Cf);
zetai = 0.1876;
Rf = (0.5/zetai)*sqrt(Lf/Cf);

% Output filter parameter calculation
Rfout = 0;
% Lfout = 0.1*R/(0.9*2*pi*fo);
Lfout = 500e-6;
% Cfout = 1/(2*0.1*fsw*2*0.1*fsw*pi*pi*Lfout);
Cfout = 0;

% Switch and diode parasitics
R_matrix_switch = 1;
R_matrix_diode  = 1;
R_clamp_diode   = 1;

Vf_matrix_diode = 0.7;
Vf_matrix_switch = 0;
Vf_clamp_diode  = 0.7;

% Clamp Circuit Parameters and Parasitics

C_clamp = 10e-6;

C_clamp_esr = 1e-3;

R_clamp_par = 50e3;


% Commutation Parameters

delay1 = 5;
delay2 = 5;
delay3 = 5;
delay_end = 20;
tclk = 83.33e-9;

% Measurement Offsets

curr_offset = 2;
volt_offset = 0;

D = [];
T = [];

% Commutation Method

Comm_Type = 2;

out = sim("CurrentCommutationComparison.slx");



%%

D{1} = out.simout.Data;
T{1} = out.simout.Time;

%%
% 
% load("SimulationResults.mat");

%%

Time{1} = T{1};
vout_a{1} = D{1,1}(:,1);
vout_b{1} = D{1,1}(:,2);
vout_c{1} = D{1,1}(:,3);

iout_a{1} = D{1,1}(:,4);
iout_b{1} = D{1,1}(:,5);
iout_c{1} = D{1,1}(:,6);

iout_thd{1} = D{1,1}(:,7);

iin_thd{1} = D{1,1}(:,8);

iin_a{1} = D{1,1}(:,9);
iin_b{1} = D{1,1}(:,10);
iin_c{1} = D{1,1}(:,11);

vin_a{1} = D{1,1}(:,12);
vin_b{1} = D{1,1}(:,13);
vin_c{1} = D{1,1}(:,14);

switch_v_aa{1} = D{1,1}(:,15);
switch_v_ba{1} = D{1,1}(:,16);
switch_v_ca{1} = D{1,1}(:,17);

switch_v_ab{1} = D{1,1}(:,18);
switch_v_bb{1} = D{1,1}(:,19);
switch_v_cb{1} = D{1,1}(:,20);

switch_v_ac{1} = D{1,1}(:,21);
switch_v_bc{1} = D{1,1}(:,22);
switch_v_cc{1} = D{1,1}(:,23);



switch_i_aa{1} = D{1,1}(:,24);
switch_i_ba{1} = D{1,1}(:,25);
switch_i_ca{1} = D{1,1}(:,26);

switch_i_ab{1} = D{1,1}(:,27);
switch_i_bb{1} = D{1,1}(:,28);
switch_i_cb{1} = D{1,1}(:,29);

switch_i_ac{1} = D{1,1}(:,30);
switch_i_bc{1} = D{1,1}(:,31);
switch_i_cc{1} = D{1,1}(:,32);



switch_g_aa1{1} = D{1,1}(:,33);
switch_g_aa2{1} = D{1,1}(:,34);
switch_g_ba1{1} = D{1,1}(:,35);
switch_g_ba2{1} = D{1,1}(:,36);
switch_g_ca1{1} = D{1,1}(:,37);
switch_g_ca2{1} = D{1,1}(:,38);

switch_g_ab1{1} = D{1,1}(:,39);
switch_g_ab2{1} = D{1,1}(:,40);
switch_g_bb1{1} = D{1,1}(:,41);
switch_g_bb2{1} = D{1,1}(:,42);
switch_g_cb1{1} = D{1,1}(:,43);
switch_g_cb2{1} = D{1,1}(:,44);

switch_g_ac1{1} = D{1,1}(:,45);
switch_g_ac2{1} = D{1,1}(:,46);
switch_g_bc1{1} = D{1,1}(:,47);
switch_g_bc2{1} = D{1,1}(:,48);
switch_g_cc1{1} = D{1,1}(:,49);
switch_g_cc2{1} = D{1,1}(:,50);

clamp_voltage{1} = D{1,1}(:,51);
clamp_current{1} = D{1,1}(:,52);


%% Create the UI Figure and Setup the Grid Layout
fig = uifigure('Position', [100 100 2000 1200]);
gl = uigridlayout(fig, [6, 3], ...                     
    'RowSpacing', 10, ...
    'ColumnSpacing', 25, ...               % Increase spacing slightly to prevent label overlap
    'Padding', [25 10 10 10], ...          % Extra padding on the far left
    'RowHeight', {'1x', '1x', '1x', '1x', '2.5x', 60}, ... 
    'ColumnWidth', {'1x', '1x', '1x'});   % Keep this for now, handled below

all_axes = []; % Array to store all axes handles

%% -------- Row 1: Input Voltages (3 identical) --------
for col = 1:3
    ax = uiaxes(gl);
    all_axes = [all_axes, ax];
    ax.Layout.Row = 1;
    ax.Layout.Column = col;
    
    hold(ax, 'on')
    plot(ax, Time{1}, vin_a{1}, 'g-');
    plot(ax, Time{1}, vin_b{1}, 'r-');
    plot(ax, Time{1}, vin_c{1}, 'b-');
    title(ax, 'Input Voltage')
    grid(ax, 'on')
end

%% -------- Row 2: Output Currents --------
% Col 1
ax = uiaxes(gl); 
all_axes = [all_axes, ax];
ax.Layout.Row = 2; ax.Layout.Column = 1;
plot(ax, Time{1}, iout_a{1}, 'g-'); grid(ax, 'on');
title(ax, 'Iout A');

% Col 2
ax = uiaxes(gl); 
all_axes = [all_axes, ax];
ax.Layout.Row = 2; ax.Layout.Column = 2;
plot(ax, Time{1}, iout_b{1}, 'r-'); grid(ax, 'on');
title(ax, 'Iout B');

% Col 3
ax = uiaxes(gl); 
all_axes = [all_axes, ax];
ax.Layout.Row = 2; ax.Layout.Column = 3;
plot(ax, Time{1}, iout_c{1}, 'b-'); grid(ax, 'on');
title(ax, 'Iout C');

%% -------- Row 3: Switch Voltages --------
% Col 1
ax = uiaxes(gl); 
all_axes = [all_axes, ax];
ax.Layout.Row = 3; ax.Layout.Column = 1;
hold(ax, 'on'); plot(ax, T{1}, switch_v_aa{1}, 'g'); plot(ax, T{1}, switch_v_ba{1}, 'r'); plot(ax, T{1}, switch_v_ca{1}, 'b');
title(ax, 'V_{sw} A'); grid(ax, 'on');

% Col 2
ax = uiaxes(gl); 
all_axes = [all_axes, ax];
ax.Layout.Row = 3; ax.Layout.Column = 2;
hold(ax, 'on'); plot(ax, T{1}, switch_v_ab{1}, 'g'); plot(ax, T{1}, switch_v_bb{1}, 'r'); plot(ax, T{1}, switch_v_cb{1}, 'b');
title(ax, 'V_{sw} B'); grid(ax, 'on');

% Col 3
ax = uiaxes(gl); 
all_axes = [all_axes, ax];
ax.Layout.Row = 3; ax.Layout.Column = 3;
hold(ax, 'on'); plot(ax, T{1}, switch_v_ac{1}, 'g'); plot(ax, T{1}, switch_v_bc{1}, 'r'); plot(ax, T{1}, switch_v_cc{1}, 'b');
title(ax, 'V_{sw} C'); grid(ax, 'on');

%% -------- Row 4: Switch Currents --------
% Col 1
ax = uiaxes(gl); 
all_axes = [all_axes, ax];
ax.Layout.Row = 4; ax.Layout.Column = 1;
hold(ax, 'on'); plot(ax, T{1}, switch_i_aa{1}, 'g'); plot(ax, T{1}, switch_i_ba{1}, 'r'); plot(ax, T{1}, switch_i_ca{1}, 'b');
title(ax, 'I_{sw} A'); grid(ax, 'on');

% Col 2
ax = uiaxes(gl); 
all_axes = [all_axes, ax];
ax.Layout.Row = 4; ax.Layout.Column = 2;
hold(ax, 'on'); plot(ax, T{1}, switch_i_ab{1}, 'g'); plot(ax, T{1}, switch_i_bb{1}, 'r'); plot(ax, T{1}, switch_i_cb{1}, 'b');
title(ax, 'I_{sw} B'); grid(ax, 'on');

% Col 3
ax = uiaxes(gl); 
all_axes = [all_axes, ax];
ax.Layout.Row = 4; ax.Layout.Column = 3;
hold(ax, 'on'); plot(ax, T{1}, switch_i_ac{1}, 'g'); plot(ax, T{1}, switch_i_bc{1}, 'r'); plot(ax, T{1}, switch_i_cc{1}, 'b');
title(ax, 'I_{sw} C'); grid(ax, 'on');

%% -------- Row 5: Gate Signals --------
offset = 2;

% Col 1
ax = uiaxes(gl); 
all_axes = [all_axes, ax];
ax.Layout.Row = 5; ax.Layout.Column = 1;
hold(ax, 'on');
plot(ax, T{1}, switch_g_aa1{1} + 5*offset, 'g');
plot(ax, T{1}, switch_g_ba1{1} + 3*offset, 'r');
plot(ax, T{1}, switch_g_ca1{1} + 1*offset, 'b');
plot(ax, T{1}, switch_g_aa2{1} + 4*offset, 'y');
plot(ax, T{1}, switch_g_ba2{1} + 2*offset, 'm');
plot(ax, T{1}, switch_g_ca2{1} + 0*offset, 'c');
set(ax, 'YTick', []); ylim(ax, [-0.5 11.5])
title(ax, 'Gates A'); grid(ax, 'on'); xlabel(ax, 'Time (s)')

% Col 2
ax = uiaxes(gl); 
all_axes = [all_axes, ax];
ax.Layout.Row = 5; ax.Layout.Column = 2;
hold(ax, 'on');
plot(ax, T{1}, switch_g_ab1{1} + 5*offset, 'g');
plot(ax, T{1}, switch_g_bb1{1} + 3*offset, 'r');
plot(ax, T{1}, switch_g_cb1{1} + 1*offset, 'b');
plot(ax, T{1}, switch_g_ab2{1} + 4*offset, 'y');
plot(ax, T{1}, switch_g_bb2{1} + 2*offset, 'm');
plot(ax, T{1}, switch_g_cb2{1} + 0*offset, 'c');
set(ax, 'YTick', []); ylim(ax, [-0.5 11.5])
title(ax, 'Gates B'); grid(ax, 'on'); xlabel(ax, 'Time (s)')

% Col 3
ax = uiaxes(gl); 
all_axes = [all_axes, ax];
ax.Layout.Row = 5; ax.Layout.Column = 3;
hold(ax, 'on');
plot(ax, T{1}, switch_g_ac1{1} + 5*offset, 'g');
plot(ax, T{1}, switch_g_bc1{1} + 3*offset, 'r');
plot(ax, T{1}, switch_g_cc1{1} + 1*offset, 'b');
plot(ax, T{1}, switch_g_ac2{1} + 4*offset, 'y');
plot(ax, T{1}, switch_g_bc2{1} + 2*offset, 'm');
plot(ax, T{1}, switch_g_cc2{1} + 0*offset, 'c');
set(ax, 'YTick', []); ylim(ax, [-0.5 11.5])
title(ax, 'Gates C'); grid(ax, 'on'); xlabel(ax, 'Time (s)')

%% -------- Force Absolute Alignment While Keeping Native Y-Ticks --------
% We will explicitly find the maximum width requirement of your labels 
% and force a uniform spacing footprint across every plot.

for i = 1:length(all_axes)
    % 1. Turn on crisp hardware edge clipping
    all_axes(i).Clipping = 'on';
    
    % 2. Decouple standard responsive layout scaling for the inner box bounds
    all_axes(i).ActivePositionProperty = 'position';
    
    % 3. Standardize font sizing so character width calculations are identical
    all_axes(i).FontName = 'Helvetica';
    all_axes(i).FontSize = 9.5;
    all_axes(i).XColor = [0.7 0.7 0.7];
    all_axes(i).YColor = [0.7 0.7 0.7];
    
    % 4. Handle Row 5 (Gates) alignment matching
    if i >= 13
        % Instead of removing ticks, give Row 5 a standard set of numbers 
        % matching the text character widths of rows 1-4 (e.g., -20, 0, 20).
        set(all_axes(i), 'YTick', [2, 6, 10], 'YTickLabel', {'-20', ' 0', ' 20'});
        
        % Paint the labels the exact color of the background so they are invisible,
        % but MATLAB still uses their width to align the plot frame perfectly!
        all_axes(i).YColor = [0.07 0.07 0.07]; 
    else
        % Reset rows 1-4 back to normal display mode
        all_axes(i).YAxis.Visible = 'on';
    end
end

% Force a quick canvas refresh to update the inner bounds layout engine
drawnow;

% 5. Master Pass: Find the minimum left boundary position across the rows
% and snap every plot in that column to match it perfectly down to the pixel.
for col = 1:3
    col_idx = col:3:15; % Indices for columns [1,4,7,10,13], [2,5,8,...], etc.
    
    % Track the ideal inner position layout bounds
    max_left_margin = 0;
    for idx = col_idx
        pos = all_axes(idx).InnerPosition;
        % Find the plot box that had to push furthest right due to long labels
        if pos(1) > max_left_margin
            max_left_margin = pos(1);
        end
    end
    
    % Force every single plot box in this column to use that exact same left margin
    for idx = col_idx
        current_pos = all_axes(idx).InnerPosition;
        % Override the inner box left anchor and adjust width to prevent stretching
        width_adjustment = current_pos(3) + (current_pos(1) - max_left_margin);
        all_axes(idx).InnerPosition = [max_left_margin, current_pos(2), width_adjustment, current_pos(3)];
    end
end

%% -------- Row 6: Synchronized Panning Slider & Window Dropdown (Same Row) --------
% Define the available window choices and their numeric values in seconds
window_labels = {'10 ms', '1 ms', '100 us', '10 us', '1 us'};
window_values = [10e-3, 1e-3, 100e-6, 10e-6, 1e-6];

% Default configuration (Starting at 1 ms)
initial_idx = 2; 
window_size = window_values(initial_idx);

% Create the panning slider (Slightly less wide: spans columns 1 and 2 in row 6)
sld = uislider(gl);
sld.Layout.Row = 6;
sld.Layout.Column = [1 2]; 
sld.Limits = [0, simulation_time - window_size]; 
sld.Value = 0; 

% Create the window selector dropdown (Placed in column 3 in row 6)
dd = uidropdown(gl);
dd.Layout.Row = 6;
dd.Layout.Column = 3;
dd.Items = window_labels;
dd.ItemsData = window_values; % Stores raw numeric values behind text labels
dd.Value = window_size;       % Set default value to 1 ms

% Cache all original data arrays inside appdata handles for the callbacks to use
for i = 1:length(all_axes)
    lines = all_axes(i).Children;
    for j = 1:length(lines)
        if isprop(lines(j), 'XData')
            setappdata(lines(j), 'OrigX', lines(j).XData);
            setappdata(lines(j), 'OrigY', lines(j).YData);
        end
    end
    all_axes(i).XLim = [0, window_size];
end

% Connect the slider and dropdown callback events
sld.ValueChangingFcn = @(src, event) scrollTimeAxis(event.Value, all_axes, dd.Value);
dd.ValueChangedFcn = @(src, event) changeWindowSize(src, sld, all_axes);

%% -------- Clamp Voltage & Current Figure --------
fig_clamp = figure('Name', 'Clamp Waveforms', 'Position', [300 200 1000 600]);

% --- Clamp Voltage ---
subplot(2,1,1)
plot(Time{1}, clamp_voltage{1}, 'b')
grid on
title('Clamp Voltage')
ylabel('Voltage (V)')
xlabel('Time (s)')

% --- Clamp Current ---
subplot(2,1,2)
plot(Time{1}, clamp_current{1}, 'r')
grid on
title('Clamp Current')
ylabel('Current (A)')
xlabel('Time (s)')

%% Helper Callback 1: Handles Moving the Slider
function scrollTimeAxis(tStart, axesList, current_window)
    tEnd = tStart + current_window;
    
    for i = 1:length(axesList)
        if isvalid(axesList(i))
            axesList(i).XLim = [tStart, tEnd];
            
            % Crop data vectors to the current visible window for clean edge lines
            lines = axesList(i).Children;
            for j = 1:length(lines)
                if isappdata(lines(j), 'OrigX')
                    X = getappdata(lines(j), 'OrigX');
                    Y = getappdata(lines(j), 'OrigY');
                    
                    mask = (X >= tStart) & (X <= tEnd);
                    lines(j).XData = X(mask);
                    lines(j).YData = Y(mask);
                end
            end
        end
    end
end

%% Helper Callback 2: Handles Changing the Dropdown Value
function changeWindowSize(ddHandle, sldHandle, axesList)
    % Get the newly selected window size from the dropdown
    new_window = ddHandle.Value;
    
    % Check the current slider position
    current_start = sldHandle.Value;
    global_max_time = 0.03; % Matches your simulation_time variable
    
    % Recalculate slider boundaries so you can't scroll past the total simulation time
    sldHandle.Limits = [0, global_max_time - new_window];
    
    % If expanding the window pushes the boundary past the slider's maximum edge, snap it back
    if current_start > (global_max_time - new_window)
        current_start = global_max_time - new_window;
        sldHandle.Value = current_start;
    end
    
    % Trigger a plot window update immediately using the new scaling criteria
    scrollTimeAxis(current_start, axesList, new_window);
end