Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Changing the speed of animations
#1
Apologies in advance for newbie question!

I wish to change the speed of animations, how is this possible please?

Specifically, I wish to run a continuous loop of the pixel chaser animation on an LED strip, but on each iteration I want to check an external input which will determine the speed that the led appears to whizz down the strip.

Any advice greatfully received, thanks Smile

p.s. I am ok on getting the external input as a variable (e.g. a value between 0.5 and 1), it's the Bibliopixel bit I need help with

Aha 'fps' seems like it might do what I want:
https://github.com/ManiacalLabs/BiblioPixel/wiki/Animations#runamt--1-fpsnone-sleepnone-max_steps--0-untilcomplete--false-max_cycles--0

I'll have a play around with that
#2
Could you include the code you have now? Need to get a better idea of how you are currently trying to do this.
But basically, there's 2 ways of doing it.

1) Tell the animation to only run for one cycle. With pixel chase, each frame moves one pixel. One cycle would be N frames where N is the number of LEDs you have. But again, this is dependent on the animation involved (which is why I need your code... not sure which exact one).

anim.run(fps=<your_var_here>, max_steps=<num_leds>)

Or something like that.

2) Thread the animation. "anim.run(threaded=True)"
Then, in your main thread where you check the input update the animations "_internalDelay" variable. This is just the amount of time (in milliseconds) that's between each frame. (i.e. _internalDelay = (fps / 1000) so 100ms for 10fps)
So let's say you want to range from 5 fps (f_min) to 60 fps (f_max) and your input (we'll call it "i") variable is 0.5 (i_min) - 1.0 (i_max).

i_norm = (i - i_min) / (i_max - i_min) #normalize i to 0.0 - 1.0 range
# so, for example, 0.75 in range 0.5 - 1.0 becomes 0.5 (halfway between the min and max)
fps = int((f_max - f_min) * i_norm) # this would mean 55 * 0.5 (from above example) = 27.5 but we'll cast to an int, so 28
fps = fps_min + fps # we need to add back to the min since fps was made an offset because of (f_max - f_min)

now, just do:

anim._internalDelay = int(fps / 1000.0)

whenever you get the external input. One downside to this is that the animation will speed up immediately, instead of on the next full cycle.
#3
This is really useful, thank you Adam.
So far I've only tested the example scripts and have also got my external input script working, so the next stage is to marry the two together, and wanted pointers before embarking - your response is exactly what I needed.
The threaded option sounds like it may be even more suited to my purpose. I'll be able to spend some more time on it this evening and will post my results back here.
#4
Ok I made a little progress this evening. I've implemented the threaded option. A pulse is now sent along the led strip in response to my external input. I have two  one further question:

1. Is it possible to update the colour of the animation on-the-fly, in a similar manner to the how the internalDelay can be updated? Something like anim._led_color = (255,0,255) ?

2. I've noticed with the threaded option, the rest of the code seems slightly less responsive (it's receiving cadence data from a bike via a USB dongle). Is there any way to lower the priority of the Bibliopixel thread? I might be talking nonsense here, just hoping to be pointed in the right direction!   Removing this as it is not directly related to BiblioPixel

The Bibliopixel additions are  lines 11-46 and 151-155:


Code:
#1  #!/usr/bin/python2.7

#2  """
#3      Code based on:
#4          https://github.com/mvillalba/python-ant/blob/develop/demos/ant.core/03-basicchannel.py
#5      in the python-ant repository and
#6          https://github.com/tomwardill/developerhealth
#7      by Tom Wardill
#8  """
#9  
#10  
#11  from bibliopixel.led import *
#12  from bibliopixel.animation import BaseStripAnim
#13  from bibliopixel.drivers.LPD8806 import *
#14  from bibliopixel import LEDStrip
#15  import bibliopixel.colors as colors
#16  
#17  numLeds = 50
#18  driver = DriverLPD8806(numLeds, c_order = ChannelOrder.BRG, use_py_spi = True, SPISpeed = 8 )
#19  led = LEDStrip(driver)
#20  
#21  
#22  class ColorChase(BaseStripAnim):
#23      """Chase one pixel down the strip."""
#24  
#25      def __init__(self, led, color, width=1, start=0, end=-1):
#26          super(ColorChase, self).__init__(led, start, end)
#27          self._color = color
#28          self._width = width
#29  
#30      def step(self, amt = 1):
#31          self._led.all_off() #because I am lazy
#32  
#33          for i in range(self._width):
#34              self._led.set(self._start + self._step + i, self._color)
#35  
#36          self._step += amt
#37          overflow = (self._start + self._step) - self._end
#38          if overflow >= 0:
#39              self._step = overflow
#40  
#41  
#42              
#43  anim = ColorChase(led,(100,0,0),5)      
#44  
#45  anim.run(threaded=True) 
#46  anim._internalDelay = 250
#47  
#48  
#49  
#50  import sys
#51  import time
#52  import serial
#53  from ant.core import driver, node, event, message, log
#54  from ant.core.constants import CHANNEL_TYPE_TWOWAY_RECEIVE, TIMEOUT_NEVER
#55  ser=serial.Serial()
#56  ser.port='/dev/ttyAMA0'
#57  ser.open()
#58  
#59  class HRM(event.EventCallback):
#60  
#61      def __init__(self, serial, netkey):
#62          self.serial = serial
#63          self.netkey = netkey
#64          self.antnode = None
#65          self.channel = None
#66          self.cadence_cnt = 0
#67          self.cadence_time = 0
#68          self.cadence_cnt_old = -1
#69          self.cadence_time_old = -1
#70          self.cadence = 0
#71          self.speed_cnt=0
#72          self.speed_time=0
#73          self.speed_cnt_old=-1
#74          self.speed_time_old=-1
#75          self.speed=0
#76  
#77      def start(self):
#78          print("starting node")
#79          self._start_antnode()
#80          self._setup_channel()
#81          self.channel.registerCallback(self)
#82          print("start listening for hr events")
#83  
#84      def stop(self):
#85          if self.channel:
#86              self.channel.close()
#87              self.channel.unassign()
#88          if self.antnode:
#89              self.antnode.stop()
#90  
#91      def __enter__(self):
#92          return self
#93  
#94      def __exit__(self, type_, value, traceback):
#95          self.stop()
#96  
#97      def _start_antnode(self):
#98          stick = driver.USB2Driver(self.serial)
#99          self.antnode = node.Node(stick)
#100          self.antnode.start()
#101  
#102      def _setup_channel(self):
#103          key = node.NetworkKey('N:ANT+', self.netkey)
#104          self.antnode.setNetworkKey(0, key)
#105          self.channel = self.antnode.getFreeChannel()
#106          self.channel.name = 'C:HRM'
#107          self.channel.assign('N:ANT+', CHANNEL_TYPE_TWOWAY_RECEIVE)
#108          self.channel.setID(121, 0, 0)
#109          self.channel.setSearchTimeout(TIMEOUT_NEVER)
#110          self.channel.setPeriod(8086)
#111          self.channel.setFrequency(57)
#112          self.channel.open()
#113  
#114      def process(self, msg):
#115          if isinstance(msg, message.ChannelBroadcastDataMessage):
#116              self.cadence_cnt = int(ord(msg.payload[3]))+256*ord(msg.payload[4])
#117              self.cadence_time = ord(msg.payload[1])+256*ord(msg.payload[2])
#118              if self.cadence_cnt == self.cadence_cnt_old:
#119                  return
#120              if self.cadence_time == self.cadence_time_old:
#121                  return
#122              if self.cadence_cnt_old == -1:
#123                  self.cadence_cnt_old = self.cadence_cnt
#124                  self.cadence_time_old = self.cadence_time
#125                  return
#126              if self.cadence_cnt < self.cadence_cnt_old:
#127                  self.cadence_cnt += 65536
#128              if self.cadence_time < self.cadence_time_old:
#129                  self.cadence_time += 65536
#130              self.cadence=(self.cadence_cnt-self.cadence_cnt_old)*1024*60.0/(self.cadence_time-self.cadence_time_old)
#131              #print "cadence="+str(self.cadence)
#132              if self.cadence_time > 65536:
#133                  self.cadence_time -= 65536
#134              if self.cadence_cnt > 65536:
#135                  self.cadence_cnt -= 65536
#136              self.cadence_cnt_old = self.cadence_cnt
#137              self.cadence_time_old = self.cadence_time
#138  
#139  SERIAL = '/dev/ttyUSB0'
#140  NETKEY = 'B9A521FBBD72C345'.decode('hex')
#141  
#142  hrm = HRM(serial=SERIAL, netkey=NETKEY)
#143  hrm.start()
#144  stop_cnt=0
#145  stop_cadence_pre=0
#146  
#147  while True:
#148          try:
#149              time.sleep(0.3)
#150              
#151              if hrm.cadence<>0:
#152                  hrm.ledspeed = (1000/hrm.cadence)
#153                  anim._internalDelay = (hrm.ledspeed)
#154   #anim._internalDelay = (hrm.ledspeed)
#155              else:
#156                  anim._internalDelay = 250
#157              
#158              print "cadence:"+str(hrm.cadence)
#159  
#160          except KeyboardInterrupt:
#161              sys.exit(0)
#5
Good news - I got the script working pretty much how I wanted it.

You can see a demo here: https://www.youtube.com/watch?v=mZNnt9KaNtU
As you pedal faster the light changes colour and the animation gets faster.

The full code can be found here: https://github.com/jimlondon/cadence-led/

Thanks for the help Smile


Forum Jump:


Users browsing this thread: 1 Guest(s)