[Pendel-Simulation] Was mache ich falsch?

Es gibt 3 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    [Pendel-Simulation] Was mache ich falsch?

    Hallo,

    ich versuche die Dynamiken eines simplen Pendels mittels RK4/Euler-Verfahren zu simulieren (wie es analytisch geht, ist mir relativ bekannt).

    Das Vektor-Diagramm sollte doch eigentlich so aussehen:



    Erklärung: Auf den "Bob"(= Kugel) wirken sowohl die Gravitations -als auch "Zug"kraft vom "Bob" zum Seil.
    Der Winkel "theta" ist nichts anderes als das Skalarprodukt der normierten Vektoren g und d.

    Man stelle sich vor der Bob befinde sich in den Extremstellen (genau dann wenn exemplarisch die Y-Koordinate des Bobs und des Zentrums übereinstimmen).
    Das die Formel (a = g + (-d * |g| * theta)) Sinn macht ist offensichtlich: Wenn der Bob sich in jener Extremstelle befindet, liegen die Vektoren g und d orthogonal zueinander:
    Das Skalarprodukt ist also 0.

    Setzt man das in die Formel ein
    a = g + (-d * 0 * theta) = g dann wirkt in jener Extremstelle ausschließlich die Gravitationskraft, was ja auch richtig sein sollte.

    Dennoch "reißt" der Bob in der Simulation einfach ab... Habe es mit verschiedenen Delta-Werten versucht: Stets ist die Geschwindigkeit in Y Richtung einfach zu hoch, sodass der Bob gar nicht hochgezogen wird.
    Ich mache also irgendwo was falsch.

    Wo und vor allem was?

    Hier der Source-Code:

    Die Form:
    Spoiler anzeigen

    C#-Quellcode

    1. public partial class Form1 : Form
    2. {
    3. public Form1()
    4. {
    5. InitializeComponent();
    6. }
    7. Vector2 center = new Vector2(500, 250);
    8. Vector2 bobPosition = new Vector2(550, 250);
    9. Vector2 bobVelocity = new Vector2(0, 0);
    10. IApproximation positionIntegrator;
    11. IApproximation velocityIntegrator;
    12. Vector2 gravitationalForce = new Vector2(0, 10);
    13. private void Form1_Load(object sender, EventArgs e)
    14. {
    15. this.Size = new Size(1000, 1000);
    16. this.DoubleBuffered = true;
    17. this.Paint += new PaintEventHandler(draw);
    18. Application.Idle += new EventHandler((a, b) =>
    19. {
    20. bobPosition = positionIntegrator.Run();
    21. Console.WriteLine(velocityIntegrator.Output);
    22. this.Invalidate();
    23. });
    24. positionIntegrator = new RK4(bobPosition, 0.005f);
    25. velocityIntegrator = new RK4(bobVelocity, 0.005f);
    26. positionIntegrator.f = pPrime;
    27. velocityIntegrator.f = pPrimePrime;
    28. }
    29. Vector2 pPrime(float dt, Vector2 y)
    30. {
    31. return velocityIntegrator.Run();
    32. }
    33. Vector2 pPrimePrime(float dt, Vector2 y)
    34. {
    35. Vector2 dir = bobPosition - center;
    36. dir.Normalize();
    37. Vector2 force = new Vector2(gravitationalForce.X, gravitationalForce.Y);
    38. force.Normalize();
    39. float forceMagnitude = gravitationalForce.Length();
    40. float theta = Vector2.Dot(dir, force);
    41. dir = -dir;
    42. Vector2 acceleration = gravitationalForce + dir * theta * (forceMagnitude);
    43. return acceleration;
    44. }
    45. private void draw( object sender, PaintEventArgs e)
    46. {
    47. Graphics g = e.Graphics;
    48. g.FillEllipse(Brushes.Red, center.X - 25f, center.Y - 25f, 50, 50);
    49. g.FillEllipse(Brushes.Blue, bobPosition.X - 12.5f, bobPosition.Y - 12.5f, 25, 25);
    50. g.DrawLine(Pens.Black, center.X, center.Y, bobPosition.X, bobPosition.Y);
    51. }
    52. }



    Hier die numerische Integrator-Klasse:

    IApproximation:
    Spoiler anzeigen

    C#-Quellcode

    1. public abstract class IApproximation
    2. {
    3. public delegate Vector2 f_delegate(float x, Vector2 y);
    4. public f_delegate f { get; set; }
    5. public float Delta { get; set; }
    6. public float Time { get; set; }
    7. public Vector2 Output { get; set; }
    8. public abstract Vector2 Run();
    9. }


    RK4:
    Spoiler anzeigen

    C#-Quellcode

    1. public class RK4 : IApproximation
    2. {
    3. public RK4(Vector2 _0, float delta)
    4. {
    5. Output = _0;
    6. Delta = delta;
    7. }
    8. public override Vector2 Run()
    9. {
    10. Vector2 k1 = Delta * f(Time, Output);
    11. Vector2 k2 = Delta * f(Time + .5f * Delta, Output + .5f * k1);
    12. Vector2 k3 = Delta * f(Time + .5f * Delta, Output + .5f * k2);
    13. Vector2 k4 = Delta * f(Time + Delta, Output + k3);
    14. Output += 1.0f / 6.0f * (k1 + 2 * k2 + 2 * k3 + k4);
    15. Time += Delta;
    16. return Output;
    17. }
    18. }



    Liebe Grüße!

    EDIT: Okay, wenigstens eine kleine Besserung.
    Benutzt man stattdessen folgende Formel:

    a = (1 - |theta|) * g + (-d * |g| * |theta|) reißt er wenigstens nicht ab.
    Eine Pendel-Simulation ist das dennoch nicht, eher eine "Sprungfeder" -Simulation...

    _
    Und Gott alleine weiß alles am allerbesten und besser.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „φConst“ ()

    @φConst Ich hab Deinen Code in ein neues Projekt gepackt und Vector2 durch meine eigene Vektor-Klasse ersetzt.
    Es pendelt permanent, aber der "Faden" wird immer länger.
    Spoiler anzeigen

    C#-Quellcode

    1. using System;
    2. namespace WindowsFormsApplication1
    3. {
    4. public class Vector2
    5. {
    6. public float X { get; set; }
    7. public float Y { get; set; }
    8. public Vector2(Vector2 vector)
    9. {
    10. this.X = vector.X;
    11. this.Y = vector.Y;
    12. }
    13. public Vector2(float x, float y)
    14. {
    15. this.X = x;
    16. this.Y = y;
    17. }
    18. public Vector2()
    19. {
    20. this.X = 0;
    21. this.Y = 0;
    22. }
    23. public static Vector2 operator *(float factor, Vector2 vector)
    24. {
    25. Vector2 v = new Vector2(vector);
    26. v.X *= factor;
    27. v.Y *= factor;
    28. return v;
    29. }
    30. public static Vector2 operator *(Vector2 vector, float factor)
    31. {
    32. Vector2 v = new Vector2(vector);
    33. v.X *= factor;
    34. v.Y *= factor;
    35. return v;
    36. }
    37. public static Vector2 operator +(Vector2 v1, Vector2 v2)
    38. {
    39. Vector2 v = new Vector2(v1);
    40. v.X += v2.X;
    41. v.Y += v2.Y;
    42. return v;
    43. }
    44. public static Vector2 operator -(Vector2 v1, Vector2 v2)
    45. {
    46. Vector2 v = new Vector2(v1);
    47. v.X -= v2.X;
    48. v.Y -= v2.Y;
    49. return v;
    50. }
    51. public static Vector2 operator -(Vector2 v1)
    52. {
    53. return new Vector2(-v1.X, -v1.Y);
    54. }
    55. /// <summary>
    56. /// Vektor dividiert durch Skalar
    57. /// </summary>
    58. /// <param name="d"></param>
    59. /// <param name="v"></param>
    60. /// <returns></returns>
    61. public static Vector2 operator /(float d, Vector2 v)
    62. {
    63. return new Vector2(v.X / d, v.Y / d);
    64. }
    65. /// <summary>
    66. /// Vektor dividiert durch Skalar
    67. /// </summary>
    68. /// <param name="v"></param>
    69. /// <param name="d"></param>
    70. /// <returns></returns>
    71. public static Vector2 operator /(Vector2 v, float d)
    72. {
    73. return new Vector2(v.X / d, v.Y / d);
    74. }
    75. /// <summary>
    76. /// Berechnung der Länge des Vektors
    77. /// </summary>
    78. /// <returns>die Länge</returns>
    79. public float Length()
    80. {
    81. return (float)(Math.Sqrt(this.X * this.X + this.Y * this.Y));
    82. }
    83. internal void Normalize()
    84. {
    85. float bet = this.Length();
    86. this.X /= this.Length();
    87. this.Y /= this.Length();
    88. }
    89. /// <summary>
    90. /// Skalarprodukt
    91. /// </summary>
    92. /// <param name="v1"></param>
    93. /// <param name="v2"></param>
    94. /// <returns></returns>
    95. internal static float Dot(Vector2 v1, Vector2 v2)
    96. {
    97. return v1.X * v2.X + v1.Y * v2.Y;
    98. }
    99. }
    100. }
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Es pendelt permanent, aber der "Faden" wird immer länger.


    Eben, genau das ist das Problem.. ^^
    Ich habe mir mal die Werte des Beschleunigungsvektors ausgegeben: Sind - ohne den "Fix" im Edit - stets positiv: Genau das ist ja falsch.
    Aber wie sonst würde man denn den Beschleunigungsvektor konstruieren?
    Und Gott alleine weiß alles am allerbesten und besser.
    @φConst Ohne jetzt alles nachvollziehen zu wollen, schau mal hier rein:
    Runge Kutta Verfahren 4. Ordnung
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!