翻译自博客
Tips for Writing Dynamics Models in MATLAB
https://sassafras13.github.io/MatlabModeling/
对于MATLAB写脚本给出了很多有用的建议。
编写脚本
-
使用 LiveEditor 脚本。MATLAB 具有与 Python 中的 Jupyter 笔记本等效的文档格式,称为 LiveEditor 脚本。这种文档格式允许您在一个地方混合文本、LaTeX 方程式、图像和代码,它可以成为处理模型推导时交流模型推导的主要工具。可以写出数学方程式,然后在下一行演示如何用代码编写该方程式。也可以将 LiveEditor 脚本保存为 PDF(也可能保存为 Markdown 文件?)并与其他人共享,即使他们没有 MATLAB 许可证。
-
使用符号工具箱。MATLAB 包含一个符号数学工具箱,可以使用它来编写表达式,而无需为每个变量分配实际值。它甚至允许您求导数、积分、计算叉积等。我使用符号工具箱编写我的整个模型,然后将最终表达式保存为我可以在常规脚本中调用的 MATLAB 函数。
-
但是不要对符号数学太自信。如果符号工具箱可以做所有的数学运算,为什么还要做微积分呢?有必要检查自己的工作并确保信任它。特别是当为一个还没有太多直觉或理解的新系统建模时,应该手工检查你的数学。这也有助于您更好地了解模型的工作原理。
-
确保将符号变量设置为real。如果不这样做,可能会发现在只需要实部的情况下函数会返回复数。这种情况下的正确代码示例如下:
syms x y z ‘real’ % real command forces these variables to be real-valued
-
将您的 LiveEditor 脚本分成多个部分,并为文件名添加数字前缀以保持它们的顺序。如果单个文档中有很多文本行或大量计算,我的 LiveEditor 脚本有可能会变慢。为了解决这个问题,可以将文档分成多个 LiveEditor 脚本并使用数字前缀保存它们,例如
01_setup.mlx
、02_forces.mlx
等。MATLAB 可能会抱怨文件名的数字开头,但可以覆盖它并且它似乎工作正常. -
声明一次变量并按顺序运行 LiveEditor 脚本。如果您确实按照上面的提示 5 进行操作,则需要按顺序运行脚本,以便后面的脚本中所需的所有变量都已由前面的脚本创建。我找到了两种方法来确保这种情况发生:第一种是记住始终按顺序运行脚本。第二种是在后面的脚本顶部添加命令以运行前面的脚本,如下所示:
01_setup ; % runs file 01_setup.mlx
-
利用所有有用的符号工具箱功能。同样,应该始终检查符号工具箱的功能,但可以让它做一些非常酷的事情,例如项、使用、积分和微分collect找到特定变量的系数。coeffs如果它们真的很乱,也一定要用它simplify来清理东西。
-
可以使用符号工具箱进行时间导数。在这一点上,一些学者可能对所有事情都使用符号工具箱感到非常生气。但与使用符号工具箱相比,手工求导更容易出错。这里有一个示例,说明如何使用符号数学计算函数的时间导数:
% create symbolic variables for x (position) and xdot (velocity)
% these variables are not dependent on time
syms x xdot 'real'
% define function y(x, t) for which we will compute time derivative
y = x^2 ;
% define the symbolic variables x_t and dx_t
% these variables are dependent on time
syms t x_t(t) 'real'
dx_t = diff(x_t, t) ;
% replace the value x with x_t to create a time-dependent version of
% the function y, called y_t
y_t = subs(y, x, x_t) ;
% take the time derivative of y_t to get dy_t, also a function of time
dy_t = diff(y_t, t)
% replace x_t and dx_t with the symbolic variables that are
% not functions of time, x and xdot
dy = subs(dy_t, [x_t, dx_t], [x, xdot])
模块化
-
编写模块化代码。假设有多个版本的模型,您希望将它们相互比较。编写模块化代码将是必不可少的。每段代码只编写一次并通过函数调用重用它对于确保您正确测试模型至关重要。模块化代码将允许您一次定义所有模型参数,并使用不同版本的模型无限次加载它们,因此您可以测试模型的性能,而不必担心参数值因测试而异。
-
编写包含所有模型参数的脚本并将它们保存在 .mat 文件中。模型可能需要定义一些参数。如果要比较都需要这些相同参数的不同模型,那么可以将它们编写到一个单独的脚本中是有意义的,可以重复调用。通常可以编写一个常规脚本来定义我所有的参数,然后使用
save(“myfile.mat”)
命令将变量保存为.mat
文件。然后可以将这个文件加载到一个单独的脚本中,该脚本将使用load(“myfile.mat”)
. 这些变量会立即加载到您的工作区中,您就可以开始了。 -
使用对你有利的文件夹结构。好的代码库的一个特征是所有脚本都在有意义的子目录中,并且整个 repo 结构是合乎逻辑且有序的。例如,在我的
\src\
目录中,我喜欢为\utils
(我所有脚本使用的函数)、\parameters
(我想重用的模型参数,根据提示)和\model
(对于我正在尝试的不同模型)创建子目录。你可以做任何你想做的事,但拥有这样的结构确实有助于让事情井井有条。在 MATLAB 中使用fileparts
和addpath
函数将您需要的任何子目录添加到当前工作区,以便 IDE 知道在运行代码时在那里查找这些函数。 -
可以查看在 ode45 调用中计算的中间值以帮助调试。中间值有助于调试或更好地了解您的系统。实际上,可以编写动态函数以将中间值作为附加输出返回,并在 ode45 调用后重新运行动态函数以获取这些中间值。考虑这个示例:
[t,q] = ode45(@(t, q) dynamics(t, q),Tspan,q0) ; % original call to ode45
% call your dynamics function again with the output from the line above
[~,torque] = cellfun(@(t,x) dynamics(t,q.'), num2cell(t), num2cell(q,2),'uni',0);
torque = cell2mat(torque) ;
模型测试
为什么测试你的模型和必要的功能如此重要。在研究中,我们经常开发新的我们以前从未研究或构建过的事物的模型。这意味着我们正在冒险进入未知领域,我们可能没有直觉一眼就能知道我们的模型是否正常工作,或者它是否存在错误。我们必须确保它正常工作,而不能只写一个模型就行了。
-
**针对课本中的例子测试执行特定数学运算的函数。**例如部分建模活动包括为机器人运动学实施大量数学运算,可以使用Murray、Li 和 Sastry的教科书作为参考。每次我实现一个函数,比如计算矩阵指数、扭转或计算雅可比矩阵时,可以会在从他们的教科书中提取的示例上运行该函数。在 LiveEditor 脚本中执行此操作,因此我可以直接从教科书中包含解决方案的屏幕截图,以显示我的函数输出正确答案。这无助于证明你的整个模型是正确的,但它会让你更有信心你的组件功能正常工作。
-
设计简单的测试来检查你的模型是否遵循简单的物理定律。如果你有一个复杂的系统(比如那个到处都有扭力弹簧的三连杆摆),考虑一下你可以在你的模型上运行的最简单的测试,以确保它能正确地做一些简单的事情。例如,如果您将一个连杆从静止位置移开,它会回落到静止位置并停止移动吗?如果所有扭力弹簧的弹簧常数都为零,那么连杆是否只是沿垂直轴悬挂?这可能看起来微不足道,但如果您的模型不能做到这一点,那么您就知道您有问题需要解决。使用这种策略的另一个好处是很容易知道模型应该做什么在这种情况下做 - 对于更复杂的情况,这可能并非如此。
-
将实验数据用作基本事实。当然,在测试过程中,您可能会遇到一个您确信模型在简单测试中正确执行的时刻,但您想看看它在更复杂的情况下是否能正确执行。如果可能,请考虑构建一个快速且廉价的原型,您可以使用它来研究系统对不同输入的响应。
-
从您已经拥有或信任的模型构建您想要的模型。如果您正在从事其他人已经编写了与您的模型类似的模型的项目,请考虑使用他们的模型作为起点并对其进行修改,直到它具有您需要的所有功能。同样,我们在这里探索未知,别人的模型就像一块垫脚石,可以帮助您开始渡河。但是,我还应该提醒您,如果您需要以生成截然不同的结果的方式修改模型,则此过程可能会使事情变得更加复杂。比较这些模型可能会变得困难,并且与从头开始相比,您可能会产生更多的困惑。
得到你需要的答案
现在假设您已经为您的系统构建了一个动力学模型并且您已经对其进行了严格的测试。现在您想使用很酷的技术来分析它,例如绘制极限环或生成约束曲率函数。让我分享一些关于如何进行分析以获得巨大成功的最后提示。
-
为每个分析过程编写一个新的脚本。这可能看起来很简单,但是对于您要进行的每个分析都有单独的、命名良好的脚本。由于您已经拥有带有可从 .mat 文件加载的参数的模块化代码,因此您可以使用相同的参数轻松地对同一模型执行不同的分析。还可以编写代码来保存这些脚本中每个分析过程的数据。理想情况下,每个脚本都应该输出一个你在论文中使用的图形,而查看你的代码库的其他研究人员应该能够从这些脚本中重新创建你的所有图形。
-
如果您想运行生成大量数据或图形的分析,请考虑自动创建目录和保存文件以组织此分析的输出。在这里给你一个具体的例子,假设我们想看看我们的摆模型在不同的长度和质量组合下的表现。在您的分析脚本中,您可以创建子目录并将绘图和数据文件自动保存到这些子目录中。您可以在外出吃午饭时运行脚本,然后回来查看所有已为您组织到子文件夹中的结果图。
-
将图形保存为 .fig 文件,以便您以后可以继续编辑它们。我们经常需要多次迭代才能得到真正喜欢的图形,但是如果将我图形保存为 .png 文件,那么我以后就无法编辑这些文件,而是必须重新运行所有用于在我编辑之前制作那个图形。这很蹩脚,浪费您宝贵的时间,因此请将您的图形保存为 .fig 文件,这是 MATLAB 原生图形格式(或者保存您的图形两次,一次为 .fig,一次为 .png,如果您愿意的话!)。现在,您始终可以将这些图形重新加载到您的工作区并使用 MATLAB 的图形编辑器对其进行编辑。
-
为你的动态系统制作动画几乎总是值得你投入时间的。这可能是最重要的提示之一:如果你想让人们真正理解你在做什么,构建信息丰富的动画是让他们快速理解你在做什么的最好方法。MATLAB 允许您生成动画(.gif很好,但也可以根据需要输出 .mp4和其他格式)。MATLAB 当然有关于构建动画的不同方法的优秀文档,另外也可以参考麻省理工学院的这个脚本。