简体   繁体   English

D3.js双摆奇行为

[英]D3.js double pendulum odd behavior

I'm using D3.js to make a double pendulum simulator.我正在使用 D3.js 制作双摆模拟器。 I'm using 4th order Runge-Kutta to integrate the ODE's and I'm pretty sure I'm getting the correct values for theta and phi as I checked the output data with mathamatica.我正在使用 4 阶 Runge-Kutta 来集成 ODE,我很确定当我使用 mathamatica 检查 output 数据时,我得到了正确的 theta 和 phi 值。 However the length of second pendulum keeps changing as it moves which should not be occurring and the path traced out by the pendulum doesn't seem correct either.然而,第二个钟摆的长度随着它的移动而不断变化,这不应该发生,并且钟摆描绘的路径似乎也不正确。 Since I'm almost certain I am getting the correct values for phi and theta from the integration I think the issue may be how I calculate the x and y coordinates but I'm not sure.因为我几乎可以肯定我从积分中得到了正确的 phi 和 theta 值,所以我认为问题可能是我如何计算 x 和 y 坐标,但我不确定。

 var width = 710, height = 710; var margin = {top: -20, right: 30, bottom: 40, left: 40}; var g = 9.81; //so length is in meters var l = 0.4; //length of pendulum 1 (top pendulum) var ell = 0.5; //ibid pendulum 2 (bottom pendulum) var m = 5; //mass of pendulum 1 var M = 5; // ibid pendulum 2 var theta = 40 * (Math.PI/180); //angle of top pendulum var phi = 10 * (Math.PI/180); //angle of bottom pendulum var thetaDot = 0; var phiDot = 0; var xBall1 = l*Math.sin(theta); var yBall1 = -l*Math.cos(theta); var xBall2 = ell*Math.sin(phi); var yBall2 = -ell*Math.cos(phi) + yBall1; var t = 0; var stepSize =.01; function uDot(theta, thetaDot, phi, phiDot) { //first ODE return(thetaDot); } function vDot(theta, thetaDot, phi, phiDot) { //second ODE val = -g*(2*m + M)*Math.sin(theta) - M*g*Math.sin(theta - (2*phi)) - 2*Math.sin(theta-phi)*M*((phiDot*phiDot*ell) + thetaDot*thetaDot*l*Math.cos(theta-phi)); val = val/( l*(2*m + M - M*Math.cos(2*theta - 2*phi)) ); return(val); } function wDot(theta, thetaDot, phi, phiDot) { //third ODE return(phiDot); } function sDot(theta, thetaDot, phi, phiDot) { val = 2*Math.sin(theta-phi)*(thetaDot*thetaDot*l*(m+M) + g*(m+M)*Math.cos(theta) + phiDot*phiDot*ell*M*Math.cos(theta-phi)); val = val/( ell*(2*m + M - M*Math.cos(2*theta - 2*phi)) ); return(val); } function RK4() { /* 4th order Runge-Kutta solver for system of 4 equations with 4 variables */ k0 = stepSize * uDot(theta, thetaDot, phi, phiDot); l0 = stepSize * vDot(theta, thetaDot, phi, phiDot); q0 = stepSize * wDot(theta, thetaDot, phi, phiDot); p0 = stepSize * sDot(theta, thetaDot, phi, phiDot); k1 = stepSize * uDot(theta+(0.5*k0), thetaDot+(0.5*l0), phi+(0.5*q0), phiDot+(0.5*p0)); l1 = stepSize * vDot(theta+(0.5*k0), thetaDot+(0.5*l0), phi+(0.5*q0), phiDot+(0.5*p0)); q1 = stepSize * wDot(theta+(0.5*k0), thetaDot+(0.5*l0), phi+(0.5*q0), phiDot+(0.5*p0)); p1 = stepSize * sDot(theta+(0.5*k0), thetaDot+(0.5*l0), phi+(0.5*q0), phiDot+(0.5*p0)); k2 = stepSize * uDot(theta+(0.5*k1), thetaDot+(0.5*l1), phi+(0.5*q1), phiDot+(0.5*p1)); l2 = stepSize * vDot(theta+(0.5*k1), thetaDot+(0.5*l1), phi+(0.5*q1), phiDot+(0.5*p1)); q2 = stepSize * wDot(theta+(0.5*k1), thetaDot+(0.5*l1), phi+(0.5*q1), phiDot+(0.5*p1)); p2 = stepSize * sDot(theta+(0.5*k1), thetaDot+(0.5*l1), phi+(0.5*q1), phiDot+(0.5*p1)); k3 = stepSize * uDot(theta+k2, thetaDot+l2, phi+q2, phiDot+p2); l3 = stepSize * vDot(theta+k2, thetaDot+l2, phi+q2, phiDot+p2); q3 = stepSize * wDot(theta+k2, thetaDot+l2, phi+q2, phiDot+p2); p3 = stepSize * sDot(theta+k2, thetaDot+l2, phi+q2, phiDot+p2); theta = theta + ((1/6) * (k0 + 2*k1 + 2*k2 + k3)); thetaDot = thetaDot + ((1/6) * (l0 + 2*l1 + 2*l2 + l3)); phi = phi + ((1/6) * (q0 + 2*q1 + 2*q2 + q3)); phiDot = phiDot + ((1/6) * (p0 + 2*p1 + 2*p2 + p3)); } d3.select("canvas").attr("width", width).attr("height", height); var canvas = d3.select("canvas"); var context = canvas.node().getContext("2d"); var svg = d3.select("#doublePendulum").attr("width", width).attr("height", height).append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var xAxis = d3.scaleLinear().domain([-1.5, 1.5]).range([0, width]); svg.append("g").attr("transform", "translate(0," + height + ")").call(d3.axisBottom(xAxis)); var yAxis = d3.scaleLinear().domain([-1.5, 1.5]).range([height, 0]); svg.append("g").call(d3.axisLeft(yAxis)); var rod1 = svg.append('line') /* put before circle code so it doesn't go over the circle */.attr('id', 'rod1').attr('x2', xAxis(0)).attr('y2', yAxis(0)).attr('x1', xAxis(xBall1)).attr('y1', yAxis(yBall1)).attr('stroke', '#000000').attr('stroke-width', '2px'); var ball1 = svg.append('circle').attr('id', 'ball1').attr('cx', xAxis(xBall1)).attr('cy', yAxis(yBall1)).attr('r', 10).style('fill', '#000000'); var rod2 = svg.append('line').attr('id', 'rod2').attr('x2', xAxis(xBall1)).attr('y2', yAxis(yBall1)).attr('x1', xAxis(xBall2)).attr('y1', yAxis(yBall2)).attr('stroke', '#000000').attr('stroke-width', '2px'); var ball2 = svg.append('circle').attr('id', 'ball2').attr('cx', xAxis(xBall2)).attr('cy', yAxis(yBall2)).attr('r', 10).style('fill', '#000000'); function update() { context.beginPath(); context.strokeStyle = '#0026FF'; context.moveTo(xAxis(xBall2) + margin.left, yAxis(yBall2) + margin.top); t +=.01; RK4(); xBall1 = l*Math.sin(theta); yBall1 = -l*Math.cos(theta); xBall2 = ell*Math.sin(phi); yBall2 = -ell*Math.cos(phi) + yBall1; context.lineTo(xAxis(xBall2) + margin.left, yAxis(yBall2) + margin.top); context.stroke(); d3.select('#rod1').attr('y2', yAxis(0)).attr('x2', xAxis(0)).attr('y1', yAxis(yBall1)).attr('x1', xAxis(xBall1)); d3.select('#ball1').attr('cx', xAxis(xBall1)).attr('cy', yAxis(yBall1)); d3.select('#rod2').attr('y2', yAxis(yBall1)).attr('x2', xAxis(xBall1)).attr('y1', yAxis(yBall2)).attr('x1', xAxis(xBall2)); d3.select('#ball2').attr('cx', xAxis(xBall2)).attr('cy', yAxis(yBall2)); } var runApp = setInterval(function () { update(); }, 5);
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <div style="position: relative;"> <canvas style="position: absolute;"></canvas> <svg id="doublePendulum" style="position: absolute; z-index: 1;"></svg> </div>

You are correct, you aren't calculating the x,y positions correctly, more specifically, you are not calculating x correctly:你是对的,你没有正确计算 x,y 位置,更具体地说,你没有正确计算 x:

xBall1 = l*Math.sin(theta);
yBall1 = -l*Math.cos(theta);
xBall2 = ell*Math.sin(phi);
yBall2 = -ell*Math.cos(phi) + yBall1;

This is the same as how you calculate the initial positions, but after initialization, xBall2 is dependent on xBall1.这与计算初始位置的方式相同,但在初始化之后,xBall2 依赖于 xBall1。

xBall1 = l*Math.sin(theta);
xBall2 = ell*Math.sin(phi) + xBall1;

Without looking at the rest of the internals, it doesn't quite feel right but I'm not sure what the resulting pattern should look like.不看内部的 rest,感觉不太对劲,但我不确定最终的图案应该是什么样子。 However, doing this keeps both pendulums the same length throughout, so I'd venture it is correct:但是,这样做可以使两个钟摆始终保持相同的长度,所以我敢说这是正确的:

 var width = 710, height = 710; var margin = {top: -20, right: 30, bottom: 40, left: 40}; var g = 9.81; //so length is in meters var l = 0.4; //length of pendulum 1 (top pendulum) var ell = 0.5; //ibid pendulum 2 (bottom pendulum) var m = 5; //mass of pendulum 1 var M = 1; // ibid pendulum 2 var theta = 40 * (Math.PI/180); //angle of top pendulum var phi = 10 * (Math.PI/180); //angle of bottom pendulum var thetaDot = 0; var phiDot = 0; var xBall1 = l*Math.sin(theta); var yBall1 = -l*Math.cos(theta); var xBall2 = ell*Math.sin(phi); var yBall2 = -ell*Math.cos(phi) + yBall1; var t = 0; var stepSize =.01; function uDot(theta, thetaDot, phi, phiDot) { //first ODE return(thetaDot); } function vDot(theta, thetaDot, phi, phiDot) { //second ODE val = -g*(2*m + M)*Math.sin(theta) - M*g*Math.sin(theta - (2*phi)) - 2*Math.sin(theta-phi)*M*((phiDot*phiDot*ell) + thetaDot*thetaDot*l*Math.cos(theta-phi)); val = val/( l*(2*m + M - M*Math.cos(2*theta - 2*phi)) ); return(val); } function wDot(theta, thetaDot, phi, phiDot) { //third ODE return(phiDot); } function sDot(theta, thetaDot, phi, phiDot) { val = 2*Math.sin(theta-phi)*(thetaDot*thetaDot*l*(m+M) + g*(m+M)*Math.cos(theta) + phiDot*phiDot*ell*M*Math.cos(theta-phi)); val = val/( ell*(2*m + M - M*Math.cos(2*theta - 2*phi)) ); return(val); } function RK4() { /* 4th order Runge-Kutta solver for system of 4 equations with 4 variables */ k0 = stepSize * uDot(theta, thetaDot, phi, phiDot); l0 = stepSize * vDot(theta, thetaDot, phi, phiDot); q0 = stepSize * wDot(theta, thetaDot, phi, phiDot); p0 = stepSize * sDot(theta, thetaDot, phi, phiDot); k1 = stepSize * uDot(theta+(0.5*k0), thetaDot+(0.5*l0), phi+(0.5*q0), phiDot+(0.5*p0)); l1 = stepSize * vDot(theta+(0.5*k0), thetaDot+(0.5*l0), phi+(0.5*q0), phiDot+(0.5*p0)); q1 = stepSize * wDot(theta+(0.5*k0), thetaDot+(0.5*l0), phi+(0.5*q0), phiDot+(0.5*p0)); p1 = stepSize * sDot(theta+(0.5*k0), thetaDot+(0.5*l0), phi+(0.5*q0), phiDot+(0.5*p0)); k2 = stepSize * uDot(theta+(0.5*k1), thetaDot+(0.5*l1), phi+(0.5*q1), phiDot+(0.5*p1)); l2 = stepSize * vDot(theta+(0.5*k1), thetaDot+(0.5*l1), phi+(0.5*q1), phiDot+(0.5*p1)); q2 = stepSize * wDot(theta+(0.5*k1), thetaDot+(0.5*l1), phi+(0.5*q1), phiDot+(0.5*p1)); p2 = stepSize * sDot(theta+(0.5*k1), thetaDot+(0.5*l1), phi+(0.5*q1), phiDot+(0.5*p1)); k3 = stepSize * uDot(theta+k2, thetaDot+l2, phi+q2, phiDot+p2); l3 = stepSize * vDot(theta+k2, thetaDot+l2, phi+q2, phiDot+p2); q3 = stepSize * wDot(theta+k2, thetaDot+l2, phi+q2, phiDot+p2); p3 = stepSize * sDot(theta+k2, thetaDot+l2, phi+q2, phiDot+p2); theta = theta + ((1/6) * (k0 + 2*k1 + 2*k2 + k3)); thetaDot = thetaDot + ((1/6) * (l0 + 2*l1 + 2*l2 + l3)); phi = phi + ((1/6) * (q0 + 2*q1 + 2*q2 + q3)); phiDot = phiDot + ((1/6) * (p0 + 2*p1 + 2*p2 + p3)); } d3.select("canvas").attr("width", width).attr("height", height); var canvas = d3.select("canvas"); var context = canvas.node().getContext("2d"); var svg = d3.select("#doublePendulum").attr("width", width).attr("height", height).append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var xAxis = d3.scaleLinear().domain([-1.5, 1.5]).range([0, width]); svg.append("g").attr("transform", "translate(0," + height + ")").call(d3.axisBottom(xAxis)); var yAxis = d3.scaleLinear().domain([-1.5, 1.5]).range([height, 0]); svg.append("g").call(d3.axisLeft(yAxis)); var rod1 = svg.append('line') /* put before circle code so it doesn't go over the circle */.attr('id', 'rod1').attr('x2', xAxis(0)).attr('y2', yAxis(0)).attr('x1', xAxis(xBall1)).attr('y1', yAxis(yBall1)).attr('stroke', '#000000').attr('stroke-width', '2px'); var ball1 = svg.append('circle').attr('id', 'ball1').attr('cx', xAxis(xBall1)).attr('cy', yAxis(yBall1)).attr('r', 10).style('fill', '#000000'); var rod2 = svg.append('line').attr('id', 'rod2').attr('x2', xAxis(xBall1)).attr('y2', yAxis(yBall1)).attr('x1', xAxis(xBall2)).attr('y1', yAxis(yBall2)).attr('stroke', '#000000').attr('stroke-width', '2px'); var ball2 = svg.append('circle').attr('id', 'ball2').attr('cx', xAxis(xBall2)).attr('cy', yAxis(yBall2)).attr('r', 10).style('fill', '#000000'); // for debug: var i = 0; // function update() { context.beginPath(); context.strokeStyle = '#0026FF'; context.moveTo(xAxis(xBall2) + margin.left, yAxis(yBall2) + margin.top); t +=.01; RK4(); xBall1 = l*Math.sin(theta); yBall1 = -l*Math.cos(theta); xBall2 = ell*Math.sin(phi) + xBall1; yBall2 = -ell*Math.cos(phi) + yBall1; context.lineTo(xAxis(xBall2) + margin.left, yAxis(yBall2) + margin.top); context.stroke(); d3.select('#rod1').attr('y2', yAxis(0)).attr('x2', xAxis(0)).attr('y1', yAxis(yBall1)).attr('x1', xAxis(xBall1)); d3.select('#ball1').attr('cx', xAxis(xBall1)).attr('cy', yAxis(yBall1)); d3.select('#rod2').attr('y2', yAxis(yBall1)).attr('x2', xAxis(xBall1)).attr('y1', yAxis(yBall2)).attr('x1', xAxis(xBall2)); d3.select('#ball2').attr('cx', xAxis(xBall2)).attr('cy', yAxis(yBall2)); // for debug: if(i%50 == 0) { var dx = yBall1 - yBall2; var dy = xBall1 - xBall2; var h1 = Math.sqrt(dx*dx+dy*dy); var h0 = Math.sqrt(yBall1*yBall1+xBall1*xBall1); console.log("Pendulum Lengths: " + h0 + ", " + h1 ); } i++; // } var runApp = setInterval(function () { update(); }, 5);
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <div style="position: relative;"> <canvas style="position: absolute;"></canvas> <svg id="doublePendulum" style="position: absolute; z-index: 1;"></svg> </div>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM