2223|1

402

帖子

9

TA的资源

纯净的硅(初级)

楼主
 

【平头哥RVB2601创意应用开发】+火柴人播放器 [复制链接]

  本帖最后由 tobot 于 2022-6-9 10:08 编辑

火柴人播放器

项目背景

原本是想借助RVB-2601的语音功能做一个可以自动报价和讨价还价的价格指示牌,后来发现实在搞不定w800https功能,也就是说没法直接调用百度AI语音功能,进一步研究,试图采用本地语音,虽然使用已经经过高压缩的mp3格式,但转成文件后,常用的几句话就占满内存了(和网友们尝试的方法不一样,我这里扩内存是修改gcc_flash.ldSRAM而不是改SPIFLASH),于是改换了方向,试图做一个火柴人播放器。

作品简介

火柴人(或者叫做稻草人),是在上个世纪八九十年代,显示器只有单色,屏幕分辨率比较低的时候,出现的一种图形化游戏,人物使用简笔画风格用几根线条表示躯干四肢,用圆圈表示头部,对比只有命令行的输入输出,这种游戏很是惊艳,但是随着技术发展,视窗取代了命令行风格,火柴人游戏很快没落了。但近年来像素游戏又有反弹趋势,于是考虑在RVB-2601上实现火柴人播放功能,让火柴人在一个128*64的屏幕上跳舞也算是怀旧吧。

系统框图

 

各部分功能说明和解析 视频源

在我的构想中,视频源可以是来自摄像头或者取用某一段录制视频,在本项目里面,按时间,将视频截取成单图片,再分别处理。

AI处理

将视频源中的图像转换成人物坐标,姿态识别本来是AI的常见应用,本来准备使用全志R329实现的,但调来调去没弄成,最后只好采用百度AI实现,为了配合这个项目,新做了一个界面,如下图:

 

百度AI可以将人体的动作、五官等都分别识别出来,但在我们这个项目中,并不需要那么多变量,一共只取用14组坐标(头、颈、左肩、左肘、左腕、右肩、右肘、右腕、左髋、左膝、左踝、右髋、右膝、右踝),根据上述坐标画出头部(根据头部位置,以指定半径画圆)、躯干(颈部到两髋之间)、左右手臂(连接肩肘腕)、左右腿(连接髋膝踝),在电脑上为了叠加原图显示,放大了5倍,显示区的大小是640*320

将火柴人和原图叠加起来,可以看到基本能够匹配人物的肢体动作。

 

当然,也有不少出错的情况,比如:

 

在上图,MARiA手腕的位置就错了。

数组上传阿里云

阿里云的可视化开发非常方便,但目前暂时不需要这个功能,只需要提供一个转储的空间就行,阿里云是通过MQTT协议实现订阅和下发信息,因此最主要的是创建MQTT相关功能(地址、主题、用户、信息)。输入地址https://studio.iot.aliyun.com/createProject,基本是直接用鼠标就能实现配置,首先创建产品:

 

添加设备:

 

自定义功能,创建一个自定义的属性,在这里选择array,是为了配合RVB-2601的开发:

 

接下来就可以直接使用MQTT协议上传坐标数组了,对于阿里云来说,有一个现成的SDK工具,只需要pip install aliyun-iot-linkkit就可以很便捷的安装,但执行的时候可能会出现错误:

ImportError: cannot import name 'Iterable' from 'collections'

这和SDK工具无关,主要是python3.10改动了collections,找到文件

Python310\Lib\collections\__init__.py

增加

from collections.abc import *

就能修复这个错误。

如果不想用SDK工具,也可以自行配置MQTT参数:

1)首先是网址

在阿里云中,对于每个产品都有单独的网址,也就是

产品名.iot-as-mqtt.服务器.aliyuncs.com

产品名是刚才申请的,一般来说,服务器可以选择上海(cn-shanghai)。

2ClientId

Clientid需要将多个字符串拼接起来,如:

mqttClientId="<ClientId>"+"|securemode=3,signmethod=hmacsha1|"

3Username

拼接<DeviceName><ProductKey>

mqttUsername = "<DeviceName>&<ProductKey>"

4mqttPassword

把若干参数按字典键名排序,再把键名都拼接起来生成content,然后以DeviceSecret采用hma_sha1加密,转为十六进制字符串。

mqttPassword = hmac_sha1(DeviceSecret, content).toHexString()

当然,也可以直接在https://1024tools.com/hmac上快速获取,写进代码。

5)主题

主题就是刚申请的修改属性,由于我们再电脑端只需要发布这个属性值就行,同样的属性在RVB-2601上被订阅,于是RVB-2601就能根据这个主题来修改屏幕上的内容了。

作为MQTT的客户端,主要使用CONNECTPUBLISHSUBSCRIBE三个事件。

RVB-2601获取数据

包括配网和取值,使用AT指令集实现:

配网:

AT+WJAP=<ssid>,<password>

配置五元组鉴权信息:

AT+IDMAU="PRODUCT_KEY","DEVICE_NAME","DEVICE_SECRET","PRODUCT_SECRET"

具体配置方法小伙伴们都已经写得太多,这里不展开,后面源码里详细说明。

在阿里云中,消息使用JSON格式,简单说:键值对表示对象,对象在花括号内,数组在方括号内,数据用逗号分隔,很方便解析。

屏幕绘图

RVB-2601已经内置了lvgl,画线条非常简单,而对于一个火柴人来说,只要有手有脚,有躯干脑袋,就能看出是个人样,至于比例其实没有那么重要。可以将画人封装成单个函数,通过若干坐标直接生成人物图形,后面源码里详细说明。

人物姓名标签是从人物id生成的,因为在我这个项目里面,几个舞者的姓名都是已知固定的,所以直接将姓名固化为字体文件“lv_tobot_define.c”参与编译,实现方法在之前已做分享。

作品源码

考虑到该作品包括部分比较多,仅对RVB-2601上的几个主要函数做一个简短的介绍:

网络连接

网络使用w800,基本上是直接照抄例程。

在这里注册w800:

void wifi_w800_register(utask_t *task, w800_wifi_param_t *param)
{
    int ret = w800_module_init(task, param);

    if (ret < 0) {
        LOGE(TAG, "driver init error");
        return;
    }

    //run w800_dev_init to create wifi_dev_t and bind this driver
    ret = driver_register(&w800_driver.drv, NULL, 0);

    if (ret < 0) {
        LOGE(TAG, "device register error");
    }

    memcpy(&w800_param, param, sizeof(w800_wifi_param_t));
}
 

W800发送AT指令,实现配网和订阅主题功能

这部分代码需要自己写,但并不复杂。例如:

int w800_living_wjap(const char *myssid,const char *mypassword)
{
    int ret = -1;
   aos_mutex_lock(&g_cmd_mutex,AOS_WAIT_FOREVER);
    atparser_clr_buf(g_atparser_uservice_t);
    if (atparser_send(g_atparser_uservice_t, "AT+WJAP=%s,%s", myssid ,mypassword) == 0) {
        if (atparser_recv(g_atparser_uservice_t, "OK\n") == 0) {
            ret = 0;
        } else {
            printf("Destination Host Unreachable!\r\n");
        }
    }
    atparser_cmd_exit(g_atparser_uservice_t);
    if (ret == 0) {
        printf("WIFI set OK!\r\n");
    }
    aos_mutex_unlock(&g_cmd_mutex);
    return ret;
}
int w800_living_idmau(const char *mykey,const char *myname,const char *mysecret,const char *mypsecret)
{
    int ret = -1;
    aos_mutex_lock(&g_cmd_mutex,AOS_WAIT_FOREVER);
    atparser_clr_buf(g_atparser_uservice_t);
    if (atparser_send(g_atparser_uservice_t,"AT+IDMAU=\"%s\",\"%s\",\"%s\",\"%s\"", mykey , myname , mysecret ,mypsecret) == 0) {
        if (atparser_recv(g_atparser_uservice_t, "OK\n") == 0) {
            ret = 0;
        }else {
            printf("Destination Host Unreachable!\r\n");
        }
    }
    atparser_cmd_exit(g_atparser_uservice_t);
    if (ret == 0) {
        printf("IoT set OK!\r\n");
    }
    aos_mutex_unlock(&g_cmd_mutex);
    return ret;
}
 
int w800_living_idmcon(void)
{
    int ret = -1;
   aos_mutex_lock(&g_cmd_mutex,AOS_WAIT_FOREVER);
    atparser_clr_buf(g_atparser_uservice_t);
    if (atparser_send(g_atparser_uservice_t, "AT+IDMCON") == 0) {
        if (atparser_recv(g_atparser_uservice_t, "OK\n") == 0) {
            ret = 0;
       } else {
            printf("Destination Host Unreachable!\r\n");
       }
    }
    atparser_cmd_exit(g_atparser_uservice_t);
    if (ret == 0) {
        printf("AT+IDMCON \r\n");
    }
    aos_mutex_unlock(&g_cmd_mutex);
    return ret;
}

 

OLED的显示

         初始化时实现OLED的调用,实际使用,可以直接拷贝内存内数据

void oled_init()
{
    oled_pinmux_init();
    oled_gpio_init();
    oled_initialize();

    // lv_disp porting
    /*Create a display buffer*/
    lv_disp_buf_init(&disp_buf1, buf1, buf2, 64 * 128);

    /*Create a display*/
    lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv); /*Basic initialization*/
    disp_drv.buffer   = &disp_buf1;
    disp_drv.flush_cb = oled_flush;
    disp_drv.rotated  = 0;
    lv_disp_drv_register(&disp_drv);
}

画图

一直以来,画图可能是最麻烦的环节。通过结构体实现人物

struct dancer{
    char name[20];
    lv_obj_t * head;
    lv_obj_t * body;
    lv_obj_t * left_arm;
    lv_obj_t * right_arm;
    lv_obj_t * left_leg;
    lv_obj_t * right_leg;
}
dancer[MAX_DANCERS];

 

 

视频演示

网络版的视频演示

只做了极乐净土

单机版的视频演示

极乐净土单机版:

红叶爱呗单机版:

WeChat_20220609094104

原版视频

极乐净土:

极乐净土

       枫叶爱歌:
枫叶爱歌

 

项目总结

这次活动非常有意义,对于课题经过了反复尝试,知道最后几周才不得不承认一方面个人能力有限,一方面设备可用资源有限,不太可能做到项目申报时想做到的实时对话功能。不过管理员很给力,也鼓励我及时改换课题,当时想到了两个课题,一个是根据声音来拍照(录像),一个就是现在这个课题。前个课题的材料我也有现成的相机,只要写gpio就能实现,感觉没什么挑战性和趣味性,最终选择的是当前课题。

就我个人经验来看,拖延症的两个表现:在很短的时间(比如一两个小时)可以做完很长时间(比如一两周)的工作;很少量的工作(比如一两个小时)可以慢慢话很长时间(比如一两周)来完成。这两种情况,在我做任务时都出现过。

另外发现即使做了计划,但只要跑偏一个开头,后面的任务都歪到天边去了,比如为了研究文字的显示,专门开发了一个字模工具,但最终只需要使用其建立三个名字(日文显示);又比如本来只想研究一下如何使用,阿里云转存一个数组,为此几乎重写了网络相关的SDK(而且还bug多多)

 

  • 其它

说实话,还有一些想法未能实现,程序中的bug也还没有改完,比如那个名字乱跳的问题。还有mqtt传输数组仍然感觉有点问题,现在的效率太低。另外大概率是阿里云的原因,不支持QoS2模式,也就是说类似我这种应用没法通过缓存实现云上的记录播放,只能实时传输播放,这也是为什么在视频效果中,网络版的图像卡顿和掉帧严重的原因。

当然瑕不掩瑜,阿里云和平头哥的无缝接入的确使得开发难度大幅降低,未来准备继续研究阿里云的图形化接口。

 



WeChat_20220609094104

 

最新回复

加油,期待你的作品哦。  详情 回复 发表于 2022-6-7 08:41
点赞 关注
 
 

回复
举报

7047

帖子

11

TA的资源

版主

沙发
 
加油,期待你的作品哦。
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/6 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表