diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 122b344d..00000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1 +0,0 @@ -FROM t0mmyn/uavf_2024 \ No newline at end of file diff --git a/.devcontainer/bashrc_setup.sh b/.devcontainer/bashrc_setup.sh new file mode 100755 index 00000000..b098db69 --- /dev/null +++ b/.devcontainer/bashrc_setup.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Sourcing things +echo 'source /opt/ros/humble/setup.bash' >> /root/.bashrc +echo 'source /root/ros2_ws/install/setup.bash' >> /root/.bashrc + +echo "export PATH=/root/ros2_ws/src/ardupilot/Tools/autotest:$PATH" >> /root/.bashrc +echo "export PATH=/usr/lib/ccache:$PATH" >> /root/.bashrc + +echo "export PATH=\"$PATH:$HOME/.local/bin\"" >> /root/.bashrc + +# So that the container doesn't close +/bin/bash \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 8afd1697..328f74f8 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -9,7 +9,12 @@ "workspaceFolder": "/home/ws/uavf_2024", "workspaceMount": "source=${localWorkspaceFolder},target=/home/ws/uavf_2024,type=bind", "containerEnv": { - "DISPLAY": "unix:0" + // For x86_64 + "DISPLAY": "unix:0", + // For ARM64 + // "DISPLAY": "host.docker.internal:0", + "ROS_LOCALHOST_ONLY": "1", + "ROS_DOMAIN_ID": "42" }, "mounts": [ "source=/tmp/.X11-unix,target=/tmp/.X11-unix,type=bind,consistency=cached" @@ -20,5 +25,15 @@ ], "forwardPorts": [ 6080 - ] + ], + "postCreateCommand": "/usr/local/bin/bashrc_setup.sh", + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "ms-vscode.cmake-tools", + "ms-azuretools.vscode-docker" + ] + } + } } \ No newline at end of file diff --git a/.gitignore b/.gitignore index c4fd38e2..d8641eb7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ c_generated_code acados_ocp_nlp.json __pycache__ *.egg-info +.devcontainer/Dockerfile +.devcontainer/devcontainer.json mav.* terrain eeprom.bin diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..582abbcd --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "cmake.configureOnOpen": false, + "python.testing.unittestArgs": [ + "-v", + "-s", + "./tests", + "-p", + "*test*.py" + ], + "python.testing.pytestEnabled": false, + "python.testing.unittestEnabled": true +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 0367356c..a828a145 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,21 +1,140 @@ -FROM ardupilot/ardupilot-dev-ros - -RUN apt-get update - -# opencv dependencies (https://stackoverflow.com/questions/55313610/importerror-libgl-so-1-cannot-open-shared-object-file-no-such-file-or-directo) +# This file is essentally the template file for the actual dockerfile which will be generated by setup_image.py +# Do not change the positioning of anything until after the "---"" line +FROM PLACEHOLDER + +# from ardupilot +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install --no-install-recommends -y \ + lsb-release \ + sudo \ + wget \ + software-properties-common \ + build-essential \ + ccache \ + g++ \ + gdb \ + gawk \ + git \ + make \ + cmake \ + ninja-build \ + libtool \ + libxml2-dev \ + libxslt1-dev \ + python3-numpy \ + python3-pyparsing \ + python3-serial \ + python-is-python3 \ + libpython3-stdlib \ + libtool-bin \ + zip \ + default-jre \ + socat \ + ros-dev-tools \ + ros-humble-launch-pytest \ + && apt-get clean \ + && apt-get -y autoremove \ + && apt-get autoclean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +# TAKEN from https://github.com/docker-library/python/blob/a58630aef106c8efd710011c6a2a0a1d551319a0/3.11/bullseye/Dockerfile +# if this is called "PIP_VERSION", pip explodes with "ValueError: invalid truth value ''" +ENV PYTHON_PIP_VERSION 23.1.2 +# https://github.com/docker-library/python/issues/365 +ENV PYTHON_SETUPTOOLS_VERSION 65.5.1 +# https://github.com/pypa/get-pip +ENV PYTHON_GET_PIP_URL https://github.com/pypa/get-pip/raw/9af82b715db434abb94a0a6f3569f43e72157346/public/get-pip.py +ENV PYTHON_GET_PIP_SHA256 45a2bb8bf2bb5eff16fdd00faef6f29731831c7c59bd9fc2bf1f3bed511ff1fe + +RUN set -eux; \ + \ + wget -O get-pip.py "$PYTHON_GET_PIP_URL"; \ + echo "$PYTHON_GET_PIP_SHA256 *get-pip.py" | sha256sum -c -; \ + \ + export PYTHONDONTWRITEBYTECODE=1; \ + \ + python get-pip.py \ + --disable-pip-version-check \ + --no-cache-dir \ + --no-compile \ + "pip==$PYTHON_PIP_VERSION" \ + "setuptools==$PYTHON_SETUPTOOLS_VERSION" \ + ; \ + rm -f get-pip.py; \ + \ + pip --version + +RUN python -m pip install --no-cache-dir -U future lxml pexpect flake8 empy pyelftools tabulate pymavlink pre-commit + +FROM eclipse-temurin:19-jdk-jammy as dds-gen-builder + +RUN apt-get update && apt-get install --no-install-recommends -y \ + git \ + && apt-get clean \ + && apt-get -y autoremove \ + && apt-get autoclean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN git clone -b develop --recurse-submodules https://github.com/ArduPilot/Micro-XRCE-DDS-Gen.git --depth 1 --no-single-branch --branch develop dds-gen \ + && cd dds-gen \ + && ./gradlew assemble + +FROM main-setup + +WORKDIR /dds-gen +COPY --from=dds-gen-builder /dds-gen/scripts scripts/ +COPY --from=dds-gen-builder /dds-gen/share share/ +WORKDIR / + +# Get STM32 GCC10 toolchain +ARG ARM_ROOT="gcc-arm-none-eabi-10" +ARG ARM_ROOT_EXT="-2020-q4-major" +ARG ARM_TARBALL="$ARM_ROOT$ARM_ROOT_EXT-x86_64-linux.tar.bz2" +ARG ARM_TARBALL_URL="https://firmware.ardupilot.org/Tools/STM32-tools/$ARM_TARBALL" + +RUN cd /opt \ + && wget -qO- "$ARM_TARBALL_URL" | tar jx \ + && mv "/opt/$ARM_ROOT$ARM_ROOT_EXT" "/opt/$ARM_ROOT" \ + && rm -rf "/opt/$ARM_ROOT/share/doc" + +# manual ccache setup for arm-none-eabi-g++/arm-none-eabi-gcc +RUN ln -s /usr/bin/ccache /usr/lib/ccache/arm-none-eabi-g++ \ + && ln -s /usr/bin/ccache /usr/lib/ccache/arm-none-eabi-gcc + +# Set STM32 toolchain to the PATH +ENV PATH="/opt/$ARM_ROOT/bin:$PATH" + +RUN mkdir -p $HOME/arm-gcc \ + && ln -s -f /opt/gcc-arm-none-eabi-10/ g++-10.2.1 + + +ENV PATH="/dds-gen/scripts:$PATH" +# Set ccache to the PATH +ENV PATH="/usr/lib/ccache:$PATH" + +# Gain some time by disabling mavnative +ENV DISABLE_MAVNATIVE=True + +# Set the buildlogs directory into /tmp as other directory aren't accessible +ENV BUILDLOGS=/tmp/buildlogs + +ENV TZ=UTC + +#------------------------------------- +# EVERYTHING BELOW THIS LINE IS EDITABLE +#------------------------------------- +RUN apt update +RUN apt-get update --fix-missing +RUN apt-get install -y python3-pip RUN apt-get install -y ffmpeg libsm6 libxext6 -# utils (not strictly necessary) -RUN apt-get install -y tmux vim - -# comment this out if you have a GPU -RUN pip3 install torch torchvision --index-url https://download.pytorch.org/whl/cpu - WORKDIR "/home/ws/uavf_2024" COPY setup.py setup.py RUN pip install -e . +# GNC setup stuff (gazebo garden, ardupilot sitl, and respective ros2 plugins etc) +RUN apt-get install -y tmux vim RUN apt-get install -y default-jre socat ros-humble-geographic-msgs ros-dev-tools WORKDIR /root/ros2_ws/src @@ -24,7 +143,8 @@ RUN wget https://raw.githubusercontent.com/ArduPilot/ardupilot/master/Tools/ros2 RUN vcs import --recursive < ros2.repos RUN wget https://raw.githubusercontent.com/ArduPilot/ardupilot_gz/main/ros2_gz.repos RUN vcs import --recursive < ros2_gz.repos -ARG GZ_VERSION=garden +# env so now its persistant for future colcon builds +ENV GZ_VERSION=garden RUN apt-get -y install lsb-release wget gnupg RUN wget https://packages.osrfoundation.org/gazebo.gpg -O /usr/share/keyrings/pkgs-osrf-archive-keyring.gpg @@ -32,25 +152,30 @@ RUN echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/p RUN apt-get update RUN apt-get -y install gz-garden - WORKDIR /root/ros2_ws RUN apt update RUN rosdep update RUN rosdep install --rosdistro ${ROS_DISTRO} --from-paths src -i -r -y -RUN bash -c "source /opt/ros/humble/setup.bash && colcon build --cmake-args -DBUILD_TESTING=ON" +# we dont need the extra args here +RUN bash -c "source /opt/ros/humble/setup.bash && colcon build" -RUN echo 'source /opt/ros/humble/setup.bash' >> ~/.bashrc -RUN echo 'source /root/ros2_ws/install/setup.bash' >> ~/.bashrc +# Unfortunately this is not persistant in docker so you need to source them during runtime with the CMD at the end +#RUN echo 'source /opt/ros/humble/setup.bash' >> /root/.bashrc +#RUN echo 'source /root/ros2_ws/install/setup.bash' >> /root/.bashrc +#RUN echo 'export PATH=/root/ros2_ws/src/ardupilot/Tools/autotest:$PATH' >> /root/.bashrc +#RUN echo 'export PATH=/usr/lib/ccache:$PATH' >> /root/.bashrc -RUN echo 'export PATH=/root/ros2_ws/src/ardupilot/Tools/autotest:$PATH' >> ~/.bashrc -RUN echo 'export PATH=/usr/lib/ccache:$PATH' >> ~/.bashrc - -RUN sudo apt-get install -y python3-dev python3-opencv python3-wxgtk4.0 python3-pip python3-matplotlib python3-lxml python3-pygame +RUN sudo apt-get install -y python3-dev python3-wxgtk4.0 python3-pip python3-matplotlib python3-lxml python3-pygame RUN pip3 install PyYAML mavproxy --user -RUN echo 'export PATH="$PATH:$HOME/.local/bin"' >> ~/.bashrc +#RUN echo 'export PATH="$PATH:$HOME/.local/bin"' >> /root/.bashrc RUN cp src/ardupilot/Tools/vagrant/mavinit.scr /root/.mavinit.scr -# i love python env management!!! <.< -RUN python -m pip uninstall matplotlib -y +#since apt installs the specific version we want we uninstall the pip version +RUN python -m pip uninstall matplotlib -y + +# Sourcing script at runtime +COPY .devcontainer/bashrc_setup.sh /usr/local/bin/bashrc_setup.sh +RUN chmod 777 /usr/local/bin/bashrc_setup.sh +#CMD ["/usr/local/bin/bashrc_setup.sh"] -CMD ["bash"] +CMD ["/bin/bash"] diff --git a/README.md b/README.md index 6a6b9a04..38d8629a 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,50 @@ -# UAV Forge's ROS2 package for GN&C and Aerial Imagery Object Detection. - -## Dev Container Setup -1. Clone the repo -2. In VSCode, open the command palette and run `rebuild and reopen in dev container` -3. To verify your setup, run `run_tests.sh` - -## Usage - -1. Refer to `sim_instructions.md` for instructions on starting and running the simulation. - - -## Install instructions - -### Install required and local Python libraries - -1. cd into this repo's root directory. - -2. Run: - ``` - pip install -e . - ``` - - -### Dev container - -1. Open this project in vscode -2. Install the "Dev Containers" extension -3. Open the command pallete (ctrl-shift-p), then search for and execute "Dev Containers: (Re-)build and Reopen in Container" -4. Congratulations, you get to skip all those tedious steps to install ROS 2 manually, and your environment is isolated from the rest of your computer -5. To make downloading dependencies reproducible, add any important software installation steps to the Dockerfile in this repo. -6. To use git inside the docker container, you may have to manually log in to GitHub again if the built-in credential forwarding isn't working. I recommend using the [GitHub CLI](https://cli.github.com/) to do this. -7. If you want to use the simulator: - 1. Follow instructions in `sim_instructions.md`. - 2. If you want it to run it in a GUI, one way is using the remote desktop environment in the dev container. Open `localhost:6080` in a web browser, then enter password `vscode`, then use the menu in the bottom left to open a terminal, `cd /home/ws/PX4-Autopilot`, then run `make px4_sitl gazebo-classic`. - 3. The X sockets should also be mounted and should work if you run `xhost +` on your machine. - - -I copied a lot of the config from this tutorial: https://docs.ros.org/en/foxy/How-To-Guides/Setup-ROS-2-with-VSCode-and-Docker-Container.html - - -### Manual - -(WIP). It's recommended to use the Dockerfile for development. \ No newline at end of file +# UAV Forge's ROS2 package for GN&C and Aerial Imagery Object Detection. + +## Dev Container Setup (one time setup) +1. Ensure you have Docker and X-11 forwarding installed on your device (google this) +2. Clone the repo and edit devcontainer.json accordingly for your X-11 setup +3. In VSCode, first run `python3 setup_image.py` +4. Then open the command palette (cmd/ctrl+shift+p) and run `rebuild and reopen in dev container` +5. To verify your setup, run `run_tests.sh` + +## Dev Container Usage +1. Open VScode to the repo +2. Open the command palette (cmd/ctrl+shift+p) and run `rebuild and reopen in dev container` + +## Sim Usage + +1. Refer to `sim_instructions.md` for instructions on starting and running the simulation. + + +## Install instructions + +### Install required and local Python libraries + +1. cd into this repo's root directory. + +2. Run: + ``` + pip install -e . + ``` + + +### Dev container + +1. Open this project in vscode +2. Install the "Dev Containers" extension +3. Open the command pallete (ctrl-shift-p), then search for and execute "Dev Containers: (Re-)build and Reopen in Container" +4. Congratulations, you get to skip all those tedious steps to install ROS 2 manually, and your environment is isolated from the rest of your computer +5. To make downloading dependencies reproducible, add any important software installation steps to the Dockerfile in this repo. +6. To use git inside the docker container, you may have to manually log in to GitHub again if the built-in credential forwarding isn't working. I recommend using the [GitHub CLI](https://cli.github.com/) to do this. +7. If you want to use the simulator: + 1. Follow instructions in `sim_instructions.md`. + 2. If you want it to run it in a GUI, one way is using the remote desktop environment in the dev container. Open `localhost:6080` in a web browser, then enter password `vscode`, then use the menu in the bottom left to open a terminal, `cd /home/ws/PX4-Autopilot`, then run `make px4_sitl gazebo-classic`. + 3. The X sockets should also be mounted and should work if you run `xhost +` on your machine. + + +I copied a lot of the config from this tutorial: https://docs.ros.org/en/foxy/How-To-Guides/Setup-ROS-2-with-VSCode-and-Docker-Container.html + + +### Manual + +(WIP). It's recommended to use the Dockerfile for development. diff --git a/setup_image.py b/setup_image.py new file mode 100644 index 00000000..dd6c1a9e --- /dev/null +++ b/setup_image.py @@ -0,0 +1,43 @@ +# Author: Eesh Vij +# Date: 2023-10-18 +# Purpose: Dynamically create Dockerfile based on correct system archiecture + +import platform +import pathlib + +arm64image = "arm64v8/ros:humble" +x86image = "ros:humble" + +# additional arm64 commands (add to list as needed) +arm64commands = ["RUN pip install --force-reinstall -v torch==2.0.1 torchvision==0.15.2 torchaudio==2.0.2\n"] + +# Get system architecture +print(f"System: {platform.system()}") +print(f"System Architecture: {platform.machine()}") +if platform.machine() == "arm64": + arm64 = True +else: + arm64 = False +# Get current directory +current_dir = pathlib.Path(__file__).parent.absolute() +print(f"Current Directory: {current_dir}") +# Read Dockerfile template +data = None +with open(str(current_dir) + "/Dockerfile", "r") as file: + print("Generating Dockerfile...") + data = file.readlines() + +if arm64: + data[2] = f"FROM {arm64image} as main-setup\n" + data[13] = " g++-aarch64-linux-gnu \\\n" + data.insert(14, " gcc-aarch64-linux-gnu \\\n") + data.insert(15, " pkg-config \\\n") + data.append("\n") + data.extend(arm64commands) +else: + data[2] = f"FROM {x86image} as main-setup\n" + +# Write to Dockerfile +with open(str(current_dir) + "/.devcontainer/Dockerfile", "w") as file: + print("Writing to Dockerfile...") + file.writelines(data) \ No newline at end of file