蓝牙防丢器Android源码解析:经典蓝牙SPP打通搜索连接到断开报警

简介:一份针对蓝牙防丢器APP开发的完整源码工程,面向需要学习BLE蓝牙通信与Android APP开发的初学者或进阶者。资源核心为BLEDemo3项目,涵盖设备扫描、连接管理、命令发送、事件响应及用户界面等模块,并基于Eclipse ADT环境构建,可帮助理解Android蓝牙低功耗开发的整体流程。包内共63个文件,以Java源码、XML界面布局、class编译文件为主,辅以APK安装包、JAR库、声音资源及项目配置文件,压缩后仅4.19MB,便于快速下载分析。当前已有734人浏览学习,说明主题受到关注。通过阅读源码,读者可以掌握BluetoothAdapter与BluetoothGatt的用法、GATT协议交互、连接生命周期管理以及UI实时状态显示等关键知识点,并可将方案迁移到钥匙、钱包、宠物等防丢场景,或进一步扩展远程控制与云服务功能。

1. 蓝牙防丢器:一套源码把“搜索-连接-报警”做成完整闭环

钥匙串上的防丢贴片和手机断开连接的那一刻,你正站在电梯口对着手机屏幕发呆——迟到的报警提醒让找钥匙变成了一场翻包现场。这套「蓝牙防丢器」源码解决的正是这个场景:它基于经典蓝牙协议,用 Java 在 Eclipse ADT 环境下实现了从设备搜索、RFCOMM socket 连接、链路监测到断开报警的完整防丢闭环。手机和防丢贴片之间维持一条长连接,一旦链路断开或连续多次检测异常,App 立刻响铃加震动,提醒你东西落下或已远离。适合正在做蓝牙外设配套 APP 的开发者,也适合想找一份完整 APP 源码对照学习蓝牙连接流程的初学者。工程体量不大,但搜索、连接、监测、报警四个环节的逻辑全部打通,跑通一遍基本能掌握蓝牙开发的主干。

2. 工程结构与技术选型:Eclipse ADT 时代的蓝牙开发骨架

2.1 技术选型:经典蓝牙 SPP 协议与 Java

这项目的选型带着明显的时代特征。Eclipse ADT 是 Android 早期的官方开发环境,对应 Android 4.x 时代,那时候 BLE 低功耗蓝牙还没普及,手机和防丢配件之间做实时双向通信,最稳的方案就是经典蓝牙的 SPP 串口协议。SPP 协议的实现方式很直白:手机端通过 BluetoothAdapter 拿到远端设备对象,用固定的 UUID 服务号创建一条 RFCOMM socket 通道,连接建立后双方在这条通道上收发数据。

对比今天主流的 BLE 方案,经典蓝牙的差异点很明显:

对比项经典蓝牙 SPPBLE 低功耗蓝牙
连接方式RFCOMM socketGATT 服务与 characteristic
功耗较高,适合持续供电设备极低,纽扣电池可用数月
传输速率高,适合大数据量低,适合小数据量控制指令
断开判断socket.isConnected() 直观依赖 RSSI 阈值或心跳包
代码复杂度阻塞式连接,逻辑简单异步回调,状态机较多

放到今天看,经典蓝牙的代码依然有学习价值:它把连接的建立、维护、断开讲得非常直白,先理解这套“建立 socket → 守护链路 → 断开报警”的模型,再去看 BLE 的 GATT 连接那一套异步回调,会轻松很多。而且不少工业级蓝牙设备至今还在用 SPP 协议,这份源码里的连接代码拆出来可以直接复用。

2.2 工程目录拆解:每个文件在防丢链路里的位置

拿到源码解压后,先别急着导入 Eclipse,把目录结构过一遍。典型的 ADT 工程长这样:

蓝牙防丢器/ ├── AndroidManifest.xml // 权限与组件声明 ├── src/com/antilost/demo/ │ ├── MainActivity.java // 主界面:搜索、连接、状态展示、报警按钮 │ ├── DeviceListActivity.java // 设备列表页:搜索结果的展示与选中回传 │ ├── ConnectThread.java // 连接线程:socket 建立与断开 │ ├── ConnectionMonitor.java // 连接监视线程:轮询链路状态 │ └── AlarmPlayer.java // 报警播放器:铃声 + 震动 ├── res/ │ ├── layout/ │ │ ├── activity_main.xml // 主界面布局:状态区 + 设备信息 + 按钮区 │ │ └── device_list.xml // 设备列表布局 │ ├── values/ │ │ ├── strings.xml // 字符串资源 │ │ └── colors.xml // 颜色资源 │ └── drawable/ │ └── ic_launcher.png // 应用图标 └── libs/ // 第三方库目录(本项目为空)

几个关键文件在防丢链路里的位置先记一下。MainActivity 是入口,负责蓝牙适配器的初始化和主界面刷新,搜索按钮、连接状态显示、报警停止按钮都在这;DeviceListActivity 是独立页面,搜索结果全部在这展示,用户点击某个设备后把设备对象通过 Intent 回传;ConnectThread 和 ConnectionMonitor 是核心,一个负责建立连接,一个负责守护连接,防丢判断全在 ConnectionMonitor 里;AlarmPlayer 只干一件事——响铃加震动,它被设计成独立类,和连接逻辑完全解耦。

这个分层有一个值得借鉴的设计:报警逻辑不直接写在 Activity 里,而是抽成 AlarmPlayer;连接线程也不直接操作 UI,而是通过 Handler 发消息。这样拆的好处是,后续如果要加“远程报警”“静音时段”“音量渐强”这些功能,只需要改 AlarmPlayer,不用动连接代码。

2.3 AndroidManifest 权限配置:蓝牙开发第一步

蓝牙开发的第一步是权限声明,漏了权限后面所有功能都会翻车。这份源码的 AndroidManifest.xml 里声明了三个权限:

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.antilost.demo"> <!-- 蓝牙基础权限:发起连接、查询状态 --> <uses-permission android:name="android.permission.BLUETOOTH" /> <!-- 蓝牙管理权限:开启蓝牙、发起搜索 --> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <!-- 震动权限:报警时手机震动提醒 --> <uses-permission android:name="android.permission.VIBRATE" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".DeviceListActivity" android:label="@string/device_list_title" /> </application> </manifest>

BLUETOOTH 权限是基础权限,用于发起连接、查询蓝牙状态这类常规操作;BLUETOOTH_ADMIN 权限级别更高,用于开启蓝牙、发起搜索这类管理性质的操作。两个权限缺一不可,少了 BLUETOOTH_ADMIN,startDiscovery 会直接抛 SecurityException。VIBRATE 权限是给 AlarmPlayer 的震动提醒用的,声明了它才能调用 Vibrator 服务。

注意:如果这份老代码要迁移到 Android 6.0 以上系统,还需要动态申请 ACCESS_FINE_LOCATION 定位权限。系统把蓝牙扫描归类到定位权限之下,不申请的话搜索功能会静默失败,这是老工程移植到新系统时最容易踩的坑。

3. 核心模块拆解:从设备搜索到报警响铃的完整链路

3.1 设备搜索:适配器发现与广播接收机制

搜索设备的核心是 BluetoothAdapter 的 startDiscovery() 方法。它发起的是异步搜索,结果通过系统广播返回,所以 MainActivity 里注册了一个 BroadcastReceiver 来接收两种关键广播:ACTION_FOUND(发现新设备)和 ACTION_DISCOVERY_FINISHED(本次搜索结束)。

// MainActivity.java 中的广播接收器 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // 发现新设备 if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent.getParcelableExtra( BluetoothDevice.EXTRA_DEVICE); short rssi = intent.getShortExtra( BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE); // 过滤已配对设备,只展示新的可连接设备 if (device.getBondState() != BluetoothDevice.BOND_BONDED) { mDeviceList.add(device); mAdapter.notifyDataSetChanged(); Log.d(TAG, "发现设备: " + device.getName() + " RSSI: " + rssi); } } // 搜索结束,更新界面按钮状态 else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { mSearching = false; updateUI(); } } };

启动搜索的代码在搜索按钮的点击事件里:

// 点击搜索按钮 public void onSearchClick(View view) { // 先取消已有搜索,避免重复搜索导致结果混乱 if (mBluetoothAdapter.isDiscovering()) { mBluetoothAdapter.cancelDiscovery(); } mBluetoothAdapter.startDiscovery(); mSearching = true; updateUI(); }

这段代码里有几个细节值得注意。EXTRA_RSSI 是设备信号强度,单位是 dBm,数值一般在 -40 到 -100 之间,越大表示信号越强、距离越近。源码把 RSSI 取出来显示在设备列表里,用户可以选择信号最强的设备优先连接。BOND_BONDED 是系统常量,表示设备已经和手机配对过,过滤掉这些设备可以让列表更干净,避免把以前连过的旧设备混进来。

广播接收器的生命周期管理是关键:必须在 onResume 里注册、在 onPause 里注销。漏掉注销的话,Activity 销毁后仍然能收到系统广播,轻则内存泄漏,重则在一个不存在的界面上操作导致空指针崩溃。把这行注销代码放在 onPause 里,能一次性解决整个项目里最容易犯的低级错误。

3.2 建立连接:ConnectThread 与 socket 生命周期

用户从设备列表选中一个设备后,DeviceListActivity 通过 Intent 把 BluetoothDevice 传回 MainActivity,然后 MainActivity 启动 ConnectThread 建立连接。这段代码是整个项目的核心:

// ConnectThread.java public class ConnectThread extends Thread { private final BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; public ConnectThread(BluetoothDevice device) { mmDevice = device; BluetoothSocket tmp = null; try { // SPP 串口服务 UUID,经典蓝牙通信的全球默认服务号 tmp = device.createRfcommSocketToServiceRecord( UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")); } catch (IOException e) { Log.e(TAG, "创建socket失败", e); } mmSocket = tmp; } @Override public void run() { // 连接前必须取消设备发现,否则会显著降低连接速度 mBluetoothAdapter.cancelDiscovery(); try { mmSocket.connect(); // 连接成功,通知主线程刷新UI mHandler.obtainMessage(MSG_CONNECT_SUCCESS).sendToTarget(); // 启动连接监视线程,开始防丢判断 new ConnectionMonitor(mmSocket).start(); } catch (IOException e) { Log.e(TAG, "连接失败", e); mHandler.obtainMessage(MSG_CONNECT_FAILED).sendToTarget(); try { mmSocket.close(); } catch (IOException closeException) { Log.e(TAG, "关闭socket失败", closeException); } } } }

UUID "00001101-0000-1000-8000-00805F9B34FB" 是 SPP 串口服务在全球统一注册的服务号,绝大多数经典蓝牙串口设备都认这个号。如果你的防丢器是定制固件,可能使用私有 UUID,这时要查设备厂商协议文档来替换。connect() 是一个阻塞调用,可能耗时几秒甚至更久,所以必须放在子线程里执行,放在主线程会直接触发 ANR,这是蓝牙开发入门最常见的一个翻车点。

连接成功后的动作也值得琢磨:源码没有停留在“连上就行”,而是立刻启动 ConnectionMonitor 守护线程。这个线程每隔一秒检查一次 socket 的连接状态,一旦发现异常就触发防丢报警。连接和守护分开两个线程,各司其职,后期想调整监测频率只需要改 ConnectionMonitor,不用动连接逻辑。

3.3 防丢判断与报警触发:连续失败确认机制

ConnectionMonitor 是防丢判断的核心,它解决一个关键问题:怎么区分“设备真丢了”和“信号瞬时波动”。如果检测到一次异常就报警,用户坐在工位上手机都会响个不停。源码用了一个连续失败计数机制:

// ConnectionMonitor.java public class ConnectionMonitor extends Thread { private final BluetoothSocket mSocket; private int mMissCount = 0; // 连续检测失败次数 private static final int MAX_MISS = 3; // 连续3次判定为丢失 public ConnectionMonitor(BluetoothSocket socket) { mSocket = socket; } @Override public void run() { while (!isInterrupted()) { // socket 状态异常视为一次失败 if (!mSocket.isConnected()) { mMissCount++; Log.w(TAG, "连接异常,第 " + mMissCount + " 次"); if (mMissCount >= MAX_MISS) { // 连续多次失败,判定设备丢失,通知主线程报警 mHandler.obtainMessage(MSG_DEVICE_LOST).sendToTarget(); break; } } else { mMissCount = 0; // 状态恢复,计数清零 } try { Thread.sleep(1000); // 每秒检查一次 } catch (InterruptedException e) { break; } } } }

MAX_MISS 设为 3 是有实际考量的。蓝牙信号在室内受墙壁、金属物体、人体遮挡影响很大,偶尔一次 isConnected() 返回 false 可能只是瞬时波动,立刻报警只会让用户每次都冲过去检查。连续 3 次检测失败(也就是 3 秒内持续异常)才判定为真丢失,这个阈值可以通过修改 MAX_MISS 调整:室内环境信号稳定可以改成 2,让报警更灵敏;户外复杂环境改成 5,减少误报。

主线程收到 MSG_DEVICE_LOST 消息后,调用 AlarmPlayer 的 startAlarm 方法:

// AlarmPlayer.java public void startAlarm(Context context) { // 使用系统闹钟铃声,音量高、辨识度强 Uri alertSound = RingtoneManager.getDefaultUri( RingtoneManager.TYPE_ALARM); mMediaPlayer = new MediaPlayer(); try { mMediaPlayer.setDataSource(context, alertSound); mMediaPlayer.setLooping(true); // 循环播放,直到用户手动停止 mMediaPlayer.prepare(); mMediaPlayer.start(); } catch (IOException e) { Log.e(TAG, "播放报警音失败", e); } // 同步触发震动,pattern: 停0ms、震500ms、停1000ms,循环 Vibrator vibrator = (Vibrator) context.getSystemService( Context.VIBRATOR_SERVICE); long[] pattern = {0, 500, 1000}; vibrator.vibrate(pattern, 0); }

选择 TYPE_ALARM 而不是默认通知音是有意为之。通知音音量偏小,放在口袋里很容易听不见;闹钟铃声的音量和辨识度都更高,是防丢报警场景下更合理的选择。震动模式 {0, 500, 1000} 的意思是立即开始、震动 500 毫秒、停止 1000 毫秒,然后循环。这样的节奏比持续震动更容易让人注意到,也不容易和普通消息提醒混淆。停止报警时调用 stopAlarm(),同步释放 MediaPlayer 和 Vibrator,避免资源占用。

到这里,整个防丢链路完成了闭环:搜索设备 → 建立 socket 连接 → 每秒守护链路 → 连续 3 次异常触发报警 → 响铃加震动直到用户确认。这套逻辑看着简单,但已经是防丢产品的最小完整实现。

4. 运行与调试:从 Eclipse 导入到真机跑通的全过程

4.1 环境搭建:Eclipse ADT 与 JDK 版本匹配

这项目是老工程,跑起来最关键的是环境版本匹配。Eclipse ADT 本身已经停止更新,但跑老工程绰绰有余。我常用的组合是 JDK 1.7 + ADT 23.0.2 + Android SDK 4.4(API 19),这套组合跑绝大多数 ADT 时代的工程都顺滑。如果本机装的是新版 JDK,注意在 Eclipse 的 Installed JREs 里把编译级别切到 1.7,否则工程可能因为字节码版本不兼容报错。

导入步骤就四条:Eclipse 菜单 File → Import → Existing Android Code Into Workspace,选中解压后的工程目录,勾选 Copy projects into workspace,然后点 Finish。如果导入后出现红色报错,先看是不是 R.java 没有生成——右键项目选择 Project → Clean,让它强制重建资源文件,大部分情况这一步就好使了。如果还报错,看 Console 面板里的具体信息,最常见的两类:一是 SDK 版本不匹配,右键项目 Properties → Android,把 Target 换成已安装的 SDK 版本;二是 JDK 编译级别不匹配,按上段的方式调整。

注意:Eclipse 对中文工程路径支持不好,工程所在目录最好全英文,否则可能出现资源文件无法编译的怪异问题,属于环境类的玄学坑。

4.2 真机调试:为什么必须用真机以及老代码的新系统适配

蓝牙功能在模拟器上是跑不起来的,必须真机调试,而且需要有实际的蓝牙防丢设备配合。调试前先打开手机蓝牙,把防丢器调到配对模式。如果手头没有现成的防丢器,用一块支持 SPP 的蓝牙串口模块也能替代,连接逻辑完全一致。

这里有个实际经验:老工程最好用 Android 8.0 以下的手机跑。Android 8.0 起系统对隐式广播做了严格限制,老代码里注册的部分系统广播可能收不到;而且运行时权限机制变化较大,蓝牙扫描需要动态申请定位权限。如果手头只有新手机,需要在 MainActivity 里补一段权限申请代码,把 ACCESS_COARSE_LOCATION 和 ACCESS_FINE_LOCATION 在 startDiscovery 之前申请到,优先级很高——不申请的话搜索不到任何设备,而且不报错,排查起来特别费劲。

4.3 调试技巧:日志驱动的问题定位

调试时建议打开 LogCat,过滤 Tag 为 "Antilost" 的日志。源码里关键节点都有 Log 输出:搜索开始、发现设备、连接成功、连接失败、链路断开。照着日志走一遍流程,整个状态机一目了然。我在调试时会做一次“走远测试”:连接成功后把防丢器放在桌上,人往后退 10 到 15 米,观察日志里 MSG_DEVICE_LOST 出现的时间点。这个时间点加上当时日志里的 RSSI 值,就是验证报警灵敏度设置是否合理的最直接数据。

连接失败时不要瞎猜,按顺序排查会快很多。先看 ConnectThread 输出的异常堆栈:如果是 "read failed, socket might closed",说明远端设备主动断了连接,可能是防丢器没进入配对模式或电量不足;如果是 "Service discovery failed",说明 UUID 对不上,确认防丢器使用的服务号;如果是 "Connection timed out",说明设备在范围内但连接握手超时,重启一下防丢器再试。按这套顺序能解决八成问题,剩下两成就靠多试几台设备验证——蓝牙兼容性这东西,偶尔会给你来点玄学惊喜。

5. 避坑指南:蓝牙防丢器开发中五个高频问题

蓝牙防丢器的开发流程看着不长,实际跑起来几乎没有一次是顺风顺水的。下面这五个问题是我在调试过程中反复遇到的,每条都按“现象 → 原因 → 解决”的顺序记录,照着排查能省下不少时间来。

5.1 搜索不到任何设备

现象:点击搜索按钮后列表一直为空,LogCat 里连 ACTION_FOUND 广播都没出现。

原因:最常见的是权限没配齐,或者防丢器没有进入可发现模式。还有一个隐蔽原因:Android 6.0 及以上系统把蓝牙扫描归到定位权限管理之下,老工程如果没做动态权限申请,搜索功能会在新系统上静默失败,连日志都没有。

解决:先确认 AndroidManifest 里 BLUETOOTH 和 BLUETOOTH_ADMIN 权限都在;再把防丢器重新进入配对模式,一般是长按按键 5 秒直到指示灯闪烁;如果跑在新系统上,在 MainActivity 里补上运行时定位权限申请,拿到权限后再调用 startDiscovery。

5.2 配对成功但连接一直失败

现象:设备能搜到,也能配对,但点击连接后几秒钟报连接失败,日志显示 "Service discovery failed"。

原因:UUID 不匹配。防丢器的服务端用的是私有 UUID,而代码里写死的是 SPP 默认值,两端服务号对不上,socket 握手自然失败。

解决:查设备厂商协议文档,拿到私有 UUID 替换 createRfcommSocketToServiceRecord 的参数。没有文档的,用通用蓝牙调试工具扫描设备服务,把扫描到的 UUID 填进去。实在不行还有一个民间偏方:用 createRfcommSocket(1) 直接指定通道号建立连接,跳过服务发现,但这个方法在不同设备上的兼容性差异大,不到万不得已不推荐。

5.3 报警误报频繁,坐在工位上都响

现象:设备明明放在桌上,距离不到一米,App 却每隔几分钟响一次,不胜其烦。

原因:ConnectionMonitor 的检测逻辑太敏感。socket 的 isConnected() 在信号受遮挡时会返回假阴性,加上源码只检测一次异常就累加计数,阈值设得太低,瞬时波动直接触发了报警。

解决:把 MAX_MISS 从 3 调大到 5,相当于连续 5 秒异常才报警。同时把 isConnected() 的判断换成实际读数据:尝试从 socket 输入流读一个字节,读到才算连接正常,读不到才计一次失败。两种手段一起上,误报率基本可以降到零。

5.4 切到后台后 App 被杀,防丢失效

现象:手机锁屏放口袋里,过几分钟切回来看,App 已经被系统回收,防丢报警自然不响,等于白装。

原因:老工程没有做后台保活。ConnectionMonitor 是普通线程,Activity 被系统回收后线程也跟着没了。国产 ROM 的省电策略还会主动杀后台进程,这个问题在新手机上尤其严重。

解决:把 ConnectionMonitor 迁移到一个前台服务里,用 startForeground 发一个常驻通知,让系统知道这个服务不能杀。同时监听灭屏广播,在灭屏时保持服务运行。这是防丢类 App 必须做的一步改造,不做的话“防丢”在后台场景下就是摆设。

5.5 RSSI 显示不稳定,没法判断距离

现象:设备列表里 RSSI 数值跳来跳去,同一位置一会儿 -50 一会儿 -80,根本没法用来看距离。

原因:RSSI 本身受环境干扰大,电磁波在室内被墙体、金属反射后会形成多径效应,数值波动是常态。老代码直接显示原始采样值,没有做平滑处理,所以看起来特别跳。

解决:对 RSSI 做滑动平均滤波,取最近 5 次采样的平均值再显示。或者用低通滤波:newValue = oldValue * 0.7 + currentValue * 0.3,让数值变化更平滑。滤波之后 RSSI 稳定很多,用来做距离判断才靠谱。

6. 二次开发:把防丢器改造成自己的蓝牙工具

6.1 首选改造:向 BLE 低功耗蓝牙迁移

如果想把这份老工程用到现代硬件上,第一件要做的事是迁移到 BLE。经典蓝牙和 BLE 的 API 差异很大:搜索从 startDiscovery 换成 BluetoothLeScanner 的 startScan;连接从建立 RFCOMM socket 换成连接 GATT 服务,通过回调接收连接状态;数据收发从 InputStream 换成 characteristic 的读写。迁移过程里,原来 ConnectThread 那套阻塞式连接可以整体废弃,改成异步回调模式。BLE 的优点是功耗低,防丢贴片用纽扣电池能撑一年,缺点是断开判断不像 socket 那么直观,要依赖 RSSI 阈值或者定期读 characteristic 里的心跳字段。

6.2 验证方法与实用功能补强

迁移完先别急着加功能,验证方法很重要:把防丢器放在固定位置,人拿着手机后退,每隔 1 米记录一次 RSSI 和连接状态,画出距离与信号强度的关系曲线,用这条曲线校准报警阈值,而不是凭感觉填一个数。之后可以再加一个 SQLite 断开记录表,把每次断开的时间、设备地址、当时的 RSSI 存下来,用户能回看“昨天下午 3 点设备在 -70dBm 时断开”,结合时间线推断丢失位置。这个功能实现不难,大幅提升 App 的实用价值。

从那以后我每次拿到蓝牙类项目,都强制先走一遍“老代码跑通 → 画清状态机 → 再改功能”的流程,不急着动代码,先把连接、监测、报警这三条线在纸上理一遍。这套蓝牙防丢器源码正是练这个流程的好素材——工程小、链路完整、坑点又足够典型,跑通一遍收获会很大,希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

永磁同步电机MTPA+弱磁+前馈补偿双闭环控制仿真解析

永磁同步电机MTPA+弱磁+前馈补偿双闭环控制仿真解析

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

2026/10/21 1:26:01 阅读更多 →
电压电流检测方案全解析:从分压电阻到隔离ADC的选型指南

电压电流检测方案全解析:从分压电阻到隔离ADC的选型指南

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

2026/10/21 1:26:01 阅读更多 →
Octop 自托管 AI 助手 API 文档实战指南:5 步从开启 /api/docs 到调通第一个接口

Octop 自托管 AI 助手 API 文档实战指南:5 步从开启 /api/docs 到调通第一个接口

Octop 自托管 AI 助手 API 文档实战指南&#xff1a;5 步从开启 /api/docs 到调通第一个接口 【免费下载链接】Octop A smarter, self-hosted AI assistant — multi-user, multi-agent. 项目地址: https://gitcode.com/GitHub_Trending/oct/Octop Octop 是一个多用户、…

2026/10/21 1:26:01 阅读更多 →

最新新闻

Terminal-Bench完整指南:4步跑通你的第一个AI终端评测

Terminal-Bench完整指南:4步跑通你的第一个AI终端评测

Terminal-Bench完整指南&#xff1a;4步跑通你的第一个AI终端评测 【免费下载链接】terminal-bench-1 A benchmark for LLMs on complicated tasks in the terminal 项目地址: https://gitcode.com/GitHub_Trending/tb/terminal-bench-1 你的Agent演示很精彩&#xff0c…

2026/10/21 2:02:39 阅读更多 →
免 root 三步跑通 WorkTool:你的企业微信自动回复机器人

免 root 三步跑通 WorkTool:你的企业微信自动回复机器人

免 root 三步跑通 WorkTool&#xff1a;你的企业微信自动回复机器人 【免费下载链接】worktool 一款安全稳定的Android无障碍服务工具&#xff0c;支持控制企微/微信来运行的无人值守群管理企业微信机器人 项目地址: https://gitcode.com/GitHub_Trending/wo/worktool W…

2026/10/21 2:02:39 阅读更多 →
STM32驱动W25Q64 SPI Flash实战:从硬件连接到代码实现

STM32驱动W25Q64 SPI Flash实战:从硬件连接到代码实现

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

2026/10/21 2:02:39 阅读更多 →
如何用 Jasna 自动修复 VR180 视频:从分眼处理到投影选择的完整新手教程

如何用 Jasna 自动修复 VR180 视频:从分眼处理到投影选择的完整新手教程

如何用 Jasna 自动修复 VR180 视频&#xff1a;从分眼处理到投影选择的完整新手教程 【免费下载链接】jasna Video restoration tool 项目地址: https://gitcode.com/GitHub_Trending/ja/jasna Jasna 是一款面向 GPU 的视频修复工具&#xff0c;内置 VR180 自动修复能力…

2026/10/21 2:02:39 阅读更多 →
gamdl:Apple Music 无损下载工具完全指南,一键收藏歌曲与 4K 音乐视频

gamdl:Apple Music 无损下载工具完全指南,一键收藏歌曲与 4K 音乐视频

gamdl&#xff1a;Apple Music 无损下载工具完全指南&#xff0c;一键收藏歌曲与 4K 音乐视频 【免费下载链接】gamdl A command-line app for downloading Apple Music songs, music videos and post videos. 项目地址: https://gitcode.com/GitHub_Trending/ga/gamdl …

2026/10/21 2:02:39 阅读更多 →
Django数据可视化实践:超市销售系统全流程开发与热词分析

Django数据可视化实践:超市销售系统全流程开发与热词分析

毕业设计做到一半才意识到"选对题"比“写代码”重要得多——这是我在带几个应届生做完整套毕设流程后最深的体会。今天这篇想好好拆解一个很有代表性的题目&#xff1a;基于PythonDjango的超市销售系统&#xff0c;以及其中数据可视化模块的具体实现。这个选题好在哪…

2026/10/21 2:01:38 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事&#xff1a;AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里&#xff0c;按宣传口径&#xff0c;本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”&#xff0c;而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起&#xff1a;用户到底在追求什么“封号重开”这四个字&#xff0c;放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流&#xff0c;背后是大量的时间和精力成本。正常情况下&#xff0c;没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”&#xff0c;而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时&#xff0c;就意识到&#xff1a;单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 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/20 10:02:08 阅读更多 →
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/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练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/20 9:50:45 阅读更多 →