#!/usr/bin/env python # coding: utf-8 # $\renewcommand{\vec}[1]{\mathbf{#1}}$ # # # # # # # Roller Coaster # # ### Examples - Mechanics #
# By Jonas Tjemsland, Eilif Sommer Øyre and Jon Andreas Støvneng #
# Last edited: April 17th 2018 # ___ # # # ## Introduction # # In a series of laboratory sessions in a physics course at NTNU, the students are studying the rolling motion of objects on a curved track. Due to the curvature, the velocity and acceleration will vary. The students uses a high speed camera to record the motion and compare the results with numerical simulations. # # In this notebook, we will simulate a rolling ball on some arbitrary track in two dimensions by solving Newton's second law. The track is made by performing cubic spline interpolation on a set of points. The setup will be based on the setup used in the aforementioned laboratory sessions. For more information, see [1]. # First, we import neccesary library packages. # In[1]: import numpy as np import scipy.interpolate as interp import matplotlib.pyplot as plt plt.style.use('bmh') # Nicer looking plots # # # ## Theory # #### Equation of Motion # # An object, such as a ball, is rolling on a curved track. The rotation axis passes thorugh the center of mass. Let $m$ be the mass, $r$ be the radius of the ball and $I_n$ the moment of inertia. The forces on the center of mass is described by Newton's second law # # \begin{equation} # \mathbf F = m\mathbf a. # \end{equation} # # # The forces on the ball can be decomposed into a component parallel to the track and a component normal to the track, as shown in figure 1. Let the local slope of the track be described by an angle $\theta(x)$. The gravitational force acts on the center of mass vertically downwards. This amounts to a component parallel to the track $mg\sin \theta(x)$ and a normal component $mg\cos \theta(x)$. The forces from the track on the ball have a normal component $N$ (normal force) and the parallel component $f$ (friction). # # # # # **Figure 1:** *A cylinder or sphere is rolling on curve $y(x)$. The forces acting on the object are indicated on the figure. At each point $x$, the slope of the curve is defined by an angle $\theta(x)$.* # # # # Due to the curvature of the track, the center of mass has a centripetal acceleration normal to the track. The total acceleration is thus given by # # \begin{equation} # \mathbf a = \dot v \mathbf{e}_\parallel + \frac{v^2}{R(x)} \mathbf{e}_\perp, # \end{equation} # # where $R(x)$ is the *radius of the curvature* and $v$ is the speed (along the track). Thus, we obtain the two equations # # \begin{equation} # mg \sin \theta(x)-f=m\dot v, # \label{eq:parallel} # \end{equation} # \begin{equation} # N-mg\cos\theta(x) = \frac{mv^2}{R(x)}. # \label{eq:normal} # \end{equation} # # The first equation describes the motion parallel to the track, while the second equation yields, with $N\geq 0$, a condition for when the ball loses contact with the surface. # # An expression for the friction is found by using Newton's second law of rotation, # # \begin{equation} # \tau = I_0\omega. # \end{equation} # # The friction $f$ is the only force that do not pass through the rotation axis, and is thus the only force that contribute to the total torque $\tau$. The friction acts on the object a distance $r$ from the rotation axis (and at an angle $\pi/2$), so that $fr = I_0 \omega$. The ball is assumed to roll without any gliding. By using the rolling condition $v=\omega r$, equation \eqref{eq:parallel} can be written as # # \begin{equation} # \dot v = \frac{g\sin (\theta(x))}{1 + I_0/mr^2}. # \label{eq:vdot} # \end{equation} # # A complete discussion on rotational dynamics can be found in e.g. [chap. 9-10, 2] and [chap. 6, 3]. # #### Curvature # # Consider the curve from $y(x)$ from $A$ to $B$ as shown in figure 2. The curve from $A$ to $B$, with an arclenth $\Delta s$, can be considered as a small circle sector with angle $\Delta \theta$. The circle has a centre at $C$ and a radius $R\approx R_A\approx R_B$. The radius of the curvature thus becomes $R=\Delta s/\Delta \theta$. The *curvature* is in turn defined as $\kappa= 1/R$. # # # # **Figure 2:** *Sketch used to describe curvature and the radius of the curvature. (Taken from [1])* # # # Consider now a infinitesimal arclenth $\Delta s \to\text{d} s$. From the figure one can see that # # \begin{equation} # \text{d}y/\text{d} x = \tan\theta, # \label{eq:theta} # \end{equation} # # which gives # # $$\frac{\text{d} \theta}{\text{d}x} = \frac{\text{d}}{\text{d}x}\arctan\left(\frac{\text{d}y}{\text{d}x}\right)=\frac{1}{1+\left(\text d y/\text dx\right)^2}\frac{\text d^2 y}{\text dx^2}.$$ # # The differential $\text d s$ is given by # # $$(\text{d}s)^2 = (\text d x)^2 + (\text d y)^2 \quad \Longrightarrow \quad \text d s=\text d x\sqrt{1 + \left(\text d y/dx\right)^2}.$$ # # The curvature can thus be written as # # \begin{equation} # \kappa =\frac{\text{d}\theta}{\text{d}s} = \frac{\text{d}^2 y/\text{d}x^2}{\left(1 +\left(\text d y/\text d x\right)^2\right)^{3/2}}, # \end{equation} # # and the radius of the curvature becomes # # \begin{equation} # R(x) = \frac{\text{d}s}{\text{d}\theta} = \frac{\left(1 +\left(\text d y/\text d x\right)^2\right)^{3/2}}{\text{d}^2 y/\text{d}x^2}. # \label{eq:R} # \end{equation} # # Note that $\kappa$ is always finite as long as $\text d y/\text d x$ is continuous, while $R\to \infty$ if $\text d^2 y/\text d x^2\to 0$. # # Curvature is discussed in more detail in e.g. [chap. 11, 4] and [chap. 13, 5]. # ## Setup # # We will be considering a solid sphere (ball), which has a moment of inertia $I_0= 2mr^2/5$. The moment of inertia is easily computed for such objects, but this will not be discussed here. See e.g. [chap. 9, 2] for a general discussion and [6] for a list of some typical values for $I_0$. Assume that the ball has a radius $r=1\,\text{cm}$ and is made of iron with a density $\rho=7850\,\text{kg/m}^3$ (density found in [7]). The mass of the ball is in this case $m= 32.9\,\text{g}$ and the moment of inertia is thereby $I_0=13.2\,\mathrm{g\,cm^2}$. # In[2]: # Properties of the rolling object r = 0.01 # m (radius) rho = 7850 # kg/m^3 (density) g = 9.81 # m/s^2 (gravitational acceleration) m = (4/3)*np.pi*r**3*rho # kg (mass) c = 2/5 I0 = c*m*r**2 # kg m^2 (moment of inertia) # # **Figure 3:** *The actual setup used in the laboratory frame is shown to the left. A sketch is shown to the right. (Right image taken from [1])* # # # The track is made by a plastic bar which is attached to a solid frame at $N=8$ different mounts, as shown in figure 3. Let $(x_i, y_i)$ be the position of the $i$th mount. The $x$ positions are fixed and uniformly distributed across the frame. We will assume that the frame has a length $L=1.4\,\text{m}$, such that the distance between the mounts is $l = 20\,\text{cm}$. The $y$ positions can be changed. # In[3]: # Properties of the frame L = 1.4 # m (length) yi = [.5, .3, .25, .3, .35, .34, .25, .15] # m (y-positions) N = len(yi) # (# of mounts) xi = np.linspace(0, L, N) # m (x-positions) # The track can be described by a cubic spline that interpolates $(x_i, y_i)$. The cubic spline consists of a set of cubic polynomials that has continuous first and second derivaties at the interpolation points. See our notebook on [Cubic Splines](https://nbviewer.jupyter.org/urls/www.numfys.net/media/notebooks/cubic_splines.ipynb) for a general discussion. We will be using the function [`CubicSpline`](https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.CubicSpline.html#scipy.interpolate.CubicSpline) from `scipy.interpolate` to perform the interpolation. This creates a callable class, which we can treat as a function $y(x)$ that describes the track. # In[4]: # Callable class for the track curve get_y = interp.CubicSpline(xi, yi, bc_type="natural") # As we saw earlier, $\theta$ and $R$ depends on $\text dy/\text dx$ and $\text d^2y/\text dx^2$. The class `CubicSpline` has a function called `derivative` which returns a class `PPoly`, which essentially is the derivative of the spline. `PPoly` has also a function `derivative`. We can thus easily compute $\theta(x)$ and $R(x)$ by using equations \eqref{eq:theta} and \eqref{eq:R}. # In[5]: get_dydx = get_y.derivative() # Callable derivative of track get_d2ydx2 = get_dydx.derivative() # Callable double derivative of track def get_theta(x): """ Returns the angle of the track. """ return -np.arctan(get_dydx(x)) def get_R(x): """ Returns the radius of the curvature. """ return (1 + (get_dydx(x))**2)**1.5/get_d2ydx2(x) def get_curvature(x): """ Returns the curvature (1/R). """ return get_d2ydx2(x)/(1 + (get_dydx(x))**2)**1.5 # Let's plot the track! # In[6]: x = np.linspace(xi[0], xi[-1], 200) # Create figure fig, axarr = plt.subplots(3, 1, sharex=True, figsize=(12, 9), dpi=600) fig.subplots_adjust(hspace=0.02) # Axes 1: axarr[0].plot(x, get_y(x), 'C0', label=r"$y(x)$") axarr[0].plot(xi, yi, 'C1o', label="Mounts") axarr[0].set_ylabel(r"$y(x)$, [m]", size='15') #axarr[0].set_aspect('equal') # Axes 2: axarr[1].plot(x, get_theta(x), 'C0') axarr[1].set_ylabel(r"$\theta(x)$, [rad]", size='15') # Axes 2: axarr[2].plot(x, get_curvature(x), 'C0') axarr[2].set_ylabel(r"$\kappa(x)$, [1/m]", size='15') plt.show() # We are assuming that there is no loss of mechanical energy. Thus, if the highest point of the track is $y(x=0)$, then the ball will fall of to the right. If this is not the case, the ball will roll back and forth. # ## Numerics # # We start by assuming that the ball is always in contact with the track. Equation \eqref{eq:vdot} yields a coupled set of ordinary differential equations (ODE) # # $$\frac{\text dv}{\text dt} = \frac{g\sin (\theta(t))}{1 + I_0/mr^2},$$ # # where $v$ is the momentary velocity along the track. The $x$ position is in turn given by # # $$ \text dx = \text ds \cos(\theta)\: \Longrightarrow \: \frac{\text dx}{\text dt} = v\cos(\theta).$$ # In[7]: def get_vdot(theta): """ Returns the time derivative of the (total) velocity. """ return g*np.sin(theta)/(1 + c) def RHS(z): """ Evaluates the right hand side of the two coupled ODEs given in the text. Parameters: z : array-like, len(2), float. [v, x] The first element is the velocity, the second is the x-position. Returns: array-like, len(2), float. [a, v] The first element is the time derivative of the velocity (acceleration), the second is the time derivative of the position (velocity). """ # z = [x, v] # w = [vx, a] w = np.zeros(2) theta = get_theta(z[0]) w[0] = z[1]*1/np.sqrt(1+np.tan(theta)**2) w[1] = get_vdot(theta) return w # $v(t)$ and $x(t)$ can be found by applying a method for solving ODEs, such as an [Euler method](https://nbviewer.jupyter.org/urls/www.numfys.net/media/notebooks/eulers_method.ipynb), a [Runge-Kutta method](https://nbviewer.jupyter.org/urls/www.numfys.net/media/notebooks/runge_kutta_method.ipynb) or a more advanced [addative method](https://nbviewer.jupyter.org/urls/www.numfys.net/media/notebooks/adaptive_runge_kutta_methods.ipynb). We refer you to the respective modules or an example that solves an ODE at [numfys.net](https://www.numfys.net). In this notebook we will be using the 4th order Runge-Kutta method. # In[8]: def rk4step(f, y, h): """ Performs one step of the 4th order Runge-Kutta method. Parameters: f : Callable function with one input parameter. The right hand side of the ODE. Note that the RHS is in our case not a function of time. y : array-like, float. Current position. h : float. Time step. """ s1 = f(y) s2 = f(y + h*s1/2.0) s3 = f(y + h*s2/2.0) s4 = f(y + h*s3) return y + h/6.0*(s1 + 2.0*s2 + 2.0*s3 + s4) # We choose $x=0$ and $v=0$ as initial conditions, and a time-step $\Delta t = 10^{-3}\, \text{s}$. We have assumed that the ball do not loose any mechanical energy. One method of checking whether the numerical computation gives realistic results is to check if the total mechanical energy # # \begin{equation} # E = \frac{1}{2}mv^2 + mgh + \frac{1}{2}I_0 \omega^2 = \frac{1}{2}m(1 + c)v^2 + mgh, # \end{equation} # # is constant. # In[9]: dt = 1e-3 # s tstop = 5 # s. If the ball has not reached the end within 5 seconds, we stop. x0 = 0 # m. Initial x-position v0 = 0 # m/s. Inital velocity def get_K(v): """ Returns the kinetic energy. """ return .5*m*(1 + c)*v**2 def get_U(h): """ Returns the potential energy. """ return m*g*h # Everything is now set to roll the ball down the track. # In[10]: # Set initial values x = [x0] # x-position v = [v0] # velocity h = get_y(x0) # height K = [get_K(v0)] # kinetic energy U = [get_U(h)] # potential energy it = 0 # Iterator itmax = tstop/dt # Maximum number og iterations while x0 <= L and it < itmax: # Perform one step of the Runge-Kutta method [x0, v0] = rk4step(RHS, [x0, v0], dt) # Append different values to their arrays x = np.append(x, x0) v = np.append(v, v0) h = get_y(x0) K = np.append(K, get_K(v0)) U = np.append(U, get_U(h)) it += 1 print("Iterations: %i"%(it)) print("Final time: %.2f s"%(it*dt)) dE = (K[0] - K[-1] + U[0] - U[-1])/(K[0] + U[0]) print("Relative change in mechanical energy: %.2e"%(dE)) # The relative change in the mechanical energy was minimal, which means that the time step was more than small enough. The ball used $1.07\,\text{s}$ to reach the end of the track. # # Let's plot the computed quantities! # In[11]: t = np.linspace(0, it*dt, it + 1) # Create figure fig, axarr = plt.subplots(3, 1, sharex=True, figsize=(10, 8), dpi=400) fig.subplots_adjust(hspace=0.02) # Axes 1: axarr[0].plot(t, x) axarr[0].set_ylabel(r"$x(t)$, [m]") # Axes 2: axarr[1].plot(t, v) axarr[1].set_ylabel(r"$v(t)$, [m/s]") # Axes 2: axarr[2].plot(t, U, label="Potential energy, $U$") axarr[2].plot(t, K, label="Kinetic energy, $K$") axarr[2].plot(t, K + U, label="Mechanical energy, $E=U+K$") axarr[2].set_ylabel(r"Energy, [J]") axarr[2].set_xlabel(r'$t$, [s]') axarr[2].legend() plt.show() # Equation \eqref{eq:normal} gives a condition for when the ball looses its contact with the track: # # $$g\cos\theta\left(x\right) + v^2\kappa\left(x\right) \leq 0.$$ # # Let's check if the ball lost contact in the above calculation. # In[12]: theta = get_theta(x) kappa = get_curvature(x) N = g*np.cos(theta) + v**2*kappa index = np.argmax(N < 0) if index <= 0: print("The ball did not loose contact.") else: print("The ball lost contact at x = %.2f m."%(x[index])) # We can also check whether the ball begins to slide or not. The maximum static friction force is proportional to the normal force: # # \begin{equation} # f_\mathrm{max} = \mu_s N. # \end{equation} # # $\mu_s$ is called the static friction coefficient. The static friction coefficient between plastic and metal is $0.25<\mu_s<0.4$ [8]. The normal force can be calculated using equation \eqref{eq:normal}. If the gravitational pull along the track exceeds the maximum static friction, $mg\sin\theta>f_\mathrm{max}$, the ball begins to glide. # In[13]: mu = 0.25 f = mu*N Fgx = m*g*np.sin(theta) index = np.argmax(f < Fgx) if index <= 1: print("The ball did not glide.") else: print("The ball began to glide at x = %.2f m."%(x[index])) # ## Animation # In[14]: y = get_y(x) from matplotlib import animation from IPython.display import HTML plt.style.use('default') # Set up the figure fig = plt.figure(figsize=(8, 4), dpi=120) ax = plt.axes(xlim=(xi[0], xi[-1]), ylim=(np.min(yi), np.max(yi))) ax.set_aspect('equal') # Define the different elements in the animation track, = ax.plot(x,y, "k") obj = plt.Circle((x[0], y[0]), r, fc="grey") # Obj. 1 ax.add_patch(obj) ax.set_xlabel(r"$x$, [m]") ax.set_ylabel(r"$y$, [m]") props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) textbox = ax.text(0.775, 0.95, '', transform=ax.transAxes, fontsize=12, verticalalignment='top', bbox=props) # Calculates the number of frames FPS = 30 n = len(t) framesNum = int(FPS*t[-1]) # Animation function. This is called sequentially. def animate(j): i = j*int(n/framesNum) theta = get_theta(x[i]) obj.center = (x[i] + r*np.sin(theta), y[i] + r*np.cos(theta)) t_str = r'$t = %.2f$ ' % (t[i]) x_str = r'$x = %.2f$ ' % (x[i]) y_str = r'$y = %.2f$ ' % (y[i]) textbox.set_text(t_str + "\n" + x_str + "\n" + y_str) # Create animation anim = animation.FuncAnimation(fig, animate, frames=framesNum, interval=1000/FPS) plt.close(anim._fig) # Display the animation HTML(anim.to_html5_video()) # ## Further Work # # - If the ball slips, will it reach the end faster or slower? # - How can we consider slipping? # ## Resources # # [1] NTNU fysikklab: http://home.phys.ntnu.no/brukdef/undervisning/fyslab/. In particular, the assignment text is found at http://home.phys.ntnu.no/brukdef/undervisning/fyslab/files/oppgave2.pdf. # # [2] H.D. Young and R.A. Freedman: *University Physics*. Pearson Education, 14th edition, 2016 # # [3] J.R. Lien and G. Løvhøiden: *Generell fysikk for universiteter og høgskoler*. Bind 1, Universitetsforlaget, 2001 # # [4] R. A. Adams, C. Essex: *Calculus a Complete Course*. Pearson, 8th edition, 2013 # # [5] J. Hass, M.D. Weir og G.B. Thomas Jr.: *Thomas’ Calculus*. Pearson Education, 13th edition, 2014 # # [6] Wikipedia: *List of moments of inertia*. https://en.wikipedia.org/wiki/List_of_moments_of_inertia. 2017 (aquired 31th November 2017) # # [7] The Engineering ToolBox: *Metals and Alloys - Densities*. https://www.engineeringtoolbox.com/metal-alloys-densities-d_50.html. (aquired 31th November 2017) # # [8] Coefficient of friction: http://www.tribology-abc.com/abc/cof.htm