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:
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);