4. Creating your own Starling Project¶
This tutorial takes you through using the Starling templates repository to generate your own custom Starling application.
4.1 Prerequisites¶
In order to complete this tutorial, you will need to install the following. You may have already installed these during Getting Started.
The template generation uses the cookiecutter
tool for generating custom projects from a template.
To install it, run the following:
python3 -m pip install --user cookiecutter
# or
easy_install --user cookiecutter
See Cookiecutter Installation for details for different platforms.
This will give you access to the cookiecutter
command line interface, which is used in this 4.3 section.
We also recommend you sign up for Docker Hub and Github as both are necessary if you wish to fly your controller in the real world.
4.2 Project Structure Planning¶
Before diving in to creating your project, you first need to decide on the structure of the project. The structure is determined by the application, and the functionality is split between the central server and each vehicle. Let's review our task for this tutorial:
In this scene, a number of drones take off and automatically fly to starting points equidistant around a circle of a given radius. They then start circling around the edge of the circle attempting to stay equidistant to their neighbours. It is determined that the vehicles have not been well tuned and can end up lagging, so a centralised server monitors all the vehicles and notifies them if they are lagging behind.
A Starling Project is comprised of one or more ROS Nodes which each encompass one piece of functionality. In this project we can identify the following requirements:
- A node onboard the vehicle which can arm, takeoff, land and fly it in a circle of radius r from a given start location in a safe manner.
- A node offboard on the central server which receives vehicle locations, finds the ideal locations and then sends that information back to the vehicles.
We can see that we may also need to provide a set of custom messages for the communication of specific information between server and vehicle.
Therefore, we will need this Starling Project to contain the source code for an onboard node, an offboard node and a set of custom messages.
An important task is then to name these beforehand as we need a way to refer to them in the next step. We've used some example names below, but please try to give yours better names :)
- Offboard node:
template_python_node
- Onboard node:
template_cpp_node
- Custom msgs:
template_msgs
4.3 Generating the base Starling Project¶
The first step is to build your own Starling project. The following will start the process of generating the base Starling project. In your workspace, run the following command.
cookiecutter https://github.com/StarlingUAS/starling_controller_templates.git --directory starling_template
You will then be prompted to fill in some details. The value in the angle brackets indicate the default value if you choose not to enter anything. Press Enter to go on to the next one. The inputs include the following.
Name | Description |
---|---|
Full Name | Your name used for documentaiton |
Your email address used for documentation | |
Short Description | A short description of your project added to the documentation and project files |
Project Name | The name you have given to this Starling Project, make sure that you are happy with this as changing the name after fact is a bit of a pain. |
Github Username | (Optional) Your Github username for filling in the README and metadata |
Docker Username | (Optional) Your Docker Hub username which is used for naming the Starling image. Will be used to upload to if you wish to push it online. |
Docker Image Name | The name of the Starling image that this repository produces |
Docker Image Name Full | An autogenerated name based on your username and image name, you can leave as the default unless you really want to change it. By default it is <Docker Username>/<Docker Image Name> |
Onboard ROS2 Package Name | The name of the onboard controller which this containers Dockerfile will run in onboard mode. |
Offboard ROS2 Package Name | The name of the offboard controller which this containers Dockerfile will run in offboard mode. |
Note: The last two entries should correspond to the node names you came up with in the planning phase.
Note: The last two entries are for automatically populating the
run.sh
script. Therun.sh
script is the default script your project's Docker container runs on startup. You can leave these two as defaults and edit therun.sh
script later.
Once complete, the project will be generated into a directory named as project_name
. For example, the default project with name starling_controller
produces a project with the following structure:
starling_controller
|-- buildtools
|-- docker-bake.hcl
|-- deployment
|-- docker-compose.yml
|-- kubernetes.yaml
|-- starling_controller
|-- run.sh
|-- Dockerfile
|-- LICENSE
|-- Makefile
|-- README.md
We have the following folders and files.
- starling_controller will be populated by user-created ros packages. Anything in this folder is directly copied to the Dockerfile and built.
- deployment contains a sample
docker-compose.yml
file which runs a default simulation stack, and a sample Kubernetes file for deployment, both will need to be edited to run properly. - buildtools contains the specification that Docker uses to build the container. It contains the naming for the Docker image.
- Dockerfile specifies the build steps for this project. It already specifies the installation of a number of dependencies, including the libInterpolate interpolation library.
Once generated, the Makefile can be used to build and run commands:
cd <Your Application Name> # Go into the your new Starling application directory
make # Will build the project
make run # Will build and run the container
make run_bash # Will build and run the container, putting you in a bash shell inside it.
make help # Shows the help screen
This should successfuly build your project container which you can try and run or inspect. Currently it has no functionality so nothing can happen. Have a look inside the container using make run_bash
.
Note On Windows you can either use WSL to run the
make
commands. Alternatively, check out this link for other solutions.
4.4 Adding Nodes to your project¶
The generated project has no functionality right now. This repository contains other templates which will generate rosnodes for you. In particular
- cpp_ros2_node_onboard_template: Generates a ROS2 node, designed for running onboard the vehicle written in CPP.
- python_ros2_node_offboard_template: Generates a ROS2 node, designed for running offboard (central server) written in Python
- ros2_msgs_template: Generates a ROS2 msgs package which can be used by any ROS2 package within this container.
These nodes can be added to your project using the following cookiecutter
commands. Note that the packages should be generated in the starling_project_name
directory of the base Starling project. Each of these commands are single line commands.
Navigate into <your project name>/<your project name>
e.g. starling_controller/starling_controller
(by default this should only contain the file run.sh
and run the following)
# CPP Onboard
cookiecutter https://github.com/StarlingUAS/starling_controller_templates.git --directory cpp_ros2_node_onboard_template
# Python Offboard
cookiecutter https://github.com/StarlingUAS/starling_controller_templates.git --directory python_ros2_node_offboard_template
# Messages
cookiecutter https://github.com/StarlingUAS/starling_controller_templates.git --directory ros2_msgs_template
Note the
--directory
argument pointscookiecutter
to the correct template; the-o
argument specifies the output directory, which in our case should be inside the created Starling project.
Similar to the base project generation, these commands will ask you a number of questions during the generation. Most importantly, it will ask what the package_name
is which will become the name of that particular node package.
Name | Default | Description |
---|---|---|
full_name | starling_user | Your name used for documentation |
starling.user@starling.co.uk | Your email address used for documentation | |
year | 2022 | The year of creation for documentation |
package_name | template_node | The name of this ROS2 Node, make sure this is correct and note it down. It should match the ones given in the initial Starling setup |
short_description | ROS2 node template | A short description of the functionality of this node for documentation and project files |
custom_ros2_msgs_name | template_msgs | Important! The name of the custom msgs package name you added as part of this Starling project. The name must match exactly otherwise the default functionality will fail! |
Note convention for msg packages is to have a package name of format
<mymessages>_msgs
, e.g.circle_experiment_msgs
.Note For those familiar with ROS1, it is ROS2 convention to keep messages in a separate package to the ros nodes themselves.
This should give you a file tree that looks something like the following (of course with your package names instead)
starling_controller
|-- buildtools
|-- docker-bake.hcl
|-- deployment
|-- docker-compose.yml
|-- kubernetes.yaml
|-- starling_controller
|-- template_onboard_controller
|-- ...
|-- template_offboard_controller
|-- ...
|-- template_msgs
|-- ...
|-- run.sh
|-- Dockerfile
|-- LICENSE
|-- Makefile
|-- README.md
The nodes can be run standalone for your own projects, one at a time, or whenever you need a new node within your project.
Once these packages have been placed within the correct directory inside the Starling project, you can simply run make
to check that they successfully build.
4.5 What is in the templates¶
The set of node templates above should create you a project which runs the example scenario specifically developed for the purpose of this tutorial. This example has been designed to show the development of an onboard and an offboard container, as well as demonstrate communication between the two containers. The scenario is as follows:
- We have \(n\) drones which we would like to fly equidistant around a circle of fixed radius at a initial velocity.
- A central server will send each drone an id \(i<n\) to determine its start location around the circle.
- Once received the drones will start flying around the circle and send its current position to the server.
- The server collates the drones information to determine if any of the drones are lagging or ahead of where they should be. This ideal position is sent back to each drone.
- The drone adjusts its velocity to try and match with the ideal.
We can now quickly run through what is in each of the ros node templates.
Note: More detail about the actual functionality within these nodes will be in this tutorial section
4.5.1 cpp_ros2_node_onboard_template
¶
|-- CMakeLists.txt
|-- include
| |-- controller.hpp
| |-- main.hpp
| `-- state.hpp
|-- launch
| `-- template_cpp_node.launch.xml
|-- package.xml
`-- src
|-- controller.cpp
`-- main.cpp
This node runs onboard the vehicle and interfaces with MAVROS. It contains a state machine with functionality to arm, takeoff, land, loiter and takes care of safety functionality.
CMakeLists.txt
: contains the instructions to build this rosnode. This includes specifying dependencies and extra libraries (e.g. messages such asgeometry_msgs
or external dependencies). It also specifies the name of the binary containing all of your functionality. By default this iscontroller
.include
andsrc
: In CPP, your code files are split into header files (specifying object definitions) and source files (specifying object functionality), these are stored herelaunch
: contains a ROSlaunch
file. We use XML notation to describe how your rosnode gets launched, including extra parameters or other changes you want to make at runtime instead of buildtime. This is what gets run to run your rosnodepackage.xml
: ROS2 Metadata file specifying ros2 dependencies of your project.
As a brief overview of the code files:
main.hpp
andmain.cpp
: Contains the program entrypoint function and the core of the ros node. It contains all of the core functionality to fly a vehicle as well as the state machine. It includes the user controller specified incontroller.hpp
to be expected to run during theexecution
phase of the state machine.controller.hpp
andcontroller.cpp
: For the majority of simple applications, a user should only need to provide their own version of these files. The controller contains an initialisation and loop function which a user can fill in.state.hpp
: A header only file containing the states of the state machine.
4.5.2 python_ros2_node_offboard_template
¶
|-- package.xml
|-- resource
| `-- template_python_node
|-- setup.cfg
|-- setup.py
|-- template_python_node
| |-- __init__.py
| `-- main.py
`-- test
|-- test_copyright.py
|-- test_flake8.py
`-- test_pep257.py
This node runs offboard on the central server. It is designed to run on its own with no external dependency on anything outside of the application.
package.xml
: ROS2 Metadata file specifying ros2 dependencies of your project.resource
: A Python ros package special folder, do not touchsetup.cfg
: Configuration file specifying where key resources aresetup.py
: The Python equivalent ofCMakeLists.txt
and contains the instructions to build this rosnode. It specifies which resources are copied over and available to the rosnode at runtime. Also specifies the name of the binary, and which function it is intended to run. By default this iscontroller
<your rosnode name>
e.g.template_python_node
: The source directory for your python files.test
: A number of testing utilities which can be run with pytest. Currently not used.
A brief overview of code files:
main.py
: Contains a ros node which uses a timer to repeat poll the current state of vehicles on the network at given intervals. It then performs the calculation of ideal vehicle location and sends that to the vehicles.
4.5.3 ros2_msgs_template
¶
|-- CMakeLists.txt
|-- msg
| |-- NotifyVehicles.msg
| `-- TargetAngle.msg
`-- package.xml
This node is purely for specifying and building the custom ros messages in our application. Any application which uses these messages need a compiled version of this node.
CMakeLists.txt
: contains the instructions to build the messages. Any extra messages or services need to be added to the CMakeLists.msg
: A list of specified custom messages.package.xml
: ROS2 Metadata file specifying ros2 dependencies of your project.
4.5.4 Dockerfile¶
As mentioned in the previous tutorial, a Dockerfile is used as a recipe to build your controller Docker container.
The Dockerfile in this template contains the command line instructions to build your controller.
By default, it will install a number of useful libraries for compatibility.
If you need any new libraries, you will have to add their installation here!
It then essentially copies in all of the rosnodes specified within the project name directory and runs them through the standard ROS2 build tool named colcon
.
It also copies over the run.sh
file which the Dockerfile will run on container startup. Therefore the run.sh
should have the instructions for running your applications.
Thankfully, you do not need to run docker build
automatically as we have set up a special build system which is wrapped up inside the Makefile
.
4.6 Running your new project¶
With your project now constructed, you can now re-run your container with the same make
commands as earlier.
cd <Your Application Name> # Go into the your new Starling application directory
make run # Will build and run the container
This will build a container called
<Docker Username>/<Docker Image Name>
with tag latest e.g.myname/starling_template:latest
This will start the onboard controller by default, but it will start complaining that it hasn't received any state or position messages for initialisation. This is normal for now!
If you want to start the offboard controller, you can add the extra option ENV="-e OFFBOARD=true"
to the make command like so make run ENV="-e OFFBOARD=true"
. It will then start trying to identify the number of vehicles on the network, but of course it cannot find any!
You can have a look inside both containers using make run_bash
.
4.7 Initialising Git¶
Optionally, at this point you can set up version control on your project in order to save your progress. To initialise git
and create your first commit, go to the root of your project and run:
git init
git add -A
git commit -m "Initial Commit"
If you want to push this code onto github, you can follow this tutorial. In short, create an empty Github repository of the same name in your Github account, and change the remote locally:
git remote add origin <remote repository URL>
git remote -v
git push origin master
4.8 Next Steps¶
Congratulations, you now have your own Starling application! It doesn't have any functionality yet but before we get to adding some, it's important to understand how you run the simulation for you to test your controller against!