New Motion Sensor

In the summer of 2017, a company called Bitcraze, makers of the Crazyflie drone, announced a new motion sensor for general purpose use. This product is their Flow Breakout board. We tried to use this sensor on our robot for the 2018 season. The attached white paper describes what we learned about how to get started using the sensor. In total it took several months to figure out how to use the sensor correctly, because the sample program from the vendor was incorrect (still is), and because we didn’t pay proper reverence to the sensor chip’s SPI interface timing requirements, assuming the RoboRio SPI interface would be just fine. If you decide to use this sensor, and we do recommend it, you will be months ahead if take advantage of what we learned the hard way. We would love to hear what you learn if you use this new sensor, too.

FlowSensorWhitePaper.doc (82 KB)

FlowSensorWhitePaper.doc (82 KB)

This sensor seems extremely useful, thank you for sharing! I read through your white paper and it seems like you have been able to get readings and should have been able to use it on the real field for an auto, but viewing your most recent matches it seems your team still only had a simple drive forward auto. Have you been able to develop this further and create accurate auto modes or is just testing the sensor as far as you have gotten?

Thank you for your interest, Brian. We had the sensor mounted on the competition robot, but because of the problems described in the paper, we really never got it working correctly until after the season was finished. It was in the post-season school year that we added the timing delays, rearranged the register read sequence, and added the squal and shutter variables. Because the Arduino code “appeared” to work, and because we “appeared” to get good readings three times out of four, the diagnostic process took us down a lot of dead ends. So you’re seeing about six months of trial and error learning collapsed into a paper. I hope the team decides to use the sensor this year for better autonomous performance.

The attachment herein has the spreadsheet we used to capture and analyze the test data for the Flow Motion sensor on the Arduino. I have it in xls format, but we did it originally with OpenOffice. Not sure if that will cause problems.
By way of explanation, Sheets 1 through 7 show the data from the sensor with different loop delay values. You can see the spikey data, but at this point, these can be ignored.
Sheet 8 is where most of the good stuff resides. At this point the software library problem had been solved. The left columns are the ascii data from the sensor via the Arduino. The next columns are the same data in numeric form. The column called “Rounds” is an Arduino count of times through a “sensor ready to read” loop, and its not likely useful any longer.
Then there is the same data through a low pass filter with half second time constant that can be adjusted. The graph shows motion going out and back to zero.
Under the top graph is a sorted list of runs made sequentially, in one direction only. The table is sorted by the number of readings, which goes down as the speed goes up for the fixed distance. To the left of the table is a graph of the same data.
Sheet 9 is the data from just one run of the Y axis, and we see no surprises.
Sheet 10 is a single run by turning (spinning by hand) the sensor. No surprises there either, so far.
I hope this provides some enlightenment on how to begin to interpret the data from the sensor. We certainly have more work to do to understand it before build season.

FlowMotionTests.xls (285 KB)

sailerjoe: After years of lurking on CD, I registered just to say thank you for sharing this! We also purchased a couple of these sensors last year, and had to throw in the towel :frowning:

Quickly scanning through your code, it looks like the key piece we were missing was reading all of the registers in order. I’ll try to get some team members on integrating your paper into our code and report back if they find anything interesting.

Our solution to the timing problem was to tap directly into the underlying C libraries to use Linux SPI functions which allow you to add a delay directly. Reliable timing and no extra pins; at the cost of an extra dependency through JNA. We were able to perform basic communication this way, but the motion register readings never made sense. Now I know why (…I hope…)

If you’re interested in this approach, it’s on the Team 568 GitHub. One difference that stands out is that - by my reading (and recollection) of SPI mode 3 - we should have been setting spiFlow.setClockActiveLow() and spiFlow.setSampleDataOnRising(). However - due to a WPILib bug - the 568 code used setSampleDataOnFalling(); while your team used setClockActiveHigh(). It’s equally possible that your team tripped on the same bug we did and chose the opposite fix; or that my team just read the definition wrong - the wikipedia article is a bit hard to parse. Either way, I wanted to point out that the WPILib bug was fixed over the Summer, and - if I’m not mistaken - you should migrate to setClockActiveLow() and the new setSampleDataOnTrailingEdge() method instead. If I am mistaken, I’d appreciate your input all the more.

Danny Tix
Mentor - Team 568

Thank you, Danny, for your thoughts on using the Flow Motion Sensor. I rather like how you solved the timing problem, but for now, I think we will stick with what we have since it works and we already have the cable built.
I agree that the wikipedia article is a bit tricky, as is the Arduino article for the same topic. Our initial settings for SPI Mode 3 didn’t work as expected. We finally just tried a number of combinations of settings until we found one that worked, which is what we have in the published code. Thanks for letting us know about the bug fix. We will have to take another look at the settings again, and we will republish the code if it needs to be changed.
Looking at your code, on lines 177 and 191, I see timing delays specified in microseconds. My understanding was that delays less than 1 millisecond resolved to just 1 millisecond delays, using the Timer.delay() function. Is this not correct? I asked this question in the white paper, too. We had to use delay loops in the code, but specific delay statements like yours would be better if they are accurate.

Joe - thank you again for your valuable insight. It appears that you are right about Timer.delay() only supporting millisecond accuracy. It’s just a simple wrapper around the less precise version of Thread.sleep().

That still leaves the (supposedly) nanosecond accurate overload of Thread.sleep() though. Do you know if your team was able to rule that out? If not, we’ll try it next.

Hi, Danny. Those are good questions. We got the millisecond limit by scouring the web for information about the wpilib function. So we immediately discarded it from consideration for 50 microsecond timing delays. I’m never sure the RoboRio implements all the features of Java, so I usually just stick with the wpilib. Thus, we decided to go old-school and use a delay loop like you see in the code. However, if you’re exploration of the Thread.sleep() method turns up a gem, we’d really like to know about it. When we measured the timing loops on the oscilloscope, we saw very stable timing pulses, like within a tenth of a microsecond of repeatability. I’d love to know if the overloaded version of Thread.sleep() works that well on the RIO. Looking forward to your test results. If they’re good, we can update the code.

First off, I want to say that this is an amazing example of how amazing this community is at sharing information. This is a great find that you’ve graciously shared with everyone on here and I appreciate it.

Regarding the issue of microsecond-level timing in Java. Here is a great article I found on the topic, but I have yet to test it: It concludes that busy waiting is the most accurate way to go using System.nanoTime().

How did you wire the breakout board to the RoboRio? We’ve never created a custom cable before or anything along those lines, so if have a wiring diagram or something like that, it would help a lot.

Secondly, I was looking at the spreadsheet you attached and it mentions that you were filtering the data. How were you doing that? Was it a Kalman filter? I didn’t see anything like that in the code.

Thank You

Never mind, I see that you were averaging the last filtered value with the current deltaX to get the current filtered value. I don’t know why that works so well to smooth the data, but the results are pretty good.

It’s a standard low pass filter with a 0.5 time constant. The time cconstant can be modified in the spreadsheet with values from 0 to 1, and you can see the effects. I tried various values, and settled on .5 as the number with good smoothing and minimal settling delay.

Thanks very much for the tip on microsecond timing. After we test to make sure it works on the Rio, we might switch over to using that, making us less susceptible to hardware changes.

As to the cable, you’ll find it’s a useful skill to make your own cables sometimes. This is a case in point. In order to avoid modifying the SPI Rio code, we needed a cable that connects to both the SPI interface and to a DIO channel. The Breakout Board has two separate interfaces, one for the distance sensor using I2C, and one for the motion sensor using SPI. We didn’t use the I2C part. We buy a kit of parts from Amazon that we use to make circuit board headers and matching cable connectors.
You also need a crimping tool:
We typically use ribbon cable between the connectors at each end, but sometimes we use regular wire. or
And you’ll need a wire stripper to bare the ends of the wires. I recommend adding solder to the bare wire ends so they hold the crimped connector better. Check YouTube for how-to videos.
I’ll post a pic of what we did tomorrow, along with a schematic.
Note: Don’t forget to illuminate the floor where the sensor is looking…a lot!

What about the wiring? I was looking at the arduino getting started guide for the wiring diagram and trying to match it with SPI, but some didn’t line up. Here’s a link to the diagram:

Flow GND -> Roborio SPI GND
Flow VCC -> Roborio SPI 5V
Flow CS -> Roborio SPI CS0
Flow MISO -> Roborio SPI MISO
Flow CLK -> Roborio SPI SCLK? This could also be SCL
Flow MOSI -> Roborio SPI MOSI
Flow SCL -> Roborio SPI SCLK? This could also be CLK
Flow SDA -> I’m not sure. It’s plugged into an Arduino analog input, so maybe an analog in. If I had to guess, I’d go with the top pin on the third input because you mention port 3 inside the code, but it doesn’t specify port three on what part of the board. m not sure. It’s plugged into an Arduino analog input, so maybe an analog in. If I had to guess, I’d go with the top pin on the third input because you mention port 3 inside the code, but it doesn’t specify port three on what part of the board.

Can you shed some light on the SCL, SDA, and CLK ports? Or am I getting this wiring all wrong?

Thank You

I’ll try to describe the wiring, because I won’t be able to get a picture until tomorrow.
Starting with Pin 1 on the Flow Breakout board, which is Gnd, connect that to ground on the Rio SPI connector.
Pin 2, Vcc connects to 5V on the Rio SPI connector.
Pin 3, CS connects to the S pin of your choice of DIO channel on the Rio.
Pin 4, MISO connects to MISO on the Rio SPI connector.
Pin 5, CLK connects to SCLK on the Rio SPI connector.
Pin 6, MOSI connects to MOSI on the Rio SPI connector.
That’s it! None of the other pins are needed, unless you want to. You could connect Pin 7, Motion to any DIO channel, and use it to detect motion. We didn’t need it, and our software doesn’t support it. You could also connect Pin 8, *RESET to a DIO channel so you can reset the sensor, but it’s not necessary.
The remaining pins are the I2C interface for the distance sensor, and they’re not used in our application.
*Note: we only have one sensor on the SPI interface. If you have more, you’re wiring might change somewhat.
Hope that helps.

Thank you very very much. This is going to be so much fun to play around with. :smiley:

RIP auto