Введение

Всем здравствуйте! Несложно придумать ситуацию, в которой автоматическая сборка схемы в Simulink сильно упростила бы жизнь. Допустим, вы проводите эксперименты с КИХ-фильтром и часто меняете его порядок. Схема при этом, конечно, изменяется, но при этом строится она всё время по одному и тому же алгоритму. Это прекрасно видно на этой картинке:

Схема КИХ-фильтра
Схема КИХ-фильтра

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

Функция инициализации

В Simulink есть специальный инструмент, который позволяет автоматически задать параметры схемы перед началом моделирования. Представляет он из себя код на языке Matlab (его мы будем называть функцией инициализации), в котором задаются требуемые характеристики модели. Чтобы начать писать эту функцию, нужно зайти в Modeling -> Model Settings -> Model Properties -> Callbacks -> InitFcn*. Также во вкладку Model Properties можно войти, кликнув правой кнопкой мыши по пустому месту в схеме. Ниже будут рассмотрены основные функции, которые нам потребуются.

Стоит отметить, что также функции инициализации можно использовать в маскированных подсистемах (раздел Code в редакторе маски). Если вы поняли о чём я — пользуйтесь, если нет — не берите в голову :)

Функция add_block

Добавить блок на схему можно с помощью функции add_block. Первым её параметром нужно передать путь к блоку, а вторым — путь к месту, куда этот блок нужно добавить. Допустим, я хочу добавить блок Constant на самый верхний уровень схемы. Сделать это можно с помощью следующего кода:

add_block('simulink/Sources/Constant', 'modelName/newBlockName')

Код сработает при условии, что файл модели называется "modelName". Путь к любому блоку можно узнать следующим образом: заходим в Simulation -> Library Browser, далее кликаем правой кнопкой мыши по интересующей нас библиотеке и нажимаем на Open ... library. В открывшемся окне ищем интересующий нас блок. Сверху будет путь, куда нужно будет лишь добавить название требуемого блока. Путь к блоку для примера использования функции add_block, который был приведён выше, можно отследить по следующему скриншоту:

Пример поиска пути к блоку
Пример поиска пути к блоку

Записываем то, что выделено красным, как путь, и добавляем название блока. Получили "simulink/Sources/Constant". Нужно отметить, что название библиотеки в Library Browser почти всегда не совпадает с названием, которое используется в пути. Допустим, Audio Toolbox называется audiolibv1.

Добавим в нашу схему подсистему (Subsystem). Чтобы блок появился в подсистеме, а не на самом верхнем уровне, нужно написать следующее:

add_block('simulink/Sources/Constant', 'modelName/Subsystem/newBlockName')

Думаю, что логика тут понятна. Далее для удобства будем считать, что блок у нас всё-таки располагается на самом верхнем уровне модели.

Добавить блок и сразу же задать его параметр можно следующим образом:

add_block('simulink/Sources/Constant', 'modelName/newBlockName', 'Value', '25')

Можно такими "парами" задать и сразу несколько параметров блока. Откуда брать названия этих параметров рассмотрим далее.

Параметры блоков, функции get_param и set_param

У каждого блока в Simulink есть параметры, которые обычно задаются вручную (например, параметр Constant value у блока Constant). Получить и задать их значения можно и программно, для этого используются функции get_param и set_param соответственно.

Приравняем к нулю параметр Value у ранее добавленного нами блока Constant:

set_param('modelName/newBlockName', 'Value', '0')

Теперь получим этот параметр и запишем в переменную v:

v = get_param('modelName/newBlockName', 'Value')

Важно заметить, что значение параметра, которое записывается в textbox, является строкой, даже если по логике это должно быть число. Так что в данном случае значение v будет равно '0', в не 0.

Чтобы понять, как называется нужный параметр, нужно обратиться к документации. Допустим, нас интересует всё тот же блок Constant и его поле Constant value . В разделе Parameters находим параметр Constant value и ищем пункт Programmatic Use — в нём и будет находится интересующая нас информация. На скриншоте ниже видно, что параметр этот называется 'Value', это название и использовалось в предыдущем примере кода.

Часть документации к блоку Constant
Часть документации к блоку Constant

Таким образом можно найти уникальные параметры блока, но есть и общие параметры — допустим, размеры и тому подобное. Для таких параметров была создана отдельная страничка в документации. Уникальные параметры тоже собраны в одном месте, но так их искать, как мне кажется, не очень удобно.

Функция add_line

Блоки необходимо соединять друг с другом, это тоже можно сделать программно — для этого существует функция add_line. Использовать её крайне просто. Допустим, в подсистеме Subsystem модели model, есть два блока. Первый называется Gain1, а второй — Scope1. Соединим их программно:

add_line('model/Subsystem', 'Gain1/1', 'Scope1/1')

Первый параметр указывает место в модели, где находятся наши блоки. Второй и третий параметры — порты, которые нужно соединить. Название порта формируется просто — сначала идёт название блока, потом '/n', где n — номер порта (обычно они нумеруются сверху вниз). Информация о портах, конечно, есть в документации (раздел Ports).

Функции delete_block и delete_line

Информации выше уже достаточно, чтобы автоматизировать построение ряда схем. Только вот если вы запустите модель два раза подряд, то на второй раз ничего не скомпилируется, потому что два блока с одним и тем же названием в одно место модели добавить нельзя, а вы именно это и попытаетесь сделать. Из-за этого перед каждым автоматическим построением схемы нужно удалять то, что уже есть. Для этого существуют функции delete_block и delete_line.

Удалить блок можно достаточно просто. Удалим блок с названием blockName, который находится на верхнем уровне модели с названием model:

delete_block('model/blockName')

Удалить линии, которые соединяют блоки, уже сложнее. Самый простой вариант — получить дескрипторы инцидентных линий для каждого блока (будем называть линию и блок инцидентными, если эта линия входит в какой-либо порт этого блока), и, используя их, всё удалить. Приступим к получению дескрипторов всех инцидентных линий для всё того же блока blockName:

lh = get_param('model/blockName', 'LineHandles')

Переменная lh является структурой, в которой нас интересуют поля Inport и Outport. Они являются векторами, в которых находятся дескрипторы линий входящих в блок и выходящих из блока соответственно. Если у порта нет инцидентных линий, то на месте искомого дескриптора будет -1. Теперь мы в состоянии удалить все инцидентные линии:

for i = 1 : length(lh.Inport)
  try  
    delete_line(lh.Inport(i));
  end
end

for i = 1 : length(lh.Outport)
  try
    delete_line(lh.Outport(i));
  end
end

Конструкция try ... end использовалась для того, чтобы код корректно сработал в ситуации, когда у порта нет инцидентной линии, ведь выражение delete_line(-1) вызовет ошибку.

Почти всё! Осталось только узнать, как найти все блоки по указанному пути. Сделать это несложно — с помощью следующего кода мы получим вектор со всеми хэндлами блоков верхнего уровня модели с названием model:

blocks = Simulink.findBlocks('model')

Осталось только "пройтись" по полученному вектору blocks и удалить все инцидентные линии для каждого блока:

for i = 1 : length(blocks)
    lh = get_param(blocks(i), 'LineHandles');
    for j = 1 : length(lh.Inport)
        try
            delete_line(lh.Inport(j));
        end
    end
    for j = 1 : length(lh.Outport)
        try
            delete_line(lh.Outport(j));
        end
    end
end

Ну а после этого можно удалить все блоки:

delete_block(blocks)

Вот и всё! Теперь ваших знаний достаточно, чтобы автоматически собирать большинство схем в Simulink!

Пример использования

Конечно, без простенького примера я вас не оставлю:

%Параметры схемы
bias = '0.5';
freq = '2*pi*10';
%Удаляем линии и блоки
blocks = Simulink.findBlocks('habr');
for i = 1 : length(blocks)
    lh = get_param(blocks(i), 'LineHandles');
    for j = 1 : length(lh.Inport)
        try
            delete_line(lh.Inport(j));
        end
    end
    for j = 1 : length(lh.Outport)
        try
            delete_line(lh.Outport(j));
        end
    end
end
delete_block(blocks);
%Добавляем блоки
add_block('simulink/Sources/Sine Wave', 'habr/Sine');
add_block('simulink/Math Operations/Bias', 'habr/Bias');
add_block('simulink/Sinks/Scope', 'habr/Scope');
%Меняем параметры блоков
set_param('habr/Sine', 'position', [100 100 150 150]);
set_param('habr/Bias', 'position', [200 100 250 150]);
set_param('habr/Scope', 'position', [300 100 350 150]);
set_param('habr/Sine', 'Frequency', freq);
set_param('habr/Bias', 'Bias', bias);
%Соединяем блоки
add_line('habr', 'Sine/1', 'Bias/1');
add_line('habr', 'Bias/1', 'Scope/1');

Тут можно задавать значения переменных bias и freq, меняя тем самым величину постоянного смещения синусоиды и её частоту соответственно. Сама схема выглядит так:

Полученная схема
Полученная схема

Надеюсь, что статья вам помогла!

Комментарии (0)