This repository contains a number of template repositories for building up your own custom Starling compatible controller. This is designed in a modular way which allows you to mix and match different elements as you need.
The idea is that a base Starling project is generated, but it contains no ROS nodes. Then you add whatever cpp/python or message custom ros packages afterwards.
This repository is used within the tutorial: https://starlinguas.github.io/starling_controller_templates/
For more detailed explanation of usage, please go to section 4 of the tutorial
The template generation uses the cookiecutter
tool for generating custom projects from a template.
Then run:
python3 -m pip install --user cookiecutter
# or
easy_install --user cookiecutter
See cookiecutter installation for further details on different platforms.
This will give you access to the cookiecutter
command line interface.
Other than that you will need the Prerequisites for Starling
Both are optional but suggested to make the most of the system.
The first step is to build your own Starling project. The following will start the process of using the template to generate a staring project. In your workspace, run the following command.
cookiecutter https://github.com/StarlingUAS/starling_controller_templates.git --directory starling_template
It will follow by asking you to fill in a number of details. The value in the square brackets indicates the default value if you choose not to enter anything. Press Enter to go on to the next one. The inputs include
- Full Name - Your name
- Project Name - The name you have given to this Starling Project
- Github Username - Optionally your github username for filling in the README and metadata
- Docker Username - Optionally 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.
Once complete, the project will be generated into a folder named by your 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 file which runs a default simulation stack, and a sample kubernetes file for deployment, both will most definitely need to be edited.
- buildtools contains the specification that docker uses to build the container. It contains the naming for the docker image.
- Dockerfile contains the dockerfile which specifies the build steps for this project. It already specifies the installation of a number of dependencies, including libInterpolate interpolation library.
Once generated, the Makefile can be used to build and run commands:
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 funcionality so nothing can happen. Have a look inside the container using make run_bash
.
Note On windows you can either use WSL or have a look at some of the solutions in this link
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 into 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
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
Similar to the generating of the base project, these will ask a number of questions to you during the generation. In particular it will ask what the package_name
is which will become the name of that particular node package.
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 seperate package to the ros nodes themselves.
Note See below for the default example functionality that these template provide.
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_cpp
|-- ...
|-- template_offboard_controller_python
|-- ...
|-- template_msgs
|-- ...
|-- run.sh
|-- Dockerfile
|-- LICENSE
|-- Makefile
|-- README.md
That being said, 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 are successfully built.
By default, the templates are used to solve the following problem.
You have been asked to prototype a particular scene within a drone display!
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, therefore a centralised server monitors all the vehicles and notifies them if they are lagging behind.
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 demonstratee communication between the two. 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 informations 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 based on the error to match with the ideal.
With this in mind we can 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
|-- 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.
|-- 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 dependence on anything outside of the application.
package.xml
: ROS2 Metadata file specifying ros2 dependencies of your project.resource
: A python ros package special folder, no need to 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.
As 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.
|-- 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.
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
as we have setup an automatic special build system which is wrapped up inside the Makefile
.
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 build 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"
. Where it will 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 container using make run_bash
.
Optionally, at this point you can start version controller on your project in order to save your progress. To initalise 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, then change the remote locally:
git remote add origin <remote repository URL>
git remote -v
git push origin master
This project is covered under the MIT License.