CrazyS has one branch for each ROS and Gazebo version:
| Branch | Configuration | Versions | Build | Instructions |
|---|---|---|---|---|
dev/ros2-jazzy |
Ubuntu 24.04, ROS 2 Jazzy, Gazebo Harmonic | 8.x | this README | |
dev/ros-noetic |
Ubuntu 20.04, ROS Noetic, Gazebo 11 | 7.x | README on master |
|
dev/ros-melodic |
Ubuntu 18.04, ROS Melodic, Gazebo 9 | 6.x | README on master |
|
dev/gazebo9 |
Ubuntu 16.04, ROS Kinetic, Gazebo 9 | 5.x | README on master |
|
master |
Ubuntu 16.04, ROS Kinetic, Gazebo 7 | 4.x | README on master |
Each branch is built on every push and pull request to it, and the weekly check builds all of them every Monday. When you open a pull request, choose the branch of the version you changed as the base.
This branch (dev/ros2-jazzy) is the port of CrazyS to ROS 2 Jazzy and Gazebo Sim (Harmonic) on Ubuntu 24.04. CrazyS simulates the Crazyflie 2.x nano-quadcopter together with its flight controllers. The ROS 1 versions are on the other branches in the table above; their installation and usage instructions are in the README on master.
The ROS 1 version is built on the RotorS Gazebo classic plugins. Gazebo Sim already ships a port of the RotorS motor model (MulticopterMotorModel), so the ROS 2 version uses the systems of Gazebo Sim and ros_gz instead of custom plugins. The dynamics are the same: with the same motor commands the drone moves as in the ROS 1 simulator.
If you use CrazyS in your research, please cite the papers listed in the Publications page.
- Install ROS 2 Jazzy (
ros-jazzy-desktop) and the Gazebo Harmonic integration:
$ sudo apt install ros-jazzy-ros-gz ros-jazzy-actuator-msgs ros-jazzy-xacro ros-jazzy-joy \
python3-colcon-common-extensions python3-rosdep- Clone and build:
$ mkdir -p ~/crazys_ws/src && cd ~/crazys_ws/src
$ git clone -b dev/ros2-jazzy https://github.com/gsilano/CrazyS.git
$ cd ~/crazys_ws
$ rosdep install --from-paths src --ignore-src -y
$ colcon build
$ source install/setup.bashThe mav_comm repository is no longer needed: the two messages CrazyS used from it (DroneState and RollPitchYawrateThrustCrazyflie) are now in rotors_comm.
Every launch file accepts gui:=false to run Gazebo without the graphical interface.
| Command | What it does |
|---|---|
ros2 launch rotors_gazebo crazyflie2_hovering_example.launch.py |
Hovering 1 m above the ground with the controller of the Crazyflie firmware |
ros2 launch rotors_gazebo crazyflie2_hovering_example.launch.py enable_state_estimator:=true |
Same, with the attitude estimated from the IMU by the complementary filter of the firmware |
ros2 launch rotors_gazebo crazyflie2_internal_model_controller.launch.py |
Internal Model Controller following a quintic spline |
ros2 launch rotors_gazebo crazyflie2_lee_geometric_controller.launch.py trajectory:=semicircle |
Lee geometric controller; trajectories: hovering, linear, linear_polynomial, semicircle, semicircle_polynomial |
ros2 launch rotors_gazebo crazyflie2_swarm_hovering_example.launch.py |
Five drones with the Internal Model Controller |
ros2 launch rotors_gazebo crazyflie2_swarm_lee_geometric_controller.launch.py |
Three drones with the Lee controller |
ros2 launch rotors_gazebo crazyflie2_with_joy.launch.py |
Piloting with a joystick |
ros2 launch rotors_gazebo crazyflie2_without_controller.launch.py |
Only the drone: send the motor speeds yourself |
A waypoint can be sent to the firmware position controller with
$ ros2 run rotors_gazebo waypoint_publisher <x> <y> <z> <yaw_deg> --ros-args -r __ns:=/crazyflie2Note The firmware position controller holds the hover well, also in long flights: after take-off the altitude overshoots by 10-15 cm and settles on the reference within about 40 s. It does not track lateral waypoints well, though: its attitude loop is too slow, so the drone overshoots the target and its yaw drifts. This is a tuning problem of the original controller and happens in ROS 1 too. For trajectories use the Lee geometric controller or the Internal Model Controller.
To record a flight use ros2 bag record /crazyflie2/odometry /crazyflie2/command/motor_speed (the CSV logging of the ROS 1 version has been removed).
spawn_crazyflie.launch.py creates the drone in Gazebo from rotors_description/urdf/crazyflie2.urdf.xacro and starts a ros_gz_bridge for its topics. Everything lives in the namespace of the drone (crazyflie2 by default):
| Topic | Type | Direction |
|---|---|---|
odometry |
nav_msgs/Odometry |
Gazebo → ROS, 1 kHz, ideal (pose in the world frame, twist in the body frame) |
imu |
sensor_msgs/Imu |
Gazebo → ROS, 500 Hz, ideal or with the MPU-9250 noise (noisy_imu:=true) |
joint_states |
sensor_msgs/JointState |
Gazebo → ROS, propeller joints |
command/motor_speed |
actuator_msgs/Actuators |
ROS → Gazebo, propeller speeds [rad/s] in the velocity field |
command/trajectory |
trajectory_msgs/MultiDOFJointTrajectory |
waypoint for the firmware position controller |
drone_state |
rotors_comm/DroneState |
reference for the Internal Model Controller and the Lee controller |
The controllers are in rotors_control. The control library does not depend on ROS, and the nodes only convert messages. The gains are in rotors_gazebo/config, written as ROS 2 parameter files.
The propeller model has two sets of coefficients: the default CrazyS ones and the ones identified by J. Forster (ETH Zurich, 2015), selected with forster_parameters:=true. The Lee controller was tuned with Forster's coefficients, so its launch files select them.
- Only the Crazyflie is ported. The other RotorS vehicles, the fixed-wing aircraft, the VI sensor, the octomap example,
rotors_evaluationandrqt_rotorsare not part of this branch. - The Mellinger controller is not ported: in ROS 1 it was a draft that never worked.
- State estimator mode: in ROS 1 the ideal odometry overwrote the attitude estimated from the IMU, and in Noetic the drone flipped after a few seconds. Here only position and linear velocity come from the odometry, and the gyroscope bias is estimated on the ground before take-off, as the firmware does.
- Position controller: fixed the lower saturation of the thrust correction (
+1718instead of-1718). - Position controller without state estimator: the integrators now use the real time between two odometry messages (1 ms) instead of 10 ms. With the old value every integral gain was ten times too large, the altitude oscillated after take-off and in long flights the drone drifted away and crashed. The same fix is in the ROS 1 branches.
- Lee controller: the control step is triggered by the time stamps of the odometry, and the dirty derivatives use the real time between samples. Under load (e.g. in the swarm), the ROS 2 timers drifted by up to ±20% with respect to the simulation time, and the fourth order derivatives made the drone unstable.
waypoint_publisher_filesends the waypoints one at a time (in ROS 1 they were sent in a single message, but the controller only used the first one).
CrazyS was developed by Giuseppe Silano, Emanuele Aucone and Luigi Iannelli, with contributions by Benjamin Rodriguez, Ria Sonecha and Daniele Morra. The Lee geometric controller was developed by Stefano Grossi in his MSc thesis at the University of Modena and Reggio Emilia (2021), supervised by Lorenzo Sabattini, Beatrice Capelli and Federico Pratissoli.
Note The ROS 2 port was carried out with the help of Claude, an AI model by Anthropic, used through Claude Code. The commits written this way list Claude as author or co-author. Every example in the table above was run in Gazebo Harmonic and compared with the ROS 1 version, but the port has not been tested on a real Crazyflie. If something does not work as expected, please open an issue.
Apache License 2.0, see LICENSE.