ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

电磁流量计电路图与程序:从励磁驱动到相敏解调的仪表骨架

电磁流量计电路图与程序:从励磁驱动到相敏解调的仪表骨架

简介:面向工业自动化、仪器仪表及电子设计学习者的一份电磁流量计电路图与程序参考材料,围绕导电液体流量测量中的励磁、电极信号检测、放大滤波与数据处理展开,可用于理解法拉第电磁感应原理在流量计中的硬件实现与软件控制逻辑。压缩包为rar格式,共2个文件,包含1个C程序源码和1个Sch电路原理图,整体约68KB,体量精简,便于对照查看电路连接与程序流程。目前已有1567人学习,适合需要排查故障、维护设备或优化测量方案的工程人员与高年级学生参考。内容侧重展示励磁方式选择、微弱信号处理、抗干扰设计及流量计算思路,能帮助读者把电路图与程序模块对应起来,形成从传感检测到结果输出的整体认识。

1. 电磁流量计电路图与程序:一套能直接对照硬件读的仪表骨架

管道里跑的是带细砂的浆液,泵一启动就夹气,孔板用了半年边缘磨圆,读数一天比一天低——这种工况换电磁流量计几乎是默认答案。原理不复杂:导电流体切割磁感线,一对电极上拾到和流速成正比的毫伏级电压,电路图把这点电压干净地放大,程序再把正负励磁半周的差值解调成瞬时流量。难点从来不在原理,在于几十微伏的信号旁边躺着几伏的励磁干扰和工频噪声。这份「电磁流量计电路图与程序」把励磁驱动、前置放大、ADC 采样、MCU 外围和配套下位机源码放在一起,硬件和代码是能对上的:哪个引脚采样、哪个定时器打励磁节拍、哪个变量存标定系数,都能在原理图里找到落点。

它适合三类人:做仪表调试、手里有样机但读数和实际对不上的工程师;电子类专业要交一台小口径样机、卡在"放大后全是噪声"这一步的开发者;以及想把这套东西改成自己口径、自己通讯协议的技术人员。不适合只想看一眼原理的读者——后面每一节都落在可执行的参数和代码上。

2. 硬件信号链拆解:从励磁方波到差分电极的完整通路

这一章按信号流向来走:励磁线圈产生磁场,电极拾取感应电动势,前级把它抬到 ADC 量程,MCU 在正确的时间点采样。顺序弄反了,后面写多少滤波代码都是白搭。

2.1 测量管与电极:先把机械边界和信号量级定死

感应电动势的公式是 E = B·D·v,B 是磁感应强度,D 是电极间距(约等于管道内径),v 是平均流速。这三个量决定了整套电路要处理多小的信号。以 DN100 管道、流速 1 m/s、励磁线圈在管道中心产生 3 mT 磁感应强度计算,E ≈ 0.003 × 0.1 × 1 = 300 μV。口径越小信号越小:DN25 在同样流速下只有 75 μV 左右。这个量级意味着前级放大器的输入失调漂移、电极极化电压、导线热电势全都能把它淹掉。

所以机械部分有几条硬约束必须提前定:衬里选 PTFE 还是橡胶,直接决定耐温和耐负压;电极材料按介质腐蚀性选 316L、钛或哈氏合金,选错了三个月就点蚀穿孔;电极必须与衬里齐平,凸出一点就会挂料、产生附加电动势。线圈绕组的直流电阻一般在 30~60 Ω,励磁电流设计在 100~250 mA 之间,算下来单次励磁功耗几瓦,这也是整机温升的主要来源。

提示:线圈电阻和励磁电流这两个参数是后面恒流源设计的输入,图纸上通常标在线圈出线端附近,改口径时一定要重新核对,不能照抄小口径的参数。

2.2 励磁驱动:恒流源配合 H 桥的方波输出

电磁流量计不用直流励磁,因为直流会在电极上积累极化电压,几分钟就把信号吃掉。工程上普遍采用低频矩形波励磁,频率取工频的 1/16 或 1/8,也就是 50 Hz 电网下 3.125 Hz 或 6.25 Hz。这个频率的好处是:方波边沿远离工频,正负半周对称可以相互抵消直流偏置,而且和工频周期成整数倍关系,便于做同步采样。

驱动拓扑一般是恒流源加 H 桥。H 桥负责换向,四个开关管两两导通形成正、反两个电流方向;恒流源负责把电流锁死,因为线圈电阻随温度会漂,恒压驱动的话励磁电流跟着漂,流量系数就跟着漂。常见的恒流实现是用运放加一个采样电阻构成电流负反馈,采样电阻上的压降和设定值比较,误差放大后驱动 H 桥的使能端。

/* 励磁电流设定值换算:12 位 DAC 输出,采样电阻 0.5R,目标 150mA DAC 参考 3.3V,运放增益 1,则 V_dac = I * R_sense = 0.15 * 0.5 = 75mV DAC 码值 = 75mV / 3.3V * 4095 ≈ 93 */ #define EXC_CURRENT_MA 150 #define R_SENSE_MILLIOHM 500 /* 0.5Ω = 500mΩ */ #define DAC_VREF_MV 3300 #define DAC_FULL_SCALE 4095 static uint16_t exc_dac_code(uint16_t current_ma) { /* 采样电阻压降(mV) = 电流(mA) * 电阻(mΩ) / 1000 */ uint32_t v_sense_mv = (uint32_t)current_ma * R_SENSE_MILLIOHM / 1000; uint32_t code = v_sense_mv * DAC_FULL_SCALE / DAC_VREF_MV; return (uint16_t)(code > DAC_FULL_SCALE ? DAC_FULL_SCALE : code); }

这段代码只做一件事:把"我要 150 mA"翻译成 DAC 寄存器该写多少。参数里R_SENSE_MILLIOHM必须和原理图上采样电阻的实物值一致,改板子换了电阻却没改这个宏,励磁电流就会整体偏移,表现为全量程读数按同一比例偏大或偏小,很容易被误判成标定问题。EXC_CURRENT_MA一般不要超过线圈额定值,短时间超一点能让信号变大,但线圈发热后电阻上升,恒流环如果余量不够就会掉出调节范围。

H 桥的换向由两个互补的 PWM 或普通 GPIO 控制,正半周开 Q1/Q4,负半周开 Q2/Q3,中间必须插入死区,否则直通瞬间就是一次炸管。

2.3 前置放大与滤波:仪表放大器把微伏信号抬到 ADC 量程

电极出来的是差分信号,源阻抗高(几百欧到几千欧),共模成分大(励磁线圈耦合过来的几伏共模)。这种场合不能用普通运放搭差分,标准做法是仪表放大器,常见的是 INA128、AD620 这一类,增益用一只外部电阻设定:G = 1 + 50 kΩ / Rg。想要 100 倍增益,Rg 取 505 Ω 左右。

增益不能拍脑袋定。设 ADC 参考 2.5 V、差分输入范围 ±2.5 V,电极信号最大 2 mV(对应满量程流速),那增益最多 1000 倍就会顶到轨。实际取 200~500 倍,留出两倍以上的过载余量,因为泵启停瞬间的冲击信号往往是稳态的好几倍。

放大之后还要过一道滤波。工频 50 Hz 及其谐波是最主要的干扰源,通常用一个 50 Hz 陷波器加一个二阶低通,低通截止频率设在 10 Hz 附近——励磁方波的边沿含有高频成分,但采样窗口是在半周稳定后才打开的,所以可以放心把带宽压窄。陷波器的 Q 值别设太高,Q 太大对元件容差极其敏感,批量生产时 50 Hz 中心频率一偏,衰减就没了。我一般把 Q 压在 3~5,牺牲一点深度换一致性。

/* 一阶低通的离散化写法,用于 ADC 采样后的软件滤波 fc = fs / (2π * (1-a) / a ),a 越接近 1 截止频率越低 */ #define LPF_ALPHA_Q15 0.02f /* 采样率 1kHz 时对应约 3.2Hz 截止 */ static float lpf_state = 0.0f; float lpf_update(float raw) { /* 一阶 IIR:y[n] = a*x[n] + (1-a)*y[n-1] */ lpf_state = LPF_ALPHA_Q15 * raw + (1.0f - LPF_ALPHA_Q15) * lpf_state; return lpf_state; }

这个一阶 IIR 是软件侧的第二道防线,硬件陷波器负责压掉 50 Hz 主峰,软件低通负责把残余的宽带噪声再削一轮。LPF_ALPHA_Q15这个名字里带 Q15 是历史遗留,实际用的是浮点,改的时候注意别被名字误导。α 取太小响应会变慢,标定时会发现流量阶跃后读数要好几秒才跟上,现场表现为"阀门关了读数还在慢慢往下掉"。

2.4 ADC 与 MCU 选型:采样率、同步和隔离怎么排

ADC 选型看三个指标:有效位数、采样率、输入类型。电极信号解调后落在几十微伏到几毫伏,12 位 ADC 在 2.5 V 参考下 1 LSB 就是 0.6 mV,根本不够用,所以要么选 16 位以上的 ΔΣ ADC,要么在 ADC 前再加一级可编程增益。这套电路用的是外置 16 位差分 ADC,采样率设 1 kHz,配合 MCU 的定时器触发,保证采样点和励磁方波的相位关系是固定的。

MCU 这边要求不高:一颗带 12 位 ADC(用于监测励磁电流和电源电压)、至少两个定时器、一个 UART、最好带 DAC 的 Cortex-M3 就够。选型时容易忽略的是 ADC 触发源——如果 ADC 由软件启动,采样时刻会随主循环抖动,而励磁方波是定频的,抖动会让同步解调的相位慢慢漂移,最终表现为零点缓慢爬升。所以必须用定时器硬件触发 ADC,软件只负责读结果。

模拟地和数字地的处理是老生常谈但必须提:前置放大器的地、ADC 的模拟地单独走一条,最后在 ADC 芯片下方单点连到数字地。励磁回路的大电流走线要远离电极信号线,实在绕不开就垂直交叉,别平行走。整机如果接 4-20 mA 输出,输出侧和测量侧建议做隔离,否则现场变频器一开,共模干扰直接顺着电流环灌进来。

3. 下位机程序骨架:定时器节拍、同步采样与流量换算

硬件定了,程序要做的事其实很收敛:按固定节拍推励磁状态机、在采样窗口里攒够数据、解调出直流量、换算成流量、再输出。麻烦的是每一步都不能被中断打断得太随意。

3.1 工程目录与文件职责划分

拿到一个下位机工程,先看目录就能判断作者水平。比较合理的分法是按"硬件抽象—算法—业务"三层:

flowmeter/ ├── bsp/ │ ├── bsp_adc.c /* ADC 初始化、定时器触发、DMA 搬运 */ │ ├── bsp_exc.c /* 励磁 DAC 设定、H 桥换向 GPIO */ │ ├── bsp_lcd.c /* 段码屏或 12864 驱动 */ │ └── bsp_uart.c /* Modbus RTU 从站 */ ├── algo/ │ ├── demod.c /* 相敏解调、滑动平均 */ │ ├── empty_pipe.c /* 空管检测 */ │ └── calibrate.c /* 标定系数存储与换算 */ ├── app/ │ ├── app_flow.c /* 主状态机、流量计算调度 */ │ └── app_menu.c /* 按键菜单、参数设置 */ └── main.c

这种分法的好处是换 MCU 时只动 bsp,算法层原样搬。我见过不少工程把所有东西塞在 main.c 里,一个 800 行的 while(1),改一个滤波系数要在里面翻十分钟。如果你拿到的是后者,别急着重构,先把算法层的函数抽出来,再逐步替换。

文件职责还有一条隐性约定:bsp_exc.c里不应该出现任何浮点运算。励磁换向是在定时器中断里做的,中断里跑浮点会拉长执行时间,中断抖动会让采样点相位偏移。所有电流换算、系数计算都应该在初始化阶段算完,中断里只写寄存器。

3.2 定时器节拍:励磁状态机的实现

励磁方波周期 320 ms(3.125 Hz),每个半周 160 ms。这 160 ms 不是全部用来采样的,前段要等信号建立(线圈是电感,电流上升需要时间),后段要留出换向死区。实际分配通常是:建立 40 ms、采样 100 ms、留白 20 ms。

状态机挂在定时器更新中断上,每 10 ms 进一次,计数值到点就切状态。

typedef enum { PHASE_POS_SETTLE = 0, /* 正向励磁,等待建立 */ PHASE_POS_SAMPLE, /* 正向采样窗口 */ PHASE_POS_TAIL, /* 正向留白,准备换向 */ PHASE_NEG_SETTLE, PHASE_NEG_SAMPLE, PHASE_NEG_TAIL } exc_phase_t; static exc_phase_t s_phase = PHASE_POS_SETTLE; static uint16_t s_tick_in_phase = 0; /* 每 10ms 调用一次,由 TIM3 更新中断驱动 */ void exc_tick_10ms(void) { s_tick_in_phase++; switch (s_phase) { case PHASE_POS_SETTLE: if (s_tick_in_phase >= 4) { /* 40ms */ s_phase = PHASE_POS_SAMPLE; s_tick_in_phase = 0; adc_window_open(ADC_WIN_POS); /* 打开正向采样窗口 */ } break; case PHASE_POS_SAMPLE: if (s_tick_in_phase >= 10) { /* 100ms */ adc_window_close(); s_phase = PHASE_POS_TAIL; s_tick_in_phase = 0; } break; case PHASE_POS_TAIL: if (s_tick_in_phase >= 2) { /* 20ms,含死区 */ exc_set_direction(EXC_NEG); /* 换向到负半周 */ s_phase = PHASE_NEG_SETTLE; s_tick_in_phase = 0; } break; /* 负半周三个状态与正向对称,此处省略 */ default: break; } }

这里的关键是adc_window_open/close:采样窗口只在这 100 ms 内打开,窗口外的 ADC 数据一律丢弃。因为励磁换向瞬间线圈会产生几百毫伏的反电动势尖峰,这个尖峰耦合到电极上,如果被采进去,解调结果就会带上一个和流量无关的固定偏置。

exc_set_direction内部要保证先关断再开通,中间插入至少 2 ms 死区。这个死区时间写在PHASE_*_TAIL的计数值里,改励磁频率时两个地方要一起改,只改一个会出现"换向瞬间两只管子同时导通"——这是我最不想在调试台上看到的画面。

3.3 同步采样与滑动平均滤波

采样窗口一开,定时器以 1 kHz 触发 ADC,100 ms 内正好攒 100 个点。这 100 个点先做一次软件低通,再累加求均值。

#define WIN_SAMPLES 100 static int32_t s_pos_acc = 0, s_neg_acc = 0; static uint16_t s_pos_cnt = 0, s_neg_cnt = 0; /* ADC 转换完成回调,由 DMA 或中断调用 */ void adc_sample_isr(int16_t raw) { float filtered = lpf_update((float)raw); if (adc_window_is(ADC_WIN_POS)) { s_pos_acc += (int32_t)filtered; s_pos_cnt++; } else if (adc_window_is(ADC_WIN_NEG)) { s_neg_acc += (int32_t)filtered; s_neg_cnt++; } } /* 负半周采样结束后调用,产出一次解调结果 */ int32_t demod_get_delta(void) { if (s_pos_cnt == 0 || s_neg_cnt == 0) return 0; int32_t pos_avg = s_pos_acc / s_pos_cnt; int32_t neg_avg = s_neg_acc / s_neg_cnt; s_pos_acc = 0; s_pos_cnt = 0; s_neg_acc = 0; s_neg_cnt = 0; /* 正负半周相减再除以 2,抵消共模和直流偏置 */ return (pos_avg - neg_avg) / 2; }

demod_get_delta返回的是一个 ADC 码值差,物理意义是"正励磁时电极电压减去负励磁时电极电压的一半"。为什么要除以 2:正半周时信号是 +E 加上偏置 Voff,负半周是 -E 加上同一个 Voff,相减得到 2E,所以除以 2 才是 E。这个操作就是相敏解调,比任何模拟滤波都有效,因为它对与励磁不同频的干扰完全不敏感。

滑动平均还要在解调结果上再做一层。单次解调结果的噪声仍然不小,工程上用 8~32 点的滑动窗口平滑。窗口长度的取舍很实际:窗口长,静态读数稳,但流量阶跃响应慢;窗口短,响应快,但小流量时数字跳得厉害。DN100 以上大口径流速慢,可以用 32 点;DN15 小口径建议压到 8 点,否则标定时等一个稳定值要等到怀疑人生。

注意:滑动平均的缓冲区一定要在每次上电时清零,或者用足够长的预热时间。我遇到过一批样机,缓冲区是全局变量默认 0,上电后前几秒的解调结果被 0 拉低,显示从 0 慢慢爬到真实值,被现场当成"仪表响应慢"投诉。

3.4 流量换算与 4-20 mA 输出

解调出来的 ADC 码值要经过三步才变成流量:先折算成电极电压(mV),再乘标定系数变成流速或流量,最后做量程映射输出 4-20 mA。

/* 换算参数,标定时写入 EEPROM */ #define ADC_LSB_MV 0.0763f /* 2.5V 参考 / 32768,16 位差分 */ #define AMP_GAIN 200.0f extern float g_cal_k; /* 标定系数:L/min per mV */ float adc_to_flow_lpm(int32_t delta) { float v_electrode_mv = (float)delta * ADC_LSB_MV / AMP_GAIN; float flow = v_electrode_mv * g_cal_k; /* 小信号切除,避免零点附近来回跳 */ if (flow < 0.0f && flow > -CUTOFF_LPM) flow = 0.0f; if (flow > 0.0f && flow < CUTOFF_LPM) flow = 0.0f; return flow; } /* 4-20mA 输出:量程下限对应 4mA,上限对应 20mA */ uint16_t flow_to_dac(float flow_lpm) { float ratio = (flow_lpm - g_range_min) / (g_range_max - g_range_min); if (ratio < 0.0f) ratio = 0.0f; if (ratio > 1.0f) ratio = 1.0f; /* 4mA 对应 DAC 码 0.2*FS,20mA 对应 FS */ uint32_t full = 4095; return (uint32_t)(full * (0.2f + 0.8f * ratio)); }

ADC_LSB_MV这个常数必须和 ADC 的实际参考电压、位宽一一对应。换一颗参考源芯片却没改这个宏,全量程会按比例偏,而且偏得很隐蔽——因为标定环节会把这个误差吸收进g_cal_k,等到换量程时才发现怎么都对不上。CUTOFF_LPM是切除阈值,一般取满量程的 0.5%~1%,太小了不起作用,太大了小流量段直接丢。

flow_to_dac里的 0.2 和 0.8 是 4 mA/20 mA 的归一化比例,这个映射是线性的,但现场有些系统要求开方输出,那就得在 ratio 上先做一次开方,改之前一定要确认上位机侧要的是线性还是开方信号。

4. 相敏解调与零点稳定:把微伏级信号从噪声里捞出来

电路和程序骨架搭完,真正的硬仗在零点。电磁流量计行业里有句话:满量程准不准看标定,零点稳不稳看设计。零点漂移是这类仪表最常见的返修原因,而漂移的来源大多能追溯到解调环节。

4.1 极化电压和直流偏置从哪里来

电极插在电解液里,本身就是一个电化学半电池。即使没有励磁,两个电极之间也存在几十到几百毫伏的直流电位差,这就是极化电压。它比我们要测的信号大两三个数量级,直接放大必然饱和。低频矩形波励磁之所以能work,就是因为它让极化电压在正负半周基本保持不变,相减时被抵消掉。

但"基本不变"是有条件的。如果励磁频率太低(比如 0.5 Hz),半周时间长达 1 秒,极化电压在这个尺度上会缓慢变化,抵消就不彻底,表现为零点随时间漂移。如果频率太高,电极和流体的界面电容来不及充放电,信号本身会被衰减。3.125 Hz 这个值是多年试出来的折中。

除了极化电压,还有几路直流偏置会混进来:前级运放的输入失调电压(几十微伏到几百微伏)、电极引线的热电势(不同金属接触产生,几微伏每摄氏度)、以及 ADC 本身的偏移。这些加起来可能比信号还大,但它们有一个共同特点——不随励磁方向变化。所以相减能一次干掉全部。

4.2 正负半周差分:相敏解调的代码实现

前面demod_get_delta已经给出了基础版本,但工程上还要处理两件事:半周采样点数不一致的容错,以及解调结果的符号约定。

/* 增强版解调:带点数容错和极性校正 */ int32_t demod_delta_ex(int32_t *out_common) { if (s_pos_cnt < WIN_SAMPLES / 2 || s_neg_cnt < WIN_SAMPLES / 2) { /* 采样点太少说明窗口被中断打乱,本次结果作废 */ s_pos_acc = s_neg_acc = 0; s_pos_cnt = s_neg_cnt = 0; return 0; } int32_t pos_avg = s_pos_acc / s_pos_cnt; int32_t neg_avg = s_neg_acc / s_neg_cnt; /* 共模分量,用于空管和故障判断 */ if (out_common) *out_common = (pos_avg + neg_avg) / 2; s_pos_acc = s_neg_acc = 0; s_pos_cnt = s_neg_cnt = 0; int32_t delta = (pos_avg - neg_avg) / 2; /* 极性校正:接线反了或线圈绕向反了,在这里一次性翻正 */ return g_polarity_invert ? -delta : delta; }

out_common这个输出很多人会忽略,但它非常有用:正负半周的平均值(也就是共模)反映的是电极直流通路的状态。满管时它是个稳定的小值,空管时电极暴露在空气里,共模会明显抬高,这就是下一节空管检测的依据。

g_polarity_invert解决的是装配一致性问题。线圈绕向、电极接线、H 桥换向逻辑,这三处任何一处方向反了,流量符号就是反的。与其在生产线上要求工人百分之百不出错,不如在软件里留一个标志位,标定时发现反了就置位,省下一整批返工。

4.3 空管检测与电导率下限判断

空管检测是电磁流量计的安全功能:管道里没液体时,电极间阻抗极高,前级放大器可能饱和,输出一个虚假的大流量,下游的配料系统照着这个数投料,后果很直接。

判断方法有两类。一类看共模电平,也就是上面out_common的输出;另一类主动注入一个小信号测电极间阻抗。前者不增加硬件,后者更可靠但要额外电路。这套方案用的是前者加一个阈值判断:

#define EMPTY_CM_THRESHOLD 1800 /* ADC 码值,需按实际样机标定 */ #define EMPTY_HOLD_MS 2000 /* 连续超限 2 秒才判定空管 */ static uint32_t s_empty_timer = 0; pipe_state_t check_pipe_state(int32_t common) { if (common > EMPTY_CM_THRESHOLD || common < -EMPTY_CM_THRESHOLD) { if (++s_empty_timer >= EMPTY_HOLD_MS / 10) { return PIPE_EMPTY; } } else { s_empty_timer = 0; } return PIPE_FULL; }

EMPTY_CM_THRESHOLD这个值必须实测:满管时记录共模电平,空管时再记录一次,取两者中间偏空管侧的值。不同口径、不同衬里材料差别很大,不能用一个数通吃。EMPTY_HOLD_MS是防抖,泵启停时管道里会短暂夹气,共模会跳一下,没有这个延时就会频繁误报。

还有一个容易被忽略的点:介质电导率有下限。电磁流量计要求介质电导率一般不低于 5 μS/cm,去离子水、纯溶剂这类介质测不了。电导率太低时电极间阻抗高,前级放大器的输入偏置电流会在上面产生额外压降,表现为读数偏低且不线性。选型阶段就要确认介质电导率,装到现场再发现,换表成本比换介质高得多。

4.4 标定系数:从 mV 到 L/min 的映射

标定的本质是求一个比例系数。理想情况下这个系数可以从 E = B·D·v 和管道截面积算出来,但实际磁场分布不均匀、边缘效应、装配公差都让理论值偏差 5%~20%,所以必须实测。

标定流程一般是:把流量调到满量程的 80%~100%,稳定运行几分钟,记录标准表读数和本机的解调码值;再把流量调到 20% 左右重复一次;用两点算斜率和截距。如果只做单点标定,零点误差会被斜率吸收,小流量段就偏了。

/* 两点标定:已知两个标准流量点对应的解调码值 */ void calibrate_two_point(int32_t d1, float q1, int32_t d2, float q2) { if (d1 == d2) return; /* 两点重合,拒绝写入 */ /* 斜率 k = (q2-q1)/(d2-d1),单位 L/min per ADC码 */ float k = (q2 - q1) / (float)(d2 - d1); float b = q1 - k * (float)d1; /* 截距 */ /* 折算到 per mV 存进 EEPROM,方便换 ADC 位宽 */ g_cal_k = k * ADC_LSB_MV / AMP_GAIN * 0.001f; g_cal_b = b; eeprom_write(CAL_K_ADDR, g_cal_k); eeprom_write(CAL_B_ADDR, g_cal_b); }

d1和d2的间距要足够大,至少差 50% 满量程,否则斜率对噪声极其敏感。g_cal_b这个截距项很多人省掉,直接强制过零点——如果传感器本身零点做得好,这样确实更稳;但如果零点有系统性偏差,强制过零会让小流量段持续偏。我的习惯是记录截距,但只有当截距超过满量程 1% 时才启用补偿,否则宁可强制归零换取稳定性。

存 EEPROM 时要注意寿命,标定不是天天做,但程序里如果有"每次上电都写一次"的逻辑,几次调试就把扇区写坏了。带掉电检测的写入加一个脏标志,只在参数真正变化时落盘。

5. 调试排查:现场最容易翻车的五个点

图纸能看懂、程序能编译,不等于装到管道上就能读准。下面五条是这类项目里出现频率最高的问题,按"现象—原因—解决"整理,遇到对不上的情况可以先照着排一遍。

5.1 硬件与接地:三条把读数搞飘的硬伤

第一条,泵一启停读数就大幅跳变。现象是静态时读数很稳,只要旁边水泵或变频器动作,流量值就上下乱窜,有时能跳到满量程。原因是信号线与动力线平行走线,或者屏蔽层两端都接了地形成地环路,变频器的共模干扰顺着屏蔽层灌进前级。解决办法是屏蔽层只在传感器侧单端接地,信号线与动力线分开桥架、间距至少 20 cm,实在绕不开就垂直交叉;如果干扰仍然明显,在 4-20 mA 输出侧加隔离模块,把测量地和输出地彻底断开。

第二条,零点缓慢漂移,一天能漂几个百分点。现象是管道满管静止时,读数从小往大慢慢爬。原因通常是电极表面附着了一层污物或者气泡,改变了电极的电化学状态;也可能是励磁电流随温度漂了而恒流环没有真正闭环。排查顺序是先看励磁采样电阻上的压降是否稳定,稳定就说明恒流环没问题,问题在电极;拆下电极检查有没有结垢,清洗后重新做一次满管静止校零。如果介质容易结垢,选型阶段就该考虑可拆卸式电极或者刮刀式结构。

第三条,励磁电流只有设定值的一半。现象是信号幅度明显偏低,标定时发现灵敏度不够。原因是 H 桥的 MOS 管栅极驱动电压不足,导通电阻偏大,或者恒流环的运放输出摆幅被限制。用示波器看采样电阻上的波形,如果方波顶部有塌陷,基本可以确定是驱动能力不够;检查栅极驱动电阻是不是选大了,或者把 H 桥供电电压提高一档。

注意:排查励磁回路时不要直接用手碰线圈端子,反向电动势的尖峰比供电电压高得多,示波器探头也要先确认耐压量程。

5.2 程序与标定:两条让曲线对不上的软坑

第四条,读数随流量变化的曲线是弯的,不是直线。现象是零点和小流量都还行,越往满量程偏得越多,或者反过来。原因一般是标定点选得太近,两点标定的斜率被噪声带偏了;也可能是满量程时前级放大器进入非线性区,增益下降。先确认标定点间距是否超过 50% 满量程,再看满量程时 ADC 码值有没有接近满幅的 80%,接近了就说明增益太大,把AMP_GAIN降一档重标。

第五条,显示值和 4-20 mA 输出对不上。现象是屏幕显示 35 m³/h,上位机收到的电流换算出来是 32 m³/h。原因是 DAC 输出没有做两点校准,或者量程参数g_range_min/g_range_max和上位机配置的不一致。用万用表实测输出电流,分别在 4 mA 和 20 mA 点校准 DAC 码值,再核对上位机侧的量程配置。这类问题排查起来最快,但最容易在交接时扯皮,建议出厂前把显示值、电流值、通讯值三者做一次一致性记录。

还有一个隐性问题值得单独提:定时器中断里做浮点运算。现象是流量读数带规律性的小幅波动,频率和主循环差不多。原因是中断执行时间随数据变化,采样点相位不固定。把浮点运算全部移到主循环,中断里只做计数和标志位,波动通常立刻消失。

6. 二次开发路线:改量程、换口径与标定验证的具体做法

拿到这套电路图和程序,最常见的改造需求是换口径。换口径不是改一个常数那么简单,至少要同步动四个地方,顺序错了会白折腾一轮。

改造项涉及位置注意点
电极间距 D硬件测量管直接影响信号幅度,DN 减半信号也减半
励磁电流EXC_CURRENT_MA、采样电阻大口径可适当提高,但受线圈功耗限制
放大增益AMP_GAIN、Rg 电阻保证满量程时 ADC 不超过 80% 满幅
标定系数g_cal_k、g_cal_b换口径必须重新实测,不能按比例推算

改完参数后先做静态验证:满管静止,连续观察 30 分钟零点,漂移应该在满量程的 0.2% 以内。再做动态验证:至少取 20%、50%、80% 三个流量点,每点稳定运行 5 分钟,记录本机值和标准表值,算相对误差。三个点误差同号且接近,说明是斜率问题,调g_cal_k;误差随流量增大而变号,说明是非线性,得回头查放大链路。

有个技巧我一直在用:把每次标定的原始数据(解调码值、标准表读数、温度、励磁电流)一起存进 EEPROM 的一块日志区,只写不覆盖。后面现场再出问题,把日志读出来一看就知道是标定当时的条件变了,还是运行中漂了,比反复追问现场人员靠谱得多。

typedef struct { uint16_t magic; /* 0x5A5A 表示本条有效 */ int32_t demod_code; /* 标定时的解调码值 */ float std_flow; /* 标准表读数 L/min */ int16_t temp_c; /* 环境温度 */ uint16_t exc_ma; /* 当时的励磁电流 */ } cal_log_t; /* 每条 16 字节,一个扇区能存 64 条 */

这个结构体不参与实时计算,只在标定完成时追加一条,代价极小,但事后追溯的价值很高。

改造时还有一条血泪经验:不要在原工程上直接改,先复制一份打上日期标签,改完能跑再合并。我吃过一次亏,为了调一个滤波系数在主干上改了三处,结果把励磁状态机的死区时间也带偏了,样机换向瞬间直通,两只管子当场报废,查了两天才定位到是一次顺手修改。从那以后我每次动励磁相关代码,都强制走一遍示波器看死区波形,再上管道实测。希望这些内容能帮到你,少走几段弯路。

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

返回列表