artikel text fertig
This commit is contained in:
parent
5a12eef8a6
commit
d792f5b19e
Binary file not shown.
@ -3,21 +3,22 @@
|
||||
|
||||
0) Organisiere dir an deinem lokalen makerspace folgende Fertigungsmöglichkeiten: Ein 3D-Drucker mit mindestens 25cm Bauraumhöhe. Ein Lasercutter oder eine andere Möglichkeit, um 3mm dickes Sperrholz zuzuschneiden. Ein Lötkolben.
|
||||
1) Baue an deinem 3D-Drucker eine 0.8mm-Düse ein.
|
||||
2) Drucke Boden und Deckel mit normalen Druckeinstellungen mit hellem PLA, support ist nicht nötig. Drucke die Kralle in einer anderen Farbe, support ist ebenfalls nicht nötig.
|
||||
3) Drucke das mittlere Teil im Vasenmodus mit hellem PLA.
|
||||
4) Schneide die Holzteile zurecht.
|
||||
5) Presse die Muttern in die kleinen Ringe und klebe die kleinen Ringe mit Muttern an die großen Ringe. Beachte die Bilder, um die richtige Seite zu treffen.
|
||||
6) Montiere die Holzteile innerhalb der Vase zusammen und verklebe die Holz-Holz-Kontakte mit Holzleim.
|
||||
7) Stecke das LED-Band in einen Kanal, und schneide es so ab, dass es oben noch 10mm heraus schaut. Ziehe es wieder heraus und schneide für die weiteren Kanäle LED-Streifen mit der selben Länge ab.
|
||||
8) Löte 3-polige Steckverbinder an das obere Ende der LED-Streifen. Das obere Ende erkennt man so, dass die aufgedruckten Pfeile vom oberen Ende wegschauen.
|
||||
9) Ziehe die LED-Streifen in die Kanäle ein.
|
||||
2) Drucke Boden und Deckel mit normalen Druckeinstellungen mit hellem PLA, support ist nicht nötig. Drucke die Kralle in einer anderen Farbe. Support ist ebenfalls nicht nötig, aber falls sich das Druckbett schnell bewegt, kann ein brim helfen.
|
||||
3) Drucke das mittlere Teil im Vasenmodus mit hellem PLA. Stelle die Anzahl der bottom layers auf Null. Auch hier kann ein brim helfen.
|
||||
4) Schneide die Holzteile in 3mm Dicke zurecht. Teste, ob sich die Holzteile ineinander stecken lassen.
|
||||
5) Presse die Muttern in die kleinen Ringe und klebe die kleinen Holzringe mit Muttern an die großen Holzringe. Beachte die Bilder, um die richtige Seite zu treffen. (TODO)
|
||||
6) Senke mit einem Löteisen die Einsenkmuttern in das Bodenteil.
|
||||
7) Montiere die Holzteile innerhalb der Vase zusammen und verklebe die Holz-Holz-Kontakte mit Holzleim. Vermeide dabei Express-Holzleim.
|
||||
8) Stecke das LED-Band in einen Kanal, und kürze sie derart, dass es oben noch 10mm heraus schaut. Ziehe es wieder heraus und schneide für die weiteren Kanäle LED-Streifen mit der selben Länge ab.
|
||||
9) Löte 3-polige Steckverbinder an das obere Ende der LED-Streifen. Orientiere es dabei so, dass die aufgedruckten Pfeile später nach unten (TODO) zeigen.
|
||||
10) Schraube den Boden am Mittelteil fest.
|
||||
11) Verlöte Mikrofon, ESP, Beschleunigungssensor, Spannungsversorgung und die Anschlussleitungen für die LEDs gemäß der Darstellung (Todo).
|
||||
12) Montiere die gelöteten Teile auf dem gelochten Holzbrett, z.B. mit Hilfe von dünnen Kabelbindern und Heißkleber.
|
||||
13) Schraube die Kralle an den Boden.
|
||||
14) Stecke das Holzbrett in die dafür vorgesehene Aussparung im Inneren des Bodens.
|
||||
15) Verbinde die Steckverbinder der LEDs.
|
||||
16) Schließe den Deckel und schraube ihn fest.
|
||||
17) Erinnere dich daran, dass du den Akku nicht geladen hast.
|
||||
18) Wiederhole Schritte 14 bis 16.
|
||||
19) Stecke die Kralle auf den Stab.
|
||||
11) Ziehe die LED-Streifen in die Kanäle ein.
|
||||
12) Spiele die Software auf den ESP, z.B. mit Visual Studio Code und der Erweiterung PlatformIO.
|
||||
13) Verlöte Mikrofon, ESP, Beschleunigungssensor, Spannungsversorgung und die Anschlussleitungen für die LEDs gemäß der Darstellung (Todo).
|
||||
14) Montiere die gelöteten Teile auf dem gelochten Holzbrett, z.B. mit Hilfe von dünnen Kabelbindern und Heißkleber.
|
||||
15) Lade den Akkupack, schließe und schalte ihn an.
|
||||
16) Schraube die Kralle an den Boden.
|
||||
17) Stecke das Holzbrett in die dafür vorgesehene Aussparung im Inneren des Bodens.
|
||||
18) Verbinde die Steckverbinder der LEDs.
|
||||
19) Schließe den Deckel und schraube ihn fest.
|
||||
20) Stecke die Kralle auf den Stab.
|
@ -1,9 +1,7 @@
|
||||
|
||||
Fusion Zauberstab
|
||||
=================
|
||||
<!--- Vorspann --->
|
||||
|
||||
<!--- Artikel --->
|
||||
# Motivation
|
||||
|
||||
# Konzept
|
||||
|
@ -7,12 +7,12 @@ Dafür muss zu jedem Zeitpunkt sowohl die Frequenz als auch die Phase des beats
|
||||
Die zentrale Idee des Algorithmus ist es, Bandpassfilter mit schwacher Dämpfung als Phasenschätzer zu missbrauchen.
|
||||
Hierfür wird eine ganze Reihe an Bandpässen mit unterschiedlichen Durchlassfrequenzen parallel geschaltet. Diese Durchlassfrequenzen gehören zu unterschiedlichen beat-Geschwindigkeiten. In unserem Fall von 105 bis 150bpm, der typische Bereich elektronischer Tanzmusik. Auf jeden dieser Bandpässe wird nun die Signalenergie gegeben. Der Bandpass, der sich daraufhin am meisten aufschwingt, hat dann offensichtlich am besten die richtige Geschwindigkeit getroffen.
|
||||
|
||||
Im Detail: Zuerst sammeln wir samples vom Mikrofon. Wir ziehen den gleitenden Durchschnitt ab, um ein Audiosignal zu erzeugen, welches um Null herum schwingt. Über jeweils ein chunk - eine Vierzigstelsekunde - summieren wir über quadrierte samples, um die Signalenergie in diesem chunk zu erhalten. Alles weitere passiert nun ebenfalls 40 mal die Sekunde. Wir geben die Signalenergie parallel als input auf alle Bandpässe - zufällig ebenfalls 40 an der Zahl. Der Bandpass, der sich am weitesten aufschwingt, wird wohl der richtige sein. Nun haben wir das Problem, dass der Bandpass einmal pro beat schwingt, unsere gewollte Animation aber zwei beats benötigt. Wir müssen also irgendwie die Frequenz halbieren. Dafür verzögern wir das Signal um Pi/4 und schätzen mit der atan2-Funktion die Phase. Mit der bekannten Phase und einem einfachen Zustandsautomat konstruieren wir uns die Phase eines Schwingers mit halber Frequenz. Diese Phase skalieren wir linear und erhalten so die Sollposition des Maximums der Lichtwelle.
|
||||
Im Detail: Zuerst sammeln wir samples vom Mikrofon. Wir ziehen den gleitenden Durchschnitt ab, um ein Signal zu erzeugen, welches um Null herum schwingt. Über jeweils einen chunk - eine Vierzigstelsekunde - summieren wir quadrierte samples, um die Signalenergie in diesem chunk zu erhalten. Alles weitere passiert nun ebenfalls 40 mal die Sekunde. Wir geben die Signalenergie parallel als input auf alle Bandpässe - zufällig ebenfalls 40 an der Zahl. Eine der Bandpässe schwingt sich am meisten auf. Wir gehen davon aus, dass dessen Resonanzfrequenz am nähesten am tatsächlichen beat liegt. Kleine Abweichungen werden durch das ständige feedback fortlaufend korrigiert. Nun haben wir das Problem, dass der Bandpass einmal pro beat schwingt, unsere gewollte Animation aber zwei beats benötigt. Wir müssen also irgendwie die Frequenz halbieren. Dafür verzögern wir das Signal um Pi/4 und schätzen mit der atan2-Funktion die Phase. Mit der bekannten Phase und einem einfachen Zustandsautomat konstruieren wir uns die Phase eines Systems mit halber Frequenz. Diese Phase skalieren wir linear und erhalten so die Sollposition des Maximums der Lichtwelle.
|
||||
|
||||
Wir wissen nun also zu jedem Zeitpunkt die Position des Scheitelpunktes der Lichtwelle. Um diese Position herum wird die Helligkeit langsam gesenkt und damit die Welle geformt. Die Maxima für den roten und blauen Kanal sind dabei gegenläufig verschoben, so dass ein räumlicher Farbverlauf entsteht.
|
||||
|
||||
Das Ganze ist hier illustriert:
|
||||
|
||||
![Blockdiagramm Beaterkennung](grafiken/illustrationen/beaterkennung.PNG "Blockdiagramm Beaterkennung")
|
||||
![Illustration Beaterkennung](grafiken/illustrationen/beaterkennung.PNG "Illustration Beaterkennung")
|
||||
|
||||
|
||||
|
12
make_artikel/fackel.md
Normal file
12
make_artikel/fackel.md
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
# Fackel
|
||||
|
||||
Die zweite Animation ahmt eine Fackel nach, wobei der Brennstoff entsprechend dem beat nachgeführt wird. Entsprechend der Musik wabern dann Feuerschwaden langsam nach oben. Kurzfristige Änderungen in der Musik - z.B. durch bass drops - führen temporär zu größeren Feuerschwaden, was besonder spektakulär aussieht.
|
||||
|
||||
Wie bei der ersten Animation normieren wir zunächst das Mikrofonsignal auf einen Durchschnitt von Null und ermitteln daraus in chunks von 10 Millisekunden die Signalenergie. Die Signalenergie wird nun mit zwei unterschiedlichen Tiefpässen gefiltert. Ein Tiefpass hat eine kurze Zeitkonstante und dient nur zur Glättung des Signals. Der andere Tiefpass hat eine Zeitkonstante von mehreren Sekunden und gibt Auskunft über die durchschnittliche Lautstärke.
|
||||
|
||||
Der folgende Teil wird alle 45 Millisekunden ausgeführt:
|
||||
Wenn der schnelle Tiefpass mindestens 15% über dem langsamen Tiefpass liegt, dann gehen wir davon aus, dass aktuell ein beat vorliegt. Die untersten beiden LEDs werden nun mit mit der maximalen Helligkeit angesteuert. Alle anderen LEDs ermitteln ihre Helligkeit aus der durchschnittlichen Helligkeit der beiden LEDs darunter im vorhergehenden Zeitschritt, abzüglich einer kleinen Dämpfung. Das führt dazu, dass die Feuerschwaden nach oben hin dunkler, aber auch größer werden. Das Vorgehen ist im folgenden Bild illustriert:
|
||||
|
||||
![Illustration Beaterkennung](grafiken/illustrationen/fackel.PNG "Illustration Fackel")
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.1 MiB |
@ -3,7 +3,7 @@ import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
|
||||
n_plots = 8
|
||||
n_plots = 7
|
||||
endtime = 4
|
||||
size = 1000
|
||||
|
||||
@ -12,7 +12,7 @@ audio = 1023*np.random.random(size=(size))
|
||||
audio = 512+(audio-512)*(0.2)*np.sin(np.linspace(0,endtime*2*3.14*2, size))+(audio-512)*0.3
|
||||
|
||||
#fig, axs = plt.subplots(n_plots, 1, sharex=True)
|
||||
fig = plt.figure(figsize=(10,10))
|
||||
fig = plt.figure(figsize=(10,6))
|
||||
gs = fig.add_gridspec(n_plots, hspace=0)
|
||||
axs = gs.subplots(sharex=True)
|
||||
|
||||
@ -26,8 +26,10 @@ axs[0].plot(time,audio)
|
||||
axs[0].set_ylabel("Mikrofon\n-signal", x=titlex, y=titley)
|
||||
|
||||
audio_norm = audio-512
|
||||
axs[1].plot(time,audio_norm)
|
||||
axs[1].set_ylabel("Mikrofon\n-signal\nnormiert", x=titlex, y=titley)
|
||||
# axs[1].plot(time,audio_norm)
|
||||
# axs[1].set_ylabel("Mikrofon\n-signal\nnormiert", x=titlex, y=titley)
|
||||
|
||||
offset = 1
|
||||
|
||||
spc = size/endtime
|
||||
|
||||
@ -36,8 +38,8 @@ spc = size/endtime
|
||||
audio_norm = np.array(audio_norm)
|
||||
audio_squared = np.square(audio_norm)
|
||||
|
||||
axs[2].plot(time, audio_squared)
|
||||
axs[2].set_ylabel("Signal\n-energie", x=titlex, y=titley)
|
||||
axs[2-offset].plot(time, audio_squared)
|
||||
axs[2-offset].set_ylabel("Signal\n-energie\n(gefiltert)", x=titlex, y=titley)
|
||||
|
||||
spc = int(size/endtime/40)
|
||||
|
||||
@ -54,8 +56,8 @@ for sample, timepoint in zip(audio_squared, time):
|
||||
chunktimes.append(timepoint)
|
||||
energy = 0
|
||||
|
||||
axs[3].plot(chunktimes, chunks)
|
||||
axs[3].set_ylabel("Signale\n-nergie\nchunks", x=titlex, y=titley)
|
||||
axs[3-offset].plot(chunktimes, chunks)
|
||||
axs[3-offset].set_ylabel("Signale\n-nergie\nchunks", x=titlex, y=titley)
|
||||
|
||||
n_BP = 5
|
||||
SAMPLING_FREQUENCY_BP = 40
|
||||
@ -118,23 +120,23 @@ for i in range(n_BP):
|
||||
|
||||
filter_output.append(y)
|
||||
|
||||
axs[4].plot(chunktimes, filter_output, color="#1f77b4" if i == 1 else "grey")
|
||||
axs[4-offset].plot(chunktimes, filter_output, color="k" if i == 1 else "#1f77b4")
|
||||
|
||||
if i == 1:
|
||||
axs[5].plot(chunktimes, filter_output)
|
||||
axs[5-offset].plot(chunktimes, filter_output)
|
||||
|
||||
filter_outputs.append(filter_output)
|
||||
|
||||
axs[4].set_ylabel("Band\n-pässe", x=titlex, y=titley)
|
||||
axs[4-offset].set_ylabel("Band\n-pässe", x=titlex, y=titley)
|
||||
|
||||
axs[5].plot(chunktimes, delayed, color="grey")
|
||||
axs[5].set_ylabel("Delay", x=titlex, y=titley)
|
||||
axs[5-offset].plot(chunktimes, delayed, color="k")
|
||||
axs[5-offset].set_ylabel("Delay", x=titlex, y=titley)
|
||||
|
||||
axs[6].plot(chunktimes, angles)
|
||||
axs[6].set_ylabel("Geschätzter\nPhasen\nwinkel", x=titlex, y=titley)
|
||||
axs[6-offset].plot(chunktimes, angles)
|
||||
axs[6-offset].set_ylabel("Geschätzter\nPhasen\nwinkel", x=titlex, y=titley)
|
||||
|
||||
axs[7].plot(chunktimes, angles2)
|
||||
axs[7].set_ylabel("Halbierte\nFrequenz", x=titlex, y=titley)
|
||||
axs[7-offset].plot(chunktimes, angles2)
|
||||
axs[7-offset].set_ylabel("Halbierte\nFrequenz", x=titlex, y=titley)
|
||||
|
||||
for i in range(n_plots):
|
||||
axs[i].set_yticks(())
|
||||
|
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 124 KiB |
BIN
make_artikel/grafiken/illustrationen/fackel.png
Normal file
BIN
make_artikel/grafiken/illustrationen/fackel.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 793 KiB |
115
make_artikel/grafiken/illustrationen/fackel.py
Normal file
115
make_artikel/grafiken/illustrationen/fackel.py
Normal file
@ -0,0 +1,115 @@
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
n_plots = 3
|
||||
titlex = 0.5
|
||||
titley = 0.5
|
||||
endtime = 4
|
||||
size = 500
|
||||
|
||||
#fig, axs = plt.subplots(n_plots, 1, sharex=True)
|
||||
fig = plt.figure(figsize=(10,4))
|
||||
gs = fig.add_gridspec(n_plots, hspace=0)
|
||||
axs = gs.subplots()
|
||||
|
||||
|
||||
##############################################
|
||||
|
||||
time = np.linspace(0,endtime,size)
|
||||
audio = 1023*np.random.random(size=(size))
|
||||
audio = 512+(audio-512)*(0.2)*np.sin(np.linspace(0,endtime*2*3.14*2, size))+(audio-512)*0.3
|
||||
|
||||
for i in range(size):
|
||||
if i < size/2:
|
||||
audio[i] = 0.5*audio[i]+256
|
||||
|
||||
axs[0].set_xlim((-0,4))
|
||||
axs[0].plot(time,audio)
|
||||
axs[0].set_ylabel("Mikrofon\n-signal", x=titlex, y=titley)
|
||||
|
||||
|
||||
audio_norm = audio-512
|
||||
|
||||
|
||||
spc = size/endtime
|
||||
|
||||
|
||||
|
||||
audio_norm = np.array(audio_norm)
|
||||
audio_squared = np.square(audio_norm)
|
||||
|
||||
|
||||
audio_squared_filtered = list()
|
||||
state = 0
|
||||
for sample in audio_squared:
|
||||
state += (sample-state)*0.01
|
||||
audio_squared_filtered.append(state)
|
||||
|
||||
|
||||
audio_squared_filtered2 = list()
|
||||
state = 0
|
||||
for sample in audio_squared:
|
||||
state += (sample-state)*0.1
|
||||
audio_squared_filtered2.append(state)
|
||||
|
||||
|
||||
audio_squared = np.array(audio_squared)
|
||||
audio_squared_filtered = np.array(audio_squared_filtered)
|
||||
audio_squared_filtered2 = np.array(audio_squared_filtered2)
|
||||
|
||||
axs[1].plot(time, audio_squared)
|
||||
axs[1].plot(time, audio_squared_filtered, "k--")
|
||||
axs[1].plot(time, audio_squared_filtered2, "k")
|
||||
axs[1].set_ylabel("Signal\n-energie\n(gefiltert)", x=titlex, y=titley)
|
||||
axs[1].set_xlim((-0,4))
|
||||
|
||||
|
||||
n_LED = 500
|
||||
|
||||
animation = np.zeros((size, n_LED))
|
||||
|
||||
for i in range(size):
|
||||
|
||||
|
||||
|
||||
for j in range(n_LED):
|
||||
|
||||
|
||||
if i == 0:
|
||||
animation[i,j] = 0
|
||||
|
||||
else:
|
||||
if j == 0:
|
||||
animation[i,j] = 10 if audio_squared_filtered2[i]*1.15 < audio_squared_filtered[i] else 255
|
||||
elif j == 1:
|
||||
animation[i,j] = 10 if audio_squared_filtered2[i]*1.15 < audio_squared_filtered[i] else 255
|
||||
elif j == 2:
|
||||
animation[i,j] = 10 if audio_squared_filtered2[i]*1.15 < audio_squared_filtered[i] else 255
|
||||
else:
|
||||
animation[i,j] = (animation[i-1,j-1]+animation[i-1,j-2]+animation[i-1,j-3])*0.33
|
||||
|
||||
|
||||
print(repr(animation))
|
||||
|
||||
animation = animation.T
|
||||
|
||||
animation = np.flip(animation, axis=0)
|
||||
|
||||
axs[2].imshow(animation, cmap="Blues")
|
||||
axs[2].set_aspect('auto')
|
||||
axs[2].set_ylabel("LED\nLicht-\nverlauf", x=titlex, y=titley)
|
||||
|
||||
|
||||
|
||||
for i in range(n_plots):
|
||||
axs[i].set_yticks(())
|
||||
axs[i].set_xticks(())
|
||||
|
||||
|
||||
#axs[2].set_xlabel("Zeit")
|
||||
|
||||
|
||||
plt.savefig("fackel.svg")
|
||||
plt.savefig("fackel.png", dpi=500)
|
||||
plt.show()
|
2163
make_artikel/grafiken/illustrationen/fackel.svg
Normal file
2163
make_artikel/grafiken/illustrationen/fackel.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 90 KiB |
17
make_artikel/software.md
Normal file
17
make_artikel/software.md
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
# Software
|
||||
|
||||
Softwareseitig kam ein selbst geschriebenes Programm in C und C++ zum Einsatz. Dieses ist strukturiert in verschiedene "apps", welche jeweils unterschiedliche Animationen und Funktionen auf dem Ei ausführen. Eine übergeordnete Kontrollschleife schaltet zwischen den apps umher.
|
||||
|
||||
Für das Ansprechen der LED-Streifen nutzten wir die FastLED-Bibliothek, für die Kommunikation mit dem Beschleunigungssensor die ADXL345-Bibliothek.
|
||||
|
||||
Insgesamt entwickelten wir fünf apps, wovon jedoch nicht mehr alle produktiv genutzt werden:
|
||||
- Eine Fackelanimation, die bei lauten beats "Brennstoff ins Feuer schmeißt"
|
||||
- Eine Animation mit Beaterkennung, die sich mittelfristig in Frequenz und Phase an die bpm anpasst
|
||||
- Ein vu-Meter, um die Aussteuerung des Mikrofons zu testen
|
||||
- Eine fft-Anzeige, zu Testzwecken
|
||||
- Deep sleep - das Ei hat keinen Ein/Ausschalter, diese app ermöglicht aber eine recht lange Zeit im standby
|
||||
|
||||
Für das Umschalten zwischen den Apps verwendeten wir interrupts seiten des Beschleunigungssensors. Die ADXL345-Bibliothek bietet hier bereits die Möglichkeit, um die nötigen Parameter eines double taps einfach einzustellen. Sobald das Ei mit dem richtigen zeitlichen Abstand und der richtigen Beschleunigung zwei mal bewegt wird, wird ein double tap ausgelöst und die app umgeschaltet.
|
||||
|
||||
Weitere apps sind leicht ergänzbar. Es muss jeweils nur eine init, eine deinit, und eine loop-Methode definiert werden.
|
@ -3,5 +3,4 @@
|
||||
2) fotos machen beim aufbau
|
||||
3) explosionszeichnung labeln
|
||||
4) fritzing
|
||||
5) komponentenliste (stab (20mm außen, 17mm innen), schrauben (TODO), akkupack, steckverbinder, esp, mikro, beschleunigungssensor, litzen, schrumpfschläuche, sperrholz, 2 sorten pla, einpressmuttern 4x4), dünne lange kabelbinder
|
||||
6) (optional) obere 3 verbindungen zwischen kralle und ei weg
|
||||
5) komponentenliste (stab (20mm außen, 17mm innen), schrauben (M4 Schraubenset), akkupack (z.b. die intenso XS 10000 mit den Abmessungen 91 x 63 x 22 mm, 182 g), steckverbinder (z.b. JST SM 3-pin), esp32, mikrofon (MAX4466), beschleunigungssensor (TODO), litzen, schrumpfschläuche, 3mm sperrholz, 2 sorten pla (hell+dunkel), einpressmuttern 4x4), dünne kabelbinder
|
||||
|
Loading…
Reference in New Issue
Block a user