罗兰关于MATLAB的艺术

将想法转化为matlab

函数是functiontests

今天我们有一篇来自客座博主Andy Campbell的文章,强调了一种在MATLAB中编写测试的基于函数的方法。我们将演示如何编写帮助您设计物理系统的简单函数。这种方法允许您快速迭代并改进设计,同时保护您不违反早期解决方案所满足的需求。金宝搏官方网站

说到这里,让我们开始吧!

内容

问题陈述

假设你正在处理一个由二阶微分方程控制的设计问题。数学的美妙之处在于,它实际上在许多不同的领域都有广泛的适用性。对于我们的例子,让我们考虑一个简单的质量-弹簧阻尼器的设计。这种系统可以用来模拟汽车或卡车的悬架系统。这通常用以下简化模型表示:

地点:

  • $ m $ = mass
  • $k$ =弹簧常数
  • $c$ =阻尼系数
  • $x$ =平衡位移

假设没有外力,该系统的运动方程是对二阶微分方程的解决方案$ m \ ddot {x} + c \ dot {x} + kx = 0 $。遵循类似的方法之前的一篇博客文章,我们可以准备这个方程,由一个MATLAB的ode求解器来求解,并编写以下函数来评估我们的设计:

类型simulateSystem
函数[x, t] = simulateSystem(design) %设计变量c = design.c;k = design.k;%常量变量z0 = [-0.1;0);%初始位置和速度m = 1500;%质量odefun = @(t,z) [0 1;- k / m - c / m] * z;[t, z] = ode45(odefun, [0,3], z0);%第一列为位置(平衡位移)x = z(:, 1);

在这个函数中,$z$是一个表示系统状态的向量。它包含$x$和它的导数$\。{x}$。注意,初始条件是质量的位置为-0.1,速度为0。这个州可能会模拟这样的情况:你在高速公路上开车时撞到了一个坑。初始位置模拟壶穴底部的质量,零初始速度模拟悬架压缩、停止和即将后坐力。为了这个例子,我们设质量在1500kg恒定,这对于大型轻型卡车单轮承载的负载是合理的。

设计变量为弹簧常数$k$和阻尼系数$c$。一开始它们的值都是1。

类型springDamperDesign0
功能设计= springdamperdesign0%设计变量Design.c = 1;%阻尼系数(初始设计)Design.k = 1;%弹簧常数(初始设计)

输入functiontests

如果我被要求设计一个这样的系统,我无疑会寻找一些设计的要求,并确保我的设计满足所有的要求。然而,如果我实现了一个满足一个需求的设计,但随后调整设计以满足第二个需求,最终破坏了第一个需求,那么问题就会浮出水面。对于一个或两个需求来说,这是相当容易管理的,但如果有很多需求或者在稍后添加了新的需求,它很快就变得难以管理。如果我们有一些简单的机制来捕获第一个需求,以确保它永远不会被违反,那不是很好吗?我们所做的!让我们编写一个测试!

开始写自己的测试功能的简单方法是使用functiontests函数。的functiontests可以在测试功能文件的顶部调用函数,允许您在同一文件中创建和组组的许多相关测试。

类型testTemplate
functiontests = testTemplate tests = functiontests(localfunctions);结束

让我们剖析一下这里发生了什么。按照约定,此文件的名称以单词“test”(不区分大小写)开头或结尾,以帮助将其标识为包含测试的函数文件。它只需要调用localfunctions函数的输入functiontests函数,然后将结果分配给主函数的输出。

是什么functiontests

functiontests是一个MATLAB函数(R2013b中新增的),它接受一个单元格数组,该数组包含MATLAB文件中的局部函数句柄。然后根据命名约定确定这些局部函数中哪些是测试,并创建测试元素。所有以“test”(不区分大小写)开头或结尾的局部函数都被认为是测试。

是什么localfunctions

localfunctions功能是另一个在R2013B中的MATLAB函数。此函数不需要输入参数,只需返回到调用文件中所有本地函数的单元格阵列。在测试时functiontests,使用localfunctions强烈推荐,以便当您向文件添加额外的测试函数时,它们的函数句柄不需要手动添加到文件顶部的单元格数组中。相反,localfunctions自动发现这些新测试,并将其添加到传递给的功能手柄数组中functiontests

第一个测试

有了这个基本框架,我们就可以编写第一个测试了。第一个设计要求可能是系统在一定的时间内回到平衡状态,例如,2秒。让我们编写一个测试,看看我们如何满足这个需求。

类型design0Test
功能测试= Design0Test测试= FunctionTess(LocalFunctions);testSettlingTime(testCase) %测试系统是否在2秒内稳定到0.001的零。[位置,时间] = Simulateystem(SpringDamperDesign0);position = position(时间> 2);%在这个例子中,验证在沉淀时间之后的第一个值。verifyEqual(testCase, positionaftersetting(1), 0, 'AbsTol', 0.001);结束

这个测试:

  1. 使用指定的设计变量来模拟系统。
  2. 在规定的固定时间后捕获位置。
  3. 验证在要求的时间内,位置在+/- 0.001内达到0。通常情况下,在实际测试中,你需要验证沉降时间后的所有位置都足够接近于零,而不仅仅是在期望的时间点。

注意,这个测试函数使用并要求一个测试用例论点。此参数包含重要的测试上下文,并允许使用丰富的限定函数库,例如verifyEqual

当我们称之为这个功能时会发生什么?

测试= design0Test;disp(测试)
使用属性测试:Name: 'design0Test/testSettlingTime' SharedTestFixtures: []

您可以看到这个函数创建了一个测试。要运行此测试,只需调用运行函数,并将此测试作为参数。

运行(测试);
运行design0Test  ================================================================================ 验证失败design0Test / testSettlingTime。--------------------- 框架的诊断 : --------------------- verifyEqual失败了。——> NumericComparator失败了。——>使用“isequaln”时值不相等。——> AbsoluteTolerance失败了。——>该值不在绝对允许范围内。公差定义:abs(expected - actual) <=公差公差值:1.0000000000000000000e -03实际值:-0.099863405108097期望值:0 ------------------堆栈信息:------------------ In H:\Documents\LOREN\MyJob\Art of MATLAB\Andy Campbell\非正式测试接口\design0Test。在C:\Program Files\MATLAB\R2013b\toolbox\matlab\ testframework\+matlab\+unittest\FunctionTestCase. conf . conf . conf . conf . conf . conf . conf。(FunctionTestCase.test) 90  ================================================================================ .完成design0Test  __________ 失败失败总结:名字不完整的原因(s ) =========================================================================== design0Test / testSettlingTime X验证失败。

如您所见,测试失败并打印了一些信息verifyEqual帮助诊断失败。让我们看看我们在图形上的近距离。请注意,对于此示例,为了显示我们需要显示比我们的系统所需更大的时间跨度的动态。的simulateSystemOverLargeTime包含相同的内容simulateSystem定义在上面,但它有不同的时间跨度。

[x,t] = simulateystemoverlargetime(springdamperdesign0);绘图(t,x);标题(“偶然的反应”);包含(“时间”);ylabel (“位置”

这张图展示了这种设计如何在2秒内无法接近平衡(在1000秒后更是如此)。然而,这是意料之中的,因为我们还没有设计任何东西。

第一个设计

假设我们提出了以下设计:

类型springDamperDesign1
功能设计= springDamperDesign1设计。k = 5 e6;%弹簧常数设计。c = 1e4;%阻尼系数

这个设计是否符合我们的标准?

运行(design1Test);
跑步设计1test。完成设计1TEST __________

是的,我们通过了!让我们看看我们的反应:

[x, t] = simulateSystem(springDamperDesign1);plot(t,x)标题(“设计# 1”)Xlabel(“时间”)ylabel(“位置”

很棒,我们迅速达到均衡。然而,不幸的是,在您的全新卡车中看起来并不舒适。它看起来像是击中坑洞的所有振动都会摇晃着贫困的乘客的眼睛。此外,该系统已过度达到孔隙深度的80%(0.08个最大位置,坑洞深度为0.1)。我们可以做得更好。让我们写一个测试来证明它。

第二个测试

这里缺少的要求可能是对头寸能超过平衡点的程度的限制。我们现在可以在我们的武器库中添加第二个测试:

类型design1SecondTest
函数测试= design1SecondTest;testSettlingTime(testCase) %测试系统是否在2秒内稳定到0.001的零。[position, time] = simulateSystem(springDamperDesign1);position = position(时间> 2);%在这个例子中,验证在沉淀时间之后的第一个值。verifyEqual(testCase, positionaftersetting(1), 0, 'AbsTol', 0.001);end函数testOvershoot(testCase) %测试以确保超调小于0.01 position = simulateSystem(springDamperDesign1);过度= max(位置);verifyLessThan (testCase,过度,0.01);结束

此超调测试使用verifyLessThan以确保位置的移动不会超过平衡点太远。按照我们目前的设计,我们预计这个测试会失败。首先,我们再次创建测试数组中。注意,第二个测试是自动拾取的测试数组现在包含两个元素而不是一个元素。

测试= design1SecondTest;disp(测试)运行(测试);
1x2带有属性的测试数组:名称SharedTestFixtures正在运行design1SecondTest。================================================================================ 验证失败design1SecondTest / testOvershoot。--------------------- 框架的诊断 : --------------------- verifyLessThan失败了。——>小于最大值。实际值:0.082943282378938最大值(排他):0.010000000000000 ------------------堆叠信息:------------------ In H:\Documents\LOREN\MyJob\Art of MATLAB\Andy Campbell\非正式测试接口\design1SecondTest。In C:\Program Files\MATLAB\R2013b\toolbox\matlab\ testframework\+matlab\+unittest\FunctionTestCase. conf . conf . conf . conf . conf . conf . conf . conf . conf。(FunctionTestCase.test) 90  ================================================================================ .完成design1SecondTest  __________ 失败失败总结:名字不完整的原因(s ) ============================================================================== design1SecondTest / testOvershoot X验证失败。

超调测试失败了,这不是一件坏事!它不仅告诉我们我们缺少这个需求,它还作为一个完整性检查来确认我们的测试确实在做我们认为它们在做的事情。

第二种设计

基于系统的响应,似乎我们目前的设计被拒绝了,所以为什么我们不只是增加一些阻尼,以防止我们的过冲并再次运行测试。

类型springDamperDesign2
功能设计= springdamperdesign2 design.k = 5e6;%弹簧常量设计.C = 2.5E6;%从1E4增加阻尼系数

让我们看看它是怎么做的

[x,t] = simulateystem(springdamperdesign2);plot(t,x)标题(“设计# 2”)Xlabel(“时间”)ylabel(“位置”

看看这个系统的响应它看起来像一个很平稳的平稳性在扰动后缓慢地将卡车推回到平衡状态。我们就这么办吧!首先我们要确保它通过了测试。

运行(design2Test);
运行design2Test  ================================================================================ 验证失败design2Test / testSettlingTime。--------------------- 框架的诊断 : --------------------- verifyEqual失败了。——> NumericComparator失败了。——>使用“isequaln”时值不相等。——> AbsoluteTolerance失败了。——>该值不在绝对允许范围内。公差定义:abs(expected - actual) <=公差公差值:1.0000000000000000000e -03实际值:-0.001823826310161期望值:0 ------------------堆栈信息:------------------ In H:\Documents\LOREN\MyJob\Art of MATLAB\Andy Campbell\非正式测试接口\design2Test。在C:\Program Files\MATLAB\R2013b\toolbox\matlab\ testframework\+matlab\+unittest\FunctionTestCase. conf . conf . conf . conf . conf . conf . conf。(FunctionTestCase.test) 90  ================================================================================ ..完成design2Test  __________ 失败失败总结:名字不完整的原因(s ) =========================================================================== design2Test / testSettlingTime X验证失败。

看那!这个设计通过了超调测试,但我们已经回归,再次没有通过沉淀时间测试。这就是我们通过测试对这些需求进行编码的价值所在。在我们确定了沉淀时间要求之后,我们将重点转向了超调要求。当调整过调时,我们很容易改变设计,忽略了沉淀时间。专注于过度,最新的设计似乎是合理的,因为它是关闭为了满足沉降时间的要求,通过人工检查很难注意到。显然,接近是不够的!这些测试现在正在保护我们不违反先前满足的要求。这些测试可以用来锁定所有过去的需求,即使它们不再是焦点。添加更多的测试只会使这些需求更严格或更完整。

随着系统需求的增长,这一点尤其重要。您可以通过更大、更全面的测试集得到更大的保护。您可以想象这样一种情况:以一种方式调整设计,100个测试中有一个失败,但随后以另一种方式调整设计,100个测试中有75个失败。当使用MATLAB在单个调用中执行所有测试时,您可以深入了解此类情况,这表明前者的设计可能比后者更接近于满足您的全部需求。

由于这些测试使用标准的MATLAB测试框架,他们可以与可以轻松运行的同事和/或客户共享并验证它们是否充分捕获这些要求。该测试充当要求的可执行规范,以及满足这些要求的验证。

最终的设计

为了完整性,让我们继续并通过这个测试,然后我们可以进行汇报。利用二阶系统,我们可以设计临界阻尼并再次进行试验。

类型springDamperDesign3.m结果=运行(design3Test);[x, t] = simulateSystem(springDamperDesign3);plot(t,x)标题(“设计# 3”)Xlabel(“时间”)ylabel(“位置”
功能设计= springDamperDesign3 m = 1500;%需要知道质量来确定临界阻尼设计。k = 5 e6;%弹簧常数design.c = 2*m*sqrt(design.k/m);%阻尼系数需达到临界阻尼运行设计试验完成design3Test  __________

测试通过,响应看起来符合临界阻尼的预期。现在,我们可以继续改进我们的设计,因为我们知道我们不会无法满足前两个需求。

结论

通过一个简单的例子,我们证明了使用测试来验证工程和科学问题的力量。它所需要的只是编写一个MATLAB函数,其中包含一些结构化的局部函数。接下来是什么?嗯,对于这个设计,可能有许多下一步,例如:

  1. 测试以确保弹簧常数足够低,以具有更平滑的,更少的生涩的响应。
  2. 测试以限制弹簧和/或阻尼器的成本(也许更硬的弹簧成本更高)。
  3. 测试以确保系统的固有频率在一定范围内。
  4. 改进模型。在现实世界中,一个精确的模型可能比一个规范的二阶系统要复杂得多。
  5. 用有效载荷测试设计。例如,如果质量需要与附加质量在0到500kg范围内的货物一起操作。
  6. 测试频率响应的形状和特性。

我们今天编写的测试只测试给定设计的结果。记住,基础系统根本不需要是质量-弹簧-阻尼器。设计和/或模型可以完全更改,但这些测试仍然可用,以确保系统的响应满足需求。

预先编写测试的初始投资产生了持久的红利,并为分析和设计提供了安全网。有趣的是,这些活动正是我们作为科学家和工程师已经在做的事情。我们总是测试我们的设计,我们只是不总是以可执行测试的形式捕获过程以供将来执行。你掌握的诀窍越多,这种方法就越容易使用。此外,通过允许安全、快速的设计迭代,预先在这类测试中投入的任何时间都能迅速在生产率方面获得回报。

这个例子仅仅开始触及MATLAB测试功能的表面。想了解更多信息,请看MATLAB单元测试框架的文档,包括这个使用测试函数的方法.让我们知道您的想法在这里




发布与MATLAB®R2013b

|

注释

要留下评论,请点击在这里登录到您的MathWorks帐户或创建一个新帐户。