Matlab:在ode45中使用interp2

时间:2014-03-26 17:41:53

标签: matlab matrix

我有一个带有离散值的二维矩阵,我想使用ode45命令。 为此,我使用interp2创建了一个ode45可以使用的函数。

问题是,看起来ode45正在移出我定义的区域,因此interp2返回NaN值。为了摆脱NaN,我使用了一个外推值,但是现在似乎ode45从我的初始值到这个外推值的整合,忽略了我在矩阵中的任何给定值。

这是一个带有2x2矩阵的小例子:

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]

我做错了什么? 感谢您提前提供任何帮助!

1 个答案:

答案 0 :(得分:1)

我认为这不会像你设置的那样起作用。您的ODE函数B应该接受时间输入和状态变量的单个输入(列)向量,并返回状态变量的变化率列向量。请查看ode45的帮助文档。

如果状态变量是坐标对(x,y),则需要为每对返回dx / dt和dy / dt。我不确定如何在单个阵列上进行插值。我认为您需要以下内容:

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

显然,这段代码在很大程度上依赖于管理数组形状来维持x,y,dx / dt和dy / dt的正确排序。可能有一种更简单的方法来编写它。

修改
这里的关键是清楚地定义状态向量并定义ODE函数以匹配该状态向量。状态向量必须是列向量,ODE函数必须返回列向量。在上面的代码中,我选择将系统状态表示为格式化为states(:,1) = [x1 y1 x2 y2 x3 y3 ... ]的向量。这意味着您的ODE必须返回格式为

的列向量
[ d/dt(x1) ]
[ d/dt(y1) ]
[ d/dt(x2) ]
[ d/dt(y2) ]
[ d/dt(x2) ]
[ d/dt(y2) ]
[   ...    ]
[   ...    ]

您还需要2个插值,1个用于x个组件,1个用于y,以获得基于AxAy的更改率。我选择这样做的方式是使用

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));

这一行有点复杂且难以理解,因为它是作为匿名函数编写的。如果为此定义一个独立的函数,它将更加清晰,可以写成

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);

最后一点:
要使用ode45,您的ODE函数必须输入一个时间(t以上)和状态向量,即使您没有使用时间。其他参数是可选的,有关详细信息,请参阅ode45上的文档。