前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >创建自动滑雪模拟器

创建自动滑雪模拟器

作者头像
代码医生工作室
发布2019-07-16 15:44:49
6430
发布2019-07-16 15:44:49
举报
文章被收录于专栏:相约机器人相约机器人

作者 | Erekle Shishniashvili

来源 | Medium

编辑 | 代码医生团队

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

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

http://www.snowthegame.com/

代码管道

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

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

获取输入

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

代码语言:javascript
复制
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来操作键盘键。键盘上的每个键都有自己的十六进制表示(十六进制)。使用这些十六进制值来访问键盘上的键。

代码语言:javascript
复制
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。在其中定义以下参数

代码语言:javascript
复制
self.vel = 0
self.acc = 0
 
self.tuck = 0
self.forward_time = 0

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

现在讨论一下运动步骤

代码语言:javascript
复制
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]使用正常转向。

代码语言:javascript
复制
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/

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 相约机器人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档