Hallo,
Ik ben bezig in Python een algoritme te schrijven. Momenteel draait dit op 64 bit Linux Ubuntu, de bedoeling was echter om dit op een Raspberry Pi first gen te draaien. Probleem is echter dat de Athlon A6-3500 in mijn desktop al tegen de 70% gaat onder dit proces. Ik moet eerlijk toegeven het nog niet daadwerkelijk op mijn Pi getest te hebben, maar die kroop al bang in een hoekje bij het zien van de code
Doel is om een 50 pixel LED string aan te sturen met WS2811 IC's om zodoende een moodlight te bouwen. Hardware heb ik werkend en kan ik uit Python aansturen, dat is het probleem niet.
(code en probleemstelling staan onderaan, eerst een wat uit de hand gelopen uitleg van het eigenlijke doel)
Om effecten te kunnen maken op deze LED string ga ik uit van het volgende principe. Ik maak 'waves' van kleuren. Een wave kan door de main class op elk willekeurig moment toegevoegd worden en heeft als informatie een kleur, een fadetime, een lifetime, een walkspeed en een wobblefactor. Kleur spreekt voor zich, een lifetime geeft aan hoe lang de wave moet blijven leven en de fadetime geeft aan in hoeveel tijd de wave moet in- en uitfaden. De walkspeed (die overigens nog niet geimplementeerd is) geeft aan hoe snel de wave moet bewegen over de LED string. De wobblefactor geeft aan in hoeverre de wave aan de hand van een random waarde moet 'groeien' (infaden) of 'krimpen' (uitfaden). Dit zodat het geheel wat levendiger wordt. Tijdens het programmeren heb ik geprobeerd dit voor me te zien als een 2D beeld. Een vlak op de grond van 50 vakjes (LEDs) en daar bovenop de waves die zich als bergen op het landschap vormen. Een stuk omhoog, de top, en dan weer omlaag.
De implementatie dan. Om die bergvorm te kunnen maken, ga ik uit van de formule y=1-x^2. Zo krijg ik een parabool die op x=-1 en x=1 de nullijn kruist en zijn top op 1 heeft. Dit bereik van 2 verdeel ik in bijvoorbeeld 6 vakjes, en daarvan pak ik de gemiddelde waarde (ongeveer, ik ga uit van de linker en rechter grenzen van de vakjes, pak daar de waardes van, en neem daar het gemiddelde van. Dat het een gebogen lijn betreft en het dus geen exact gemiddelde is, vind ik niet zo belangrijk: zo nauw komt het niet). Dan heb ik een waarde tussen 1 en 0. Die waarde vermeningvuldig ik vervolgens met een waarde die ik afleid uit de fader. Die geeft, afhankelijk van waar de wave is in zijn lifespan, een waarde aan. Stel dat ik op de helft tussen de linker grens van de wave en de top zit, en de wave 1 seconde bestaat met een fadetime van 3 seconden. Dan krijg ik dus 0.5 * 0.3 = 0.15. Die factor vermeningvuldig ik eventueel met de wobbleFactor, en en met elk van de drie kleuren om uit te komen op de uiteindelijke waarde. Dit doe ik elke 30 ms, omdat ik ongeveer 30 'fps' wil bereiken. Elke 30ms wordt van het object colorWave de functie beat aangeroepen om dat te bewerkstelligen.
De praktijk dan:
Ik denk zelf dat het probleem zit in het geklooi met al die floats, gecombineerd met het voor elk block twee keer moeten machtverheffen. Echter heb ik geen idee hoe ik daar onderuit kan komen en óf het daadwerkelijk het probleem is. Opzich zijn dat natuurlijk snelle berekeningen, maar ze gebeuren zo wel 2 berekeningen * 20 pixels * 4 waves * 30 is 4800 keer per seconde.
Ik heb al geprobeerd niet met floats te werken maar met integers, dat geeft natuurlijk een zeer hoekig effect of ik moet met integers van 16 bits gaan werken. Ten eerste krijg ik dat niet voor elkaar (als in, ik krijg gigantische koppijn van het implementeren daarvan) en ten tweede blijken testen die ik met stukjes van de code gedaan heb daarmee (de fader en wobble) eigenlijk nauwelijks van invloed. Ook heb ik geprobeerd in plaats van de dictionary (die ik maak op 95 en vul op 187) een list te maken. Ik ging er vanuit dat die sneller zouden zijn omdat ze niet associated zijn, maar dat had ik dus mis: de load gaat omhoog.
Iemand enig idee hoe ik dit geheel wat lichter kan maken, of uberhaupt wat nou datgene is waar die load vandaan komt? Of is eigenlijk de enige goede manier om dit te gaan herschrijven in een gecompileerde taal met fixed data types?
Overigens ben ik mij ervan bewust dat er diverse stukken software rondzwerven die iets vergelijkbaars doen. Mijn toepassing is echter vrij specifiek en daarnaast moet ik in de toekomst meer van dit soort gerelateerde problemen op gaan lossen, dus ik zou graag inzien wat ik hier nou fout doe of wat er beter kan.
Ik ben bezig in Python een algoritme te schrijven. Momenteel draait dit op 64 bit Linux Ubuntu, de bedoeling was echter om dit op een Raspberry Pi first gen te draaien. Probleem is echter dat de Athlon A6-3500 in mijn desktop al tegen de 70% gaat onder dit proces. Ik moet eerlijk toegeven het nog niet daadwerkelijk op mijn Pi getest te hebben, maar die kroop al bang in een hoekje bij het zien van de code
Doel is om een 50 pixel LED string aan te sturen met WS2811 IC's om zodoende een moodlight te bouwen. Hardware heb ik werkend en kan ik uit Python aansturen, dat is het probleem niet.
(code en probleemstelling staan onderaan, eerst een wat uit de hand gelopen uitleg van het eigenlijke doel)
Om effecten te kunnen maken op deze LED string ga ik uit van het volgende principe. Ik maak 'waves' van kleuren. Een wave kan door de main class op elk willekeurig moment toegevoegd worden en heeft als informatie een kleur, een fadetime, een lifetime, een walkspeed en een wobblefactor. Kleur spreekt voor zich, een lifetime geeft aan hoe lang de wave moet blijven leven en de fadetime geeft aan in hoeveel tijd de wave moet in- en uitfaden. De walkspeed (die overigens nog niet geimplementeerd is) geeft aan hoe snel de wave moet bewegen over de LED string. De wobblefactor geeft aan in hoeverre de wave aan de hand van een random waarde moet 'groeien' (infaden) of 'krimpen' (uitfaden). Dit zodat het geheel wat levendiger wordt. Tijdens het programmeren heb ik geprobeerd dit voor me te zien als een 2D beeld. Een vlak op de grond van 50 vakjes (LEDs) en daar bovenop de waves die zich als bergen op het landschap vormen. Een stuk omhoog, de top, en dan weer omlaag.
De implementatie dan. Om die bergvorm te kunnen maken, ga ik uit van de formule y=1-x^2. Zo krijg ik een parabool die op x=-1 en x=1 de nullijn kruist en zijn top op 1 heeft. Dit bereik van 2 verdeel ik in bijvoorbeeld 6 vakjes, en daarvan pak ik de gemiddelde waarde (ongeveer, ik ga uit van de linker en rechter grenzen van de vakjes, pak daar de waardes van, en neem daar het gemiddelde van. Dat het een gebogen lijn betreft en het dus geen exact gemiddelde is, vind ik niet zo belangrijk: zo nauw komt het niet). Dan heb ik een waarde tussen 1 en 0. Die waarde vermeningvuldig ik vervolgens met een waarde die ik afleid uit de fader. Die geeft, afhankelijk van waar de wave is in zijn lifespan, een waarde aan. Stel dat ik op de helft tussen de linker grens van de wave en de top zit, en de wave 1 seconde bestaat met een fadetime van 3 seconden. Dan krijg ik dus 0.5 * 0.3 = 0.15. Die factor vermeningvuldig ik eventueel met de wobbleFactor, en en met elk van de drie kleuren om uit te komen op de uiteindelijke waarde. Dit doe ik elke 30 ms, omdat ik ongeveer 30 'fps' wil bereiken. Elke 30ms wordt van het object colorWave de functie beat aangeroepen om dat te bewerkstelligen.
De praktijk dan:
Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
| import math from time import sleep from random import random import socket UDP_IP = "127.0.0.1" UDP_PORT = 5005 sock = socket.socket(socket.AF_INET, # Internet socket.SOCK_DGRAM) # UDP class colorWave( object ): def __init__(self, colorRed, colorGreen, colorBlue, intensityWobble, intensityWobbleChance, walkPace, fadeTime, lifeTime, blockWidth, blockLocation, worldLength): self.colorRed = colorRed self.colorGreen = colorGreen self.colorBlue = colorBlue self.intensityWobble = intensityWobble self.intensityWobbleChance = intensityWobbleChance self.walkPace = walkPace self.fadeTime = fadeTime self.lifeTime = lifeTime self.blockWidth = blockWidth self.blockLocation = blockLocation self.worldLength = worldLength self.timeSinceBirth = 0 self.wobbleFader = 1 def getFadeValue(self): # Hier moeten we bedenken hoe helder de Wave moet zijn. Vooralsnog laten # we de waves even linear in en uitfaden. Parabolisch moet ook wel kunnen, # maar dan zou de Wave gedurende zijn hele leven aan het faden zijn en dat # willen we niet. Eigenlijk moet hier dus Iets op gevonden worden zodat # we binnen de fadetime, aan het begin en einde van het leven, een halve # parabool meenemen in de brekenening. Maar dat is nu even teveel moeite. # default fade = 1 if( self.timeSinceBirth < ( self.lifeTime / 2 ) ): # We zitten dus nog voor de helft van de lifetime. Moet er nog # iets gefaded worden? if( self.timeSinceBirth < self.fadeTime ): # Ja dus. En nu even uitrekenen hoe ver we in de fade zitten. # 100% fade zou moeten gebeuren als timeSinceBirth gelijk is aan # fadeTime. 0% fade, moet gebeuren als timeSinceBirth gelijk is aan 0. fade = ( self.timeSinceBirth + 0.0 ) / ( self.fadeTime + 0.0 ) if( self.timeSinceBirth > ( self.lifeTime / 2 ) ): # Fadeout.. if( self.fadeTime + self.timeSinceBirth >= self.lifeTime ): # Tijd om te gaan faden, maar nu de andere kant op fadeOutProgress = self.timeSinceBirth - ( self.lifeTime - self.fadeTime ) fade = 1.0 - ( fadeOutProgress + 0.0 ) / ( self.fadeTime + 0.0 ) # IntensityWobble! if( random() < self.intensityWobbleChance ): if( random() > 0.5 ): self.wobbleFader = self.wobbleFader * 1.0+self.intensityWobble else: self.wobbleFader = self.wobbleFader * 1.0-self.intensityWobble # Niet te ver, want dan groeit de parabool te hard en gaat het faden aan # de randen eraan if self.wobbleFader > 1.5: self.wobbleFader = 1.3 fade = fade * self.wobbleFader return fade def beat(self, time): # We verzamelen alle blocks die we nodig hebben. Omdat we weten waar # deze colorWave zich bevindt, gebruiken we een dictionary, omdat we # dan de kleuren met indexes kunnen opslaan. blocks = {} if( self.timeSinceBirth > self.lifeTime ): return 0 # Eerst even de fade value. Die is overal gelijk, dus hoeven we maar # een keer op te halen blockFade = self.getFadeValue() # Alle eigen blocks doorlopen for block in range( 0, self.blockWidth ): # Even hardop denken.. # block is 0 # Aantal is 6 # Het midden zit op 0 # B1 B2 B3 B4 B5 B6 # 1 0.6 0.3 0 0.3 0.6 1 # Dus voor blok 0, moet link op 1 en rechts op 0.6 komen # Als ik 1 deel door 6, krijg ik ongeveer 0.16. Twee keer dat, # is 0.3. Dus.. blockDivider = ( 1.0 / self.blockWidth ) * 2 # Blockdivider is nu 0.3, dus min of meer de breedte van een blok # Als ik nu wil uitrekenen wat de linkerkant van blok 1 is, moet ik dus # weten hoe vaak ik 0.3 van 0 af moet halen om bij -1 te komen. # Er zijn 6 blokken. Waarvan er dus 3 aan de linkerkant zitten. Ik moet # dus ook drie keer 0.3 van 0 afhalen. blockLeftBoundary = -1.0 + block * blockDivider # En voor de rechterkant dus slechts twee keer blockRightBoundary = -1.0 + ( block + 1 ) * blockDivider # Elk block, heeft nu een linker en rechter kant. Die lopen van -1 tot 1. # Das mooi binnen het bereik van y = 1 - x^2. # Vet! blockLeftHeight = 1 - blockLeftBoundary**2 blockRightHeight = 1 - blockRightBoundary**2 # Nu hebben we dus de twee waardes. Dat het een gebogen lijn is en een # gemiddeld punt pakken niet werkt interesseert me niet. blockHeight = ( blockLeftHeight + blockRightHeight ) / 2 # I FUCKING SOLVED IT # Maar nu nog rekening houden met de fadetime. Die hadden we al klaar # liggen, dus die kan meteen mee in de berekening en hoeft niet opniew # Elk block krijgt een list met daarin 0-255 waarden voor R, G en B # Wel eerst even checken of de waarde binnen dit bereik ligt, anders # gaat straks de LED string over zijn nek thisBlock = [] calcRed = int( self.colorRed * blockHeight * blockFade ); if calcRed > 255: calcRed = 255 if calcRed < 0: calcRed = 0 thisBlock.append( calcRed ) calcGreen = int( self.colorGreen * blockHeight * blockFade ); if calcGreen > 255: calcGreen = 255 if calcGreen < 0: calcGreen = 0 thisBlock.append( calcGreen ) calcBlue = int( self.colorBlue * blockHeight * blockFade ); if calcBlue > 255: calcBlue = 255 if calcBlue < 0: calcBlue = 0 thisBlock.append( calcBlue ) # En nu het plaatsen van de blocks. En dus weer met de index ten opzichte # van de volledige LED string. currentBlockPosition = ( self.blockLocation - ( self.blockWidth / 2 ) + block ) if currentBlockPosition > 0 and currentBlockPosition < self.worldLength: # Alleen als ie niet van het kaartje af dondert natuurlijk blocks[ currentBlockPosition ] = thisBlock # En om te zorgen dat we FPS on the fly kunnen aanpassen zonder dat de # timings in de waves verkeerd gaan lopen, houden we de tijd hier ook bij # De herder heeft aangegeven hoeveel tijd er verstreken is, dus dat nemen # we over self.timeSinceBirth += time return blocks class colorHerder( object ): leds = 50 colorRed = 255 colorRedDiffer = 50 colorGreen = 10 colorGreenDiffer = 10 colorBlue = 0 colorBlueDiffer = 5 spawnRate = 4000 spawnRateDiffer = 1000 walkPace = 2000 walkPaceDiffer = 750 fadeTime = 3000 fadeTimeDiffer = 2000 lifeTime = 10000 lifeTimeDiffer = 4000 intensityWobble = 0.1 intensityWobbleChance = 0.3 blockWidth = 7 blockWidthDiffer = 3 maxAlive = 3 def __init__(self): self.colors = [] # Hier slaan we de kleuren op self.ticker = 0 # Debug.. Even scripten wanner kleuren erbij moeten komen def beat(self): self.ticker = self.ticker+1 # Om te beginnen twee rooie if self.ticker == 0: self.colors.append( colorWave(255,0,0, 0.02, 0.02, 2000, 6000, 20000, 14, 5, self.leds-1) ) self.colors.append( colorWave(220,0,0, 0.044, 0.14, 2000, 9000, 20000, 6, 27, self.leds-1) ) if self.ticker == 180: # Na 100 ticks nog een groene self.colors.append( colorWave(0,255,0, 0.03, 0.4, 2000, 6000, 20000, 23, 15, self.leds-1) ) if self.ticker == 250: # En na 250 ticks nog een blauwe self.colors.append( colorWave(105,250,250, 0.025, 0.18, 2000, 6000, 20000, 20, 38, self.leds-1) ) # Voor elk block houden we de kleur bij, we beginnen altijd met een lege blocks = [] # En die vullen we met zwarte pixels for x in range(0,self.leds-1): blocks.append([0,0,0]) # En nu loopen we door de lijst met waves for colorid, color in enumerate( self.colors ): # Beat.. Er zijn zo'n 30ms verstreken wave = color.beat( 30 ) # Een wave stuurt 0 terug als ie klaar is, anders gaan we hem overnemen if isinstance( wave, dict ): for i, block in wave.iteritems(): # Als het block van de wave een hogere waarde heeft dan van het bestaande block if block[0] > blocks[i][0]: # dan houden we die aan blocks[i][0] = block[0] else: # zo niet, dan pakken we een gemiddelde blocks[i][0] = ( blocks[i][0] + block[0] ) / 2 if blocks[i][1] == 0: blocks[i][1] = block[1] else: blocks[i][1] = ( blocks[i][1] + block[1] ) / 2 if blocks[i][2] == 0: blocks[i][2] = block[2] else: blocks[i][2] = ( blocks[i][2] + block[2] ) / 2 else: # Hij gad 0 terug, dus weg hiermee del self.colors[colorid] # Resultaat naar de server sturen for index,bl in enumerate(blocks): lt = bl[0] # printstring = int( (lt) / 7 ) # print "#" * printstring, " " * ( int(255/7) - printstring ), "| " #C.itemconfigure( demoBlocks[index-1], fill='#%02x%02x%02x' % (bl[0], bl[1], bl[2])) message = "0" + str(index).zfill(2) + str(bl[0]).zfill(3) + str(bl[1]).zfill(3) + str(bl[2]).zfill(3) sock.sendto( message, (UDP_IP, UDP_PORT) ) sock.sendto( "1push", (UDP_IP, UDP_PORT) ) # Workertje maken worker = colorHerder() while True: # En loopen worker.beat() sleep(0.03) |
Ik denk zelf dat het probleem zit in het geklooi met al die floats, gecombineerd met het voor elk block twee keer moeten machtverheffen. Echter heb ik geen idee hoe ik daar onderuit kan komen en óf het daadwerkelijk het probleem is. Opzich zijn dat natuurlijk snelle berekeningen, maar ze gebeuren zo wel 2 berekeningen * 20 pixels * 4 waves * 30 is 4800 keer per seconde.
Ik heb al geprobeerd niet met floats te werken maar met integers, dat geeft natuurlijk een zeer hoekig effect of ik moet met integers van 16 bits gaan werken. Ten eerste krijg ik dat niet voor elkaar (als in, ik krijg gigantische koppijn van het implementeren daarvan) en ten tweede blijken testen die ik met stukjes van de code gedaan heb daarmee (de fader en wobble) eigenlijk nauwelijks van invloed. Ook heb ik geprobeerd in plaats van de dictionary (die ik maak op 95 en vul op 187) een list te maken. Ik ging er vanuit dat die sneller zouden zijn omdat ze niet associated zijn, maar dat had ik dus mis: de load gaat omhoog.
Iemand enig idee hoe ik dit geheel wat lichter kan maken, of uberhaupt wat nou datgene is waar die load vandaan komt? Of is eigenlijk de enige goede manier om dit te gaan herschrijven in een gecompileerde taal met fixed data types?
Overigens ben ik mij ervan bewust dat er diverse stukken software rondzwerven die iets vergelijkbaars doen. Mijn toepassing is echter vrij specifiek en daarnaast moet ik in de toekomst meer van dit soort gerelateerde problemen op gaan lossen, dus ik zou graag inzien wat ik hier nou fout doe of wat er beter kan.