Пользовательская периодическая функция без счетчика

Я использую ode45 для решения простой ОДУ:

function dCdt=u_vent(t,C)
if t>   600 &&  t<= 720
    Q=Q2;               
elseif t>   1320    &&  t<= 1440
    Q=Q2;           
elseif t>   2040    &&  t<= 2160
    Q=Q2;           
elseif t>   2760    &&  t<= 2880
    Q=Q2;               
elseif t>   3480    &&  t<= 3600
    Q=Q2;
else
    Q=Q1;
end

V=100;
C_i=400;
S=100;

dCdt=Q/V*C_i+S/V-Q/V*C(1);
return

Я использую then для решения:

[t,C]=ode45(@u_vent, [0 1*3600], 400);

Я хотел бы создать периодическую функцию, такую ​​как на картинке для Q, 0<t<3600, без использования этих операторов if... есть мысли?

введите здесь описание изображения


person HCAI    schedule 06.12.2013    source источник
comment
Если у вас есть Simulink, то это легко сделать в графическом виде   -  person am304    schedule 06.12.2013
comment
Я предполагаю, что вы знаете, что ваша система имеет аналитическое решение? Если вы плохо разбираетесь в дифференциальных уравнениях, вы можете использовать функцию Matlab dsolve : syms Q V C_i S t C(t) C0; dsolve(diff(C,t)==(Q*(C_i-C)+S)/V,C(0)==C0).   -  person horchler    schedule 06.12.2013


Ответы (3)


Это распространенный тип вопроса. Пользователи часто хотят периодически изменять переменную в функции интегрирования, используемой решателями ODE Matlab. К сожалению, обычно это плохая идея. Эти решатели предполагают, что дифференциальные уравнения являются гладкими и непрерывными. В лучшем случае ваш код будет медленнее. В худшем случае у вас будут большие ошибки или даже совершенно неправильные результаты. Использование жесткого решателя, такого как ode15s, может немного помочь, но это слишком предполагает преемственность. Поскольку ваша система, как указано, имеет аналитическое решение, самый простой способ «симулировать» это может состоять в том, чтобы пропустить последовательность импульсов через эту функцию времени.

Случай, когда параметр изменяется скачкообразно во времени, т. е. по независимой переменной, решается легко. Просто интегрируйте по каждому временному интервалу необходимое количество периодов:

t1 = 600;
t2 = 120;
TSpan = [0 t1]; % Initial time vector
NPeriods = 5;   % Number of periods
C0 = 400;       % Initial condition
Q1 = ...
Q2 = ...
V = 100;
C_i = 400;
S = 100;
u_vent = @(t,C,Q)(Q*(C_i-C(1))+S)/V; % Integration function as anonymous function
Cout = C0;       % Array to store solution states
tout = Tspan(1); % Array to store solution times
for i = 1:NPeriods
    [t,C] = ode45(@(t,C)u_vent(t,C,Q1), TSpan, C0);
    tout = [tout;t(2:end)];   % Append time, 2:end used to avoid avoid duplicates
    Cout = [Cout;C(2:end,:)]; % Append state
    TSpan = [0 t2]+t(end);    % New time vector
    C0 = C(end);              % New initial conditions
    [t,C] = ode45(@(t,C)u_vent(t,C,Q2), TSpan, C0);
    tout = [tout;t(2:end)];
    Cout = [Cout;C(2:end,:)];
    TSpan = [0 t1]+t(end);
    C0 = C(end);
end

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

Необязательный. Каждый раз, когда вы вызываете/перезапускаете ode45, функция должна определить начальный размер шага для использования. Это основные (минимальные) затраты/накладные расходы на перезапуск решателя. В некоторых случаях вы можете помочь решателю, указав 'InitialStep'< /a> параметр, основанный на последнем размере шага, использованном в предыдущем вызове. См. демонстрацию ballode для получения дополнительной информации об этом, набрав edit ballode в командном окне.

Заметка. Массивы tout и Cout добавляются сами к себе после каждого шага интегрирования. Это эффективное динамическое выделение памяти. Пока NPeriods достаточно мал, это, вероятно, не будет проблемой, поскольку динамическое выделение памяти в последних версиях Matlab может быть очень быстрым, и вы перераспределяете только несколько раз в больших блоках. Если вы укажете выход с фиксированным размером шага (например, TSpan = 0:1e-2:600;), то вы будете точно знать, сколько памяти предварительно выделить для tout и Cout.

person horchler    schedule 06.12.2013
comment
Ах, это фантастика! Большое спасибо! Кажется, это единственное разумное решение. Если бы Q была функцией, например той, которую предложил @am304, было бы возможно сделать то же самое? - person HCAI; 07.12.2013
comment
Если это плавно меняющийся вход (например, синусоидальное форсирование), вы можете включить его в функцию интегрирования. Негладкие вводы, такие как ваш, могут работать или не работать. Под словом «может работать» я подразумеваю получение результатов, которые не совсем ошибочны, но содержат больше ошибок. Это зависит от конкретного ОДУ, решателя и настроек решателя. Некоторые ODE должны быть тщательно интегрированы, если они подвержены переходным процессам или ошибкам. В любом случае ошибки будут больше, чем если бы вы использовали этот ответ для интеграции систем с разрывами (для тех же допусков решателя). И это плохая привычка. - person horchler; 07.12.2013

Если у вас есть набор инструментов обработки сигналов, вы можете использовать что-то вроде:

>> t = 0:3600;
>> y = pulstran(t,[660:720:3600],'rectpuls',120);
>> plot(t,y)
>> ylim([-0.1 1.1])

что дает следующее (в Octave должно быть то же самое в MATLAB):

введите здесь описание изображения

Затем вам нужно масштабировать y, чтобы оно находилось между Q1 и Q2 вместо 0 и 1.

person am304    schedule 06.12.2013
comment
Предложение Хорхлера, вероятно, лучше моего. - person am304; 06.12.2013

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

Q = Q2 + (Q1 - Q2) * (mod(t, period) < t_rise);

который работает как в скалярном, так и в векторном контексте.

person Ben Voigt    schedule 06.12.2013