Useful links and other resources

Here is a collection of useful links that might help you develop better code and give some hints when stuck:

How to Git - intro

Git is a useful versioning tool we use during the whole course. There are some useful patterns to know before you start the robot project to avoid pitfalls and make everyone’s life a little bit easier.

Overall I suggest ot use an IDE when working with Git, not the command line interface. Visuals provided by and IDE makes navigating in the project a lot simpler. Command examples below are provided as commands, but they are identically named inside an IDE.

How to Git - basics

Git consists of the following elements:

  • Repository - A Git repository can be identified by the .git folder in the root of a directory. It tracks the changes of all files done in the project. These changes are contained inside branches. Branches can diverge and contain changes different changes to the same file. A Git repository contains a local and remote component. Can be created using

    git init

    or when using a remote repository

    git clone git@github.com:ut-robotics/picr-docs.git

    the status of a repository can be checked using

    # Get status
    git status
    #On branch ....
    #Your branch is up to date with 'origin/...'.
    
    #Get git history log
    git log
    # commit b8cbbff7c8f260a18373e2cc121708d3a5b8a21d (HEAD -> ..., origin/...)
    #Author: ...
  • Local Repository - part of the Repository that resides in your file system. It can be synced with the remote Repository.

  • Remote Repository - part of the Repository that resides off-site in another server. Local changes can be pushed to and remote changes can be pulled from a Remote Repository. Useful commands when working with a remote repository:

    git pull # pulls remote changes from a repository
    git fetch # downloads objects and refs from a repository
    git push # updates objects and refs in a remote repository
  • Commit - a snapshot of all staged changes in the working directory. Staged changes are everything that is to be commited. Unless pushed to a remote repository a commit present only locally. Every commit must include a meaning ful message explaining the changes done.

    # we add all changed files and commit the changes
    git add -A && git commit -m "A reasonable and meaningful message"
    
    # git push REMOTE-NAME BRANCH-NAME
    git push origin master # pushes the changes to the 'master' branch in 'origin'

    Sometimes there is a need to remove files from a repository. This can be done so:

    git rm bad_file
    git commit -m "Remove bad_file"
    git push
  • Git ignore - some files, like local configuration files or files containing secrets should not be commited. To enforce this it is possible to define a .gitignore file that makes said objects invisible to git. In our course we have multiple folders with different contents. It is good to have separate .gitignore files to limit the scope of changes to said folders.

    echo venv/ >> software/.gitignore # ignores the contents of software/venv
    echo *.log >> .software/gitignore # ignores all files with the *.log suffix
    
    # The file can be edited with any text based editor or IDE tools
  • Branching - allows you to create separate lines of development in your repository. Branches have an origin point and can be later joined back to the main branch. Branches, like commit messages, must have meaningful names.

    # Creating a branch. Remember to be in the branch you want to merge into later (eg. software)
    git branch follow_ball
    
    # Switch to a branch
    git checkout follow_ball
    
    # Create and switch to a branch
    git checkout -b follow_ball
  • Reverting to a previous commit - all commits are assigned unique ID-s. If there is a need to roll-back changes, this can be done using the checkout command. Some examples follow:

    # Checkout a previous commit
    git checkout a0f81fkaq # the ID hash of a commit
    
    # If you want to delete all local changes with the rollback
    git reset --hard a0f81fkaq
    
    # If you want to keep current changes safe you can stash them
    git stash
    git reset --hard a0f81fkaq
    git stash pop
    # At this point a merge conflict is possible
    
    # If you want to restore a file to a previous commit
    git restore --source=a0f81fkaq -- software/important_code.py
  • Merge conflicts - happen when Git can’t automatically patch changes from two sources. This usually happens when two branches edit the same file or when the source of a patch has changed. They need to be resolved manually to decide what state of the change is correct. Usually an IDE provides tools to resolve them.

Basics can be read up on here: https://git-scm.com/doc or utilize some good old search-engine-fu.

How to Git - Feature branches

We prefer to use a version of feature branches (https://www.atlassian.com/git/tutorials/comparing-workflows/feature-branch-workflow). This Git versioning workflow utilizes feature branches from the main branch where development takes place. Once a feature is complete, it can be merged back to the branch.

This approach allows to encapsulate changes, isolating them from each other. In this way the scope of a merge request is limited and makes reviewing them easier.

TL;DR: Git feature branch advantages and disadvantages:

+ Positives +

  • Scope of changes is encapsulated

  • Main branch is not poisoned by broken development commits

  • Simpler to review merge requests due to smaller scope

  • Easy to see what is currently in development by branches

  • If combined with Git rebasing keeps the main branch Git history clean from development commits

- Negatives -

  • Multiple feature branches on the same code can get messy

  • Some management overhead

A visual example how development would look like:

software git flow chart

How to Git - Advanced stuff

When doing more specific things with Git there are some interesting concepts:

  • Differences between merging and rebasing

    Merge: Combines two branches by creating a new “merge commit” that joins their histories. The history stays branched and shows all the separate work.

    Rebase: Moves (or “replays”) your changes from one branch on top of another branch, rewriting history to make it look like all work happened in a straight line.

    Both actions result in the same end set of Objects. The difference is in the Git history. Merging keeps all branch history and shows the merge point. Rebasing creates a cleaner, linear history by “reapplying” commits.

Pseudocode examples and ideas

Here you can find some simple algorithms that might help you in writing code for your robot.

Finding interesting objects

Here is some pseudocode that describes a way to find objects with segmented data and how one could filter out noise or invalid objects.

# We take 2 arguments:
# segmented_frame: only our interested color segmented out data
# unfiltered_segmented_frame: contains all colors segmented out data
def find_objects(segmented_frame, unfiltered_segmented_frame):
    # Filter out noise and transform raw segmented frame
    segmented_frame = morphology(segmented_frame)
    # Find contours from processed segmented frame
    contours = find_contours(segmented_frame)

    found_objects = []

    for contour in contours:
        if contour.size < MIN_OBJECT_SIZE:
            continue

        obj_x, obj_y = contour.get_centre_coordinates()

        obj_size = contour.get_size()

        if obj_size < MINIMUM_SIZE:
            # Object is smaller than minimum threshold
            continue

        # we convert the pixel values to a normalized range between 0.0 and 1.0
        normalized_x = normalize(obj_x, 0, FRAME_SIZE_X)
        normalized_y = normalize(obj_y, 0, FRAME_SIZE_Y)

        # Different filters to sort out invalid objects
        if normalized_y < 0.05:
            # Object in the top 5% of the frame. Implausible to be of interest
            continue

        # ADVANCED FILTERING START

        # Analyze what pixels are between the object and center of the frame
        # Find the color sequence of pixels between 2 points in the frame
        line_sequence = get_line_color_sequence(
            unfiltered_segmented_frame,
            FRAME_SIZE_X/2,
            FRAME_SIZE_Y,
            obj_x,
            obj_y)

        if FORBIDDEN_SEQUENCE in line_sequence:
            # We found a forbidden sequence in the line data. Object most likely behind a line
            continue

        if invalid_pixel_percentabe_below(obj_x, obj_y) > INVALID_PIXEL_THRESHOlD:
            # We found too many wrong/non-orange pixels below the object. It most likely is not on the playing area
            continue

        # ADVANCED FILTERING END

        found_objects.append(TrackedElement(obj_x, obj_y, obj_size))

    # Sort the found objects based on a metric
    found_objects.sort()

    return found_objects