Tag Archives: Teensy

IR Homing Module Integration Part X

Posted 15 October 2017

Well, we have just about reached the end of the road with respect to the ‘IR Homing Module Integration’ adventure.  As you may recall, at the very start of this trip (a couple of centuries ago back in August of this year) I showed the following block diagram of the proposed integration architecture

Teensy 3.2-based IR Demod module block diagram

The above architecture actually worked almost exactly as planned, except for using only two phototransistors vs the four in the original design, as shown below

Revised IR Demodulator block diagram. Note the removal of two phototransistors and the input combiner

After literally dozens of trials on my 1m (aka workbench) and 2m ranges (aka the floor of my lab), I believe I have arrived at a reasonably successful set of PID parameters for Wall-E2 to use when homing to a charging station.  As usual, after going all around the barn with different P, I, & D values, I wound up with the simplest possible set of parameters, namely PID = (200,0,0).  The ‘200’ value is due to the use of a  (L-R)/(L+R) computation in the IR homing module, resulting in a steering  value output that ranges from -1 to +1.

While the result at the moment is far from perfect, it is pretty good.  Wall-E2 can now successfully home to and mate with the charging station from at least 3m away, with wall offset distances from 50-91 cm as shown in the following videos (as can be seen in the last video, there is still some work to be done for wall offsets in the 25cm range)


So, the IR Homing Module Integration project is basically complete.  To recap, the idea for an interference-resistant IR homing scheme using a square-wave modulated IR beacon and a companion ‘degenerate N-path Band-pass Filter’ demodulator started back in May of this year during a visit from my old friend and mentor John Jenkins (see this post).  Since then, Wall-E2 and I have done the following:

  • Researched the basic theory behind the ‘N-path band-pass filter’ technology
  • Designed and implemented (with John’s help) a two-channel version of the technique using an arduino Uno.
  • When the Uno turned out to be too slow for operation at the desired 520Hz square-wave frequency, reduced the operating frequency by a factor of 10 to 52Hz to verify proper operation
  • Researched faster alternatives to the Uno, and eventually settled on the Teensy 3.x line of micro-controllers.  This hardware change allowed me to run the algorithm successfully at 520Hz, while simultaneously cutting the required real-estate by a factor of four, and the current drain by a factor of two – neat!
  • Designed and implemented the ‘crushed funnel’ two-phototransistor detector sunshade.
  • Integrated everything onto the Wall-E2 robot.
  • Demonstrated successful homing in the presence of overhead halogen lighting

Remaining Work:

There are still some things that need to be cleaned up to complete the integration, but it is mostly minor stuff:

  • I’m still not entirely happy with the inability to successfully home & mate with the charger for wall-following offsets below about 25cm, but I don’t think there is anything I can do realistically to solve that problem; what I can do, however, is to make sure Wall-E2 starts homing far enough away from the nearest wall to avoid that problem. This will probably mean something like a pre-homing maneuver if the nearest wall distance is too small (or maybe even changing the basic wall-following algorithm to maintain a minimum distance of > 25cm)
  • Along the lines of belt-and-suspender designs, I probably also need to implement some sort of fallback algorithm if, despite all my best efforts, Wall-E2 still manages to impale itself on one of the lead-in rails.  When Wall-E2 is in normal wall-following mode and gets stuck, it has a way of detecting that condition and recovering, but that scheme isn’t currently active during homing operations.  I just need to copy that capability into the homing mode, and hopefully Wall-E2 will cooperate.
  • There’s a whole bunch of commented-out stuff and debugging printout code scattered through the program right now, and that stuff needs to be cleaned out before I forget (if I haven’t already) what it was originally intended to do.

In any case, it is time to declare victory move on to the next challenge, as soon as I figure out what that might be.

Stay tuned!




IR Homing Module Integration Part IX

Posted 09 October 2017

In my last post on this subject, I showed that the azimuth response from the V9 ‘crushed funnel’ sunshade design was almost ideal for the intended homing operation, and it was time to start seriously integrating it onto Wall-E2.  As the following photos show, this worked out fairly well.

As shown in the above photos, the Teensy 3.2 IR Homing Module is mounted on the sunshade, with connections to the sunshade IR phototransister outputs on one end, and connections via I2C to the Mega on the other.

After making the necessary modifications to Wall-E2’s operating system to incorporate the steering value input from the homing module, I ran a short azimuth scan at 1m range using the completed IR Flashlight-based transmit module, as shown in the following short video.

During the azimuth scan, steering values were acquired by the IR Homing module and transferred via I2C to the Mega on an as-requested basis.  These values were then printed out to a PC using a Wixel wireless serial link.  The result, as shown below, looks pretty good. It was pretty clear that the azimuth response was excellent over almost the entire azimuth range from -90º to +90º


The next step is to modify the PID parameters to translate the -1 to +1 range of steering values to appropriate wheel motor speed variations.

So, the error term varies from -1 to +1, and the motor wheel speed range is from 0-255, or MOTOR_SPEED_HALF +/- 127.  So, I would expect to have to have a Kp value on the order of 100 or so to achieve the full range of motor speeds.  I ran some manual scans for different Kp values to see what would happen with motor speeds, and got the following plots:

As expected, a Kp value of 20 appears to be a bit weak; it would do the job eventually, but probably not fast enough to get captured by the lead-in rails.

A Kp value of 100 looks better – motor speeds vary over almost the full range, so a full off-axis detection should cause Wall-E2 to almost spin in place to turn toward the beacon.


As shown above, using just Kp with Kd = Ki = 0 results in a constant offset with a constant error term.  However, for this application a constant output will still result in the robot turning toward the beacon, so a constant offset shouldn’t be a problem.

After this last test, I noticed that an offset to the right of the beacon boresight line (as was the case for this test produced a high left wheel speed and a low right wheel speed, exactly the opposite of what would be required to reduce the error term – oops!  This means I need to change the PID direction parameter from REVERSE to DIRECT to get the correct wheel speed adjustment sense.

To produce the above plot, the robot was left in the same position as it was at the end of the last run – offset about 20-30º to the right of the beacon boresight, but with the PID sense changed from REVERSE to DIRECT. As shown above, now the right motor speed is higher than the left, which would turn the robot back toward the beacon boresight.

The data for all the above plots was collected with the wheel motors disabled.  The next step will be to enable the motors and see what happens.  I re-enabled the motors, but also implemented a wireless ‘kill switch’ so I could keep Wall-E2 from disappearing over the horizon (or more likely, over the edge of the bench!).  Here’s Wall-E2’s maiden run with PID = (100,0,0)


Well, Wall-E2 didn’t home properly to the beacon, but it did manage to correct at least a little bit, and managed to not leap off the edge of my test bench – yay!  The plot below shows the data from the run

From the above plot, it is clear that Wall-E2 was trying to do the right thing, but couldn’t change the wheel speeds fast enough for effective homing.  The input value started off at about -0.5, and the wheel speeds at L = 75, R = 175, which caused Wall-E2 to correct left, as it should.  This started the input trending upwards toward zero, and the wheel speeds both tending toward 127 (i.e. half-speed).  Everything arrived at the setpoint at position 5, so Wall-E2 continued straight, which took it back off the boresight and to the right side again.  The input started down again, and the motor speeds started adjusting, but they couldn’t adjust fast enough to keep Wall-E2 from blowing past the beacon – oops!

I’m not real sure what this tells me about PID tuning, but I suspect I’m going to need a non-zero differential term to deal with the close-in rapid angle changes.  It’s late, so I’m going to quit for tonight, but I hope to run some more tests tomorrow.

11 October Testing:

Here’s another run on my 1m test range (aka test bench). The only change from the previous run is that the data is being acquired at four times the rate – at 100mSec intervals vice 400mSec

The two plots shown above are almost identical, as would be expected, but the second plot has 4x the data points and is a lot smoother.  Still, it tells the same story; the PID_In line (blue, plotted on the right-hand scale) stays relatively constant at about -0.5 until about position 13.  With PID_In at -0.5, PID_Out (orange curve) is about 50, resulting in L/R speeds of about 75 & 175 (grey & yellow curves, resp).  These speeds cause a very gentle turn to the left (way too gentle, as it turns out).  After position 13, PID_In starts rising slowly (and then more rapidly) toward zero, indicating that the robot heading is nearing the beacon boresight.  At position 23  PID_In  hits -0.1 and the wheel speeds cross at a value of 125, meaning the robot is moving more or less straight ahead.  For some currently unknown reason, PID_In actually goes significantly above zero between positions 23 and 25, causing the robot to ‘twitch’ to the right, away from the beacon!  Then at position 25 PID_In reverses course, diving from +0.3 to -1 as the robot goes past the beacon.  This causes the robot to reverse course again, undoing the ‘twitch’ just before hitting the end-of-range condition (aka my tool chest).

So, why did PID_In (i.e. the steering value coming from the IR Homing Module) continue to increase even as the angle between the beacon boresight and the robot centerline continued to increase – not decrease?

More 11 October Testing:

Rather than worry about the ‘twitch’ phenomenon observed just before Wall-E2 passed the IR beacon, I decided to attack the first part of the run, where the PID input stayed relatively constant, but well offset from the setpoint. From my reading of PID tuning, this indicates a need for a non-zero I (integration) term.  To test this, I adjusted my PID value from (100,0,0) to (100, 20, 0) and ran some more testing.  The 1m range runs were encouraging, so I tried a run on my 2m range (aka my lab floor). As the following video and accompanying data plots show, this was pretty darned successful.


Starting at an offset angle of about 30-40º relative to beacon boresight, Wall-E2 homed in on the beacon very smoothly and accurately.  In fact, when I picked up Wall-E2, I found that it had partially mated with the charging probe, even without lead-in rails for physical registration – neat!

Here’s another run, with Wall-E2 starting from the other side, with more of an initial offset (almost 90º)

As can be seen in the above video clip and accompanying plot, Wall-E2 makes an abrupt initial turn to point (generally) toward the beacon, but then doesn’t make any additional significant corrections until it is almost past the beacon.  From the plot, it appears that the I value isn’t quite high enough.  So, I plan to make some more runs with increased I values.


Stay tuned!


IR Homing Module Integration, Part V


Posted 31 August 2017

The goal for this part of my evil master plan to bend my robot to my will (or at least get it to breakfast) is to demonstrate that I can utilize steering information generated by the 2-channel N-path digital band-pass filter to track the movement of a square-wave modulated IR source.

As you may recall from a post long ago in a galaxy far away, I started this particular tangential odyssey as a result of a visit from my old friend and mentor John Jenkins.  I mentioned to him that my robot was having some difficulty homing in on a charging station with an IR beacon, due to interference from sunlight and overhead incandescent lighting.  His recommendation at the time was to use a square-wave modulated IR beacon, and detect it using an ‘N-path digital band-pass filter.  Since that time, I have successfully implemented the receiver algorithm and am now working on integrating the whole thing back onto the robot.  In my last post on this subject, I described how I created a rotary table using an Arduino Uno driving a cheap stepper motor via a ULN2003 driver module, and then used the rotary table to acquire azimuth scan data for the IR detector module that is to go on the front of the robot.

After getting all the azimuth scan stuff to work, and enjoying my new stepper motor super-powers, it occurred to me that the rotary table could also work as a way to test the homing performance of the system.  In the final configuration, the IR detector module will be mounted on the front of the robot, and the system will home in on a fixed position IR beacon by modulating the left & right wheel motor speeds.  However, I could turn that around a bit and use the current rotary table setup where the IR detector is fixed (but can rotate) and the IR beacon can be moved to simulate tracking/homing perturbations.  Then the stepper motor would be used to rotate the IR detector module right or left to follow the moving IR source.  If I can get the stepper motor/IR detector module to properly follow the moving beacon source, then I can work many of the bugs before working with the entire robot.

In a previous post, I demonstrated I2C communications between two Teensy modules, so I planned to use this method to transmit steering values from the IR homing module to the rotary stepper motor controller.  Unfortunately, I ran into a problem right away, because the Uno I was using to control the stepper motor runs on 5V logic, and the Teensy modules all run on 3.3V – oops!  I solved this problem by replacing the Uno with yet another Teensy 3.2 from my inexhaustible Teensy drawer (thank you again, Paul Stoffregen!).  This also allowed me to use the previous I2C master/slave demo code pretty much unaltered – yay!!

So, the first baby step in getting all this going was to verify that the Teensy 3.2 replacement for the Uno would indeed run the stepper motor via the ULN2003 driver, as shown below

Teensy 3.2 used as rotator controller in place of Arduino Uno

Here’s a short movie demonstrating that the stepper motor can indeed be controlled using the Teensy.

The next step is to integrate the code from my I2C Master/Slave example code that I used to produce the output described in this post, so the rotary table movement can be controlled by data transmitted over the I2C connection between the table controller and the IR Homing module.

01 Sept 2017 Update:

Turned out that getting the I2C master/slave code and the stepper motor code integrated wasn’t too hard.  Here’s a short video showing the sensor module tracking my square-wave modulated IR beacon transmitter

In the above video, The Teensy 3.5 SBC that I am moving around by hand is transmitting a square-wave modulated IR signal, which is being received by the detector module mounted to the rotary table.  The signal from the detector module is being fed to the Teensy 3.2 SBC in the far background, which decodes the modulated signal and generates real-time diff/sum steering values. The stepper motor controller module (the Teensy 3.2 in the near background) acquires these values about 5 times/sec via the I2C channel between the two Teensy’s, and uses this information to turn the stepper motor cw or ccw to track the moving beacon transmitter.

I’m probably not going to win any awards for smoothness and accuracy of tracking, but that’s not the point.  The point is that I now have implemented all of the components needed for a fully functional tracking system.  In the above video, the tracking system controls a cheap stepper motor to track a moving IR beacon, but in the intended application, the tracking system will control the robot’s wheel motors to home in on a stationary IR beacon.

Still lots to do, but at least I now know that I can make all the pieces work together once I get each piece optimized.

  1. Still need to finalize the IR detector part.  I’m now leaning toward the OSRAM SFH-314 +/- 40º beamwidth phototransistor.
  2. Still need to finalize the collector resistor value for the detector.  Need a sufficiently low value to prevent detector saturation under worst-case (or nearly worst-case) conditions for the intended environment (not the thermonuclear warfare on Mars environment that my friend and mentor John Jenkins apparently recommends, but still stressful), but a sufficiently high value so that the IR beacon can be detected from far enough away (approx 2m) so the robot has a chance to engage the lead-in rails and mate with the charging connector.
  3. Still need to finalize the sunshade configuration.  Currently it is a simple rectangular cylinder with the detectors angled away from the centerline.  There is some evidence in the azimuth scan data that the side walls are too close to each other, overly restricting the side-look angles of the detectors.  This issue may be further exacerbated when I go to the wider beamwidth SFH-314’s vs the SFH-300’s.  I may wind up with a sort of ‘squashed funnel’ shape in the end – but more testing is required to nail this down.
  4. And finally, I still need to get this all back on the robot and actually get it to work!

Stay tuned,





IR Homing Module Integration, Part II

Posted 17 August 2017

In my last post on this subject, I noted that the output from one of the four IR LEDs used in the current arrangement was considerably larger than from the others (like several times larger).  This was a bit disturbing, to say the least, as proper homing operation depends in large part on having consistent responses from all four sensors.  After some troubleshooting, I came to the conclusion that the culprit here is the narrow beamwidth of the SFH-309FA IR phototransistors  I am using.  I originally chose this unit for it’s narrow beamwidth as a replacement for the IR photodiodes in the OSEPP IR Follower module (see this post).  This setup seemed to work OK, but as I developed the ‘sunshade’ addition to suppress unwanted interference from sunlight and overhead incandescent lighting, and the reflector idea for the charging station, I think I inadvertently exacerbated the effect of narrow beamwidth in the vertical dimension.  When the now-narrower transmit beam is well-aligned with the narrow receive beamwidth of a particular phototransistor, the response can be several times higher than when they are not aligned.  A further complication is that just statically aligning the transmitter beam with the receiver beam may not be sufficient, as there may be some physical misalignment of the charging station with the robot as the robot approaches.

Independently of the above, a lot of water has flowed over the dam in the ten months since the original implementation of the 4-element phototransistor array.  Most importantly, in collaboration with my friend and mentor John Jenkins, I have successfully implemented a two-channel ‘degenerate N-path band-pass filter’ to allow for discrimination between unwanted IR interference and the desired charging station IR beacon.  Now the homing beacon will be modulated by a 520Hz squarewave, the center frequency of the BPF.  For this implementation, only two IR detectors are needed, rather than the four that I currently have.   In my initial attempt at integrating the new BPF capability into the robot, I simply averaged the two left and the two right phototransistor outputs to simulate a two-element setup, but as noted above this ran into problems due to the narrow vertical beamwidths of the SF309FA devices.

Rather than try and get all four phototransistors aligned with each other and with the charging station transmit beam, I decided to punt on the entire 4-element array design and start over again, using two detectors with wider beamwidths and a sunshade with a center divider, as shown below:

2-detector sunshade for use with TSL267 IR-to-voltage devices

2-detector sunshade, top view showing interior and exterior angles

In the above screenshot, the green plates represent the TSL267 beamwidths, as modified by the walls of the sunshade.  The front-to-back dimension of the sunshade was set to allow a 30º exterior beamwidth, and the location of the device with respect to the center divider was set to allow a 10º overlap between left and right detectors.  These values were somewhat arbitrary, but I think they represent a good starting point.

After a couple of false starts, I got a decent sunshade printed out, and mounted two TSL267s.  Then I connected them to my Teensy 3.2 and ran an azimuth using my 1.8m range, as shown in the photos below.

Front view of new sunshade, with two TSL267s installed

Sunshade with 267s installed and connected to Teensy 3.2 for azimuth testing. Note ‘sta-strap’ pointer

1.8m range. Note charging station in background (orange assembly just to left of 3D printer) with IR LED at center of reflector

As shown below, the azimuth response is pretty close to the predicted values – about 35º either side of boresight, with about 10-20º overlap between the left and right responses.  A nice benefit of switching to the TLS267 is the internal inverting op-amp, which produces an output with the same sign as the received field intensity.  This will require a few modifications to the IR demodulation algorithm, but well worth the minor trouble.

Azimuth response for 2-TLS267s installed in sunshade

Looking at the above plot, I’m not sure that the overlap region is narrow enough for good tracking.  If I understand the dynamics correctly, the two channels will show the same demodulated value for +/- 15º, so the system won’t know to correct within this range.  I’m thinking I would like to have this be more like +/- 5-10º maximum.  So, I will print another version of the sunshade with a 10mm longer front-back dimension and see how it works.

Stay tuned!






IR Modulation Processing Algorithm Development – Part XVII

Posted 30 July 2017

This long-running saga started actually started almost exactly two months ago with the weekend visit of my friend and long-time mentor John Jenkins.  Naturally, being fellow geeks, I showed him all my new toys, including Wall-E2, my autonomous wall-following robot.  When I explained that Wall-E2 was having some trouble homing in on an IR beacon to connect to a charging station due to the ‘flooding’ effect of direct sunlight and overhead incandescent lighting (see for instance, this post), John opined that the way to address this problem was to modulate the homing beacon with a square wave, and then use a  ‘simple’ digital filter on the robot to better discriminate between the wanted (square-wave modulated) and unwanted (sunlight and/or incandescent lighting) sources.

Right then and there I should have realized I was in trouble, because I have (or should have!) learned over the years that whenever John uses the word ‘simple’, what he really means is “this is going to be so complex that you will wish you never listened to me”, and what he means by the word ‘better’ is “this will make your robot capable of operating from the vacuum of space to the depths of the ocean, in the middle of a thermonuclear war.  All other life on earth will have long since been reduced to its constituent atoms before your robot fails to meet spec”.  What I should have done was say to John “that’s nice John, but I think I’ll just operate Wall-E2 at night with the lights off”

But noooo, I fell for this line, (again!), and said “hmm, sounds interesting John”, thus going down yet another rabbit hole in my quest to make Wall-E2 completely autonomous. So now I started working on the design and implementation of a ‘degenerate N-path band pass filter’ (John’s term), a project tangential to the implementation of a charging station, which in itself was tangential to my original wall-following robot project.  My only justification (well justifications) for this clearly insane behavior are:

  1. am clearly insane – I’m a twice-retired engineer, after all!
  2. The entire impetus for the Wall-E2 project was to give me a way to waste as much time as possible in an intellectually stimulating way, and the idea of implementing a ‘degenerate N-path band pass filter’ promised to waste a lot of time (and besides, being able to tell people I had implemented a “degenerate N-path band pass filter” was just too sexy to pass up!)

Well, it has been a heck of a journey these last two months, but I believe that with John’s help (or possibly in spite of it), we now have a working two-channel 520Hz N-path BPF, and as a bonus – a working high-accuracy frequency/amplitude sweep generator, both based on Paul Stoffregen’s wonderful Arduino-ish Teensy 3.5 SBC.  The last piece of the puzzle for the sweep generator design fell into place just yesterday with a post from ‘tni’ on the Teensy user forum, describing the proper technique for updating the count-down value for the Periodic Interrupt Timer (PIT) used in the sweep generator.

Sweep generator recap:

The original plan for the sweep generator was to use the same ‘elapsedMicros’ object type that I had used to generate the transmit waveform, but it turned out (see this post) that t he ‘elapsedMicros’ technique produced an unavoidable frequency offset.  So, searching for other alternatives, I tried a rounding technique using ‘elapsedMicros’ which helped somewhat but didn’t really solve the problem, and then a method using Daniel Gilbert’s IntervalTimer library (this library wraps access to the Periodic Interrupt Timer (PIT) module of the FreeScale Cortex-M4 microcontoller used in the Teensy 3.x line).  This technique produced a very accurate frequency output, but unfortunately also produced bad artifacts in the frequency response curves from the N-path BPF implementation due to the delays inherent in the need to stop and then restart the PIT for each new frequency step.  A representative ‘raw’ and ’round-trip’ frequency response curve set is shown below to illustrate the problem, along with the timestamp representation of the PIT operation.

‘Round-trip’ and ‘raw’ FinalValue vs Frequency, with IntervalTimer technique

Square-wave transition times vs time using the IntervalTimer technique

I was just about ready to give up on the IntervalTimer technique, and just deal with the frequency offset inherent in the ‘elapsedMicros’ technique. However, John shamed me into putting a post up on the Teensy forum explaining my issues and asking for help.  I hate to say it but I’m glad he did, as among the other helpful posts was one from ‘tni’ with the complete code for an add-on function to the IntervalTimer library to do just what I wanted – update the count-down count in the PIT without disturbing anything else.  As a result of this addition, I was able to get frequency-accurate response curves from the IR demodulator filter without any ugly artifacts, as shown below

‘Round-trip’ and ‘raw’ FinalValue vs Frequency, with IntervalTimer technique, using tni’s updateInterval() function

Square-wave transition times vs time using the IntervalTimer technique, using tni’s updateInterval() function

So now I have a sweep generator that works – albeit one that needs a little cleanup before I push it up to GitHub

August 02 2017 Update:

Today I finally ‘finished’ (to the extent that I ever really finish anything) the 2-channel IR demodulator algorithm, and the accompanying frequency/amplitude sweep generator, and posted both to my GitHub account (see https://github.com/paynterf/SqWaveIQDemodV2 and https://github.com/paynterf/TeensySweepGen).  After squashing a few last buglets, I was able to run fairly detailed frequency sweeps on both demodulator channels using the sweep generator, with the following results

If you like data, this is pretty good stuff – nice and smooth, no unusual artifacts, nicely centered around 520-522Hz, and the Ch1/Ch2 overlap is almost perfect.  Whatever else has happened in this little 2-month vacation from reality, the ‘N-path band pass filter’ algorithm clearly works, at least on the bench.

Now that I have the BPF algorithm humming along, the next challenge will be to integrate the BPF module onto the robot, and modify the charging station to modulate the IR homing beacon with the requisite 520Hz square wave, and of course test it all to verify functionality.  Still plenty of work to do, so I don’t have to worry about getting bored anytime soon!  Stay tuned…




IR Modulation Processing Algorithm Development – Part X

Posted 24 June 2017

Well, I may have spoken too soon about the perfection of my implementation of John’s ‘N-path’ band-pass filter (BPF) intended to make Wall-E2 impervious to IR interference.  After my last post on this subject, I re-ran some of the ‘Final Value’ plots for different received IR modulation amplitudes and the results were, to put it bluntly, crap 🙁 . Shown below is my original plot from yesterday, followed by the same plot for different input amplitudes

Computed final values vs complete input data cycles for sensor channel 1 (This is the original from yesterday)


So, clearly something is ‘fishy in Denmark’ here, when the ‘no-signal’ case with only high-frequency noise causes the output to increase without limit, and the ‘input grounded’ case is decidedly non-zero (although the values are much lower than in the ‘signal present’ cases).

Time to go back through the entire algorithm (again!) looking for the problem(s).  

25 June 2017

My original implementation of the algorithm was set up to handle four sensor input channels, so each step of the process required an iteration step to go through all four, something like the code snippet below:

In order to simplify the debug problem, I decided to eliminate all these iteration steps and just focus on one channel.  To do this I ‘branched’ my project into a ‘SingleChannel’ branch using GIT and TortoiseGit’s wonderful GIT user interface (thanks TortoiseGit!).  This allows me to muck about in a new sandbox without completely erasing my previous work – yay!

Anyway, I eliminated all the 4-sensor iteration steps, and went back through each step to make sure each was operating properly.  When I was ‘finished’ (I never really finish with any program – I just tolerate whatever level of bugs or imperfections it has for some time).  After this, I ran some tests for proper operation using just one channel.  For these tests, the Teensy ADC channel being used was a) grounded, b) connected to 3.3VDC, c) unterminated.  For each condition I captured the ‘Final Value’ output from the algorithm and plotted it in Excel, as shown below.

Single channel testing with grounded, unterminated, and +3.3VDC input

As can be seen from the above plot, things seem to be working now, at least for a single channel.  The ‘grounded’ and ‘3.3VDC’ cases are very nearly zero for all time, as expected, and the ‘unterminated’ case is also very low.

Next, I added a 0.5V p-p signal at ~520Hz to the sensor input, and re-ran the program.  After capturing the ‘Final Value’ data as before, I added it to the above plot, as shown below

Final Value vs Cycles for 0.5V p-p input

As can be seen in the above plot, the ‘Final Value looks much more reasonable than before. When plotted on the same scale as the ‘grounded’, ‘unterminated’, and ‘+3.3VDC’ conditions, it is clear that the 0.5V p-p case is a real signal.

Then I ran a much longer term (11,820 cycles, or about 22-23 sec) test with 0.5V p-p input, with the following results.

As can be seen from the above plot, the final value is a lot more ‘spiky’ than I expected.  The average value appears to be around 30,000, but the peaks are more like 60,000, an approximately 3:1 ratio.  With this sort of variation, I doubt that a simple thresholding operation for initial IR beam detection would have much chance of success.  Hopefully, these ‘spikes’ are an artifact of one or more remaining bugs in the algorithm, and they will go away once I find & fix them 😉

Update:  Noticed that there was a lot of time jitter on the received IR waveform – wonder if that is the cause of the spikes?

sensor waveform jitter. Note that this display is separately (Vert Mode) triggered.

Following up on this thread, I also looked at the IR LED (transmit) and photodetector (receive) waveforms together, and noted that there is quite a bit of time jitter on the Tx waveform as well, and this is received faithfully by the IR photodetector, as shown in the following short video clip


So, based on the above observations, I decided to replace the Trinket transmit waveform generator with another Teensy 3.5 to see if I could improve the stability of the transmit signal.  Since I never order just one of anything, I happened to have another Teensy 3.5 hanging around, and I soon had it up and running in the setup, as shown below

Replaced Trinket transmitter with Teensy 3.5

Transmit and receive waveforms

As the above short video and photos show, the Teensy implementation of the transmit waveform is much more stable than the Trinket version.  Hopefully this will result in better demodulation performance.

The next step was to acquire some real data using a 0.5V p-p input signal through the IR beam path.  I took this in stages, first verifying that the raw samples were an accurate copy of the input signal, and then proceeding on to the group-sum, cycle-sum, and final value stages of the algorithm.

Sample capture using an input of 0.5V p-p through the IR path

GroupSum I/Q plots using 0.5V p-p Input Signal

I then used Excel to compute the cycle sums associated with each group of 4 group sums

Calculated Cycle Sums for a 0.5V p-p Input Signal

And then I used Excel again to calculate the ‘Final Value’ from the previously calculated cycle sum data

Calculated final value

Keep in mind that all the above plots are generated starting with real IR photodector data, and not that large of an input at that (0.5V p-p out of a possible 3.3V p-p).

The next step was the real ‘proof of the pudding’.  I ran the algorithm again, but this time I simply printed out final values – no intermediate stages, and got the following plots

Final Value vs time, for 0.5V p-p Input Signal

Detail of previous plot

From the above plots, I think it is clear that the algorithm is working fine, and most of the previous crappy results were caused by poor transmit timing stability.  I’m not sure what causes the ripple in the above results, but I have a feeling my friend and mentor John Jenkins is about to tell me! 😉

Sleeeeeep, I need sleeeeeeeeeeep….





IR Modulation Processing Algorithm Development – Part IX

Posted 19 June, 2017

In my last post on this subject, I showed that my 4-sensor band-pass filter (BPF) algorithm was feasible when run on a Teensy 3.5 SBC.  However, what I haven’t done  yet is to verify that the algorithm is indeed producing valid results, when fed with real sensor input.

I should be able to verify proper algorithm operation with my single-sensor test bed (as shown in the following photo) by moving the single sensor input line to each sensor channel (ADC input) in turn, and monitoring the data at different stages in the processing chain.

Teensy 3.5 installed on my algorithm test bed, with the Uno shown for size comparison. The small processor in the foreground is an Adafruit ‘Trinket’

Since I now have plenty of RAM to play with, I should be able to save a representative sample of the input data and intermediate results in suitably sized arrays, run the algorithm long enough to fill those arrays, and then print them all out at the end.

  • I will probably want to run the process long enough to completely fill the 64-element I & Q ‘running sum’ arrays.  These arrays already exist for all 4 sensor channels, so this has no effect on available RAM
  • The next step backward in the chain are the I & Q ‘cycle group sum’ elements (one pair per sensor channel) used to generate one element in the running sum arrays.  To store all these cycle group sum elements will require two 256-element arrays per sensor channel.
  • And the first step in the process is the raw sensor input data.  To store all the data required to generate 64 elements in the running sum arrays will require a single 1280-element array per sensor channel.

In summary, to instrument one sensor channel from start to finish will require

  • 1ea 1280-element array to hold the raw data
  • 2ea 256-element arrays to hold the cycle group sums

for a total of 1280 + 512 = 1796 elements at 2 bytes/element = 3592 bytes.  If I wanted to do this for all 4 sensor channels at once, the total would be 14368 bytes, still well within the 192KB RAM availability on the Teensy – nice!

Results – Capture Stage:

The first step was to capture/display the raw ADC data to make sure that part was operating correctly.  The plots below show all 4 sensor channels.

Raw ADC data for all 4 sensor channels, 1280 elements (enough to fill the entire 64-element running sum array)

First 40 elements of the raw ADC capture

As can be seen in the above plots, channel 1 shows the 520Hz detected IR waveform, and the other three channels show just noise.

Results – Intermediate Stages:

The next step was to verify proper operation of the step that accumulates a 1/4 cycle group of samples and generates the I & Q ‘sample group sum’ components.  To verify this stage of the algorithm, I captured 5 cycles of data, as shown below:

Sensor channel 1 raw data and I/Q sample group sums

In the above plot, the dark blue line is the raw ADC data input, which varies from about the ADC maximum of 4096 to about 3890,  or about 161mV (3.3V ADC reference and IR detector supply).  The resulting ‘sample group sums’ are shown in orange (for the I component) and gray (for the Q component).  The significance of the plot is that the sample group sums and the I/Q component generation appears to be happening correctly.  The orange points follow a {+1, +1, -1, -1} sequence, while the gray ones follow a {+1, -1, -1, +1} sequence, as expected.

Next, I printed out this same 5-cycle segment in text form, as shown below (double-click in code window to enable scrollbar)

The above table shows the raw data, the sample-group sums, and the corresponding cycle-group sums. For example, the first set of 5 data samples adds to 19673.  Since this is the first sample-group sum, it is multiplied by “+1” to form the I component, and “-1” to form the Q component, and these are shown adjacent to the last raw data in that sample group.  After 4 such sample-group sums, the cycle-group sum I/Q components are generated by adding the 4 sample-group I/Q components respectively; for the first cycle these are -1736 & -246 as shown adjacent to the 20th sample (sample #19).

Results – Final Stages:

The cycle-sum I & Q components generated above are saved in separate 64-element circular buffers, and the running sum of these buffers are then used to form the final demodulated value for the channel of interest.  The final value is computed as the sum of the absolute values of the I & Q component running sums, i.e. FV = abs(RunningSumI) + abs(RunningSumQ).  To demonstrate proper algorithm functioning, I printed out the computed final values for well over 1000 cycles of raw data, as shown in the Excel plot below

Computed final values vs complete input data cycles for sensor channel 1

As shown in the plot above, the final value rapidly rises from zero to around 2×106 in the first 64 cycles of the run, after which it generally levels off for the rest of the run.  There is quite a bit of ripple on the signal, which my friend and mentor John Jenkins mentioned might happen as the non phase-locked input and sampling frequencies slowly slid by each other (at least I hope that is what is happening!).

So, it looks like the algorithm is doing what it should, and my ‘scope measurements to date indicate that the Teensy is doing it all without breaking a sweat, even with print statements thrown in.  It appears that I could probably double the number of samples/cycle and still have plenty of time to finish all the computations.

However, there are still a number of things to be accomplished before this new feature makes it into the field.

For starters, I’m not sure how to normalize the final value.  For a fairly weak (~160mv out of 3V) signal the final value is north of 2 million – what happens for stronger signals, and how to I normalize this down to a range that I can use to drive an analog output?  I suppose I could simply apply the IR modulation signal directly to the analog input (bypassing the IR path entirely) and see what happens, but I’d also like to understand the math.  Maybe John Jenkins can help with this (hint, hint, wink, wink!).

Also, I’d like to validate the idea that this algorithm will selectively reject other signals that aren’t close to the desired 520Hz modulation frequency.  I plan to test this by modifying the Trinket algorithm to make it a swept frequency generator (say from 470 – 570 Hz) and see how the output changes.

Stay tuned!







IR Modulation Processing Algorithm Development – Part VIII

Posted 18 June 2017

In my last post on this subject, I showed how I could speed up ADC cycles for the Teensy 3.5 SBC, ending up with a configuration that took only about 5μSec/analog read.  This in turn gave me some confidence that I could implement a full four-sensor digital BPF running at 20 samples/cycle at 520Hz without running out of time.

So, I decided to code this up in an Arduino sketch and see if my confidence was warranted.  The general algorithm for one sensor channel is as follows:

  1. Collect a 1/4 cycle group of samples, and add them all to form a ‘sample_group’
  2. For each sample_group, form I & Q components by multiplying the single sample_group by the appropriate sign for that position in the cycle.  The sign sequence for I is (+,+,-,-), and for Q it is (-,+,+,-) .
  3. Perform steps 1 & 2 above 4 times to collect an entire cycle’s worth of samples.  As each I/Q sample_group component is generated, add it to a ‘cycle_group_sum’ – one for the I and one for the Q component.
  4. When a new set of cycle_group_sums (one for I, one for Q) is completed, use it to update a set of two N-element running sums (one for I, one for Q).
  5. Add the absolute values of the I & Q running sums to form the final demodulated signal value for the sensor channel.

To generalize the above algorithm for K sensor channels, the ‘sample_group’ and ‘cycle_group_sum’ variables become K-element arrays, and each step becomes a K-step loop. The N-element running sum arrays (circular buffers) become [K][M] arrays, i.e. two M-element array for each sensor (one for I, one for Q).

All of the above sampling, summing, and circular buffer management must take place within the ~96μSec ‘window’ between samples, but not all steps have to be performed each time.  A new sample for each sensor channel is acquired at each point, but sample groups are converted to cycle group sums only once every 5 passes, and  the running sum and final values are only updated every 20 passes.

I built up the algorithm in VS2017 and put in some print statements to show how the gears are turning.  In addition, I added code to set a digital output HIGH at the start of each sample window, and LOW when all processing for that pass was finished.  The idea is that if the HIGH portion of the pulse is less than the available window time, all is well. When I ran this code on my Teensy 3.5, I got the following print output (truncated for brevity)

And the digital output pulse on the scope is shown in the following photo

Timing pulse for BPF algorithm run, shown at 10uS/cm. Note the time between rising edges is almost exactly 96uSec, and there is well over 60uSec ‘free time’ between the end of processing and the start of the next acquisition window.

As can be seen in the above photo, there appears to be plenty of time (over 60μSec) remaining between the end of processing for one acquisition cycle, and the start of the next acquisition window.  Also, note the fainter ‘fill-in’ section over the LOW part of the digital output.  I believe this shows that not all acquisition cycles take the same amount of processing time.  Four acquisition cycles out of every 5 require much less processing, as all that happens is the individual samples are grouped into a ‘sample_group’.  So the faint ‘fill-in’ section probably shows the additional time required for the processing that occurs after collection/summation of each ‘sample_group’.

The code for these measurements is included below:

More to come,



IR Modulation Processing Algorithm Development – Part VI

Posted 14 June 2017

In my previous posts on this subject, I have been working with an Arduino Uno as the demodulator processor, but I have been plagued by its limitation of 2KB for program memory. This has caused severe limitations with timing debug, as I can’t make debug arrays long enough for decent time averaging, and I can’t do more than one sensor channel at a time.

So, I finally took the plunge and acquired some of Paul J Stoffregen’s Teensy 3.5 processors from their store.  From their site: “Version 3.5 features a 32 bit 120 MHz ARM Cortex-M4 processor with floating point unit. All digital pins are 5 volt tolerant.” The tech specs are shown on this page, but the main features I was interested in are:

  • 120MHz processor speed vs 16MHz for the Uno
  • 192KB RAM vs 2KB for the Uno
  • Analog input has 13 bit resolution vs 12 for the Uno
  • As an added bonus, the Cortex-M4 has an FPU, so integer-only math may be unnecessary.
  • Much smaller physical footprint – the Teensy 3.5 is about 1/4 the area of the Uno
  • Lower power consumption – The Teensy 3.5 at 120MHz consumes about 30mA at 5V vs about 45mA at 5V for the Uno.

Here are some photos of the Teensy 3.5 as installed on my algorithm test bed, and also on my Wall-E2 robot where it might be installed:

Teensy 3.5 installed on my algorithm test bed, with the Uno shown for size comparison. The small processor in the foreground is an Adafruit ‘Trinket’

Side-by-side comparison of the Uno and Teensy 3.5 SBC’s

Closeup of the Teensy 3.5 shown atop the ‘sunshade’ surrounding the IR sensors.  this is a possible installed location

Wider view of a Teensy 3.5 placed atop the ‘sunshade’ surrounding the IR sensors

In addition to all these goodies, the folks at Visual Micro added the Teensy line to their Microsoft Visual Studio add-on, so programming a Teensy 3.5 is just as easy as programming a Uno – YAY!

Of course, I’ll need to re-run all the timing tests I did before, but being able to create and load (almost) arbitrary-length sample capture arrays for debugging purposes will be a great help, not to mention the ability to use floating-point calculations for better accuracy.

Stay tuned,