diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..3e6b88f --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,10 @@ +# Use the public base image so the devcontainer build is intentionally minimal. +# If you later need to add extra layers (tools, caches, deps), add them below. +FROM kestreldev/l4t-ros2-docker:jazzy + +# Keep the devcontainer image minimal; runtime apt/source configuration is performed +# by the repository-level installer script `scripts/rosdep_install.sh` so the +# devcontainer image doesn't force package installation at build-time. + +# Example of adding small, local dev-only tooling later: +# RUN apt-get update && apt-get install -y --no-install-recommends vim less && rm -rf /var/lib/apt/lists/* diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 0000000..1c0e3ac --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,18 @@ +This devcontainer builds the `l4t-ros2:jazzy` image using the provided `Dockerfile` and starts a container with the same runtime options used by the project's documentation. + +Before opening the folder in the devcontainer, allow X connections from the host by running on your host machine: + +``` +xhost + +``` + +Then open the repository in VS Code and choose "Reopen in Container". The container is built from `.devcontainer/Dockerfile` and is started with GPU support, host networking, DISPLAY forwarded, and the host's `$HOME` bind-mounted so GUI and ROS tooling work similarly to the manual `docker run` command. + +The devcontainer will now run `scripts/rosdep_install.sh` once after the container is created to install ROS package dependencies from the workspace `src/` directory using `rosdep`. + +To re-run the installer manually inside the container: + +```bash +# from inside the container or via 'devcontainer exec' +bash $HOME/Kestrel/scripts/rosdep_install.sh +``` diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..4e34f23 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,24 @@ +{ + "name": "Kestrel l4t-ros2:jazzy", + "build": { + "dockerfile": "Dockerfile", + "context": ".." + }, + "runArgs": [ + "--net=host", + "--runtime=nvidia", + "-e", + "DISPLAY=${localEnv:DISPLAY}", + "-v", + "/tmp/.X11-unix/:/tmp/.X11-unix:rw", + "-v", + "${localEnv:HOME}:${localEnv:HOME}:rw" + ], + "containerEnv": { + "DISPLAY": "${localEnv:DISPLAY}" + }, + "workspaceFolder": "${localEnv:HOME}/Kestrel", + "postCreateCommand": "bash -lc 'set -euo pipefail; if [ -f \"${containerWorkspaceFolder}/scripts/rosdep_install.sh\" ]; then echo \"Running scripts/rosdep_install.sh\"; bash \"${containerWorkspaceFolder}/scripts/rosdep_install.sh\"; else echo \"No scripts/rosdep_install.sh found; skipping rosdep install.\"; fi'", + "postStartCommand": "echo \"If GUI apps fail to open, run 'xhost +' on the host to allow X connections.\"", + "remoteUser": "jetson" +} diff --git a/.github/workflows/base_image.yaml b/.github/workflows/base_image.yaml new file mode 100644 index 0000000..b891cda --- /dev/null +++ b/.github/workflows/base_image.yaml @@ -0,0 +1,43 @@ +# Process for building and pushing the full Jetson Orin Nano docker image + +name: Docker Build Base image + +on: + workflow_dispatch: + +jobs: + build-base: + name: Build Base Image + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + with: + submodules: 'recursive' + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + with: + platforms: arm64,amd64 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and Push Base Image + uses: docker/build-push-action@v5 + with: + context: ./docker/l4t-ros2-docker/jazzy + file: ./docker/l4t-ros2-docker/jazzy/Dockerfile + push: true + platforms: linux/arm64 + tags: kestreldev/l4t-ros2-docker:jazzy + cache-from: | + type=registry,ref=kestreldev/l4t-ros2-docker:cache-jazzy + cache-to: | + type=registry,ref=kestreldev/l4t-ros2-docker:cache-jazzy,mode=max diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index a4d9d1c..94660f5 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,10 +1,7 @@ name: docker-build-and-push on: - push: - branches: - - main - - pathing_and_workflows + workflow_dispatch: jobs: docker-build-and-push: diff --git a/.gitmodules b/.gitmodules index 68dbf7a..6501b0e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,3 @@ -[submodule "src/libraries/mavlink"] - path = src/libraries/mavlink - url = https://github.com/mavlink/mavlink -[submodule "src/libraries/mavros"] - path = src/libraries/mavros - url = https://github.com/mavlink/mavros [submodule "src/driver/vl53l1x"] path = src/driver/vl53l1x url = https://github.com/Autonomous-droneProject/ROS2-VL53L1X.git @@ -19,6 +13,6 @@ [submodule "docs/KestrelAssembly"] path = docs/KestrelAssembly url = https://github.com/Autonomous-droneProject/KestrelAssembly.git -[submodule "src/libraries/geographiclib"] - path = src/libraries/geographiclib - url = https://github.com/geographiclib/geographiclib.git +[submodule "docker/l4t-ros2-docker"] + path = docker/l4t-ros2-docker + url = https://github.com/atinfinity/l4t-ros2-docker.git diff --git a/docker/l4t-ros2-docker b/docker/l4t-ros2-docker new file mode 160000 index 0000000..e8534a5 --- /dev/null +++ b/docker/l4t-ros2-docker @@ -0,0 +1 @@ +Subproject commit e8534a513feb17d9d6d4112c8e572f21a70b3841 diff --git a/docker/Dockerfile b/docker/old/Dockerfile similarity index 100% rename from docker/Dockerfile rename to docker/old/Dockerfile diff --git a/docker/ros_entrypoint.sh b/docker/ros_entrypoint.sh deleted file mode 100644 index e69de29..0000000 diff --git a/scripts/rosdep_install.sh b/scripts/rosdep_install.sh new file mode 100755 index 0000000..e457da2 --- /dev/null +++ b/scripts/rosdep_install.sh @@ -0,0 +1,139 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Simplified, robust rosdep install script +# Implements the requested sequence: +# 1) install software-properties-common and enable 'universe' +# 2) install curl +# 3) download and install ros2-apt-source .deb from latest GitHub release +# 4) ensure only 'ros2.sources' remains under /etc/apt/sources.list.d/ +# 5) run rosdep install --from-paths src --ignore-src -r -y + +WORKSPACE_ROOT="$(pwd)" +SRC_DIR="$WORKSPACE_ROOT/src" + +echo "[rosdep_install] workspace root: $WORKSPACE_ROOT" + +command_exists() { command -v "$1" >/dev/null 2>&1; } + +require_sudo() { + if ! command_exists sudo; then + echo "[rosdep_install] ERROR: sudo is required to run this script." >&2 + exit 1 + fi +} + +backup_file() { + local f="$1" + if [ -e "$f" ]; then + sudo cp -a "$f" "${f}.bak-rosdep" || true + echo "[rosdep_install] Backed up $f -> ${f}.bak-rosdep" + fi +} + +echo "[rosdep_install] Ensuring apt helpers and universe repository are available..." +require_sudo +sudo env DEBIAN_FRONTEND=noninteractive apt-get update -qq +sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends software-properties-common || true +sudo add-apt-repository -y universe || true + +echo "[rosdep_install] Updating apt and installing curl..." +sudo env DEBIAN_FRONTEND=noninteractive apt-get update -qq +sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends curl ca-certificates gnupg lsb-release dpkg || true + +# determine latest ros-apt-source release tag +echo "[rosdep_install] Determining latest ros-apt-source release from GitHub..." +ROS_APT_SOURCE_VERSION=$(curl -s https://api.github.com/repos/ros-infrastructure/ros-apt-source/releases/latest | grep -F '"tag_name"' | awk -F'"' '{print $4}' || true) +if [ -z "${ROS_APT_SOURCE_VERSION:-}" ]; then + echo "[rosdep_install] ERROR: could not determine ros-apt-source release tag from GitHub." >&2 + echo "[rosdep_install] Aborting. You can add the ROS apt source manually or retry later." >&2 + exit 1 +fi + +# compute UBUNTU codename +CODENAME=$(. /etc/os-release && echo "${UBUNTU_CODENAME:-${VERSION_CODENAME}}") +if [ -z "${CODENAME:-}" ]; then + # fallback to lsb_release + if command_exists lsb_release; then + CODENAME=$(lsb_release -cs) + else + echo "[rosdep_install] ERROR: cannot determine Ubuntu codename." >&2 + exit 1 + fi +fi + +DEBNAME="ros2-apt-source_${ROS_APT_SOURCE_VERSION}.${CODENAME}_all.deb" +TMP_DEB="/tmp/ros2-apt-source.deb" +DOWNLOAD_URL="https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/${DEBNAME}" + +echo "[rosdep_install] Downloading ros-apt-source package: $DOWNLOAD_URL" +if ! curl -L -o "$TMP_DEB" "$DOWNLOAD_URL" --fail -s; then + echo "[rosdep_install] ERROR: failed to download $DOWNLOAD_URL" >&2 + exit 1 +fi + +echo "[rosdep_install] Installing $TMP_DEB" +if ! sudo dpkg -i "$TMP_DEB"; then + echo "[rosdep_install] dpkg reported problems, attempting to fix with apt-get -f install..." + sudo env DEBIAN_FRONTEND=noninteractive apt-get -y -f install + sudo dpkg -i "$TMP_DEB" || { + echo "[rosdep_install] ERROR: dpkg installation failed after fixing dependencies." >&2 + rm -f "$TMP_DEB" || true + exit 1 + } +fi +rm -f "$TMP_DEB" + +echo "[rosdep_install] Ensuring only 'ros2.sources' remains under /etc/apt/sources.list.d/" +require_sudo +shopt -s nullglob +ROSDIR=/etc/apt/sources.list.d +for f in "$ROSDIR"/ros2*; do + [ -e "$f" ] || continue + bn=$(basename "$f") + if [ "$bn" != "ros2.sources" ]; then + backup_file "$f" + echo "[rosdep_install] Removing $f" + sudo rm -f "$f" || true + else + echo "[rosdep_install] Keeping $f" + fi +done +shopt -u nullglob + +echo "[rosdep_install] Refreshing apt lists..." +sudo env DEBIAN_FRONTEND=noninteractive apt-get update -qq + +# Ensure rosdep is installed +if ! command_exists rosdep; then + echo "[rosdep_install] rosdep not found. Installing python3-rosdep via apt..." + sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends python3-rosdep || true + if ! command_exists rosdep; then + echo "[rosdep_install] Attempting pip3 install for rosdep..." + if command_exists pip3; then + pip3 install --user rosdep + export PATH="$HOME/.local/bin:$PATH" + else + echo "[rosdep_install] ERROR: pip3 not found; please install rosdep manually." >&2 + exit 1 + fi + fi +fi + +# Initialize rosdep if needed +if [ ! -f /etc/ros/rosdep/sources.list.d/20-default.list ]; then + echo "[rosdep_install] Initializing rosdep database (sudo may be required)..." + sudo rosdep init || true +fi + +echo "[rosdep_install] Updating rosdep database..." +rosdep update || echo "[rosdep_install] rosdep update failed or already up-to-date" + +if [ -d "$SRC_DIR" ]; then + echo "[rosdep_install] Running: rosdep install --from-paths $SRC_DIR --ignore-src -r -y" + rosdep install --from-paths "$SRC_DIR" --ignore-src -r -y +else + echo "[rosdep_install] No src directory at $SRC_DIR; skipping rosdep install." >&2 +fi + +echo "[rosdep_install] Done." diff --git a/src/kestrel_communication/CMakeLists.txt b/src/kestrel_communication/CMakeLists.txt index b44a99c..d2eace3 100644 --- a/src/kestrel_communication/CMakeLists.txt +++ b/src/kestrel_communication/CMakeLists.txt @@ -9,9 +9,8 @@ find_package(std_msgs REQUIRED) find_package(kestrel_msgs REQUIRED) find_package(sensor_msgs REQUIRED) find_package(rclpy REQUIRED) - -set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};/usr/share/cmake/mavros_msgs") find_package(mavros_msgs REQUIRED) + # Include directory for headers include_directories(include) diff --git a/src/kestrel_control/CMakeLists.txt b/src/kestrel_control/CMakeLists.txt index 2004b74..e1d8b0e 100644 --- a/src/kestrel_control/CMakeLists.txt +++ b/src/kestrel_control/CMakeLists.txt @@ -1,9 +1,6 @@ cmake_minimum_required(VERSION 3.10) project(kestrel_control) -# -#set(CMAKE_PREFIX_PATH "/opt/ros/${ROS_DISTRO}:${CMAKE_PREFIX_PATH}") - # Core build dependencies find_package(ament_cmake REQUIRED) find_package(ament_cmake_gtest REQUIRED) @@ -12,16 +9,10 @@ find_package(rclpy REQUIRED) find_package(std_msgs REQUIRED) find_package(geometry_msgs REQUIRED) find_package(kestrel_msgs REQUIRED) - -set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};/usr/share/cmake/geographiclib") -find_package(GeographicLib REQUIRED) - -set(control_toolbox_DIR "${ROS_PREFIX}/share/control_toolbox/cmake") find_package(control_toolbox REQUIRED) - - -set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};/usr/share/cmake/mavros_msgs") find_package(mavros_msgs REQUIRED) +set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};/usr/share/cmake/geographiclib") +find_package(GeographicLib REQUIRED) # Include directory for headers include_directories(include) diff --git a/src/kestrel_control/include/kestrel_control/frame_transformer.hpp b/src/kestrel_control/include/kestrel_control/frame_transformer.hpp index a9033f4..d2de3c9 100644 --- a/src/kestrel_control/include/kestrel_control/frame_transformer.hpp +++ b/src/kestrel_control/include/kestrel_control/frame_transformer.hpp @@ -4,7 +4,7 @@ #include "rclcpp/rclcpp.hpp" #include "sensor_msgs/msg/nav_sat_fix.hpp" #include "geometry_msgs/msg/pose_stamped.hpp" -#include "GeographicLib/LocalCartesian.hpp" +#include #include namespace kestrel_control diff --git a/src/kestrel_control/package.xml b/src/kestrel_control/package.xml index 9457dad..77eb808 100644 --- a/src/kestrel_control/package.xml +++ b/src/kestrel_control/package.xml @@ -14,7 +14,7 @@ sensor_msgs geometry_msgs kestrel_msgs - GeographicLib + libgeographiclib-dev control_toolbox mavros_msgs ament_cmake_gtest diff --git a/src/kestrel_description/package.xml b/src/kestrel_description/package.xml index 5dab962..c191a93 100644 --- a/src/kestrel_description/package.xml +++ b/src/kestrel_description/package.xml @@ -13,7 +13,7 @@ robot_state_publisher rviz2 joint_state_publisher_gui - gazebo_ros + ros-jazzy-gazebo-ros diff --git a/src/libraries/geographiclib b/src/libraries/geographiclib deleted file mode 160000 index 0a6067b..0000000 --- a/src/libraries/geographiclib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0a6067b74d2c5316afceb61e3a7a2b2f262960d8 diff --git a/src/libraries/mavlink b/src/libraries/mavlink deleted file mode 160000 index b27d03b..0000000 --- a/src/libraries/mavlink +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b27d03bffd7969b30242a00aeaa6c0430d34d429 diff --git a/src/libraries/mavros b/src/libraries/mavros deleted file mode 160000 index e7a3e40..0000000 --- a/src/libraries/mavros +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e7a3e40e55f57e7b70d16b029f4cea37a1e971cf