From 4bbd98d7ca2ad6b9cf6d763d7df29dc3e96d372d Mon Sep 17 00:00:00 2001 From: dominikn Date: Fri, 19 Jul 2024 00:09:00 +0200 Subject: [PATCH] building with new husarion-snap-common --- .github/workflows/build.yaml | 73 +++++++++ .github/workflows/publish.yaml | 54 +++++++ .github/workflows/snap.yaml | 61 ------- README.md | 108 ++++++++----- justfile | 34 +++- render_template.py | 22 +++ snap/gui/icon.png | Bin 21880 -> 12518 bytes snap/hooks/configure | 50 ++---- snap/hooks/connect-plug-ros-humble-ros-base | 8 - .../hooks/disconnect-plug-ros-humble-ros-base | 4 - snap/hooks/install | 6 +- ...params.yaml => camera-params-default.yaml} | 0 ...mplate.yaml => ffmpeg-params-default.yaml} | 0 snap/local/launcher.sh | 18 ++- snap/snapcraft.yaml | 42 +---- snapcraft_template.yaml.jinja2 | 151 ++++++++++++++++++ 16 files changed, 436 insertions(+), 195 deletions(-) create mode 100644 .github/workflows/build.yaml create mode 100644 .github/workflows/publish.yaml delete mode 100644 .github/workflows/snap.yaml create mode 100755 render_template.py delete mode 100755 snap/hooks/connect-plug-ros-humble-ros-base delete mode 100755 snap/hooks/disconnect-plug-ros-humble-ros-base rename snap/local/{depthai_params.yaml => camera-params-default.yaml} (100%) rename snap/local/{ffmpeg_params_template.yaml => ffmpeg-params-default.yaml} (100%) create mode 100644 snapcraft_template.yaml.jinja2 diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..acb8de2 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,73 @@ +name: Build snap +on: + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + include: + - ros_distro: humble + - ros_distro: jazzy + + # outputs: + # snap-file: ${{ steps.build-snap.outputs.snap }} + + steps: + + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-tags: true + + - name: Render snapcraft.yaml + run: | + pip install jinja2 + export ROS_DISTRO=${{ matrix.ros_distro }} + ./render_template.py ./snapcraft_template.yaml.jinja2 snap/snapcraft.yaml + + - name: Build snap + uses: snapcore/action-build@v1 + with: + snapcraft-channel: latest/edge + id: build-snap + env: + SNAPCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS: 1 + + - name: Make sure the snap is installable + run: | + sudo snap install --dangerous ${{ steps.build-snap.outputs.snap }} + + # # Save snap for subsequent job(s) + # - uses: actions/upload-artifact@v3 + # with: + # name: husarion-camera-snap + # path: ${{ steps.build-snap.outputs.snap }} + + # publish: + # if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') + # needs: build + # runs-on: ubuntu-latest + + # steps: + + # # Retrieve the snap + # - uses: actions/download-artifact@v3 + # with: + # name: husarion-camera-snap + # path: . + + # # Publish the snap on the store + # # by default on 'edge' but on 'candidate' for tags + # - uses: snapcore/action-publish@v1 + # env: + # SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.STORE_LOGIN }} + # with: + # snap: ${{needs.build.outputs.snap-file}} + # release: ${{ startsWith(github.ref, 'refs/tags/') && '${{ matrix.ros_distro }}/candidate' || '${{ matrix.ros_distro }}/edge'}} diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..a66bffe --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,54 @@ +name: Build and publish snap +on: + push: + tags: + - '*' + branches: + - main + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + include: + - ros_distro: humble + - ros_distro: jazzy + + steps: + + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-tags: true + + - name: Render snapcraft.yaml + run: | + pip install jinja2 + export ROS_DISTRO=${{ matrix.ros_distro }} + ./render_template.py ./snapcraft_template.yaml.jinja2 snap/snapcraft.yaml + + - name: Build snap + uses: snapcore/action-build@v1 + with: + snapcraft-channel: latest/edge + id: build-snap + env: + SNAPCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS: 1 + + - name: Make sure the snap is installable + run: | + sudo snap install --dangerous ${{ steps.build-snap.outputs.snap }} + + # Publish the snap on the store + # by default on 'edge' but on 'candidate' for tags + - name: Publish snap + uses: snapcore/action-publish@v1 + env: + SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.STORE_LOGIN }} + with: + snap: ${{ steps.build-snap.outputs.snap }} + release: ${{ matrix.ros_distro }}/${{ startsWith(github.ref, 'refs/tags/') && 'candidate' || 'edge' }} diff --git a/.github/workflows/snap.yaml b/.github/workflows/snap.yaml deleted file mode 100644 index 9c4de46..0000000 --- a/.github/workflows/snap.yaml +++ /dev/null @@ -1,61 +0,0 @@ -name: snap -on: - push: - tags: - - '*' - branches: - - main - pull_request: - branches: - - main - workflow_dispatch: - -jobs: - build: - runs-on: ubuntu-22.04 - outputs: - snap-file: ${{ steps.build-snap.outputs.snap }} - steps: - - - uses: actions/checkout@v3 - with: - fetch-tags: true - - # Build the snap - - uses: snapcore/action-build@v1 - with: - snapcraft-channel: latest/edge - id: build-snap - env: - SNAPCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS: 1 - - # Make sure the snap is installable - - run: | - sudo snap install --dangerous ${{ steps.build-snap.outputs.snap }} - - # Save snap for subsequent job(s) - - uses: actions/upload-artifact@v3 - with: - name: husarion-depthai-snap - path: ${{ steps.build-snap.outputs.snap }} - - publish: - if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') - needs: build - runs-on: ubuntu-22.04 - steps: - - # Retrieve the snap - - uses: actions/download-artifact@v3 - with: - name: husarion-depthai-snap - path: . - - # Publish the snap on the store - # by default on 'edge' but on 'candidate' for tags - - uses: snapcore/action-publish@v1 - env: - SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.STORE_LOGIN }} - with: - snap: ${{needs.build.outputs.snap-file}} - release: ${{ startsWith(github.ref, 'refs/tags/') && 'candidate' || 'edge'}} diff --git a/README.md b/README.md index 1f8f974..08f60cd 100644 --- a/README.md +++ b/README.md @@ -16,42 +16,16 @@ Snap for OAK-x cameras customized for Husarion robots | `husarion-depthai.start` | Start the `husarion-depthai.daemon` service | | `husarion-depthai.stop` | Stop the `husarion-depthai.daemon` service | | `husarion-depthai` | Start the application in the foreground (run in the current terminal). Remember to stop the daemon first | -| `husarion-depthai.image-view` | Preview the image from the camera | -## Setup FFMPEG +## Setup Camera Params -The default values for `ffmpeg-image-transport` are: - -```bash -$ sudo snap get husarion-depthai driver.ffmpeg-image-transport -Key Value -driver.ffmpeg-image-transport.encoding libx264 -driver.ffmpeg-image-transport.preset ultrafast -driver.ffmpeg-image-transport.tune zerolatency -``` - -to check available options run: - -```bash -ffmpeg -encoders -``` - -find the list of available presets by running +The default config parameters for camera node are selected with: ```bash -ffmpeg -h encoder=$SELECTED_ENCODER` +$ sudo snap set husarion-depthai driver.camera-config=default ``` -## Setup Astra Params - -Default astra params are stored in the following file: - -```bash -$ sudo snap get husarion-depthai driver.params-file -/var/snap/husarion-depthai/common/depthai_params.yaml -``` - -The default `depthai_params.yaml` file content: +The default config is get from: `/var/snap/husarion-depthai/common/camera-config-default.yaml` file: ```yaml --- @@ -111,6 +85,12 @@ The default `depthai_params.yaml` file content: i_low_bandwidth: true #false i_low_bandwidth_quality: 20 i_max_q_size: 30 + # i_width: 1280 #valid if i_output_isp: false + # i_height: 720 #valid if i_output_isp: false + # i_interleaved: false + # scaling 1920x1080 1:3 to 640x360 + # https://docs-beta.luxonis.com/develop/ros/depthai-ros-driver#Available%20sensors%20and%20their%20resolutions%3A + # IMX378, 1080P is 1920x1080, i_isp_den=3 and i_isp_num=2 will give 1280x720 (/16=) i_output_isp: true i_preview_height: 300 i_preview_size: 300 @@ -136,24 +116,78 @@ The default `depthai_params.yaml` file content: ``` -To set a new params create a copy of the `depthai_params.yaml` file: +You can create your own config file: `/var/snap/husarion-depthai/common/camera-config-.yaml`: + +```bash +sudo cp \ +/var/snap/husarion-depthai/camera-config-default.yaml \ +/var/snap/husarion-depthai/camera-config-myconfig.yaml +``` + +Modify the content of the `camera-config-myconfig.yaml` file, eg: + +```bash +sudo vim /var/snap/depthai-camera/common/camera-config-myconfig.yaml +``` + +And set the new path to the config file: + +```bash +sudo snap set depthai-camera driver.camera-params=myconfig +``` + +## Setup FFMPEG Params + +The default values for `ffmpeg-image-transport` are: + +```bash +$ sudo snap set husarion-depthai driver.ffmpeg-config=default +``` + +The default config is get from: `/var/snap/husarion-depthai/common/ffmpeg-config-default.yaml` file: + +```yaml +--- +/**: + ros__parameters: + ffmpeg_image_transport: + + # find the list of available encoders by running `ffmpeg -encoders` + encoding: libx264 + + # find the list of available presets by running `ffmpeg -h encoder=libx264` + preset: ultrafast + tune: zerolatency +``` + +You can create your own config file: `/var/snap/husarion-depthai/common/ffmpeg-config-.yaml`: ```bash sudo cp \ -/var/snap/husarion-depthai/common/depthai_params.yaml \ -/var/snap/husarion-depthai/common/depthai_params2.yaml +/var/snap/husarion-depthai/ffmpeg-config-default.yaml \ +/var/snap/husarion-depthai/ffmpeg-config-myconfig.yaml ``` -Modify the content of the `astra-params2.yaml` file, eg: +Modify the content of the `ffmpeg-config-myconfig.yaml` file, eg: ```bash -sudo vim /var/snap/husarion-depthai/common/depthai_params2.yaml +sudo vim /var/snap/husarion-depthai/common/ffmpeg-config-myconfig.yaml ``` And set the new path to the config file: ```bash -sudo snap set husarion-depthai driver.params-file=/var/snap/husarion-depthai/common/depthai_params2.yaml +sudo snap set husarion-depthai driver.ffmpeg-params=myconfig ``` -List of all available parameters for OAK-x cameras is [here](https://docs.luxonis.com/software/ros/depthai-ros/driver/) \ No newline at end of file +To check available options run: + +```bash +ffmpeg -encoders +``` + +find the list of available presets by running + +```bash +ffmpeg -h encoder=$SELECTED_ENCODER` +``` diff --git a/justfile b/justfile index 68eb0ac..3e5cd3e 100644 --- a/justfile +++ b/justfile @@ -1,6 +1,24 @@ -build: +[private] +default: + @just --list --unsorted + +build target="humble": #!/bin/bash export SNAPCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=1 + + if [ {{target}} == "humble" ]; then + export ROS_DISTRO=humble + export CORE_VERSION=core22 + elif [ {{target}} == "jazzy" ]; then + export ROS_DISTRO=jazzy + export CORE_VERSION=core24 + else + echo "Unknown target: $target" + exit 1 + fi + + ./render_template.py ./snapcraft_template.yaml.jinja2 snap/snapcraft.yaml + snapcraft install: @@ -21,7 +39,7 @@ clean: export SNAPCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=1 snapcraft clean -iterate: +iterate target="humble": #!/bin/bash start_time=$(date +%s) @@ -31,8 +49,20 @@ iterate: sudo rm -rf squashfs-root/ sudo rm -rf husarion-depthai*.snap export SNAPCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=1 + + if [ {{target}} == "humble" ]; then + export ROS_DISTRO=humble + elif [ {{target}} == "jazzy" ]; then + export ROS_DISTRO=jazzy + else + echo "Unknown target: {{target}}" + exit 1 + fi + snapcraft clean + ./render_template.py ./snapcraft_template.yaml.jinja2 snap/snapcraft.yaml snapcraft + unsquashfs husarion-depthai*.snap sudo snap try squashfs-root/ sudo snap connect husarion-depthai:raw-usb diff --git a/render_template.py b/render_template.py new file mode 100755 index 0000000..8975a24 --- /dev/null +++ b/render_template.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 + +import sys +import os +from jinja2 import Environment, FileSystemLoader + +def render_template(template_path, output_path, context): + env = Environment(loader=FileSystemLoader(os.path.dirname(template_path))) + template = env.get_template(os.path.basename(template_path)) + + with open(output_path, 'w') as f: + f.write(template.render(context)) + +if __name__ == "__main__": + template_path = sys.argv[1] + output_path = sys.argv[2] + context = { + 'ros_distro': os.getenv('ROS_DISTRO'), + # 'core_version': os.getenv('CORE_VERSION') + } + + render_template(template_path, output_path, context) diff --git a/snap/gui/icon.png b/snap/gui/icon.png index 3adc62278e05b9bed33cf56a89422a03e3a20d77..befffb3f2804c772962038c6701efd4ca8492541 100644 GIT binary patch literal 12518 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 E0CqZAH2?qr literal 21880 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 z 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/dev/null | awk '{print $2}' | grep -q "$FFMPEG_ENCODING"; then - log_and_echo "Error: Codec $FFMPEG_ENCODING is not available:" - log_and_echo "Find available codecs here: $SNAP_COMMON/ffmpeg_codecs.txt" - ffmpeg -encoders 2>/dev/null | awk '/^ V/ {print $0}' > $SNAP_COMMON/ffmpeg_codecs.txt - exit 1 - fi +# validate driver.camera-params +validate_config_param "driver.camera-params" "camera-params-VALUE.yaml" - cp $SNAP_COMMON/ffmpeg_params_template.yaml $SNAP_COMMON/ffmpeg_params.yaml - yq -i './**.ros__parameters.ffmpeg_image_transport = {}' $SNAP_COMMON/ffmpeg_params.yaml - - # Get all options in JSON format - OPTIONS=$(snapctl get driver.ffmpeg-image-transport) - keys=$(echo $OPTIONS | yq '. | to_entries | .[].key') - for key in $keys; do - export FFMPEG_KEY=$key - export FFMPEG_VALUE=$(snapctl get driver.ffmpeg-image-transport.$FFMPEG_KEY) - yq -i './**.ros__parameters.ffmpeg_image_transport += {env(FFMPEG_KEY): env(FFMPEG_VALUE)}' $SNAP_COMMON/ffmpeg_params.yaml - done -else - cp $SNAP_COMMON/ffmpeg_params_template.yaml $SNAP_COMMON/ffmpeg_params.yaml - yq -i './**.ros__parameters.ffmpeg_image_transport = {}' $SNAP_COMMON/ffmpeg_params.yaml -fi +# validate driver.ffmpeg-params +validate_config_param "driver.ffmpeg-params" "ffmpeg-params-VALUE.yaml" $SNAP/usr/bin/configure_hook_ros.sh diff --git a/snap/hooks/connect-plug-ros-humble-ros-base b/snap/hooks/connect-plug-ros-humble-ros-base deleted file mode 100755 index 39d5c18..0000000 --- a/snap/hooks/connect-plug-ros-humble-ros-base +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -e - -# now we can start the service -# if snapctl services ${SNAP_NAME}.${SNAP_NAME} | grep -q inactive; then -# snapctl start --enable ${SNAP_NAME}.${SNAP_NAME} 2>&1 || true -# fi - -logger -t ${SNAP_NAME} "Plug 'ros-humble-ros-base' connected" \ No newline at end of file diff --git a/snap/hooks/disconnect-plug-ros-humble-ros-base b/snap/hooks/disconnect-plug-ros-humble-ros-base deleted file mode 100755 index bc2c60d..0000000 --- a/snap/hooks/disconnect-plug-ros-humble-ros-base +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -e - -logger -t ${SNAP_NAME} "Plug 'ros-humble-ros-base' disconnected" -snapctl stop --disable ${SNAP_NAME}.daemon 2>&1 || true diff --git a/snap/hooks/install b/snap/hooks/install index b000830..3800fba 100755 --- a/snap/hooks/install +++ b/snap/hooks/install @@ -14,10 +14,8 @@ snapctl set driver.cam-pos-z=0.0 snapctl set driver.cam-roll=0.0 snapctl set driver.cam-pitch=0.0 snapctl set driver.cam-yaw=0.0 -snapctl set driver.params-file=$SNAP_COMMON/depthai_params.yaml -snapctl set driver.ffmpeg-image-transport.encoding=libx264 -snapctl set driver.ffmpeg-image-transport.preset=ultrafast -snapctl set driver.ffmpeg-image-transport.tune=zerolatency +snapctl set driver.camera-params=default +snapctl set driver.ffmpeg-params=default if ! snapctl is-connected raw-usb; then log "Plug 'raw-usb' isn't connected, please run:" diff --git a/snap/local/depthai_params.yaml b/snap/local/camera-params-default.yaml similarity index 100% rename from snap/local/depthai_params.yaml rename to snap/local/camera-params-default.yaml diff --git a/snap/local/ffmpeg_params_template.yaml b/snap/local/ffmpeg-params-default.yaml similarity index 100% rename from snap/local/ffmpeg_params_template.yaml rename to snap/local/ffmpeg-params-default.yaml diff --git a/snap/local/launcher.sh b/snap/local/launcher.sh index ecf3625..9dbcc1f 100755 --- a/snap/local/launcher.sh +++ b/snap/local/launcher.sh @@ -15,7 +15,7 @@ OPTIONS=( cam-roll cam-pitch cam-yaw - params-file + # params-file ) LAUNCH_OPTIONS="" @@ -28,9 +28,21 @@ for OPTION in "${OPTIONS[@]}"; do fi done +# Check if ros.namespace is set and not empty +ROS_NAMESPACE="$(snapctl get ros.namespace)" +if [ -n "${ROS_NAMESPACE}" ]; then + LAUNCH_OPTIONS+="namespace:=${ROS_NAMESPACE} " +fi + +CAMERA_PARAMS="$(snapctl get driver.camera-params)" +LAUNCH_OPTIONS+="params_file:=${SNAP_COMMON}/camera-params-${CAMERA_PARAMS}.yaml " + +FFMPEG_PARAMS="$(snapctl get driver.ffmpeg-params)" +LAUNCH_OPTIONS+="ffmpeg_params_file:=${SNAP_COMMON}/ffmpeg-params-${FFMPEG_PARAMS}.yaml " + if [ "${LAUNCH_OPTIONS}" ]; then - # watch the log with: "journalctl -t husarion-depthai" + # watch the log with: "journalctl -t husarion-astra" log_and_echo "Running with options: ${LAUNCH_OPTIONS}" fi -ros2 launch $SNAP/usr/bin/depthai.launch.py ${LAUNCH_OPTIONS} ffmpeg_params_file:=$SNAP_COMMON/ffmpeg_params.yaml +ros2 launch $SNAP/usr/bin/depthai.launch.py ${LAUNCH_OPTIONS} diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index b59678a..1327268 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -31,21 +31,13 @@ description: | * cam-roll: `0.0` * cam-pitch: `0.0` * cam-yaw: `0.0` - * params-file: `$SNAP_COMMON/depthai_params.yaml` - * ffmpeg-image-transport.encoding: `libx264` - * ffmpeg-image-transport.preset: `ultrafast` - * ffmpeg-image-transport.tune: `zerolatency` - + * camera-params: `default` - Sets the camera parameters based on `/var/snap/husarion-depthai/common/camera-params-.yaml` + * ffmpeg-params: `default` - Sets the FFMPEG parameters based on `/var/snap/husarion-camera/common/ffmpeg-params-.yaml` + To set the parameters, use the snap set command, e.g., snap set husarion-depthai driver.namespace=robot - Default values for `ffmpeg-image-transport`: - - * encoding: `libx264` - * preset: `ultrafast` - * tune: `zerolatency` - grade: stable confinement: strict base: core22 @@ -54,14 +46,6 @@ contact: https://github.com/husarion/depthai-snap/issues issues: https://github.com/husarion/depthai-snap/issues website: https://husarion.com/ -architectures: - - build-on: amd64 - build-for: amd64 - # - build-on: amd64 - # build-for: arm64 - - build-on: arm64 - build-for: arm64 - slots: shm-slot: interface: shared-memory @@ -106,16 +90,12 @@ apps: start: command: usr/bin/start_launcher.sh + restart: + command: usr/bin/restart_launcher.sh + stop: command: usr/bin/stop_launcher.sh - image-view: - command: usr/bin/image_view_launcher.sh - command-chain: [usr/bin/ros_setup.sh] - plugs: [network, network-bind, shm-plug, desktop, desktop-legacy, wayland, unity7, opengl] - slots: [shm-slot] - extensions: [ros2-humble-ros-base] - parts: husarion-depthai: @@ -138,14 +118,6 @@ parts: craftctl set version="$version" craftctl set grade="stable" - image-view: - plugin: nil - stage-packages: - - ros-humble-image-view - - libcanberra-gtk-module - - libcanberra-gtk3-module - - libglu1-mesa - local-files: plugin: dump source: snap/local/ @@ -159,7 +131,7 @@ parts: husarion-snap-common: plugin: dump source: https://github.com/husarion/husarion-snap-common - source-branch: "0.3.0" + source-branch: "0.5.0" source-type: git build-environment: - YQ_VERSION: "v4.35.1" diff --git a/snapcraft_template.yaml.jinja2 b/snapcraft_template.yaml.jinja2 new file mode 100644 index 0000000..e5f9f08 --- /dev/null +++ b/snapcraft_template.yaml.jinja2 @@ -0,0 +1,151 @@ +name: husarion-depthai +adopt-info: husarion-depthai +license: Apache-2.0 +summary: OAK-x cameras driver for Husarion robots +icon: snap/gui/icon.png +description: | + The `husarion-depthai` snap contains all the necessary software to bring the OAK-x cameras up. + + **Parameters** + + The snap provides the following configurable parameters (`param name`: `default value`): + + * `driver`: `{...}` + * `ros`: `{...}` + + The `ros` contains the following keys: + + * `ros.domain-id`: `0` - Sets the `ROS_DOMAIN_ID` environment variable for the ROS driver. + * `ros.localhost-only`: `0` - Sets the `ROS_LOCALHOST_ONLY` environment variable for the ROS driver. + * `ros.transport`: `udp` - Configures DDS transport. Options are `udp`, `shm`, `builtin` (or `rmw_fastrtps_cpp`), `rmw_cyclonedds_cpp`. Corresponding DDS XML files can be found in the `/var/snap/husarion-depthai/common` directory (custom FastDDS setups can also be created here). + * `ros.namespace`: `(unset)` - Namespace for all topics and transforms. + + The `driver` contains the following keys: + + * name: `oak` + * parent-frame: `oak-d-base-frame` + * camera-model: `OAK-D` + * cam-pos-x: `0.0` + * cam-pos-y: `0.0` + * cam-pos-z: `0.0` + * cam-roll: `0.0` + * cam-pitch: `0.0` + * cam-yaw: `0.0` + * camera-params: `default` - Sets the camera parameters based on `/var/snap/husarion-depthai/common/camera-params-.yaml` + * ffmpeg-params: `default` - Sets the FFMPEG parameters based on `/var/snap/husarion-camera/common/ffmpeg-params-.yaml` + + To set the parameters, use the snap set command, e.g., + + snap set husarion-depthai driver.namespace=robot + +grade: stable +confinement: strict +base: {{ 'core22' if ros_distro == 'humble' else 'core24' }} + +contact: https://github.com/husarion/depthai-snap/issues +issues: https://github.com/husarion/depthai-snap/issues +website: https://husarion.com/ + +slots: + shm-slot: + interface: shared-memory + write: ['*'] # paths are relative to /dev/shm + # c189-slot: + # interface: custom-device + # custom-device: c189 + # files: + # write: + # - /run/udev/data/c189:* + +plugs: + shm-plug: + interface: shared-memory + shared-memory: shm-slot + private: false + + # c189-plug: + # interface: custom-device + # custom-device: c189 + +apps: + + daemon: + command: usr/bin/launcher.sh + command-chain: [usr/bin/ros_setup.sh] + daemon: simple + install-mode: enable + plugs: [network, network-bind, shm-plug, raw-usb] + slots: [shm-slot] + extensions: [ros2-{{ ros_distro }}-ros-base] + + husarion-depthai: + command: usr/bin/launcher.sh + command-chain: [usr/bin/check_daemon_running.sh, usr/bin/ros_setup.sh] + plugs: [network, network-bind, shm-plug, raw-usb] + # plugs: [network, network-bind, shm-plug, raw-usb, system-observe, hardware-observe, network-control, network-observe, camera, browser-support] + # block-devices, + slots: [shm-slot] + extensions: [ros2-{{ ros_distro }}-ros-base] + + start: + command: usr/bin/start_launcher.sh + + restart: + command: usr/bin/restart_launcher.sh + + stop: + command: usr/bin/stop_launcher.sh + +parts: + + husarion-depthai: + plugin: nil + stage-packages: + - ros-{{ ros_distro }}-depthai-ros + - ros-{{ ros_distro }}-image-transport + - ros-{{ ros_distro }}-image-transport-plugins + # https://index.ros.org/p/ffmpeg_image_transport/github-ros-misc-utilities-ffmpeg_image_transport/#humble + - ros-{{ ros_distro }}-ffmpeg-image-transport + - ffmpeg + - ros-{{ ros_distro }}-cv-bridge + # needed to run ffmpeg without errors: + - libpulse-dev + - libblas3 + - libjpeg-turbo8-dev + override-stage: | + craftctl default + version="$(apt-cache policy ros-{{ ros_distro }}-depthai-ros-driver | grep Candidate | awk '{print $2}')" + craftctl set version="$version" + craftctl set grade="stable" + + local-files: + plugin: dump + source: snap/local/ + organize: + '*.sh': usr/bin/ + '*.py': usr/bin/ + '*.yaml': usr/share/husarion-depthai/config/ + # '*.xml': usr/share/husarion-depthai/config/ + # '*.json': usr/share/husarion-depthai/config/ + + husarion-snap-common: + plugin: dump + source: https://github.com/husarion/husarion-snap-common + source-branch: "0.5.0" + source-type: git + build-environment: + - YQ_VERSION: "v4.35.1" + build-packages: + - curl + organize: + 'local-ros/*.sh': usr/bin/ + 'local-ros/*.xml': usr/share/husarion-snap-common/config/ + 'local-ros/ros.env': usr/share/husarion-snap-common/config/ + override-build: | + craftctl default + curl -L "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_${CRAFT_ARCH_BUILD_FOR}" -o $CRAFT_PART_BUILD/yq + override-prime: | + craftctl default + cp $CRAFT_PART_BUILD/yq $CRAFT_PRIME/usr/bin/yq + chmod +x $CRAFT_PRIME/usr/bin/yq + rm -rf $CRAFT_PRIME/local-ros \ No newline at end of file