Редактирование: Сравнение методов Рунге-Кутта и Липфрога
Внимание! Вы не авторизовались на сайте. Ваш IP-адрес будет публично видимым, если вы будете вносить любые правки. Если вы войдёте или создадите учётную запись, правки вместо этого будут связаны с вашим именем пользователя, а также у вас появятся другие преимущества.
Правка может быть отменена. Пожалуйста, просмотрите сравнение версий, чтобы убедиться, что это именно те изменения, которые вас интересуют, и нажмите «Записать страницу», чтобы изменения вступили в силу.
Текущая версия | Ваш текст | ||
Строка 1: | Строка 1: | ||
[[Виртуальная лаборатория]]>[[Сравнение методов Рунге-Кутта и Липфрога]] <HR> | [[Виртуальная лаборатория]]>[[Сравнение методов Рунге-Кутта и Липфрога]] <HR> | ||
− | + | ||
− | |||
− | |||
== '''Постановка задачи ''' == | == '''Постановка задачи ''' == | ||
− | Дано простейшее уравнение движения грузика на пружине: <math>\ddot{x} = -cx</math>. Необходимо интегрировать его с помощью двух методов: | + | Дано простейшее уравнение движения грузика на пружине: <math>\ddot{x} = -cx</math>. Необходимо интегрировать его с помощью двух методов: Липфрога и Рунге-Кутта. Сравнить результаты. |
<br/> | <br/> | ||
== '''Метод численного интегрирования Рунге-Кутты''' == | == '''Метод численного интегрирования Рунге-Кутты''' == | ||
− | + | Рассмотри задачу Коши для системы обыкновенных дифференциальных уравнений первого порядка. | |
− | + | <math>\dot{y} = f(x, y), y(x_0)=y_0</math> | |
− | + | Тогда приближенное значение в последующих точках вычисляется по итерационной формуле: | |
− | <math> | + | <math>y_n+1 = y_n + \frac{h}{6}(k_1 + 2k_2 + 2k_3 + k_4)</math> |
− | + | =='''Основные уравнения'''== | |
− | + | <div style="font-size: 16px;">1)''Расчет статических координат:''</div> | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | Тогда приближенное значение в последующих точках вычисляется по итерационной формуле: | ||
− | <math> | ||
− | < | ||
− | == | + | <math>{y}_{i}=i{l}_{0}+\sum^{i}_{k=1} {\Delta l_k}</math> |
− | + | <br/> | |
− | <math>\ | + | <br/> |
− | <math> | + | <math>\Delta{l_k} = \frac{(n-k+1)mg}{C} </math> |
+ | <br/> | ||
+ | <br/> | ||
+ | где <math>{n}</math> — количество грузиков ; <math>{i}</math> — текущий грузик ; <math>{l_0}</math> — длина не растянутой пружины; <math>\Delta{l_k}</math> — статическое удлинение пружины. | ||
− | + | <div style="font-size: 16px;">2) ''Уравнение движения грузиков при отсутствии верхней пружины:''</div> | |
− | {{#widget:Iframe |url=http://tm.spbstu.ru/htmlets/ | + | <math>\ddot y_i = - \frac{C}{m} ({y}_{i+1} - 2{y}_{i}+{y}_{i-1}) + g </math> |
+ | <br/> | ||
+ | <br/> | ||
+ | при <math>i = n </math> : | ||
+ | <br/> | ||
+ | <math> \ddot y_n = - \frac{C}{m}(y_n - {y}_{n-1} - l_0) + g</math> | ||
+ | <br/> | ||
+ | <br/>'''Программа (лучше всего смотреть ее в Mozilla Firefox)''' | ||
+ | <br/> | ||
+ | {{#widget:Iframe |url=http://tm.spbstu.ru/htmlets/Alexandrov/Spring_illusion.html |width=800|height=800 |border=0 }} | ||
<br /> | <br /> | ||
<br /> | <br /> | ||
− | Скачать программу [[Медиа: | + | Скачать программу [[Медиа:Spring_illusion.rar|Spring_illusion.rar]]. |
<br /> | <br /> | ||
<br /> | <br /> | ||
<div class="mw-collapsible mw-collapsed" style="width:100%" > | <div class="mw-collapsible mw-collapsed" style="width:100%" > | ||
− | '''Текст программы на языке JavaScript (разработчик [[ | + | '''Текст программы на языке JavaScript (разработчик [[Александров Сергей]]):''' <div class="mw-collapsible-content"> |
− | Файл '''" | + | Файл '''"Spring_illusin.js"''' |
<syntaxhighlight lang="javascript" line start="1" enclose="div"> | <syntaxhighlight lang="javascript" line start="1" enclose="div"> | ||
− | + | window.addEventListener("load", Main_Spring, true); | |
− | + | function Main_Spring() { | |
− | + | ||
− | + | // *** Некие исходные данные *** | |
− | + | ||
− | + | var canvas = spring_canvas; | |
− | + | canvas.onselectstart = function () {return false;}; // запрет выделения canvas | |
− | + | var ctx = canvas.getContext("2d"); // на ctx происходит рисование | |
− | + | var w = canvas.width; // ширина окна в расчетных координатах | |
− | + | var h = canvas.height; // высота окна в расчетных координатах | |
− | + | ||
− | + | var Pi = 3.1415926; // число "пи" | |
− | + | var g = 9.8; // гравитационная постоянная | |
− | + | ||
− | + | var T0 = 0.01; // масштаб времени (период колебаний исходной системы) | |
− | + | ||
− | + | var m0 = 0.15; // масштаб массы маятника | |
− | + | var C0 = 1; // масштаб жесткости пружины | |
− | + | ||
− | + | var count = true; // проводить ли расчет системы | |
− | + | var v = 0; // скорость тела | |
− | + | var t = 0; | |
− | + | ||
− | + | // параметры полученные из размеров холста | |
− | + | var rw = canvas.width / 4; | |
− | + | var rh = canvas.height / 100; | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | // параметры грузиков | |
− | + | var x0 = 0; | |
− | + | var y0 = 0; | |
− | + | var vy0 = 0; | |
− | + | var rad0 = 7; | |
− | var | + | var sTime = 0; |
− | var | + | // параметры пружины |
− | + | var Lp = 30; //длина пружины | |
− | |||
− | |||
− | |||
− | |||
− | + | // *** Задание вычислительных параметров *** | |
− | + | var fps = 60; // frames per second - число кадров в секунду (качечтво отображения) | |
+ | var spf = 5; // steps per frame - число шагов интегрирования между кадрами | ||
+ | var dt = 50 * T0 / fps; // шаг интегрирования (качество расчета) | ||
+ | var steps = 0; // количество шагов интегрирования | ||
− | |||
− | + | // *** Задание физических параметров *** | |
− | + | var m = 1 * m0; // масса грузика | |
− | + | var C = 1 * C0; // жесткость пружины | |
− | + | var L0 = 0; // изначальное удлинение пружины | |
− | + | var n = 5; // количество тел | |
+ | var circle; // круглые тела | ||
− | + | // *** Установка слайдеров для переменных величин *** | |
− | + | slider_m.value = (m / m0).toFixed(1); | |
− | + | number_m.value = (m / m0).toFixed(1); | |
− | + | slider_spf.value = (spf).toFixed(1); | |
+ | number_spf.value = (spf).toFixed(1); | ||
slider_n.value = parseInt(n); | slider_n.value = parseInt(n); | ||
number_n.value = parseInt(n); | number_n.value = parseInt(n); | ||
− | + | ||
− | + | function setM(new_m) { | |
− | + | m = new_m * m0; | |
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
− | + | ||
− | + | function setSpf(new_spf) { | |
− | + | spf = new_spf; | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
− | + | function setN(new_n) { | |
− | + | n = new_n; | |
− | + | StaticStart(); | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
− | + | ||
− | + | slider_m.oninput = function () { | |
− | + | number_m.value = slider_m.value; | |
− | + | setM(slider_m.value); | |
+ | }; | ||
+ | number_m.oninput = function () { | ||
+ | slider_m.value = number_m.value; | ||
+ | setM(number_m.value); | ||
+ | }; | ||
+ | |||
+ | |||
+ | slider_spf.oninput = function () { | ||
+ | number_spf.value = slider_spf.value; | ||
+ | setSpf(slider_spf.value); | ||
+ | }; | ||
+ | number_spf.oninput = function () { | ||
+ | slider_spf.value = number_spf.value; | ||
+ | setSpf(number_spf.value); | ||
+ | }; | ||
+ | |||
slider_n.oninput = function () { | slider_n.oninput = function () { | ||
number_n.value = slider_n.value; | number_n.value = slider_n.value; | ||
setN(slider_n.value); | setN(slider_n.value); | ||
− | + | }; | |
− | |||
number_n.oninput = function () { | number_n.oninput = function () { | ||
slider_n.value = number_n.value; | slider_n.value = number_n.value; | ||
setN(number_n.value); | setN(number_n.value); | ||
− | + | }; | |
+ | |||
+ | // *** Условие падения тела *** | ||
+ | var spring_dropped = false; | ||
+ | drop_spring.onclick = function () { | ||
+ | spring_dropped = true; | ||
+ | }; | ||
+ | |||
+ | // *** Пересчет статических координат | ||
+ | new_static.onclick = function () { | ||
+ | StaticStart() | ||
+ | }; | ||
+ | |||
+ | // *** Создание массива элементов *** | ||
+ | function StaticStart() { | ||
+ | spring_dropped = false; | ||
+ | circle = []; | ||
+ | for (var i = 0; i < n; i++) { | ||
+ | var circ = {}; | ||
+ | circ.x = x0; | ||
+ | circ.y = y0; | ||
+ | circ.vy = vy0; | ||
+ | circ.rad = rad0; | ||
+ | circ.L = L0; | ||
− | + | var rgb = HSVtoRGB(i / n * 2, 1, 1); | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | circ.fill = "rgba(" + rgb.r + ", " + rgb.g + ", " + rgb.b + ", 1)"; | |
− | + | circle[i] = circ; | |
− | + | } | |
− | + | for (var i = 0; i < n; i++) { | |
− | + | if (i == 0) { | |
− | + | circle[i].x = w / 2; | |
− | + | circle[i].y0 = Lp + (n - i) * m * g / C; | |
− | + | circle[i].y = circle[i].y0; | |
+ | |||
+ | } else { | ||
+ | circle[i].x = w / 2; | ||
+ | circle[i].y0 = circle[i - 1].y0 + Lp + (n - i) * m * g / C; | ||
+ | circle[i].y = circle[i].y0; | ||
+ | console.log(circle[i].y0); | ||
− | + | } | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
} | } | ||
− | + | // *** функция разукрашивания объектов | |
− | + | function HSVtoRGB(h, s, v) { | |
− | + | var r, g, b, i, f, p, q, t; | |
− | + | i = Math.floor(h * 6); | |
− | + | f = h * 6 - i; | |
− | + | p = v * (1 - s); | |
− | + | q = v * (1 - f * s); | |
− | + | t = v * (1 - (1 - f) * s); | |
− | + | switch (i % 6) { | |
− | + | case 0: r = v, g = t, b = p; break; | |
− | + | case 1: r = q, g = v, b = p; break; | |
− | + | case 2: r = p, g = v, b = t; break; | |
+ | case 3: r = p, g = q, b = v; break; | ||
+ | case 4: r = t, g = p, b = v; break; | ||
+ | case 5: r = v, g = p, b = q; break; | ||
} | } | ||
+ | return { | ||
+ | r: Math.floor(r * 255), | ||
+ | g: Math.floor(g * 255), | ||
+ | b: Math.floor(b * 255) | ||
+ | }; | ||
+ | } | ||
− | + | // график | |
− | // | + | var vGraph1 = new TM_graph( // определить график |
− | + | "#vGraph1", // на html-элементе #vGraph | |
− | + | 250, // сколько шагов по оси "x" отображается | |
− | + | 0, 100, 5); // мин. значение оси Y, макс. значение оси Y, шаг по оси Y | |
− | for (var | + | |
+ | // *** Функция обеспечивающая "жизнь" пружин *** | ||
+ | function control() { | ||
+ | calculate(); | ||
+ | draw(); | ||
+ | requestAnimationFrame(control); | ||
+ | } | ||
+ | StaticStart(); | ||
+ | control(); | ||
+ | |||
+ | |||
+ | |||
+ | // *** Функция расчетов координат *** | ||
+ | function calculate() { | ||
+ | |||
+ | for (var s = 1; s <= spf; s++) { | ||
+ | var k = n; | ||
+ | for (var i = 1; i < n; i++) { | ||
+ | if (spring_dropped) | ||
+ | circle[0].L = 0; | ||
+ | else | ||
+ | circle[0].L = circle[0].y - Lp; | ||
+ | circle[i].L = circle[i].y - circle[i - 1].y - Lp; | ||
+ | } | ||
+ | |||
+ | for (var i = 0; i < n; i++) { | ||
+ | if (i == n - 1) | ||
+ | circle[i].vy += ((-1) * C * (circle[i].L) / m + g) * dt; | ||
+ | else | ||
+ | circle[i].vy += ((-1) * C * (circle[i].L - circle[i + 1].L) / m + g) * dt; | ||
+ | |||
− | + | circle[i].y += circle[i].vy * dt; | |
− | + | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
− | + | steps++; | |
+ | if (spring_dropped) | ||
+ | { sTime ++; | ||
+ | if (sTime % 50 == 0) vGraph1.graphIter(sTime/50, (circle[n-1].y-circle[n-1].y0)) ; | ||
+ | }; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | // *** Функция рисования объектов *** | ||
+ | function draw() { | ||
+ | ctx.clearRect(0, 0, w, h); | ||
+ | |||
+ | // Рисование закрепления | ||
+ | ctx.lineWidth = 6; | ||
+ | ctx.strokeStyle = "#7394cb"; | ||
+ | ctx.beginPath(); | ||
+ | ctx.moveTo(200, 0); | ||
+ | ctx.lineTo(300, 0); | ||
+ | ctx.stroke(); | ||
+ | |||
+ | for (var i = 0; i < n; i++) { | ||
+ | |||
+ | // Рисование пружинок | ||
+ | if (i == 0) { | ||
+ | if (!spring_dropped) | ||
+ | draw_spring(0, circle[i].y, w / 2, 10, 40); | ||
+ | } else | ||
+ | draw_spring(circle[i - 1].y, circle[i].y, w / 2, 10, 40); | ||
+ | |||
+ | |||
+ | } | ||
+ | for (var i = 0; i < n; i++) { | ||
− | + | // Рисование грузиков | |
+ | ctx.lineWidth = 6; | ||
+ | ctx.strokeStyle = "#8B008B"; | ||
+ | ctx.beginPath(); | ||
+ | ctx.moveTo(circle[i].x-20, circle[i].y); | ||
+ | ctx.lineTo(circle[i].x+20, circle[i].y); | ||
+ | ctx.stroke(); | ||
+ | } | ||
+ | |||
+ | } | ||
− | + | // *** Функция рисования пружины *** | |
− | + | function draw_spring(x_start, x_end, y, n, h) { | |
− | + | ctx.lineWidth = 1; | |
− | + | ||
− | + | var L = x_end - x_start; | |
− | + | for (var i = 0; i < n; i++) { | |
− | + | var rgb = HSVtoRGB(i / n * 0.5, 1, 1); | |
− | + | ctx.strokeStyle = "rgba(" + rgb.r + ", " + rgb.g + ", " + rgb.b + ", 1)"; | |
− | + | ||
− | + | var x_st = x_start + L / n * i; | |
− | + | var x_end = x_start + L / n * (i + 1); | |
− | + | var l = x_end - x_st; | |
− | + | ctx.beginPath(); | |
− | + | ctx.bezierCurveTo(y, x_st, y + h, x_st + l / 4, y, x_st + l / 2); | |
− | + | ctx.bezierCurveTo(y, x_st + l / 2, y - h, x_st + 3 * l / 4, y, x_st + l); | |
− | + | ctx.stroke(); | |
− | + | } | |
− | + | } | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Строка 305: | Строка 317: | ||
</div> | </div> | ||
− | ==''' | + | =='''Физическое объяснение'''== |
− | Мы наблюдаем | + | Мы наблюдаем иллюзию "зависания" нижнего грузика потому, что до него должна дойти волна возмущений верхней пружины, прежде чем он придет в движение. |
=='''Ссылки'''== | =='''Ссылки'''== | ||
− | |||
− | |||
− | |||
− | |||
* [[Виртуальная лаборатория]] | * [[Виртуальная лаборатория]] | ||
<br/> | <br/> |