如何计算摆的运动?

问题描述

我正在尝试使用Runge-Kutta四阶方法对摆进行简单的模拟。我正在使用p5.js。通常,它可以正确地计算角度,但有时它只是开始随机旋转等。我不知道如何解决这个问题,我认为我说的算法的实现似乎有问题吗?

我以陶韬的《计算物理入门》中的代码为灵感,看起来很相似

上述代码在Fortran中的第一部分

第二部分

let screenWidth = 1300;
let screenHight = 1970;
let angleChangeDifference;

let gSlider;
let lSlider;
let aFrequencySlider;
let dumpingSlider;
let startAngleSlider;
let timestepSlider;
let timeMaxSlider;
let dForceSlider;
let initForceSlider;

let startAngle = 0;
let currentAngle = 0;

let circleX = 0;
let circleY = 200;
let circleRWidth = 100;
let circleRHeight = 100;

let lineXStart = 0;
let lineYStart = 0;
let lineXEnd = circleX;
let lineYEnd = (circleY - circleRHeight / 2);

function setup() {
  createCanvas(screenWidth,screenHight);
  changeScreenDeafultStartingPoint(screenWidth / 2,100);
  frameRate(60)
  createSliders();
  setInterval(showSliderValue,100);
  startButton.mouseClicked(start);
  restartButton.mouseClicked(restart);
  chartButton.mouseClicked(enableChart);
  background(200);
}
let angleSign = '\u00B0';
let omegaSign = '\u03C9';
let chartOn = false;
let step = 0;

function draw() {
  startupConfiguration()
  showSliderValue()
  if (step >= 1) {
    rotatePendulum();
    if (step == 1) {}
    if (step == 2)
      startButton.remove();
    chartButton.position(20,340);
    if (chartOn == true) {
      createChart(0,1,'czas (s)','kat (' + angleSign + ')',degreesArr);
      createChart(0,320,omegaSign + ' (' + angleSign + '/s)',omegaArr);
      scale(2);
      createPhaseChart(290,80,degreesArr);
      scale(0.5);
    }
  }
  line(lineXStart,lineYStart,lineXEnd,lineYEnd + (20 * lSlider.value()));
  fill(200,76,43)
  ellipse(circleX,circleY + (20 * lSlider.value()),circleRWidth,circleRHeight);
}

function createSliders() {

  gSlider = createSlider(0.05,20,9.81,0.01);
  gSlider.position(1100,-90);

  lSlider = createSlider(0.5,10,5,0.5);
  lSlider.position(1100,-50);

  initForceSlider = createSlider(-5,0.5);
  initForceSlider.position(1100,50);

  dForceSlider = createSlider(-2,2,0.9,0.05)
  dForceSlider.position(1100,90);

  aFrequencySlider = createSlider(-2,2 / 3,1 / 3);
  aFrequencySlider.position(1100,130);

  dumpingSlider = createSlider(0.00,1.5,0.5,0.05);
  dumpingSlider.position(1100,170);

  startAngleSlider = createSlider(-Math.PI / 2,Math.PI / 2,Math.PI / 32);
  startAngleSlider.position(1100,210);

  timestepSlider = createSlider(0,1000,100,10);
  timestepSlider.position(1100,250);

  timeMaxSlider = createSlider(10,10000,10);
  timeMaxSlider.position(1100,290);


  startButton = createButton('ZATWIERDZ',false);
  startButton.position(100,310)

  restartButton = createButton('RESTART',false);
  restartButton.position(20,310)

  chartButton = createButton('WYKRES',false);
  chartButton.position(-200,340);

}

function showSliderValue() {
  background(200);
  fill(0,0)

  text('sila poczatkowa',440,-60)
  text(initForceSlider.value(),400,-42)
  text('sila sprawcza',-20)
  text(dForceSlider.value(),-2)
  text('czestosc katowa',20)
  text(aFrequencySlider.value(),42)

  text('tlumienie',60)
  text(dumpingSlider.value(),82)
  text('kat poczatkowy',100)
  text(int(degrees(startAngleSlider.value())),122)
  text('krok czasowy (N1)',140)
  text(timestepSlider.value(),162)
  text('dlugosc symulacji (N2)',180)
  text(timeMaxSlider.value(),202)


}

function start() {
  angleIndex = 0;
  step++;
  startAngle = startAngleSlider.value();
  currentAngle = startAngle;
  angleChangeDifference = simulate();
  rotatePendulum()

  startButton.html("START")
}

function restart() {
  window.location.reload();
}



function enableChart() {
  chartOn = true;
}

function createChart(moveByX,moveByY,xName,yName,table) {
  rotate(-currentAngle);
  scale(1.1);
  translate(moveByX,moveByY);
  strokeWeight(1);
  line(-500,500,530,500);
  line(-500,700,700);
  line(-500,-500,700);
  line(530,700);
  strokeWeight(1);
  let counter = 0;
  for (i = 510; i < 700; i += 10) {
    if (counter < 9 && counter % 2 == 0)
      text(90 - 10 * counter,-520,i + 5)
    else if (counter == 9 && counter % 2 == 0)
      text(90 - 10 * counter,-515,i + 5)
    else if (counter > 9 && counter % 2 == 0)
      text(90 - 10 * counter,-525,i + 5)

    line(-505,i,i);
    counter++;
  }
  textSize(25);
  text(xName,-20,750)
  textSize(12);

  counter = 0;
  for (i = -490; i < 535; i += 25) {
    line(i,705);
    if (counter % 4 == 0) {
      line(i,705);
      text(counter * 2.5,i - 5,715);
    }
    counter++;
  }
  rotate(-90);
  textSize(25);
  text(yName,-670,-550)
  textSize(12);
  rotate(90);

  fillChartByTableValues(table);
  translate(-moveByX,-moveByY);
  scale(0.91);
  rotate(currentAngle);
}

function fillChartByTableValues(table) {
  strokeWeight(2);
  stroke(0,255);
  for (i = 0; i < timeArr.length - 1; i++) {
    FirstPointX = -490 + timeArr[i] * 10;
    FirstPointY = 600 + table[i] * (-1);
    SecondPointX = -490 + timeArr[i + 1] * 10;
    SecondPointY = 600 + table[i + 1] * (-1);
    line(FirstPointX,FirstPointY,SecondPointX,SecondPointY);
  }
  stroke(0,0);
  strokeWeight(0.1);
}


function createPhaseChart(moveByX,-300,700);
  line(-300,700);
  strokeWeight(1);
  let counter = 0;
  textSize(8);
  for (i = 510; i < 700; i += 10) {
    if (counter < 9 && counter % 2 == 0)
      text(90 - 10 * counter,-517,i + 3)
    else if (counter == 9 && counter % 2 == 0)
      text(90 - 10 * counter,-512,i + 3)
    else if (counter > 9 && counter % 2 == 0)
      text(90 - 10 * counter,i + 3)

    line(-505,i);
    counter++;
  }
  textSize(12);

  textSize(15);
  text(xName,-430,735)
  textSize(12);

  counter = 0;
  textSize(8);
  for (i = -490; i < -300; i += 10) {
    line(i,705);

    if (counter < 9 && counter % 2 == 0)
      text(-90 + 10 * counter,i - 7,715);
    else if (counter == 9 && counter % 2 == 0)
      text(-90 + 10 * counter,i - 2,715);
    else if (counter > 9 && counter % 2 == 0)
      text(-90 + 10 * counter,i - 4,715);

    counter++;
  }
  textSize(12);

  rotate(-90);
  textSize(15);
  text(yName,-620,-528)
  textSize(12);
  rotate(90);

  fillPhaseChartByTableValues(degreesArr,omegaArr)

  translate(-moveByX,-moveByY);
  scale(0.91);
  rotate(currentAngle);
}


function fillPhaseChartByTableValues(tableX,tableY) {
  translate(-400,600);
  strokeWeight(1);
  stroke(0,255);
  for (i = 0; i < tableX.length; i++) {
    ellipse(tableX[i],tableY[i],0.5);
  }
  translate(400,-600);
  stroke(0,0);
  strokeWeight(0.1);
}


function startupConfiguration() {
  background(200);
  angleMode(DEGREES);
  changeScreenDeafultStartingPoint(screenWidth / 2,100);
}

function changeScreenDeafultStartingPoint(x,y) {
  translate(x,y);
}


let angleIndex = 0;

function rotatePendulum() {
  currentAngle = angleChangeDifference[angleIndex] * (180 / PI);
  rotate(currentAngle);
  if (step > 1) {
    angleIndex++
  }
}

function calculateIntegral(t,q,dt,f) {

  let k1 = f(t,q).map(val => val * dt);

  let temp = k1.map(val => val * 0.5);
  temp = temp.map((val,index) => val + q[index])

  let k2 = f(t + 0.5 * dt,temp).map(val => val * dt);

  temp = k2.map(val => val * 0.5);
  temp = temp.map((val,index) => val + q[index])
  let k3 = f(t + 0.5 * dt,temp).map(val => val * dt);

  temp = q.map((val,index) => val + k3[index]);
  let k4 = f(t + dt,temp).map(val => val * dt);

  temp = k2.map((val,index) => val + k3[index])
  temp = temp.map(val => val * 2)
  temp = temp.map((val,index) => (val + k1[index] + k4[index]) / 6)

  temp = temp.map((val,index) => val + q[index])
  return [t + dt,temp];
}


function modelPendulum(t,q) {
  let c = dumpingSlider.value();
  let fw = dForceSlider.value();
  let w = aFrequencySlider.value();

  let x1 = q[0];
  let x2 = q[1];
  return [x2,-(Math.sin(x1)) - (c * x2) + (fw * Math.cos(w * t))];
}

let degreesArr,timeArr;

function simulate() {
  let t = 0.0;
  let dt = (3 * Math.PI) / timestepSlider.value()
  let tf = timeMaxSlider.value() * dt
  let q = [startAngle,initForceSlider.value()];
  let Nt = int(Math.round((tf - t) / dt)) + 1;
  let solution = new Array(q.length + 1);

  for (i = 0; i < q.length + 1; i++) {
    solution[i] = new Array(Nt).fill(0);
  }
  solution[0][0] = t;
  solution[1][0] = q[0];
  solution[2][0] = q[1];

  k = 1;
  while (t <= tf) {
    let temporaryResult = [];
    temporaryResult = calculateIntegral(t,modelPendulum);
    t = temporaryResult[0];
    q = temporaryResult[1];
    solution[0][k] = t;
    solution[1][k] = q[0];
    solution[2][k] = q[1];

    k = k + 1
  }

  timeArr = solution[0];
  degreesArr = solution[1];
  omegaArr = solution[2];
  let counter = 0;
  let ifChaos = false;
  while (counter != degreesArr.length - 1 && ifChaos != true) {
    if (degreesArr[counter] > 13.5 || degreesArr[counter] < -13.5) {
      ifChaos = true;
    }
    counter++;
  }

  if (ifChaos == true) {
    degreesArr = degreesArr.map(val => val * 5.32);
    omegaArr = omegaArr.map(val => val * 23.32);
  } else {
    degreesArr = degreesArr.map(val => val * 35.32);
    omegaArr = omegaArr.map(val => val * 35.32);
  }
  return solution[1]
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/p5.js"></script>

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)