带振幅与偏移约束的正弦曲线拟合优化及异常排查
Let's break down what's going wrong with your fitting and fix it step by step:
Core Problems in Your Current Setup
Misunderstanding of Sine Parameter Relationships
You want the fitted curve's minimum to match your raw data's minimum, and offsetc=0—but your constraint logic for amplitudeAis inverted. For the functionA*sin(wt+phi)+c:- When
c=0, the curve's minimum is-|A|(ifAis positive) orA(ifAis negative). Your raw data's minimum is-12.08, so settingA = min(s)(a negative value) forces the curve's minimum to beA(matching your raw data) but makes the curve's maximum-A(~12.08)—way higher than your raw data's max of ~0.9. This is why your fit only matches at the minimum.
- When
Incorrect Constraint Bounds
Yourparam_boundslocksAin a narrow negative range aroundmin(s). Since this range is incompatible with your raw data's upper values, the optimizer can't adjust other parameters (like frequency) to fit the rest of the curve.Missing Variable Assignment
Your code comments outA, w, phi, c = popt, which would cause errors when calculatingf,T, ands_fit.
Corrected Solution
Here's the revised code with fixes for constraints, initial guesses, and optimizer settings:
import matplotlib.pyplot as plt from matplotlib.ticker import FormatStrFormatter import numpy as np from scipy.optimize import curve_fit # Raw data t = np.array([313.544, 313.545, 313.546, 313.547, 313.548, 313.549, 313.55, 313.551, 313.552, 313.553, 313.554, 313.555, 313.556, 313.557, 313.558, 313.559, 313.56, 313.561, 313.562, 313.563, 313.564, 313.565, 313.566, 313.567]) s = np.array([0.911188, -0.43135, -1.80997, -3.27816, -4.85784, -6.59428, -8.2214, -9.53617, -10.6892, -11.6003, -12.0844, -12.0524, -11.9749, -11.4891, -10.6131, -9.49873, -8.1154, -6.41442, -5.09357, -3.99165, -2.72991, -1.71446, -0.56306, 0.440741]) # FFT-based frequency guess (this part was correct) ff = np.fft.fftfreq(len(t), (t[1]-t[0])) fa = abs(np.fft.fft(s, len(t))) pos_amax = np.argmax(fa[1:])+1 ff_max = abs(ff[pos_amax]) # Corrected initial guesses min_s = np.min(s) A_guess = -min_s # Amplitude equals absolute value of raw data's minimum c_guess = 0.0 f_guess = np.array([A_guess, 2*np.pi*ff_max, 0., c_guess]) # Sinusoid function (unchanged) def sin_func(t, A, w, phi, c): return A * np.sin(w*t + phi) + c # Corrected bounds: lock A to |min_s|, lock c to near 0 tolerance = 1e-6 param_bounds = ( [A_guess - tolerance, -np.inf, -np.inf, -tolerance], [A_guess + tolerance, np.inf, np.inf, tolerance] ) # Use TRF method for better constraint handling popt, pcov = curve_fit( sin_func, t, s, p0=f_guess, bounds=param_bounds, maxfev=10000000, method='trf' ) # Assign fitted parameters A, w, phi, c = popt f = w/(2.*np.pi) T = 1000/f # Generate fitted curve s_fit = A * np.sin(w*t + phi) + c # Plotting plt.rcParams['figure.figsize'] =10, 5 fig, ax = plt.subplots() plt.plot(t, s, "b", label="Original Data") plt.plot(t, s_fit, 'k--', label="Constrained Fit") ax.set(xlabel='Time', ylabel='Amplitude') ax.xaxis.set_major_formatter(FormatStrFormatter('%.2f')) ax.yaxis.set_major_formatter(FormatStrFormatter('%.2f')) ax.grid() # Add fit info ausgabe = ("Sinus-Fit Results \nAmplitude = {:.2f} \nPeriod = {:.2f} ms \nOffset = {:.4f}".format(A, abs(T), c)) plt.text(0.795, 0.7, ausgabe, family="sans-serif", fontsize=10, ha='left', va='top', transform=fig.transFigure) # Adjust plot position to fit text box = ax.get_position() ax.set_position([box.x0, box.y0, box.width * 0.85, box.height]) plt.legend() plt.show()
Key Improvements
Fixed Constraint Logic
We now lockAto the absolute value of your raw data's minimum, so the fitted curve's minimum (-A + c) matchesmin(s)whenc≈0. This aligns the curve's amplitude with your data's range.Better Optimizer Choice
Usingmethod='trf'(trust-region-reflective algorithm) instead of the defaultlm(Levenberg-Marquardt) gives more stable results when handling strict boundary constraints.Corrected Initial Guess
SettingA_guess = -min(s)gives the optimizer a starting point that aligns with your constraints, preventing it from getting stuck in invalid parameter ranges.
Additional Notes
If you truly meant "amplitude equals raw data's minimum" (a negative value, which is non-standard), you'll need to re-evaluate your constraint's validity—it will force the curve's maximum to be much higher than your raw data, making a good fit impossible. The solution above assumes you wanted the fitted curve's minimum to match your raw data's minimum (a much more common use case).
内容的提问来源于stack exchange,提问作者smi242

