Category: Frontpage

Guest post by Dominik Grzelak, Dresden University of Technology, Germany

This blog post accompanies a research article published in the XR Salento 2025 proceedings (Springer LNCS).

Setting up a mobile indoor positioning system with the Crazyflie platform—specifically using the Loco Positioning System (LPS)—typically involves walking through a large room with a laser measure, sticky notes, and a bit of patience.

That was exactly my situation at work when setting up UWB anchors for mobile robot demonstrations, which often had to be relocated to different rooms.
And it got me thinking—what if there were a more intuitive, flexible way to define and deploy these setups, especially in labs or educational settings where reconfigurability is key?

This question led to the development of XR-PALS, a mixed reality tool that simplifies the entire LPS configuration process.
Instead of manually measuring anchor positions and inputting coordinates into cfclient, XR-PALS allows users to create and sync virtual and physical setups almost seamlessly—just by moving virtual anchors in space using a VR headset.

Diagram showing the three-step setup for the Bitcraze Loco Positioning System: 1) Connect anchors, 2) Configure the system, 3) Track the Crazyflie drone in 3D space.
Three-step setup process for the Bitcraze Loco Positioning System, illustrating anchor connection, system configuration, and 3D Crazyflie drone tracking.

What is XR-PALS?

XR-PALS (XR-Powered Assistance for Loco Positioning Systems) is a tool we (Victor Victor and Dominik Grzelak) developed for defining cyber-physical spaces using UWB anchors on tripods.
It runs on a passthrough VR headset (we used Meta Quest 3), allowing users to visualize the LPS layout and manipulate anchor placements in a virtual 3D environment until they are virtually and physically aligned.

Once everything is aligned, a single tap on the “Export” button sends the virtual anchor positions to a Rust-based middleware service running on a host machine within the same network as the VR headset.

It’s fast, visual, and eliminates the need for hand-measuring coordinates for our mobile robot demonstrators.

You can watch the system in action in our demo video:

Why We Built It

Usually, the LPS setup involves manual measurements and manual data entry in cfclient.
This is time-consuming when environments change frequently—as is often the case in education and research.

With XR-PALS, the goals are to:

  • Speed up setup – Users in our study completed setups significantly faster.
  • Reduce error – Anchor placement is visual and interactive. No calculations needed.

How It Works

XR-PALS consists of two main components:

  1. VR application
    Runs on a VR headset and displays the full anchor layout. Users can drag anchors, view distances, toggle info layers, and adjust alignment using natural hand gestures.
  2. Middleware service
    A Rust-based REST API that basically bridges the VR app with the LPS anchors. This service receives the coordinates and remotely updates the LPS anchors—enabled by the open-source nature of the LPS firmware: bitcraze/lps-node-firmware. Coordinates are exported as YAML that can be imported by cfclient.
Diagram showing the system architecture for the XR PALS setup, including UWB anchors, Crazyflie drone, tracking system, and XR interface components connected through data and control flows.
System architecture.
Images of XR PALS user interactions, showing a person wearing an XR headset controlling a Crazyflie drone in a physical space with visual overlays and positional feedback.
User interactions.

Real-World Evaluation

We compared XR-PALS against a traditional laser measurement tool in a user study involving 18 participants from our Faculty of Computer Science.
Each participant had to configure a LPS layout (trapezoidal or rectangular) using one of the two tools.

Results:

  • XR-PALS users completed the task faster
  • Fewer measurement errors
  • Higher usability scores and confidence
  • Lower cognitive load, especially for the trapezoidal layout
Bar chart comparing average task completion times for XR-PALS and laser methods, showing XR-PALS with shorter times. Lower values indicate better performance.
Bar chart comparing task completion times between XR-PALS and laser. Lower values are better.
Bar chart comparing average measurement errors for XR-PALS and laser methods, showing XR-PALS with lower errors. Lower values indicate better accuracy.
Measurement error comparison between XR-PALS and Laser. Lower values are better.

The minimal loss in accuracy compared to the time saved is not dramatic for our use cases, compared to the time saved—especially when more participants were able to correctly determine the coordinates overall.
In general, users were more confident using VR glasses than a laser measurement device.

Summary and What’s Next

So if you’re working with dynamic robot setups or swarm experiments, XR-PALS might save you a lot of setup time.

The app works natively with the Bitcraze Loco Positioning System and, so to speak, extends the capabilities of the cfclient.

Here’s what we’re currently thinking about:

  • Supporting larger anchor sets within the app (currently limited to 8 anchors)
  • Improving user interactions based on feedback from our user study
  • Displaying anchor diagnostics (e.g., latency, power levels)
  • Simpler layout reusability (layouts can be saved and shared only via the computer)
  • Adding support for LPS-ROS
  • Releasing the Code here
  • Better casing and docking solutions for the nodes (3D printing)

Check out the project page for updates—and feel free to reach out if you’d like to share how you’re using the LPS.


At the beginning of the year, we released the Crazyflie 2.1 Brushless charging dock. This project was very much an experiment for us since this is the first product we are mainly manufacturing and assembling by ourselves in Sweden. We though we would write a little bit about the reason we made it that way and how it is going.

The Chaging dock is already described in a brunch of pevious block post. It is basically a landing pad for the Crazyflie Brushless that charges the Crazyflie when landed. This is an idea and a design we have been using for years for our fair demos and that has been very useful, we would not be able to continuously fly at fair without it! Some of us even started using is on their desk to keep their Crazyflie Brushless fully charged at all time while developing with it:

However, even though it has been so useful for us, and we designed the Crazyflie Brushless to be compatible with contact-charging, we where not sure of how many people out there would want or need such a charging dock. So we decided to make it available in an experimental manner by manufacturing it by ourselves!

Why ‘made in Malmö’?

While the manufacturing we have in place for all our other products works really well, it requires a non-trivial amount of effort to start the first manufacturing batch. This is mainly due to the fact that the full mass production chain needs to be setup for the first batch and that production happens outside Bitcraze, this requires a lot of work in documentation, planning and administration.

However by doing the production in house, we are able to fix issues as they arises and to work in a much more agile way. In house production will of course no scale, but for a proof of concept it might work, this is at least what we wanted to experiment with.

There are two main improvements that has allowed us to even consider in-house experimental production: the advent of cheap and efficient PCBA services and the improvement in 3D printers reliability. This allows us to source all the parts and assemble them to make the final product.

How is a Charging Dock made?

The charging dock is comprised of two main parts: the plastic landing pad and the electronic.

The Landing pad is 3D printed by us. We now have a mini-print-farm at the office (if a Swarm starts at 2 drones, a print farm shall start at 2 concurrently running printer :):

What made it possible for us to consider running this kind of production was when we got our Bambulab X1 carbon. It is much more reliable and most importantly easier to maintain that any printer we got before, which gave confidence that we could start making products of what we printed. We now have an H2D as well. This currently allows us to print 12 landing docks per working day.

On the electronic side, we are now able to order fully assembled PCB, and even custom cable within weeks.

All we then need is assembly and testing and we got ourselves a small production line with very little risk and a lot of flexibility.

What now?

We are very pleased with what we have achieved so far with the charging dock. The first batch is sold out and we have started manufacturing a new batch with no big pain-point in sight. At some point we will have some decision to take though: do we continue in house or transition to more traditional manufacturing? Will all the work we put so far be useful for setting up mass manufacturing or will we have to restart from zero? At what batch size or frequency will we need to transition?

However this is also one of the great advantage of this: we have full control and we can decide when to manufacture where. As we have talked a bit previously, Bitcraze is a self-organized company, and this experiment actually fits very well with our way of working and keeps us agile. We hope this can free us from the doubts we usually have when thinking about more ‘niche’ products and will allow us to try new things in the future.

UAV Recharging for Remote Applications

Unmanned aerial vehicles (UAV) are invaluable to challenging remote applications, including coastal monitoring, surveillance for safe passage in icy waterways, and search-and-rescue missions. However, after deployment in a remote setting, the functional life of the multirotor is ultimately limited by its battery life. The research we continue to investigate is the ability to cooperatively and autonomously land a multirotor on an uncrewed surface vessel (USV) for recharging. We address this problem in real time with safe control algorithms that we apply on a Crazyflie.

Approach

Our approach enables the Crazyflie to cooperatively coordinate, with a simulated USV, a safe landing in severe wave conditions. It is critical to the autonomy of the system that the agents do not know when or where they’re going to land at the outset, they are cooperating in real time to make these determinations. The novelty of this work is three primary contributions:

Learning a Spatial-Temporal Wave Model as a Gaussian Process

We first learn the local tilt model, representative of the spatial and temporal impact of waves on the tilt angle of a USV, using Gaussian Process (GP) regression. Prior to the execution of the landing, the USV collects Nd noisy observations D = {q, t, φ²}, where q is the position of the USV, t is time, and φ is the tilt angle of the USV. We use GP regression to learn the spatial-temporal tilt model: fw(q, t) = (φ(q, t))² . The predicted tilt and uncertainty, conditioned on the observed data D, at a query point a = [q, t] can be inferred using the posterior distribution.

Distributed Model Predictive Control

Our proposed model predictive control (MPC) architecture combines standard tracking MPCs for the Crazyflie and USV and augments them with additional artificial goal locations. These artificial goals enable the vehicles to coordinate without prior guidance. Each vehicle solves an individual optimization problem for both the artificial goal and an input that tracks it but only communicates the former to the other vehicle. The MPC integrates, into the cost functions for both vehicles, the learned mean and uncertainty quantification of the spatial-temporal wave model from the GP regression. This encourages the agents to converge to calmer waters enabling safer landings in variable wave conditions.

Low-Cost USV Simulation Testbed Platform

To validate the proposed MPC scheme for landing on a USV, we simulate the spatial-temporal motion of a USV in waves of variable intensity using a custom tilting platform. The custom tilting platform has two degrees of freedom (roll, pitch) and is affixed to the deck of a differential-drive unmanned ground vehicle (UGV), the ClearPath Robotics Husky. We selected a differential-drive UGV to replicate the motion of a broad range of USVs, including those with differential drive and conventional rudder steering. Our platform is low-cost, modular, and open source, enabling rapid testing and benchmarking of UAV-USV landing strategies indoors before going onto the water which is high-risk and expensive. 

Block diagram of our proposed distributed model predictive control scheme.
Overview of the USV simulation testbed tilting platform for ground vehicles. Left: the platform is shown mounted on the deck of the ClearPath Robotics Husky with the Crazyflie 2.1 hovering above. Center: a closer view of the landing pad configuration and platform components. Right: Section view showing the ball-and-socket joint and the linkage mechanism.

Experimental Setup

Full system architecture.

We evaluate the proposed MPC scheme for UAV-USV cooperative landing in indoor experiments using the Crazyflie 2.1 and our tilting platform. These experiments represent a scaled-down version of real-world harsh wave conditions. Amplitude and frequency are informed by a survey of waves along the coastlines of the Great Lakes of North America. We select a spatially decaying sine wave whose amplitude decreases gradually with increasing x-position. We expect the Crazyflie and the simulated USV to cooperatively select the safest landing spatially and temporally by learning the GP and incorporating it the MPC scheme.

The UAV MPC is run off-board on a Thinkpad X1 Carbon with Intel Core i7-1270P Processor. The MPC runs at a frequency of 50Hz and transmits control inputs to the Crazyflie via long range Crazyradio USB. The Raspberry Pi onboard the Husky runs its MPC at 10Hz. The two vehicles communicate their goals using ROS topics on a local WiFi network and receive their own pose feedback at 240 Hz from a VICON motion capture system.

Experimental Results

Experiment 1. We define the tilt model. Our proposed distributed MPC scheme can locate a low-tilt landing location from all six initial platform positions.
Experiment 2. We learn the wave model using a GP. The MPC scheme can locate a low-tilt landing location from all three initial platform positions.

In the first set of experiments, we assume no uncertainty in the tilt model. We compare four MPC weighting strategies ranging from a purely cooperative strategy in red (where neither vehicle weights wave tilt in the MPC, neglecting spatial-temporal tilt motion), to our proposed strategy in blue (where both vehicles weight wave tilt highly in the MPC). In this set of experiments, our proposed approach (blue) reduces the tilt angle of the platform at landing by between 68-89% and results in a 53% increase in landing success rate over the purely cooperative strategy (red).

In the second set of experiments, we learn the wave tilt model as a GP. We compare the purely cooperative strategy in red from before to our proposed strategy in blue where we weight the posterior mean cost from the GP regression. In this set of experiments, our proposed approach (blue) reduces tilt angle of the platform at landing by 23-32% and results in a 47% increase in the landing success rate over pure cooperation (red).

Future Work

In the work presented above we assume that there is a local region with calm waves that can be reached by both vehicles to then perform a safe landing. However, in practical scenarios, spatial-temporal assumptions are not realistic if an emergency landing is necessary or if time and resources are constrained. Recently, we explored quadratic MPC strategies for landing a Crazyflie on the tilting platform in high frequency high amplitude conditions. In these strategies we include optimization costs that weigh position, attitude, and altitude errors between the multirotor and the platform.

Though all strategies successfully land in low-frequency, low-amplitude conditions, they have low success in the higher-amplitude conditions. Therefore, designing a safe controller that is robust to a wide range of wave amplitudes and frequencies is an ongoing research area.

Poster presented at the workshop ’25 Years of Aerial Robotics: Challenges and Opportunities’ at ICRA 2025.

Links

Debugging the Crazyflie using the debug-adapter kit gives you direct access to the STM32 with a hardware debugger. It makes it possible to pause execution, step through code, and inspect what’s happening in real time.

Even if you’re working mostly at a higher level, having this kind of visibility can be a huge time-saver when something unexpected happens. It’s a tool I use frequently when tracking down firmware issues or verifying low-level behavior.

We already have documentation for debugging the STM32 on the Crazyflie using ST-LINK and VS Code, but there are still a few missing pieces; like how to use J-Link, how to debug other platforms (like the Crazyflie 2.1 Brushless), and how enabling debug builds can help.

Debug Build

If you’re debugging and your breakpoints aren’t landing where you expect, or stepping seems unpredictable, or variables aren’t visible, it’s probably because the firmware was built with compiler optimizations enabled. When you select a debug build, the build system disables optimization by setting the compiler flag -O0, which preserves line-by-line correspondence between source code and machine instructions. This makes stepping and inspecting variables much more reliable.

To enable a debug build, you need to set the CONFIG_DEBUG option in your Kconfig configuration. You can do this, for example, by running make menuconfig in a terminal. Then navigate to Build and debug options and select Enable debug build. After changing Kconfig options, re-run make to rebuild the firmware with the new configuration.

Enabling debug build Kconfig option through menuconfig

VS Code Debug Configuration

With debug builds enabled, you’ll get more predictable stepping and reliable breakpoints. The next step is setting up your debugger. Below is a launch.json configuration for VS Code that supports both ST-Link and J-Link, and works with Crazyflie 2.x and the 2.1 Brushless.

If you’re using a J-Link probe, you’ll need to install the J-Link software from Segger.

{
    // VS Code debug launch configurations for Crazyflie 2.x and 2.1 Brushless using J-Link and ST-Link
    "version": "0.2.0",
    "configurations": [
        {
            // ST-LINK configuration for Crazyflie 2.x
            "name": "STLink CF21 Debug",
            "cwd": "${workspaceRoot}",
            "executable": "${workspaceRoot}/build/cf2.elf",
            "request": "launch",
            "type": "cortex-debug",
            "device": "STM32F405",
            "svdFile": "${workspaceRoot}/tools/debug/STM32F405.svd",
            "servertype": "openocd",
            "configFiles": ["interface/stlink-v2.cfg", "target/stm32f4x.cfg"],
            "runToEntryPoint": "main",
            "preLaunchCommands": [
                "set mem inaccessible-by-default off",
                "enable breakpoint",
                "monitor reset"
            ]
        },
        {
            // ST-LINK configuration for Crazyflie 2.1 Brushless
            "name": "STLink CF21BL Debug",
            "cwd": "${workspaceRoot}",
            "executable": "${workspaceRoot}/build/cf21bl.elf",
            "request": "launch",
            "type": "cortex-debug",
            "device": "STM32F405",
            "svdFile": "${workspaceRoot}/tools/debug/STM32F405.svd",
            "servertype": "openocd",
            "configFiles": ["interface/stlink-v2.cfg", "target/stm32f4x.cfg"],
            "runToEntryPoint": "main",
            "preLaunchCommands": [
                "set mem inaccessible-by-default off",
                "enable breakpoint",
                "monitor reset"
            ]
        },
        {
            // J-Link configuration for Crazyflie 2.x
            "name": "JLink CF2 Debug",
            "cwd": "${workspaceRoot}",
            "executable": "${workspaceRoot}/build/cf2.elf",
            "request": "launch",
            "type": "cortex-debug",
            "device": "STM32F405RG",
            "svdFile": "${workspaceRoot}/tools/debug/STM32F405.svd",
            "servertype": "jlink",
            "runToEntryPoint": "main",
            "preLaunchCommands": [
                "set mem inaccessible-by-default off",
                "enable breakpoint",
                "monitor reset"
            ]
        },
        {
            // J-Link configuration for Crazyflie 2.1 Brushless
            "name": "JLink CF21BL Debug",
            "cwd": "${workspaceRoot}",
            "executable": "${workspaceRoot}/build/cf21bl.elf",
            "request": "launch",
            "type": "cortex-debug",
            "device": "STM32F405RG",
            "svdFile": "${workspaceRoot}/tools/debug/STM32F405.svd",
            "servertype": "jlink",
            "runToEntryPoint": "main",
            "preLaunchCommands": [
                "set mem inaccessible-by-default off",
                "enable breakpoint",
                "monitor reset"
            ]
        },
    ]
}Code language: JSON / JSON with Comments (json)

To use this setup with a different platform, like the Flapper, just change the executable field to point to the correct .elf file. By default, starting a debug session in VS Code will erase and reflash the firmware, so make sure the firmware is built beforehand. If you need to attach to a running target without flashing, you’ll need to modify the launch.json to skip loading the binary.

Today’s blogpost comes from Joseph La Delfa, who is currently doing his Industrial Post-Doc with Bitcraze.

The Qi deck and the Brushless charging dock allow you to start charging a Crazyflie quickly, without having to fiddle with a plug or a battery change. But when you need to charge 10 or more Crazyflies 2.x and don’t want the weight penalty of the Qi deck then some some other solutions are needed.

This blog post is about a couple of chargers I made for the Crazyflie 2.x for my research prototypes. I research interaction design, which often means building something new and then putting in the hands of a user and getting them to try it out. What is important in these scenarios is that when there is unexpected behavior, they don’t think that the prototype is bugging out or broken. One way to prevent this is to make things that have a higher quality to raise the expectations of the user. This can help them stay immersed in the interaction and not look over to me when there is unexpected behavior and say… “is this working properly?”

Wiring Harness for Drone Chi

This charger is essentially a pair of JST 2-pin extensions for a 1S battery charger that I soldered together. Then weaved them through some fake hanging plants. With the drones already looking like flowers for the Drone Chi project, they blended well into the fake plants and all the wires were well hidden. When you wanted to fly, you would disconnect the battery from the wiring harness. Plus it brings the nice experience of picking a flower from a bush before you start flying!

Magnetic Mantle Piece Charger for How To Train Your Drone

This charger allows 10 Crazyflies to charge from their USB ports, but on a bit of a statement piece charger that lived in the lounge room of a group of friends who were participating in a month-long user study during the How to Train Your Drone Project. This charger contained a powered USB hub with cables running through each of the medusa like tubes rising from the base. What made this charger special was the magnetic USB charging adapters (available widely on e-bay, amazon, temu etc) that were plugged into the the USB ports on the drone. These allowed you to securely place the drone on the charger in one action as the magnetic cables integrated into the charger were always close enough to the drone when you set it down, giving you a satisfying * click * every time! They also gave off a eerie blue glow which I think matched the Crazyflie well.

At Bitcraze we like making decks! When we released the Crazyflie 2.0 we were really excited about the new deck connector we put on the platform. Using this, it was suddenly possible to expand the platform, adding new, more complex functionalities years after the initial release. It’s something that we really like about the Crazyflie. Over the years we’ve released a bunch of decks and also updated a few of them as new, improved sensors became available.

Deck limitations

As the number of decks has increased, along with their complexity, we’ve started noticing some limitations. We’ve touched on this a while back in this blog post where you can find more details, but here’s a short summary:

  • Resource sharing in the STM32F4: Mainly DMA conflicts between different peripherals, like conflict for DMA using DSHOT on the Crazyflie 2.1 Brushless and the WS2812b driver for the LED-ring.
  • Bus arbitration and performance: Some decks make excessive use of some buses, which can cause issues with certain combinations like the LPS deck and micro-SD card deck.
  • Deck combinations and pins: As more interesting decks are released and we’re able to carry more weight, users want to combine more decks. Although we try to be smart with pin allocation there’s a limit on how you can combine the decks.
  • MCUs on decks: As the complexity increases, separate MCUs are also included on the decks. Although working well for offloading the main MCUs on the Crazyflie, the complexity quickly increases both for usage and for development. This is something we’ve seen with the AI deck for instance, which contains 2 MCUs.

Like suggested in the original blog post, the solution will be to move more intelligence onto the decks by putting MCUs on them. These MCUs can then use the hardware on the decks without sharing resources with the main Crazyflie MCU and can also help process the data to make the communication with the Crazyflie more high level. But doing this will of course then make the issue raised in the last point even worse. To mitigate this we need to have better control over the decks. Our proposed solution for this is adding a new MCU to the decks which acts as a controller. Being able to switch on/off power, bootstrap and control various aspects of the deck. Some of the features include:

  • Uniquely identify the deck and also identify the model of deck – This will replace the current 1-wire memory subsystem we have today
  • Uses minimal amount of pins, basically only using the I2C pins
  • Use the MCU pins as GPIOs or as various peripherals:
    • GPIO expander enables switching on/off onboard power supply, simplifying bootloading and saving power. It also enables bootstrapping of onboard MCUs, which can be used for selecting what output pins to use for various functionality
    • Bridge I2C to SPI/UART for bootloading of onboard MCU. This would of course be much slower than using the SPI/UART but would simplify bootloading and not need pins allocated on the expansion connector
  • Bootstrapping the board makes it possible to to implement support for duplicate decks, for instance selecting what UART (1 or 2) on startup makes it possible for the driver on the Crazyflie to start up each deck and select what UART is used.

Our implementation

For our implementation of this system we have selected the STM32C011 MCU, as it comes in various packages from very small to slightly larger, so it’s possible to solder by hand. It also has the features we needed. To maximize the the flexibility of the system we don’t want to program in the I2C addresses in production. Even if you disregard the fact that the address might conflict with other I2C devices in the future, it will definitely conflict if you add two of the same type of deck.

To fix this we will automatically detect all the deck-ctrl MCUs on the bus and then move them to individual addresses. This will enable us to change the addresses in the future if there are collisions and also allows multiple instances of the same deck attached at the same time. Below is a video demonstrating this in action, implemented on custom development decks we’ve made for simplifying firmware development. Each deck runs the same deck-ctrl firmware and all 3 are auto-detected at startup.

The future

The current solution will be used on our next two upcoming decks: the new LED deck and the new Lighthouse deck. As we get closer to their release we will publish the firmware for the deck-ctrl MCU on GitHub. If the design turns out well we will keep rolling it out to future decks, increasing the flexibility of the deck expansion system even more.

You might already be familiar with the Crazyflie’s presence in numerous publications across various research fields. However, in this blog post, we’ll return to the basics and showcase some robotics concepts that can be taught using our platform.
The Crazyflie has already found its way into several classrooms such as the “Introduction to Robotics” in the Mechanical & Aerospace Engineering Department at Princeton University, the “Introduction to Control of Unmanned Aerial Vehicles” at UC Berkeley and the “Embedded control systems” at Chalmers University of Technology.
Whether you’re designing a robotics course for undergraduates or developing advanced labs for graduate students, here’s some fields where the Crazyflie can help your students grasp the fundamentals of modern robotics.

Basic Drone Principles

How does the quadcopter generate enough thrust? In which direction should the motors spin? How does the shape of a propeller affect performance?
As an introduction to drones and specifically quadcopters, students can explore these basic principles behind how they work. Then, by flying them, they can better understand the three axes of motion, roll, pitch and yaw and even find out their limitations, such as the ground effect.

Control Systems

What is the difference between controllers? How does a different controller tuning affect performance? How does an estimator work? What types of commands can be sent to a drone?
The Crazyflie platform offers a rich “playground” for exploring the stabilization process from sensor acquisition to motor control, that we often call “stabilizer module“. This includes a variety of controllers, estimators and commanders that can be modified to visualize results in the real world. Also, with the firmware being open-source and modular, it is relatively easy to build your own controller or estimator and integrate it to the platform.

Localization

How can a drone know its position and orientation in 3D space? What is the difference between a local and a global positioning system?
With a wide variety of deck sensors and positioning systems, students can find ways to control the Crazyflie through relative or absolute position/attitude commands. The different sensing methods used in these systems are also interesting to explore – for example, IR signals from the Lighthouse Positioning System, UWB radio from the Loco Positioning System, or optical flow data from the Flow deck v2.

Autonomous Navigation

How could the Crazyflie perform a collision-free trajectory? What is the most efficient way of flying from point A to point B?
In the field of autonomous navigation, the Crazyflie can be treated like any other moving robot by applying existing path planning algorithms or testing newly developed ones. Environment-aware problems are always exciting to work on and the Multi-ranger deck makes them feasible.

Swarm Robotics

What happens when you add another Crazyflie to your setup? How could multiple Crazyflies operate in a swarm? How could you make sure that they won’t collide? What is the difference between a centralized and a decentralized swarm?
Scaling a system up is always challenging but also fascinating. The examples provided in our Python library help you get a swarm in the air, but it’s up to you and your students to explore how the Crazyflies should coordinate and cooperate.

Small Drone, Big Educational Impact

The Crazyflie ecosystem is a fully capable robotics lab in the palm of your hand. Its flexibility, safety, and robust API support make it ideal for hands-on learning in a wide variety of robotics fields. Integrating the Crazyflie into a university robotics curriculum, gives students the chance to explore, test, fail, and fly their way to mastery.

Lately, at home and at work during my Fun Fridays, I have been trying to learn more about 3D CAD and more precisely about FreeCAD, mostly in the context of (ab)using our 3D printers :). Inspired by a couple of Crazyradio cases that have already been published, I started working on a Crazyradio 2.0 case since this has not yet been done, I am quite happy about the result:

The design is mostly press-fit: the top and bottom parts are pressed together and hold thanks to the 3D printed layers interlocking in each-other. The LED lens is pressed in the top and the button actually slides and is guided by the top. The button is flush with the case since it is mainly a bootloader button and is not required to be pressed during normal use.

ECAD/CAD design

One of my goal when starting with this project was to experiment working both with Electronic CAD (KiCAD in my case) and Mechanical CAD (FreeCAD). There is an extension for FreeCAD that allows to go back-and-forth between the two tools, but in this case it was much simpler since my board was already finished, so I only needed to get a model of it in FreeCAD.

To do so, I made sure all the important components had 3D models in the Crazyradio electronic design. I had to import a couple of models from Mouser, and had to re-create the RGB LED in FreeCAD. I then exported it as a STEP file. This file can be imported in FreeCAD and retain all the interesting shape and surfaces useful to work with the model:

Shape binder: Keeping it DRY

Coming from the software/electronic world, we have this notion of DRY: Do not Repeat Yourself. Ideally I would like to apply the same to mechanical design and avoid as much as possible to write any measurement by hand. one way to do that with FreeCAD is with Shape binder. A good example of its use is with the LED lens.

I wanted to put a translucent lens just on top of the Crazyradio LED. One way to achieve that is to create a Shape binder of the LED top surface onto the TOP and Lens. The LED top is the yellow square in the next picture and its presence allows to align perfectly the hole in the top cover to the middle of the LED on the PCB. This prevent all hazardous manual measurement when placing the hole.

For the lens design I can go one step further, I can create a shape binder both for the LED and for the hole in the top layer, this way the shape of the lens is derived from existing geometry and, to a large extent, does not have to be specified manually:

This allows to quite easily align the lens perfectly on top of the LED. The same principle is used for the button to get it to slide and press on the PCB switch with minimal play.

Final product

I pushed the current state of the case on GitHub. It is also available on Maker World. I plan on improving the design before deciding to name it 1.0 and to eventually upload it on Printables and Thingiverse.

If you want to learn more about FreeCAD, I can recommend this great video series on YouTube, it goes through a lot of very useful functionalities like the shape binders.

With the Swedish summer upon us, things are more calm at Bitcraze. The summer is usually a time for us to look a bit more at fixing infrastructure and other things that we do not have time to work on the rest of the year. One of the things I have been looking at improving lately is the state of our Python projects.

We currently use Python as the default language for everything we do on PC. This includes the Crazyflie lib, Client and other tools for our ecosystem. Over the years the state of the Python projects have greatly evolved. It started in ~2011 with almost no project management at all; just Python files. Then we switched to setup.py/pip support. Recently the Crazyflie client got the pyproject.toml treatment. Now that most Linux distributions prevent pip install-ing packages we need to juggle with venvs in order to use or develop in Python.

In essence, Python started with an easy to use language but has now become quite complex and hard to handle. Things become even more complicated when we take into account CI in Github actions that have to test that our projects actually work with all supported version of Python on Linux/Mac/Windows.

A bit of Rust (tooling) in our Python

As you might have gathered from our previous blog posts over the years, we like Rust quite a bit at Bitcraze and hope to use it more in our products moving forward. One of the great parts of Rust is the quality of the tooling and of the compiler feedback. Cargo as a project management tool helps a lot working with projects in a comfortable and repeatable way.

This is why we are now quite interested in using uv going forward as an official tool to work with the Bitcraze Python projects. uv can replace both venv and pip and makes working with a Python project as easy as working on a Rust one with cargo. It is also very fast and efficient since it is written in … Rust of course :-).

We are also looking at switching from Flake8 to Ruff and Ty for linting and type checking respectively. These two tools, from the same developer as uv are very fast and give very high quality error messages and warnings – this should make it much easier to maintain good code quality.

These changes would mostly be on our documentation and development side. The resulting projects are still compatible with pip and can still be used as they where used before. However we would make sure the projects can be efficiently used with uv.

Example

The Crazyflie client is currently already fully able to run with uv since it already uses the ‘new’ pyproject.toml project config file. So working with the project, from within the project folder, would look like that:

crazyflie-clients-python $ uv run cfclient    # Run the Crazyflie client GUI
(...)

crazyflie-clients-python $ uv run ruff check  # Check the code, runs in ~100ms!
All checks passed!

crazyflie-clients-python $ uv run pre-commit
      Built cfclient @ file:///(...)/crazyflie-clients-python
Uninstalled 1 package in 0.97ms
░░░░░░░░░░░░░░░░░░░░ [0/1] Installing wheels...
Installed 1 package in 4ms
[WARNING] Unstaged files detected.
[INFO] Stashing unstaged files to /home/arnaud/.cache/pre-commit/patch1750684440-121836.
flake8...............................................(no files to check)Skipped
[INFO] Restored changes from /home/arnaud/.cache/pre-commit/patch1750684440-121836.Code language: Bash (bash)

This last command is a great example of the usefulness of uv: currently one need to install pre-commit on a virtual environment, enter it, and run pre-commit in the project. With uv is just works out of the box. In the back of course, pre-commit is installed in a virtual environment in the project folder. But this is all done automatically.

Finally we will also easily be able to test multiple versions of Python:

crazyflie-clients-python $ uv run --python 3.10 pre-commit
Using CPython 3.10.18 interpreter at: /home/linuxbrew/.linuxbrew/opt/python@3.10/bin/python3.10
Removed virtual environment at: .venv
Creating virtual environment at: .venv
      Built cfclient @ file:///(...)/crazyflie-clients-python
    Updated https://github.com/bitcraze/crazyflie-lib-python.git (3a35d22026c2ed8251b821e4f5b10e67091f811f)
      Built cflib @ git+https://github.com/bitcraze/crazyflie-lib-python.git@3a35d22026c2ed8251b821e4f5b10e67091f811f
░░░░░░░░░░░░░░░░░░░░ [0/32] Installing wheels...                                                                                             warning: Failed to hardlink files; falling back to full copy. This may lead to degraded performance.
Installed 32 packages in 699ms
[WARNING] Unstaged files detected.
[INFO] Stashing unstaged files to /home/arnaud/.cache/pre-commit/patch1750684632-123219.
[INFO] Installing environment for https://github.com/PyCQA/flake8.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
flake8...............................................(no files to check)Skipped
[INFO] Restored changes from /home/arnaud/.cache/pre-commit/patch1750684632-123219.Code language: JavaScript (javascript)

And, for the end user, uvx also simplify running the client:

$ uvx cfclient   # Pulls and run cfclient from Pypi
(...)

$ uvx --from cfclient cfloader
      Built cfclient
Installed 22 packages in 164ms

==============================
 CrazyLoader Flash Utility
==============================

 Usage: /(...)/.cache/uv/archive-v0/_OZwn5_zGeTE-qFoK_kEG/bin/cfloader [CRTP options] <action> [parameters]

The CRTP options are described above

Crazyload option:
   info                    : Print the info of the bootloader and quit.
                             Will let the target in bootloader mode
   reset                   : Reset the device in firmware mode
   flash <file> [targets]  : flash the <img> binary file from the first
                             possible  page in flash and reset to firmware
                             mode.Code language: HTML, XML (xml)

This last command would likely be added to all our firmware Makefiles to be used when calling make cload to flash the Crazyflie.

Feedback?

This currently does seem like a good idea to us. If you have any feedback or ideas on how to handle Python projects in a better way we are very interested to hear them. Like I mentioned, the summer is kind of a ‘clean up’ time for us so this is when we have time to look at this kind of things.

Swarm robotics has undergone rapid evolution and is now used in real-world applications. At the center of this exciting journey is the Crazyflie. Although small, its capabilities make it ideal for swarming applications in research, education, and prototyping.

Small and Safe for Indoor Use

The Crazyflie 2.1+ is a nano-quadcopter that weights only 29g. Crazyflie swarms are safe to interact with and can fly in confined spaces like labs or classrooms. If the maximum recommended payload of 15g is not enough for your application, the Crazyflie 2.1 Brushless is a suitable alternative, as it has a recommended payload of 40g. These two platforms are compatible, allowing them to cooperate within a mixed swarm of your preference.

Setting Up a Crazyflie Swarm

Transitioning from a single Crazyflie to a swarm setup requires certain adjustments. Depending on the amount of data that you want to transfer to and from your Crazyflies, you might need to use more Crazyradio 2.0 dongles. We recommend 3-4 Crazyflies per radio but under ideal conditions each one can handle up to 15 drones. To get the most out of your swarm, you will also need an external positioning system. This could be a Lighthouse positioning system, a Loco positioning system or a Motion Capture system. This allows each Crazyflie to know its absolute position in space. A very interesting swarming project is Crazyswarm where they managed to fly a swarm of 49 Crazyflies using 3 Crazyradios and different positioning systems.

The Crazyswarm project. You can find the full video here.

Available Swarming Frameworks and Examples

To make your introduction to a Crazyflie swarm smoother, our python library contains a swarm class. It allows the user to control each drone in the swarm simultaneously, sending commands either in parallel or sequentially. The library also includes examples that demonstrate the capabilities of a Crazyflie swarm.
For users interested in exploring advanced decentralized swarm schemes, we support firmware that enables peer-to-peer communication. This is at an experimental level and has been used for the decentralized brushless swarm demo and for the ICRA 2025 demo.

Getting Started

Interested in building your first swarm? Explore our swarm bundles featuring multiple platforms and positioning systems that suit your research or development needs. If you are new to the Crazyflie ecosystem, make sure to follow the step-by-step swarm tutorial to better understand the setup process, communication flow, and control mechanisms involved in operating a drone swarm.