The event location function fail in finding an easy event when solving a 4 variables system of ode. Here's the code:
options1 = odeset('RelTol',1e-5,'AbsTol',1e-9,'Events',#evento1);
[T_ode1,X_ode1,te]=ode15s(#Seno,[0 2],[0 0 0 0 0],options1);
function [y] = Seno(t,x)
%Parameters
V=20;
R=1e6;
epsilon=8.87e-12;
d=4.5e-5;
k_sp=10;
gamma1=0.04;
gamma2=0.1;
m=66e-3;
A=0.1;
omega=80;
h=3.8e-6;
l=2e-3;
N=142;
%Variable redefinition
%x=[x,xpunto,q,y,ypunto]
X=x(1);
Xp=x(2);
Q=x(3);
Y=x(4);
Yp=x(5);
%sistema eq differenziali
y(1)=Xp; %y1(1)=position1
y(2)=-(2*k_sp*X/m)-(gamma1*Xp/m)+((epsilon*2*d*h*N*l)*X*V^2/((d^2-X^2)^2))+A*sin(omega*t); %y1(2)=velocity1
y(3)=1/R*(V-Q*(d^2-X^2)/(epsilon*2*d*h*N*l)); %y1(3)=charge
y(4)=Yp; %position 2
y(5)=-gamma2*Yp; %velocity2
y=y';
end
function [condition,ends,directions] = evento1(t,y)
a=2e-6;
c=2e-6;
b=1.5e-6;
condition= [(y(1)^2)-(a+c)^2, (y(4)^2)-(y(1)+b)^2, (y(4)^2)-(y(1)-b)^2];
ends = [1, 1, 1]; % Halt integration
directions = [1, -1, 1];
end
Setting all the initial condition to 0, as you can see, the first event that the event function should find is when the tird condition when y(1) pass for 1.5e-6 (y(4) is 0). Unfortunally ode ignore that event and stop the solution when the 1st one is satisfied.
I can't see why this happen! I tryed the debugging mode and the systems properly pass across 1.5e-6 but doesn't consider it as an event (ie it doesn't start to evaluate the solution in more points near the event).
Thanks for your time and sorry for my english.
As I mentioned in the comments, you can plot your event conditions after simulating the system:
options1 = odeset('RelTol',1e-5,'AbsTol',1e-9,'Events',#evento1);
[T_ode1,X_ode1,te] = ode15s(#Seno,[0 2],[0 0 0 0 0],options1);
a = 2e-6;
c = 2e-6;
b = 1.5e-6;
y = X_ode1;
condition = [y(:,1).^2-(a+c)^2 y(:,4).^2-(y(:,1)+b).^2 y(:,4).^2-(y(:,1)-b).^2];
figure;
plot(T_ode1,condition);
hold on;
plot(T_ode1([1 end]),[0 0],'k--',te,0,'k*');
legend('Condition 1','Condition 2','Condition 3','Location','W');
xlabel('Time');
ylabel('State');
ylim([-1.8e-11 2e-12]);
This results in a plot that looks like this:
If you zoom in on the plot you'll see that the third condition (yellow) never crosses zero and thus never triggers an event. Eventually the first event (blue) crosses zero and does trigger an event. Adjusting the integration tolerances doesn't appear to change this behavior (at best the third condition might asymptotically touch, but not cross, zero). If you want the third condition to trigger an event, you'll either need to change your parameters, change the condition, change your initial conditions, or change your ODEs.
I'm not sure if or why you think the third condition should cross zero, but if the system is numerically sensitive, then you may need to compensate for this by specifying parameter more precisely or artificially biasing the point of zero crossing.
Related
I'm currently working on a simple hybrid dynamical system with discrete state jumps. For that, I'm using ODE events. The problem is that my 'value's' (that which will cross zero) are not directly related to the state of the dynamical system. I'm not able to express these variables alongside the proper state (e.g. position + velocity of angles).
Currently, I'm using global variables but it seems that MATLAB does not decrease the step-size when the 'value' crosses zero. I assume this is because the value is not part of the state. Here is the code for the events;
function [value, isterminal,direction] = leg_events(t,x)
global isSwingL isSwingR frcLegL frcLegR posFooL posFooR frcComL frcComR
value = [];
isterminal = [];
direction = [];
if isSwingL
value = [value; posFooL(2)];
isterminal = [isterminal; 1];
direction = [direction; -1];
else
value = [value; frcComL(2)];
isterminal = [isterminal; 1];
direction = [direction; -1];
end
if isSwingR
value = [value; posFooR(2)];
isterminal = [isterminal; 1];
direction = [direction; -1];
else
value = [value; frcComR(2)];
isterminal = [isterminal; 1];
direction = [direction; -1];
end
end
Does anyone have any suggestions? Please let me know.
The model is implemented in Simulink which doe seem to be able to deal with this kind of thing but I need it specifically in code for optimization purposes.
I've also considered changing the 'MaxStep' but this makes the simulation unnecessary slow and unusable for the optimization.
[some context]: In concrete terms, the problem is that if I print the value of the event function during operation, the values occur as such ->
[0.2 0.12 0.4 -0.2 detected crossing]
Where the event is detected at -0.2. The state at this instance in time is taken to continue with the state but the system has clearly been progressed further making the subsequent dynamics of the system nonsense. I would have expected this ->
[0.2 0.12 0.4 0.1 0.01 0.001 -0.001 detected crossing]
which is what happens when the 'value' in the event function is part of the input arguments of the event function itself. Is it possible to ensure that the step size gets decreased when a 'value' that is not based on an input variable appears to cross the zero line?
Hope this helps
I've implement a moving maximum filter in matlab and now I have to determine if it's LTI or not. I've already prove it's linear but I can't prove if it's time ivnariant. To prove this I have to give as an input a signal x[n] and get the output X[n]. After that I'm giving the same signal but this time it have to delay it that is x[n-a] and the output must be X[n-a] the same as the X[n] but delayed. My problem is that I don't know what input to give. Also, the moving maximum is a LTI system or not?
Thanks in advance.
Max-Filter Using For-Loop and Testing System for Linear and Time-Invariance
Filter Construction:
To filter an array/matrix/signal a for-loop can be applied. Within this for-loop, portions of the signal that overlaps the window must be stored in this case called Window_Sample. To apply the maximum filter the max() of the Window_Sample must be taken upon each iteration of the loop. Upon each iteration of the loop the window moves. Here I push all the code that calculates the maximum filtered signal into a function called Max_Filter().
Time-Invariant or Not-Time Invariant:
With proper padding, I think the maximum filter is time-invariant/shift-invariant. Concatenating zeros to the start and ends of the signal is a good way to combat this. The amount of padding I used on each end is the size of the window, the requirement would be half the window but there's no harm in having a little bit of buffer. Unfortunately, without proper padding, this may appear to break down in MATLAB since the max filter will be introduced to points early. In this case without padding the ramp the max filter will take the 5th point as the max right away. Here you can apply a scaling factor to test if the system is linear. You'll see that the system is not linear since the output is not a scaled version of the input.
clf;
%**********************************************************%
%ADJUSTABLE PARAMETERS%
%**********************************************************%
Window_Size = 3; %Window size for filter%
Delay_Size = 5; %Delay for the second test signal%
Scaling_Factor = 1; %Scaling factor for the second test signal%
n = (0:10);
%Ramp function with padding%
Signal = n; %Function for test signal%
%**********************************************************%
Signal = [zeros(1,Window_Size) Signal zeros(1,Window_Size)];
Filtered_Result_1 = Max_Filter(Signal,Window_Size);
%Ramp function with additional delay%
Delayed_Signal = Scaling_Factor.*[zeros(1,Delay_Size) Signal];
Filtered_Result_2 = Max_Filter(Delayed_Signal,Window_Size);
plot(Filtered_Result_1);
hold on
plot(Filtered_Result_2);
xlabel("Samples [n]"); ylabel("Amplitude");
title("Moving Maximum Filtering Signal");
legend("Original Signal","Delayed Signal");
xticks([0: 1: length(Filtered_Result_2)]);
xlim([0 length(Filtered_Result_2)]);
grid;
function [Filtered_Signal] = Max_Filter(Signal,Filter_Size)
%Initilizing an array to store filtered signal%
Filtered_Signal = zeros(1,length(Signal));
%Filtering array using for-loop and acccesing chunks%
for Sample_Index = 1: length(Signal)
Start_Of_Window = Sample_Index - floor(Filter_Size/2);
End_Of_Window = Sample_Index + floor(Filter_Size/2);
if Start_Of_Window < 1
Start_Of_Window = 1;
end
if End_Of_Window > length(Signal)
End_Of_Window = length(Signal);
end
Window_Sample = Signal(Start_Of_Window:End_Of_Window);
Filtered_Signal(1,Sample_Index) = max(Window_Sample);
end
end
Alternatively Using Built-In Functions:
To test the results of the created filter it is a good idea to compare this to the output results of MATLAB's built-in function movmax(). Here the results appear to be the same as the for-loop implementation.
clf;
Window_Size = 3;
n = (0:10);
%Ramp function with padding%
Signal = n;
Signal = [zeros(1,Window_Size) Signal zeros(1,Window_Size)];
Filtered_Result_1 = movmax(Signal,Window_Size);
%Ramp function with additional delay%
Delay_Size = 5;
Delayed_Signal = [zeros(1,Delay_Size) Signal];
Filtered_Result_2 = movmax(Delayed_Signal,Window_Size);
plot(Filtered_Result_1);
hold on
plot(Filtered_Result_2);
xlabel("Samples [n]"); ylabel("Amplitude");
title("Moving Maximum Filtering Signal");
legend("Original Signal","Delayed Signal");
xticks([0: 1: length(Filtered_Result_2)]);
xlim([0 length(Filtered_Result_2)]);
grid;
Ran using MATLAB R2019b
I have the following simple ODE:
dx/dt=-1
With initial condition x(0)=5, I am interested in when x(t)==1. So I have the following events function:
function [value,isterminal,direction] = test_events(t,x)
value = x-1;
isterminal = 0;
direction = 0;
end
This should produce an event at t=4. However, if I run the following code I get two events, one at t=4, and one at the nearby location t=4+5.7e-14:
options = odeset('Events',#test_events);
sol = ode45(#(t,x)-1,[0 10],5,options);
fprintf('%.16f\n',sol.xe)
% 4.0000000000000000
% 4.0000000000000568
If I run similar codes to find when x(t)==0 or x(t)==-1 (value = x; or value = x+1; respectively), I have only one event. Why does this generate two events?
UPDATE: If the options structure is changed to the following:
options = odeset('Events',#test_events,'RelTol',1e-4);
...then the ODE only returns one event at t=4+5.7e-14. If 'RelTol' is set to 1e-5, it returns one event at t=4. If 'RelTol' is set to 1e-8, it returns the same two events as the default ('RelTol'=1e-3). Additionally, changing the initial condition from x(0)=5 to x(0)=4 produces a single event, but setting x(0)=4 and 'RelTol'=1e-8 produces two events.
UPDATE 2: Observing the sol.x and sol.y outputs (t and x, respectively), the time progresses as integers [0 1 2 3 4 5 6 7...], and x progresses as integers up until x(t=5) like so: [5 4 3 2 1 1.11e-16 -1.000 -2.000...]. This indicates that there is something that occurs between t=4 and t=5 that creates a 'bump' in the ODE solution. Why?
One speculation that might explain how rounding errors could occur in this simple problem: The solution is interpolated between the internal steps using the evaluations k_n of the ODE derivatives function, also called "dense output". The theoretical form is
b_1(u)k_1 + b_2(u)k_2 + ...b_s(u)k_s
where 0 <= u<= 1 it the parameter over the interval between the internal points, that is, t = (1-u)*t_k+u*t_{k+1}.
The coefficient polynomials are non-trivial. While in the example all the k_i=1 are constant, the evaluation of the sum b_1(u)+...+b_s(u) can accumulate rounding errors that become visible in the solution value close to a root, even if y_k and y_{k+1} are exact. In that range of accumulated floating point noise, the value might oscillate around the root, leading to the detection of multiple zero crossings.
In my textbook I have encountered an example of a function I am supposed to use when specifying an event location for a system of ODEs. The function example is as follows:
function [value, isterminal, dircn] = proj(t,z,flag);
g = 9.81;
if nargin < 3 | isempty(flag)
value = [z(2); 0; z(4); -g];
else
switch flag
case 'events'
value = z(3);
isterminal = 1;
dircn = -1;
otherwise
error('function not programmed for this event');
end
end
There is one part of the logic here which I don't understand. Say that I activate the "events" option and then run ode45. How, then, can ode45 actually read the system of equations (which is specified in the above function as value = [z(2); 0; z(4); -g];)? I have ran ode45 based on the above function, after specifying tspan and inital conditions of course, and it works like a charm. But I don't understand how ode45 can read the system properly when it is only presented in the "if"-part of the script above.
If anyone can explain the logic here, I would greatly appreciate it!
Well I think I can explain some parts. As I wrote above it is strange that the dimension of value changes.
given your statespace and the names of your variables it looks like 2 dimensional motion.
in the case of no flag it seems that state space is:
horizontal position (x)
horizontal speed (vx)
vertical position (y)
vertical speed (vy)
correction
It seems that ode can send 'events' when you specify them. So your function outputs 3rd component of the state space. Look at this site explaining it: http://www.mathworks.de/help/techdoc/math/f1-662913.html
unless you specify it beforehand ode can't send 'events' to the function so this part will not be called.
But your function won't work anyway - as the derivative would need to have same dimension as the statespace (4x1). But has only 1x1.
But I really don't know what you mean by "specifying an event location". Maybe the secret is hidden there.
With some creativity I think you could use the function to extract the 3rd component of the state space.
The answer is in the if command:
if nargin < 3 | isempty(flag)
value = [z(2); 0; z(4); -g];
else
If number of arguments is less than 3 or if variable flag is empty, then set variable value to [z(2); 0; z(4); -g]. Otherwise, if variable flag is 'events', then set variable value to z(3), and when flag is not 'events', report an error. So this function always assigns some return value for variable value or, reports an error using error command.
I can supplement a bit more information about how we proceed after writing the function above. We then define, say:
tspan = [0 6];
z0 = [0, 5*cos(pi/4), 0, 5*sin(pi/4)];
options = odeset('events','on');
[t y] = ode42('proj',tspan,z0,options)
By writing this the output is 5 columns, 1 column for tspan , and 1 column for each of the z-values (z(1), z(2), z(3) and z(4)). When z(3) hits zero, the calculations are terminated since the "event" occurs.
If, on the other hand I do not include the options = odeset('events','on') line, and simply write:
[t y] = ode42('proj',tspan,z0)
The calculations are performed for the entire tspan range.
Yet, I still fail to see how ode42 is capable of calculating all the output vectors when we activate "events" since it then looks logical me that MatLab should only execute the "else"-statement in the "proj"-function. And in this part of the function, the actual system of the differential equations is not included.
I'm trying to plot the response of a control system which has two unit steps - one a t=0 and one at t=0.3
Here is the code I'm using:
% Set up the transfer function
numer = [K*K_m*r];
denom = [(L_m*J_e) (L_m*b_m+R_m*J_e) (K_b*K_m+R_m*b_m) K*K_m*r];
transfun = tf(numer, denom);
% Apply a step function at t=0
[y,T] = step(transfun, 0:0.001:0.5);
% Second step function at t=0.3
[y_b, T_b] = step(transfun, 0.3:0.001:0.5);
% How to add the above two outputs together?
plot( ???? )
I've searched google, but the only solutions I can find are for systems with multiple inputs (MIMO). This system has only one input, I just want to use superposition principal to add them together.
When I try adding the two vectors together I get "Vectors must be the same lengths."
What you're trying to do isn't the step response. The step response measures the system's ability to track a constant input, and what you're proposing isn't a constant input.
To do what you want, look into the lsim function and the gensig function.
(Side note: As for why you couldn't add your vectors, the first goes from 0 to 0.5 in 0.001 increments, and the other goes from 0.3 to 0.5 in 0.001 increments. Of course they're going to be different lengths! If you're really intent on adding these together, do something like:
[y_b, T_b] = step(transfun, 0:0.001:0.5);
y_b(T_b<0.3) = 0;
I can't test this at the moment, but if that doesn't work, try:
y_b(0:0.001:0.3) = 0;
)