This example demonstrates chaotic dynamics through real-time simulation of a double pendulum system, showcasing the complex, unpredictable motion that emerges from simple nonlinear differential equations and sensitive dependence on initial conditions.
The double pendulum system is governed by the coupled nonlinear differential equations:
The system demonstrates classic chaotic behavior where small changes in initial conditions lead to dramatically different long-term evolution.
#include <array>
#include <chrono>
#include <cmath>
#include <cstddef>
#include <thread>
#include <vector>
using State = std::array<double, 4>;
};
return std::remainder(x, 2.0 * M_PI);
}
static inline auto accelerations(
const State &s,
const Params &p)
-> std::array<double, 2> {
const double th1 = s[0], th2 = s[1];
const double w1 = s[2], w2 = s[3];
const double d = th1 - th2;
const double sd = std::sin(d);
const double cd = std::cos(d);
const double twoD = 2.0 * th1 - 2.0 * th2;
const double denom0 = (2.0 * p.m1 + p.m2) - p.m2 * std::cos(twoD);
const double den1 = p.l1 * denom0;
const double den2 = p.l2 * denom0;
const double num1 = -p.g * (2.0 * p.m1 + p.m2) * std::sin(th1) -
p.m2 * p.g * std::sin(th1 - 2.0 * th2) -
2.0 * sd * p.m2 * (w2 * w2 * p.l2 + w1 * w1 * p.l1 * cd);
const double num2 =
2.0 * sd *
(w1 * w1 * p.l1 * (p.m1 + p.m2) + p.g * (p.m1 + p.m2) * std::cos(th1) +
w2 * w2 * p.l2 * p.m2 * cd);
double a1 = num1 / den1;
double a2 = num2 / den2;
a1 += -p.c1 * w1;
a2 += -p.c2 * w2;
return {a1, a2};
}
const auto acc = accelerations(y, p);
return State{y[2], y[3], acc[0], acc[1]};
}
static inline void rk4Step(
State &y,
double dt,
const Params &p) {
const State k1 = dynamics(y, p);
for (int i = 0; i < 4; ++i)
y2[i] = y[i] + 0.5 * dt * k1[i];
const State k2 = dynamics(y2, p);
for (int i = 0; i < 4; ++i)
y3[i] = y[i] + 0.5 * dt * k2[i];
const State k3 = dynamics(y3, p);
for (int i = 0; i < 4; ++i)
y4[i] = y[i] + dt * k3[i];
const State k4 = dynamics(y4, p);
for (int i = 0; i < 4; ++i) {
y[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]);
}
}
const double dt = 0.003;
const int substepsPerFrame = 3;
s[0] = M_PI / 2.0;
s[1] = M_PI / 2.0;
s[2] = 0.0;
s[3] = 0.0;
std::vector<double> x1Trail, y1Trail;
std::vector<double> x2Trail, y2Trail;
const int maxTrailLength = 500;
auto computePositions = [&](
const State &st) -> std::array<double, 4> {
const double x1 = p.
l1 * std::sin(st[0]);
const double y1 = -p.
l1 * std::cos(st[0]);
const double x2 = x1 + p.
l2 * std::sin(st[1]);
const double y2 = y1 - p.
l2 * std::cos(st[1]);
return std::array<double, 4>{x1, y1, x2, y2};
};
auto pos = computePositions(s);
{{{"x", {0.0, pos[0], pos[2]}},
{"y", {0.0, pos[1], pos[3]}},
{"mode", "lines+markers"},
{"type", "scatter"},
{"marker",
{{"size", {8, 12, 12}}, {"color", {"black", "red", "blue"}}}},
{"line", {{"color", "gray"}, {"width", 2}}},
{"name", "Pendulum"}},
{{"x", x1Trail},
{"y", y1Trail},
{"mode", "lines"},
{"type", "scatter"},
{"line", {{"color", "red"}, {"width", 1}}},
{"name", "Mass 1 Trail"}},
{{"x", x2Trail},
{"y", y2Trail},
{"mode", "lines"},
{"type", "scatter"},
{"line", {{"color", "blue"}, {"width", 1}}},
{"name", "Mass 2 Trail"}}},
{{"title", {{"text", "Double Pendulum Simulation (RK4, canonical EoM)"}}},
{"xaxis", {{"title", "x"}, {"range", {-2.5, 2.5}}}},
{"yaxis", {{"title", "y"}, {"range", {-2.5, 1.0}}}},
{"showlegend", true},
{"plot_bgcolor", "white"}});
for (int i = 0; i < substepsPerFrame; ++i) {
rk4Step(s, dt, p);
}
pos = computePositions(s);
const double x1 = pos[0], y1 = pos[1], x2 = pos[2], y2 = pos[3];
x1Trail.push_back(x1);
y1Trail.push_back(y1);
x2Trail.push_back(x2);
y2Trail.push_back(y2);
auto trim = [&](std::vector<double> &vx, std::vector<double> &vy) -> void {
if (vx.size() > static_cast<size_t>(maxTrailLength)) {
vx.erase(vx.begin());
vy.erase(vy.begin());
}
};
trim(x1Trail, y1Trail);
trim(x2Trail, y2Trail);
fig.
update({{
"x", {{0.0, x1, x2}, x1Trail, x2Trail}},
{"y", {{0.0, y1, y2}, y1Trail, y2Trail}}});
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return 0;
}
auto main() -> int
Definition gallery_animate_sin_wave.cpp:48
auto wrapAngle(double x) -> double
Definition gallery_double_pendulum.cpp:103
std::array< double, 4 > State
Definition gallery_double_pendulum.cpp:90
Public Plotly C++ API header.
Definition gallery_double_pendulum.cpp:92
double m1
Definition gallery_double_pendulum.cpp:95
double l2
Definition gallery_double_pendulum.cpp:94
double c1
Definition gallery_double_pendulum.cpp:98
double c2
Definition gallery_double_pendulum.cpp:99
double g
Definition gallery_double_pendulum.cpp:97
double m2
Definition gallery_double_pendulum.cpp:96
double l1
Definition gallery_double_pendulum.cpp:93