深入建模艺术:MiniZinc工具的使用指南(续)——揭秘模型与数据分离的抽象化策略
优化建模之MiniZinc(二) 模型的抽象化 -- 模型与信息的隔离
在前一篇文章中,我们阐述了MiniZinc程序的核心构成要素。
为了回顾,让我们审视一个生产安排问题:
我们在边界与敌对势力对峙,由于禁止开火,我们需要制造一批冷兵器来装备军队,可供选择的兵器有长矛、剑和锤子;制造兵器需耗费一定资源和工匠的时间,各类兵器对应的资源消耗如下:
我们的资源是有限的:
每种兵器对应一定的战斗力:
我们期望充分利用有限资源以最大化军队战斗力,那么各类兵器需要生产多少件呢?
相应的模型如下:
经过求解,我们可以得到答案:
模型中涉及数组和内置函数的相关内容,可以查阅后续对MiniZinc语法的详细解释。
该模型看似无误,求解过程也顺利。然而,在众多情况下,我们在建模时往往会遇到一系列假设性问题:若资源有所增加,情形将如何?若增加更多兵器种类,又将如何?
我们必须不断调整上述具体模型,以应对不断变化的数据。
若我们不是在解决兵器生产问题,而是在制定期末复习计划呢?我们是否需要重新编写整个模型呢?
在软件工程领域,设计模式为我们提供了重要的启示,即将变化的部分与相对稳定的部分分离,对于模型而言,亦是如此。在相同类型的生产安排模型中,存在一些共性,我们可以提取这些共性,并将数据独立出来。
生产安排模型
在生产安排模型中,我们可以提炼出一些共性:
基于这些共性,我们可以构建一个抽象模型(2_2_AbstractProductionModel.mzn):
我们注意到,在抽象模型中并未包含任何数据。这样我们就实现了模型与信息的分离,对于同一个模型,我们可以用它来解决具有相似性质的不同数据集。
为了验证,我们可以将之前的兵器生产问题构建一个独立的数据集2_2_WeaponProduction.dzn如下:
在命令行中执行minizinc--solver CBC 2_2_AbstractProductionModel.mzn 2_2_WeaponProduction.dzn,指定数据集和求解器进行求解,得到结果:
可以看到,对于兵器生产问题,我们得到的结果与之前相同。
学习时间分配问题
抽象模型的优势在于可以用它来处理同一类问题。利用之前建立的生产安排模型,我们可以探讨以下一个时间分配问题:
我们面临三门课程的期末考试,分别是运筹学、离散数学和C语言,对于每门课程的复习,一个理想的方案是分别分配一定的上机复习、讨论和书本自学时间,各门课程理想复习方案的时间比例如下:
但由于机房、讨论室和自学室各自存在时间限制,我们在考试前能够预约的时间如下:
每花费一个小时在不同科目上,我们能够提升不同的平均GPA:
假设我们起点较低,这轮复习不会使任何科目的GPA达到满分,但我们希望尽可能提高GPA,那么应该如何分配剩余的复习时间呢?
我们发现这个问题实际上也是生产安排问题的一类,我们并不需要对模型进行任何修改,只需修改数据集即可。我们建立一个数据文件2_2_StudyTime.dzn,放入相应的课程、用时和收益:
在命令行执行minizinc--solver CBC 2_2_AbstractProductionModel.mzn 2_2_StudyTime.dzn进行求解:
得到结果:
因此,在建模过程中,通常建议对模型进行抽象化,将模型文件和数据文件分离。
在上面的程序中,我们使用了如下语句:
这条语句定义了一个二维数组consumption,其维度分别为WEAPONS和RESOURCES。
数组是一种常用的数据结构,我们将在后面介绍数组的相关知识:
数组可以是一维或多维的,在声明时使用如下格式:
array[index_set1, index_set2,...] of
数组下标的集合可以是枚举类型或者是某个集合表达式。
数组内的元素可以是除了数组以外的任何内置类型,也就是说MiniZinc不支持数组嵌套。
一维数组
对于一维数组,我们可以用列表的方式进行初始化,这与其他大多数语言类似,例如以下这些示例:
二维数组
MiniZinc中二维数组的初始化具有特定的语法:
一个示例就是我们上面定义的二维数组:
特别需要注意的是结尾,不要忘记|符号。
内置函数初始化任意维度数组
对于任何维度k不大于6的数组,我们都可以使用内置函数arraykd进行初始化,例如:
分别等价于我们上面的一维和二维数组初始化方式。
除了使用内置函数或列表形式进行初始化外,MiniZinc还提供了类似于Python的推导式方式对数组进行初始化。其语法为:
[|,,...]
在推导过程中,我们也可以进行一些检验:
[|,,..., where]
例如:
会生成以下两个数组
注意,由于我们事先不知道数组的长度,我们使用了array[int]来让求解器自行推导数组长度。
相应的,集合也可以使用推导式的方式生成,唯一的不同在于用{}替换[]。
由于在列表推导式的应用中,我们可能产生不知道列表下标范围的情况。此时,我们可以使用内置函数index_set来获取一维数组的下标范围。
例如:
这里,我们的k将得到范围1..12。
对于多维数组,我们需要指定数组的总维度k和想要获取的具体维度m的下标。
MiniZinc中内置了一系列函数index_set_of来帮助我们获取下标,其中k<=6。例如,我们想获取二维数组第二个维度的下标,我们就可以使用函数index_set_2of2。
MiniZinc内置了一系列函数 index_set_of来协助我们获取索引,其中 k≤6。例如,若要获取二维数组第二个维度的索引,我们可以使用函数 index_set_2of2。
MiniZinc支持的基本整数运算包括加减乘除等四则运算:+,-,, div。对于浮点数,除法以/表示。
请注意,这里的整数除法使用的是 div而非/,/在MiniZinc中专门指代浮点数的除法,处理整数时可能会出现意想不到的结果。
除此之外,还有我们常用的取余运算 mod,整数的取余运算 a mod b会得到与a同号的结果,也就是说,一定存在 a= b(a div b)+(a mod b)。
以下是一些在MiniZinc中常用的内置运算函数。
对于数字:
对于数组:
离线编程工业机器人有哪些注意事项
离线编程指的是在计算机上预先构建和优化工业机器人的程序,随后将这些程序导入机器人控制器中,以便机器人在实际生产中自动执行任务。在执行工业机器人的离线编程时,以下是一些需要注意的事项:
1.明确工作环境:在离线编程前,了解机器人将要工作的实际环境至关重要。考虑到机器人在作业中可能遇到的障碍、安全风险和工作空间限制,以便在编程过程中做出恰当的决策。
2.确定任务要求:明确机器人需要完成的任务,包括运动轨迹、工具路径和操作顺序等。确保在离线编程中将所有任务要求准确建模。
3.选择恰当的离线编程软件:选择适用于你机器人品牌和型号的离线编程软件。这些软件通常提供虚拟仿真环境,以便在计算机上模拟机器人的运动和任务。
4.建立恰当的模型:在离线编程软件中构建真实的机器人模型,包括其构型、工具和传感器。确保模型的准确性,以避免在实际应用中出现故障。
5.优化路径规划:利用离线编程软件中的路径规划工具,优化机器人的运动轨迹,以提高任务的效率和速度。
6.考虑碰撞检测:在进行离线编程时,确保机器人的运动路径不会导致与其他设备、工件或工作环境发生碰撞。使用碰撞检测功能帮助避免潜在的冲突。
7.安全考量:离线编程时务必牢记安全因素。确保机器人在执行任务时不会对操作员或其他设备造成伤害。使用虚拟环境进行安全分析是一个好方法。
8.实地验证:尽管离线编程可以显著减少机器人在实际生产中的停机时间,但在将程序导入机器人控制器之前,最好在实地进行验证。通过实地测试,确保机器人在实际生产环境中能够按预期执行任务。
9.持续优化:离线编程不是一次性任务,随着生产需求的变化和改进的机会,需要持续优化机器人的程序和任务。
总的来说,离线编程是一个关键的步骤,有助于提升机器人的生产效率和降低停机时间,但在进行离线编程时,需要仔细考虑任务需求、安全因素和实际生产环境,以确保机器人能够安全、高效地执行任务。