学习记录:ESP32控制舵机 FREERTOS BLE
控制舵机
PWM信号
PWM信号是一种周期性变化的方波信号,它有两个关键参数:
- 周期(Period):一个完整的PWM信号的时间长度,通常用秒(s)或毫秒(ms)表示。
- 占空比(Duty Cycle):高电平(ON状态)持续时间与周期的比值,通常以百分比表示。占空比决定了信号的平均功率。
脉宽
脉宽(Pulse Width)是指PWM信号中高电平的持续时间。它与占空比密切相关,脉宽越长,占空比越大;脉宽越短,占空比越小。
PWM信号的工作原理
PWM信号通过快速切换开关,在一个固定周期内改变脉冲的宽度,以达到控制功率输出的目的。比如在电机控制中,通过调整PWM信号的占空比,可以控制电机的转速。占空比越高,电机转速越快;占空比越低,电机转速越慢。
例子说明
假设我们有一个周期为10毫秒的PWM信号:
-
占空比为50%:
- 脉宽:5毫秒
- 效果:在10毫秒的周期中,5毫秒为高电平,5毫秒为低电平。
-
占空比为25%:
- 脉宽:2.5毫秒
- 效果:在10毫秒的周期中,2.5毫秒为高电平,7.5毫秒为低电平。
-
占空比为75%:
- 脉宽:7.5毫秒
- 效果:在10毫秒的周期中,7.5毫秒为高电平,2.5毫秒为低电平。

代码# include <ESP32Servo.h>
Servo servo1; // 定义servo对象
Servo servo2; // 定义servo对象
int minUs=500; //0度时的脉宽,单位us
int maxUs=2500; //180度时的脉宽,单位us
int servo1Pin=15;
int servo2Pin=16;
int pos=-1;
bool up=true;void setup() {ESP32PWM::allocateTimer(1);//使用指定的硬件定时器servo1.setPeriodHertz(50);//指定PWM的频率servo2.setPeriodHertz(50);//指定PWM的频率servo1.attach(servo1Pin,minUs,maxUs);servo2.attach(servo2Pin,minUs,maxUs);// servo1.write(pos);//转到指定的角度(0~180)// servo1.detach();//不需要的时候将引脚和ledc分离
}void loop() {if(pos == 181)up=false;else if(pos==-1)up=true;if(up)pos++;elsepos--;servo1.write(pos);servo2.write(180-pos);delay(15);
}
1
FREERTOS

void task1(void *pt){pinMode(47,OUTPUT);while(1){digitalWrite(47,!digitalRead(47));// delay(1000);vTaskDelay(1000);}
}void task2(void *pt){pinMode(42,OUTPUT);while(1){digitalWrite(42,!digitalRead(42));vTaskDelay(2000);}
}
void setup() {xTaskCreate(task1,"Blink,47,19",1024,NULL,1,NULL);xTaskCreate(task2,"Blink,42,39",1024,NULL,1,NULL);
}void loop() {}
1. 任务堆栈大小的单位
在 FreeRTOS 中,任务堆栈大小的单位通常是字(word),而不是字节(byte)。在大多数微控制器平台上,一个字是 4 字节(32 位)。因此,当你指定堆栈大小时,实际上是指定了多少个 4 字节的块。
示例:
如果你指定 configMINIMAL_STACK_SIZE 为 128,则实际上分配的堆栈空间是 128 * 4 = 512 字节。
2. 优先级的作用
任务优先级决定了任务的执行顺序和调度策略。在 FreeRTOS 中,数值越高的优先级表示任务的重要性越高。任务调度器会优先执行优先级高的任务。当多个任务具有相同优先级时,它们将以时间片轮转的方式执行。
示例:
-
任务 A 优先级为 2
-
任务 B 优先级为 1
在这种情况下,任务 A 会优先于任务 B 执行。如果任务 A 阻塞或进入等待状态,任务 B 才会执行。
3.任务句柄的使用
任务句柄(Task Handle)用于在运行时引用特定任务。你可以使用任务句柄来挂起、恢复或删除任务。
l示例:
c
复制代码
#include <FreeRTOS.h>#include <task.h>#include <stdio.h>// 任务函数声明void vTaskFunction(void *pvParameters);// 全局变量,用于存储任务句柄
TaskHandle_t taskHandle = NULL;int main(void)
{xTaskCreate(vTaskFunction, // 任务函数"Task 1", // 任务名称configMINIMAL_STACK_SIZE, // 任务堆栈大小NULL, // 任务参数tskIDLE_PRIORITY + 1, // 任务优先级&taskHandle // 任务句柄);
// 启动调度器vTaskStartScheduler();
// 正常情况下,不会运行到这里for(;;);
}void vTaskFunction(void *pvParameters)
{for(;;){printf("Task is running\n");vTaskDelay(1000 / portTICK_PERIOD_MS);
// 示例:挂起任务vTaskSuspend(taskHandle);
// 示例:恢复任务vTaskResume(taskHandle);}
}
蓝牙BLE
-
服务器(Server)
服务器是 BLE 外设设备的一部分,负责管理多个服务并处理与中央设备的连接和通信。服务器本身不直接包含数据,而是通过服务和特性来组织和传输数据。
-
角色:在 BLE 通信中,服务器(通常是传感器设备)提供数据和功能,而中央设备(通常是手机或电脑)请求和使用这些数据和功能。
-
服务(Service)
服务是服务器中用于组织和描述特定功能的一组相关特性。每个服务都有一个唯一的标识符(UUID),用来标识和区分不同的服务。
-
角色:服务是功能的逻辑集合。例如,一个心率监测设备可能有一个“心率服务”,包含心率测量、心率控制点等特性。
-
特性(Characteristic)
特性是服务中的基本数据单元,包含实际的数据和相关属性。每个特性也有一个唯一的标识符(UUID),用于标识特性。特性可以包含一个值和零个或多个描述符。
-
角色:特性是传输数据的具体单元。例如,“心率服务”中的“心率测量”特性可能包含当前的心率值。
它们之间的关系
-
服务器包含多个服务。
-
每个服务包含多个特性。
-
每个特性包含一个值和零个或多个描述符。
图示关系
scss
复制代码
服务器 (Server)├── 服务1 (Service 1, UUID: 0000180f-0000-1000-8000-00805f9b34fb)│ ├── 特性1 (Characteristic 1, UUID: 00002a19-0000-1000-8000-00805f9b34fb)│ │ └── 描述符1 (Descriptor 1)│ └── 特性2 (Characteristic 2, UUID: 00002a1a-0000-1000-8000-00805f9b34fb)└── 服务2 (Service 2, UUID: 0000180a-0000-1000-8000-00805f9b34fb)├── 特性1 (Characteristic 1, UUID: 00002a29-0000-1000-8000-00805f9b34fb)└── 特性2 (Characteristic 2, UUID: 00002a2a-0000-1000-8000-00805f9b34fb)
示例代码中的对应关系
-
服务器:在代码中,通过
BLEDevice::createServer()创建。 -
服务:在代码中,通过
pServer->createService(SERVICE_UUID)创建。 -
特性:在代码中,通过
pService->createCharacteristic(CHARACTERISTIC_UUID, ...)创建。
问题 1:pServer->setCallbacks(new MyServerCallbacks()); 这一行代码的作用是什么?
解答:
这行代码的作用是为 BLE 服务器设置回调函数。具体来说,它将一个自定义的回调类 MyServerCallbacks 注册到 BLE 服务器 pServer 上。当设备连接或断开连接时,BLE 库会自动调用 MyServerCallbacks 类中的 onConnect 和 onDisconnect 方法,以便我们在这些事件发生时执行特定的操作。
问题 2:在什么情况下会调用服务器回调类?又是谁调用?
解答:
服务器回调类中的方法会在以下情况下被调用:
-
设备连接:当一个中央设备(如手机)连接到 ESP32 BLE 服务器时,BLE 库会调用
onConnect方法。 -
设备断开连接:当中央设备断开连接时,BLE 库会调用
onDisconnect方法。
这些方法是由 BLE 库自动调用的。当 BLE 库检测到连接或断开连接事件时,会触发相应的回调函数。
问题 3:pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE); 这段代码的作用是什么?| 符号是按位或吗?
解答:
这段代码的作用是创建一个新的 BLE 特性,并将其添加到服务中。具体步骤如下:
-
pService->createCharacteristic(...):这是一个方法调用,用于在服务中创建一个新的 BLE 特性。 -
CHARACTERISTIC_UUID:这是特性的唯一标识符,用于区分不同的特性。 -
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE:这是特性的属性,指定该特性支持读取和写入操作。
| 是按位或(bitwise OR)运算符,用于将多个标志(flags)组合在一起。在这个上下文中,它用于同时设置特性的读取和写入属性。
问题 4:BLECharacteristic 类是从哪里来的?
解答:
BLECharacteristic 类定义在我们引入的头文件中,如 BLEDevice.h 和 BLEServer.h。这些头文件包含了所有与 BLE 相关的类和函数。在代码的顶部,我们通过以下方式包含了这些头文件:
完整示例代码
#include <BLEDevice.h> // 包含 BLE 设备库
#include <BLEServer.h> // 包含 BLE 服务器库
#include <BLEUtils.h> // 包含 BLE 工具库
#include <BLE2902.h> // 包含 BLE 描述符库BLEServer *pServer = NULL; // 声明服务器指针
BLECharacteristic *pCharacteristic = NULL; // 声明 BLE 特性指针
bool deviceConnected = false; // 设备是否连接#define SERVICE_UUID "0000180f-0000-1000-8000-00805f9b34fb" // 服务的 UUID
#define CHARACTERISTIC_UUID "00002a19-0000-1000-8000-00805f9b34fb" // 特性的 UUID// 回调类,处理连接和断开事件
class MyServerCallbacks: public BLEServerCallbacks {void onConnect(BLEServer *pServer) {deviceConnected = true; // 设置设备连接标志Serial.println("客户端连接"); // 打印连接消息}void onDisconnect(BLEServer *pServer) {deviceConnected = false; // 重置设备连接标志Serial.println("客户端断开"); // 打印断开连接消息}
};// 回调类,处理特性写操作
class MyCharacteristicCallbacks: public BLECharacteristicCallbacks {void onWrite(BLECharacteristic *pCharacteristic) {string value = pCharacteristic->getValue();Serial.print("Received Value: ");for (int i = 0; i < value.length(); i++) {Serial.print(value[i]);}Serial.println();}
};void setup() {Serial.begin(115200); // 初始化串口通信Serial.println("Starting BLE work!"); // 打印初始化消息BLEDevice::init("ESP32_BLE"); // 初始化 BLE 设备,并命名// 创建服务器pServer = BLEDevice::createServer();// 设置服务器回调pServer->setCallbacks(new MyServerCallbacks());// 创建服务BLEService *pService = pServer->createService(SERVICE_UUID);// 创建特性,支持读、写和通知属性pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID,BLECharacteristic::PROPERTY_READ |BLECharacteristic::PROPERTY_WRITE |BLECharacteristic::PROPERTY_NOTIFY);// 添加描述符,使能通知pCharacteristic->addDescriptor(new BLE2902());// 设置特性回调pCharacteristic->setCallbacks(new MyCharacteristicCallbacks());// 启动服务pService->start();// 开始广播pServer->getAdvertising()->start();Serial.println("Waiting for a client connection to notify...");
}void loop() {if (deviceConnected) {// 更新特性值pCharacteristic->setValue("Hello World");// 通知客户端pCharacteristic->notify();// 通知间隔delay(1000);}
}
蓝牙控制舵机
#include <ESP32Servo.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>Servo servo1; // 定义 servo 对象
Servo servo2; // 定义 servo 对象
int minUs = 500; // 0度时的脉宽,单位us
int maxUs = 2500; // 180度时的脉宽,单位us
int servo1Pin = 37;
int servo2Pin = 36;
int pos = -1;
bool up = true;BLEServer *pServer = NULL; // 声明服务器指针
BLECharacteristic *pCharacteristic = NULL; // 声明 BLE 特性指针
bool deviceConnected = false; // 设备是否连接#define SERVICE_UUID "0000180f-0000-1000-8000-00805f9b34fb" // 服务的 UUID
#define CHARACTERISTIC_UUID "00002a19-0000-1000-8000-00805f9b34fb" // 特性的 UUIDvoid servo(unsigned char num) {while (num > 0) {for (int i = 0; i <= 360; i++) {if (pos == 181)up = false;else if (pos == -1)up = true;if (up)pos++;elsepos--;servo1.write(pos);servo2.write(180 - pos);delay(3);}num--;}Serial.println("完成一次摆动");
}// 回调类,处理连接和断开事件
class MyServerCallbacks: public BLEServerCallbacks {void onConnect(BLEServer *pServer) {deviceConnected = true; // 设置设备连接标志Serial.println("客户端连接"); // 打印连接消息}void onDisconnect(BLEServer *pServer) {deviceConnected = false; // 重置设备连接标志Serial.println("客户端断开"); // 打印断开连接消息}
};class MyCharacteristicCallbacks: public BLECharacteristicCallbacks {void onWrite(BLECharacteristic *pCharacteristic) {String value = pCharacteristic->getValue();Serial.print("Received Value: ");Serial.println(value); // 打印接收到的值if (value == "1") {servo(2);}}
};void setup() {Serial.begin(115200);ESP32PWM::allocateTimer(1); // 使用指定的硬件定时器servo1.setPeriodHertz(50); // 指定 PWM 的频率servo2.setPeriodHertz(50); // 指定 PWM 的频率servo1.attach(servo1Pin, minUs, maxUs);servo2.attach(servo2Pin, minUs, maxUs);// Serial.begin(115200); // 已在 setup 中初始化串口,可注释掉Serial.println("Starting BLE work!"); // 打印初始化消息BLEDevice::init("ESP32_BLE"); // 初始化 BLE 设备,并命名// 创建服务器pServer = BLEDevice::createServer();// 设置服务器回调pServer->setCallbacks(new MyServerCallbacks());// 创建服务BLEService *pService = pServer->createService(SERVICE_UUID);// 创建特性,支持读、写和通知属性pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID,BLECharacteristic::PROPERTY_READ |BLECharacteristic::PROPERTY_WRITE |BLECharacteristic::PROPERTY_NOTIFY);// 添加描述符,使能通知pCharacteristic->addDescriptor(new BLE2902());// 设置特性回调pCharacteristic->setCallbacks(new MyCharacteristicCallbacks());// 启动服务pService->start();// 开始广播pServer->getAdvertising()->start();Serial.println("Waiting for a client connection to notify...");
}void loop() {// 你的主循环代码
}
相关文章:
学习记录:ESP32控制舵机 FREERTOS BLE
控制舵机 PWM信号 PWM信号是一种周期性变化的方波信号,它有两个关键参数: 周期(Period):一个完整的PWM信号的时间长度,通常用秒(s)或毫秒(ms)表示。占空比…...
react中的useState和Hook、副作用
react的组件分为类组件和函数组件,Hook 是一种特殊的函数,可以让你在函数组件中使用类组件中才有的一些特性。useState、useEffect、useReducer都是Hook。其中useState用于在函数组件中添加状态,useEffect用于在函数组件中执行副作用…...
Linux嵌入式学习——数据结构——线性表的链式结构
线性表的链式存储 解决顺序存储的缺点,插入和删除,动态存储问题。 特点: 线性表链式存储结构的特点是一组任意的存储单位存储线性表的数据元素,存储单元可以是连续的,也可以不连续。可以被存储在任意内存未被占…...
文本编辑 文本中的各种空格
参考资料 欧文の半角スペースは2種類ある!?无中断空格常见空格一览浅析什么是零宽度字符以及零宽度字符在实际中的应用场景空格象形字间隔无中断空格零宽间隔 目录 零. 各种空格在Notepad中的效果一. 半角空格二. 全角空格三. TAB空格四. 无中断空格4.1 定义4.2 H…...
Vue插槽 (Slots)详解
目录 前言基础插槽具名插槽作用域插槽默认插槽动态插槽名总结相关阅读 前言 Vue的插槽(Slots)是一个非常强大的特性,它允许你在组件的模板中嵌入父组件的内容。插槽使得组件之间的内容分发变得灵活,尤其在构建可复用组件时非常…...
Unity中有关Animation的一点笔记
也许更好的阅读体验 Animation Unity中Animation类并不是直接记载了和播放动画有关的信息,可以简单理解Animation为一个动画播放器,播放的具体内容就像卡带一样,当我们有了卡带后我们可以播放动画。 对应的则是编辑器中的组件 所以Anima…...
module federation模块联邦与微前端
module federation是什么 webpack5新增了module federation,module federation的作用,将每个构建(build)作为容器(这是一个概念),构建后的资源可以正常部署,同时还具备在运行时对外暴露其中的模块,这就意味着多个构建…...
日常开发记录分享——C#控件ToolTip实现分栏显示内容
文章目录 需求来源实现思路实施请看VCR等等别走,有优化 需求来源 需要在鼠标浮动到指定位置后提示出详细的信息,一开始使用的tooltip实现,但是里面的内容效果并不理想,需要有条理性,于是就想到能不能将展示的东西分列…...
Kettle下载安装
环境说明 虚拟机:Win7;MySql8.0 主机:Win11;JDK1.8;Kettle 9.4(Pentaho Data Integration 9.4)(下载方式见文末) 安装说明 【1】解压后运行Spoon.bat 【2】将jar包 复…...
最新版Golang pprof使用(引入、抓取、分析,图文结合)
最新版Golang pprof使用 🔥具体实践: Go调试神器pprof使用教程Golang线上内存爆掉问题排查(pprof) Github地址:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/go-demo/go-pprof 引入pprof:import _ “net/http/pprof” …...
vue3学习记录1:emit的写法
emit是用于child组件向parent组件通信的工具,因为vue3的script可以设置为setup,写法同vue2有较大区别。 一、script setup - 直接写 <script lang"ts" setup>const emit defineEmits([close]);function handleClose() {emit(close);}…...
Visual Studio Code + vue快速安装配置Node.js+Vue+webpack+vscode
第一部分:Node.js 第一步:下载Node.js 方法1:链接 下载 | Node.js 中文网 (nodejs.cn) 方法2:百度网盘 链接:https://pan.baidu.com/s/1zIqu8H9rb_I1i-1OWD7swQ?pwdaurk 提取码:aurk --来自百度网盘…...
【Dart 教程系列第 49 篇】什么是策略设计模式?如何在 Dart 中使用策略设计模式
这是【Dart 教程系列第 49 篇】,如果觉得有用的话,欢迎关注专栏。 博文当前所用 Flutter SDK:3.22.1、Dart SDK:3.4.1 文章目录 一:什么是策略设计模式?二:为什么要使用策略设计模式࿱…...
BGP路由反射器
原理概述 缺省情况下,路由器从它的一个 IBGP对等体那里接收到的路由条目不会被该路由器再传递给其他IBGP对等体,这个原则称为BGP水平分割原则,该原则的根本作用是防止 AS内部的BGP路由环路。因此,在AS内部,一般需要每台…...
DolphinDB Web 端权限管理:可视化操作指南
在现代数据库管理中,高效和直观的权限管理对于用户的数据安全是至关重要的。过去 DolphinDB 用户需要依赖系统脚本来管理用户和权限,这对于缺乏技术背景的管理员来说既复杂又容易出错。 为了提升用户体验和操作效率,DolphinDB 目前在 Web 上…...
学习Vue2收藏这一篇就够了(如何创建Vue实例)
什么是Vue? Vue是什么:是一个用于构建用户界面的渐进式框架 什么是构建用户界面:基于数据动态渲染页面 什么是渐进式:循序渐进的学习 什么是框架:一整套完整的项目解决方案 创建Vue实例 核心步骤(4步…...
Mysql数据库第四次作业
mysql> create table student(sno int primary key auto_increment,sname varchar(30) not null unique,Ssex varchar(2) check (Ssex男 or Ssex女) not null,Sage int not null,Sdept varchar(10) default计算机 not null); mysql> create table Course(Con int primar…...
使用Docker搭建MySql的主从同步+ShardingSphere搭建Mysql的读写分离
参考课程 尚硅谷ShardingSphere5实战教程(快速入门掌握核心)_哔哩哔哩_bilibili 主服务器 创建容器 docker run -d \ -p 3306:3306 \ -v /kira/mysql/master/conf:/etc/mysql/conf.d \ -v /kira/mysql/master/data:/var/lib/mysql \ -e MYSQL_ROOT…...
数据结构:数据类型与抽象数据类型
数据类型与抽象数据类型 数据类型基本数据类型构造数据类型指针类型枚举类型 抽象数据类型(ADT)抽象数据类型的组成部分常见的抽象数据类型示例 数据类型与抽象数据类型的区别实现抽象数据类型的具体方式用数组实现栈用链表实现栈 总结 数据类型 数据类…...
西方逻辑史简介
西方逻辑史研究,对形式逻辑实现现代化,对加强西方哲学史研究,对开展科学方法论的研究都有重要意义。西方逻辑史一般被划分成古代、中世纪、现代三个历史时期。本文拟对这三个时期中的七个重要逻辑学家和逻辑学派:亚里士多德、斯多…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...
一些实用的chrome扩展0x01
简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序,无论是测试应用程序、搜寻漏洞还是收集情报,它们都能提升工作流程。 FoxyProxy 代理管理工具,此扩展简化了使用代理(如 Burp…...
【实施指南】Android客户端HTTPS双向认证实施指南
🔐 一、所需准备材料 证书文件(6类核心文件) 类型 格式 作用 Android端要求 CA根证书 .crt/.pem 验证服务器/客户端证书合法性 需预置到Android信任库 服务器证书 .crt 服务器身份证明 客户端需持有以验证服务器 客户端证书 .crt 客户端身份…...
用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法
用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法 大家好,我是Echo_Wish。最近刷短视频、看直播,有没有发现,越来越多的应用都开始“懂你”了——它们能感知你的情绪,推荐更合适的内容,甚至帮客服识别用户情绪,提升服务体验。这背后,神经网络在悄悄发力,撑起…...
