简体   繁体   English

在Matlab中的ode45中保存派生值

[英]Saving derivative values in ode45 in Matlab

I'm simulating equations of motion for a (somewhat odd) system with mass-springs and double pendulum, for which I have a mass matrix and function f(x), and call ode45 to solve 我正在模拟具有质量弹簧和双摆的(有点奇怪)系统的运动方程,为此我有一个质量矩阵和函数f(x),并调用ode45求解

M*x' = f(x,t);

I have 5 state variables, q= [ QDot, phi, phiDot, r, rDot]'; 我有5个状态变量,q = [QDot,phi,phiDot,r,rDot]'; (removed Q because nothing depends on it, QDot is current.) Now, to calculate some forces, I would like to also save the calculated values of rDotDot, which ode45 calculates for each integration step, however ode45 doesn't give this back. (删除了Q,因为没有什么依赖于Q,QDot是当前的。)现在,为了计算一些力,我还想保存rDotDot的计算值,该值由ode45为每个积分步骤计算,但是ode45并没有给出。 I've searched around a bit, but the only two solutions I've found are to a) turn this into a 3rd order problem and add phiDotDot and rDotDot to the state vector. 我进行了一些搜索,但是发现的唯一两个解决方案是:a)将其变成三阶问题,并将phiDotDot和rDotDot添加到状态向量。 I would like to avoid this as much as possible, as it's already non-linear and this really makes matters a lot worse and blows up computation time. 我想尽可能避免这种情况,因为它已经是非线性的了,这确实使情况变得更糟,并且浪费了计算时间。

b) augment the state to directly calculate the function, as described here . B)增加的状态下直接计算功能,如所描述这里 However, in the example he says to make add a line of zeros in the mass matrix. 但是,在示例中,他说要在质量矩阵中添加零线。 It makes sense, since otherwise it will integrate the derivative and not just evaluate it at the one point, but on the other hand it makes the mass matrix singular. 这是有道理的,因为否则它将积分导数,而不仅是在一点上求值,另一方面,它会使质量矩阵变得奇异。 Doesn't seem to work for me... 似乎不适合我...

This seems like such a basic thing (to want the derivative values of the state vector), is there something quite obvious that I'm not thinking of? 这似乎是一件很基本的事情(想要状态向量的导数),有没有我明显没有想到的东西? (or something not so obvious would be ok too....) (或者不太明显的事情也可以。...)

Oh, and global variables are not so great because ode45 calls the f() function several time while refining it's step, so the sizes of the global variable and the returned state vector q don't match at all. 哦,全局变量不是那么好,因为ode45在完善步长时多次调用f()函数,因此全局变量的大小和返回的状态向量q根本不匹配。

In case someone needs it, here's the code: 万一有人需要它,下面是代码:

%(Initialization of parameters are above this line)
   options = odeset('Mass',@massMatrix);
   [T,q] = ode45(@f, tspan,q0,options);

function dqdt = f(t,q,p)
    % q = [qDot phi phiDot r rDot]';

    dqdt = zeros(size(q));

    dqdt(1) = -R/L*q(1) - kb/L*q(3) +vs/L;
    dqdt(2) = q(3);
    dqdt(3) = kt*q(1) + mp*sin(q(2))*lp*g;
    dqdt(4) = q(5);
    dqdt(5) = mp*lp*cos(q(2))*q(3)^2 - ks*q(4) - (mb+mp)*g;
end

function M = massMatrix(~,q)
    M = [
        1 0 0 0 0;
        0 1 0 0 0;
        0 0 mp*lp^2 0 -mp*lp*sin(q(2));
        0 0 0 1 0;
        0 0 mp*lp*sin(q(2)) 0 (mb+mp)
        ];
end

The easiest solution is to just re-run your function on each of the values returned by ode45 . 最简单的解决方案是ode45返回的每个值重新运行您的函数。

The hard solution is to try to log your DotDots to some other place (a pre-allocated matrix or even an external file). 困难的解决方案是尝试将DotDots记录到其他位置(预分配的矩阵甚至是外部文件)。 The problem is that you might end up with unwanted data points if ode45 secretly does evaluations in weird places. 问题是,如果ode45秘密地在怪异的地方进行评估,您可能最终会得到不需要的数据点。

Since you are using nested functions, you can use their scoping rules to mimic the behavior of global variables. 由于使用的是嵌套函数,因此可以使用它们的作用域规则来模拟全局变量的行为。

It's easiest to (ab)use an output function for this purpose: 为此目的,最简单的方法是使用输出函数

function solveODE

    % ....        
    %(Initialization of parameters are above this line)

    % initialize "global" variable
    rDotDot = [];

    % Specify output function 
    options = odeset(...
        'Mass', @massMatrix,...
        'OutputFcn', @outputFcn);

    % solve ODE
    [T,q] = ode45(@f, tspan,q0,options);

    % show the rDotDots    
    rDotDot



    % derivative 
    function dqdt = f(~,q)

        % q = [qDot phi phiDot r rDot]';

        dqdt = [...
            -R/L*q(1) - kb/L*q(3) + vs/L
            q(3)
            kt*q(1) + mp*sin(q(2))*lp*g
            q(5)
            mp*lp*cos(q(2))*q(3)^2 - ks*q(4) - (mb+mp)*g
        ];

    end % q-dot function 

    % mass matrix
    function M = massMatrix(~,q)
        M = [
            1 0 0 0 0;
            0 1 0 0 0;
            0 0 mp*lp^2 0 -mp*lp*sin(q(2));
            0 0 0 1 0;
            0 0 mp*lp*sin(q(2)) 0 (mb+mp)
         ];
    end % mass matrix function


    % the output function collects values for rDotDot at the initial step 
    % and each sucessful step
    function status = outputFcn(t,q,flag)

        status = 0;

        % at initialization, and after each succesful step
        if isempty(flag) || strcmp(flag, 'init')
            deriv = f(t,q);
            rDotDot(end+1) = deriv(end);
        end

    end % output function 

end 

The output function only computes the derivatives at the initial and all successful steps, so it's basically doing the same as what Adrian Ratnapala suggested; 输出函数仅在初始步骤和所有成功步骤中计算导数,因此它基本上与Adrian Ratnapala所建议的相同。 re-run the derivative at each of the outputs of ode45 ; ode45每个输出处重新运行导数; I think that would even be more elegant (+1 for Adrian). 我认为这样会更优雅(Adrian +1)。

The output function approach has the advantage that you can plot the rDotDot values while the integration is being run (don't forget a drawnow !), which can be very useful for long-running integrations. 输出函数方法的优势在于,您可以在运行集成时绘制rDotDot值(请不要忘了一个drawnow !),这对于长时间运行的集成非常有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM