新闻详情

新闻详情

首页 / 资讯中心 / 详情

ESP32+MAX30102+OLED:DIY心率血氧监测仪全攻略

发布时间:2026/9/20 3:08:46来源:尧图网络
ESP32+MAX30102+OLED:DIY心率血氧监测仪全攻略
1. 从一堆散件到一台能用的健康监测仪整体设计思路1.1 这个项目到底在做什么手头有一块 ESP32 开发板、一个 MAX30102 心率血氧模块、一块 0.96 寸 OLED 屏外加几根杜邦线。目标很明确把这三样东西拼在一起做成一台能实时显示心率、血氧饱和度的小型健康监测仪。插上电手指往传感器上一放OLED 屏幕上就能跳出当前的心率和血氧数值不需要连电脑不需要额外的仪器。听起来简单但真正动手做的时候你会发现事情没那么轻松。MAX30102 出来的原始数据是一堆红外和红光的 ADC 采样值直接显示毫无意义OLED 屏幕要正确点亮I2C 地址、初始化序列、供电电压都得对ESP32 的 I2C 引脚分配、上拉电阻、时钟频率也会影响整个系统的稳定性。这些问题一个接一个每一个都可能让你卡上半天。这个项目适合谁如果你刚接触 ESP32学过一点 MicroPython 或者 Arduino想找一个既有实用性又能把 I2C 通信、传感器驱动、屏幕显示串起来的综合练习那这个项目非常合适。它不像点个 LED 那么简单但也不至于复杂到让人放弃。做完之后你对 I2C 总线的理解、对传感器数据处理的思路、对嵌入式项目调试的方法都会上一个台阶。1.2 为什么选 ESP32 MAX30102 OLED 这个组合市面上做心率监测的方案不少为什么偏偏选这三个凑一起这里面有讲究。ESP32 的核心优势在于它同时具备 Wi-Fi 和蓝牙能力双核 240MHz 的主频处理传感器数据绰绰有余GPIO 资源丰富I2C 接口可以灵活映射到几乎任意引脚。更重要的是它的生态非常成熟MicroPython 和 Arduino 两种开发方式都有大量现成库可以用。对于这个项目来说ESP32 的性能是过剩的但过剩意味着你后续想加功能——比如把数据传到手机、加个温度传感器、驱动更大的屏幕——都不用换主控。MAX30102 是一颗集成度很高的心率血氧传感器内部集成了红光 LED、红外 LED、光电探测器、光学元件和低噪声信号处理电路。它通过 I2C 接口输出原始数据最高采样率可以到 3200Hz内置 FIFO 可以缓存 32 组样本。相比自己搭光电电路用 MAX30102 省去了大量模拟电路设计的麻烦。它的工作电压是 1.8V 核心电压加 3.3V 或 5V 的 LED 供电模块化产品通常已经做好了电平转换直接接 ESP32 的 3.3V 就行。OLED 选 0.96 寸 I2C 版本原因就两个字省事。I2C 接口只需要两根信号线和 MAX30102 可以挂在同一条总线上只要地址不冲突就行。0.96 寸的尺寸刚好能显示心率和血氧两个核心数值分辨率 128x64 足够显示清晰的数字和简单图标。SSD1306 驱动芯片的库非常成熟MicroPython 和 Arduino 都有现成支持。1.3 系统架构与数据流整个系统的数据流是这样的MAX30102 以一定频率采集红光和红外光的反射信号通过 I2C 总线把原始数据传给 ESP32。ESP32 拿到数据后先做滤波处理去掉高频噪声和基线漂移然后用峰值检测算法找出心跳周期计算心率同时利用红光和红外光吸收比值的经验公式估算血氧饱和度。最后把计算好的数值格式化通过另一路 I2C 或者同一路 I2C 发给 OLED 屏幕显示。这里有个关键点MAX30102 和 OLED 如果挂在同一条 I2C 总线上必须确保它们的 I2C 地址不冲突。MAX30102 的固定地址是 0x577 位地址OLED 的 SSD1306 常见地址是 0x3C 或 0x3D两者不冲突可以共用一条总线。ESP32 作为 I2C 主机轮流和两个从机通信这在 MicroPython 里用同一个 I2C 对象就能实现。注意虽然可以共用总线但 MAX30102 的数据读取频率较高如果 OLED 刷新太频繁可能会影响传感器数据的实时性。建议 OLED 刷新率控制在 5-10Hz传感器采样率保持在 50-100Hz。2. 硬件连接与 I2C 总线那些坑2.1 引脚分配与接线方案先把手上的模块认清楚。ESP32 开发板常见的有 ESP32-DevKitC、NodeMCU-32S、ESP32-WROOM-32 等引脚编号在不同板子上可能标注不一样但 GPIO 编号是统一的。我一般选 GPIO21 作为 SDAGPIO22 作为 SCL这是 ESP32 默认的 I2C 引脚大部分库都认这个默认值。接线的时候注意几点MAX30102 模块通常有 VIN、GND、SCL、SDA、INT 五个引脚。VIN 接 ESP32 的 3.3VGND 共地SCL 接 GPIO22SDA 接 GPIO21INT 引脚如果不用中断可以悬空。OLED 模块一般有 VCC、GND、SCL、SDA 四个引脚VCC 接 3.3VGND 共地SCL 和 SDA 分别接到和 MAX30102 相同的总线上。模块引脚ESP32 引脚说明MAX30102VIN3.3V供电不要接 5VMAX30102GNDGND共地MAX30102SCLGPIO22I2C 时钟线MAX30102SDAGPIO21I2C 数据线MAX30102INT悬空不用中断可不接OLEDVCC3.3V供电OLEDGNDGND共地OLEDSCLGPIO22与传感器共用OLEDSDAGPIO21与传感器共用2.2 I2C 上拉电阻最容易忽略的细节I2C 总线是开漏输出结构这意味着总线上的设备只能把线拉低不能主动拉高。所以必须要有上拉电阻把线拉到高电平否则总线永远处于低电平状态通信直接失败。很多模块板子上已经自带了 4.7k 或 10k 的上拉电阻但有些便宜的模块为了省成本没焊这时候你就得自己加。怎么判断需不需要加上拉电阻用万用表测一下 SDA 和 SCL 对 VCC 的电阻如果读到 4.7k 或 10k 左右说明板载已经有了。如果读到无穷大那就得自己在 SDA 和 SCL 上各接一个 4.7k 电阻到 3.3V。上拉电阻太小会导致功耗增加和信号上升沿过陡太大则上升沿太慢导致通信失败。4.7k 在 3.3V 系统、100kHz 到 400kHz 的速率下是比较稳妥的选择。实操心得我曾经遇到过一块 OLED 模块单独用的时候正常和 MAX30102 挂一起就点不亮。查了半天发现是 OLED 模块板载上拉电阻是 10kMAX30102 模块板载也是 10k两个并联变成 5k理论上没问题但实际因为走线电容和模块质量波形已经烂到无法识别。后来把 OLED 的上拉电阻拆掉只保留 MAX30102 的问题解决。所以多个模块共用总线时上拉电阻不是越多越好并联后总阻值不要低于 2.2k。2.3 I2C 地址扫描与设备确认接线完成后第一件事不是急着写驱动而是先扫描 I2C 总线确认两个设备都能被识别到。MicroPython 下用下面这段代码from machine import Pin, I2C i2c I2C(0, sclPin(22), sdaPin(21), freq400000) devices i2c.scan() print(I2C devices found:, [hex(d) for d in devices])正常应该输出类似[0x3c, 0x57]的结果。如果只看到一个或者一个都没有先检查接线是否松动、供电是否正常、上拉电阻是否到位。如果看到一堆乱七八糟的地址通常是总线干扰或者上拉电阻不合适导致的。Arduino 环境下可以用 Wire 库的扫描示例#include Wire.h void setup() { Wire.begin(21, 22); Serial.begin(115200); Serial.println(I2C Scanner); } void loop() { byte error, address; int nDevices 0; for (address 1; address 127; address) { Wire.beginTransmission(address); error Wire.endTransmission(); if (error 0) { Serial.print(Found device at 0x); Serial.println(address, HEX); nDevices; } } if (nDevices 0) Serial.println(No I2C devices found); delay(5000); }扫描到设备只是第一步接下来要确认设备能正常读写。MAX30102 的 Part ID 寄存器地址是 0xFF读出来应该是 0x15。如果读出来是 0x00 或者 0xFF说明通信有问题可能是地址错了或者芯片没正常工作。3. MAX30102 驱动与数据处理核心细节3.1 MAX30102 寄存器配置与初始化MAX30102 内部有一堆寄存器需要配置包括模式选择、采样率、LED 电流、FIFO 控制等。初始化流程大致是复位、设置 FIFO 配置、设置模式为 SpO2 模式、设置 LED 电流、设置采样率和脉宽、清除中断状态。关键寄存器说明寄存器地址名称功能推荐值0x09Mode Config工作模式0x03 (SpO2)0x0ASpO2 Config采样率、脉宽0x27 (100Hz, 411us)0x0CLED1 PA红光 LED 电流0x24 (约 7mA)0x0DLED2 PA红外 LED 电流0x24 (约 7mA)0x04FIFO ConfigFIFO 平均、回卷0x500x08FIFO Write Ptr写指针0x000x02Int Enable中断使能0xC0LED 电流的设置很关键。电流太小信号弱信噪比差电流太大功耗高而且可能让光电探测器饱和。7mA 左右对大多数人的手指厚度来说是比较合适的起点。如果发现信号幅度太小可以逐步增加到 10mA 或 12mA如果信号削顶就往下调。采样率设 100Hz 意味着每秒采 100 组红光和红外数据。脉宽 411us 对应 18 位 ADC 分辨率这是精度和速度的平衡点。FIFO 平均设为 4相当于硬件做了 4 次平均能有效降低随机噪声。3.2 原始数据读取与 FIFO 操作MAX30102 的 FIFO 是 32 组样本的循环缓冲区每组样本包含红光和红外两个 18 位数据实际存储时每个数据占 3 个字节所以一组样本是 6 个字节。读 FIFO 的时候要先读写指针和读指针计算可读样本数然后连续读取。def read_fifo(self): wr_ptr self.i2c.readfrom_mem(self.addr, 0x04, 1)[0] rd_ptr self.i2c.readfrom_mem(self.addr, 0x06, 1)[0] num_samples (wr_ptr - rd_ptr) 0x1F if num_samples 0: return [] data self.i2c.readfrom_mem(self.addr, 0x07, num_samples * 6) samples [] for i in range(num_samples): red (data[i*6] 16 | data[i*61] 8 | data[i*62]) 0x3FFFF ir (data[i*63] 16 | data[i*64] 8 | data[i*65]) 0x3FFFF samples.append((red, ir)) return samples这里有个细节18 位数据在 3 个字节里存储时高 6 位是无效的所以要和 0x3FFFF 做与运算把无效位清掉。不清的话数据会偏大很多导致后续计算全错。注意读 FIFO 之前一定要先读写指针和读指针不能直接读数据寄存器。直接读数据寄存器不会自动推进读指针会导致重复读取同一组数据。另外读指针在读取数据后会自动更新不需要手动写回。3.3 心率计算从原始信号到 BPM拿到原始红外数据后不能直接拿来做峰值检测因为信号里包含直流分量DC和交流分量AC。直流分量来自组织、骨骼、静脉血等非搏动成分交流分量才反映动脉血的搏动。所以第一步是去直流通常用滑动平均或者一阶高通滤波。我常用的是一个简单的滑动窗口平均法维护一个长度为 N 的窗口每次新数据进来就替换最老的数据然后计算窗口内平均值作为直流估计原始值减去这个平均值就得到交流信号。N 取采样率的 0.5 到 1 倍比较合适100Hz 采样的话 N 取 50 到 100。去直流之后信号会围绕零点波动。接下来做峰值检测当信号从负变正且超过某个阈值时认为检测到一个峰值。两个峰值之间的时间间隔就是心跳周期60 除以周期秒就是心率。class HeartRateDetector: def __init__(self, sample_rate100): self.sample_rate sample_rate self.buffer [] self.buffer_size sample_rate self.last_peak_time 0 self.threshold 1000 self.bpm 0 def update(self, ir_value, timestamp): self.buffer.append(ir_value) if len(self.buffer) self.buffer_size: self.buffer.pop(0) if len(self.buffer) self.buffer_size: return dc sum(self.buffer) / len(self.buffer) ac ir_value - dc if ac self.threshold and timestamp - self.last_peak_time 300: if self.last_peak_time 0: interval timestamp - self.last_peak_time self.bpm 60000 / interval self.last_peak_time timestamp阈值设 1000 是个经验值实际要根据信号幅度调整。如果发现心率跳动很大通常是阈值太低导致噪声被误判为峰值或者太高导致漏检。可以加一个自适应阈值根据最近几秒的信号幅度动态调整。3.4 血氧饱和度估算原理与实现血氧饱和度的计算基于红光和红外光吸收比值的经验公式。氧合血红蛋白和还原血红蛋白对红光和红外光的吸收特性不同氧合血红蛋白对红外光吸收少、对红光吸收多还原血红蛋白相反。通过计算 R (AC_red / DC_red) / (AC_ir / DC_ir)再代入经验公式 SpO2 110 - 25 * R就能估算出血氧值。这个公式是经验性的不同厂家的传感器、不同个体的手指厚度、不同环境光条件都会影响结果。所以血氧值只能作为参考不能用于医疗诊断。实际使用中R 值通常在 0.4 到 1.0 之间对应 SpO2 从 100% 到 85% 左右。def calculate_spo2(red_ac, red_dc, ir_ac, ir_dc): if red_dc 0 or ir_dc 0: return 0 r (red_ac / red_dc) / (ir_ac / ir_dc) spo2 110 - 25 * r return max(0, min(100, spo2))实操心得血氧值对信号质量非常敏感。手指按得太轻信号弱R 值波动大按得太重血流受阻读数也会偏低。最好的状态是手指自然覆盖传感器窗口不施加明显压力。另外环境光太强的时候光电探测器会饱和读数完全不可信。可以在传感器上方加一个遮光罩或者用手掌稍微遮挡一下。4. OLED 显示驱动与界面设计4.1 SSD1306 初始化与 I2C 通信0.96 寸 OLED 模块的核心是 SSD1306 驱动芯片它支持 I2C 和 SPI 两种接口。I2C 版本的地址通常是 0x3C 或 0x3D具体看模块上的电阻配置。初始化序列比较长包括关闭显示、设置时钟分频、设置多路复用比、设置显示偏移、设置起始行、设置电荷泵、设置内存寻址模式、设置段重映射、设置 COM 扫描方向、设置对比度、设置预充电周期、设置 COM 引脚配置、设置对比度、设置显示模式等。MicroPython 下直接用ssd1306库最省事from machine import Pin, I2C import ssd1306 i2c I2C(0, sclPin(22), sdaPin(21), freq400000) oled ssd1306.SSD1306_I2C(128, 64, i2c, addr0x3C) oled.fill(0) oled.text(Hello, 0, 0) oled.show()Arduino 下用Adafruit_SSD1306和Adafruit_GFX库#include Wire.h #include Adafruit_GFX.h #include Adafruit_SSD1306.h Adafruit_SSD1306 display(128, 64, Wire, -1); void setup() { Wire.begin(21, 22); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.clearDisplay(); display.setTextSize(2); display.setTextColor(SSD1306_WHITE); display.setCursor(0, 0); display.println(Hello); display.display(); }4.2 显示布局与刷新策略128x64 的屏幕空间有限要合理规划布局。我一般把屏幕分成三行第一行显示心率用大号字体第二行显示血氧同样大号字体第三行显示状态信息比如信号质量或者提示文字。刷新策略很重要。如果每次循环都全屏刷新I2C 通信量会很大影响传感器数据读取。更好的做法是只在数值变化时才刷新或者固定 5-10Hz 的刷新率。另外oled.show()操作比较耗时不要在中断里调用。last_display_time 0 display_interval 200 # ms def update_display(oled, bpm, spo2, timestamp): global last_display_time if timestamp - last_display_time display_interval: return last_display_time timestamp oled.fill(0) oled.text(HR:, 0, 0) oled.text(str(int(bpm)), 40, 0) oled.text(SpO2:, 0, 24) oled.text(str(int(spo2)), 60, 24) oled.text(%, 100, 24) oled.show()4.3 显示汉字与自定义图形SSD1306 本身不支持汉字要显示汉字需要自己取模生成点阵数据。常用的方法是使用 PCtoLCD2002 或者在线取模工具生成 16x16 或 24x24 的字节数组然后用oled.blit()或者逐像素绘制。# 16x16 汉字点阵示例心字 heart_icon bytearray([ 0x00,0x00,0x1F,0xF0,0x10,0x10,0x10,0x10, 0x1F,0xF0,0x10,0x10,0x10,0x10,0x1F,0xF0, 0x00,0x00,0x04,0x40,0x08,0x20,0x10,0x10, 0x20,0x08,0x40,0x04,0x00,0x00,0x00,0x00 ]) def draw_heart(oled, x, y): for row in range(16): for col in range(16): byte_index row * 2 col // 8 bit_index 7 - (col % 8) if heart_icon[byte_index] (1 bit_index): oled.pixel(x col, y row, 1)注意取模的时候要注意字节排列顺序不同工具生成的格式可能不一样。有的按列优先有的按行优先有的高位在前有的低位在前。用之前先用一个小图案测试一下确认方向正确再批量生成。5. 系统联调与常见问题排查5.1 传感器数据不稳定的排查思路MAX30102 数据不稳定是最常见的问题表现包括心率跳动大、血氧值乱跳、信号时有时无。排查的时候按以下顺序来先看原始数据。把红光和红外的原始值打印出来正常应该看到红外值在几万到十几万之间波动红光值略低一些。如果两个值都是 0 或者都是 0x3FFFF说明通信有问题或者传感器没正常工作。如果数值很小几百可能是 LED 电流太小或者手指没放好。再看波形。把去直流后的交流信号用串口绘图工具画出来正常应该看到规律的搏动波形。如果波形杂乱无章可能是环境光干扰或者电源噪声。可以尝试在传感器和手指之间加一层遮光材料或者在电源引脚加 100uF 和 0.1uF 的退耦电容。最后看算法参数。阈值、窗口大小、峰值间隔这些参数都需要根据实际信号调整。没有一套参数能适应所有情况必须根据实际波形来调。现象可能原因解决方法原始值全 0I2C 通信失败检查接线、地址、上拉电阻原始值全 0x3FFFF传感器饱和降低 LED 电流、遮光心率跳动大阈值不合适调整阈值或加自适应血氧值偏低手指压力大放松手指、调整位置数据时有时无接触不良检查杜邦线、焊接点OLED 不亮地址错误扫描 I2C 确认地址OLED 花屏初始化序列不对检查库版本和初始化代码5.2 I2C 总线冲突与性能优化当 MAX30102 和 OLED 挂在同一条总线上时如果 OLED 刷新太频繁会占用总线时间导致传感器 FIFO 溢出。FIFO 溢出后数据会丢失心率计算就会出现断层。优化方法有几个降低 OLED 刷新率到 5Hz 以下提高 I2C 时钟频率到 400kHz 甚至 1MHz如果模块支持把 OLED 和传感器分到两条不同的 I2C 总线上。ESP32 有两个 I2C 控制器可以一个接传感器一个接屏幕彻底避免冲突。# 双 I2C 总线配置 i2c_sensor I2C(0, sclPin(22), sdaPin(21), freq400000) i2c_oled I2C(1, sclPin(19), sdaPin(18), freq400000)实操心得我一开始把两个设备挂一条总线OLED 刷新率设了 20Hz结果心率数据经常断。后来把刷新率降到 5Hz情况好转但偶尔还是丢数据。最后改成双总线问题彻底解决。如果你的项目对实时性要求高强烈建议用双总线方案。5.3 电源噪声与信号质量提升ESP32 的 Wi-Fi 和蓝牙工作时会产生较大的电源噪声如果传感器和主控共用一路电源噪声会耦合到模拟信号里。表现就是心率波形上叠加了高频毛刺峰值检测容易误判。解决办法在 MAX30102 的 VIN 和 GND 之间就近加一个 100uF 电解电容和一个 0.1uF 陶瓷电容如果条件允许用独立的 LDO 给传感器供电在软件上增加数字滤波比如 5 点滑动平均或者 IIR 低通滤波。class LowPassFilter: def __init__(self, alpha0.1): self.alpha alpha self.value 0 def update(self, new_value): self.value self.alpha * new_value (1 - self.alpha) * self.value return self.valuealpha 越小滤波效果越强但响应越慢。对于心率信号alpha 取 0.1 到 0.3 比较合适。5.4 常见问题速查表问题排查步骤解决方案扫描不到 I2C 设备测电压、测上拉、换线检查供电和接线MAX30102 读数为 0读 Part ID (0xFF)确认地址和通信心率始终为 0打印原始数据检查信号幅度和阈值血氧值超过 100检查 R 值计算限制输出范围OLED 显示乱码检查初始化序列换库或手动初始化系统运行一段时间死机检查内存和看门狗加喂狗、优化内存数据更新太慢检查循环延时减少阻塞操作手指移动时数据跳变正常现象加运动补偿或提示6. 从能用到好用功能扩展与优化方向6.1 数据滤波与信号质量评估基础版本能跑起来之后下一步是提升数据质量。除了前面提到的滑动平均和低通滤波还可以用中值滤波去掉脉冲噪声用带通滤波0.5Hz 到 5Hz提取心跳频段。信号质量评估可以通过计算信噪比或者信号幅度来判断质量差的时候在屏幕上显示提示而不是显示不可信的数值。def signal_quality(ac_values): if len(ac_values) 10: return 0 signal_power sum(v*v for v in ac_values) / len(ac_values) noise_estimate sum(abs(ac_values[i] - ac_values[i-1]) for i in range(1, len(ac_values))) / (len(ac_values) - 1) if noise_estimate 0: return 100 snr 10 * math.log10(signal_power / (noise_estimate * noise_estimate 1e-6)) return max(0, min(100, snr * 5))6.2 加入温度补偿与运动检测MAX30102 内部有一个温度传感器可以读取芯片温度。虽然它测的是芯片温度不是体温但可以用来做温度漂移补偿。另外如果加一个 MPU6050 之类的加速度计可以检测手指是否在移动移动时暂停测量或者降低数据可信度权重。6.3 数据记录与导出ESP32 的 Flash 可以存一些历史数据或者通过 Wi-Fi 把数据传到电脑上做进一步分析。MicroPython 下可以用open()写文件Arduino 下可以用 SPIFFS 或 LittleFS。数据格式建议用 CSV方便用 Excel 或 Python 分析。def log_data(bpm, spo2, timestamp): with open(/data.csv, a) as f: f.write({},{},{}\n.format(timestamp, bpm, spo2))6.4 低功耗设计与电池供电如果想让设备便携化低功耗是必须考虑的。ESP32 支持 light sleep 和 deep sleepMAX30102 也有低功耗模式。可以在没有手指接触的时候让系统进入休眠检测到手指再唤醒。OLED 也可以关闭显示或者降低亮度来省电。实操心得我在做电池供电版本的时候发现 Wi-Fi 开启时电流直接飙到 100mA 以上电池根本撑不了多久。后来把 Wi-Fi 关掉只用蓝牙按需传输平均电流降到 20mA 左右续航从几小时提升到一天多。所以如果你的项目不需要一直联网果断关掉 Wi-Fi。6.5 外壳设计与佩戴方式裸板用起来不方便可以设计一个简单的外壳。3D 打印是最方便的没有打印机的话用亚克力板或者塑料盒改造也行。关键是要让手指能稳定地覆盖传感器窗口同时屏幕朝向用户。我试过用橡皮筋把传感器固定在手指上虽然丑但效果不错信号稳定性比手按好很多。这个项目从零开始做快的话一个周末能跑通基础功能但要调到数据稳定可靠可能需要一到两周的反复调试。最大的坑往往不在代码而在硬件连接和信号处理。耐心一点把原始数据打印出来多看波形比盲目改代码有效得多。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

OneUptime 用户、团队与权限模型完全指南:从角色与作用域到授权判定源码解析 2026/9/20 3:47:52

OneUptime 用户、团队与权限模型完全指南:从角色与作用域到授权判定源码解析

OneUptime 用户、团队与权限模型完全指南:从角色与作用域到授权判定源码解析 【免费下载链接】oneuptime Complete open-source monitoring and observability platform. 项目地址: https://gitcode.com/GitHub_Trending/on/oneuptime 导读 OneUptime 的一切…

阅读更多 →
BrewUI:用SwiftUI重构macOS包管理交互范式 2026/9/20 3:47:52

BrewUI:用SwiftUI重构macOS包管理交互范式

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

阅读更多 →
SpringBoot+Vue3+MyBatis搭建企业客户管理系统全流程实战 2026/9/20 3:47:52

SpringBoot+Vue3+MyBatis搭建企业客户管理系统全流程实战

做了两三个后台管理系统之后,我越来越觉得“客户管理”这类业务是最适合拿来练手也最适合拿来落地的项目。它不像电商那样牵扯复杂的交易链路,也不像审批流那样被一堆状态机追着跑,但该有的东西全都有:用户登录鉴权、数据列表分页…

阅读更多 →
从黑库到蓝库:Simulink电力电子仿真迁移实战指南 2026/9/20 3:47:52

从黑库到蓝库:Simulink电力电子仿真迁移实战指南

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

阅读更多 →
Java缓存体系实战:从本地缓存到Redis,穿透击穿雪崩一次讲透 2026/9/20 3:47:52

Java缓存体系实战:从本地缓存到Redis,穿透击穿雪崩一次讲透

1. 先别急着写代码,把缓存这盘棋看明白1.1 缓存到底解决了什么问题做Java后端这些年,我最大的感受就是:很多系统一开始没有缓存也能跑,等到QPS上来了、数据库连接被打满、接口响应从20毫秒变成2秒的时候,大家才开始补缓…

阅读更多 →
开源智能客服系统AI-CS实战:私有化部署与大模型调优全记录 2026/9/20 3:44:52

开源智能客服系统AI-CS实战:私有化部署与大模型调优全记录

最近在整理开源AI智能客服系统AI-CS的部署文档,发现这个项目我从搭骨架到跑通生产,前后折腾了三个多月。起因很直接——有个朋友的电商团队每天要处理三四百条客服咨询,其中七成以上是"发货了吗""怎么退""优惠券怎么…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞