基于ESP32的远程开关灯控制(ESP32+舵机+Android+物联网云平台)
目录
- 材料+环境准备
- 物理材料
- 软件环境
- 物联网平台配置(MQTT)
- MQTT
- 阿里云平台配置
- 创建产品
- 添加设备
- 自定义topic
- esp32配置
- 接线
- 代码
- Android部分和云平台数据流转
前言:出租屋、宿舍网上关灯问题,计划弄一个智能开关以及带一点安防能力,如检测有人进入之类的,同时也是回顾一下以前掌握的知识。
材料+环境准备
本身这个东西不难做,网上也有了很多的实现案例,没什么技术难度,但是有一定的实用效果。
本设计分为三个部分:ESP32(硬件)、Android、阿里云
物理材料
esp32开发板:控制核心、网络连接
舵机(SG90-180°):用于控制拨动开关实现开关灯
杜邦线:连接模块或者进行跳线接线
供电部分:考虑供电部分的话,需要锂电池、充放电模块、电烙铁等焊接工具。
扩展:超声波模块,用于检测是否有人进入,或者实现夜间回家自动开灯等。
软件环境
esp32 arduino开发环境,需要设置首选项,json、下载esp32开发板的包(耗时长,可能失败),网上有一些离线安装的方法教程可以参考。
esp32 esp-idf框架,这个也是一个esp32的开发环境,可以在vscode上集成,听说用起来很方便,和arduino二选一即可,我目前用的arduino,配置好了esp-idf框架还未使用(感觉编译要太长的时间了。。。。)
android studio:用于开发android软件,方便调试,自己开发具有更高的扩展性,方便连接到云平台,这个需要一定的基础,最好以前弄过,不然搭环境下软件也挺费时间。
阿里云:主要也是为了实现无距离限制控制,这就是上云的好处,此外也可以实现,如果不在家,有人进了房子配合超声波或者激光实现提醒入侵检测的功能。此外也许也可以通过其他的物联网平台和对应的app实现远程控制,比如电灯科技还可以加入米家,实现小爱语言远程控制,但是相对而言可扩展性小。
物联网平台配置(MQTT)
首先要介绍云平台是因为其他两个部分都需要连接到云平台,所以这里的配置有限,另外就是介绍mqtt协议进行介绍,方便理解后续代码的含义。
MQTT
八股文我就不说了,直接说大概实现的原理和通讯的过程。
条件:设备联网可以使用mqtt协议、云平台(中间做消息流转)
云平台首先要创建产品、创建设备,产品可以理解为一个类,设备是实现的子类,这些子类可以有一些共同的特性,这是产品里面定义的。一个设备就是对应实际的一个物理设备,创建好设备后,会有对应的密码、设备名称、ID之类的一些标志,用来将你的设备连接到云平台。这应该就是mqtt协议中规定好的。
MQTT是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议。发布就是指发布消息,你发布消息给云平台,或者云平台发布给你。订阅就是你订阅某一个主题(topic),这样云平台就可以通过这个主题来给你发布消息。
消息流转:设备和云平台可以通过订阅和发布的方式进行消息传递,但是设备和设备之间无法直接进行通讯,需要通过云平台来进行中间消息转发。这个是通过云平台实现的,云平台设置即可,无需代码实现。
阿里云平台配置
本次设计是通过阿里云物联网平台实现相应的功能。
阿里云物联网平台
注册登录,阿里云物联网平台提供免费的公共实例可以试用。
创建产品

添加设备
然后为对应产品添加设备,需要添加两个设备,一个是esp32的,一个是手机app的。

创建好设备后可以查看设备,里面有相关的连接信息。

这些信息都需要在后续中使用,届时进行查看复制进入对应代码区即可。
自定义topic
注:产品可以设置物理模型,设置一些共有属性,这样物联网平台也可以进行控制和查看对应的属性,设置好物理模型后,会有专门对应的topic进行发送相关属性的消息,但同样也包含了一些其他无关信息,为了方便我可以可以自定义topic,这些操作都需要在产品中区定义实现。
定义topic类可以自己命名topic,并选择类型,发布还是订阅,或者两个都选,订阅的话表示这个主题是用来订阅的,也就是设备订阅了这个主题后,可以从云台中看到,并通过这个订阅发布消息。
发布的主题指的就是设备用来发布消息的,将消息传递至云平台的topic。
如果不需要向云平台发布消息,可以不进行定义,产品中需要手机向云平台发布开灯消息,所以需要进行定义。
我定义了两个主题一个是舵机的,一个是超声波,主要是想实现两个功能,都是基于发布和订阅的,这有利有弊,比较方便吧。

区别可以从下面的例子中看出来
我的设备定义了三个topic,分别是物理模型,自带的一个属性设置的topic,两个自定义topic

产品物理模型自带的topic

可以看到自带的主题的操作权限,只有订阅,也就是设备用来订阅,无法进行发布,这样在设备列表中订阅的topic中无法实现发布消息功能,而另外两个自定义主题由于设置了发布和订阅,所以订阅后,同时也可以进行消息发布。
本节的主要目的创建产品,创建对应的自定义主题,创建两个设备获取连接信息,用于设备进行mqtt通讯连接。
esp32配置
本节主要介绍esp32的配置和物理设备的接线。
接线

首先看接线,由于用到超声波,需要的也是5v的电压供电,但是开发板只有一个5V口,此外还要和充放电模块接线,所以这里肯定需要将几根线剪开连接起来,并用电工胶带绑好。
如果你只有一个就直接用杜邦线就可以,但是考虑供电也需要接线,或者找一个带usb口的充放电模块,这样直接用usb供电给开发板也可以。
如果自行设计PCB自然就会更方便,初步测试时就没有打板。
代码
现阶段,代码非常简单,因为有很多的封装库实现,所以控制代码调用函数就可以,设置都不用去管什么pwm控制,设置占空比什么什么的,非常方便,esp32和esp8266这些arduino的都是一样的,确实是加快了开发进程。
控制代码非常简单,主要就是上云,连接到阿里云了。
以下就是完整代码
主要包含功能:
ESP32连接wifi
初始化舵机(控制信号线),控制舵机(设置角度)
超声波测距,并自动开关灯(根据时间) getDistance()
上报(到云)人员经过超声波检测的时间,可以进行记录查看到有人进出房间的时间
(超声波这部分代码注销了,原因有很多,一个是不便调试,另外超声波的布置位置很不方便,也容易导致误触,线长的问题,另外就是房间的问题,不便部署,激光模块效果更优)
阿里云mqtt通讯连接的代码,回调处理代码,就是接收到云平台的订阅消息时做出的逻辑处理
aliyun_callback()
还有连接和订阅操作,这些都比较简单调用函数即可,这里的回调中实现了两种方式,一种是基于产品的物理模型提供的topic进行订阅和操作,一个是自定义的进行操作,自定义的topic相对比较简单。
#include <WiFi.h>
#include <ESP32Servo.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>#include <NTPClient.h>
#include <WiFiUdp.h>
#include <time.h> // 用于时间处理// 定义 NTP 客户端
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 8 * 3600, 60000);
// 使用 pool.ntp.org 服务器,时区偏移为 8 小时(东八区)// WiFi配置
const char* ssid = "*****";//wifi名称
const char* password = "******";//wifi密码// 阿里云物联网平台配置
const char* mqtt_server = ""; // 替换为你的阿里云IoT endpoint
const char* mqtt_username = ""; // 替换为你的设备名称和产品Key
const char* mqtt_password = ""; // 替换为你的设备密钥
const char* mqtt_client_id = ""; // 替换为你的客户端ID
// esp32_601
// MQTT主题
const char* servo_topic = ""; // 替换为你的舵机控制主题
const char* person_detected_topic = ""; // 替换为你的人员检测主题WiFiClient espClient; //创建网络连接客户端
PubSubClient mqttClient(espClient); //通过网络客户端连接创建mqtt连接客户端// 舵机引脚
const int servoPin = 27;
Servo myServo;//调试后的固定开关等角度值
const int light_off = 112;
const int light_on = 72;
// 这两个值需要自己进行调试,此外我设置的是90°的时候舵机是横向的
// 超声波引脚
const int trigPin = 25;
const int echoPin = 26;// led引脚
const int led0Pin = 2;
//板载led可以用来调试观察现象
// 初始化LED
void setupLED() {pinMode(led0Pin, OUTPUT);digitalWrite(led0Pin, LOW); // 初始状态为关闭
}// 初始化舵机
void setupServo() {myServo.attach(servoPin); // 将舵机连接到指定引脚myServo.write(90); // 初始角度设为90度
}// 初始化超声波传感器
void setupUltrasonic() {pinMode(trigPin, OUTPUT);pinMode(echoPin, INPUT);
}// 超声波测距函数
float getDistance() {digitalWrite(trigPin, LOW);delayMicroseconds(2);digitalWrite(trigPin, HIGH);delayMicroseconds(10);digitalWrite(trigPin, LOW);long duration = pulseIn(echoPin, HIGH); // 读取回波时间float distance = duration * 0.034 / 2; // 计算距离(单位:厘米)return distance;
}//初始化连接wifi
void setup_wifi() {delay(10);Serial.println();Serial.print("Connecting to ");Serial.println(ssid);WiFi.begin(ssid, password);while (WiFi.status() != WL_CONNECTED) {delay(500);Serial.print(".");}Serial.println("");Serial.println("WiFi connected");Serial.print("IP address: ");Serial.println(WiFi.localIP());
}void aliyun_callback(char* topic, byte* payload, unsigned int length) {Serial.print("Message arrived [");Serial.print(topic);Serial.print("] ");for (int i = 0; i < length; i++) {Serial.print((char)payload[i]);}Serial.println();//上面是打印接收到的订阅信息,调试用// 处理舵机控制消息 --- 物理模型版本// if(strcmp(topic, "/sys/${}/${设备名}/thing/service/property/set")== 0){// // 将 payload 转换为字符串// char message[length + 1];// memcpy(message, payload, length);// message[length] = '\0'; // 添加字符串结束符// Serial.println(message);// // 解析 JSON 数据// StaticJsonDocument<200> doc; // 根据 JSON 数据大小调整缓冲区大小// DeserializationError error = deserializeJson(doc, message);// // 检查解析是否成功// if (error) {// Serial.print("Failed to parse JSON: ");// Serial.println(error.c_str());// return;// }// // {"method":"thing.service.property.set","id":"1107300794","params":{"servo":1},"version":"1.0.0"} 这是物理模型消息模板,真正需要的数据就是{"servo":1}// // 提取 JSON 字段// int servo_angle = doc["params"]["servo"]; // 获取 command 字段// // int angle = doc["angle"]; // 获取 angle 字段// // int duration = doc["duration"]; // 获取 duration 字段// myServo.write(servo_angle);//设置角度// }// 处理舵机控制消息 自定义模型,自己定义主题topic和解析,比较简单方便,不需要多余的处理if (strcmp(topic, servo_topic) == 0) {//获取消息 获取设置值// 将字节数组转换为字符串char payloadStr[length + 1]; // 加1是为了加上字符串结束符 '\0'memcpy(payloadStr, payload, length);payloadStr[length] = '\0'; // 确保字符串结束// 假设 payloadStr 是一个整数的文本格式 消息体内容只有舵机的设置参数为纯数字int intValue = atoi(payloadStr);Serial.print("Received integer value: ");Serial.println(intValue);if(intValue>=0 && intValue<=180){myServo.write(intValue); // 控制舵机转动到90度delay(500);myServo.write(90);//设置完后自动调整为90,这样不会卡住手动拨动灯开关}}
}void reconnect() {while (!mqttClient.connected()) {Serial.print("Attempting MQTT connection...");if (mqttClient.connect(mqtt_client_id, mqtt_username, mqtt_password)) {Serial.println("connected");mqttClient.subscribe(servo_topic);mqttClient.subscribe(person_detected_topic);//连接成功后订阅主题,这样才能接收到消息} else {Serial.print("failed, rc=");Serial.print(mqttClient.state());Serial.println(" try again in 5 seconds");delay(5000);}}
}
void setup() {Serial.begin(115200);setupLED();setupServo(); // 初始化舵机setupUltrasonic(); // 初始化超声波传感器setup_wifi(); //初始化连接wifi// 初始化 NTP 客户端timeClient.begin();timeClient.update(); // 获取时间//mqtt 初始化配置mqttClient.setServer(mqtt_server, 1883);mqttClient.setCallback(aliyun_callback);
}void loop() {if (!mqttClient.connected()) {reconnect();}mqttClient.loop();// 主循环中可以添加其他逻辑// 固件升级 先不考虑// 检测到有人进入 --两种情况,无人,报警// 检测有人进入,上报时间,获取时间并上报,同时根据时间判断是否开灯// float distance = 0;// distance = getDistance();未进行测量似乎结果为不确定的?不为0,不对可能为0// Serial.println("当前距离: " + String(distance));// if(distance < 50 && distance > 0){ //超声波// //有人经过// //获取时间// // 更新 NTP 时间// timeClient.update();// // 获取当前小时数(24小时制)// int currentHour = timeClient.getHours();// if(currentHour >= 18 || currentHour < 7) { // 如果是18点(即下午6点)以后或早上6点之前// myServo.write(light_on);// Serial.println("It's evening or night, turning the light on.");// delay(500);// myServo.write(90);// } else {// // digitalWrite(ledPin, LOW); // 关灯// myServo.write(light_off);// Serial.println("It's daytime, turning the light off.");// delay(500);// myServo.write(90);// }// //上报消息// unsigned long epochTime = timeClient.getEpochTime();// // 将 Unix 时间戳转换为格式化字符串时间// time_t rawTime = (time_t)epochTime;// struct tm *ptm = gmtime(&rawTime);// char timeStr[30];// strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", ptm); // // 打印完整的时间字符串// Serial.print("Current date and time: ");// Serial.println(timeStr);// // 发布人员检测消息// String message = "Person detected at " + String(timeStr);// mqttClient.publish(person_detected_topic, message.c_str());// }delay(200); // 延时20m秒
}
代码中都有详细的注释,不懂可以评论区留言。
// 阿里云物联网平台配置
const char* mqtt_server = ""; // 替换为你的阿里云IoT endpoint
const char* mqtt_username = ""; // 替换为你的设备名称和产品Key
const char* mqtt_password = ""; // 替换为你的设备密钥
const char* mqtt_client_id = ""; // 替换为你的客户端ID
// esp32_601
// MQTT主题
const char* servo_topic = ""; // 替换为你的舵机控制主题
const char* person_detected_topic = ""; // 替换为你的人员检测主题
这些就是上一节提到的创建设备时,可以查阅的相关连接参数,以及创建产品的时候自定义的消息topic
注意、踩坑: 这里有一个需要注意的地方就是主题这里,从云平台复制出来的主题,往往包含${deviceName}这个部分,这里需要替换为你对应的设备名称,记住是设备名称别复制错了。
/*********/${deviceName}/user/person_detect
假如你的设备名称是esp32
替换为
/*********/esp32/user/person_detect
另外回调函数中的处理
// 处理舵机控制消息 自定义模型,自己定义主题topic和解析,比较简单方便,不需要多余的处理if (strcmp(topic, servo_topic) == 0) {
这里就是判断,到来的消息是哪个主题的,是否为舵机控制主题的,是才进入舵机主题消息的解析,因为发布的消息可能有别的主题的消息,不判断直接解析可能会出错,不同的topic的解析也不一样,因为数据内容可能不一样。
Android部分和云平台数据流转
剩下的部分就下一篇再介绍吧,码字不易,点个赞吧,有什么问题欢迎评论区留言。
关注我分享更多的实用小设计。💕
相关文章:
基于ESP32的远程开关灯控制(ESP32+舵机+Android+物联网云平台)
目录 材料环境准备物理材料软件环境 物联网平台配置(MQTT)MQTT阿里云平台配置创建产品添加设备自定义topic esp32配置接线代码 Android部分和云平台数据流转 前言:出租屋、宿舍网上关灯问题,计划弄一个智能开关以及带一点安防能力…...
协议-ACLLite-ffmpeg
是什么? FFmpeg是一个开源的多媒体处理工具包,它集成了多种功能,包括音视频的录制、转换和流式传输处理。FFmpeg由一系列的库和工具组成,其中最核心的是libavcodec和libavformat库。 libavcodec是一个领先的音频/视频编解码器库&…...
ARM嵌入式学习--第十四天(SPI)
SPI -介绍 SPI(Serial Peripheral Interface)串行外围设备接口。是由Motorola公司开发,用来在微控制器和外围设备芯片之间提供一个低成本,易使用的接口。这样接口可以用来连接存储器、AD转换器、DA转换器、实时时钟、LCD驱动器、…...
DeepSeek-V2 论文解读:混合专家架构的新突破
论文链接:DeepSeek-V2: A Strong, Economical, and Efficient Mixture-of-Experts Language Model 目录 一、引言二、模型架构(一)多头部潜在注意力(MLA):重塑推理效率(二)DeepSeekM…...
5分钟了解回归测试
1. 什么是回归测试(Regression Testing) 回归测试是一个系统的质量控制过程,用于验证最近对软件的更改或更新是否无意中引入了新错误或对以前的功能方面产生了负面影响(比如你在家中安装了新的空调系统,发现虽然新的空…...
路由器如何进行数据包转发?
路由器进行数据包转发的过程是网络通信的核心之一,主要涉及以下几个步骤: 接收数据包:当一个数据包到达路由器的一个接口时,它首先被暂时存储在该接口的缓冲区中。 解析目标地址:路由器会检查数据包中的目标IP地址。…...
【HarmonyOS之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(四) -> 常见组件(一)
目录 1 -> List 1.1 -> 创建List组件 1.2 -> 添加滚动条 1.3 -> 添加侧边索引栏 1.4 -> 实现列表折叠和展开 1.5 -> 场景示例 2 -> dialog 2.1 -> 创建Dialog组件 2.2 -> 设置弹窗响应 2.3 -> 场景示例 3 -> form 3.1 -> 创建…...
iOS 自动翻滚广告条(榜单条)实现方案
引言 在直播场景中,榜单信息、活动公告或者广告推广通常需要以醒目的方式展示,但由于屏幕空间有限,一次只能显示一条内容。为了让用户能够持续关注这些信息,我们可以实现一个自动翻滚的广告条(或榜单条)&a…...
TensorFlow深度学习实战(7)——分类任务详解
TensorFlow深度学习实战(7)——分类任务详解 0. 前言1. 分类任务1.1 分类任务简介1.2 分类与回归的区别 2. 逻辑回归3. 使用 TensorFlow 实现逻辑回归小结系列链接 0. 前言 分类任务 (Classification Task) 是机器学习中的一种监督学习问题,…...
动态规划问题——青蛙跳台阶案例分析
问题描述: 一只青蛙要跳上n级台阶,它每次可以跳 1级或者2级。问:青蛙有多少种不同的跳法可以跳完这些台阶? 举个例子: 假设台阶数 n 3 ,我们来看看青蛙有多少种跳法。 可能的跳法: 1. 跳1级…...
element-ui使用el-table,保留字段前的空白
项目名称项目编号1、XXXXX1111111111111111111 1.1 XXXXX11111111111111222222222 如上表格中,实现项目名称字段1.1前空白的效果。 从JAVA返回的数据带有空白,即数据库中插入的数据带有空白。 原先写法: <el-table><el-tabl…...
kamailio中路由模块汇总
功能模块描述请求路由 (request_route)主要处理进入的SIP请求,包含初步检查、NAT检测、CANCEL请求处理、重传处理等。处理通过REQINIT、NATDETECT、RELAY等子模块的调用。CANCEL处理对CANCEL请求进行处理,包括更新对话状态并检查事务。如果事务检查通过&…...
如何使用 DeepSeek 搭建本地知识库
使用 DeepSeek 搭建本地知识库可以帮助您高效管理和检索本地文档、数据或知识资源。以下是详细的步骤指南: 1. 准备工作 (1) 安装 DeepSeek 确保您的系统已安装 Python 3.8 或更高版本。使用 pip 安装 DeepSeek: bash pip install deepseek (2) 准备…...
网络HTTP详细讲解
学习目标 什么是HTTPHTTP的请求和响应常见的HTTP状态码HTTP的安全性 什么是HTTP?HTTP的请求和响应,常见的HTTP状态码,HTTP的安全性 什么是HTTP HTTP(HyperText Transfer Protocol,超文本传输协议)是一种用…...
《Origin画百图》之边际分布曲线图
《Origin画百图》第六集——边际分布曲线图 入门操作可看《30秒,带你入门Origin》 边际分布曲线图,其中包含散点图形,而在图的边际有着分布曲线图。在比较数据以查看多个变量之间是否存在关系时非常有用。 1.数据准备:为多列XY数…...
【Milvus】向量数据库pymilvus使用教程
以下是根据 Milvus 官方文档整理的详细 PyMilvus 使用教程,基于 Milvus 2.5.x 版本: PyMilvus 使用教程 目录 安装与环境准备连接 Milvus 服务数据模型基础概念创建集合(Collection)插入数据创建索引向量搜索删除操作完整示例注…...
React 生命周期函数详解
React 组件在其生命周期中有多个阶段,每个阶段都有特定的生命周期函数(Lifecycle Methods)。这些函数允许你在组件的不同阶段执行特定的操作。以下是 React 组件生命周期的主要阶段及其对应的生命周期函数,并结合了 React 16.3 的…...
第 26 场 蓝桥入门赛
2.对联【算法赛】 - 蓝桥云课 问题描述 大年三十,小蓝和爷爷一起贴对联。爷爷拿出了两副对联,每副对联都由 N 个“福”字组成,每个“福”字要么是正的(用 1 表示),要么是倒的(用 0 表示&#…...
组合(力扣77)
从这道题开始,我们正式进入回溯算法的学习。之前在二叉树中只是接触到了一丢丢,而这里我们将使用回溯算法解决很多经典问题。 那么这道题是如何使用回溯算法的呢?在讲回溯之前,先说明一下此题是如何递归的。毕竟回溯递归不分家&a…...
网络工程师 (22)网络协议
前言 网络协议是计算机网络中进行数据交换而建立的规则、标准或约定的集合,它规定了通信时信息必须采用的格式和这些格式的意义。 一、基本要素 语法:规定信息格式,包括数据及控制信息的格式、编码及信号电平等。这是协议的基础,确…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
【Veristand】Veristand环境安装教程-Linux RT / Windows
首先声明,此教程是针对Simulink编译模型并导入Veristand中编写的,同时需要注意的是老用户编译可能用的是Veristand Model Framework,那个是历史版本,且NI不会再维护,新版本编译支持为VeriStand Model Generation Suppo…...
