兔子的演变
将机器人用兔子的形象来表现是深受《那免》的影响,只是TB上没有现成的“好”兔子。最后买了萌萌的兔子和穿 了军服的其他玩偶,回来之后进行了改装。
把器材放进兔子里费老劲了,将兔子“开膛破肚”,将里面的填充棉花全部掏出来,用连接件搭了个“骨架”,再把器材分别固定进去,还要注意各部件的位置。机器视觉传感器的摄像头放在鼻子的位置,TFT显示屏放在兔子的肚子位置。语音识别的咪头在兔子后面开个小孔露出来,否则拾音不灵敏。掌控板的显示是调试用的,所以没有把掌控板外置。因为掌控板和UNO需要进行引脚实时通信,是共用电源的,所以给UNO的供电采用了电池盒。器材固定后将棉花填充进去。为了方便在兔子背后缝了条拉链,所有工作完成后将拉链拉上即可。
思维导图
任务分析
因为其他模块都有IoT,所以党史机器人只做离线版的应用,没有做IoT的应用,当然要加上也简单,通过掌控板连接就可以了,在本项目中不做讨论。
从上面的思维导图可以看到,党史机器人分为了4个部分。想实现的功能是:
· 通过语音识别可以识别百年精神的关键字;
· 通过二哈可以识别百年精神的标签;
· 当识别语音或标签成功后,通过mp3模块朗读对应精神的解读;
· 当识别语音或标签成功后,通过TFT屏幕显示对应精神的图片;
这4个部分都不简单,每一部分对学生来说都是一个挑战。我没有让学生一开始就动手做。而是针对每个部分先进行学习。这个项目是两个学生负责的,我把他们两人的座位编排在一起,这样方便他们可以随时进行讨论。根据wiki及示例先跑demo,遇到问题再问老师。
刚开始的方案是主控用UNO,再加上人工智能三剑客:语音合成、语音识别和二哈一起来完成这个项目。后来发现语音合成的工作量太大了,文字太多就没法实现。所以就把离线的语音合成模块去掉,将文字转语音的功能使用电脑完成,转为mp3文件后通过MP3模块来实现朗读。而原来的主控UNO跑太多代码内存也会爆,最后主控改用掌控板,但是掌控板没有TFT屏的扩展和示例,迁移对学生来说难度太大也太耗时。最后决定用一个取巧的办法,掌控板接语音识别和二哈,UNO接TFT,两个板子之间通过引脚相连。只要掌控板有空闲5个引脚就可以有32种不同的状态,除去全0,可以有31种状态,我们一共28种精神,足够使用了。
本帖为了方便描述任务功能,28种精神选取了其中的三种来实现,但是实现方法全部采用模块的方式,扩展极为简单。
本任务很好的培养了计算思维的四个维度:分解、抽象、模块化及算法设计。主要体现在:
· 使用列表使判断和操作能够自动的进行
· 使用带参数的函数让代码做到最大化的复用
步骤1 线路连接
本说明了所有模块的连接方式,但是在实际进行时不要一次性的全部把线接好。而是每个功能单独连线,测试成功后再进行下一个功能的测试。当全部功能都实现后再将线全部接好,测试完整功能。
掌控板连接了语音识别传感器、机器视觉传感器和MP3语音播放模块。
1. 语音识别使用I2C通讯方式,SDA接P18,SCL接P17。在步骤2时进行。
2. 视觉识别使用I2C通讯方式,SDA接P20,SCL接P19。在步骤3时进行。
3. MP3模块RX接P6,TX接P1。在步骤4时进行。在步骤4时进行。
4. UNO连接TFT屏,接线方式见步骤5。在步骤5时进行。
5. 掌控板的引脚0,7接UNO的引脚8和引脚7。
前面所述,可以使用5个引脚来发送28种精神的状态。本贴为了方便描述,使用两个引脚。两个引脚可以有4种状态00,01,10,11,除去00,可以表达三种有效状态,即可以传递三种不同的精神类别。本贴中,01表示向UNO传送“红船”信号,10表示向UNO传送“铁人”信号,11表示向UNO传送“延安”信号。
在步骤2~步骤5中,都需要对每种功能进行测试,测试的目的是确定每个模块都可以独立正常工作。
计算思维的“实验与迭代”,先做一点点,测试一下,再做一点点,不断的前进和优化。
步骤2 语音播放准备
语音播放使用MP3模块,需要:
1. 将文字转换为语音
28种精神的内涵及解读全部来自官方网站:共gong产chan党dang员网。将每个中国精神的内涵进行提炼,使用工具将文字转换为语音
2. 将语音文件保存到MP3模块中。
3. 将MP3模块的RX接掌控板的P6,TX接掌控板的P1。
4. 编写代码测试语音文件是否可以正常播放。如果能够正常播放则进行到下一步。
步骤3 二哈标签识别准备
1. 二哈默认可以识别35种标签,将标签在泡沫棉上打印出来并切割成一小块备用。
2. 二哈学习标签,方法参见文末的参考资料,二哈的wIki。
3. 视觉识别使用I2C通讯方式,SDA接掌控板的P20,SCL接掌控板的P19。
4. 编写代码测试标签是否可以识别正常。识别正常则进行下一步骤。
步骤4 语音识别
1. 语音识别使用I2C通讯方式,SDA接掌控板的P18,SCL接掌控板的P17。
2. 编写代码测试语音识别功能是否正常,如果正常则进行下一步。
步骤5 TFT显示屏显示图片
1. 搜索百年精神相关图片。
2. 将图片转换为BMP格式。
这款TFT屏幕可以显示 bmp / jpg / jpeg格式的图片。 jpg 图片仅支持JFIF格式。网上搜索的图片大部分都是JPG格式的,但很多不支持JFIF,所以最稳妥的方法是使用Windows自带的画图程序将图片转换为BMP格式,或者重新保存为JPG格式。经过这样折腾一圈就可以在屏幕上正常显示图片了。
* @brief Read pictures in bmp / jpg / jpeg format from the SD card and display them on the screen. bmp supports 16-bit / 24-bit / 32-bit.
* @n jpg only supports JFIF format, you can open with windows drawing and save it as this format.
3. 将图片保存到SD卡中,并将卡插到TFT卡槽中。
4. 将TFT与UNO如下图相连。
5. 将改写好的DEMO程序烧录到UNO中,测试图片能否正常显示。如果能够正常显示则进行到下一步。
TFT屏幕与UNO连接图
将JPG转换为BMP
选择24位图是因为如果选择16位图,这些图片的颜色信息会部分丢失。
图片通过画图程序另存为jpg格式也是可以的。
已经转换好的三张BMP范图:红船、铁人、延安。
步骤6 编写程序
所有子模块测试完毕后,开始编写代码,实现语音识别、标签识别精神关键字并朗读对应精神文本,同时TFT屏幕显示对应精神图片。
掌控板程序(总)
函数:初始化精神列表
为方便描述,仅将3种精神加入列表。28种精神可以原样操作。
函数:初始化语音识别
· “铁人”和“王进喜”是同一种精神,因此编号相同。
· 识别模式为“循环模式”时表示语音识别传感器一直处于拾音状态,不停的拾取环境中的声音进行分析识别。
· 麦克风模式为“默认”时,表明此时板载咪头和3.5mm输入接口工作。
函数:初始化mp3
函数:初始化二哈
代码被遮住的部分是“HuskyLens切换到‘标签识别’算法直到成功”。
函数:语音识别
调用带参数的函数“播放”,参数为每种精神对应的序号。
函数:播放(序号)
通过语音或标签识别到精神关键字后,播放对应精神内容,掌控板屏幕显示精神名称。
函数:停止
通过语音或标签识别到“停止”命令后,停止播放语音,掌控板屏幕“停止播放”。
函数:标签识别
标签识别的逻辑与语音识别的逻辑一样,加上5秒钟等待是避免因为标签没有及时拿开造成重复识别。
函数:UNO信号_红船
函数:UNO信号_延安
函数:UNO信号_铁人
使用2个引脚可以传送三种不同的精神,同理使用5个引脚可以传送2的5次方种不同信号。
Arduino代码
/*!
@file SD.ino
@brief 从SD卡上读取bmp / jpg / jpeg格式的图片,显示在屏幕上。bmp支持16位/ 24位/ 32位。
@n jpg只支持JFIF格式,可以用windows drawing打开并保存为这种格式。
@n 这个演示的图片存储在example-> SD-> picture下;只需将图片文件夹复制到SD卡上
@n 该演示支持主板ESP8266、FireBetele-M0、MAGE2560和UNO。
@copyright 版权所有(c) 2010德国机器人有限公司(http://www.dfrobot.com)
@licence The MIT License (MIT)
@author [YeHangYu] (hangyu.ye@dfrobot.com)
@version V0.1
@date 2020-03-20
@url https://github.com/DFRobot/DFRobot_GDL
*/
#include <SD.h>
#include <SPI.h>
#include "DFRobot_GDL.h"
#include "DFRobot_Picdecoder_SD.h"
DFRobot_Picdecoder_SD decoder;
/* AVR series mainboard */
#define TFT_DC 2
#define TFT_CS 3
#define TFT_RST 4
#define TFT_SD 5
/**
@brief 硬件串行接口通信的构造函数
@param 用于SPI通信的直流命令/数据线引脚
@param 用于SPI通信的cs芯片选择引脚
@param 屏幕的rst复位引脚
*/
//DFRobot_ST7789_240x240_HW_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST);
DFRobot_ST7789_240x320_HW_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST);
//DFRobot_ILI9341_240x320_HW_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST);
//DFRobot_ILI9488_320x480_HW_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST);
/* M0 mainboard DMA transfer */
//DFRobot_ST7789_240x240_DMA_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST);
//DFRobot_ST7789_240x320_DMA_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST);
//DFRobot_ILI9341_240x320_DMA_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST);
//DFRobot_ILI9488_320x480_DMA_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST);
/*
用户可选择的宏定义颜色
COLOR_RGB565_BLACK COLOR_RGB565_NAVY COLOR_RGB565_DGREEN COLOR_RGB565_DCYAN
COLOR_RGB565_MAROON COLOR_RGB565_PURPLE COLOR_RGB565_OLIVE COLOR_RGB565_LGRAY
COLOR_RGB565_DGRAY COLOR_RGB565_BLUE COLOR_RGB565_GREEN COLOR_RGB565_CYAN
COLOR_RGB565_RED COLOR_RGB565_MAGENTA COLOR_RGB565_YELLOW COLOR_RGB565_ORANGE
COLOR_RGB565_WHITE
*/
void setup()
{
//初始化串行端口
pinMode(8, INPUT);
pinMode(7, INPUT);
Serial.begin(115200);
//初始化screen
screen.begin();
//初始化SD卡,等待初始化成功
while (1)
{
#if defined ARDUINO_SAM_ZERO
if (SD.begin(/*sdcs=*/TFT_SD,/*type = */TYPE_NONBOARD_SD_MOUDLE)) {
#else
if (SD.begin(/*sdcs=*/TFT_SD)) {
#endif
Serial.println("initialization done.");
break;
}
Serial.println("initialization failed!");
}
}
void loop()
{
if ((digitalRead(8)==0 && digitalRead(7)==0))
{
screen.fillScreen(COLOR_RGB565_WHITE);
}
else
{
if ((digitalRead(8)== 1 && digitalRead(7)== 1))
{
screen.fillScreen(COLOR_RGB565_WHITE);
decoder.drawPicture(/*filename=*/"ya24.bmp",/*sx=*/0,/*sy=*/0,/*ex=*/screen.width(),/*ey=*/screen.height(),/*screenDrawPixel=*/screenDrawPixel);
delay(1000);
}
if (digitalRead(8) == 1 && digitalRead(7) == 0)
{
screen.fillScreen(COLOR_RGB565_WHITE);
decoder.drawPicture(/*filename=*/"wjx24.bmp",/*sx=*/0,/*sy=*/0,/*ex=*/screen.width(),/*ey=*/screen.height(),/*screenDrawPixel=*/screenDrawPixel);
delay(1000);
}
if (digitalRead(7) == 1 && digitalRead(8) == 0)
{
screen.fillScreen(COLOR_RGB565_WHITE);
decoder.drawPicture(/*filename=*/"hc24.bmp",/*sx=*/0,/*sy=*/0,/*ex=*/screen.width(),/*ey=*/screen.height(),/*screenDrawPixel=*/screenDrawPixel);
delay(1000);
}
}
}
//对于解码函数调用,这个函数的作用是在屏幕上画一个像素
void screenDrawPixel(int16_t x, int16_t y, uint16_t color)
{
//在屏幕上画一个点
screen.writePixel(x, y, color);
}
MakingForGood创善2021.11.16
您好,我是盲童福利机构的志愿者,要为机构的孩子们用树莓派做一款智能寻物眼镜,希望与您取得进一步交流
hnyzcj2021.07.09
老板下血本了