返回 登录
0

斗牛太危险,来斗HEXA吧

阅读6732

图片描述

视频观看地址:https://v.qq.com/x/page/e05452jnlwo.html

第一次看到HEXA这个小家伙的时候,就被这个六只脚的小家伙萌翻了。开箱照?不好意思,实在太激动,忘拍了。

开箱之后迫不及待的根据说明书提示,下了配套的app,玩了一会,这小家伙动起来实在太令人欢乐了。但光是玩儿还不过瘾,我觉得她还可以更好玩一些,哈哈!

官网的SDK和文档已经放出来了,看了下文档,支持功能和接口还是挺全的。看过电视节目里,看过西班牙的斗牛表演,热血沸腾啊,但真要自己去尝试,还是不敢的,我可不想被两根牛角顶着到处逛。但我们是技术派嘛,不敢斗牛斗HEXA这个萌货还是可以的吧,哈哈!

当然HEXA是牛,为了让HEXA成为一头勇猛的西班牙公牛,第一步需要让HEXA可以识别红色和找到红色。第二步就是需要让HEXA一旦发现红色,就勇猛的冲过去。开始吧!

这个小程序的pseudo-code如下:

  • 转动HEXA的头部,每转动一定角度,HEXA就拍一张照片
  • 判断照片里面是否有红色
  • 如果有红色,HEXA停止转动头部,并向前跑去
  • 如果没有红色,则转动头部继续寻找
  • 跑动过程中继续拍照,如果还有红色则继续前进
  • 跑动过程中如果没有红色里则停下寻找红色

我用了3个go routinue分别控制头部转动、向前走以及在向前走的时候检测前方红色是否还在。转动头部时,头部移动以及红色的检测在同一个routinue里面实现了。这几个routinue由几个状态分别控制。为了快速能用,这儿实现得不怎么优雅。这里建议大家看下官网给出的几个简单的示例。虽然简单,但对快速上手HEXA的开发很有帮助。

https://github.com/vincross/hexa-example-skills

具体的开发过程如下:

1. 移动头部

HEXA身体控制接口由hexabody package提供,使用前需要调用Start()函数,结束后调用Stop()。Start调用后就可以愉快的调用其他接口控制HEXA了。MoveHead可以控制头部的移动,参数degree控制移动的角度,duration控制移动的时间。这里吐槽下,接口文档说的是将头部移动到指定的角度,但实际上是以当前角度为参考,顺时钟移动值为‘degree’的角度。duration表示需要多长时间做完这个动作,也就是决定移动的速度,但我在文档中找了一会没有找到HEXA到底能把头甩多快…

func (skill *ScanRed) searchRed() {
    for skill.status {
        if skill.round {
            direction := hexabody.Direction()
            direction += 30
            hexabody.MoveHead(direction, 200)
            skill.checkRedLightDistrict()
            time.Sleep(time.Millisecond * 100)
        }
        time.Sleep(time.Millisecond * 200)
    }
}

2. 检查图片是否为红色

多媒体接口由Media接口提供,同hexabody 一样,需要调用Start和Stop。我们需要用到的是SnapshotRGBA这个接口,它返回一张RGBA Image。我太想看到最终的效果是怎样的,所以用了一种最简单的方式判断这张图片是否有红色。先从图片中取出中间那一部分,然后判断里面有多少个像素,大于一个阈值则认为是红色,这种方法比较笨,效率也不高,但可用,哈哈!对了,这里用到了goalng的image package。这部分代码如下:

func isRed() bool {
    thresHold := 200
    subRed := 0
    srcImg := media.SnapshotRGBA()
    srcBounds := srcImg.Bounds()
    m := image.NewRGBA(srcBounds)
    ptX := (srcBounds.Size().X * 1) / 10
    ptY := (srcBounds.Size().Y * 1) / 10
    draw.Draw(m, srcImg.Bounds(), srcImg, image.Pt(ptX, ptY), draw.Src)
    subBounds := image.Rect(srcBounds.Min.X/2, srcBounds.Min.Y/2, srcBounds.Max.X/2, srcBounds.Max.Y/2)
    newImg := m.SubImage(subBounds)
    wight := newImg.Bounds().Size().X
    hight := newImg.Bounds().Size().Y
    for w := 0; w < wight; w++ {
        for h := 0; h < hight; h++ {
            r, g, b, _ := newImg.At(w, h).RGBA()
            r = r >> 8
            g = g >> 8
            b = b >> 8
            c := (int(r) - int(g)) + (int(r) - int(b))
            if c > thresHold {
                subRed++
            }
        }
    }
    log.Info.Printf("%d %d", subRed, wight*hight)
    if subRed > (wight*hight)/200 {
        return true
    }
    return false
}

3. 往前走

当HEXA检测到前方有红色时,HEXA会停止转动头部,并且朝红色方向走去。这儿调用了heaxbody的Walk函数,很明显这个函数的左右就是让HEXA往你指定的方向走。接着吐槽下,文档说在给定的时间里往前走动一步(frame),但里面没有说明一步(frame)有多远…
好啦,贴代码。

func (skill *ScanRed) goToRed() {
    for skill.status {
        if skill.run {
            log.Info.Printf("RUN...")
            hexabody.Walk(hexabody.Direction(), 100)
        }
        else {
            time.Sleep(time.Millisecond * 200)
        }
    }
}

我大概花了一天的时间来做这个小程序,时间主要花在熟悉文档以及判断图片是否是红色的功能上面。这儿给开发SDK的大牛们点个赞,虽然上面有吐槽,但总的来说文档结构已经内容都还是非常清晰明了,非常容易看懂。

我勇猛的HEXA版“公牛”当然跑起来了。额,其实跟勇猛挨不上边,跑起来感觉她萌萌得笨笨的。程序还有不少的优化空间,之后可能会优化一下。如果优化了,我会接着发一个帖子并且附上源码的。但我还是想先试试HEXA的其他功能。嗯,这段时间周末有得玩啦,感觉在宅男的路上越走越远了,囧…

评论