This project is by Matthew Hurley, and is a submission for the NXP HoverGames Challenge 2: Help Drones, Help Others During Pandemics.
MotivationBats have received a large amount of attention surrounding the current pandemic. Negative attention, to be more precise, given that they were identified as carriers and potentially sources of the SARS-CoV-2 strand. Of course, since one of our team members is a PhD candidate in wildlife biology, who specializes in bats, we thought that this project and competition was a perfect opportunity to facilitate, and shed light on, bat research. In particular, our efforts were focused on the process of finding radio-tagged bats.
You may be thinking, "what does bat research have to do with pandemics?" Since bats are carriers of some high profile disease, including rabies, Ebola, and as mentioned, SARS-CoV-2, the fundamental research of bats serves a vital role in global pandemic efforts. While bats are certainly not unique in their ability to harbor new strands of viruses (a majority of deadly viruses have come from other mammals), the viruses that they do carry tend to be especially virulent. In addition, their unique immune systems, and the fact that they are the only flying mammals, make bat species excellent case studies for pandemic-causing viruses. With increasing human encroachment on bat habitat, which in turn means more opportunity for contact with them, there has never been a better time for us to step our bat research game to the next level.
The ProblemIt's easier to understand what we are trying to solve if the reader has a short background on the bat research process. It generally goes as follows:
Large, lightweight nets are strung up in areas where it seems likely that bats will hunt, starting in the early evening. When the bats are caught, scientists take a number of measurements, record their sex, note the species, and finally, shave the bat's back and glue a little battery-powered radio tag onto it. The tag remains stuck there for one or two days, until the hair grows back enough and the tag falls off. During that short window, which usually means the morning after tagging the bats, researchers must head out into the woods and start tracking. The interest, here, is in finding the bats' roosts before the tags fall off, or the bats move out of range the following evening. For most teams around the world, this means driving, hiking, and bushwhacking with a hand-held Yagi (directional) antenna. This is a laborious and time consuming process, and given that the time frame to track the bat is so short, it can be detrimental to successful research efforts. The pandemic has only compounded these problems, necessitating smaller teams, and restricting time and resources.
Our solution:
Our goal is to speed the tracking process up, and make it less resource intensive. There are a number of ways to do this with a drone, but we stuck with the simple method: strap a directional antenna to a drone, have it fly in circles in a variety of locations, and then triangulate the position of the bat using the data gathered. This process can be used as a rough characterization of the bats location, or to inform researchers where to begin searching. Note, that this method is obviously not limited to researching bats; it can be used to locate any sort of radio beacon. However, since bat tags must be tiny, and GPS modules are too big, this solution has a special advantage in the realm of bat studies. Our hope is that this will not only help research efforts, but also help researchers who are limited by the pandemic.
Building the BatcopterNow that the basics have been covered, let's get into the details of constructing the bat tracking drone. There are three main categories of components needed for this:
- A drone - in this case the NXP Hovergames KIT-HGDRONEK66
- A companion computer - we used a Raspberry Pi Zero W
- A antenna/receiver system - we used a RTL-SDR software defined radio and DIY Yagi antenna (KD5IVP Backpacker Yagi)
The project also necessitates some sort of radio beacon which can be "tracked". We used a Raspberry Pi 3B+ and morrolinux's mpradio.
Hopefully the following not only serves as useful information about the project setup, but also as a guide for future users.
Drone Assembly
Construction began with drone assembly. NXP has a fantastic gitbook for complete setup of the drone, and additional resources related to development and offboard control: https://nxp.gitbook.io/hovergames/
Mounting and Connecting the Companion Computer (RPi Zero W)
Next we had to mount the Raspberry Pi zero W and associated hardware. The DROK 90010 5V buck converter was used to tap power from the drone's battery. The case and other Hovergames attachments can be found on Thingiverse.
Power wires were soldered to pins 4 (5V in) and 6 (Gnd) which could then be connected with WAGOs or wire nuts to the buck converter. The power-in leads of the converter were soldered to the power distribution board on the drone. A JST-GH connector was also soldered to pins 8 (Tx), 10 (Rx), and 9 (Gnd), matching the description of the pinouts for the telem2 port on the FMU. The telem2 port is what we used to communicate with the drone fmu, via mavsdk-python. After installing support for mavsdk-python,
pip3 install --user --upgrade mavsdk
the connection was tested using firmware_version.py in the GitHub page examples folder. Note the correct system address must be called,
system_address=“serial:///dev/serial0:921600”
Bluetooth must be disabled, and the Linux serial console must be disabled also.
Ad Hoc Network
The final piece of the companion computer puzzle was figuring out a way to initiate the tracking missions remotely. Apparently, for mavsdk-python, there are no existing APIs for monitoring RC switch states. Since that isn't our area of expertise, we opted for setting up an Ad Hoc network on the raspberry pi. The following is the edited /etc/network/interfaces
file on the raspberry pi, which will allow the pi to broadcast the network:
# interfaces(5) file used by ifup(8) and ifdown(8)
# Please note that this file is written to be used with dhcpcd
# For static IP, consult /etc/dhcpcd.conf and 'man dhcpcd.conf'
# Include files from /etc/network/interfaces.d:
source-directory /etc/network/interfaces.d
auto lo
iface lo inet loopback
iface eth0 inet dhcp
auto wlan0
iface wlan0 inet static
address 10.2.1.1
netmask 255.255.255.0
wireless-channel 1
wireless-essid PiAdHocNetwork
wireless-mode ad-hoc
The following must also be executed from the terminal:
systemctl stop dhcpcd.service
With this network set up, all you have to do is connect to the Ad Hoc network via ssh, and run python scripts from there. This method is crude, but gives full functionality, in that you can start programs, terminate them, and monitor printed messages to the terminal.
Connecting the Software Defined Radio
The next step was to connect the RTL-SDR and install support for it on the raspberry pi. All that was needed was a micro-USB to USB-A cable with which the radio could be connected to the Raspberry Pi. The radio was then zip-tied to the drone.
Instructions for installing the RTL-SDR drivers manually from source were followed, and python support was added via -roger's pyrtlsdr. Running
rtl_test
is all that was needed to test the connection.
Building the Antenna
The method proposed for locating a radio source necessitates a directional antenna. Yagi antennas have high gain when pointed at a signal, and the KD5IVP Backpacker Yagi is a lightweight solution that looked promising, considering it had to be carried by the drone. We used the same dimensions as the original designer, making an antenna for the 2 meter band since bat transmitter tags often broadcast at around 150MHz. We constructed the antenna using an arrow shaft as the central support, 10 gauge stripped wire for the elements, and a SMA connector/alligator clips (soldered together) for connecting to the driven elements. This old YouTube video provides some good visuals and instruction for assembling the antenna. The antenna was mounted to the drone landing gear using two small, wooden boards and zip ties, ensuring that the axis was in line with the digital compass on the drone.
Creating a Radio Beacon
The final step was to come up with a radio beacon that could be located by the drone. For this, we used a Raspberry Pi 3B+ with morrolinux's mpradio on board. The Pi was set to broadcast at 150MHz in order to match the antenna's design. A small piece of wire was added to GPIO pin 4 of the Pi to function as its own transmit antenna.
Please note that broadcasting at VHF frequencies without a license is illegal, and it is advised to do so only using low power (which we did).
Before testing full functionality of the drone, it was necessary to test the component parts (outside of simple connection tests).
A large number of SITL simulation runs were performed, and served an important role in the development process, allowing for code to be added and tested one piece at a time. jMAVSim running in combination with an instance of PX4 SITL was used according to the instructions in the PX4 developer guide. All of the simulation and initial development was done in a Linux virtual environment, and communication to the PX4 instance was performed via mavsdk-python. Both of these processes are well documented in the PX4 dveloper guide, mavsdk-python documentation, and the NXP gitbook.
To test the functionality of the SDR, antenna, and beacon, a program called CubicSDR was used within the Linux virtual environment. The program displays the spectral power distribution as a function of frequency in a visually friendly way. We used it to test beacon broadcast frequency and strength, and antenna reception. It was also used to do a qualitative characterization of the Yagi antenna's gain and directionality.
There is also code attached in the "code" section of this project, detailing connection through the terminal and generation of plots from the raw radio data. Generating these plots turned out to be a necessary test. Unlike CubicSDR, which presumably does a lot of filtering and averaging, the terminal generated plots revealed the RTL-SDR hardware inherent center frequency spike. This spike (as I crudely understand) is due to hardware FFT, and must be accounted for in software. To work around the problem, the center frequency was simply shifted by 500kHz. This resulted in a sampling bandwidth of 2MHz, centered around 150.5MHz, freeing the frequency of interest (150MHz) from any effect by the center frequency spike.
Finally, drone simulation code and radio data code were combined and run in the simulator. Attached in the "code" section of this document is the final simulator tested code providing the full theoretical functionality.
The drone itself was tested (outside of SITL or HITL) using more example code from the GitHub page, including arming, health, and telemetry. These examples are straightforward and self-explanatory, so there will be no further detail on them here.
Brief Code Description
As quoted from the description of the code attached:
The drone will fly up to 10 meters, and then slowly spin in a circle, logging data at every 3rd degree increment. It logs longitude, latitude, compass direction (degrees from North, clockwise when looking from above), signal frequency (the radio scans a band of frequencies), and real-valued power spectrum values in dB. Note that the frequencies are shifted off-center to avoid the center frequency spike that results from rtl-sdr inherent hardware design. You can then average a few frequencies around the desired frequency (150 MHz in this case).
Maiden Tracking Mission and ResultsDue to time constraints and non-stop rain for the final two weeks of this project, we only performed the mission of locating a beacon one time. There were/are bugs to work out, but overall the concept was certainly proven. Keep in mind that the goal was to get compass bearings at two separate locations, indicative of the direction of strongest signal and the (hopefully) the beacon location. Results are detailed below:
The mission was performed in a large field, with the beacon active out near the center of the field, while the drone was launched in two locations on opposite sides of one end of the field. It was drizzling and slightly windy, so we certainly could have asked for better conditions, but at the very least the weather indicates resiliency in the hardware.
Flight #1
The first flight had a few bugs out of the gate, including problems with the connection error block of code, which we ended up removing.
async for state in drone.core.connection_state():
if state.is_connected:
print(f"Drone discovered with UUID: {state.uuid}")
break
After the first successful takeoff, the drone got stuck in the following block (presumably because its altitude was not high enough), so it was also removed on the fly, and replaced with a 15 second sleep call.
async for position in drone.telemetry.position():
altitude = position.relative_altitude_m
if altitude > 9.5:
break
After those two fixes, the mission was successful...
OTHER THAN THE LANDING!
Note that we had issues with the antenna catching the ground on takeoff (you can see the bent reflector element in the first video above), which is definitely undesirable and can potentially point to some issues down the road.
Flight #1 Data
The data from the first flight looked really good. A bandwidth of 2MHz, 256 samples within that band, was gathered in increments of 3 degrees. The power spectrum (Pxx; the units are somewhat arbitrary, and more comparative) at the six closest frequencies surrounding 150MHz were averaged, for each compass increment. As expected, there were two "spikes" in Pxx (one presumably for the signal being directly in front of the antenna, and one directly behind) around 190 degrees and 0 degrees. In this case, the compass bearing at the max Pxx was taken.
This flight alone was a great proof of concept, given that we had the added benefit of knowing where the beacon was, which lines up well with this location's bearing by itself.
Flight #2
After the crash in flight one, the following code was removed to prevent another action.return_to_launch disaster:
print("Returning to launch")
await drone.action.return_to_launch()
It is worth noting here that after the crash the drone sustained some damage, albeit fairly minimal. The antenna, however, was pretty severely bent (so we bent it back!), which may have affected performance on the second flight. The same mission was run at a new location. We should also mention that the antenna caught the ground again on takeoff, bending one of the driven elements out of alignment.
Flight #2 Data
The data from the second flight was much dirtier, which is discussed briefly below.
The problems on this flight are a bit of a tossup. It looks like the drone started off receiving relatively strong signal, which then dropped as it turned, and started to come back up again - a trend consistent with what we expect out of a Yagi antenna's performance. However, at ~130 degrees, something weird happened, indicating a malfunction somewhere. This may have been on-board power fluctuations affecting the SDR (unknown damage from the crash), but it looks more like something happened to the beacon. In addition, since one of the driven elements was bent on takeoff, the data here was expectedly, at least partially, unreliable.
Choosing which data point to use to get a bearing was not necessarily straightforward. However, since choosing the bearing at the max (near 0 degrees) would have led to a non-intersection of bearings, and given that the data was trending up at 130 degrees before the "weird" event happened, we chose the point just before the drastic dip. Of course, this was somewhat served by the benefit of knowing approximately where the beacon was, but the trend is certainly there, and it doesn't seem like an unreasonable analysis even without some previous beacon knowledge.
Calculating the Final Location
With two locations and bearings for each, the calculation for the point of interest is straightforward:
Given point (x1,y1) with bearing D1, and point (x2,y2) with bearing D2, the intersection of those two bearings can be found by equating the following two formulas, where (x3,y3) is the point of interest:
y3 = s1(x3 - x1) + y1 , y3 = s2(x3 - x2) + y2
where s1 = tan(90 - D1) , s2 = tan(90 - D2)
This results in the following final map:
In the end, the triangulated position was about 10m off of the actual position. This was a really good result, with obvious room for improvement.
Final Thoughts and Future IdeasWhile the result was notable, there are three main areas for improvement:
- Antenna design and mounting
This is the biggest issue, in our opinion. Having a more precise design (e.g. improve on stripped 10 gauge wire for the elements), and making the antenna less susceptible to stress, should greatly improve the narrow receiving bandwidth that we desire. In addition, the antenna really needs to be mounted in a better way to prevent it from getting caught on takeoff.
- Filtering on the SDR input/output
There are a number of ways to do this, with the goal, again, being to narrow the receiving bandwidth.
- Antenna alignment with the compass
We didn't talk too much about this, because, well, it was really just eyeballed. Misalignment could be corrected as an offset (many more runs would be needed with a very stable mount), but it would be nice to have a jig and/or calibration routine to get everything properly aligned.
It's also desirable to scan for multiple frequencies at one time, and potentially have the drone fly to multiple GPS locations, limiting the number of takeoffs and landings. The challenge here will be in optimizing the scanning speed in order to complete all tasks before the battery runs low.
In the future, we want to experiment with using a much smaller Yagi antenna for UHF frequencies (around 800-900MHz). The advantage of such a system would be having smaller, more affordable, and more durable tags and antennas (solving most of the issues described above). The obvious disadvantage here is that higher frequencies don't travel as far. That problem could be relatively easily solved by having the drone fly a search pattern until it finds a significant signal (or one of a certain form e.g. 900MHz on a 1Hz carrier), after which it would rotate as before in a couple of locations, triangulate, and return. It is definitely a more complex system that has a lot of potential.
GitHub for code: https://github.com/Bat-Tracking-Drone/Track_Bats
Comments