Повторите алгоритм, используя For Each Subsystem

Если вы повторяете алгоритмы в схеме путем копирования и вставки блоков и подсистем, поддержание модели может стать трудным. Отдельные сигнальные линии и подсистемы могут толпить схему, уменьшая читаемость и затрудняя простые изменения. Переменные могут также распространять рабочие области, уменьшая переносимость модели. Модель может развить эти проблемы эффективности, когда вы добавляете к проекту с течением времени.

Чтобы повторить алгоритм, можно итерировать алгоритм над сигналами, подсистемами и параметрами, которые сгруппированы в массивы и структуры. Этот пример показывает, как преобразовать неэффективно сложный повторяющийся алгоритм в компактную форму, которой легче управлять.

Исследуйте модель примера

  1. Откройте пример модели ex_repeat_algorithm. Модель создает около 30 переменных в базовом рабочем пространстве.

  2. Осмотрите Burner_1_Analysis подсистемы. Эта подсистема выполняет алгоритм, используя переменные базового рабочего пространства в качестве параметров в блоках, таких как Constant и Discrete-Time Integrator.

  3. Осмотрите подсистемы Burner_2_Analysis и Burner_3_Analysis. Все три подсистемы выполняют один и тот же алгоритм, но используют различные переменные рабочей области, чтобы параметризовать блоки.

  4. Осмотрите три подсистемы Analysis_Delay. Эти подсистемы повторяют другой алгоритм по сравнению с алгоритмом в Analysis подсистемах.

  5. Вернитесь к верхнему уровню модели. Блоки Memory задерживают входные сигналы, прежде чем они войдут в Analysis_Delay подсистемы.

  6. Проверьте панель Data Import/Export диалогового окна Параметры конфигурации. Модель использует переменные SensorsInput и t как входы симуляции.

    Во время симуляции каждый из девяти столбцов матричной переменной SensorsInput предоставляет входные данные для блока Inport на верхнем уровне модели.

Уменьшите плотность сигнальных линий с помощью шин

Можно использовать шины, чтобы сгруппировать связанные сигналы в один структурированный сигнал, уменьшая плотность линии и улучшая читаемость модели.

Каждая подсистема в модели примера требует трёх входов сигнала. Можно объединить каждую группу из трех сигналов в одну шину.

Можно изменить все подсистемы в модели примера, чтобы использовать шины. Однако, поскольку некоторые подсистемы идентичны, можно удалить их и позже заменить на For Each Subsystem блоков.

  1. Откройте Редактор шины.

    buseditor

  2. Создайте тип шины SensorData с тремя сигнальными элементами: sensor1, sensor2, и sensor3.

  3. Удалите блоки, как показано на рисунке, оставив только Burner_1_Sensor1 и Burner_1_Delay1 блоки в качестве входов для двух оставшихся подсистем.

  4. На вкладке Signal Attributes диалогового окна Burner_1_Sensor1 Inport block задайте Data type Bus: SensorData.

    Выходом блока является шина, которая содержит три сигнальных элемента sensor1, sensor2, и sensor3.

  5. Откройте Burner_1_Analysis подсистемы. Удалите выходные линии сигнала трех блоков Inport. Удалите In2 и In3 блоки Inport.

  6. Добавьте Bus Selector блок справа от блока In1 Inport. Соедините выход блока Inport с блоком Bus Selector.

  7. В Bus Selector диалогового окна блока выберите сигналы sensor1, sensor2, и sensor3.

    Блок Bus Selector извлекает три элемента сигнала из шины входа. Другие блоки в модели могут использовать извлеченные элементы сигнала.

  8. В подсистеме соедините блоки как показано.

  9. В Burner_1_Analysis_Delay подсистемы используйте блок Bus Selector, чтобы извлечь сигналы в шине. Используйте тот же метод, что и в Burner_1_Analysis подсистемы.

Повторите алгоритм

Блок For Each Subsystem разбивает входной сигнал и последовательно выполняет алгоритм на каждом разделе. Например, если вход в подсистему является массивом из шести сигналов, можно сконфигурировать подсистему, чтобы выполнить тот же алгоритм для каждого из шести сигналов.

Можно использовать For Each subsystem s, чтобы повторить алгоритм итеративным способом. Этот подход улучшает читаемость модели и облегчает изменение повторного алгоритма.

  1. Добавьте два блока For Each Subsystem к модели. Назовите одну из подсистем Burner_Analysis. Назовите другие Burner_Analysis_Delay подсистемы.

  2. Скопируйте содержимое Burner_1_Analysis подсистемы в Burner_Analysis подсистемы. Прежде чем вставлять блоки, удалите Inport и Outport блоки в For Each subsystem.

  3. В For Each диалогового окна блока в подсистеме Burner_Analysis установите флажок, чтобы разбить вход на разделы In1.

  4. Скопируйте содержимое Burner_1_Analysis_Delay подсистемы в Burner_Analysis_Delay подсистемы.

  5. В For Each диалогового окна блока в подсистеме Burner_Analysis_Delay установите флажок, чтобы разбить вход на разделы In1.

  6. На верхнем уровне модели удалите подсистемы Burner_1_Analysis и Burner_1_Analysis_Delay. Соедините новые блоки For Each Subsystem на их место.

  7. На вкладке Signal Attributes диалогового окна Burner_1_Sensor1 Inport block задайте Port dimensions 3.

    Выход блока представляет собой трехэлементный массив шин. For Each subsystem s в модели повторяют алгоритм для каждой из трех шин массива.

  8. Создайте Simulink.SimulationData.Dataset объект, который может использовать блок Inport для импорта данных моделирования. Можно использовать этот код для создания объекта и хранения его в переменной SensorsInput.

    % First, create an array of structures whose field values are
    % timeseries objects
    
    for i = 1:3 % Burner number
        
        % Sensor 1
        eval(['tempInput(1,' num2str(i) ').sensor1 = ' ...
            'timeseries(SensorsInput(:,' num2str(3*(i-1)+1) '),t);'])
        
        % Sensor 2
        eval(['tempInput(1,' num2str(i) ').sensor2 = ' ...
            'timeseries(SensorsInput(:,' num2str(3*(i-1)+2) '),t);'])
        
        % Sensor 3
        eval(['tempInput(1,' num2str(i) ').sensor3 = ' ...
            'timeseries(SensorsInput(:,' num2str(3*(i-1)+3) '),t);'])
        
    end
    
    % Create the Dataset object.
    
    SensorsInput = Simulink.SimulationData.Dataset;
    SensorsInput = addElement(SensorsInput,tempInput,'element1');
    
    clear tempInput t i

    Код сначала создает переменную tempInput который содержит массив из трех структур. Каждая структура имеет три поля, которые соответствуют сигнальным элементам в типе шины SensorData, и каждое поле хранит MATLAB® timeseries объект. Каждый timeseries объект хранит один из девяти столбцов данных от переменной SensorsInput, который хранит входные данные симуляции для каждого из датчиков.

    Затем код перезаписывается SensorsInput с новым Simulink.SimulationData.Dataset объект и добавляет tempInput как элемент объекта.

  9. Установите параметр конфигурации Input равным SensorsInput.

    Начиная с SensorsInput обеспечивает входные данные симуляции в виде timeseries объекты, вам не нужно задавать переменную, которая содержит временные данные.

  10. Создайте массив структур, который инициализирует оставшийся Memory блок и сохраняет массив в переменной initForDelay. Задайте поля структуры со значениями существующих переменных инициализации, таких как initDelay_1_sensor1.

    for i = 1:3 % Burner number
        
        % Sensor 1
        eval(['initForDelay(' num2str(i) ').sensor1 = ' ...
            'initDelay_' num2str(i) '_sensor1;'])
        
        % Sensor 2
        eval(['initForDelay(' num2str(i) ').sensor2 = ' ...
            'initDelay_' num2str(i) '_sensor2;'])
        
        % Sensor 3
        eval(['initForDelay(' num2str(i) ').sensor3 = ' ...
            'initDelay_' num2str(i) '_sensor3;'])
    end
    

    Чтобы просмотреть содержимое новой переменной initForDelayдважды кликните имя переменной в базовом рабочем пространстве. Переменная содержит массив из трех структур, каждая из которых имеет три поля: sensor1, sensor2, и sensor3.

  11. В Memory диалогового окна блока установите Initial condition равным initForDelay.

    Выход блока Memory является массивом шин, который требует инициализации. Каждый сигнальный элемент в массиве шин получает начальное значение из соответствующего поля в массиве структур.

Организуйте параметры в массивы структур

Базовое рабочее пространство содержит много переменных, которые используется в модели примера для параметров блоков. Чтобы уменьшить количество переменных рабочей области, упакуйте их в массивы структур и используйте отдельные поля структуры, чтобы задать параметры блоков.

Блок For Each Subsystem может разделить массив значений, которые вы задаете в качестве параметра маски. Каждая итерация подсистемы использует один раздел массива, чтобы задать параметры блоков. Если вы задаете параметр как массив структур, каждая итерация подсистемы может использовать одну из структур в массиве.

  1. Создайте массив структур, который параметризовает Burner_Analysis For Each subsystem, и сохраните массив в переменной paramsNormal. Задайте поля структуры с помощью значений существующих переменных параметра, таких как gainNormal_1, offsetNormal_1, и initDelayed_1.

    for i = 1:3
        eval(['paramsNormal(' num2str(i) ').gain = gainNormal_' num2str(i) ';'])    
        eval(['paramsNormal(' num2str(i) ').offset = offsetNormal_' num2str(i) ';'])
        eval(['paramsNormal(' num2str(i) ').init = initNormal_' num2str(i) ';'])
    end

    Переменная содержит массив из трех структур, каждая из которых имеет три поля: gain, offset, и init.

  2. В модели щелкните правой кнопкой мыши по Burner_Analysis For Each subsystem и выберите Mask > Create Mask.

  3. На Parameters & Dialog панели диалогового окна в разделе Parameter нажмите Edit. Для нового параметра маски установите Prompt равным Parameter structure и Name к paramStruct. Нажмите OK.

  4. В маске для подсистемы Burner_Analysis установите Parameter structure равным paramsNormal.

  5. Откройте подсистему. В For Each диалогового окна блока на панели Parameter Partition установите флажок, чтобы разбить параметр на разделы paramStruct. Установите Partition dimension значение 2.

  6. Для блоков в подсистеме установите эти параметры.

    БлокИмя параметраЗначение параметров
    GainGainparamStruct.gain
    Discrete-Time IntegratorInitial conditionparamStruct.init
    ConstantConstant valueparamStruct.offset

  7. Создайте массив структур, который параметризовает Burner_Analysis_Delay For Each subsystem, и сохраните массив в переменной paramsForDelay.

    for i = 1:3
        eval(['paramsForDelay(' num2str(i) ').gain = gainDelayed_' num2str(i) ';'])
        eval(['paramsForDelay(' num2str(i) ').offset = offsetDelayed_' num2str(i) ';'])
        eval(['paramsForDelay(' num2str(i) ').init = initDelayed_' num2str(i) ';'])
    end

  8. На верхнем уровне модели щелкните правой кнопкой мыши по Burner_Analysis_Delay For Each subsystem и выберите Mask > Create Mask.

  9. На Parameters & Dialog панели диалогового окна в разделе Parameter нажмите Edit. Для нового параметра маски установите Prompt равным Parameter structure и Name к paramStruct. Нажмите OK.

  10. В маске для блока For Each Subsystem установите Parameter structure paramsForDelay.

  11. Откройте подсистему. В For Each диалогового окна блока на панели Parameter Partition установите флажок, чтобы разбить параметр на разделы paramStruct. Установите Partition dimension значение 2.

  12. Для блоков в подсистеме установите эти параметры.

    БлокИмя параметраЗначение параметров
    GainGainparamStruct.gain
    Discrete-Time IntegratorInitial conditionparamStruct.init
    ConstantConstant valueparamStruct.offset

  13. Очистите ненужные переменные из базового рабочего пространства.

    % Clear the old parameter variables that you replaced 
    % with arrays of structures
    clear -regexp _
    
    % Clear the iteration variables
    clear i

    Модель требует мало переменных в базовом рабочем пространстве.

Просмотр преобразованной модели

Чтобы просмотреть новый сигнал и организацию подсистемы, обновите схему.

Вход модели представляет собой массив из трех шин. Модель использует два For Each subsystems, чтобы выполнить два алгоритма на каждой из трех шин в вход массиве.

В базовом рабочем пространстве массивы структур заменяют многие переменные, которые использовала модель. Математически измененная модель ведет себя так же, как и при запуске, потому что массивы структур содержат значения всех старых переменных.

Совет

Можно записать небусовые сигналы в подсистему For Each. Однако вы не можете использовать регистрацию сигналов для шин или массивов шин из подсистемы For Each. Либо используйте блок Bus Selector, чтобы выбрать сигналы элемента шины, которые вы хотите записать, либо добавьте блок Outport за пределами подсистемы, и затем зарегистрируйте этот сигнал. Для получения дополнительной информации смотрите Журнал сигналов в Для каждой подсистемы.

Примеры работы с каждой подсистемой

Векторизация алгоритмов, использующих для каждой подсистемы

Этот пример показывает, как упростить моделирование векторизованных алгоритмов. Использование For Each Subsystem блоков упрощает модель, где три входных сигналов фильтруются тремя идентичными блоками Transfer Fcn. Этот пример также показывает, как добавить больше управления к фильтрам путем изменения их коэффициентов для каждой итерации подсистемы.

Эта модель использует одинаковые блоки Transfer Fcn, чтобы независимо обработать каждый элемент входного сигнала синусоиды. Блок Vector Concatenate объединяет результирующие выходные сигналы. Этот повторяющийся процесс является графически сложным и трудным для поддержания. Добавление другого элемента к сигналу также требует значительной переработки модели.

Можно упростить эту модель, заменив повторяющиеся операции одним блоком For Each Subsystem.

Блок For Each subsystem содержит блок For Each и модель, представляющую алгоритм трех блоков, которые он заменяет посредством блока Transfer Fcn. Блок For Each задает, как разбить вектор входного сигнала на отдельные элементы и как объединить обработанные сигналы, чтобы сформировать вектор выходного сигнала. Каждый блок, который имеет состояние, поддерживает отдельный набор состояний для каждого входного элемента, обработанный во время заданного шага выполнения.

В данном примере входной сигнал выбирается для разбиения. Для параметров Partition Dimension и Partition Width блока For Each задано значение 1 для входов.

screenshot of For Each Block Parameters dialog, Input Partition tab

Можно масштабировать этот подход, чтобы добавить больше сигналов, не меняя модель значительно. Этот подход легко масштабируется и графически проще.

Изменение параметра модели без изменения структуры модели.  Этот пример показывает, как смоделировать изменение параметра в алгоритме. Он использует модель разбиения на разделы для каждой подсистемы из вышеописанного примера и создает различные фильтры для каждого входного сигнала с сохранением простоты модели. Массив коэффициентов фильтра подается в блок For Each subsystem как маскирующий параметр, отмеченный для разбиения. В каждой итерации блока For Each subsystem в блок Transfer Fcn подается разбиение массива коэффициентов фильтра.

  1. Откройте ex_ForEachSubsystem_Partitioning модели. Создайте маску для блока For Each Subsystem и добавьте параметр редактируемой маски. Установите имя равным FilterCoeffs и приглашение к Filter Coefficient Matrix. Дополнительные сведения о том, как добавить параметр маски, см. в разделе «Создание простой маски».

  2. Откройте For Each subsystem блок. Внутри подсистемы откройте диалоговое окно For Each блока.

  3. На вкладке Parameter Partition установите флажок рядом с параметром FilterCoeffs , чтобы включить разбиение этого параметра. Сохраните параметры Размерности ширины и разбиения на значение по умолчанию 1.

    screenshot of For Each Block Parameters dialog, Parameter Partition tab

  4. Дважды кликните блок For Each Subsystem и введите матрицу коэффициентов фильтра, имея по одной строке коэффициентов фильтра для каждого входного сигнала. Для примера введите [0.0284 0.2370 0.4692 0.2370 0.0284; -0.0651 0 0.8698 0 -0.0651; 0.0284 -0.2370 0.4692 -0.2370 0.0284] для реализации различных фильтров четвертого порядка для каждого входного сигнала.

  5. В блоке For Each Subsystem дважды кликните блок Transfer Fcn и введите FilterCoeffs для параметра Коэффициенты знаменателя. Эта настройка заставляет блок получать свои коэффициенты из параметра маски.

Блок For Each Subsystem срезает входной параметр в горизонтальные разделы ширины 1, что эквивалентно одной строке коэффициентов. Параметр коэффициентов преобразуется из одинарного массива

в три строки параметров:

Улучшенный код для каждой подсистемы.  В этом примере показано, как улучшить повторное использование кода, когда у вас есть два или более одинаковых блоков For Each Subsystem. Рассмотрим следующую модель, rtwdemo_foreachreuse.

Цель состоит в том, чтобы три подсистемы - Vector SS1, Vector SS2 и Vector SS3 - применили ту же обработку к каждому скалярному элементу векторного сигнала на своих соответствующих входах. Поскольку эти три подсистемы выполняют одинаковую обработку, им желательно создать одну общую функцию Output (и Update) для всех трех подсистем в коде, сгенерированном для этой модели. Для примера подсистема Vector SS3 содержит эти блоки.

Чтобы сгенерировать одну общую функцию для трех подсистем, строение разбиения, которое они выполняют на своих входных сигналах, должна быть одинаковой. Для Вектора SS1 и Вектора SS3 это строение просто, потому что можно задать размерность и ширину раздела равными 1. Однако порядок чтобы Вектор SS2 также разбить его входной сигнал вдоль размерности 1, необходимо вставить блок Math Function, чтобы транспонировать вектор-строку 1 на 8 в вектор-столбец 8 на 1. Можно затем преобразовать выход подсистемы назад в вектор-строку 1 на 8 с помощью второго набора блоков Math Function в transpose оператор.

Если вы нажимаете Ctrl+B, чтобы сгенерировать код, полученный код использует единственную выходную функцию. Эта функция совместно используется всем тремя образцами <reservedrangesplaceholder0>.

/*
 * Output and update for iterator system:
 *    '<Root>/Vector SS1'
 *    '<Root>/Vector SS2'
 *    '<Root>/Vector SS3'
 */
void VectorProcessing(int32_T NumIters, const real_T rtu_In1[], 
                      real_T rty_Out1[],
                      rtDW_VectorProcessing *localDW)

Функция имеет параметр входа NumIters это указывает количество независимых скаляров, которые каждый For Each Subsystem должен обработать. Эта функция вызывается трижды с параметром NumIters установите значение 10, 8 и 7 соответственно.

Оставшиеся две подсистемы в этой модели показывают, как переиспользуемый код также может быть сгенерирован для матричных сигналов, которые обрабатываются с использованием блока For Each Subsystem. Снова, нажатие Ctrl+B для генерации кода обеспечивает повторное использование кода одной функции.

См. также

Объекты

Блоки

Похожие темы