diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..92caf46 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +* @wentywenty +* @Luo1imasi diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..113edd2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,34 @@ +--- +name: Bug / Issue +about: 报告问题或提出需求 / Report a bug or request a feature +labels: triage +--- + + +- [ ] 🔨 构建问题 Build issue(deb 编译失败、版本不一致 / compile fail, version mismatch) +- [ ] 📦 APT 源问题 APT repo issue(同步失败 / sync fail, 403) +- [ ] 🛠️ 新功能 / 新包请求 Feature request / new package + +## 环境 / Environment + +- 架构 Arch: [amd64 / arm64] +- 系统 OS: [RobopiOS 1.0.6 / RobopiOS 1.2.7 / Ubuntu 22.04 / Ubuntu 24.04] +- 包名和版本 Package & version: + +## 现象 / What happened + + + + +## 复现步骤 / Steps to reproduce + +1. +2. +3. + +## 期望行为 / Expected behavior + + + +> ⚠️ **禁止使用 AI 生成 Issues。AI 生成的 Issue 将被直接关闭。** +> **Do not use AI to generate Issues. AI-generated issues will be closed immediately.** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..e0d5224 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,33 @@ +--- +name: Pull Request +about: 提交代码变更 / Submit a code change +--- + + +- [ ] 🐛 Bug fix 修复 +- [ ] ✨ New feature 新功能 +- [ ] 📦 Packaging 打包(debian/ 变更) +- [ ] 📝 Docs 文档 +- [ ] 🔧 CI / workflow 变更 + +## 变更说明 / Description + + + +## 版本检查 / Version check + + + +- [ ] 本次变更不需要改版本号 / This change does NOT bump version +- [ ] 已更新 debian/changelog +- [ ] 已同步 package.xml(仅 ROS 包 / ROS packages only) +- [ ] changelog 和 package.xml 版本一致 / Versions match + +## 测试 / Testing + +- [ ] 构建通过 / Build passes: `dpkg-buildpackage -us -uc -b` +- [ ] 已在 [amd64 / arm64] 上测试 +- [ ] 其他说明 / Notes: + +> ⚠️ **禁止使用 AI 生成 PR 描述。AI 生成的 PR 将被直接关闭。** +> **Do not use AI to generate PR descriptions. AI-generated PRs will be closed immediately.** diff --git a/.github/workflows/build-deb.yml b/.github/workflows/build-deb.yml new file mode 100644 index 0000000..aa123a9 --- /dev/null +++ b/.github/workflows/build-deb.yml @@ -0,0 +1,204 @@ +name: Build RoboParty Navigation Deb Packages + +on: + push: + pull_request: + workflow_dispatch: + +permissions: + contents: write + +env: + ROS_DISTRO: ${{ vars.ROS_DISTRO || 'jazzy' }} + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + - arch: amd64 + os: ubuntu-24.04 + - arch: arm64 + os: [self-hosted, Linux, ARM64, "24.04"] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: false + + - name: Checkout submodules + run: | + git submodule sync --recursive + for attempt in 1 2 3 4 5; do + if git submodule update --init --force --recursive --depth=1; then + exit 0 + fi + echo "::warning::submodule checkout failed (attempt $attempt)" + git submodule deinit -f --all || true + sleep 15 + done + echo "::error::submodule checkout failed after retries" + exit 1 + + - name: Version check (package.xml vs changelog) + run: | + PKG_XML_VERSION=$(grep -oP '\K[^<]+' nav2_localization_adapter/package.xml) + CHANGELOG_VERSION=$(head -1 debian/changelog | grep -oP '\((.*?)\)' | tr -d '()' | cut -d'-' -f1) + + echo "package.xml version: $PKG_XML_VERSION" + echo "changelog version: $CHANGELOG_VERSION" + + if [ "$PKG_XML_VERSION" != "$CHANGELOG_VERSION" ]; then + echo "::error::Version mismatch! package.xml=$PKG_XML_VERSION, changelog=$CHANGELOG_VERSION" + exit 1 + fi + + echo "✅ Version check passed: $PKG_XML_VERSION" + + - name: Tag vs changelog version check + if: startsWith(github.ref, 'refs/tags/v') + run: | + TAG_VERSION="${GITHUB_REF#refs/tags/v}" + CHANGELOG_VERSION=$(head -1 debian/changelog | grep -oP '\((.*?)\)' | tr -d '()' | cut -d'-' -f1) + + echo "Tag version: $TAG_VERSION" + echo "Changelog version: $CHANGELOG_VERSION" + + if [ "$TAG_VERSION" != "$CHANGELOG_VERSION" ]; then + echo "::error::Version mismatch! tag=v$TAG_VERSION, changelog=$CHANGELOG_VERSION" + exit 1 + fi + + echo "✅ Tag matches changelog: v$TAG_VERSION" + + - name: Setup ROS 2 + if: matrix.arch == 'amd64' + uses: ros-tooling/setup-ros@v0.7 + with: + required-ros-distributions: ${{ env.ROS_DISTRO }} + + - name: Add RoboParty APT source + run: | + keyring="/tmp/roboparty-archive-keyring-${GITHUB_RUN_ID}.gpg" + sources="/tmp/roboparty-apt-${GITHUB_RUN_ID}" + curl -fsSL https://apt.roboparty.com/roboparty.gpg | gpg --dearmor > "$keyring" + chmod 644 "$keyring" + rm -rf "$sources" + mkdir -p "$sources" + chmod 755 "$sources" + cp /etc/apt/sources.list.d/*.list "$sources"/ 2>/dev/null || true + cp /etc/apt/sources.list.d/*.sources "$sources"/ 2>/dev/null || true + if [ -s /etc/apt/sources.list ]; then + cp /etc/apt/sources.list "$sources/ubuntu.list" + fi + echo "deb [arch=$(dpkg --print-architecture) signed-by=$keyring] https://apt.roboparty.com common main" > "$sources/roboparty.list" + chmod 644 "$sources"/* 2>/dev/null || true + echo "APT_OPTS=-o Dir::Etc::sourcelist=/dev/null -o Dir::Etc::sourceparts=$sources" >> "$GITHUB_ENV" + + - name: Install build tools + run: | + sudo apt-get $APT_OPTS update -q + sudo apt-get $APT_OPTS install -y --no-install-recommends \ + build-essential cmake ccache dpkg-dev debhelper devscripts \ + git wget pkg-config python3-colcon-common-extensions \ + libpcl-dev libeigen3-dev libceres-dev libfmt-dev libyaml-cpp-dev \ + libboost-all-dev libapr1-dev python3-dev libpython3-dev \ + python3-yaml python3-numpy \ + ros-${ROS_DISTRO}-ament-cmake-auto ros-${ROS_DISTRO}-rclcpp \ + ros-${ROS_DISTRO}-rclcpp-components ros-${ROS_DISTRO}-std-msgs \ + ros-${ROS_DISTRO}-nav-msgs ros-${ROS_DISTRO}-sensor-msgs \ + ros-${ROS_DISTRO}-geometry-msgs ros-${ROS_DISTRO}-visualization-msgs \ + ros-${ROS_DISTRO}-std-srvs ros-${ROS_DISTRO}-tf2 \ + ros-${ROS_DISTRO}-tf2-ros ros-${ROS_DISTRO}-tf2-geometry-msgs \ + ros-${ROS_DISTRO}-pcl-ros ros-${ROS_DISTRO}-pcl-conversions \ + ros-${ROS_DISTRO}-sophus ros-${ROS_DISTRO}-rosidl-default-generators \ + ros-${ROS_DISTRO}-common-interfaces ros-${ROS_DISTRO}-rosbag2 \ + ros-${ROS_DISTRO}-nav2-bringup ros-${ROS_DISTRO}-nav2-map-server \ + ros-${ROS_DISTRO}-nav2-lifecycle-manager ros-${ROS_DISTRO}-nav2-msgs + + - name: Download RoboParty SDK debs + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + out="/tmp/roboparty-debs-${GITHUB_RUN_ID}" + rm -rf "$out" + mkdir -p "$out" + chmod 755 "$out" + DEB_ARCH=$(dpkg --print-architecture) OUT_DIR="$out" python3 - <<'PY' + import json, os, pathlib, shutil, time, urllib.request + + arch = os.environ["DEB_ARCH"] + out = pathlib.Path(os.environ["OUT_DIR"]) + headers = {"Accept": "application/vnd.github+json"} + token = os.environ.get("GH_TOKEN") + if token: + headers["Authorization"] = f"Bearer {token}" + + def download(url, dest): + for attempt in range(1, 6): + try: + with urllib.request.urlopen(url, timeout=90) as response, open(dest, "wb") as target: + shutil.copyfileobj(response, target) + return + except Exception as exc: + print(f"download failed (attempt {attempt}): {exc}") + time.sleep(10) + raise RuntimeError(f"could not download {url}") + + repos = { + "Roboparty/nlink_parser_ros2": "nlink-parser-ros2", + "Roboparty/livox_ros_driver2": "livox-ros-driver2", + "Roboparty/Livox-SDK2": "livox-sdk2", + "Roboparty/serial": "ros-jazzy-serial", + } + for repo, prefix in repos.items(): + req = urllib.request.Request( + f"https://api.github.com/repos/{repo}/releases/latest", headers=headers) + with urllib.request.urlopen(req, timeout=90) as response: + data = json.load(response) + for asset in data.get("assets", []): + name = asset["name"] + if name.startswith(prefix) and name.endswith(f"_{arch}.deb"): + print(f"download {name}") + download(asset["browser_download_url"], out / name) + PY + chmod 644 "$out"/* + + - name: Install RoboParty SDK debs + run: | + sudo apt-get $APT_OPTS install -y --no-install-recommends roboparty-base + sudo apt-get $APT_OPTS install -y /tmp/roboparty-debs-${GITHUB_RUN_ID}/*.deb + + - name: Python syntax checks + run: | + python3 -m py_compile \ + nav2_localization_adapter/scripts/*.py \ + robots_localization_ros2/script/*.py + + - name: Build deb packages + run: | + . /opt/ros/${ROS_DISTRO}/setup.sh + dpkg-buildpackage -us -uc -b + mv ../*.deb . + VERSION=$(dpkg-parsechangelog -S Version | cut -d'-' -f1 || echo "unknown") + echo "PKG_VERSION=$VERSION" >> $GITHUB_ENV + + - name: Upload deb artifacts + uses: actions/upload-artifact@v4 + with: + name: roboparty-navigation-v${{ env.PKG_VERSION }}-${{ matrix.arch }} + path: "*.deb" + + - name: Collect release files + if: startsWith(github.ref, 'refs/tags/v') + run: | + mkdir -p release + cp roboparty-localization_*.deb roboparty-navigation_*.deb release/ + + - name: Upload to GitHub Release + if: startsWith(github.ref, 'refs/tags/v') + uses: softprops/action-gh-release@v2 + with: + files: release/*.deb diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..72ef733 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,10 @@ +roboparty-navigation (1.0.0-1) stable; urgency=medium + + * Initial packaging. + * Split the runtime into roboparty-localization (FAST-LIO based + localization, no Nav2 dependency) and roboparty-navigation (Nav2 + adapter). + * Keep both packages on the shared install prefix (default /opt/roboparty) so + roboparty-base/setup.bash discovers them automatically. + + -- RoboParty <2321901849@qq.com> Sat, 07 Nov 2026 00:00:00 +0800 diff --git a/debian/cmake/deps.cmake b/debian/cmake/deps.cmake new file mode 100644 index 0000000..4b2ba2f --- /dev/null +++ b/debian/cmake/deps.cmake @@ -0,0 +1,2 @@ +find_package(fmt REQUIRED) +find_package(Boost REQUIRED COMPONENTS system thread) diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..c5799d0 --- /dev/null +++ b/debian/control @@ -0,0 +1,78 @@ +Source: roboparty-navigation +Section: misc +Priority: optional +Maintainer: RoboParty <2321901849@qq.com> +Build-Depends: debhelper-compat (= 13), + cmake, + build-essential, + pkg-config, + python3, + python3-colcon-common-extensions, + python3-yaml, + libpcl-dev, + libeigen3-dev, + libceres-dev, + libfmt-dev, + libyaml-cpp-dev, + libboost-all-dev, + python3-dev, + libpython3-dev, + ros-jazzy-ament-cmake, + ros-jazzy-rclcpp, + ros-jazzy-rclcpp-components, + ros-jazzy-std-msgs, + ros-jazzy-nav-msgs, + ros-jazzy-sensor-msgs, + ros-jazzy-geometry-msgs, + ros-jazzy-visualization-msgs, + ros-jazzy-std-srvs, + ros-jazzy-tf2, + ros-jazzy-tf2-ros, + ros-jazzy-tf2-geometry-msgs, + ros-jazzy-pcl-ros, + ros-jazzy-pcl-conversions, + ros-jazzy-sophus, + ros-jazzy-rosidl-default-generators, + ros-jazzy-common-interfaces, + ros-jazzy-rosbag2, + livox-sdk2-dev, + livox-ros-driver2, + nlink-parser-ros2 +Standards-Version: 4.6.0 + +Package: roboparty-localization +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + roboparty-base (>= 1.0.0), + ros-jazzy-ros-base, + ros-jazzy-common-interfaces, + ros-jazzy-pcl-ros, + ros-jazzy-pcl-conversions, + ros-jazzy-tf2, + ros-jazzy-tf2-ros, + ros-jazzy-tf2-geometry-msgs, + ros-jazzy-rosidl-default-runtime, + livox-ros-driver2, + livox-sdk2, + nlink-parser-ros2 +Description: RoboParty LiDAR-inertial localization + FAST-LIO based localization for RoboParty robots, providing odometry, + point-cloud mapping and the robots_localization ROS 2 interface. + This package does not depend on Nav2. + +Package: roboparty-navigation +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + roboparty-base (>= 1.0.0), + roboparty-localization (= ${binary:Version}), + ros-jazzy-nav2-bringup, + ros-jazzy-nav2-map-server, + ros-jazzy-nav2-lifecycle-manager, + ros-jazzy-nav2-msgs, + python3-yaml, + python3-numpy +Recommends: ros-jazzy-rviz2 +Description: RoboParty Nav2 navigation stack + Nav2 adapter for RoboParty robots. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..bc04b65 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,39 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: roboparty-navigation +Source: https://github.com/Roboparty/roboparty_navigation +Comment: + The localization implementation is derived from FAST-LIO2. The original + FAST-LIO2, ikd-Tree and IKFoM sources carry the copyright of The University + of Hong Kong and are distributed under GPL-2.0. The robot-specific history + was imported from gitee.com/hitcrt2019_session/rm_sentry_localization_2023. + The Nav2 adapter, site tooling and packaging are RoboParty work. + +Files: nav2_localization_adapter/* +Copyright: 2026 Zhang-Yongqi +License: GPL-3.0-or-later + +Files: robots_localization/* +Copyright: 2019-2023 The University of Hong Kong + 2022-2026 HIT CRT and RoboParty contributors +License: GPL-2.0 + +Files: debian/* +Copyright: 2026 RoboParty +License: GPL-3.0-or-later + +License: GPL-2.0 + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + . + On Debian systems, the complete text of the GNU General Public License + version 2 can be found in "/usr/share/common-licenses/GPL-2". + +License: GPL-3.0-or-later + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + . + On Debian systems, the complete text of the GNU General Public License + version 3 can be found in "/usr/share/common-licenses/GPL-3". diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..bda4013 --- /dev/null +++ b/debian/rules @@ -0,0 +1,79 @@ +#!/usr/bin/make -f + +SHELL := /bin/bash + +INSTALL_PREFIX ?= /opt/roboparty + +export DH_VERBOSE = 1 +export DEB_BUILD_MAINT_OPTIONS = optimize=-lto + +ROS_DISTRO ?= jazzy +ROS_SETUP ?= /opt/ros/$(ROS_DISTRO) +BUILD_ROOT := $(CURDIR)/build +INSTALL_ROOT := $(CURDIR)/debian/colcon-install +LOC_PKG := $(CURDIR)/debian/roboparty-localization +NAV_PKG := $(CURDIR)/debian/roboparty-navigation + +%: + dh $@ + +override_dh_auto_configure: + @true + +override_dh_auto_build: + # Do not bake the build host CPU into the package. + sed -i 's/ -march=native//g' robots_localization_ros2/CMakeLists.txt + + . $(ROS_SETUP)/setup.bash && \ + { [ ! -f $(INSTALL_PREFIX)/setup.bash ] || . $(INSTALL_PREFIX)/setup.bash; } && \ + colcon build \ + --base-paths . \ + --packages-up-to robots_localization nav2_localization_adapter \ + --merge-install \ + --build-base $(BUILD_ROOT)/colcon \ + --install-base $(INSTALL_ROOT) \ + --cmake-args \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="$(INSTALL_PREFIX):$(ROS_SETUP)" \ + -DCMAKE_PROJECT_INCLUDE=$(CURDIR)/debian/cmake/deps.cmake \ + -DCMAKE_INSTALL_RPATH="$(INSTALL_PREFIX)/lib;$(ROS_SETUP)/lib" \ + -DCMAKE_BUILD_RPATH="$(INSTALL_PREFIX)/lib;$(ROS_SETUP)/lib" \ + -DSophus_INCLUDE_DIRS="$(ROS_SETUP)/include" + +override_dh_auto_test: + @true + +override_dh_auto_install: + install -d $(LOC_PKG)$(INSTALL_PREFIX) + cp -a $(INSTALL_ROOT)/. $(LOC_PKG)$(INSTALL_PREFIX)/ + + # Split navigation-owned files out of the localization package. + install -d $(NAV_PKG)$(INSTALL_PREFIX)/share $(NAV_PKG)$(INSTALL_PREFIX)/lib + mv $(LOC_PKG)$(INSTALL_PREFIX)/share/nav2_localization_adapter $(NAV_PKG)$(INSTALL_PREFIX)/share/ + mv $(LOC_PKG)$(INSTALL_PREFIX)/lib/nav2_localization_adapter $(NAV_PKG)$(INSTALL_PREFIX)/lib/ + + # Move ament/colcon index entries for the navigation package. + for marker in \ + share/ament_index/resource_index/packages/nav2_localization_adapter \ + share/ament_index/resource_index/rosidl_interfaces/nav2_localization_adapter \ + share/colcon-core/packages/nav2_localization_adapter; do \ + if [ -e "$(LOC_PKG)$(INSTALL_PREFIX)/$$marker" ]; then \ + install -D "$(LOC_PKG)$(INSTALL_PREFIX)/$$marker" "$(NAV_PKG)$(INSTALL_PREFIX)/$$marker"; \ + rm -f "$(LOC_PKG)$(INSTALL_PREFIX)/$$marker"; \ + fi; \ + done + + # The colcon prefix itself comes from roboparty-base; keep only the + # package-level local_setup files. + rm -f $(LOC_PKG)$(INSTALL_PREFIX)/setup.* \ + $(LOC_PKG)$(INSTALL_PREFIX)/local_setup.* \ + $(LOC_PKG)$(INSTALL_PREFIX)/_local_setup_util_*.py + +override_dh_auto_clean: + rm -rf $(BUILD_ROOT) $(INSTALL_ROOT) + +override_dh_shlibdeps: + dh_shlibdeps -- -l$(INSTALL_PREFIX)/lib --ignore-missing-info + +override_dh_missing: + @true