Autonomy Bootcamp

Autonomy Bootcamp

Overview

Before embarking on this subteam bootcamp, please ensure you’ve completed the instructions in https://uwarg-docs.atlassian.net/wiki/spaces/AD/pages/2241298916.

This bootcamp is in effect starting 2025-09-04. Please start this bootcamp if you haven’t already started https://uwarg-docs.atlassian.net/wiki/spaces/BOOT/pages/2299756545. Create a post in the #auto-bootcamps forum to get started! You can ask for help there and notify the Autonomy bootcamp reviewers when you are done.

Git Setup

  1. If you haven’t already, download and install Git: https://git-scm.com/

    1. Git should already be installed on Linux and MacOS.

  2. Windows (Linux and MacOS users skip step): Set line endings.

    1. Check: git config --get core.autocrlf

      1. If it is true or input , you do not need to set anything.

    2. Set: git config --global core.autocrlf [setting] .

      1. --global is optional, and replace [setting] with either true or input

      2. When in doubt, use input

    3. Additional information here: https://www.git-scm.com/book/en/v2/Customizing-Git-Git-Configuration#_core_autocrlf

  3. Configure Git with your name and email: https://linuxize.com/post/how-to-configure-git-username-and-email/

    • --global if you want to use it as the default, otherwise it will just be for the current repository.

    • You can use your anonymous GitHub no-reply email.

Forking & Cloning Repositories

  1. Fork the following repositories: https://github.com/UWARG/autonomy-bootcamp-2025-p1 & https://github.com/UWARG/autonomy-bootcamp-2025-p2

    1. Click on the Fork button (towards the top right, above the About section).

    2. Click on the green Create fork button.

  2. Make sure you have Python 3.11 https://www.python.org/downloads/

    1. When you run python --version or python3 --version, make sure you see that it’s Python 3.11.x (e.g. Python 3.11.9)

  3. Pick a parent directory to clone your forked repos. From now on, it is assumed you’re in the project root directory.

    1. Example: Cloning in C:\Users\Username\ will create a folder and then populate it: C:\Users\Username\computer-vision-python\[files]

    2. Run git clone [clone link] , where [clone link] is the link under the green Code button on the repository.

      1. This is the HTTPS link for Windows and MacOS users, or SSH link for Linux users.

  4. Create a venv (virtual environment) named venv: python -m venv venv.

    1. Do not use any name other than venv . Otherwise the linters and formatters will break.

  5. Run the setup script:

    1. Windows: run .\setup_project.ps1

    2. Linux/Mac: run source ./setup_project.sh

  6. You’re now ready to start the bootcamp!

Development

Every time you want to change code in your repository.

  • Ask questions if you need help!

  • In all of the files you need to edit, you will see the following pattern:

# ======================================================= # ↓ BOOTCAMPERS MODIFY BELOW THIS COMMENT ↓ # ======================================================= # ======================================================== # ↑ BOOTCAMPERS MODIFY ABOVE THIS COMMENT ↑ # ========================================================

There may be codes and comments outside of these areas that may be useful to read and understand!

  • Make sure the linter and formatter pass (run within project root):

Name

Command

Description

Passing Criteria

Name

Command

Description

Passing Criteria

Black

black .

Black is a formatter that edits the code layout (e.g. indentation, multiline).

Running Black is sufficient for it to pass.

Flake8

flake8 .

Flake8 is a linter that reports whether function and methods are correctly type annotated.

Flake8 outputting nothing is a pass.

Pylint

pylint

Pylint is a general linter.

Pylint outputting a code rating of 10.00/10 is a pass. It must be exactly 10.00 , so 9.99/10 is not passing.

Unit tests (run in project root):

Name

Command

Description

Passing Criteria

Name

Command

Description

Passing Criteria

Pytest

pytest -vv

Pytest is a testing framework for Python. It helps run tests and makes it easy to integrate them into CI pipelines.

Every single test needs to be a pass.

These will also be run automatically by GitHub Actions after you make a pull request, so reviewers will be able to see if your code passes them or not. No shortcuts!

  • Make a commit:

    1. Check which files have changed: git status

    2. Run: git add [files you changed] , where [files you changed] are the files you want to add to the commit.

      1. Use git add . if you want to add all of them (the dot means wildcard in Git).

    3. Run: git commit -m "Your commit message"

    4. When you’re ready to push your latest commits to GitHub: git push

      1. No harm in doing this after every commit.

Opening a PR (Pull Request)

When you’re finished the bootcamp, you will submit a pull request to the main bootcamp repository with your fork’s changes by clicking on the “Pull Requests” tab. You will submit all of your code changes, including any auto-generated testing logs.

  1. At the top, click Pull requests.

  2. To the right, click the green New pull request button.

  3. Under Compare changes, click the “compare across forks” link.

  4. The 3rd from the left: Click the head repository dropdown and select your repository (you can search for your username).

  5. The 4th from the left: Click the compare dropdown and select the branch you want to submit.

    1. Do not open more than 1 pull request! The branch you select should contain (or eventually contain) all tasks that will be reviewed.

    2. Once you have an open PR, you can keep updating the same branch as you get feedback. You do not need to open another PR.

  6. Click on the green Create pull request button.

  7. Once you have an open PR and are ready for review, go to your Discord bootcamp forum and send: @AUTO-Bootcamp Reviewer My bootcamp is ready for review: [link to your PR on GitHub]

  8. The Autonomy bootcamp reviewers will review your PR (a message will be sent on Discord, and the comments will be on GitHub).

  9. Read the feedback and go back to development. If any of the feedback is unclear or confusing, don’t hesitate to ask for clarification (make sure to send a message on Discord as well for visibility (e.g. I asked some questions as replies on GitHub ).

    1. Please do not click on Resolve conversation for any of the comments that the reviewers may leave! It causes confusion for the reviewers since they use the resolved/unresolved state to confirm that the code meets the quality to their satisfaction. Instead, you can use a reaction or reply to the comment for your own tracking purposes.

    2. If you are ready for another review, repeat step 8 (you do not need to open a new PR or send a new link).

  10. Once your review is fully complete and you’re done with the bootcamp, please follow the steps in https://uwarg-docs.atlassian.net/wiki/spaces/AD/pages/1697349760 to onboard to the team.

Phase 1

You are going to implement a colour-detecting algorithm that will detect either red or blue colours from an image. There is a general framework provided to guide you, and tests you must pass to complete the task.

Instructions and Requirements

The files to change are:

  • modules/detect_colours.py

Completion criteria: All tests pass in tests/test_detectcolours.py (all green).

Before starting, make sure to remove the following lines from your detect_colours.py for the linters to work:

# Bootcampers remove the following lines: # Allow linters and formatters to pass for bootcamp maintainers # pylint: disable=unused-argument,unused-variable,used-before-assignment

One way to detect colours is to convert images to HSV (Hue, Saturation and Value), which is a colour representation model. Hue refers to the actual colour type (such as red, blue, or green), represented as an angle on a colour wheel. Saturation measures the intensity or purity of the colour, with higher saturation meaning more vivid colours and lower saturation appearing more washed out. Value represents brightness, ranging from the full intensity of the colour. Detecting colours in the HSV space is often more effective than using the RGB model, since HSV separates colour information from brightness. By specifying a range of HSV values, computer vision systems can accurately identify and segment colours in images, even under varying lighting conditions.

Note that while HSV Hue angles vary from 0-360 degrees, in OpenCV this is from 0-180 degrees! Read https://docs.opencv.org/3.2.0/df/d9d/tutorial_py_colorspaces.html and https://medium.com/%40dijdomv01/a-beginners-guide-to-understand-the-color-models-rgb-and-hsv-244226e4b3e3 for more information and understanding.

Real HSV Hue (degrees, 0-360)

OpenCV HSV Hues (degrees, 0-180)

Real HSV Hue (degrees, 0-360)

OpenCV HSV Hues (degrees, 0-180)

0-60° (red)

0-30°

60-120° (yellow)

30-60°

120-180° (green)

60-90°

180-240° (cyan)

90-120°

240-300° (blue)

120-150°

300-360° (magenta)

150-180°

360° (red again)

180°

In detect_colours.py, there are two classes, DetectBlue and DetectRed, that require you to configure the HSV values required for detection.

Make sure to read the whole file to implement the required code; if you miss anything the tests will not pass!

Use quick_trial.py to see what your code outputs! This will give you a good idea on how the algorithm works, and show you what needs to be fixed for tests to pass. Feel free to edit the code in this file to best suit your needs of perfecting your code.

You can uncomment cv2.imwrite(str(output_path), res) to more clearly see how your colour detection is working, but if you no longer need the variable, don’t forget to delete it.

When you’re ready, run pytest or pytest tests/test_detectcolours.py in the project root and debug away! All 6 tests must pass to continue to the next phase.

Do not edit test_detectcolours.py, and do not change the name of the output folder generated by quick_trial.py.

Phase 2

You are going to work on creating and testing a multi-process project that represents our Autonomy system (a computer/laptop on the ground controlling a drone). The bootcamp is setup with many comments as helpful instructions and examples. This bootcamp is meant to help you get used to Autonomy’s code base and get familiar with what we do.

Scenario

You are building a ground station program (Note: You can replace ground station with airside compute throughout the docs, if you want to make it more consistent with what we have. It makes no difference) which is trying to control a drone. The drone and the ground station communicate using the MAVLink protocol, which is explained later. Your goal is to both monitor the status of this connection through HEARTBEAT messages, while controlling the drone to always face a certain object (like taking a cool video of something while flying around it). The flight path is pre-determined, so you will not need to control that, but you do need to adjust the drone’s orientation and altitude. This requires you to gather data about the drone’s position and orientation, do some calculations, and control the drone, all at the same time! Hence the need for our multi-processing framework. (More explanation and help if needed, maybe a diagram if we want, story?) Good luck!

Project Structure

  • documentation/: Contains examples of how multiprocessing works - highly recommend reading this first!

  • logs/: Auto-generated folder containing the test results and outputs.

  • modules/: All the code goes in here:

    • modules/common/: A submodule that contains some common code and utilities across WARG’s Autonomy projects. It contains a lot of stuff, you don’t need to look at all of it. In this bootcamp, the main usage is for the Logger class (https://github.com/UWARG/common/blob/main/modules/logger/logger.py ).

    • modules/command/: The Command worker will go in here.

    • modules/heartbeat/: The Heartbeat Sender and Heartbeat Receiver workers will go in here.

    • modules/telemetry/: The Telemetry worker will go in here.

  • tests/

    • tests/integration/: Integration tests. The tests you will run at the end to verify your project!

      • tests/integration/mock_drones/: Mock drones paired with the tests. You do not need to change these files, but you may look at them if you would like!

    • tests/unit/: Unit tests that can be run with pytest. You can make your own unit tests if you wish.

  • utilties/: Utility classes for managing processes and inter-process communication; this is done using queues.

  • bootcamp_main.py: Main file that puts all the processes together (the main process)

Instructions and Requirements

The files you need to change are:

  • modules/command/*.py

  • modules/heartbeat/*.py

  • modules/telemetry/*.py

  • tests/integration/test_*.py

  • bootcamp_main.py

It is suggested you complete them in the order given by the instructions. Implement the workers first, then test them, and then finally put them all together in bootcamp_main.py.

Worker Schematic and MAVLink

This is the diagram containing each worker type in a box. You will have to create and implement each of them. The drone is in the oval, and you do not need to implement that. To communicate with the drone, we use the MAVLink protocol (a set of rules on what to send and receive so the devices can understand each other). It is essentially just a massive enum indicating which numbers mean what message, so both communicating parties knows what’s going on. For example, HEARTBEAT (0) is a message you will be using. This message is just the number 0 (that’s what gets sent over in reality), but you will be working with the enum HEARTBEAT (although not exactly typed like that). The pymavlink library will define all these enums, as well as utility functions to send and receive these MAVLink messages.

Hint: the most useful ones would be <MAVLink message>_send() and recv_match()!

You may assume each worker can communicate with the drone by passing in a “connection” instance, as highlighted in a comment in bootcamp_main.py, and using this “connection” to send and receive MAVLink messages. This is to simplify your bootcamp code.

In reality, this will not work. Hence, your bootcamp_main.py won’t be able to run even after completion of the bootcamp! Instead, you will run some integration tests that test each worker individually, and bootcamp reviewers will review everything together after you are done.

Here are some MAVLink resources that you may find useful (Hint: some of the heartbeat stuff you’ll be able to find here!):

It is very highly recommended that you read the entire tutorial webpage to fully understand how to use pymavlink and how MAVLink messages work. You will need all of it!

Processes communicate with each other using queues. We provide a QueueProxyWrapper class that handles all the synchronization issues that come with multiprocessing, so you don’t have to worry about it! The way to use these queues is well documented in the examples in documentation/, so it is up to you to read it and understand it!

Not reading and understanding documentation is a common pitfall for developers! This bootcamp is meant to encourage you to read the documentation available to help you learn about our system and how to use the tools that you are given. (At least we have good documentation given to you like this guide and helpful links with examples, many other things in the real world have quite questionable documentation…)

(If you are curious about how this worker structure works, you can read more at Wikipedia: https://en.wikipedia.org/wiki/Kahn_process_networks )

General Worker Structure

See documentation/ for a good example of the general worker structure. Try playing around with it and running it with python -m documentation.main_multiprocess_example!

Workers should always have their logic written in a separate class, with a run() function that executes this logic. It will be used within a forever loop that lasts until the worker is requested to terminate. In general, a worker will take inputs from all of its input queues (arrows pointing into a worker), then do some processing on it (the class’s run() function), and then finally put it into all of its output queues (arrows pointing out of a worker). This sort of simulates a factory conveyor belt style workflow, if you want to think of it like that. Data flows between workers through queues (and not conveyor belts!), which are the blue arrows in the diagram. Each worker (or box in the diagram) will take in some data through the input queues (arrows pointing in towards the box), do some processing on it, then put it back in other queues (arrows pointing out of the box). It will continuously do this forever, until it is told to stop.

Helpful instructions are in both docstrings and comments to guide you as you work through the bootcamp!

BEFORE YOU START:

Please make sure that every single one of your workers has a Logger to log messages and outputs! There is a logger created and provided for you. Make sure to use it!

Please DO NOT modify any provided code. If there is provided code in a editable space, only edit the parts where you fill in your own arguments (i.e. if you see args, you can replace it with your own arguments. Otherwise, please don’t change stuff there.)

In this bootcamp, please make sure to log everything that the worker process outputs during integration tests. You can do this by logging whatever is received in the provided read_queue function if needed. This way, when you submit your logs, reviewers can see what your worker is outputting in main.log.

Heartbeat Sender Worker Requirements

This worker sends HEARTBEAT (0) messages to the drone. It will do so once per second. This is all it does.

Make sure to log any errors that occur using the given Logger class! This is good for yourself when debugging, and it also serves as a comment to help others!

You will edit the following files:

  • modules/heartbeat/heartbeat_sender.py

  • modules/heartbeat/heartbeat_sender_worker.py

  • tests/integration/test_heartbeat_sender.py

Test your worker using tests/integration/test_heartbeat_sender.py. Edit it to make sure it uses your heartbeat sender worker correctly. Then, you can run it by running python -m tests.integration.test_heartbeat_sender in the project root. This will create a new folder under logs/ with what your worker outputted! You can rename one of these to be logs/heartbeat_sender to indicate to bootcamp reviewers this is the run you want them to see.

Always print stuff using the Logger class! Otherwise it won’t get written to a file and you will fail the bootcamp.

If you are using a MacOS and see [Error] Connection refused or something similar when running your test, try opening a new terminal to run the mock drone manually before running the test. The mock drones are located in tests/integration/mock_drones. Again, you would still use python -m ….

Heartbeat Receiver Worker Requirements

This worker receives HEARTBEAT (0) messages from the drone. It expects to receive these messages once per second. Once the worker receives a HEARTBEAT message, it will consider itself to be "Connected" to the drone. If it misses a HEARTBEAT message, give a warning log. If it misses 5 HEARTBEAT messages in a row, it will now consider itself to be "Disconnected" to the drone. Every second, it will report to the main process a string with its current state ("Connected" or "Disconnected"). Again, make sure to log any errors and warnings (such as missed heartbeats)!

You will edit the following files:

  • modules/heartbeat/heartbeat_receiver.py

  • modules/heartbeat/heartbeat_receiver_worker.py

  • tests/integration/test_heartbeat_receiver.py

Test your worker using tests/integration/test_heartbeat_receiver.py. Edit it to make sure it uses your heartbeat receiver worker correctly. Then, you can run it by running python -m tests.integration.test_heartbeat_receiver in the project root. This will create a new folder under logs/ with what your worker outputted! You can rename one of these to be logs/heartbeat_receiver to indicate to bootcamp reviewers this is the run you want them to see.

Telemetry Worker Requirements

This worker receives both ATTITUDE (30) and LOCAL_POSITION_NED (32) messages from the drone.

Although technically LOCAL_POSITION_NED represents a position in the NED coordinate system, for simplicity of the bootcamp, assume it’s in the standard right-handed x-y-z coordinate system, where x is right, y is forward, and z is up. TelemetryData will be in this standard x-y-z coordinate system.

Once it receives both messages, it will return a TelemetryData object with the most recent data from both messages. Since the time_since_boot is shared between the two messages, it will be from the most recent of all messages. If it doesn’t receive both messages within 1 second, it will timeout and restart. The TelemetryData will need to be passed to the Command worker, as described in the worker diagram. Again, make sure to log any errors!

You will edit the following files:

  • modules/telemetry/telemetry.py

  • modules/telemetry/telemetry_worker.py

  • tests/integration/test_telemetry.py

Test your worker using tests/integration/test_telemetry.py. Edit it to make sure it uses your telemetry worker correctly. Then, you can run it by running python -m tests.integration.test_telemetry in the project root. This will create a new folder under logs/ with what your worker outputted! You can rename one of these to be logs/telemetry to indicate to bootcamp reviewers this is the run you want them to see.

Command Worker Requirements

For this worker, you will make decisions based upon the TelemetryData you obtain. Then, you will send COMMAND_LONG (76) messages to the drone to indicate you want to command it to take some action. The action that you take will be specified as a parameter to the COMMAND_LONG message. For this worker, the 2 possible actions you can take are:

  • MAV_CMD_CONDITION_CHANGE_ALT (113)

  • MAV_CMD_CONDITION_YAW (115) (This command has a turning speed, it doesn’t matter what you put for this bootcamp)

The goal of the Command worker is to ensure that the drone is always at the same height as the target and facing towards the target, while the drone moves around the target. The target is just a point in 3D space. If the drone is more than 0.5 meters off the target’s altitude, it will need to adjust its altitude using MAV_CMD_CONDITION_CHANGE_ALT (113). Then, it will send to the main process a string of the form "CHANGE ALTITUDE: {amount you changed altitude by in meters (delta z)}". If the drone is facing more than 5 degrees away from the target, it will need to adjust its yaw using MAV_CMD_CONDITION_YAW (115). It is required to use a relative angle adjustment. Then, it will send to the main process a string of the form "CHANGE YAW: {degrees you changed it by in the range [-180, 180]}".

In addition to that, the Command worker should also calculate the average velocity (velocity, not speed! It should be a 3d vector) of the trip so far, and log it. You can assume the given velocity is the average velocity for the entire time period between data reports, and that the data reports are sent perfectly periodically. Again, make sure to log any errors!

Yaw is the 2D angle in the x-y plane, it rotates about the z-axis. Counter-clockwise is positive as usual in a right-handed system.

You will edit the following files:

  • modules/command/command.py

  • modules/command/command_worker.py

  • tests/integration/test_command.py

To test your worker, you will edit the tests/integration/test_command.py to make sure it uses your command worker correctly. Then, you can run it by running python -m tests.integration.test_command in the project root. This will create a new folder under logs/ with what your worker outputted! You can rename one of these to be logs/command to indicate to bootcamp reviewers this is the run you want them to see.

Test Logs

As mentioned in the worker requirements, after you run the integration tests, change the log folder from a time (e.g. 2025-07-07_10-37-17) to the required name given for each worker. There should be 3 .log files in there: *_drone_#####.log, *_worker_#####.log, and main.log. You will submit all the logs as well as your code for reviewers to verify.

To know that your test succeeded, you can check the *_drone_#######.log to see if you can find a passed! message at the end. If your worker isn’t behaving as described, that is also where you can perhaps find information on what went wrong.

Additionally, the tests are set up for a graceful stop. Ending the tests by simply killing the process is not allowed.

Main thread

Hooray, you’re almost done! Just the easy part left. Once you are done with all your workers, you can fill in the bootcamp_main.py, which is the main thread that controls all the workers and orchestrates what’s happening. You can think of this as the “manager” of the factory. Make sure to go back to the worker schematic to understand how each worker relates to the drone and each other.

You will edit the following files:

  • bootcamp_main.py

There are many comments that tell you step-by-step what to do. The result should be very similar to the example main thread found in documentation/ (You really should have read that by now!). You do not need to test or run this script, as it will not work. Reviewers will manually review this code.

Submitting

Once you are done, you will run the linters and unit tests as per WARG Autonomy’s software development procedure. Then, you will open a PR for each phase.