{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "

Interactive Plotting of the Free Vibration of a
Mass-Spring-Damper System

\n", "

MCHE 485: Mechanical Vibrations

\n", "

Dr. Joshua Vaughan
\n", "joshua.vaughan@louisiana.edu
\n", "http://www.ucs.louisiana.edu/~jev9637/

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

\n", "\t\"A
\n", " Figure 1: A Mass-Spring-Damper System \n", "

\n", "\n", "This notebook simluates the free vibration of a simple mass-spring-damper system like the one shown in Figure 1. More specifically, we'll look at how the system response to non-zero initial conditions. The interactive widget allows us to explore how the response changes as the natural frequency and damping ratio changes.\n", "\n", "The equation of motion for the system is:\n", "\n", "$ \\quad m \\ddot{x} + c \\dot{x} + kx = 0 $\n", "\n", "We could also write this equation in terms of the damping ratio, $\\zeta$, and natural frequency $\\omega_n$.\n", "\n", "$ \\quad \\ddot{x} + 2\\zeta\\omega_n\\dot{x} + \\omega_n^2x = 0$\n", "\n", "For information on how to obtain this equation, you can see the lectures at the [class website](http://www.ucs.louisiana.edu/~jev9637/MCHE485.html)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll use the solution to the differential equation that we developed in class to plot the response. The solution for the underdamped case is:\n", "\n", "$ \\quad x(t) = e^{-\\zeta\\omega_nt}\\left(a_1 e^{i \\omega_d t} + a_2 e ^{-i \\omega_d t}\\right) $ \n", "\n", "*or*\n", "\n", "$ \\quad x(t) = e^{-\\zeta\\omega_nt}\\left(b_1 \\cos{\\omega_d t} + b_2 \\sin{\\omega_d t}\\right) $\n", "\n", "To use this equation, we need to solve for $a_1$ and $a_2$ or $b_1$ and $b_2$ using the initial conditions. Here, let's use the sin/cosine form. Solving the equation for generic intial velocity, $\\dot{x} = v_0$, and a generic initial displacement, $x = x_0$, we find:\n", "\n", "$ \\quad x(t) = e^{-\\zeta\\omega_nt}\\left(x_0 \\cos{\\omega_d t} + \\frac{\\zeta \\omega_n x_0 + v_0}{\\omega_d} \\sin{\\omega_d t}\\right) $" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import numpy as np # Grab all of the NumPy functions with \"nickname\" np" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# We want our plots to be displayed inline, not in a separate window\n", "%matplotlib inline \n", "\n", "# Import the plotting functions\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Set up simulation parameters\n", "t = np.linspace(0, 5, 501) # Time for simulation, 0-5s with 500 points in-between\n", "\n", "# Define the initial conditions x(0) = 1 and x_dot(0) = 0 \n", "x0 = [1.0, 0.0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Interactive Simluation\n", "Let's use the Jupyter Notebook's relatively-new interactive widgets. We'll set up sliders for natural frequecny (in Hz) and damping ratio. The response will update as you move the sliders. There may be some flickering during the first time you change the sliders. \n", "\n", "The interactive portion will not run in the online notebook viewer, so you'll have to run it locally to play with the interactive part. The .gif in Figure 2 shows what the resulting interactive plot should look like after you run the next cell locally.\n", "

\n", "\t\"An\n", " Figure 2: The Interactive Mass-Spring-Damper Response \n", "

\n", "
" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "a334f8474bc841259f552e31721e4bf1", "version_major": 2, "version_minor": 0 }, "text/html": [ "

Failed to display Jupyter Widget of type interactive.

\n", "

\n", " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n", " that the widgets JavaScript is still loading. If this message persists, it\n", " likely means that the widgets JavaScript library is either not installed or\n", " not enabled. See the Jupyter\n", " Widgets Documentation for setup instructions.\n", "

\n", "

\n", " If you're reading this message in another frontend (for example, a static\n", " rendering on GitHub or NBViewer),\n", " it may mean that your frontend doesn't currently support widgets.\n", "

\n" ], "text/plain": [ "interactive(children=(FloatSlider(value=1.0, description='f', max=1.8, min=0.2, step=0.01), FloatSlider(value=0.05, description='z', max=0.9, step=0.01), Output()), _dom_classes=('widget-interact',))" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# import the IPython widgets\n", "from ipywidgets import interact\n", "import ipywidgets as widgets\n", "\n", "# Set up the function that plots the repsonse based on slider changes\n", "def plot_response(f = 1.0, z = 0.05):\n", " # Make the figure pretty, then plot the results\n", " # \"pretty\" parameters selected based on pdf output, not screen output\n", " # Many of these setting could also be made default by the .matplotlibrc file\n", " fig = plt.figure(figsize=(9, 6))\n", " ax = plt.gca()\n", " plt.subplots_adjust(bottom=0.17, left=0.17, top=0.96, right=0.96)\n", " plt.setp(ax.get_ymajorticklabels(), family='serif', fontsize=18)\n", " plt.setp(ax.get_xmajorticklabels(), family='serif', fontsize=18)\n", " ax.spines['right'].set_color('none')\n", " ax.spines['top'].set_color('none')\n", " ax.xaxis.set_ticks_position('bottom')\n", " ax.yaxis.set_ticks_position('left')\n", " ax.grid(True, linestyle=':', color='0.75')\n", " ax.set_axisbelow(True)\n", " \n", " wn = 2 * np.pi * f\n", " wd = wn * np.sqrt(1 - z**2)\n", " \n", " # Define x(t)\n", " x = np.exp(-z * wn * t) * (x0[0] * np.cos(wd*t) + (z * wn * x0[0] + x0[1])/wd * np.sin(wd*t))\n", " \n", " plt.plot(t, x, linewidth=2)\n", " plt.xlabel('Time (s)',family='serif', fontsize=22, weight='bold', labelpad=5)\n", " plt.ylabel('Position (m)',family='serif', fontsize=22, weight='bold', labelpad=10)\n", " plt.ylim(-1.1, 1.1)\n", " plt.xlim(0, 5)\n", " \n", "\n", "# Call the slider interaction\n", "# f is changes in frequency, allowing between 0.2 and 1.8Hz at 0.1Hz increments\n", "# z is damping ratio, allowing between 0 and 0.9 and 0.05 increments\n", "interact(plot_response, f=(0.2, 1.8, 0.01), z = (0, 0.9, 0.01));" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Licenses\n", "Code is licensed under a 3-clause BSD style license. See the licenses/LICENSE.md file.\n", "\n", "Other content is provided under a [Creative Commons Attribution-NonCommercial 4.0 International License](http://creativecommons.org/licenses/by-nc/4.0/), CC-BY-NC 4.0." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# This cell will just improve the styling of the notebook\n", "from IPython.core.display import HTML\n", "import urllib.request\n", "response = urllib.request.urlopen(\"https://cl.ly/1B1y452Z1d35\")\n", "HTML(response.read().decode(\"utf-8\"))" ] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.2" } }, "nbformat": 4, "nbformat_minor": 1 }