当前位置: 首页 > article >正文

esp8266_TFTST7735语音识别UI界面虚拟小助手

文章目录

  • 一 实现思路
    • 1 项目简介
      • 1.1 项目效果
      • 1.2 实现方式
    • 2 项目构成
      • 2.1 软硬件环境
      • 2.2 完整流程总结(重点整合)
        • (1) 功能逻辑图
        • (2) 接线
        • (3) 使用esp8266控制TFT屏
        • (4)TFT_espI库配置方法
        • (5) TFT_esp库常用代码详解
        • (6)TFT屏显示图片
        • (7) TFT屏显示汉字
        • (8) wif联网与HTTP获取时间
    • 3 代码实现
      • 3.1 不同UI界面
      • 3.2 UI切换方法
      • 3.3 LooP函数中的内容与优先级
      • 3.4 随时间滚动的计划事项
      • 3.5 天问代码
  • 二 展示
    • 1 图片
    • 2 视频
    • 3 下载
      • 3.1 项目下载
      • 3.2 arduino esp8266开发板离线包
      • 3.3 processin下载
      • 3.4 天问BLOCK下载

一 实现思路

摘要:esp8266wifi,HTTP通信,TFT_espI库使用,语音交互系统,从0开始自定义UI界面,防低帧不爆刷

1 项目简介

功能与实现思路

1.1 项目效果

集成了ESP8266开发板,语音识别模块,TFT显示屏和小喇叭,实现了一个语音交互系统,拥有图标菜单和4个交互界面。完全使用语音口令操作,效率很高。有以下基础功能:

(1) 计划管理
这是最主要的功能,可以在TFT屏幕上查看每天、每周和每月的计划安排,随时随地的查看下一步要做的事情,不再需要翻阅复杂的计划表。其中存入了周,月,日的具体计划,esp8266会联网获取时间,到时间了自动更新下一步计划内容,且仅在发生变化时更新屏幕,不会爆刷形成低帧率。.看一眼便知道当前时间段应该做什么事

(2) 提醒功能
目前设置了五个固定时间点提醒喝水,通过蜂鸣器的振动提示。任何固定时间需要提醒的事件,都可以加在里面,形成一个提醒清单。

(3) 打卡功能
这一功能是为了帮助自己,每天高效做一件事,每天坚持一个最重要的习惯,可以设置打卡内容。

(4) 特殊功能
这里暂时显示一些重要的事件,比如生日纪念日或节气,或从网络获取一些信息,预留后续开发

本项目最大的特点是,设计了一个UI框架,把一个TFT屏幕划分为4个界面去利用。不论是直接使用它的计划功能,还是用于修改,都很方便。之所以不用手机app,是因为容易分散注意力,嵌入式产品的一大有点就是功能性强,不掺杂娱乐事项。

1.2 实现方式

(1) esp8266中
编写主程序即界面框架和功能实现,4个UI界面内容划分在不同的模块中编写,主函数中仅设计界面交互方初始化必备信息,并且把wifi与HTTP功能也单独划分,主函数逻辑清晰,所有修改接在各自模块中进行。并连接蜂鸣器,提醒事件时会触发蜂鸣器发声。

(2) 天问block中
在这里,我们主要使用其控制引脚电平的功能,听到说某个 UI界面的名称,通过喇叭说出回复语并控制对应引脚电平发生变化。

这是一个集成好的模块,使用简便,并且官方软件是图形化积木编程(可以随时查看c代码),不过积木封装的不太行,容易遇到各种稀奇古怪的bug。该语音模块主要使用方式为:听到固定语音指令后,使用预设好的声音,说出预设好的回复词。或是听到指令,控制引脚电平发生改变。该模块本身也可以接入其它简单模块,诸如oled,tft,温湿度,数码管超声波等等,但bug仍比较多,debug还得翻看积木的c代码,只建议简单使用。最大优点仍然在其快捷的语音交互上

(3) 交互原理
天问asr_pro模块检测到说出了指令——>asr_pro引脚电平变化(GPIO output)——>连接esp82266与asr_pro引脚——>esp8266检测到引脚电平变化(GPIO Iutput)——>切换UI界面/执行其它功能

本质是通过引脚电平信号变化串联二者功能,但对电平稳定要求较高,否则容易波动。(之所以只做4个UI界面,是因为esp8266引脚不不够了,连接TFT就占据了5个个。也可以考虑使用RXTX进行通讯)


2 项目构成

软硬件环境,代码逻辑结构,与事项项目中,会遇到的所有问题与解决方案

2.1 软硬件环境

(1) 软件环境
上传程序:arduino,天问block,
图片转c数组:lcd-image-converter(底部项目文件链接含此软件)
字体编辑:processing

(2) 所需硬件
esp8266(cp2102)+电机扩展板
天问语音识别模块:ASR_PRO
TFTst7735_RGB128*160——8引脚
小喇叭,蜂鸣器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2 完整流程总结(重点整合)

从0开始搭建项目中,遇到的所有问题

(1) 功能逻辑图

在这里插入图片描述


(2) 接线
TFTesp8266
SCLd5
SDAd7
RSTd4
DCd3
CSd8
BLK背光,可不接
V3.3v即可
GG
esp8266天问语音模块
D0A5
D6A6
D1A2
D2A3
Tx蜂鸣器IO
天问语音模块接线
5v55v (3.3v容易带不动,因为有喇叭)
GG

(3) 使用esp8266控制TFT屏

一般我们从淘宝买的屏幕模块,会自带stm32的测试程序,或是arduino UNO板的测试样例和库,但这二者,前者空间较小存不了几个图,后者没有wifi功能,一般也不会送esp8266或者32的库和案例。

所以如何使用esp8266控制tft就是个问题,解决这个问题我们可以使用一个arduino中可以直接下载的库:TFT_espI,只需要预先修改User_Setup.h中的一些参数,根据自己屏幕的 驱动如st7735,型号,分辨率等等参数,取消掉一些注释即可使用。


(4)TFT_espI库配置方法

设置好参数和引脚,才可以驱动tft屏,打开库文件中的User_Setup.h,需要修改以下内容:

必要设置

  • step1:44-65行,找到自己tft的驱动型号,取消那一行的注释
    如我的: #define ST7735_DRIVER

  • step2:76-77行,根据自己的屏幕型号,选择RGB 或 BGR,取消其中一行的注释

  • step3: 85-89行,根据屏幕的宽和高型号,取消注释对应代码
    如128*160:#define TFT_WIDTH 128 #define TFT_HEIGHT 160

  • step4:在112行以下,找到自己开发板的型号与引脚分布,设置SPI与引脚的连接,
    ( esp8266 :167行以下,esp32: 209行以下)

白屏问题:如TFTST7735,128*160,仍需要在102-111行间,找到对应分辨率的一行取消注释(因为不是同一批制造的)
其余型号,如有问题需自己查找驱动部分代码注释查看解决
如碰到屏幕反色,将116或117其中一行取消注释

其余设置

  • 310-321 为库自带字体(一般不需改动)
  • 359-372 为SPI频率和触摸屏相关参数

有问题,多看英文注释翻译解释

注意esp8266在arduino中的引脚,以GPIO数字编号为,不是DX

这里是引用


(5) TFT_esp库常用代码详解

1 基本功能

TFT_eSPI tft = TFT_eSPI(); //初始化tft对象
tft.init();
tft.setRotation(0);    //屏幕旋转0123: 0 90 180 270
tft.fillScreen(TFT_BLACK);//清屏
tft.setSwapBytes(true);//不加容易颜色异常
tft.pushImage(0,0,128,160,test2);//背景图
tft.loadFont(HGY316); //加载自定义中文字体,设置字体大小对加载的字体无效

2 字体设置

tft.setCursor(0,0);   //改字体显示位置
tft.setCursor(0,0,a);  //第三参数选择自带字体样式:a=124678
tft.setTextSize(1);  //设置文本显示的大小,,对中文字体无效
tft.setTextFont(1);   //选择库自带字体:1,2, 4, 6, 7, 8
tft.setTextColor(TFT_GREEN, TFT_BLACK);//字体颜色,字体背景色
tft.println("周一");  //输出中文或字符串,draw没有自动换行

tft.drawChar('#', 100, 64, 2);  //输出字符(字符,x,y,大小)字符或ascii码都可以
tft.drawNumber(num, 0, 100, 4);  //输出数字
tft.drawString("zifucahun", 0, 80, 2);//上传字符串(坐标,大小)

3 常见字体颜色代码:

#define TFT_BLACK       0x0000      /*   0,   0,   0 */
#define TFT_NAVY        0x000F      /*   0,   0, 128 */
#define TFT_DARKGREEN   0x03E0      /*   0, 128,   0 */
#define TFT_DARKCYAN    0x03EF      /*   0, 128, 128 */
#define TFT_MAROON      0x7800      /* 128,   0,   0 */
#define TFT_PURPLE      0x780F      /* 128,   0, 128 */
#define TFT_OLIVE       0x7BE0      /* 128, 128,   0 */
#define TFT_LIGHTGREY   0xD69A      /* 211, 211, 211 */
#define TFT_DARKGREY    0x7BEF      /* 128, 128, 128 */
#define TFT_BLUE        0x001F      /*   0,   0, 255 */
#define TFT_GREEN       0x07E0      /*   0, 255,   0 */
#define TFT_CYAN        0x07FF      /*   0, 255, 255 */
#define TFT_RED         0xF800      /* 255,   0,   0 */
#define TFT_MAGENTA     0xF81F      /* 255,   0, 255 */
#define TFT_YELLOW      0xFFE0      /* 255, 255,   0 */
#define TFT_WHITE       0xFFFF      /* 255, 255, 255 */
#define TFT_ORANGE      0xFDA0      /* 255, 180,   0 */
#define TFT_GREENYELLOW 0xB7E0      /* 180, 255,   0 */
#define TFT_PINK        0xFE19      /* 255, 192, 203 */    
#define TFT_BROWN       0x9A60      /* 150,  75,   0 */
#define TFT_GOLD        0xFEA0      /* 255, 215,   0 */
#define TFT_SILVER      0xC618      /* 192, 192, 192 */
#define TFT_SKYBLUE     0x867D      /* 135, 206, 235 */
#define TFT_VIOLET      0x915C      /* 180,  46, 226 */
(6)TFT屏显示图片

1 想要显示图片,需要将图片转化为c数组,然后存入头文件中调用显示

显示图片函数为:
tft.pushImage(0,0,128,160,test2);//x1,y1-->x2,y2

2 值得一提,tft屏幕的坐标系与旋转问题:

在这里插入图片描述

3 把图片转化为数组步骤(使用lcd-image-converter):

1 准备好分辨率图,可以使用window画图软件在这里插入图片描述
2 导入图片
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3 设置参数
在这里插入图片描述
在这里插入图片描述
4 保存数组
在这里插入图片描述
头文件格式:

#pragma once
#include<pgmspace.h>

const uint8_t name[] PROGMEM ={  //name可自己设置
	//放生成的代码
}

4 本项目中计划内容的坐标分配:

在这里插入图片描述


(7) TFT屏显示汉字
  • 1 c/user/windows/fonts/想用的字体.ttf(或网上下)
    移动到TFT_espI库/tools/Creat_Smooth_Font/Creat_Font/data目录下

  • 2 下载processing软件,打开TFT_eSPI\Tools\Create_Smooth_Font\的
    Create_font.pde 进行编辑:
    -130行改为自己的ttf字体文件名
    -132行选中后缀ttf解注释
    -140行设置字体大小(使用中文库:在arduino中不可修改,只有在里可修改)

  • 3 准备好项目中使用的所有汉字,使用网页在线转换:汉字转Unicode编码
    然后可以在记事本中,把所有的\u替换为 ,\0x ,删除开头的逗号

  • 4 在Create_font.pde
    -330行下,把上一步的汉字Unicode编码,粘贴到specificUnicodes数组中去

  • 5 点击precessing 运行按钮,弹窗会显示所有生成的汉字和其余字符

  • 6 成功后,回到TFT_eSPI\Tools\Create_Smooth_Font\Create_font\FontFiles目录,
    把生成对的 字体名.h 头文件,粘贴到项目文件夹中,然后引用头文件就可以显示中文。

  tft.print("内容") 函数输出即可

该步可直接搜b站教程:如何使用TFT_espI库在tft屏上显示汉字
注:只有生成好的汉字,才能够在TFT屏上显示,否则乱码


(8) wif联网与HTTP获取时间

事先注意:上传代码前,一定查看清楚自己的模块到底是什么型号
若是CP2102,则对应NodeMCU板载ESP-12E ( 4MB Flash) WIFI模组
在arduino中一旦选择错误,wif功能基本报废,且波特率也对不上
cp2102:应设115200,若设为9600烧录代码会很慢

1 给出cp2102的arduino工具参数图:

在这里插入图片描述

2 wifi连接基本操作
led灯显示是否连接成功,闪烁表示正在连接,长亮表示连接成功

#include <ESP8266WiFi.h>
const char *ssid = "....";
const char *pass = "....";

int led=14; //wifi连接指示灯D5连接led
void setup() {
  
  Serial.begin(115200);
  WiFi.mode(WIFI_STA); //使用STA模式用8266去连接wifi
  WiFi.begin(ssid, pass);
  
  //等待wifi连接结果
  while (WiFi.status()!=WL_CONNECTED) {
     bool is=digitalRead(led);//巧妙反转指示灯
     digitalWrite(led, !is);
     Serial.println("....");
     delay(500);
  }digitalWrite(led, 1);//成功常亮

  Serial.println("WiFi 已连接");
  Serial.println("IP地址为:");
  Serial.println(WiFi.localIP());

}

3 HTTP获取网络时间
使用网址为:http://quan.suning.com/getSysTime.do
显示格式为:{"sysTime2":"2024-12-20 07:59:14","sysTime1":"20241220075914"}

#include <ESP8266HTTPClient.h>
HTTPClient http;
String GetURL="http://quan.suning.com/getSysTime.do"; //获取链接
String res;//回应
void setup() {
  //需提前连接wifi
  
  Serial.begin(115200)
  //http连接
  http.setTimeout(5000);//预启动连接,不加也行
  http.begin(GetURL);//网址
}
void loop() {
  //连接
  int httpCode = http.GET();//一个数值,表示连接情况
  if(httpCode>0){
    Serial.printf("[HTTP] GET... code: %d\n", httpCode);
      if (httpCode == HTTP_CODE_OK){   //是否是一个成功的请求
        //读取响应内容
        res = http.getString();//得到固定内容
        Serial.println(res);
        //可使用substring(a,b)--:显示s[a]~s[b-1]的字符串内容,截取时间
        delay(300);
      }
  }else{
       Serial.println("HTTP Get ERROR");
  }  http.end();//关闭连接
}

4 因为没有周几的情况,故本项目使用zeller公式,根据年月日计算出周几

在这里插入图片描述

注意:

实测定时器内置定时器Tricker库法:
-若开机只获取一次时间,然后采用tricker定时器库,每几秒联网更新一次时间
这样容易反复重启,可能tricker要求调用函数精简,而网络获取不稳定,需要1-2s
或是其它的冲突问题,开发板容易无限重启,最后仍采用在loop中重复获网络时间的方案


3 代码实现

代码模块功能介绍与注意事项

3.1 不同UI界面

项目共有5个界面,可想而知人如果全把代码写在主程序,修改非常不遍,故根据功能将不同的界面写入不同的.c和.h文件,由主函数统一调用,主函数只负责调用写的方法,每个UI的美化与内容设计,都在各自模块中中进行,见下头文件方法:

(1) 计划内容wifi_HTTP 头文件:

/*
1 计划界面设计:月,日,周 计划设计函数
2 功能接口函数,便于修改
3 wifi+http获取网络时间
*/
//日期结构体
typedef struct {
  int year;
  int mon;//月
  int day;//日
  int wk;//周
  int h;//小时 0-24
  int m;//分 0-60
  int s;//秒0-60
}DateTypedef;

//tft屏上显示月,日,周 计划信息
void Get_monthPlan(DateTypedef x);//显示月初事项
void Get_weekPlan(DateTypedef x);//显示周信息 
void Get_dayPlan(DateTypedef x);//显示日计划
void Get_bottomtime();//显示底部时间数据

//网络功能
void WiFiHTTP_init();
void Update_http();//并更新时间数值

(2) 提醒 与 UI界面头文件:

/*
1 功能:U1-4界面设计
2 提醒事件设计,如:喝水,日期提醒
*/

//弹出提醒函数,可扩展类似功能
void Remind_water(DateTypedef x);//每日定时喝水,弹出几s

//仅查看UI界面函数
void Remind_menu();//UI1
void Remind_warn();//UI2
void Remind_habit(DateTypedef x); //UI3
void Remind_special(DateTypedef x);//UI4

3.2 UI切换方法

功能逻辑为,天问语音模块检测到口令后给对应引脚一个高电平(正常为低),所以在loop中检测对应引脚变化即可。使用UI表示每个界面的编号,0-4,0为计划界面在swith的default分支,1234分别对应:菜单,提醒,打卡,特殊。

值得注意的是:Loop中不要放刷新屏幕数,否则会爆刷,屏幕帧率会极端低,效果很差。而这里采用了标记法,记录上一次屏幕UI编号本次编号,只有两次编号不同的时候,说明进行了界面切换,此时才刷新屏幕,然后及时更新两个标记就可以吗,大大减少刷新次数,增加稳定性。


void loop() {
  Get_freshUI();//更新UI编号,显示不同界面
  switch (UI){
    case 1:     //U1菜单
      Remind_menu();
      UI=0;
      break;
    case 2:     //U2提醒
      Remind_warn();
      UI=0;
      break;
    case 3:    //U3打卡
      Remind_habit(wifi_date);
      UI=0;
      break;
    case 4:   //U4特殊
      Remind_special(wifi_date);
      UI=0;
      break;
    default:  //计划界面
      Get_dayPlan(wifi_date);
      Get_weekPlan(wifi_date); 
      Get_monthPlan(wifi_date);
      Get_bottomtime();//多久更新一次
      UI=0;//默认为计划界面
  }
}
//获取当前口令下的UI编号
int tem=0;//标记上一次UI界面的编号牌
void Get_freshUI(){
  //不同的口令,在语音模块设置对应引脚电平为高
  //esp8266读取到高电平信号,更新界面UI值,显示不同界面
  if(digitalRead(u1_pin)==HIGH)
    UI=1;
  if(digitalRead(u2_pin)==HIGH)
    UI=2;
  if(digitalRead(u3_pin)==HIGH)
    UI=3;
  if(digitalRead(u4_pin)==HIGH)
    UI=4;
  if(tem!=UI){
     tft.fillScreen(TFT_BLACK);//清屏
     tem=UI;  //仅在切换不同界面时更新一次,防低帧爆闪
  }
}

3.3 LooP函数中的内容与优先级

(1) 网络更新需要放在这里,获取网络时间应立即对本地结构体时间尽心修改
(2) 像闹钟,纪念日,喝水,这种固定时间的时间,需要弹出立刻提醒的,应放在界面切换之上,时间到立刻弹出一段时间,然后自动退出即可。
(3) 更新界面编号的函数,应随时检测引脚电平变化确定口令的有效性

void loop() {
  Update_http();//从网上获取与更新本地时间
  Get_freshUI();//更新UI编号,显示不同界面
  digitalWrite(u5_pin,HIGH);//我的蜂鸣器低电平触发
  Remind_water(wifi_date);//提醒喝水,所有弹出提醒事件,都是高优先级,不在swith中

  switch (UI){
    case 1:     //U1菜单
      Remind_menu();
      UI=0;
      break;
    case 2:     //U2提醒
      Remind_warn();
      UI=0;
      break;
    case 3:    //U3打卡
      Remind_habit(wifi_date);
      UI=0;
      break;
    case 4:   //U4特殊
      Remind_special(wifi_date);
      UI=0;
      break;
    default:  //计划界面
      Get_dayPlan(wifi_date);
      Get_weekPlan(wifi_date); 
      Get_monthPlan(wifi_date);
      Get_bottomtime();//多久更新一次
      UI=0;//默认为计划界面
  }

  //测试:快速模拟 时分秒,查看计划
  // wifi_date.m++;
  // if(wifi_date.s==60){ wifi_date.s=0; wifi_date.m++;}
  // if(wifi_date.m==60){ wifi_date.m=0; wifi_date.h++;}
  // if(wifi_date.h==24){ wifi_date.h=0; wifi_date.wk++; wifi_date.day++;}
  // if(wifi_date.wk==8) { wifi_date.wk=1; }
}

3.4 随时间滚动的计划事项

(1) 首先我们的时间结构体,存取的是整数值
而非简单的截取显示一段字符串,所以对于网络获取的数据,应对字符串进行准换,保留为整型数据

String s;
s[i]-'0' 可得到数字字符代表的整数值
然后通过十进制计算出具体值保留即可(乘以若干10

(2)涉及时间区间,需要判断一个时间是否在某个区间内,用反向判断更简单:

typedef struct {
  int year;
  int mon;//月
  int day;//日
  int wk;//周
  int h;//小时 0-24
  int m;//分 0-60
  int s;//秒0-60
}DateTypedef;
//判断当前时间是否在[m1:n1]~[m2:n2]之间,含区间端点
int Set_section(DateTypedef x,int m1,int n1,int m2,int n2){
	if(x.h<m1||x.h>m2) return 0; //小时超界 
	else if(x.h==m1&&x.m<n1)  return 0; //不到区间左端点 
	else if(x.h==m2&&x.m>n2)  return 0;  //超过区间右端点
	else{
		return 1; 
	} 
}

(3)最后一个问题
每一步计划的更新,应该是在某一秒到达计划时间后,更新一次,而不是反复的刷新屏幕,那样会降低帧率,效果极差,同上,我们依旧使用标记法,这次是对所有的计划事件编号,两个标记,本次时间与上次事件(万能的flag)

如不刷新屏幕,会有字体重叠显现出现

/*
周计划显示
*/
int int wk_s=1,wk_n=0; //st上一次计划编号,now本次计划编号
void Get_weekPlan(DateTypedef x){
  tft.setTextColor(TFT_GREEN);
	switch (x.wk){
		case 1:
      wk_n=1;
      locadt(0,60,"周一"); //封装的显示函数,坐标与内容一起设置,简化代码
      locadt(0,80,"今日内务");
      locadt(0,100,"耳鼻,清灰,耳机");
		    break;
		case 2:
     wk_n=2;
      locadt(0,60,"周二");
      locadt(0,80,"今日内务");
      locadt(0,100,"洗澡,大扫除");
		    break;
		case 3:
		default:
      locadt(0,100,"ERROR!");
	}
	if(wk_s!=wk_n){ //同理,防爆刷
    tft.fillRect(0, 60, 128, 120, TFT_BLACK);//部分刷新屏幕
    wk_s=wk_n;
  }
}

3.5 天问代码

仅需要注意:语音标识ID不会自懂更新
在这里插入图片描述


重点内容便是上述部分,还有数不清对的小功能,便不再详细描述,项目文件链接中包含全部的程序,开源供大家学习交流。
项目难度不大,代码和结构半小时就编好了,但各种模块的坑,真的是一个接一个,在此综合的整合一下所有问题,希望对需要的人有所帮助,节省时间。硬件学习并不是件很难的事,只不过麻烦而已,请不要丧失你的信心,加油各位


二 展示

1 图片

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


2 视频

✨太一·庚辰✨赛博少女助手,esp8266+TFT+语音识别


3 下载

3.1 项目下载

项目完整文件

3.2 arduino esp8266开发板离线包

esp8266+32开发板包

3.3 processin下载

processing官网

3.4 天问BLOCK下载

天问官网



http://www.kler.cn/a/444915.html

相关文章:

  • LeetCode 69. x 的平方根 (C++实现)
  • 如何通过HTTP API新建Collection
  • C++ OpenGL学习笔记(1、Hello World空窗口程序)
  • SparkSQL运行架构及原理
  • ArcGIS计算土地转移矩阵
  • armsom产品Debian系统开发
  • 信号处理相关的东东(学习解惑)
  • 高浓度盐酸除铁的详细介绍
  • mlr3超参数Hyperparameter 自动寻找auto
  • 9_HTML5 SVG (5) --[HTML5 API 学习之旅]
  • 详解ROS环境配置:setup.bash 文件的功能与操作
  • Oracle 中什么情况下 可以使用 EXISTS 替代 IN 提高查询效率
  • 8K+Red+Raw+ProRes422分享5个影视级视频素材网站
  • mysql同一张表中数据一样的问题和解决
  • 远程桌面连接
  • vue create 创建项目 提示 Failed to check for updates 淘宝 NPM 镜像站喊你切换新域名啦
  • 如何测量分辨率
  • Java8 Stream编码问题
  • 【HTML】动态闪烁圣诞树+雪花+音效
  • 教育版idea及jetbrains全家桶免费使用
  • 七、网络安全-企业数据脱敏
  • 【YOLO】 YOLOv3原理
  • 深入解析 Vue 3 源码:原理与学习指南
  • ffmpeg.exe 命令使用
  • 模型优化之知识蒸馏
  • [Unity Shader] 【游戏开发】【图形渲染】Unity Shader的种类2-顶点/片元着色器与固定函数着色器的选择与应用