本网页已闲置超过3分钟,按键盘任意键或点击空白处,即可回到网页

基于 Arduino Leonardo 的星露谷物语村民识别和礼物偏好机器人

发布时间:2021-11-16
分享到:

基于 Arduino Leonardo 的星露谷物语村民识别和礼物偏好机器人

发布时间:2021-11-16
分享到:

通过 OpenCV,在 LattePanda Alpha 上玩 Stardew Valley 时,识别村民以显示他们的生日、爱、喜欢和讨厌。

在这个项目中,我想专注于一个新兴领域,以改善我们在玩和探索视频游戏时的整体体验,即视频游戏中的计算机视觉实现。在使用计算机视觉来改进视频游戏时,我们有几乎无穷无尽的选择,从用于第一人称射击 (FPS) 游戏的瞄准辅助机器人到用于角色扮演 (RPG) 游戏的挖掘机器人。尽管电子游戏中的计算机视觉实现通常用于竞争性在线游戏,但我决定在我最喜欢的电子游戏之一 - 星露谷中使用计算机视觉。

在《星露谷物语》中,虽然我已经玩过不止一次,但我仍然需要仔细检查指南以记住村民的礼物偏好,同时试图增加我与某些角色的友谊。因此,我决定使用计算机视觉来识别村民并在玩 Stardew Valley 时自动显示他们的生日和礼物偏好(喜欢、喜欢和讨厌)。

由于我希望这个项目尽可能紧凑,所以我选择使用 LattePanda Alpha 864s,这是一款高性能 SBC(单板计算机),具有嵌入式 Arduino Leonardo。为了在探索 Stardew Valley 世界时识别村民,我部署了 OpenCV 模板匹配。在成功检测村民后,为了在不中断游戏体验或性能的情况下显示他们的礼物偏好和生日,我在 LattePanda Alpha 上使用了嵌入式 Arduino Leonardo。Arduino Leonardo 在 3.5" 320x480 TFT LCD 触摸屏 (ILI9488) 上打印检测到的村民信息,并通过蜂鸣器和 5mm 绿色 LED 通知玩家。

因为平时都在努力增加对下面这八个字符的友谊,所以我用 OpenCV 模板匹配来识别它们:

  • 阿比盖尔
  • 艾米丽
  • 海莉
  • 一分钱
  • 亚历克斯
  • 哈维
  • 山姆

运行这个项目并获得准确的结果后,它消除了村民在玩星露谷时需要精明的天赋敏锐度。

第 1 步:使用 OpenCV 在玩 Stardew Valley 时识别村民
我决定在玩游戏时使用模板匹配来识别星露谷的村民。模板匹配是一种在较大(输入)图像中搜索和查找模板图像位置的方法。OpenCV 提供了一个内置函数 - cv.matchTemplate() - 用于部署模板匹配。它基本上是在输入图像上滑动模板图像(如在 2D 卷积中)并将输入图像的补丁与模板图像进行比较以找到重叠的补丁。当函数找到重叠的补丁时,它会保存比较结果。如果输入图像的大小为 (W x H),模板图像的大小为 (wxh),则输出(结果)图像的大小将为 (W - w + 1, H - h + 1)。

OpenCV 中实现了几种比较方法,每种比较方法都应用一个独特的公式来生成结果。您可以检查TemplateMatchModes以获取有关比较方法的更多信息,描述可用比较方法的公式。

我用 Python 开发了一个应用程序来识别 Stardew Valley 的村民并将他们的信息(礼物偏好和生日)发送到 Arduino Leonardo。如下图,该应用程序由一个文件夹和五个代码文件组成:

  • 主文件
  • 窗口框架.py
  • 视觉.py
  • arduino_serial_comm.py
  • 图片.py
  • /资产
  • -- 模板(裁剪的村民姿势)
  • -- 村民.json

我将在以下步骤中详细说明每个文件。

为了执行我的应用程序以成功识别村民,我在Thonny上安装了所需的模块。如果需要,您可以使用不同的 Python IDE。

第 1.1 步:从游戏截图中获取模板并定义村民偏好
首先,为了能够使用模板匹配来检测 Stardew Valley 中的村民,我需要为上面显示的列表中的每个村民(角色)创建模板。由于 Stardew Valley 中的角色有不同的姿势,我利用游戏中的截图来捕捉独特的角色姿势 - 坐着、向右转、向左转、向前等。 因为我在 LattePanda Alpha 上运行 Steam 上的 Stardew Valley,所以我把在 Steam 上按F12玩游戏时的屏幕截图。

然后,我从游戏中的屏幕截图中裁剪了角色姿势,以获得我的村民模板。

在为列表中的每个村民收集模板(裁剪的角色姿势)后,我将它们保存在资产文件夹中。然后,我创建了一个包含每个村民的模板路径的字典(character_pics)并将其存储在pics.py文件中。

最后,我创建了一个 JSON 文件(villagers.json)来存储我列表中每个村民的礼物偏好和生日:

  • 生日
  • 喜欢
  • 喜欢
  • 讨厌
  • 介绍

步骤 1.2:捕获实时游戏窗口数据(帧)
我需要在玩 Stardew Valley 时从游戏窗口中获取实时帧(屏幕截图),以使用模板匹配来识别我列表中的村民。尽管 Python 中有多种截屏方法,但我还是决定使用 Windows (Win32) API,因为它可以比其他方法更快地抓取游戏窗口的屏幕数据。此外,它可以在非全屏时捕获游戏窗口的帧(屏幕截图)。

我在windowframe.py文件中创建了一个名为captureWindowFrame的类,用于在玩Stardew Valley时获取实时游戏窗口数据(帧),并将原始帧数据转换为OpenCV处理。

def get_frames(self):
        # Get the frame (screenshot) data:
        wDC = win32gui.GetWindowDC(self.hwnd)
        dcObj = win32ui.CreateDCFromHandle(wDC)
        cDC = dcObj.CreateCompatibleDC()
        dataBitMap = win32ui.CreateBitmap()
        dataBitMap.CreateCompatibleBitmap(dcObj, self.w, self.h)
        cDC.SelectObject(dataBitMap)
        cDC.BitBlt((0, 0), (self.w, self.h), dcObj, (self.border_px, self.titlebar_px), win32con.SRCCOPY)
        # Convert the raw frame data for OpenCV:
        signedIntsArray = dataBitMap.GetBitmapBits(True)
        img = np.fromstring(signedIntsArray, dtype='uint8')
        img.shape = (self.h, self.w, 4)
        # Clear the frame data:
        dcObj.DeleteDC()
        cDC.DeleteDC()
        win32gui.ReleaseDC(self.hwnd, wDC)
        win32gui.DeleteObject(dataBitMap.GetHandle())
        # Drop the alpha channel and make the image C_CONTIGUOUS to avoid errors while running OpenCV:
        img = img[...,:3]
        img = np.ascontiguousarray(img)
        # Return:
        return img

步骤 1.3:将实时对象检测应用于捕获的游戏窗口框架
在引出实时游戏窗口帧(屏幕截图)并为 OpenCV 适当地格式化它们之后,我将模板匹配应用于捕获的帧,以便检测我列表中的八个村民(角色)之一:

  • 阿比盖尔
  • 艾米丽
  • 海莉
  • 一分钱
  • 亚历克斯
  • 哈维
  • 山姆

我在vision.py文件中创建了一个名为computerVision的类,通过模板匹配,使用 OpenCV 处理捕获的实时游戏窗口帧(屏幕截图)。该类在捕获的帧中搜索村民的所有给定姿势(模板),并为每个模板生成结果。此外,它通过在捕获的帧上绘制矩形或标记来呈现实时村民检测结果。

我使用了TM_CCOEFF_NORMED比较方法(上面解释过)来生成结果。由于模板匹配 ( cv.matchTemplate) 会为主(输入)图像中每个可能的匹配位置生成置信度分数结果,因此我定义了一个阈值 (0.65) 以获得多个对象(模板)的最准确结果。您可以在应用模板匹配时检查其他比较方法和不同的阈值。

步骤 1.4:将检测到的村民信息发送到 Arduino Leonardo
如上所示,我创建了一个 JSON 文件(villagers.json)来存储列表中每个村民的礼物偏好和生日。通过实时游戏窗框应用模板匹配成功检测村民后,我创建了一个名为功能arduino_serial_comm在arduino_serial_comm.py文件在送检测村民信息villagers.json通过串行文件到内置的Arduino的莱昂纳多端口沟通。

步骤 1.5:启动并运行实时村民识别
在创建了上面提到的所有代码文件后,我决定在main.py文件中组合所有必需的类和函数,以使我的代码更加连贯。

在detect_character函数中:

获得一个新的游戏窗口框架(截图)。

在捕获的游戏窗口框架中检测模板位置并生成结果。

获取输出图像和检测到的模板位置的中心点。

如果模板匹配识别出给定帧中的村民,则通过串行通信将检测到的村民信息发送到Arduino Leonardo。除非检测到的村民消失在帧中,否则不要再次发送数据以避免重复消息。

识别后,使用检测到的村民的名称保存输出图像 - cv.imwrite 。

显示输出图像 - cv.imshow 。

成功运行main.py文件后:

它会在 LattePanda 上打开一个名为 Op enCV 计算机视觉的新窗口,以将实时捕获的游戏窗口帧(屏幕截图)和检测结果显示为视频流。

然后,根据选择的结果类型,在检测到的模板位置周围绘制矩形(框)或在检测到的模板位置的中心点上绘制标记。

最后,它在外壳上显示给定游戏窗口框架中检测到的模板位置的数量,并通过串行通信将识别出的村民信息发送到 Arduino Leonardo。如上所述,它会发送一次数据,直到检测到的村民消失在帧中以避免重复消息。

第二步:用Arduino Leonardo显示传输的村民信息
完成上述所有步骤并通过串行通信将检测到的村民信息成功传输到 LattePanda Alpha 上的嵌入式 Arduino Leonardo 后,我使用了一个 3.5" 320x480 TFT LCD 触摸屏 (ILI9488) 来显示该信息。此外,我使用了蜂鸣器和当模板匹配识别出村民(角色)时,一个 5 毫米的绿色 LED 会通知玩家。

包括所需的库

 定义 320x480 TFT LCD 触摸屏 (ILI9488) 所需的引脚,并使用 Arduino Leonardo(SCK、MISO、MOSI)上的硬件 SPI 启动它。

初始化串行通信和 320x480 TFT LCD 触摸屏 (ILI9488)。

如果有传入的串口数据,保存到gameData字符串中,获取模板匹配检测到的村民信息。

获取检测到的村民信息后,在 TFT LCD 触摸屏上显示该信息(角色名称、生日、喜欢、喜欢、讨厌),并通过蜂鸣器和 5mm 绿色 LED 通知玩家。然后,清除gameData字符串。

在LattePanda Alpha 上的嵌入式 Arduino Leonardo 上运行此代码

模板匹配检测到一个村民(字符)并将检测到的村民信息通过串口通讯传输到Arduino Leonardo后,显示:

  • 角色名字
  • 生日
  • 喜欢
  • 喜欢
  • 讨厌

探索星露谷时每个村民的结果
完成我的项目后,我探索了 Stardew Valley 世界,通过模板匹配检测我列表中的村民(角色),并在 Arduino Leonardo 上显示他们的信息(生日和礼物偏好):

  • 阿比盖尔
  • 艾米丽
  • 海莉
  • 一分钱
  • 亚历克斯
  • 哈维
  • 山姆

如果您对此项目有任何想法、意见或问题,请在下方留言。

加入微信技术交流群

技术交流,职业进阶

关注与非网服务号

获取电子工程师福利

加入电路城 QQ 交流群

与技术大牛交朋友

讨论