
Предположим, существует некий проект, где перед двумя разработчиками стоит задача расчёта/сбора каких-либо данных, а также их грамотной визуализации. При этом, один из разработчиков хорошо разбирается в матанализе или физике и имеет представление о том, как эта задача может решаться, а также дружит с MATLAB. Другой же разработчик, напротив, знает, как правильно интерпретировать набор данных и представить наглядный анимированный график, а также дружит с LabVIEW. Для подобных задач существует инструмент "Interface for MATLAB" в LabVIEW NXG, который позволяет обращаться к синтаксису MATLAB и совмещать преимущества графического и текстового языков программирования. Именно этот инструмент будет рассмотрен в данной статье.

Статья расчитана на тех, кто на базовом уровне знаком с пакетами MATLAB и LabVIEW. Показанные примеры воспроизводились на версиях MATLAB R2020b и LabVIEW NXG 5.10 Community. Поставленная задача и методы её решения имеют показательный характер, основной упор делается на демонстрации возможности использования синтаксиса MATLAB при создании проектов LabVIEW.
Статья будет разделена на следующие части:
Постановка задачи.
Решение задачи с помощью MATLAB.
Интеграция кода MATLAB в LabVIEW NXG на примере поставленной задачи.
Решение задачи с помощью MATLAB и LabVIEW NXG.
Заключение.
1. Постановка задачи
В качестве примера будет численно решаться система уравнений Лоренца, а полученное решение будет воспроизводиться похожим образом, как это продемонстрировано на рис. 1.
Система Лоренца представляет из себя три дифференциальных уравнения:
где x, y, z – искомые переменные; t – время; σ, r, β – параметры, которые, как правило, задаются в виде чисел и не меняются в ходе решения.
Тем, кто не особо хорошо знаком с дифференциальными уравнениями, не стоит пугаться, потому что данная система решается численно множеством способов. Один из самых простых способов - метод Эйлера, представленный ниже:
где Δt – шаг по времени, с которым производится расчёт, чем он меньше, тем "точнее" вычисление; i = 1, 2, 3, ... – порядковый номер.
Иными словами, процедура, описанная выше, имеет следующий смысл: для нахождения каждого последующего значения искомой переменной xi+1 требуется к предыдущему значению xi добавить правую часть дифференциального уравнения, умноженную на заданный шаг по времени Δt.
Таким образом, для данной задачи у нас будут следующие входные данные:
Начальные условия: xi, yi, zi, ti,
Параметры: σ, r, β,
Шаг интегрирования: Δt.
Выходными же данными будут: xi+1, yi+1, zi+1, ti+1. Стоит также отметить, что найденные выходные данные будут служить входными начальными условиями для следующей итерации. Все найденные значения x, y, z должны либо сохраняться в памяти, для последующей обработки, либо сразу выводиться на график, динамично удаляя ненужные "хвосты".
2. Решение задачи с помощью MATLAB
Для начала создадим подпрограмму вида function, которая будет производить основные вычисления и которую мы будем использовать далее с помощью LabVIEW. Начнём с входных и выходных параметров, а также с имени функции:
function [X_out,Y_out,Z_out,T_out]=FLorenz(par,dt,X_in,Y_in,Z_in,T_in)
end
В данном случае "FLorenz" – имя функции; par – вектор-строка 1x3 из параметров σ, r, β; dt – шаг интегрирования Δt; X_in, Y_in, Z_in, T_in – векторы-строки входных данных; X_out, Y_out, Z_out, T_out – векторы-строки выходных данных. Далее напишем тело программы, выполняющее вычисления в соответствии с тем, что было описано в первом пункте:
function [X_out,Y_out,Z_out,T_out]=FLorenz(par,dt,X_in,Y_in,Z_in,T_in)
dx=dt*(par(1)*Y_in(end)-par(1)*X_in(end)); %Нахождение приращения X
dy=dt*(par(2)*X_in(end)-X_in(end)*Z_in(end)-Y_in(end)); %Нахождение приращения Y
dz=dt*(X_in(end)*Y_in(end)-par(3)*Z_in(end)); %Нахождение приращения Z
X_out=[X_in,X_in(end)+dx]; %Добавление найденной координаты X
Y_out=[Y_in,Y_in(end)+dy]; %Добавление найденной координаты Y
Z_out=[Z_in,Z_in(end)+dz]; %Добавление найденной координаты Z
T_out=[T_in,T_in(end)+dt]; %Добавление элемента времени T
end
Далее добавим логической условие, которое будет контролировать размер вычисленных данных и удалять ненужные элементы. Для этого нам понадобится добавить входную переменную ms к остальным входным данным:
function [X_out,Y_out,Z_out,T_out]=FLorenz(par,dt,X_in,Y_in,Z_in,T_in,ms)
dx=dt*(par(1)*Y_in(end)-par(1)*X_in(end)); %Нахождение приращения X
dy=dt*(par(2)*X_in(end)-X_in(end)*Z_in(end)-Y_in(end)); %Нахождение приращения Y
dz=dt*(X_in(end)*Y_in(end)-par(3)*Z_in(end)); %Нахождение приращения Z
X_out=[X_in,X_in(end)+dx]; %Добавление найденной координаты X
Y_out=[Y_in,Y_in(end)+dy]; %Добавление найденной координаты Y
Z_out=[Z_in,Z_in(end)+dz]; %Добавление найденной координаты Z
T_out=[T_in,T_in(end)+dt]; %Добавление элемента времени T
nowsize=length(T_out); %Нахождение размера массивов
if nowsize>ms
randot=nowsize-ms+1:nowsize; %Определение диапазона сохранения данных
X_out=X_out(randot); %Сохранение X в рамках диапазона
Y_out=Y_out(randot); %Сохранение Y в рамках диапазона
Z_out=Z_out(randot); %Сохранение Z в рамках диапазона
T_out=T_out(randot); %Сохранение T в рамках диапазона
end
end
На этом написание функции закончено. Разумеется, её можно дополнить и оптимизировать или заставить производить несколько итераций за один вызов, но в рамках показательного примера будет достаточно того, что изложено выше. Теперь напишем скрипт в котором будут численно определены все параметры и который будет ссылаться на нашу функцию. Важно отметить, что при стандартных настройках MATLAB можно вызывать только те функции (не считая базовых или библиотечных), которые находятся в одной папке со скриптом, поэтому создаём следующий скрипт в той же папке, что и наша написанная функция:

clear
clc
sigma=10; r=28; beta=8/3; %Параметры системы
par=[sigma r beta]; %Объединение параметров в один вектор
x0=1; y0=1; z0=1; %Начальные условия
t0=0; dt=0.005; %Начальное время и шаг интегрирования
X=x0; Y=y0; Z=z0; T=t0; %Создание векторов координат и времени
ms=4000; %Максимальное количество элементов
[X,Y,Z,T]=FLorenz(par,dt,X,Y,Z,T,ms); %Вызов ранее созданного файла function
Скрипт выдаёт нам следующий результат:

Мы видим, что к нашим начальным условиям добавились вычисленные функцией значения, поэтому можем считать, что скрипт и функция работают правильно, и двигаться дальше. Теперь осталось дописать скрипт, чтобы при запуске воспроизводилась анимация, и на этом, можно сказать, поставленная задача полностью решена:
clear
clc
sigma=10; r=28; beta=8/3; %Параметры системы
par=[sigma r beta]; %Объединение параметров в один вектор
x0=1; y0=1; z0=1; %Начальные условия
t0=0; dt=0.005; %Начальное время и шаг интегрирования
X=x0; Y=y0; Z=z0; T=t0; %Создание векторов координат и времени
ms=4000; %Максимальное количество элементов
F=figure; %Создание окна figure
F.WindowState='maximized'; %Включение полноэкранного режима
P=plot(T,X,'linewidth',2); %Создание графика с толщиной линии 2
grid on; grid minor; %Создание сетки на графике
xlabel('Время, t'); %Создание подписи горизонтальной оси
ylabel('Координата, x(t)'); %Создание подписи вертикальной оси
ylim([-20 20]); %Задание пределов по вертикальной оси
while isvalid(F) %Создание цикла, прекращающего работу в случае закрытия figure
[X,Y,Z,T]=FLorenz(par,dt,X,Y,Z,T,ms); %Вызов ранее созданного файла function
P.XData=T; P.YData=X; %Обновление данных зависимости на графике
xlim([T(1) T(end)]); %Обновление пределов по горизонтальной оси
drawnow limitrate; %Ограничение количества обновлений figure
end
Результатом будет являться следующая анимация:

3. Интеграция кода MATLAB в LabVIEW NXG на примере поставленной задачи
Открываем LabVIEW NXG и создаём новый проект VI:

Сохраняем проект. В данном случае будет неважно место сохранения, путь к файлу MATLAB мы будем задавать вручную далее:

Создаём Interface for MATLAB:

Справа, на вкладке Document выбираем тип файла function:

В главной рабочей области указываем путь к нашему файлу function (.m):

Нажимаем Add interface node. Перед нами появляется область, которую требуется заполнить входными и выходными параметрами точно в таком порядке, как они заданы в самом файле function (.m). Начнём с создания входного вектора параметров par, нажав Add parameter:

Как видно, нам потребовалось указать Array в графе Shape, By row в графе Orientation и Input в окне Behavior. Разумееся, что это справедливо для par – входного вектора-строки 1x3, следующий параметр dt будем обычным скаляром (переменной):

Хочу обратить внимание, что в столбце Prototype показан пример функции MATLAB, которую мы хотим создать. Желательно регулярно сверяться с тем, что написано в этом столбце при добавлении и настройке входных/выходных параметров. Добавим остальные входные параметры:

Обращаю внимание, что входные X, Y, Z, T – векторы-строки, а ms – скаляр. Теперь добавим выходные X, Y, Z, T, которые также будут векторами-строками:

Теперь LabVIEW полностью готов работать с нашим MATLAB файлом. Сохраняем проделанную работу сочетанием CTRL+SHIFT+S, переходим в рабочее пространство VI в блок Diagram и находим нашего FLorenz'a в палитре Project Items:

Видим, что у нашего элемента FLorenz имеется 7 входных и 4 выходных терминала, остаётся только правильно их подключить, что будет показано в следующей части:

4. Решение задачи с помощью MATLAB и LabVIEW NXG
Добавим цикл while:

Добавим требуемые Numeric Control и график на переднюю панель:

Соединим входные терминалы с созданными Numeric Control. Следует обратить внимание, что входные скаляры X, Y, Z, T мы должны преобразовать в массив 1x1 с помощью Build Array:

Нажимаем на все входные Tunnel и преобразуем их в Shift Register, попутно добавляя их с правого (выходного) края цикла while:


Подключаем выходные X, Y, Z, T к выходным Shift Register:

Объединяем данные с X и T в кластер и подключаем его к графику, тем самым завершая работу с блок-диаграммой:


Заполним входные данные:

В итоге получаем следующий результат:

5. Заключение
В стаье был рассмотрен способ интеграции кода MATLAB в проект LabVIEW на примере задачи о численном решении уравнений Лоренца методом Эйлера. Для получения большей информации советую ознакомиться со следующими источниками:
piva
Это именно интерфейс, а не эмулятор? Т.е., если MATLAB не установлен на компьютер, эта программа, написанная на MATLAB выполняться не сможет?
Есть ли возможность подменить MATLAB в работе с Labview каким-либо аналогом вроде Octave или Scilab?
Borjomy
Математический пакет LabView достаточно богат и сравним с Matlab. Вопрос привычки. Модуль поддержки Matlab, насколько мне известно, встроен в пакет Labview. Также встроена поддержка Питона. Однако по быстродействию они проигрывают родным пакетам.
Scheiner88 Автор
В статье речь именно про интерфейс. Для запуска проекта LabVIEW NXG, который ссылается на элемент этого интерфейса, должен быть установлен MATLAB.
В классических версиях LabVIEW существуют как модули (не требующие установленного MATLAB), так и элементы на диаграмме, куда вписывается код. Подробнее про эти возможности для классических версий можно найти тут.