You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于openpyxl与scipy求解Pi衰减器电阻网络非线性方程组问题

问题描述

我正在用数据科学工具实现Excel自动化,处理Pi衰减器的真实阻值计算:该元件端子3短接R1和R2共地,无法直接用万用表测真实阻值。我已通过手动输入测量值,用scipy的fsolve求解出真实阻值(等效R1、R2无短接的情况),验证代码正常。

方程组由欧姆表跨接引脚(1,3)、(2,3)、(1,2)的串并联阻值推导,确认正确,属于非线性方程组(变量在分母)。

现在需要批量处理数百组Excel测试数据,适配不同衰减器型号。已将Excel数据读取为numpy数组,但传入fsolve时出现大量小数位输出,以及RuntimeWarning(double_scalars溢出、除零错误),警告出现在方程组赋值环节。

目前用单行数据调试,尝试过强制numpy数组为float64/float32、在函数内创建新数组、转换参数类型、四舍五入等方法,均未解决问题。需要基于numpy、fsolve和openpyxl的解决方案。

核心问题分析

你的代码里方程组的定义完全错误:

  • 手动测试时,方程逻辑是1/(1/x+1/(y+w)) - 测量值 = 0(x/y/w是真实阻值,测量值是固定输入数据)
  • 但现在代码中写成了1/(1/z[0]+1/(z[1]+z[2])) - z[0],把待求解的真实阻值z[0]当成了测量值,直接导致方程组逻辑混乱,fsolve迭代时出现除零、溢出。

此外还有两个次要问题:

  1. 初始猜测值zGuess用固定的[1,1,1],和实际电阻值差距过大,容易导致迭代发散
  2. 未处理Excel数据可能存在的非数值情况(比如空单元格)
解决方案

1. 修正方程组逻辑

将测量值作为参数传入convert_measured函数,用functools.partial绑定每组的测量值,避免混淆待求解变量和输入数据。

2. 优化初始猜测值

用当前行的测量值作为初始猜测,真实阻值和测量值不会相差过大,能提升fsolve的收敛速度和稳定性。

3. 数据类型校验与处理

确保从Excel读取的数值是float类型,处理空单元格避免程序崩溃。

完整修正代码
import numpy as np
from scipy.optimize import fsolve
import openpyxl
from functools import partial

wb = openpyxl.load_workbook("datafile.xlsx")
ws = wb['Group A']

# 测量数据区域:I15-K34(列9-11,行15-34)
MeasColBegin = 9
MeasColEnd = 11
MeasRowBegin = 15
MeasRowEnd = 15  # 调试时只用1行,实际改为34

# 计算结果写入区域:I39-K58(列9-11,行39-58)
CalcColBegin = 9
CalcColEnd = 11
CalcRowBegin = 39

def convert_measured(z, measured_vals):
    """
    z: 待求解的真实阻值数组 [R1_real, R2_real, R3_real]
    measured_vals: 测量得到的等效阻值数组 [R_13, R_23, R_12]
    """
    x, y, w = z
    R13, R23, R12 = measured_vals
    
    F = np.empty(3, dtype=np.float64)
    # 修正后的方程组:等效测量值等于计算值
    F[0] = 1/(1/x + 1/(y + w)) - R13
    F[1] = 1/(1/y + 1/(x + w)) - R23
    F[2] = 1/(1/w + 1/(x + y)) - R12
    return F

def main():
    # 遍历测量数据行
    for idx, row in enumerate(ws.iter_rows(min_row=MeasRowBegin, max_row=MeasRowEnd,
                                           min_col=MeasColBegin, max_col=MeasColEnd), start=0):
        # 提取测量值,转为float,跳过空值
        measured_vals = []
        for cell in row:
            val = cell.value
            if val is None:
                print(f"第{MeasRowBegin+idx}行存在空值,跳过")
                measured_vals = None
                break
            measured_vals.append(float(val))
        
        if measured_vals is None:
            continue
        
        measured_vals = np.array(measured_vals, dtype=np.float64)
        print(f"测量值: {measured_vals}")
        
        # 用测量值作为初始猜测,提升收敛性
        z_guess = measured_vals.copy()
        
        # 用partial绑定当前组的测量值,传递给fsolve
        solve_func = partial(convert_measured, measured_vals=measured_vals)
        z_sol, infodict, ier, mesg = fsolve(solve_func, z_guess, full_output=True)
        
        # 检查求解是否成功(ier=1表示成功)
        if ier == 1:
            print(f"真实阻值: {np.round(z_sol, 4)}")
            # 将结果写入Excel对应行
            calc_row = CalcRowBegin + idx
            for col_offset, val in enumerate(z_sol):
                ws.cell(row=calc_row, column=CalcColBegin + col_offset, value=np.round(val, 4))
        else:
            print(f"第{MeasRowBegin+idx}行求解失败: {mesg}")
    
    # 保存修改后的Excel文件
    wb.save("datafile_processed.xlsx")
    print("计算完成,结果已保存到datafile_processed.xlsx")

if __name__ == "__main__":
    main()
关键说明
  • 使用functools.partial将每组的测量值绑定到目标函数,符合fsolve的参数要求(仅接受待求解变量作为第一个参数)
  • 初始猜测值用测量值复制,保证迭代起点接近真实解,减少发散概率
  • 增加求解结果状态判断(ier参数),及时发现求解失败的情况
  • 结果保留4位小数写入Excel,避免过多无效小数位
  • 处理Excel空单元格,防止程序崩溃

内容的提问来源于stack exchange,提问作者Kritischer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 01:00:59