这次 HEXA 不是尬舞或逗宠物那么简单了,而是学会追逐眼前的一抹红了。(孩子大了,不省心呐)。开发者利用拍照功能确认红色,并发起千里追凶。现在这个功能已经成功上架到 Vincross Skill Store,工作人员亲测任何在 HEXA 距离 1 米以内的红色物体都会引起它的亲密追逐。
by @Heinegan 第一次看到这个小家伙的时候,就被这个六只脚的小家伙萌翻了。开箱照?实在太激动,忘拍了。现在有点后悔没记录下那一刻。 我很快下载了 App,并且玩了一会儿。它的动作太逗了,天知道我摆弄了多久。但光是玩儿还不过瘾,我觉得它还可以更好玩一些!
大概想了一刻钟我都没想出还能怎么玩。于是我改变了想法——也许我不该执着于 HEXA 能干嘛,而应该专注于我能让它干什么。 由于我之前做过一些关于计算机视觉的工作,所以我决定帮 HEXA 醒醒目。另外,我喜欢西班牙的斗牛表演,我是 coder 外表斗牛士的心,不过真要自己去尝试我这个体格肯定不行。但我们是技术派嘛,HEXA 可以当我的公牛!我可以看着它追逐红布但不会伤到我。
于是我去官网查了文档和 SDK 和文档,必须说 Vincross 干得不错。在找到了我需要的信息后,我就很快去投入创作了。
为了让 HEXA 变成公牛,我必须:
教 HEXA 的图像识别系统可以识别和找到红色。
让 HEXA 一旦发现红色,就勇猛地冲过去。
Pseudo-code 如下:
我用了 3 个 go routinue 分别控制头部转动、向前走以及监测红色。转动头部时,头部移动以及红色的检测在同一个 routinue 里面实现了。然后我将这些 go routinue 映射到几个状态。作为一个 hacking demo,这儿实现得不怎么优雅。这里建议大家看下官网给出的几个简单的示例,对快速上手 HEXA 的开发很有帮助。 https://github.com/vincross/hexa-example-skills
以下是具体的开发过程,欢迎分享你的意见。
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)
}
}
多媒体接口由 Media 接口提供,同 hexabody 一样,需要调用 Start 和 Stop。我们需要用到的是 SnapshotRGBA 这个接口,它返回一张 RGBA Image。我太想看到最终的效果是怎样的,所以用了一种最简单的方式判断这张图片是否有红色。先从图片中取出中间那一部分,然后判断里面有多少个像素,大于一个阈值则认为是红色,这种方法比较笨,效率也不高,但可用。对了,这里用到了 goalng 的 image package。这部分代码如下:
newImg := m.SubImage(subBounds)
width := newImg.Bounds().Size().X
height := newImg.Bounds().Size().Y
for w := 0; w < width; w++ {
for h := 0; h < height; 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, width*height)
if subRed > (width*height)/200 {
return true
}
return false
}
当 HEXA 检测到前方有红色时,HEXA 会停止转动头部,并且朝红色方向走去。这儿调用了 heaxbody 的 Walk 函数,很明显这个函数的方向是需要你定义的。接着吐槽下,文档里有 Moving forward one 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)
}
}
}
我大概花了一天的时间来做这个 skill,时间主要花在熟悉文档以及判断红色的算法上面。虽然上面有吐槽,但总的来说文档结构已经内容都还是非常清晰明了,非常容易看懂。
现在我的 HEXA 已经变身勇猛的“公牛”了。额,其实也许并不很猛,说它萌还差不多。接下来我会继续更新这个帖子的。但也许我还想先试试为 HEXA 发展出其他功能。
这个帖子的内容来自 Vincross (也就是我们自己)的开发者论坛。原贴是英文的,我们觉得很有意思,所以把帖子翻译成中文。 开发使用的是 Golang 语言。
文中提到的 frame 缺乏说明和示例问题,Documentation 里已经改正了~
特别说明:Vincross 拥有使用这个帖子的转载和宣传的权力。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.