LaundrySorcery

Log | Files | Refs

commit 3332c4afb8cdc0715bf86d8e2ccafc60769dc439
parent a98a715793c8f678e09e22c3db8ff06569f98d49
Author: Dominik Schmidt <das1993@hotmail.com>
Date:   Sat, 30 Jun 2018 23:42:49 +0000

Write a large-ish latex document to describe the laundrysorcery

Diffstat:
doc/Documentation.tex | 217+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
doc/Graph/Cut_Gaussians/Cut_Gaussian.asy | 49+++++++++++++++++++++++++++++++++++++++++++++++++
doc/Graph/distribution.asy | 21+++++++++++++++++++++
doc/Graph/simulation.gnuplot | 4++++
doc/Graph/simulator.m | 36++++++++++++++++++++++++++++++++++++
doc/Makefile | 18++++++++++++++++++
doc/Schematics.tex | 6+-----
doc/Schematics_Core.tex | 5+++++
8 files changed, 351 insertions(+), 5 deletions(-)

diff --git a/doc/Documentation.tex b/doc/Documentation.tex @@ -0,0 +1,217 @@ +\documentclass{scrreprt} +\usepackage{fontspec} +\setmainfont{STIX Two Text} +\usepackage{amsmath} +\usepackage{unicode-math} +\setmathfont{STIX Two Math} + +\DeclareMathOperator*{\argmax}{argmax} + +\usepackage{polyglossia} +\setdefaultlanguage{english} + +\usepackage{microtype} + +\usepackage[margin=2cm, bottom=2.5cm]{geometry} + +\usepackage{float} +\usepackage[export]{adjustbox} +\usepackage{graphicx} +\usepackage{circuitikz} + +\newcommand{\abs}[1]{\lvert{}#1\rvert{}} + +\usepackage{hyperref} +\usepackage[all]{hypcap} +\hypersetup{pdfborder = {0 0 0}, colorlinks=true, allcolors=black, urlcolor=blue} + + +\title{LaundrySorcery} +\author{Dominik Schmidt} +\date{\today} + +\begin{document} +\maketitle + \chapter{Sensory} + To determine whether the washing machine is on or off we tape a light-sensor to the ON-LED. + This has a drawback: The machine registers as ON while in delayed start mode, which is good for displaying whether the + machine is free but bad for the machine learning chapter. + + The light sensor we chose is a light dependant resistor (LDR), leaving us with the problem on how to read out the resistance + with the given hardware (a Raspberry Pi) + + \section{Read-out Circuit} + Since the Raspberry Pi does not have an ADC, we need a different way to read out the resistance of the LDR. + \begin{figure}[H] + \centering + \begin{tikzpicture} + \input{Schematics_Core.tex} + \end{tikzpicture} + \caption{The circuit for our read-outs. We measure the resistance by timing-differences in the RC-circuit.} + \end{figure} + + We drive the input of the RC-circuit to high (3.3V) when we measure low at the input (approximately at 1.8V). + Conversely, we drive the input to low (0V) when we measure high at the input (approximately 2.8V). + We can simulate this behaviour with a sinusoidal resistance for demonstration: + \begin{figure}[H] + \centering + \includegraphics[max width=0.75\linewidth]{Graph/simulation.pdf} + \caption{Simulation of the timing-differences with a sinusoidal resistance} + \end{figure} + + To get our measurement data we use the time for a charge-cycle. + \section{Interpretation of Timings} + Our program now gets a series of charging times $\Delta t$. + How do we interpret these values to figure out whether the washing machine is on or off? + To figure that out, we make two assumptions: + \begin{enumerate} + \item When we begin measuring, the machine is off + \item The readings from our sensor have a gaussian distribution + \end{enumerate} + + \begin{figure}[H] + \centering + \includegraphics[max width=0.75\linewidth]{Graph/distribution.pdf} + \caption{Two gaussian distributions of our $\Delta t$ timings, one centered around $300\mu s$ the other around $1300\mu s$} + \end{figure} + + Decision theory tells us that the optimal way to decide whether the machine is on or off is to look + whether the measurement is above or below the threshold: + \begin{equation} + \footnotesize + \frac{-\mu_1\cdot \sigma_2^{2}+\mu_2\cdot \sigma_1^{2}\pm\sqrt{\mu_1^{2}-2\cdot \mu_1\cdot \mu_2+\mu_2^{2}+2\cdot \sigma_1^{2} \ln\left(\sigma_1\right)-2\cdot \sigma_1^{2} \ln\left(\sigma_2\right)-2\cdot \sigma_2^{2} \ln\left(\sigma_1\right)+2\cdot \sigma_2^{2} \ln\left(\sigma_2\right)} \abs{\sigma_1} \abs{\sigma_2}}{(\sigma_1^{2}-\sigma_2^{2})} + \end{equation} + This is rather complex, so if we assume equal variance we get that the optimal boundary lies at: + \begin{equation} + \frac{\mu_1 + \mu_2}{2} + \end{equation} + + \subsection{Measurement of Gaussians} + We do not want hard-coded gaussians in our program. Therefore, the gaussians are guessed continuously from the measurements with the following method: + \begin{align} + \mu[k] &= \mu[k-1] \lambda + \Delta t[k] \cdot (1-\lambda)\\ + \tilde{\sigma}^2[k] &= \tilde{\sigma}^2[k-1] \cdot \lambda + (\Delta t[k])^2 (1-\lambda)\\ + \sigma^2[k] &= \tilde{\sigma}^2[k] - (\mu[k])^2 + \end{align} + + The paramter $\lambda$ defines how much of the old value we are going to retain. + Since we want the Gaussian to be quickly updated at the beginning, and only slowly update to small changes of lighting after it has stabilized, + we increase $\lambda$ as the number of measurements progresses. + + \begin{equation} + \lambda[k] = \min\left\{0.99999, 0.75 + 0.25\frac{k}{1000}\right\} + \end{equation} + \subsection{Bootstrapping} + We make no a-priori assumption on these gaussians. + This results in a problem: While we know the decision boundary between two gaussians, + we have no idea on how the on-state is distributed at the beginning. + Hence we use a crutch: When the machine is initialized, it decides to switch to ON + if there is a value outside of $5\sigma_{OFF}$. + After that first decision we can train the $(\mu_{ON},\sigma_{ON}^2)$ and use the + proper decider. + + \subsection{Outlier Resistancy} + If we redecided every time a single value across the decision boundary occurded, + we would switch between states all the time. + Hence, we only switch the guessed state once $100$ \emph{consecutive} values have been measured. + + \subsection{Updates During Possible Transition} + What do we do when we detect an outlier? + Do we train our gaussian with it, or do we ignore it? + Both versions have drawbacks: Training the gaussian yields a large spurious variance during a transition. + Not using it for training could possibly block out the adaption to large variance changes. + + Hence we do something in between: when we are detecting outliers, we make a copy of the gaussian before the outliers + started, update the gaussian with the outliers and when we decide to switch, we restore the gaussian to its copy before the + outliers. + \chapter{Machine Learning} + We now want to guess how long it will take until the washing is finished. + + This is complicated by the fact that the washing machine has multiple programs, which all have different durations. + Additionally, program duration can vary by how much laundry is in the machine. + + \section{Learning Program Lengths} + To address this issue, we use the K-Means clustering algorithm. + Since we do not know the number of clusters, we start with $k=1$ and increase k, + until the maximal variance of the cluster is below a threshold. + + We check for the standard deviation to be lower than a threshold in two modes: absolute and relative. + \begin{itemize} + \item The absolute mode checks whether the standard deviation is below a certain value, e.g. below 30 minutes. + \item The relative mode checks whether the standard deviation from the mean is below a certain percentage, + e.g. we allow only standard deviations smaller than 10\% of the mean + \end{itemize} + + Hence, we increase $k$ until the two following conditions hold + \begin{align} + \max_{i} \sigma_i &< \tau_{abs}\\ + \max_{i} \frac{\sigma_i}{\mu_i} &< \tau_{rel} + \end{align} + + \section{Guessing the Remaining Time} + We now have clusters of gaussians, and want to know: How long is a washing going to take? + + \begin{align} + \hat{t} &= \argmax_{t} P[T=t] \\ + & = \argmax_{t} \lim_{\Delta \to 0} \frac{P[T\leq t] - P[T \leq t+\Delta]}{\Delta} \\ + & = \argmax_{t} f(t) + \end{align} + + We now have to know $P[T\leq t]$. This is easily calculated through marginalization with respect to the Cluster $C$ being active: + \begin{equation} + P[T\leq t] = \sum_{i} P[T\leq t | C=i] \cdot P[C=i] + \end{equation} + $P[C=i]$ is the a-priori distribution of cluster $i$ being active. + This we know since we know the number of points inside that cluster. + + Therefore, it holds that: + \begin{align} + \hat{t} &= \argmax_{t} \sum_i P[C=i] \cdot f_{C=i}(t)\\ + &= \sum_i P[C=i] \cdot \mu_i + \end{align} + + Which is exactly the expectation $E[T]$. + + \subsection{Improving the Guess} + This can be improved, since we have some additional information: + We know, the program has been running for $t_0$ seconds. + Hence, we rewrite our guess to: + + \begin{align} + \hat{t} &= \argmax_{t} P[T = t | T\geq t_0] \\ + &= \argmax_t \sum_i P[C=c_i | T \geq t_0] \cdot P[T = t | C=i \land T \geq t_0]\\ + &= \sum_i P[C=c_i | T \geq t_0] \cdot \argmax_t P[T = t | C=i \land T \geq t_0] + \end{align} + + The term $P[T=t|C=i \land T\geq t_0]$ is easily calculated: it is just a cut and rescaled version of a normal gaussian. + + \begin{figure}[H] + \centering + \includegraphics[max width=0.4\linewidth]{Graph/Cut_Gaussians/Cut_Gaussian.pdf}\hspace{2em} + \includegraphics[max width=0.4\linewidth]{Graph/Cut_Gaussians/Cut_Gaussian_-2.pdf}\\ + \includegraphics[max width=0.4\linewidth]{Graph/Cut_Gaussians/Cut_Gaussian_0.pdf}\hspace{2em} + \includegraphics[max width=0.4\linewidth]{Graph/Cut_Gaussians/Cut_Gaussian_2.pdf} + \caption{Gaussians given the knowledge that the variable is bigger than a certain value} + \end{figure} + + It is easy to see that the maximum lies at $\argmax f_{N|T\geq >t_0}(t) = \left\{\begin{matrix} \mu & t\leq t_0 \\ t_0 & t > t_0\end{matrix}\right.$ + + The a-priori distribution of $P[C=c | T \geq t_0]$ is a more complex matter, but can be calculated using Bayes Theorem: + \begin{align} + P[C=c | T \geq t_0] &= \frac{P[C=c]}{P[T \geq t_0]} \cdot P[T \geq t_0 | C=c]\\ + &= \frac{P[C=c]}{\sum_i P[C=c]\cdot P[T \geq t_0 | C=c]} \cdot P[T \geq t_0 | C=c] + \end{align} + This expression, while computationally difficult for large number of clusters, can be evaluated. + \section{Guessing the Cluster} + Now, since we are here anyways, we can as well guess the most probable cluster, too. + \begin{equation} + \hat{C} = \argmax_{i} P[C=i | T \geq t_0] + \end{equation} + This can, again, be addressed by the Bayes Theorem: + \begin{align} + \argmax_i P[C=i | T \geq t_0] &= \argmax_i P[T \geq t_0 | C=i] \cdot \frac{P[C=i]}{P[T \geq t_0]} + \intertext{Now, since $P[T \geq t_0]$ does not depend on $i$, we can ignore it for the maximization:} + &= \argmax_i P[T \geq t_0]\cdot P[C=i] + \end{align} + Since the number of clusters is quite small, we can evaluate this expression for all possible $i$ and choose the largest. +\end{document} diff --git a/doc/Graph/Cut_Gaussians/Cut_Gaussian.asy b/doc/Graph/Cut_Gaussians/Cut_Gaussian.asy @@ -0,0 +1,49 @@ +import graph; +real normalpdf(real x, real mu, real var){ + return 1/sqrt(2*pi*var) * exp(-((x-mu)^2/(4*var))); +} +real pdf(real x){ + return normalpdf(x,0,1); +} +real normalcdf(real to, real mean, real variance){ + real z = (to-mean)/sqrt(2*variance); + real t = 1/(1+0.3275911*abs(z)); + real a1 = 0.254829592; + real a2 = -0.284496736; + real a3 = 1.421413741; + real a4 = -1.453152027; + real a5 = 1.061405429; + real erf = 1-(((((a5*t + a4)*t) + a3)*t + a2)*t+ a1)*t*exp(-z*z); + real sign = 1; + if(z < 0){ + sign = -1; + } + return (1/2)*(1+sign*erf); +} + +real cutpdf(real x, real x0, real mu, real var){ + if(x<x0){ + return 0; + } + return normalpdf(x,mu,var)*1/(1-normalcdf(x0,mu,var)); +} + +real mean=0; +real variance=1; + +picture p; +size(p,5cm,5cm,IgnoreAspect); +draw(p, graph(new real(real x){return normalpdf(x, mean, variance);}, mean-5*variance,mean+5*variance)); +xaxis(p, "$t$",BottomTop, LeftTicks); +yaxis(p, "$f_N(t)$",LeftRight,RightTicks(trailingzero)); +shipout("Cut_Gaussian", p); + +real[] cuts={-2,0,2}; +for(real cut: cuts){ + picture p; + size(p,5cm,5cm,IgnoreAspect); + draw(p, graph(new real(real x){return cutpdf(x, cut, mean, variance);}, mean-5*variance,mean+5*variance)); + xaxis(p, "$t$",BottomTop, LeftTicks); + yaxis(p, "$f_{N|T \geq "+string(cut)+"}(t)$",LeftRight,RightTicks(trailingzero)); + shipout("Cut_Gaussian_"+string(cut), p); +} diff --git a/doc/Graph/distribution.asy b/doc/Graph/distribution.asy @@ -0,0 +1,21 @@ +real gauss(real x, real mu, real sigma){ + return 1/(sqrt(2*pi)*sigma)*exp(-((x-mu)/(1.41*sigma))**2); +} + +real on_gauss(real x){ + return gauss(x, 300, 200); +} +real off_gauss(real x){ + return gauss(x, 1300, 200); +} +import graph; +size(10cm,5cm,IgnoreAspect); +draw(graph(on_gauss, -500,2000, operator ..), green); +draw(graph(off_gauss, -500,2000, operator ..), red); +xaxis("$\Delta t$",BottomTop,LeftTicks); +yaxis("$P$",LeftRight,RightTicks(trailingzero)); +draw((300,on_gauss(300)) -- (300,0), dashed+green); +draw((1300,off_gauss(1300)) -- (1300,0), dashed+red); +label("ON", (300,0), S,green); +label("OFF", (1300,0), S,red); +draw((800, max(currentpicture,true).y) -- (800,0), scale(1.5)*blue); diff --git a/doc/Graph/simulation.gnuplot b/doc/Graph/simulation.gnuplot @@ -0,0 +1,4 @@ +file='simulation.csv' +set key right bottom +set y2tics auto nomirror +plot file using 1:2 w l t 'Output Voltage', file using 1:4 axes x1y2 w l t 'Resistance', 1.8 w l lc "black" dt 2 t '', 2.8 w l dt 2 lc "black" t '' diff --git a/doc/Graph/simulator.m b/doc/Graph/simulator.m @@ -0,0 +1,36 @@ +#!/usr/bin/env octave + +R=200; +C=1e-6; +dt=1e-5; +vh=3.3; +vl=0; +vhh=2.8; +vhl=1.8; + +te=0.1; + +sincoeff=pi/te; + +Rf=@(t) 10e3 - sin(sincoeff*t)*(10e3-1e3); + +tv=0:dt:te; +u=zeros(length(tv),1); +uiv=zeros(length(tv),1); +rv=ones(length(tv),1)*Rf(0); +ui=vh; +con=dt/(R*C); +for i=1:length(tv)-1 + if u(i) > vhh + ui=vl; + end + if u(i) < vhl + ui=vh; + end + uiv(i)=ui; + R=Rf(tv(i)); + rv(i)=R; + u(i+1) = dt/(R*C)*(ui-u(i))+u(i); +end + +dlmwrite("simulation.csv", [tv', u, uiv, rv], "\t") diff --git a/doc/Makefile b/doc/Makefile @@ -0,0 +1,18 @@ + + +Graph/simulation.csv: Graph/simulator.m + ./Graph/simulator.m + +Graph/simulation.pdf: Graph/simulation.gnuplot Graph/simulation.csv + +Documentation.pdf: Documentation.tex Graph/simulation.pdf Graph/Cut_Gaussians/Cut_Gaussian.pdf Graph/distribution.pdf + lualatex $< + +%.pdf: %.gnuplot + gnuplot -e "set terminal \"pdfcairo\" enhanced; set output \"$@\"" $< + +%.pdf: %.tex + pdflatex $< + +%.pdf: %.asy + asy -f pdf $^ -o $@ diff --git a/doc/Schematics.tex b/doc/Schematics.tex @@ -3,10 +3,6 @@ \usepackage[europeanresistors]{circuitikz} \begin{document} \begin{tikzpicture} -\draw[o-] (0,0) -- (1,0); -\draw (1,0) to[phR,l={LDR}] (3,0); -\draw[-o] (3,0) -- (5,0); -\draw (3.5,0) to[C,l={$1\mathrm{\mu F}$}] (3.5,-2) node[ground]{}; -\fill (3.5,0) circle(0.5ex); +\input{Schematics_Core.tex} \end{tikzpicture} \end{document} diff --git a/doc/Schematics_Core.tex b/doc/Schematics_Core.tex @@ -0,0 +1,5 @@ +\draw[o-] (0,0) -- (1,0); +\draw (1,0) to[phR,l={LDR}] (3,0); +\draw[-o] (3,0) -- (5,0); +\draw (3.5,0) to[C,l={$1\mathrm{\mu F}$}] (3.5,-2) node[ground]{}; +\fill (3.5,0) circle(0.5ex);