ITPub博客

首页 > 应用开发 > IT综合 > 模拟电路设计:科学计算与仿真验证的艺术

模拟电路设计:科学计算与仿真验证的艺术

投稿 IT综合 作者:bhluo 时间:2026-01-21 14:56:22 0 删除 编辑

# 模拟电路设计:科学计算与仿真验证的艺术


## 非线性电路的神奇现象


模拟电路中存在着诸多看似神奇的行为,实则是物理学定律的精确体现。以负阻振荡器为例,这种电路能够自发产生振荡,违反直觉却能稳定工作。


```python

import numpy as np

import matplotlib.pyplot as plt


# 负阻振荡器仿真

class NegativeResistanceOscillator:

    def __init__(self, L=1e-3, C=1e-9, R_negative=-50, R_series=10):

        self.L = L  # 电感

        self.C = C  # 电容

        self.Rn = R_negative  # 负电阻

        self.Rs = R_series   # 串联电阻

        self.f0 = 1/(2*np.pi*np.sqrt(L*C))  # 谐振频率

        

    def simulate(self, V0=0.1, duration=1e-6, steps=10000):

        """模拟振荡器启动过程"""

        dt = duration/steps

        time = np.linspace(0, duration, steps)

        V = np.zeros(steps)  # 电容电压

        I = np.zeros(steps)  # 电感电流

        

        # 初始条件

        V[0] = V0

        

        for i in range(1, steps):

            # 负阻器件特性(分段线性模型)

            if abs(V[i-1]) < 0.7:  # 小信号区

                Gn = 1/self.Rn  # 负电导

            else:  # 饱和区

                Gn = -1/100  # 正电阻

            

            # 状态方程: dV/dt = I/C - V/(R*C)

            #          dI/dt = -V/L - I*R/L

            dV_dt = I[i-1]/self.C - Gn*V[i-1]

            dI_dt = -V[i-1]/self.L - self.Rs*I[i-1]/self.L

            

            # 欧拉法积分

            V[i] = V[i-1] + dV_dt * dt

            I[i] = I[i-1] + dI_dt * dt

            

        return time, V, I


# 运行仿真

osc = NegativeResistanceOscillator()

time, V, I = osc.simulate(V0=0.001, duration=2e-6)


# 可视化结果

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6))


ax1.plot(time*1e6, V*1e3)

ax1.set_xlabel('时间 (µs)')

ax1.set_ylabel('电压 (mV)')

ax1.set_title('负阻振荡器电压波形')

ax1.grid(True, alpha=0.3)


ax2.plot(time*1e6, I*1e3)

ax2.set_xlabel('时间 (µs)')

ax2.set_ylabel('电流 (mA)')

ax2.set_title('负阻振荡器电流波形')

ax2.grid(True, alpha=0.3)


plt.tight_layout()

plt.show()


print(f"理论谐振频率: {osc.f0/1e6:.2f} MHz")

print(f"实际振荡频率: {1/(time[np.argmax(V)]*4)*1e-6:.2f} MHz")

```


## 运放电路的"诡异"行为


运算放大器在某些配置下会表现出反直觉的特性,特别是涉及相位裕度和稳定性问题时。


```python

# 运放稳定性分析

class OpAmpStability:

    def __init__(self, A0=1e5, f1=10, f2=1e6, f3=10e6):

        """三极点运放模型"""

        self.A0 = A0  # 直流增益

        self.f1 = f1  # 第一极点 (Hz)

        self.f2 = f2  # 第二极点 (Hz)

        self.f3 = f3  # 第三极点 (Hz)

    

    def open_loop_gain(self, f):

        """开环增益频率响应"""

        s = 1j * 2 * np.pi * f

        # 三极点模型传递函数

        A_ol = self.A0 / ((1 + s/(2*np.pi*self.f1)) * 

                          (1 + s/(2*np.pi*self.f2)) * 

                          (1 + s/(2*np.pi*self.f3)))

        return A_ol

    

    def closed_loop_response(self, f, beta):

        """闭环响应"""

        A_ol = self.open_loop_gain(f)

        A_cl = A_ol / (1 + A_ol * beta)

        return A_cl

    

    def phase_margin(self, beta):

        """计算相位裕度"""

        frequencies = np.logspace(0, 9, 1000)

        phases = []

        

        for f in frequencies:

            A_ol = self.open_loop_gain(f)

            loop_gain = A_ol * beta

            phase = np.angle(loop_gain, deg=True)

            phases.append(phase)

            

            # 找到增益为1的频率(0dB点)

            if abs(loop_gain) <= 1:

                phase_margin = 180 + phase

                return phase_margin, f

        

        return None, None


# 分析不同反馈系数下的稳定性

opamp = OpAmpStability()

betas = [0.1, 0.01, 0.001]  # 反馈系数


fig, axes = plt.subplots(2, 2, figsize=(12, 8))

frequencies = np.logspace(0, 9, 1000)


for idx, beta in enumerate(betas):

    ax = axes[idx//2, idx%2]

    

    # 计算幅度和相位

    magnitudes = []

    phases = []

    

    for f in frequencies:

        A_ol = opamp.open_loop_gain(f)

        loop_gain = A_ol * beta

        

        magnitudes.append(20*np.log10(abs(loop_gain)))

        phases.append(np.angle(loop_gain, deg=True))

    

    # 绘制波特图

    ax.semilogx(frequencies, magnitudes, 'b-', label='幅度')

    ax.set_xlabel('频率 (Hz)')

    ax.set_ylabel('增益 (dB)', color='b')

    ax.tick_params(axis='y', labelcolor='b')

    ax.grid(True, alpha=0.3)

    

    ax2 = ax.twinx()

    ax2.semilogx(frequencies, phases, 'r--', label='相位')

    ax2.set_ylabel('相位 (度)', color='r')

    ax2.tick_params(axis='y', labelcolor='r')

    

    # 计算相位裕度

    pm, f0db = opamp.phase_margin(beta)

    ax.axhline(y=0, color='k', linestyle=':', alpha=0.5)

    if f0db:

        ax.axvline(x=f0db, color='g', linestyle=':', alpha=0.5)

    

    ax.set_title(f'β={beta}, 相位裕度={pm:.1f}°\n0dB点={f0db/1e6:.2f}MHz')

    ax.legend(loc='upper right')


axes[1, 1].axis('off')  # 隐藏多余子图

plt.tight_layout()

plt.show()

```


## 有源滤波器设计的"套路"


有源滤波器设计中存在着多种标准拓扑,每种都有其特定的数学基础和设计公式。


```python

# 有源滤波器设计与分析

class ActiveFilterDesign:

    def __init__(self):

        self.filter_types = {

            'butterworth': self.butterworth_coeffs,

            'chebyshev': self.chebyshev_coeffs,

            'bessel': self.bessel_coeffs

        }

    

    def butterworth_coeffs(self, n):

        """巴特沃斯滤波器系数"""

        # n阶巴特沃斯极点位置

        poles = []

        for k in range(1, n+1):

            theta = (2*k - 1) * np.pi / (2*n)

            real = -np.sin(theta)

            imag = np.cos(theta)

            poles.append(complex(real, imag))

        return poles

    

    def sallen_key_lpf(self, fc, Q, gain=1):

        """Sallen-Key低通滤波器设计"""

        # 设计公式

        R = 1e3  # 基准电阻

        C = 1/(2*np.pi*fc*R)  # 计算电容

        

        # 根据Q值调整元件值

        if Q > 0.5:

            R1 = R

            R2 = (2*Q - 1)*R

        else:

            R1 = R/(1 - 2*Q)

            R2 = R

        

        # 增益调整

        if gain != 1:

            Rf = R * (gain - 1)

            Rg = R

        else:

            Rf = 0

            Rg = float('inf')

        

        return {

            'R1': R1, 'R2': R2,

            'C1': C, 'C2': C,

            'Rf': Rf, 'Rg': Rg,

            'fc': fc, 'Q': Q, 'gain': gain

        }

    

    def simulate_response(self, design, f_range):

        """模拟滤波器频率响应"""

        R1, R2 = design['R1'], design['R2']

        C1, C2 = design['C1'], design['C2']

        gain = design['gain']

        

        responses = []

        for f in f_range:

            s = 1j * 2 * np.pi * f

            

            # Sallen-Key传递函数

            # H(s) = K / (s^2*R1*R2*C1*C2 + s*(R1*C1 + R2*C1) + 1)

            numerator = gain

            denominator = (s**2 * R1 * R2 * C1 * C2 + 

                          s * (R1*C1 + R2*C1) + 1)

            H = numerator / denominator

            responses.append(H)

        

        return np.array(responses)


# 设计不同Q值的滤波器

filter_designer = ActiveFilterDesign()

fc = 1e3  # 1kHz截止频率

Q_values = [0.5, 0.707, 1.0, 2.0]  # 不同品质因数


fig, axes = plt.subplots(2, 2, figsize=(12, 8))

f_range = np.logspace(1, 5, 1000)  # 10Hz到100kHz


for idx, Q in enumerate(Q_values):

    ax = axes[idx//2, idx%2]

    

    # 设计滤波器

    design = filter_designer.sallen_key_lpf(fc, Q, gain=2)

    

    # 模拟响应

    H = filter_designer.simulate_response(design, f_range)

    

    # 绘制幅度响应

    magnitude = 20*np.log10(np.abs(H))

    phase = np.angle(H, deg=True)

    

    ax.semilogx(f_range, magnitude, 'b-', linewidth=2)

    ax.set_xlabel('频率 (Hz)')

    ax.set_ylabel('增益 (dB)', color='b')

    ax.tick_params(axis='y', labelcolor='b')

    ax.grid(True, alpha=0.3)

    ax.set_xlim([10, 1e5])

    ax.set_ylim([-40, 10])

    

    # 标记-3dB点

    fc_idx = np.argmin(np.abs(magnitude + 3))

    fc_actual = f_range[fc_idx]

    ax.axvline(x=fc_actual, color='r', linestyle=':', alpha=0.7)

    ax.axhline(y=-3, color='r', linestyle=':', alpha=0.7)

    

    # 添加相位图

    ax2 = ax.twinx()

    ax2.semilogx(f_range, phase, 'g--', alpha=0.7)

    ax2.set_ylabel('相位 (度)', color='g')

    ax2.tick_params(axis='y', labelcolor='g')

    ax2.set_ylim([-180, 0])

    

    ax.set_title(f'Sallen-Key LPF\nfc={fc/1e3:.1f}kHz, Q={Q}, Gain=2\n实际fc={fc_actual/1e3:.2f}kHz')


plt.tight_layout()

plt.show()

```


## 振荡器启动的微妙平衡


振荡器启动过程涉及复杂的非线性动力学,初始条件的微小变化可能导致完全不同的结果。


```python

# 振荡器启动过程分析

class OscillatorStartupAnalysis:

    def __init__(self, R=1000, L=1e-6, C=1e-9, gm=0.01, Is=1e-12):

        self.R = R  # 谐振回路电阻

        self.L = L  # 电感

        self.C = C  # 电容

        self.gm = gm  # 晶体管跨导

        self.Is = Is  # 饱和电流

        self.w0 = 1/np.sqrt(L*C)  # 谐振频率

        

    def nonlinear_gm(self, V):

        """非线性跨导模型"""

        # 双曲正切函数模拟非线性

        return self.gm * np.tanh(V / 0.026)  # 0.026 = kT/q

    

    def simulate_startup(self, V0_range, noise_level=1e-6, duration=10e-6):

        """模拟不同初始条件的启动过程"""

<"key.q6k0.org.cn"><"lmd.q6k0.org.cn"><"sjXee.q6k0.org.cn">

        results = []

        

        for V0 in V0_range:

            # 添加微小噪声

            V0_noisy = V0 + np.random.normal(0, noise_level)

            

            # 时域仿真

            dt = 1e-9  # 1ns步长

            steps = int(duration/dt)

            

            V = np.zeros(steps)

            I = np.zeros(steps)

            V[0] = V0_noisy

            

            for i in range(1, steps):

                # 非线性微分方程

                dV_dt = I[i-1]/self.C - V[i-1]/(self.R*self.C)

                dI_dt = -V[i-1]/self.L - self.R*I[i-1]/self.L + \

                       self.nonlinear_gm(V[i-1])*V[i-1]/self.L

                

                # 改进的积分方法

                V[i] = V[i-1] + dV_dt * dt

                I[i] = I[i-1] + dI_dt * dt

                

                # 添加热噪声

                V[i] += np.random.normal(0, np.sqrt(4*1.38e-23*300*self.R*dt))

            

            results.append(V)

        

        return results


# 分析振荡器启动敏感性

oscillator = OscillatorStartupAnalysis()

V0_range = np.linspace(1e-9, 1e-6, 5)  # 不同初始电压


results = oscillator.simulate_startup(V0_range, duration=5e-6)


# 可视化结果

fig, axes = plt.subplots(3, 2, figsize=(12, 10))

time = np.linspace(0, 5e-6, len(results[0]))


for idx, (V0, V) in enumerate(zip(V0_range, results)):

    ax = axes[idx//2, idx%2]

    

    # 时域波形

    ax.plot(time*1e6, V*1e3)

    ax.set_xlabel('时间 (µs)')

    ax.set_ylabel('电压 (mV)')

    ax.set_title(f'初始V0={V0*1e6:.3f}µV')

    ax.grid(True, alpha=0.3)

    

    # 添加包络线

    envelope = np.abs(V)

    ax.plot(time*1e6, envelope*1e3, 'r--', alpha=0.5, linewidth=1)

    ax.plot(time*1e6, -envelope*1e3, 'r--', alpha=0.5, linewidth=1)


# 最后一张图显示所有曲线对比

ax_last = axes[2, 1]

for idx, (V0, V) in enumerate(zip(V0_range, results)):

    ax_last.plot(time*1e6, V*1e3, label=f'V0={V0*1e6:.3f}µV', alpha=0.7)


ax_last.set_xlabel('时间 (µs)')

ax_last.set_ylabel('电压 (mV)')

ax_last.set_title('不同初始条件对比')

ax_last.grid(True, alpha=0.3)

ax_last.legend(fontsize=8)


plt.tight_layout()

plt.show()


print(f"谐振频率: {oscillator.w0/(2*np.pi)/1e6:.2f} MHz")

print(f"起振时间变化范围: {(time[np.argmax(results[0] > 1e-3)] - time[np.argmax(results[-1] > 1e-3)])*1e6:.1f} µs")

```


## 噪声分析的数学基础


电路中的噪声看似随机,实则遵循严格的统计规律,可以通过数学方法精确分析。


```python

# 电路噪声分析

class CircuitNoiseAnalysis:

    def __init__(self, R=1000, T=300, BW=1e6):

        self.R = R  # 电阻值 (Ω)

        self.T = T  # 温度 (K)

        self.BW = BW  # 带宽 (Hz)

        self.k = 1.38e-23  # 玻尔兹曼常数

        self.q = 1.6e-19  # 电子电荷

        

    def thermal_noise_voltage(self):

        """热噪声电压"""

        return np.sqrt(4 * self.k * self.T * self.R * self.BW)

    

    def shot_noise_current(self, I_dc):

        """散粒噪声电流"""

        return np.sqrt(2 * self.q * I_dc * self.BW)

    

    def flicker_noise_psd(self, f, K_f, a_f):

        """闪烁噪声功率谱密度"""

        # 1/f噪声模型

        return K_f / (f ** a_f)

    

    def total_noise_simulation(self, circuit_transfer_func, num_samples=10000):

        """总噪声仿真"""

        # 生成频率轴

        f = np.logspace(0, 7, 1000)  # 1Hz到10MHz

        

        # 计算各噪声源

        thermal_psd = 4 * self.k * self.T * self.R  # V²/Hz

        flicker_psd = self.flicker_noise_psd(f, 1e-12, 1)

        

        # 考虑电路传输函数

        thermal_at_output = thermal_psd * np.abs(circuit_transfer_func(f))**2

        flicker_at_output = flicker_psd * np.abs(circuit_transfer_func(f))**2

        

        # 积分得到总噪声

        total_noise_power = np.trapz(thermal_at_output + flicker_at_output, f)

        total_noise_rms = np.sqrt(total_noise_power)

        

        # 时域噪声生成

        time = np.linspace(0, 1e-3, num_samples)

        dt = time[1] - time[0]

        fs = 1/dt

        

        # 生成带限白噪声

        noise_freq = np.fft.fftfreq(num_samples, dt)

        noise_spectrum = np.zeros(num_samples, dtype=complex)

        

        for i, freq in enumerate(noise_freq):

            if abs(freq) <= self.BW/2:

                # 根据功率谱密度生成随机相位

                magnitude = np.sqrt(thermal_psd * fs / 2)

                phase = np.random.uniform(0, 2*np.pi)

                noise_spectrum[i] = magnitude * np.exp(1j*phase)

        

        # 转换为时域

        noise_time = np.fft.ifft(noise_spectrum).real

        

        return {

            'frequency': f,

            'thermal_psd': thermal_psd,

            'flicker_psd': flicker_psd,

            'thermal_output': thermal_at_output,

            'flicker_output': flicker_at_output,

            'total_noise_rms': total_noise_rms,

            'time_domain': noise_time,

            'time': time

        }


# 定义电路传输函数(简单RC低通)

def rc_lpf_transfer(f, R=1e3, C=1e-9):

    """RC低通滤波器传输函数"""

    w = 2*np.pi*f

    return 1 / (1 + 1j*w*R*C)


# 执行噪声分析

noise_analyzer = CircuitNoiseAnalysis(R=1000, BW=1e6)

results = noise_analyzer.total_noise_simulation(

    lambda f: rc_lpf_transfer(f, R=1e3, C=1e-9)

<"wom.q6k0.org.cn"><"opq.q6k0.org.cn"><"polar.q6k0.org.cn">

)


# 可视化噪声分析结果

fig, axes = plt.subplots(2, 2, figsize=(12, 8))


# 噪声功率谱密度

ax1 = axes[0, 0]

ax1.loglog(results['frequency'], np.sqrt(results['thermal_output']), 'b-', label='热噪声')

ax1.loglog(results['frequency'], np.sqrt(results['flicker_psd']), 'r--', label='闪烁噪声')

ax1.set_xlabel('频率 (Hz)')

ax1.set_ylabel('噪声电压密度 (V/√Hz)')

ax1.set_title('噪声功率谱密度')

ax1.grid(True, which="both", alpha=0.3)

ax1.legend()


# 累积噪声功率

ax2 = axes[0, 1]

cumulative_noise = np.cumsum(results['thermal_output'] + results['flicker_psd']) * \

                   np.diff(results['frequency'][:2])[0]

ax2.semilogx(results['frequency'], np.sqrt(cumulative_noise))

ax2.set_xlabel('频率 (Hz)')

ax2.set_ylabel('累积噪声电压 (V)')

ax2.set_title('累积噪声功率')

ax2.grid(True, which="both", alpha=0.3)

ax2.axhline(y=results['total_noise_rms'], color='r', linestyle=':', 

            label=f'总RMS={results["total_noise_rms"]*1e6:.2f}µV')

ax2.legend()


# 时域噪声

ax3 = axes[1, 0]

ax3.plot(results['time']*1e3, results['time_domain']*1e6)

ax3.set_xlabel('时间 (ms)')

ax3.set_ylabel('噪声电压 (µV)')

ax3.set_title('时域噪声波形')

ax3.grid(True, alpha=0.3)


# 噪声直方图

ax4 = axes[1, 1]

ax4.hist(results['time_domain']*1e6, bins=50, density=True, alpha=0.7)

ax4.set_xlabel('噪声电压 (µV)')

ax4.set_ylabel('概率密度')

ax4.set_title('噪声幅度分布')

ax4.grid(True, alpha=0.3)


# 添加高斯拟合

from scipy.stats import norm

mu, std = norm.fit(results['time_domain']*1e6)

x = np.linspace(ax4.get_xlim()[0], ax4.get_xlim()[1], 100)

ax4.plot(x, norm.pdf(x, mu, std), 'r-', linewidth=2, 

         label=f'高斯拟合\nμ={mu:.2f}µV, σ={std:.2f}µV')

ax4.legend()


plt.tight_layout()

plt.show()


print(f"理论热噪声电压: {noise_analyzer.thermal_noise_voltage()*1e6:.2f} µV")

print(f"仿真总噪声RMS: {results['total_noise_rms']*1e6:.2f} µV")

print(f"噪声峰峰值: {(results['time_domain'].max()-results['time_domain'].min())*1e6:.2f} µV")

```


## 结语


模拟电路中的"神奇魔力"与"诡异行为",实则是物理学原理在不同边界条件下的表现。负阻振荡、相位反转、噪声生成等现象,看似违反直觉,实则都有严格的数学描述和物理基础。


理解这些现象的关键在于掌握三个层面:1)器件物理特性,包括非线性、寄生参数等;2)电路拓扑结构,决定了信号的处理方式;3)系统级交互,特别是反馈与稳定性分析。通过计算机仿真与数学建模相结合,可以深入理解这些现象的本质,将看似"诡异"的行为转化为可预测、可设计的技术特性。模拟电路设计正是这种将物理原理转化为实用功能的工程艺术。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/19545/viewspace-3108521/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2015-11-01

  • 博文量
    895
  • 访问量
    933954