exponenta event banner

nlmpcmoveCodeGeneration

Вычислительный нелинейный контроль MPC перемещается с поддержкой генерации кода

Описание

пример

[mv,newOnlineData] = nlmpcmoveCodeGeneration(coreData,x,lastMV,onlineData) вычисляет оптимальные нелинейные перемещения управления MPC и поддерживает генерацию кода для развертывания в целях реального времени. Контрольные перемещения вычисляются с использованием состояний текущей модели прогнозирования (x), управление перемещается из предыдущего интервала управления (lastMV) и структуры входных данных (coreData и nlOnlineData) генерируется с помощью getCodeGenerationData.

nlmpcmoveCodeGeneration не проверяет входные аргументы на правильность измерений и типов данных.

[___,info] = nlmpcmoveCodeGeneration(___) возвращает дополнительную информацию о результате оптимизации, включая количество итераций и стоимость целевой функции.

Примеры

свернуть все

Создайте нелинейный контроллер MPC с четырьмя состояниями, двумя выходами и одним входом.

nlobj = nlmpc(4,2,1);
In standard cost function, zero weights are applied by default to one or more OVs because there are fewer MVs than OVs.

Укажите время выборки и горизонты контроллера.

Ts = 0.1;
nlobj.Ts = Ts;
nlobj.PredictionHorizon = 10;
nlobj.ControlHorizon = 5;

Укажите функцию состояния контроллера, которая находится в файле pendulumDT0.m. Эта дискретно-временная модель интегрирует непрерывную временную модель, определенную в pendulumCT0.m с использованием многоступенчатого прямого метода Эйлера.

nlobj.Model.StateFcn = "pendulumDT0";
nlobj.Model.IsContinuousTime = false;

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

nlobj.Model.NumberOfParameters = 1;
params = {Ts};

Укажите функцию вывода модели, передав параметр времени выборки в качестве входного аргумента.

nlobj.Model.OutputFcn = "pendulumOutputFcn";

Определите стандартные ограничения для контроллера.

nlobj.Weights.OutputVariables = [3 3];
nlobj.Weights.ManipulatedVariablesRate = 0.1;
nlobj.OV(1).Min = -10;
nlobj.OV(1).Max = 10;
nlobj.MV.Min = -100;
nlobj.MV.Max = 100;

Проверка функций модели прогнозирования.

x0 = [0.1;0.2;-pi/2;0.3];
u0 = 0.4;
validateFcns(nlobj,x0,u0,[],params);
Model.StateFcn is OK.
Model.OutputFcn is OK.
Analysis of user-provided model, cost, and constraint functions complete.

Измеримы только два состояния растений. Поэтому создайте расширенный фильтр Калмана для оценки четырех состояний установки. Его функция перехода состояния определена в pendulumStateFcn.m и его измерительная функция определена в pendulumMeasurementFcn.m.

EKF = extendedKalmanFilter(@pendulumStateFcn,@pendulumMeasurementFcn);

Определите начальные условия для моделирования, инициализируйте расширенное состояние фильтра Калмана и укажите нулевое начальное управляемое значение переменной.

x0 = [0;0;-pi;0];
y0 = [x0(1);x0(3)];
EKF.State = x0;
mv0 = 0;

Создание структур данных генерации кода для контроллера с указанием исходных условий и параметров.

[coreData,onlineData] = getCodeGenerationData(nlobj,x0,mv0,params);

Укажите ссылочное значение вывода в оперативной структуре данных.

onlineData.ref = [0 0];

Чтобы проверить работу контроллера, выполните моделирование для 10 секунд. В течение каждого контрольного интервала:

  1. Исправьте предыдущий прогноз с помощью текущего измерения.

  2. Вычисление оптимальных перемещений управления с помощью nlmpcmoveCodeGeneration. Эта функция возвращает вычисленные оптимальные последовательности в onlineData. Передача обновленной структуры данных в nlmpcmoveCodeGeneration в следующем интервале управления обеспечивает начальные догадки для оптимальных последовательностей.

  3. Спрогнозировать состояния модели.

  4. Примените первое вычисленное оптимальное контрольное перемещение к установке, обновив состояния установки.

  5. Генерируйте данные датчика с белым шумом.

  6. Сохраните состояния завода.

mv = mv0;
y = y0;
x = x0;
Duration = 10;
xHistory = x0;
for ct = 1:(Duration/Ts)
    % Correct previous prediction
    xk = correct(EKF,y);
    % Compute optimal control move
    [mv,onlineData] = nlmpcmoveCodeGeneration(coreData,xk,mv,onlineData);
    % Predict prediction model states for the next iteration
    predict(EKF,[mv; Ts]);
    % Implement first optimal control move
    x = pendulumDT0(x,mv,Ts);
    % Generate sensor data
    y = x([1 3]) + randn(2,1)*0.01;
    % Save plant states
    xHistory = [xHistory x];
end

Создайте функцию MEX с помощью MATLAB ® Coder™, указавcoreData как константа.

func = 'nlmpcmoveCodeGeneration';
funcOutput = 'nlmpcmoveMEX';
Cfg = coder.config('mex');
Cfg.DynamicMemoryAllocation = 'off';
codegen('-config',Cfg,func,'-o',funcOutput,'-args',...
    {coder.Constant(coreData),xk,mv,onlineData});
Code generation successful.

Входные аргументы

свернуть все

Нелинейные параметры конфигурации MPC, постоянные во время выполнения, заданные как структура, сгенерированная с помощью getCodeGenerationData.

Примечание

При использовании codegen (Кодер MATLAB), coreData должно быть определено как coder.Constant (Кодер MATLAB).

Текущие состояния модели прогнозирования, заданные как вектор lengthNx, где Nx - количество состояний модели прогнозирования. Функция состояния модели прогнозирования определена в nlobj.Model.StateFcn.

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

Управляющие сигналы, используемые в установке на предыдущем интервале управления, определяемом как вектор столбца lengthNmv, где Nmv - количество манипулируемых переменных.

Примечание

Определить lastMV как управляемые переменные сигналы, подаваемые на установку в предыдущем интервале управления. Обычно эти сигналы являются значениями, генерируемыми контроллером (mv). Однако это не всегда так. Например, если контроллер находится в автономном режиме и работает в режиме отслеживания; то есть выход контроллера не приводит в действие установку, а затем подает фактический управляющий сигнал на last_mv может помочь достичь безударной передачи, когда контроллер переключен обратно в оперативный режим.

Данные оперативного контроллера, которые необходимо обновить во время выполнения, указанные как структура со следующими полями. Создание начальной структуры с помощью getCodeGenerationData. Некоторые поля структуры не требуются, в зависимости от конфигурации контроллера и того, какие веса или ограничения изменяются во время выполнения.

Исходные значения вывода растения, заданные как вектор строки длиной Ny или массив со столбцами Ny, где Ny - количество выходных переменных.

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

Чтобы изменять опорные значения по горизонту прогнозирования от времени k + 1 до времени k + p, укажите массив, содержащий до p строк. Здесь k - текущее время, а p - горизонт прогнозирования. Каждая строка содержит ссылочные значения для одного шага горизонта прогнозирования. Если указано меньше p строк, значения в последней строке используются для оставшихся шагов горизонта прогнозирования.

Если функция затрат контроллера не использует ref, оставить ref по его значению по умолчанию.

Управляемые переменные цели, определяемые как вектор строки длиной Nmv или массив со столбцами Nmv, где Nmv - количество управляемых переменных.

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

Чтобы изменять цели по горизонту прогнозирования (предварительный просмотр) от времени k до времени k + p-1, укажите массив, содержащий до p строк. Здесь k - текущее время, а p - горизонт прогнозирования. Каждая строка содержит цели для одного шага горизонта прогнозирования. Если указано меньше p строк, конечные цели используются для оставшихся шагов горизонта прогнозирования.

Если функция затрат контроллера не использует mvTarget, оставить mvTarget по его значению по умолчанию.

Начальные догадки для решений оптимального состояния, заданные как вектор строки длиной Nx или массив со столбцами Nx, где Nx - число состояний.

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

Чтобы изменить начальные догадки по горизонту прогнозирования от времени k + 1 до времени k + p, укажите массив, содержащий до p строк. Здесь k - текущее время, а p - горизонт прогнозирования. Каждая строка содержит начальные догадки для одного шага горизонта прогнозирования. Если указано меньше p строк, последние предположения используются для оставшихся шагов горизонта прогнозирования.

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

Начальные предположения для оптимальных решений с управляемыми переменными, определяемые как вектор строки длиной Nmv или массив со столбцами Nmv, где Nmv - количество обрабатываемых переменных.

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

Чтобы изменять начальные догадки по горизонту прогнозирования от времени k до времени k + p-1, укажите массив с длиной до p строк. Здесь k - текущее время, а p - горизонт прогнозирования. Каждая строка содержит начальные догадки для одного шага горизонта прогнозирования. Если указано меньше p строк, последние предположения используются для оставшихся шагов горизонта прогнозирования.

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

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

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

Измеренные значения возмущений, определенные как вектор строки длиной Nmd или матрица со столбцами Nmd, где Nmd - количество измеренных возмущений. Если контроллер измерил нарушения, необходимо указать md. Если у вашего контроллера нет измеренных возмущений, то getCodeGenerationData пропускает это поле.

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

Чтобы изменять значения возмущений по горизонту прогнозирования от времени k до времени k + p, укажите массив, содержащий до p + 1 строк. Здесь k - текущее время, а p - горизонт прогнозирования. Каждая строка содержит значения возмущений для одного шага горизонта прогнозирования. Если указано меньше p строк, значения в последней строке используются для оставшихся шагов горизонта прогнозирования.

Значения параметров, используемые моделью прогнозирования, функцией пользовательских затрат и пользовательскими ограничениями, заданными как вектор ячейки с длиной, равной Model.NumberOfParameters свойства контроллера. Если у контроллера нет параметров, то getCodeGenerationData пропускает это поле.

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

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

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

Чтобы изменять веса по горизонту прогнозирования от времени k + 1 до времени k + p, укажите массив, содержащий до p строк. Здесь k - текущее время, а p - горизонт прогнозирования. Каждая строка содержит веса для одного шага горизонта прогнозирования. Если указано меньше p строк, конечные веса используются для оставшихся шагов горизонта прогнозирования.

Манипулируемые веса настройки переменных, заменяющие веса настройки по умолчанию во время выполнения, указанные как вектор строки длиной Nmv или массив со столбцами Nmv, где Nmv - количество манипулируемых переменных. Если вы ожидаете, что управляемые веса переменных будут изменяться во время выполнения, вы должны добавить это поле в оперативную структуру данных при вызове getCodeGenerationData.

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

Чтобы изменять веса по горизонту прогнозирования от времени k + 1 до времени k + p, укажите массив, содержащий до p строк. Здесь k - текущее время, а p - горизонт прогнозирования. Каждая строка содержит веса для одного шага горизонта прогнозирования. Если указано меньше p строк, конечные веса используются для оставшихся шагов горизонта прогнозирования.

Управляемые веса настройки переменной скорости, которые заменяют веса настройки по умолчанию во время выполнения, заданные как вектор строки длиной Nmv или массив со столбцами Nmv, где Nmv - количество управляемых переменных. Если вы ожидаете, что ваши манипулируемые веса переменной скорости будут изменяться во время выполнения, вы должны добавить это поле в оперативную структуру данных при вызове getCodeGenerationData.

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

Чтобы изменять веса по горизонту прогнозирования от времени k + 1 до времени k + p, укажите массив, содержащий до p строк. Здесь k - текущее время, а p - горизонт прогнозирования. Каждая строка содержит веса для одного шага горизонта прогнозирования. Если указано меньше p строк, конечные веса используются для оставшихся шагов горизонта прогнозирования.

Слабый вес настройки переменной скорости, заменяющий вес настройки по умолчанию во время выполнения, заданный как положительный скаляр. Если во время выполнения ожидается изменение веса переменной дефицита, необходимо добавить это поле в оперативную структуру данных при вызове getCodeGenerationData.

Нижние границы выходной переменной, заменяющие нижние границы по умолчанию во время выполнения, заданные как вектор строки длиной Ny или массив со столбцами Ny, где Ny - количество выходных переменных. Если предполагается, что нижние границы выходной переменной будут изменяться во время выполнения, необходимо добавить это поле в оперативную структуру данных при вызове getCodeGenerationData.

Чтобы использовать те же границы по горизонту прогнозирования, укажите вектор строки.

Чтобы изменить границы по горизонту прогнозирования от времени k + 1 до времени k + p, укажите массив, содержащий до p строк. Здесь k - текущее время, а p - горизонт прогнозирования. Каждая строка содержит границы для одного шага горизонта прогнозирования. Если указано меньше p строк, конечные границы используются для оставшихся шагов горизонта прогнозирования.

Верхние границы выходных переменных, заменяющие верхние границы по умолчанию во время выполнения, заданные как вектор строки длиной Ny или массив со столбцами Ny, где Ny - количество выходных переменных. Если предполагается, что верхние границы выходных переменных изменятся во время выполнения, необходимо добавить это поле в оперативную структуру данных при вызове getCodeGenerationData.

Чтобы использовать те же границы по горизонту прогнозирования, укажите вектор строки.

Чтобы изменить границы по горизонту прогнозирования от времени k + 1 до времени k + p, укажите массив, содержащий до p строк. Здесь k - текущее время, а p - горизонт прогнозирования. Каждая строка содержит границы для одного шага горизонта прогнозирования. Если указано меньше p строк, конечные границы используются для оставшихся шагов горизонта прогнозирования.

Нижние границы состояния, заменяющие нижние границы по умолчанию во время выполнения, заданные как вектор строки длиной Nx или массив со столбцами Nx, где Nx - количество состояний. Если предполагается, что нижние границы состояния изменятся во время выполнения, необходимо добавить это поле в оперативную структуру данных при вызове getCodeGenerationData.

Чтобы использовать те же границы по горизонту прогнозирования, укажите вектор строки.

Чтобы изменить границы по горизонту прогнозирования от времени k + 1 до времени k + p, укажите массив, содержащий до p строк. Здесь k - текущее время, а p - горизонт прогнозирования. Каждая строка содержит границы для одного шага горизонта прогнозирования. Если указано меньше p строк, конечные границы используются для оставшихся шагов горизонта прогнозирования.

Верхние границы состояния, заменяющие верхние границы по умолчанию во время выполнения, заданные как вектор строки длиной Nx или массив со столбцами Nx, где Nx - количество состояний. Если предполагается, что верхние границы состояния изменятся во время выполнения, необходимо добавить это поле в оперативную структуру данных при вызове getCodeGenerationData.

Чтобы использовать те же границы по горизонту прогнозирования, укажите вектор строки.

Чтобы изменить границы по горизонту прогнозирования от времени k + 1 до времени k + p, укажите массив, содержащий до p строк. Здесь k - текущее время, а p - горизонт прогнозирования. Каждая строка содержит границы для одного шага горизонта прогнозирования. Если указано меньше p строк, конечные границы используются для оставшихся шагов горизонта прогнозирования.

Манипулируемые переменные нижние границы, которые заменяют нижние границы по умолчанию во время выполнения, указанные как вектор строки длиной Nmv или массив со столбцами Nmv, где Nmv - количество манипулируемых переменных. Если предполагается, что управляемые переменные нижние границы изменятся во время выполнения, необходимо добавить это поле в оперативную структуру данных при вызове getCodeGenerationData.

Чтобы использовать те же границы по горизонту прогнозирования, укажите вектор строки.

Чтобы изменить границы по горизонту прогнозирования от времени k + 1 до времени k + p, укажите массив, содержащий до p строк. Здесь k - текущее время, а p - горизонт прогнозирования. Каждая строка содержит границы для одного шага горизонта прогнозирования. Если указано меньше p строк, конечные границы используются для оставшихся шагов горизонта прогнозирования.

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

Чтобы использовать те же границы по горизонту прогнозирования, укажите вектор строки.

Чтобы изменить границы по горизонту прогнозирования от времени k + 1 до времени k + p, укажите массив, содержащий до p строк. Здесь k - текущее время, а p - горизонт прогнозирования. Каждая строка содержит границы для одного шага горизонта прогнозирования. Если указано меньше p строк, конечные границы используются для оставшихся шагов горизонта прогнозирования.

Манипулируемые нижние границы переменной скорости, которые заменяют нижние границы по умолчанию во время выполнения, указанные как вектор строки длиной Nmv или массив со столбцами Nmv, где Nmv - количество манипулируемых переменных. Если предполагается, что управляемые нижние границы переменной скорости будут изменяться во время выполнения, необходимо добавить это поле в оперативную структуру данных при вызове getCodeGenerationData.

Чтобы использовать те же границы по горизонту прогнозирования, укажите вектор строки.

Чтобы изменить границы по горизонту прогнозирования от времени k + 1 до времени k + p, укажите массив, содержащий до p строк. Здесь k - текущее время, а p - горизонт прогнозирования. Каждая строка содержит границы для одного шага горизонта прогнозирования. Если указано меньше p строк, конечные границы используются для оставшихся шагов горизонта прогнозирования.

Верхние границы манипулируемой переменной скорости, заменяющие верхние границы по умолчанию во время выполнения, заданные как вектор строки длиной Nmv или массив со столбцами Nmv, где Nmv - количество манипулируемых переменных. Если предполагается, что верхние границы регулируемой переменной скорости будут изменяться во время выполнения, необходимо добавить это поле в оперативную структуру данных при вызове getCodeGenerationData.

Чтобы использовать те же границы по горизонту прогнозирования, укажите вектор строки.

Чтобы изменить границы по горизонту прогнозирования от времени k + 1 до времени k + p, укажите массив, содержащий до p строк. Здесь k - текущее время, а p - горизонт прогнозирования. Каждая строка содержит границы для одного шага горизонта прогнозирования. Если указано меньше p строк, конечные границы используются для оставшихся шагов горизонта прогнозирования.

Выходные аргументы

свернуть все

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

Если решатель сходится к локальному оптимальному решению (info.ExitFlag является положительным), то mv содержит оптимальное решение.

Если решатель достигает максимального числа итераций, находит выполнимое неоптимальное решение (info.ExitFlag = 0) и:

  • coredata.usesuboptimalsolution является true, то mv содержит неоптимальное решение

  • coredata.usesuboptimalsolution является false, то mv содержит lastMV

Если решателю не удается найти осуществимое решение (info.ExitFlag отрицательный), то mv содержит lastMV.

Обновленные данные онлайн-контроллера, возвращенные в виде структуры. Эта структура аналогична onlineData, за исключением того, что исходная догадка переменной решения (X0, MV0, и Slack0) обновляются.

Для последующих контрольных интервалов выполните теплый запуск решателя путем изменения оперативных данных в newOnlineData и передачу обновленной структуры nlmpcmoveCodeGeneration как onlineData. Это позволяет решателю использовать начальные догадки переменной решения в качестве отправной точки для своего решения.

Подробные данные решения, возвращенные в виде структуры со следующими полями.

Оптимальная последовательность манипулируемых переменных, возвращаемая как массив (p + 1) -byNmv, где p - горизонт прогнозирования, а Nmv - количество манипулируемых переменных.

MVopt(i,:) содержит вычисленные оптимальные значения манипулируемых переменных в момент времени k+i-1, для i = 1,...,p, где k - текущее время. MVopt(1,:) содержит те же значения манипулируемой переменной, что и выходной аргумент mv. Поскольку контроллер не вычисляет оптимальные перемещения управления в момент времени k+p, MVopt(p+1,:) равно MVopt(p,:).

Оптимальная последовательность состояний модели прогнозирования, возвращаемая как массив (p + 1) -byNx, где p - горизонт прогнозирования, а Nx - количество состояний в модели прогнозирования.

Xopt(i,:) содержит вычисленные значения состояния в момент времени k+i-1, для i = 2,...,p+1, где k - текущее время. Xopt(1,:) совпадает с текущими состояниями в x.

Оптимальная последовательность выходных переменных, возвращаемая как массив (p + 1) -by-Ny, где p - горизонт прогнозирования, а Ny - количество выходов.

Yopt(i,:) содержит вычисленные выходные значения в момент времени k+i-1, для i = 2,...,p+1, где k - текущее время. Yopt(1,:) вычисляется на основе текущих состояний в x и ток измеренных возмущений в md, если есть.

Временная последовательность горизонта прогнозирования, возвращаемая как вектор столбца длиной p + 1, где p - горизонт прогнозирования.Topt содержит временную последовательность от времени k до времени k + p, где k - текущее время.

Topt(1) = 0 представляет текущее время. Последующие шаги времени Topt(i) являются Ts*(i-1), где Ts - время выборки контроллера.

Использовать Topt при печати MVopt, Xopt, или Yopt последовательности.

Ослабьте переменную в оптимуме, ε, используемый в ограничительном смягчении, возвращенном как неотрицательное скалярное значение.

  • start= 0 - все мягкие ограничения удовлетворяются на всем горизонте прогнозирования.

  • start> 0 - Нарушено по крайней мере одно мягкое ограничение. Если нарушено более одного ограничения, λ представляет наихудшее нарушение мягкого ограничения (масштабируется значениями ECR для каждого ограничения).

Код завершения оптимизации, возвращаемый как один из следующих:

  • Положительное целое число - найдено оптимальное решение

  • 0 - выполнимое неоптимальное решение, найденное после максимального числа итераций;

  • Отрицательное целое число - выполнимое решение не найдено

Число итераций, используемых решателем, возвращаемое как положительное целое число.

Стоимость целевой функции, возвращаемая как неотрицательное скалярное значение. Стоимость количественно определяет степень достижения контролером своих целей.

Значение стоимости имеет значение только тогда, когда ExitFlag является неотрицательным.

Расширенные возможности

.
Представлен в R2020a