فهرست مقاله
در محاسبات مهندسی برق، اجرای بیخطای برنامه برای اعتماد به نتیجه کافی نیست. ممکن است کد ولتاژی صاف و معقول تولید کند، اما واحد ظرفیت، علامت معادله یا وضعیت آغازین اشتباه باشد. مدار RC یک تمرین کوچک و قابل بررسی برای ساختن مرجع مستقل است: ابتدا پاسخ تحلیلی را به دست میآوریم، سپس خروجی حلگر عددی را با آن میسنجیم. ارزش این تمرین برای پروژههای هوش مصنوعی نیز همین است؛ پیش از پیچیده کردن مدل، باید بدانیم چه نتیجهای را درست محسوب میکنیم.
۱. فرضیات مدار و واحدها را روشن کنید

مثال این مقاله شبیهسازی آموزشی است و دادهٔ اندازهگیریشده از برد یا محصول نیست. مقاومت ایدهآل ۲۲۰۰ اهم، ظرفیت ۴۷ میکروفاراد و منبع ثابت ۳٫۳ ولت انتخاب شدهاند. ولتاژ آغازین خازن صفر است. مقاومت و ظرفیت با دما تغییر نمیکنند و مقاومت داخلی منبع، نشتی خازن، بار خروجی و القای سیمها در مدل حضور ندارند. این مرزها را باید کنار نتیجه نگه داشت؛ کوچک بودن خطای محاسبه، درست بودن مدل برای هر مدار واقعی را اثبات نمیکند.
در برنامه، ظرفیت را به فاراد تبدیل میکنیم: ۴۷ میکروفاراد برابر ۰٫۰۰۰۰۴۷ فاراد است. حاصل ضرب مقاومت در ظرفیت، ثابت زمانی ۰٫۱۰۳۴ ثانیه را میدهد. برای جلوگیری از خطای پنهان، نام متغیر و کلید گزارش واحد را نشان میدهد. اگر کسی عدد ۴۷ را مستقیماً جای ظرفیت بگذارد، زمان شبیهسازی به اندازهٔ یک میلیون برابر تغییر میکند؛ نرمافزار لزوماً چنین اشتباهی را تشخیص نمیدهد، چون ورودی هنوز عددی معتبر است.
در مدل شارژ ایدهآل، قانون حلقه به رابطهٔ dV/dt = (Vs - V)/(R*C) میرسد. تعریف ثابت زمانی و پاسخ نمایی در بخش مدارهای RC کتاب OpenStax توضیح داده شده است. در اینجا پارامترهای مثبت و متناهی را میپذیریم؛ ورودی صفر، منفی، بینهایت یا نامعین رد میشود. این محدودیت قرارداد همین مثال است و برای مدلهایی با منبع منفی باید قرارداد تازهای تعریف کرد.
۲. جواب تحلیلی را مرجع مستقل قرار دهید

با منبع ثابت و ولتاژ آغازین صفر، مرجع ما V(t) = Vs*(1-exp(-t/tau)) است. در زمان صفر ولتاژ صفر میشود و با گذشت زمان به مقدار منبع نزدیک میشود. برای این پارامترها، ولتاژ در یک ثابت زمانی حدود ۲٫۰۸۶ ولت و در پنج ثابت زمانی حدود ۳٫۲۷۷۷۶۵ ولت است. مقدار نهایی این بازه هنوز دقیقاً برابر ۳٫۳ ولت نیست؛ رسیدن به حد مجانبی را نباید با پایان زمان شبیهسازی یکی گرفت.
سه بررسی مستقل میسازیم: شرط اولیه، نزدیک بودن حل عددی به فرمول و زمان عبور از نصف ولتاژ منبع. زمان عبور مرجع برابر tau*ln(2) است؛ در این مثال حدود ۰٫۰۷۱۶۷۱۴۱۸ ثانیه. بررسی دامنه و افزایش ولتاژ نیز مفید است، اما به تنهایی کافی نیست: یک پاسخ افزایشی اشتباه هم ممکن است بین صفر و ولتاژ منبع باقی بماند. مرجع تحلیلی چنین خطایی را آشکار میکند.
شبکهٔ گزارش شامل ۵۰۱ نقطه از صفر تا پنج ثابت زمانی است. این شبکه محل مشاهدهٔ نتیجه را مشخص میکند و فاصلهٔ گامهای داخلی حلگر نیست. بنابراین افزودن نقاط خروجی را نباید به معنی افزایش دقت محاسبه دانست. در هر آزمون، زمانهای مقایسه، واحد خطا و حد پذیرش باید مشخص باشند؛ تغییر این سه مورد ممکن است نتیجهٔ پذیرش را عوض کند، حتی اگر معادله یکسان بماند.
۳. کد کامل، دو تنظیم حلگر و آزمون خطا

کد زیر فقط به NumPy و SciPy نیاز دارد. دو اجرای RK45 با تنظیم معمولی و تنظیم سختگیرانه انجام میدهد. مطابق مستندات solve_ivp، تلورانس حلگر برای کنترل برآورد خطای محلی به کار میرود. حد خطای کل روی شبکهٔ گزارش را جدا تعریف میکنیم: برای اجرای دقیق این مثال، اختلاف مطلق با مرجع باید کمتر از ۲۰ نانوولت باشد. این حد انتخاب آموزشی ماست و تضمین عمومی حلگر محسوب نمیشود.
برای مقایسه از assert_allclose در NumPy با تلورانس نسبی صفر، تلورانس مطلق مشخص و مقایسهٔ سختگیرانهٔ شکل و نوع داده استفاده میکنیم. قبل از آن، متناهی بودن خروجی بررسی میشود. آزمون آخر عمداً رابطهٔ غلطی با تقسیم مقاومت بر ظرفیت میسازد و انتظار دارد مرجع آن را رد کند. این کنترل نشان میدهد آزمون فقط همیشه موفق نمیشود و توانایی تشخیص یک خطای واقعی در معادله را دارد.
"""Authored ideal RC experiment; simulation, not measured hardware data."""
import json
import math
import platform
import numpy as np
import scipy
from scipy.integrate import solve_ivp
def parameters(resistance_ohm, capacitance_f, supply_v):
values = (resistance_ohm, capacitance_f, supply_v)
if any(not math.isfinite(x) or x <= 0 for x in values):
raise ValueError("R, C and supply voltage must be finite and positive")
return resistance_ohm * capacitance_f
R = 2200.0
C = 47e-6
VS = 3.3
tau = parameters(R, C, VS)
times = np.linspace(0.0, 5.0 * tau, 501)
reference = VS * (-np.expm1(-times / tau))
def rhs(t, voltage):
return (VS - voltage) / tau
def half_voltage(t, voltage):
return voltage[0] - VS / 2.0
half_voltage.direction = 1
half_voltage.terminal = False
runs = {}
solutions = {}
for name, rtol, atol in (("standard", 1e-3, 1e-6),
("tight", 1e-9, 1e-11)):
solution = solve_ivp(rhs, (times[0], times[-1]), [0.0],
method="RK45", t_eval=times,
events=half_voltage, rtol=rtol, atol=atol)
if not solution.success:
raise RuntimeError(solution.message)
voltage = solution.y[0]
solutions[name] = voltage
runs[name] = {
"rtol": rtol, "atol_v": atol,
"max_error_v_on_501_points": float(np.max(np.abs(voltage-reference))),
"rhs_evaluations": solution.nfev,
"half_voltage_time_s": float(solution.t_events[0][0]),
}
checks = {}
assert math.isclose(tau, 0.1034, abs_tol=1e-15)
checks["si_units_and_time_constant"] = True
assert solutions["tight"][0] == 0.0
checks["initial_condition"] = True
assert all(np.all(np.isfinite(v)) for v in solutions.values())
checks["finite_outputs"] = True
np.testing.assert_allclose(solutions["tight"], reference,
rtol=0.0, atol=2e-8, equal_nan=False, strict=True)
checks["analytic_reference_on_sample_grid"] = True
assert np.all(np.diff(solutions["tight"]) >= -1e-10)
assert np.all((solutions["tight"] >= -1e-10) &
(solutions["tight"] <= VS + 1e-10))
checks["monotonic_and_voltage_range"] = True
expected_half = tau * math.log(2.0)
assert abs(runs["tight"]["half_voltage_time_s"]-expected_half) < 1e-9
checks["half_voltage_event"] = True
for bad in ((0.0, C, VS), (R, -C, VS), (R, math.nan, VS),
(math.inf, C, VS), (R, C, 0.0)):
try:
parameters(*bad)
except ValueError:
pass
else:
raise AssertionError("Invalid parameter was accepted")
checks["invalid_parameters_rejected"] = True
# Deliberately wrong dimensional model: R/C is not a time constant.
wrong = solve_ivp(lambda t, v: (VS-v)/(R/C),
(times[0], times[-1]), [0.0], t_eval=times).y[0]
try:
np.testing.assert_allclose(wrong, reference, rtol=0.0,
atol=2e-8, equal_nan=False, strict=True)
except AssertionError:
checks["wrong_model_detected"] = True
else:
raise AssertionError("Reference test failed to detect wrong equation")
report = {
"versions": {"python": platform.python_version(), "numpy": np.__version__,
"scipy": scipy.__version__},
"data_type": "authored ideal-circuit simulation; no measured device data",
"parameters": {"resistance_ohm": R, "capacitance_f": C,
"supply_v": VS, "initial_v": 0.0, "tau_s": tau},
"sampling": {"points": len(times), "end_time_s": float(times[-1]),
"output_spacing_s": float(times[1]-times[0]),
"output_grid_is_not_solver_step_size": True},
"reference": {"half_voltage_time_s": expected_half,
"voltage_at_tau_v": VS*(-math.expm1(-1.0)),
"voltage_at_5tau_v": float(reference[-1])},
"runs": runs,
"test_contract": "2e-8 V global sampled-grid bound chosen for this fixture; not implied by solver rtol",
"checks": checks,
}
print(json.dumps(report, ensure_ascii=False, indent=2))
۴. خروجی اجرا و پاسخ تمرینها

کد با Python 3.14.5، NumPy 2.5.4 و SciPy 1.18.1 اجرا شد و هر هشت بررسی گذشت. بیشترین اختلاف اجرای معمولی روی شبکه حدود ۰٫۰۰۰۴۳۸۲۸۱ ولت بود؛ برای اجرای دقیق حدود ۰٫۰۰۰۰۰۰۰۰۰۶۲۱۳۶۳ ولت ثبت شد. تعداد ارزیابی تابع مشتق به ترتیب ۵۰ و ۳۸۰ بود. این تعداد شاخصی از کار حلگر در همین اجراست؛ زمان پردازنده اندازهگیری نشده و از آن ادعای سرعت برای دستگاه دیگر نمیسازیم. گزارش کامل برای بازبینی در ادامه آمده است.
{
"versions": {
"python": "3.14.5",
"numpy": "2.5.4",
"scipy": "1.18.1"
},
"data_type": "authored ideal-circuit simulation; no measured device data",
"parameters": {
"resistance_ohm": 2200.0,
"capacitance_f": 4.7e-05,
"supply_v": 3.3,
"initial_v": 0.0,
"tau_s": 0.10339999999999999
},
"sampling": {
"points": 501,
"end_time_s": 0.5169999999999999,
"output_spacing_s": 0.0010339999999999998,
"output_grid_is_not_solver_step_size": true
},
"reference": {
"half_voltage_time_s": 0.07167141846989834,
"voltage_at_tau_v": 2.08599784413424,
"voltage_at_5tau_v": 3.2777647749030177
},
"runs": {
"standard": {
"rtol": 0.001,
"atol_v": 1e-06,
"max_error_v_on_501_points": 0.0004382811238268225,
"rhs_evaluations": 50,
"half_voltage_time_s": 0.07167451026136827
},
"tight": {
"rtol": 1e-09,
"atol_v": 1e-11,
"max_error_v_on_501_points": 6.213634051732697e-10,
"rhs_evaluations": 380,
"half_voltage_time_s": 0.0716714184601603
}
},
"test_contract": "2e-8 V global sampled-grid bound chosen for this fixture; not implied by solver rtol",
"checks": {
"si_units_and_time_constant": true,
"initial_condition": true,
"finite_outputs": true,
"analytic_reference_on_sample_grid": true,
"monotonic_and_voltage_range": true,
"half_voltage_event": true,
"invalid_parameters_rejected": true,
"wrong_model_detected": true
}
}
تمرین اول: اگر مقاومت دو برابر شود و ظرفیت ثابت بماند، ثابت زمانی و زمان نصف شدن فاصله تا ولتاژ نهایی چه تغییری میکنند؟ پاسخ: ثابت زمانی به ۰٫۲۰۶۸ ثانیه میرسد و زمان عبور از نصف ولتاژ، حدود ۰٫۱۴۳۳۴۲۸۳۷ ثانیه میشود. شکل پاسخ برحسب زمان نرمالشدهٔ t/tau همان است؛ پاسخ برحسب ثانیه کندتر میشود.
تمرین دوم: آیا تلورانس نسبی کوچک به تنهایی در نزدیکی ولتاژ صفر کافی است؟ پاسخ: خیر؛ سهم نسبی در نزدیکی صفر کوچک میشود و تلورانس مطلق اهمیت پیدا میکند. به همین دلیل هر دو مقدار را ثبت کردهایم. حد پذیرش آزمون نیز باید با واحد ولتاژ و نیاز مسئله تعریف شود، نه صرفاً با تعداد رقمهایی که در خروجی چاپ میشوند.
تمرین سوم: چرا جایگزینی R*C با R/C باید رد شود؟ پاسخ: حاصل تقسیم، واحد زمانی مورد نیاز معادله را ندارد و پاسخ آن در بازهٔ انتخابشده به مرجع شارژ نزدیک نیست. در آزمون عمدی، رد شدن پاسخ غلط یک موفقیت آزمون است. گزارش فقط نشان میدهد این خطای خاص تشخیص داده شده؛ همهٔ خطاهای ممکن پوشش داده نشدهاند.
تمرین چهارم: اگر ولتاژ آغازین خازن ۱ ولت باشد، مرجع چه میشود؟ پاسخ: رابطه به V(t)=Vs+(1-Vs)*exp(-t/tau) تغییر میکند. باید مقدار اولیهٔ حلگر، آزمون شرط اولیه و زمان رویداد نیز دوباره تنظیم شوند. اجرای فعلی برای این وضعیت انجام نشده است؛ تغییر فقط یک خط و باقی گذاشتن مرجع قبلی، آزمون ناسازگار میسازد.
۵. مرز اعتبار مدل و ارتباط با هوش مصنوعی

در یک پروژهٔ واقعی، ممکن است معادلهٔ درست را با دادهٔ نامناسب مقایسه کنید. مقاومت خروجی منبع، تلورانس قطعات، بار ابزار اندازهگیری و وضعیت کلید میتوانند پاسخ را تغییر دهند. راه بررسی، ثبت شرایط و مقایسهٔ مدل با اندازهگیری مستقل است. برای تبدیل این تمرین به کار قابل اتکا، مسیر از نمونهٔ اولیه تا محصول قابل اتکا را نیز ببینید؛ آزمون عددی فقط یکی از شواهد لازم است.
گزارش ۲۴ شهریور ۱۴۰۵ MathWorks دربارهٔ PINNLab و مسئلهٔ معکوس نمونهای از ترکیب داده و معادله برای تخمین پارامتر معرفی میکند. این مقاله آموزش پایه است، نه خبر تازه یا بازتولید آن پژوهش. اینجا شبکهٔ عصبی آموزش ندادهایم؛ مرجع تحلیلی ساختهایم که بعداً میتوان خروجی یک مدل آموختهشده را نیز با آن سنجید.
انتخاب AI باید از مسئله شروع شود. وقتی پارامترها معلوماند و جواب ساده وجود دارد، افزودن شبکهٔ عصبی لزوماً فایدهای ندارد. اگر هدف تخمین پارامتر ناشناخته باشد، باید دادهٔ آموزش و آزمون، پوشش زمان، نویز و امکان تشخیص مستقل پارامترها مشخص شوند. راهنمای انتخاب بخش مناسب اپلیکیشن برای هوش مصنوعی این تصمیم را تکمیل میکند. معیار پیشرفت در این تمرین، گزارش قابل بازبینی و تشخیص خطای تعریفشده است؛ یک نمودار زیبا یا کم بودن خطای آموزش جای آن را نمیگیرد.
منابع و مطالعهٔ بیشتر
- مدارهای RC در کتاب فیزیک دانشگاهی OpenStax
- حل مسئلهٔ مقدار اولیه با solve_ivp در SciPy
- قرارداد مقایسهٔ عددی assert_allclose در NumPy
- تخمین پارامتر معادلات دیفرانسیل با PINNLab؛ گزارش MathWorks
بررسی منابع: ۲۳ مهر ۱۴۰۵. تحلیلها و پیشنهادهای اجرایی، جمعبندی نویسندهاند.

