andrewandrade.ca/blogandrewandrade.ca/blog - Andrew Andrade
https://mrandrewandrade.github.io/blog
Sun, 15 Sep 2024 19:45:10 +0000Sun, 15 Sep 2024 19:45:10 +000060Generalized Solution to Multi-Echelon Inventory Decisions<p>This post outlines the generalized solution to Multi-Echelon Inventory Decisions by finding the lower, upper and average bound for an optimal solution. It then outlines a genetic algorithm approach to finding the optimal solution. The generalized solution procedure is applied to the <a href="http://ptgmedia.pearsoncmg.com/images/9780133757880/samplepages/0133757889.pdf">Multi-Echelon Inventory
Decisions at Jefferson
Plumbing Supplies
To Store or Not to Store?</a> Case Study</p>
<h1 id="introduction">Introduction:</h1>
<p>Alex, a NASCAR enthusiast, is the Inventory Manager of Jefferson
Plumbing Supplies, and he faces an interesting quandary. He has been
notified by one of his suppliers that there is an upcoming increase to
their minimum order quantity. The following case study summary presents
a description, a system model, and a summary and conclusion for the
problem Alex faces.</p>
<h1 id="problem">Problem:</h1>
<p>Jefferson Plumbing has know demand of 100 units annually, from loyal
customers, of a specialty faucet that is ordered from a long time
supplier. Alex received a letter from the supplier informing him that
they would be increasing their minimum order quantity. The new minimum
order quantity is triple what would normally be ordered. Due to their
storefront’s city location, inventory holding costs for the faucet are
expensive. Jefferson Plumbing Supplies has a warehouse with a cheaper
annual holding cost, and Alex would like to determine the optimum costs
previous to, and after the policy change.</p>
<h2 id="options">Options:</h2>
<p>1. convince the company to maintain relations to drop the minimum order
amount</p>
<p>2. not stock the item -> lost revenue and customers</p>
<p>3. order and store in the store</p>
<p>4. order and store in the storage facility (multi-echelon system)</p>
<p>5. find alternative storage</p>
<p>Given that option 1, 2, and 5 are not feasible there is option 3 (the
base case) and option 4.</p>
<h2 id="variables">Variables</h2>
<p>Holding cost h <span>[</span>$ unit/time<span>]</span><br />
Stockout Penalty p <span>[</span>$/unit/time <span>]</span><br />
Fixed Cost k <span>[</span>$/order <span>]</span><br />
Purchase cost c <span>[</span>$/ unit <span>]</span><br />
Demand λ <span>[</span>units/time <span>]</span><br />
Order quantity Q <span>[</span>units <span>]</span><br />
Optimal order quantity Q* <span>[</span>units<span>]</span><br />
Re-order interval u <span>[</span> time<span>]</span><br />
Average Annual Cost C(Q) <span>[</span>$/year<span>]</span><br />
Optimal order quantity C* =C(Q*) <span>[</span>units<span>]</span>\</p>
<h2 id="assumptions">Assumptions:</h2>
<p>Assumption given in case study:\</p>
<ol>
<li>Continuous deterministic demand of λ = 100 units/year</li>
</ol>
<p>2. The cost of not having inventory is very high (lost of profit and
lost of customers and future profit)</p>
<p>3. Minimum order quantity \(Q_{min} = 130\) units, previous order quantity
was about 130/3 units 4. No capacity limits 5. Must always have positive
inventory (no backorders) based on assumption 2</p>
<p>Assumptions taken to relax problem:\</p>
<ol>
<li>No lead time</li>
</ol>
<p>2. Purchase cost c is ignored in optimization as it remains constant</p>
<p>3. The holding cost includes the cost of held capital (no interest rate)</p>
<h2 id="givens">Givens:</h2>
<p>Purchase Fixed ording cost \(k_p = \$100/order\)</p>
<p>Warehouse ordering cost \(k_w = \$50/order\)</p>
<p>Store holding cost \(h_s = \$10/unit/year\)</p>
<p>Warehouse hoding cost \(h_w = \$1/unit/year\)</p>
<h1 id="system-model">System model:</h1>
<h2 id="base-case-deterministic-economic-order-quantity-model">Base Case: Deterministic Economic Order Quantity Model</h2>
<p>In the base case, the system can be modeled as a simple single order
system where the quantity is stored at the retail store.</p>
<p>Manufacturer -> Retailer</p>
<p>Using a basic economic order quantity (EOQ) model and the assumptions
previously made, the yearly cost of the base case would be:</p>
\[C(Q) = \frac{k_p \lambda}{Q} + \frac{h_s Q}{2}\]
<p>Given the first order condition:</p>
\[\frac{\partial C} {\partial Q} = - \frac{k_p \lambda}{Q^2} + \frac{h_s}{2} = 0\]
<p>Given this is a local minimum, the optimal quantity would be:</p>
\[Q^* = \sqrt{\frac{2 k_p \lambda}{h_s}} = 44.72135955 = 45 units\]
<p>With the optimal annual cost would be:</p>
\[C^* = C(Q^*) = \sqrt{2 \lambda k_p h_s} \cong \$447.21\]
<p>Since the minimum order quantity \(Q_{min} \ge 130\) as stated in the
problem and the actual optimal annual cost would be:</p>
\[C(Q_{min}) = \frac{k_p \lambda}{Q_{min}} + \frac{h_s Q_{min}}{2} \cong \$726\]
<p>The annual cost is convex optimum, therefore the smallest minimum order
quantity would be optimal in this model.</p>
<h2 id="two-stage-deterministic-multi-echelon-serial-system">Two stage deterministic multi-echelon serial system</h2>
<p>An alternative system model would be two stage: after an order is placed
from the manufacturer it would be stored at a lower cost in the
warehouse and be shipped to the retailer. In shematic form:</p>
<p>Manufacturer -> Warehouse -> Retailer</p>
<p>Each stage functions like an EOQ system and determines the optimal order
quantity and period. Solution will have a zero inventory ordering
(ordering will occur when inventory is zero) assuming no lead time.</p>
<p>Since the minimum order quantity is greater than yearly demand,
therefore there will only be a maximum of 1 order within a year from the
manufacturer. The initial assumption will be made that it is cheaper to
store inventory at the warehouse and order parts when required. This
assumption will be later verified by comparing the total cost to the
base case.</p>
<p>We are going to introduce the reorder interval \(\textbf{u} = Q /
\lambda\), and can be thought of as a vector for each stage (j) of the
ordering process. In our case, \(u_w\) is the ordering period made <strong>at</strong>
the warehouse <strong>from the store</strong> and \(u_p\) is the ordering period <strong>at
the production factory</strong> from the warehouse.</p>
<p>The averaged annual cost function (to be minimized) of the most ideal
function would be the following:</p>
\[C (\textbf{u}) = \sum_{j} \left(\frac{k_j}{u_j}+ \frac{h_j \lambda u_j}{2}\right) = \frac{k_w}{u_w}+ \frac{(h_s -h_w)\lambda u_w}{2} + \frac{k_p}{u_p}+ \frac{h_w \lambda u_p}{2}\]
<p>Such that:</p>
\[u_j = \theta_j u_{j+1},\]
\[u_j \ge 0,\]
\[\theta_j \in \mathbb{Z}^+ = \{1, 2, 3, ...\}\]
<p>There is also the added constraint due to the minimum quantity order for
$u_p$</p>
\[u_p \ge \frac{Q_{min}}{\lambda} = \frac{13}{10}\]
<p>Solving this optimization problem for the optimum reorder interval
\(\textbf{u*}\) leads to a non-convex mixed integer non-linear
programming. Since the constrained solution is non-convex, it does not
have a guaranteed optimal solution except in limit. For this reason, a
generalized approach to finding the lower, upper and mean average annual
cost must be used.</p>
<h2 id="generalized-solution">Generalized Solution:</h2>
<p>The generalized solution to an non-convex, mixed interger non-linear
programming optimization problem is to get an upper bound by solving a
relaxed problem (non-interger constraint) which represents the worst
cast scenario.</p>
<p>The relaxed optimization problem is the following:</p>
\[C (\textbf{u}) = \sum_{j} \left(\frac{k_j}{u_j}+ \frac{h_j \lambda u_j}{2}\right) = \frac{k_w}{u_w}+ \frac{h_s \lambda u_w}{2} + \frac{k_p}{u_p}+ \frac{h_w \lambda u_p}{2}\]
<p>Such that:</p>
<p>\(u_j \ge u_{j+1}\) \(u_j \ge 0\)
\(u_p \ge \frac{Q_{min}}{\lambda} = \frac{13}{10}\)</p>
<p>This can be solved using multi-variate calculus or [numerically]</p>
<p>Using calculus, the partial derivative of the cost function with respect
to \(u_p\) can be taken as:</p>
\[\frac{\partial}{\partial u_p}(\frac{k_w}{u_w}+ \frac{h_s \lambda u_w}{2} + \frac{k_p}{u_p}+\frac{h_w \lambda u_p}{2})= \frac{\lambda h_w}{2}- \frac{k_p}{u_p^2}\]
<p>Now solving for $u_p$</p>
\[u_p^* = ±\frac{\sqrt{\frac{2k_p}{h_w}} }{\sqrt{\lambda}} = \sqrt{2} \ge \frac{13}{10}\]
<p>Similarly, the partial derivative of the cost function with respect to
\(u_w\) can be taken as:</p>
\[\frac{\partial}{\partial u_w}(\frac{k_w}{u_w}+ \frac{h_s \lambda u_w}{2} + \frac{k_p}{u_p}+ \frac{h_w \lambda u_p}{2})= \frac{h_s \lambda}{2}-\frac{k_w}{u_w^2}\]
<p>Now solving for $u_w$</p>
\[u_w^* = ± \frac{\sqrt{2 k_w}}{\sqrt{h_s \lambda}} = \frac{\sqrt{10}}{10}\]
<p>The upper bound of the overall cost function can now be calculated as:</p>
\[C_* = C(\textbf{u*}) \ge 100(\sqrt{2} + \sqrt{10}) \cong 457.649122254\]
<p>In this case, about every 1.414 years the warehouse would order:</p>
\[Q_p^* = u_p \lambda = 100 \sqrt{2} \cong 141.421 = 142 units\]
<p>The retail store would order:</p>
\[Q_w^* = u_w \lambda = 100 \sqrt{10} \cong 31.522 = 32 units\]
<p>This would result in the overall cost to be:</p>
\[C(\textbf{Q}) = \frac{129979}{284} \cong \$457.67\]
<p>Using the same approach using the un-relaxed cost function (and assuming
incorrectly an optimal solution), the optimal order quantities becomes:</p>
\[Q_p^* = u_p \lambda = 100 \sqrt{2} \cong 141.421 = 142 units\]
<p>The retail store would order:</p>
\[Q_w^* = u_w \lambda = 100/3 \cong 33.33 = 34 units\]
<p>This results in the following approximate ideal average annual cost
\(C(\textbf{Q})_{ideal} \cong\) <a href="http://www.wolframalpha.com/input/?i=%2850%29%2F%28x%29+%2B+%289+*+100+*+x%29%2F%282%29+%2B+%28100%29%2F%28y%29%2B+%281+*+100+*+y%29%2F%282%29%2C+x+%3D+0.34%2C+y+%3D+1.42">$441.81</a> This lower bound would be
better estimated by evaluating the cost of storing the remaining
inventory in the warehouse. For example, after the first order cycle of
142 parts from the supplier and first three shipments to the store,
there will be 14 parts remaining. At a cost of $1 per year per item,
this also adds a $14 cost over the three cycles. In the next cycle
(order the minimum quantity of 130 units), there will be 16 left over.
This continues by units of 2 (over 30 cycles) until 30 units and then
finally there will be no remain parts and the cycle starts again. This
means over 30 order cycles from the store to the warehouse, we would
face a mean unit period cost of 198 units * $1 /30 or $6.6 per order
cycle. This means the actual lower bound of the solution is around
448.41. The difference between the upper and ideal lower bound of the
solution is around $16.19 or the price of a beer and a Speedway Big Hog
Chedder Dog at a Nascar event and the approximate average annual cost
would sit closer to $448.41. Since the optimal order quantity from the
warehouse is \(Q_w* = 34\) units,
\(\theta_{theoretical}^* = Q_w^* / Q_p^* \cong 4.176470588\) and
\(\theta_{actual}^* = 4\). Since the difference between
\(\theta_{theoretical}^*\) and \(\theta_{actual}^*\) is small, the actual
solution would be much closer to the lower bound.</p>
<h1 id="constrained-optimization-solution">Constrained Optimization Solution</h1>
<p>To improve upon the generalized approach and attempt to find the true
optimal solution, the optimization problem can be solved using a
non-linear programming tool such as Goal Seek in Excel, as demonstrated in this <a href="https://andrewandrade.ca/blog/images/case-study.xls">spreadsheet</a>. Alternatively,
the lower bound of the optimal solution can be estimated <a href="http://www.wolframalpha.com/input/?i=minimize+%2850%29%2F%28x%29+%2B+%2810+*+100+*+x%29%2F%282%29+%2B+%28100%29%2F%28y%29%2B+%281+*+100+*+y%29%2F%282%29+such+that+x%3E+0+%2C+y+%3E+13%2F10">numerically</a></p>
<p>or using calculus. Then $\theta_j$ can be calculated and rounded to find
the order quantities (as shown in the generalized solution).</p>
<p>The results from both methods are in agreement and the result in
$Q_w^* = 34$, $Q_p^*= 136$ and an overall average annual cost of about
$441.59 which is $5.62 dollars cheaper than before the policy change.</p>
<h1 id="summary">Summary and Conclusion</h1>
<h2 id="optimum-cost-before-policy-change">Optimum Cost before policy change</h2>
<p>The optimum annual cost before the policy change in the problem is
$447.21/year</p>
<h2 id="optimum-cost-ordering-and-storing-to-retail-store-alone">Optimum Cost Ordering and Storing to Retail Store Alone</h2>
<p>Given the assumptions in this problem, using the optimum solution of a
basic EOQ model results in an optimum ordering 130 units (when stock
reaches zero), results in an actual optimal annual cost of around $726</p>
<p>This means that this would cost the store about $277.79 more if they
choose to order, ship and store to the retail store.</p>
<h2 id="optimum-cost-using-a-warehouse">Optimum Cost Using a Warehouse</h2>
<p>Given the assumptions in this problem, the approximate optimum solution
of the two stage deterministic multi-echelon serial system provides an
annual cost of around $447.21. This means that the policy change would
cost the store at most about $5.61 less if they order 144 parts to the
warehouse and 136 parts from the warehouse to the retail store. Alex
Halek wwould be able to rest at ease and possibly use those savings
towards the purchase of a delicious Speedway Big Hog Cheddar Dog at next
year’s Spring Cup.</p>
https://mrandrewandrade.github.io/blog/2016/02/16/generalized-solution-to-multi-echelon-inventory-decisions.html
https://mrandrewandrade.github.io/blog/2016/02/16/generalized-solution-to-multi-echelon-inventory-decisionsTue, 16 Feb 2016 00:00:00 +0000Production and Operations Management 101 Reference Sheet<h1 id="learning-curve">Learning Curve</h1>
<p>Given Y(u) is the number of time units to produce the uth unit, then using $$Y(u) = au^{-b}</p>
<p>Now to describe the % decline in labour hours to produce 2n compared to hours required to produce item 2n compaures to the hours required to produce item n:</p>
\[\frac{Y(2u)}{Y(u)} = \frac{a (2u)^{-b}}{a (u)^{-b}} = 2^{-b}\]
<p>Since 80% is a historically determined learning curve, \(2^{-b} = 0.8\):
\(b = \frac{-ln 0.8}{ln 2} = 0.322\)</p>
<h1 id="timing-capacity-additions">Timing Capacity Additions</h1>
<p>D = annual increase in demand, x = time intervals between capacity additions, r = annual discount rate (compounded continously), f(y) = cost of operating a plant of capacity y. Now given C(x) is the total discounted cost of all capacity additions over an infinite horizon, if new plants are built even x units of time:</p>
<p>$$ C(x) = \frac{f(xD)}{(1-e^{-rx}}</p>
<p>TODO: Understand the next equations better, before writing them here.</p>
<h1 id="inventory-control-under-known-demand">Inventory Control Under Known Demand</h1>
<p>Holding or Carrying cost (\(h = Ic\)) where h is the holding cost, I is the annual interest rate, and c is the $ per unit of inventory.</p>
https://mrandrewandrade.github.io/blog/2016/01/16/production-operations-management.html
https://mrandrewandrade.github.io/blog/2016/01/16/production-operations-managementSat, 16 Jan 2016 00:00:00 +0000Thermodynamics 101 Reference Sheet<h1 id="assumptions-and-common-terms">Assumptions and Common Terms</h1>
<p>First lets start with some common assumptions (refer back here when they come up):</p>
<ol>
<li>Kinetic and potential energy remains about constant (\(\Delta KE, \Delta PE ~= 0\))</li>
<li>Isobaric (pressure is constant)</li>
<li>Isothermal (temperature is constant)</li>
<li>Isovolumetic (total volume (V) is constant)</li>
<li>Rigid (change in volume is zero or \(dV = 0\))</li>
<li>Adiabatic (heat transfer is zero or \(\dot Q = 0\)</li>
<li>Reversible (\(\dot P_s = 0\))</li>
<li>Ideal Gas more below</li>
<li>stead-state or SS (change is zero or d/dt = 0)</li>
<li>steady flow or SD (\(U_{IN}, h_{IN},\) … = constant)</li>
<li>Liquids and solids have constant volume</li>
<li>Liquids are solids are assumed incompressible</li>
</ol>
<p>Next lets learn some commons terms (refer back here when they come up):</p>
<p><strong>compressed liquid</strong> or <strong>subcooled liquid</strong>: fluid in liquid phase and not about to vaporize.</p>
<p><strong>saturated liquid</strong>: liquid about to vaporize</p>
<p><strong>saturated vapour</strong>: vapor about to condense</p>
<p><strong>saturated (liquid-vapor) mixture:</strong> liquid and vapor phases co-existing in equilibrium.</p>
<p><strong>super heated vapor</strong>: vapor not about to condense</p>
<p><strong>saturation temperature</strong>: temperature at which a pure substance changes phase</p>
<p><strong>saturation pressure</strong>: pressure at which a pure substance change phase</p>
<p><strong>critical point</strong>: point where thje saturated liquid and saturated vapour states are the same</p>
<h1 id="laws">Laws</h1>
<p><strong>zero law of thermodynamics:</strong>
if two seperate bodies are in thermo equilibrium with a third, they the first two are in equilibrium. (think of the third body as a thermometer and the first two as non conected bodies)</p>
<p><strong>first law of thermodynamics:</strong><br />
chance in energy of system = energy into system - energy out of system + energy produced by system</p>
<p><strong>concervation of mass:</strong>
change in mass of system = mass into system - mass out of system + mass produced by system.</p>
<p>Note: both the mass produced and energy produced by system is usually equal to zero.</p>
<p><strong>second law of thermodynamics:</strong>
energy has <em>quality</em> in addition to having a <em>quantity</em> (more about that later)</p>
<h1 id="properties">properties</h1>
<p><strong>primary/fundamental properties:</strong>
basic dimentions which describe the universe as we know it (length, mass, time, temperature, electic current, amount of light, amount of matter)</p>
<p><strong>secondary properties:</strong>
properties derived from the fundamental properties (ie velocity = displacement/time)</p>
<h3 id="mass">Mass</h3>
<p>I am not going to go into what mass is. It relates to gravitational attraction and
\(E = m c^2\)</p>
<p>Its measured in grams in the SI system.</p>
<p><strong>Note:</strong> unity conversion ratio
There is a difference between lbm pound mass (slug) and pound force lbf in the imperial system</p>
<p>pound mass or slug is the mass which accelerates by 1 ft/s when the force of one pound is exerted on it</p>
\[1 lbf = 32.174 lbm * ft/s^2\]
<h3 id="force">Force</h3>
<p>Force (F) is mass times acceleration or pressure times area, measured in newtons.</p>
\[F = P A = m g\]
<p>Pressure is force divided by area and is measured in Pascals or Pa.</p>
\[1 Pa = 1 \frac{N}{m^2} = 1 \frac{kg}{m \times s^2}\]
<p>Note: <strong>absolute pressure</strong> is measured in an absolute vacuum (absolute zero pressure) and <strong>guage pressure</strong> is relative to atmosphere. <strong>Vacuum pressures</strong> are when a pressure is less than atmosphere. Mathematically this means:</p>
\[P_{gage} = P_{abs} - P_{atm} = \rho g h\]
\[P_{vac} = P_{atm} - P_{abs}\]
<p>PSI is pressure measured in pounds per square inch. PSIg is measuring guage pressure, PSIa is measuring absolute pressure.</p>
<p>Pressure can also be measured from the following equations:</p>
\[P = P_{gage} + P_{atm}\]
\[\Delta P = P_2 - P_1 = - \int \rho g dz\]
<p>There are a bunch of devices with measure pressure, just look them up if ever you have to use them.</p>
<p>Pressure is the same in any fluid measured at the same depth. Read the book, it explains it.</p>
<h2 id="types-of-properties">types of properties</h2>
<p><strong>intensive property</strong> independant of mass/volume (temperature, pressure, density)</p>
<p><strong>extensive propety</strong> property dependant on size of system (mass, volume)</p>
<p><strong>specific property</strong> extensive property per unit mass (just divide by mass)</p>
<p><strong>volume</strong> I am using the following symbol for volume:</p>
\[\vee\]
<p><strong>density</strong> mass per unit volume</p>
\[\rho = \frac {m}{ \vee }\]
<p><strong>weight is a force</strong>
\(W = mg\) (Newtons)</p>
<p>Where m is mass, and g is the acceleration due to gravity</p>
<p><strong>specific weight</strong> is density times acceleration due to gravity
\(\gamma = \rho g\)</p>
<p><strong>specific volume</strong> is volume per unit mass</p>
\[\nu = \frac{\vee}{m} = \frac{1}{\rho}\]
<p>specific gravity or relative density is the ratio of the density of a substance to the density of some standard substance at a specified temperature (ususally water at 4 degress C where the density is 1000 kh/m^3)</p>
\[SG = \frac{\rho}{\rho_{H_2 O}}\]
<h2 id="state-postulate">State Postulate</h2>
<p>Okay now we now that we can use properties to descripte the state of a system The thing is we don’t need to specify <em>all</em> the properties to fix a state. This means if we define a sufficient number of properties, then we can determine the rest. As a different example if we know the distance and time someone traveled, we can determine their average velocity. In the same way if we have two <em>independant</em> properties, then we can fix the state if all the other properties are functions of those two properties. Being <strong>independant</strong> means that if one property is fixed, the other is still able to be varied. For exmaple, if we fix the x and y axis by telling someone they are not allowed to step forwards,backwards or sideways, the z axis is independant so the person can, in theory, still jump. Now let us call this a fancy idea the <strong>state postulate</strong>. The state postulate dictates how many number of properties is necessary to fix a state.</p>
<blockquote>
<p>The state postulate says that the state of a simple compresssible system is completly specific by two independant, intensive properties.</p>
</blockquote>
<p>We define a a simple compressible system as on where there isn’t any other external effect acting on it. This means there isn’t any electrical, magnetic, gravitational, motion or surface tension effects (or those are so small they are assumed to be neglitable since they require external force to make). Now if we add an signiticant effect on our system, we now need to add an additional property (for every unique type of effect). For example if we want to consider gravational effect, we must consider z (for height/elevation). Since temperature and specific volume are always independant, we can always use them to fix the state of a simple compressible system. If we fix the volume of a system, it can always change temperature. Same if we fix temperature, we can change the volume. Why not pressure and temperature? This is because pressures changes with temperature. For example at sea (P = 1 atm) level water boild at 100 degrees C but on a mountain water can boil at a lower temperature.</p>
<h1 id="processes-paths-and-cycles">Processes, Paths and cycles</h1>
<p>A <strong>process</strong> is then a system goes from one equilibrium state to another. The exacty series of states which a system passes through is called the <strong>path.</strong></p>
<h2 id="understanding-assumptions">Understanding assumptions</h2>
<p>Now here is where those assumptions come in. When someone says a process is isothermal, it means the path which the system takes stays a fixed temperature. That is the temperature remains constant. Isobaric means pressure remains constant etc. etc. The systems follows a path based on the assumption</p>
<h2 id="cycle">Cycle</h2>
<p>A system goes through a <strong>cycle</strong> if it returns to its initial state, that is the start and the end states are the same.</p>
<h2 id="steady-and-uniform">Steady and Uniform</h2>
<p>Steady means no change with time, uniform means no change with location. You can use these terms in everyday life but if a Waterloo engineering student says he/she has a steady boyfriend/girlfriend they are probably lying.</p>
<p>A steady-flow process is when a fluid flows through a control volume steadily. This means they can change with position, but not with time. Mass flowing at 1pm should be the same as mass flowing at 3 pm. This means the energy contents of a control volume stays constant.</p>
<h1 id="zeroth-law-of-thermodynamics">Zeroth law of thermodynamics</h1>
<p>Now its time for a law: if two bodies are in thermal equilibrium with a third, it means they are thermal equilibrium with each other (even if they are not in contact). It means temperature measured at one point will be the exact same if the value measured is the same at another. This is why thermometers work.</p>
<h1 id="energy">Energy</h1>
<p>We set the total energy (E) to a convenient a reference point (usually 0) since we are usually only interested in the change of energy in our system. We denote e as energy per unit mass.</p>
\[e= \frac{E}{m}\]
<h2 id="internal-energy">internal energy</h2>
<p>Internal energy is the sum of all microscopic forms of energy (related to molecular structure and activity), and is denoted using the symbol U</p>
<h2 id="kinetic-energy">kinetic energy</h2>
\[KE = m \frac {\vee^2}{2}\]
\[ke = \frac{\vee^2}{2}\]
<h2 id="potential-energy">Potential energy</h2>
\[PE = mgz\]
\[pe = gz\]
<h2 id="mass-and-energy-flow-rate">mass and energy flow rate</h2>
<p>Mass is a form of energy.</p>
<p><strong>Mass flow rate</strong> amount of mass flowing through a cross section per unit time and can be related to the <em>volume flow rate</em>:</p>
\[\dot{m} = \rho \dot{\vee} = \rho A_c V_{avg}\]
<p>where \(\dot{m}\) is mass flow rate, \(\dot{\vee}\) is volume flow rate, and \(A_c\) is cross sectional area of the flow, and \(V_{avg}\) is the average flow velocity (perpendicular to Ac)</p>
<p>Now we can relate that to energy using:
\(\dot{E} = \dot{m} e\)</p>
<h2 id="mechanical-energy-flow-energy">mechanical energy (flow energy)</h2>
<p>The mechanical energy of a flowing fluid or flow energy per unit mass is the following:</p>
\[e_{mech} = \frac{P}{\rho} + \frac{V^2}{2} + gz\]
<p>We can express it in rate form as:</p>
\[\dot{E}_{mech} = \dot{m} e_{mech} = \dot{m} (\frac{P}{\rho} + \frac{V^2}{2} + gz)\]
<p>If the fluid assumed to be incompressible (density is constant) flow becomes</p>
\[\Delta e_{mech} = \frac{P_2 - P_1}{\rho} + \frac{V_2^2-V_1^2}{2} + g(z_2-z_1)\]
<p>and</p>
\[\Delta \dot{E}_{mech} = \dot{m} e_{mech} = \dot{m} (\frac{P_2-P_1}{\rho} + \frac{V_2^2-V_1^2}{2} + g(z_2-z_1)\]
<h1 id="closed-vs-open-systems">Closed vs open systems</h1>
<p>Closed system (or control mass)
fixed mass where no mass crosses system boundary</p>
<p>Open system (or control volume)
system is an arbitary colume in space</p>
<h1 id="heat">Heat</h1>
<p>Heat is a form of energy which is transferred between two systems through a temperatuer difference. Heat transfer is denoted by Q, and can be related per unit mass as q.</p>
\[q = \frac{Q}{m}\]
<p>It crosses the boundary of a closed system. We assume (for simplicity) that heat always goes into the system. Since it is a directional quantity, if the heat transfer calculated is negative, then it is going out of the system.</p>
<h1 id="work">Work</h1>
<p>Work as also cross the boundary of a closed system, and since only heat and work can cross the boundary of a closed system, any form of energy crossing the boundary which is not heat, is work. Same deal: (for simplicity) work always goes into the system.</p>
<p><strong>work</strong> form of energy defined as force multiplied by distance.</p>
\[W = \int \overrightarrow{F} \dot{} \overrightarrow{dx} = - \int PAdx = - \int P d\vee\]
<p>Where F is force and x is a displacement vector, P is pressure, A is area. (note Area multiplied by displacement is a volume)</p>
<p>SI unit is joules, english system is Btu (British thermal unit)</p>
<p>Think of a Joule as a newton- meter</p>
\[1 J = 1 N \times 1 m\]
<p>Btu
energy to raise temperature of 1 lbm (slug) of water at 68 degrees F by 1 degree F.</p>
<p>1 Btu = 1.0551 kJ.</p>
<p>1 calorie
amount of energy needed to raise temperature of 1 g of water at 14.5 degress by 1 degree C.</p>
<p>1 cal = 4.16868 J</p>
<p>Work per unit mass is denoted by w and is expressed by:</p>
\[w = \frac{W}{m}\]
<p>Total work is boundary work (see below) plus other work
\(W = W_b + W_{other}\)</p>
<p><strong>Boundary work</strong> is work done by the expansion and compression of substances. On a PV diagram it
is the <em>area under the process curve</em>. Here are a couple of forms of boundary work:</p>
<p>General:
\(W_b = - \int_{1}^{2} P dV\)</p>
\[\delta W_b = F dx = PA dx = P d\vee\]
<p>Where dx is teh change in distance.</p>
<p>Isobaric (\(P_1 = P_2 = P_0 = constant\)) :</p>
\[W_b = - P_0 (V_2 - V_1)\]
<p>Polytropic(\(PV^n\)):</p>
<p>\(W_b = - \frac{P_2 V_2 - P_1 V_1}{1-n}\) , where \(n \ne 1\)</p>
<p>Isothernal of ideal gas (\(PV = m R T_0 = constant\)):</p>
\[W_b = P_1 V_1 ln \frac{V_2}{V_1} = mRT_0 ln \frac{V_2}{V_1}\]
<h2 id="power">Power</h2>
<p>power is just work done per unit time and is denoted by:</p>
\[\dot{W}\]
<h2 id="important-things-about-work-and-heat-transfer">Important things about work and heat transfer:</h2>
<p>Work and heat transfer is actaully path dependant and their magnitudes depend on the path followed during a process. If you don’t understand this, try and read chapter three of the Cengel textbook, I can’t really explain how I understand this (or if I really do). I would suggest to read the path dependance dependance section for thermodynamic <a href="https://en.wikipedia.org/wiki/Work_(thermodynamics)">work</a>or <a href="https://en.wikipedia.org/wiki/Nonholonomic_system">nonholonomic systems</a> on Wikipedia, but it is pretty complex and poorly explained there :(.</p>
<p>Ok now so path functions have inexact differentials while point functions (such as properies as volume) have exact differentials. This means that they depend on the state only and now how a system reached that state. We use a different symbol for exact and inexact differentals.</p>
\[\int_{1}^{2} d \vee = \vee_2 - \vee_1 = \Delta V\]
\[\int_{1}^{2} \delta W = W_{1 \rightarrow 2}\]
<p>Note result of the work integral is not \(\Delta W\) or W2-W1. This is because work is done by following a process path and is not just measuring a magnitude.</p>
\[E_2 - E_1 = W_{1 \rightarrow 2} + Q_{1 \rightarrow 2}\]
\[dE = \delta W + \delta Q\]
<p>There equations with determine work of various types (electrical, shaft, spring and many more). You can look them up if you need to use them.</p>
<h2 id="first-law-of-thermodynamics">First law of thermodynamics</h2>
<p><em>first law of thermodynamics</em>: simple expression of conservation of energy (energy is a thermodynamics property)</p>
<p>Think of it like energy balance:</p>
<p>Net energy transfer by heat, work and mass = change in all the energies (internal, kinetic, potetial etc.)</p>
\[E_{in} - E{out} = \Delta E_{system}\]
<p>Now think of it in <em>rate form</em>:</p>
\[\dot E_{in} - \dot E{out} = dE_{system}/dt\]
<p>Now think energy is the heat transfer to system (Q) and work done to system (W) is also the same as the sum of the change in internal energy, kinetic energy, potential energy</p>
\[E = Q + W = \Delta U + \Delta KE + \Delta PE\]
<p>where
\(\Delta U = m(u_2-u_1)\),
\(\Delta KE = \frac{1}{2}m (V_2^2 - V_1^2)\),
\(\Delta PE = mg(z_2-z_1)\),</p>
<p>Now when there is a <em>constant-pressure process</em>:</p>
<p>\(W_b + \Delta U + \delta H\) <br />
therefore:</p>
\[Q -W_{other} = \Delta H + \Delta KE + \Delta PE\]
<h3 id="mechanisms-of-energy-transfer">Mechanisms of Energy Transfer</h3>
<ol>
<li>Heat Transfer Q</li>
<li>Work Transfer W</li>
<li>Mass flow m (when mass leaves or enters a system it brings or takes energy with it. Imagine removing hot water from a water heater and replacing it with cold water).</li>
</ol>
<h3 id="energy-conversion-efficiencies">Energy conversion efficiencies</h3>
<p>Efficiency describes how well an energy conversion or transfer process is accomplided</p>
\[Performance = \frac{designed output}{required input}\]
<p>Look up the equation for the specific type of efficiency you are asked when you need it.</p>
<h1 id="introducing-enthalpy-and-entropy">Introducing Enthalpy and Entropy</h1>
<p>Enthalpy (h) and entropy (s) are also properties of systems. Don’t worry about entropy until we reach the second law for thermodynamics, just know it exists.</p>
<p>Enthalpy is a property defined for simplicity and convenience. This means we can use use it often enough to give it a special name and defination:</p>
\[h = u + P \nu\]
\[H = U + P \vee\]
<p>where U is internal energy and u is internal energy per unit mass. The book doesn’t explain U or H yet, it just says that they exist and they can be used.</p>
<p>For example, if you look at table A-4 or A-5 of the Cengel book, you can find $$h_{fg} which is the difference of enthalpy of the saturated liquid and the saturated vapor. This represents the amount of energy needed to vaporize a unit mass of a saturated liquid at a given temperature or pressure</p>
<h1 id="quality">Quality</h1>
<p><strong>Quality</strong> x is the ratio of the mass of vapor to the total mass of the mixture:</p>
\[x = \frac{m_{vapor}}{m_{total}}\]
<p>where\(m_{total} = mass_{liquid} + mass_{vapor} = m_f + m_g\)</p>
<p>Therefore a saturated liquid has quality = 0 and a saturated vapor has quality = 1.</p>
<p>If a system consists of a satured mixture (defined at the very top of this page), we can get the average property by using the quality. Note: properties are independant depending on phase. For example liquid water will have the properties of liquid water, and gaseous water (steam) will have the properties of gasous water even if in the same system.</p>
\[\nu_{avg} = \nu_f + x \dot{} (\nu_{g} - \nu_{f})\]
<p>we can also rearrange and solve for x</p>
\[x = \frac{\nu_{avg} - \nu_{f}}{\nu_{g} - \nu_{f}}\]
<p>In the same way we can define this analysis for internal energy (u), enthalpy (h) and enthropy (s) or any general property (y)</p>
\[u_{avg} = u_f + x \dot{} (u_{g} - u_{f})\]
\[h_{avg} = h_f + x \dot{} (h_{g} - h_{f})\]
\[s_{avg} = s_f + x \dot{} (s_{g} - s_{f})\]
\[y_{avg} = y_f + x \dot{} (y_{g} - y_{f})\]
<p>Note, the average properties of the mixture will always be betwen the values for a saturated liquid and satured vapor:</p>
\[y_f \leq y_{avg} \leq y_g\]
<h1 id="ideal-gas">Ideal Gas</h1>
<p>Ideal gas equation of state:</p>
\[P \nu = RT\]
<p>The other (non specific) form is:</p>
\[P \vee = mRT\]
<p>where R is the gas constant. R is different for each gas as is determined by:</p>
\[R = \frac{R_{universal}}{M}\]
<p>Where Runiversal is the universal gas constant and M is the molar mass.</p>
<p>Molar mass M is the mass of one mole. The mass of a system is equal to its molar mass M and the total number of moles N:
\(m = MN\)</p>
<h1 id="specific-heats">Specific Heats</h1>
<p><strong>Specific heat</strong> is the energy required to raise the temperature of a unit mass of a substance by one degree.</p>
<p>We are interested in two types of specific heat: constant pressure \(c_p\) and constant volume \(c_{\nu}\).</p>
<p>\(c_{\nu}\) is the energy required to raise the temperature of a unit mass of a substance by one degree when <em>volume is constant</em>. The book says to think of it as the change in internal energy divided by the change in temperature.</p>
<p>\(C_{\nu} = \frac {\partial u} {\partial T}\) <br />
or<br />
\(u_2 - u_1 = c_{\nu} (T_2-T_1)\) (kJ/kg)</p>
<p>This is because the change in internal energy for an ideal gas using a process is determined by the following integral:</p>
<p>\(\Delta u = u_2 - u_1 = \int_{1}^{2} C_{\nu} (T) dT\) (kJ/kg)</p>
<p>Cp is energy required to raise the temperature of a unit mass of a substance by one degree when <em>pressure is constant</em>. The books says to think of it as the change in enthalpy divided by the change in termperature.</p>
\[C_{p} = \frac {\partial h} {\partial T}\]
<p>\(h_2 - h_1 = c_{p} (T_2-T_1)\) (kJ/kg)</p>
<p>This is because the change in enthalpy for an ideal gas using a process is determined by the following integral:</p>
<p>\(\Delta h = h_2 - h_1 = \int_{1}^{2} C_{p} (T) dT\) (kJ/kg)</p>
<p>Specific heat at constant pressure is always greater than at constant volume since at constant pressuer the system is allowed to expand and energy during the expansion creates work into the system.</p>
<p>We can also relate the gas constant R to specific heat.
\(R = C_p - C_{\nu}\)</p>
<p>We can also relate Cp and Cv through a property called the <strong>specific heat ratio</strong> (k)</p>
\[k = \frac{c_p}{c_{\nu}}\]
<p>I will explain this better below (after real gas and boundary work).</p>
<h2 id="real-gas">Real Gas:</h2>
<p>Near the saturated region and critial point, gasses can behave unideally, so we use Z to denote a compressibility factor:</p>
\[P \nu = ZRT\]
<p>More on this soon to come!</p>
\[\frac{dE}{dt}= \dot{W} +\dot{Q}+ \dot{M_{in}} (e+pv)_{in} - \dot{M_{out}} (e+pv)_{out}\]
<h1 id="u-s-h-of-solids-and-liquids">U, S, H of Solids and Liquids</h1>
<p>We usually assume that solids and liquids have a constant specific volume, and we called this an incompressible substance. In this case, the specifics will be the same and just noted as c
\(c_{\nu} = c_p = c\)</p>
<p>Now we can do some derivations and show that if we know c value at an average temperature, we can determine the approximate internal energy. That is:</p>
\[\Delta u \cong c_{avg} (T_2 - T_1)\]
<p>\(\Delta h = \Delta u + \nu \Delta P \cong c_{avg} \Delta T + \nu \Delta P\) (kJ/kg)</p>
<p>We can also show that since solids are incompressible, the liquid properties remain that same at difference temperature.</p>
\[du = dh = C_v dT = C_p dT = \bar c dT\]
<p>That is:
\(\Big\{u, v, h, s \Big\}_{liq} \cong \Big\{u(T), v(T), h(T), s(T) \Big\}_{T}\)</p>
https://mrandrewandrade.github.io/blog/2015/10/25/thermodynamics-101-reference.html
https://mrandrewandrade.github.io/blog/2015/10/25/thermodynamics-101-referenceSun, 25 Oct 2015 00:00:00 +0000How measure temperature using a thermistor and BeagleBone Black<h1 id="results-from-modeling-a-thermister">Results from Modeling a Thermister</h1>
<p>In the <a href="https://andrewandrade.ca/blog/2015/10/22/modeling-thermistor-using-data-science.html">last blog post</a>, I described a long winded approach about how I built a model for a thermister to relate the voltage measured from a BeagleBoneBlack (BBB) microcontroller to the temperature. This post will summarize the results from the model, and explain how to connect up the system.</p>
<h2 id="thermal-sensor-data">Thermal Sensor Data</h2>
<p>The thermal sensor used is a 100k thermistor <a href="http://www.robotdigg.com/product/198/3950+100k+thermistor+with+1m+cables">Part No. HT100K3950-1</a> invidually connected to a 1m cable:</p>
<p><strong>Hotend Thermistor</strong> <br />
100K Glass-sealed Thermistor
3950 1% thermistor 1.8mm glass head <br />
1M long cables 2 wires <br />
PTFE Tube 0.6*1mm to protect from the thermistor <br />
Shrink wrap between thermistor and the cables.</p>
<p>The site included a weird <a href="http://www.robotdigg.com/upload/pdf/100k-thermistor.doc">word document</a> with a bunch of numbers. I saved the data as a <a href="https://andrewandrade.ca/datasets/100k-thermistor.csv">.csv</a> file so I can use the data in python. If you want to follow along, you can you download the document <a href="https://andrewandrade.ca/datasets/100k-thermistor.csv">here</a></p>
<p>I then made a chart which includes the expected resistance and the errors. This chart summarizes the data provided by the manufacturer:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="c1">#import necessary libraries:
</span><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="nn">pylab</span> <span class="k">as</span> <span class="n">P</span>
<span class="kn">import</span> <span class="nn">plotly.plotly</span> <span class="k">as</span> <span class="n">py</span>
<span class="kn">import</span> <span class="nn">math</span>
<span class="kn">from</span> <span class="nn">scipy.optimize</span> <span class="kn">import</span> <span class="n">curve_fit</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="kn">import</span> <span class="nn">matplotlib.mlab</span> <span class="k">as</span> <span class="n">mlab</span>
<span class="kn">import</span> <span class="nn">matplotlib.gridspec</span> <span class="k">as</span> <span class="n">gridspec</span>
<span class="c1">#comment below if not using ipython/jupyter notebook
</span><span class="o">%</span><span class="n">matplotlib</span> <span class="n">inline</span>
<span class="c1">#read csv and label columns
</span><span class="n">data</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">read_csv</span><span class="p">(</span><span class="s">'100k-thermistor.csv'</span><span class="p">)</span>
<span class="n">data</span><span class="p">.</span><span class="n">columns</span> <span class="o">=</span> <span class="p">[</span><span class="s">'temperature'</span><span class="p">,</span> <span class="s">'r_max'</span><span class="p">,</span> <span class="s">'r_norm'</span><span class="p">,</span><span class="s">'r_min'</span><span class="p">]</span>
<span class="n">data</span><span class="p">[</span><span class="s">'lower_error'</span><span class="p">]</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="o">-</span><span class="n">data</span><span class="p">.</span><span class="n">r_min</span>
<span class="n">data</span><span class="p">[</span><span class="s">'upper_error'</span><span class="p">]</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">r_max</span><span class="o">-</span><span class="n">data</span><span class="p">.</span><span class="n">r_norm</span>
<span class="n">asymmetric_error</span> <span class="o">=</span> <span class="p">[</span><span class="n">data</span><span class="p">.</span><span class="n">lower_error</span><span class="p">,</span> <span class="n">data</span><span class="p">.</span><span class="n">upper_error</span><span class="p">]</span>
<span class="n">fig1</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">,</span> <span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="p">,</span><span class="n">color</span><span class="o">=</span><span class="s">'k'</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'expected resistance'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">'Expected Resistance vs Temperature'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (degrees C)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Resistance (kohm)"</span><span class="p">)</span>
<span class="c1">#plt.show()
#py.iplot_mpl(fig1)</span></code></pre></figure>
<iframe frameborder="0" seamless="seamless" scrolling="no" src="https://plot.ly/~mrandrewandrade/84.embed" height="525px" width="100%"></iframe>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">fig2</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">,</span><span class="n">data</span><span class="p">.</span><span class="n">r_min</span><span class="o">-</span><span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'lower error'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">,</span><span class="n">data</span><span class="p">.</span><span class="n">r_max</span><span class="o">-</span><span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'upper error'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Upper and Lower Error vs Temperature"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Resistance (kohm)"</span><span class="p">)</span>
<span class="c1">#py.iplot_mpl(fig2)</span></code></pre></figure>
<iframe frameborder="0" seamless="seamless" scrolling="no" src="https://plot.ly/~mrandrewandrade/80.embed" height="525px" width="100%"></iframe>
<p>We than then limit the data to operating temperature range which I care about:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="c1">#set the desired upper and lower temperature
</span><span class="n">lower_temp_range</span> <span class="o">=</span> <span class="mi">19</span>
<span class="n">upper_temp_range</span> <span class="o">=</span> <span class="mi">81</span>
<span class="c1">#starting_temp sets the index adjustement for array
</span><span class="n">starting_temp</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">selected_temp</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">[</span><span class="n">lower_temp_range</span><span class="o">-</span><span class="n">starting_temp</span><span class="p">:</span><span class="n">upper_temp_range</span><span class="o">-</span><span class="n">starting_temp</span><span class="p">]</span>
<span class="n">selected_r_norm</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="p">[</span><span class="n">lower_temp_range</span><span class="o">-</span><span class="n">starting_temp</span><span class="p">:</span><span class="n">upper_temp_range</span><span class="o">-</span><span class="n">starting_temp</span><span class="p">]</span>
<span class="n">fig1</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span> <span class="n">selected_r_norm</span><span class="p">,</span><span class="n">color</span><span class="o">=</span><span class="s">'k'</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'expected resistance'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">'Expected Resistance vs Temperature'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (degrees C)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Resistance (kohm)"</span><span class="p">)</span>
<span class="c1">#plt.show()
</span>
<span class="c1">#this makes an interative plot using plotly
#py.iplot_mpl(fig1)</span></code></pre></figure>
<iframe frameborder="0" seamless="seamless" scrolling="no" src="https://plot.ly/~mrandrewandrade/95.embed" height="525px" width="100%"></iframe>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">selected_r_min</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">r_min</span><span class="p">[</span><span class="n">lower_temp_range</span><span class="o">-</span><span class="n">starting_temp</span><span class="p">:</span><span class="n">upper_temp_range</span><span class="o">-</span><span class="n">starting_temp</span><span class="p">]</span>
<span class="n">selected_r_max</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">r_max</span><span class="p">[</span><span class="n">lower_temp_range</span><span class="o">-</span><span class="n">starting_temp</span><span class="p">:</span><span class="n">upper_temp_range</span><span class="o">-</span><span class="n">starting_temp</span><span class="p">]</span>
<span class="n">fig2</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span><span class="n">selected_r_min</span><span class="o">-</span><span class="n">selected_r_norm</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'lower error'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span><span class="n">selected_r_max</span><span class="o">-</span><span class="n">selected_r_norm</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'upper error'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Upper and Lower Error vs Temperature"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Resistance (kohm)"</span><span class="p">)</span>
<span class="c1">#comment if not using plotly
#py.iplot_mpl(fig2)</span></code></pre></figure>
<iframe frameborder="0" seamless="seamless" scrolling="no" src="https://plot.ly/~mrandrewandrade/97.embed" height="525px" width="100%"></iframe>
<p>Based on the Resistance vs Temperature Chart, we can make an assumption that the distribution follows the relationship followed by this fitness function:</p>
<p>\(resistance = a e^{-b \times temperature} + c\)
where resistance is meaured in \(k\Omega\), temperature measued in degrees celcius, and the constants a, b, and c can be found (through curve fitting).</p>
<p>Lets define the fitness function and find the constants a, b, and c.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">fitness_function</span><span class="p">(</span><span class="n">temperature</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">):</span>
<span class="k">return</span> <span class="n">a</span><span class="o">*</span><span class="n">np</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">b</span><span class="o">*</span><span class="n">temperature</span><span class="p">)</span> <span class="o">+</span> <span class="n">c</span> <span class="c1"># kohm
</span>
<span class="n">selected_temp_fit_parms</span><span class="p">,</span> <span class="n">selected_temp_fit_covariances</span> <span class="o">=</span> <span class="n">curve_fit</span><span class="p">(</span><span class="n">fitness_function</span><span class="p">,</span>
<span class="n">selected_temp</span><span class="p">,</span> <span class="n">selected_r_norm</span><span class="p">)</span>
<span class="k">print</span> <span class="s">' fit coefficients:</span><span class="se">\n</span><span class="s">'</span><span class="p">,</span> <span class="n">selected_temp_fit_parms</span><span class="p">,</span> <span class="s">'</span><span class="se">\n</span><span class="s">'</span>
<span class="k">print</span> <span class="s">'a='</span><span class="p">,</span><span class="n">selected_temp_fit_parms</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span><span class="s">'</span><span class="se">\n</span><span class="s">'</span>
<span class="k">print</span> <span class="s">'b='</span><span class="p">,</span><span class="n">selected_temp_fit_parms</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span><span class="s">'</span><span class="se">\n</span><span class="s">'</span>
<span class="k">print</span> <span class="s">'c='</span><span class="p">,</span><span class="n">selected_temp_fit_parms</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span><span class="s">'</span><span class="se">\n</span><span class="s">'</span>
<span class="k">print</span> <span class="s">' Covariance matrix:</span><span class="se">\n</span><span class="s">'</span><span class="p">,</span> <span class="n">selected_temp_fit_covariances</span>
<span class="n">selected_temp_fit</span> <span class="o">=</span> <span class="n">fitness_function</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span>
<span class="n">selected_temp_fit_parms</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span>
<span class="n">selected_temp_fit_parms</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span>
<span class="n">selected_temp_fit_parms</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span>
<span class="n">residual_error_selected</span> <span class="o">=</span> <span class="n">selected_temp_fit</span><span class="o">-</span><span class="n">selected_r_norm</span>
<span class="n">fig3</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span> <span class="n">selected_temp_fit</span><span class="p">,</span><span class="n">label</span><span class="o">=</span><span class="s">'curve fit'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'k'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Resistance (kohm)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Curve Fit Resitance vs Temperature"</span><span class="p">)</span>
<span class="c1">#py.iplot_mpl(fig3)</span></code></pre></figure>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> fit coefficients:
[ 2.95517151e+02 4.52495454e-02 5.12190680e+00]
a= 295.517150959
b= 0.0452495454444
c= 5.12190680215
Covariance matrix:
[[ 6.94401115e-01 1.20117237e-04 8.77447476e-02]
[ 1.20117237e-04 2.41377772e-08 2.04485474e-05]
[ 8.77447476e-02 2.04485474e-05 2.05394937e-02]]
</code></pre></div></div>
<iframe frameborder="0" seamless="seamless" scrolling="no" src="https://plot.ly/~mrandrewandrade/105.embed" height="525px" width="100%"></iframe>
<p>Now lets plot the expected resistance, curve fit resistance and residuals to understand the error in the curve fit:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span> <span class="n">selected_temp_fit</span><span class="p">,</span><span class="n">label</span><span class="o">=</span><span class="s">'curve fit'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span> <span class="n">selected_r_norm</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'expected'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span><span class="n">residual_error_selected</span><span class="p">,</span> <span class="s">'r'</span> <span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'residual error'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">legend</span><span class="p">(</span><span class="n">bbox_to_anchor</span><span class="o">=</span><span class="p">(</span><span class="mf">0.</span><span class="p">,</span> <span class="mf">1.02</span><span class="p">,</span> <span class="mf">1.</span><span class="p">,</span> <span class="p">.</span><span class="mi">102</span><span class="p">),</span> <span class="n">loc</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
<span class="n">ncol</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="s">"expand"</span><span class="p">,</span> <span class="n">borderaxespad</span><span class="o">=</span><span class="mf">0.</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Resistance (kohm)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span><span class="n">residual_error_selected</span><span class="p">,</span> <span class="s">'r'</span> <span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'residual error'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Residual Error in Curve Fit (kohm)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Residual Error in Curve Fit Across Full Temperature Range"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
<span class="c1"># convert numpy series to array
</span><span class="n">residual_error_list</span> <span class="o">=</span> <span class="n">residual_error_selected</span><span class="p">.</span><span class="n">tolist</span><span class="p">()</span>
<span class="n">residual_error_array</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">asarray</span><span class="p">(</span><span class="n">residual_error_list</span><span class="p">)</span>
<span class="n">error_mean</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">mean</span><span class="p">(</span><span class="n">residual_error_array</span><span class="p">)</span>
<span class="n">error_sigma</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">std</span><span class="p">(</span><span class="n">residual_error_array</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"Residual mean (Kohm):"</span>
<span class="k">print</span> <span class="n">error_mean</span>
<span class="k">print</span> <span class="s">"Residual std dev (Kohm):"</span>
<span class="k">print</span> <span class="n">error_sigma</span>
<span class="n">n</span><span class="p">,</span> <span class="n">bins</span><span class="p">,</span> <span class="n">patches</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">hist</span><span class="p">(</span><span class="n">residual_error_array</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="n">normed</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">facecolor</span><span class="o">=</span><span class="s">'red'</span><span class="p">,</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.75</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Error in Curve fit (kohm)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Residual Distribution"</span><span class="p">)</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">P</span><span class="p">.</span><span class="n">normpdf</span><span class="p">(</span> <span class="n">bins</span><span class="p">,</span> <span class="n">error_mean</span><span class="p">,</span> <span class="n">error_sigma</span><span class="p">)</span>
<span class="n">l</span> <span class="o">=</span> <span class="n">P</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">bins</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="s">'k--'</span><span class="p">,</span> <span class="n">linewidth</span><span class="o">=</span><span class="mf">1.5</span><span class="p">)</span></code></pre></figure>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Residual mean (Kohm):
-8.15077359012e-09
Residual std dev (Kohm):
0.264370345418
</code></pre></div></div>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_23_1.png" alt="png" /></p>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_23_2.png" alt="png" /></p>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_23_3.png" alt="png" /></p>
<p>As we can see, the residual error is very small compared to the range of resistance, and follows a (some what) normal distribution. That we have a model of resistance vs temperature, lets set up our microcontroller (BBB) to measure temperature. Since our sensor is a variable resistor, we can take an analog mesaurement by passing a current though the thermister and measuring the voltage accross it. Since we need to limit our input voltage to 1.8 V (according to the BBB spec, we can use a voltage divider to limit the maximum voltage accross the thermistor (as shown below)</p>
<iframe title="measuring-temperature-thermistor" width="100%" height="600px" scrolling="no" frameborder="0" name="measuring-temperature-thermistor" class="eda_tool" src="https://upverter.com/eda/embed/#designId=c31c38d140bec875,actionId="></iframe>
<p>In our case, we need to calculate the correct R1 to ensure the voltage accross the thermistor (which is read through the analog input pin of the BBB) does not exceed 1.8V based on the maximum operating range of temperature. For us this is between 0C and 100 C. Based on the plot of the resistance vs temperature for the thermistor, we know that at 0C, the resistance of the thermistor is a maximum of 327.240 Kohm. We can then use this value as R2 in a generic voltage divider, to calculate for R1.</p>
<iframe title="voltage-divider" width="100%" height="600px" scrolling="no" frameborder="0" name="voltage-divider" class="eda_tool" src="https://upverter.com/eda/embed/#designId=79ccecb425cbbda4,actionId="></iframe>
\[R_1 = R_2 \frac{V_{in}}{V_o} - R_2\]
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">v_in</span> <span class="o">=</span> <span class="mf">3.3</span> <span class="c1">#V
</span><span class="n">v_out</span> <span class="o">=</span> <span class="mf">1.8</span> <span class="c1">#V
</span><span class="n">r_2_max</span> <span class="o">=</span> <span class="mi">327260</span> <span class="c1">#ohm
</span>
<span class="n">r_1</span> <span class="o">=</span> <span class="n">r_2_max</span> <span class="o">*</span> <span class="n">v_in</span> <span class="o">/</span> <span class="n">v_out</span> <span class="o">-</span> <span class="n">r_2_max</span> <span class="c1">#ohm
</span><span class="k">print</span> <span class="s">"Minimum value for R1 in kohms"</span>
<span class="k">print</span> <span class="n">r_1</span> <span class="o">/</span><span class="mi">1000</span> <span class="c1">#kohm</span></code></pre></figure>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Minimum value for R1 in kohms
272.716666667
</code></pre></div></div>
<p>Looking at the standard 1% <a href="http://www.brannonelectronics.com/images/STANDARD%20VALUE.pdf">resistor chart</a> or <a href="http://www.daycounter.com/Calculators/Standard-Resistor-Value-Calculator.phtml">calculator</a> we now know we should use 274k ohm resistor for R1.</p>
<p>Now that we have R1 and Vin set as constants and we can measure Vo from the BBB’s analog input pin, we can now write a rearrange the voltage divider equation and fitness function to relate Vo to temperature. The previous blog post goes over this, but the resulting function is here:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">voltage_to_temperature</span><span class="p">(</span><span class="n">voltage_reading</span><span class="p">):</span>
<span class="c1">#calculated to limit input voltage 1.8V max
</span> <span class="n">v_in</span> <span class="o">=</span> <span class="mf">3.3</span> <span class="c1">#Volts
</span>
<span class="n">r_1</span> <span class="o">=</span> <span class="mi">274</span> <span class="c1">#kohm
</span>
<span class="c1">#taken by curve fitting
</span> <span class="n">a</span> <span class="o">=</span> <span class="mf">2.94311453</span><span class="o">*</span><span class="nb">pow</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span>
<span class="n">b</span> <span class="o">=</span> <span class="mf">4.51009053</span><span class="o">*</span><span class="nb">pow</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span>
<span class="n">c</span> <span class="o">=</span> <span class="mf">5.054388390</span>
<span class="c1">#taken from voltage divider equation
</span> <span class="n">r_2</span> <span class="o">=</span> <span class="n">r_1</span> <span class="o">*</span> <span class="n">voltage_reading</span> <span class="o">/</span> <span class="p">(</span><span class="n">v_in</span> <span class="o">-</span> <span class="n">voltage_reading</span><span class="p">)</span> <span class="c1"># kohm
</span>
<span class="c1">#taken from rearraging fitness function
</span> <span class="c1">#solve r_2 = a*np.exp(-b*temperature) + c) for t
</span> <span class="n">t</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span> <span class="o">*</span> <span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">log</span><span class="p">((</span><span class="n">r_2</span><span class="o">-</span><span class="n">c</span><span class="p">)</span><span class="o">/</span><span class="n">a</span><span class="p">))</span><span class="o">/</span><span class="n">b</span> <span class="c1">#degrees C
</span> <span class="k">return</span> <span class="n">t</span></code></pre></figure>
<p>We can now use this function and plot for the full range of input voltages:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">v_measured_min</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1">#V
</span><span class="n">v_measured_max</span> <span class="o">=</span> <span class="mf">1.8</span> <span class="c1">#V
</span><span class="n">v_sweep</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">linspace</span><span class="p">(</span><span class="n">v_measured_min</span><span class="p">,</span> <span class="n">v_measured_max</span><span class="p">,</span> <span class="n">num</span><span class="o">=</span><span class="mi">1000</span><span class="p">)</span>
<span class="n">temperature_profile</span> <span class="o">=</span> <span class="n">voltage_to_temperature</span><span class="p">(</span><span class="n">v_sweep</span><span class="p">)</span>
<span class="n">fig</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">v_sweep</span><span class="p">,</span> <span class="n">temperature_profile</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Measured Voltage (V)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Temperature vs Measured Voltage"</span><span class="p">)</span>
<span class="c1">#py.iplot_mpl(fig)</span></code></pre></figure>
<iframe frameborder="0" seamless="seamless" scrolling="no" src="https://plot.ly/~mrandrewandrade/107.embed" height="525px" width="100%"></iframe>
https://mrandrewandrade.github.io/blog/2015/10/23/measure-temperature-thermistor-beaglebone.html
https://mrandrewandrade.github.io/blog/2015/10/23/measure-temperature-thermistor-beagleboneFri, 23 Oct 2015 00:00:00 +0000Modeling a Thermister: Merging Hardware Engineering and Data Science<p>In the <a href="https://andrewandrade.ca/blog/2015/10/21/battery-testing.html">last blog post</a>, I talked about voltage dividers and how they can be used to limit voltage to eventually connect to an ADC to measure voltage on a BeagleBone. Today I am going to build on the voltage divider and pair hardware engineering with data science. The next couple of posts will be pieces of battery testing rig, and then I will put it all togeather to explain how the full system works. First things first, what temperature sensor should we use?</p>
<h2 id="selecting-a-thermal-sensor">Selecting a Thermal Sensor</h2>
<p>Half due to avaliability, and the other half due to shear laziness (of not wanting to buy sensors), I decided we are going to use thermisters as our temperature sensor of choice. A friend hand a bunch so why use them?</p>
<p>Specifically, he gave me a bunch of 100k thermistors <a href="http://www.robotdigg.com/product/198/3950+100k+thermistor+with+1m+cables">Part No. HT100K3950-1</a> invidually connected to a 1m cable.</p>
<h2 id="what-is-a-thermister">What is a thermister?</h2>
<p>A thermister can be simply defined as a special type of resistor whose resistance is highly dependent on temperature. Therefore if you are able to measure the resistance on the thermister, you can easily determine the temperature if you know how the thermister behaves. Simple enough right?</p>
<p>If you go to the <a href="http://www.robotdigg.com/product/198/3950+100k+thermistor+with+1m+cables">site</a> which sells them, they give a bit more information:</p>
<p><strong>Hotend Thermistor</strong>
100K Glass-sealed Thermistor
3950 1% thermistor 1.8mm glass head <br />
1M long cables 2 wires <br />
PTFE Tube 0.6*1mm to protect from the thermistor <br />
Shrink wrap between thermistor and the cables.</p>
<p>The most important piece of information to get started using the thermister is its behaviour responding to temperature. Luckly for me, my friend sent me the data which was on the site. When you have a table or chart which maps the resistance to temperature, you can simply measure the resistance with a multimemter and look up the temperature on the chart, or you can again use a voltage divider and connect it to an ADC (more on that later).</p>
<h2 id="mapping-resistance-to-temperature-using-curve-fitting">Mapping Resistance to Temperature using Curve Fitting</h2>
<p>The site included a weird <a href="http://www.robotdigg.com/upload/pdf/100k-thermistor.doc">word document</a> with a bunch of numbers. It really confuses me why one would have tabular data in a word document, a spreadsheet serves that purpose. Anyways, the information was easily extractable and I was able to put it into an spreadsheet and save as a .CSV file. If you want to follow along, you can you download the document <a href="https://andrewandrade.ca/datasets/100k-thermistor.csv">here</a></p>
<p>Once you own the file in a speadsheet program or in your text editor of choice, you can see there are four columns: temperature in Celcius, maximum resistance in \(k\Omega\), normal (average)resistance in \(k\Omega\) and minimum resistance in \(k\Omega\) . If we were measuring the resistance by hand, we could simply just look up (and eyeball) the closest resistance value and read of the temperature. We could be more fancy and use <a href="https://en.wikipedia.org/wiki/Linear_interpolation">linear interpolation</a> as an alternative to eyeballing.</p>
<p>That is all great, but our goal is to use a micocontroller (or computer) to store, measure and use the temperature readings. This means we have to mathematically model how the thermister behaves. Since we have data provided from the manufacturer, we do this by plotting the data which was provided:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="c1">#import necessary libraries:
</span><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="nn">pylab</span> <span class="k">as</span> <span class="n">P</span>
<span class="kn">import</span> <span class="nn">math</span>
<span class="kn">from</span> <span class="nn">scipy.optimize</span> <span class="kn">import</span> <span class="n">curve_fit</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="kn">import</span> <span class="nn">matplotlib.mlab</span> <span class="k">as</span> <span class="n">mlab</span>
<span class="kn">import</span> <span class="nn">matplotlib.gridspec</span> <span class="k">as</span> <span class="n">gridspec</span>
<span class="c1">#comment below if not using ipython/jupyter notebook
</span><span class="o">%</span><span class="n">matplotlib</span> <span class="n">inline</span>
<span class="c1">#read csv and label columns
</span><span class="n">data</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">read_csv</span><span class="p">(</span><span class="s">'100k-thermistor.csv'</span><span class="p">)</span>
<span class="n">data</span><span class="p">.</span><span class="n">columns</span> <span class="o">=</span> <span class="p">[</span><span class="s">'temperature'</span><span class="p">,</span> <span class="s">'r_max'</span><span class="p">,</span> <span class="s">'r_norm'</span><span class="p">,</span><span class="s">'r_min'</span><span class="p">]</span>
<span class="c1">#plot temperature as x, r_norm as y over full range of temperature
</span><span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">,</span><span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'average resistance'</span><span class="p">)</span>
<span class="c1">#label axis
</span><span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Resistance (kohm)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Expected Resistance vs Temperature of 100K Thermistor"</span><span class="p">)</span></code></pre></figure>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_1_1.png" alt="png" /></p>
<p>Now we can see that it does not have a linear relation. Actually it has a inverse relation or an \(x^{n}\) where $n<0$ most probably. Since I am curious, I plotted the min and the max resistance to get a better feel for the error in the temperature reading. My ituition tells me that that is th range the thermister operates in.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">,</span><span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'expected resistance'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">,</span><span class="n">data</span><span class="p">.</span><span class="n">r_min</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'min resistance'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">,</span><span class="n">data</span><span class="p">.</span><span class="n">r_max</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'max resistance'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">legend</span><span class="p">(</span><span class="n">bbox_to_anchor</span><span class="o">=</span><span class="p">(</span><span class="mf">0.</span><span class="p">,</span> <span class="mf">1.02</span><span class="p">,</span> <span class="mf">1.</span><span class="p">,</span> <span class="p">.</span><span class="mi">102</span><span class="p">),</span> <span class="n">loc</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
<span class="n">ncol</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="s">"expand"</span><span class="p">,</span> <span class="n">borderaxespad</span><span class="o">=</span><span class="mf">0.</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Resistance (kohm)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Resistance vs Temperature of 100K Thermistor"</span><span class="p">)</span></code></pre></figure>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_3_1.png" alt="png" /></p>
<p>Now, the top graph, isn’t that useful. All it shows is that the range is very small, and is wider (there is more error) when temperatures are below 0 degrees. To see if we can do better, lets limit the range (with contingency) of the temperatures we will be dealing with on the project: 0-100 degrees.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span><span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span> <span class="n">label</span><span class="o">=</span><span class="s">'expected resistance'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span><span class="n">data</span><span class="p">.</span><span class="n">r_min</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span> <span class="n">label</span><span class="o">=</span><span class="s">'min resistance'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span><span class="n">data</span><span class="p">.</span><span class="n">r_max</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span> <span class="n">label</span><span class="o">=</span><span class="s">'max resistance'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Resistance (kohm)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">legend</span><span class="p">(</span><span class="n">bbox_to_anchor</span><span class="o">=</span><span class="p">(</span><span class="mf">0.</span><span class="p">,</span> <span class="mf">1.02</span><span class="p">,</span> <span class="mf">1.</span><span class="p">,</span> <span class="p">.</span><span class="mi">102</span><span class="p">),</span> <span class="n">loc</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
<span class="n">ncol</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="s">"expand"</span><span class="p">,</span> <span class="n">borderaxespad</span><span class="o">=</span><span class="mf">0.</span><span class="p">)</span></code></pre></figure>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_5_1.png" alt="png" /></p>
<p>The plot is a bit clearer but not perfect, let’s try and be more fancy and reprensent the error with error bars like they do in stats 101.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">data</span><span class="p">[</span><span class="s">'lower_error'</span><span class="p">]</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="o">-</span><span class="n">data</span><span class="p">.</span><span class="n">r_min</span>
<span class="n">data</span><span class="p">[</span><span class="s">'upper_error'</span><span class="p">]</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">r_max</span><span class="o">-</span><span class="n">data</span><span class="p">.</span><span class="n">r_norm</span>
<span class="n">asymmetric_error</span> <span class="o">=</span> <span class="p">[</span><span class="n">data</span><span class="p">.</span><span class="n">lower_error</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span> <span class="n">data</span><span class="p">.</span><span class="n">upper_error</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">]]</span>
<span class="n">plt</span><span class="p">.</span><span class="n">errorbar</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span> <span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span> <span class="n">yerr</span><span class="o">=</span><span class="n">asymmetric_error</span><span class="p">,</span> <span class="n">ecolor</span><span class="o">=</span><span class="s">'r'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Resistance vs Temperature of 100K Thermistor"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Resistance (kohm)"</span><span class="p">)</span>
<span class="c1">#plt.show()</span></code></pre></figure>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_7_1.png" alt="png" /></p>
<p>Great! A bit better, but it is still hard to read. Let’s try plotting the error on the same axis as the expected (normal) resistance.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span><span class="n">data</span><span class="p">.</span><span class="n">r_min</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">]</span><span class="o">-</span><span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span> <span class="n">label</span><span class="o">=</span><span class="s">'lower error'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span><span class="n">data</span><span class="p">.</span><span class="n">r_max</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">]</span><span class="o">-</span><span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span> <span class="n">label</span><span class="o">=</span><span class="s">'upper error'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span><span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span> <span class="n">label</span><span class="o">=</span><span class="s">'expected resistance'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Resistance (kohm)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">legend</span><span class="p">(</span><span class="n">bbox_to_anchor</span><span class="o">=</span><span class="p">(</span><span class="mf">0.</span><span class="p">,</span> <span class="mf">1.02</span><span class="p">,</span> <span class="mf">1.</span><span class="p">,</span> <span class="p">.</span><span class="mi">102</span><span class="p">),</span> <span class="n">loc</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
<span class="n">ncol</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="s">"expand"</span><span class="p">,</span> <span class="n">borderaxespad</span><span class="o">=</span><span class="mf">0.</span><span class="p">)</span></code></pre></figure>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_9_1.png" alt="png" /></p>
<p>From this figure it is quite clear as tempertature decreases, there is more error in the thermistor reading. This figure also shows that reading taken over 20 C should have good accuracy. We can even take this futher by one more plot</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">[</span><span class="mi">50</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span><span class="n">data</span><span class="p">.</span><span class="n">r_min</span><span class="p">[</span><span class="mi">50</span><span class="p">:</span><span class="mi">130</span><span class="p">]</span><span class="o">-</span><span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="p">[</span><span class="mi">50</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span> <span class="n">label</span><span class="o">=</span><span class="s">'lower error'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">[</span><span class="mi">50</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span><span class="n">data</span><span class="p">.</span><span class="n">r_max</span><span class="p">[</span><span class="mi">50</span><span class="p">:</span><span class="mi">130</span><span class="p">]</span><span class="o">-</span><span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="p">[</span><span class="mi">50</span><span class="p">:</span><span class="mi">130</span><span class="p">],</span> <span class="n">label</span><span class="o">=</span><span class="s">'upper error'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Resistance (kohm)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">legend</span><span class="p">(</span><span class="n">bbox_to_anchor</span><span class="o">=</span><span class="p">(</span><span class="mf">0.</span><span class="p">,</span> <span class="mf">1.02</span><span class="p">,</span> <span class="mf">1.</span><span class="p">,</span> <span class="p">.</span><span class="mi">102</span><span class="p">),</span> <span class="n">loc</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
<span class="n">ncol</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="s">"expand"</span><span class="p">,</span> <span class="n">borderaxespad</span><span class="o">=</span><span class="mf">0.</span><span class="p">)</span></code></pre></figure>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_12_1.png" alt="png" /></p>
<p>This figure shows the upper and lower bound of error (to be around \(\pm 1.5k\Omega\))+ 125 kohm, since the expected reading would be around \(125kOmega\) at 20 degrees C. Knowing the smallest resistance within our operating range will occur at 100 degrees (around \(6720\Omega\)), R_1 can be calculated to be around \(1200\Omega\) using the voltage divider presented in the previous post. Now, the largest possible error can be calculated and used as a very conservative estime of the temperature reading resolution. Before we do that, let us fit a curve to the data. Using grade 11 math, we can estimate that the function which discribes the invest curve would look something like \(resistance = a \times e^{-b \times temprature} + c\). We can then use SciPy’s <a href="http://docs.scipy.org/doc/scipy-0.16.0/reference/generated/scipy.optimize.curve_fit.html">curve_fit</a> to determine the fit parameters and the covarience matrix.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">fitness_function</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">):</span>
<span class="k">return</span> <span class="n">a</span><span class="o">*</span><span class="n">np</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">b</span><span class="o">*</span><span class="n">t</span><span class="p">)</span> <span class="o">+</span> <span class="n">c</span>
<span class="n">full_temp_fit_parms</span><span class="p">,</span> <span class="n">full_temp_fit_covariances</span> <span class="o">=</span> <span class="n">curve_fit</span><span class="p">(</span><span class="n">fitness_function</span><span class="p">,</span>
<span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">,</span> <span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="p">)</span>
<span class="k">print</span> <span class="s">' full temperature fit coefficients:</span><span class="se">\n</span><span class="s">'</span><span class="p">,</span> <span class="n">full_temp_fit_parms</span>
<span class="k">print</span> <span class="s">' Covariance matrix:</span><span class="se">\n</span><span class="s">'</span><span class="p">,</span> <span class="n">full_temp_fit_covariances</span></code></pre></figure>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> full temperature fit coefficients:
[ 3.22248984e+02 5.51886907e-02 4.56056442e+00]
Covariance matrix:
[[ 1.82147996e+00 -2.16345654e-04 -2.82466975e-01]
[ -2.16345654e-04 2.97792772e-08 2.87522862e-05]
[ -2.82466975e-01 2.87522862e-05 2.25733939e-01]]
</code></pre></div></div>
<p>The fit coefficients are now known! This means that the following equation approximates the behaviour of the thermister:
\(resistance = 322 \times e^{-0.055 \times temprature} + 4.56\)</p>
<p>We can also determine the standard deviation of the fit from the diagonal of the covariance matrix, and plot it for each parameter.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="c1"># standard deviation of each paramter
</span><span class="n">sigma</span> <span class="o">=</span> <span class="p">[</span><span class="n">math</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">full_temp_fit_covariances</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">]),</span>
<span class="n">math</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">full_temp_fit_covariances</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">]),</span>
<span class="n">math</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">full_temp_fit_covariances</span><span class="p">[</span><span class="mi">2</span><span class="p">,</span><span class="mi">2</span><span class="p">])</span> <span class="p">]</span>
<span class="n">x0</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">300</span><span class="p">,</span><span class="mi">340</span><span class="p">,</span><span class="mi">1000</span><span class="p">)</span>
<span class="n">x1</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">linspace</span><span class="p">(</span><span class="mf">0.05</span><span class="p">,</span><span class="mf">0.06</span><span class="p">,</span><span class="mi">1000</span><span class="p">)</span>
<span class="n">x2</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">1000</span><span class="p">)</span>
<span class="n">fig</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">x0</span><span class="p">,</span><span class="n">mlab</span><span class="p">.</span><span class="n">normpdf</span><span class="p">(</span><span class="n">x0</span><span class="p">,</span><span class="n">full_temp_fit_parms</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span><span class="n">sigma</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Uncertainty in fit parameter a"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Parameter a"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Standard Deviation"</span><span class="p">)</span>
<span class="n">fig</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">x1</span><span class="p">,</span><span class="n">mlab</span><span class="p">.</span><span class="n">normpdf</span><span class="p">(</span><span class="n">x1</span><span class="p">,</span><span class="n">full_temp_fit_parms</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span><span class="n">sigma</span><span class="p">[</span><span class="mi">1</span><span class="p">]),</span> <span class="n">label</span><span class="o">=</span><span class="s">'uncertainty in fit parameter b'</span><span class="p">)</span>
<span class="n">fig</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">x2</span><span class="p">,</span><span class="n">mlab</span><span class="p">.</span><span class="n">normpdf</span><span class="p">(</span><span class="n">x2</span><span class="p">,</span><span class="n">full_temp_fit_parms</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span><span class="n">sigma</span><span class="p">[</span><span class="mi">2</span><span class="p">]),</span> <span class="n">label</span><span class="o">=</span><span class="s">'uncertainty in fit parameter c'</span><span class="p">)</span>
<span class="c1">#plt.show()</span></code></pre></figure>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_16_1.png" alt="png" /></p>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_16_2.png" alt="png" /></p>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_16_3.png" alt="png" /></p>
<p>As we can see, the standard deviation is very small and thus results in a good fit accross the full range of temperature as shown in the three figures below:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">full_temp_fit</span> <span class="o">=</span> <span class="n">fitness_function</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">,</span>
<span class="n">full_temp_fit_parms</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span>
<span class="n">full_temp_fit_parms</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span>
<span class="n">full_temp_fit_parms</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span>
<span class="n">residual_error</span><span class="o">=</span> <span class="n">full_temp_fit</span> <span class="o">-</span><span class="n">data</span><span class="p">.</span><span class="n">r_norm</span>
<span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">,</span><span class="n">full_temp_fit</span><span class="p">,</span><span class="n">label</span><span class="o">=</span><span class="s">'curve fit'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">,</span><span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'expected'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">,</span><span class="n">residual_error</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'residual error'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">legend</span><span class="p">(</span><span class="n">bbox_to_anchor</span><span class="o">=</span><span class="p">(</span><span class="mf">0.</span><span class="p">,</span> <span class="mf">1.02</span><span class="p">,</span> <span class="mf">1.</span><span class="p">,</span> <span class="p">.</span><span class="mi">102</span><span class="p">),</span> <span class="n">loc</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
<span class="n">ncol</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="s">"expand"</span><span class="p">,</span> <span class="n">borderaxespad</span><span class="o">=</span><span class="mf">0.</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Resistance (kohm)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Resistance vs Temperature of 100K Thermistor"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">,</span><span class="n">residual_error</span><span class="p">,</span> <span class="s">'r'</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'residual error'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Residual Error in Curve Fit (kohm)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Residual Error in Curve Fit Across Full Temperature Range"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="n">n</span><span class="p">,</span> <span class="n">bins</span><span class="p">,</span> <span class="n">patches</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">hist</span><span class="p">(</span><span class="n">residual_error</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="n">normed</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">facecolor</span><span class="o">=</span><span class="s">'red'</span><span class="p">,</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.75</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Error in Curve fit (kohm)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Residual Distribution"</span><span class="p">)</span></code></pre></figure>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_18_1.png" alt="png" /></p>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_18_2.png" alt="png" /></p>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_18_3.png" alt="png" /></p>
<p>While the model accross the full temperature is useful, we can improve our model by curve fitting only in the temperature range we are interested in. This prevents the algorithm for compesating for select set of data which is irrevant.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="c1">#temperature ranger 0 to 100 degrees
</span><span class="n">selected_temp</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">]</span>
<span class="n">selected_r_norm</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="p">[</span><span class="mi">30</span><span class="p">:</span><span class="mi">130</span><span class="p">]</span>
<span class="n">selected_temp_fit_parms</span><span class="p">,</span> <span class="n">selected_temp_fit_covariances</span> <span class="o">=</span> <span class="n">curve_fit</span><span class="p">(</span><span class="n">fitness_function</span><span class="p">,</span>
<span class="n">selected_temp</span><span class="p">,</span> <span class="n">selected_r_norm</span><span class="p">)</span>
<span class="k">print</span> <span class="s">' fit coefficients:</span><span class="se">\n</span><span class="s">'</span><span class="p">,</span> <span class="n">selected_temp_fit_parms</span>
<span class="k">print</span> <span class="s">' Covariance matrix:</span><span class="se">\n</span><span class="s">'</span><span class="p">,</span> <span class="n">selected_temp_fit_covariances</span>
<span class="n">selected_temp_fit</span> <span class="o">=</span> <span class="n">fitness_function</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span>
<span class="n">selected_temp_fit_parms</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span>
<span class="n">selected_temp_fit_parms</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span>
<span class="n">selected_temp_fit_parms</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span>
<span class="n">residual_error_selected</span> <span class="o">=</span> <span class="n">selected_temp_fit</span><span class="o">-</span><span class="n">selected_r_norm</span>
<span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span> <span class="n">selected_temp_fit</span><span class="p">,</span><span class="n">label</span><span class="o">=</span><span class="s">'curve fit'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span> <span class="n">selected_r_norm</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'expected'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span><span class="n">residual_error_selected</span><span class="p">,</span> <span class="s">'r'</span> <span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'residual error'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">legend</span><span class="p">(</span><span class="n">bbox_to_anchor</span><span class="o">=</span><span class="p">(</span><span class="mf">0.</span><span class="p">,</span> <span class="mf">1.02</span><span class="p">,</span> <span class="mf">1.</span><span class="p">,</span> <span class="p">.</span><span class="mi">102</span><span class="p">),</span> <span class="n">loc</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
<span class="n">ncol</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="s">"expand"</span><span class="p">,</span> <span class="n">borderaxespad</span><span class="o">=</span><span class="mf">0.</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Resistance (kohm)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span><span class="n">residual_error_selected</span><span class="p">,</span> <span class="s">'r'</span> <span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'residual error'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Residual Error in Curve Fit (kohm)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Residual Error in Curve Fit Across Full Temperature Range"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="c1"># convert numpy series to array
</span><span class="n">residual_error_list</span> <span class="o">=</span> <span class="n">residual_error_selected</span><span class="p">.</span><span class="n">tolist</span><span class="p">()</span>
<span class="n">residual_error_array</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">asarray</span><span class="p">(</span><span class="n">residual_error_list</span><span class="p">)</span>
<span class="n">n</span><span class="p">,</span> <span class="n">bins</span><span class="p">,</span> <span class="n">patches</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">hist</span><span class="p">(</span><span class="n">residual_error_array</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="n">normed</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">facecolor</span><span class="o">=</span><span class="s">'red'</span><span class="p">,</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.75</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Error in Curve fit (kohm)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Residual Distribution"</span><span class="p">)</span></code></pre></figure>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> fit coefficients:
[ 3.16643400e+02 4.84933743e-02 6.53548105e+00]
Covariance matrix:
[[ 3.29405526e-01 4.07305280e-05 -1.67742297e-02]
[ 4.07305280e-05 3.89687128e-08 4.14707796e-05]
[ -1.67742297e-02 4.14707796e-05 7.51633680e-02]]
</code></pre></div></div>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_20_2.png" alt="png" /></p>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_20_3.png" alt="png" /></p>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_20_4.png" alt="png" /></p>
<p>We can see the difference by comparing both of the curve fit models on the interested temperature range:</p>
<p>While the testing spec we developed states we should have the capability of measuring from 0-100 degress C, the average range of operation is actually between 20-80 degrees C, so we can change the range to match the standard operating range</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="c1">#temperature ranger 0 to 100 degrees
</span><span class="n">selected_temp</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">temperature</span><span class="p">[</span><span class="mi">50</span><span class="p">:</span><span class="mi">110</span><span class="p">]</span>
<span class="n">selected_r_norm</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">r_norm</span><span class="p">[</span><span class="mi">50</span><span class="p">:</span><span class="mi">110</span><span class="p">]</span>
<span class="n">selected_temp_fit_parms</span><span class="p">,</span> <span class="n">selected_temp_fit_covariances</span> <span class="o">=</span> <span class="n">curve_fit</span><span class="p">(</span><span class="n">fitness_function</span><span class="p">,</span>
<span class="n">selected_temp</span><span class="p">,</span> <span class="n">selected_r_norm</span><span class="p">)</span>
<span class="k">print</span> <span class="s">' fit coefficients:</span><span class="se">\n</span><span class="s">'</span><span class="p">,</span> <span class="n">selected_temp_fit_parms</span>
<span class="k">print</span> <span class="s">' Covariance matrix:</span><span class="se">\n</span><span class="s">'</span><span class="p">,</span> <span class="n">selected_temp_fit_covariances</span>
<span class="n">selected_temp_fit</span> <span class="o">=</span> <span class="n">fitness_function</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span>
<span class="n">selected_temp_fit_parms</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span>
<span class="n">selected_temp_fit_parms</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span>
<span class="n">selected_temp_fit_parms</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span>
<span class="n">residual_error_selected</span> <span class="o">=</span> <span class="n">selected_temp_fit</span><span class="o">-</span><span class="n">selected_r_norm</span>
<span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span> <span class="n">selected_temp_fit</span><span class="p">,</span><span class="n">label</span><span class="o">=</span><span class="s">'curve fit'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span> <span class="n">selected_r_norm</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'expected'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span><span class="n">residual_error_selected</span><span class="p">,</span> <span class="s">'r'</span> <span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'residual error'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">legend</span><span class="p">(</span><span class="n">bbox_to_anchor</span><span class="o">=</span><span class="p">(</span><span class="mf">0.</span><span class="p">,</span> <span class="mf">1.02</span><span class="p">,</span> <span class="mf">1.</span><span class="p">,</span> <span class="p">.</span><span class="mi">102</span><span class="p">),</span> <span class="n">loc</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
<span class="n">ncol</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">mode</span><span class="o">=</span><span class="s">"expand"</span><span class="p">,</span> <span class="n">borderaxespad</span><span class="o">=</span><span class="mf">0.</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Resistance (kohm)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">selected_temp</span><span class="p">,</span><span class="n">residual_error_selected</span><span class="p">,</span> <span class="s">'r'</span> <span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'residual error'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Residual Error in Curve Fit (kohm)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Residual Error in Curve Fit Across Full Temperature Range"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="c1"># convert numpy series to array
</span><span class="n">residual_error_list</span> <span class="o">=</span> <span class="n">residual_error_selected</span><span class="p">.</span><span class="n">tolist</span><span class="p">()</span>
<span class="n">residual_error_array</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">asarray</span><span class="p">(</span><span class="n">residual_error_list</span><span class="p">)</span>
<span class="n">error_mean</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">mean</span><span class="p">(</span><span class="n">residual_error_array</span><span class="p">)</span>
<span class="n">error_sigma</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">std</span><span class="p">(</span><span class="n">residual_error_array</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"Residual mean (Kohm):"</span>
<span class="k">print</span> <span class="n">error_mean</span>
<span class="k">print</span> <span class="s">"Residual std dev (Kohm):"</span>
<span class="k">print</span> <span class="n">error_sigma</span>
<span class="n">n</span><span class="p">,</span> <span class="n">bins</span><span class="p">,</span> <span class="n">patches</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">hist</span><span class="p">(</span><span class="n">residual_error_array</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="n">normed</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">facecolor</span><span class="o">=</span><span class="s">'red'</span><span class="p">,</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.75</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Error in Curve fit (kohm)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Residual Distribution"</span><span class="p">)</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">P</span><span class="p">.</span><span class="n">normpdf</span><span class="p">(</span> <span class="n">bins</span><span class="p">,</span> <span class="n">error_mean</span><span class="p">,</span> <span class="n">error_sigma</span><span class="p">)</span>
<span class="n">l</span> <span class="o">=</span> <span class="n">P</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">bins</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="s">'k--'</span><span class="p">,</span> <span class="n">linewidth</span><span class="o">=</span><span class="mf">1.5</span><span class="p">)</span></code></pre></figure>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> fit coefficients:
[ 2.94311453e+02 4.51009053e-02 5.05438839e+00]
Covariance matrix:
[[ 6.57786227e-01 1.12143572e-04 8.24269669e-02]
[ 1.12143572e-04 2.18837795e-08 1.84056321e-05]
[ 8.24269669e-02 1.84056321e-05 1.81122519e-02]]
Residual mean (Kohm):
9.99334067349e-10
Residual std dev (Kohm):
0.2302444024
</code></pre></div></div>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_23_1.png" alt="png" /></p>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_23_2.png" alt="png" /></p>
<p><img src="https://andrewandrade.ca/blog/images/modeling-thermistor-using-data-science/output_23_3.png" alt="png" /></p>
<p>The results of the curve fit within the standard operating temperature is much better. Now only is residual error mean essentually zero (9.99e-10 k ohm) with a relatively small standard deviation (0.230 kohm), but the residual error have the general appearance of being normally distributed (unlike the previous curve fits). What this means is that, the model will predict the resistance very well (have a very low error) for the stardard operating temperatures, but will perform poorer outside. Luckly for us, our battries will not be operating below 20 degrees C or above 80 degrees C.</p>
<h3 id="curve-fit-model">Curve fit model:</h3>
<p>The model we created can now be sumarized to the following equation:</p>
<p>\(R_2 = a e^{-b \times temperature} + c\)
where R_2 is meaured in \(k\Omega\), temperature measued in degree celcius, and the constants a, b, and c were found (through curve fitting) to be:</p>
<p>\(a = 2.94311453 \times 10^2\) <br />
\(b = 4.51009053 \times 10 ^{-02}\) <br />
\(c = 5.054388390\)</p>
<p>In addition, the error in the curve fitting is averaged at 9.99334067349e-10 kohm and the standard deviation of 0.2302444024 kohm. This error, while small can later be used in filtering algorithms when we are monitoring the temperature. I will touch on this in a later post, but this data about statistical noise while reading can aid in estimating temperature through more advance software (such as using <a href="https://www.google.ca/url?sa=t&rct=j&q=&esrc=s&source=web&cd=4&cad=rja&uact=8&ved=0CCsQFjADahUKEwiW9b_tiNXIAhWCbB4KHb6oDBA&url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FKalman_filter&usg=AFQjCNEN1xRr0hrNRhPHUMGQ_eWCws60CA&sig2=G1o4RCdqnRXSgX1LLMSooQ">Kalman Filtering</a>).</p>
<h2 id="writing-software-to-convert-voltage-into-temperature-readings">Writing software to convert voltage into temperature readings</h2>
<p>If we think of the system as a whole, the input to the system is Vo which is measured on an analog input pin of the BBB. Based on this voltage reading, we have to write software which estimates the temperature.</p>
<p>To begin, we know that the thermister (R_2) changes resistance values depending on temperature. We can then use the voltage devider to map this relation:</p>
\[V_o = \frac{R_2 V_{in}}{R_1 + R_2}\]
<p>Vin in this case will be 3.3 V which is provided from the BBB. As noted in the last post, the spec on the BBB states that that analog input pin can only take in voltage in the range of 0 to 1.8 V. This means we can set the max Vout = 1.8. Finally based on the resistance to temperature data, we know that resistance increases as the temperature decreases. This means that R_2 (the resistance of the thermistor), will be the greatest when T = 0 degrees (R_2 will be around 327 kohms. We can then use this in our voltage divider equation and solve for R_1. The solution is R_1 = 272700 ohms. Because resistors come in standard sizes, 274K ohm is the stardard 1% error resistor to use. Technically, in this case we should go to the next highest resistor value (to limit the voltage to 1.8V), this doesn’t necessarily have to be true since we will not be cooling the batteries lower than room temperature. While I recommend using the 274k ohm resistor (with 1% error), one can use a 270k ohm (with 5% error) without much concequece if they ensure that the temperature does not fall below 5 degrees. Even if it does, the Beaglebone has some circuity to help prevent damamge for a slightly larder analog input voltage.</p>
<p>We can use algebra to solve for R_2 in terms of the other variables as the following:</p>
<p>\(R_2 = \frac{R_1 V_o}{V_{in} - V_o}\) where \(V_{in} \neq V_o\)</p>
<p>In this equation, R_2 can be solved from R_1 (set by us, V_o (measured), V_in (set to by us).</p>
<p>Next we can use the previous curve fit equation, and use algebra solve for temperature:</p>
<p>\(temperature = - \frac{ln (\frac {R_2-c}{a}}{b}\) where \(R_2 > c\)$$</p>
<p>We can then substitute our relation of R_2 to the measured Vo to get the following equation:</p>
<p>\(temperature = -ln (\frac {\frac{R_1 V_o}{V_{in} - V_o}-c}{ab}\) where \(R_2 > c\) and \(V_{in} \neq V_o\)$$</p>
<p>Before we can use this, we have to ensure the conditions hold. V_o will on equal V_in if R_2 is equal to zero, and we also ensure C (about 5.5 kohm) > R_2. If we use the data found in the csv, we see that the smallest resistance in our operaterting temperature (100 C) is 6.7100 kohm, so we are safe for both conditions.</p>
<h1 id="results">Results!</h1>
<p>We can now write a simple which takes the a volatage as a parameter, and returns the temperature.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">voltage_to_temperature</span><span class="p">(</span><span class="n">voltage_reading</span><span class="p">):</span>
<span class="n">r_1</span> <span class="o">=</span> <span class="mi">274</span> <span class="c1">#kohm
</span> <span class="n">v_in</span> <span class="o">=</span> <span class="mf">3.3</span> <span class="c1">#Volts
</span> <span class="n">a</span> <span class="o">=</span> <span class="mf">2.94311453</span><span class="o">*</span><span class="nb">pow</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span>
<span class="n">b</span> <span class="o">=</span> <span class="mf">4.51009053</span><span class="o">*</span><span class="nb">pow</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span>
<span class="n">c</span> <span class="o">=</span> <span class="mf">5.054388390</span>
<span class="n">r_2</span> <span class="o">=</span> <span class="n">r_1</span> <span class="o">*</span> <span class="n">voltage_reading</span> <span class="o">/</span> <span class="p">(</span><span class="n">v_in</span> <span class="o">-</span> <span class="n">voltage_reading</span><span class="p">)</span> <span class="c1"># kohm
</span>
<span class="n">t</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span> <span class="o">*</span> <span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">log</span><span class="p">((</span><span class="n">r_2</span><span class="o">-</span><span class="n">c</span><span class="p">)</span><span class="o">/</span><span class="n">a</span><span class="p">))</span><span class="o">/</span><span class="n">b</span>
<span class="k">return</span> <span class="n">t</span>
<span class="k">print</span> <span class="n">voltage_to_temperature</span><span class="p">(</span><span class="mf">1.8</span><span class="p">)</span></code></pre></figure>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-2.11347171107
</code></pre></div></div>
<p>We can now use this function and plot for the full range of input voltages:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">v_sweep</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mf">1.8</span><span class="p">,</span> <span class="n">num</span><span class="o">=</span><span class="mi">1000</span><span class="p">)</span>
<span class="n">temperature_profile</span> <span class="o">=</span> <span class="n">voltage_to_temperature</span><span class="p">(</span><span class="n">v_sweep</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">v_sweep</span><span class="p">,</span> <span class="n">temperature_profile</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">"Temperature (Celcius)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">"Measured Voltage (V)"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Temperature vs Measured Voltage"</span><span class="p">)</span></code></pre></figure>
<iframe title="temperature-vs-measured-voltage" width="600" height="600" frameborder="0" seamless="seamless" scrolling="no" src="https://plot.ly/~mrandrewandrade/39.embed"> </iframe>
<p>Using <a href="https://plot.ly/~mrandrewandrade/39.embed">this chart</a> we can now test the system and measure temperature! The next post will be about combining all the prieces and doing the testing!</p>
https://mrandrewandrade.github.io/blog/2015/10/22/modeling-thermistor-using-data-science.html
https://mrandrewandrade.github.io/blog/2015/10/22/modeling-thermistor-using-data-scienceThu, 22 Oct 2015 00:00:00 +0000Designing a Battery Testing Rig<h1 id="background">Background</h1>
<p>As <a href="https://andrewandrade.ca/blog/2015/09/11/fydp-ess.html">previously mentioned</a> my fourth year design project (FYDP) involves building an energy storage system (ESS) using repurposed electric vehicle (EV) batteries. After individual battery cells are removed from the large high voltage EV battery, the first step the project is testing the battery cells to see if they are “healthy” and ready to use. This means we have to charge and discharge the cells while monitoring both the voltage and the temperture. We are planning on using a BeagleBone Black (BBB) as our microcontroller (show below), and have to pick sensors to measure voltage and temperature.</p>
<p><img src="http://beagleboard.org/static/images/black_hardware_details.png" alt="" /></p>
<h1 id="project-scope">Project Scope</h1>
<p>Since we are running lean, our goal is to try and get to stage of safetly changing and discharging the battery autonomously as quickly. This means for our <code class="language-plaintext highlighter-rouge">minimal viable product</code> (MVP), we had the following goals:</p>
<ol>
<li>Label every battery cell with a unique identifier, and keep log of all tests and data.</li>
<li>Be able to autonomously charge the battery. This means be able to stop charging when either the battery reaches 50 degrees C or the voltage accross the battery cell exceed 7.2 V.</li>
<li>Constantly measure voltage accross the battery over time</li>
<li>Constantly measure the temperature of the battery over time</li>
<li>Be able to autonomously discharge the battery by pressenting a simple resistive load</li>
</ol>
<p>These are a couple of stretch goals:</p>
<ol>
<li>Steam data online</li>
<li>Test Multiple Cells</li>
<li>Coloumb counting</li>
</ol>
<p>Let’s start with choosing the sensors and figuring out how to take measurements.</p>
<h1 id="instrumentation-and-measurement">Instrumentation and Measurement</h1>
<h2 id="measuring-voltage">Measuring Voltage</h2>
<p>Measuring voltage using a BBB is simple: we can simply use the analog input pins on the BBB to measure voltage. The first issue is that according the the <a href="http://beagleboard.org/support/BoneScript/analogRead/">BBB documentation</a>, to safetly read analog values, the input voltage to the analog to digital convert (ADC) is limited to 1.8V. This means that the input voltage must be properly limited to ensure the safe operstion. To achieve this, a simple <a href="https://learn.sparkfun.com/tutorials/voltage-dividers">voltage divider</a> can be used (by apply Ohm’s Law \(V = I R\)).</p>
<h3 id="voltage-divider">Voltage Divider</h3>
<iframe title="voltage-divider" width="600" height="600" scrolling="no" frameborder="0" name="voltage-divider" class="eda_tool" src="https://upverter.com/eda/embed/#designId=79ccecb425cbbda4,actionId="></iframe>
<p>Based on Ohm’s Law, the circuit above can be simplified to \(V_o= \frac{I R_2}{I ( R_1 + R_2 ) } V_{in} \), and finally to this form:</p>
<p>\[V_o=\frac{R_2}{R_1+R_2}V_{in} \]</p>
<p>Now given a desired \(V_o\) and known \(V_{in}\), \(R_1\) and \(R_2\) can be solved for. We wanted the ability to read up to 30V (including contingency), and still limit the voltage to the BBB to 1.8 V. Here we use \(V_{in}=30V\) and \(V_o=1.8V\). By setting the smaller resistor \(R_2\) in this case) to \(10k\Omega\), one can solve \(R_1=160k\). Note, resistor come in standard resistances, so one must round to ensure the limits are sets correctly. In this example, \(R_1\) should be rounded up not down. Why? Think about how the voltage devider works.</p>
<h3 id="how-to-not-drain-battery-while-measuring">How to not drain battery while measuring</h3>
<p>The issue with this method is that with a total resistance \(R1 + R2 = 180K\Omega\), then the total current drawn \(I_{total} ={V_{in}} / R_{total} = 30V / 180k\Omega = 1.66\times10^{-4} A\) While this is a very smal current drawn, slowly by slowly it will be drain the battery. The way to reduce this effect is to increase the resistors, to the \(M\Omega\) range, such at that current being drawn will be very low. The issue with this is that there will not be a minimum current which exceeds the leakage current in a pin. In short, the current can not be too small or it will cause measurement issues. But can this be solved? Yes! The trick is to add a small capacitor in parallel with the \(R_2\) resistor. This improves the readings of the voltage out, yet enables us maintain a very low current.</p>
<h2 id="temperature-sensors">Temperature Sensors</h2>
<p>There exist many different types of temperature sensors which function in different ways. The choice of the temperature sensor and implmentation will be talked about in the next blog post!</p>
https://mrandrewandrade.github.io/blog/2015/10/21/battery-testing.html
https://mrandrewandrade.github.io/blog/2015/10/21/battery-testingWed, 21 Oct 2015 00:00:00 +0000Building an Energy Storage System using Re-purposed Electric Vehicle Batteries<p>As I am in my final year of Mechatronics Engineering at the University of Waterloo, we must complete a <a href="https://uwaterloo.ca/engineering/entrepreneurship/capstone-design">Capstone Design Project</a> (otherwise know as a Fourth Year Design Project or FYDP).</p>
<blockquote>
<p>Capstone Design is the culmination of the undergraduate student experience, creating a blueprint for innovation in engineering design. - Waterloo Engineering</p>
</blockquote>
<p>For my fourth year design project I am working on a very exciting project which involves building an energy storage system (ESS) using repurposed Electric Vehicle (EV) batteries. Stay tuned for more information about the project!</p>
https://mrandrewandrade.github.io/blog/2015/09/11/fydp-ess.html
https://mrandrewandrade.github.io/blog/2015/09/11/fydp-essFri, 11 Sep 2015 00:00:00 +0000How non-linear dynamic (chaos theory) equations describe user growth in lean startups<p>This is a answer I gave on quora to <a href="https://www.quora.com/Lean-Startups/Do-any-non-linear-dynamic-chaos-theory-equations-describe-user-growth-in-startups">this question</a>: Do any non-linear dynamic (chaos theory) equations describe user growth in lean startups?</p>
<p>I am going to generalize my answer to technology startups, but the concepts can be abstracted to most startups.</p>
<h1 id="introduction">Introduction:</h1>
<p>Forgetting the complex equations, lets try and make a function where only technology development and business development (for simplicity) are inputs and get user growth as an output. If the system was linear then the more technology and the more business which goes into the company would result in more users.</p>
<p>But in real life, user acquisition is more complex than this: Not all tech and business development is the same and company change and pivot and users come in cycles, not linearly. There are competitors in the market and fundamental changes in technology. All of these will contribute to the user growth in a startup.</p>
<p>Now lets bring in a non-linear dynamic systems model: taking the technology adoption lifecycle and constant product iteration into the picture. If we still use the tech and business development to shows user growth, but describe the function as a chaotic system, then small changes in tech and business development results in very different user growth making the full system unpredictable.</p>
<p>That is how chaos theory describes user grown in startups!</p>
<p>Read the full explanation for more details with a specific strange attractor model:</p>
<h2 id="prerequisites">Prerequisites:</h2>
<ol>
<li>Understand Technology Adoption Lifecycle</li>
<li>Understand the Pivot in terms of Lean Startups</li>
<li>Non-linear equations, chaos theory and strange attractors</li>
<li>Rössler attractor model for non-linear dynamic user growth in startups</li>
</ol>
<h1 id="1-technolgy-adoption-lifecycle">1. Technolgy Adoption Lifecycle</h1>
<p>If you have read Geoffrey Moore’s <a href="http://amzn.to/1N9VK9S">Crossing the Chasm</a> he describes the technology/innovation adoption lifecycle in detail as following a normal distribution or bell curve:</p>
<p><img src="https://qph.is.quoracdn.net/main-qimg-3005bfa31f5708c14682dcb91d4a042e?convert_to_webp=true" alt="" /></p>
<h1 id="2-the-pivot">2. The Pivot</h1>
<p>Now if you have read <a href="http://amzn.to/1NUsKot">Running Lean</a> by Ash Maurya, <a href="http://amzn.to/1NUsMMU">The Lean Startup</a> by Eric Ries or <a href="http://amzn.to/1N9XfVC">The Startup Owner’s Manual</a> by Steve Blank you will learn about the <strong>pivot</strong>.</p>
<p>The pivot is a “structured course correction designed to test a new fundamental hypothesis about the product, strategy, and engine of growth.” Steve Blank defines a pivot as “changing (or even firing) the plan instead of the executive (the sales exec, marketing or even the CEO).” The main idea behind the pivot is after building a minimal viable product (MVP) and testing it in the market, you take the feedback you got from launching the early prototype to understanding the market to hopefully finding better product-market fit in the next interation.</p>
<p>A great example of a pivot used by a company was <a href="https://en.wikipedia.org/wiki/Groupon">Groupon</a>; when the company first started, it was an online activism platform called The Point. After receiving almost no traction, the founders opened a Wordpress blog and launched their first coupon promotion for a pizzeria located in their building lobby. Although they only received 20 redemptions, the founders realized that their idea was significant, and had successfully empowered people to coordinate group action in purchasing. Three years later, Groupon would grow into a billion dollar business.</p>
<h1 id="3-non-linear-equations-chaos-theory-and-strange-attractors">3. Non-linear equations, chaos theory and strange attractors</h1>
<p>To understand the concepts better, you can read the answer to this question:
<a href="https://www.quora.com/Briefly-what-is-chaos-theory">Briefly, what is chaos theory?</a> but I will summarize it here.</p>
<p>When Newton invented calculus, scientists and methematicains were able to derive “dynamic models” of real world physical systems. For example you can model and predict the trajectory of a canon on earth (assuming no wind) very accurately since it follows a curve determined by an ordinary differential equation that is derived from Newton’s second law.</p>
<p><img src="https://qph.is.quoracdn.net/main-qimg-a3aabca98f2ad4c55d6851a9c97ebb62?convert_to_webp=true" alt="" /></p>
<p>By understanding the initial position of the canon, its initial velocity and the acceleration force of gravity we can predict where it will land. Even if the measurement of initial conditions are slightly incorrect, the prediction will be very close to the actual trajectory (within a couple feet/meters lets say). But what if there is no linear equation which can govern the motion? Let’s enter the world of Chaos Theory.</p>
<p>In a chaotic dynamical system, a small change in initial conditions will lead to a qualitatively different behaviour. A great example is weather. Choas theory was discovered by Edward Lorenz when he was making weather predictions. He used sample data of initial weather conditions (beginning of the week) to simulate future weather conditions for a week. When he did this a second time, he thought it might take too long to start from the beginning of the week so he started from the middle. What he found was that the predictions from the starting conditions of the week were very different to those that were the outcome of the conditions at the middle of the week.</p>
<p>This system is still deterministic; if you are able to reproduce the initial conditions exactly, it will follow the same behaviour over and over again. However, slight perturbations of the initial conditions lead to very different trajectories.</p>
<p>Now take the animation below.</p>
<iframe width="420" height="315" src="https://www.youtube.com/embed/AhrhpfN91po" frameborder="0" allowfullscreen=""></iframe>
<p>Let’s say a simple equation when plotted at a certain point of time looked like the following:</p>
<p><img src="https://raw.githubusercontent.com/petroleum101/figures/master/lorenz_attractor/top_left_axis.png" alt="" /></p>
<p>What is unique is that the line goes back upon itself. At one instant you are at the top right left (Figure above), the next you are at the bottom right (figure below)</p>
<p><img src="https://raw.githubusercontent.com/petroleum101/figures/master/lorenz_attractor/bottom_right_axis.png" alt="" /></p>
<p>If the top left represented sunny, hot and humid and bottom right represented cloudy, cold and dry, what makes the system chaotic is that a small change in the input would result in a very different output. For example, a change of 0.5 in the current temperature reading after 6 time periods for example shown in the figure blow would be a completely different forecast (hot and humid vs dry and cold) and a seemly random output. This is the so called “butterfly effect”.</p>
<p><img src="https://qph.is.quoracdn.net/main-qimg-672cabba49bf4be15ed119b49e0e688e?convert_to_webp=true" alt="" /></p>
<p>If the dynamic system model evolves through phase space in a way which appears to repeat (called a <a href="https://en.wikipedia.org/wiki/Fractal">Fractal</a> in mathematics), then the system of equations which describes its motion can be described a <a href="https://en.wikipedia.org/wiki/Attractor#Strange_attractor">Strange Attractor</a> (there is a more formal mathematical definition, unnecessary for this explanation).</p>
<h1 id="4-combining-the-concepts-1-and-2-to-form-the-rössler-attractor-model">4. Combining the concepts 1 and 2 to form the Rössler Attractor Model**</h1>
<p>So if you combine the technology adoption cycle with the idea of a pivot, every-time a startup company pivots, their company/product has a new addressable market and follows a the bell curve described by the technology adoption lifecycle.</p>
<p>This can be modeled using a <a href="https://en.wikipedia.org/wiki/R%C3%B6ssler_attractor">Rössler attractor</a> shown below to described the non-linear dynamic user growth in startups.</p>
<p><img src="https://qph.is.quoracdn.net/main-qimg-c987b6b9d42e981174d0d1e4f187b867?convert_to_webp=true" alt="" /></p>
<p>You can think of the z axis as user acquisition, and the x and y axis as technology and business development. After putting in some initial tech and business development, when a startup their first MVP, then have a certain adressable market where they are able to get customers. This can be seen by a curve describing the small jump in user acquisition. As companies iteratate their product, their user aquistion will (ideally) increase as described by the model.</p>
<p>Realistically though, in one iteration of the pivot, only part of the technology development cycle will be reached so in reality the model describing real life user acquisition would be much more complex variation of the Rössler attractor.</p>
<p>The point is that with very small changes in the technology development (a specific bug or feature) or business development (one sales deal or one marketing campaign which goes viral), there would be an unpredictable change in user acquisition (as modelled in this example).</p>
<p>Also of note, most people talk about the butterfly effect or chaos theory as a sort of a misnomer. It isn’t “small changes will cause large effects” but Chaos is more actually “small changes lead to unpredictability”. So if user acquisition can be modelled with a strange attractor such as the Rössler attractor, what the model is saying is that it is practically impossible to predict/model the success of a startup (or use growth) even if you have full control on the technology and business development.</p>
<p>Hope this makes sense!</p>
<p>For fun here is a nice render of the Rössler attractor.</p>
<p><img src="https://qph.is.quoracdn.net/main-qimg-3b823ec721f4fb440b741b7836ca034b?convert_to_webp=true" alt="" /></p>
https://mrandrewandrade.github.io/blog/2015/09/07/chaos-theory-and-user-growth-in-lean-startups.html
https://mrandrewandrade.github.io/blog/2015/09/07/chaos-theory-and-user-growth-in-lean-startupsMon, 07 Sep 2015 00:00:00 +0000Growing and Scaling High Performance Teams<p>This is a cross-post of a blog post originally written for CBET’s <a href="https://uwaterloo.ca/conrad-business-entrepreneurship-technology/blog/post/scaling-your-startup-team-and-knowledge">Leadership Blog</a> during my <a href="https://uwaterloo.ca/conrad-business-entrepreneurship-technology/undergraduate-students/enterprise-co-op">enterprise co-op term</a> (intern at my own company) in the summer of 2014. The blog post was also featured on LinkedIn’s <a href="https://www.linkedin.com/pulse/20140816182325-143036205-growing-and-scaling-high-performance-teams?trk=prof-post">Leadership and Management Pulse</a></p>
<p>When I started <a href="http://petropredict.com">PetroPredict</a> with Dominic, we were just a team of two co-founders looking to use technologies we used in tech companies and Silicon Valley to optimize decision making in the oil and gas industry. Since then we added on 4 others, effectively tripling our team. Over the past couple of months we were able to run a high performance team enabling our company to grow and scale.</p>
<h1 id="new-skillset-as-a-leader">New Skillset as a Leader</h1>
<p>Leading and building high performance teams was new to me, but as any new skill, with effort, it is possible to acquire. The very best thing about being a leader is you can reflect on leadership and management that you have experienced while working in previous roles. By reflecting on the techniques used by your previous managers, you can effectively outline what techniques you would like to use. Over my work terms in highschool co-op, summer jobs and university co-op I had a great opportunity to work in different corporate environment and with many different leaders and mentors. Learning from the past is one of the best ways to optimize the future.</p>
<h1 id="startup--growth">Startup = Growth</h1>
<blockquote>
<p>A startup is a company designed to grow fast.” — Paul Graham</p>
</blockquote>
<p>The biggest thing that a startup has is the ability to grow and scale. It is obvious that companies can scale in technology, customers and sales, but almost every aspect of a startup has to scale. A great example of this is when <a href="http://en.wikipedia.org/wiki/Chris_Cox_(Facebook)">Chris Cox</a> (Chief Product Officer at Facebook) told me about the idea of scaling company culture from a handful of employees to a couple of thousand.</p>
<h1 id="growing-knowledge">Growing Knowledge</h1>
<p>One aspect I wanted to elaborate on is growing knowledge. Working at a startup means that you are going to have to take many roles and have a diverse number of skills. The other thing is that the company is moving so fast that you have to gain required skills as soon as possible. When new members come on the team they have to gain certain knowledge before they can contribute. Even if you take an iterative approach, where you learn a little, do a little, it can still be difficult for rapid knowledge acquirement at the speed of the start up.</p>
<h1 id="udacity-and-moocs">Udacity and MOOCs</h1>
<p>A great solution to this is by the use of <a href="https://en.wikipedia.org/wiki/Massive_open_online_course">MOOCs</a> (massively open online courses). We are lucky to be in a time where there is free access to very useful courses. Specifically I found that Udacity was one of the very best ways to allow team members to gain knowledge they needed to suceed.</p>
<p>For example when our highschool summer student, <a href="http://www.linkedin.com/pub/kevin-peng/9b/7b3/346">Kevin Peng</a>, joined the team, he never used <code class="language-plaintext highlighter-rouge">git</code> or version control before. Instead of having to spend time on reading boring documentation or a <a href="https://git-scm.com/book/en/v2">book on git</a>, he made his own version of 2048 in a couple of hours by taking <a href="https://www.udacity.com/course/ud248">Udacity’s course</a>. This fun iterative approach allowed him to have a great summer co-op experience and enabled to complete a <a href="https://github.com/kevinpeng7/Data-Dumper">technically difficult project</a>.</p>
<h1 id="eating-your-own-dogfood">Eating your own dogfood</h1>
<p>A great philosophy I learnt at Facebook is the idea of “eating your own dog food”, or using your own product to validate the quality and capabilities of the product. While we do use data informed decision making techniques in running the company, this also can be applied to being a leader. Being a leader means setting an example to others, so throughout the term, I used Udacity to scale my own knowledge.</p>
<p>It began by taking Steve Blank’s course on <a href="https://www.udacity.com/course/ep245">How to Build a Startup</a>, and was able to apply the teachings to my own start up. One of the biggest learnings was the idea of minimal viable product and market validation. By taking the advice from the course, we were able to make demos and presentations and found many other applications for our technology which companies are very interested in.</p>
<p>As <a href="http://ca.linkedin.com/in/jonathanestrella">Jonathan Estralla</a> (our software developer) and I found, Udacity provides many other courses in topics such and data science, web development, and even machine learning. Udacity really helps scale knowledge as every lesson is a very short video (usually less than 5 minute) and has quick quizzes and application based assignments. Sometimes there are only a couple of topics we would need to brush up on, we could just watch specific topics which allowed us to complete the task at hand. Udacity in combination of other online resources enables our team to gain the required skills to be successful.
Running Mean, Lean and Agile</p>
<p>Running lean and agile as now become colloquial in software development and startups and is fundamental to running high performance teams. The biggest idea behind these techniques is another Facebook philosophy: <strong>focus on impact</strong>. Understanding what is the most important and valuable thing at the moment and executing as quick as you can. Here are some techniques I found to be useful for running high performing teams by running lean and Agile:</p>
<p><strong>Beginning of term goal setting</strong> – Get team members to set goals in the beginning of the term, and help motivate them to achieve their goals</p>
<p><strong>Weekly reports</strong> – At the end of the week do a quick summary of what was worked on during the week and set goals for the next week.</p>
<p><strong>Weekly meetings</strong> – At the beginning of the week, summarize what went on last week and the high level goals for this week. This is also a great opportunity to talk about company news</p>
<p><strong>Daily goals</strong> – Daily scrum meetings or stand ups can be used effectively to get to low level goal setting. It also provides an opportunity for members talk about issues or roadblocks they are facing or opportunities for collaboration.</p>
<p><strong><a href="http://asana.com">Asana</a></strong> – Asana is a great product management and collaboration tool. It works great as our team works remotely</p>
<p><strong>Company wiki</strong> – Setting up and running a central source of knowledge for the company enables quick collaboration and opportunity for knowledge capture</p>
<h1 id="the-most-powerful-technique-of-all">The Most Powerful Technique of All</h1>
<p>Finally, the biggest take away from running a startup is learning from the past by getting advice from mentors and experts. Using information from the past to make better decisions is one of the most powerful things anyone can do.</p>
<p>Throughout the term I have been fortunate to get advice from many mentors from the the <a href="http://uwaterloo.ca">University of Waterloo</a>, the <a href="http://velocity.uwaterloo.ca">Velocity Program</a>, the <a href="https://uwaterloo.ca/conrad-business-entrepreneurship-technology/">Conrad Business, Entrepreneurship and Technology Centre</a>, companies out of the <a href="http://velocity.uwaterloo.ca/workspaces/velocity-garage/">Velocity Garage</a> and many work colleges and friends. While I do not have room here to name everyone, I wanted to thank everyone for their help and support throughout the term!</p>
https://mrandrewandrade.github.io/blog/2014/08/16/growing-teams.html
https://mrandrewandrade.github.io/blog/2014/08/16/growing-teamsSat, 16 Aug 2014 00:00:00 +0000Tools to get things done!<h1 id="tools-i-use">Tools I use</h1>
<p>Most of the tools I use can be found in my <a href="https://backpak.io/u/MrAndrewAndrade">backpak</a></p>
<h1 id="wishlist">Wishlist</h1>
<p><a href="http://www.amazon.ca/gp/product/B00IU4JM52/ref=as_li_ss_tl?ie=UTF8&camp=15121&creative=390961&creativeASIN=B00IU4JM52&linkCode=as2&tag=mrandrewandra-20">Battery Replacement</a></p>
https://mrandrewandrade.github.io/blog/2014/07/01/tools.html
https://mrandrewandrade.github.io/blog/2014/07/01/toolsTue, 01 Jul 2014 00:00:00 +0000How I won $5000 at a Facebook hackathon without a single line of code<p><em>This a cross post from the</em> <a href="http://www.opencompute.org/"><em>Open Compute Project</em></a> <a href="http://www.opencompute.org/blog/ocp-hackathon-winner-the-codeless-hack/"><em>official blog</em></a></p>
<p><em>Our second blog post about the OCP Summit’s hardware hackathon comes from Derek Jouppi and</em> <a href="http://ca.linkedin.com/pub/andrew-andrade/3b/a9b/6b9"><em>Andrew Andrade</em></a> <em>who are interns at Facebook and won the OCP Summit Hackathon with a new design for the <a href="http://www.opencompute.org/wiki/Open_Rack">Open Rack</a> V2’s Battery Backup Unit.</em></p>
<p>Derek and I decided to take part in the hardware hackathon at the Open Compute Summit on a whim. We’d heard about the hackathon and were intrigued enough to stop by, but didn’t plan on joining in. Once we were there, however, we realized that the opportunity was too much to pass up, and we jumped in without the team size and equipment that many other groups had.</p>
<blockquote>
<p>We didn’t come with a team, or had years of experience. We have something better: an idea and killer execution.</p>
</blockquote>
<h1 id="the-idea">The Idea</h1>
<p>We wanted to build something that was highly impactful, that could be contributed immediately to Open Compute, and that we hoped would be a shippable prototype within 24 hours. Given our criteria, we decided we wanted to do a hack for the newly released Battery Backup Unit (BBU) on the Open Rack V2 design, since this is the area that Derek was most familiar with.</p>
<p>The problem with the BBU is that if it is not functional, debugging a failed unit is extremely difficult and time consuming. You have to use a nest of wires, probes, DMM, and/or oscilloscope to find the solution – and it’s definitely not a great solution. Instead, we envisioned a coupler that would take in the output from the scope, process the signals and intelligently display what the error was. Using a simple ATMEGA microcontroller, and LED display and a few other components, we could make a compact and intelligent tool for technicians working in data centers. It was simple enough we could design and build a working prototype in only 24 hours, and yet it had high impact as it could directly contribute to running a better data center. It seemed like a perfect project for our two-man team.</p>
<p>Thanks to one of the hackathon’s organizers, John Kenevey, we were able to run back to Menlo Park to grab the supplies we needed: two Arduinos, bread breadboards, LED’s, wires, DMM, and an assortment of other parts we thought might be useful.</p>
<p><img src="https://andrewandrade.ca/blog/images/preparing-to-win-hackathon.jpg" alt="" /><br />
<strong>Derek Jouppi and I begin to assemble the equipment we need for our hack.</strong></p>
<p>Then we got to work. Since the display BBU’s were inaccessible, I quickly began to write Arduino code to simulate a failing BBU. I also wrote some code that acted as a DAC to take in the signals from the BBU. Meanwhile, Derek got to work doing a pin out drawing and sketching the initial schematic. We worked for a few hours before bringing our design to an Open Rack prototype to investigate how we could implement it. That’s when we realized our solution was all wrong.</p>
<p>Our monitoring system that sat on all the racks didn’t make sense. There was not enough room in the V2 rack to hold a coupler, and a microcontroller solution would be too expensive and not scalable in the large data center.</p>
<p>With only a few hours left in the hackathon, we decided we needed to pivot to simplicity. We’d been working furiously to design a complex solution, when what we really needed was to take the Open Compute approach: remove anything in the data center that didn’t contribute to efficiency. In this case, we decided we wanted to create a simple, minimal device to solve the problem of bringing visibility to failing BBU’s.</p>
<h1 id="the-pivot">The Pivot</h1>
<p>We realized that, given the mapping of the pin outs, one solution would be to connect the signals to the status LED’s, which would give instant visualization to the problems with the BBU. While it would take manual diagnostics to determine issues, the cost of the device would be significantly lower, and it would be much easier to operate. For added functionality we included an output header. This could be used in the future to connect to a microcontroller if a more automated method had to be deployed. Finally, since the BBU relied on a power supply to charge, we included a power header that could be used to connect to the power supply.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/rrX7r5jM19A" frameborder="0" allowfullscreen=""></iframe>
<p><strong>The debug card that resulted from our hack could easily attach as a component to the back of a Battery Backup Unit as shown is this quickly rendered animation</strong></p>
<p>Our hack had become a codeless debug tool that would enable techs to quickly and efficiently debug issues with battery back up units in their datacenters.</p>
<p>After drafting out a quick design on paper, we quickly iterated our design on breadboards attempting to gain the designed functionality. We borrowed an actual failing BBU from the demo and after some iteration, we were able to come up with a very simple, “bare bones” design for the debug card.</p>
<iframe title="hackathon" width="600" height="500" scrolling="no" frameborder="0" name="hackathon" class="eda_tool" src="https://upverter.com/eda/embed/#designId=67c971c45790061b,actionId="></iframe>
<p><strong>Schematic of the debug card we made using Upverter’s schematic layout tool.</strong></p>
<p><strong>EDIT</strong>: The interactive <code class="language-plaintext highlighter-rouge">iframe</code> is breaking will all the traffic, you can view the <a href="https://upverter.com/mrandrewandrade/53180db3a9c16395/hackathon/">schematic here</a>.</p>
<p>For our circuit layout we used Upverter, a start-up that focuses on online board design CAD software. We drew up a quick schematic by sourcing our components online and imported their drawings on Upverter’s schematic tool. We then used the tool to create a bill of materials that included the component prices and the cost to spin the board.</p>
<p>We then created a PCB layout for the board. To parallelize our efforts, we performed a quick mock-up on MS Paint of the layout of the board. Derek finished up the PCB layout on Upverter while I did a quick rending of our final product using mechanical CAD software as shown below.</p>
<h1 id="the-demo">The Demo</h1>
<p>When we demonstrated our project for the judges, we were proud to watch as the BBU powered up and our LED’s lit up a split second later, indicating the status of the unit. Our simple solution had made it easy and inexpensive to know if the BBU was functional at any given time. This vital information will make it easier to be sure that any power loss won’t result in a loss of functionality.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/KAKc50apXxI" frameborder="0" allowfullscreen=""></iframe>
<p>We were incredibly excited by what we’d built, and we couldn’t help but note the community spirit of Open Compute that had gotten us there. Throughout the hack, we frequently traded tools with other teams and asked other participants for their input as we worked. Everyone lived and breathed the open source philosophy – that sharing knowledge and experience will result in better hacks overall. It seems to us that this desire to collaborate is what Open Compute is all about, and in that vein, we plan to release the specifications for our BBU hack to the open source community in the next few weeks.</p>
https://mrandrewandrade.github.io/blog/2014/03/14/codeless-hackathon-winner.html
https://mrandrewandrade.github.io/blog/2014/03/14/codeless-hackathon-winnerFri, 14 Mar 2014 00:00:00 +0000