UP | HOME

Date: [2020-10-18 Sun]

Coupled Pendulum

Table of Contents

When two simple pendulum are connected by a string, and one is moved, then gradually the osciallations are transferred from the first pendulum to the second (with the first one coming to a full stop) and then it transfers back from second to first, …

I came across a interesting this interesting fact, and then tried deriving it mathematically as well as by writing a simulation.

1. Simulation

1.2. Code

< Collapse code block> Expand code block
(ql:quickload :lispbuilder-sdl)
(defpackage :pendulum-coupled
  (:use :cl))

(in-package :pendulum-coupled)

(defstruct (state :conc-name) 
  (x1 0.0d0)
  (v1 0.0d0)
  (θ1 0.0d0)
  (ω1 0.0d0)
  (x2 0.0d0)
  (v2 0.0d0)
  (θ2 0.0d0)
  (ω2 0.0d0))

(defstruct (configuration :conc-name )
  (l 0.1d0)
  (m0 1d0)
  (s 0.15d0)
  (h 0.05d0)
  (m 3d0)
  (g 9.81d0))

(defparameter *config* (make-configuration))

(defun Tsinθ (y s)
  (* -35d4 y (- 1 (/ (sqrt (+ 1 (expt (/ y s) 2)))))))

(defun support-force (mex otherx)
  "force at support i.e. f"
  (+ (Tsinθ mex (h *config*))
     (Tsinθ (- mex otherx) (s *config*))))

(defun a-α% (ω θ support-force)
  "calculate a i.e. d²x/dt² and α i.e. d²θ/dt² for a single pendulum"
  (let ((c₁ (+ (/ support-force (m *config*) (l *config*))
               (* (expt ω 2) (sin θ))))
        (c₂ (* -1 (g *config*) (sin θ) (/ (l *config*))))
        (det (/ (+ 1 
                   (/ (m0 *config*) (m *config*)) 
                   (- (cos θ)))
                (l *config*))))
    (values (/ (- c₁ (* c₂ (cos θ))) det)
            (/ (- (* c₂ (+ 1 (/ (m0 *config*) (m *config*))))
                  c₁)
               (* det (l *config*))))))

(defun a-α (state)
  (multiple-value-bind (a1 α1) (a-α% (ω1 state) (θ1 state) 
                                     (support-force (x1 state) (x2 state)))
    (multiple-value-bind (a2 α2) (a-α% (ω2 state) (θ2 state) 
                                       (support-force (x2 state) (x1 state)))
      (values a1 a2 α1 α2))))

(defun dS/dt (state) 
  "State = (θ x ω v)"
  (multiple-value-bind (a1 a2 α1 α2) (a-α state)
    (make-state :θ1 (ω1 state)  :θ2 (ω2 state) 
                :x1 (v1 state) :x2 (v2 state)
                :ω1 α1  :ω2 α2
                :v1 a1 :v2 a2)))

(defun state+  (state1 state2)
  (make-state :x1 (+ (x1 state1) (x1 state2))
              :v1 (+ (v1 state1) (v1 state2))
              :θ1 (+ (θ1 state1) (θ1 state2))
              :ω1 (+ (ω1 state1) (ω1 state2))
              :x2 (+ (x2 state1) (x2 state2))
              :v2 (+ (v2 state1) (v2 state2))
              :θ2 (+ (θ2 state1) (θ2 state2))
              :ω2 (+ (ω2 state1) (ω2 state2))))

(defun state* (k state)
  (make-state :x1 (* k (x1 state))
              :v1 (* k (v1 state))
              :θ1 (* k (θ1 state))
              :ω1 (* k (ω1 state))
              :x2 (* k (x2 state))
              :v2 (* k (v2 state))
              :θ2 (* k (θ2 state))
              :ω2 (* k (ω2 state))))

(defun euler-forward (state Δt) 
  (state+ (state* Δt (dS/dt state))
          state))

(defun rk4 (state Δt)
  (let* ((k₁ (dS/dt state))
         (k₂ (dS/dt (state+ state (state* (/ Δt 2) k₁))))
         (k₃ (dS/dt (state+ state (state* (/ Δt 2) k₂))))
         (k₄ (dS/dt (state+ state (state* Δt k₃))))
         (k (state* (/ Δt 6) (reduce #'state+ 
                                     (list k₁ 
                                           (state* 2 k₂)
                                           (state* 2 k₃)
                                           k₄)))))
    (state+ state k)))

(defun physics-update (state Δt)
  "We update the state using RK4. We could also use euler forward but that would be much inaccurate"
  (rk4 state Δt))

(defun speedup (dt)
  (* 0.02 dt))

(defun create-edges (state) 
  (let ((a (vector 0 0 (+ (h *config*))))
        (b (vector 0 (x1 state) 0))
        (c (vector (s *config*) (x2 state) 0))
        (d (vector (s *config*) 0 (+ (h *config*)))))
    (list (list a b)
          (list b 
                (vector 0 
                        (+ (x1 state) (* (l *config*) (sin (θ1 state)))) 
                        (* -1 (l *config*) (cos (θ1 state)))))
          (list b c)
          (list c
                (vector (s *config*)
                        (+ (x2 state) (* (l *config*) (sin (θ2 state)))) 
                        (* -1 (l *config*) (cos (θ2 state)))))
          (list c d))))


(defun main3d () 
  (let ((state (make-state :θ1 0.3d0))
        (t1 (get-internal-real-time)) t2
        (loops 1))
    (setf 3d::*scaling* #2A((3418.224 0.0 0.0) (0.0 3418.224 0.0) (0.0 0.0 3418.224)))
    (setf 3d::*translation* #(240 -250 0))
    (3d::main-loop 
     :update-function (lambda () 
                        (setf t2 (get-internal-real-time))
                        (let ((dt (/ (- t2 t1) internal-time-units-per-second)))
                          (loop repeat loops do 
                            (setf state (physics-update state (speedup dt)))))
                        (setf t1 t2)
                        (setf 3d::*edges* (create-edges state)))
     :drawing-function (lambda () 
                         (sdl:draw-string-solid-* (format nil "~dx" loops) 50 50)
                         (sdl:draw-circle (3d:process-coordinate 
                                           (vector 0 
                                                   (+ (x1 state) (* (l *config*) (sin (θ1 state)))) 
                                                   (* -1 (l *config*) (cos (θ1 state)))))
                                          5)
                         (sdl:draw-circle (3d:process-coordinate 
                                           (vector (s *config*)
                                                   (+ (x2 state) (* (l *config*) (sin (θ2 state)))) 
                                                   (* -1 (l *config*) (cos (θ2 state)))))
                                          5))
     :keypress-function (lambda (key) 
                          (case key 
                            (:sdl-key-kp-plus (setf loops (* loops 2)))
                            (:sdl-key-kp-minus (setf loops (ceiling (/ loops 2)))))))))

2. Derivation

IMG_20201017_223819.jpg IMG_20201017_223857.jpg IMG_20201017_223918.jpg


You can send your feedback, queries here