赞
踩
官方链接: 2023华为软件精英挑战赛——普朗克计划 (huaweicloud.com)
官方赛题介绍: 2023华为软件精英挑战赛初赛赛题及相关材料发布_2023华为软件精英挑战赛_华为云论坛 (huaweicloud.com)
比赛场景如图所示
简单来说,在一张50m*50m的地图上,分布着许多固定的工作台和可以移动的机器人(4个)。机器人通过前进,后退,旋转等操作进行移动,当移动到工作台后,可以购买产品、出售产品,此外在携带产品时可以随时销毁产品。
开始时,系统会基于一笔初始资金(20万),通过调度机器人在各个工作台之间进行购买、出售产品,从而赚取差价获利。不同的产品购买和出售价格不同,可获取的利润也不同,且有的产品必须基于其他产品作为原料才可以生产。各类产品的购买和出售价格以及合成路线如下表所示:
此外,物品的价值会随着时间的流逝和机器人与其他机器人或墙壁的碰撞而减少,因此应当以尽快的速度卖出物品并尽量减少碰撞。
本次比赛最大的两点就是把判题器直接提供给了选手下载,给了选手更方便灵活的本机测试机会。
判题器与用户之间通过标准输入输出进行交互,每一帧判题器都会向选手程序更新地图和机器人状态,选手需要根据当前状态,决定每个机器人的输入输出。
首先,首次参赛,三位队友也是首次组队的情况下(实验室还有一堆其他任务,以及有人生病等一系列客观原因),能走到决赛也属实不易,但这其中也多多少少有些运气因素。
经验教训方面,主要总结了一下几点:
整体上可以分为机器人的运动和决策两大部分。运动主要包括机器人的移动、路径规划、避障和死锁解除等功能。决策需要协调不同机器人的买卖方案,最大化利润。
我们整体采用面向对象的思路,将划分为机器人、工作台和整体决策类,之后又根据需求衍生出放置类型无关函数的tools类,和用于动态调参的network类。
由于初赛时尝试使用向量化加速,因此实际上将所有机器人或工作台都作为了一个二维矩阵,每个机器人或工作台是其中的一行,并定义了一系列静态变量以便于索引。但实际体验下来效率没有得到提升,反而因为频繁的int和float转化拖慢的执行速度,于是代码在复赛时用传统的面向对象方式进行了重构。
除了官方判题器提供的交互信息外,机器人类还包含以下成员变量。
status: 机器人状态,机器人行为决策的基础,具体包含以下几个状态
状态转移图如下,考虑到机器人等待购买和出售时有可能由于运动控制或其他机器人碰撞等原因超出工作台的可交互范围,因此等待状态有可能重新切换到移动状态。
target: 机器人当前目标工作台,运动控制使用
target_buy:机器人要前往购买物品的工作台
target_sell:机器人要前往出售物品的工作台
除了官方判题器提供的交互信息外,工作台类还包含以下成员变量。
控制类是本代码的核心部分,我们将所有的控制和决策相关内容都在控制类中实现。
成员变量主要包括一些决策参数和运动模型参数具体将在下一节展开。
核心方法包括:
如果机器人只有一个的话,可以建模为旅行商问题,但四个机器人的情况实在过于复杂,因此只实现了一个简单的贪心算法,后续又在这个的基础上添加了一些参数优化。
我们总是选择性价比最高的认为执行,性价比的定义如下:
性价比(ratio)=(售价(
V
s
e
l
l
V_{sell}
Vsell)*价值系数(
R
a
t
e
t
i
m
e
Rate_{time}
Ratetime)-进价(
V
b
u
y
V_{buy}
Vbuy))/总帧数(F)
r
a
t
i
o
=
V
s
e
l
l
∗
R
a
t
e
t
i
m
e
−
V
b
u
y
F
ratio=\frac{V_{sell}*Rate_{time}-V_{buy}}{F}
ratio=FVsell∗Ratetime−Vbuy
对于一个买或者卖的行为,所需帧数与移动时间和物品准备时间有关。
进一步解释,买的时间就是移动到对应工作台和工作台生产时间的最大值,而买的过程中,卖的工作台也在生产,所以卖的工作台的等待时间应当减去买的过程的这段时间
F
b
u
y
=
m
a
x
(
f
a
,
b
m
o
v
e
,
f
a
,
b
w
a
i
t
)
F_{buy}=max(f^{move}_{a,b},f^{wait}_{a,b})
Fbuy=max(fa,bmove,fa,bwait)
F s e l l = m a x ( f b , c m o v e , ( f b , c w a i t − F b u y ) ) F_{sell}=max(f^{move}_{b,c},(f^{wait}_{b,c}-F_{buy})) Fsell=max(fb,cmove,(fb,cwait−Fbuy))
动作的总帧数: F = F b u y + F s e l l F=F_{buy}+F_{sell} F=Fbuy+Fsell
我们的模型中没有考虑碰撞因素,因为这是不可预知的。时间价值系数如下。
R
a
t
e
t
i
m
e
=
{
[
1
−
1
−
(
1
−
F
s
e
l
l
9000
)
2
]
∗
(
1
−
0.8
)
+
0.8
F
s
e
l
l
<
9000
0.8
F
s
e
l
l
>
9000
Rate_{time}=\left\{
关于移动时间的计算,我们选择使用距离去进行一次拟合。即:
f
a
,
b
m
o
v
e
≈
d
i
s
a
,
b
∗
θ
f^{move}_{a,b} \approx dis_{a,b}*\theta
fa,bmove≈disa,b∗θ(后面也试过理论上更好的拟合但结果更差了)
其中 θ \theta θ是我们的可调参数。
经过观察,我们又在后序补充了以下优化方案:
最终的决策参数如下:
# 控制参数
MOVE_SPEED = 1 / 4.15 * 50 # 估算移动时间 4.1 4.2 相同
MAX_WAIT = 3.14 * 50 # 最大等待时间 3.15
SELL_WEIGHT = 1.45 # 优先卖给格子被部分占用的 1.43 1.45 相同
SELL_DEBUFF = 0.8 # 非 7 卖给89的惩罚
CONSERVATIVE = 1+1/MOVE_SPEED*4 # 保守程度 最后时刻要不要操作 比4.3和3.8好
BUY_WEIGHT = [1]*4+[1]*3+[1] # 购买优先级,优先购买高级商品 比0.9和1.1好
运动模型最开始使用了人工势场,即机器人和目标之间存在吸引力,机器人之间存在斥力,通过调节这些参数实现避撞。这一方案在练习赛阶段表现的很好,但到了正式赛阶段,因为地图的设计使得机器人之间会有完全相对前进的情况,机器人之间的斥力无法是机器人避免碰撞,因而导致了对撞死锁的情况。人工势场在对撞时表现不好的原因如下图所示。
为此,我们的运动模型中添加了碰撞预测方案,当预测要发生碰撞时,会根据条件指定一个机器人避让
另外,运动模型中也有一些优化方案:
经过我们的观察发现,不同地图的最优参数是不同的。虽然规则里没有禁止,但我们总觉得直接硬编码地图调参可能会被当成作弊。(实际好多其他队伍都有针对性调参,甚至出现了直接输出序列的情况,万幸我们所在赛区没有那么卷,否则我们可能初赛就无法出线了)
为此我们想到了使用神经网络调参的方案。在初版的方案中,我们直接把整张地图当成了输入,输出是策略的6个参数。但官方禁止上传非python文件,于是我们使用了将权重文件直接写入python文件的放置。
由于python的格式和json的格式完全兼容,我们只需要将list json化写入py文件,读取时之间import即可。
def save(self, weight_path='src/weight.py'): file = open(weight_path, 'w') file.write(f'W1 = {json.dumps(self.W1.tolist())}\n') file.write(f'W2 = {json.dumps(self.W2.tolist())}\n') file.write(f'W3 = {json.dumps(self.W3.tolist())}\n') file.write(f'b1 = {json.dumps(self.b1.tolist())}\n') file.write(f'b2 = {json.dumps(self.b2.tolist())}\n') file.write(f'b3 = {json.dumps(self.b3.tolist())}\n') file.close() def weight_loader(self): import weight as weights self.W1 = weights.W1 self.W2 = weights.W2 self.W3 = weights.W3 self.b1 = weights.b1 self.b2 = weights.b2 self.b3 = weights.b3
然而,官方还限制了上传的文件大小,将整张地图作为输入会导致参数过多,超出限制。于是我们将输入调整为[x, y, 类型]*54。其中xy是坐标,类型包括1-9号工作台和机器人。因为工作台最多50个,机器人固定6个,如果工作台数目不足50个,则剩余位置用全0填充。
这样输入的大小从100*100下降到了3*24,满足了要求。
数据集方面,我们又根据官方规则生成了100张地图,并使用暴力搜索找到了每张地图的最优参数。
但也存在问题,这一方案在训练赛阶段表现不错,但由于每次修改运动模型都需要重新寻找最优参数,故由于时间原因,在正式赛阶段未能实际应用。
之后到了复赛由于赛题发生了巨大改变,虽然添加了不公开地图,减少了硬编码地图针对性调参的操作空间,但也增加了地图的复杂度,使得神经网络参数量飙升,导致本方案被完全废弃。
以下是初赛阶段练习赛和正式赛阶段,最高提交分数的回放。其中练习赛最高得分为2878573,正式赛为2726069。
ps: 初赛代码相对简陋且不规范,复赛对代码进行了重构,并且添加了更多设计,敬请期待。
求star!!!
gitee:
codecraft2023: 华为软挑赛2023初赛 (gitee.com)
github:
ningfenger/huaweicc2023_preliminary_contest: 华为软挑赛2023初赛,锐总把我们带飞队开源 (github.com)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。