【问题标题】:How to combine simulated animation with graphs in CodeWorld?如何在 CodeWorld 中将模拟动画与图形结合起来?
【发布时间】:2023-03-28 13:36:01
【问题描述】:

我使用 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 个时间距离为 5 秒的 h 和 t 样本,并随着数据的进入扩展图表。

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

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

【问题讨论】:

    标签: haskell simulation codeworld


    【解决方案1】:

    我将使用“常规”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 分成两部分,以便以正确的间隔对图表进行采样。
    • 它会将包装活动提供的Picture 与图表组成

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

    【讨论】:

    • 对我来说是一个好的开始。通过这种方式,使用 activityOf 的代码可以很容易地重新用于新的“入口点”,对吧?我理解这个新“入口点”的开发必须在更丰富的 Haskell 环境中完成,而不是在 CodeWorld 中。最后在 CodeWorld 中“安装”。我理解对了吗?
    • 这是倒数第二行的一个小错字。用世界代替单词。此外,我认为您需要“记录”一些数字。过一段时间。然后我猜输出是一个列表 [Double],或者更确切地说是一个表格或矩阵,但不确定你如何表达。
    • 这个入口点只是一个普通函数,你也可以在CodeWorld环境中定义和使用。
    • “它会跟踪最近的图表值”——这是存储列表或表格的地方;它在activityWithGraphOf 里面。
    • 如果我理解正确,您的意思是保留 program = activityOf(world, change, picture) 然后在内部定义一个函数 activityWithGraphOf(world,change, picture, table) 。我认为该函数应该映射到元组 (world,change,picture) 而不是 IO,但我无法让它工作。我怀疑必须更改入口点,否则我认为我无法存储图表。也许你可以把草图加长一点?
    【解决方案2】:

    我提出了一个“繁琐”的解决方案。希望我能从我得到的输入中更好地构建它。感谢改进建议,以便代码可以更短。我觉得元组处理很笨拙。表格的进一步创建和以后的图表可以与坦克过程更加分离。这将有助于重用于 CodeWorld 中的其他模拟任务。欢迎改进!

    --

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

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

    运行链接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))
    

    【讨论】:

      猜你喜欢
      • 2016-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多