<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Mathias Dalheimer</title>
 <link href="http://gonium.net/atom.xml" rel="self"/>
 <link href="http://gonium.net/"/>
 <updated>2012-02-20T19:44:04+01:00</updated>
 <id>http://gonium.net/</id>
 <author>
   <name>Mathias Dalheimer</name>
   <email>md@gonium.net</email>
 </author>

 
 <entry>
   <title>Warum ACTA eher so meh ist.</title>
   <link href="http://gonium.net//blog/2012/02/09/warum-acta-eher-so-meh-ist"/>
   <updated>2012-02-09T00:00:00+01:00</updated>
   <id>id:/blog/2012/02/09/warum-acta-eher-so-meh-ist</id>
   <content type="html">&lt;p&gt;ACTA, das &lt;a href='http://de.wikipedia.org/wiki/Anti-Counterfeiting_Trade_Agreement'&gt;Anti-Counterfeiting Trade Agreement&lt;/a&gt;, ist momentan in aller Munde. Das ist eigentlich schon bemerkenswert für ein derart abstraktes Abkommen &amp;#8212; es geht schließlich nicht direkt um unmittelbare Konsequenzen, im Gegenteil: ACTA ist überaus löchrig formuliert und für den Laien kaum nachvollziehbar. Auch in der Tagespresse spielt das Thema kaum eine Rolle. Was steckt also dahinter? Was veranlasst polnische Politiker, mit &lt;a href='http://www.spiegel.de/fotostrecke/fotostrecke-77904.html'&gt;Guy Fawkes-Masken im Parlament&lt;/a&gt; zu sitzen?&lt;/p&gt;

&lt;h3 id='fotos_aus_disneyland'&gt;Fotos aus Disneyland&lt;/h3&gt;

&lt;p&gt;Nehmen wir einmal einen Teilbereich von ACTA, der sich auf Diensteanbieter (Provider) im Internet bezieht. Demnach sind Provider für Urheberrechtsverletzungen ihrer Nutzer auf ihren Angeboten haftbar. Die Provider wären damit (obwohl das nicht direkt in ACTA so drin steht) gezwungen, Inhalte ihrer Kunden vor der Veröffentlichung auf Urheberrechtsverletzungen zu überprüfen.&lt;br /&gt;Das da ist zum Beispiel &lt;a href='http://www.flickr.com/photos/tostie14/65962067/'&gt;Kevin (CC-BY):&lt;/a&gt;&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='375' src='/images/20120209-acta-kevin.jpg' width='500' /&gt;
&lt;/div&gt;
&lt;p&gt;Kevin war offenbar zu Gast in irgendeinem Disneyland und hat sich einen Hut gekauft. Eigentlich ein harmloses Bild, kein Problem, bitte weitergehen &amp;#8212; derartiges findet sich in Massen auf Twitter &lt;a href='https://twitter.com/#!/search/%23mickeymouse'&gt;(Suche nach #mickeymouse)&lt;/a&gt;. Allerdings hat Twitter unter ACTA ein Problem mit dem Bild von Kevin: Um die Rechte von Disney zu wahren (und aus der Störerhaftung wegen Urheberrechtsverletzungen zu vermeiden) müsste Twitter alle Bilder automatisiert auf das Vorhandensein von Mickey Mäusen scannen. Das legitime Bild von Kevin ist allerdings für einen Computer verdammt schwer von einer potentiellen Urheberrechtsverletzung wie das &lt;a href='https://twitter.com/#!/__MickeyMouse_'&gt;Profilbild hier&lt;/a&gt; zu unterscheiden. Im Zweifelsfall tut Twitter aber aus eigenem Interesse gut daran, Kevins Bild nicht anzunehmen.&lt;/p&gt;

&lt;p&gt;In letzter Konsequenz bedeutet ACTA für Twitter also, möglichst viele Inhalte präventiv zu filtern, sprich: zu zensieren. Da dies realistisch nur automatisiert geschehen kann, muss sich ein Diensteanbieter also entscheiden, wie &amp;#34;scharf&amp;#34; die Filter eingestellt werden müssen. Im Zweifelsfall werden tendenziell mehr Informationen weggefiltert als zwingend notwendig.&lt;/p&gt;

&lt;h3 id='privatunternehmen_zensieren'&gt;Privatunternehmen zensieren&lt;/h3&gt;

&lt;p&gt;Das oben beschriebene Szenario lässt sich aus dem &lt;a href='http://register.consilium.europa.eu/pdf/de/11/st12/st12196.de11.pdf'&gt;deutschen Vertragstext&lt;/a&gt; direkt nicht ableiten. Interessanter ist vielmehr, dass der Text sehr viel Raum für die Interpretation zulässt. Die EU-Kommission hat eine Roadmap für eine sog. &amp;#34;Enforcement-Richtlinie&amp;#34; vorgelegt, welche das ACTA-Handelsabkommen in europäisches Recht umsetzen soll. In dem Entwurf heisst es (gefunden bei &lt;a href='http://www.internet-law.de/2012/02/acta-und-die-enforcement-richtlinie.html'&gt;RA Stadler&lt;/a&gt;):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Other possible impacted parties may include various intermediaries such as Internet Platforms, Internet Service Providers or transport establishments who could play an important role in the fight against infringements of intellectual property rights.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Diese Konkretisierung von ACTA lässt sich sehr genau auf Kevins Bild anwenden. Dazu kommt, dass hier auch ISPs direkt angesprochen werden. In Analogie zu Twitter, die neue Tweets und Bilder im Hinblick auf ihren Inhalt kontrollieren müssten, müssten also auch normale Internetanbieter mein Surfverhalten überwachen.&lt;/p&gt;

&lt;p&gt;Das ACTA-Abkommen ist kein Gesetz, es ist ein Handelsabkommen zwischen Staaten. Es wurde hinter verschlossenen Türen ohne Beteiligung der Internetnutzer ausgehandelt und muss &amp;#8212; sollte es ratifiziert werden &amp;#8212; in europäisches Recht umgesetzt werden. Gleichzeitig wird man sich bei der Umsetzung von härteren Maßnahmen auf &amp;#34;internationale Verpflichtungen&amp;#34; berufen. Insofern hat &lt;a href='http://www.internet-law.de/2012/02/ist-die-acta-hysterie-berechtigt.html'&gt;RA Stadler&lt;/a&gt; Unrecht, wenn er sagt, dass im Prinzip alle Forderungen von ACTA im deutschen Recht sowieso umgesetzt sind.&lt;/p&gt;

&lt;h3 id='the_coming_war_on_general_computation'&gt;The Coming War on General Computation&lt;/h3&gt;

&lt;p&gt;Cory Doctorov hat auf dem 28C3 einen spannenden Vortrag gehalten, der den Blick für die gegenwärtigen Konflikte schärft (&lt;a href='https://github.com/jwise/28c3-doctorow/blob/master/transcript.md'&gt;Mitschrift &amp;#34;The Coming War on General Computation&amp;#34;&lt;/a&gt;). Ich möchte diesen Vortrag uneingeschränkt jedem empfehlen, der den großen Zusammenhang von ACTA etc. verstehen möchte. Der Talk ist auf &lt;a href='http://www.youtube.com/watch?v=HUEvRyemKSg'&gt;Youtube&lt;/a&gt; und auch zum &lt;a href='http://mirror.fem-net.de/CCC/28C3/mp4-h264-HQ/28c3-4848-en-the_coming_war_on_general_computation_h264.mp4'&gt;Download&lt;/a&gt; verfügbar.&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='422' src='/images/20120209-acta-doctorow.png' width='580' /&gt;
&lt;/div&gt;
&lt;p&gt;Unsere Gesellschaft hat bis jetzt keinen sinnvollen Ausgleich zwischen den legitimen Interessen der Urheberrechtsinhaber und der Nutzer von Computern gefunden. Computer sind Maschinen, die Informationen verarbeiten, indem sie diese &lt;em&gt;kopieren&lt;/em&gt; und verändern. Wenn ich eine Email schreibe und versende, verschwindet sie nicht von meinem Computer und taucht beim Empfänger wieder auf. Im Gegenteil, meine Email wird zu meinem Mailserver kopiert, dann zu weiteren Mailservern, bis sie schließlich beim Empfänger ankommt. Computer sind darauf angewiesen, perfekte Kopien schnell anzufertigen und diese gegebenenfalls zu verändern.&lt;/p&gt;

&lt;p&gt;Statt über nachhaltige Geschäftsmodelle nachzudenken versucht die Lobby der Rechteinhaber, meinen Computer seine Kernkompetenz (das Kopieren) durch künstliche Beschränkungen wie DRM abzugewöhnen. ACTA etc. geht nun noch einen Schritt weiter und versucht, Privatunternehmen als Hilfssheriff zu missbrauchen. Dabei ist absehbar, dass diese Unternehmen Inhalte falsch einschätzen und zensieren.&lt;/p&gt;

&lt;p&gt;&lt;a href='http://blog.fefe.de/?ts=b1ce8871'&gt;Fefe&lt;/a&gt; hat die Relationen treffend festgestellt: Die &lt;a href='http://de.wikipedia.org/wiki/Unternehmensgruppe_Theo_M%C3%BCller'&gt;Müller Milch Gruppe&lt;/a&gt; hat einen Jahresumsatz von 2,3 Milliarden Euro, die &lt;a href='http://www.musikindustrie.de/jwb-umsatz-10/'&gt;Musikindustrie&lt;/a&gt; liegt bei 1,7 Milliarden Euro. Genausowenig, wie ich akzeptieren würde, dass ein Konzern der Milchwirtschaft meine Freiheit einschränkt, akzeptiere ich das von der noch kleineren Musikverlegerlobby.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Constraint Programming und Sudokus: 20ms</title>
   <link href="http://gonium.net//blog/2011/09/18/constraint-programming-und-sudokus--20ms"/>
   <updated>2011-09-18T00:00:00+02:00</updated>
   <id>id:/blog/2011/09/18/constraint-programming-und-sudokus--20ms</id>
   <content type="html">&lt;p&gt;Sudoku ist in meinen Augen ein eher langweiliges Puzzle, ich steh mehr auf Solitaire. Aber Sudoku eignet sich sehr gut als Beispielproblem für Constraint Programming. Die Regeln sind einfach: Ziffern von 1 bis 9 werden in eine 9x9-Matrix eingefügt, sodass&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;in jeder Zeile keine Ziffer doppelt vorkommt&lt;/li&gt;

&lt;li&gt;in jeder Spalte keine Ziffer doppelt vorkommt&lt;/li&gt;

&lt;li&gt;in jeder kleinen 3x3-Submatrix keine Ziffer doppelt vorkommt.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Üblicherweise sind je nach Schwierigkeitsgrad unterschiedlich viele Zahlen vorgegeben, sodass der Lösungsraum beschränkt wird. Bei schwereren Sudokus ist der Lösungsraum so stark beschränkt, dass nur eine Lösung möglich ist.&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img alt='CC-BY Tim Psych' class='top left' height='375' src='/images/20110918-sudoku.jpg' width='500' /&gt;
&lt;p&gt;Bild: &lt;a href='http://www.flickr.com/photos/01-17-05_t-m-b/2156513671'&gt;CC-BY Tim Psych&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Zum Lösen von Sudokus gibt es &lt;a href='http://en.wikipedia.org/wiki/Algorithmics_of_sudoku'&gt;verschiedene Ansätze&lt;/a&gt;, z.B. Backtracking oder auch brute-force Algorithmen. Als Beispiel sei folgender Algorithmus genannt:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='c'&gt; &lt;span class='n'&gt;solve&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;puzzle&lt;/span&gt;&lt;span class='p'&gt;[][])&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;all&lt;/span&gt; &lt;span class='n'&gt;cells&lt;/span&gt; &lt;span class='n'&gt;are&lt;/span&gt; &lt;span class='n'&gt;known&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;
      &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='n'&gt;success&lt;/span&gt; &lt;span class='n'&gt;immediately&lt;/span&gt;
    &lt;span class='k'&gt;for&lt;/span&gt; &lt;span class='n'&gt;each&lt;/span&gt; &lt;span class='n'&gt;unknown&lt;/span&gt; &lt;span class='n'&gt;cell&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;x&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;y&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;
        &lt;span class='k'&gt;for&lt;/span&gt; &lt;span class='n'&gt;each&lt;/span&gt; &lt;span class='n'&gt;possible&lt;/span&gt; &lt;span class='n'&gt;digit&lt;/span&gt; &lt;span class='mf'&gt;1..9&lt;/span&gt;&lt;span class='o'&gt;:&lt;/span&gt;
            &lt;span class='n'&gt;puzzle&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;x&lt;/span&gt;&lt;span class='p'&gt;][&lt;/span&gt;&lt;span class='n'&gt;y&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;digit&lt;/span&gt;
            &lt;span class='k'&gt;if&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;valid&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;puzzle&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt; &lt;span class='n'&gt;solve&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;puzzle&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
            &lt;span class='n'&gt;puzzle&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;x&lt;/span&gt;&lt;span class='p'&gt;][&lt;/span&gt;&lt;span class='n'&gt;y&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;unknown&lt;/span&gt;
 &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Dieser rekursive Algorithmus versucht einfach für alle 81 Felder, eine Zahl zu finden, die keine Constraints verletzen. Dabei kostet der Aufruf&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='c'&gt; &lt;span class='k'&gt;if&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;valid&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;puzzle&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;nochmal erheblich Rechenzeit, da sowohl Spaltenconstraint als auch Zeilen- und Submatrixconstraint geprüft werden müssen.&lt;/p&gt;

&lt;h3 id='was_ist_constraint_programming'&gt;Was ist Constraint Programming?&lt;/h3&gt;

&lt;p&gt;Im Gegensatz zum imperativen Algorithmus oben (&amp;#8220;tue A, dann B, dann &amp;#8230;&amp;#8221;) gehört &lt;a href='http://en.wikipedia.org/wiki/Constraint_programming'&gt;Constraint Programming (CP)&lt;/a&gt; zu den deklarativen Ansätzen, d.h. es werden nur die Anforderungen an eine valide Lösung spezifiziert. Der Lösungsweg wird dann dynamisch durch die Programmierumgebung bestimmt.&lt;/p&gt;

&lt;p&gt;Formal kann man ein &lt;em&gt;Constraint Satisfaction Problem (CSP)&lt;/em&gt; wie folgt definieren: Gegeben sei eine Menge von &lt;em&gt;n&lt;/em&gt; Entscheidungsvariablen &lt;em&gt;x&lt;sub&gt;i&lt;/sub&gt;&lt;/em&gt;. Der zulässige Wertebereich &lt;em&gt;D&lt;sub&gt;i&lt;/sub&gt;&lt;/em&gt; der Entscheidungsvariablen wird die Domäne der Entscheidungsvariable genannt und kann beliebige Symbole (Integer, Reals, oder auch Mengen) enthalten.&lt;/p&gt;

&lt;p&gt;Ein Constraint &lt;em&gt;c(x&lt;sub&gt;1&lt;/sub&gt;, x&lt;sub&gt;2&lt;/sub&gt;, &amp;#8230;, x&lt;sub&gt;n&lt;/sub&gt;)&lt;/em&gt; ist eine Relation, die den Lösungsraum einschränkt. Wenn &lt;em&gt;(x&lt;sub&gt;1&lt;/sub&gt;, x&lt;sub&gt;2&lt;/sub&gt;, &amp;#8230;, x&lt;sub&gt;n&lt;/sub&gt;)&lt;/em&gt; in &lt;em&gt;S&lt;/em&gt; liegt und &lt;em&gt;S&lt;/em&gt; eine Untermenge von &lt;em&gt;D&lt;sub&gt;1&lt;/sub&gt; x &amp;#8230; x D&lt;sub&gt;n&lt;/sub&gt;&lt;/em&gt; ist, dann ist der Constraint erfüllt. Üblicherweise wird ein Constraint als Funktion &lt;em&gt;f&lt;/em&gt; beschrieben, so dass &lt;em&gt;f(x&lt;sub&gt;1&lt;/sub&gt;, x&lt;sub&gt;2&lt;/sub&gt;, &amp;#8230;, x&lt;sub&gt;n&lt;/sub&gt;) = 1&lt;/em&gt; genau dann wenn das Constraint erfüllt ist. Ein &lt;em&gt;Constraint Satisfaction Problem (CSP)&lt;/em&gt; kann man nun wie folgt definieren:&lt;/p&gt;

&lt;p&gt;Gegeben sind &lt;em&gt;n&lt;/em&gt; Domänen &lt;em&gt;D&lt;sub&gt;1&lt;/sub&gt; x &amp;#8230; x D&lt;sub&gt;n&lt;/sub&gt;&lt;/em&gt; und &lt;em&gt;m&lt;/em&gt; Constraints &lt;em&gt;f&lt;sub&gt;1&lt;/sub&gt;, &amp;#8230;, f&lt;sub&gt;m&lt;/sub&gt;&lt;/em&gt;. Eine Lösung &lt;em&gt;(x&lt;sub&gt;1&lt;/sub&gt;, x&lt;sub&gt;2&lt;/sub&gt;, &amp;#8230;, x&lt;sub&gt;n&lt;/sub&gt;)&lt;/em&gt; erfüllt&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;f(x&lt;sub&gt;1&lt;/sub&gt;, x&lt;sub&gt;2&lt;/sub&gt;, &amp;#8230;, x&lt;sub&gt;n&lt;/sub&gt;) = 1&lt;/em&gt; für &lt;em&gt;1 &amp;leq; k &amp;leq; m&lt;/em&gt;&lt;/li&gt;

&lt;li&gt;x&lt;sub&gt;j&lt;/sub&gt; ist Element von &lt;em&gt;D&lt;sub&gt;j&lt;/sub&gt;&lt;/em&gt; für &lt;em&gt;1 &amp;leq; j &amp;leq; n&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Um aus einem CSP ein Optimierungsproblem zu formulieren kann man natürlich noch eine Zielfunktion hinzufügen, z.B.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Minimize/Maximize &lt;em&gt;g(x&lt;sub&gt;1&lt;/sub&gt;, x&lt;sub&gt;2&lt;/sub&gt;, &amp;#8230;, x&lt;sub&gt;n&lt;/sub&gt;)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Man sieht: Die Anforderungen an eine Lösung sind sehr flexibel und können quasi alles abbilden. Das Schöne an Constraint Programming: Es ist eine eigenständige Disziplin und hat viele Bibliotheken hervorgebracht. Effektiv kann man also sein Problem beschreiben und die Bibliothek den Rest machen lassen. Insbesondere für sich ändernde Problemstellungen ist diese Methode praktisch. Eine weitergehende Einführung in Constraint Programming ist in &lt;a href='http://www.jstor.org/pss/25062753'&gt;Lustig/Puget, &amp;#8220;Program Does Not Equal Program&amp;#8221;&lt;/a&gt; zu finden.&lt;/p&gt;

&lt;h3 id='sudoku_als_csp'&gt;Sudoku als CSP&lt;/h3&gt;

&lt;p&gt;Zurück zum Sudoku: Die eingangs beschriebenen Restriktionen für eine korrekte Lösung lassen sich direkt in Constraints für einen CSP-Solver übersetzen. Dabei unterstützen viele Solver den Benutzer mit vorgefertigten, mächtigen Constraints. Ein Beispiel dafür ist das &lt;em&gt;alldifferent&lt;/em&gt;-Constraint, mit dem man schnell beschreiben kann, dass alle Elemente in einer Menge unterschiedliche Werte haben sollen. Das Constraint ist quasi für Sudoku gemacht.&lt;/p&gt;

&lt;p&gt;Mein Code für einen einfachen CSP-basierten Sudokusolver ist auf &lt;a href='https://github.com/gonium/sudoku'&gt;Github (gonium/sudoku)&lt;/a&gt; zu finden. Ich benutze die C++-Solverbibliothek &lt;a href='http://www.gecode.org/'&gt;gecode&lt;/a&gt;, d.h. diese muss installiert sein, bevor der Code übersetzt werden kann. Ein einfaches&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt; &lt;span class='nv'&gt;$ &lt;/span&gt;make
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;sollte das Binary build/src/sudokusolve erzeugen. Ein Aufruf sieht so aus:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt; &lt;span class='nv'&gt;$ &lt;/span&gt;./build/src/sudokusolve
 Setup - Using this sudoku:
  9 0 0 1 0 4 0 0 2
  0 8 0 0 6 0 0 7 0
  0 0 0 0 0 0 0 0 0
  4 0 0 0 0 0 0 0 1
  0 7 0 0 0 0 0 3 0
  3 0 0 0 0 0 0 0 7
  0 0 0 0 0 0 0 0 0
  0 3 0 0 7 0 0 8 0
  1 0 0 2 0 9 0 0 4
 Setup - row constraints
 Setup - column constraints
 Setup - square constraints
   9 5 7 1 8 4 3 6 2 
   2 8 1 9 6 3 4 7 5 
   6 4 3 7 2 5 1 9 8 
   4 9 6 3 5 7 8 2 1 
   8 7 5 4 1 2 9 3 6 
   3 1 2 8 9 6 5 4 7 
   7 2 9 5 4 8 6 1 3 
   5 3 4 6 7 1 2 8 9 
   1 6 8 2 3 9 7 5 4 
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Das hier verwendete Sudoku ist fest einkompiliert (hey, das hier ist nur ein Proof of Concept!). Es handelt sich dabei um die Instanz &lt;a href='http://en.wikipedia.org/wiki/Sudoku_algorithms#Solving_Sudokus_by_a_brute-force_algorithm'&gt;&amp;#8220;Star Burst Leo&amp;#8221;&lt;/a&gt;, von der bekannt ist, dass sie nur eine Lösung besitzt. Die Laufzeit für den Solver beträgt auf meinem Notebook ca. 20ms - nicht schlecht für eine fast automatische Methode. Der Solveraufruf in src/sudokusolve.cpp sieht so aus:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='cpp'&gt; &lt;span class='n'&gt;SudokuSolver&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt; &lt;span class='n'&gt;s&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='k'&gt;new&lt;/span&gt; &lt;span class='n'&gt;SudokuSolver&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;star_burst_leo&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
 &lt;span class='n'&gt;Gecode&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;DFS&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='n'&gt;SudokuSolver&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;e&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;s&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
 &lt;span class='k'&gt;delete&lt;/span&gt; &lt;span class='n'&gt;s&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
 &lt;span class='k'&gt;while&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;SudokuSolver&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt; &lt;span class='n'&gt;result&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;e&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;next&lt;/span&gt;&lt;span class='p'&gt;())&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
   &lt;span class='n'&gt;result&lt;/span&gt;&lt;span class='o'&gt;-&amp;gt;&lt;/span&gt;&lt;span class='n'&gt;print&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;std&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;cout&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
   &lt;span class='k'&gt;delete&lt;/span&gt; &lt;span class='n'&gt;result&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
 &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Nice and clean. Die while-Schleife gibt einfach alle gefundenen Lösungen aus &amp;#8212; diese Instanz hat jedoch nur eine. Der Solver verbirgt sich hinter Gecode::DFS. Dies ist ein generischer Solver, der eine Depth-First-Search benutzt. In der Klasse SudokuSolver wird das CSP formuliert. Aus lib/sudokusolver.cpp:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='cpp'&gt; &lt;span class='n'&gt;std&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;cout&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='s'&gt;&amp;quot;Setup - Using this sudoku:&amp;quot;&lt;/span&gt; 
 &lt;span class='k'&gt;for&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='mi'&gt;9&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='o'&gt;++&lt;/span&gt;&lt;span class='p'&gt;){&lt;/span&gt;
   &lt;span class='k'&gt;for&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt; &lt;span class='n'&gt;j&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='n'&gt;j&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='mi'&gt;9&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='n'&gt;j&lt;/span&gt;&lt;span class='o'&gt;++&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
     &lt;span class='kt'&gt;int&lt;/span&gt; &lt;span class='n'&gt;v&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;sudokuproblem&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='mi'&gt;9&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='n'&gt;j&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
     &lt;span class='n'&gt;std&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;cout&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='s'&gt;&amp;quot; &amp;quot;&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='n'&gt;v&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
     &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;v&lt;/span&gt; &lt;span class='o'&gt;!=&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
       &lt;span class='n'&gt;rel&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;m&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;j&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;),&lt;/span&gt; &lt;span class='n'&gt;IRT_EQ&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;v&lt;/span&gt; &lt;span class='p'&gt;);&lt;/span&gt;
   &lt;span class='p'&gt;}&lt;/span&gt;
   &lt;span class='n'&gt;std&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;cout&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='n'&gt;std&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;endl&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
 &lt;span class='p'&gt;}&lt;/span&gt;
 &lt;span class='n'&gt;std&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;cout&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='s'&gt;&amp;quot;Setup - row constraints&amp;quot;&lt;/span&gt; 
 &lt;span class='k'&gt;for&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='mi'&gt;9&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='o'&gt;++&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
   &lt;span class='n'&gt;distinct&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;m&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;row&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;));&lt;/span&gt;
 &lt;span class='n'&gt;std&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;cout&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='s'&gt;&amp;quot;Setup - column constraints&amp;quot;&lt;/span&gt; 
 &lt;span class='k'&gt;for&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='mi'&gt;9&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='o'&gt;++&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
   &lt;span class='n'&gt;distinct&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;m&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;col&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;));&lt;/span&gt;
 &lt;span class='n'&gt;std&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;cout&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='s'&gt;&amp;quot;Setup - square constraints&amp;quot;&lt;/span&gt;
 &lt;span class='k'&gt;for&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='mi'&gt;9&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='o'&gt;+=&lt;/span&gt;&lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
   &lt;span class='k'&gt;for&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt; &lt;span class='n'&gt;j&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='n'&gt;j&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='mi'&gt;9&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='n'&gt;j&lt;/span&gt;&lt;span class='o'&gt;+=&lt;/span&gt;&lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
     &lt;span class='n'&gt;distinct&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;m&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;slice&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='o'&gt;+&lt;/span&gt;&lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;j&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;j&lt;/span&gt;&lt;span class='o'&gt;+&lt;/span&gt;&lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Im ersten Teil wird das Sudoku ausgelesen und als einzelne Constraints in den Solver übernommen:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='cpp'&gt; &lt;span class='n'&gt;rel&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;m&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;j&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;),&lt;/span&gt; &lt;span class='n'&gt;IRT_EQ&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;v&lt;/span&gt; &lt;span class='p'&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Sprich: An der Stelle &lt;em&gt;(i,j)&lt;/em&gt; des Sudokus muss der Wert &lt;em&gt;v&lt;/em&gt; (aus der Problemstellung) stehen. Ähnlich funktioniert auch das Setup der anderen Constraints, wobei hier der Constraint &lt;em&gt;distinct&lt;/em&gt; benutzt wird. Das &amp;#8212; sehr lesbare &amp;#8212; Handbuch zu gecode liefert hier mehr Informationen.&lt;/p&gt;

&lt;h3 id='fazit'&gt;Fazit&lt;/h3&gt;

&lt;p&gt;Constraint Programming ist IMHO eine sehr interessante und mächtige Technik. Wenn es um Fahrplanplanung oder Raumzuweisungen geht, kann Constraint Programming genau das richtige sein. In meinem kleinen Sudoku-Experiment funktioniert CP sowohl einfach als auch schnell.&lt;/p&gt;

&lt;p&gt;Das bedeutet allerdings nicht, das CP für alle Probleme geeignet ist. Hier muss man im Einzelfall entscheiden. Dazu kommt, dass eventuell andere Algorithmen bessere Performance liefern. Wie immer: It depends. Einen Blick ist CP in jedem Fall wert.&lt;/p&gt;

&lt;h3 id='weiterfhrende_ressourcen'&gt;Weiterführende Ressourcen&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='http://www.jstor.org/pss/25062753'&gt;Lustig/Puget, &amp;#8220;Program Does Not Equal Program&amp;#8221;&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://minion.sourceforge.net'&gt;Minion&lt;/a&gt; ist eine einfache Programmierumgebung für CP.&lt;/li&gt;

&lt;li&gt;&lt;a href='http://www.gecode.org/'&gt;Gecode Homepage&lt;/a&gt;. Das Handbuch ist empfehlenswert.&lt;/li&gt;
&lt;/ul&gt;</content>
 </entry>
 
 <entry>
   <title>Photovoltaikanlagen und der Eigenverbrauch</title>
   <link href="http://gonium.net//blog/2011/06/13/photovoltaikanlagen-und-der-eigenverbrauch"/>
   <updated>2011-06-13T00:00:00+02:00</updated>
   <id>id:/blog/2011/06/13/photovoltaikanlagen-und-der-eigenverbrauch</id>
   <content type="html">&lt;p&gt;&lt;em&gt;Nachtrag 17.06.2011: &lt;a href='http://www.photovoltaikforum.com/allgemeine-anlagenplanung-f69/eigenstromverbrauch-t64062.html#p539830'&gt;FBonNET&lt;/a&gt; weisst im Photovoltaik-Forum darauf hin, dass die Eigenverbrauchsregelung im §33(2) EEG einen zweistufigen Tarif vorsieht: Falls der Eigenverbrauchsanteil größer als 30% ist, so wird nur der Anteil größer 30% mit dem höheren Vergütungssatz abgerechnet. Das war in meinem ursprünglichem Artikel so nicht beschrieben, dies habe ich angepasst. Ich setze voraus, dass mein Eigenverbrauchsanteil kleiner 30% ist.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ich verbrauche ja eigentlich recht wenig Strom - mit einer 2,5kWp-Photovoltaikanlage könnte ich quasi meinen Strombedarf in der Jahressumme komplett selbst decken. Dabei hilft natürlich, dass ich kaum daheim bin :-) Allerdings wohne ich zur Miete, und eine eigene Dachfläche ist derzeit kaum in Sicht. Also: Die folgenden Überlegungen sind rein hypothetisch, wenn auch mit realen Daten hinterlegt.&lt;/p&gt;

&lt;p&gt;Für den Betrieb einer eigenen PV-Anlage finde ich den Eigenverbrauch besonders interessant, also den Strom meiner (hypothetischen) Photovoltaikanlage direkt im eigenen (realen) Haushalt zu verbrauchen. Aus der Sicht des Stromnetzes macht das durchaus Sinn, denn dann muss weniger Strom durch das Verteilnetz transportiert werden. Das hat auch der Gesetzgeber erkannt und fördert durch eine separate Eigenverbrauchsvergütung den lokalen Verbrauch:&lt;/p&gt;
&lt;table&gt;
    &lt;tr&gt;
        &lt;th&gt;Preiskategorie&lt;/th&gt;
        &lt;th&gt;ct/kWh&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Einspeisevergütung&lt;/td&gt;
        &lt;td&gt;28.74&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Eigenverbrauch &amp;lt; 30 Prozent&lt;/td&gt;
        &lt;td&gt;12.36&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Eigenverbrauch &amp;gt; 30 Prozent&lt;/td&gt;
        &lt;td&gt;16.74&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Strombezug (Lichtblick)&lt;/td&gt;
        &lt;td&gt;23.64&lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Alle Angaben in dieser Tabelle beziehen sich auf PV-Installationen kleiner 30 kWp auf Gebäuden, die im ersten Halbjahr 2011 ans Netz gingen (Quelle: Photon Mai 2011, S. 181). Rein rechnerisch gilt also: Entweder, man speist eine kWh Photovoltaik-Strom direkt in das Netz ein und erhält dafür 28.74 Eurocent. Oder man verbraucht die kWh direkt im eigenen Haushalt, was etwas komplizierter ist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bei einem Eigenverbrauchsanteil &amp;lt; 30% erhält man 12.36 Eurocent und spart (da man ja keinen Strom bezieht) gleichzeitig 23.64 Eurocent beim Stromkauf. Macht also 36.00 Eurocent.&lt;/li&gt;

&lt;li&gt;Verbraucht man auf Jahressicht mehr als 30% seines eigenen Stroms, so ergibt dies 16.74+23.64=40.37 Eurocent für jede Kilowattstunde, die über den 30% liegen. Für den Anteil darunter bleibt es bei 36.00 Eurocent.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id='bestimmung_des_eigenverbrauchs'&gt;Bestimmung des Eigenverbrauchs&lt;/h3&gt;

&lt;p&gt;Dies gilt natürlich nur, wenn man auch den entsprechenden Stromverbrauch &lt;em&gt;zeitgleich&lt;/em&gt; zur Produktion hat. Die folgende Zähleranordnung stellt die entsprechende Erfassung von Bezug, Einspeisung und Produktion sicher:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='306' src='/images/20110613-Dreiwege-Zaehler.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Oft sind Einspeise- und Bezugszähler auch in einem Zweirichtungszähler integriert. Die Gleichzeitigkeit von Produktion und Eigenverbrauch ist durch diese Anordnung ebenfalls sichergestellt &amp;#8212; wenn die PV-Anlage produziert, wird dieser Strom &amp;#8220;zuerst&amp;#8221; verbraucht. Erst dann wird eventuell notwendige zusätzliche Leistung aus dem Netz entnommen.&lt;/p&gt;

&lt;p&gt;Mit diesen drei Messpunkten können nun alle für die Abrechnung relevanten Größen errechnet werden. In meiner Simulation (siehe unten) mache ich folgendes: Für jede Minute wird die Situation im Hausnetz analysiert und daraus die Stromflüsse abgeleitet. Der entsprechende C++-Code sieht so aus:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='c'&gt; &lt;span class='k'&gt;for&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;uint16_t&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='n'&gt;MINS_PER_DAY&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='o'&gt;++&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
   &lt;span class='n'&gt;netzsaldo&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='n'&gt;consumption&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;&lt;span class='o'&gt;-&lt;/span&gt;&lt;span class='n'&gt;_production&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
   &lt;span class='n'&gt;einspeisung&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='n'&gt;std&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;max&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;-&lt;/span&gt;&lt;span class='n'&gt;netzsaldo&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;]);&lt;/span&gt;
   &lt;span class='n'&gt;eigenverbrauch&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='n'&gt;std&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;min&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;_production&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;],&lt;/span&gt; 
       &lt;span class='n'&gt;consumption&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;]);&lt;/span&gt;
   &lt;span class='n'&gt;netzbezug&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='n'&gt;std&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;max&lt;/span&gt;&lt;span class='p'&gt;((&lt;/span&gt;&lt;span class='kt'&gt;int16_t&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;netzsaldo&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;]);&lt;/span&gt;
 &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Man sieht: Es reicht nicht aus, eine Überschlagsrechnung zu machen. Es kommt darauf an, &lt;em&gt;wann&lt;/em&gt; ein Gerät betrieben wird. Am einfachsten versteht man das anhand eines (realen) Beispiels.&lt;/p&gt;

&lt;h3 id='ein_ganz_normaler_sommertag'&gt;Ein ganz normaler Sommertag&lt;/h3&gt;

&lt;p&gt;Meinen kleinen Simulationscode habe ich dazu benutzt, einmal einen prototypischen Sommertag durchzurechnen. Zunächst einmal: Welche Daten gehen als Eingabe in die Simulation?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Meine (hypothetische) Photovoltaikanlage. Diese Daten sind aus einer (realen) Anlage in der Nachbarschaft abgeleitet und auf 2.5kWp skaliert. Die Tagesproduktion sieht so aus, man kann ein paar Wolken erkennen: &lt;div class='span-14'&gt;
&lt;img class='top left' height='400' src='/images/20110613/production.png' width='480' /&gt;
&lt;/div&gt;&lt;/li&gt;

&lt;li&gt;Mein Grundverbrauch. Wie oben bereits angedeutet bin ich oft nicht daheim, insofern ist diese Kurve recht unspektakulär. Morgens mache ich mir einen Cappucino, abends sitze ich am Rechner. Ansonsten sieht man nur meinen Kühlschrank.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='400' src='/images/20110613/baseload.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Mit diesen Grundannahmen wäre das Modell recht langweilig, denn aufgrund meines geringen Tagesverbrauchs wäre ich nicht in der Lage, einen Eigenverbrauchsanteil &amp;#62; 30% zu erreichen. Ich simuliere daher noch eine Waschmaschine und einen Geschirrspüler. Die Simulation versucht, den Eigenverbrauch zu maximieren, indem sie die Einschaltzeitpunkte der Geräte optimiert. In der Realität wäre allerdings niemand da, der die Geräte dann einschalten kann. Eine Lösung zur Heimautomatisierung wäre in meinem Fall Pflicht.&lt;/p&gt;

&lt;p&gt;Was errechnet nun die Simulation? Zunächst einmal ergibt sich nach der Optimierung der Einschaltzeiten folgender Netzsaldo:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='400' src='/images/20110613/netzsaldo.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Negative Werte stellen hier Einspeisung, positive Werte den Bezug dar. Ein Teil meines Cappuccino-Stroms kann die Photovoltaikanlage liefern, jedoch nicht alles. Auch bei der Waschmaschine, die hier um die Mittagszeit läuft, muss Strom aus dem Netz bezogen werden. Dafür läuft der Geschirrspüler (vor der Waschmaschine) ausschließlich mit dem PV-Strom.&lt;/p&gt;

&lt;p&gt;Etwas deutlicher sieht man das auch an dem Eigenverbrauch, sprich: Dem Anteil des PV-Stroms, der direkt im Haushalt verbraucht und mit 12.36 bzw. 16.74 Eurocent vergütet wird:&lt;/p&gt;
&lt;div class='span-14'&gt; 
&lt;img class='top left' height='400' src='/images/20110613/eigenverbrauch.png' width='480' /&gt; 
&lt;/div&gt;
&lt;p&gt;Trotzdem wäre ich nicht in der Lage, den Strombedarf all meiner Geräte aus der PV-Anlage zu decken. Folgende Leistung wird aus dem Netz bezogen:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='400' src='/images/20110613/netzbezug.png' width='480' /&gt;
&lt;/div&gt;
&lt;h3 id='fazit'&gt;Fazit&lt;/h3&gt;

&lt;p&gt;Inwiefern diese Simulation einen typischen Tag darstellt, kann ich momentan nicht genau belegen. Ich halte das Szenario jedoch für realistisch (mehr Statistik wäre notwendig, um das auch zu belegen).&lt;/p&gt;

&lt;p&gt;Die Eckpunkte der Simulation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Die PV-Anlage produziert 14,1909kWh. Bei direkter Einspeisung und einer Einspeisevergütung von 28,74 Eurocent würde die Anlage an diesem Tag 4,01744 Euro erwirtschaften. Gleichzeitig muss aus dem Stromnetz natürlich noch Strom für den Betrieb der Geräte gekauft werden: 7,67198kWh, macht -1,81366 Euro. Das Gesamtsaldo ist also 2,20379 Euro.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Für die Eigenverbrauchsoptimierung ergibt sich folgendes Szenario: Der Geschirrspüler wird um 10:15, die Waschmaschine gegen 12:20 eingeschaltet. Das sind jeweils die optimalen Einschaltzeiten, bei denen der Eigenverbrauchsanteil maximal ist. Die Einspeisung beträgt 8,7552kWh, dies entspricht 2,4786 Euro. Der Eigenverbrauch beträgt 5,4357kWh und wird mit 0,671853 Euro vergütet. Um den restlichen Strombedarf des Haushalts zu decken, müssen 2,23628kWh aus dem Netz bezogen werden, entspricht -0,528657 Euro. Das Gesamtsaldo beträgt also 2,62179 Euro. Wenn der Eigenverbrauch über das gesamte Jahr über 30% liegen würde, dann wäre die Vergütung nochmal höher.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Der Eigenverbrauch würde sich in diesem Szenario also knapp rechnen: Rund 41,8 Eurocent Mehrertrag würden sich realisieren lassen. Dem gegenüber stehen allerdings Investitionen in zusätzliches Zählerequipment. Ebenso unberücksichtigt sind Investitionen in ein Hausbussystem und eine Optimierung, welche die Einschaltzeitpunkte der Haushaltsgeräte optimiert.&lt;/p&gt;

&lt;p&gt;Zusammenfassend lässt sich sagen, dass eine Diskussion über die Nutzung der Eigenverbrauchsregelung nur Sinn macht, wenn auch die Verbrauchsmöglichkeiten des PV-Stroms mit einbezogen werden. Auf der Intersolar 2011 war ich etwas verwundert, dass dieser Aspekt in vielen Gesprächen untergegangen ist. Bei anderen Stromverbrauchscharakteristiken kann die Rechnung auch anders aussehen, dies ist im Zweifelsfall nochmal separat zu simulieren. Wer mittags elektrisch kocht, dürfte auch andere Freiheitsgrade bei der Optimierung haben.&lt;/p&gt;

&lt;p&gt;Was ein erschwingliches Hausbussystem anbelangt: Daran arbeite ich gerade im Projekt &lt;a href='http://www.mysmartgrid.de'&gt;mySmartGrid&lt;/a&gt;. Auch die Eigenverbrauchsoptimierung ist Gegenstand der Arbeit meines Kollegen Matthias Klein. Stay tuned.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>PID FTW!</title>
   <link href="http://gonium.net//blog/2011/04/14/pid-ftw%21"/>
   <updated>2011-04-14T00:00:00+02:00</updated>
   <id>id:/blog/2011/04/14/pid-ftw!</id>
   <content type="html">&lt;p&gt;&lt;a href='../../../../../blog/2010/12/12/das-leben-ist-zu-kurz-fur-schlechten-kaffee'&gt;Silvia&lt;/a&gt; fühlt sich mittlererweile ganz wohl bei mir. Manchmal ist sie eine Zicke, aber die Qualität meines Cappuccinos scheint immer weniger mit der Anzahl der schwarzen Katzen im Stadtteil zu korrelieren. Wenn der Cappuccino nicht schmeckt, hab ich einen Fehler gemacht &amp;#8212; so einfach ist das. Und auch Spinnen finden Silvia toll:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='320' src='/images/20110414-silvia-spinne.jpg' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Das heisst aber nicht, dass es in Sachen Cappuccino nicht noch technische Weiterentwicklungen geben würde. Bis vor drei Wochen lief der PID-Controller, der die normale Temperaturregelung der Silvia ersetzt hat, nur als einfacher P-Controller, sprich: Da fehlte noch das komplette Feintuning. Der Hintergrund: Ich war mit dem &lt;a href='http://www.arduino.cc/playground/Main/BarebonesPIDForEspresso'&gt;Bare Bones Coffee Controller&lt;/a&gt; nicht wirklich zufrieden, der Code war nicht schön. Daher stand also eine Neuimplementation an. Gesagt, getan: Im &lt;a href='https://github.com/gonium/ChaosCoffeeControl'&gt;ChaosCoffeeControl-Repository&lt;/a&gt; gibt es nun einen development-Branch, in dem der neue Code liegt. Einzig das Senden des DTR-Signals, um den µC neu zu starten und so über den Bootloader zu programmieren, ist noch etwas häßlich. Aber irgendwas ist ja immer.&lt;/p&gt;

&lt;h3 id='der_pidalgorithmus'&gt;Der PID-Algorithmus&lt;/h3&gt;

&lt;p&gt;Der PID-Algorithmus war dann auch recht schnell implementiert. Der Kern sieht so aus:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='cpp'&gt;&lt;span class='kt'&gt;float&lt;/span&gt; &lt;span class='n'&gt;curTemp&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='n'&gt;getTemperatureFloat&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;
&lt;span class='kt'&gt;float&lt;/span&gt; &lt;span class='n'&gt;error&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;_pid_data&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;setpoint&lt;/span&gt; &lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='n'&gt;curTemp&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='n'&gt;_p_term&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;_pid_data&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;p_gain&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt; &lt;span class='n'&gt;error&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='c1'&gt;// iState keeps changing over time; it&amp;#39;s overall &lt;/span&gt;
&lt;span class='c1'&gt;// &amp;quot;performance&amp;quot; over time, or accumulated error&lt;/span&gt;
&lt;span class='n'&gt;_iState&lt;/span&gt; &lt;span class='o'&gt;+=&lt;/span&gt; &lt;span class='n'&gt;error&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='c1'&gt;// Use a static windup guard to keep the balance &lt;/span&gt;
&lt;span class='c1'&gt;// of the istate.&lt;/span&gt;
&lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;_iState&lt;/span&gt; &lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;WINDUP_GUARD&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; 
  &lt;span class='n'&gt;_iState&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;WINDUP_GUARD&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='k'&gt;else&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;_iState&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='o'&gt;-&lt;/span&gt;&lt;span class='n'&gt;WINDUP_GUARD&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; 
  &lt;span class='n'&gt;_iState&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='o'&gt;-&lt;/span&gt;&lt;span class='n'&gt;WINDUP_GUARD&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='n'&gt;_i_term&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;_pid_data&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;i_gain&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt; &lt;span class='n'&gt;_iState&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='c1'&gt;// the dTerm, the difference between the temperature &lt;/span&gt;
&lt;span class='c1'&gt;// now and our last reading, indicated the &amp;quot;speed,&amp;quot; &lt;/span&gt;
&lt;span class='c1'&gt;// how quickly the temp is changing. &lt;/span&gt;
&lt;span class='n'&gt;_d_term&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;_pid_data&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;d_gain&lt;/span&gt; &lt;span class='o'&gt;*&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;curTemp&lt;/span&gt; &lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='n'&gt;_last_temp&lt;/span&gt;&lt;span class='p'&gt;));&lt;/span&gt;
&lt;span class='c1'&gt;// now that we&amp;#39;ve use lastTemp, put the current temp &lt;/span&gt;
&lt;span class='c1'&gt;// in our pocket until for the next round&lt;/span&gt;
&lt;span class='n'&gt;_last_temp&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;curTemp&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='c1'&gt;// the magic feedback bit&lt;/span&gt;
&lt;span class='n'&gt;_pid_value&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;_p_term&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='n'&gt;_i_term&lt;/span&gt; &lt;span class='o'&gt;-&lt;/span&gt; &lt;span class='n'&gt;_d_term&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Als ich dann von dem einfachen P-Regler auf den PID-Algorithmus umstellen wollte, funktionierte zunächst alles prima. Nach etwas Parameter-Raten war die Kombination P=60, I=0.48 und D=1800 recht brauchbar. Um die zu ermitteln habe ich den Regler zunächst als reinen P-Regler betrieben und P langsam erhöht. Bei P=100 schwingt das System, und zwar mit einer Periode von 250 Sekunden. Die Einstellregeln von Ziegler-Nichols ergeben dann die Parameter oben. Ich hab hierfür folgendes R-Skript benutzt:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='rconsole'&gt;&lt;span class='go'&gt;K_P_krit&amp;lt;-100&lt;/span&gt;
&lt;span class='go'&gt;T_krit&amp;lt;-250&lt;/span&gt;

&lt;span class='go'&gt;print(paste(&amp;quot;Estimation of PID controller &amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;      &amp;quot;parameters for K_pkrit=&amp;quot;,&lt;/span&gt;
&lt;span class='go'&gt;      K_P_krit, &amp;quot; and T_krit=&amp;quot;, T_krit));&lt;/span&gt;

&lt;span class='go'&gt;K_P&amp;lt;-0.6*K_P_krit&lt;/span&gt;
&lt;span class='go'&gt;T_N&amp;lt;-0.5*T_krit&lt;/span&gt;
&lt;span class='go'&gt;T_V&amp;lt;-0.12*T_krit&lt;/span&gt;
&lt;span class='go'&gt;K_I&amp;lt;-K_P/T_N&lt;/span&gt;
&lt;span class='go'&gt;K_D&amp;lt;-K_P*T_V&lt;/span&gt;

&lt;span class='go'&gt;print(paste(&amp;quot;K_P: &amp;quot;, K_P));&lt;/span&gt;
&lt;span class='go'&gt;print(paste(&amp;quot;K_I: &amp;quot;, K_I));&lt;/span&gt;
&lt;span class='go'&gt;print(paste(&amp;quot;K_D: &amp;quot;, K_D));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Danach zeigt der Regler ein sehr stabiles Temperaturverhalten:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='400' src='/images/20110414-silvia-p60i048d1800-heatig-temp.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Klasse, denke ich mir, mahle Kaffee und will mir einen Cappuccino machen. Leider ist nach der Betätigung der Pumpe auf einmal der Mikrocontroller tot. WTF?&lt;/p&gt;

&lt;h3 id='emv__nicht_witzig'&gt;EMV &amp;#8212; nicht witzig&lt;/h3&gt;

&lt;p&gt;Ich bin dann erst einmal für zwei Tage zurück zur Senseo-Kaffeemaschine. Es stellte sich heraus, dass die Pumpe massive Einstrahlungen in die Schaltung liefert. Da ein Atmel-Mikrocontroller Überspannungen an der Resetleitung als Aufforderung zur Hochvolt-Programmierung versteht, hat er sich quasi beim Anschalten der Pumpe selbst gelöscht.&lt;/p&gt;

&lt;p&gt;Also die Platine mit diversen Dioden und Kondensatoren ausgestattet. Der Schaltplan sieht nun so aus (&lt;a href='/images/2011414-silvia-schematic.pdf'&gt;großes PDF&lt;/a&gt;):&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='339' src='/images/20110414-silvia-schematic.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Danach lief die Maschine zumindest meistens. Irgendwo kommen immer noch Störungen rein, aber der Mikrocontroller resettet sich nur und startet dann wieder. Das ist unbefriedigend, wenn man Geräte an den USB-Port anschließen will:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='320' src='/images/20110414-silvia-usb.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Das Loch habe ich mit einer Lochstanze in die Rückwand der Silvia gestanzt. Der USB-Port ist von Neutrik und setzt auf der Innenseite auf eine USB-A-Buchse um. Ein USB-Kabel verbindet dann die Neutrik-Buchse mit dem Mikrocontroller.&lt;/p&gt;

&lt;p&gt;Meine erste Maßnahme war das Schirmen der Kabel, die direkt an die Platine führen. Das hat allerdings nicht ausgereicht. Als nächstes habe ich zwischen Platine und Magnetventil eine Schirmung aus Alufolie angebracht &amp;#8212; ebenso ohne Erfolg. Letztlich hat dann ein Entstörfilter (LC-Glied) direkt an der Pumpe funktioniert:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='337' src='/images/20110414-silvia-emvfilter.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Silvia sieht nun innen so aus:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='424' src='/images/20110414-silvia-emvsolved.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Der EMV-Filter ist im Vordergrund zu erkennen. Ausserdem sind alle Kabel in einen schwarzen, temperaturbeständigen EMV-Schutzschlauch eingepackt. Der Lohn der Mühe: Die Kaffeemaschine läuft nun störungsfrei, der Schaltplan ist vollständig. Schließt man einen Rechner via USB an, kann man Temperatur etc. in einer Konsole einstellen:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;C8H10N4O2 menu
Press &lt;span class='s1'&gt;&amp;#39;x&amp;#39;&lt;/span&gt; to leave, &lt;span class='s1'&gt;&amp;#39;?&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;for &lt;/span&gt;&lt;span class='nb'&gt;help&lt;/span&gt;
&lt;span class='nb'&gt; &lt;/span&gt;b: print PID configuration
 o: toggle PID debug output
 +/-: increase/decrease adjustment delta, 
      current delta: 1.00000
 p/P: increase/decrease p gain
 i/I: increase/decrease i gain
 d/D: increase/decrease d gain
 r: reset PID configuration to default values
 s: save PID configuration to EEPROM
Current PID controller configuration:
 - temperature setpoint: 102.00000
 - P gain: 60.00000, I gain: 0.48000, 
   D gain: 1800.00000
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id='und_nun'&gt;Und nun?&lt;/h3&gt;

&lt;p&gt;Mögliche Erweiterungen in der Zukunft:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ein automatischer Bezugstaster&lt;/li&gt;

&lt;li&gt;Ein Durchflussmesser&lt;/li&gt;

&lt;li&gt;Twitter-Anschluss&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stay tuned.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Das Leben ist zu kurz für schlechten Kaffee</title>
   <link href="http://gonium.net//blog/2010/12/12/das-leben-ist-zu-kurz-fur-schlechten-kaffee"/>
   <updated>2010-12-12T00:00:00+01:00</updated>
   <id>id:/blog/2010/12/12/das-leben-ist-zu-kurz-fur-schlechten-kaffee</id>
   <content type="html">&lt;p&gt;Nachdem die &lt;a href='../../../../../blog/2010/11/21/espresso-temperatur--morphy-richards-auf-dem-prüfstand/'&gt;Morphy Richards&lt;/a&gt; nicht wirklich temperaturstabil war, musste eine Alternative her. Vor allem der Thread &amp;#8221;&lt;a href='http://www.kaffee-netz.de/espresso-und-kaffeemaschinen/13619-silvia-mein-eisenschwein-ff-ff.html'&gt;Silvia, mein Eisenschwein... öfföff&lt;/a&gt;&amp;#8221; hat dann in der Folge zu kleineren eBay-Ausflügen geführt. Das Resultat ist eine gebrauchte &lt;a href='http://www.kaffeewiki.de/index.php?title=Silvia'&gt;Rancilio Silvia Modell 2009&lt;/a&gt;, sehr guter Zustand, und gar nicht teuer (ähem).&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='320' src='/images/20101212-silvia-2.jpg' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Die Espressomaschine ist technisch sehr einfach: Temperaturregelung über Thermostate (mit entsprechender Hysterese) und ansonsten nur ein paar Schalter, Kabel, Magnetventil und Pumpe. Simpel, dabei aber mit einem massiven Messingkessel und solider Verarbeitung ausgerüstet. Da die Maschine von vielen Leuten schon modifiziert wurde, ist sie eine ideale Basis für eigene Experimente. Noch bevor die Maschine da war hab ich mir zusätzliche Silikonkabel und Solid-State Relais geordert, um in diversen Basteltagen einen PID-Regler selbst einzubauen.&lt;/p&gt;

&lt;p&gt;Zunächst muss jedoch der Status Quo dokumentiert werden. Also wie schon bei der Morphy Richards einen Temperatursensor auf den Tank geklebt und losgemessen:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='400' src='/images/20101212-silvia-temp.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Nach dem Aufheizen sieht man deutlich die Hysterese, verursacht durch die einfache Thermostatregelung. Die Temperatur pendelt zwischen 87°C und 109°C:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='400' src='/images/20101212-silvia-heating.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Beim Bezug schwankt die Temperatur ebenso. Im folgenden Bild habe ich bei ca. 105°C Kesseltemperatur den Bezug gestartet (erster Strich). Danach hab ich direkt auf Dampfbetrieb umgeschaltet und Milch aufgeschäumt. Der Sensor clippt jedoch bei 127°C:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='400' src='/images/20101212-silvia-bezug.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Zusammenfassend kann man sagen, dass das Temperaturverhalten der Silvia im Originalzustand nicht wirklich prickelnd ist. Durch Temperatursurfen und andere Tricks kann man zwar die Temperatur beim Bezug in recht engen Grenzen halten, aber das ist nicht wirklich praxistauglich. Diverse Hersteller bieten daher einen PID-Regler speziell für diese Maschine als Umrüstsatz an. Keiner dieser Regler ist wirklich ein Open Source-Produkt &amp;#8212; uncool, wenn man damit basteln möchte.&lt;/p&gt;

&lt;h3 id='diy_pidregler'&gt;DIY PID-Regler&lt;/h3&gt;

&lt;p&gt;Also selbst einen Regler entwerfen. Die Reglersoftware basiert momentan noch auf dem &lt;a href='http://www.arduino.cc/playground/Main/BarebonesPIDForEspresso'&gt;Bare Bones
Coffee Controller&lt;/a&gt;, einem Arduino-basierten PID. Ich habe einen anderen Temperatursensor, nämlich den DS18S20, verwendet &amp;#8212; insofern habe ich den Code entsprechend angepasst. Darüber hinaus gibt es momentan die ersten Anfänge einer Autotuning-Routine, doch darüber später mehr.&lt;/p&gt;

&lt;p&gt;Nachdem die grobe Richtung für die Software feststand ging es an die Hardware. Der Schaltplan V0.1 sieht so aus (aufs Bild klicken für eine PDF-Version):&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;a href='/images/20101212-silvia-basecircuit.pdf'&gt;
&lt;img class='top left' height='328' src='/images/20101212-silvia-schematic.png' width='480' /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Im Prinzip ist die Schaltung ein kleiner Arduino, der zusammen mit einem FTDI TTL-USB-Konverter mit der Aussenwelt kommunizieren kann. Am Arduino Pin 9 hängt ein SSR, welches das Heizelement im Kessel schaltet. Der Onewire-Bus für den Temperatursensor ist an Arduino Pin 7 angeschlossen. Auf Lochraster aufgebaut sieht die Schaltung so aus:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='320' src='/images/20101212-silvia-9.jpg' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Ich habe wenig Lust, die Maschine bei jedem Softwareupdate auseinander zu bauen, daher muss der Mikrocontroller einen Bootloader haben. Praktischerweise bringt Arduino gleich einen mit. Dieser kann direkt aus der Arduino-GUI über einen ISP-Programmer auf den Chip kopiert werden. Leider kennt die Arduino-GUI meinen geliebten USBasp-Programmer jedoch nicht. &lt;a href='
http://www.chrmoll.de/dokuwiki/doku.php?id=elektronik:arduino:usbasp
'&gt;Die Abhilfe&lt;/a&gt; findet sich nach kurzer Recherche im Internetz: Einfach die folgenden Zeilen in die &amp;#8220;programmer.txt&amp;#8221; im Verzeichnis &amp;#8220;hardware&amp;#8221; der Arduino-Installation hinzufügen:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='text'&gt;usbasp.name=USBasp
usbasp.communication=usb
usbasp.protocol=usbasp
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Danach bietet die Arduino-GUI auch ein Flashen über meinen Programmer an. Den Mikrocontroller also in mein Pollin-Board gesteckt, programmiert und wieder zurück auf die Lochraster-Platine. Fortan kann der Mikrocontroller über USB programmiert werden.&lt;/p&gt;

&lt;p&gt;Bleibt noch die Frage, wie der Mikrocontroller mit Strom versorgt werden kann. Einen Trafo in das Gehäuse zu setzen wäre zwar eine Möglichkeit, erscheint mir jedoch aufgrund der offenen Verkabelung etwas unsicher. Daher habe ich ein kleines Steckernetzteil mit einer Eurobuchse in selbstverschweissendes Klebeband eingepackt und mit Silikonkabeln versehen:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='320' src='/images/20101212-silvia-8.jpg' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Diese Variante hat zusätzlich den Vorteil, ziemlich kompakt zu sein.&lt;/p&gt;

&lt;h3 id='einbau'&gt;Einbau&lt;/h3&gt;

&lt;p&gt;Zuerst musste ich die Halterung für die Pumpe drehen und einen kleinen Halter installieren, um genug Platz für meine Stromversorgung zu haben. Das Netzteil liegt dabei nicht auf dem Boden der Maschine auf - selbst wenn sich dort Wasser sammelt, sollte das Netzteil trocken bleiben:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='320' src='/images/20101212-silvia-6.jpg' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Die Versorgungsspannung für das Netzteil greife ich direkt hinter dem Hauptschalter der Silvia ab:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='320' src='/images/20101212-silvia-11.jpg' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Die Kabelschuhe habe ich nach dem Foto nochmal separat isoliert. Die Lochrasterplatine wird mit Distanzhülsen am Träger der Maschine befestigt:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='320' src='/images/20101212-silvia-13.jpg' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Unter der Platine ist nun auch das Netzteil installiert. Die weisse Folie hinter der Platine ist Overheadfolie, welche eine Lücke in der Rückwand wasserdicht verschliesst. Das Solid State Relay (SSR) findet seinen Platz auf der anderen Seite der Trennwand, ähnlich wie bei den Auber-PIDs:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='320' src='/images/20101212-silvia-14.jpg' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Es ist wie das Netzteil mit Silikonkabeln sowohl mit der Kesselheizung als auch mit dem Mikrocontroller verbunden. Die Anschlüsse sind so gewählt, dass das SSR den 100°C-Thermostaten ersetzt. Für Dampf etc. wird also nach wie vor der andere Thermostat benutzt. Auch der Sicherheitsthermostat am Kessel behält seine Funktion.&lt;/p&gt;

&lt;p&gt;Abschliessend noch ein Foto von der Seite: Alles passt unter die Abdeckung für den Wassertank. Sollte also hier einmal Wasser von oben eindringen, so sind alle Komponenten davor geschützt.&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='320' src='/images/20101212-silvia-17.jpg' width='213' /&gt;
&lt;/div&gt;
&lt;h3 id='der_lohn_der_mhe'&gt;Der Lohn der Mühe&lt;/h3&gt;

&lt;p&gt;Nach diversen kleineren Softwareanpassungen läuft das System nun. Allerdings ist der PID-Regler noch nicht kallibriert - im Moment sind die I- und D-Komponenten ausgeschaltet. Wie vorher auch habe ich das Heizverhalten über den Temperatursensor aufgezeichnet und ausgewertet. Da nun der Temperatursensor am PID hängt, muss die Aufzeichnung direkt aus den Logausgaben des PIDs erfolgen. Dazu zunächst einmal die Konfiguration des seriellen Devices prüfen:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt; &lt;span class='nv'&gt;$ &lt;/span&gt;stty -f /dev/cu.usbserial-A600e1dh 
 speed 9600 baud;
 lflags: -icanon -isig -iexten -echo
 iflags: -icrnl -ixon -ixany -imaxbel -brkint
 oflags: -opost -onlcr -oxtabs
 cflags: cs8 -parenb
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Wenn alles passt kann die Aufnahme gestartet werden:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt; &lt;span class='nv'&gt;$ &lt;/span&gt;cat /dev/cu.usbserial-A600e1dh | tee run1.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Dort zeigt sich jedoch nur das Menü, welches der PID normalerweise über die serielle Konsole ausgibt. Um automatische Updates zu erhalten, muss ein &amp;#8220;u&amp;#8221; an den PID gesandt werden:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt; &lt;span class='nv'&gt;$ &lt;/span&gt;&lt;span class='nb'&gt;echo&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;u\n&amp;quot;&lt;/span&gt; &amp;gt; /dev/cu.usbserial-A600e1dh
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Der Temperaturverlauf nach dem Aufheizen hat schon eine ganz andere Form:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='400' src='/images/20101212-silvia-p-regler-idle.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Der Sollwert liegt bei 104°C. Wenn Silvia aufgeheizt ist, wird die Temperatur in einem Korridor zwischen 103°C und 105.5°C gehalten &amp;#8212; entspricht einer Hysterese von 2.5°C. Verglichen mit der alten Hysterese von 22°C wesentlich besser, vor allem, wenn man in Betracht zieht, das hier nur ein P-Regler arbeitet. Das macht sich leider bemerkbar, wenn kaltes Wasser in den Kessel einströmt. Testweise habe ich die Temperatur im Siebträger gemessen:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='400' src='/images/20101212-silvia-p-regler-test.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Jeweils bei den vertikalen Markierungen habe ich einen Bezug simuliert. Die gute Nachricht: Bei allen Bezügen liegt die Temperatur bei 96°C, d.h. die korrekte Temperatur liegt an. Die schlechte Nachricht: Die Temperatur im Kessel sinkt stark ab. Ausserdem überschwingt die Temperatur im Anschluss auch relativ stark. Diese Phänomene sollten jedoch durch einen vollen PID-Regler abgemildert werden. Die Temperaturmessung im Siebträger macht ein Type K-Thermocouple, angeschlossen an ein Multimeter:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='720' src='/images/20101212-silvia-18.jpg' width='480' /&gt;
&lt;/div&gt;
&lt;h3 id='probleme'&gt;Probleme&lt;/h3&gt;

&lt;p&gt;Noch ist das Projekt nicht abgeschlossen, denn es gibt noch viel zu tun:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Der PID-Algorithmus braucht Feintuning. Die Autotuning-Routine funktioniert so nicht, hier gibt es noch etwas Arbeit. Dann gehören die Überschwinger aber hoffentlich der Vergangenheit an.&lt;/li&gt;

&lt;li&gt;Der Controller resettet sich manchmal nach dem Bezug. Ich gehe davon aus, dass durch das Abschalten der Pumpe und des Magnetventils ein paar Störungen den Reset auslösen. Mehr Kondensatoren in der Spannungsversorgung sollten helfen.&lt;/li&gt;

&lt;li&gt;Das USB-Kabel muss verschwinden und wird durch eine Neutrik USB-Buchse nach aussen geführt.&lt;/li&gt;

&lt;li&gt;Ich mag den Arduino-Code nicht. Hier steht noch ein Rewrite in puren, AVR-GCC-kompatiblen Code an.&lt;/li&gt;

&lt;li&gt;Ein anderes Userinterface als mein Laptop. Der steht meistens nicht neben der Kaffeemaschine. Ausserdem soll die Bedienung die Optik der Silvia nicht zerstören.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mein momentaner Zwischenstand liegt auch bei &lt;a href='https://github.com/ChaosCoffeeControl/CCC-PIDController'&gt;github im ChaosCoffeeControl-Repository&lt;/a&gt;. Stay tuned.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Morphy Richards auf dem Pr&uuml;fstand</title>
   <link href="http://gonium.net//blog/2010/11/21/espresso-temperatur--morphy-richards-auf-dem-pru%CC%88fstand"/>
   <updated>2010-11-21T00:00:00+01:00</updated>
   <id>id:/blog/2010/11/21/espresso-temperatur--morphy-richards-auf-dem-prüfstand</id>
   <content type="html">&lt;p&gt;Guter Espresso heisst: Richtige Temperatur. OK, noch &lt;a href='http://kaffee-netz.de'&gt;viele andere Dinge&lt;/a&gt;, aber die Temperatur hat einen erheblichen Einfluss darauf, wie gut das Aroma der Kaffeebohne extrahiert wird. Wenn natürlich der Kaffee schon schlecht ist, kann auch kein guter Espresso herauskommen.&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='320' src='/images/20101121-morphy-temp-1.jpg' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Stellt sich also die Frage: Wie verhält sich mein 30 Euro eBay-Schnäppchen in dieser Disziplin? Die &lt;a href='http://www.morphyrichards.co.uk/ProductDetail.aspx?Product=47507'&gt;Morphy Richards 47507&lt;/a&gt; ist eine kleine &lt;a href='http://www.kaffeewiki.de/index.php?title=Thermoblockmaschinen'&gt;Thermoblockmaschine&lt;/a&gt;, d.h. sie enthält keinen Kessel, sondern heizt über einen Thermoblock das Wasser direkt auf. Das Verfahren ähnelt einem Durchlauferhitzer. Der Vorteil dieser Technik ist die geringe Vorheizzeit. Dafür hat die Technik aber den Nachteil, dass die Temperatur des Brühwassers relativ stark schwankt.&lt;/p&gt;

&lt;p&gt;Nach einem kurzen Blick auf meine Werkbank steht die Wahl des Materials auch fest: Ein DS18S20, kombiniert mit einem Arduino-Klon. Der &lt;a href='http://www.maxim-ic.com/datasheet/index.mvp/id/2815'&gt;DS1820&lt;/a&gt; ist ein One-Wire Temperatursensor mit einer Genauigkeit von 0.5°C. Ein einfacher Arduino-Sketch zum Auslesen ist auch schnell gefunden. Das Ganze war schnell zusammengelötet und mit &lt;a href='http://sugru.com'&gt;Sugru&lt;/a&gt; am Thermoblock befestigt:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='320' src='/images/20101121-morphy-ds1820.jpg' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Dabei natürlich etwas Wärmeleitpaste auf den Sensor, damit eventuelle Luftspalte die Wärmeübertragung nicht behindern können.&lt;/p&gt;

&lt;h3 id='die_messungen'&gt;Die Messungen&lt;/h3&gt;

&lt;p&gt;Das Sugru muss über Nacht aushärten. Heute hab ich dann eine kleine Messung gemacht. Der DS1820 kann Temperaturen bis 125°C messen - während der Dampfphase reicht das leider nicht aus. Eigentlich schade, denn die Sensoren sind genau und einfach in der Anwendung. Sugru ist übrigens bis 165°C stabil, also bestens als Montagematerial in Kaffeemaschinen geeignet. Der Temperaturverlauf sieht so aus:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='400' src='/images/20101121-morphy-temp.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Die horizontale Linie markiert 95°C, also quasi die Idealtemperatur. Während der initialen Heizphase schwankt die Temperatur zwischen 85°C und 104.5 ° C:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='400' src='/images/20101121-morphy-heating.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Das ist natürlich relativ weit von der optimalen Brüh-Temperatur entfernt. Eine Rancilio Silvia hat ein ähnliches Problem, dem man jedoch mit Hilfe eines PID-Reglers abhelfen kann. Bei der Zubereitung eines Cappucinos tritt folgender Verlauf auf:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='400' src='/images/20101121-morphy-bezug.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Beim ersten vertikalen Strich (T=2300) hab ich einen Leerbezug gemacht. Die Temperatur sinkt daraufhin doch recht stark. Bei T=2475 hab ich dann den Bezug gestartet, also 175 Sekunden (knapp 3 Minuten) nach dem Leerbezug. Die Temperatur war noch etwas zu gering. Bei T=2615 habe ich dann auf Dampf umgeschaltet, die Heizleuchte ging bei T=2576 wieder aus. Die Temperatur war trotzdem zu gering, als ich bei T=2615 den Dampfhahn aufdrehte &amp;#8212; da kam nur ein warmes Lüftchen aus der Dampflanze. Die Temperatur im Thermoblock steigt allerdings weiter - bei 127°C clippt der Sensor, die Temperatur war denke ich aber deutlich über 130°C.&lt;/p&gt;

&lt;p&gt;Der Espresso war übrigens akzeptabel. Unbewusst habe ich quasi &lt;a href='http://www.kaffeewiki.de/index.php?title=Temperatursurfen'&gt;Temperatursurfen&lt;/a&gt; betrieben. In Zukunft werde ich nach dem Leerbezug einfach drei Minuten warten, dann sollte die Temperatur relativ nahe bei 95°C sein.&lt;/p&gt;

&lt;p&gt;Alternativ dazu könnte ich natürlich auch einen PID-Regler nachrüsten - aber der Aufwand lohnt sich für die kleine Maschine nicht. Das mache ich dann nach dem nächsten Upgrade ;-)&lt;/p&gt;

&lt;h3 id='materialien'&gt;Materialien&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Der &lt;a href='/heap/20101121-morphy/ds18s20.pde'&gt;Arduino-Sketch&lt;/a&gt; zum Auswerten des DS1820&lt;/li&gt;

&lt;li&gt;Das &lt;a href='/heap/20101121-morphy/graph.R'&gt;R-Script&lt;/a&gt; zur Erzeugung der Plots&lt;/li&gt;

&lt;li&gt;Die &lt;a href='/heap/20101121-morphy/templog.txt'&gt;Messwerte&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;Für die Zukunft: &lt;a href='http://www.arduino.cc/playground/Main/BarebonesPIDForEspresso'&gt;Bare Bones Coffee Controller&lt;/a&gt;, eine PID-Regelung auf der Basis eines Arduinos.&lt;/li&gt;

&lt;li&gt;Recht ähnliche Herangehensweise: &lt;a href='http://espresso.liljenberg.net/pidproject'&gt;The PID Project&lt;/a&gt; &amp;#8212; hab ich allerdings erst später gefunden.&lt;/li&gt;
&lt;/ul&gt;</content>
 </entry>
 
 <entry>
   <title>Smart Meter-Datenkabel selbst gemacht: libehz</title>
   <link href="http://gonium.net//blog/2010/10/24/smart-meter-datenkabel-selbst-gemacht--libehz"/>
   <updated>2010-10-24T00:00:00+02:00</updated>
   <id>id:/blog/2010/10/24/smart-meter-datenkabel-selbst-gemacht--libehz</id>
   <content type="html">&lt;p&gt;&lt;em&gt;Hinweis 13.06.2011:&lt;/em&gt; &lt;em&gt;Die in diesem Artikel beschriebene Schaltung funktioniert sehr gut mit Zählern von Hager. Andere Zähler (z.B. der Fa. Easymeter) liefern jedoch nur ein sehr schwaches Signal, sodass die Schaltung hier versagt. Dominik Keller, einer meiner Studenten, arbeitet an einer verbesserten Schaltung. Seinen aktuellen Zwischenstand hat er hier dokumentiert:&lt;/em&gt; &lt;a href='http://developer.mysmartgrid.de/doku.php?id=optoreader'&gt;Optoreader im mySmartGrid Developer Wiki.&lt;/a&gt; &lt;em&gt;Wir werden &amp;#8212; sobald die Schaltung fertig gestellt ist &amp;#8212; einen Bausatz anbieten.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Mit Smart Metern sollen Stromkunden Ihren Stromverbrauch genauer kontrollieren können. Aber: Wer geht schon regelmäßig in den Keller, um seinen Stromzähler abzulesen? Der elektronische Haushaltszähler (eHZ), in Deutschland durch den VDE definiert und bei diversen Herstellern zu beziehen, schreibt eine Infrarotschnittstelle für den Stromkunden vor. Dort sollen beliebige Geräte den Momentanverbrauch, den Zählerstand und andere Informationen auslesen können.&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='320' src='/images/20101024-libehz-leser-1.jpg' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Das Problem dabei: Die optischen Ausleseköpfe (z.B. von &lt;a href='http://www.petechnik.de/de/produkte/k01-usb.html'&gt;PE Technik&lt;/a&gt;) sind ziemlich teuer, die Preise für einzelne Geräte schwanken zwischen 100 und 300 Euro. Für eine simple serielle Schnittstelle ein stolzer Preis!&lt;/p&gt;

&lt;p&gt;Also: Selbst ist der Mann. Meine ersten Experimente mit einem TSOP-1738 scheiterten. Dieser Standardbaustein empfängt Infrarotsignale im Bereich 880nm und wird normalerweise in Infrarotfernbedienungen eingesetzt. Dafür ist er auch optimiert: Ein integrierter Bandpass filtert eingehende Signale. Dazu erwartet der Baustein alle Signale aufmoduliert auf einen Träger von 38kHz &amp;#8212; normalerweise um Sonnenstrahlen von &amp;#8220;echten&amp;#8221; Fernbedienungssignalen zu unterscheiden. Ich konnte damit zwar Signale im Oszilloskop sehen, aber diese waren im Wesentlichen Binärabfall. Also: 880nm scheint zu funktionieren, der TSOP-1738 kann allerdings nur bis zu 2400 Baud übertragen. Der von mir verwendete eHZ von Hager sendet laut Datenblatt aber mit 9600 Baud. Der Binärabfall kommt also durch eine zu geringe Samplingrate zustande.&lt;/p&gt;

&lt;h3 id='diyempfnger'&gt;DIY-Empfänger&lt;/h3&gt;

&lt;p&gt;Mit einer Infrarotdiode vom Typ BPW82 funktioniert der Empfang allerdings prima. Im Oszilloskop erkennt man, das die Diode in Kombination mit einem Spannungsteiler ein Signal mit ca. 0.7V zwischen logisch LOW und HIGH ausgibt. Das Signal ist die obere, gelbe Linie in folgendem Screenshot:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='320' src='/images/20101024-libehz-oszi.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Das reicht leider noch nicht aus, um via TTL-USB-Konverter ausgelesen zu werden. In einer nächtlichen Hackaktion mit eBrnd und &amp;#8216; (vielen Dank an die Beiden!) entstand die folgende Transistorschaltung:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='265' src='/images/20101024-libehz-schaltplan.png' width='500' /&gt;
&lt;/div&gt;
&lt;p&gt;Damit wird der Pegel auf ein akzeptables Niveau gebracht und zudem noch das Signal geschärft. Im Oszilloskop-Screenshot oben ist die untere, grüne Linie das aufbereitete Signal. Auf Lochraster aufgebaut sieht die Schaltung (ohne TTL-USB-Adapter) so aus:&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='320' src='/images/20101024-libehz-leser-2.jpg' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;Rechts erkennt man die Infrarotdiode, eingebettet in ein Stück Schaumstoff und professionell festgeklebt&amp;#8482;. Der Schaumstoff hält die Diode an der richtigen Stelle direkt über der Sendediode des eHZ. Gleichzeitig wird Infrarotlicht aus der Umgebung abgeschirmt. Ich hatte bis jetzt keine Probleme mit Umgebungslicht.&lt;/p&gt;

&lt;h3 id='die_daten'&gt;Die Daten&lt;/h3&gt;

&lt;p&gt;Mit einem USB-TTL-Adapter kann man sich nun den Klartext in einer Terminal anschauen. Ich verwende dafür einen Kermit mit folgenden Einstellungen (in der .kermrc, 9600 7N1):&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='text'&gt; SET LINE /dev/cu.SLAB_USBtoUART
 SET SPEED 9600
 SET COMMAND BYTESIZE 7
 SET PARITY NONE
 SET STOP 1
 SET CARRIER-WATCH OFF
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Der eHZ gibt die folgenden Werte alle 1-4 Sekunden (abhängig von der Last am Zähler) aus:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='text'&gt; /HAG5eHZ010C_EHZ1vA02
 
 1-0:0.0.0*255(1095100000067865)
 1-0:1.8.0*255(000001.2963)
 1-0:96.5.5*255(82)
 0-0:96.1.255*255(0000067865)
 1-0:52.7.0*255(228.38*V)
 1-0:51.7.0*255(000.16*A)
 1-0:41.7.0*255(+00026*W)
 1-0:96.50.0*0(45)
 1-0:96.50.0*1(07D0)
 1-0:96.50.0*2(16)
 1-0:96.50.0*3(11)
 1-0:96.50.0*4(1F)
 1-0:96.50.0*5(18)
 1-0:96.50.0*6(003D381B070AF6B0CF05140900009F80)
 1-0:96.50.0*7(00)
 !
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Die Daten sind jeweils mit einer OBIS-Kennzahl versehen. Die Wirkleistung auf L2 hat z.B. die Kennzahl &amp;bdquo;1-0:41.7.0&amp;#8221; und den Wert 26W. Nun fehlt noch eine kleine C++-Applikation, welche die Datagramme auswertet und sauber zugänglich macht.&lt;/p&gt;

&lt;h3 id='cclient_fr_den_ehz'&gt;C++-Client für den eHZ&lt;/h3&gt;

&lt;p&gt;Einen minimalen Client in C++ gibt es nun schon einmal &lt;a href='http://github.com/gonium/libehz'&gt;im Repository auf Github.&lt;/a&gt;. Im Moment funktioniert der Client auf meinem Mac, allerdings sollte er auf jeder Plattform mit termios-Support laufen (also auch Linux). Mit einem einfachen&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='text'&gt; ehzread /dev/cu.SLAB_USBtoUART
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;gibt der Client die Datagramme des eHZ auf der Konsole aus. &lt;a href='http://github.com/gonium/libehz/blob/master/src/ehzread.cpp'&gt;Der Sourcecode&lt;/a&gt; ist recht simpel. Im Wesentlichen wird ein Filedesktriptor (tty_fd) an das Terminal-Device gebunden:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='cpp'&gt; &lt;span class='c1'&gt;// 7n1, see termios.h for more information&lt;/span&gt;
 &lt;span class='n'&gt;memset&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;tio&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='k'&gt;sizeof&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;tio&lt;/span&gt;&lt;span class='p'&gt;));&lt;/span&gt;
 &lt;span class='n'&gt;tio&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;c_iflag&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
 &lt;span class='n'&gt;tio&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;c_oflag&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
 &lt;span class='n'&gt;tio&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;c_cflag&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='n'&gt;CS7&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;CREAD&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;CLOCAL&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;           
 &lt;span class='n'&gt;tio&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;c_lflag&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
 &lt;span class='n'&gt;tio&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;c_cc&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;VMIN&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
 &lt;span class='n'&gt;tio&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;c_cc&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;VTIME&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='mi'&gt;5&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
 
 &lt;span class='n'&gt;tty_fd&lt;/span&gt;&lt;span class='o'&gt;=&lt;/span&gt;&lt;span class='n'&gt;open&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;argv&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;],&lt;/span&gt; &lt;span class='n'&gt;O_RDWR&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;O_NONBLOCK&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;      
 &lt;span class='n'&gt;cfsetospeed&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;tio&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='n'&gt;B9600&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;            &lt;span class='c1'&gt;// 9600 baud&lt;/span&gt;
 &lt;span class='n'&gt;cfsetispeed&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;tio&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;&lt;span class='n'&gt;B9600&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;            &lt;span class='c1'&gt;// 9600 baud&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In einer Endlosschleife liest der Client dann von tty_fd und gibt die Zeichen auf der Konsole aus. Hier kann dann ein Parser ansetzen.&lt;/p&gt;

&lt;h3 id='hilfreiche_ressourcen'&gt;Hilfreiche Ressourcen&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In Deutschland hat der VDE in zwei Lastenheften die Anforderungen an Smart Meter definiert: &lt;a href='http://www.vde.com/DE/FNN/ARBEITSGEBIETE/MESSWESEN/Seiten/zaehler.aspx'&gt;Lastenhefte EDL und eHZ beim FNN&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://www.vishay.com/photo-detectors/list/product-81529/'&gt;Vishay BPW82 Datasheet&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://www.hager.de/files/download/0/12505_1/0/ehz_betriebsanleitung_mid.pdf'&gt;Betriebsanleitung des Wechselstromzählers EHZ161WA von Hager&lt;/a&gt;: Hier ist der Aufbau der Datentelegramme und die Kommunikationsparameter beschrieben.&lt;/li&gt;

&lt;li&gt;&lt;a href='http://www.mayor.de/lian98/doc.de/html/g_iec62056_struct.htm'&gt;Beschreibung des IEC 62056-21 Datenformats&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://en.wikibooks.org/wiki/Serial_Programming/Serial_Linux'&gt;Wikibook: Serial Programming&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://stackoverflow.com/questions/2360063/pcrecpp-pcre-extract-hostname-from-url-code-problem'&gt;PCRE-CPP: Beispiel auf stackoverflow.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content>
 </entry>
 
 <entry>
   <title>Webserver Performance</title>
   <link href="http://gonium.net//blog/2010/10/10/webserver-performance"/>
   <updated>2010-10-10T00:00:00+02:00</updated>
   <id>id:/blog/2010/10/10/webserver-performance</id>
   <content type="html">&lt;p&gt;Since I now have a shiny new blog, I was curious how the runtime behaviour (loading times etc.) improved. As a first indicator, I use the developer tools included in Webkit (via Google&amp;#8217;s Chrome):&lt;/p&gt;
&lt;table&gt;
    &lt;tr&gt;
        &lt;th /&gt;
        &lt;th&gt;html&lt;/th&gt;
        &lt;th&gt;style&lt;/th&gt;
        &lt;th&gt;image&lt;/th&gt;
        &lt;th&gt;font&lt;/th&gt;
        &lt;th&gt;total&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;old blog&lt;/td&gt;
        &lt;td&gt;439 ms&lt;/td&gt;
        &lt;td&gt;317 ms&lt;/td&gt;
        &lt;td&gt;430 ms&lt;/td&gt;
        &lt;td&gt;210 ms&lt;/td&gt;
        &lt;td&gt;1.5 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;new blog&lt;/td&gt;
        &lt;td&gt;112 ms&lt;/td&gt;
        &lt;td&gt;61 ms&lt;/td&gt;
        &lt;td&gt;39 ms&lt;/td&gt;
        &lt;td&gt;56 ms&lt;/td&gt;
        &lt;td&gt;195 s&lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;As you can see, the new (static) website performs much better than the old (wordpress-based) one. Both pages were served from my Linux server, over the internet. I am currently sitting at the local university which has a decent internet connection, so it is safe to assume that the internet connectivity does not influence the measurements significantly.&lt;/p&gt;

&lt;h3 id='apache_benchmark'&gt;Apache Benchmark&lt;/h3&gt;

&lt;p&gt;For a real world benchmark, I assume 5 clients to access the site concurrently. Of course this does not reflect a Slashdotting, but I want to see the impact of PHP page creation vs. static pages. Apache provides a nifty tool for running such benchmarks, called ab. The ab command simulates a number of clients and measures the response times of a webserver. By default, ab prints a short statistic summary, but does not output the page load time by request. The parameter &amp;#8220;-g&amp;#8221; dumps these times in a file:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt; &lt;span class='nv'&gt;$ &lt;/span&gt;ab -n 1000 -c 5 -g oldblog.txt &lt;span class='nv'&gt;$URL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Please note that the ab tool does not evaluate the HTML file, so included pictures and CSS files are not loaded. Therefore I measure my webserver, the PHP blog software, and the SQL database in combination. I used the ggplot2 package in GNU R to generate some graphs:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='rconsole'&gt;&lt;span class='go'&gt; require(ggplot2);&lt;/span&gt;
&lt;span class='go'&gt; old&amp;lt;-read.table(&amp;quot;oldblog.txt&amp;quot;, header &amp;lt;- TRUE);&lt;/span&gt;
&lt;span class='go'&gt; png(filename = &amp;quot;old-hist.png&amp;quot;, width = 480,\&lt;/span&gt;
&lt;span class='go'&gt;   height = 400);&lt;/span&gt;
&lt;span class='go'&gt; qplot(ttime, data=old, geom=&amp;quot;histogram&amp;quot;,\&lt;/span&gt;
&lt;span class='go'&gt;   binwidth=25) + scale_x_continuous(&amp;quot;Load time (ms)&amp;quot;)\&lt;/span&gt;
&lt;span class='go'&gt;   + opts(title=&amp;quot;Old Blog&amp;quot;);&lt;/span&gt;
&lt;span class='go'&gt; dev.off();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;My old blog suffers from the PHP page generation. The median of the turnaround times is 724ms, slightly higher than the value measured with just one browser. This is a clear indicator that the webserver is already saturated. Another sign of saturation is the long tail of up to 4.7s.&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='400' src='/images/20101010-old-hist.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;The plot of the new blog looks rather uninteresting. The site is much faster &amp;#8212; a single page is delivered in 38ms (median), with a maximum of 163ms. The server does not seem to be saturated by the 5 simulated clients.&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='400' src='/images/20101010-new-hist.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;To push my little virtual server to the limits, I increased the number of concurrently simulated clients from 5 to 250. The median of the turnaround time is now 824ms, with a maximum of 7.1s.&lt;/p&gt;
&lt;div class='span-14'&gt;
&lt;img class='top left' height='400' src='/images/20101010-new-250-hist.png' width='480' /&gt;
&lt;/div&gt;
&lt;p&gt;In other words: static websites rock, they&amp;#8217;re fast and secure. No more wordpress security updates for me.&lt;/p&gt;

&lt;h3 id='related_links'&gt;Related Links&lt;/h3&gt;

&lt;p&gt;Some links I&amp;#8217;ve read during the work on this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='http://railslove.com/weblog/2009/06/15/benchmariking-und-uberwachen-von-webapps-mit-httperf-munin-und-gltail/'&gt;Benchmarking Websites @ Railslove&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://www.cyberciti.biz/tips/howto-performance-benchmarks-a-web-server.html'&gt;Howto: Performance Benchmarks a Webserver (sic)&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://www.debianroot.de/server/webserver-benchmark-mit-dem-apache-benchmark-tool-ab-1187.html'&gt;Debianroot: Apache Benchmark Tool&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://had.co.nz/ggplot2/'&gt;ggplot2 Website&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content>
 </entry>
 
 <entry>
   <title>Jekyll for the Rescue</title>
   <link href="http://gonium.net//blog/2010/10/03/Jekyll-for-the-Rescue"/>
   <updated>2010-10-03T00:00:00+02:00</updated>
   <id>id:/blog/2010/10/03/Jekyll-for-the-Rescue</id>
   <content type="html">&lt;p&gt;I am tired of Wordpress. Don&amp;#8217;t get me wrong, Wordpress is a really powerful blogging engine. But do I really need to have a complex software to do my occasional blogging? Whenever I need to do something that is not built directly into Wordpress, I start hunting for a plugin that does the trick. Then I fiddle it into my template, and during the next security upgrade, everything breaks. The consequence: I don&amp;#8217;t update my Wordpress installation often, which is a really bad idea. Most compromised webservers have been opened using the web applications they serve.&lt;/p&gt;
&lt;img alt='CC-BY-NC B Tal' class='top left pull-4' src='/images/gears.jpg' /&gt;
&lt;p&gt;In the early days I wrote a small generator that took a bunch of scripts that generated a static site and uploaded it to a simple webspace. Although simple, it worked great and did not require any scripting language or database on the server side. Now, I am going back to it.&lt;/p&gt;

&lt;h3 id='meet_jekyll'&gt;Meet Jekyll&lt;/h3&gt;

&lt;p&gt;Jekyll is a static site generator written in Ruby by Tom Preston-Werner, co-founder of github.com. In &lt;a href='http://tom.preston-werner.com/2008/11/17/blogging-like-a-hacker.html'&gt;&amp;#8220;Blogging Like a Hacker&amp;#8221;&lt;/a&gt; he introduces the system. I write this text in vim. Then, jekyll takes the text (formatted in Markdown syntax) and renders the website, using various template files. The tags are stored as properties associated with each post. When I create the website using a Rakefile, a task collects all tags, builds index pages and creates the tag cloud. In addition, the list of blog entries is updated and formatted. Everything is nice and simple. An integrated Webrick server allows me to check how the page looks in a web browser.&lt;/p&gt;

&lt;p&gt;This is the first time in years that I actually care about the look of a website. Since jekyll does not provide a default layout, I wrote one from scratch and learned a lot along the way. The layout is implemented using the Blueprint framework. The font (&amp;#8220;Vollkorn&amp;#8221;) is provided by Google. I used Adobe&amp;#8217;s Kuler to select the colors. Surprisingly, this was much simpler than to fiddle with various Wordpress templates.&lt;/p&gt;

&lt;p&gt;The cool thing is that I can store the whole website in a git repository (its just text, after all) and use rsync to push it onto my server. No dynamic components are needed. One disadvantage is that I don&amp;#8217;t have comments yet, but I&amp;#8217;ve seen people integrating Disqus, so it can be done.&lt;/p&gt;

&lt;h3 id='helpful_links'&gt;Helpful links&lt;/h3&gt;

&lt;p&gt;What follows is a collection of links that were helping me to understand Jekyll and CSS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='http://en.wikipedia.org/wiki/Markdown'&gt;Markdown syntax notes on Wikipedia&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://net.tutsplus.com/tutorials/html-css-techniques/a-closer-look-at-the-blueprint-css-framework/'&gt;A closer look at the blueprint css framework&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://www.blueprintcss.org/tests/parts/sample.html'&gt;Blueprint CSS example page&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://www.garethjmsaunders.co.uk/blueprint/'&gt;Blueprint Cheatsheets&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://www.codeography.com/2009/03/30/getting-started-with-jekyll.html'&gt;Getting Started with Jekyll&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://github.com/mojombo/jekyll/wiki/Plugins'&gt;Jekyll Plugins&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://www.justkez.com/generating-a-tag-cloud-in-jekyll'&gt;Generating a Tag Cloud in Jekyll&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://stackoverflow.com/questions/1408824/an-easy-way-to-support-tags-in-a-jekyll-blog'&gt;Stackoverflow thread on Tag Clouds with Jekyll&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://github.com/mattfoster/mattfoster.github.com/blob/master/Rakefile'&gt;Tags with a Rakefile&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://github.com/scottkf/tesoriere.com/'&gt;Source of tesoriere.com&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://www.informationarchitects.jp/en/the-web-is-all-about-typography-period/'&gt;Web Design is 95% Typography&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The image has been CC&amp;#8217;ed by &lt;a href='http://www.flickr.com/photos/b-tal/428943971/'&gt;B Tal&lt;/a&gt;, thanks!&lt;/p&gt;</content>
 </entry>
 

</feed>

