How can I plot the time response of a system when the system, input, output matrices are polynomials? For example
A(x) = [0.59*x 1.67*x; 0.1 0.2]
B(x) = [2.3*x; 0.3]
C = [1 0]
Operating region of x = [-2, 2]
Initial condition x(0) = [2 0]
If the matrices were constant, I could use ss and lsim to plot it. But, how do I do it in this case? I am new to Matlab and control systems.
Using your example, I will run you through the basic idea of general ODE simulation in MatLab (which is very similar across programming languages).
(First though, I am assuming that the x you have written in your A and B matrices is actually x1 since you did not specify. I am also assuming from context that your state vector is [x1 x2]).
Create a function that takes in the
current time, t_now
current state vector, x_now
full input array, u
full time array, t
and uses them to compute the state derivative (xdot). The full time array (t) will only be needed for indexing the control input (u), as you will see. Basically, it will be a coded function for
xdot = f(t, x, u)
which is the general form of an ODE.
function xdot = myODE(t_now, x_now, u, t)
A = [ 0.59*x_now(1), 1.67*x_now(1);
0.1, 0.2 ] ;
B = [ 2.3*x_now(1);
0.3 ] ;
u_now = interp1(t, u, t_now) ; % get u(t=t_now)
xdot = A*x_now + B*u_now ;
end
Next, create a script that runs the simulation using an ODE solver like MatLab's ode45. If you want to know how these solvers work, read up on numerical integration. MatLab's ode45 uses a Runge-Kutta method.
%// time range over which to compute simulation
t = [0 : 0.01 : 5] ;
%// define the input signal
%// why not make it zero to look at the natural response
u = zeros(1, length(t)) ;
%// initial condition
x0 = [2, -5] ;
%// call ode45
%// first argument: anonymous call to myODE that tells it which inputs to use
%// second argument: the time range for the simulation
%// third argument: the initial condition
%// first output: the sim time, may be more detailed than the original t
%// second output: the full output including all states
[t_out, response] = ode45(#(t_now, x_now) myODE(t_now, x_now, u, t), t, x0) ;
%// The response contains two column vectors: one for x1 and one for x2 at
%// every time in t. You can extract "the true output" which based on
%// your C matrix is x1.
y = response(:,1) ;
%// Plot results
plot(t, u, 'r--', t_out, y, 'k') ;
grid on ;
legend('input', 'output')
xlabel('time')
ylabel('value')
Suppose you don't have u as a predefined input signal, but rather as a function of the current state. Then simply modify the computation of u_now in the myODE function. In the simplest case, u is not a function of time at all, in which case you don't even need to pass u or the full time array into myODE at all! For example, if
u := -k*x1
then your ODE function can be
function xdot = myODE(t_now, x_now)
A = [ 0.59*x_now(1), 1.67*x_now(1);
0.1, 0.2 ] ;
B = [ 2.3*x_now(1);
0.3 ] ;
k = 5 ;
u_now = -k*x_now(1) ;
xdot = A*x_now + B*u_now ;
end
with the call to ode45 being
[t_out, response] = ode45(myODE(t_now, x_now), t, x0) ;
and no need to define u in the script.
Hope that helps!
Related
I'm having this problem: Let's suposse that a user wants to add an arbitrary Force (a function of time) to a spring mass damper system. In order to do that, we request the force as an input, i.e. F = cos(t). Well, I noticed that if we add a function of time in the code where the F is placed (inside ODE), then the solutions for the movement equations are correct. But i can't find a way to achieve this goal. I mean, to request the force as input so that ODE changes everytime i put a new force. I've tried almost everything like converting the input F to a function, etc.
I hope you could help me. Here is the problem in the code:
F = input('Insert force F(t)..............[N] = ','s');
u0 = [x0,v0]; % INITIAL CONDITIONS AS A VECTOR
ODE = #(t,u) [u(2) ; F - (c/m)*u(2) - (k/m)*u(1)]; % FORCED SPRING MASS DAMPER SYSTEM
[t,u] = ode45(ODE, [0 tf], u0);
The fix code results:
m = input('Insert mass of the system..........................[kg] = ');
c = input('Insert viscosity coefficient........................[ ] = ');
k = input('Insert elasticity constant..........................[m] = ');
x0 = input('Insert initial position.............................[m] = ');
v0 = input('Insert initial velocity...........................[m/s] = ');
tf = input('Insert time of complete movement....................[s] = ');
F = input('Insert external force as F(t).......................[N] = ','s');
F = str2func( ['#(t)' F] ); % CONVERT STRING TO FUNCTION
%=========================================================== ODE 45 =======
u0 = [x0,v0]; % INITIAL CONDITIONS AS A VECTOR
ODE = #(t,u) [u(2) ; F(t) - (c/m)*u(2) - (k/m)*u(1)];
[t,u] = ode45(ODE, [0 tf], u0);
u(:,3) = F(t) - (c/m)*u(:,2) - (k/m)*u(:,1); % ACCELERATION
%============================================================= PLOT =======
v = {'x','dx/dt','d2x/dt2'}; % CHAR VECTOR FOR PLOT
for i = 1:3 % PLOT WITH LOOP
subplot(3,1,i)
plot(t,u(:,i));
xlabel('t');
ylabel(v(i));
grid on;
end
Code just for personal uses. Not public because input allows malicious commands.
Thanks to #Wolfie and #Ander Biguri.
I'm going to take the liberty of simplifying your problem down to its main parts.
You have some char array from your user input which defines a function in terms of t. I'm going to entirely ignore all the reasons that blindly evaluating user-input functions is dangerous, but you shouldn't.
You want to combine this function with an existing function in terms of t to produce a single function
We can do this like so:
F = 'cos(t)'; % char version of function, e.g. from the input command
y = #(t) t; % some base function in terms of t
F = str2func( ['#(t)' F] ); % Convert function char to anonymous function
yF = #(t) y(t) + F(t); % combine the functions
You can test this with a quick plot
tt = 0:0.01:10; % axis data
figure; hold on;
plot( tt, y(tt), 'displayname', 'y(t)' );
plot( tt, yF(tt), 'displayname', 'yF(t)' );
legend('show');
In your actual example you have ODE in place of my example f, so assuming F is converted to an anonymous function as shown above, you would get
ODE = #(t,u) [u(2) ; F(t) - (c/m)*u(2) - (k/m)*u(1)];
I have written a script to compute and solve a simple inverted pendalum system.Now suppose that I want to solve the nonlinear dynamic equation of the system with ODE45 function with different values of initial conditions.How could I use a for loop to solve for state vector of X for different values of initial conditions?I wrote a for loop to do that but I could not get the answer I wanted.Help me please.Here are my function and mfile as follows:
function xDot = of(x,g,L,u)
xDot = zeros(2,1);
xDot(1) = x(2);
xDot(2) = ((g./L)*sin(x(1)))+u;
end
And this is my main code:
clc;
clear;close all;
%% Solve The Nonlinear Equation
L = 1;
g = 9.81;
h = 0.25;
t = [0:h:5];
A = [0 1;(g/L) 0];
B =[0 1]';
Ics = [pi,0;pi/2 0;pi/5 0;0.001 0;pi 0.5;pi/2 0.5;pi/5 0.5;0.001 0.5];
[Poles,~] = eig(A); %Poles Of Closed LOop System
R = 0.01;
Q = eye(2);
K = lqr(A,B,Q,R);
u = #(x)-K*(x);
for i=1:size(Ics,1)
[~,X] = ode45(#(t,x)of(x,g,L,u(x)),t,Ics(i,:));
end
Also note that I want the first column of X vector which is the angular displacements of the pendulum in each iteration because the second column of X vector in ODE45 is always the Derivative of the main state vector.
You can store all the integration outputs for the different initial conditions in a 3D array.
The number of rows of Xout will equal the number of time steps at which you want to evaluate your solution, so numel(t). The number of columns is the number of states, and then the third dimension will be the number of initial conditions you want to test.
Xout = zeros(numel(t), size(Ics, 2), size(Ics, 1)); % initialize the 3D array
for k = 1:size(Ics, 1)
[~, Xout(:, :, k)] = ode45(#(t, x)of(x, g, L, u(x)), t, Ics(k, :));
end
I'm using MATLAB to make a function that returns the probability mass function (PMF) for a Geometric distribution when I enter the values of p, q, and the number of attempts (x) as the inputs.
My function:
function Probability = Geometric(p, q, x)
Probability = p*q^x-1
Now whenever I try to calculate the probability by typing in the values of p, q, and x, such as:
Geometric(0.5, 0.5, 1),
The exact error:
Geometric(0.5,0.5,1)
??? Undefined function or method 'Geometric' for input arguments of type 'double'.
I've tried changing functions, and reducing them to one input and one output.
I expect the probabilities to be calculated, but they just don't.
What's going wrong?
p*q^x-1 % Your original code
Your original code is taking q, raising it the xth power, multiplying it by p, then subtracting 1. This is equivalent to the following code which you certainly didn't intend.
(p*(q^x)) - 1 % What your code was doing written differently
Considering order of operations, the correction is easy.
p*q^(x-1) % Your corrected code
Another possible error source is your function is not saved as a standalone m-file "Geometric.m" which must also be on your MATLAB path (MATLAB has to "see" it). If you have your function file "MyFunction.m" stored in a folder, you can add that folder to MATLAB's visible path with one line (or manually navigate there). For more details, see how to create a function.
mypathtoMyFunction = 'C:\Users\SonnyJordan\Documents\SweetCode\FunctionFolder';
path(path,'mypathtoMyFunction')
A Full Solution (3 approaches)
From your parameterization of the Geometric distribution, you're wanting the support on {1, 2, 3, 4, ...}.
Two things. (1) I'd recommend an anonymous function for something like this. (2) There really isn't a need to separate p and q as separate variables since p + q = 1 and therefore one determines the other (i.e. q = 1-p).
Approach 1: Anonymous function
% MATLAB R2018b
geopmfh =#(p,k) p.*((1-p).^(k-1)); % Define pmf
k = 5; % Number of trials
p = 0.2; % Prob("Success" on trial)
geopmfh(p,k) % Probability
Above code is fully vectorized so you could pass it vectors and/or arrays of inputs.
A quick check to validate it is a valid probability mass function (pmf).
M = 500;
sum(geopmfh(p,[1:M])) % should return 1 if M large enough
Approach 2: Function (w/ error checking)
As an aside, making a function in MATLAB would make a lot of sense if you wanted to add error checking on the function inputs to ensure k is a positive integer and that p is between [0 1].
function [pmf] = geopmf(p,k)
%GEOPMF Calculates pmf for Geometric(p,k) distribution on {1,2,3,...}
% pmf = geopmf(p,k)
% p = n x d matrix of n d-dimensional success probabilities; must be [0,1]
% k = m x d matrix of m d-dimensional numbers of trials
% pmf = n x m matrix of probabilities
%
% Examples:
% k = 4; p = .5;
% pmf = geopmf(p,k) % pmf = 0.0625
% Input Error Checking ****************************************************
if isempty(p) | isempty(k), pmf = []; return, end
if nargin ~= 2, error('Function requires two inputs.'), end
if p < 0 | p > 1, error('p must be between 0 and 1.'), end
if k < 1 | ~isint(k), error('k must be positive integer & k > 0.'), end % with this parameterization
n = size(p,1); d = size(p,2);
m = size(k,1);
if isempty(p) | ~isnumeric(p) | ~ismatrix(p)
error('p must be non-empty numeric scalar, vector, or 2-D matrix.');
elseif isempty(k) | ~isnumeric(k) | ~ismatrix(k)
error('k must be non-empty numeric scalar, vector, or 2-D matrix.');
elseif size(k,2) ~=d
error('Rows of p and k must have same dimensions.');
end
% End (Input Error Checking) **********************************************
pmf = p.*((1-p).^(k-1));
end
Approach 3: MATLAB's built-in function
If you have the Statistics toolbox, MATLAB has a function for this already called geopdf but note it is parameterized according to the other "version" with support {0, 1, 2, ...} (see wiki page).
p.*((1-p).^k) % The other parameterization
geopdf(k,p) % Note order of inputs
You can correct for that by adjusting your input.
geopdf(k-1,p) % Subtract 1 trial
Code tested with MATLAB R2018b.
So I am given a certain system: y(n) = 10x(n)cos(0.25pi*n + 0.1pi)
And I am to test if the system is time invariant by plotting two input signals x(n) and x(n-2), and their corresponding output signals. X(n) is supposed to be a causal signal with 10 elements using the rand function.
This is the code I've written thus far:
clear all; clc; close all;
n = 0:9; n2 = 0:11;
xN1 = [rand(1,10) 0 0]; %x(n)
xN2 = [0 0 rand(1,10)]; %x(n-2)
yN1 = 10.*xN1.*cos(0.25.*pi.*n2+0.1.*pi); %y(n)
yN2 = 10.*xN2.*cos(0.25.*pi.*n2+0.1.*pi); %y(n-2)
figure,
subplot(2,2,1)
stem(n2,xN1),title('x1')
subplot(2,2,2)
stem(n2,yN1),title('y1')
subplot(2,2,3)
stem(n2,xN2),title('x2')
subplot(2,2,4)
stem(n2,yN2),title('y2')
My question is what am I being asked to plot? x1 vs. x2, and then y1 vs. y2? Or x1 vs. n and x2 vs. n, and so on.
This is the result I obtain with my current code, http://imgur.com/iho2LDX. Does this mean the signal is time variant?
No, There is a very problem with your code. To prove that the system is time invariant we have to delay the input first and get the output and then delay the output for the same input and see whether both inputs are the same you are taking rand() which changes the input for y1 and y2 so you can never see if the system is time invariant
Here is an example for Time invariant system :
n0 = 1; %delay
n = 0:0.1:1;
x1 = [zeros(1,n0) cos(n)]%delaying the input
y1 = 2.5*x1;
x2 = [cos(n)]
y2 = [zeros(1,n0) 2.5*x2]%delaying the ouput
subplot(2,1,1)
stem(1:length(n) + 1,y1)
title('DELAYED INPUT')
subplot(2,1,2)
stem(1:length(n) + 1,y2)
title('DELAYED OUTPUT')
You can observe that the input remains the same just delayed in the input for the first time and the output is delayed for the second time but the output remains the same.
Plus one more thing your input is not time dependent xn1 and xn2 are not time dependent
I have a 2-dimensional matrix with discrete values and I want to use the ode45 command.
To do this I used interp2 to create a function ode45 can use.
The problem is, it looks like ode45 is moving out of my defined area and so interp2 returns NaN-values. To get rid of the NaN I used an extrapolation value but now it seems that ode45 integrates from my initial values to this extrapolation value, disregarding any of my given values in the matrix.
Here is a little example with a 2x2 matrix:
clear all;
close all;
clc;
A = rand(2, 2); % the matrix with my values
x = 1:2;
y = x;
[X, Y] = meshgrid(x, y); % grid on which my values are defined
xi = 1:0.5:2;
yi = xi;
[Xi, Yi] = meshgrid(xi, yi); % interpolation grid
tspan = 0:0.2:1;
B = #(xq,yq)interp2(X, Y, A, xq, yq, 'linear', 10); % interpolation function with extrapval of 10
[T,C] = ode45(#(Xi,Yi)B(Xi,Yi), tspan, [Xi,Yi]); % ode45 function using interp-function and intital values [Xi,Yi]
What am I doing wrong?
Thanks for any help in advance!
I don't think this will work as you have set it up. Your ODE function B should take inputs of time, and a single input (column) vector of state variables and return a column vector of rates of change for the state variables. Please look at the help documentation for ode45.
If your state variables are coordinate pairs (x,y), you need to return dx/dt and dy/dt for each pair. I'm not sure how this could come from interpolating on a single array. I think you need something like the following:
clear
%// Define the right hand side of your ODE such that
%// dx/dt = Ax(x,y)
%// dy/dt = Ay(x,y)
%// For demonstration I'm using a circular velocity field.
xref = linspace(-pi,pi);
yref = linspace(-pi,pi);
[xref yref] = meshgrid(xref,yref);
r=sqrt(xref.^2+yref.^2);
Ax = [-yref./r];
Ay = [xref./r];
%// Initial states:
xi = 1:0.05:2;
yi = xi;
[Xi, Yi] = meshgrid(xi, yi); % interpolation grid
%// state array = [x1; y1; x2; y2; ... ]
states = [Xi(:)'; Yi(:)'];
states = states(:);
B = #(t,states) reshape([ interp2(xref,yref,Ax,states(1:2:end),states(2:2:end)) ...
interp2(xref,yref,Ay,states(1:2:end),states(2:2:end))]',size(states));
tspan=0:.02:10;
[T,C] = ode45(B, tspan, states);
for i=1:size(C,1)
figure(1)
clf
plot(C(i,1:2:end),C(i,2:2:end),'k.')
axis(pi*[-1 1 -1 1])
drawnow
end
Obviously this code relies heavily on managing array shapes to maintain the correct ordering for x, y, dx/dt, and dy/dt. There may be a simpler way to write it.
EDIT
The key here is to clearly define a state vector and define your ODE function to match that state vector. The state vector must be a column vector and your ODE function must return a column vector. In the code above I have chosen to represent the state of the system as a vector formated as states(:,1) = [x1 y1 x2 y2 x3 y3 ... ]. That means your ODE must return a column vector of the form
[ d/dt(x1) ]
[ d/dt(y1) ]
[ d/dt(x2) ]
[ d/dt(y2) ]
[ d/dt(x2) ]
[ d/dt(y2) ]
[ ... ]
[ ... ]
You will also require 2 interpolations, 1 for the x components and 1 for the y, to get the rates of change based on Ax and Ay. The way I chose to do this was with the line
B = #(t,states) reshape([ interp2(xref,yref,Ax,states(1:2:end),states(2:2:end)) ...
interp2(xref,yref,Ay,states(1:2:end),states(2:2:end))]',size(states));
This line is a bit complex and difficult to understand because it is written as an anonymous function. If you define a stand-alone function for this it will be much more clear and could be written as
function ODEvals = B(t,states,xref,yref,Ax,Ay)
x(:,1) = states(1:2:end); %// extract x values from states as a column vector
y(:,1) = states(2:2:end); %// extract y values
dxdt(:,1) = interp2(xref,yref,Ax,x,y); %// interpolate Ax
dydt(:,1) = interp2(xref,yref,Ay,x,y); %// interpolate Ay
%// concatenate the results, dxdt and dydt are column vectors
%// 1) put the as column 1 and 2
%// 2) take the transpose so they become rows one and two:
%// [d/dt(x1) d/dt(x2) ... ]
%// [d/dt(y1) d/dt(y2) ... ]
%// 3) reshape into a single column, the ordering will be:
%// [ d/dt(x1) ]
%// [ d/dt(y1) ]
%// [ d/dt(x2) ]
%// [ d/dt(y2) ]
%// [ d/dt(x2) ]
%// [ d/dt(y2) ]
%// [ ... ]
%// [ ... ]
ODEvals = reshape([dxdt dydt]',[],1);
One last note:
To use ode45, your ODE function must take inputs of a time (t above) and a state vector, even if you don't use the time. Additional arguments are optional, see the documentation on ode45 for more details.