斗牛太危险,来斗HEXA吧
视频观看地址: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的其他功能。嗯,这段时间周末有得玩啦,感觉在宅男的路上越走越远了,囧…
推荐阅读