Опыт использования стандартных интерфейсов MATLAB для организации разноязыкового программирования в задачах математического и полунатурного моделирования авиационной техники
Просмотров: 3622
Декабрь 2013 года
Набатчиков А. М., Бурлак Е. А. Опыт использования стандартных интерфейсов MATLAB для организации разноязыкового программирования в задачах математического и полунатурного моделирования авиационной техники // Электронный научно-технический журнал "Инженерный вестник". – 2013. – №12. – С. 501-516.
УДК: 629.7:004:331.101.1
Ключевые слова: matlab; mat; mex; C++; code::blocks; работа с dll
Статья на сайте журнала
Ключевые слова: matlab; mat; mex; C++; code::blocks; работа с dll
Статья на сайте журнала
Аннотация
В статье рассматриваются два внешних интерфейса популярного пакета прикладных программ MATLAB: MAT и MEX. Приведены примеры создания расширений в бесплатной кросплатформенной IDE (Integrated Development Environment) Code::Blocks с использованием языка программирования C++. Даны рекомендации по конфигурации систем и практически проверенные примеры описания интерфейсов.
В качестве практического применения MAT и MEX могут выступать такие задачи, как обработка экспериментальных данных, математический анализ моделей динамики авиационной техники, организация полунатурного моделирования в реальном масштабе времени. Приведённый исходный код ориентирован на читателя с базовыми познаниями языков C++ и M.
Введение
MATLAB - один из лидеров в области обработки данных и математического моделирования. Пакет обладает большим количеством штатных библиотек и алгоритмических решений. Цель данной статьи – рассмотреть некоторые интерфейсы системы MATLAB, позволяющие расширять функциональные возможности за счёт использования приложений, написанных на языке C++. Указанные средства применялись на практике авторами данной статьи для анализа моделей динамики летательного аппарата [1] и для послеполётной обработки экспериментальных стендовых данных [12]. Интерфейсы позволяют создать предельно быструю реализацию алгоритма (за счёт многопоточности [2, 3, 15], использования языка ассемблера или вычислительных мощностей видеокарты) и создавать более быстродействующий транслированный код вместо принятой в MATLAB интерпретации [4, 5, 6]. Кроме того, как известно [7], никакой другой язык, созданный до или после языка C++, не давал программисту более полного контроля над компьютером.
Несмотря на существование штатной справки, учебников и других материалов, разобраться в подобных пособиях программисту, не имеющему обширного опыта разработки и использования внешних динамически подключаемых библиотек, весьма сложно. Среди причин можно выделить:
- отсутствие в описаниях процедур разработки ряда важных деталей;
- наличие в инструкциях неточностей и неоднозначностей;
- ориентация пособий на определённую среду разработки (как правило, Visual Studio) и отсутствие рекомендаций по использованию Code::Blocks;
В статье приводятся простые и ясные, полностью работоспособные примеры, прошедшие апробацию на исследовательском стенде. Указанный далее исходный код составлен таким образом, чтобы продемонстрировать максимум гибкости и возможностей того или иного способа обмена информацией. Тем не менее предполагается, что читатель обладает базовыми знаниями используемых языков программирования [9, 5], и при необходимости может осуществить дополнительную оптимизацию и контроль [8] в соответствии с требованиями конкретных задач.
Характеристики стенда
Рассматриваемые примеры собираются на ПЭВМ с установленной операционной системой Windows (версия системы должна позволять инсталлировать приведённые далее программы) и пакетом прикладных программ MATLAB версии 7.5.0.342 (R2007b) (32-битная версия). Сборка программ и библиотек производится с помощью бесплатной кросплатформенной IDE Code::Blocks 10.05 и компилятора GNU (GNUs Not UNIX) GCC (GNU Compiler Collection) (идущего в комплекте). Разработка ведётся на языках C++ и M. Корневая директория MATLAB в приведённых далее путях именуется
matlabroot
Работа с форматом MAT
MAT-файл - это формат файла данных системы MATLAB, предназначенный для сохранения данных в энергонезависимой памяти ПЭВМ. Таким образом, формат позволяет обмениваться данными между платформами или совершать операции импорта и экспорта данных отдельным самостоятельным приложениям. Для упрощения работы стороннего программиста, пакет MATLAB включает в себя файлы динамической линковки (DLL) и сопутствующие им библиотеки импорта (LIB), реализующие API (Application Programming Interface) работы с MAT-файлом.
Полный список функций, для работы с форматом MAT (функции имеют префикс
mat
Шаг 1. Создаём консольное приложение (
Console application
Шаг 2. В меню
«Project»
«Build options…»
«Linker settings»
«Link libraries»
«Add»
libmat.lib
libmx.lib
matlabroot\extern\lib\win32\microsoft
Шаг 3. В меню
«Project»
«Build options…»
«Search directories»
«Compiler»
«Add»
matlabroot\extern\include
Шаг 4. Для использования API подключаем в исходном коде разрабатываемой программы (директивой препроцессора
#include
mat.h
matrix.h
libmat.dll
libmx.dll
matlabroot\bin\win32
Path
Path
На этом процесс подготовки проекта завершён, и можно переходить непосредственно к работе с API.
Для того чтобы начать экспериментировать с форматом MAT, необходимо подготовить тестовый файл, например, описанным далее способом. Выполним следующий код в командной строке MATLAB (см. листинг 1).
Листинг 1. Создание переменных в MATLAB
1 | FOO=[1 2;3 4];BAR=[10 9+0.1i 8 7];QAZ='test';
|
Результатом выполнения команды станет создание матрицы
FOO
BAR
QAZ
Для сохранения этих данных в виде MAT-файла необходимо выбрать меню
«File»
«Save Workspace As…»
matlab.mat
Теперь перейдём к созданию программы взаимодействия с файлом. В листинге 2 приведён пример чтения данных из файла.
Листинг 2. Открытие и закрытие файла; получение переменных
12345678910111213141516171819202122232425262728 | MATFile *hFILE=matOpen("matlab.mat","r"); //Попытка открыть MAT-файл в режиме чтения if(hFILE==0){ //Ошибка открытия cout<<"error: open"; return 1; } int num; //Попытка получить список переменных char **names=matGetDir(hFILE,&num); if(names==0){ //Ошибка получения списка cout<<"error: GetDir"; } else{ for(int i=0;i<num;i++){ //Здесь будет код вывода данных } //Освобождение памяти, занятой именами mxFree(names); } int CCode=matClose(hFILE); //Попытка закрыть файл if(CCode==-1){ //Ошибка закрытия cout<<"error: close"; } |
Скрытый за комментарием блок вывода данных приводится в листинге 3. Листинг иллюстрирует получение некоторой информации о каждой переменной, хранящейся в MAT-файле.
Листинг 3. Вывод информации о переменных
12345678910111213141516171819202122232425262728293031323334353637383940 | cout<<names[i]<<"; ["; //Попытка получить указатель на массив данных по имени mxArray *Arr=matGetVariable(hFILE,names[i]); if(Arr!=0){ //Попытка удалась //Получаем количество измерений массива mwSize NoD=mxGetNumberOfDimensions(Arr); //Получаем указатель на массив размеров const mwSize *DimSz=mxGetDimensions(Arr); //В цикле выводим размеры массива //(например: [2x3]) for(int j=0;j<NoD;j++){ cout<<DimSz[j]; if(j!=NoD-1){ cout<<"x"; } } cout<<"] total "; //Выводим общее количество элементов cout<<mxGetNumberOfElements(Arr); cout<<"; size 1 el = "; //Выводим объём памяти, занимаемый //одним элементов cout<<mxGetElementSize(Arr); cout<<" bytes ["; //Выводим текстовое представление //класса данных массива cout<<mxGetClassName(Arr); cout<<"]"<<endl; //Проверяем: имеет ли массив //мнимую часть if(mxIsComplex(Arr)){ cout<<"[complex]"<<endl; } //Здесь будет код вывода элементов массива //Очищаем память mxDestroyArray(Arr); } cout<<endl; |
Следующий листинг последовательно выводит значения элементов массивов. Отметим, что многомерные массивы в MATLAB индексируются одним числом, причём «пробегающим» элементы столбец за столбцом, вместо более привычного построчного прохода [10]. Данное обстоятельство вынуждает использовать «нетрадиционную» формулу для сопоставления индекса с номерами строки и столбца, пересечение которых даёт требуемый элемент матрицы.
Листинг 4. Вывод значений элементов массивов
1234567891011121314151617181920212223242526272829 | //Циклы перебора элементов for(int y=0;y<mxGetM(Arr);y++){ for(int x=0;x<mxGetN(Arr);x++){ //Выбор наиболее подходящего метода вывода switch(mxGetClassID(Arr)){ //числа case mxDOUBLE_CLASS:{ //Получение указателей на //действительную… double *Re=mxGetPr(Arr); //… и мнимую части double *Im=mxGetPi(Arr); //вывод значения cout<<setw(3)<<Re[x*mxGetM(Arr)+y]; if(Im!=0){ cout<<"+"<<setw(3)<<Im[x*mxGetM(Arr)+y]<<"i;"; } break; } //текст case mxCHAR_CLASS:{ //вывод очередного символа cout<<char(mxGetChars(Arr)[x*mxGetM(Arr)+y]); break; } } } cout<<endl; } |
Результат работы программы представлен на рисунке 4.
Рассмотрим теперь процесс записи данных в файл. Для открытия файла в режиме записи данных, необходимо использовать флаг
u
w
Листинг 5. Изменение переменных и запись их в файл
1234567891011121314 | //Получение переменной по имени mxArray *Vec=matGetVariable(hFILE,"BAR"); //Получение указателя на массив действительных значений double *RePart=mxGetPr(Vec); //Изменение действительной части первого элемента RePart[0]=99; //Сохранение изменённого массива под именем BAR2 matPutVariable(hFILE,"BAR2",Vec); Vec=matGetVariable(hFILE,"QAZ"); //Изменение второго символа в строке mxGetChars(Vec)[1]='E'; //Сохранение под существующим именем QAZ matPutVariable(hFILE,"QAZ",Vec); |
Рисунок 5 показывает загруженный в MATLAB модифицированный MAT-файл.
На этом рассмотрение данного интерфейса закончим. Полный список функций для работы со встроенными массивами MATLAB (функции имеют префикс
mx
Создание и эксплуатация MEX-файла
MATLAB предусматривает возможность использовать C-код, написанный сторонним разработчиком, как встроенную в систему функцию. Данная возможность реализуется с помощью DLL-файла, который MATLAB может автоматически загружать и выполнять. Подобные файлы, созданные с соблюдением ряда требований, реализуют собой MEX-функции (M – MATLAB, EX – External («внешний») [5]). Использование MEX-файлов имеет два основных преимущества: отсутствие необходимости переписывать давно существующее решение на язык M [12] и возможность повышения производительности в «узких местах» [10]. Вызов MEX-функции в среде MATLAB аналогичен вызову M-функции: в качестве имени выступает имя DLL-файла.
В справке [10] утверждается, что в системе Windows, MEX-файл должен иметь расширение
mexw32
mexw64
mexext
dll
Рассмотрим последовательность действий по созданию MEX-файла.
Шаг 1. Создаём проект динамически подключаемой библиотеки (Dynamic Link Library).
Шаг 2. Создаём Module-Definition File (DEF-файл) и добавляем его к проекту. Для этого выбираем меню
«File»
«New»
«Empty file»
def
Шаг 3. Указываем в созданном DEF-файле содержимое листинга 6 (пример для проекта с именем
«MexTest»
Листинг 6. Содержимое файла MexTest.def
12 | LIBRARY MexTest EXPORTS mexFunction |
Шаг 4. В меню
«Project»
«Build options…»
«Linker settings»
«Link libraries»
«Add»
libmat.lib
libmx.lib
libmex.lib
matlabroot\extern\lib\win32\microsoft
Шаг 5. В меню
«Project»
«Build options…»
«Search directories»
«Compiler»
«Add»
matlabroot\extern\include
Шаг 6. Для использования API подключаем в исходном коде разрабатываемой программы (директивой препроцессора
#include
mex.h
Шаг 7. Удаляем созданные автоматически в проекте участки кода: объявление (из файла
main.h
main.cpp
void DLL_EXPORT SomeFunction
Шаг 8. В меню
«Project»
«Properties…»
«Build targets»
«Output filename»
mexw32
«Auto-generate filename extension»
Debug
Release
«Build targets»
Шаг 9. Основной код размещается в функции со следующим фиксированным именем и прототипом
void mexFunction(int nOut, mxArray* pOut[], int nIn, const mxArray* pIn[]).
Именно функция
mexFunction
Таблица 1. Аргументы функции
mexFunction
Аргумент | Описание |
int nOut | Количество аргументов, переданных из MATLAB. |
mxArray* pOut[] | Массив указателей на переданные аргументы. |
int nIn | Количество выходных параметров, запрашиваемых MATLAB-ом. |
const mxArray* pIn[] | Массив указателей на возвращаемые параметры. |
Полный список функций для работы с mex интерфейсом (функции имеют префикс
mex
Листинг 7. Исходный код MEX-функции
123456789101112131415161718192021222324252627282930313233343536373839404142 | //Проверка количества аргументов if(nIn!=1){ mexErrMsgTxt("nIn error"); } //Проверка количества возвращаемых значений if(nOut!=2){ mexErrMsgTxt("nOut error"); } //Указатель на первый аргумент const mxArray *Arr=pIn[0]; //Проверка: входной массив комплексный if(mxIsComplex(Arr)){ mexErrMsgTxt("pIn[0] is complex"); } //Проверка: входной массив – двумерная матрица if(mxGetM(Arr)!=1 && mxGetN(Arr)!=1){ mexErrMsgTxt("pIn[0] is 2D"); } //все проверки пройдены. //Выделяем память под возвращаемые значения: //две матрицы [1x1] типа double без мнимой части pOut[0]=mxCreateNumericMatrix(1, 1, mxDOUBLE_CLASS, mxREAL); pOut[1]=mxCreateNumericMatrix(1, 1, mxDOUBLE_CLASS, mxREAL); //указатель на массив действительных значений //аргумента double *f=mxGetPr(Arr); //Ссылки на будущие min и max double &min=mxGetPr(pOut[0])[0]; double &max=mxGetPr(pOut[1])[0]; //Первичное значение min=max=f[0]; //Поиск экстремумов в оставшейся части массива for(unsigned i=1;i<mxGetNumberOfElements(Arr);i++){ if(f[i]<min)min=f[i]; else if(f[i]>max)max=f[i]; } |
Программист может завершить вызов функции с выдачей диагностического сообщения в окно MATLAB, с помощью функции
mexErrMsgTxt
Среди прочих отметим интересную возможность: вызывать M-функции и операторы MATLAB из MEX-функции посредством
mexCallMATLAB
clear all
Листинг 8. Использование mexCallMATLAB
12345678910111213141516171819202122 | //Выше происходят необходимые проверки //Массивы указателей – аргументы mexCallMATLAB mxArray *OutMTLB[1]; mxArray *InMTLB[1]; //На вход – копия массива, //переданного в MEX InMTLB[0]=mxDuplicateArray(Arr); //Вызов m-функции “min” mexCallMATLAB(1,OutMTLB,1,InMTLB,"min"); //Создание скаляра и сохранение в //него возвращённого значения pOut[0]=mxCreateDoubleScalar(mxGetPr(OutMTLB[0])[0]); //Уничтожение ненужного массива //(созданного MATLAB при вызове “min”) mxDestroyArray(OutMTLB[0]); //Аналогично… mexCallMATLAB(1,OutMTLB,1,InMTLB,"max"); pOut[1]=mxCreateDoubleScalar(mxGetPr(OutMTLB[0])[0]); mxDestroyArray(OutMTLB[0]); //Уничтожение ненужного массива mxDestroyArray(InMTLB[0]); |
Листинг 9 демонстрирует пример использования функции в среде MATLAB.
Листинг 9. Вызов MEX-функции
12345678910 | >> [min max]=MexTest([1 2 3 -1]) min = -1 max = 3 |
К сожалению, MEX, в отличие от аналогичного интерфейса UserEFI, предоставляемого системой Mathcad, позволяет экспортировать только одну функцию в файле, но данный недостаток можно компенсировать, используя непосредственное взаимодействие с DLL-файлом (
loadlibrary
calllib
Дополнительную информацию по взаимодействию с MATLAB можно найти в [13].
Заключение
Таким образом, в работе предложено простое описание интерфейсов, снабжённое практическими примерами. Рассмотренные интерфейсы – одни из наиболее популярных в MATLAB, хоть и не единственные. Именно они широко используются для разноязыкового программирования, поскольку MAT совмещён с развитыми библиотеками по обмену и хранению информации, а MEX обеспечивает возможность исследовать модели, реализованные на C++, методами, предоставляемыми MATLAB. Данные средства применяются, например, при полунатурном стендовом моделировании: для обработки экспериментальных данных, математического анализа используемых моделей, организации полунатурного моделирования в реальном масштабе времени [14].
Список литературы
- Аэродинамика, устойчивость и управляемость сверхзвуковых самолетов. /Под редакцией Г.С. Бюшгенса. М.: Наука, 1998, – 811 с.
- Огинский А.А., Набатчиков А.М., Бурлак Е.А. Организация межпотокового взаимодействия с использованием объектов ядра операционной системы // Вестник компьютерных и информационных технологий. – 2012. – №7. – С. 48-52.
- Огинский А.А., Набатчиков А.М., Бурлак Е.А. Организация межпотокового взаимодействия с использованием объектов ядра операционной системы // Вестник компьютерных и информационных технологий. – 2012. – №8. – С. 52-56.
- Таненбаум Э. Архитектура компьютера. – СПб.: Питер, 2007. – 844 с.: ил.
- Мартынов Н.Н., Иванов А.П. MATLAB 5.x Вычисление, визуализация, программирование – М.: КУДИЦ-ОБРАЗ, 2000. – 336 с.
- Ахо А. и др. Компиляторы: принципы, технологии и инструментарий, 2-е изд.: Пер. с англ. - М.: ООО "И.Д. Вильямс", 2008. - 1184 с. : ил. - Парал. тит. англ.
- Шилдт Г. Искусство программирования на C++. – СПб.: БХВ-Петербург, 2005. – 496 с.: ил.
- Дьюхэрст С. Скользкие места C++. – М.: ДМК Пресс, 2006. – 264 с.: ил.
- Страуструп Б. Язык программирования С++. – М.: Бином, 2011. – 1136 с.
- Справка MATLAB.
- Извращенный перехват // Хакер. – 2012. – №5. – С. 96-98.
- Корсун О.Н., Бурлак Е.А., Набатчиков А.М. Исследовательский полунатурный стенд для анализа задач пилотирования и алгоритмов обработки полетных данных // Седьмой международный аэрокосмический конгресс IAC’2012. Сб. научн. тр. М.: 2013. 1 электрон. опт. диск (CD-ROM) рег. № 0321303652/03.06.2013.
- Бей И. Взаимодействие разноязыковых программ. Руководство программиста – М.: Издательский дом "Вильямс", 2005. – 880 с.: ил. эл. опт. диск (CD-ROM).
- Корсун О. Н., Набатчиков А. М., Бурлак Е. А. Синхронизация информационных потоков при полунатурном моделировании движения летательных аппаратов // Электронный научно-технический журнал "Инженерный вестник", - 2013. - №10. - С. 1-16.
- Е.А. Бурлак, А.М. Набатчиков, А.А. Огинский "Повышение производительности систем моделирования полёта на базе многоядерных ПЭВМ". С. 334-339. Проблемы совершенствования робототехнических и интеллектуальных систем летательных аппаратов: Сб. докл. IX-й Всерос. науч.-техн. конф. "Проблемы совершенствования робототехнических и интеллектуальных систем летательных аппаратов", Москва, Мос. авиац. ин-т, 25-26 июня 2012 г. /Редкол.: К.М. Тихонов (пред.). - М.: Изд-во МАИ-ПРИНТ, 2012. - 448 с.: ил.
Комментарии