In this post I’ll show how I built a motion tracker using Arduino and a single ultrasonic sensor. The main purpose of this post is to show how the ASM method can help you think differently to solve problems.
In particular, I noticed during my research that most, if not all, motion trackers end up using multiple ultrasonic sensors (at least two). This post will show that you don’t need more than one sensor to accurately follow motion, and that such a simple design emerges when using the ASM method.
Please note that I’m discussing motion trackers here, not simple motion detectors, which can trivially use only one sensor.
As I explained before, the ASM method is a systems design and modeling methodology that allows you to design systems from the ground up. At each incremental refinement step, you add complexity and functionality to the model. As you add complexity, you can easily use the currently refined ASM to reason about (and prove) characteristics that you want your system to have. By doing so, you iteratively arrive at a final model of the desired system that is easily portable to implementation, be it code or hardware.
As with any project, I always start by doing my homework and researching concepts thoroughly. Let’s start with the sensor.
Robotics/hobbyist ultrasonic sensors are all fairly similar. For example, several different companies sell ultrasonic sensors that operate in very similar ways and use the same fundamental principles.
One surprising thing I found by reading the datasheets and doing my research is that an ultrasonic beam produced by such sensors is very different than I expected. Intuitively, I expected the sensors to produce a simple cone (or perhaps a trapezoid) of ultrasound, where the beam would become proportionally and linearly wider (and inversely weaker) as it traveled farther away from the sensor. In other words, I expected the beam to look something like the diagram below.
The reality, however, is much more interesting. It turns out that the air pressure created by the ultrasound creates a rather complex pattern that many datasheet simply portray as a 2-D bubble (or tear-drop) of ultrasound. For example, the simplified 2-D “bubble” of ultrasound for one MB1000 model transducer looks like this:
These bubbles are useful for people to quickly see the range, width and depth of each of the beams that each sensor produces. Nevertheless, these bubbles are still over-simplifications of a very complex pattern. The complexity of the beam in 2-D becomes quite apparent when we visualize the details, as shown next.
In 3-D, the beam ends up looking simply fascinating. The image below illustrates how the air pressure changes with ultrasound.
Donald P Massa has a very thorough and detailed explanation of how these things work that I recommend you read.
The ground model
Having investigated how ultrasound and ultrasonic transducers work, I then proceeded to design a model for my ultrasonic motion tracker.
As you know by now, ground models are meant to be rough sketches that represent what the system needs to do, with the details of how it needs to do it largely ignored. This is great, because ground models allow us to actually start with a design from the ground up, without being overwhelmed by all the details at once.
With respect to my motion tracker, I observed that for my little system to be able to track motion, I needed it to first detect motion.
My ground model for this is shown next in both graphical form and as a macro.
The beauty of the ASM method is that the macro form is equivalent to the graphical form, so you can always switch between the two depending on your needs.
If you carefully inspect the ground model, you will notice that it is a very simple proximity sensor. First, there is something sensing proximity of whatever is closest within the line of sight. Then there is a delay. Then we sense proximity again and calculate a difference. If there is a difference, then, voila, we have detected motion.
Now, notice that the “something sensing proximity” is modeled as a function. In fact, every “action” in the ASM method is a function. The term nullary simply means that it takes no arguments. The senseProximity function therefore models our ultrasonic sensor well because it simply returns proximity, as per the datasheet.
The “seq” in the macro form simply informs us that the operations performed by the DetectMotion rule are executed sequentially, rather than in parallel. This is important because as we saw before: ASMs are, by definition and nature, parallel reactive machines that execute all their active updates simultaneously. This means that if we didn’t have “seq” in there, then all the operations within the ground model would execute at the same time leaving an undefined system.
So we have our simply motion detector. Now, I observe that the next step after detecting motion, but before tracking motion, is possibly to actively scan for motion. My goal is to scan for motion and then, once motion has been detected, to actively track it in a subsequent refinement.
Let’s refine our ground model and let’s use a feature of ASMs called bounded parellelism. The refinement looks like this.
The reason I’m using bounded parallelism (i.e. the forall) is so that the detector can detect motion on all angles, from 0 to 180 degrees, at the same time!
Conceptually, this system works great and easily follows from the extension. In other words, we can prove by inspection that if there is a motion detector scanning all 180 degrees simultaneously, then the system will for sure detect any motion on any and all 180 degrees.
However, even though this system is conceptually sound, it is not realistic for the following reasons:
- An Arduino does not have 180 GPIO pins that can be used in parallel
- An Arduino cannot read from more than one GPIO pin at a time, due to a single processor
- Even if the above were not limitations, there is no physical way for one single ultrasonic sensor to be directed on all 180 angles at the same time.
We therefore have to change our refinement slightly to bring it closer to reality so we can actually implement it.
To remove the unrealistic (but conceptually useful) parallelism from the previous refinement, we now limit our model to non-deterministically select one single angle to detect motion. The new model is shown next.
This model is much closer to reality because it does not have the sensor scanning all directions at once. Rather, it more closely mimics the physical implementation by scanning in only one direction at a time.
The refinement is also better as a whole because it captures the requirements while not imposing which angle to scan a priori. In other words, we let the system somehow choose for us the angle to scan in this refinement, and we do not worry about the actual scanning policy (how exactly that angle is chosen) here.
Indeed, the system might as well be scanning degree 180, then 0, then 90, then 30, then 180, etc.. We don’t worry about that because we can actually specify how to select the angle in later refinements.
Let’s now refine our model further to get our system to more closely track motion. The new refinement is shown next.
Note that we still rely on the system somehow choosing the initial angle for us. Now, however, the ASM is more restricted in the way that it uses the angles.
In particular, note that if motion is detected in the current chosen angle, then the machine will scan (simultaneously) one degree to the left as well as one degree to the right, and then it will call itself again recursively to track this motion.
The moment that no motion is detected in degree, say, degree-x, then the machine will no longer recursively call itself and motion tracking around that degree will end. Motion tracking will, of course, continue for the other set of recursive calls as long as there is motion.
With the previous refinement, we have created a motion tracker. Unfortunately, it uses bounded parallelism once again to accomplish its goal.
Let’s remove that parallelism and make sequential. With this change, believe it or not, we have everything we need to implement the tracker with Arduino.
The final refinement is shown next.
The Motion Tracker
The schematic of the motion tracker is very simple.
I used a servo motor to physically move the ultrasonic sensor very precisely. Even though I could have moved the sensor by one degree at a time, doing so would have been unnecessary. The reason for that is because the sensor detects a field of roughly 30 degrees. Therefore, I can get the sensor to detect objects through each degree, from 0 to 180, by only scanning 6 degrees in total: 15, 45, 75, 105, 135, 165.
The scanning is done back and forth, forward then reverse ad infinitum.
For example, the following image depicts how objects really look like to the sensor. You will note that if an object is at any one particular point within the 30 degree range of sight of the sensor, then it is as if the object spans the entire range of sight, regardless of where exactly it is located.
This aspect of my hobbyist ultrasonic sensor is seen more clearly in the following image, where an object is in the middle of two ranges of sight.
The motion tracker implementation is shown in the following videos. Note that the jittery movement of the tracker is due to the fact that, once it locks onto a moving object, it not only follows it precisely through each degree, but it also scans one degree more on each side, as the MotionTrackerSeq refinement shows.
I’m sure the model and/or implementation can be refined even further to allow for smoother motion, but this is quite good with the basics we have thus far.
Motion tracker at work:
From the perspective of the sensor (using a GoPro). Warning: this might cause nausea!
A natural refinement of the machine is to not only have it detect motion, but also whether objects are approaching or receding.
This is shown in the next ASM.
Although I have not yet implemented this machine, I can easily imagine it being useful for a tracking car that follows a receding object (and reverses when the object approaches, thus always keeping a “safe” distance).
There are many possibilities to this, and using the ASM method helps make it super easy to conceptualize and to verify properties that we desire our systems to have.