From d485de4f910d69badf86613e14d11ada5172354d Mon Sep 17 00:00:00 2001 From: marcojob <44396071+marcojob@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:44:59 +0100 Subject: [PATCH 1/7] readme: Start to add some stuff --- {scripts => .devcontainer}/build_opencv.sh | 0 README.md | 195 ++------------------- 2 files changed, 11 insertions(+), 184 deletions(-) rename {scripts => .devcontainer}/build_opencv.sh (100%) diff --git a/scripts/build_opencv.sh b/.devcontainer/build_opencv.sh similarity index 100% rename from scripts/build_opencv.sh rename to .devcontainer/build_opencv.sh diff --git a/README.md b/README.md index 88789847..d5c9c9b3 100644 --- a/README.md +++ b/README.md @@ -1,189 +1,16 @@ -# usb_cam [![ROS 2 CI](https://github.com/ros-drivers/usb_cam/actions/workflows/build_test.yml/badge.svg)](https://github.com/ros-drivers/usb_cam/actions/workflows/build_test.yml) +# v4l2_camera +This repository contains was adapted from [usb_cam](https://github.com/ros-drivers/usb_cam) to run Arducam AR0234 camera's on a Jetson Xavier NX. The AR0234 camera supports mono and Bayer 10-bit output in this [layout](https://www.kernel.org/doc/html/v4.9/media/uapi/v4l/pixfmt-srggb10.html). The driver applies debayering and conversion to RGB8 and outputs the raw image as a ros topic. -## A ROS 2 Driver for V4L USB Cameras -This package is based off of V4L devices specifically instead of just UVC. +## Setup +### Arducam dependencies +In order to run the cameras on the Jetson Xavier NX, the [Arducam quickstart guide](https://docs.arducam.com/Nvidia-Jetson-Camera/Jetvariety-Camera/Quick-Start-Guide/) needs to be followed. Mainly the following steps: -For ros1 documentation, see [the ROS wiki](http://ros.org/wiki/usb_cam). - -## Supported ROS 2 Distros and Platforms - -All Officially supported Linux Distros and corresponding ROS 2 releases are supported. Please create an issue if you experience any problems on these platforms. - -Windows: TBD/Untested/Unproven -MacOS: TBD/Untested/Unproven - -For either MacOS or Windows - if you would like to try and get it working please create an issue to document your effort. If it works we can add it to the instructions here! - -## Quickstart - -Assuming you have a supported ROS 2 distro installed, run the following command to install the binary release: - -```shell -sudo apt-get install ros--usb-cam ``` - -As of today this package should be available for binary installation on all active ROS 2 distros. - -If for some reason you cannot install the binaries, follow the directions below to compile from source. - -## Building from Source - -Clone/Download the source code into your workspace: - -```shell -cd /path/to/colcon_ws/src -git clone https://github.com/ros-drivers/usb_cam.git -``` - -Or click on the green "Download zip" button on the repo's github webpage. - -Once downloaded and ensuring you have sourced your ROS 2 underlay, go ahead and install the dependencies: - -```shell -cd /path/to/colcon_ws -rosdep install --from-paths src --ignore-src -y -``` - -From there you should have all the necessary dependencies installed to compile the `usb_cam` package: - -```shell -cd /path/to/colcon_ws -colcon build -source /path/to/colcon_ws/install/setup.bash +1. wget https://github.com/ArduCAM/MIPI_Camera/releases/download/v0.0.3/install_full.sh +2. chmod +x install_full.sh +3. ./install_full.sh -m arducam +4. apt-get install v4l-utils ``` -Be sure to source the newly built packages after a successful build. - -Once sourced, you should be able to run the package in one of three ways, shown in the next section. - -## Running - -The `usb_cam_node` can be ran with default settings, by setting specific parameters either via the command line or by loading in a parameters file. - -We provide a "default" params file in the `usb_cam/config/params.yaml` directory to get you started. Feel free to modify this file as you wish. - -Also provided is a launch file that should launch the `usb_cam_node_exe` executable along with an additional node that displays an image topic. - -The commands to run each of these different ways of starting the node are shown below: - -**NOTE: you only need to run ONE of the commands below to run the node** - -```shell -# run the executable with default settings (without params file) -ros2 run usb_cam usb_cam_node_exe - -# run the executable while passing in parameters via a yaml file -ros2 run usb_cam usb_cam_node_exe --ros-args --params-file /path/to/colcon_ws/src/usb_cam/config/params.yaml - -# launch the usb_cam executable that loads parameters from the same `usb_cam/config/params.yaml` file as above -# along with an additional image viewer node -ros2 launch usb_cam camera.launch.py -``` -## Launching Multiple usb_cam's - -To launch multiple nodes at once, simply remap the namespace of each one: - -```shell -ros2 run usb_cam usb_cam_node_exe --remap __ns:=/usb_cam_0 --params-file /path/to/usb_cam/config/params_0.yaml -ros2 run usb_cam usb_cam_node_exe --remap __ns:=/usb_cam_1 --params-file /path/to/usb_cam/config/params_1.yaml -``` - -## Supported formats - -### Device supported formats - -To see a connected devices supported formats, run the `usb_cam_node` and observe the console output. - -An example output is: - -```log -This devices supproted formats: - Motion-JPEG: 1280 x 720 (30 Hz) - Motion-JPEG: 960 x 540 (30 Hz) - Motion-JPEG: 848 x 480 (30 Hz) - Motion-JPEG: 640 x 480 (30 Hz) - Motion-JPEG: 640 x 360 (30 Hz) - YUYV 4:2:2: 640 x 480 (30 Hz) - YUYV 4:2:2: 1280 x 720 (10 Hz) - YUYV 4:2:2: 640 x 360 (30 Hz) - YUYV 4:2:2: 424 x 240 (30 Hz) - YUYV 4:2:2: 320 x 240 (30 Hz) - YUYV 4:2:2: 320 x 180 (30 Hz) - YUYV 4:2:2: 160 x 120 (30 Hz) -``` - -### Driver supported formats - -The driver has its own supported formats. See [the source code](include/usb_cam/formats/) -for details. - -After observing [the devices supported formats](#device-supported-formats), specify which -format to use via [the parameters file](config/params.yaml) with the `pixel_format` parameter. - -To see a list of all currently supported driver formats, run the following command: - -```shell -ros2 run usb_cam usb_cam_node_exe --ros-args -p pixel_format:="test" -``` - -Note: "test" here could be replaced with any non-supported pixel format string. The driver -will detect if the given pixel format is supported or not. - -More formats and conversions can be added, contributions welcome! - -### Supported IO methods - -This driver supports three different IO methods as of today: - -1. `read`: copies the video frame between user and kernal space -1. `mmap`: memory mapped buffers allocated in kernel space -1. `userptr`: memory buffers allocated in the user space - -To read more on the different methods, check out [this article that provides a good overview -of each](https://lwn.net/Articles/240667/) - -## Compression - -Big thanks to [the `ros2_v4l2_camera` package](https://gitlab.com/boldhearts/ros2_v4l2_camera#usage-1) and their documentation on this topic. - -The `usb_cam` should support compression by default since it uses `image_transport` to publish its images as long as the `image_transport_plugins` package is installed on your system. With the plugins installed the `usb_cam` package should publish a `compressed` topic automatically. - -Unfortunately `rviz2` and `show_image.py` do not support visualizing the compressed images just yet so you will need to republish the compressed image downstream to uncompress it: - -```shell -ros2 run image_transport republish compressed raw --ros-args --remap in/compressed:=image_raw/compressed --remap out:=image_raw/uncompressed -``` - -## Address and leak sanitizing - -Incorporated into the `CMakelists.txt` file to assist with memory leak and address sanitizing -is a flag to add these compile commands to the targets. - -To enable them, pass in the `SANITIZE=1` flag: - -```shell -colcon build --packages-select usb_cam --cmake-args -DSANITIZE=1 -``` - -Once built, run the nodes executable directly and pass any `ASAN_OPTIONS` that are needed: - -```shell -ASAN_OPTIONS=new_delete_type_mismatch=0 ./install/usb_cam/lib/usb_cam/usb_cam_node_exe -``` - -After shutting down the executable with `Ctrl+C`, the sanitizer will report any memory leaks. - -By default this is turned off since compiling with the sanatizer turned on causes bloat and slows -down performance. - -## Documentation - -[Doxygen](http://docs.ros.org/indigo/api/usb_cam/html/) files can be found on the ROS wiki. - -### License - -usb_cam is released with a BSD license. For full terms and conditions, see the [LICENSE](LICENSE) file. - -### Authors - -See the [AUTHORS](AUTHORS.md) file for a full list of contributors. +### OpenCV 4.9.X +OpenCV >=4.9.0 is required in order to run the debayering process. An installation scripts inside `.devcontainer` is provided as help on how to compile it from source. From 65977d6b6bc086376ec2f3e15f9c57de4acb8e70 Mon Sep 17 00:00:00 2001 From: marcojob <44396071+marcojob@users.noreply.github.com> Date: Wed, 30 Oct 2024 17:43:52 +0100 Subject: [PATCH 2/7] devcontainer: Start adding + Bonus learning interface start --- .devcontainer/Dockerfile | 37 ++++++++++ .devcontainer/build_and_push_images.sh | 1 + .devcontainer/build_opencv.sh | 68 ++++++++++++++++--- .devcontainer/devcontainer.json | 44 ++++++++++++ .devcontainer/devcontainer_all_packages.sh | 45 ++++++++++++ .devcontainer/devcontainer_optional_mounts.sh | 9 +++ .gitignore | 4 +- README.md | 3 + include/usb_cam/learning/interface.hpp | 57 ++++++++++++++++ src/interface.cpp | 58 ++++++++++++++++ 10 files changed, 314 insertions(+), 12 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100755 .devcontainer/build_and_push_images.sh create mode 100644 .devcontainer/devcontainer.json create mode 100755 .devcontainer/devcontainer_all_packages.sh create mode 100755 .devcontainer/devcontainer_optional_mounts.sh create mode 100644 include/usb_cam/learning/interface.hpp create mode 100644 src/interface.cpp diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..c036ccfb --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,37 @@ +FROM ros:noetic-ros-core + +ARG TZ=Europe/Zurich +ENV DEBIAN_FRONTEND=noninteractive +ENV PATH="${PATH}:/opt/hpcx/ompi/bin" + +# All apt packages +COPY devcontainer_all_packages.sh /tmp/devcontainer_all_packages.sh +RUN /tmp/devcontainer_all_packages.sh && rm -f /tmp/devcontainer_all_packages.sh + +ENV LANG="en_US.UTF-8" \ + LANGUAGE="en_US:en" \ + LC_ALL="en_US.UTF-8" + +# Add user "asl" with sudo rights +RUN groupadd -r asl && \ + useradd --create-home --gid asl --groups dialout,plugdev --shell /bin/bash asl && \ + mkdir -p /etc/sudoers.d && \ + echo 'asl ALL=NOPASSWD: ALL' > /etc/sudoers.d/asl + +# CUDA: Install +RUN wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-keyring_1.1-1_all.deb && \ + sudo dpkg -i cuda-keyring_1.1-1_all.deb && \ + sudo apt update && \ + sudo apt -y install cuda-toolkit-12-6 cudnn9-cuda-12 + +# OpenCV 4.10.0 install +COPY build_opencv.sh /tmp/build_opencv.sh +RUN bash /tmp/build_opencv.sh + +# Git-prompt: Source +RUN echo 'if [ -f "$HOME/.bash-git-prompt/gitprompt.sh" ]; then GIT_PROMPT_ONLY_IN_REPO=1; source "$HOME/.bash-git-prompt/gitprompt.sh"; fi' >> /home/asl/.bash_aliases + +# Clean up +RUN rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/* /var/cache/apt/archives/* + +ENTRYPOINT ["/bin/bash"] diff --git a/.devcontainer/build_and_push_images.sh b/.devcontainer/build_and_push_images.sh new file mode 100755 index 00000000..1e3dd10a --- /dev/null +++ b/.devcontainer/build_and_push_images.sh @@ -0,0 +1 @@ +docker buildx build --progress=plain -t omavteam/v4l2_camera:latest -f Dockerfile --push . diff --git a/.devcontainer/build_opencv.sh b/.devcontainer/build_opencv.sh index 145481d8..2e3ffcff 100755 --- a/.devcontainer/build_opencv.sh +++ b/.devcontainer/build_opencv.sh @@ -7,7 +7,7 @@ set -e readonly PREFIX=/usr/local # install prefix, (can be ~/.local for a user install) readonly DEFAULT_VERSION=4.10.0 # controls the default version (gets reset by the first argument) readonly CPUS=$(nproc) # controls the number of jobs -readonly BUILD_DIR="/home/asl/Downloads" +readonly BUILD_DIR="/tmp" # better board detection. if it has 6 or more cpus, it probably has a ton of ram too if [[ $CPUS -gt 5 ]]; then @@ -26,7 +26,7 @@ cleanup () { echo "(Doing so may make running tests on the build later impossible)" fi # read -p "Y/N " yn - yn="n" + yn="n" case ${yn} in [Yy]* ) rm -rf $BUILD_DIR/build_opencv ; break;; [Nn]* ) break ;; @@ -102,19 +102,63 @@ install_dependencies () { zlib1g-dev } -configure () { +# Automatically detect installed CUDA version +detect_cuda_version() { + if command -v /usr/local/cuda/bin/nvcc &> /dev/null; then + CUDA_VERSION=$(/usr/local/cuda/bin/nvcc --version | grep -oP 'release \K[0-9]+\.[0-9]+') + else + echo "CUDA not found. Please install CUDA before running this script." + exit 1 + fi +} + +# Automatically detect installed cuDNN version +detect_cudnn_version() { + if [[ -f "/usr/include/cudnn_version.h" ]]; then + CUDNN_MAJOR=$(grep -oP '(?<=#define CUDNN_MAJOR )\d+' /usr/include/cudnn_version.h) + CUDNN_MINOR=$(grep -oP '(?<=#define CUDNN_MINOR )\d+' /usr/include/cudnn_version.h) + CUDNN_PATCHLEVEL=$(grep -oP '(?<=#define CUDNN_PATCHLEVEL )\d+' /usr/include/cudnn_version.h) + CUDNN_VERSION="${CUDNN_MAJOR}.${CUDNN_MINOR}.${CUDNN_PATCHLEVEL}" + else + echo "cuDNN not found. Please install cuDNN before running this script." + exit 1 + fi +} + +# Set CUDA compute capabilities based on detected CUDA version +set_cuda_arch_bin() { + case "$CUDA_VERSION" in + 11.*) + CUDA_ARCH_BIN="5.3,6.1,6.2,7.0,7.5,8.0" + ;; + 12.*) + CUDA_ARCH_BIN="6.1,6.2,7.5,8.0,8.6,8.7" + ;; + *) + echo "Unsupported CUDA version detected: $CUDA_VERSION" + exit 1 + ;; + esac +} + +# Update the configure function to use dynamic CUDA and cuDNN version detection +configure() { + detect_cuda_version + detect_cudnn_version + set_cuda_arch_bin + local CMAKEFLAGS=" -D BUILD_EXAMPLES=OFF -D BUILD_opencv_python2=ON -D BUILD_opencv_python3=ON -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=${PREFIX} - -D CUDA_ARCH_BIN=5.3,6.2,7.2,8.7 + -D CUDA_ARCH_BIN=${CUDA_ARCH_BIN} -D CUDA_ARCH_PTX= -D CUDA_FAST_MATH=ON - -D CUDNN_VERSION='8.4.1' + -D CUDNN_VERSION='${CUDNN_VERSION}' -D EIGEN_INCLUDE_PATH=/usr/include/eigen3 - -D ENABLE_NEON=ON + -D ENABLE_NEON=OFF -D OPENCV_DNN_CUDA=ON -D OPENCV_ENABLE_NONFREE=ON -D OPENCV_EXTRA_MODULES_PATH=$BUILD_DIR/build_opencv/opencv_contrib/modules @@ -133,9 +177,11 @@ configure () { -D BUILD_TESTS=OFF" fi + echo "CUDA version: $CUDA_VERSION" + echo "cuDNN version: $CUDNN_VERSION" + echo "CUDA_ARCH_BIN: $CUDA_ARCH_BIN" echo "cmake flags: ${CMAKEFLAGS}" - - cd opencv + cd $BUILD_DIR/build_opencv/opencv mkdir -p build cd build cmake ${CMAKEFLAGS} .. 2>&1 | tee -a configure.log @@ -157,9 +203,9 @@ main () { # prepare for the build: setup install_dependencies - if [[ ! -d "$BUILD_DIR/build_opencv" ]] ; then - git_source ${VER} - fi + # if [[ ! -d "$BUILD_DIR/build_opencv" ]] ; then + git_source ${VER} + # fi if [[ ${DO_TEST} ]] ; then configure test diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..ed321b79 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,44 @@ +{ + "image": "omavteam/v4l2_camera:latest", + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python" + ], + "settings": { + "files.hotExit": "off", + "window.restoreWindows": "none", + "workbench.startupEditor": "none" + } + } + }, + "remoteEnv": { + "DISPLAY": "${localEnv:DISPLAY}" + }, + "remoteUser": "asl", + "initializeCommand": ".devcontainer/devcontainer_optional_mounts.sh", + "postStartCommand": "pip install -e .", + "mounts": [ + { + "source": "${localEnv:HOME}/.bash-git-prompt", + "target": "/home/asl/.bash-git-prompt", + "type": "bind" + }, + { + "source": "${localEnv:HOME}/.cache", + "target": "/home/asl/.cache", + "type": "bind" + } + ], + "runArgs": [ + "--gpus", + "all", + "--privileged", + "--device=/dev/dri:/dev/dri", + "-v", + "/tmp/.X11-unix:/tmp/.X11-unix", + "-v", + "/dev/bus/usb:/dev/bus/usb", + "--net=host" + ] +} diff --git a/.devcontainer/devcontainer_all_packages.sh b/.devcontainer/devcontainer_all_packages.sh new file mode 100755 index 00000000..6a3a84bd --- /dev/null +++ b/.devcontainer/devcontainer_all_packages.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +set -eux +export DEBIAN_FRONTEND=noninteractive + +main() { + local pkgs=( + apt-transport-https + build-essential + ca-certificates + ccache + curl + gnupg + htop + locales + net-tools + software-properties-common + ssh + sudo + udev + unzip + v4l-utils + wget + zlib1g + ) + + apt-get update + apt-get upgrade -y + apt-get -y --quiet --no-install-recommends install "${pkgs[@]}" + + mkdir -p /root/.ssh \ + && chmod 0700 /root/.ssh \ + && ssh-keyscan github.com > /root/.ssh/known_hosts + + ln -snf /usr/share/zoneinfo/${TZ} /etc/localtime && echo ${TZ} > /etc/timezone + sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen + locale-gen en_US.UTF-8 + dpkg-reconfigure locales + + apt-get -y autoremove + apt-get clean autoclean + rm -rf /var/lib/apt/lists/{apt,dpkg,cache,log} /tmp/* /var/tmp/* +} + +main "$@" diff --git a/.devcontainer/devcontainer_optional_mounts.sh b/.devcontainer/devcontainer_optional_mounts.sh new file mode 100755 index 00000000..f89d7381 --- /dev/null +++ b/.devcontainer/devcontainer_optional_mounts.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +if [ ! -d "$HOME/.bash-git-prompt" ]; then + mkdir "$HOME/.bash-git-prompt" +fi + +if [ ! -d "$HOME/.cache" ]; then + mkdir "$HOME/.cache" +fi diff --git a/.gitignore b/.gitignore index 719d27cd..f2ea1490 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ # documentation docs/xml -site/ \ No newline at end of file +site/ + +resources/* diff --git a/README.md b/README.md index d5c9c9b3..fba26b3e 100644 --- a/README.md +++ b/README.md @@ -12,5 +12,8 @@ In order to run the cameras on the Jetson Xavier NX, the [Arducam quickstart gui 4. apt-get install v4l-utils ``` +### User groups +TODO: Is the spi group actually needed? Or gpio? + ### OpenCV 4.9.X OpenCV >=4.9.0 is required in order to run the debayering process. An installation scripts inside `.devcontainer` is provided as help on how to compile it from source. diff --git a/include/usb_cam/learning/interface.hpp b/include/usb_cam/learning/interface.hpp new file mode 100644 index 00000000..420030cb --- /dev/null +++ b/include/usb_cam/learning/interface.hpp @@ -0,0 +1,57 @@ +#ifndef LEARNING_INTERFACE_HPP_ +#define LEARNING_INTERFACE_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class LearningInterface { +public: + LearningInterface() : _model_path("") {} + + virtual void set_input(const float* input_buffer) { + cudaMemcpy(_input_buffer, input_buffer, _input_size, cudaMemcpyHostToDevice); + } + + virtual void get_output(float* output_buffer) { + cudaMemcpy(output_buffer, _outputBuffer, _outputSize, cudaMemcpyDeviceToHost); + } + + void load_model(); + bool run_inference(size_t batch_size); + + virtual ~LearningInterface() { + if (_context) _context->destroy(); + if (_engine) _engine->destroy(); + if (_runtime) _runtime->destroy(); + + if (_buffers[0]) cudaFree(_buffers[0]); + if (_buffers[1]) cudaFree(_buffers[1]); + + delete[] _input_buffer; + delete[] _output_buffer; + } + +protected: + std::string _model_path; + +private: + float* _input_buffer = nullptr; + float* _output_buffer = nullptr; + nvinfer1::ICudaEngine* _engine = nullptr; + nvinfer1::IExecutionContext* _context = nullptr; + nvinfer1::IRuntime* _runtime = nullptr; + size_t _input_size = 0; + size_t _output_size = 0; + std::string _model_path; + void* _buffers[2] = { nullptr, nullptr }; +}; + +#endif // LEARNING_INTERFACE_HPP_ diff --git a/src/interface.cpp b/src/interface.cpp new file mode 100644 index 00000000..01d0f43c --- /dev/null +++ b/src/interface.cpp @@ -0,0 +1,58 @@ +#include "usb_cam/learning/interface.hpp" + +void LearningInterface::load_model() { + // Open and try to read the file + std::ifstream file(_model_path, std::ios::binary); + if (file.good()) { + file.seekg(0, std::ios::end); + const size_t model_size = file.tellg(); + file.seekg(0, std::ios::beg); + + // Read the model data + std::vector model_data(model_size); + file.read(model_data.data(), model_size); + file.close(); + + _runtime = nvinfer1::createInferRuntime(nvinfer1::Logger()); + if (_runtime != nullptr) { + _engine = _runtime->deserializeCudaEngine(model_data.data(), model_size, nullptr); + + if (_engine != nullptr) { + _context = _engine->createExecutionContext(); + + if (_context != nullptr) { + // Allocate buffers for input and output + _inputSize = _engine->getBindingDimensions(0).volume() * sizeof(float); + _outputSize = _engine->getBindingDimensions(1).volume() * sizeof(float); + + // Allocate device buffers + cudaMalloc(&_buffers[0], _inputSize); + cudaMalloc(&_buffers[1], _outputSize); + + // Allocate CPU buffers + _inputBuffer = new float[_inputSize / sizeof(float)]; + _outputBuffer = new float[_outputSize / sizeof(float)]; + + std::cout << "TensorRT model loaded successfully from: " << model_path << std::endl; + + } else { + std::cout << "Failed to create execution context." << std::endl; + } + } else { + std::cout << "Failed to create TensorRT engine." << std::endl; + } + } else { + std::cout << "Failed to create TensorRT runtime." << std::endl; + } + } else { + std::cout << "Failed to open model file." << std::endl; + } +} + +bool LearningInterface::run_inference(size_t batch_size) { + if (!_context->execute(batch_size, _buffers)) { + std::cerr << "Failed to execute inference." << std::endl; + return false; + } + return true; +} From c8d40736bf3a209752738c1741f7dbbb696dec48 Mon Sep 17 00:00:00 2001 From: marcojob <44396071+marcojob@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:21:37 +0100 Subject: [PATCH 3/7] ci: Add container + actions workflow --- .devcontainer/Dockerfile | 8 ++ .devcontainer/build_and_push_images.sh | 2 +- .devcontainer/devcontainer.json | 1 - .devcontainer/devcontainer_all_packages.sh | 2 + .github/actions/generate-doxybook/action.yml | 59 --------- .github/actions/generate-doxygen/action.yml | 32 ----- .github/workflows/build_test.yml | 118 ----------------- .github/workflows/ci.yml | 23 ++++ .github/workflows/docs.yml | 130 ------------------- .gitignore | 1 + CMakeLists.txt | 31 ++--- README.md | 2 +- ci/pr_compile.sh | 19 +++ 13 files changed, 65 insertions(+), 363 deletions(-) delete mode 100644 .github/actions/generate-doxybook/action.yml delete mode 100644 .github/actions/generate-doxygen/action.yml delete mode 100644 .github/workflows/build_test.yml create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/docs.yml create mode 100755 ci/pr_compile.sh diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index c036ccfb..7609b60e 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -31,6 +31,14 @@ RUN bash /tmp/build_opencv.sh # Git-prompt: Source RUN echo 'if [ -f "$HOME/.bash-git-prompt/gitprompt.sh" ]; then GIT_PROMPT_ONLY_IN_REPO=1; source "$HOME/.bash-git-prompt/gitprompt.sh"; fi' >> /home/asl/.bash_aliases +# ROS: Source +RUN echo "source /opt/ros/noetic/setup.bash" >> /home/asl/.bash_aliases + +# ROS: rosdep init +RUN sudo rosdep init && \ + rosdep update && \ + rosdep install --from-paths . --ignore-src -r -y + # Clean up RUN rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/* /var/cache/apt/archives/* diff --git a/.devcontainer/build_and_push_images.sh b/.devcontainer/build_and_push_images.sh index 1e3dd10a..50c54d5d 100755 --- a/.devcontainer/build_and_push_images.sh +++ b/.devcontainer/build_and_push_images.sh @@ -1 +1 @@ -docker buildx build --progress=plain -t omavteam/v4l2_camera:latest -f Dockerfile --push . +docker buildx build -t omavteam/v4l2_camera:latest -f .devcontainer/Dockerfile --push diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ed321b79..61a37ae1 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -17,7 +17,6 @@ }, "remoteUser": "asl", "initializeCommand": ".devcontainer/devcontainer_optional_mounts.sh", - "postStartCommand": "pip install -e .", "mounts": [ { "source": "${localEnv:HOME}/.bash-git-prompt", diff --git a/.devcontainer/devcontainer_all_packages.sh b/.devcontainer/devcontainer_all_packages.sh index 6a3a84bd..c570e40d 100755 --- a/.devcontainer/devcontainer_all_packages.sh +++ b/.devcontainer/devcontainer_all_packages.sh @@ -14,12 +14,14 @@ main() { htop locales net-tools + python3-rosdep software-properties-common ssh sudo udev unzip v4l-utils + vim wget zlib1g ) diff --git a/.github/actions/generate-doxybook/action.yml b/.github/actions/generate-doxybook/action.yml deleted file mode 100644 index 03c0c59d..00000000 --- a/.github/actions/generate-doxybook/action.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Generate doxybook2 -description: Generate markdown using doxybook2 with given configuration -inputs: - input-doxygen-artifact: - required: true - description: Name of input doxygen artifact to download - doxygen-artifact-extraction-path: - required: true - description: Path to extract input doxygen artifact to - doxybook2-version: - required: true # TODO: make this optional - description: Version of doxybook2 to download and use - output-path: - required: true - description: The path to generate the doxybook2 markdown to - doxybook2-config-path: - required: true # TODO: make this optional with smart default - description: Path to the doxybook2 configuration file (including .json extension) - base-url: - required: true # TODO: make this optional by adding logic below - description: Base URL to overwrite the default with - artifact-path: - required: true - description: Path to directory to upload as artifact - artifact-name: - required: true - description: Name of the artifact to upload - artifact-retention-days: - required: true - description: Number of days to keep the artifact for -runs: - using: composite - steps: - - name: Download Doxygen XML - uses: actions/download-artifact@v3 - with: - name: ${{ inputs.input-doxygen-artifact }} - path: ${{ inputs.doxygen-artifact-extraction-path }} - - name: Build API Reference - shell: bash - run: | - # ensure output directory exists - mkdir -p ${{ inputs.output-path }} - export DOXYBOOK_URL=https://github.com/matusnovak/doxybook2/releases/download - export DOXYBOOK_URL=${DOXYBOOK_URL}/${{ inputs.doxybook2-version }} - export DOXYBOOK_ZIP=doxybook2-linux-amd64-${{ inputs.doxybook2-version }}.zip - wget ${DOXYBOOK_URL}/${DOXYBOOK_ZIP} - sudo apt-get install unzip - unzip ${DOXYBOOK_ZIP} - ./bin/doxybook2 --input ${{ inputs.doxygen-artifact-extraction-path }} \ - --output ${{ inputs.output-path }} \ - --config ${{ inputs.doxybook2-config-path }} \ - --config-data '{"baseUrl": "${{ inputs.base-url }}"}' - - name: Upload API reference - uses: actions/upload-artifact@v3 - with: - name: ${{ inputs.artifact-name }} - path: ${{ inputs.artifact-path }} - retention-days: ${{ inputs.artifact-retention-days }} \ No newline at end of file diff --git a/.github/actions/generate-doxygen/action.yml b/.github/actions/generate-doxygen/action.yml deleted file mode 100644 index 2c769e8a..00000000 --- a/.github/actions/generate-doxygen/action.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Generate doxygen documentation and upload as artifact -description: Build doxygen documentation with given configuration -inputs: - working-directory: - required: true - description: Working directory to run doxygen from - doxyfile-path: - required: true - description: The path to checkout the given repository too - artifact-path: - required: true - description: Path to directory to upload as artifact - artifact-name: - required: true - description: Name of the artifact to upload - artifact-retention-days: - required: true - description: Number of days to keep the artifact for -runs: - using: composite - steps: - - name: Run doxygen on source - uses: mattnotmitt/doxygen-action@v1 - with: - working-directory: ${{ inputs.working-directory }} - doxyfile-path: ${{ inputs.doxyfile-path }} - - name: Upload Doxygen XML - uses: actions/upload-artifact@v3 - with: - name: ${{ inputs.artifact-name }} - path: ${{ inputs.artifact-path }} - retention-days: ${{ inputs.artifact-retention-days }} \ No newline at end of file diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml deleted file mode 100644 index 8cb6df2c..00000000 --- a/.github/workflows/build_test.yml +++ /dev/null @@ -1,118 +0,0 @@ -name: Build and Test - -on: - push: - branches: - - 'ros2' - pull_request: - branches: - - 'ros2/*' - - 'ros2' - workflow_dispatch: - -jobs: - get_ros_distros: - runs-on: ubuntu-latest - steps: - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get -y install curl jq - - name: Get active distributions - run: | - wget https://raw.githubusercontent.com/flynneva/active_ros_distros/0.1.0/main.py -O active_ros_distros.py - python3 -m pip install rosdistro - python3 active_ros_distros.py --distribution-type ros2 - - name: Generate actions matrix - id: set-matrix - run: | - ACTIVE_ROS_DISTROS=(noetic) - DOCKER_DISTRO_MATRIX=() - RAW_DOCKER_JSON=$(curl -s "https://hub.docker.com/v2/repositories/rostooling/setup-ros-docker/tags?page_size=1000") - while read distro; do - ACTIVE_ROS_DISTROS+=( $distro ) - done < "/tmp/active_ros_distros.txt" - DISTRO_STR="[" - MATRIX_STR="[" - for distro in ${ACTIVE_ROS_DISTROS[@]}; do - docker_image=$(echo $RAW_DOCKER_JSON | - jq -r --arg DISTRO "$distro" '.results[] | select(.tag_status=="active") | select(.name | contains("ros-base-latest")) | select(.name | contains($DISTRO)) | .name' | - sort -u) - - # Handle the case if two docker images were declared for one distro - # e.g. rolling moving from one Ubuntu Jammy to Ubuntu Noble - docker_image_arr=($docker_image) - - DISTRO_STR+="\"${distro}\", " - MATRIX_STR+="{docker_image:\"${docker_image_arr[-1]}\",ros_distro:\"${distro}\"}, " - done - - # Remove trailing , at end - DISTRO_STR=${DISTRO_STR%??} - MATRIX_STR=${MATRIX_STR%??} - # Close up brackets - DISTRO_STR+="]" - MATRIX_STR+="]" - echo "DISTRO_STR: ${DISTRO_STR}" - echo "MATRIX_STR: ${MATRIX_STR}" - echo "series=$DISTRO_STR" >> $GITHUB_OUTPUT - echo "matrix=$MATRIX_STR" >> $GITHUB_OUTPUT - outputs: - series: ${{ steps.set-matrix.outputs.series }} - matrix: ${{ steps.set-matrix.outputs.matrix }} - - build_and_test: - runs-on: [ubuntu-latest] - needs: get_ros_distros - strategy: - fail-fast: false - matrix: - ros_distro: ${{ fromJson(needs.get_ros_distros.outputs.series) }} - include: - ${{ fromJson(needs.get_ros_distros.outputs.matrix) }} - container: - image: rostooling/setup-ros-docker:${{ matrix.docker_image }} - steps: - - name: Setup Directories - run: mkdir -p ros_ws/src - - name: checkout - uses: actions/checkout@v4 - with: - path: ros_ws/src - - name: Build and Test ROS 1 - uses: ros-tooling/action-ros-ci@master - if: ${{ contains('["melodic", "noetic"]', matrix.ros_distro) }} - continue-on-error: true - with: - package-name: usb_cam - target-ros1-distro: ${{ matrix.ros_distro }} - vcs-repo-file-url: "" - - name: Build and Test ROS 2 - id: build_and_test_step - uses: ros-tooling/action-ros-ci@master - if: ${{ contains('["melodic", "noetic"]', matrix.ros_distro) == false }} - with: - package-name: usb_cam - target-ros2-distro: ${{ matrix.ros_distro }} - vcs-repo-file-url: "" - colcon-defaults: | - { - "build": { - "mixin": ["coverage-gcc"] - } - } - # If possible, pin the repository in the workflow to a specific commit to avoid - # changes in colcon-mixin-repository from breaking your tests. - colcon-mixin-repository: https://raw.githubusercontent.com/colcon/colcon-mixin-repository/1ddb69bedfd1f04c2f000e95452f7c24a4d6176b/index.yaml - - uses: actions/upload-artifact@v1 - with: - name: colcon-logs-${{ matrix.ros_distro }} - path: ${{ steps.build_and_test_step.outputs.ros-workspace-directory-name }}/log - if: always() - continue-on-error: true - - uses: actions/upload-artifact@v1 - with: - name: lcov-logs-${{ matrix.ros_distro }} - path: ${{ steps.build_and_test_step.outputs.ros-workspace-directory-name }}/lcov - if: always() - continue-on-error: true \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..6a4b6bf3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,23 @@ +name: CI +on: pull_request +jobs: + pr_build: + name: "${{ matrix.ci_script }}" + runs-on: self-hosted + container: + image: omavteam/v4l2_camera:latest + + strategy: + matrix: + ci_script: [pr_compile] + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + set-safe-directory: true + + - name: Run ${{ matrix.ci_script }} + run: | + bash -x ./ci/${{ matrix.ci_script }}.sh diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 64183b09..00000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,130 +0,0 @@ -name: Build Documentation - -on: - push: - branches: [ros2] - pull_request: - branches: [ros2] - workflow_dispatch: - -env: - WORKSPACE_PATH: ros_ws/src/usb_cam - DOXYGEN_ARTIFACT: doxygen_xml - DOXYBOOK_ARTIFACT: api_reference - DOXYBOOK_VERSION: v1.4.0 - -# only run one build doc workflow at a time, cancel any running ones -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - generate-doxygen: - runs-on: ubuntu-latest - steps: - - name: Make sure output directory exists - run: mkdir -p ${{ env.WORKSPACE_PATH }} - - name: Checkout repository - uses: actions/checkout@v2 - with: - path: ${{ env.WORKSPACE_PATH }} - - name: Generate doxygen and upload as artifact - # TODO: figure out way to use WORKSPACE_PATH var here - uses: ./ros_ws/src/usb_cam/.github/actions/generate-doxygen - with: - working-directory: ${{ env.WORKSPACE_PATH }} - doxyfile-path: 'docs/doxygen.config' - artifact-path: ${{ env.WORKSPACE_PATH }}/docs/xml - artifact-name: ${{ env.DOXYGEN_ARTIFACT }} - artifact-retention-days: 30 - generate-doxybook2: - runs-on: ubuntu-latest - needs: generate-doxygen - steps: - - name: Make sure output directory exists - run: mkdir -p ${{ env.WORKSPACE_PATH }} - - name: Checkout repository - uses: actions/checkout@v2 - with: - path: ${{ env.WORKSPACE_PATH }} - - name: Generate API reference and upload as artifact - # TODO: figure out way to use WORKSPACE_PATH var here - uses: ./ros_ws/src/usb_cam/.github/actions/generate-doxybook - with: - input-doxygen-artifact: ${{ env.DOXYGEN_ARTIFACT }} - doxygen-artifact-extraction-path: ${{ env.WORKSPACE_PATH }}/docs/xml - doxybook2-version: ${{ env.DOXYBOOK_VERSION }} - doxybook2-config-path: ${{ env.WORKSPACE_PATH }}/docs/doxybook2_config.json - output-path: ${{ env.WORKSPACE_PATH }}/docs/api-reference - base-url: /usb_cam/latest/api-reference/ - artifact-path: ${{ env.WORKSPACE_PATH }}/docs/api-reference - artifact-name: ${{ env.DOXYBOOK_ARTIFACT }} - artifact-retention-days: 30 - build-mkdocs: - runs-on: ubuntu-latest - if: github.ref != 'refs/heads/ros2' - needs: generate-doxybook2 - steps: - - name: Make sure output directory exists - run: mkdir -p ${{ env.WORKSPACE_PATH }} - - name: Checkout repository - uses: actions/checkout@v2 - with: - path: ${{ env.WORKSPACE_PATH }} - - name: Download API Reference - uses: actions/download-artifact@v3 - with: - name: ${{ env.DOXYBOOK_ARTIFACT }} - path: ${{ env.WORKSPACE_PATH }}/docs/api-reference - - name: Build mkdocs site - run: | - cd ${{ env.WORKSPACE_PATH }} - # ensure gh-pages git history is fetched - git fetch origin gh-pages --depth=1 - sudo apt-get update -y - # install mkdocs dependencies - python3 -m pip install -r docs/requirements.txt - # build site - mkdocs build - - name: Upload docs site - uses: actions/upload-artifact@v3 - with: - name: usb_cam_site - path: ${{ env.WORKSPACE_PATH }}/site - deploy_docs: - runs-on: ubuntu-latest - # only run on main ros2 branch after jobs listed in `needs` have finished (successful or not) - if: github.ref == 'refs/heads/ros2' && always() - needs: [build-mkdocs] - steps: - - name: Make sure output directory exists - run: mkdir -p ${{ env.WORKSPACE_PATH }} - - name: Checkout repository - uses: actions/checkout@v2 - with: - path: ${{ env.WORKSPACE_PATH }} - - name: Download API reference - uses: actions/download-artifact@v3 - with: - name: ${{ env.DOXYBOOK_ARTIFACT }} - path: ${{ env.WORKSPACE_PATH }}/docs/api-reference - - name: Deploy mkdocs site - shell: bash - run: | - cd ${{ env.WORKSPACE_PATH }} - # ensure gh-pages git history is fetched - git fetch origin gh-pages --depth=1 - sudo apt-get update -y - # install docs dependencies - python3 -m pip install -r docs/requirements.txt - # TODO: mike rebuilds entire site, instead we should - # skip the build and download site artifact from previous workflow - if [ -z ${{ github.event.release.tag_name }}]; then - export NEW_VERSION=main - else - export NEW_VERSION=${{ github.event.release.tag_name }} - fi - git config user.name doc-bot - git config user.email doc-bot@usb_cam.com - mike deploy --push --update-aliases $NEW_VERSION latest - diff --git a/.gitignore b/.gitignore index f2ea1490..b9fc4b5c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ docs/xml site/ resources/* +build/* diff --git a/CMakeLists.txt b/CMakeLists.txt index 454ce4df..a70e031e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,27 +6,16 @@ if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 17) endif() -if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - if($ENV{ROS_VERSION} EQUAL 2) - add_compile_options(-Wall -Wextra -Wpedantic -Werror) - endif() -endif() - -# Remove once ROS 1 Noetic is deprecated -if($ENV{ROS_VERSION} EQUAL 1) - find_package(catkin REQUIRED COMPONENTS - cv_bridge - roscpp - std_msgs - std_srvs - sensor_msgs - camera_info_manager - image_transport - rosconsole) -else() - find_package(ament_cmake_auto REQUIRED) - ament_auto_find_build_dependencies() -endif() +find_package(catkin REQUIRED COMPONENTS + cv_bridge + roscpp + std_msgs + std_srvs + sensor_msgs + camera_info_manager + image_transport + rosconsole +) find_package(OpenCV REQUIRED) find_package(Eigen3 REQUIRED) diff --git a/README.md b/README.md index fba26b3e..b4ede0fb 100644 --- a/README.md +++ b/README.md @@ -15,5 +15,5 @@ In order to run the cameras on the Jetson Xavier NX, the [Arducam quickstart gui ### User groups TODO: Is the spi group actually needed? Or gpio? -### OpenCV 4.9.X +### OpenCV OpenCV >=4.9.0 is required in order to run the debayering process. An installation scripts inside `.devcontainer` is provided as help on how to compile it from source. diff --git a/ci/pr_compile.sh b/ci/pr_compile.sh new file mode 100755 index 00000000..99e58cd7 --- /dev/null +++ b/ci/pr_compile.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +rm -rf build +mkdir -p build +cd build +if cmake .. +then + echo "CMake successfull" + if make + then + echo "Make successfull" + else + echo "Make failed" + exit 1 + fi +else + echo "CMake failed" + exit 1 +fi From 0922c05d94b6747e2cdd97039eee0220823aa58a Mon Sep 17 00:00:00 2001 From: marcojob <44396071+marcojob@users.noreply.github.com> Date: Fri, 1 Nov 2024 15:59:06 +0100 Subject: [PATCH 4/7] devcontainer: Install rosdeps --- .devcontainer/Dockerfile | 31 ++++++++++++----- .devcontainer/build_and_push_images.sh | 2 +- .devcontainer/build_opencv.sh | 9 +++-- .devcontainer/devcontainer_all_packages.sh | 40 ++++++++++++++++++++++ 4 files changed, 67 insertions(+), 15 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 7609b60e..56556d98 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,11 +1,12 @@ -FROM ros:noetic-ros-core +# Base Stage: ROS and CUDA Installation +FROM ros:noetic-ros-core AS base ARG TZ=Europe/Zurich ENV DEBIAN_FRONTEND=noninteractive ENV PATH="${PATH}:/opt/hpcx/ompi/bin" # All apt packages -COPY devcontainer_all_packages.sh /tmp/devcontainer_all_packages.sh +COPY .devcontainer/devcontainer_all_packages.sh /tmp/devcontainer_all_packages.sh RUN /tmp/devcontainer_all_packages.sh && rm -f /tmp/devcontainer_all_packages.sh ENV LANG="en_US.UTF-8" \ @@ -24,9 +25,18 @@ RUN wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86 sudo apt update && \ sudo apt -y install cuda-toolkit-12-6 cudnn9-cuda-12 +# OpenCV Build Stage +FROM base AS opencv_build + # OpenCV 4.10.0 install -COPY build_opencv.sh /tmp/build_opencv.sh -RUN bash /tmp/build_opencv.sh +COPY .devcontainer/build_opencv.sh /tmp/build_opencv.sh +RUN bash /tmp/build_opencv.sh && rm -rf /tmp/build_opencv.sh + +# Final Stage: Combine ROS, CUDA, and OpenCV +FROM base AS final + +# Copy OpenCV installation from the build stage +COPY --from=opencv_build /usr/local /usr/local # Git-prompt: Source RUN echo 'if [ -f "$HOME/.bash-git-prompt/gitprompt.sh" ]; then GIT_PROMPT_ONLY_IN_REPO=1; source "$HOME/.bash-git-prompt/gitprompt.sh"; fi' >> /home/asl/.bash_aliases @@ -34,12 +44,15 @@ RUN echo 'if [ -f "$HOME/.bash-git-prompt/gitprompt.sh" ]; then GIT_PROMPT_ONLY_ # ROS: Source RUN echo "source /opt/ros/noetic/setup.bash" >> /home/asl/.bash_aliases -# ROS: rosdep init -RUN sudo rosdep init && \ - rosdep update && \ - rosdep install --from-paths . --ignore-src -r -y +# ROS: rosdep install (TODO: maybe not so clean?) +RUN sudo rosdep init +USER asl +RUN rosdep update +COPY package.xml /tmp/ +RUN rosdep install --from-paths /tmp/ --ignore-src -r -y -v +USER root # Clean up RUN rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/* /var/cache/apt/archives/* -ENTRYPOINT ["/bin/bash"] +ENTRYPOINT ["/bin/bash"] \ No newline at end of file diff --git a/.devcontainer/build_and_push_images.sh b/.devcontainer/build_and_push_images.sh index 50c54d5d..35ec1b1f 100755 --- a/.devcontainer/build_and_push_images.sh +++ b/.devcontainer/build_and_push_images.sh @@ -1 +1 @@ -docker buildx build -t omavteam/v4l2_camera:latest -f .devcontainer/Dockerfile --push +docker buildx build -t omavteam/v4l2_camera:latest -f .devcontainer/Dockerfile --push . diff --git a/.devcontainer/build_opencv.sh b/.devcontainer/build_opencv.sh index 2e3ffcff..208995b9 100755 --- a/.devcontainer/build_opencv.sh +++ b/.devcontainer/build_opencv.sh @@ -149,15 +149,14 @@ configure() { local CMAKEFLAGS=" -D BUILD_EXAMPLES=OFF - -D BUILD_opencv_python2=ON - -D BUILD_opencv_python3=ON + -D BUILD_opencv_python2=OFF + -D BUILD_opencv_python3=OFF -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=${PREFIX} -D CUDA_ARCH_BIN=${CUDA_ARCH_BIN} - -D CUDA_ARCH_PTX= -D CUDA_FAST_MATH=ON -D CUDNN_VERSION='${CUDNN_VERSION}' - -D EIGEN_INCLUDE_PATH=/usr/include/eigen3 + -D EIGEN_INCLUDE_PATH=/usr/include/eigen3 -D ENABLE_NEON=OFF -D OPENCV_DNN_CUDA=ON -D OPENCV_ENABLE_NONFREE=ON @@ -202,7 +201,7 @@ main () { # prepare for the build: setup - install_dependencies + # install_dependencies # if [[ ! -d "$BUILD_DIR/build_opencv" ]] ; then git_source ${VER} # fi diff --git a/.devcontainer/devcontainer_all_packages.sh b/.devcontainer/devcontainer_all_packages.sh index c570e40d..cdf99f60 100755 --- a/.devcontainer/devcontainer_all_packages.sh +++ b/.devcontainer/devcontainer_all_packages.sh @@ -9,12 +9,51 @@ main() { build-essential ca-certificates ccache + cmake curl + gfortran + git gnupg htop + libatlas-base-dev + libavcodec-dev + libavformat-dev + libavresample-dev + libcanberra-gtk3-module + libdc1394-22-dev + libeigen3-dev + libglew-dev + libgstreamer-plugins-base1.0-dev + libgstreamer-plugins-good1.0-dev + libgstreamer1.0-dev + libgtk-3-dev + libjpeg-dev + libjpeg-turbo8-dev + libjpeg8-dev + liblapack-dev + liblapacke-dev + libopenblas-dev + libpng-dev + libpostproc-dev + libswscale-dev + libtbb-dev + libtbb2 + libtesseract-dev + libtiff-dev + libv4l-dev + libx264-dev + libxine2-dev + libxvidcore-dev locales net-tools + pkg-config + python-dev + python-numpy + python3-dev + python3-matplotlib + python3-numpy python3-rosdep + qv4l2 software-properties-common ssh sudo @@ -24,6 +63,7 @@ main() { vim wget zlib1g + zlib1g-dev ) apt-get update From 00717807840a2a30967119cf62a1981dbb6c7bdf Mon Sep 17 00:00:00 2001 From: marcojob <44396071+marcojob@users.noreply.github.com> Date: Fri, 1 Nov 2024 16:10:06 +0100 Subject: [PATCH 5/7] ci: Source ros? --- ci/pr_compile.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/pr_compile.sh b/ci/pr_compile.sh index 99e58cd7..58fd480c 100755 --- a/ci/pr_compile.sh +++ b/ci/pr_compile.sh @@ -1,4 +1,5 @@ #!/bin/bash +source /opt/ros/noetic/setup.bash rm -rf build mkdir -p build From fa8d35e2ccba5837bcbe242296b09e9f6fc09ca4 Mon Sep 17 00:00:00 2001 From: marcojob <44396071+marcojob@users.noreply.github.com> Date: Fri, 1 Nov 2024 20:45:22 +0100 Subject: [PATCH 6/7] devcontainer: Add TensorRT to container - Code still in draft state --- .devcontainer/Dockerfile | 9 + CMakeLists.txt | 88 ++++--- include/usb_cam/learning/interface.hpp | 28 +-- include/usb_cam/learning/raft.hpp | 43 ++++ src/interface.cpp | 48 +++- src/usb_cam_node.cpp | 336 ++++++++++++------------- 6 files changed, 321 insertions(+), 231 deletions(-) create mode 100644 include/usb_cam/learning/raft.hpp diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 56556d98..3140129a 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -25,6 +25,15 @@ RUN wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86 sudo apt update && \ sudo apt -y install cuda-toolkit-12-6 cudnn9-cuda-12 +# CUDA: Add PATH and LD_LIBRARY_PATH to .bash_aliases +RUN echo 'export PATH=$PATH:/usr/local/cuda/bin' >> /home/asl/.bash_aliases && \ + echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda/lib64' >> /home/asl/.bash_aliases + +# TensorRT: Install from .deb file +COPY resources/nv-tensorrt-local-repo-ubuntu2004-10.6.0-cuda-12.6_1.0-1_amd64.deb /tmp/tensorrt.deb +RUN sudo dpkg -i /tmp/tensorrt.deb || sudo apt-get install -f -y +RUN sudo cp /var/nv-tensorrt-local-repo-ubuntu2004-10.6.0-cuda-12.6/*-keyring.gpg /usr/share/keyrings/ && sudo apt update && sudo apt install -y tensorrt + # OpenCV Build Stage FROM base AS opencv_build diff --git a/CMakeLists.txt b/CMakeLists.txt index a70e031e..733b3bd4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 17) endif() +# Find ROS and catkin dependencies find_package(catkin REQUIRED COMPONENTS cv_bridge roscpp @@ -17,106 +18,129 @@ find_package(catkin REQUIRED COMPONENTS rosconsole ) +# Find OpenCV, Eigen, and CUDA find_package(OpenCV REQUIRED) find_package(Eigen3 REQUIRED) +find_package(CUDA REQUIRED) +# Find FFmpeg libraries with PkgConfig find_package(PkgConfig REQUIRED) pkg_check_modules(avcodec REQUIRED libavcodec) pkg_check_modules(avutil REQUIRED libavutil) pkg_check_modules(swscale REQUIRED libswscale) -if(EXISTS ${avcodec}) - message(STATUS "Found libavcodec: ${avcodec}") +# Output messages if libraries are found +if(avcodec_FOUND) + message(STATUS "Found libavcodec: ${avcodec_LIBRARIES}") endif() -if(EXISTS ${avutil}) - message(STATUS "Found libavutil: ${avutil}") +if(avutil_FOUND) + message(STATUS "Found libavutil: ${avutil_LIBRARIES}") endif() -if(EXISTS ${swscale}) - message(STATUS "Found libswscale: ${swscale}") +if(swscale_FOUND) + message(STATUS "Found libswscale: ${swscale_LIBRARIES}") +endif() + +# Find TensorRT libraries +set(TENSORRT_LIB_DIR /usr/lib/x86_64-linux-gnu CACHE PATH "Path to TensorRT libraries") +find_library(NVINFER nvinfer PATHS ${TENSORRT_LIB_DIR}) +find_library(NVINFER_PLUGIN nvinfer_plugin PATHS ${TENSORRT_LIB_DIR}) + +# Check if TensorRT libraries are found +if(NOT NVINFER OR NOT NVINFER_PLUGIN) + message(FATAL_ERROR "TensorRT libraries not found. Set TENSORRT_LIB_DIR correctly.") endif() ## Build the USB camera library -## Do not use ament_auto here so as to not link to rclcpp add_library(${PROJECT_NAME} SHARED src/usb_cam.cpp + src/interface.cpp ) +# Set include directories target_include_directories(${PROJECT_NAME} PUBLIC "include" ${OpenCV_INCLUDE_DIRS} ${Eigen3_INCLUDE_DIRS} ${catkin_INCLUDE_DIRS} + ${CUDA_INCLUDE_DIRS} ) +# Link libraries for the usb_cam library target_link_libraries(${PROJECT_NAME} - ${avcodec_LIBRARIES} - ${avutil_LIBRARIES} - ${swscale_LIBRARIES} - ${OpenCV_LIBRARIES} - Eigen3::Eigen + PUBLIC + Eigen3::Eigen + ${OpenCV_LIBRARIES} + ${CUDA_LIBRARIES} + PRIVATE + ${avcodec_LIBRARIES} + ${avutil_LIBRARIES} + ${swscale_LIBRARIES} + ${NVINFER} + ${NVINFER_PLUGIN} ) +# Define catkin package catkin_package( INCLUDE_DIRS include LIBRARIES ${PROJECT_NAME} ) +# Build the USB camera node executable add_executable(${PROJECT_NAME}_node src/usb_cam_node.cpp) target_link_libraries(${PROJECT_NAME}_node ${PROJECT_NAME} ${catkin_LIBRARIES} - Eigen3::Eigen + ${avcodec_LIBRARIES} + ${avutil_LIBRARIES} + ${swscale_LIBRARIES} ) + +# Ensure include directories are set target_include_directories(${PROJECT_NAME}_node PUBLIC ${catkin_INCLUDE_DIRS}) +# Testing if(BUILD_TESTING) if($ENV{ROS_VERSION} EQUAL 2) find_package(ament_lint_auto REQUIRED) ament_lint_auto_find_test_dependencies() - find_package(ament_cmake_gtest) # Unit tests - ament_add_gtest(test_usb_cam_utils - test/test_usb_cam_utils.cpp) - target_link_libraries(test_usb_cam_utils - ${PROJECT_NAME}) - ament_add_gtest(test_pixel_formats - test/test_pixel_formats.cpp) - target_link_libraries(test_pixel_formats - ${PROJECT_NAME}) - # TODO(flynneva): rewrite this test in another PR - # Integration tests - # ament_add_gtest(test_usb_cam_lib - # test/test_usb_cam_lib.cpp) - # target_link_libraries(test_usb_cam_lib - # ${PROJECT_NAME}) + ament_add_gtest(test_usb_cam_utils test/test_usb_cam_utils.cpp) + target_link_libraries(test_usb_cam_utils ${PROJECT_NAME}) + + ament_add_gtest(test_pixel_formats test/test_pixel_formats.cpp) + target_link_libraries(test_pixel_formats ${PROJECT_NAME}) + + # Integration tests (commented out as per TODO) + # ament_add_gtest(test_usb_cam_lib test/test_usb_cam_lib.cpp) + # target_link_libraries(test_usb_cam_lib ${PROJECT_NAME}) endif() endif() - +# Installation rules if($ENV{ROS_VERSION} EQUAL 1) install(TARGETS ${PROJECT_NAME}_node ${PROJECT_NAME} RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} ) - ## Copy launch files + # Copy launch files install(DIRECTORY launch/ DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch FILES_MATCHING PATTERN "*.launch" ) + # Include header files install(DIRECTORY include/${PROJECT_NAME}/ DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" ) else() - install( - PROGRAMS scripts/show_image.py + install(PROGRAMS scripts/show_image.py DESTINATION lib/${PROJECT_NAME}) install(TARGETS diff --git a/include/usb_cam/learning/interface.hpp b/include/usb_cam/learning/interface.hpp index 420030cb..30e75dcc 100644 --- a/include/usb_cam/learning/interface.hpp +++ b/include/usb_cam/learning/interface.hpp @@ -16,21 +16,18 @@ class LearningInterface { public: LearningInterface() : _model_path("") {} - virtual void set_input(const float* input_buffer) { - cudaMemcpy(_input_buffer, input_buffer, _input_size, cudaMemcpyHostToDevice); - } - - virtual void get_output(float* output_buffer) { - cudaMemcpy(output_buffer, _outputBuffer, _outputSize, cudaMemcpyDeviceToHost); - } + virtual void set_input(const uint8_t* input_buffer, size_t height, size_t width) = 0; + virtual void get_output(uint8_t* output_buffer) = 0; + virtual void publish() = 0; void load_model(); bool run_inference(size_t batch_size); + virtual ~LearningInterface() { - if (_context) _context->destroy(); - if (_engine) _engine->destroy(); - if (_runtime) _runtime->destroy(); + // if (_context) _context->destroy(); + // if (_engine) _engine->destroy(); + // if (_runtime) _runtime->destroy(); if (_buffers[0]) cudaFree(_buffers[0]); if (_buffers[1]) cudaFree(_buffers[1]); @@ -40,17 +37,18 @@ class LearningInterface { } protected: + float* _input_buffer = nullptr; + float* _output_buffer = nullptr; + size_t input_height; + size_t input_width; + size_t output_height; + size_t output_width; std::string _model_path; private: - float* _input_buffer = nullptr; - float* _output_buffer = nullptr; nvinfer1::ICudaEngine* _engine = nullptr; nvinfer1::IExecutionContext* _context = nullptr; nvinfer1::IRuntime* _runtime = nullptr; - size_t _input_size = 0; - size_t _output_size = 0; - std::string _model_path; void* _buffers[2] = { nullptr, nullptr }; }; diff --git a/include/usb_cam/learning/raft.hpp b/include/usb_cam/learning/raft.hpp new file mode 100644 index 00000000..1cf9d439 --- /dev/null +++ b/include/usb_cam/learning/raft.hpp @@ -0,0 +1,43 @@ +#ifndef RAFT_HPP_ +#define RAFT_HPP_ + +#include "interface.hpp" +#include +#include + +class Raft : public LearningInterface { +public: + Raft(std::string model_path, size_t network_height, size_t network_width) : _network_height(network_height), _network_width(network_width) { + _model_path = model_path; + _network_size = cv::Size(_network_width, _network_height); + } + + void set_input(const uint8_t* input_buffer, size_t height, size_t width) override { + // Resize frame to network size + cv::Mat input_frame(height, width, CV_8UC1, (void*)input_buffer); + cv::Mat resized_frame; + cv::resize(input_frame, resized_frame, _network_size); + + cv::Mat float_frame; + resized_frame.convertTo(float_frame, CV_32FC1, _uint8_to_float); + + cudaMemcpy(_input_buffer, float_frame.ptr(), _network_width * _network_height * sizeof(float), cudaMemcpyHostToDevice); + } + + void get_output(uint8_t* output_buffer) override { + // TODO + } + + void publish() override { + // TODO + } + + +private: + const size_t _network_height; + const size_t _network_width; + cv::Size _network_size; + static constexpr float _uint8_to_float = 1.0f / 255.0f; +}; + +#endif // RAFT_HPP_ \ No newline at end of file diff --git a/src/interface.cpp b/src/interface.cpp index 01d0f43c..6c93eca3 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -13,28 +13,50 @@ void LearningInterface::load_model() { file.read(model_data.data(), model_size); file.close(); - _runtime = nvinfer1::createInferRuntime(nvinfer1::Logger()); - if (_runtime != nullptr) { - _engine = _runtime->deserializeCudaEngine(model_data.data(), model_size, nullptr); + // Create logger instance + class Logger : public nvinfer1::ILogger { + public: + void log(Severity severity, const char* msg) noexcept override { + std::cout << msg << std::endl; // Log the message + } + } logger; // Create a logger instance + _runtime = nvinfer1::createInferRuntime(logger); + if (_runtime != nullptr) { + _engine = _runtime->deserializeCudaEngine(model_data.data(), model_size); if (_engine != nullptr) { _context = _engine->createExecutionContext(); - if (_context != nullptr) { // Allocate buffers for input and output - _inputSize = _engine->getBindingDimensions(0).volume() * sizeof(float); - _outputSize = _engine->getBindingDimensions(1).volume() * sizeof(float); + size_t input_size; + size_t output_size; + for (int io = 0; io < _engine->getNbIOTensors(); io++) { + const char* name = _engine->getIOTensorName(io); + std::cout << io << ": " << name; + const nvinfer1::Dims dims = _engine->getTensorShape(name); + + size_t total_dims = 1; + for (int d = 0; d < dims.nbDims; d++) { + total_dims *= dims.d[d]; + } + + std::cout << " size: " << total_dims << std::endl; + if (io == 0) { + input_size = total_dims * sizeof(float); + } else if (io == 1) { + output_size = total_dims * sizeof(float); + } + } // Allocate device buffers - cudaMalloc(&_buffers[0], _inputSize); - cudaMalloc(&_buffers[1], _outputSize); + cudaMalloc(&_buffers[0], input_size); + cudaMalloc(&_buffers[1], output_size); // Allocate CPU buffers - _inputBuffer = new float[_inputSize / sizeof(float)]; - _outputBuffer = new float[_outputSize / sizeof(float)]; - - std::cout << "TensorRT model loaded successfully from: " << model_path << std::endl; + _input_buffer = new float[input_size / sizeof(float)]; + _output_buffer = new float[output_size / sizeof(float)]; + std::cout << "TensorRT model loaded successfully from: " << _model_path << std::endl; } else { std::cout << "Failed to create execution context." << std::endl; } @@ -50,7 +72,7 @@ void LearningInterface::load_model() { } bool LearningInterface::run_inference(size_t batch_size) { - if (!_context->execute(batch_size, _buffers)) { + if (!_context->executeV2(_buffers)) { std::cerr << "Failed to execute inference." << std::endl; return false; } diff --git a/src/usb_cam_node.cpp b/src/usb_cam_node.cpp index 0fab4abd..de1d0761 100644 --- a/src/usb_cam_node.cpp +++ b/src/usb_cam_node.cpp @@ -25,6 +25,7 @@ // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. +#include #include #include @@ -36,195 +37,188 @@ #include "usb_cam/usb_cam.hpp" #include "usb_cam/utils.hpp" +#include "usb_cam/learning/interface.hpp" +#include "usb_cam/learning/raft.hpp" -namespace usb_cam -{ - -class UsbCamNode -{ +namespace usb_cam { +class UsbCamNode { public: - // private ROS node handle - ros::NodeHandle m_node; - - // shared image message - sensor_msgs::Image m_image; - image_transport::CameraPublisher m_image_pub; - - boost::shared_ptr m_camera_info; - - UsbCam m_camera; - - ros::ServiceServer m_service_start, m_service_stop; - - parameters_t m_parameters; - - bool m_service_startcap(std_srvs::Empty::Request & req, std_srvs::Empty::Response & res) - { - (void)req; - (void)res; - m_camera.start_capturing(); - return true; - } - - - bool m_service_stopcap(std_srvs::Empty::Request & req, std_srvs::Empty::Response & res) - { - (void)req; - (void)res; - m_camera.stop_capturing(); - return true; - } - - UsbCamNode() - : m_node("~") - { - // advertise the main image topic - image_transport::ImageTransport it(m_node); - m_image_pub = it.advertiseCamera("image_raw", 1); - - // grab the parameters - m_node.param("video_device", m_parameters.device_name, std::string("/dev/video0")); - m_node.param("brightness", m_parameters.brightness, -1); // 0-255, -1 "leave alone" - m_node.param("contrast", m_parameters.contrast, -1); // 0-255, -1 "leave alone" - m_node.param("saturation", m_parameters.saturation, -1); // 0-255, -1 "leave alone" - m_node.param("sharpness", m_parameters.sharpness, -1); // 0-255, -1 "leave alone" - // possible values: mmap, read, userptr - m_node.param("io_method", m_parameters.io_method_name, std::string("mmap")); - m_node.param("image_width", m_parameters.image_width, 640); - m_node.param("image_height", m_parameters.image_height, 480); - m_node.param("framerate", m_parameters.framerate, 30); - // possible values: yuyv, uyvy, mjpeg, yuvmono10, rgb24 - m_node.param("pixel_format", m_parameters.pixel_format_name, std::string("mjpeg")); - m_node.param("av_device_format", m_parameters.av_device_format, std::string("")); - // enable/disable autofocus - m_node.param("autofocus", m_parameters.autofocus, false); - m_node.param("focus", m_parameters.focus, -1); // 0-255, -1 "leave alone" - // enable/disable autoexposure - m_node.param("autoexposure", m_parameters.autoexposure, true); - m_node.param("exposure", m_parameters.exposure, 100); - m_node.param("gain", m_parameters.gain, -1); // 0-100?, -1 "leave alone" - // enable/disable auto white balance temperature - m_node.param("auto_white_balance", m_parameters.auto_white_balance, true); - m_node.param("white_balance", m_parameters.white_balance, 4000); - m_node.param("wb_blue_gain", m_parameters.wb_blue_gain, 0.0f); - m_node.param("wb_green_gain", m_parameters.wb_green_gain, 0.0f); - m_node.param("wb_red_gain", m_parameters.wb_red_gain, 0.0f); - - // load the camera info - m_node.param("camera_frame_id", m_image.header.frame_id, std::string("head_camera")); - m_node.param("camera_name", m_parameters.camera_name, std::string("head_camera")); - m_node.param("camera_info_url", m_parameters.camera_info_url, std::string("")); - m_camera_info.reset( - new camera_info_manager::CameraInfoManager( - m_node, m_parameters.camera_name, m_parameters.camera_info_url)); - - // create Services - m_service_start = \ - m_node.advertiseService("start_capture", &UsbCamNode::m_service_startcap, this); - m_service_stop = \ - m_node.advertiseService("stop_capture", &UsbCamNode::m_service_stopcap, this); - - // check for default camera info - if (!m_camera_info->isCalibrated()) { - m_camera_info->setCameraName(m_parameters.camera_name); - sensor_msgs::CameraInfo camera_info; - camera_info.header.frame_id = m_image.header.frame_id; - camera_info.width = m_parameters.image_width; - camera_info.height = m_parameters.image_height; - m_camera_info->setCameraInfo(camera_info); - } - - - ROS_INFO( - "Starting '%s' (%s) at %dx%d via %s (%s) at %i FPS", - m_parameters.camera_name.c_str(), m_parameters.device_name.c_str(), - m_parameters.image_width, m_parameters.image_height, m_parameters.io_method_name.c_str(), - m_parameters.pixel_format_name.c_str(), m_parameters.framerate); - - // set the IO method - io_method_t io_method = usb_cam::utils::io_method_from_string(m_parameters.io_method_name); - if (io_method == io_method_t::IO_METHOD_UNKNOWN) { - ROS_FATAL("Unknown IO method '%s'", m_parameters.io_method_name.c_str()); - m_node.shutdown(); - return; - } + // Private ROS node handle + ros::NodeHandle m_node; - // setup camera according to new interface. - m_camera.configure(m_parameters, io_method); + // Shared image message + sensor_msgs::Image m_image; + image_transport::CameraPublisher m_image_pub; + boost::shared_ptr m_camera_info; + UsbCam m_camera; - set_v4l2_params(); + ros::ServiceServer m_service_start, m_service_stop; - m_camera.start(); - } + parameters_t m_parameters; - virtual ~UsbCamNode() - { - m_camera.shutdown(); - } + bool m_service_startcap(std_srvs::Empty::Request & req, std_srvs::Empty::Response & res) { + (void)req; + (void)res; + m_camera.start_capturing(); + return true; + } - bool take_and_send_image() - { - // grab the new image + bool m_service_stopcap(std_srvs::Empty::Request & req, std_srvs::Empty::Response & res) { + (void)req; + (void)res; + m_camera.stop_capturing(); + return true; + } - // fill in the image message - auto stamp = m_camera.get_image_timestamp(); - m_image.header.stamp.sec = stamp.tv_sec; - m_image.header.stamp.nsec = stamp.tv_nsec; + UsbCamNode() : m_node("~") { + // Setup the network that outputs derivates of the image captured + networks.push_back(std::make_unique("resources/raft.onnx", 240, 320)); + + // Advertise the main image topic + image_transport::ImageTransport it(m_node); + m_image_pub = it.advertiseCamera("image_raw", 1); + + // grab the parameters + m_node.param("video_device", m_parameters.device_name, std::string("/dev/video0")); + m_node.param("brightness", m_parameters.brightness, -1); // 0-255, -1 "leave alone" + m_node.param("contrast", m_parameters.contrast, -1); // 0-255, -1 "leave alone" + m_node.param("saturation", m_parameters.saturation, -1); // 0-255, -1 "leave alone" + m_node.param("sharpness", m_parameters.sharpness, -1); // 0-255, -1 "leave alone" + // possible values: mmap, read, userptr + m_node.param("io_method", m_parameters.io_method_name, std::string("mmap")); + m_node.param("image_width", m_parameters.image_width, 640); + m_node.param("image_height", m_parameters.image_height, 480); + m_node.param("framerate", m_parameters.framerate, 30); + // possible values: yuyv, uyvy, mjpeg, yuvmono10, rgb24 + m_node.param("pixel_format", m_parameters.pixel_format_name, std::string("mjpeg")); + m_node.param("av_device_format", m_parameters.av_device_format, std::string("")); + // enable/disable autofocus + m_node.param("autofocus", m_parameters.autofocus, false); + m_node.param("focus", m_parameters.focus, -1); // 0-255, -1 "leave alone" + // enable/disable autoexposure + m_node.param("autoexposure", m_parameters.autoexposure, true); + m_node.param("exposure", m_parameters.exposure, 100); + m_node.param("gain", m_parameters.gain, -1); // 0-100?, -1 "leave alone" + // enable/disable auto white balance temperature + m_node.param("auto_white_balance", m_parameters.auto_white_balance, true); + m_node.param("white_balance", m_parameters.white_balance, 4000); + m_node.param("wb_blue_gain", m_parameters.wb_blue_gain, 0.0f); + m_node.param("wb_green_gain", m_parameters.wb_green_gain, 0.0f); + m_node.param("wb_red_gain", m_parameters.wb_red_gain, 0.0f); + + // Load the camera info + m_node.param("camera_frame_id", m_image.header.frame_id, std::string("head_camera")); + m_node.param("camera_name", m_parameters.camera_name, std::string("head_camera")); + m_node.param("camera_info_url", m_parameters.camera_info_url, std::string("")); + m_camera_info.reset(new camera_info_manager::CameraInfoManager(m_node, m_parameters.camera_name, m_parameters.camera_info_url)); + + // Create Services + m_service_start = m_node.advertiseService("start_capture", &UsbCamNode::m_service_startcap, this); + m_service_stop = m_node.advertiseService("stop_capture", &UsbCamNode::m_service_stopcap, this); + + // Check for default camera info + if (!m_camera_info->isCalibrated()) { + m_camera_info->setCameraName(m_parameters.camera_name); + sensor_msgs::CameraInfo camera_info; + camera_info.header.frame_id = m_image.header.frame_id; + camera_info.width = m_parameters.image_width; + camera_info.height = m_parameters.image_height; + m_camera_info->setCameraInfo(camera_info); + } + + ROS_INFO("Starting '%s' (%s) at %dx%d via %s (%s) at %i FPS", + m_parameters.camera_name.c_str(), m_parameters.device_name.c_str(), + m_parameters.image_width, m_parameters.image_height, m_parameters.io_method_name.c_str(), + m_parameters.pixel_format_name.c_str(), m_parameters.framerate + ); + + // Set the IO method + io_method_t io_method = usb_cam::utils::io_method_from_string(m_parameters.io_method_name); + if (io_method == io_method_t::IO_METHOD_UNKNOWN) { + ROS_FATAL("Unknown IO method '%s'", m_parameters.io_method_name.c_str()); + m_node.shutdown(); + return; + } + + // Setup camera according to new interface. + m_camera.configure(m_parameters, io_method); + set_v4l2_params(); + m_camera.start(); + } - // Only resize if required - if (m_image.data.size() != m_camera.get_image_size_in_bytes()) { - m_image.width = m_camera.get_image_width(); - m_image.height = m_camera.get_image_height(); - m_image.encoding = m_camera.get_pixel_format()->ros(); - m_image.step = m_camera.get_image_step(); + virtual ~UsbCamNode() { + m_camera.shutdown(); + } - m_image.data.resize(m_camera.get_image_size_in_bytes()); + bool take_and_send_image() { + // Fill in the image message + auto stamp = m_camera.get_image_timestamp(); + m_image.header.stamp.sec = stamp.tv_sec; + m_image.header.stamp.nsec = stamp.tv_nsec; + + // Only resize if required + if (m_image.data.size() != m_camera.get_image_size_in_bytes()) { + m_image.width = m_camera.get_image_width(); + m_image.height = m_camera.get_image_height(); + m_image.encoding = m_camera.get_pixel_format()->ros(); + m_image.step = m_camera.get_image_step(); + m_image.data.resize(m_camera.get_image_size_in_bytes()); + } + + // Fill in image data + m_camera.get_image(reinterpret_cast(&m_image.data[0])); + + // Grab the camera info + sensor_msgs::CameraInfoPtr ci(new sensor_msgs::CameraInfo(m_camera_info->getCameraInfo())); + ci->header.frame_id = m_image.header.frame_id; + ci->header.stamp = m_image.header.stamp; + + // Publish the image + m_image_pub.publish(m_image, *ci); + + // Run all the networks + for (const auto& net : networks) { + net->set_input(m_image.data.data(), 1920, 1200); + if (net->run_inference(1)) { + net->publish(); + } + } + + return true; } - // Fill in image data - m_camera.get_image(reinterpret_cast(&m_image.data[0])); - - // grab the camera info - sensor_msgs::CameraInfoPtr ci(new sensor_msgs::CameraInfo(m_camera_info->getCameraInfo())); - ci->header.frame_id = m_image.header.frame_id; - ci->header.stamp = m_image.header.stamp; - - // publish the image - m_image_pub.publish(m_image, *ci); - - return true; - } - - bool spin() - { - ros::Rate loop_rate(this->m_parameters.framerate); - while (m_node.ok()) { - if (m_camera.is_capturing()) { - if (!take_and_send_image()) {ROS_WARN("V4L camera did not respond in time.");} - } - ros::spinOnce(); - loop_rate.sleep(); + bool spin() { + ros::Rate loop_rate(this->m_parameters.framerate); + while (m_node.ok()) { + if (m_camera.is_capturing()) { + if (!take_and_send_image()) { + ROS_WARN("V4L camera did not respond in time."); + } + } + ros::spinOnce(); + loop_rate.sleep(); + } + return true; } - return true; - } - void set_v4l2_params() - { - // Sheck auto exposure - m_camera.set_v4l_parameter("exposure", m_parameters.exposure); + void set_v4l2_params() { + // Sheck auto exposure + m_camera.set_v4l_parameter("exposure", m_parameters.exposure); - if (m_parameters.framerate >= 0) { - m_camera.set_v4l_parameter("frame_rate", m_parameters.framerate); + if (m_parameters.framerate >= 0) { + m_camera.set_v4l_parameter("frame_rate", m_parameters.framerate); + } } - } + +private: + std::vector> networks; }; } // namespace usb_cam -int main(int argc, char ** argv) -{ - ros::init(argc, argv, "usb_cam"); - usb_cam::UsbCamNode a; - a.spin(); - return EXIT_SUCCESS; +int main(int argc, char ** argv) { + ros::init(argc, argv, "usb_cam"); + usb_cam::UsbCamNode a; + a.spin(); + return EXIT_SUCCESS; } From 4c03a36ee9ad37f1136b4f88c7d7893211f31c4f Mon Sep 17 00:00:00 2001 From: marcojob <44396071+marcojob@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:35:42 +0100 Subject: [PATCH 7/7] devcontainer: Downgrade tensorrt --- .devcontainer/Dockerfile | 10 ++++----- .devcontainer/build_and_push_images.sh | 2 +- src/interface.cpp | 30 +++++++++++++------------- src/usb_cam_node.cpp | 14 ++++++------ 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 3140129a..ba7d683e 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -30,9 +30,10 @@ RUN echo 'export PATH=$PATH:/usr/local/cuda/bin' >> /home/asl/.bash_aliases && \ echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda/lib64' >> /home/asl/.bash_aliases # TensorRT: Install from .deb file -COPY resources/nv-tensorrt-local-repo-ubuntu2004-10.6.0-cuda-12.6_1.0-1_amd64.deb /tmp/tensorrt.deb -RUN sudo dpkg -i /tmp/tensorrt.deb || sudo apt-get install -f -y -RUN sudo cp /var/nv-tensorrt-local-repo-ubuntu2004-10.6.0-cuda-12.6/*-keyring.gpg /usr/share/keyrings/ && sudo apt update && sudo apt install -y tensorrt +COPY resources/nv-tensorrt-repo-ubuntu2004-cuda11.6-trt8.4.3.1-ga-20220813_1-1_amd64.deb /tmp/ +RUN sudo dpkg -i /tmp/resources/nv-tensorrt-repo-ubuntu2004-cuda11.6-trt8.4.3.1-ga-20220813_1-1_amd64.deb || sudo apt-get install -f -y +# RUN sudo cp /var/nv-tensorrt-repo-ubuntu2004-cuda11.6-trt8.4.3.1-ga-20220813/*-keyring.gpg /usr/share/keyrings/ && sudo apt update && sudo apt install -y tensorrt +RUN sudo apt update && sudo apt install -y tensorrt # OpenCV Build Stage FROM base AS opencv_build @@ -63,5 +64,4 @@ USER root # Clean up RUN rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/* /var/cache/apt/archives/* - -ENTRYPOINT ["/bin/bash"] \ No newline at end of file +ENTRYPOINT ["/bin/bash"] diff --git a/.devcontainer/build_and_push_images.sh b/.devcontainer/build_and_push_images.sh index 35ec1b1f..9cc14abd 100755 --- a/.devcontainer/build_and_push_images.sh +++ b/.devcontainer/build_and_push_images.sh @@ -1 +1 @@ -docker buildx build -t omavteam/v4l2_camera:latest -f .devcontainer/Dockerfile --push . +docker buildx build --tag omavteam/v4l2_camera:latest -f .devcontainer/Dockerfile --push . diff --git a/src/interface.cpp b/src/interface.cpp index 6c93eca3..1a402f48 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -30,23 +30,23 @@ void LearningInterface::load_model() { // Allocate buffers for input and output size_t input_size; size_t output_size; - for (int io = 0; io < _engine->getNbIOTensors(); io++) { - const char* name = _engine->getIOTensorName(io); - std::cout << io << ": " << name; - const nvinfer1::Dims dims = _engine->getTensorShape(name); + // for (int io = 0; io < _engine->getNbIOTensors(); io++) { + // const char* name = _engine->getIOTensorName(io); + // std::cout << io << ": " << name; + // const nvinfer1::Dims dims = _engine->getTensorShape(name); - size_t total_dims = 1; - for (int d = 0; d < dims.nbDims; d++) { - total_dims *= dims.d[d]; - } + // size_t total_dims = 1; + // for (int d = 0; d < dims.nbDims; d++) { + // total_dims *= dims.d[d]; + // } - std::cout << " size: " << total_dims << std::endl; - if (io == 0) { - input_size = total_dims * sizeof(float); - } else if (io == 1) { - output_size = total_dims * sizeof(float); - } - } + // std::cout << " size: " << total_dims << std::endl; + // if (io == 0) { + // input_size = total_dims * sizeof(float); + // } else if (io == 1) { + // output_size = total_dims * sizeof(float); + // } + // } // Allocate device buffers cudaMalloc(&_buffers[0], input_size); diff --git a/src/usb_cam_node.cpp b/src/usb_cam_node.cpp index de1d0761..388fa13b 100644 --- a/src/usb_cam_node.cpp +++ b/src/usb_cam_node.cpp @@ -72,7 +72,7 @@ class UsbCamNode { UsbCamNode() : m_node("~") { // Setup the network that outputs derivates of the image captured - networks.push_back(std::make_unique("resources/raft.onnx", 240, 320)); + // networks.push_back(std::make_unique("resources/raft.onnx", 240, 320)); // Advertise the main image topic image_transport::ImageTransport it(m_node); @@ -177,12 +177,12 @@ class UsbCamNode { m_image_pub.publish(m_image, *ci); // Run all the networks - for (const auto& net : networks) { - net->set_input(m_image.data.data(), 1920, 1200); - if (net->run_inference(1)) { - net->publish(); - } - } + // for (const auto& net : networks) { + // net->set_input(m_image.data.data(), 1920, 1200); + // if (net->run_inference(1)) { + // net->publish(); + // } + // } return true; }