news 2026/10/20 8:11:59

嵌入式数字滤波算法C代码实现:ADC噪声与传感器毛刺处理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
嵌入式数字滤波算法C代码实现:ADC噪声与传感器毛刺处理

简介:一套面向嵌入式开发与单片机应用的实用滤波算法代码包,针对传感器数据采集中的脉冲干扰、周期性波动、快速变化信号等场景,整理了限幅滤波、中位值滤波、算术平均滤波、递推平均、一阶滞后、消抖等10种常用处理方法。每种算法均按方法、优点、缺点给出说明,便于开发者结合流量、压力、液位、温度等被测参数的特点快速选型。压缩包共3个文件,约198KB,含C源程序、Word版算法说明文档及一篇数字滤波方法研究的PDF论文。C代码可直接参考移植到STM32、51等常见MCU工程中,Word文档便于离线查阅完整思路,PDF论文可辅助理解滤波原理与选型依据。资源已有3000余人学习下载,适合刚接触数字滤波的嵌入式初学者,也适合在项目中需要快速对比算法特性的工程师,可节省整理和测试时间。

1. 数字滤波不是套公式:这份C代码包解决的是ADC毛刺和传感器噪声

有一次调一块温控采集板,恒温箱里放着的温度传感器,原始ADC读数却在稳态下上下跳了十几个LSB,输出曲线看起来和心电图差不多。网上找来的滤波算法大多是片段,不是缺数据结构就是没考虑嵌入式环境,抄下来根本没法直接用。这份“10种常用的滤波算法(C代码)”把十种经典数字滤波算法整理成了可直接编译的C函数,不依赖第三方库,扔进工程就能跑。它适合刚接触嵌入式数据采集的开发者照着配参数,也适合老工程师直接借用状态结构体和组合策略。真正值钱的不是那几行公式,而是每种算法对应的数据结构、边界条件和工程参数取舍。

2. 十种滤波算法全景与代码包结构:先看清单再动手

2.1 十种算法的三个家族:去脉冲、压噪声、组合策略

拿到代码包先别急着编译,先把这十种算法分清楚。它们不是十个平级方案,按处理目标可以分成三组:去脉冲、压噪声、组合策略。去脉冲组解决偶发尖峰和触点抖动,压噪声组解决随机噪声和周期性纹波,组合组则把两种需求拼在一起。

分组算法适用信号特征关键参数
去脉冲限幅滤波偶发尖峰、脉冲噪声最大允许跳变值L
去脉冲中位值滤波缓慢变化信号上的离群点窗口N,常用5~15
去脉冲消抖滤波按键、开关量计数阈值
压噪声算术平均高斯噪声、周期性噪声窗口N
压噪声递推平均连续采集流,要求O(1)更新窗口N(建议2的幂)
压噪声加权递推平均需要强调近期数据权重表
压噪声一阶滞后工频纹波、慢变化模拟量滤波系数a
组合限幅平均尖峰和白噪声同时存在L、N
组合中位值平均缓慢信号加强脉冲加噪声N(奇数)
组合限幅消抖开关量防抖加限幅L、计数阈值

代码包的文件结构是典型的嵌入式工程布局,静态数组分配内存,不用malloc。根目录下是算法源文件,test目录里是演示程序和验证数据生成器,README里有每种算法的适用场景和参数表。

filter/ ├── filter.h # 公共接口与状态结构体定义 ├── filter.c # 十种算法实现 ├── test/ │ ├── main.c # 演示程序:生成合成信号并打印滤波前后序列 │ └── gen_data.py # 生成带毛刺和噪声的模拟数据 └── README.md # 每种算法的适用场景与参数表

下载后先在PC上编译验证,再把filter.c和filter.h拷贝到目标工程里。README里已经写好了每种算法对应的场景,但参数必须按你自己的采样周期和信号物理量重新标定,后面第4章会详细说怎么换算。

2.2 接口设计:状态结构体是十个算法的公共骨架

滤波算法和普通函数最大的区别在于“有记忆”。中位值滤波要记住窗口内的历史样本,递推平均要维护累计和,一阶滞后要记住上一次输出。如果用全局变量存这些状态,十路采集通道就得复制十份代码;所以代码包统一用状态结构体承接记忆,每个通道独立实例化一个结构体。

/* 一阶滞后滤波器状态 */ typedef struct { int32_t last_y; /* 上一次滤波输出 */ uint8_t shift; /* 定点精度,取值8~14 */ uint16_t alpha; /* 滤波系数,范围0~(1<<shift) */ } lowpass_t; /* 滑动平均滤波器状态 */ typedef struct { uint16_t ring[64]; /* 环形缓冲,窗口最大64 */ uint8_t head; /* 当前写入位置 */ uint8_t count; /* 已积累的样本数 */ uint32_t sum; /* 窗口内累计值 */ } sliding_avg_t;

以这两个结构体为典型,其余八个算法基本是它们的变体或组合。结构体实例化放在调用方,可以静态分配,也可以放进另外的结构体里。启动时清零结构体,再调用一次初始化函数把首采值填入last_y,避免滤波从0开始爬升。

接口上用函数指针封装统一入口是常见的做法。如果采集循环里同时跑八路传感器,每路用不同的滤波策略,可以定义一个函数指针表,让调度代码不关心具体算法。

typedef uint16_t (*filter_func_t)(void *state, uint16_t x); typedef struct { filter_func_t func; void *state; } filter_chan_t;

这样做的工程价值在于:切换滤波策略不用改采集主循环,只换函数指针。代价是函数指针调用有少量开销,但在几十kHz采样率以下完全无所谓。

2.3 编译与数据类型选型:ADC采样进uint16_t,累加进uint32_t

数据类型直接决定这个代码包在MCU上跑不跑得动。ADC采样值常用12位或16位,原始值放uint16_t;窗口内的累计值上限要按“样本数×满量程”预估。比如窗口64、每个样本最大65535,累计上限约419万,uint32_t足够。但如果做32位ADC或累加256个样本,就要小心边界的累积。一阶滞后的中间乘加用int64_t,防止两个16位数值相乘后累加溢出。

gcc -std=c99 -O2 -Wall -o test_filter test/main.c filter.c

要说明的是这段代码依赖C99标准,因为用到了stdint.h和指定初始化器。老旧的单片机工程如果还停留在C89,需要先检查编译器是否支持C99。编译命令建议加-Wall,让隐式类型转换的警告暴露出来。浮点运算在验证阶段没问题,但放到没有FPU的MCU上会拖慢中断响应,代码包里的核心算法统一采用定点或整数实现。

3. 滑动窗口滤波家族:移动平均、中位值与组合窗口的参数权衡

3.1 算术平均和递推平均:把O(N)降成O(1)的关键是环形缓冲

普通算术平均每次滤波都要把窗口内N个样本重新加一遍,复杂度O(N)。100kHz采样率下,窗口64,每秒钟要额外做640万次加法,中断里根本撑不住。递推平均的优化思路是维护一个滚动累计值:来一个新样本就加进去,同时把最旧样本减掉,计算量与窗口大小无关。

/* 窗口长度必须是2的幂,方便位运算取模 */ #define AVG_WINDOW 32 uint16_t sliding_average(sliding_avg_t *st, uint16_t x) { if (st->count < AVG_WINDOW) { /* 窗口未满:直接累加并记录样本 */ st->sum += x; st->ring[st->head] = x; st->head = (st->head + 1) & (AVG_WINDOW - 1); st->count++; } else { /* 窗口已满:减掉最旧的样本,加入新样本 */ uint8_t old_pos = st->head; st->sum -= st->ring[old_pos]; st->sum += x; st->ring[old_pos] = x; st->head = (old_pos + 1) & (AVG_WINDOW - 1); } return (uint16_t)(st->sum / st->count); }

代码里最关键的两行是& (AVG_WINDOW - 1)。这个位运算只在窗口长度为2的幂时才等价于取模;如果窗口长度改成了50,这里就必须换成if (++head >= 50) head = 0;。sum字段放结构体里而不是局部变量,是为了让多次调用之间保持累计值,这也是O(1)算法能成立的前提。初始化时清零sum和head,第一个样本进来走count < AVG_WINDOW分支,直接累加,不会出现除以0的情况。

递推平均适合采样率稳定、中断频繁的场景。但要注意窗口时间跨度带来的延迟,以一个采样点进入、经过半个窗口后才能影响输出,窗口越大,信号突变看起来越像斜坡。后面第6章会给一个缓解方案。

3.2 中位值滤波:排序窗口的临时区拷贝是防止缓冲区错乱的底线

中位值滤波的思路简单:取窗口内所有样本排序,输出中间值。它对脉冲毛刺的抑制效果比平均滤波好得多,一个极大的离群点不会污染输出,代价是排序需要额外内存和计算时间。窗口小的时候,插入排序比快排更合适,因为几乎有序的数据插入排序接近O(N)。

typedef struct { uint16_t buf[16]; /* 窗口数据,FIFO顺序 */ uint8_t count; /* 当前样本数 */ } median_filter_t; uint16_t median_filter(median_filter_t *st, uint16_t x) { uint16_t temp[16]; /* 先复制,不破坏原FIFO数据 */ memcpy(temp, st->buf, st->count * sizeof(uint16_t)); if (st->count < 16) { temp[st->count++] = x; } else { memmove(temp, temp + 1, 15 * sizeof(uint16_t)); temp[15] = x; } /* 插入排序:16个样本以内排序开销很低 */ for (uint8_t i = 1; i < st->count; i++) { uint16_t key = temp[i]; uint8_t j = i; while (j > 0 && temp[j - 1] > key) { temp[j] = temp[j - 1]; j--; } temp[j] = key; } return temp[st->count / 2]; }

这里最容易翻车的地方是把排序直接作用在st->buf上。buf里的数据代表时间顺序,下一次滤波时窗口要剔除的“最旧样本”是buf[0];如果排过序,下一次就找不到真正的最旧样本了,输出会出现随机毛刺。所以代码里先用memcpy拷贝到临时数组,排序在临时区完成,buf保持FIFO原序,代价是每次滤波多一次16字节的内存拷贝,在MCU上完全可以接受。

窗口长度的选择要和信号变化率匹配。温度、压力这类缓变信号,窗口15左右能有效削掉脉冲且不产生明显相位滞后;振动信号如果窗口开太大,峰值会被削平,后续特征提取就没意义了。

3.3 中位值平均滤波:先剔除最大最小再平均,工业采集的常青树

中位值平均滤波是中位值和算术平均的组合:先排序,去掉一个最大值和一个最小值,剩下的样本做算术平均。它对“脉冲噪声+高斯噪声”混合的场景非常实用,既不像纯中位值那样丢失信号能量,也不像纯平均那样被一个尖峰带偏。这类组合算法在工业现场用得最多,振动烈度监测、称重变送器里经常能看到它的变体。

uint16_t median_average(median_filter_t *st, uint16_t x) { uint16_t tmp[16]; uint16_t min_v, max_v; uint32_t sum = 0; uint8_t i; memcpy(tmp, st->buf, st->count * sizeof(uint16_t)); if (st->count < 16) { tmp[st->count++] = x; } else { memmove(tmp, tmp + 1, 15 * sizeof(uint16_t)); tmp[15] = x; } min_v = tmp[0]; max_v = tmp[0]; for (i = 0; i < st->count; i++) { if (tmp[i] < min_v) min_v = tmp[i]; if (tmp[i] > max_v) max_v = tmp[i]; sum += tmp[i]; } sum -= min_v + max_v; /* 剔除一个最大一个最小 */ return (uint16_t)(sum / (st->count - 2)); }

注意它和中位值滤波的区别:中位值输出的是排序后正中间那一个点,而中位值平均输出的是剔除极值后的均值,保留了更多信号能量。窗口长度必须是奇数,而且至少要大于5,否则剔除两个极值后剩余样本太少,平均结果波动大。实际使用中窗口取15,即一次滤波要处理15个样本,计算量和内存都可控。对16位ADC数据,sum累加最多15×65535≈98万,uint32_t安全。

4. 一阶滞后与限幅滤波:低通、防突变与组合拳的工程设定

4.1 一阶低通滤波的定点实现:alpha 的物理意义是截止频率

一阶滞后滤波的公式是Y[n] = a·X[n] + (1-a)·Y[n-1],在所有十种算法里它是最平滑的一种,也是坑最多的一种。浮点实现直接照抄这个公式,MCU没有FPU的时候执行时间可能膨胀五倍以上,中断周期稍紧就会溢出。代码包里的定点版本把这个公式改成了整数运算。

typedef struct { int32_t last_y; /* 上次输出,需初始化为首个采样值 */ uint8_t shift; /* 定点精度,8~14 */ uint16_t alpha; /* 系数,范围[0, 1<<shift) */ } low_pass_t; int16_t low_pass_update(low_pass_t *f, uint16_t x) { int64_t y; y = (int64_t)f->alpha * x; y += (int64_t)((1u << f->shift) - f->alpha) * f->last_y; y >>= f->shift; f->last_y = (int32_t)y; return (int16_t)y; }

alpha的换算是整套代码里最需要耐心调试的地方。浮点a=0.1、定点精度shift=10时,alpha换算成整数取0.1×1024≈102,量化误差会带来一个小幅直流偏置。中间乘法用int64_t是因为alpha×(1<<shift)×last_y在特定组合下会接近百万量级,虽然int32也能装下,但一旦采样值扩到24位就危险了,一律用int64_t最稳妥。

alpha不对应某个直观的“力度”,它对应截止频率。采样频率fs下,一阶低通的截止频率近似为fc ≈ a·fs/(2π)。比如采样率1kHz、a=0.1,截止频率约16Hz;想滤掉50Hz工频纹波,同样采样率下a要压到0.03左右。下面的换算表可以省去反复试参的功夫。

浮点ashift=10定点值等效截止频率@fs=1kHz
0.03314.8Hz
0.05518.0Hz
0.1010215.9Hz
0.2020531.8Hz

初始化时把last_y设为首采值,而不是0。如果按0启动,前几十个采样点会看到一条从0爬向真实值的斜坡,这在仪表开机阶段很容易被误判为真实跳变。

4.2 限幅滤波:阈值按物理最大变化率换算,不做玄学拍脑袋

限幅滤波的逻辑很直白:当前值跟上一次输出之差超过阈值就丢弃,不超过就接受。它专门对付偶发尖峰,对缓慢漂移和高频噪声无计可施。阈值L是唯一参数,很多人把L当成“噪声幅度”来拍,结果要么滤不住脉冲,要么把真实变化也削成了平头。

typedef struct { uint16_t last; uint16_t threshold; /* 最大允许跳变量,单位是ADC LSB */ } limit_filter_t; uint16_t limit_filter_update(limit_filter_t *f, uint16_t x) { uint16_t delta; if (x >= f->last) delta = x - f->last; else delta = f->last - x; if (delta > f->threshold) return f->last; /* 判定为脉冲,丢弃 */ f->last = x; return x; }

阈值应该从物理量的最大变化率换算。测温度,传感器最大升温速率是1℃/s,采样周期100ms,即一个采样周期最多变0.1℃;12位ADC满量程对应100℃,每个LSB约0.0244℃,阈值取0.1/0.0244≈4 LSB。把这个换成工程写法:

L = ceil(max_slew_rate / ADC_scale * sample_period)

把阈值写死成常数是个常见错误。同一个温度模块,采样率从100Hz改到1kHz,每周期允许变化量缩小十倍,阈值不跟着改,真实升温过程会被限幅切成台阶状。

4.3 限幅+一阶滞后串行组合:先拦脉冲、再压噪声

单独的限幅滤波器输出带有明显的高频噪声,单独的一阶滞后会把脉冲污染拖成持续几个周期的隆起。把两者串起来是工程上最常用的组合策略:先限幅剔除脉冲,再低通压噪声。这个组合覆盖了大多数工业模拟量采集场景。

typedef struct { limit_filter_t lim; low_pass_t lp; } lim_lowpass_t; uint16_t lim_lowpass_update(lim_lowpass_t *f, uint16_t x) { uint16_t x1 = limit_filter_update(&f->lim, x); return (uint16_t)low_pass_update(&f->lp, x1); }

顺序不能反。如果先低通后限幅,脉冲经过一阶滞后会保留能量,限幅看到的是一个被拖宽的“假台阶”,阈值再小也拦不住。串行组合的参数标定方法是:先用纯限幅去掉尖峰,观察静态噪声峰峰值,再将一阶滞后的alpha设到对应该噪声衰减需要的截止频率。整个过程有点像先把大石子捡掉,再筛细沙。

5. 滤波算法避坑与排查:窗口缓冲、整数除法和阶跃滞后的五条现场记录

这一章的内容来自实际调试中反复出现的问题,每一条都是“现象→原因→解决”的结构。代码包能编译能跑只是第一步,真正麻烦的是参数和内存上的这些隐蔽问题。

5.1 现象:中位值滤波输出偶发跳变,窗口越大越严重

现象是滤波输出在稳态下偶尔冒出一个偏离真实值很远的点,而且加大窗口长度后跳变频率反而变高。原因是实现时直接把排序作用在了状态结构体的数据缓冲区上,buf里保存的时间顺序被排序破坏了。下一次滤波把排过序的“中间值”当成最旧样本剔除,窗口内的数据同一时间可能混了几个不同时刻的样本,输出自然乱跳。

解决方法是排序前先拷贝到临时数组,保持缓冲区时间顺序不被破坏。代码包里默认就是这么做的,如果自己重写,务必把memcpy保住,不要为了省那几十个字节的内存去掉。

5.2 现象:滑动平均输出长期为0,只有阶跃瞬间有值

现象出现在窗口较大、输入信号幅度较小的时候,比如12位ADC在低量程段工作,滑动平均结果恒为0。原因是整数除法截断:sum/count中sum小于count时,商直接截成0。稳态信号本身幅度只有几个LSB,窗口64时sum约几百,count=64,算完还能剩几个LSB,如果信号幅度1~2LSB,平均结果就归零了。

解决方法是输出前做四舍五入:(sum + count/2) / count。但对极低幅度信号,更要检查的是窗口长度是不是开得过大。窗口时间跨度超过信号本身的变化周期,平均就是在抹平有效信息,不是噪声。

5.3 现象:一阶滞后放在中断里,中断周期从100us膨胀到200us

现象是系统原本100us的中断周期,在加入一阶滞后滤波后整整翻倍,CPU空闲时间骤降。原因是浮点运算在无FPU的MCU上由软浮点库处理,一个浮点乘加可能消耗几十个甚至上百个周期,中断上下文里反复调用自然拖垮实时性。

解决方法是改用定点实现,同时把滤波从中断搬到主循环。中断里只做采样和置标志位,主循环检测到标志后把新采样值推进滤波器。这样做还有一个额外好处:滤波器的状态结构体不会被中断和主循环同时访问,省去了加锁的麻烦。

5.4 现象:大窗口把真实突变当成噪声,阶跃响应变成斜坡

现象是输入一个阶跃跳变,输出却拉成了一条斜线,达到目标值要等半个窗口长度。原因是递推平均的窗口太长,窗口64意味着每个采样点要经过32个周期才能在平均值里占主导地位,阶跃看起来自然成了斜坡。滤波器对真实变化和脉冲噪声在响应上的区别是,滤波器只认幅度不认语义,大窗口天然把快速变化抑制掉。

解决方法是并联一个快窗口,两个窗口输出差值超过阈值时直接启用快窗口结果。这就是第6章双窗口联动的基础思路,代码包test目录里的演示程序也包含了这组逻辑。参数上,快窗口8,慢窗口64,阈值按静态噪声峰峰值标定。

5.5 现象:多路采集通道串扰,A通道的脉冲跑到B通道输出里

现象是两路传感器共用一个滤波函数,A通道来了一个尖峰,B通道的滤波输出也出现了毛刺。原因是全局只定义了一个状态结构体,A通道的数据把结构体里的历史值更新掉了,B通道下一拍进来时,看到的是A通道的残留数据。

解决方法是给每一路采集通道分配独立状态结构体实例,用数组或直接命名区分。初始化时用首采值填充历史状态,而不是用0初始化后等待爬升。这个错误在代码量大的工程里特别隐蔽,因为滤波函数本身没有任何并发保护。

6. 双窗口联动滤波:现场调参技巧和验证用例

6.1 快速响应和长期平滑的切换逻辑

递推平均窗口越大越平滑,但越平滑越迟钝。要同时保住稳态精度和阶跃响应,可以同时跑快慢两个窗口,正常输出慢窗口,检测到两者差值超过阈值就切入快窗口。

typedef struct { sliding_avg_t fast; /* 快窗口,建议8~16 */ sliding_avg_t slow; /* 慢窗口,建议48~64 */ uint16_t threshold; /* 切换阈值,单位LSB */ } dual_window_t; uint16_t dual_window_update(dual_window_t *f, uint16_t x) { uint16_t fast = sliding_average(&f->fast, x); uint16_t slow = sliding_average(&f->slow, x); if (fast > slow) { if (fast - slow > f->threshold) return fast; } else { if (slow - fast > f->threshold) return slow; } return slow; }

注意两边比较差值时要先判断大小关系再相减,无符号数直接做减法会借位,得到的结果会是一个巨大的假差值。切换阈值建议取静态噪声峰峰值的一半。比如稳定状态下原始噪声是±3LSB,阈值取6;如果阈值取到2,正常噪声就会频繁触发切换,输出反而出现小台阶。

6.2 验证手法:阶跃用例和零漂用例

调完参数先别急着上产线,先跑两组用例。第一组是阶跃测试:给传感器输入端一个物理阶跃,记录滤波输出从开始变化到稳定在最终值63%处的时间,这就是实际延迟。第二组是零漂测试:保持输入恒定,连续记录五分钟滤波输出,看峰峰值有没有压到预期范围内。

资源包test目录里的数据生成脚本大致长这样,可以生成合成信号验证算法行为:

import numpy as np # 生成2000点合成数据:基线5000 + 阶跃 + 脉冲噪声 + 高斯噪声 rng = np.random.default_rng(0) x = np.full(2000, 5000, dtype=np.uint16) x[800:1200] += 200 # 阶跃段 x[400] += 800 # 脉冲噪声 x += rng.normal(0, 15, 2000).astype(np.int16).clip(0, 65535) np.savetxt("sim_signal.csv", x, fmt="%d")

把生成的数据喂进test/main.c里的滤波循环,对比滤波前后的数组:阶跃段看响应时间,稳态段看噪声峰峰值,脉冲点看抑制幅度。这两组用例我每次调完参数都会强制跑一遍,只有两者都达标才敢说这组参数能上线。

以前在某项目上把双窗口的切换阈值调得过小,结果轻微噪声就让两个窗口来回“打架”,输出锯齿状跳动。后来按静态噪声的3倍标准差重新标定阈值,才稳定下来。从那以后我每次调完滤波参数,都强制自己走一遍阶跃和零漂两组用例再发布,省下的返工时间远超写脚本的时间。希望这个习惯和这组代码能帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 8:11:39

FPGA硬核CRC校验实战:并行逻辑设计与Verilog实现

1. 项目背景与硬核动机&#xff1a;为什么数据跑得快&#xff0c;校验就更难干FPGA这一行的朋友应该都有过类似的经历&#xff1a;某天主控端跟你反馈&#xff0c;说链路上偶尔会抓到“灵异数据”——明明是同一包数据&#xff0c;发过去好好的&#xff0c;接收端读出来却偶尔错…

作者头像 李华
网站建设 2026/10/20 8:10:31

51单片机红外遥控电子时钟设计与Proteus仿真实现

做这个题目的时候&#xff0c;我第一反应是&#xff1a;又是一个把单片机课设经典题给玩明白的例子。基于51单片机红外遥控电子时钟&#xff0c;说通俗点就是给一个电子时钟加了个红外遥控器&#xff0c;你坐在沙发上按一下就能调时、调分、看秒&#xff0c;不用伸手去戳板子上…

作者头像 李华
网站建设 2026/10/20 8:10:21

财税监管升级下,金融与企服系统的过程合规架构改造

今年年初开始&#xff0c;我连着给几家金融机构和企服平台做财税相关的系统体检&#xff0c;一个特别强烈的感受是&#xff1a;以前大家做财税软件&#xff0c;核心是“把功能做完”&#xff1b;现在光做完已经不够了&#xff0c;必须把“规则内嵌、数据留痕、结果可解释”这三…

作者头像 李华
网站建设 2026/10/20 8:10:11

AI Agent底层实现与DeepSeek Harness设计差异解析

1. 项目概述&#xff1a;这不是“对比测评”&#xff0c;而是一次底层逻辑的自我复盘“自己实现的AI Agent与DeepSeek Harness差异”——这个标题乍看像技术博客常见的横向评测&#xff0c;但实际完全不是。它背后是一个典型的技术人成长路径&#xff1a;从调用现成框架起步&am…

作者头像 李华
网站建设 2026/10/20 8:10:10

AI量化知识图谱:从数据到风控的六段流水线

五年前我第一次拿到一批分钟级行情数据时&#xff0c;第一反应是赶紧把各种机器学习库装齐&#xff0c;用神经网络对着K线“看”一个晚上。结果一个月内画出了无数条异常漂亮的资金曲线&#xff0c;实盘一上手却立刻原形毕露。后来我把所有试错的碎片拼起来才慢慢意识到&#x…

作者头像 李华
网站建设 2026/10/20 8:09:01

为什么有人为Claude重开账号?封号原因与稳定使用指南

最近在好几个技术社群里都看到同一个话题在反复刷屏&#xff1a;账号被冻结了&#xff0c;第一反应不是换一个AI工具&#xff0c;而是琢磨怎么申诉、怎么重开一个号也要继续用Claude。作为一个把AI助手深度嵌进日常写作和代码工作流的人&#xff0c;我太理解这种执念了。Claude…

作者头像 李华