创建自动滑雪模拟器

作者 | Erekle Shishniashvili

来源 | Medium

编辑 | 代码医生团队

关于自治代理,它们的应用和改进,有很多研究。所以在考虑自动驾驶汽车,它可以在没有任何碰撞的情况下在雪地上行驶。不幸的是,没有足够的资源和时间来构建一个真正的机器人,其中有特殊的硬件可以在雪地上行驶。所以决定在模拟器上运行我的实验。

为这个项目选择的模拟器是名为SNOW的正常游戏。该游戏是免费的(有免费地图以及必须支付的访问权限)。游戏玩法非常简单。有男人可以控制,可以玩两个,使用滑雪板和滑雪。对于这个项目,选择滑雪。这就是基于游戏的模拟器的样子。

http://www.snowthegame.com/

代码管道

在这篇文章中,将尝试解释这个自动移动滑雪者的端到端管道。可以在Github上看到整个项目的代码。

https://github.com/Ersho/Autonomous-Skiing

获取输入

首先,想解释如何从游戏中获取图像。由于SNOW不是真正的模拟器,因此没有特殊的代码可以从游戏中获取图像流,以获得必须使用python抓住屏幕并切割整个屏幕所在的部分。这是抓取图像和获取输入的代码。

def get_screen(*args):
    
    h_start, h_end = args[0], args[1]
    w_start, w_end = args[2], args[3]
    
    screen_pil = np.array(ImageGrab.grab(bbox = (h_start,
                                                 h_end,
                                                 w_start,
                                                 w_end,
                                                 )))
    
return cv2.cvtColor(screen_pil, cv2.COLOR_BGR2RGB)

这从游戏中获取的框架。讨论一下如何使用python来操作键盘键。键盘上的每个键都有自己的十六进制表示(十六进制)。使用这些十六进制值来访问键盘上的键。

key_dict = {‘A’ : 0x1E, ‘B’ : 0x30, ‘W’ : 0x11,
 ‘S’ : 0x1F, ‘LSHIFT’ : 0x2A, ‘D’ : 0x20}

为键及其十六进制值设置字典后。为按下和释放键的事件编写函数。这些函数位于“get_input.py”中。

现在已准备好开始处理感知和决策步骤。

知觉

首先,想简要概述计算机视觉部分,它使AI代理有可能了解它周围的环境。

幸运的是,雪是一个非常好的自动驾驶车辆环境,因为它是完全白色的,必须从树木和障碍物中确定它,这些树木和障碍物大多是黑色或比灰色更暗。为了区分雪和障碍物,采用像素的阈值并实现函数,该函数接收输入图像帧并返回具有相同大小的二进制图像,其中白色是可导航区域,黑色是所有障碍物。

接下来,裁剪感兴趣的区域。这意味着在分析图像时,不需要整个屏幕,只对面前发生的事情感兴趣,以便滑雪者可以根据他前面的视觉移动。基于这一事实,只裁剪了一小部分图像。这种技术提供了一个更小的图像,计算效率更高,只获得感兴趣的特定区域。如果从人类的角度来看,对于导航,只能使用所拥有的景象。在面前避开障碍物,几乎不会对距离你100米的树木产生兴趣。上面应用了相同的原理。

使用黑色线条,确定了感兴趣的区域

在得到裁剪区域后,进行透视变换以获得裁剪区域的鸟瞰图。这里看起来如何使用2张图片。

此图片取自Udacity Robotics软件工程师课程

接下来,参考滑雪者位置位于图像的中心底部来计算像素位置。

然后作为最后一步,计算导航角度,从[-15,15]剪辑。0表示在没有转向的情况下向前移动,前方没有任何障碍物。15意味着向右转,向左转15度。

这就是最终的视觉表现形式。

左:游戏的原始屏幕,右上方:阈值图像,右下方:鸟类视图

操作

已经完成了感知部分的实施,了解虚拟滑雪者所面临的环境。下一步是决策过程。这是非常重要的一步。想一想,在一瞬间可以在水平的场地上滑雪,没有任何障碍。在这种情况下,导航角始终为0,可以始终按下按钮W以向前移动并提高速度。但是在这个模拟器中按下W使得滑雪者使用杆子来加速并不断地发送垃圾邮件W看起来很荒谬。

相反,按下“LFShift”允许滑雪者进入折叠位置并在增加速度的同时自由移动。如果想要一个好的自主代理人,应该在决策步骤背后写下逻辑。为了有效地解决这些类型的问题,创建了类决策。在这种情况下,有诸如转向,加速,速度等参数。因此首先讨论想要实现的内容。

折叠位置

(按W)。如果这种情况持续一段时间(假设连续10帧),就开始进入褶皱状态(按下左移)。当角度在(-7,7)范围内时,继续在折叠位置移动。但是如果得到的航行角度低于或高于之前的值((-15,-7),(7,15)),会根据角度转向并慢慢停止滑雪者并重置状态,这意味着减少后开始决定的速度从开始,使用带有极点的运动。如果没有达到10个连续帧,由于导航角度小于或高于阈值而处于折叠模式,继续在正常状态下移动。这更安全,更容易调节。

注意在处于折叠状态时速度增加很多,在正常情况下fps范围从1-5,这并不多。当滑雪者速度非常快且fps下降到1时,决策步骤无法跟上该男子的运动,因此它会崩溃。

实施决策过程

在案例中,决策是最有趣和最具挑战性的任务。特别是想要从倾斜状态平稳过渡到向前移动时。要做到这一点,必须跟踪我们的人。因此创建了一个名为Decision的Object。在其中定义以下参数

self.vel = 0
self.acc = 0
 
self.tuck = 0
self.forward_time = 0

前两行用于创建变量,用于跟踪速度和加速度。在这种情况下,实际上并没有跟踪对象的实际速度和加速度,而是将它们附加一些值,大致知道速度有多快。这提供了物体速度和加速度的基本概念。最后两个变量tuck和forward_time用于更改对象的状态。如果在连续10帧之后没有大的角度变化,就会改变滑雪者的状态,然后进入低位。名称为tuck的变量定义状态,当tuck为0时,使用极点向前移动,但是当它为2时,意味着滑雪者处于折叠位置。迭代“forward_time”来计算角度未改变的连续帧数。

现在讨论一下运动步骤

def next_move(self, angle):
 
  if self.tuck == 1:
    self.tuck = 2
    self.enter_tuck_state()
 
  if angle >= -0.2 and angle <= 0.2:
 
    if self.tuck != 2:
      self.accelerate(angle)
 
    if self.forward_time > 20:
      self.tuck = 1
    self.forward_time += 1
  else:
    self.steer(angle)
 
    if self.tuck == 2:
      self.tuck = 0
      self.forward_time = 0
      self.end_tuck_state()
      self.stop()
 
  if self.vel > 5:
    self.stop()
    self.vel -= 2
self.acc = 0

一步一步地指导

首先检查褶皱状态,如果它为零,保持杆状态(滑雪者正在使用杆)。接下来,检查变量“angle”,它是上面给出的函数参数,在感知步骤中计算。如果角度接近0,则意味着正在移动而未检测到前方的任何障碍物。如果发生这种情况,只是附加到前进时间。如果角度仍然接近0,并且前进时间超过20,将进入状态“1”。当再次调用此函数时,如果将tuck状态设置为1,则进入tuck状态,滑雪者开始在折叠位置移动。如果角度不接近0,意味着前面有一些障碍,立即转向角度方向,然后检查褶皱状态,如果处于褶皱状态,离开状态,重置前进时间,并且减慢滑雪者,通过调用函数“停止”。减速过程至关重要,这样滑雪者就不会获得太快的速度。随着高速控制滑雪者变得更加困难,特别是有1-5的低fps时。

最后,已经到了最后的if语句。记住有变量“vel”来跟踪速度。在每个函数调用上检查此速度。如果它变得大于某个值(在例子中为5),会减慢滑雪者的速度。

这是滑雪者的端到端决策步骤,效果非常好,这是一个小演示。

小演示

注意

对于转向,将看到2个函数“faster_steer”和正常的“转向”。在游戏SNOW中,如果按下左移然后转向,它将向右或向左移动得更快。实际上考虑了这一步。并将转向角分为2级,如果它在[-15,-7],[7,15]之间,使用快速转向,从[-7,0.5],[0.5,7]使用正常转向。

def steer(self, angle):
  if angle > 7:
   self.go_faster_right(angle)
  elif angle < -7:
   self.go_faster_left(angle)
  elif angle > 0.5:
    self.go_right(angle)
  elif angle < -0.5:
self.go_left(angle)

结论

在这篇文章中,解释了如何设法从正常的游戏SNOW,创建自动滑雪示例的模拟器。在这个项目上工作非常有趣,它有一些很大的挑战,比如不要太快,机动快,检测前方的每一个障碍等等。该项目旨在用于实验和学习目的。

进一步改进

  • 通过在任何其他方法上使用2帧的差异来检测实际速度。
  • 检测上升。
  • 跳跃时的机动。

参考

https://www.udacity.com/course/robotics-software-engineer--nd209

https://pythonprogramming.net/game-frames-open-cv-python-plays-gta-v/

http://www.snowthegame.com/

原文发布于微信公众号 - 相约机器人(xiangyuejiqiren)

原文发表时间:2019-07-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券