Вспоминаю, как в раннем возрасте меня терзал вопрос о том, как же умные люди (ученые-программисты) реализуют полеты искусственных спутников, старт, и выход на орбиту. Потом это оказалось очень просто. Сам процесс я понимал. Носитель отрывается от Земли под углом 90 градусов к плоскости взлета или от перпендикуляра к вектору силы тяжести, на определенной высоте начинает менять этот угол с прямого на острый, например, 80, 70, 60 и т.д. до 20-10 и далее этот градус не имеет никакого значения, т.к. задача спутника - увеличить значение апогея.
Хорошо, апогей достигнут. Именно в этой точке спутник должен выровняться по направлению своей орбиты и включить двигатели, тем самым приблизив отношение перигея и апогея к единице. Таким образом он сделает форму своей орбиты окружностью. Теперь вопрос: как реализовать эту ситуацию в компьютерной модели? Мой молодой мозг сразу полез не в ту сторону. Дело в том, что я очень любил описывать траекторию движения всех явлений. У меня даже тело, падающее под углом к горизонту использовало следующее уравнение:
Проще говоря, я не рационально использовал ресурсы ЭВМ. Начал искать уравнения эллипсов, траекторий полета спутников и натыкался на то, что в моем тогдашнем возрасте было тяжело или даже невозможно понять. А недавно в голову пришла очевидная мысль - реализовать все с помощью векторов. Вот как раз этим мы сейчас с вами и займемся. Сам процесс выглядит так:
Результатом работы будет это:
Я думаю, что рисунок мало говорит о функциональности демонстрации, поэтому, я считаю, что объяснение в данном случае просто необходимо. Итак, созерцая скриншот, вы видите на нем две точки. Маленькая - это наш спутник, массой один килограмм(1кг), а большая - планета с массой двадцать тысяч килограмм(20000кг). Значение я взял "от фонаря", хотя немного повлиял практический результат т.к. при массах 5кг и 2000кг соответственно было очень заметно влияние массы спутника на планету (в программе это учитывается!). Красная линия - это вектор скорости спутника, увеличенный в 20 раз для того, чтобы он был заметен глазу. Циановая линия соединяет центр спутника и положение мыши. При щелчке левой кнопкой к вектору скорости добавляется вектор, численно равный циановой линии, тем самым добавляет скорость телу. Это нужно для того, чтобы вывести тело с баллистической траектории на геоцентрическую орбиту. Проще говоря, сделать так, чтобы тело не упало обратно на Землю. Серые линии - декартова система координат.
Приступим к реализации. Я буду это делать на C++ с помощью OpenGL, используя freeglut, но с подробными объяснениями, которые позволять перевести код на любой другой язык.
Начнем с небольшой теории. Есть такая вещь, как закон всемирного тяготения. Выражается формулой
Тела действуют друг на друга с силой F, на рисунке это выглядит нагляднее.
На этом теории достаточно, приступим к практике. Для начала нужно оформить класс, в моем случае Body.
Положения установил случайно, как в голову взбрело, а дальше опытным путем подобрал скорость. В функции рендера окна необходимо вызвать сердце нашей программы - процедуру SpeedBody.
Исходный код C++ и исполняемые файлы
----------------------------------------------------------------
Не могу не отблагодарить википедию, из которой я взял формулы и некоторые илюстрации.
Хорошо, апогей достигнут. Именно в этой точке спутник должен выровняться по направлению своей орбиты и включить двигатели, тем самым приблизив отношение перигея и апогея к единице. Таким образом он сделает форму своей орбиты окружностью. Теперь вопрос: как реализовать эту ситуацию в компьютерной модели? Мой молодой мозг сразу полез не в ту сторону. Дело в том, что я очень любил описывать траекторию движения всех явлений. У меня даже тело, падающее под углом к горизонту использовало следующее уравнение:
Проще говоря, я не рационально использовал ресурсы ЭВМ. Начал искать уравнения эллипсов, траекторий полета спутников и натыкался на то, что в моем тогдашнем возрасте было тяжело или даже невозможно понять. А недавно в голову пришла очевидная мысль - реализовать все с помощью векторов. Вот как раз этим мы сейчас с вами и займемся. Сам процесс выглядит так:
Результатом работы будет это:
Я думаю, что рисунок мало говорит о функциональности демонстрации, поэтому, я считаю, что объяснение в данном случае просто необходимо. Итак, созерцая скриншот, вы видите на нем две точки. Маленькая - это наш спутник, массой один килограмм(1кг), а большая - планета с массой двадцать тысяч килограмм(20000кг). Значение я взял "от фонаря", хотя немного повлиял практический результат т.к. при массах 5кг и 2000кг соответственно было очень заметно влияние массы спутника на планету (в программе это учитывается!). Красная линия - это вектор скорости спутника, увеличенный в 20 раз для того, чтобы он был заметен глазу. Циановая линия соединяет центр спутника и положение мыши. При щелчке левой кнопкой к вектору скорости добавляется вектор, численно равный циановой линии, тем самым добавляет скорость телу. Это нужно для того, чтобы вывести тело с баллистической траектории на геоцентрическую орбиту. Проще говоря, сделать так, чтобы тело не упало обратно на Землю. Серые линии - декартова система координат.
Приступим к реализации. Я буду это делать на C++ с помощью OpenGL, используя freeglut, но с подробными объяснениями, которые позволять перевести код на любой другой язык.
Начнем с небольшой теории. Есть такая вещь, как закон всемирного тяготения. Выражается формулой
Тела действуют друг на друга с силой F, на рисунке это выглядит нагляднее.
Автор рисунка: Dennis Nilsson
То есть она направлена по прямой, соединяющей центры масс объектов.
Эта сила действует, но нам будет необходимо перевести ее в скорость. Тут нас встречает второй закон Ньютона.
struct Point
{
double x,y;
};
class BodyОписание класса:
{
public:
Point Pos,Speed; //позиция и скорость соответственно.
double m; // масса
void Move(); // Передвижение объекта
void PaintPhysic(); // Рисование векторов
};
void Body::Move()В главной функции (main()) инициализируем два объекта. Я не буду пока путать вас указателями.
{
Pos.x+=Speed.x;
Pos.y+=Speed.y;
}
void Body::PaintPhysic() // Рисование векторов: голубого и красного
{
glColor3f(1,0,0);
glBegin(GL_LINES);
glVertex2f(Pos.x,Pos.y);
glVertex2f(Pos.x+Speed.x*20,Pos.y+Speed.y*20); // увеличиваем вектор в 20 раз для придания вида.
glColor3f(0,1,1);
glVertex2f(Pos.x,Pos.y); // линия от планеты до курсора(циановая линия)
glVertex2f(Xf,Yf);
glEnd();
}
Body Planet[2];
Planet[0].Pos.x=0.0; Planet[0].Pos.y=0.0;
Planet[0].m=20000;
Planet[0].Speed.x=0;
Planet[0].Speed.y=0;
Planet[1].Pos.x=0.0; Planet[1].Pos.y=0.3;
Planet[1].m=1;
Planet[1].Speed.x=-0.004;
Planet[1].Speed.y=-0.008;
Положения установил случайно, как в голову взбрело, а дальше опытным путем подобрал скорость. В функции рендера окна необходимо вызвать сердце нашей программы - процедуру SpeedBody.
void SpeedBody(Body * t1,Body * t2)На самом деле и это можно упростить, но на мой взгляд так намного понятнее, нежели вынесение части кода в класс с созданием указателей. Здесь важно понять смысл второго закона Ньютона. В нашем случае ускорение - вектор который нужно прибавить к скорости тела. Рассмотрим пример. В нашей программе определенное количество кадров в секунду(FPS), например 30. Следовательно между кадрами 1000/30 = 33.(3). Когда по формуле мы находим силу гравитационного взаимодействия, то по второму закону Ньютона узнаем ускорение. Это ускорение которое получило тело при действии на него гравитации в течении 0,033 сек. Получается, что скорость увеличилась на полученное значение ускорения, именно поэтому я нагло умножаю нормализированный вектор на ускорение и прибавляю к скорости. Для создания реального времени нам необходимо прибегать к формуле ускорения a=v/t.
{
double x=(t2->Pos.x)-(t1->Pos.x); //вычисление координат вектора, соединяющего центры тел.
double y=(t2->Pos.y)-(t1->Pos.y);
double length=sqrt(x*x+y*y); //Узнаем длину вектора, для его нормализации
x=x/length; //непосредственно нормализация
y=y/length;
double a = (G*t1->m*t2->m)/(length*length); //расчет силы по закону всемирного тяготения
double a1 = a/t1->m; //вычисляем ускорение по второму закону Ньютона для первого тела
double a2 = a/t2->m; // и для второго
t1->Speed.x+=x*a1; // перемножаем координаты нормализированного вектора на ускорение
t1->Speed.y+=y*a1;// тем самым получаем вектор ускорения
t2->Speed.x-=x*a2; // делаем тоже самое для второго тела
t2->Speed.y-=y*a2; // только с минусом, иначе он будет отталкиваться
t1->Move(); // двигаем тело функцией из класса.
t2->Move();
}
Исходный код C++ и исполняемые файлы
----------------------------------------------------------------
Не могу не отблагодарить википедию, из которой я взял формулы и некоторые илюстрации.
Perfect!
ОтветитьУдалить