Я попытался написать физически точный двойной маятник. И я нашел метод под названием XPBD. https://matthias-research.github.io/pages/publications/XPBD.pdf вы можете прочитать это отсюда.
https://thewikihow.com/video_EzuNFaqsfEY
Как видно на видео, система со временем теряет энергию. Я думаю, что это связано с ограничением длины в классе Spring, но я не смог найти свою ошибку. Как заставить систему работать правильно без потери энергии?
void Spring::solveForConstraints(float dt) {
Vector2Custom diffVector = end_p->pos - start_p->pos;
double currentLen = diffVector.Length();
double a_hat = inverseStiffnes / (dt * dt);
double w1 = start_p->invMass;
double w2 = end_p->invMass;
if (currentLen != originalLen) {
double lambda = 0;
double lambda_delta;
for (int k = 0; k < 8; k++)
{
double C = (end_p->pos - start_p->pos).Length() - originalLen;
Vector2Custom delta_C1 = (start_p->pos - end_p->pos).Normalize();
Vector2Custom delta_C2 = (end_p->pos - start_p->pos).Normalize(); // -delta_C1
// formula from paper delta_x = inverseMass * delta_C * delta_lambda;
// formula from paper delta lambda = (-C - â * lambda) / (delta_C * inverseMass + a_hat);
if ((!start_p->isFixed) && (!end_p->isFixed)) { // w1 / (w1+ w2) * (l-l0 ) * diffvectorNormalized
lambda_delta = (-C - a_hat * lambda) / (delta_C1.DotProduct(delta_C1 * w1) + delta_C2.DotProduct(delta_C2 * w2) + a_hat);
lambda += lambda_delta;
Vector2Custom delta_x1 = delta_C1 * lambda_delta * w1;
Vector2Custom delta_x2 = delta_C2 * lambda_delta * w2;
start_p->pos = start_p->pos + delta_x1;
end_p->pos = end_p->pos + delta_x2;
}
else if (!start_p->isFixed) {
lambda_delta = (-C - a_hat * lambda) / (delta_C1.DotProduct(delta_C1 * w1) + a_hat);
lambda += lambda_delta;
Vector2Custom delta_x1 = delta_C1 * lambda_delta * w1;
start_p->pos = start_p->pos + delta_x1;
}
else if (!end_p->isFixed) {
lambda_delta = (-C - a_hat * lambda) / (delta_C2.DotProduct(delta_C2 * w2) + a_hat);
lambda += lambda_delta;
Vector2Custom delta_x2 = delta_C2 * lambda_delta * w2;
end_p->pos = end_p->pos + delta_x2;
}
}
}
}
формулы 17 и 18 — это то, что я пытался сделать.
для лучшего понимания. есть алгоритм ниже
алгоритм
Ниже я вызываю функцию.
// Update
int substeps = 10;
float dt_sub = dt / substeps;
for (int j = 0; j < substeps; j++)
{
//Verlet Integration
// First, guess where is particles is going to be.
for (int i = 1; i < numOfParticles; i++)
{
Particle* currentBall = &ParticleList[i];
Vector2Custom accVector = gravVec * currentBall->invMass; //acceleration vector a = F/m
currentBall->pos = currentBall->pos + accVector * (dt_sub * dt_sub); //add acc * dt * dt
currentBall->pos = currentBall->pos + currentBall->speed * dt_sub; //add speed * dt
}
// Solve lenght Constraints
for (int j = 0; j < springList.size(); j++)
{
springList[j].solveForConstraints(dt_sub);
}
//Update velocity
for (int i = 1; i < numOfParticles; i++)
{
Particle* currentBall = &ParticleList[i];
currentBall->speed = (currentBall->pos - currentBall->old_pos) / dt_sub;
currentBall->old_pos = currentBall->pos;
}
}
Как исправить формулу, если я использую ее неправильно?
@AlanBirtles, но в этом видео youtube.com/watch?v=9gQQAO4I1Ck&t=314s pezza использует тот же метод xpbd. Его маятник работает нормально.
Пробовали ли вы запустить их код и измерить энергию так же, как вы?
Я пытался запустить, но не смог заставить его работать. Репо для меня сложен. Но сейчас попробую еще раз запустить.
Сначала замените все float
на double
. Тип float
часто недостаточно точен для научных вычислений.
Я заменил floats
на double
, но, похоже, ничего не изменилось.
@AlanBirtles Репо очень сложное и нет объяснений. Я смог его построить, но он не работает. Есть ли у вас другие предложения?
Какое отношение пружина имеет к двойному маятнику?
@lastchance — это просто имя класса для ограничения длины. Вы можете установить жесткость ограничения. Если inverseStiffnes равен нулю, он ведет себя как твердая палка.
Мне следует просто использовать уравнения Эйлера-Лагранжа для двух степеней свободы; (например, два угла, образуемые стержнями маятника с вертикалью). Тогда вы гарантированно сэкономите электроэнергию. Каким бы ни был ваш метод, вам лучше включить в свой пост фактические уравнения, которые вы используете (чтобы их не вытащили из чьего-либо исследовательского отчета), и представить полный код, который действительно можно использовать для его запуска.
@lastchance Когда я использую уравнения Эйлера-Лагранжа, смогу ли я переместить точку, на которой она висит? Мне нужна система, которая реагирует на движение точки?
Да, вы можете переместить точку (горизонтальный импульс сохранится, как и энергия), но не могли бы вы четко сформулировать свою проблему в своем вопросе.
Фактически в статье говорится, что потеря энергии будет ограничением метода XPBD:
5 Демпфирование
Наш метод основан на неявной схеме временного шага, которая естественно включает в себя некоторую диссипацию механической энергии. Тем не менее, может быть полезно смоделировать дополнительное демпфирование ограничений[....]
Ожидаются потери энергии.
Возможно вы и правы, но потери энергии были очень велики, поэтому я и задал вопрос. Я установил номер подшага равным 10, когда я увеличил его до 6000, производительность снизилась, но точность значительно возросла. Я думаю, в этом была проблема.
Я предполагаю, что это просто ошибки округления, подобные дополнительные вычисления имеют тенденцию усиливать ошибки с плавающей запятой en.wikipedia.org/wiki/Floating-point_error_mitigation