CodeWorld中如何将模拟动画与图形相结合?

How to combine simulated animation with graphs in CodeWorld?

我玩 CodeWorld 并使用“入口点”activityOf() 来模拟由坦克组成的物理系统。这里我们有一个水箱,其出口流量 qout(h) 取决于容积高度。当然,对于严肃的模拟工作,应该使用其他软件,但在这里做起来仍然很有趣!

program = activityOf(initial, change, tank_water)
composedOf = pictures

-- Initial value and parameter
initial(rs) = 5
g = 9.81
area = 0.015

-- Inlet flow
qin = 0.0

-- Dynamics using Euler approximation
qout(h) = area*sqrt(2*g*max(h,0))
change(h, TimePassing(dt)) = max(h,0) - qout(h)*dt + qin*dt
change(h, other) = h

-- Animation of system
tank_water(h) = composedOf [tank, water(h), graph, coordinatePlane]  

tank = translated(thickRectangle(width,height,0.2),position,4)
width = 3
height = 8
position = -5

water(h) = translated(colored(solidPolygon [(-width/2, 0), (-width/2, h), (width/2, h), (width/2,0)], 
                          light(blue)),position,0)

-- Graph of evolution of h(t) - here used monitored values and about 5 seconds between each data point
graph = polyline [(0,5), (1,3.7), (2,2.3), (3,1.3), (4,0.7), (5,0.2), (6, 0)]

出于教育目的,我认为将动画与图表结合起来显示身高随时间变化的图表是很好的。这里我在代码中输入了一个“假”图,因为它很容易测量(我也可以输入解析解使图适应参数)。

我想知道如何包含一个子系统,该子系统从动画中收集数据并在模拟过程中将其呈现在图表中。有什么想法吗?

我想到的一个麻烦的想法是扩展模型状态,其中包括在模拟过程中要收集的测量点。我们可以事先说我们收集了 10 个 h 和 t 的样本,时间距离为 5 秒,然后随着数据的到来扩展图表。

另一个想法是以某种方式将一些通用数据记录器“附加”到 activityOf() 程序中,该程序将数据存储在一个文件中,之后您可以使用任何软件研究该文件。也许这样的记录器已经在 Haskell 环境中可用?

但我不确定如何做到这一点,也许这里有一些更好、更通用的方法?

我将使用“常规”Haskell 环境中的语法和类型进行响应,https://code.world/haskell

一个activityOf的类型是:

activityOf ::
   world ->
   (Event -> world -> world) ->
   (world -> Picture) ->
   IO ()

正如您所观察到的,没有内置的方法来记录值​​并将它们绘制成图表。将其添加到 activityOf 似乎是错误的(我们可能想要添加的东西太多了)。

但是你想要这样的功能是什么类型的?可能是以下内容:

activityWithGraphOf ::
  world ->
  (Event -> world -> world) ->
  (world -> Picture) ->
  (world -> Double) ->
  IO ()

考虑到这个抽象,我会去实现那个功能

  • 它自己当然会使用 activityOf
  • 它会跟踪 world 状态
  • 它会记录时间
  • 它会跟踪最近的图表值
  • 也许它将传入的 TimePassing 一分为二,如果需要的话,以正确的间隔对图表进行采样。
  • 并且它会将包装的 activity 提供的 Picture 与图形
  • 组合起来

听起来是个有趣的练习。现在我有了一个通用函数,可以用于许多图形绘制模拟。

我用“麻烦”的方式解决了问题。希望我能从我得到的输入中更好地构建它。感谢改进建议,以便代码可以更短。我对元组处理感到笨拙。进一步创建 table 和后来的图表可以与罐过程更加分离。这将有助于重用到 CodeWorld 中的其他模拟任务。欢迎改进!

--

我现在更新了代码并使其更短,但还在 table 中添加了更多数据点。 state_and_table 必须是 CodeWorld 中的元组,并且无法使用索引处理元组。 此外,您现在可以使用上下箭头更改泵速 qin。

扩展到两个水箱(或更多)让控制水高变得更具挑战性并将其作为练习留给您!享受吧!

Link 到 运行 https://code.world/#P2uQaw2KBSbyspnQSn5E4Gw

program = activityOf(initial, change, tank_water)

-- Initial values of the total state, i.e. state_and_table
initial(rs) = state_and_table
state_and_table = (time_0, h_0, u_0,
                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
time_0 = 0
h_0 = 7
u_0 = 0

-- Functions to read elements of state_and_table 
time_of(time_value,_,_,_,_,_,_,_,_,_,_,_,_,
                       _,_,_,_,_,_,_,_,_,_) = time_value
h_of(_,h_value,_,_,_,_,_,_,_,_,_,_,_,
                 _,_,_,_,_,_,_,_,_,_) = h_value
u_of(_,_,u_value,_,_,_,_,_,_,_,_,_,_,
                 _,_,_,_,_,_,_,_,_,_) = u_value
table_entry((_,_,_,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,
             x11,x12,x13,x14,x15,x16,x17,x18,x19,x20), k)
 | k==1 = x1
 | k==2 = x2
 | k==3 = x3
 | k==4 = x4
 | k==5 = x5
 | k==6 = x6
 | k==7 = x7
 | k==8 = x8
 | k==9 = x9
 | k==10 = x10
 | k==11 = x11
 | k==12 = x12
 | k==13 = x13
 | k==14 = x14
 | k==15 = x15
 | k==16 = x16
 | k==17 = x17
 | k==18 = x18
 | k==19 = x19
 | k==20 = x20

-- Events of recording state to table at the following times
table_time = [ 5*t-5 | t <- [1..20]]

-- Parameters related to the water flow
g = 9.81
area = 0.025

-- Parameters of the tank
width = 5
height = 8
position = -5

-- Inlet and outlet flow
qin(u) = max(u, 0)
qout(h) = area*sqrt(2*g*max(h,0))  -- Bernoulli's physical model

-- Change of state_and_table 
change(state_and_table, TimePassing(dt)) = 

   -- Update time
   (time_of(state_and_table) + dt,

   -- Physical state equation -- Euler approximation of differantial equation  
   max(h_of(state_and_table),0) - qout(h_of(state_and_table))*dt + qin(u_of(state_and_table))*dt, 

   -- Control variable u
   u_of(state_and_table),

   -- Event of recording state to table at predfined times
   table_update(state_and_table,1),
   table_update(state_and_table,2),
   table_update(state_and_table,3),
   table_update(state_and_table,4),
   table_update(state_and_table,5),
   table_update(state_and_table,6),
   table_update(state_and_table,7),
   table_update(state_and_table,8),
   table_update(state_and_table,9),
   table_update(state_and_table,10),
   table_update(state_and_table,11),
   table_update(state_and_table,12),
   table_update(state_and_table,13),
   table_update(state_and_table,14),
   table_update(state_and_table,15),
   table_update(state_and_table,16),
   table_update(state_and_table,17),
   table_update(state_and_table,18),
   table_update(state_and_table,19),
   table_update(state_and_table,20))

-- Key input
change(state_and_table, KeyPress("Up")) = 
  (time_of(state_and_table),
   h_of(state_and_table),
   u_of(state_and_table)+0.1,
   table_update(state_and_table,1),
   table_update(state_and_table,2),
   table_update(state_and_table,3),
   table_update(state_and_table,4),
   table_update(state_and_table,5),
   table_update(state_and_table,6),
   table_update(state_and_table,7),
   table_update(state_and_table,8),
   table_update(state_and_table,9),
   table_update(state_and_table,10),
   table_update(state_and_table,11),
   table_update(state_and_table,12),
   table_update(state_and_table,13),
   table_update(state_and_table,14),
   table_update(state_and_table,15),
   table_update(state_and_table,16),
   table_update(state_and_table,17),
   table_update(state_and_table,18),
   table_update(state_and_table,19),
   table_update(state_and_table,20))

change(state_and_table, KeyPress("Down")) = 
  (time_of(state_and_table),
   h_of(state_and_table),
   u_of(state_and_table)-0.1,
   table_update(state_and_table,1),
   table_update(state_and_table,2),
   table_update(state_and_table,3),
   table_update(state_and_table,4),
   table_update(state_and_table,5),
   table_update(state_and_table,6),
   table_update(state_and_table,7),
   table_update(state_and_table,8),
   table_update(state_and_table,9),
   table_update(state_and_table,10),
   table_update(state_and_table,11),
   table_update(state_and_table,12),
   table_update(state_and_table,13),
   table_update(state_and_table,14),
   table_update(state_and_table,15),
   table_update(state_and_table,16),
   table_update(state_and_table,17),
   table_update(state_and_table,18),
   table_update(state_and_table,19),
   table_update(state_and_table,20))

-- Default equation
change(state_and_table, other) = state_and_table

table_update(state_and_table, k)
 | time_of(state_and_table) > table_time # k && table_entry(state_and_table,k)==0 = h_of(state_and_table)
 | otherwise = table_entry(state_and_table, k)

-- Animation of system
composedOf = pictures
tank_water(state_and_table) = composedOf [headline, pump(qin(u_of(state_and_table))),
                                          tank, water(h_of(state_and_table)), 
                                          graph(state_and_table),  
                                          coordinatePlane]  

headline = translated(lettering("Tank dynamics"),5,7.5)
pump(qin) = translated(composedOf [lettering("- pump rate ="), translated(lettering(printed(qin)),3.7,0)], 4.5,6.5)
tank = translated(thickRectangle(width,height,0.2),position,4)
water(h) = translated(colored(solidPolygon [(-width/2, 0), (-width/2, h), (width/2, h), (width/2,0)], 
                              light(blue)), position,0)

-- Graph of evolution of h(t) - note time scale is 1 = 10 s etc
scale = 1/10
graph(state_and_table) = colored(polyline [(scale*table_time#k, table_entry(state_and_table, k)) 
                                            | k <- [1..length(table_time)], table_entry(state_and_table, k) /=0], 
                                 light(blue))