КП: Динамика бильярда

Материал из Department of Theoretical and Applied Mechanics
Перейти к: навигация, поиск
А.М. Кривцов > Теоретическая механика > Курсовые проекты ТМ 2015 > Динамика бильярда


Курсовой проект по Теоретической механике
Центральное разбиение

Исполнитель: Булдаков Павел

Группа: 09 (23604)

Семестр: весна 2015

Аннотация проекта[править]

Данный проект посвящен изучению динамики бильярда .В ходе работы над проектом было рассмотрено разбиение пирамиды из шаров, данный процесс смоделирован на языке JavaScript.

Формулировка задачи[править]

- Написать программу, моделирующую динамику взаимодействия шаров при игре в Бильярд. Взаимодействие между шарами описывается с помощью потенциала Леннарда-Джонса.
-Рассмотреть классическое разбиение в русском бильярде, проследить траекторию разлета.

Общие сведения по теме[править]

Впервые о математическом базисе бильярдной игры заговорил Гаспар Гюстав Кориолис в своей книге «Théorie mathématique du jeu de billard» (Русск. перевод: «Математическая теория явлений бильярдной игры») в 1835 году. Он использовал в своей работе элементы теории вероятностей, теории пределов и общего анализа. Однако особого интереса у современников книга не вызвала: ни у математиков, ни у бильярдистов.

Прошло более полутораста лет, и математический бильярд развился в свою теорию, породив несколько побочных. «Теория бильярдов» сегодня неотъемлемая часть эргодической теории и теории динамических систем, имеет важнейшее применение в физике. Математиком Гальпериным создан способ определения числа pi с помощью бильярда. Намного ближе общеобразованному читателю результаты исследований математиков Штайнхауса, Альхазена и Гарднера.

При реализации данной задачи используется стол с размерами игрового поля 2240 х 1120 мм, диаметром шара 68 мм и размерами луз 72 и 82 мм соответственно.

Решение[править]

Приняты некоторые допущения:

  • все шары считаются идеально упругими и почти идеально жёсткими;
  • каждый шар имеет массу в 1 единицу и радиус в 1 единицу;
  • взаимодействие между двумя шарами описывается формулой

[math] \left\{ \begin{array}{ll} F_{LJ} = \frac{12D}{a}\left[\left(\frac a r\right)^{13}-\left(\frac a r\right)^{7}\right], \qquad & r\lt d; \\ F_{LJ} = 0, \qquad & r \gt d; \\ \end{array} \right. [/math]

где d — расстояние между центрами шаров,[math]F_{LJ}(r)[/math] — сила Леннард-Джонса


Профессор Джим Белк (Jim Belk) рассчитал направление и скорость движения каждого из 15 шаров пирамиды, а также битка , после соударения.Для сравнения, помните, что начальная скорость битка была 10 ед/сек.
Траектория разлета шаров при центральном разбиении пирамиды. [[1]]

017cae37a4318f94cff572023a52bddb.png




Ниже приведена программа( созданная совместно со Степановым Матвеем на основании программы Динамика взаимодействующих частиц) , в которой видно, что траектория разлета шаров схожи с расчетными траекториями профессора.


Текст программы на языке JavaScript:

Файл "Billyard1version.js"

  1 window.addEventListener("load", MainBalls, true);
  2 function MainBalls(slider_01, text_01, slider_02, text_02) {
  3 
  4     // Предварительные установки
  5 
  6     var canvas = canvasBalls;
  7     var context = canvas.getContext("2d");                  // на context происходит рисование
  8     canvas.oncontextmenu = function (e) {return false;};    // блокировка контекстного меню
  9 
 10     var Pi = 3.1415926;                   // число "пи"
 11 
 12     var m0 = 1;                           // масштаб массы
 13     var T0 = 1;                           // масштаб времени (период колебаний исходной системы)
 14     var a0 = 1;        // масштаб расстояния (диаметр шара)
 15     var q = 90;                     // угол 
 16   
 17 
 18     var g0 = a0 / T0 / T0;                // масштаб ускорения (ускорение, при котором за T0 будет пройдено расстояние a0)
 19     var k0 = 2 * Pi / T0;                 // масштаб частоты
 20     var C0 = m0 * k0 * k0;                // масштаб жесткости
 21     var B0 = 2 * m0 * k0;                 // масштаб вязкости
 22   var v0 = 1;      //начальная скорость
 23 
 24 
 25     // *** Задание физических параметров ***
 26 
 27     var Ny = 32;                          // число шаров, помещающихся по вертикали в окно (задает размер шара относительно размера окна)
 28     var m = 1 * m0;                       // масса
 29     var Cwall = 10 * C0;                  // жесткость стен
 30     var Cball = 0.1 * Cwall;              // жесткость между частицами
 31     var B = 0.008 * B0;                   // вязкость среды
 32     var Bwall = 0.03 * B0;                // вязкость на стенках
 33     var mg = 0.25 * m * g0;               // сила тяжести
 34     var r = 0.5 * a0;                     // радиус частицы в расчетных координатах
 35     var K = 0.85;                         // сила взаимодействия ограничивается значением, реализующимся при r/a = K
 36     var a = 2 * r;                        // равновесное расстояние между частицами
 37     var aCut = 2.00001 * r;               // радиус обрезания
 38 
 39     // *** Задание вычислительных параметров ***
 40 
 41     var fps = 50;                         // frames per second - число кадров в секунду (качечтво отображения)
 42     var spf = 100;                        // steps per frame   - число шагов интегрирования между кадрами (скорость расчета)
 43     var dt  = 0.045 * T0 / fps;           // шаг интегрирования (качество расчета)
 44 
 45     // Выполнение программы
 46 
 47     var scale = canvas.height / Ny / a0;  // масштабный коэффициент для перехода от расчетных к экранным координатам
 48     var r2 = r * r;                       // ___в целях оптимизации___
 49     var aCut2 = aCut * aCut;              // ___в целях оптимизации___
 50     var a2 = a * a;                       // ___в целях оптимизации___
 51     var D = a2 * Cball / 72;              // энергия связи между частицами
 52     var LJCoeff = 12 * D / a2;            // коэффициент для расчета потенциала Л-Дж
 53   
 54     var Ka = K * a;                       // ___в целях оптимизации___
 55     var K2a2 = K * K * a2;                // ___в целях оптимизации___
 56 
 57     var w = canvas.width / scale;           // ширина окна в расчетных координатах
 58     var h = canvas.height / scale;          // высота окна в расчетных координатах
 59 
 60     // Работа с массивом
 61 
 62     var balls = [];                             // массив шаров
 63     var addNewBall =  function(x, y) {
 64         // проверка - не пересекается ли новый шар со стенами или уже существующими шарами
 65         if (x - r < 0 || x + r > w || y - r < 0 || y + r > h) return null;
 66         for (var i = 0; i < balls.length; i++) {
 67             var rx = balls[i].x - x;
 68             var ry = balls[i].y - y;
 69             var rLen2 = rx * rx + ry * ry;
 70             if (rLen2 < 4 * r2) return null;
 71         }
 72         var b = [];
 73 
 74         b.x = x;                b.y = y;        // расчетные координаты шара
 75         b.fx = 0;               b.fy = 0;      // сила, действующая на шар
 76         b.vx = 0;               b.vy = 0;       // скорость
 77 
 78         balls[balls.length] = b;                // добавить элемент в конец массива
 79         return b;
 80     };
 81 
 82     // Основной цикл программы
 83 
 84     function control() {
 85         physics();
 86         draw();
 87     }
 88 
 89     // Расчетная часть программы
 90 
 91     function physics() {                        // то, что происходит каждый шаг времени
 92         for (var s = 1; s <= spf; s++) {
 93 
 94             // пересчет сил идет отдельным массивом, т.к. далее будут добавляться силы взаимодействия между шарами
 95             for (var i0 = 0; i0 < balls.length; i0++) {
 96                 balls[i0].fx = - B * balls[i0].vx;
 97                 balls[i0].fy = - B * balls[i0].vy;
 98             }
 99 
100             for (var i = 0; i < balls.length; i++) {  // пеерсчет взаимодействия между шарами
101 
102 
103     //попадание в лузу 
104     
105       if ((balls[i].x >= (300/scale-r/2))  && (balls[i].y >= (300/scale-r/2)) && (balls[i].y <= (300/scale+r/2))) {balls.splice(i, 1)}; // когда координаты шара совпадают с координатами, записанными в условии цикла, шар удаляется с поля при помощи balls.splice
106     if ((balls[i].x >= (300/scale-r/2))   && (balls[i].y <= (r/2))) {balls.splice(i, 1)};
107     if ((balls[i].x >= (300/scale-r/2))   && (balls[i].y >= (600/scale -r/2))) {balls.splice(i, 1)};
108     if ((balls[i].x <= (r/2))   && (balls[i].y >= (600/scale -r/2))) {balls.splice(i, 1)};
109     if ((balls[i].x <= (r/2))  && (balls[i].y >= (300/scale-r/2)) && (balls[i].y <= (300/scale+r/2))) {balls.splice(i, 1)};
110     if ((balls[i].x <= (r/2))   && (balls[i].y <= (r/2))) {balls.splice(i, 1)};  
111 
112                 // расчет взаимодействия производится со всеми следующими шарами в массиве,
113                 // чтобы не считать каждое взаимодействие дважды
114                 var b = balls[i];
115                 for (var j = i + 1; j < balls.length; j++) {
116                     var b2 = balls[j];
117                     var rx = b.x - b2.x;   var ry = b.y - b2.y;         // вектор смотрит на первый шар (b)
118                     var r2 = rx * rx + ry * ry;                         // квадрат расстояния между шарами
119                     if (r2 > aCut2) continue;                           // проверка на радиус обрезания
120                     var rLen = (Math.sqrt(r2));
121         
122 
123                     // если расстояние между частицами мало, силы будут посчитаны для K * a
124                     if (r2 < K2a2) {
125                         if (rLen > 0.00001) {                           // проверка, чтобы избежать деления на 0
126                             rx = rx / rLen * Ka;
127                             ry = ry / rLen * Ka;
128                         }
129                         r2 = K2a2;
130                         rLen = Ka;                                      // корень K2a2
131                     }
132 
133                     // сила взаимодействия
134                     var s2 = a2 / r2;         var s4 = s2 * s2;         // ___в целях оптимизации___
135                     var F = LJCoeff * s4 * s4 * (s4 * s2 - 1);          // сила взаимодействия Леннарда-Джонса
136 
137                     var Fx = F * rx;        var Fy = F * ry;
138                     b.fx += Fx;             b.fy += Fy;
139                     b2.fx -= Fx;            b2.fy -= Fy;
140                 }
141 
142                 if (b.y + r > h) { b.fy += -Cwall * (b.y + r - h) - Bwall * b.vy; }    // рассчет взаимодействия со стенками : когда координаты шара совпадают с координатами в условии цикла, шару придается скорость и направление 
143                 if (b.y - r < 0) { b.fy += -Cwall * (b.y - r) - Bwall * b.vy;}
144                 if (b.x + r > w) { b.fx += -Cwall * (b.x + r - w) - Bwall * b.vx; }
145                 if (b.x - r < 0) { b.fx += -Cwall * (b.x - r) - Bwall * b.vx; }
146 
147                 b.vx += b.fx / m * dt;        b.vy += b.fy / m * dt;
148                 b.x += b.vx * dt;             b.y += b.vy * dt;
149             }
150         }
151     }
152 
153     // Рисование
154 
155     var rScale13 = r * scale * 1.3;         // ___в целях оптимизации___
156     var rScaleShift = r * scale / 5;        // ___в целях оптимизации___
157   var line ;
158   var radi = 30;              // линия которая показывает предполагаему траекторию "битка"
159   
160     function draw() {
161         context.clearRect(0, 0, w * scale, h * scale);      // очистить экран
162         for (var i = 1; i < balls.length; i++){
163             var xS = balls[i].x * scale;           var yS = balls[i].y * scale;
164             // расчет градиента нужно проводить для каждого шара
165             var gradient = context.createRadialGradient(xS, yS, rScale13, xS - rScaleShift, yS + rScaleShift, 0);
166             gradient.addColorStop(0, "#fdebeb");
167             gradient.addColorStop(1, "#fffcfc");
168             context.fillStyle = gradient;
169 
170             context.beginPath();
171             context.arc(xS, yS, r * scale, 0, 2 * Math.PI, false);
172             context.closePath();
173             context.fill();
174         }
175     for (var i = 0; i < 1; i++){
176             var xS = balls[i].x * scale;           var yS = balls[i].y * scale;
177             // расчет градиента нужно проводить для каждого шара
178             var gradient = context.createRadialGradient(xS, yS, rScale13, xS - rScaleShift, yS + rScaleShift, 0);
179             gradient.addColorStop(0, "#cd0000");
180             gradient.addColorStop(1, "#fffcfc");
181             context.fillStyle = gradient;
182 
183             context.beginPath();
184             context.arc(xS, yS, r * scale, 0, 2 * Math.PI, false);
185             context.closePath();
186             context.fill();
187         }
188 
189     
190     
191       context.lineWidth="3";
192       context.strokeStyle="#fff506";
193             context.beginPath();
194             context.moveTo(balls[0].x * scale, balls[0].y * scale);                                                                                                                                                         
195       context.lineTo(radi*v0*Math.cos(q*Pi/180) + balls[0].x * scale,radi*v0*Math.sin(q*Pi/180)+balls[0].y * scale);
196             context.stroke();
197     
198 
199     // прорисовка луз
200     // verh lev
201   context.lineWidth="3";
202     context.strokeStyle="#ffffff ";
203     context.beginPath();
204     context.moveTo(0, 0);                                                                                                                                                         
205     context.lineTo(0,14 +2.5 );
206     context.stroke();
207   
208   context.lineWidth="3";
209     context.strokeStyle="#ffffff";
210     context.beginPath();
211     context.moveTo(0, 0);                                                                                                                                                         
212     context.lineTo(14 +2.5,0 );
213     context.stroke();
214   
215       // verh prav
216   context.lineWidth="3";
217     context.strokeStyle="#ffffff";
218     context.beginPath();
219     context.moveTo(286 - 2.5, 0);                                                                                                                                                         
220     context.lineTo(300,0 );
221     context.stroke();
222   
223   context.lineWidth="3";
224     context.strokeStyle="##ffffff";
225     context.beginPath();
226     context.moveTo(300, 0);                                                                                                                                                         
227     context.lineTo(300,14 +2.5 );
228     context.stroke();
229   
230         // niz lev
231   context.lineWidth="3";
232     context.strokeStyle="#ffffff";
233     context.beginPath();
234     context.moveTo(0, 600);                                                                                                                                                         
235     context.lineTo(14 +2.5,600 );
236     context.stroke();
237   
238   context.lineWidth="3";
239     context.strokeStyle="#ffffff";
240     context.beginPath();
241     context.moveTo(0, 586 - 2.5);                                                                                                                                                         
242     context.lineTo(0,600 );
243     context.stroke();
244     
245     
246     // niz prav
247   context.lineWidth="3";
248     context.strokeStyle="#ffffff";
249     context.beginPath();
250     context.moveTo(300, 600);                                                                                                                                                         
251     context.lineTo(300,586 - 2.5);
252     context.stroke();
253   
254   context.lineWidth="3";
255     context.strokeStyle="#ffffff";
256     context.beginPath();
257     context.moveTo(300, 600);                                                                                                                                                         
258     context.lineTo(286 - 2.5,600 );
259     context.stroke();
260   
261   //sered lev
262   context.lineWidth="3";
263     context.strokeStyle="#ffffff";
264     context.beginPath();
265     context.moveTo(0, 289);                                                                                                                                                         
266     context.lineTo(0,311);
267     context.stroke();
268   
269   //sered prav
270   
271   context.lineWidth="3";
272     context.strokeStyle="#ffffff";
273     context.beginPath();
274     context.moveTo(300, 289);                                                                                                                                                         
275     context.lineTo(300,311 );
276     context.stroke();
277     
278   
279     }
280   
281 
282     // Запуск системы
283                       // добавляем 20 частиц, сдвинув их от стен
284     addNewBall(16*w/32, 16*h/32 );
285     addNewBall(16*w/32, 8*h/32 );  
286     addNewBall(16*w/32 - r,   8*h/32 - 1.7321*r);  
287     addNewBall(16*w/32 + r,   8*h/32 - 1.7321*r );
288     addNewBall(16*w/32,     8*h/32 - 2*1.7321*r );    
289     addNewBall(16*w/32 - 2*r,   8*h/32 - 2*1.7321*r );
290     addNewBall(16*w/32 + 2*r,   8*h/32 - 2*1.7321*r );
291     addNewBall(16*w/32 + r,   8*h/32 - 3*1.7321*r );
292     addNewBall(16*w/32 - r,   8*h/32 - 3*1.7321*r );
293     addNewBall(16*w/32 + 3*r,   8*h/32 - 3*1.7321*r );
294     addNewBall(16*w/32 - 3*r,   8*h/32 - 3*1.7321*r );
295     addNewBall(16*w/32,     8*h/32 - 4*1.7321*r );
296     addNewBall(16*w/32 - 2*r,   8*h/32 - 4*1.7321*r );
297     addNewBall(16*w/32 + 2*r,   8*h/32 - 4*1.7321*r );
298     addNewBall(16*w/32 - 4*r,   8*h/32 - 4*1.7321*r );
299     addNewBall(16*w/32 + 4*r,   8*h/32 - 4*1.7321*r );
300     addNewBall(16*w/32, 16*h/32 );  
301     
302   this.setSlider_01 = function(c) { q=-c ;}; // функция для слайдера угла
303   this.setSlider_02 = function(c) { v0=c ;}; // функция для слайдера угла
304 
305  // Настройка интерфейса
306 
307     slider_01.min = 0;               slider_01.max =360;
308     slider_01.step = 0.5;
309     slider_01.value = q;          // начальное значение ползунка должно задаваться после min и max
310     text_01.value = Math.abs(q);
311     slider_02.min = 0;               slider_02.max = 10;
312     slider_02.step = 0.5;
313     slider_02.value = v0;                // начальное значение ползунка должно задаваться после min и max
314     text_02.value = v0;
315   
316   this.setSlider_01(q);
317   this.setSlider_02(v0);
318     
319     this.newSystem = function() {
320     balls[0].vx = v0* Math.cos(q*Pi/180);
321         balls[0].vy = v0* Math.sin(q*Pi/180);
322   }
323 
324   this.newSystem1 = function() {
325   for (var i = 20; i >= 0; i--) 
326   {balls.splice(i, 1)};
327   addNewBall(16*w/32, 16*h/32 );
328     addNewBall(16*w/32, 8*h/32 );  
329     addNewBall(16*w/32 - r,   8*h/32 - 1.7321*r);  
330     addNewBall(16*w/32 + r,   8*h/32 - 1.7321*r );
331     addNewBall(16*w/32,     8*h/32 - 2*1.7321*r );    
332     addNewBall(16*w/32 - 2*r,   8*h/32 - 2*1.7321*r );
333     addNewBall(16*w/32 + 2*r,   8*h/32 - 2*1.7321*r );
334     addNewBall(16*w/32 + r,   8*h/32 - 3*1.7321*r );
335     addNewBall(16*w/32 - r,   8*h/32 - 3*1.7321*r );
336     addNewBall(16*w/32 + 3*r,   8*h/32 - 3*1.7321*r );
337     addNewBall(16*w/32 - 3*r,   8*h/32 - 3*1.7321*r );
338     addNewBall(16*w/32,     8*h/32 - 4*1.7321*r );
339     addNewBall(16*w/32 - 2*r,   8*h/32 - 4*1.7321*r );
340     addNewBall(16*w/32 + 2*r,   8*h/32 - 4*1.7321*r );
341     addNewBall(16*w/32 - 4*r,   8*h/32 - 4*1.7321*r );
342     addNewBall(16*w/32 + 4*r,   8*h/32 - 4*1.7321*r );
343     addNewBall(16*w/32, 16*h/32 );  
344   
345 }
346     setInterval(control, 1000 / fps);
347 }

Файл "Billyard1version.html"

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <title>Billyard</title>
 5     <script src="Billyard1version.js"></script>
 6 </head>
 7 <body>
 8      <canvas id="canvasBalls" width="300" height="600" style="border:1px none #000000;background: #008000"></canvas>
 9 <br>
10     <div>Угол:
11         <input type="range" id="slider_01" style="width: 150px;" oninput="app.setSlider_01(this.value); document.getElementById('text_01').value = this.value;">
12        q =
13         <input id="text_01" style="width: 5ex;" required pattern="[-+]?([0-9]*\.[0-9]+|[0-9]+)"  oninput="
14             // если введено не число - строка не пройдет валидацию по паттерну выше, и checkValidity() вернет false
15             if (!this.checkValidity()) return;
16             app.setSlider_01(this.value);
17             document.getElementById('slider_01').value = this.value;
18         ">
19     </div><br>
20 
21     <div>Начальная скорость:
22         <input type="range" id="slider_02" style="width: 150px;" oninput="app.setSlider_02(this.value); document.getElementById('text_02').value = this.value;">
23        v0 =
24         <input id="text_02" style="width: 5ex;" required pattern="[-+]?([0-9]*\.[0-9]+|[0-9]+)" oninput="
25             // если введено не число - строка не пройдет валидацию по паттерну выше, и checkValidity() вернет false
26             if (!this.checkValidity()) return;
27             app.setSlider_02(this.value);
28             document.getElementById('slider_02').value = this.value;
29         ">
30     </div><br> 
31   
32   
33   <input type="button" style="width: 50px" name="" onclick="app.newSystem();return false;" value="PLAY"/>
34   <input type="button" style="width: 50px" name="" onclick="app.newSystem1();return false;" value="AGAIN"/>
35   
36  <script type="text/javascript">var app = new MainBalls(
37             document.getElementById('slider_01'),
38             document.getElementById('text_01'),
39             document.getElementById('slider_02'),
40             document.getElementById('text_02')
41     );</script>
42 </body>
43 </html>


В реальности данная картина так же видна, хоть и не столь явно из-за неидеальности системы .



1.gif


2.gif 


3.gif

Обсуждение результатов и выводы[править]

В ходе работы над проектом была написана программа, моделирующая процесс игры в бильярд. Данная программа показывает, что траектория разлета шаров после центрального удара не зависит от силы удара, но зависит от малейшего изменения угла, так как меняется распространение ударной волны в пирамиде. При силе, достаточной для визуально заметного разлета шаров, при изменении угла на 1 градус - полное отклонение от симметрии, при 0.1 - заметное отклонение от симметрии, при 0.01 - трудно различимое.



Скачать отчет:docx

Ссылки по теме[править]

См. также[править]