Интеграция кода внешнего приложения с кодом, сгенерированным из ПИД-регулятора

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

Смотрите внешний код

Установите текущую папку в место с возможностью записи.

Скопируйте скрипт prepare_ext_code в текущую папку и запустите скрипт. Скрипт копирует файлы внешнего кода в несколько папок.

try
copyfile(fullfile(matlabroot,'examples','ecoder','main','prepare_ext_code.m'),...
    'prepare_ext_code.m','f');
catch
end
run('prepare_ext_code.m');

В текущей папке откройте ext_code_main.c. Этот код приложения представляет встраиваемой системе с тривиальным алгоритмом планирования (a while цикл) и три модуля, алгоритмы которых выполняются в каждом цикле выполнения: ex_ext_inputs_proc, ex_ext_ctrl_alg, и ex_ext_outputs_proc.

type('ext_code_main.c')

В shared папка, смотрите ex_ext_projTypes.h. Файл задает два пользовательских типа данных (typedef), которые используют данные в модулях.

type(fullfile('shared','ex_ext_projTypes.h'))

В io_drivers папка, смотрите ex_sensor_accessors.c. Этот файл определяет функции, get_fromSensor_flow и get_fromSensor_temp, которые возвращают необработанные данные, записанные датчиком потока и датчиком температуры. В данном примере функции возвращают тривиальные синусоидальные раздражители для алгоритма управления.

type(fullfile('io_drivers','ex_sensor_accessors.c'))

В ex_ext_inputs_proc папка, смотрите ex_ext_inputs_proc.c. The ex_ext_inputs_proc модуль считывает данные датчика (путем вызова функций доступа), фильтрует данные и хранит их в двух глобальных переменных, PROC_INPUT_FLOW и PROC_INPUT_TEMP. Эти глобальные переменные определены в файле ex_ext_proc_inputs.c и объявленный в ex_ext_proc_inputs.h.

type(fullfile('ex_ext_inputs_proc','ex_ext_inputs_proc.c'))

Алгоритм фильтра в этом модуле и алгоритмы в двух других модулях требуют данных о состоянии, которые должны сохраняться между циклами выполнения приложения. Каждый модуль хранит соответствующие данные о состоянии как глобальные переменные. Для примера, в ex_ext_inputs_proc модуль, ex_ext_inputs_proc.c задает следующие переменные:

  • flowFilterIn_state_data

  • tempFilterIn_state_data

  • flowFilterIn_tmp_data

  • tempFilterIn_tmp_data

Пустой ex_ext_ctrl_alg папка является заполнителем для сгенерированного кода. The ex_ext_ctrl_alg модуль должен выполнить управление ПИД для фильтрованных измерений расхода и температуры. Позже вы исследуете требования к этому коду, которые основаны на коде в других модулях.

В ex_ext_outputs_proc папка, смотрите ex_ext_outputs_proc.c. Этот модуль считывает ПИД выходные сигналы от глобальных переменных с именем CONTR_SIG_FLOW и CONTR_SIG_TEMP. The ex_ext_ctrl_alg модуль (сгенерированный код) должен задать эти переменные. The ex_ext_outputs_proc затем модуль фильтрует эти сигналы и передает отфильтрованные значения в функции, представляющие приводы устройства для приводов (для примера, клапана и нити накала нагревателя).

type(fullfile('ex_ext_outputs_proc','ex_ext_outputs_proc.c'))

Функции привода определены в io_drivers папка в файле ex_actuator_accessors.c.

Рисунок показывает предполагаемый поток данных через систему. Рисунок также показывает файлы кода, которые определяют и объявляют данные (глобальные переменные), которые пересекают контуры модуля.

Смотрите модель Simulink и определите требования к сгенерированному коду

Откройте пример модели ex_ext_ctrl_alg.

open_system('ex_ext_ctrl_alg')

Блоки в модели реализуют необходимый алгоритм управления ПИД. Модель использует настройки генерации кода по умолчанию для целевого файла системы на основе ERT (ert.tlc).

Чтобы завершить работу приложения, выполнив функцию ex_ext_ctrl_alg модуль, код, сгенерированный из этой модели, должен:

  • Используйте пользовательские типы данных dataPath_flow_T и dataPath_temp_T из внешнего файла ex_ext_projTypes.h.

  • Считайте отфильтрованные данные о датчике из глобальных переменных PROC_INPUT_FLOW и PROC_INPUT_TEMP. Сгенерированный код не должен определять эти переменные, потому что модуль ex_ext_inputs_proc определяет их, объявляя их в ex_ext_proc_inputs.h.

  • Запись ПИД управляющих сигналов в глобальные переменные с именем CONTR_SIG_FLOW и CONTR_SIG_TEMP. Сгенерированный код должен задать эти переменные и объявить их в файле с именем ex_ext_ctrl_sigs.h. Затем, ex_ext_outputs_proc модуль может считывать необработанные сигналы управления от них.

  • Соответствует стандартам именования переменных, которые управляют внешним приложением. Например, сгенерированный код должен хранить данные о состоянии в глобальных переменных, имена которых заканчиваются _data. В сложение для этого примера имена переменных локальной функции должны заканчиваться на _local.

  • Соответствовать стандартам, которые регулируют организацию кода в каждом из внешних файлов. Например, каждый файл содержит разделы, разделенные комментариями, которые агрегируют подобные конструкции кода, такие как определения типов, объявления переменных и определения функций.

Чтобы вы и другие могли взаимодействовать с алгоритмом управления во время выполнения, для этого примера вы также конфигурируете сгенерированный код, чтобы задать и объявить const глобальные переменные с именем PARAM_setpoint_flow и PARAM_setpoint_temp, которые представляют ПИД-регулятору уставки. Определения должны находиться в файле с именем ex_ext_ctrl_params.c и объявления должны находиться в файле с именем ex_ext_ctrl_params.h.

Сконфигурируйте модель, чтобы использовать пользовательские типы данных

Установите текущую папку в shared папка.

Используйте функцию Simulink.importExternalCTypes чтобы сгенерировать Simulink.AliasType объекты, которые представляют пользовательские типы данных dataPath_flow_T и dataPath_temp_T.

cd('shared')
Simulink.importExternalCTypes('ex_ext_projTypes.h');

Объекты появляются в базовом рабочем пространстве.

В ex_ext_ctrl_alg на вкладке Моделирование (Modeling) щелкните Редактор данных модели (Model Data Editor).

В Model Data Editor для блока Inport, который представляет PROC_INPUT_FLOW, Установите Тип Данных dataPath_flow_T.

Для блока Inport, который представляет PROC_INPUT_TEMP, Установите Тип Данных dataPath_temp_T.

Выберите вкладку Signals.

В модели выберите выходной сигнал каждого блока Constant. В Model Data Editor установите для типа данных значение Inherit: Inherit via back propagation. С помощью этой настройки каждый блок Constant наследует свой тип выходных данных от блока сразу после, в этом случае, блока Sum.

Кроме того, чтобы сконфигурировать типы данных, в командной строке используйте эти команды.

set_param('ex_ext_ctrl_alg/In1','OutDataTypeStr','dataPath_flow_T')
set_param('ex_ext_ctrl_alg/In2','OutDataTypeStr','dataPath_temp_T')
set_param('ex_ext_ctrl_alg/Flow Setpt','OutDataTypeStr',...
    'Inherit: Inherit via back propagation')
set_param('ex_ext_ctrl_alg/Temp Setpt','OutDataTypeStr',...
    'Inherit: Inherit via back propagation')

Обновите блок. Схема показывает, что из-за наследования и распространения типа данных сигналы в модели используют пользовательский тип данных.

Сконфигурируйте данные интерфейса

Сконфигурируйте модель, чтобы сгенерировать код, который обращается и определяет правильные глобальные переменные, такие как PROC_INPUT_FLOW и CONTR_SIG_TEMP.

В Model Data Editor выберите вкладку Inports/Outports и установите в раскрывающемся списке Change view значение Code.

Выберите строки, которые соответствуют двум блокам Inport.

Для каждой строки установите значение класса памяти равным ImportFromFile и заголовочный файл для ex_ext_proc_inputs.h. С классом памяти ImportFromFileкаждый блок Inport появляется в сгенерированном коде как глобальная переменная. Однако сгенерированный код не определяет переменную, вместо этого включая (#include) объявление переменной из ex_ext_proc_inputs.h.

Для строк, которые соответствуют блокам Outport, установите:

  • Класс памяти, к ExportToFile

  • Заголовочный файл для ex_ext_ctrl_sigs.h

  • Файл определения для ex_ext_ctrl_sigs.c

С ExportToFileсгенерированный код определяет глобальную переменную.

Кроме того, чтобы сконфигурировать сигналы, в командной строке используйте эти команды.

temp = Simulink.Signal;
temp.CoderInfo.StorageClass = 'Custom';
temp.CoderInfo.CustomStorageClass = 'ImportFromFile';
temp.CoderInfo.CustomAttributes.HeaderFile = 'ex_ext_proc_inputs.h';

portHandles = get_param('ex_ext_ctrl_alg/In1','portHandles');
outportHandle = portHandles.Outport;
set_param(outportHandle,'SignalObject',copy(temp))

portHandles = get_param('ex_ext_ctrl_alg/In2','portHandles');
outportHandle = portHandles.Outport;
set_param(outportHandle,'SignalObject',copy(temp))

temp.CoderInfo.CustomStorageClass = 'ExportToFile';
temp.CoderInfo.CustomAttributes.HeaderFile = 'ex_ext_ctrl_sigs.h';
temp.CoderInfo.CustomAttributes.DefinitionFile = 'ex_ext_ctrl_sigs.c';

set_param('ex_ext_ctrl_alg/Out1','SignalObject',copy(temp))
set_param('ex_ext_ctrl_alg/Out2','SignalObject',copy(temp))

Чтобы применить класс памяти к параметрам блоков, такому как параметр Constant значения блока Constant, необходимо создать такой объект параметра, как Simulink.Parameter. Можно использовать Редактор данных модели (Model Data Editor), чтобы создать объекты параметров.

Выберите вкладку Parameters и установите Change view на Design.

В модели выберите маркированный блок Constant Flow Setpt.

В Model Data Editor установите значение PARAM_setpoint_flow.

Рядом с PARAM_setpoint_flow, нажмите кнопку действия (с тремя вертикальными точками) и выберите Create.

В диалоговом окне «Создание новых данных» установите значение Simulink.Parameter(3).

Установите положение на Model Workspace и нажмите «Создать».

В PARAM_setpoint_flow диалоговое окно свойств, установите для класса памяти значение Const.

Установите значение HeaderFile на ex_ext_ctrl_params.h и DefinitionFile, чтобы ex_ext_ctrl_params.c.

Для другого блока Constant используйте Model Data Editor, чтобы создать Simulink.Parameter объект с именем PARAM_setpoint_temp со значением 2.

Кроме того, чтобы сконфигурировать блоки, в командной строке используйте эти команды.

PARAM_setpoint_flow = Simulink.Parameter(3);
PARAM_setpoint_flow.CoderInfo.StorageClass = 'Custom';
PARAM_setpoint_flow.CoderInfo.CustomStorageClass = 'Const';
PARAM_setpoint_flow.CoderInfo.CustomAttributes.HeaderFile = 'ex_ext_ctrl_params.h';
PARAM_setpoint_flow.CoderInfo.CustomAttributes.DefinitionFile = 'ex_ext_ctrl_params.c';

PARAM_setpoint_temp = copy(PARAM_setpoint_flow);
PARAM_setpoint_temp.Value = 2;

mdlwks = get_param('ex_ext_ctrl_alg','ModelWorkspace');
assignin(mdlwks,'PARAM_setpoint_flow',copy(PARAM_setpoint_flow))
assignin(mdlwks,'PARAM_setpoint_temp',copy(PARAM_setpoint_temp))

set_param('ex_ext_ctrl_alg/Flow Setpt','Value','PARAM_setpoint_flow')
set_param('ex_ext_ctrl_alg/Temp Setpt','Value','PARAM_setpoint_temp')

clear temp PARAM_setpoint_flow PARAM_setpoint_temp mdlwks portHandles outportHandle

Сконфигурируйте внутренние данные

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

В диалоговом окне Параметры конфигурации модели смотрите панель Генерация кода > Идентификаторы. Когда вы задаете значения для схем именования под управлением формата идентификатора:

  • $R представляет имя модели, ex_ext_ctrl_alg.

  • $N представляет имя каждого элемента модели, к которому применяется схема, такого как сигнал, состояние блока или стандартная структура данных.

  • $M представляет текст управления именами, который вставляет генератор кода, при необходимости, чтобы избежать конфликтов имен. Для большинства правил именования требуется эта лексема.

Установите глобальные переменные на $R$N_data$M. Этот параметр управляет именами глобальных переменных, таких как те, которые представляют данные о состоянии. Схема именования $R$N_data_$M наиболее близко аппроксимирует схему, которую используют переменные состояния во внешнем коде.

Установите Локальные временные переменные и выходные переменные Локальные блоки в $N_local$M.

Кроме того, чтобы задать параметры конфигурации, в командной строке используйте эти команды.

set_param('ex_ext_ctrl_alg','CustomSymbolStrGlobalVar','$R$N_data$M')
set_param('ex_ext_ctrl_alg','CustomSymbolStrTmpVar','$N_local$M')
set_param('ex_ext_ctrl_alg','CustomSymbolStrBlkIO','$N_local$M')

Сконфигурируйте данные о состоянии в модели, чтобы они появлялись в сгенерированном коде как отдельные глобальные переменные вместо полей стандартной структуры DWork. Включите перспективу Кода. В галерее Apps нажмите Embedded Coder.

Под блок схемой в разделе Кода Отображений > Значения по умолчанию для строки внутренних данных в Класс памяти столбце выберите ExportedGlobal.

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

coder.mapping.create('ex_ext_ctrl_alg');
coder.mapping.defaults.set('ex_ext_ctrl_alg','InternalData',...
    'StorageClass','ExportedGlobal');

Сконфигурируйте организацию кода в каждом сгенерированном файле

Установите текущую папку в ex_ext_ctrl_alg папка.

cd(fullfile('..','ex_ext_ctrl_alg'))

В командной строке перейдите к папке, содержащей встроенный шаблон генерации кода ert_code_template.cgt. По умолчанию, когда вы генерируете код с системным целевым файлом ert.tlcэтот шаблон управляет организацией и, отчасти, внешним видом кода в каждом сгенерированном файле.

currentFolder = pwd;
cd(fullfile(matlabroot,'toolbox','rtw','targets','ecoder'))

Скопируйте ert_code_template.cgt файл в буфер обмена.

Вернуться к ex_ext_ctrl_alg папка. Чтобы избежать перезаписи буфера обмена, вместо использования командной строки для перехода в папку можно использовать кнопку «Назад» в MATLAB.

cd(currentFolder)
clear currentFolder

Вставьте файл в ex_ext_ctrl_alg папка. Переименуйте файл в ex_my_code_template.cgt.

Откройте файл и замените содержимое этим кодом.

type(fullfile(matlabroot,'examples','ecoder','data','ex_my_code_template.cgt'))

Этот новый шаблон более точно соответствует организации внешних файлов. Например, шаблон организует подобные конструкции кода в именованные разделы (разделенные комментариями) и, в верхней части шаблона, задает только минимальную информацию о каждом сгенерированном файле.

В модели выберите Параметры конфигурации > Генерация кода > Шаблоны.

В разделе Шаблоны кода и шаблоны данных установите четыре параметра конфигурации равными ex_my_code_template.cgt.

Кроме того, чтобы скопировать файл и задать параметры, в командной строке используйте эти команды.

copyfile(fullfile(matlabroot,'examples','ecoder','data','ex_my_code_template.cgt'),...
    'ex_my_code_template.cgt','f')
set_param('ex_ext_ctrl_alg','ERTSrcFileBannerTemplate','ex_my_code_template.cgt')
set_param('ex_ext_ctrl_alg','ERTHdrFileBannerTemplate','ex_my_code_template.cgt')
set_param('ex_ext_ctrl_alg','ERTDataSrcFileTemplate','ex_my_code_template.cgt')
set_param('ex_ext_ctrl_alg','ERTDataHdrFileTemplate','ex_my_code_template.cgt')

Сгенерируйте и смотрите код

Потому что внешний код уже определяет main выберите Параметры конфигурации > Сгенерировать только код и очистить Параметры конфигурации > Сгенерировать пример основной программы.

set_param('ex_ext_ctrl_alg','GenCodeOnly','on')
set_param('ex_ext_ctrl_alg','GenerateSampleERTMain','off')

Сгенерируйте код из модели.

rtwbuild('ex_ext_ctrl_alg')

В отчете о генерации кода проверьте сгенерированные файлы. Код соответствует требованиям. Для примера, ex_ext_ctrl_sigs.c определяет выходные сигналы управления, CONTR_SIG_FLOW и CONTR_SIG_TEMP.

file = fullfile('ex_ext_ctrl_alg_ert_rtw','ex_ext_ctrl_sigs.c');
rtwdemodbtype(file,'dataPath_flow_T CONTR_SIG_FLOW;',...
    'dataPath_temp_T CONTR_SIG_TEMP;',1,1)

Параметры уставки появляются в ex_ext_ctrl_params.c.

file = fullfile('ex_ext_ctrl_alg_ert_rtw','ex_ext_ctrl_params.c');
rtwdemodbtype(file,'const dataPath_flow_T PARAM_setpoint_flow = 3.0;',...
    'const dataPath_temp_T PARAM_setpoint_temp = 2.0;',1,1)

Файл ex_ext_ctrl_alg.c задает глобальные переменные для хранения данных о состоянии. Переменные следуют указанной схеме именования. Код аналогичен следующему:

dataPath_temp_T ex_ext__Integrator_DSTATE_datag;
dataPath_flow_T ex_ext_c_Integrator_DSTATE_data;
dataPath_temp_T ex_ext_ctrl_Filter_DSTATE_datad;
dataPath_flow_T ex_ext_ctrl__Filter_DSTATE_data;

В том же файле функция выполнения модели, ex_ext_ctrl_alg_step, создает локальные переменные функции для хранения временных вычислений. Переменные следуют указанной схеме именования. Код аналогичен следующему:

dataPath_flow_T Diff_local;
dataPath_flow_T FilterCoefficient_local;
dataPath_temp_T Diff1_local;
dataPath_temp_T FilterCoefficient_locali;

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

Сгенерированные коды находятся в сгенерированных папках ex_ext_ctrl_alg_ert_rtw и slprj. Необходимо сконфигурировать системы управления файлами и инструменты сборки, чтобы использовать эти папки и файлы.

Похожие темы