The Swarm Ranging Protocol is a UWB-based ranging protocol that dedicates to provide a simple yet efficient ranging experience for dynamic and dense swarm networks of robots and devices.
This repository contains the implementation of the paper:
- Ultra-Wideband Swarm Ranging. Feng Shan, Jiaxin Zeng, Zengbao Li, Junzhou Luo, Weiwei Wu. INFOCOM 2021. PDF & CODE
- Ultra-Wideband Swarm Ranging Protocol for Dynamic and Dense Networks. Feng Shan, Haodong Huo, Jiaxin Zeng, Zengbao Li, Weiwei Wu, Junzhou Luo. IEEE/ACM Transactions on Networking, 2022. PDF
Currently the protocol implementation is based on the DW3000 UWB chip, we implemented the DW3000 chip driver on Crazyflie and made a custom extension deck, which we named it the adhoc deck
.
The older implementation based on the DW1000 UWB chip (loco deck) can be found here. Considering the older implementation is no longer actively maintained, we highly recommend you to upgrade loco deck
to adhoc deck
. The upgradation is very simple and straightforward, since the dw3000 and dw1000 chips are pin compatible, just replace the dw1000 chip on the loco deck with a dw3000 chip then you will get a brand new adhoc deck.
The State Machine of Swarm Ranging Protocol
The compilation requirements and basic steps are the same as the official Crazyflie firmware, if you are new to Crazyflie, please refer to the official documentation for more details.
There are three default Kbuild configuration templates in the configs
folder corresponding to three different modes, adhoc_defconfig
for uart1, adhoc_alt_defconfig
for IO2 / IO3 and adhoc_uart2_defconfig
for UART2. The pin definition of UART1 and IO2 / IO3 modes are identical to loco deck, if you want to use the UART2 mode you need to use our customised expansion board circuit.
You can make custom build options on top of our Kbuild template:
make adhoc_defconfig
or enable the corresponding configuration option via make menuconfig -> Expansion deck configuration -> Support the Adhoc deck
.
The UART1 mode is the default when the
adhoc deck alternative IRQ and RESET pins
andadhoc deck use UART2 (TX2, RX2) pins
option are unchecked.
Then compile and flash the firmware:
# Compile
make clean
make -j
# Flash
make cload
The persistent parameter MY_UWB_ADDRESS
is used as the identity of each drone, so before ranging and messaging, we should manually setting it up using the interactive python script or via cfclient -> Parameters -> ADHOC -> MY_UWB_ADDRESS
.
Ranging is enabled out of the box, so after setting MY_UWB_ADDRESS
for each drone, we can observe the computed distance through the Plotter provided by cfclient. The log parameter can be found at the log group named Ranging
.
You can also programmatically get distance informations:
// swarm_ranging.h
int16_t getDistance(uint16_t neighborAddress);
Here are the interfaces to send and receive packets via UWB.
// adhocdeck.h
int uwbSendPacket(UWB_Packet_t *packet);
int uwbSendPacketBlock(UWB_Packet_t *packet);
int uwbReceivePacket(UWB_MESSAGE_TYPE type, UWB_Packet_t *packet);
int uwbReceivePacketBlock(UWB_MESSAGE_TYPE type, UWB_Packet_t *packet);
int uwbReceivePacketWait(UWB_MESSAGE_TYPE type, UWB_Packet_t *packet, int wait);
void uwbRegisterListener(UWB_Message_Listener_t *listener);
To send a packet, you only need to call the corresponding packet sending interface:
// Non-blocking
int uwbSendPacket(UWB_Packet_t *packet);
// Blocking
int uwbSendPacketBlock(UWB_Packet_t *packet);
To receive packet you first need to specify what type of message you want to receive, there are three pre-defined message types out of the box:
typedef enum {
UWB_REVERSED_MESSAGE = 0,
UWB_RANGING_MESSAGE = 1, // Ranging message
UWB_FLOODING_MESSAGE = 2, // Flooding message
UWB_DATA_MESSAGE = 3, // Routing message
UWB_MESSAGE_TYPE_COUNT, // don't use it
} UWB_MESSAGE_TYPE;
If you want to define a custom message type, simply add the message type definition (for example, CUSTOM_MESSAGE_TYPE) to the above enumeration structure.
Then create a message reception queue, register a listener for the corresponding message type by calling uwbRegisterListener
.
static QueueHandle_t rxQueue;
void exampleTaskInit() {
// init reception queue
rxQueue = xQueueCreate(EXAMPLE_RX_QUEUE_SIZE, EXAMPLE_RX_QUEUE_ITEM_SIZE);
// init listener
UWB_Message_Listener_t listener;
listener.type = CUSTOM_MESSAGE_TYPE;
listener.rxQueue = rxQueue;
listener.rxCb = exampleRxCallback;
listener.txCb = exampleTxCallback;
// register listener
uwbRegisterListener(&listener);
// ........
}
Each message type can only bind to one listener, registered listeners of the same message type will overwrite each other in the order they are registered.
Lastly, to receive and process messages, we need to create a message receiving task:
static void uwbExampleRxTask(void *parameters) {
systemWaitStart();
UWB_Packet_t rxPacketCache;
while (true) {
if (uwbReceivePacketBlock(CUSTOM_MESSAGE_TYPE, &rxPacketCache)) {
// receive and process CUSTOM_MESSAGE_TYPE message.
}
// ........
}
}
If you find this repository helpful for your work, please kindly cite the following paper:
@article{shan2021ultra,
title={Ultra-Wideband Swarm Ranging},
author={Shan, Feng and Zeng, Jiaxin and Li, Zengbao and Luo, Junzhou and Wu, Weiwei},
booktitle={IEEE INFOCOM 2021-IEEE Conference on Computer Communications},
year={2021},
organization={IEEE}
}
@article{shan2022ultra,
title={Ultra-Wideband Swarm Ranging Protocol for Dynamic and Dense Networks},
author={Shan, Feng and Huo, Haodong and Zeng, Jiaxin and Li, Zengbao and Wu, Weiwei and Luo, Junzhou},
journal={IEEE/ACM Transactions on Networking},
year={2022},
publisher={IEEE}
}
If you have any question, please issue the project or email us and we will reply you soon.