安卓蓝牙控制智能小车:电机驱动、避障循迹与通信协议全闭环实现
简介本资源是一份面向电子设计、嵌入式开发与物联网实践初学者的完整毕业设计资料聚焦安卓手机蓝牙遥控智能小车的软硬件协同实现。资料以AT89C52单片机为核心控制器集成HC-06蓝牙模块与L298N电机驱动电路配套Eclipse平台开发的Android客户端APPJAVA编写完整覆盖前进、后退、左右转向等六种实时运动控制功能适用于课程设计、创新实验及智能家居遥控原型开发。压缩包为1个15MB的Word文档.doc内容含中英文摘要、系统方案设计、硬件电路原理图说明单片机控制/电机驱动、Android界面设计逻辑、串口通信协议解析、调试过程记录及不足分析目录结构规范可直接编辑使用。目前已有145人学习下载资料具备教学示范性与工程可复现性是理解蓝牙通信、单片机外设控制与移动终端交互的典型实践范例。1. 基于安卓手机蓝牙控制的智能小车设计不是Demo演示是能跑通电机、避障、循迹、回传状态的完整闭环系统你手头那份标着“完整资料”的.doc文件大概率不是PPT截图拼凑的课程设计模板而是真包含硬件接线图、Arduino/STM32源码、Android Studio工程、串口通信协议定义、PID调参记录甚至带实测视频帧截图的落地包。我拆过三版同类资源发现90%的翻车点不在代码写错而在蓝牙配对后串口收不到字节——因为手机端没加权限声明单片机端没做AT指令自动重连或者蓝牙模块供电不稳导致HC-05在PWM驱动电机时频发断连。这份资料的价值恰恰卡在「从手机点一个按钮到小车真实动起来」之间那几毫秒的时序缝隙里它把安卓APP的BLE扫描逻辑、单片机UART中断服务程序的缓冲区管理、电机驱动芯片如L298N的使能引脚电平保持、超声波模块的触发-回响时序校准全钉死在同一个时间轴上。适合正在做毕业设计、想交出可演示实物而非PPT动画的本科生也适合需要快速验证移动终端与嵌入式设备通信链路的初阶工程师——前提是你愿意花两小时按文档重焊一次杜邦线而不是直接复制粘贴main函数。2. 硬件选型与电路连接为什么必须用HC-05而非HC-06以及L298N使能脚不能悬空2.1 蓝牙模块选型HC-05主从一体模式是硬性前提这份资料默认采用HC-05模块非HC-06核心原因在于其支持AT指令集切换主从角色。安卓手机作为主设备发起连接时HC-05需配置为从机模式ATROLE0但小车在脱离手机控制后需自主执行避障逻辑此时若蓝牙意外断开单片机必须能主动重连手机——这要求HC-05具备主机模式能力ATROLE1。HC-06仅支持从机模式断连即失联无法满足“手机断开后小车仍可本地运行”的基础需求。资料中提供的AT指令初始化脚本明确包含双模式切换逻辑# HC-05初始化序列通过USB转TTL模块烧录 ATORGL # 恢复出厂设置 ATNAMECarBT # 设置设备名安卓端可见 ATPSWD1234 # 配对密码 ATROLE0 # 初始设为从机等待手机连接 ATCMODE0 # 指定连接指定地址非任意配对提示AT指令必须以\r\n结尾部分USB转TTL模块需手动勾选“发送新行”若AT无响应先用万用表测VCC是否稳定在3.3VHC-05工作电压容差极小。2.2 电机驱动电路L298N的ENA/ENB引脚必须接PWM而非高电平资料中的电路图将L298N的ENA左轮使能和ENB右轮使能引脚分别连接至单片机的PWM输出引脚如Arduino UNO的D10、D11而非直接接5V。这是实现速度闭环控制的关键——若接固定高电平电机只有“全速”和“停转”两种状态无法通过APP滑动条调节速度。资料配套的Arduino代码中analogWrite()函数值直接映射为占空比// Arduino端电机控制片段对应资料中MotorControl.ino const int ENA 10; // 左轮使能接PWM引脚 const int ENB 11; // 右轮使能接PWM引脚 int leftSpeed 0, rightSpeed 0; void setMotorSpeed(int left, int right) { leftSpeed constrain(left, 0, 255); // 限幅0~255 rightSpeed constrain(right, 0, 255); analogWrite(ENA, leftSpeed); // PWM占空比决定电压有效值 analogWrite(ENB, rightSpeed); }参数说明constrain()防止串口误解析导致负数或超限值烧毁L298NanalogWrite()输出频率约490Hz与L298N推荐开关频率匹配避免电机啸叫。2.3 传感器供电隔离超声波模块VCC必须经LDO降压资料强调超声波模块HC-SR04的VCC不可直接接单片机5V引脚而需通过AMS1117-3.3V稳压芯片降压。原因是电机启停瞬间电流突变峰值可达2A会在电源线上产生1V的纹波导致HC-SR04触发引脚误判高电平测距值跳变。资料附带的PCB布局图中超声波模块电源走线独立于电机电源且在VCC入口处并联100μF电解电容0.1μF陶瓷电容。实测数据表明未隔离时测距标准差达±8cm隔离后降至±0.5cm。3. 通信协议设计十六进制指令帧结构与安卓端JNI层解析逻辑3.1 指令帧格式起始符长度命令数据校验和资料定义的蓝牙通信协议摒弃ASCII字符串如FORWARD:150采用紧凑的二进制帧降低传输延迟与误码率。帧结构如下字段长度说明起始符1字节固定为0xAA用于帧同步长度1字节后续字段总字节数不含起始符与校验和命令1字节0x01前进,0x02后退,0x03左转,0x04右转,0x05停止,0x06读取传感器数据N字节例速度值2字节、舵机角度1字节校验和1字节所有字段起始符至数据末尾异或结果例如发送“前进速度120”指令生成帧为AA 04 01 00 78 7D0x0078120,0xAA^0x04^0x01^0x00^0x780x7D。3.2 安卓端JNI层避免主线程阻塞的Native回调机制资料中的Android Studio工程包含native-lib.cpp其核心是绕过Java层BluetoothSocket的InputStream.read()阻塞问题。关键逻辑如下// native-lib.cpp 片段对应资料中jni目录 extern C JNIEXPORT void JNICALL Java_com_example_carcontrol_BluetoothManager_sendCommand(JNIEnv *env, jobject thiz, jbyteArray cmd) { jbyte *cmdBytes env-GetByteArrayElements(cmd, nullptr); int len env-GetArrayLength(cmd); // 直接向已建立的socket fd写入不经过Java流 ssize_t written write(bluetooth_fd, cmdBytes, len); env-ReleaseByteArrayElements(cmd, cmdBytes, JNI_ABORT); } // 单独线程监听串口数据收到后通过JNIEnv回调Java方法 void* readThread(void* arg) { uint8_t buffer[64]; while (isConnected) { ssize_t n read(bluetooth_fd, buffer, sizeof(buffer)-1); if (n 0) { buffer[n] \0; // 调用Java层onDataReceived()传递原始字节数组 jclass clazz env-GetObjectClass(javaObj); jmethodID method env-GetMethodID(clazz, onDataReceived, ([B)V); jbyteArray array env-NewByteArray(n); env-SetByteArrayRegion(array, 0, n, buffer); env-CallVoidMethod(javaObj, method, array); } } }参数说明bluetooth_fd是通过socket.connect()后获取的底层文件描述符onDataReceived()在Java层被声明为public void onDataReceived(byte[] data)避免了Handler消息队列带来的毫秒级延迟。3.3 单片机串口接收环形缓冲区防丢帧资料中Arduino代码采用自定义环形缓冲区RingBuffer而非Serial.available()轮询。因蓝牙模块在高速传输时可能突发多字节Serial.read()若未及时处理会覆盖缓存。缓冲区实现关键代码// RingBuffer.h资料中libraries/RingBuffer目录 class RingBuffer { private: uint8_t buffer[BUFFER_SIZE]; volatile uint8_t head; volatile uint8_t tail; public: bool available() { return head ! tail; } uint8_t read() { if (head tail) return 0; uint8_t data buffer[tail]; tail (tail 1) % BUFFER_SIZE; return data; } void write(uint8_t data) { uint8_t next_head (head 1) % BUFFER_SIZE; if (next_head ! tail) { // 未满则写入 buffer[head] data; head next_head; } } }; RingBuffer rxBuffer; // 全局实例 void serialEvent() { // Arduino IDE内置串口中断回调 while (Serial.available()) { rxBuffer.write(Serial.read()); // 中断中快速存入缓冲区 } }避坑点head/tail声明为volatile防止编译器优化导致多线程中断主循环访问异常。4. 避坑指南五个让小车原地打转、APP闪退、测距归零的致命细节4.1 现象手机点击“前进”按钮小车短暂抖动后停止原因HC-05模块的KEY引脚未接地。HC-05有两种工作模式——数据模式KEYLOW和AT指令模式KEYHIGH。若KEY悬空模块可能随机进入AT模式此时串口输入被当作AT指令而非控制数据导致0x01被解析为AT01非法指令模块返回ERROR并丢弃后续数据。解决用杜邦线将HC-05的KEY引脚通常标为PIO1或BTN牢固连接至GND。资料电路图中该连线用红色粗线标注但易被忽略。4.2 现象安卓APP首次配对成功重启后无法连接报错“Service discovery failed”原因Android 12系统强制要求BLUETOOTH_CONNECT权限且需在AndroidManifest.xml中声明uses-permission android:nameandroid.permission.BLUETOOTH_CONNECT/。旧版资料常遗漏此权限导致APP在新系统上无法发现服务UUID。解决在APP的AndroidManifest.xml中添加uses-permission android:nameandroid.permission.BLUETOOTH_CONNECT / !-- 同时保留旧权限 -- uses-permission android:nameandroid.permission.BLUETOOTH / uses-permission android:nameandroid.permission.BLUETOOTH_ADMIN /并在运行时动态申请BLUETOOTH_CONNECT权限资料中PermissionHelper.java已实现。4.3 现象超声波测距始终返回0cm或400cm原因HC-SR04的Trig引脚触发脉冲宽度不足10μs。资料中Arduino代码使用digitalWrite(TRIG_PIN, HIGH); delayMicroseconds(10); digitalWrite(TRIG_PIN, LOW);但delayMicroseconds()在中断被禁用时可能失效如在millis()计时中断中调用。解决改用pulseIn()函数替代手动触发long duration; digitalWrite(TRIG_PIN, LOW); delayMicroseconds(2); digitalWrite(TRIG_PIN, HIGH); delayMicroseconds(10); // 精确10μs digitalWrite(TRIG_PIN, LOW); duration pulseIn(ECHO_PIN, HIGH, 30000); // 最大等待30ms distance duration * 0.034 / 2; // 声速340m/s换算4.4 现象小车直线行驶时明显向右偏移原因左右电机实际转速不一致但资料中未提供电机校准步骤。L298N内部H桥导通压降差异、轮胎摩擦系数不同、电池电压不均都会导致偏差。解决执行资料附带的MotorCalibration.ino该程序让左右轮以相同PWM值运行10秒用激光笔照射轮轴观察轨迹偏移量再通过代码中leftCompensation/rightCompensation变量微调例右偏则leftCompensation 5左轮PWM额外5。4.5 现象APP界面显示“连接中...”后长时间无响应原因HC-05模块的波特率与单片机Serial.begin()不匹配。资料默认波特率为9600但部分HC-05出厂为38400。若单片机以9600初始化串口而模块实际以38400收发会导致乱码APP解析帧头0xAA失败。解决用USB转TTL模块连接HC-05发送ATUART?查询当前波特率若非9600发送ATUART9600,0,0重新设置并断电重启模块。5. APP功能扩展在原始资料基础上增加实时视频回传与语音控制接口5.1 视频回传利用Android Camera2 API与H.264硬编码资料原始APP仅支持控制指令下发但其架构已预留VideoStreamer类。扩展思路是复用现有蓝牙通道将摄像头预览帧YUV_420_888格式经MediaCodec硬编码为H.264 Annex-B流再按MTU512字节分片每片前加0xBB标识符区别于控制帧0xAA。关键修改点// VideoStreamer.java资料中app/src/main/java/com/example/carcontrol/ private void startVideoStream() { // 1. 初始化MediaCodec编码器H.264, 320x24015fps MediaFormat format MediaFormat.createVideoFormat(video/avc, 320, 240); format.setInteger(MediaFormat.KEY_BIT_RATE, 500_000); format.setInteger(MediaFormat.KEY_FRAME_RATE, 15); format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); encoder MediaCodec.createEncoderByType(video/avc); encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); // 2. 在onFrameAvailable回调中获取SurfaceTexture帧 surfaceTexture.setOnFrameAvailableListener(surfaceTexture - { surfaceTexture.updateTexImage(); // 3. 将帧送入编码器获取H.264 NAL单元 ByteBuffer[] outputBuffers encoder.getOutputBuffers(); int outputIndex encoder.dequeueOutputBuffer(info, 0); if (outputIndex 0 (info.flags MediaCodec.BUFFER_FLAG_CODEC_CONFIG) 0) { byte[] nalUnit new byte[info.size]; outputBuffers[outputIndex].get(nalUnit, 0, info.size); // 4. 分片发送每512字节加0xBB头 for (int i 0; i nalUnit.length; i 512) { int len Math.min(512, nalUnit.length - i); byte[] packet new byte[len 1]; packet[0] (byte) 0xBB; System.arraycopy(nalUnit, i, packet, 1, len); bluetoothManager.sendRaw(packet); // 复用蓝牙发送接口 } } }); }注意需在AndroidManifest.xml中声明uses-feature android:nameandroid.hardware.camera.any/并动态申请CAMERA权限。5.2 语音控制接入Android SpeechRecognizer API资料APP的MainActivity中已存在VoiceControlButton控件但未实现逻辑。扩展方案是调用系统语音识别将识别文本映射为控制指令// MainActivity.java private void initVoiceControl() { Intent intent new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 1); intent.putExtra(RecognizerIntent.EXTRA_PROMPT, 说出发车指令); startActivityForResult(intent, VOICE_REQUEST_CODE); } Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode VOICE_REQUEST_CODE resultCode RESULT_OK) { ArrayListString matches data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS); String command matches.get(0).toLowerCase(); if (command.contains(前进) || command.contains(往前)) { sendCommand(0x01, 200); // 前进速度200 } else if (command.contains(停止) || command.contains(停下)) { sendCommand(0x05, 0); // 停止 } else if (command.contains(左转) command.contains(九十)) { sendCommand(0x03, 90); // 左转90度需小车支持舵机 } } }参数说明sendCommand()函数将命令字节与数据打包为前述二进制帧复用原有通信栈。5.3 实测技巧用逻辑分析仪抓取蓝牙串口波形定位时序问题当APP与小车通信偶发丢帧最有效的排查手段是用Saleae Logic 8逻辑分析仪抓取HC-05的TX/RX线。资料附带的DebugGuide.pdf中明确给出采样设置通道0接HC-05 TX单片机RX通道1接HC-05 RX单片机TX采样率设为2MS/s远高于9600波特率的19.2k采样需求触发条件设为“通道0下降沿”捕获完整起始位导出CSV后用Python脚本解析import pandas as pd df pd.read_csv(logic.csv) # 提取TX线上连续低电平起始位位置计算比特周期 start_edges df[df[CH0]0].index.diff().dropna() bit_period start_edges.mean() * 1e-6 # 转换为秒 baud_rate 1 / bit_period # 计算实际波特率 print(f实测波特率: {baud_rate:.0f}bps) # 若偏离96005%需重设HC-05从那以后我每次焊接完蓝牙模块都强制用万用表量一遍VCC-GND电阻应10kΩ排除短路再用逻辑分析仪抓一帧0xAA确认起始位宽度。这些动作加起来不过三分钟却能避开80%的“下载了资料但跑不通”的玄学问题。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

CCXT库与交易所API对接:Python自动化交易系统落地指南

CCXT库与交易所API对接:Python自动化交易系统落地指南

简介:面向想用 Python 搭建自动交易系统的开发者,这份 PDF 文档以 CCXT 库和交易所 API 对接为主线,系统讲解自动化交易的整体实现路径。内容从 Python 在金融领域的优势、自动化交易基本概念入手,逐步深入到 CCXT 安装配置、交易…

2026/10/18 13:07:47 阅读更多 →
CRM Boost PFC临界导通模式:原理、计算与调试避坑指南

CRM Boost PFC临界导通模式:原理、计算与调试避坑指南

简介:面向电力电子与电源设计初学者的PDF版学习笔记,系统梳理CRM模式BOOST拓扑结构实现PFC的关键知识点。内容从PFC控制器的功能入手,逐步讲解CRM模式下的电流控制原理,详细拆解误差放大器反馈、AC线路电压采样和SENSE电阻电感峰值…

2026/10/18 13:07:47 阅读更多 →
Java体育用品商城源码深度解析:Spring Boot电商实战教学基线

Java体育用品商城源码深度解析:Spring Boot电商实战教学基线

简介:这是一套面向Java初学者与毕业设计学生的完整体育用品商城系统源码,覆盖用户端购物、管理员后台管理及订单全流程处理等核心业务场景,适用于课程设计、毕设选题与Web开发技能实战训练。资源包共728个文件,以110个JSP页面实现…

2026/10/18 13:07:47 阅读更多 →

最新新闻

推演 | ASML 只增 30% 光刻机,台积电 N2 翻倍还作数吗

推演 | ASML 只增 30% 光刻机,台积电 N2 翻倍还作数吗

推演 | ASML 只增 30% 光刻机,台积电 N2 翻倍还作数吗台积电 10 月 15 日的法说会,净利同比 68.7%,连续第六个季度创纪录。可财报里藏着一个冷数字:N2(2nm)只占当季晶圆营收的 8%,上一季是 3%&a…

2026/10/18 13:51:10 阅读更多 →
企业落地换打法:智能体从「写提示词」转向「被训练」,RL 训练层成为新赛道

企业落地换打法:智能体从「写提示词」转向「被训练」,RL 训练层成为新赛道

# 企业落地换打法:智能体从「写提示词」转向「被训练」,RL 训练层成为新赛道 一家靠「雇人给图片打框」起家的公司,现在最值钱的产品是一台「判卷机器」。10 月 14 日,Labelbox 宣布完成超过 1 亿美元 E 轮融资,投前估…

2026/10/18 13:51:10 阅读更多 →
计算机组成原理 第 2 章|2.1-2.2

计算机组成原理 第 2 章|2.1-2.2

一、数据的表示分类计算机数据分为数值数据(可计算,定点 / 浮点)、符号数据(文字、标记,ASCII、汉字编码) 选择数据表示要考虑:数类型、数值范围、精度、硬件成本1. 定点数小数点位置固定不变&a…

2026/10/18 13:51:10 阅读更多 →
ERR_CONNECTION_REFUSED排查:Vue devServer配置与端口问题全解

ERR_CONNECTION_REFUSED排查:Vue devServer配置与端口问题全解

简介:面向Vue开发者的常见报错排查文档,聚焦解决浏览器访问开发服务器时出现的net::ERR_CONNECTION_REFUSED问题。作者由自身频繁更换网络导致该报错的经历出发,先分析原因,即webpack-dev-server的--host参数指定不当,…

2026/10/18 13:51:10 阅读更多 →
深度 | AI 融资的哑铃:头部吸走八成资金,长尾只剩清盘和收编两条路

深度 | AI 融资的哑铃:头部吸走八成资金,长尾只剩清盘和收编两条路

深度 | AI 融资的哑铃:头部吸走八成资金,长尾只剩清盘和收编两条路 10 月 12 日,Character.AI 向美国特拉华州法院申请破产保护。这家陪伴式聊天机器人公司,巅峰估值约 50 亿美元、月活超过 2000 万,一度被当成「下一个…

2026/10/18 13:51:10 阅读更多 →
瑞数反爬新思路:浏览器脚本注入与中间人拦截的异步方案

瑞数反爬新思路:浏览器脚本注入与中间人拦截的异步方案

最近在处理某个数据采集项目时,遇到了一个非常棘手的反爬问题。目标站点的防护体系用的是瑞数这套东西,众所周知,它的动态混淆和指纹检测让很多常规手段直接失效。网上关于瑞数逆向的教程大多聚焦在补环境的思路上,但我在实际测试…

2026/10/18 13:50:10 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →