diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3335c43 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +*.vscode +bag/ +report/ + +# ROS # +####### +build/ +install/ +log/ + +# Python # +########## +*.pyc +*.pyo +__pycache__/ +localization-devel-ws/ydlidar_ros2_driver +localization-devel-ws/YDLidar-SDK diff --git a/docker/local-ros2/Dockerfile b/docker/local-ros2/Dockerfile index af33058..e779433 100644 --- a/docker/local-ros2/Dockerfile +++ b/docker/local-ros2/Dockerfile @@ -64,6 +64,7 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ # ros-humble-laser-geometry \ libsuitesparse-dev \ ros-humble-libg2o \ + ros-humble-rmw-cyclonedds-cpp\ # ros-humble-imu-tools \ libusb-1.0-0 libusb-1.0-0-dev -y && \ rm -rf /var/lib/apt/lists/* diff --git a/docker/local-ros2/OdomComm/_run.sh b/docker/local-ros2/OdomComm/_run.sh new file mode 100644 index 0000000..9e82b64 --- /dev/null +++ b/docker/local-ros2/OdomComm/_run.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +## 1. make scripts & library executable +echo "=== [COMMUNICATION] ===" +find ../OdomComm -type f -name "*.sh" -exec sudo chmod +x {} \; + +## 2. environment setup +export DISPLAY=:0 +xhost +local:docker +cd docker + +## 3. deployment +echo "[COMMUNICATION] Deploying..." +docker compose -p communication up -d \ No newline at end of file diff --git a/docker/local-ros2/OdomComm/build_and_run.sh b/docker/local-ros2/OdomComm/build_and_run.sh new file mode 100644 index 0000000..a77990a --- /dev/null +++ b/docker/local-ros2/OdomComm/build_and_run.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +## 0. clean container within same group +echo "=== [COMMUNICATION] Build & Run ===" +echo "[COMMUNICATION] Remove Containers ..." +docker compose -p communication down --volumes --remove-orphans + +## 1. make scripts & library executable +find ../OdomComm -type f -name "*.sh" -exec sudo chmod +x {} \; + +## 2. environment setup +export DISPLAY=:0 +xhost +local:docker +cd docker + +## 3. build image +echo "[COMMUNICATION] Building..." +docker compose -p communication build + +## 4. deployment +echo "[COMMUNICATION] Deploying..." +docker compose -p communication up -d \ No newline at end of file diff --git a/docker/local-ros2/OdomComm/docker/Dockerfile b/docker/local-ros2/OdomComm/docker/Dockerfile new file mode 100644 index 0000000..43a1026 --- /dev/null +++ b/docker/local-ros2/OdomComm/docker/Dockerfile @@ -0,0 +1,151 @@ +################################################################################################ +# - Base stage +# - This stage serves as the foundational stage for all other stages. +# - Base image: Ubuntu 22.04 Jammy Jellyfish +# - https://hub.docker.com/_/ubuntu/tags?page=1&name=22.04 +################################################################################################ + +FROM ubuntu:22.04 AS base + +LABEL org.opencontainers.image.authors="yuzhong1214@gmail.com" + +# Reference: https://docs.ros.org/en/humble/Installation/Alternatives/Ubuntu-Development-Setup.html#set-locale +ENV LC_ALL=en_US.UTF-8 +ENV LANG=en_US.UTF-8 + +SHELL ["/bin/bash", "-c"] + +# Add user +RUN useradd -ms /bin/bash user + +################################################################################################ +# - Build stage +# - In this stage, I will build ROS Humble and ros1-bridge from source. +# - Reference: +# - https://docs.ros.org/en/humble/How-To-Guides/Using-ros1_bridge-Jammy-upstream.html +# - https://docs.ros.org/en/humble/Installation/Alternatives/Ubuntu-Development-Setup.html +################################################################################################ + +FROM base AS build + +# Install the required packages for the following command. +RUN apt-get update && \ + apt-get install -y \ + curl \ + locales \ + software-properties-common \ + lsb-release + +# Set locale. +# Reference: https://docs.ros.org/en/humble/Installation/Alternatives/Ubuntu-Development-Setup.html#set-locale +RUN locale-gen en_US en_US.UTF-8 && \ + update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 + +# Add the ROS 2 apt repository. +# Reference: https://docs.ros.org/en/humble/Installation/Alternatives/Ubuntu-Development-Setup.html#add-the-ros-2-apt-repository +RUN add-apt-repository universe && \ + curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg && \ + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | tee /etc/apt/sources.list.d/ros2.list > /dev/null + +# Install development tools and ROS tools. +# Note that we declare noninteractive mode to avoid apt asking for user input. +# Additionally, install colcon from PyPI, rather than using apt packages. +# Reference: https://docs.ros.org/en/humble/How-To-Guides/Using-ros1_bridge-Jammy-upstream.html#install-development-tools-and-ros-tools +RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + build-essential \ + cmake \ + git \ + python3-flake8 \ + python3-flake8-blind-except \ + python3-flake8-builtins \ + python3-flake8-class-newline \ + python3-flake8-comprehensions \ + python3-flake8-deprecated \ + python3-flake8-docstrings \ + python3-flake8-import-order \ + python3-flake8-quotes \ + python3-pip \ + python3-pytest \ + python3-pytest-cov \ + python3-pytest-repeat \ + python3-pytest-rerunfailures \ + python3-rosdep \ + python3-setuptools \ + ros-dev-tools +RUN python3 -m pip install -U colcon-common-extensions vcstool + +# Create ros2_humble workspace, and clone all repositories from the list. +# Reference: https://docs.ros.org/en/humble/Installation/Alternatives/Ubuntu-Development-Setup.html#get-ros-2-code +RUN mkdir -p /ros2_humble/src +WORKDIR /ros2_humble +RUN vcs import --input https://raw.githubusercontent.com/ros2/ros2/humble/ros2.repos src + +# Install dependencies. +# Reference: https://docs.ros.org/en/humble/Installation/Alternatives/Ubuntu-Development-Setup.html#install-dependencies-using-rosdep +RUN rosdep init && \ + rosdep update && \ + rosdep install --from-paths src --ignore-src -y --rosdistro humble --skip-keys "fastcdr rti-connext-dds-6.0.1 urdfdom_headers" + +# Build the ros2_humble workspace. (This will take a few minutes) +# Note that we are using colcon build without the flag --symlink-install. +# This is because we will copy the built files from the build stage to the release stage. +# If we use the flag --symlink-install, the release stage will not be able to find the built files. +# Reference: https://docs.ros.org/en/humble/Installation/Alternatives/Ubuntu-Development-Setup.html#build-the-code-in-the-workspace +RUN colcon build + +# Delete packages.ros.org from sources.list and remove conflicts packages manually. +# Removing those conflicts packages is necessary for install ros-core-dev. +# Here is the dicussion about this issue: https://github.com/j3soon/ros2-essentials/pull/9#discussion_r1375303858 +# Reference: +# - https://docs.ros.org/en/humble/How-To-Guides/Using-ros1_bridge-Jammy-upstream.html#ros-2-via-debian-packages +# - https://github.com/osrf/docker_images/issues/635#issuecomment-1217505552 +RUN rm -rf /etc/apt/sources.list.d/ros2.list && \ + apt-get remove -y \ + python3-catkin-pkg \ + python3-catkin-pkg-modules + +# Install ROS 1 from Ubuntu packages. +# Reference: https://docs.ros.org/en/humble/How-To-Guides/Using-ros1_bridge-Jammy-upstream.html#install-ros-1-from-ubuntu-packages +RUN apt-get update && \ + apt-get install -y ros-core-dev + +# Build ros1_bridge from source. +# Reference: https://github.com/ros2/ros1_bridge/tree/3d5328dc21564d2130b4ded30afe5cd1c41cf033#building-the-bridge-from-source +RUN git clone https://github.com/ros2/ros1_bridge src/ros1_bridge +RUN source install/setup.bash && \ + colcon build --packages-select ros1_bridge --cmake-force-configure + +# Other configurations +COPY ./start-bridge.sh /start-bridge.sh +RUN chmod +x /start-bridge.sh + +CMD ["/bin/bash"] + +################################################################################################ +# - Release stage +# - In this stage, I will copy the built files from the build stage. +# This is useful for reducing the size of the image. +# - Reference: +# - https://docs.docker.com/build/building/multi-stage/ +################################################################################################ + +FROM base AS release + +# Install ROS core and packages with their dependencies. +RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + libspdlog-dev \ + python3-packaging \ + ros-core-dev + +# Copy files from host and build stage +COPY --from=build /ros2_humble/install /ros2_humble/install + +# Other configurations +COPY ./start-bridge.sh /start-bridge.sh +USER user +RUN echo "source /opt/ros/$ROS_DISTRO/setup.bash" >> ~/.bashrc + +WORKDIR / +CMD ["./start-bridge.sh"] \ No newline at end of file diff --git a/docker/local-ros2/OdomComm/docker/Dockerfile-rosserial b/docker/local-ros2/OdomComm/docker/Dockerfile-rosserial new file mode 100644 index 0000000..4560487 --- /dev/null +++ b/docker/local-ros2/OdomComm/docker/Dockerfile-rosserial @@ -0,0 +1,55 @@ +################################################################################################ +# - Base stage +# - This stage serves as the foundational stage for all other stages. +# - Base image: OSRF ROS noetic Desktop Full +# - https://hub.docker.com/r/osrf/ros/tags?page=1&name=noetic-desktop-full +################################################################################################ + +FROM osrf/ros:noetic-desktop-full AS base + +LABEL org.opencontainers.image.authors="yoseph.huang@gmail.com" + +ARG USERNAME=user +ARG USER_UID=1000 +ARG USER_GID=$USER_UID + +SHELL ["/bin/bash", "-c"] + + +################################################################################################ +# - User Setup stage +# - In this stage, create a non-root user and configure passwordless sudo. +################################################################################################ + +FROM base AS user-setup + +## Create a non-root user +RUN groupadd --gid $USER_GID $USERNAME && \ + useradd --uid $USER_UID --gid $USER_GID -m $USERNAME -s /bin/bash && \ + apt-get update && \ + apt-get install -y sudo && \ + echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME && \ + chmod 0440 /etc/sudoers.d/$USERNAME && \ + rm -rf /var/lib/apt/lists/* + +FROM user-setup AS catkin + +USER ${USERNAME} +# COPY comm-ws /home/$USERNAME/comm-ws +RUN mkdir -p /home/$USERNAME/ws/src +COPY odometry /home/$USERNAME/ws/src/odometry + +# catkin_make +RUN /bin/bash -c "source /opt/ros/noetic/setup.bash && \ + rosdep update && \ + cd /home/$USERNAME/ws && \ + rosdep install -y -r -q --from-paths src --ignore-src --rosdistro noetic && \ + catkin_make" + +FROM catkin AS final + +COPY odometry/odometry/launch /home/$USERNAME/ws/src/odometry/launch + +## Final configurations +USER $USERNAME +CMD ["/bin/bash"] \ No newline at end of file diff --git a/docker/local-ros2/OdomComm/docker/compose-serial.yaml b/docker/local-ros2/OdomComm/docker/compose-serial.yaml new file mode 100644 index 0000000..f66f575 --- /dev/null +++ b/docker/local-ros2/OdomComm/docker/compose-serial.yaml @@ -0,0 +1,29 @@ +services: + ros1: + image: ros1:serial + container_name: ros1 + stdin_open: true + tty: true + network_mode: host + privileged: true + volumes: + # - ../comm-ws:/root/comm-ws + # - /dev/bus/usb:/dev/bus/usb + - /dev:/dev + stop_grace_period: 1s + # depends_on: + # ros-core: + # condition: service_healthy + # command: /bin/bash -c " + # chmod +x /root/comm-ws/devel/_setup_util.py && + # find /root/comm-ws/devel -type f -name '*.sh' -exec chmod +x {} \; && + # source /opt/ros/noetic/setup.bash && + # source /root/comm-ws/devel/setup.bash && + # cd /root/comm-ws && + # sudo chmod 777 /dev/ttyACM0 && + # roslaunch rosserial_server stm32.launch" + command: /bin/bash -c " + source /opt/ros/noetic/setup.bash && + source ~/ws/devel/setup.bash && + sudo chmod 777 /dev/ttyACM0 && + roslaunch odometry odometry_comm.launch" \ No newline at end of file diff --git a/docker/local-ros2/OdomComm/docker/compose.yml b/docker/local-ros2/OdomComm/docker/compose.yml new file mode 100644 index 0000000..48f55dd --- /dev/null +++ b/docker/local-ros2/OdomComm/docker/compose.yml @@ -0,0 +1,104 @@ +services: + ros-core: + image: osrf/ros:noetic-desktop-full + container_name: ros-core + command: "rosmaster --core" + network_mode: host + stop_grace_period: 1s + healthcheck: + # The healthcheck is required for ros1-bridge to wait until roscore is ready. + test: /ros_entrypoint.sh bash -c "rostopic list || exit 1" + interval: 3s + timeout: 10s + retries: 5 + + ros1-bridge: + build: + context: . + target: release + image: pomelo925/ttennis-humble:communication + container_name: ros2-ros1-bridge-ws + stop_grace_period: 1s + depends_on: + ros-core: + # The healthcheck is required for ros1-bridge to wait until roscore is ready. + condition: service_healthy + command: ./start-bridge.sh + stdin_open: true + tty: true + network_mode: host + working_dir: / + volumes: + # Mount local timezone into container. ( Readonly ) + # Reference: https://stackoverflow.com/questions/57607381/how-do-i-change-timezone-in-a-docker-container + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + # Direct Rendering Infrastructure + - /dev/dri:/dev/dri + # Shared Memory + - /dev/shm:/dev/shm + environment: + - ROS_DOMAIN_ID=43 + # - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp=value + + ros1: + image: jossiewang/eurobot2025-localization:ros1-serial + container_name: ros1 + stdin_open: true + tty: true + network_mode: host + privileged: true + volumes: + - /dev:/dev + stop_grace_period: 1s + depends_on: + ros-core: + condition: service_healthy + command: /bin/bash -c " + source /opt/ros/noetic/setup.bash && + source ~/ws/devel/setup.bash && + sudo chmod 777 /dev/ttyACM0 && + roslaunch odometry odometry_comm.launch" + + ros2-localization-dev: + build: + context: . + dockerfile: Dockerfile + args: + USERNAME: user + image: jossiewang/eurobot2025-localization:local-ros2-wSensors + container_name: localization-2025-dev-ros2 + stdin_open: true + tty: true + privileged: true + stop_grace_period: 1s + restart: no + # entrypoint: ["./start.sh"] + + network_mode: host + working_dir: /home/user/localization-ws + environment: + - DISPLAY=${DISPLAY} + # Set ros2 environment variables. + # References: + # - https://docs.ros.org/en/humble/Concepts/Intermediate/About-Domain-ID.html + # - https://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Configuring-ROS2-Environment.html + # - ROS_LOCALHOST_ONLY=1 + - ROS_DOMAIN_ID=43 + - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp + - ROS_WS=/home/user/localization-ws + volumes: + # Mount local timezone into container. ( Readonly ) + # Reference: https://stackoverflow.com/questions/57607381/how-do-i-change-timezone-in-a-docker-container + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + # Mount X11 server + - /tmp/.X11-unix:/tmp/.X11-unix + # Direct Rendering Infrastructure + # - /dev/dri:/dev/dri + # Mount sound card to prevent Gazebo warning. + # - /dev/snd:/dev/snd + - /dev:/dev + # Mount workspace + - ../../../../localization-devel-ws:/home/user/localization-ws/src/localization-devel-ws + command: /bin/bash \ No newline at end of file diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/odometry/CMakeLists.txt b/docker/local-ros2/OdomComm/docker/odometry/odometry/CMakeLists.txt similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/odometry/CMakeLists.txt rename to docker/local-ros2/OdomComm/docker/odometry/odometry/CMakeLists.txt diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/odometry/cfg/odometry_param.cfg b/docker/local-ros2/OdomComm/docker/odometry/odometry/cfg/odometry_param.cfg similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/odometry/cfg/odometry_param.cfg rename to docker/local-ros2/OdomComm/docker/odometry/odometry/cfg/odometry_param.cfg diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/odometry/include/odometry/odometry.h b/docker/local-ros2/OdomComm/docker/odometry/odometry/include/odometry/odometry.h similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/odometry/include/odometry/odometry.h rename to docker/local-ros2/OdomComm/docker/odometry/odometry/include/odometry/odometry.h diff --git a/docker/local-ros2/OdomComm/docker/odometry/odometry/launch/odometry_comm.launch b/docker/local-ros2/OdomComm/docker/odometry/odometry/launch/odometry_comm.launch new file mode 100644 index 0000000..a330490 --- /dev/null +++ b/docker/local-ros2/OdomComm/docker/odometry/odometry/launch/odometry_comm.launch @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + require: + publishers: [ driving_duaiduaiduai, odoo_googoogoo ] + subscribers: [ ] + + + + diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/odometry/lib/odometry.cpp b/docker/local-ros2/OdomComm/docker/odometry/odometry/lib/odometry.cpp similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/odometry/lib/odometry.cpp rename to docker/local-ros2/OdomComm/docker/odometry/odometry/lib/odometry.cpp diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/odometry/package.xml b/docker/local-ros2/OdomComm/docker/odometry/odometry/package.xml similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/odometry/package.xml rename to docker/local-ros2/OdomComm/docker/odometry/odometry/package.xml diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/odometry/param/odometry_comm.yaml b/docker/local-ros2/OdomComm/docker/odometry/odometry/param/odometry_comm.yaml similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/odometry/param/odometry_comm.yaml rename to docker/local-ros2/OdomComm/docker/odometry/odometry/param/odometry_comm.yaml diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/odometry/src/odometry_node.cpp b/docker/local-ros2/OdomComm/docker/odometry/odometry/src/odometry_node.cpp similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/odometry/src/odometry_node.cpp rename to docker/local-ros2/OdomComm/docker/odometry/odometry/src/odometry_node.cpp diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_msgs/CHANGELOG.rst b/docker/local-ros2/OdomComm/docker/odometry/rosserial_msgs/CHANGELOG.rst similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_msgs/CHANGELOG.rst rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_msgs/CHANGELOG.rst diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_msgs/CMakeLists.txt b/docker/local-ros2/OdomComm/docker/odometry/rosserial_msgs/CMakeLists.txt similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_msgs/CMakeLists.txt rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_msgs/CMakeLists.txt diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_msgs/msg/Log.msg b/docker/local-ros2/OdomComm/docker/odometry/rosserial_msgs/msg/Log.msg similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_msgs/msg/Log.msg rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_msgs/msg/Log.msg diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_msgs/msg/TopicInfo.msg b/docker/local-ros2/OdomComm/docker/odometry/rosserial_msgs/msg/TopicInfo.msg similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_msgs/msg/TopicInfo.msg rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_msgs/msg/TopicInfo.msg diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_msgs/package.xml b/docker/local-ros2/OdomComm/docker/odometry/rosserial_msgs/package.xml similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_msgs/package.xml rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_msgs/package.xml diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_msgs/srv/RequestParam.srv b/docker/local-ros2/OdomComm/docker/odometry/rosserial_msgs/srv/RequestParam.srv similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_msgs/srv/RequestParam.srv rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_msgs/srv/RequestParam.srv diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/CHANGELOG.rst b/docker/local-ros2/OdomComm/docker/odometry/rosserial_server/CHANGELOG.rst similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/CHANGELOG.rst rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_server/CHANGELOG.rst diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/CMakeLists.txt b/docker/local-ros2/OdomComm/docker/odometry/rosserial_server/CMakeLists.txt similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/CMakeLists.txt rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_server/CMakeLists.txt diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/include/rosserial_server/async_read_buffer.h b/docker/local-ros2/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/async_read_buffer.h similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/include/rosserial_server/async_read_buffer.h rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/async_read_buffer.h diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/include/rosserial_server/msg_lookup.h b/docker/local-ros2/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/msg_lookup.h similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/include/rosserial_server/msg_lookup.h rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/msg_lookup.h diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/include/rosserial_server/serial_session.h b/docker/local-ros2/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/serial_session.h similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/include/rosserial_server/serial_session.h rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/serial_session.h diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/include/rosserial_server/session.h b/docker/local-ros2/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/session.h similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/include/rosserial_server/session.h rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/session.h diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/include/rosserial_server/tcp_server.h b/docker/local-ros2/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/tcp_server.h similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/include/rosserial_server/tcp_server.h rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/tcp_server.h diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/include/rosserial_server/topic_handlers.h b/docker/local-ros2/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/topic_handlers.h similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/include/rosserial_server/topic_handlers.h rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/topic_handlers.h diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/include/rosserial_server/udp_socket_session.h b/docker/local-ros2/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/udp_socket_session.h similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/include/rosserial_server/udp_socket_session.h rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/udp_socket_session.h diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/include/rosserial_server/udp_stream.h b/docker/local-ros2/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/udp_stream.h similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/include/rosserial_server/udp_stream.h rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/udp_stream.h diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/launch/serial.launch b/docker/local-ros2/OdomComm/docker/odometry/rosserial_server/launch/serial.launch similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/launch/serial.launch rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_server/launch/serial.launch diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/launch/socket.launch b/docker/local-ros2/OdomComm/docker/odometry/rosserial_server/launch/socket.launch similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/launch/socket.launch rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_server/launch/socket.launch diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/launch/udp_socket.launch b/docker/local-ros2/OdomComm/docker/odometry/rosserial_server/launch/udp_socket.launch similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/launch/udp_socket.launch rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_server/launch/udp_socket.launch diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/package.xml b/docker/local-ros2/OdomComm/docker/odometry/rosserial_server/package.xml similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/package.xml rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_server/package.xml diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/src/msg_lookup.cpp b/docker/local-ros2/OdomComm/docker/odometry/rosserial_server/src/msg_lookup.cpp similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/src/msg_lookup.cpp rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_server/src/msg_lookup.cpp diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/src/serial_node.cpp b/docker/local-ros2/OdomComm/docker/odometry/rosserial_server/src/serial_node.cpp similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/src/serial_node.cpp rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_server/src/serial_node.cpp diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/src/socket_node.cpp b/docker/local-ros2/OdomComm/docker/odometry/rosserial_server/src/socket_node.cpp similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/src/socket_node.cpp rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_server/src/socket_node.cpp diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/src/udp_socket_node.cpp b/docker/local-ros2/OdomComm/docker/odometry/rosserial_server/src/udp_socket_node.cpp similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/rosserial_server/src/udp_socket_node.cpp rename to docker/local-ros2/OdomComm/docker/odometry/rosserial_server/src/udp_socket_node.cpp diff --git a/docker/local-ros2/OdomComm/docker/start-bridge.sh b/docker/local-ros2/OdomComm/docker/start-bridge.sh new file mode 100644 index 0000000..1cc90c5 --- /dev/null +++ b/docker/local-ros2/OdomComm/docker/start-bridge.sh @@ -0,0 +1,14 @@ +#!/bin/bash -e + +if [ $# = 1 ]; then + export ROS_MASTER_URI=$1 +else + export ROS_MASTER_URI=http://localhost:11311 +fi + +# Normally, sourcing the setup.bash file is sufficient to set up the ROS environment. +# However, the ros1_bridge package can't be found by ROS 2 even if we source the file. Manual sourcing is required. +source /ros2_humble/install/setup.bash +source /ros2_humble/install/ros1_bridge/share/ros1_bridge/local_setup.bash + +ros2 run ros1_bridge dynamic_bridge \ No newline at end of file diff --git a/docker/local-ros2/OdomComm/pull_and_run.sh b/docker/local-ros2/OdomComm/pull_and_run.sh new file mode 100755 index 0000000..213ad95 --- /dev/null +++ b/docker/local-ros2/OdomComm/pull_and_run.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +## 0. clean container within same group +echo "=== [COMMUNICATION] Pull & Run ===" +echo "[COMMUNICATION] Remove Containers ..." +docker compose -p communication down --volumes --remove-orphans + +## 1. make scripts & library executable +find ../E.Communication -type f -name "*.sh" -exec sudo chmod +x {} \; + +## 2. environment setup +export DISPLAY=:0 +xhost +local:docker +cd docker + +## 3. build image +echo "[COMMUNICATION] Pull Images ..." +docker pull pomelo925/ttennis-humble:communication + +## 4. deployment +echo "[COMMUNICATION] Deploying..." +docker compose -p communication up +# docker compose -p communication up -d \ No newline at end of file diff --git a/docker/local-ros2/docker-compose.yaml b/docker/local-ros2/docker-compose.yaml index 96b77fe..b847559 100644 --- a/docker/local-ros2/docker-compose.yaml +++ b/docker/local-ros2/docker-compose.yaml @@ -1,13 +1,13 @@ version: '3' services: - ros2-localization-dev: + noComm-ros2-localization-dev: build: context: . dockerfile: Dockerfile args: USERNAME: user - image: jossiewang/eurobot2025-localization:local-ros2-wSensors - container_name: localization-2025-dev-ros2 + image: jossiewang/eurobot2025-localization:devel + container_name: noComm-localization-2025-dev-ros2 stdin_open: true tty: true privileged: true @@ -24,8 +24,9 @@ services: # - https://docs.ros.org/en/humble/Concepts/Intermediate/About-Domain-ID.html # - https://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Configuring-ROS2-Environment.html # - ROS_LOCALHOST_ONLY=1 - # - ROS_DOMAIN_ID=42 + - ROS_DOMAIN_ID=7 - ROS_WS=/home/user/localization-ws + - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp volumes: # Mount local timezone into container. ( Readonly ) # Reference: https://stackoverflow.com/questions/57607381/how-do-i-change-timezone-in-a-docker-container @@ -48,4 +49,5 @@ services: # "source=${localWorkspaceFolder}/../cache/humble/log,target=/home/ws/log,type=bind" # Mount workspace - ../../localization-devel-ws:/home/user/localization-ws/src/localization-devel-ws - command: /bin/bash \ No newline at end of file + # command: /bin/bash + command: /bin/bash -c "ros2 launch /home/user/localization-ws/src/localization-devel-ws/cust_foxglove_launch.py" \ No newline at end of file diff --git a/docker/local-ros2/phidgets_drivers/LICENSE b/docker/local-ros2/phidgets_drivers/LICENSE new file mode 100644 index 0000000..2863577 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/LICENSE @@ -0,0 +1,5 @@ +The libphidgets22 library is licensed under the GNU Lesser General Public Licence v3 or later. +See LICENSE.gplv3 and LICENSE.lgplv3 for the text of the license. + +The rest of the code in this repository is licensed under the 3-clause BSD license. +See LICENSE.bsd for the text of the license. diff --git a/docker/local-ros2/phidgets_drivers/LICENSE.bsd b/docker/local-ros2/phidgets_drivers/LICENSE.bsd new file mode 100644 index 0000000..d06df08 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/LICENSE.bsd @@ -0,0 +1,26 @@ +Copyright (c) 2019, Open Source Robotics Foundation +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +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. diff --git a/docker/local-ros2/phidgets_drivers/LICENSE.gplv3 b/docker/local-ros2/phidgets_drivers/LICENSE.gplv3 new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/LICENSE.gplv3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/docker/local-ros2/phidgets_drivers/LICENSE.lgplv3 b/docker/local-ros2/phidgets_drivers/LICENSE.lgplv3 new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/LICENSE.lgplv3 @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/docker/local-ros2/phidgets_drivers/README.md b/docker/local-ros2/phidgets_drivers/README.md new file mode 100644 index 0000000..ccb9d82 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/README.md @@ -0,0 +1,193 @@ +Phidgets drivers for ROS 2 +========================== + +Overview +-------- + +Drivers for various [Phidgets](https://www.phidgets.com) devices. This package includes: + +* `libphidget22`: a package which downloads and builds the Phidgets C API from + phidgets.com as an external project. + +* `phidgets_api`: a package that implements a C++ wrapper for the libphidgets22 + C API, providing some base Phidget helper functions and various classes for + different phidget devices. + +* `phidgets_msgs`: a package that contains custom messages and services for + interacting with the nodes. + +* ROS 2 nodes exposing the functionality of specific phidgets devices: + + * [`phidgets_accelerometer`](phidgets_accelerometer/README.md) + + * [`phidgets_analog_inputs`](phidgets_analog_inputs/README.md) + + * [`phidgets_analog_outputs`](phidgets_analog_outputs/README.md) + + * [`phidgets_digital_inputs`](phidgets_digital_inputs/README.md) + + * [`phidgets_digital_outputs`](phidgets_digital_outputs/README.md) + + * [`phidgets_gyroscope`](phidgets_gyroscope/README.md) + + * [`phidgets_high_speed_encoder`](phidgets_high_speed_encoder/README.md) + + * [`phidgets_ik`](phidgets_ik/README.md) + + * [`phidgets_magnetometer`](phidgets_magnetometer/README.md) + + * [`phidgets_motors`](phidgets_motors/README.md) + + * [`phidgets_spatial`](phidgets_spatial/README.md) + + * [`phidgets_temperature`](phidgets_temperature/README.md) + +Concerning Phidgets +------------------- + +Phidgets are typically plugged into USB on a host computer (though there are +wireless ones, they will be ignored here). In the "old-style" Phidgets, there +was one USB plug per device. So if you have a +[temperature Phidget](https://www.phidgets.com/?tier=3&catid=14&pcid=12&prodid=1042), +and an [accelerometer Phidget](https://www.phidgets.com/?tier=3&catid=10&pcid=8&prodid=1026), +they take up 2 USB plugs on the computer. These "old-style" Phidgets are still +around and still available for sale, but most of the new development and +sensors are in "new-style" Phidgets. In "new-style" Phidgets, a +[VINT hub](https://www.phidgets.com/?tier=3&catid=2&pcid=1&prodid=643) is +connected to the host computer via USB, and then the other Phidgets connect to +a port on the VINT hub. Most of the "old-style" Phidget functions (temperature, +acclerometer, etc.) are also available as "new-style" Phidgets, and most new +functionality is only available as VINT devices. + +### Identifying Phidgets devices ### + +All Phidgets that plug directly into a USB port (including the VINT hub) have a +unique serial number. This serial number is printed on the back of the device, +and is also printed out by the phidgets drivers when they start up. The serial +number can be specified as a parameter when the driver starts up; otherwise, the +default is to connect to *any* Phidgets that are of the correct type at startup. + +Uniquely identifying a "new-style" Phidget also requires one more piece of +information, which is the VINT hub port it is connected to. This also must be +provided as a parameter when starting up the driver. + +Note that there are "smart" and "simple" VINT hub devices. "Smart" devices have +their own microcontrollers on board and use a protocol to communicate with the +VINT hub. + +"Simple" VINT hub devices don't have a microcontroller. They just provide +or accept a voltage from the VINT hub port (which can act as a digital input, +digital output, or analog inputs). + +Whether the Phidget is "smart" or "simple" can be determined by looking at the +"Connection and Compatibility" portion of the webpage for the individual sensor. +If the device is "smart", then "is_hub_port_device" must be set to "false" +when launching a driver; if the device is "simple", then "is_hub_port_device" +must be set to "true". + +Installing +---------- + +### From packages ### + +Make sure you have ROS 2 Dashing or newer installed: https://docs.ros.org + +Then run: + + sudo apt-get install ros--phidgets-drivers + +### From source ### + +Make sure you have ROS 2 Dashing or newer installed: https://docs.ros.org + +Also make sure you have git installed: + + sudo apt-get install git-core + +Change directory to the source folder of your colcon workspace. +If, for instance, your workspace is `~/colcon_ws`, make sure there is +a `src/` folder within it, then execute: + + cd ~/colcon_ws/src + +Clone the repository ( may be `dashing`, ...) + + git clone -b https://github.com/ros-drivers/phidgets_drivers.git + +Install dependencies using rosdep: + + rosdep install phidgets_drivers + +Alternatively, if rosdep does not work, install the following packages: + + sudo apt-get install libusb-1.0-0 libusb-1.0-0-dev + +Compile your colcon workspace: + + cd ~/colcon_ws + colcon build + +### Udev rules setup ### + +**Note:** The following steps are only required when installing the package +from source. When installing a binary debian package of `phidgets_api` >= 0.7.8, +the udev rules are set up automatically. + +Make sure your colcon workspace has been successfully compiled. +To set up the udev rules for the Phidgets USB devices, run the following commands: + + sudo cp ~/colcon_ws/src/phidgets_drivers/phidgets_api/debian/udev /etc/udev/rules.d/99-phidgets.rules + sudo udevadm control --reload-rules + +Afterwards, disconnect the USB cable and plug it in again (or run `sudo udevadm trigger`). + +Running +------- + +You may notice that there are no executables installed by the various phidgets packages. +Instead, all functionality is available via [ROS 2 components](https://docs.ros.org/en/rolling/Concepts/About-Composition.html). +The reason for this is that when using phidget VINT hubs, only one process at a time can access the hub. +Since the hub may have multiple devices connected to it, we need to load multiple different phidgets drivers into the same process. +We do that through loading multiple components into a single ROS 2 component container. + +Luckily, to make things easier in the single device case, we have default launch files for running a single phidgets driver in a single process. +For instance, you can run the phidgets_spatial node by running: + + ros2 launch phidgets_spatial spatial-launch.py + +See the README files in the individual packages above for exact usage of each launch file. + +Developing +---------- + +To check formatting after modifying source code: + + python3 clang-check-style.py + +To reformat source code: + + find . -name '*.h' -or -name '*.hpp' -or -name '*.cpp' | xargs clang-format-6.0 -i -style=file $1 + + +pre-commit Formatting Checks +---------------------------- + +This repo has a [pre-commit](https://pre-commit.com/) check that runs in CI. +You can use this locally and set it up to run automatically before you commit +something. To install, use apt: + +```bash +sudo apt install pre-commit +``` + +To run over all the files in the repo manually: + +```bash +pre-commit run -a +``` + +To run pre-commit automatically before committing in the local repo, install the git hooks: + +```bash +pre-commit install +``` diff --git a/docker/local-ros2/phidgets_drivers/libphidget22/CHANGELOG.rst b/docker/local-ros2/phidgets_drivers/libphidget22/CHANGELOG.rst new file mode 100644 index 0000000..324fe84 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/libphidget22/CHANGELOG.rst @@ -0,0 +1,103 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package libphidget22 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +2.3.3 (2024-03-13) +------------------ +* Update to libphidget22 1.19 (`#176 `_) +* Contributors: Martin Günther + +2.3.2 (2023-11-27) +------------------ + +2.3.1 (2023-03-03) +------------------ +* Update to libphidget22 1.13. (`#160 `_) +* Contributors: Chris Lalancette + +2.3.0 (2022-04-13) +------------------ +* Update to libphidgets 20220203 (`#138 `_) +* Contributors: Chris Lalancette + +2.2.2 (2022-02-17) +------------------ +* Remove outdated patch file (`#112 `_) +* Update to libphidget22-1.7.20210816 (`#108 `_) + This is required to support new devices such as the MOT0109. + Fixes `#99 `_, fixes `#105 `_. + This is a forward-port of `#106 `_ to ROS2. +* Make sure libphidget22 library can be found. (`#97 `_) (`#100 `_) + In Foxy and later, we need to provide the .dsv hook so that + the library can be found. +* Contributors: Chris Lalancette, Martin Günther + +2.2.1 (2021-08-03) +------------------ +* Update to the latest libphidgets 1.6 library. (`#91 `_) +* Contributors: Chris Lalancette + +2.2.0 (2021-05-20) +------------------ + +2.1.0 (2021-03-29) +------------------ +* Compile libphidget22 with -fPIC (`#88 `_) +* Update to libphidget22 from 2020. +* Contributors: Chris Lalancette, Scott K Logan + +2.0.2 (2020-06-01) +------------------ + +2.0.1 (2019-12-05) +------------------ + +2.0.0 (2019-12-05) +------------------ +* Port libphidget22 vendor package to ament. +* Ignore all packages for ROS 2 port. +* Update maintainers in package.xml +* Merge pull request `#39 `_ from clalancette/add-libphidget22 +* Patch warnings in libphidget22. +* Fix up libphidget22 package.xml dependencies. +* Add in libphidget22 package. +* Contributors: Chris Lalancette, Martin Günther + +0.7.9 (2019-06-28) +------------------ + +0.7.8 (2019-05-06) +------------------ + +0.7.7 (2018-09-18) +------------------ + +0.7.6 (2018-08-09) +------------------ + +0.7.5 (2018-01-31) +------------------ + +0.7.4 (2017-10-04) +------------------ + +0.7.3 (2017-06-30) +------------------ + +0.7.2 (2017-06-02) +------------------ + +0.7.1 (2017-05-22) +------------------ + +0.7.0 (2017-02-17 17:40) +------------------------ + +0.2.3 (2017-02-17 12:11) +------------------------ + +0.2.2 (2015-03-23) +------------------ + +0.2.1 (2015-01-15) +------------------ diff --git a/docker/local-ros2/phidgets_drivers/libphidget22/CMakeLists.txt b/docker/local-ros2/phidgets_drivers/libphidget22/CMakeLists.txt new file mode 100644 index 0000000..d0640d5 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/libphidget22/CMakeLists.txt @@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 3.5) +project(libphidget22) + +find_package(ament_cmake REQUIRED) + +set(extra_c_flags "-g -O2 -Wno-incompatible-pointer-types -Wno-deprecated-declarations -Wno-format-truncation -fPIC") + +include(ExternalProject) +ExternalProject_Add(EP_${PROJECT_NAME} + URL https://www.phidgets.com/downloads/phidget22/libraries/linux/libphidget22/libphidget22-1.19.20240304.tar.gz + URL_MD5 9b059eaef8cb8ce70b8abd7e4d309d1d + + SOURCE_DIR ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-src + CONFIGURE_COMMAND + /configure + CFLAGS=${extra_c_flags} + --prefix=${CMAKE_CURRENT_BINARY_DIR}/libphidget22_install + --disable-ldconfig + BUILD_COMMAND $(MAKE) + INSTALL_COMMAND $(MAKE) install +) + +# The external project will install to the build folder, but we'll install that on make install. +# Note that we install lib and include separately so we can add the extra level of indirection +# to the include directory. +install( + DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR}/libphidget22_install/lib/ + DESTINATION + ${CMAKE_INSTALL_PREFIX}/opt/libphidget22/lib/ +) + +install( + DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR}/libphidget22_install/include/ + DESTINATION + ${CMAKE_INSTALL_PREFIX}/opt/libphidget22/include/libphidget22/ +) + +ament_environment_hooks(env_hook/libphidget22_library_path.sh) +set(ENV_VAR_NAME "LD_LIBRARY_PATH") +set(ENV_VAR_VALUE "opt/libphidget22/lib") +ament_environment_hooks(env_hook/libphidget22_library_path.dsv.in) + +ament_package(CONFIG_EXTRAS "cmake/libphidget22-extras.cmake.in") diff --git a/docker/local-ros2/phidgets_drivers/libphidget22/cmake/libphidget22-extras.cmake.in b/docker/local-ros2/phidgets_drivers/libphidget22/cmake/libphidget22-extras.cmake.in new file mode 100644 index 0000000..7a386fa --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/libphidget22/cmake/libphidget22-extras.cmake.in @@ -0,0 +1,3 @@ +# TODO(clalancette): Does this work in general? +list(APPEND @PROJECT_NAME@_INCLUDE_DIRS "${@PROJECT_NAME@_DIR}/../../../opt/libphidget22/include") +list(APPEND @PROJECT_NAME@_LIBRARIES "${@PROJECT_NAME@_DIR}/../../../opt/libphidget22/lib/libphidget22.so") diff --git a/docker/local-ros2/phidgets_drivers/libphidget22/env_hook/libphidget22_library_path.dsv.in b/docker/local-ros2/phidgets_drivers/libphidget22/env_hook/libphidget22_library_path.dsv.in new file mode 100644 index 0000000..92145c4 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/libphidget22/env_hook/libphidget22_library_path.dsv.in @@ -0,0 +1 @@ +prepend-non-duplicate;@ENV_VAR_NAME@;@ENV_VAR_VALUE@ diff --git a/docker/local-ros2/phidgets_drivers/libphidget22/env_hook/libphidget22_library_path.sh b/docker/local-ros2/phidgets_drivers/libphidget22/env_hook/libphidget22_library_path.sh new file mode 100644 index 0000000..a6e0098 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/libphidget22/env_hook/libphidget22_library_path.sh @@ -0,0 +1,16 @@ +# copied from libphidget22/env_hook/libphidget22_library_path.sh + +# detect if running on Darwin platform +_UNAME=`uname -s` +_IS_DARWIN=0 +if [ "$_UNAME" = "Darwin" ]; then + _IS_DARWIN=1 +fi +unset _UNAME + +if [ $_IS_DARWIN -eq 0 ]; then + ament_prepend_unique_value LD_LIBRARY_PATH "$AMENT_CURRENT_PREFIX/opt/libphidget22/lib" +else + ament_prepend_unique_value DYLD_LIBRARY_PATH "$AMENT_CURRENT_PREFIX/opt/libphidget22/lib" +fi +unset _IS_DARWIN diff --git a/docker/local-ros2/phidgets_drivers/libphidget22/package.xml b/docker/local-ros2/phidgets_drivers/libphidget22/package.xml new file mode 100644 index 0000000..027d8de --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/libphidget22/package.xml @@ -0,0 +1,29 @@ + + + + libphidget22 + 2.3.3 + This package wraps the libphidget22 to use it as a ROS dependency + + Martin Günther + Chris Lalancette + + LGPL + + http://ros.org/wiki/libphidget22 + https://github.com/ros-drivers/phidgets_drivers.git + https://github.com/ros-drivers/phidgets_drivers/issues + + Alexander Bubeck + + ament_cmake + + libusb-1.0-dev + libusb-1.0 + + + ament_cmake + + diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/CHANGELOG.rst b/docker/local-ros2/phidgets_drivers/phidgets_api/CHANGELOG.rst new file mode 100644 index 0000000..887aa45 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/CHANGELOG.rst @@ -0,0 +1,179 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package phidgets_api +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +2.3.3 (2024-03-13) +------------------ + +2.3.2 (2023-11-27) +------------------ +* added new parameters for spatial precision MOT0109 onwards +* added support for phidget spatial onboard orientation estimation +* Contributors: Malte kl. Piening, Martin Günther + +2.3.1 (2023-03-03) +------------------ +* adding support for analog outputs for ROS2 (`#145 `_) +* Contributors: Alexis Fetet + +2.3.0 (2022-04-13) +------------------ + +2.2.2 (2022-02-17) +------------------ +* spatial: Add attach + detach handlers +* Fix some clang-tidy warnings +* Fix typo in error message (`#104 `_) +* Contributors: Martin Günther + +2.2.1 (2021-08-03) +------------------ + +2.2.0 (2021-05-20) +------------------ + +2.1.0 (2021-03-29) +------------------ +* Switch header guards to _HPP SUFFIX. +* Remove unnecessary cstddef. +* Contributors: Chris Lalancette + +2.0.2 (2020-06-01) +------------------ +* Use '=default' for default destructors. (`#66 `_) +* Contributors: Chris Lalancette + +2.0.1 (2019-12-05) +------------------ +* Switch the buildtoo_depend to ament_cmake_ros. (`#65 `_) +* Contributors: Chris Lalancette + +2.0.0 (2019-12-05) +------------------ +* Remove unnecessary base-class initialization. +* Get rid of C-style casts. +* Make sure what() method is marked as override. +* Rename method parameter name to match implementation. +* Make sure to include cstddef for libphidget22.h include. +* Make sure to initialize class member variables. +* Reformat using clang +* Make sure to set the VoltageRange to AUTO at the beginning. +* Only publish motor_back_emf if supported by DC Motor device (`#53 `_) +* Print out the serial number when connecting. +* Port phidgets_api to ament. +* Ignore all packages for ROS 2 port. +* Update maintainers in package.xml +* Merge pull request `#39 `_ from clalancette/add-libphidget22 +* Add in try/catch blocks for connecting. +* Fixes from review. +* Implement data interval setting for analog inputs. +* Add in the license files and add to the headers. +* Completely remove libphidget21. +* Rewrite Motor Phidget to use libphidget22. +* Rewrite High Speed Encoder to use libphidget22. +* Rewrite IR to use libphidget22. +* Rewrite IMU using libphidget22. +* Add support for Phidgets Magnetometer sensors. +* Add support for Phidgets Gyroscope sensors. +* Add support for Phidgets Accelerometer sensors. +* Add in support for Phidgets Temperature sensors. +* Rewrite phidgets_ik on top of libphidget22 classes. +* Add in support for Phidgets Analog inputs. +* Add in support for Phidgets Digital Inputs. +* Add in support for Phidgets Digital Outputs. +* Add in libphidget22 helper functions. +* Rename Phidget class to Phidget21 class. +* Switch to C++14. +* Merge pull request `#36 `_ from clalancette/phidget-cleanup2 +* Run clang-format on the whole codebase. +* Switch to C++14 everywhere. +* Remove unused indexHandler from Encoder class. +* Change API from separate open/waitForAttachment to openAndWaitForAttachment. +* Small cleanups throughout the code. +* Push libphidgets API calls down to phidgets_api. +* Quiet down the to-be-overridden callbacks. +* Consistently use nullptr instead of 0. +* Make the phidget_api destructors virtual. +* Style cleanup. +* Move libusb dependency into the libphidget21 package.xml. +* Switch to package format 2. +* Cleanup spacing in all of the CMakeLists.txt +* Contributors: Chris Lalancette, Martin Günther, Peter Polidoro + +0.7.9 (2019-06-28) +------------------ +* Add missing OnInputChange handler (`#33 `_) +* Contributors: Kai Hermann + +0.7.8 (2019-05-06) +------------------ +* Install udev rules on binary package installation +* Contributors: Martin Günther + +0.7.7 (2018-09-18) +------------------ + +0.7.6 (2018-08-09) +------------------ + +0.7.5 (2018-01-31) +------------------ +* Add support for the phidgets_ik (Phidgets Interface Kit) +* Contributors: Russel Howe, James Sarrett, Martin Günther + +0.7.4 (2017-10-04) +------------------ +* Fix typo and doxygen docs +* Contributors: Jose Luis Blanco Claraco, Martin Günther + +0.7.3 (2017-06-30) +------------------ + +0.7.2 (2017-06-02) +------------------ + +0.7.1 (2017-05-22) +------------------ +* Set event handlers for motor + encoder APIs +* Added basic motor api +* Added basic encoder board api +* Contributors: Zach Anderson, Martin Günther + +0.7.0 (2017-02-17) +------------------ +* Use our own libphidget21 instead of external libphidgets +* Contributors: Martin Günther + +0.2.3 (2017-02-17) +------------------ +* Add IMU diagnostics (`#24 `_) +* Contributors: Mani Monajjemi, Keshav Iyengar, Martin Günther + +0.2.2 (2015-03-23) +------------------ +* phidgets_api: updated build/installation rules to use 3rd party libphdigets ROS package +* phidgets_api: updated package details +* phidgets_api: added copy of udev rule to package and updated path in script +* phidgets_api: updated path to libphidgets header file +* phidgets_api: removed license and header file of phidgets library +* Contributors: Murilo FM + +0.2.1 (2015-01-15) +------------------ +* phidgets_api: add libusb dependency + This caused Jenkins CI tests to fail. +* phidgets_api: fix case in CMakeLists +* phidgets_api: added GNU LGPLv3 copy (phidget21.h) +* phidgets_api: updated license and author information +* phidgets_api: added script to setup udev rules for Phidgets devices +* phidgets_api: added libphidget21 dependency as cmake external project +* phidgets_api: updated path to libphidget header file +* phidgets_api: added libphidget header file to package +* phidgets_api: removed phidgets_c_api dependency +* Deleted comments within files of all packages +* Catkinised packages +* added missing cmakelists +* added api, imu and ir +* removed deps directory +* initial commit +* Contributors: Ivan Dryanovski, Martin Günther, Murilo FM diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/CMakeLists.txt b/docker/local-ros2/phidgets_drivers/phidgets_api/CMakeLists.txt new file mode 100644 index 0000000..c3232bf --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/CMakeLists.txt @@ -0,0 +1,57 @@ +cmake_minimum_required(VERSION 3.5) + +project(phidgets_api) + +# Default to C++14 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 14) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +find_package(ament_cmake_ros REQUIRED) +find_package(libphidget22 REQUIRED) + +include_directories(include) + +add_library(phidgets_api src/accelerometer.cpp + src/analog_input.cpp + src/analog_inputs.cpp + src/analog_output.cpp + src/analog_outputs.cpp + src/digital_input.cpp + src/digital_inputs.cpp + src/digital_output.cpp + src/digital_outputs.cpp + src/encoder.cpp + src/encoders.cpp + src/gyroscope.cpp + src/ir.cpp + src/magnetometer.cpp + src/motor.cpp + src/motors.cpp + src/phidget22.cpp + src/spatial.cpp + src/temperature.cpp) + +ament_target_dependencies(phidgets_api + libphidget22 +) + +install(TARGETS phidgets_api + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) + +install(DIRECTORY include/ + DESTINATION include +) + +ament_export_dependencies(ament_cmake libphidget22) +ament_export_include_directories(include) +ament_export_libraries(phidgets_api) + +ament_package() diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/debian/udev b/docker/local-ros2/phidgets_drivers/phidgets_api/debian/udev new file mode 100644 index 0000000..52074cc --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/debian/udev @@ -0,0 +1,7 @@ +# Very old Phidgets +SUBSYSTEMS=="usb", ACTION=="add", ATTRS{idVendor}=="0925", ATTRS{idProduct}=="8101", MODE="666" +SUBSYSTEMS=="usb", ACTION=="add", ATTRS{idVendor}=="0925", ATTRS{idProduct}=="8104", MODE="666" +SUBSYSTEMS=="usb", ACTION=="add", ATTRS{idVendor}=="0925", ATTRS{idProduct}=="8201", MODE="666" + +# All current and future Phidgets - Vendor = 0x06c2, Product = 0x0030 - 0x00af +SUBSYSTEMS=="usb", ACTION=="add", ATTRS{idVendor}=="06c2", ATTRS{idProduct}=="00[3-a][0-f]", MODE="666" diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/accelerometer.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/accelerometer.hpp new file mode 100644 index 0000000..a0f5690 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/accelerometer.hpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_ACCELEROMETER_HPP +#define PHIDGETS_API_ACCELEROMETER_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class Accelerometer final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Accelerometer) + + explicit Accelerometer( + int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function data_handler); + + ~Accelerometer(); + + int32_t getSerialNumber() const noexcept; + + void getAcceleration(double &x, double &y, double &z, + double ×tamp) const; + + void setDataInterval(uint32_t interval_ms) const; + + void dataHandler(const double acceleration[3], double timestamp) const; + + private: + int32_t serial_number_; + std::function data_handler_; + PhidgetAccelerometerHandle accel_handle_{nullptr}; + + static void DataHandler(PhidgetAccelerometerHandle input_handle, void *ctx, + const double acceleration[3], double timestamp); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_ACCELEROMETER_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/analog_input.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/analog_input.hpp new file mode 100644 index 0000000..4664874 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/analog_input.hpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_ANALOG_INPUT_HPP +#define PHIDGETS_API_ANALOG_INPUT_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class AnalogInput final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(AnalogInput) + + explicit AnalogInput(int32_t serial_number, int hub_port, + bool is_hub_port_device, int channel, + std::function input_handler); + + ~AnalogInput(); + + int32_t getSerialNumber() const noexcept; + + double getSensorValue() const; + + void setDataInterval(uint32_t data_interval_ms) const; + + void voltageChangeHandler(double sensorValue) const; + + private: + int32_t serial_number_; + int channel_; + std::function input_handler_; + PhidgetVoltageInputHandle ai_handle_{nullptr}; + + static void VoltageChangeHandler(PhidgetVoltageInputHandle input_handle, + void *ctx, double sensorValue); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_ANALOG_INPUT_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/analog_inputs.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/analog_inputs.hpp new file mode 100644 index 0000000..a961932 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/analog_inputs.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_ANALOG_INPUTS_HPP +#define PHIDGETS_API_ANALOG_INPUTS_HPP + +#include +#include +#include + +#include "phidgets_api/analog_input.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class AnalogInputs final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(AnalogInputs) + + explicit AnalogInputs(int32_t serial_number, int hub_port, + bool is_hub_port_device, + std::function input_handler); + + ~AnalogInputs() = default; + + int32_t getSerialNumber() const noexcept; + + uint32_t getInputCount() const noexcept; + + double getSensorValue(int index) const; + + void setDataInterval(int index, uint32_t data_interval_ms) const; + + private: + uint32_t input_count_{0}; + std::vector> ais_; +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_ANALOG_INPUTS_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/analog_output.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/analog_output.hpp new file mode 100644 index 0000000..fec7564 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/analog_output.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_ANALOG_OUTPUT_HPP +#define PHIDGETS_API_ANALOG_OUTPUT_HPP + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class AnalogOutput final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(AnalogOutput) + + explicit AnalogOutput(int32_t serial_number, int hub_port, + bool is_hub_port_device, int channel); + + ~AnalogOutput(); + + void setOutputVoltage(double voltage) const; + + void setEnabledOutput(int enabled) const; + + private: + PhidgetVoltageOutputHandle ao_handle_; +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_ANALOG_OUTPUT_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/analog_outputs.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/analog_outputs.hpp new file mode 100644 index 0000000..d93fa5f --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/analog_outputs.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_ANALOG_OUTPUTS_HPP +#define PHIDGETS_API_ANALOG_OUTPUTS_HPP + +#include +#include + +#include "phidgets_api/analog_output.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class AnalogOutputs final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(AnalogOutputs) + + explicit AnalogOutputs(int32_t serial_number, int hub_port, + bool is_hub_port_device); + + ~AnalogOutputs(); + + uint32_t getOutputCount() const noexcept; + + void setOutputVoltage(int index, double voltage) const; + + void setEnabledOutput(int index, int enabled) const; + + private: + uint32_t output_count_; + std::vector> aos_; +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_ANALOG_INPUTS_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/digital_input.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/digital_input.hpp new file mode 100644 index 0000000..cca92b4 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/digital_input.hpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_DIGITAL_INPUT_HPP +#define PHIDGETS_API_DIGITAL_INPUT_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class DigitalInput +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(DigitalInput) + + explicit DigitalInput(int32_t serial_number, int hub_port, + bool is_hub_port_device, int channel, + std::function input_handler); + + ~DigitalInput(); + + int32_t getSerialNumber() const noexcept; + + bool getInputValue() const; + + void stateChangeHandler(int state) const; + + private: + int32_t serial_number_; + int channel_; + std::function input_handler_; + PhidgetDigitalInputHandle di_handle_{nullptr}; + + static void StateChangeHandler(PhidgetDigitalInputHandle input_handle, + void *ctx, int state); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_DIGITAL_INPUT_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/digital_inputs.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/digital_inputs.hpp new file mode 100644 index 0000000..5188c6f --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/digital_inputs.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_DIGITAL_INPUTS_HPP +#define PHIDGETS_API_DIGITAL_INPUTS_HPP + +#include +#include +#include + +#include "phidgets_api/digital_input.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class DigitalInputs +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(DigitalInputs) + + explicit DigitalInputs(int32_t serial_number, int hub_port, + bool is_hub_port_device, + std::function input_handler); + + ~DigitalInputs() = default; + + int32_t getSerialNumber() const noexcept; + + uint32_t getInputCount() const noexcept; + + bool getInputValue(int index) const; + + private: + uint32_t input_count_{0}; + std::vector> dis_; +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_DIGITAL_INPUTS_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/digital_output.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/digital_output.hpp new file mode 100644 index 0000000..937967b --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/digital_output.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_DIGITAL_OUTPUT_HPP +#define PHIDGETS_API_DIGITAL_OUTPUT_HPP + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class DigitalOutput final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(DigitalOutput) + + explicit DigitalOutput(int32_t serial_number, int hub_port, + bool is_hub_port_device, int channel); + + ~DigitalOutput(); + + int32_t getSerialNumber() const noexcept; + + void setOutputState(bool state) const; + + private: + int32_t serial_number_; + PhidgetDigitalOutputHandle do_handle_{nullptr}; +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_DIGITAL_OUTPUT_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/digital_outputs.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/digital_outputs.hpp new file mode 100644 index 0000000..b87a57b --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/digital_outputs.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_DIGITAL_OUTPUTS_HPP +#define PHIDGETS_API_DIGITAL_OUTPUTS_HPP + +#include +#include + +#include "phidgets_api/digital_output.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class DigitalOutputs final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(DigitalOutputs) + + explicit DigitalOutputs(int32_t serial_number, int hub_port, + bool is_hub_port_device); + + ~DigitalOutputs() = default; + + int32_t getSerialNumber() const noexcept; + + uint32_t getOutputCount() const noexcept; + + void setOutputState(int index, bool state) const; + + private: + uint32_t output_count_{0}; + std::vector> dos_; +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_DIGITAL_OUTPUTS_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/encoder.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/encoder.hpp new file mode 100644 index 0000000..6ed06ec --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/encoder.hpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_ENCODER_HPP +#define PHIDGETS_API_ENCODER_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class Encoder final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Encoder) + + explicit Encoder( + int32_t serial_number, int hub_port, bool is_hub_port_device, + int channel, + std::function position_change_handler); + + ~Encoder(); + + int32_t getSerialNumber() const noexcept; + + /** @brief Reads the current position of an encoder + */ + int64_t getPosition() const; + + /** @brief Sets the offset of an encoder such that current position is the + * specified value + * @param position The new value that should be returned by + * 'getPosition(index)' at the current position of the encoder*/ + void setPosition(int64_t position) const; + + /** @brief Gets the position of an encoder the last time an index pulse + * occured. An index pulse in this context refers to an input from the + * encoder the pulses high once every revolution. + */ + int64_t getIndexPosition() const; + + /** @brief Checks if an encoder is powered on and receiving events + */ + bool getEnabled() const; + + /** @brief Set the powered state of an encoder. If an encoder is not + * enabled, it will not be given power, and events and changes in position + * will not be captured. + * @param enabled The new powered state of the encoder*/ + void setEnabled(bool enabled) const; + + void positionChangeHandler(int position_change, double time, + int index_triggered); + + private: + int32_t serial_number_; + int channel_; + std::function position_change_handler_; + PhidgetEncoderHandle encoder_handle_{nullptr}; + + static void PositionChangeHandler(PhidgetEncoderHandle phid, void *ctx, + int position_change, double time, + int index_triggered); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_ENCODER_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/encoders.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/encoders.hpp new file mode 100644 index 0000000..54a59c2 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/encoders.hpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_ENCODERS_HPP +#define PHIDGETS_API_ENCODERS_HPP + +#include +#include +#include + +#include "phidgets_api/encoder.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class Encoders final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Encoders) + + explicit Encoders( + int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function position_change_handler); + + ~Encoders() = default; + + int32_t getSerialNumber() const noexcept; + + /**@brief Gets the number of encoder input channels supported by this board + */ + uint32_t getEncoderCount() const; + + /** @brief Reads the current position of an encoder + * @param index The index of the encoder to read */ + int64_t getPosition(int index) const; + + /** @brief Sets the offset of an encoder such that current position is the + * specified value + * @param index The index of the encoder to set + * @param position The new value that should be returned by + * 'getPosition(index)' at the current position of the encoder*/ + void setPosition(int index, int64_t position) const; + + /** @brief Gets the position of an encoder the last time an index pulse + * occured. An index pulse in this context refers to an input from the + * encoder the pulses high once every revolution. + * @param index The index of the encoder to read */ + int64_t getIndexPosition(int index) const; + + /** @brief Checks if an encoder is powered on and receiving events + * @param index The index of the encoder to check */ + bool getEnabled(int index) const; + + /** @brief Set the powered state of an encoder. If an encoder is not + * enabled, it will not be given power, and events and changes in position + * will not be captured. + * @param index The index of the encoder to change + * @param enabled The new powered state of the encoder*/ + void setEnabled(int index, bool enabled) const; + + private: + uint32_t encoder_count_{0}; + std::vector> encs_; +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_ENCODERS_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/gyroscope.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/gyroscope.hpp new file mode 100644 index 0000000..b0326a9 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/gyroscope.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_GYROSCOPE_HPP +#define PHIDGETS_API_GYROSCOPE_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class Gyroscope final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Gyroscope) + + explicit Gyroscope( + int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function data_handler); + + ~Gyroscope(); + + int32_t getSerialNumber() const noexcept; + + void getAngularRate(double &x, double &y, double &z, + double ×tamp) const; + + void setDataInterval(uint32_t interval_ms) const; + + void zero() const; + + void dataHandler(const double angular_rate[3], double timestamp) const; + + private: + int32_t serial_number_; + std::function data_handler_; + PhidgetGyroscopeHandle gyro_handle_{nullptr}; + + static void DataHandler(PhidgetGyroscopeHandle input_handle, void *ctx, + const double angular_rate[3], double timestamp); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_GYROSCOPE_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/ir.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/ir.hpp new file mode 100644 index 0000000..050af4b --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/ir.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_IR_HPP +#define PHIDGETS_API_IR_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class IR final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(IR) + + explicit IR(int32_t serial_number, + std::function code_handler); + + ~IR(); + + int32_t getSerialNumber() const noexcept; + + void codeHandler(const char *code, uint32_t bit_count, int is_repeat) const; + + private: + int32_t serial_number_; + std::function code_handler_; + PhidgetIRHandle ir_handle_{nullptr}; + + static void CodeHandler(PhidgetIRHandle ir, void *ctx, const char *code, + uint32_t bit_count, int is_repeat); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_IR_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/magnetometer.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/magnetometer.hpp new file mode 100644 index 0000000..f13684a --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/magnetometer.hpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_MAGNETOMETER_HPP +#define PHIDGETS_API_MAGNETOMETER_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class Magnetometer final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Magnetometer) + + explicit Magnetometer( + int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function data_handler); + + ~Magnetometer(); + + int32_t getSerialNumber() const noexcept; + + void setCompassCorrectionParameters(double cc_mag_field, double cc_offset0, + double cc_offset1, double cc_offset2, + double cc_gain0, double cc_gain1, + double cc_gain2, double cc_T0, + double cc_T1, double cc_T2, + double cc_T3, double cc_T4, + double cc_T5); + + void getMagneticField(double &x, double &y, double &z, + double ×tamp) const; + + void setDataInterval(uint32_t interval_ms) const; + + void dataHandler(const double magnetic_field[3], double timestamp) const; + + private: + int32_t serial_number_; + std::function data_handler_; + PhidgetMagnetometerHandle mag_handle_{nullptr}; + + static void DataHandler(PhidgetMagnetometerHandle input_handle, void *ctx, + const double magnetic_field[3], double timestamp); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_MAGNETOMETER_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/motor.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/motor.hpp new file mode 100644 index 0000000..aab3ad5 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/motor.hpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_MOTOR_HPP +#define PHIDGETS_API_MOTOR_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class Motor final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Motor) + + explicit Motor(int32_t serial_number, int hub_port, bool is_hub_port_device, + int channel, + std::function duty_cycle_change_handler, + std::function back_emf_change_handler); + + ~Motor(); + + int32_t getSerialNumber() const noexcept; + + double getDutyCycle() const; + void setDutyCycle(double duty_cycle) const; + double getAcceleration() const; + void setAcceleration(double acceleration) const; + bool backEMFSensingSupported() const; + double getBackEMF() const; + void setDataInterval(uint32_t data_interval_ms) const; + + double getBraking() const; + void setBraking(double braking) const; + + void dutyCycleChangeHandler(double duty_cycle) const; + + void backEMFChangeHandler(double back_emf) const; + + private: + int32_t serial_number_; + int channel_; + std::function duty_cycle_change_handler_; + std::function back_emf_change_handler_; + PhidgetDCMotorHandle motor_handle_{nullptr}; + bool back_emf_sensing_supported_; + + static void DutyCycleChangeHandler(PhidgetDCMotorHandle motor_handle, + void *ctx, double duty_cycle); + static void BackEMFChangeHandler(PhidgetDCMotorHandle motor_handle, + void *ctx, double back_emf); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_MOTOR_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/motors.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/motors.hpp new file mode 100644 index 0000000..c7a450d --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/motors.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_MOTORS_HPP +#define PHIDGETS_API_MOTORS_HPP + +#include +#include +#include + +#include "phidgets_api/motor.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class Motors final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Motors) + + explicit Motors(int32_t serial_number, int hub_port, + bool is_hub_port_device, + std::function duty_cycle_change_handler, + std::function back_emf_change_handler); + + ~Motors() = default; + + int32_t getSerialNumber() const noexcept; + + uint32_t getMotorCount() const noexcept; + double getDutyCycle(int index) const; + void setDutyCycle(int index, double duty_cycle) const; + double getAcceleration(int index) const; + void setAcceleration(int index, double acceleration) const; + bool backEMFSensingSupported(int index) const; + double getBackEMF(int index) const; + void setDataInterval(int index, uint32_t data_interval_ms) const; + + double getBraking(int index) const; + void setBraking(int index, double braking) const; + + private: + uint32_t motor_count_{0}; + std::vector> motors_; +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_MOTORS_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/phidget22.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/phidget22.hpp new file mode 100644 index 0000000..7c31a26 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/phidget22.hpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_PHIDGET22_HPP +#define PHIDGETS_API_PHIDGET22_HPP + +#include +#include + +#include + +#define PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Classname) \ + Classname(const Classname &) = delete; \ + void operator=(const Classname &) = delete; \ + Classname(Classname &&) = delete; \ + void operator=(Classname &&) = delete; + +namespace phidgets { + +class Phidget22Error final : public std::exception +{ + public: + explicit Phidget22Error(const std::string &msg, PhidgetReturnCode code); + + const char *what() const noexcept override; + + private: + std::string msg_; +}; + +namespace helpers { + +void openWaitForAttachment(PhidgetHandle handle, int32_t serial_number, + int hub_port, bool is_hub_port_device, int channel); + +void closeAndDelete(PhidgetHandle *handle) noexcept; + +} // namespace helpers +} // namespace phidgets + +#endif // PHIDGETS_API_PHIDGET22_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/spatial.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/spatial.hpp new file mode 100644 index 0000000..ffb1302 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/spatial.hpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_SPATIAL_HPP +#define PHIDGETS_API_SPATIAL_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class Spatial final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Spatial) + + explicit Spatial( + int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function + data_handler, + std::function algorithm_data_handler, + std::function attach_handler = nullptr, + std::function detach_handler = nullptr); + + ~Spatial(); + + int32_t getSerialNumber() const noexcept; + + void setCompassCorrectionParameters(double cc_mag_field, double cc_offset0, + double cc_offset1, double cc_offset2, + double cc_gain0, double cc_gain1, + double cc_gain2, double cc_T0, + double cc_T1, double cc_T2, + double cc_T3, double cc_T4, + double cc_T5); + + void setSpatialAlgorithm(const std::string algorithm); + + void setAHRSParameters(double angularVelocityThreshold, + double angularVelocityDeltaThreshold, + double accelerationThreshold, double magTime, + double accelTime, double biasTime); + + void setAlgorithmMagnetometerGain(double magnetometer_gain); + + void setHeatingEnabled(bool heating_enabled); + + void setDataInterval(uint32_t interval_ms) const; + + void zero() const; + + void dataHandler(const double acceleration[3], const double angular_rate[3], + const double magnetic_field[3], double timestamp) const; + + void algorithmDataHandler(const double quaternion[4], + double timestamp) const; + + virtual void attachHandler(); + virtual void detachHandler(); + + private: + int32_t serial_number_; + std::function + data_handler_; + + std::function + algorithm_data_handler_; + + std::function attach_handler_; + std::function detach_handler_; + + PhidgetSpatialHandle spatial_handle_{nullptr}; + + static void DataHandler(PhidgetSpatialHandle input_handle, void *ctx, + const double acceleration[3], + const double angular_rate[3], + const double magnetic_field[3], double timestamp); + static void AlgorithmDataHandler(PhidgetSpatialHandle input_handle, + void *ctx, const double quaternion[4], + double timestamp); + static void AttachHandler(PhidgetHandle input_handle, void *ctx); + static void DetachHandler(PhidgetHandle input_handle, void *ctx); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_SPATIAL_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/temperature.hpp b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/temperature.hpp new file mode 100644 index 0000000..5d20404 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/include/phidgets_api/temperature.hpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_TEMPERATURE_HPP +#define PHIDGETS_API_TEMPERATURE_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +enum class ThermocoupleType { + J_TYPE = 1, + K_TYPE = 2, + E_TYPE = 3, + T_TYPE = 4, +}; + +class Temperature final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Temperature) + + explicit Temperature(int32_t serial_number, int hub_port, + bool is_hub_port_device, + std::function temperature_handler); + + ~Temperature(); + + int32_t getSerialNumber() const noexcept; + + void setThermocoupleType(ThermocoupleType type); + + double getTemperature() const; + + void setDataInterval(uint32_t interval_ms) const; + + void temperatureChangeHandler(double temperature) const; + + private: + int32_t serial_number_; + std::function temperature_handler_; + PhidgetTemperatureSensorHandle temperature_handle_{nullptr}; + + static void TemperatureChangeHandler( + PhidgetTemperatureSensorHandle temperature_handle, void *ctx, + double temperature); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_TEMPERATURE_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/package.xml b/docker/local-ros2/phidgets_drivers/phidgets_api/package.xml new file mode 100644 index 0000000..c1c096d --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/package.xml @@ -0,0 +1,26 @@ + + + phidgets_api + 2.3.3 + A C++ Wrapper for the Phidgets C API + + Martin Günther + Chris Lalancette + + BSD + + http://ros.org/wiki/phidgets_api + https://github.com/ros-drivers/phidgets_drivers.git + https://github.com/ros-drivers/phidgets_drivers/issues + + Tully Foote + Ivan Dryanovski + + ament_cmake_ros + + libphidget22 + + + ament_cmake + + diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/accelerometer.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/accelerometer.cpp new file mode 100644 index 0000000..869c498 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/accelerometer.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 +#include + +#include + +#include "phidgets_api/accelerometer.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +Accelerometer::Accelerometer( + int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function data_handler) + : serial_number_(serial_number), data_handler_(data_handler) +{ + PhidgetReturnCode ret = PhidgetAccelerometer_create(&accel_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to create Accelerometer handle", ret); + } + + helpers::openWaitForAttachment( + reinterpret_cast(accel_handle_), serial_number, hub_port, + is_hub_port_device, 0); + + ret = PhidgetAccelerometer_setOnAccelerationChangeHandler( + accel_handle_, DataHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set change handler for acceleration", + ret); + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(accel_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get serial number for acceleration", + ret); + } + } +} + +Accelerometer::~Accelerometer() +{ + PhidgetHandle handle = reinterpret_cast(accel_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t Accelerometer::getSerialNumber() const noexcept +{ + return serial_number_; +} + +void Accelerometer::getAcceleration(double &x, double &y, double &z, + double ×tamp) const +{ + double accel[3]; + PhidgetReturnCode ret = + PhidgetAccelerometer_getAcceleration(accel_handle_, &accel); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get acceleration", ret); + } + + x = accel[0]; + y = accel[1]; + z = accel[2]; + + double ts; + ret = PhidgetAccelerometer_getTimestamp(accel_handle_, &ts); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get acceleration timestamp", ret); + } + + timestamp = ts; +} + +void Accelerometer::setDataInterval(uint32_t interval_ms) const +{ + PhidgetReturnCode ret = + PhidgetAccelerometer_setDataInterval(accel_handle_, interval_ms); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set data interval", ret); + } +} + +void Accelerometer::dataHandler(const double acceleration[3], + double timestamp) const +{ + data_handler_(acceleration, timestamp); +} + +void Accelerometer::DataHandler(PhidgetAccelerometerHandle /* input_handle */, + void *ctx, const double acceleration[3], + double timestamp) +{ + (reinterpret_cast(ctx)) + ->dataHandler(acceleration, timestamp); +} + +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/analog_input.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/analog_input.cpp new file mode 100644 index 0000000..4298bce --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/analog_input.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 +#include + +#include + +#include "phidgets_api/analog_input.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +AnalogInput::AnalogInput(int32_t serial_number, int hub_port, + bool is_hub_port_device, int channel, + std::function input_handler) + : serial_number_(serial_number), + channel_(channel), + input_handler_(input_handler) +{ + PhidgetReturnCode ret = PhidgetVoltageInput_create(&ai_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create AnalogInput handle for channel " + + std::to_string(channel), + ret); + } + + helpers::openWaitForAttachment(reinterpret_cast(ai_handle_), + serial_number, hub_port, is_hub_port_device, + channel); + + if (!is_hub_port_device) + { + ret = + PhidgetVoltageInput_setVoltageRange(ai_handle_, VOLTAGE_RANGE_AUTO); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set analog input voltage range", + ret); + } + } + + ret = PhidgetVoltageInput_setOnVoltageChangeHandler( + ai_handle_, VoltageChangeHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to set change handler for AnalogInput channel " + + std::to_string(channel), + ret); + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(ai_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to get serial number for analog input channel " + + std::to_string(channel), + ret); + } + } +} + +AnalogInput::~AnalogInput() +{ + PhidgetHandle handle = reinterpret_cast(ai_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t AnalogInput::getSerialNumber() const noexcept +{ + return serial_number_; +} + +double AnalogInput::getSensorValue() const +{ + double sensor_value; + PhidgetReturnCode ret = + PhidgetVoltageInput_getSensorValue(ai_handle_, &sensor_value); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get analog sensor value", ret); + } + + return sensor_value; +} + +void AnalogInput::setDataInterval(uint32_t data_interval_ms) const +{ + PhidgetReturnCode ret = + PhidgetVoltageInput_setDataInterval(ai_handle_, data_interval_ms); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set analog data interval", ret); + } +} + +void AnalogInput::voltageChangeHandler(double sensorValue) const +{ + input_handler_(channel_, sensorValue); +} + +void AnalogInput::VoltageChangeHandler( + PhidgetVoltageInputHandle /* input_handle */, void *ctx, double sensorValue) +{ + (reinterpret_cast(ctx))->voltageChangeHandler(sensorValue); +} + +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/analog_inputs.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/analog_inputs.cpp new file mode 100644 index 0000000..a730b39 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/analog_inputs.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 +#include + +#include + +#include "phidgets_api/analog_input.hpp" +#include "phidgets_api/analog_inputs.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +AnalogInputs::AnalogInputs(int32_t serial_number, int hub_port, + bool is_hub_port_device, + std::function input_handler) +{ + PhidgetReturnCode ret; + + PhidgetVoltageInputHandle ai_handle; + + ret = PhidgetVoltageInput_create(&ai_handle); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create AnalogInput handle for determining channel " + "count", + ret); + } + + PhidgetHandle handle = reinterpret_cast(ai_handle); + + helpers::openWaitForAttachment(handle, serial_number, hub_port, + is_hub_port_device, 0); + + ret = Phidget_getDeviceChannelCount(handle, PHIDCHCLASS_VOLTAGEINPUT, + &input_count_); + + helpers::closeAndDelete(&handle); + + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get AnalogInput device channel count", + ret); + } + + ais_.resize(input_count_); + for (uint32_t i = 0; i < input_count_; ++i) + { + ais_[i] = std::make_unique( + serial_number, hub_port, is_hub_port_device, i, input_handler); + } +} + +int32_t AnalogInputs::getSerialNumber() const noexcept +{ + return ais_.at(0)->getSerialNumber(); +} + +uint32_t AnalogInputs::getInputCount() const noexcept +{ + return input_count_; +} + +double AnalogInputs::getSensorValue(int index) const +{ + return ais_.at(index)->getSensorValue(); +} + +void AnalogInputs::setDataInterval(int index, uint32_t data_interval_ms) const +{ + ais_.at(index)->setDataInterval(data_interval_ms); +} + +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/analog_output.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/analog_output.cpp new file mode 100644 index 0000000..6e1619f --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/analog_output.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/analog_output.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +AnalogOutput::AnalogOutput(int32_t serial_number, int hub_port, + bool is_hub_port_device, int channel) +{ + PhidgetReturnCode ret; + ret = PhidgetVoltageOutput_create(&ao_handle_); + + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create AnalogOutput handle for channel " + + std::to_string(channel), + ret); + } + + helpers::openWaitForAttachment(reinterpret_cast(ao_handle_), + serial_number, hub_port, is_hub_port_device, + channel); +} + +AnalogOutput::~AnalogOutput() +{ + PhidgetHandle handle = reinterpret_cast(ao_handle_); + helpers::closeAndDelete(&handle); +} + +void AnalogOutput::setOutputVoltage(double voltage) const +{ + PhidgetReturnCode ret = + PhidgetVoltageOutput_setVoltage(ao_handle_, voltage); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set analog output voltage", ret); + } +} + +void AnalogOutput::setEnabledOutput(int enabled) const +{ + PhidgetReturnCode ret = + PhidgetVoltageOutput_setEnabled(ao_handle_, enabled); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set analog output enabled", ret); + } +} + +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/analog_outputs.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/analog_outputs.cpp new file mode 100644 index 0000000..bb5cc89 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/analog_outputs.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 "phidgets_api/analog_output.hpp" +#include "phidgets_api/analog_outputs.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +AnalogOutputs::AnalogOutputs(int32_t serial_number, int hub_port, + bool is_hub_port_device) +{ + PhidgetReturnCode ret; + + PhidgetVoltageOutputHandle ao_handle; + + ret = PhidgetVoltageOutput_create(&ao_handle); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create AnalogOutput handle for determining channel " + "count", + ret); + } + + PhidgetHandle handle = reinterpret_cast(ao_handle); + + helpers::openWaitForAttachment(handle, serial_number, hub_port, + is_hub_port_device, 0); + + ret = Phidget_getDeviceChannelCount(handle, PHIDCHCLASS_VOLTAGEOUTPUT, + &output_count_); + + helpers::closeAndDelete(&handle); + + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get AnalogOutput device channel count", + ret); + } + + aos_.resize(output_count_); + for (uint32_t i = 0; i < output_count_; ++i) + { + aos_[i] = std::make_unique(serial_number, hub_port, + is_hub_port_device, i); + } +} + +AnalogOutputs::~AnalogOutputs() +{ +} + +uint32_t AnalogOutputs::getOutputCount() const noexcept +{ + return output_count_; +} + +void AnalogOutputs::setOutputVoltage(int index, double voltage) const +{ + aos_.at(index)->setOutputVoltage(voltage); +} + +void AnalogOutputs::setEnabledOutput(int index, int enabled) const +{ + aos_.at(index)->setEnabledOutput(enabled); +} + +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/digital_input.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/digital_input.cpp new file mode 100644 index 0000000..323cde3 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/digital_input.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/digital_input.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +DigitalInput::DigitalInput(int32_t serial_number, int hub_port, + bool is_hub_port_device, int channel, + std::function input_handler) + : serial_number_(serial_number), + channel_(channel), + input_handler_(input_handler) +{ + PhidgetReturnCode ret = PhidgetDigitalInput_create(&di_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create DigitalInput handle for channel " + + std::to_string(channel), + ret); + } + + helpers::openWaitForAttachment(reinterpret_cast(di_handle_), + serial_number, hub_port, is_hub_port_device, + channel); + + ret = PhidgetDigitalInput_setOnStateChangeHandler(di_handle_, + StateChangeHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to set change handler for DigitalInput channel " + + std::to_string(channel), + ret); + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(di_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to get serial number for digital input channel " + + std::to_string(channel), + ret); + } + } +} + +DigitalInput::~DigitalInput() +{ + PhidgetHandle handle = reinterpret_cast(di_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t DigitalInput::getSerialNumber() const noexcept +{ + return serial_number_; +} + +bool DigitalInput::getInputValue() const +{ + int state; + PhidgetReturnCode ret = PhidgetDigitalInput_getState(di_handle_, &state); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get digital input state", ret); + } + + return !!state; +} + +void DigitalInput::stateChangeHandler(int state) const +{ + input_handler_(channel_, state); +} + +void DigitalInput::StateChangeHandler( + PhidgetDigitalInputHandle /* input_handle */, void *ctx, int state) +{ + (reinterpret_cast(ctx))->stateChangeHandler(state); +} + +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/digital_inputs.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/digital_inputs.cpp new file mode 100644 index 0000000..fd404aa --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/digital_inputs.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/digital_input.hpp" +#include "phidgets_api/digital_inputs.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +DigitalInputs::DigitalInputs(int32_t serial_number, int hub_port, + bool is_hub_port_device, + std::function input_handler) +{ + PhidgetReturnCode ret; + + PhidgetDigitalInputHandle di_handle; + + ret = PhidgetDigitalInput_create(&di_handle); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create DigitalInput handle for determining channel " + "count", + ret); + } + + PhidgetHandle handle = reinterpret_cast(di_handle); + + helpers::openWaitForAttachment(handle, serial_number, hub_port, + is_hub_port_device, 0); + + ret = Phidget_getDeviceChannelCount(handle, PHIDCHCLASS_DIGITALINPUT, + &input_count_); + + helpers::closeAndDelete(&handle); + + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get DigitalInput device channel count", + ret); + } + + dis_.resize(input_count_); + for (uint32_t i = 0; i < input_count_; ++i) + { + dis_[i] = std::make_unique( + serial_number, hub_port, is_hub_port_device, i, input_handler); + } +} + +int32_t DigitalInputs::getSerialNumber() const noexcept +{ + return dis_.at(0)->getSerialNumber(); +} + +uint32_t DigitalInputs::getInputCount() const noexcept +{ + return input_count_; +} + +bool DigitalInputs::getInputValue(int index) const +{ + return dis_.at(index)->getInputValue(); +} + +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/digital_output.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/digital_output.cpp new file mode 100644 index 0000000..48daf40 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/digital_output.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/digital_output.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +DigitalOutput::DigitalOutput(int32_t serial_number, int hub_port, + bool is_hub_port_device, int channel) + : serial_number_(serial_number) +{ + PhidgetReturnCode ret; + + ret = PhidgetDigitalOutput_create(&do_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create DigitalOutput handle for channel " + + std::to_string(channel), + ret); + } + + helpers::openWaitForAttachment(reinterpret_cast(do_handle_), + serial_number, hub_port, is_hub_port_device, + channel); + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(do_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to get serial number for digital output channel " + + std::to_string(channel), + ret); + } + } +} + +DigitalOutput::~DigitalOutput() +{ + PhidgetHandle handle = reinterpret_cast(do_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t DigitalOutput::getSerialNumber() const noexcept +{ + return serial_number_; +} + +void DigitalOutput::setOutputState(bool state) const +{ + PhidgetReturnCode ret = PhidgetDigitalOutput_setState(do_handle_, state); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set state for DigitalOutput", ret); + } +} + +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/digital_outputs.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/digital_outputs.cpp new file mode 100644 index 0000000..5f96ebd --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/digital_outputs.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/digital_output.hpp" +#include "phidgets_api/digital_outputs.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +DigitalOutputs::DigitalOutputs(int32_t serial_number, int hub_port, + bool is_hub_port_device) +{ + PhidgetReturnCode ret; + + PhidgetDigitalOutputHandle do_handle; + + ret = PhidgetDigitalOutput_create(&do_handle); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create DigitalOutput handle for determining channel " + "count", + ret); + } + + PhidgetHandle handle = reinterpret_cast(do_handle); + + helpers::openWaitForAttachment(handle, serial_number, hub_port, + is_hub_port_device, 0); + + ret = Phidget_getDeviceChannelCount(handle, PHIDCHCLASS_DIGITALOUTPUT, + &output_count_); + + helpers::closeAndDelete(&handle); + + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get DigitalOutput device channel count", + ret); + } + + dos_.resize(output_count_); + for (uint32_t i = 0; i < output_count_; ++i) + { + dos_[i] = std::make_unique(serial_number, hub_port, + is_hub_port_device, i); + } +} + +int32_t DigitalOutputs::getSerialNumber() const noexcept +{ + return dos_.at(0)->getSerialNumber(); +} + +uint32_t DigitalOutputs::getOutputCount() const noexcept +{ + return output_count_; +} + +void DigitalOutputs::setOutputState(int index, bool state) const +{ + dos_.at(index)->setOutputState(state); +} + +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/encoder.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/encoder.cpp new file mode 100644 index 0000000..31731a5 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/encoder.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 "phidgets_api/encoder.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +Encoder::Encoder( + int32_t serial_number, int hub_port, bool is_hub_port_device, int channel, + std::function position_change_handler) + : serial_number_(serial_number), + channel_(channel), + position_change_handler_(position_change_handler) +{ + // create the handle + PhidgetReturnCode ret = PhidgetEncoder_create(&encoder_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to create Encoder handle", ret); + } + + helpers::openWaitForAttachment( + reinterpret_cast(encoder_handle_), serial_number, + hub_port, is_hub_port_device, channel); + + ret = PhidgetEncoder_setOnPositionChangeHandler( + encoder_handle_, PositionChangeHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to set change handler for Encoder channel " + + std::to_string(channel), + ret); + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(encoder_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to get serial number for encoder channel " + + std::to_string(channel), + ret); + } + } +} + +Encoder::~Encoder() +{ + PhidgetHandle handle = reinterpret_cast(encoder_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t Encoder::getSerialNumber() const noexcept +{ + return serial_number_; +} + +int64_t Encoder::getPosition() const +{ + int64_t position; + PhidgetReturnCode ret = + PhidgetEncoder_getPosition(encoder_handle_, &position); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get position for Encoder channel " + + std::to_string(channel_), + ret); + } + + return position; +} + +void Encoder::setPosition(int64_t position) const +{ + PhidgetReturnCode ret = + PhidgetEncoder_setPosition(encoder_handle_, position); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set position for Encoder channel " + + std::to_string(channel_), + ret); + } +} + +int64_t Encoder::getIndexPosition() const +{ + int64_t position; + PhidgetReturnCode ret = + PhidgetEncoder_getIndexPosition(encoder_handle_, &position); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to get index position for Encoder channel " + + std::to_string(channel_), + ret); + } + + return position; +} + +bool Encoder::getEnabled() const +{ + int enabled; + PhidgetReturnCode ret = + PhidgetEncoder_getEnabled(encoder_handle_, &enabled); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get enabled for Encoder channel " + + std::to_string(channel_), + ret); + } + + return enabled == PTRUE; +} + +void Encoder::setEnabled(bool enabled) const +{ + PhidgetReturnCode ret = + PhidgetEncoder_setEnabled(encoder_handle_, enabled ? PTRUE : PFALSE); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set enabled for Encoder channel " + + std::to_string(channel_), + ret); + } +} + +void Encoder::positionChangeHandler(int position_change, double time, + int index_triggered) +{ + position_change_handler_(channel_, position_change, time, index_triggered); +} + +void Encoder::PositionChangeHandler(PhidgetEncoderHandle /* phid */, void *ctx, + int position_change, double time, + int index_triggered) +{ + (reinterpret_cast(ctx)) + ->positionChangeHandler(position_change, time, index_triggered); +} + +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/encoders.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/encoders.cpp new file mode 100644 index 0000000..548c472 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/encoders.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/encoder.hpp" +#include "phidgets_api/encoders.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +Encoders::Encoders( + int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function position_change_handler) +{ + PhidgetReturnCode ret; + + PhidgetEncoderHandle enc_handle; + + ret = PhidgetEncoder_create(&enc_handle); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create Encoder handle for determining channel " + "count", + ret); + } + + PhidgetHandle handle = reinterpret_cast(enc_handle); + + helpers::openWaitForAttachment(handle, serial_number, hub_port, + is_hub_port_device, 0); + + ret = Phidget_getDeviceChannelCount(handle, PHIDCHCLASS_ENCODER, + &encoder_count_); + + helpers::closeAndDelete(&handle); + + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get Encoder device channel count", ret); + } + + encs_.resize(encoder_count_); + for (uint32_t i = 0; i < encoder_count_; ++i) + { + encs_[i] = std::make_unique(serial_number, hub_port, + is_hub_port_device, i, + position_change_handler); + } +} + +int32_t Encoders::getSerialNumber() const noexcept +{ + return encs_.at(0)->getSerialNumber(); +} + +uint32_t Encoders::getEncoderCount() const +{ + return encoder_count_; +} + +int64_t Encoders::getPosition(int index) const +{ + return encs_.at(index)->getPosition(); +} + +void Encoders::setPosition(int index, int64_t position) const +{ + return encs_.at(index)->setPosition(position); +} + +int64_t Encoders::getIndexPosition(int index) const +{ + return encs_.at(index)->getIndexPosition(); +} + +bool Encoders::getEnabled(int index) const +{ + return encs_.at(index)->getEnabled(); +} + +void Encoders::setEnabled(int index, bool enabled) const +{ + return encs_.at(index)->setEnabled(enabled); +} + +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/gyroscope.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/gyroscope.cpp new file mode 100644 index 0000000..5fca8a9 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/gyroscope.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 +#include + +#include + +#include "phidgets_api/gyroscope.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +Gyroscope::Gyroscope(int32_t serial_number, int hub_port, + bool is_hub_port_device, + std::function data_handler) + : serial_number_(serial_number), data_handler_(data_handler) +{ + PhidgetReturnCode ret = PhidgetGyroscope_create(&gyro_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to create Gyroscope handle", ret); + } + + helpers::openWaitForAttachment( + reinterpret_cast(gyro_handle_), serial_number, hub_port, + is_hub_port_device, 0); + + ret = PhidgetGyroscope_setOnAngularRateUpdateHandler(gyro_handle_, + DataHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set Gyroscope update handler", ret); + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(gyro_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get serial number for gyroscope", + ret); + } + } +} + +Gyroscope::~Gyroscope() +{ + PhidgetHandle handle = reinterpret_cast(gyro_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t Gyroscope::getSerialNumber() const noexcept +{ + return serial_number_; +} + +void Gyroscope::zero() const +{ + PhidgetReturnCode ret = PhidgetGyroscope_zero(gyro_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to calibrate Gyroscope", ret); + } +} + +void Gyroscope::getAngularRate(double &x, double &y, double &z, + double ×tamp) const +{ + double angular_rate[3]; + PhidgetReturnCode ret = + PhidgetGyroscope_getAngularRate(gyro_handle_, &angular_rate); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get angular rate from gyroscope", ret); + } + + x = angular_rate[0]; + y = angular_rate[1]; + z = angular_rate[2]; + + double ts; + ret = PhidgetGyroscope_getTimestamp(gyro_handle_, &ts); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get timestamp from gyroscope", ret); + } + + timestamp = ts; +} + +void Gyroscope::setDataInterval(uint32_t interval_ms) const +{ + PhidgetReturnCode ret = + PhidgetGyroscope_setDataInterval(gyro_handle_, interval_ms); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set data interval", ret); + } +} + +void Gyroscope::dataHandler(const double angular_rate[3], + double timestamp) const +{ + data_handler_(angular_rate, timestamp); +} + +void Gyroscope::DataHandler(PhidgetGyroscopeHandle /* input_handle */, + void *ctx, const double angular_rate[3], + double timestamp) +{ + (reinterpret_cast(ctx))->dataHandler(angular_rate, timestamp); +} + +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/ir.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/ir.cpp new file mode 100644 index 0000000..f80c0f3 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/ir.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 "phidgets_api/ir.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +IR::IR(int32_t serial_number, + std::function code_handler) + : serial_number_(serial_number), code_handler_(code_handler) +{ + // create the handle + PhidgetReturnCode ret = PhidgetIR_create(&ir_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to create IR handle", ret); + } + + helpers::openWaitForAttachment(reinterpret_cast(ir_handle_), + serial_number, 0, false, 0); + + // register ir data callback + ret = PhidgetIR_setOnCodeHandler(ir_handle_, CodeHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set code handler for ir", ret); + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(ir_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get serial number for IR", ret); + } + } +} + +IR::~IR() +{ + PhidgetHandle handle = reinterpret_cast(ir_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t IR::getSerialNumber() const noexcept +{ + return serial_number_; +} + +void IR::codeHandler(const char *code, uint32_t bit_count, int is_repeat) const +{ + code_handler_(code, bit_count, is_repeat); +} + +void IR::CodeHandler(PhidgetIRHandle /* ir */, void *ctx, const char *code, + uint32_t bit_count, int is_repeat) +{ + (reinterpret_cast(ctx))->codeHandler(code, bit_count, is_repeat); +} + +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/magnetometer.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/magnetometer.cpp new file mode 100644 index 0000000..69fcb89 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/magnetometer.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 +#include + +#include + +#include "phidgets_api/magnetometer.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +Magnetometer::Magnetometer( + int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function data_handler) + : serial_number_(serial_number), data_handler_(data_handler) +{ + PhidgetReturnCode ret = PhidgetMagnetometer_create(&mag_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to create Magnetometer handle", ret); + } + + helpers::openWaitForAttachment(reinterpret_cast(mag_handle_), + serial_number, hub_port, is_hub_port_device, + 0); + + ret = PhidgetMagnetometer_setOnMagneticFieldChangeHandler( + mag_handle_, DataHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set change handler for Magnetometer", + ret); + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(mag_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get serial number for magnetometer", + ret); + } + } +} + +Magnetometer::~Magnetometer() +{ + PhidgetHandle handle = reinterpret_cast(mag_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t Magnetometer::getSerialNumber() const noexcept +{ + return serial_number_; +} + +void Magnetometer::setCompassCorrectionParameters( + double cc_mag_field, double cc_offset0, double cc_offset1, + double cc_offset2, double cc_gain0, double cc_gain1, double cc_gain2, + double cc_T0, double cc_T1, double cc_T2, double cc_T3, double cc_T4, + double cc_T5) +{ + PhidgetReturnCode ret = PhidgetMagnetometer_setCorrectionParameters( + mag_handle_, cc_mag_field, cc_offset0, cc_offset1, cc_offset2, cc_gain0, + cc_gain1, cc_gain2, cc_T0, cc_T1, cc_T2, cc_T3, cc_T4, cc_T5); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set magnetometer correction parameters", + ret); + } +} + +void Magnetometer::getMagneticField(double &x, double &y, double &z, + double ×tamp) const +{ + double mag_field[3]; + PhidgetReturnCode ret = + PhidgetMagnetometer_getMagneticField(mag_handle_, &mag_field); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get magnetic field", ret); + } + + x = mag_field[0]; + y = mag_field[1]; + z = mag_field[2]; + + double ts; + ret = PhidgetMagnetometer_getTimestamp(mag_handle_, &ts); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get magnetic field timestamp", ret); + } + + timestamp = ts; +} + +void Magnetometer::setDataInterval(uint32_t interval_ms) const +{ + PhidgetReturnCode ret = + PhidgetMagnetometer_setDataInterval(mag_handle_, interval_ms); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set data interval", ret); + } +} + +void Magnetometer::dataHandler(const double magnetic_field[3], + double timestamp) const +{ + data_handler_(magnetic_field, timestamp); +} + +void Magnetometer::DataHandler(PhidgetMagnetometerHandle /* input_handle */, + void *ctx, const double magnetic_field[3], + double timestamp) +{ + (reinterpret_cast(ctx)) + ->dataHandler(magnetic_field, timestamp); +} + +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/motor.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/motor.cpp new file mode 100644 index 0000000..3b14270 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/motor.cpp @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/motor.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +Motor::Motor(int32_t serial_number, int hub_port, bool is_hub_port_device, + int channel, + std::function duty_cycle_change_handler, + std::function back_emf_change_handler) + : serial_number_(serial_number), + channel_(channel), + duty_cycle_change_handler_(duty_cycle_change_handler), + back_emf_change_handler_(back_emf_change_handler) +{ + PhidgetReturnCode ret = PhidgetDCMotor_create(&motor_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to create Motor handle for channel " + + std::to_string(channel), + ret); + } + + helpers::openWaitForAttachment( + reinterpret_cast(motor_handle_), serial_number, hub_port, + is_hub_port_device, channel); + + ret = PhidgetDCMotor_setOnVelocityUpdateHandler( + motor_handle_, DutyCycleChangeHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to set duty cycle update handler for Motor channel " + + std::to_string(channel), + ret); + } + + back_emf_sensing_supported_ = true; + ret = PhidgetDCMotor_setBackEMFSensingState(motor_handle_, 1); + if (ret == EPHIDGET_UNSUPPORTED) + { + back_emf_sensing_supported_ = false; + } else if (ret == EPHIDGET_OK) + { + ret = PhidgetDCMotor_setOnBackEMFChangeHandler( + motor_handle_, BackEMFChangeHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to set back EMF update handler for Motor channel " + + std::to_string(channel), + ret); + } + } else + { + throw Phidget22Error( + "Failed to set back EMF sensing state Motor channel " + + std::to_string(channel), + ret); + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(motor_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to get serial number for motor channel " + + std::to_string(channel), + ret); + } + } +} + +Motor::~Motor() +{ + PhidgetHandle handle = reinterpret_cast(motor_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t Motor::getSerialNumber() const noexcept +{ + return serial_number_; +} + +double Motor::getDutyCycle() const +{ + double duty_cycle; + PhidgetReturnCode ret = + PhidgetDCMotor_getVelocity(motor_handle_, &duty_cycle); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get duty cycle for Motor channel " + + std::to_string(channel_), + ret); + } + return duty_cycle; +} + +void Motor::setDutyCycle(double duty_cycle) const +{ + PhidgetReturnCode ret = + PhidgetDCMotor_setTargetVelocity(motor_handle_, duty_cycle); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set duty cycle for Motor channel " + + std::to_string(channel_), + ret); + } +} + +double Motor::getAcceleration() const +{ + double accel; + PhidgetReturnCode ret = + PhidgetDCMotor_getAcceleration(motor_handle_, &accel); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get acceleration for Motor channel " + + std::to_string(channel_), + ret); + } + return accel; +} + +void Motor::setAcceleration(double acceleration) const +{ + PhidgetReturnCode ret = + PhidgetDCMotor_setAcceleration(motor_handle_, acceleration); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set acceleration for Motor channel " + + std::to_string(channel_), + ret); + } +} + +bool Motor::backEMFSensingSupported() const +{ + return back_emf_sensing_supported_; +} + +double Motor::getBackEMF() const +{ + if (!back_emf_sensing_supported_) + { + throw Phidget22Error("Back EMF sensing not supported", + EPHIDGET_UNSUPPORTED); + } + double backemf; + PhidgetReturnCode ret = PhidgetDCMotor_getBackEMF(motor_handle_, &backemf); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get back EMF for Motor channel " + + std::to_string(channel_), + ret); + } + return backemf; +} + +void Motor::setDataInterval(uint32_t data_interval_ms) const +{ + PhidgetReturnCode ret = + PhidgetDCMotor_setDataInterval(motor_handle_, data_interval_ms); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set data interval for Motor channel " + + std::to_string(channel_), + ret); + } +} + +double Motor::getBraking() const +{ + double braking; + PhidgetReturnCode ret = + PhidgetDCMotor_getBrakingStrength(motor_handle_, &braking); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to get braking strength for Motor channel " + + std::to_string(channel_), + ret); + } + return braking; +} + +void Motor::setBraking(double braking) const +{ + PhidgetReturnCode ret = + PhidgetDCMotor_setTargetBrakingStrength(motor_handle_, braking); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to set braking strength for Motor channel " + + std::to_string(channel_), + ret); + } +} + +void Motor::dutyCycleChangeHandler(double duty_cycle) const +{ + duty_cycle_change_handler_(channel_, duty_cycle); +} + +void Motor::backEMFChangeHandler(double back_emf) const +{ + back_emf_change_handler_(channel_, back_emf); +} + +void Motor::DutyCycleChangeHandler(PhidgetDCMotorHandle /* motor_handle */, + void *ctx, double duty_cycle) +{ + (reinterpret_cast(ctx))->dutyCycleChangeHandler(duty_cycle); +} + +void Motor::BackEMFChangeHandler(PhidgetDCMotorHandle /* motor_handle */, + void *ctx, double back_emf) +{ + (reinterpret_cast(ctx))->backEMFChangeHandler(back_emf); +} + +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/motors.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/motors.cpp new file mode 100644 index 0000000..4cfe6ad --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/motors.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/motors.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +Motors::Motors(int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function duty_cycle_change_handler, + std::function back_emf_change_handler) +{ + PhidgetReturnCode ret; + + PhidgetDCMotorHandle motor_handle; + + ret = PhidgetDCMotor_create(&motor_handle); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create Motor handle for determining channel " + "count", + ret); + } + + PhidgetHandle handle = reinterpret_cast(motor_handle); + + helpers::openWaitForAttachment(handle, serial_number, hub_port, + is_hub_port_device, 0); + + ret = Phidget_getDeviceChannelCount(handle, PHIDCHCLASS_DCMOTOR, + &motor_count_); + + helpers::closeAndDelete(&handle); + + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get Motor device channel count", ret); + } + + motors_.resize(motor_count_); + for (uint32_t i = 0; i < motor_count_; ++i) + { + motors_[i] = std::make_unique( + serial_number, hub_port, is_hub_port_device, i, + duty_cycle_change_handler, back_emf_change_handler); + } +} + +int32_t Motors::getSerialNumber() const noexcept +{ + return motors_.at(0)->getSerialNumber(); +} + +uint32_t Motors::getMotorCount() const noexcept +{ + return motor_count_; +} + +double Motors::getDutyCycle(int index) const +{ + return motors_.at(index)->getDutyCycle(); +} + +void Motors::setDutyCycle(int index, double duty_cycle) const +{ + motors_.at(index)->setDutyCycle(duty_cycle); +} + +double Motors::getAcceleration(int index) const +{ + return motors_.at(index)->getAcceleration(); +} + +void Motors::setAcceleration(int index, double acceleration) const +{ + motors_.at(index)->setAcceleration(acceleration); +} + +bool Motors::backEMFSensingSupported(int index) const +{ + return motors_.at(index)->backEMFSensingSupported(); +} + +double Motors::getBackEMF(int index) const +{ + return motors_.at(index)->getBackEMF(); +} + +void Motors::setDataInterval(int index, uint32_t data_interval_ms) const +{ + motors_.at(index)->setDataInterval(data_interval_ms); +} + +double Motors::getBraking(int index) const +{ + return motors_.at(index)->getBraking(); +} + +void Motors::setBraking(int index, double braking) const +{ + motors_.at(index)->setBraking(braking); +} + +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/phidget22.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/phidget22.cpp new file mode 100644 index 0000000..243dc02 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/phidget22.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 "phidgets_api/phidget22.hpp" + +namespace phidgets { + +Phidget22Error::Phidget22Error(const std::string &msg, PhidgetReturnCode code) +{ + const char *error_ptr; + PhidgetReturnCode ret = Phidget_getErrorDescription(code, &error_ptr); + if (ret == EPHIDGET_OK) + { + msg_ = msg + ": " + std::string(error_ptr); + } else + { + msg_ = msg + ": Unknown error"; + } +} + +const char *Phidget22Error::what() const noexcept +{ + return msg_.c_str(); +} + +namespace helpers { + +void openWaitForAttachment(PhidgetHandle handle, int32_t serial_number, + int hub_port, bool is_hub_port_device, int channel) +{ + PhidgetReturnCode ret; + + ret = Phidget_setDeviceSerialNumber(handle, serial_number); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set device serial number", ret); + } + + ret = Phidget_setHubPort(handle, hub_port); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set device hub port", ret); + } + + ret = Phidget_setIsHubPortDevice(handle, is_hub_port_device); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set device is hub port device", ret); + } + + ret = Phidget_setChannel(handle, channel); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set device channel", ret); + } + + ret = Phidget_openWaitForAttachment(handle, PHIDGET_TIMEOUT_DEFAULT); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to open device", ret); + } +} + +void closeAndDelete(PhidgetHandle *handle) noexcept +{ + Phidget_close(*handle); + Phidget_delete(handle); +} + +} // namespace helpers +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/spatial.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/spatial.cpp new file mode 100644 index 0000000..a0738d6 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/spatial.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/phidget22.hpp" +#include "phidgets_api/spatial.hpp" + +namespace phidgets { + +Spatial::Spatial( + int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function + data_handler, + std::function algorithm_data_handler, + std::function attach_handler, std::function detach_handler) + : serial_number_(serial_number), + data_handler_(std::move(data_handler)), + algorithm_data_handler_(std::move(algorithm_data_handler)), + attach_handler_(std::move(attach_handler)), + detach_handler_(std::move(detach_handler)) +{ + PhidgetReturnCode ret = PhidgetSpatial_create(&spatial_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to create Spatial handle", ret); + } + + helpers::openWaitForAttachment( + reinterpret_cast(spatial_handle_), serial_number, + hub_port, is_hub_port_device, 0); + + ret = PhidgetSpatial_setOnSpatialDataHandler(spatial_handle_, DataHandler, + this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set change handler for Spatial", ret); + } + + if (algorithm_data_handler_ != nullptr) + { + ret = PhidgetSpatial_setOnAlgorithmDataHandler( + spatial_handle_, AlgorithmDataHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set algorithm handler for Spatial", + ret); + } + } + + if (attach_handler_ != nullptr) + { + ret = Phidget_setOnAttachHandler( + reinterpret_cast(spatial_handle_), AttachHandler, + this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set attach handler for Spatial", + ret); + } + } + + if (detach_handler_ != nullptr) + { + ret = Phidget_setOnDetachHandler( + reinterpret_cast(spatial_handle_), DetachHandler, + this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set detach handler for Spatial", + ret); + } + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(spatial_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get serial number for spatial", + ret); + } + } +} + +Spatial::~Spatial() +{ + auto handle = reinterpret_cast(spatial_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t Spatial::getSerialNumber() const noexcept +{ + return serial_number_; +} + +void Spatial::zero() const +{ + PhidgetReturnCode ret = PhidgetSpatial_zeroGyro(spatial_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to calibrate Gyroscope", ret); + } +} + +void Spatial::setCompassCorrectionParameters( + double cc_mag_field, double cc_offset0, double cc_offset1, + double cc_offset2, double cc_gain0, double cc_gain1, double cc_gain2, + double cc_T0, double cc_T1, double cc_T2, double cc_T3, double cc_T4, + double cc_T5) +{ + PhidgetReturnCode ret = PhidgetSpatial_setMagnetometerCorrectionParameters( + spatial_handle_, cc_mag_field, cc_offset0, cc_offset1, cc_offset2, + cc_gain0, cc_gain1, cc_gain2, cc_T0, cc_T1, cc_T2, cc_T3, cc_T4, cc_T5); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set magnetometer correction parameters", + ret); + } +} + +void Spatial::setSpatialAlgorithm(const std::string algorithm_name) +{ + Phidget_SpatialAlgorithm algorithm; + + if (algorithm_name.compare("none") == 0) + { + algorithm = SPATIAL_ALGORITHM_NONE; + } else if (algorithm_name.compare("ahrs") == 0) + { + algorithm = SPATIAL_ALGORITHM_AHRS; + } else if (algorithm_name.compare("imu") == 0) + { + algorithm = SPATIAL_ALGORITHM_IMU; + } else + { + throw std::invalid_argument("Unknown spatial algorithm name"); + } + + PhidgetReturnCode ret = + PhidgetSpatial_setAlgorithm(spatial_handle_, algorithm); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set spatial algorithm", ret); + } +} + +void Spatial::setAHRSParameters(double angularVelocityThreshold, + double angularVelocityDeltaThreshold, + double accelerationThreshold, double magTime, + double accelTime, double biasTime) +{ + PhidgetReturnCode ret = PhidgetSpatial_setAHRSParameters( + spatial_handle_, angularVelocityThreshold, + angularVelocityDeltaThreshold, accelerationThreshold, magTime, + accelTime, biasTime); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set AHRS parameters", ret); + } +} + +void Spatial::setAlgorithmMagnetometerGain(double magnetometer_gain) +{ + PhidgetReturnCode ret = PhidgetSpatial_setAlgorithmMagnetometerGain( + spatial_handle_, magnetometer_gain); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set algorithm magnetometer gain", ret); + } +} + +void Spatial::setHeatingEnabled(bool heating_enabled) +{ + PhidgetReturnCode ret = + PhidgetSpatial_setHeatingEnabled(spatial_handle_, heating_enabled); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set heating flag", ret); + } +} + +void Spatial::setDataInterval(uint32_t interval_ms) const +{ + PhidgetReturnCode ret = + PhidgetSpatial_setDataInterval(spatial_handle_, interval_ms); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set data interval", ret); + } +} + +void Spatial::dataHandler(const double acceleration[3], + const double angular_rate[3], + const double magnetic_field[3], + double timestamp) const +{ + data_handler_(acceleration, angular_rate, magnetic_field, timestamp); +} + +void Spatial::algorithmDataHandler(const double quaternion[4], + double timestamp) const +{ + algorithm_data_handler_(quaternion, timestamp); +} + +void Spatial::attachHandler() +{ + attach_handler_(); +} + +void Spatial::detachHandler() +{ + detach_handler_(); +} + +void Spatial::DataHandler(PhidgetSpatialHandle /* input_handle */, void *ctx, + const double acceleration[3], + const double angular_rate[3], + const double magnetic_field[3], double timestamp) +{ + (reinterpret_cast(ctx)) + ->dataHandler(acceleration, angular_rate, magnetic_field, timestamp); +} + +void Spatial::AlgorithmDataHandler(PhidgetSpatialHandle /* input_handle */, + void *ctx, const double quaternion[4], + double timestamp) +{ + ((Spatial *)ctx)->algorithmDataHandler(quaternion, timestamp); +} + +void Spatial::AttachHandler(PhidgetHandle /* input_handle */, void *ctx) +{ + ((Spatial *)ctx)->attachHandler(); +} + +void Spatial::DetachHandler(PhidgetHandle /* input_handle */, void *ctx) +{ + ((Spatial *)ctx)->detachHandler(); +} + +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_api/src/temperature.cpp b/docker/local-ros2/phidgets_drivers/phidgets_api/src/temperature.cpp new file mode 100644 index 0000000..98a69d1 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_api/src/temperature.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/phidget22.hpp" +#include "phidgets_api/temperature.hpp" + +namespace phidgets { + +Temperature::Temperature(int32_t serial_number, int hub_port, + bool is_hub_port_device, + std::function temperature_handler) + : serial_number_(serial_number), temperature_handler_(temperature_handler) +{ + PhidgetReturnCode ret = + PhidgetTemperatureSensor_create(&temperature_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to create TemperatureSensor handle", ret); + } + + helpers::openWaitForAttachment( + reinterpret_cast(temperature_handle_), serial_number, + hub_port, is_hub_port_device, 0); + + ret = PhidgetTemperatureSensor_setOnTemperatureChangeHandler( + temperature_handle_, TemperatureChangeHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set change handler for Temperature", + ret); + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(temperature_handle_), + &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get serial number for temperature", + ret); + } + } +} + +Temperature::~Temperature() +{ + PhidgetHandle handle = reinterpret_cast(temperature_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t Temperature::getSerialNumber() const noexcept +{ + return serial_number_; +} + +void Temperature::setThermocoupleType(ThermocoupleType type) +{ + PhidgetReturnCode ret = PhidgetTemperatureSensor_setThermocoupleType( + temperature_handle_, + static_cast(type)); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set Temperature thermocouple type", + ret); + } +} + +double Temperature::getTemperature() const +{ + double current_temperature; + PhidgetReturnCode ret = PhidgetTemperatureSensor_getTemperature( + temperature_handle_, ¤t_temperature); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get temperature", ret); + } + return current_temperature; +} + +void Temperature::setDataInterval(uint32_t interval_ms) const +{ + PhidgetReturnCode ret = PhidgetTemperatureSensor_setDataInterval( + temperature_handle_, interval_ms); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set data interval", ret); + } +} + +void Temperature::temperatureChangeHandler(double temperature) const +{ + temperature_handler_(temperature); +} + +void Temperature::TemperatureChangeHandler( + PhidgetTemperatureSensorHandle /* temperature_handle */, void *ctx, + double temperature) +{ + (reinterpret_cast(ctx)) + ->temperatureChangeHandler(temperature); +} + +} // namespace phidgets diff --git a/docker/local-ros2/phidgets_drivers/phidgets_drivers/CHANGELOG.rst b/docker/local-ros2/phidgets_drivers/phidgets_drivers/CHANGELOG.rst new file mode 100644 index 0000000..7cd17cd --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_drivers/CHANGELOG.rst @@ -0,0 +1,108 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package phidgets_drivers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +2.3.3 (2024-03-13) +------------------ + +2.3.2 (2023-11-27) +------------------ + +2.3.1 (2023-03-03) +------------------ + +2.3.0 (2022-04-13) +------------------ + +2.2.2 (2022-02-17) +------------------ + +2.2.1 (2021-08-03) +------------------ + +2.2.0 (2021-05-20) +------------------ + +2.1.0 (2021-03-29) +------------------ + +2.0.2 (2020-06-01) +------------------ + +2.0.1 (2019-12-05) +------------------ + +2.0.0 (2019-12-05) +------------------ +* Port phidgets_drivers to ROS 2. +* Ignore all packages for ROS 2 port. +* Update maintainers in package.xml +* Merge pull request `#39 `_ from clalancette/add-libphidget22 +* Completely remove libphidget21. +* Rewrite Motor Phidget to use libphidget22. +* Rewrite IMU using libphidget22. +* Add support for Phidgets Magnetometer sensors. +* Add support for Phidgets Gyroscope sensors. +* Add support for Phidgets Accelerometer sensors. +* Add in support for Phidgets Temperature sensors. +* Add in support for Phidgets Analog inputs. +* Add in support for Phidgets Digital Inputs. +* Add in support for Phidgets Digital Outputs. +* Add in libphidget22 package. +* Merge pull request `#36 `_ from clalancette/phidget-cleanup2 +* Split custom messages into their own package. +* Add in phidgets_ik to the phidgets_drivers metapackage. +* Switch to package format 2. +* Contributors: Chris Lalancette, Martin Günther + +0.7.9 (2019-06-28) +------------------ + +0.7.8 (2019-05-06) +------------------ + +0.7.7 (2018-09-18) +------------------ + +0.7.6 (2018-08-09) +------------------ + +0.7.5 (2018-01-31) +------------------ + +0.7.4 (2017-10-04) +------------------ +* Add phidgets_high_speed_encoder to metapackage +* Contributors: Jose Luis Blanco-Claraco, Martin Günther + +0.7.3 (2017-06-30) +------------------ + +0.7.2 (2017-06-02) +------------------ + +0.7.1 (2017-05-22) +------------------ + +0.7.0 (2017-02-17) +------------------ +* Remove phidgets_ir package + It was a stub anyway. If somebody has such a device and cares to expose + the data via ROS topics, this can be revived. +* Contributors: Martin Günther + +0.2.3 (2017-02-17) +------------------ +* Update package.xml meta info +* Contributors: Martin Günther + +0.2.2 (2015-03-23) +------------------ + +0.2.1 (2015-01-15) +------------------ +* phidgets_drivers: removed phidgets_c_api dependency +* Updated version, maintainer and author information +* Deleted comments within files of all packages +* phidgets_drivers: converted stack into metapackage +* Contributors: Murilo FM diff --git a/docker/local-ros2/phidgets_drivers/phidgets_drivers/CMakeLists.txt b/docker/local-ros2/phidgets_drivers/phidgets_drivers/CMakeLists.txt new file mode 100644 index 0000000..017c1c2 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_drivers/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.5) +project(phidgets_drivers) +find_package(ament_cmake REQUIRED) +ament_package() diff --git a/docker/local-ros2/phidgets_drivers/phidgets_drivers/package.xml b/docker/local-ros2/phidgets_drivers/phidgets_drivers/package.xml new file mode 100644 index 0000000..5839d8e --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_drivers/package.xml @@ -0,0 +1,44 @@ + + + phidgets_drivers + 2.3.3 + API and ROS drivers for Phidgets devices + + Martin Günther + Chris Lalancette + + BSD, LGPL + + http://ros.org/wiki/phidgets_drivers + https://github.com/ros-drivers/phidgets_drivers.git + https://github.com/ros-drivers/phidgets_drivers/issues + + Phidgets Inc. + Tully Foote + Ivan Dryanovski + Martin Günther + Murilo FM + José-Luis Blanco Claraco + Chris Lalancette + + ament_cmake + + libphidget22 + phidgets_accelerometer + phidgets_api + phidgets_analog_inputs + phidgets_digital_inputs + phidgets_digital_outputs + phidgets_gyroscope + phidgets_ik + phidgets_high_speed_encoder + phidgets_magnetometer + phidgets_motors + phidgets_msgs + phidgets_spatial + phidgets_temperature + + + ament_cmake + + diff --git a/docker/local-ros2/phidgets_drivers/phidgets_msgs/CHANGELOG.rst b/docker/local-ros2/phidgets_drivers/phidgets_msgs/CHANGELOG.rst new file mode 100644 index 0000000..6c7c6c7 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_msgs/CHANGELOG.rst @@ -0,0 +1,90 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package phidgets_msgs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +2.3.3 (2024-03-13) +------------------ + +2.3.2 (2023-11-27) +------------------ + +2.3.1 (2023-03-03) +------------------ +* adding support for analog outputs for ROS2 (`#145 `_) +* Contributors: Alexis Fetet + +2.3.0 (2022-04-13) +------------------ +* Merge pull request `#132 `_ from mintar/feat-pre-commit-ros2 + [galactic] Add pre-commit, move from travis to GitHub actions, fix style +* Fix trailing whitespace +* Contributors: Martin Günther + +2.2.2 (2022-02-17) +------------------ + +2.2.1 (2021-08-03) +------------------ + +2.2.0 (2021-05-20) +------------------ + +2.1.0 (2021-03-29) +------------------ + +2.0.2 (2020-06-01) +------------------ + +2.0.1 (2019-12-05) +------------------ + +2.0.0 (2019-12-05) +------------------ +* Port phidgets_msgs to ROS 2. +* Ignore all packages for ROS 2 port. +* Update maintainers in package.xml +* Merge pull request `#39 `_ from clalancette/add-libphidget22 +* Switch to C++14. +* Merge pull request `#36 `_ from clalancette/phidget-cleanup2 +* Switch to C++14 everywhere. +* Split custom messages into their own package. +* Contributors: Chris Lalancette, Martin Günther + +0.7.9 (2019-06-28) +------------------ + +0.7.8 (2019-05-06) +------------------ + +0.7.7 (2018-09-18) +------------------ + +0.7.6 (2018-08-09) +------------------ + +0.7.5 (2018-01-31) +------------------ + +0.7.4 (2017-10-04) +------------------ + +0.7.3 (2017-06-30) +------------------ + +0.7.2 (2017-06-02) +------------------ + +0.7.1 (2017-05-22) +------------------ + +0.7.0 (2017-02-17 17:40) +------------------------ + +0.2.3 (2017-02-17 12:11) +------------------------ + +0.2.2 (2015-03-23) +------------------ + +0.2.1 (2015-01-15) +------------------ diff --git a/docker/local-ros2/phidgets_drivers/phidgets_msgs/CMakeLists.txt b/docker/local-ros2/phidgets_drivers/phidgets_msgs/CMakeLists.txt new file mode 100644 index 0000000..8dbe4c6 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_msgs/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.5) +project(phidgets_msgs) + +# Default to C++14 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 14) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +find_package(ament_cmake REQUIRED) +find_package(rosidl_default_generators REQUIRED) +find_package(std_msgs REQUIRED) + +set(msg_files + "msg/EncoderDecimatedSpeed.msg" +) + +set(srv_files + "srv/SetAnalogOutput.srv" + "srv/SetDigitalOutput.srv" +) + +rosidl_generate_interfaces(${PROJECT_NAME} + ${srv_files} + ${msg_files} + DEPENDENCIES + std_msgs +) + +ament_export_dependencies(rosidl_default_runtime) + +ament_package() diff --git a/docker/local-ros2/phidgets_drivers/phidgets_msgs/msg/EncoderDecimatedSpeed.msg b/docker/local-ros2/phidgets_drivers/phidgets_msgs/msg/EncoderDecimatedSpeed.msg new file mode 100644 index 0000000..4c9a8e9 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_msgs/msg/EncoderDecimatedSpeed.msg @@ -0,0 +1,4 @@ +# Encoder averaged speed for a channel in a Phidgets High-Speed Encoder board +std_msgs/Header header +# Averaged (sliding window) speed estimation [rad/s] +float64 avr_speed diff --git a/docker/local-ros2/phidgets_drivers/phidgets_msgs/package.xml b/docker/local-ros2/phidgets_drivers/phidgets_msgs/package.xml new file mode 100644 index 0000000..428a248 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_msgs/package.xml @@ -0,0 +1,28 @@ + + + + phidgets_msgs + 2.3.3 + Custom ROS messages for Phidgets drivers + + Martin Günther + Chris Lalancette + + BSD + + Chris Lalancette + + ament_cmake + + std_msgs + + rosidl_default_generators + + rosidl_default_runtime + + rosidl_interface_packages + + + ament_cmake + + diff --git a/docker/local-ros2/phidgets_drivers/phidgets_msgs/srv/SetAnalogOutput.srv b/docker/local-ros2/phidgets_drivers/phidgets_msgs/srv/SetAnalogOutput.srv new file mode 100644 index 0000000..c587d95 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_msgs/srv/SetAnalogOutput.srv @@ -0,0 +1,6 @@ +# Sets the state of an analog output. + +uint16 index # index of the output to be set +float64 voltage +--- +bool success diff --git a/docker/local-ros2/phidgets_drivers/phidgets_msgs/srv/SetDigitalOutput.srv b/docker/local-ros2/phidgets_drivers/phidgets_msgs/srv/SetDigitalOutput.srv new file mode 100644 index 0000000..5aa15d0 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_msgs/srv/SetDigitalOutput.srv @@ -0,0 +1,6 @@ +# Sets the state of a digital output. + +uint16 index # index of the output to be set +bool state +--- +bool success diff --git a/docker/local-ros2/phidgets_drivers/phidgets_spatial/CHANGELOG.rst b/docker/local-ros2/phidgets_drivers/phidgets_spatial/CHANGELOG.rst new file mode 100644 index 0000000..d79bd22 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_spatial/CHANGELOG.rst @@ -0,0 +1,212 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package phidgets_spatial +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +2.3.3 (2024-03-13) +------------------ +* Add support for VINT networkhub (`#172 `_) + This is a port of `#127 `_ to ROS2. + Closes `#135 `_. +* Contributors: Martin Günther + +2.3.2 (2023-11-27) +------------------ +* Only set magnetometer gain if param is set (`#169 `_) +* added new parameters for spatial precision MOT0109 onwards +* added support for phidget spatial onboard orientation estimation +* Contributors: Malte kl. Piening, Martin Günther + +2.3.1 (2023-03-03) +------------------ + +2.3.0 (2022-04-13) +------------------ + +2.2.2 (2022-02-17) +------------------ +* Fix behavior after USB reattachment (`#119 `_) + The Phidged Spatial never recovered after detaching and reattaching to + the USB port. This commit fixes that. +* Add attach + detach handlers +* Fix publishing of invalid mag readings (`#116 `_) +* Contributors: Martin Günther + +2.2.1 (2021-08-03) +------------------ +* Make the magnetometer corrections optional again. (`#95 `_) +* Update the ROS 2 readme files. (`#93 `_) +* Contributors: Chris Lalancette + +2.2.0 (2021-05-20) +------------------ +* Make sure to declare the type while declaring the parameter. (`#89 `_) +* Contributors: Chris Lalancette + +2.1.0 (2021-03-29) +------------------ +* Don't publish messages that jumped back in time. (`#86 `_) +* Log synchronization window details at DEBUG level (`#84 `_) +* Get rid of deprecation warnings in Foxy. (`#75 `_) +* Switch header guards to _HPP SUFFIX. +* Contributors: Chris Lalancette, Martin Günther, Michael Grupp + +2.0.2 (2020-06-01) +------------------ +* Release build fixes (`#67 `_) +* Contributors: Chris Lalancette + +2.0.1 (2019-12-05) +------------------ +* Switch the buildtoo_depend to ament_cmake_ros. (`#65 `_) +* Contributors: Chris Lalancette + +2.0.0 (2019-12-05) +------------------ +* Include file cleanup. +* Make sure exceptions get caught by reference. +* Switch from NULL to nullptr. +* Make sure to initialize class member variables. +* Update READMEs to use "Published Topics" and "Subscribed Topics". (`#59 `_) +* Change launch output to "both" so it logs as well. +* Make publish_rate a double. +* Print out the serial number when connecting. +* Update documentation to mention device dependent fields. +* Update FIXME comments for sleep. +* Port spatial to ROS 2. +* Ignore all packages for ROS 2 port. +* Fix wrong defaults for standard deviations (`#48 `_) +* Improve the IMU calibration service (`#47 `_) +* Update maintainers in package.xml +* Merge pull request `#39 `_ from clalancette/add-libphidget22 +* Resynchronize the times at periodic intervals. +* Add launch files for all drivers. +* Add in try/catch blocks for connecting. +* Fixes from review. +* Documentation updates to README.md +* Set the publish_rate to 0 by default. +* Add in the license files and add to the headers. +* Remove nodes in favor of nodelets. +* Finish removing launch file from phidgets_spatial. +* Rewrite IMU using libphidget22. +* Contributors: Chris Lalancette, Martin Günther + +0.7.9 (2019-06-28) +------------------ + +0.7.8 (2019-05-06) +------------------ + +0.7.7 (2018-09-18) +------------------ +* Add parameter use_imu_time (default true) (`#27 `_) + Setting use_imu_time to false will disable the imu time calibration and + always use the Host time, i.e. ros::Time::now(). +* Contributors: Jochen Sprickerhof + +0.7.6 (2018-08-09) +------------------ +* phidgets_imu: Ensure strictly ordered timestamps (`#26 `_) + Fixes `#17 `_. +* Contributors: Michael Grupp, Martin Günther + +0.7.5 (2018-01-31) +------------------ +* phidgets_imu: Add roslaunch_add_file_check +* phidgets_imu: Add diagnostic_aggregator dependency +* phidgets_imu: Add missing install rule for config +* update to use non deprecated pluginlib macro (`#19 `_) +* Contributors: Martin Günther, Mikael Arguedas + +0.7.4 (2017-10-04) +------------------ + +0.7.3 (2017-06-30) +------------------ + +0.7.2 (2017-06-02) +------------------ +* First release into Lunar +* phidgets_imu: Add use_magnetic_field_msg to launch + This is required in Jade: Since Jade, phidgets_imu publishes + MagneticField messages, but imu_filter_madgwick still subscribes by + default to Vector3Stamped messages. When running as nodelets, this can + produce a silent error. + In Kinetic, this is optional: imu_filter_madgwick now defaults to + MagneticField. + From Lunar on, it should be removed, because the use_magnetic_field_msg + param was removed from imu_filter_madgwick. +* Contributors: Martin Günther + +0.7.1 (2017-05-22) +------------------ +* phidgets_imu: add optional serial number parameter (`#7 `_) +* phidgets_imu: Add imu_filter_madgwick dependency + Closes `#9 `_. +* Contributors: Johan M. von Behren, Martin Günther + +0.7.0 (2017-02-17) +------------------ +* Publish MagneticField instead of Vector3Stamped +* Report mag data in Tesla, not Gauss + This is to conform with sensor_msgs/MagneticField, which requires the + data to be in Tesla. +* Contributors: Martin Günther + +0.2.3 (2017-02-17) +------------------ +* Add IMU diagnostics (`#24 `_) +* Set data rate after reattachment + This fixes a bug where after disconnecting and reconnecting the USB + cable, the data rate would be set to the default of 125 Hz (= period of + 8ms). By moving the setDataRate call to the attachHandler, the data rate + is correctly set after each reattachment. +* Contributors: Mani Monajjemi, Keshav Iyengar, Martin Günther + +0.2.2 (2015-03-23) +------------------ +* Merge pull request #18 from ccny-ros-pkg/libphidgets + Merge libphidgets branch into indigo +* set orientation_covariance[0] to -1 + from Imu.msg: + > If you have no estimate for one of the data elements (e.g. your IMU doesn't produce an orientation + > estimate), please set element 0 of the associated covariance matrix to -1. +* phidgets_imu: fixed issue #9 +* Contributors: Martin Günther, Murilo FM + +0.2.1 (2015-01-15) +------------------ +* add boost depends to CMakeLists + All non-catkin things that we expose in our headers should be added to + the DEPENDS, so that packages which depend on our package will also + automatically link against it. + Also see: http://answers.ros.org/question/58498/what-is-the-purpose-of-catkin_depends/\#58593 +* improve error output when setting compass corr params + The previous implementation didn't catch a number of error codes + (EPHIDGET_INVALIDARG, EPHIDGET_NOTATTACHED, EPHIDGET_UNEXPECTED), and + the new one is more elegant and consistent with the previous code anyway. +* Set compass correction params on the device + Tested with a Phidget Spatial 3/3/3 1044. +* phidgets_imu: install phidgets_imu_nodelet.xml +* phidgets_imu: not exporting nodelet as library anymore +* Updated version, maintainer and author information +* phidgets_imu: added install rule to launch files +* phidgets_imu: removed unnecessary dependency +* Deleted comments within files of all packages +* Catkinised packages +* Merge pull request #1 from uos/fix_imu_time_lag + fix IMU time lag +* add some hints to error message + I just spent 30 minutes trying to figure out why the IMU works on one + computer and doesn't on another one. Felt a little foolish when I found + out that the udev rules weren't installed; maybe providing some more + info in the error message helps others. +* use ros::Time::now() if time lag exceeds threshold +* added warning if IMU time lags behind ROS time +* renamed rate parameter to period +* added timestamp in imu data +* fixed cmakelists by including lib to compile on electric +* adding missing imu_ros h file +* adding missing imu_ros cpp file +* added api, imu and ir +* initial commit +* Contributors: Ivan Dryanovski, Martin Günther, Murilo FM diff --git a/docker/local-ros2/phidgets_drivers/phidgets_spatial/CMakeLists.txt b/docker/local-ros2/phidgets_drivers/phidgets_spatial/CMakeLists.txt new file mode 100644 index 0000000..63fb3fe --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_spatial/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.5) +project(phidgets_spatial) + +# Default to C++14 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 14) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +find_package(ament_cmake_ros REQUIRED) +find_package(phidgets_api REQUIRED) +find_package(rclcpp REQUIRED) +find_package(rclcpp_components REQUIRED) +find_package(sensor_msgs REQUIRED) +find_package(std_msgs REQUIRED) +find_package(std_srvs REQUIRED) + +include_directories(include) + +add_library(phidgets_spatial SHARED src/spatial_ros_i.cpp) +ament_target_dependencies(phidgets_spatial + phidgets_api + rclcpp + rclcpp_components + sensor_msgs + std_msgs + std_srvs +) + +rclcpp_components_register_nodes(phidgets_spatial + "phidgets::SpatialRosI") + +install(TARGETS phidgets_spatial + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) + +install(DIRECTORY include/ + DESTINATION include +) + +install(DIRECTORY launch + DESTINATION share/${PROJECT_NAME} +) + +ament_package() diff --git a/docker/local-ros2/phidgets_drivers/phidgets_spatial/README.md b/docker/local-ros2/phidgets_drivers/phidgets_spatial/README.md new file mode 100644 index 0000000..ad3f350 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_spatial/README.md @@ -0,0 +1,60 @@ +Phidgets spatial ROS 2 driver +============================= + +This is the ROS 2 driver for Phidgets spatial. + +Usage +----- + +To run this driver standalone, do the following: + + ros2 launch phidgets_spatial spatial-launch.py + +Published Topics +---------------- + +* `/imu/data_raw` (`sensor_msgs/Imu`) - The raw accelerometer and gyroscope data. +* `imu/is_calibrated` (`std_msgs/Bool`) - Whether the gyroscope has been calibrated; this will be done automatically at startup time, but can also be re-done at any time by calling the `imu/calibrate` service. +* `/imu/mag` (`sensor_msgs/MagneticField`) - The raw magnetometer data. + +Services +-------- + +* `imu/calibrate` (`std_srvs/Empty`) - Run calibration on the gyroscope. + +Parameters +---------- + +* `serial` (int) - The serial number of the phidgets spatial to connect to. If -1 (the default), connects to any spatial phidget that can be found. +* `hub_port` (int) - The phidgets VINT hub port to connect to. Only used if the spatial phidget is connected to a VINT hub. Defaults to 0. +* `frame_id` (string) - The header frame ID to use when publishing the message. Defaults to [REP-0145](http://www.ros.org/reps/rep-0145.html) compliant `imu_link`. +* `use_orientation` (bool) - Use the phidget spatials onboard orientation estimation; Just available on MOT0109 onwards. Set to false for older versions. Defaults to false. +* `spatial_algorithm` (string) - Name of the spatial algorithm used for orientation estimation (one of "none", "ahrs", "imu"); Just used if `use_orientation` is set to true. Defaults to `ahrs`. +* `ahrs_angular_velocity_threshold` (double) - Parameter for AHRS orientation estimation; Just used if `use_orientation` is set to true. +* `ahrs_angular_velocity_delta_threshold` (double) - Parameter for AHRS orientation estimation; Just used if `use_orientation` is set to true. +* `ahrs_acceleration_threshold` (double) - Parameter for AHRS orientation estimation; Just used if `use_orientation` is set to true. +* `ahrs_mag_time` (double) - Parameter for AHRS orientation estimation; Just used if `use_orientation` is set to true. +* `ahrs_accel_time` (double) - Parameter for AHRS orientation estimation; Just used if `use_orientation` is set to true. +* `ahrs_bias_time` (double) - Parameter for AHRS orientation estimation; Just used if `use_orientation` is set to true. +* `algorithm_magnetometer_gain` (double) - Gain of magnetometer in orientation estimation algorithm; Just used if `use_orientation` is set to true. Defaults to 0.005 +* `heating_enabled` (bool) - Use the internal heating element; Just available on MOT0109 onwards. Do not set this parameter for older versions. +* `linear_acceleration_stdev` (double) - The standard deviation to use for the linear acceleration when publishing the message. Defaults to 280 ug. +* `angular_velocity_stdev` (double) - The standard deviation to use for the angular velocity when publishing the message. Defaults to 0.095 deg/s. +* `magnetic_field_stdev` (double) - The standard deviation to use for the magnetic field when publishing the message. Defaults to 1.1 milligauss. +* `time_resynchronization_interval_ms` (int) - The number of milliseconds to wait between resynchronizing the time on the Phidgets spatial with the local time. Larger values have less "jumps", but will have more timestamp drift. Setting this to 0 disables resynchronization. Defaults to 5000 ms. +* `data_interval_ms` (int) - The number of milliseconds between acquisitions of data on the device (allowed values are dependent on the device). Defaults to 8 ms. +* `callback_delta_epsilon_ms` (int) - The number of milliseconds epsilon allowed between callbacks when attempting to resynchronize the time. If this is set to 1, then a difference of `data_interval_ms` plus or minus 1 millisecond will be considered viable for resynchronization. Higher values give the code more leeway to resynchronize, at the cost of potentially getting bad resynchronizations sometimes. Lower values can give better results, but can also result in never resynchronizing. Must be less than `data_interval_ms`. Defaults to 1 ms. +* `publish_rate` (double) - How often the driver will publish data on the ROS topic. If 0 (the default), it will publish every time there is an update from the device (so at the `data_interval_ms`). If positive, it will publish the data at that rate regardless of the acquisition interval. +* `cc_mag_field` (double) - Ambient magnetic field calibration value; see device's user guide for information on how to calibrate. +* `cc_offset0` (double) - Calibration offset value 0; see device's user guide for information on how to calibrate. +* `cc_offset1` (double) - Calibration offset value 1; see device's user guide for information on how to calibrate. +* `cc_offset2` (double) - Calibration offset value 2; see device's user guide for information on how to calibrate. +* `cc_gain0` (double) - Gain offset value 0; see device's user guide for information on how to calibrate. +* `cc_gain1` (double) - Gain offset value 1; see device's user guide for information on how to calibrate. +* `cc_gain2` (double) - Gain offset value 2; see device's user guide for information on how to calibrate. +* `cc_t0` (double) - T offset value 0; see device's user guide for information on how to calibrate. +* `cc_t1` (double) - T offset value 1; see device's user guide for information on how to calibrate. +* `cc_t2` (double) - T offset value 2; see device's user guide for information on how to calibrate. +* `cc_t3` (double) - T offset value 3; see device's user guide for information on how to calibrate. +* `cc_t4` (double) - T offset value 4; see device's user guide for information on how to calibrate. +* `cc_t5` (double) - T offset value 5; see device's user guide for information on how to calibrate. diff --git a/docker/local-ros2/phidgets_drivers/phidgets_spatial/include/phidgets_spatial/spatial_ros_i.hpp b/docker/local-ros2/phidgets_drivers/phidgets_spatial/include/phidgets_spatial/spatial_ros_i.hpp new file mode 100644 index 0000000..572a41c --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_spatial/include/phidgets_spatial/spatial_ros_i.hpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_SPATIAL_SPATIAL_ROS_I_HPP +#define PHIDGETS_SPATIAL_SPATIAL_ROS_I_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "phidgets_api/spatial.hpp" + +namespace phidgets { + +const float G = 9.80665; + +class SpatialRosI final : public rclcpp::Node +{ + public: + explicit SpatialRosI(const rclcpp::NodeOptions& options); + + private: + std::unique_ptr spatial_; + std::string frame_id_; + std::mutex spatial_mutex_; + + rclcpp::Publisher::SharedPtr cal_publisher_; + rclcpp::Service::SharedPtr cal_srv_; + rclcpp::Publisher::SharedPtr imu_pub_; + rclcpp::Publisher::SharedPtr + magnetic_field_pub_; + void timerCallback(); + rclcpp::TimerBase::SharedPtr timer_; + double publish_rate_; + std::string server_name_; + std::string server_ip_; + + rclcpp::Time ros_time_zero_; + bool synchronize_timestamps_{true}; + uint64_t data_time_zero_ns_{0}; + uint64_t last_data_timestamp_ns_{0}; + uint64_t last_ros_stamp_ns_{0}; + int64_t time_resync_interval_ns_{0}; + int64_t data_interval_ns_{0}; + bool can_publish_{false}; + rclcpp::Time last_cb_time_; + int64_t cb_delta_epsilon_ns_{0}; + + void calibrate(); + + void calibrateService( + const std::shared_ptr req, + std::shared_ptr res); + + // Accelerometer + double linear_acceleration_variance_{0.0}; + double last_accel_x_{0.0}; + double last_accel_y_{0.0}; + double last_accel_z_{0.0}; + + // Gyroscope + double angular_velocity_variance_{0.0}; + double last_gyro_x_{0.0}; + double last_gyro_y_{0.0}; + double last_gyro_z_{0.0}; + + // Magnetometer + double magnetic_field_variance_{0.0}; + double last_mag_x_{0.0}; + double last_mag_y_{0.0}; + double last_mag_z_{0.0}; + + // Onboard orientation estimation results + double last_quat_w_; + double last_quat_x_; + double last_quat_y_; + double last_quat_z_; + + void publishLatest(); + + void spatialDataCallback(const double acceleration[3], + const double angular_rate[3], + const double magnetic_field[3], double timestamp); + void spatialAlgorithmDataCallback(const double quaternion[4], + double timestamp); + void attachCallback(); + void detachCallback(); +}; + +} // namespace phidgets + +#endif // PHIDGETS_SPATIAL_SPATIAL_ROS_I_HPP diff --git a/docker/local-ros2/phidgets_drivers/phidgets_spatial/launch/spatial-launch.py b/docker/local-ros2/phidgets_drivers/phidgets_spatial/launch/spatial-launch.py new file mode 100644 index 0000000..ab1a097 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_spatial/launch/spatial-launch.py @@ -0,0 +1,63 @@ +# Copyright 2019 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Launch a Phidgets spatial in a component container.""" + +import launch +from launch_ros.actions import ComposableNodeContainer +from launch_ros.descriptions import ComposableNode + + +def generate_launch_description(): + """Generate launch description with multiple components.""" + + params = { + # optional param use_orientation, default is false + 'use_orientation': False, + + # optional param spatial_algorithm, default is 'ahrs' + 'spatial_algorithm': 'ahrs', + + # optional ahrs parameters + 'ahrs_angular_velocity_threshold': 1.0, + 'ahrs_angular_velocity_delta_threshold': 0.1, + 'ahrs_acceleration_threshold': 0.1, + 'ahrs_mag_time': 10.0, + 'ahrs_accel_time': 10.0, + 'ahrs_bias_time': 1.25, + + # optional param algorithm_magnetometer_gain, default is 0.005 + # WARNING: do not set on PhidgetSpatial MOT0110 onwards (not supported)! + # 'algorithm_magnetometer_gain': 0.005, + + # optional param heating_enabled, not modified by default + 'heating_enabled': False, + } + + container = ComposableNodeContainer( + name='phidget_container', + namespace='', + package='rclcpp_components', + executable='component_container', + composable_node_descriptions=[ + ComposableNode( + package='phidgets_spatial', + plugin='phidgets::SpatialRosI', + name='phidgets_spatial', + parameters=[params]), + ], + output='both', + ) + + return launch.LaunchDescription([container]) diff --git a/docker/local-ros2/phidgets_drivers/phidgets_spatial/package.xml b/docker/local-ros2/phidgets_drivers/phidgets_spatial/package.xml new file mode 100644 index 0000000..7b6058e --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_spatial/package.xml @@ -0,0 +1,32 @@ + + + phidgets_spatial + 2.3.3 + Driver for the Phidgets Spatial 3/3/3 devices + + Martin Günther + Chris Lalancette + + BSD + + http://ros.org/wiki/phidgets_spatial + https://github.com/ros-drivers/phidgets_drivers.git + https://github.com/ros-drivers/phidgets_drivers/issues + + Ivan Dryanovski + + ament_cmake_ros + + phidgets_api + rclcpp + rclcpp_components + sensor_msgs + std_msgs + std_srvs + + launch + + + ament_cmake + + diff --git a/docker/local-ros2/phidgets_drivers/phidgets_spatial/src/spatial_ros_i.cpp b/docker/local-ros2/phidgets_drivers/phidgets_spatial/src/spatial_ros_i.cpp new file mode 100644 index 0000000..c232f41 --- /dev/null +++ b/docker/local-ros2/phidgets_drivers/phidgets_spatial/src/spatial_ros_i.cpp @@ -0,0 +1,617 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "phidgets_api/spatial.hpp" +#include "phidgets_spatial/spatial_ros_i.hpp" + +namespace phidgets { + +SpatialRosI::SpatialRosI(const rclcpp::NodeOptions &options) + : rclcpp::Node("phidgets_spatial_node", options) +{ + setvbuf(stdout, nullptr, _IONBF, BUFSIZ); + + RCLCPP_INFO(get_logger(), "Starting Phidgets Spatial"); + + bool use_orientation = this->declare_parameter( + "use_orientation", + false); // default do not use the onboard orientation + std::string spatial_algorithm = + this->declare_parameter("spatial_algorithm", "ahrs"); + + double ahrsAngularVelocityThreshold; + double ahrsAngularVelocityDeltaThreshold; + double ahrsAccelerationThreshold; + double ahrsMagTime; + double ahrsAccelTime; + double ahrsBiasTime; + + this->declare_parameter("ahrs_angular_velocity_threshold", + rclcpp::ParameterType::PARAMETER_DOUBLE); + this->declare_parameter("ahrs_angular_velocity_delta_threshold", + rclcpp::ParameterType::PARAMETER_DOUBLE); + this->declare_parameter("ahrs_acceleration_threshold", + rclcpp::ParameterType::PARAMETER_DOUBLE); + this->declare_parameter("ahrs_mag_time", + rclcpp::ParameterType::PARAMETER_DOUBLE); + this->declare_parameter("ahrs_accel_time", + rclcpp::ParameterType::PARAMETER_DOUBLE); + this->declare_parameter("ahrs_bias_time", + rclcpp::ParameterType::PARAMETER_DOUBLE); + + bool has_ahrs_params = + this->get_parameter("ahrs_angular_velocity_threshold", + ahrsAngularVelocityThreshold) && + this->get_parameter("ahrs_angular_velocity_delta_threshold", + ahrsAngularVelocityDeltaThreshold) && + this->get_parameter("ahrs_acceleration_threshold", + ahrsAccelerationThreshold) && + this->get_parameter("ahrs_mag_time", ahrsMagTime) && + this->get_parameter("ahrs_accel_time", ahrsAccelTime) && + this->get_parameter("ahrs_bias_time", ahrsBiasTime); + + double algorithm_magnetometer_gain; + bool set_algorithm_magnetometer_gain = true; + if (!this->get_parameter("algorithm_magnetometer_gain", + algorithm_magnetometer_gain)) + { + algorithm_magnetometer_gain = 0.0; + set_algorithm_magnetometer_gain = + false; // if parameter not set, do not call api (because this + // function is not available from MOT0110 onwards) + } + + bool heating_enabled; + bool set_heating_enabled = true; + if (!this->get_parameter("heating_enabled", heating_enabled)) + { + set_heating_enabled = + false; // if parameter not set, do not call api (because this + // function is just available from MOT0109 onwards) + } + + int serial_num = + this->declare_parameter("serial", -1); // default open any device + + int hub_port = this->declare_parameter( + "hub_port", 0); // only used if the device is on a VINT hub_port + + // As specified in http://www.ros.org/reps/rep-0145.html + frame_id_ = this->declare_parameter("frame_id", "imu_link"); + + double linear_acceleration_stdev = this->declare_parameter( + "linear_acceleration_stdev", + 280.0 * 1e-6 * + G); // 280 ug accelerometer white noise sigma, as per manual + linear_acceleration_variance_ = + linear_acceleration_stdev * linear_acceleration_stdev; + + // 0.095 deg/s gyroscope white noise sigma, as per manual + double angular_velocity_stdev = this->declare_parameter( + "angular_velocity_stdev", 0.095 * (M_PI / 180.0)); + angular_velocity_variance_ = + angular_velocity_stdev * angular_velocity_stdev; + + // 1.1 milligauss magnetometer white noise sigma, as per manual + double magnetic_field_stdev = + this->declare_parameter("magnetic_field_stdev", 1.1 * 1e-3 * 1e-4); + magnetic_field_variance_ = magnetic_field_stdev * magnetic_field_stdev; + + int time_resync_ms = + this->declare_parameter("time_resynchronization_interval_ms", 5000); + time_resync_interval_ns_ = + static_cast(time_resync_ms) * 1000 * 1000; + + int data_interval_ms = this->declare_parameter("data_interval_ms", 8); + data_interval_ns_ = data_interval_ms * 1000 * 1000; + + int cb_delta_epsilon_ms = + this->declare_parameter("callback_delta_epsilon_ms", 1); + cb_delta_epsilon_ns_ = cb_delta_epsilon_ms * 1000 * 1000; + if (cb_delta_epsilon_ms >= data_interval_ms) + { + throw std::runtime_error( + "Callback epsilon is larger than the data interval; this can never " + "work"); + } + + publish_rate_ = this->declare_parameter("publish_rate", 0.0); + if (publish_rate_ > 1000.0) + { + throw std::runtime_error("Publish rate must be <= 1000"); + } + + this->declare_parameter("server_name", + rclcpp::ParameterType::PARAMETER_STRING); + this->declare_parameter("server_ip", + rclcpp::ParameterType::PARAMETER_STRING); + if (this->get_parameter("server_name", server_name_) && + this->get_parameter("server_ip", server_ip_)) + { + PhidgetNet_addServer(server_name_.c_str(), server_ip_.c_str(), 5661, "", + 0); + + RCLCPP_INFO(get_logger(), "Using phidget server %s at IP %s", + server_name_.c_str(), server_ip_.c_str()); + } + + // compass correction params (see + // http://www.phidgets.com/docs/1044_User_Guide) + this->declare_parameter("cc_mag_field", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_offset0", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_offset1", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_offset2", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_gain0", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_gain1", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_gain2", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_t0", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_t1", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_t2", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_t3", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_t4", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_t5", rclcpp::PARAMETER_DOUBLE); + + bool has_compass_params = false; + double cc_mag_field = 0.0; + double cc_offset0 = 0.0; + double cc_offset1 = 0.0; + double cc_offset2 = 0.0; + double cc_gain0 = 0.0; + double cc_gain1 = 0.0; + double cc_gain2 = 0.0; + double cc_T0 = 0.0; + double cc_T1 = 0.0; + double cc_T2 = 0.0; + double cc_T3 = 0.0; + double cc_T4 = 0.0; + double cc_T5 = 0.0; + + try + { + cc_mag_field = this->get_parameter("cc_mag_field").get_value(); + cc_offset0 = this->get_parameter("cc_offset0").get_value(); + cc_offset1 = this->get_parameter("cc_offset1").get_value(); + cc_offset2 = this->get_parameter("cc_offset2").get_value(); + cc_gain0 = this->get_parameter("cc_gain0").get_value(); + cc_gain1 = this->get_parameter("cc_gain1").get_value(); + cc_gain2 = this->get_parameter("cc_gain2").get_value(); + cc_T0 = this->get_parameter("cc_t0").get_value(); + cc_T1 = this->get_parameter("cc_t1").get_value(); + cc_T2 = this->get_parameter("cc_t2").get_value(); + cc_T3 = this->get_parameter("cc_t3").get_value(); + cc_T4 = this->get_parameter("cc_t4").get_value(); + cc_T5 = this->get_parameter("cc_t5").get_value(); + has_compass_params = true; + } catch (const rclcpp::exceptions::ParameterUninitializedException &) + { + } + + RCLCPP_INFO(get_logger(), + "Connecting to Phidgets Spatial serial %d, hub port %d ...", + serial_num, hub_port); + + // We take the mutex here and don't unlock until the end of the constructor + // to prevent a callback from trying to use the publisher before we are + // finished setting up. + std::lock_guard lock(spatial_mutex_); + + last_quat_w_ = 0.0; + last_quat_x_ = 0.0; + last_quat_y_ = 0.0; + last_quat_z_ = 0.0; + + try + { + std::function algorithm_data_handler = + nullptr; + if (use_orientation) + { + algorithm_data_handler = + std::bind(&SpatialRosI::spatialAlgorithmDataCallback, this, + std::placeholders::_1, std::placeholders::_2); + } + + spatial_ = std::make_unique( + serial_num, hub_port, false, + std::bind(&SpatialRosI::spatialDataCallback, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3, std::placeholders::_4), + algorithm_data_handler, + std::bind(&SpatialRosI::attachCallback, this), + std::bind(&SpatialRosI::detachCallback, this)); + + RCLCPP_INFO(get_logger(), "Connected to serial %d", + spatial_->getSerialNumber()); + + spatial_->setDataInterval(data_interval_ms); + + cal_publisher_ = this->create_publisher( + "imu/is_calibrated", rclcpp::SystemDefaultsQoS().transient_local()); + + calibrate(); + + if (use_orientation) + { + spatial_->setSpatialAlgorithm(spatial_algorithm); + + if (has_ahrs_params) + { + spatial_->setAHRSParameters(ahrsAngularVelocityThreshold, + ahrsAngularVelocityDeltaThreshold, + ahrsAccelerationThreshold, + ahrsMagTime, ahrsAccelTime, + ahrsBiasTime); + } + + if (set_algorithm_magnetometer_gain) + spatial_->setAlgorithmMagnetometerGain( + algorithm_magnetometer_gain); + } + + if (has_compass_params) + { + spatial_->setCompassCorrectionParameters( + cc_mag_field, cc_offset0, cc_offset1, cc_offset2, cc_gain0, + cc_gain1, cc_gain2, cc_T0, cc_T1, cc_T2, cc_T3, cc_T4, cc_T5); + } else + { + RCLCPP_INFO(get_logger(), "No compass correction params found."); + } + + if (set_heating_enabled) + { + spatial_->setHeatingEnabled(heating_enabled); + } + } catch (const Phidget22Error &err) + { + RCLCPP_ERROR(get_logger(), "Spatial: %s", err.what()); + throw; + } + + imu_pub_ = this->create_publisher("imu/data_raw", 1); + + cal_srv_ = this->create_service( + "imu/calibrate", + std::bind(&SpatialRosI::calibrateService, this, std::placeholders::_1, + std::placeholders::_2)); + + magnetic_field_pub_ = + this->create_publisher("imu/mag", 1); + + if (publish_rate_ > 0.0) + { + double pub_msec = 1000.0 / publish_rate_; + timer_ = this->create_wall_timer( + std::chrono::milliseconds(static_cast(pub_msec)), + std::bind(&SpatialRosI::timerCallback, this)); + } +} + +void SpatialRosI::calibrate() +{ + RCLCPP_INFO(get_logger(), + "Calibrating IMU, this takes around 2 seconds to finish. " + "Make sure that the device is not moved during this time."); + spatial_->zero(); + // The API call returns directly, so we "enforce" the recommended 2 sec + // here. See: https://github.com/ros-drivers/phidgets_drivers/issues/40 + + // FIXME: Ideally we'd use an rclcpp method that honors use_sim_time here, + // but that doesn't actually exist. Once + // https://github.com/ros2/rclcpp/issues/465 is solved, we can revisit this. + std::this_thread::sleep_for(std::chrono::seconds(2)); + RCLCPP_INFO(get_logger(), "Calibrating IMU done."); + + // publish message + auto is_calibrated_msg = std::make_unique(); + is_calibrated_msg->data = true; + cal_publisher_->publish(std::move(is_calibrated_msg)); +} + +void SpatialRosI::calibrateService( + const std::shared_ptr req, + std::shared_ptr res) +{ + (void)req; + (void)res; + calibrate(); +} + +void SpatialRosI::publishLatest() +{ + auto msg = std::make_unique(); + + auto mag_msg = std::make_unique(); + + // build covariance matrices + for (int i = 0; i < 3; ++i) + { + for (int j = 0; j < 3; ++j) + { + if (i == j) + { + int idx = j * 3 + i; + msg->linear_acceleration_covariance[idx] = + linear_acceleration_variance_; + msg->angular_velocity_covariance[idx] = + angular_velocity_variance_; + mag_msg->magnetic_field_covariance[idx] = + magnetic_field_variance_; + } + } + } + + // Fill out and send IMU message + msg->header.frame_id = frame_id_; + + uint64_t imu_diff_in_ns = last_data_timestamp_ns_ - data_time_zero_ns_; + uint64_t time_in_ns = ros_time_zero_.nanoseconds() + imu_diff_in_ns; + + if (time_in_ns < last_ros_stamp_ns_) + { + RCLCPP_WARN(get_logger(), + "Time went backwards (%lu < %lu)! Not publishing message.", + time_in_ns, last_ros_stamp_ns_); + return; + } + + last_ros_stamp_ns_ = time_in_ns; + + rclcpp::Time ros_time = rclcpp::Time(time_in_ns); + + msg->header.stamp = ros_time; + + // set linear acceleration + msg->linear_acceleration.x = last_accel_x_; + msg->linear_acceleration.y = last_accel_y_; + msg->linear_acceleration.z = last_accel_z_; + + // set angular velocities + msg->angular_velocity.x = last_gyro_x_; + msg->angular_velocity.y = last_gyro_y_; + msg->angular_velocity.z = last_gyro_z_; + + // set spatial algorithm orientation estimation + msg->orientation.w = last_quat_w_; + msg->orientation.x = last_quat_x_; + msg->orientation.y = last_quat_y_; + msg->orientation.z = last_quat_z_; + + imu_pub_->publish(std::move(msg)); + + // Fill out and publish magnetic message + mag_msg->header.frame_id = frame_id_; + + mag_msg->header.stamp = ros_time; + + mag_msg->magnetic_field.x = last_mag_x_; + mag_msg->magnetic_field.y = last_mag_y_; + mag_msg->magnetic_field.z = last_mag_z_; + + magnetic_field_pub_->publish(std::move(mag_msg)); +} + +void SpatialRosI::timerCallback() +{ + std::lock_guard lock(spatial_mutex_); + if (can_publish_) + { + publishLatest(); + } +} + +void SpatialRosI::spatialDataCallback(const double acceleration[3], + const double angular_rate[3], + const double magnetic_field[3], + double timestamp) +{ + // When publishing the message on the ROS network, we want to publish the + // time that the data was acquired in seconds since the Unix epoch. The + // data we have to work with is the time that the callback happened (on the + // local processor, in Unix epoch seconds), and the timestamp that the + // IMU gives us on the callback (from the processor on the IMU, in + // milliseconds since some arbitrary starting point). + // + // At a first approximation, we can apply the timestamp from the device to + // Unix epoch seconds by taking a common starting point on the IMU and the + // local processor, then applying the delta between this IMU timestamp and + // the "zero" IMU timestamp to the local processor starting point. + // + // There are several complications with the simple scheme above. The first + // is finding a proper "zero" point where the IMU timestamp and the local + // timestamp line up. Due to potential delays in servicing this process, + // along with USB delays, the delta timestamp from the IMU and the time when + // this callback gets called can be wildly different. Since we want the + // initial zero for both the IMU and the local time to be in the same time + // "window", we throw away data at the beginning until we see that the delta + // callback and delta timestamp are within reasonable bounds of each other. + // + // The second complication is that the time on the IMU drifts away from the + // time on the local processor. Taking the "zero" time once at the + // beginning isn't sufficient, and we have to periodically re-synchronize + // the times given the constraints above. Because we still have the + // arbitrary delays present as described above, it can take us several + // callbacks to successfully synchronize. We continue publishing data using + // the old "zero" time until successfully resynchronize, at which point we + // switch to the new zero point. + + std::lock_guard lock(spatial_mutex_); + + rclcpp::Time now = this->now(); + + // At the beginning of time, need to initialize last_cb_time for later use; + // last_cb_time is used to figure out the time between callbacks + if (last_cb_time_.nanoseconds() == 0) + { + last_cb_time_ = now; + // We need to initialize the ros_time_zero since rclcpp::Duration + // below won't let us subtract an essentially uninitialized + // rclcpp::Time from another one. However, we'll still do an initial + // synchronization since the default value of synchronize_timestamp + // is true. + ros_time_zero_ = now; + return; + } + + rclcpp::Duration time_since_last_cb = now - last_cb_time_; + uint64_t this_ts_ns = static_cast(timestamp * 1000.0 * 1000.0); + + if (synchronize_timestamps_) + { + // The only time it's safe to sync time between IMU and ROS Node is when + // the data that came in is within the data interval that data is + // expected. It's possible for data to come late because of USB issues + // or swapping, etc and we don't want to sync with data that was + // actually published before this time interval, so we wait until we get + // data that is within the data interval +/- an epsilon since we will + // have taken some time to process and/or a short delay (maybe USB + // comms) may have happened + if (time_since_last_cb.nanoseconds() >= + (data_interval_ns_ - cb_delta_epsilon_ns_) && + time_since_last_cb.nanoseconds() <= + (data_interval_ns_ + cb_delta_epsilon_ns_)) + { + ros_time_zero_ = now; + data_time_zero_ns_ = this_ts_ns; + synchronize_timestamps_ = false; + can_publish_ = true; + } else + { + RCLCPP_DEBUG( + get_logger(), + "Data not within acceptable window for synchronization: " + "expected between %ld and %ld, saw %ld", + data_interval_ns_ - cb_delta_epsilon_ns_, + data_interval_ns_ + cb_delta_epsilon_ns_, + time_since_last_cb.nanoseconds()); + } + } + + if (can_publish_) // Cannot publish data until IMU/ROS timestamps have been + // synchronized at least once + { + // Save off the values + last_accel_x_ = -acceleration[0] * G; + last_accel_y_ = -acceleration[1] * G; + last_accel_z_ = -acceleration[2] * G; + + last_gyro_x_ = angular_rate[0] * (M_PI / 180.0); + last_gyro_y_ = angular_rate[1] * (M_PI / 180.0); + last_gyro_z_ = angular_rate[2] * (M_PI / 180.0); + + if (magnetic_field[0] != PUNK_DBL) + { + // device reports data in Gauss, multiply by 1e-4 to convert to + // Tesla + last_mag_x_ = magnetic_field[0] * 1e-4; + last_mag_y_ = magnetic_field[1] * 1e-4; + last_mag_z_ = magnetic_field[2] * 1e-4; + } else + { + // data is PUNK_DBL ("unknown double"), which means the magnetometer + // did not return valid readings. When publishing at 250 Hz, this + // will happen in every second message, because the magnetometer can + // only sample at 125 Hz. It is still important to publish these + // messages, because a downstream node sometimes uses a + // TimeSynchronizer to get Imu and Magnetometer nodes. + double nan = std::numeric_limits::quiet_NaN(); + + last_mag_x_ = nan; + last_mag_y_ = nan; + last_mag_z_ = nan; + } + last_data_timestamp_ns_ = this_ts_ns; + + // Publish if we aren't publishing on a timer + if (publish_rate_ <= 0.0) + { + publishLatest(); + } + } + + // Determine if we need to resynchronize - time between IMU and ROS Node can + // drift, periodically resync to deal with this issue + rclcpp::Duration diff = now - ros_time_zero_; + if (time_resync_interval_ns_ > 0 && + diff.nanoseconds() >= time_resync_interval_ns_) + { + synchronize_timestamps_ = true; + } + + last_cb_time_ = now; +} + +void SpatialRosI::spatialAlgorithmDataCallback(const double quaternion[4], + double timestamp) +{ + (void)timestamp; + last_quat_w_ = quaternion[3]; + last_quat_x_ = quaternion[0]; + last_quat_y_ = quaternion[1]; + last_quat_z_ = quaternion[2]; +} + +void SpatialRosI::attachCallback() +{ + RCLCPP_INFO(get_logger(), "Phidget Spatial attached."); + + // Set data interval. This is in attachCallback() because it has to be + // repeated on reattachment. + spatial_->setDataInterval(data_interval_ns_ / 1000 / 1000); + + // Force resynchronization, because the device time is reset to 0 after + // reattachment. + synchronize_timestamps_ = true; + can_publish_ = false; + last_cb_time_ = rclcpp::Time(0); +} + +void SpatialRosI::detachCallback() +{ + RCLCPP_INFO(get_logger(), "Phidget Spatial detached."); +} + +} // namespace phidgets + +RCLCPP_COMPONENTS_REGISTER_NODE(phidgets::SpatialRosI) diff --git a/docker/side/Dockerfile b/docker/side/Dockerfile new file mode 100644 index 0000000..74d738a --- /dev/null +++ b/docker/side/Dockerfile @@ -0,0 +1,91 @@ +################################################################################################ +# - Base stage +# - This stage serves as the foundational stage for all other stages. +# - Base image: OSRF ROS Humble Desktop Full +################################################################################################ +FROM osrf/ros:humble-desktop-full AS base +LABEL org.opencontainers.image.authors="yoseph.huang@gmail.com" +ARG USERNAME=user +ARG USER_UID=1000 +ARG USER_GID=$USER_UID +SHELL ["/bin/bash", "-c"] +################################################################################################ +# - User Setup stage +# - In this stage, create a non-root user and configure passwordless sudo. +################################################################################################ +FROM base AS user-setup +RUN groupadd --gid $USER_GID $USERNAME && \ + useradd --uid $USER_UID --gid $USER_GID -m $USERNAME -s /bin/bash && \ + apt-get update && \ + apt-get install -y sudo && \ + echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME && \ + chmod 0440 /etc/sudoers.d/$USERNAME && \ + rm -rf /var/lib/apt/lists/* +################################################################################################ +# - Tools Installation stage +################################################################################################ +FROM user-setup AS tools +RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + python3-pip \ + python3-rosdep \ + python3-setuptools \ + python3-numpy \ + apt-utils \ + git \ + nano \ + net-tools \ + ssh \ + usbutils \ + udev \ + x11-apps \ + tmux \ + htop \ + libsdl-image1.2-dev \ + libarmadillo-dev \ + libboost-all-dev \ + libsuitesparse-dev \ + ros-humble-libg2o \ + ros-humble-rmw-cyclonedds-cpp \ + && rm -rf /var/lib/apt/lists/* +################################################################################################ +# - Source clone stage +################################################################################################ +FROM tools AS source +USER $USERNAME +WORKDIR /home/$USERNAME/localization-ws/src +# === Clone required packages === +WORKDIR /home/user/localization-ws/src/YDLidar-SDK +RUN git clone https://github.com/wintera1233/YDLidar-SDK.git /home/user/localization-ws/src/YDLidar-SDK/ +WORKDIR /home/user/localization-ws/src/localization-devel-ws/rival_localization +RUN cd /home/user/localization-ws/src/YDLidar-SDK/build && \ + cmake .. && \ + make -j4 && \ + sudo make install +RUN git clone -b ros2 https://github.com/Slamtec/rplidar_ros.git /home/user/localization-ws/src/rplidar_ros \ + && git clone -b humble https://github.com/YDLIDAR/ydlidar_ros2_driver.git /home/user/localization-ws/src/ydlidar_ros2_driver +RUN git clone -b sideLidar https://github.com/DIT-ROBOTICS/Eurobot-2025-Localization.git \ + && mv Eurobot-2025-Localization/localization-devel-ws/rival/rival_localization /home/$USERNAME/localization-ws/src/localization-devel-ws/rival_localization \ + && mv Eurobot-2025-Localization/localization-devel-ws/global/obstacle_detector_2-humble-devel /home/$USERNAME/localization-ws/src/localization-devel-ws/obstacle_detector_2-humble-devel \ + && rm -rf Eurobot-2025-Localization + +RUN +################################################################################################ +# - Final Build Stage +################################################################################################ +FROM source AS final +WORKDIR /home/$USERNAME/localization-ws +USER $USERNAME +RUN sudo apt-get update && \ + rosdep update && \ + rosdep install -y -r -q --from-paths src --ignore-src --rosdistro $ROS_DISTRO && \ + /bin/bash -c "source /opt/ros/$ROS_DISTRO/setup.bash && colcon build --symlink-install" && \ + sudo rm -rf /var/lib/apt/lists/* + +RUN rm -rf /home/$USERNAME/localization-ws/src/localization-devel-ws + +COPY ./dds.xml /tmp/dds.xml +# COPY .bashrc /home/$USERNAME/.bashrc.conf +# RUN cat /home/$USERNAME/.bashrc.conf >> /home/$USERNAME/.bashrc + +#first build: wintera1233/side_lidar:test \ No newline at end of file diff --git a/docker/side/docker-compose.yaml b/docker/side/docker-compose.yaml new file mode 100644 index 0000000..7c24c9e --- /dev/null +++ b/docker/side/docker-compose.yaml @@ -0,0 +1,60 @@ +version: '3' +services: + side-ros2-localization-dev: + build: + context: . + dockerfile: Dockerfile + args: + USERNAME: user + image: wintera1233/side_lidar:porcupine + container_name: side_lidar + stdin_open: true + tty: true + privileged: true + stop_grace_period: 1s + restart: no + # entrypoint: ["./start.sh"] + + network_mode: host + working_dir: /home/user/localization-ws + environment: + - DISPLAY=${DISPLAY} + # Set ros2 environment variables. + # References: + # - https://docs.ros.org/en/humble/Concepts/Intermediate/About-Domain-ID.html + # - https://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Configuring-ROS2-Environment.html + # - ROS_LOCALHOST_ONLY=1 + - ROS_DOMAIN_ID=14 + - ROS_WS=/home/user/localization-ws + - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp + - CYCLONEDDS_URI=/tmp/dds.xml + volumes: + # Mount local timezone into container. ( Readonly ) + # Reference: https://stackoverflow.com/questions/57607381/how-do-i-change-timezone-in-a-docker-container + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + # Mount X11 server + - /tmp/.X11-unix:/tmp/.X11-unix + # Direct Rendering Infrastructure + # - /dev/dri:/dev/dri + # Mount sound card to prevent Gazebo warning. + # - /dev/snd:/dev/snd + - /dev:/dev + - /home/vision/localization/Eurobot-2025-Localization/docker/side/ethDDS.xml:/tmp/dds.xml + # Mount Gazebo models directory to reuse models downloaded during first launch. + # Reference: https://answers.ros.org/question/365658 + # - ./cache/.gazebo:/home/user/.gazebo + # Mounting the following directories will forbid direct deletion. + # Consider mount these directories only if the build process is slow. + # "source=${localWorkspaceFolder}/../cache/humble/build,target=/home/ws/build,type=bind", + # "source=${localWorkspaceFolder}/../cache/humble/install,target=/home/ws/install,type=bind", + # "source=${localWorkspaceFolder}/../cache/humble/log,target=/home/ws/log,type=bind" + # Mount workspace + - ../../localization-devel-ws/rival/rival_localization:/home/user/localization-ws/src/localization-devel-ws/rival_localization + # command: /bin/bash + command: /bin/bash -c " + source /opt/ros/humble/setup.bash && + sudo chmod 777 /dev/ttyUSB0 && + cd /home/user/localization-ws && + source /home/user/localization-ws/install/setup.bash && + ros2 launch /home/user/localization-ws/src/localization-devel-ws/rival_localization/launch/side_rival.xml" \ No newline at end of file diff --git a/docker/side/ethDDS.xml b/docker/side/ethDDS.xml new file mode 100644 index 0000000..3ba151e --- /dev/null +++ b/docker/side/ethDDS.xml @@ -0,0 +1,17 @@ + + + + + + + + true + 65500B + + + + diff --git a/docker/testBot/.bashrc b/docker/testBot/.bashrc new file mode 100644 index 0000000..478cf06 --- /dev/null +++ b/docker/testBot/.bashrc @@ -0,0 +1,6 @@ +# ========== ROS ========== +# Source ROS environment +source /opt/ros/$ROS_DISTRO/setup.bash +# Source workspace environment +# Note: If you have not built your workspace yet, the following command will fail +source $ROS_WS/install/setup.bash diff --git a/docker/testBot/.env b/docker/testBot/.env new file mode 100644 index 0000000..b4dc882 --- /dev/null +++ b/docker/testBot/.env @@ -0,0 +1,36 @@ +# Environment variables for Docker Compose + +# General settings +COMPOSE_PROJECT_NAME=localization +# COMPOSE_FILE=docker-compose.yaml:OdomComm/docker/compose.yml +COMPOSE_FILE=docker-compose.yaml + +## Release version + +# COMPOSE_PROFILES = communication, localization_main +# ROS2_LAUNCH_FILE = /home/user/localization-ws/src/localization-devel-ws/ekf/launch/y_running_launch.py +# ROS2_LAUNCH_FILE = /home/user/localization-ws/src/localization-devel-ws/ekf/launch/b_running_launch.py + +## Ready signal +COMPOSE_PROFILES = communication, localization_ready_signal +COMPOSE_PROFILES = communication, localization_main +# ROS2_LAUNCH_FILE = /home/user/localization-ws/src/localization-devel-ws/ekf/launch/y_running_launch.py +ROS2_LAUNCH_FILE = /home/user/localization-ws/src/localization-devel-ws/ekf/launch/b_running_launch.py + +## Build and Run +# COMPOSE_PROFILES = build_and_run +# ROS2_LAUNCH_FILE = /home/user/localization-ws/src/localization-devel-ws/ekf/launch/running_launch.py + +## Devel Build version +# COMPOSE_PROFILES = localization_devel +# ROS_DOMAIN_ID = 3 +# BRANCH_SELECT = devel + +## Record at PC +# COMPOSE_PROFILES = record +# ROS_DOMAIN_ID = 11 +# BAG_NAME = /home/user/localization-ws/src/localization-devel-ws/bag/0425_onboard_695050e4_v2 + +## Simply on local machine +# COMPOSE_PROFILES = localization_bash +# ROS_DOMAIN_ID = 14 \ No newline at end of file diff --git a/docker/testBot/Dockerfile b/docker/testBot/Dockerfile index 37ea01d..d410342 100644 --- a/docker/testBot/Dockerfile +++ b/docker/testBot/Dockerfile @@ -1,11 +1,11 @@ ################################################################################################ # - Base stage # - This stage serves as the foundational stage for all other stages. -# - Base image: OSRF ROS Noetic Desktop Full -# - https://hub.docker.com/r/osrf/ros/tags?page=1&name=noetic-desktop-full +# - Base image: OSRF ROS Humble Desktop Full +# - https://hub.docker.com/r/osrf/ros/tags?page=1&name=humble-desktop-full ################################################################################################ -FROM osrf/ros:noetic-desktop-full AS base +FROM osrf/ros:humble-desktop-full AS base LABEL org.opencontainers.image.authors="yoseph.huang@gmail.com" @@ -15,6 +15,7 @@ ARG USER_GID=$USER_UID SHELL ["/bin/bash", "-c"] + ################################################################################################ # - User Setup stage # - In this stage, create a non-root user and configure passwordless sudo. @@ -22,7 +23,7 @@ SHELL ["/bin/bash", "-c"] FROM base AS user-setup -# Create a non-root user +## Create a non-root user RUN groupadd --gid $USER_GID $USERNAME && \ useradd --uid $USER_UID --gid $USER_GID -m $USERNAME -s /bin/bash && \ apt-get update && \ @@ -31,6 +32,7 @@ RUN groupadd --gid $USER_GID $USERNAME && \ chmod 0440 /etc/sudoers.d/$USERNAME && \ rm -rf /var/lib/apt/lists/* + ################################################################################################ # - Tools Installation stage # - In this stage, I will install convenient tools for development. @@ -38,109 +40,141 @@ RUN groupadd --gid $USER_GID $USERNAME && \ FROM user-setup AS tools -# Install necessary packages +## Install necessary packages RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ DEBIAN_FRONTEND=noninteractive apt-get install -y \ + cmake pkg-config \ + python3-pip \ + python3-rosdep \ + python3-setuptools \ + python3-numpy \ apt-utils \ git \ nano \ net-tools \ ssh \ usbutils \ - udev \ + udev \ x11-apps \ tmux \ - htop \ + htop \ + ros-humble-foxglove-bridge \ libsdl-image1.2-dev \ - libsdl-dev \ - ros-noetic-tf2-sensor-msgs \ - ros-noetic-move-base-msgs \ - ros-noetic-mbf-costmap-core \ - ros-noetic-mbf-msgs \ + libarmadillo-dev \ + libboost-all-dev \ + # ros-humble-laser-geometry \ libsuitesparse-dev \ - ros-noetic-libg2o \ - ros-noetic-teleop-twist-keyboard \ - ros-noetic-costmap-converter \ - ros-noetic-robot-localization \ - ros-noetic-imu-tools \ + ros-humble-libg2o \ + ros-humble-rmw-cyclonedds-cpp\ + # ros-humble-imu-tools \ libusb-1.0-0 libusb-1.0-0-dev -y && \ rm -rf /var/lib/apt/lists/* +RUN pip3 install --no-cache-dir rotop + ################################################################################################ -# - RPLiDar Installation stage -# - In this stage, install necessary tools for RPLiDar. -################################################################################################ - -FROM tools AS rplidar-dep -RUN apt-get update && apt-get install -y \ - python3-pip \ - python3-rosdep \ - python3-setuptools \ - python3-numpy \ - git && \ - apt-get clean - -WORKDIR /home/${USERNAME}/localization-ws-ros1 -RUN git clone https://github.com/Slamtec/rplidar_ros.git /home/${USERNAME}/localization-ws-ros1/src/rplidar-ros-driver -RUN . /opt/ros/noetic/setup.sh && \ - catkin_make - -################################################################################################ -# - Obstacle detect Installation stage +# - IMU Installation stage +# - In this stage, install neccesary stuff for phidget IMU spatial. ################################################################################################ -FROM rplidar-dep AS obs-dep +FROM tools AS sensor-dep +## Install necessary tools for imu RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ DEBIAN_FRONTEND=noninteractive apt-get install -y \ - ros-noetic-laser-geometry \ - libarmadillo-dev \ - libboost-all-dev \ + ros-humble-imu-tools \ && rm -rf /var/lib/apt/lists/* -WORKDIR /home/${USERNAME}/localization-ws-ros1 -RUN mkdir -p /home/${USERNAME}/localization-ws-ros1/src/localization-devel-ws && \ - git clone https://github.com/tysik/obstacle_detector.git /home/${USERNAME}/localization-ws-ros1/src/localization-devel-ws/obstacle_detector - +USER ${USERNAME} +WORKDIR /home/user/localization-ws/src/phidgets_drivers +COPY phidgets_drivers /home/user/localization-ws/src/phidgets_drivers + +WORKDIR /home/user/localization-ws/src/rplidar-ros-driver +RUN git clone -b ros2 https://github.com/Slamtec/rplidar_ros.git /home/user/localization-ws/src/rplidar-ros-driver/ +# RUN git clone -b humble-devel https://github.com/harmony-eu/obstacle_detector_2.git /home/user/localization-ws/src/obstacle_detector_2 + +WORKDIR /home/user/localization-ws/src/YDLidar-SDK +RUN git clone https://github.com/wintera1233/YDLidar-SDK.git /home/user/localization-ws/src/YDLidar-SDK/ +WORKDIR /home/user/localization-ws +RUN cd /home/user/localization-ws/src/YDLidar-SDK/build && \ + cmake .. && \ + make -j4 && \ + sudo make install + +WORKDIR /home/user/localization-ws/src/ydlidar_ros2_driver +RUN git clone -b humble https://github.com/YDLIDAR/ydlidar_ros2_driver.git + +WORKDIR /home/user/localization-ws +# RUN sudo chown -R ${USERNAME}:${USERNAME} /home/${USERNAME}/localization-ws RUN sudo apt-get update && \ rosdep update && \ - rosdep install --from-paths . --ignore-src -r -y - -# Build the workspace -USER root -RUN echo "source /opt/ros/noetic/setup.bash" >> /etc/bash.bashrc && \ - echo "source /home/${USERNAME}/localization-ws-ros1/devel/setup.bash" >> /etc/bash.bashrc -# Clean up -RUN sudo rm -rf /var/lib/apt/lists/* + rosdep install -y -r -q --from-paths src --ignore-src --rosdistro $ROS_DISTRO && \ + /bin/bash -c 'source /opt/ros/$ROS_DISTRO/setup.bash; colcon build --symlink-install' \ + && sudo rm -rf /var/lib/apt/lists/* ################################################################################################ -# - Spatial-driver Installation stage +# - ros workspace build stage +# ################################################################################################ -FROM obs-dep AS imu-dep -RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y \ - python3-pip \ - ros-noetic-imu-tools \ - libusb-1.0-0 libusb-1.0-0-dev \ - && rm -rf /var/lib/apt/lists/* -COPY phidgets_drivers /home/${USERNAME}/localization-ws-ros1/src/phidgets_drivers +FROM sensor-dep AS ros-ws +WORKDIR /home/user/temp +RUN git clone -b Release https://github.com/DIT-ROBOTICS/Eurobot-2025-Localization.git /home/user/temp/Eurobot-2025-Localization +RUN mv /home/user/temp/Eurobot-2025-Localization/localization-devel-ws /home/user/localization-ws/src/localization-devel-ws +RUN rm -rf /home/user/temp + +## Set working directory +WORKDIR /home/${USERNAME}/localization-ws +# RUN sudo chown -R ${USERNAME}:${USERNAME} /home/${USERNAME}/localization-ws +USER ${USERNAME} +RUN /bin/bash -c 'source /opt/ros/$ROS_DISTRO/setup.bash; colcon build --packages-select obstacle_detector' && \ + # /bin/bash -c 'source /opt/ros/$ROS_DISTRO/setup.bash; source /home/user/localization-ws/install/setup.bash; colcon build --packages-select rival_localization --event-handlers console_cohesion+ --parallel-workers 1' + /bin/bash -c 'source /home/user/localization-ws/install/setup.bash; colcon build --symlink-install --packages-ignore imm_filter obstacle_detector' \ + && sudo rm -rf /var/lib/apt/lists/* + +RUN rm -rf /home/user/localization-ws/src/localization-devel-ws ################################################################################################ # - Final stage +# ################################################################################################ -FROM imu-dep AS final +FROM ros-ws AS final -## Set working directory -WORKDIR /home/${USERNAME}/localization-ws-ros1 +COPY .bashrc /home/$USERNAME/.bashrc.conf +RUN cat /home/$USERNAME/.bashrc.conf >> /home/$USERNAME/.bashrc -# Ensure that the ROS workspace is built and set up correctly -RUN source /opt/ros/noetic/setup.bash && \ - cd /home/${USERNAME}/localization-ws-ros1 && \ - catkin_make +USER ${USERNAME} +CMD ["/bin/bash"] +################################################################################################ +# - devel-ws build stage +# +################################################################################################ + +FROM final AS devel-ws + +ARG USERNAME +ARG BRANCH_SELECT=Raccoon + +WORKDIR /home/user/temp +RUN git clone -b ${BRANCH_SELECT} https://github.com/DIT-ROBOTICS/Eurobot-2025-Localization.git /home/user/temp/Eurobot-2025-Localization +RUN mv /home/user/temp/Eurobot-2025-Localization/localization-devel-ws /home/user/localization-ws/src/localization-devel-ws +RUN rm -rf /home/user/temp + +## Set working directory +WORKDIR /home/${USERNAME}/localization-ws USER ${USERNAME} -SHELL ["/bin/bash", "-c"] +RUN /bin/bash -c 'source /opt/ros/$ROS_DISTRO/setup.bash && \ + cd /home/user/localization-ws && \ + rm -rf build/ install/ log/ && \ + colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release' \ + && sudo rm -rf /var/lib/apt/lists/* + +RUN rm -rf /home/user/localization-ws/src/localization-devel-ws +COPY .bashrc /home/$USERNAME/.bashrc.conf +RUN cat /home/$USERNAME/.bashrc.conf >> /home/$USERNAME/.bashrc + +USER ${USERNAME} CMD ["/bin/bash"] \ No newline at end of file diff --git a/docker/testBot/OdomComm/_run.sh b/docker/testBot/OdomComm/_run.sh new file mode 100644 index 0000000..9e82b64 --- /dev/null +++ b/docker/testBot/OdomComm/_run.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +## 1. make scripts & library executable +echo "=== [COMMUNICATION] ===" +find ../OdomComm -type f -name "*.sh" -exec sudo chmod +x {} \; + +## 2. environment setup +export DISPLAY=:0 +xhost +local:docker +cd docker + +## 3. deployment +echo "[COMMUNICATION] Deploying..." +docker compose -p communication up -d \ No newline at end of file diff --git a/docker/testBot/OdomComm/blue_run.sh b/docker/testBot/OdomComm/blue_run.sh new file mode 100755 index 0000000..0b5e26d --- /dev/null +++ b/docker/testBot/OdomComm/blue_run.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +## 0. clean container within same group +#echo "=== [COMMUNICATION] Pull & Run ===" +#echo "[COMMUNICATION] Remove Containers ..." +#docker compose -p communication down --volumes --remove-orphans + +## 2. environment setup +#export DISPLAY=:0 +#xhost +local:docker +#cd docker + +## 3. build image +#echo "[COMMUNICATION] Pull Images ..." +#docker pull pomelo925/ttennis-humble:communication + +## 4. deployment +echo "[COMMUNICATION] Deploying..." +docker compose -p localization-blue -f docker/blue-compose.yml up diff --git a/docker/testBot/OdomComm/build_and_run.sh b/docker/testBot/OdomComm/build_and_run.sh new file mode 100644 index 0000000..a77990a --- /dev/null +++ b/docker/testBot/OdomComm/build_and_run.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +## 0. clean container within same group +echo "=== [COMMUNICATION] Build & Run ===" +echo "[COMMUNICATION] Remove Containers ..." +docker compose -p communication down --volumes --remove-orphans + +## 1. make scripts & library executable +find ../OdomComm -type f -name "*.sh" -exec sudo chmod +x {} \; + +## 2. environment setup +export DISPLAY=:0 +xhost +local:docker +cd docker + +## 3. build image +echo "[COMMUNICATION] Building..." +docker compose -p communication build + +## 4. deployment +echo "[COMMUNICATION] Deploying..." +docker compose -p communication up -d \ No newline at end of file diff --git a/docker/testBot/OdomComm/docker/Dockerfile b/docker/testBot/OdomComm/docker/Dockerfile new file mode 100644 index 0000000..43a1026 --- /dev/null +++ b/docker/testBot/OdomComm/docker/Dockerfile @@ -0,0 +1,151 @@ +################################################################################################ +# - Base stage +# - This stage serves as the foundational stage for all other stages. +# - Base image: Ubuntu 22.04 Jammy Jellyfish +# - https://hub.docker.com/_/ubuntu/tags?page=1&name=22.04 +################################################################################################ + +FROM ubuntu:22.04 AS base + +LABEL org.opencontainers.image.authors="yuzhong1214@gmail.com" + +# Reference: https://docs.ros.org/en/humble/Installation/Alternatives/Ubuntu-Development-Setup.html#set-locale +ENV LC_ALL=en_US.UTF-8 +ENV LANG=en_US.UTF-8 + +SHELL ["/bin/bash", "-c"] + +# Add user +RUN useradd -ms /bin/bash user + +################################################################################################ +# - Build stage +# - In this stage, I will build ROS Humble and ros1-bridge from source. +# - Reference: +# - https://docs.ros.org/en/humble/How-To-Guides/Using-ros1_bridge-Jammy-upstream.html +# - https://docs.ros.org/en/humble/Installation/Alternatives/Ubuntu-Development-Setup.html +################################################################################################ + +FROM base AS build + +# Install the required packages for the following command. +RUN apt-get update && \ + apt-get install -y \ + curl \ + locales \ + software-properties-common \ + lsb-release + +# Set locale. +# Reference: https://docs.ros.org/en/humble/Installation/Alternatives/Ubuntu-Development-Setup.html#set-locale +RUN locale-gen en_US en_US.UTF-8 && \ + update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 + +# Add the ROS 2 apt repository. +# Reference: https://docs.ros.org/en/humble/Installation/Alternatives/Ubuntu-Development-Setup.html#add-the-ros-2-apt-repository +RUN add-apt-repository universe && \ + curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg && \ + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | tee /etc/apt/sources.list.d/ros2.list > /dev/null + +# Install development tools and ROS tools. +# Note that we declare noninteractive mode to avoid apt asking for user input. +# Additionally, install colcon from PyPI, rather than using apt packages. +# Reference: https://docs.ros.org/en/humble/How-To-Guides/Using-ros1_bridge-Jammy-upstream.html#install-development-tools-and-ros-tools +RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + build-essential \ + cmake \ + git \ + python3-flake8 \ + python3-flake8-blind-except \ + python3-flake8-builtins \ + python3-flake8-class-newline \ + python3-flake8-comprehensions \ + python3-flake8-deprecated \ + python3-flake8-docstrings \ + python3-flake8-import-order \ + python3-flake8-quotes \ + python3-pip \ + python3-pytest \ + python3-pytest-cov \ + python3-pytest-repeat \ + python3-pytest-rerunfailures \ + python3-rosdep \ + python3-setuptools \ + ros-dev-tools +RUN python3 -m pip install -U colcon-common-extensions vcstool + +# Create ros2_humble workspace, and clone all repositories from the list. +# Reference: https://docs.ros.org/en/humble/Installation/Alternatives/Ubuntu-Development-Setup.html#get-ros-2-code +RUN mkdir -p /ros2_humble/src +WORKDIR /ros2_humble +RUN vcs import --input https://raw.githubusercontent.com/ros2/ros2/humble/ros2.repos src + +# Install dependencies. +# Reference: https://docs.ros.org/en/humble/Installation/Alternatives/Ubuntu-Development-Setup.html#install-dependencies-using-rosdep +RUN rosdep init && \ + rosdep update && \ + rosdep install --from-paths src --ignore-src -y --rosdistro humble --skip-keys "fastcdr rti-connext-dds-6.0.1 urdfdom_headers" + +# Build the ros2_humble workspace. (This will take a few minutes) +# Note that we are using colcon build without the flag --symlink-install. +# This is because we will copy the built files from the build stage to the release stage. +# If we use the flag --symlink-install, the release stage will not be able to find the built files. +# Reference: https://docs.ros.org/en/humble/Installation/Alternatives/Ubuntu-Development-Setup.html#build-the-code-in-the-workspace +RUN colcon build + +# Delete packages.ros.org from sources.list and remove conflicts packages manually. +# Removing those conflicts packages is necessary for install ros-core-dev. +# Here is the dicussion about this issue: https://github.com/j3soon/ros2-essentials/pull/9#discussion_r1375303858 +# Reference: +# - https://docs.ros.org/en/humble/How-To-Guides/Using-ros1_bridge-Jammy-upstream.html#ros-2-via-debian-packages +# - https://github.com/osrf/docker_images/issues/635#issuecomment-1217505552 +RUN rm -rf /etc/apt/sources.list.d/ros2.list && \ + apt-get remove -y \ + python3-catkin-pkg \ + python3-catkin-pkg-modules + +# Install ROS 1 from Ubuntu packages. +# Reference: https://docs.ros.org/en/humble/How-To-Guides/Using-ros1_bridge-Jammy-upstream.html#install-ros-1-from-ubuntu-packages +RUN apt-get update && \ + apt-get install -y ros-core-dev + +# Build ros1_bridge from source. +# Reference: https://github.com/ros2/ros1_bridge/tree/3d5328dc21564d2130b4ded30afe5cd1c41cf033#building-the-bridge-from-source +RUN git clone https://github.com/ros2/ros1_bridge src/ros1_bridge +RUN source install/setup.bash && \ + colcon build --packages-select ros1_bridge --cmake-force-configure + +# Other configurations +COPY ./start-bridge.sh /start-bridge.sh +RUN chmod +x /start-bridge.sh + +CMD ["/bin/bash"] + +################################################################################################ +# - Release stage +# - In this stage, I will copy the built files from the build stage. +# This is useful for reducing the size of the image. +# - Reference: +# - https://docs.docker.com/build/building/multi-stage/ +################################################################################################ + +FROM base AS release + +# Install ROS core and packages with their dependencies. +RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + libspdlog-dev \ + python3-packaging \ + ros-core-dev + +# Copy files from host and build stage +COPY --from=build /ros2_humble/install /ros2_humble/install + +# Other configurations +COPY ./start-bridge.sh /start-bridge.sh +USER user +RUN echo "source /opt/ros/$ROS_DISTRO/setup.bash" >> ~/.bashrc + +WORKDIR / +CMD ["./start-bridge.sh"] \ No newline at end of file diff --git a/docker/testBot/OdomComm/docker/Dockerfile-rosserial b/docker/testBot/OdomComm/docker/Dockerfile-rosserial new file mode 100644 index 0000000..1eda1ec --- /dev/null +++ b/docker/testBot/OdomComm/docker/Dockerfile-rosserial @@ -0,0 +1,51 @@ +################################################################################################ +# - Base stage +# - This stage serves as the foundational stage for all other stages. +# - Base image: OSRF ROS noetic Desktop Full +# - https://hub.docker.com/r/osrf/ros/tags?page=1&name=noetic-desktop-full +################################################################################################ + +FROM osrf/ros:noetic-desktop-full AS base + +LABEL org.opencontainers.image.authors="yoseph.huang@gmail.com" + +ARG USERNAME=user +ARG USER_UID=1000 +ARG USER_GID=$USER_UID + +SHELL ["/bin/bash", "-c"] + + +################################################################################################ +# - User Setup stage +# - In this stage, create a non-root user and configure passwordless sudo. +################################################################################################ + +FROM base AS user-setup + +## Create a non-root user +RUN groupadd --gid $USER_GID $USERNAME && \ + useradd --uid $USER_UID --gid $USER_GID -m $USERNAME -s /bin/bash && \ + apt-get update && \ + apt-get install -y sudo && \ + echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME && \ + chmod 0440 /etc/sudoers.d/$USERNAME && \ + rm -rf /var/lib/apt/lists/* + +FROM user-setup AS final + +USER ${USERNAME} +# COPY comm-ws /home/$USERNAME/comm-ws +RUN mkdir -p /home/$USERNAME/ws/src +COPY odometry /home/$USERNAME/ws/src/odometry + +# catkin_make +RUN /bin/bash -c "source /opt/ros/noetic/setup.bash && \ + rosdep update && \ + cd /home/$USERNAME/ws && \ + rosdep install -y -r -q --from-paths src --ignore-src --rosdistro noetic && \ + catkin_make" + +## Final configurations +USER $USERNAME +CMD ["/bin/bash"] \ No newline at end of file diff --git a/docker/testBot/OdomComm/docker/blue-compose.yml b/docker/testBot/OdomComm/docker/blue-compose.yml new file mode 100644 index 0000000..150df40 --- /dev/null +++ b/docker/testBot/OdomComm/docker/blue-compose.yml @@ -0,0 +1,103 @@ +services: + ros-core: + image: osrf/ros:noetic-desktop-full + container_name: ros-core + command: "rosmaster --core" + network_mode: host + stop_grace_period: 1s + healthcheck: + # The healthcheck is required for ros1-bridge to wait until roscore is ready. + test: /ros_entrypoint.sh bash -c "rostopic list || exit 1" + interval: 3s + timeout: 10s + retries: 5 + + ros1-bridge: + build: + context: . + target: release + image: pomelo925/ttennis-humble:communication + container_name: ros2-ros1-bridge-ws + stop_grace_period: 1s + depends_on: + ros-core: + # The healthcheck is required for ros1-bridge to wait until roscore is ready. + condition: service_healthy + command: ./start-bridge.sh + stdin_open: true + tty: true + network_mode: host + working_dir: / + volumes: + # Mount local timezone into container. ( Readonly ) + # Reference: https://stackoverflow.com/questions/57607381/how-do-i-change-timezone-in-a-docker-container + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + # Direct Rendering Infrastructure + - /dev/dri:/dev/dri + # Shared Memory + - /dev/shm:/dev/shm + environment: + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + # - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp=value + + ros1: + image: jossiewang/eurobot2025-localization:rosserial-chassis + container_name: ros1 + stdin_open: true + tty: true + network_mode: host + privileged: true + volumes: + - /dev:/dev + stop_grace_period: 1s + depends_on: + ros-core: + condition: service_healthy + command: /bin/bash -c " + source /opt/ros/noetic/setup.bash && + source ~/ws/devel/setup.bash && + roslaunch odometry odometry_comm.launch" + + ros2-localization-dev: + + image: jossiewang/eurobot2025-localization2:master + container_name: localization-2025-dev-ros2 + stdin_open: true + tty: true + privileged: true + stop_grace_period: 1s + restart: no + # entrypoint: ["./start.sh"] + + network_mode: host + working_dir: /home/user/localization-ws + environment: + - DISPLAY=${DISPLAY} + # Set ros2 environment variables. + # References: + # - https://docs.ros.org/en/humble/Concepts/Intermediate/About-Domain-ID.html + # - https://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Configuring-ROS2-Environment.html + # - ROS_LOCALHOST_ONLY=1 + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp + - ROS_WS=/home/user/localization-ws + volumes: + # Mount local timezone into container. ( Readonly ) + # Reference: https://stackoverflow.com/questions/57607381/how-do-i-change-timezone-in-a-docker-container + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + # Mount X11 server + - /tmp/.X11-unix:/tmp/.X11-unix + # Direct Rendering Infrastructure + # - /dev/dri:/dev/dri + # Mount sound card to prevent Gazebo warning. + # - /dev/snd:/dev/snd + - /home/share/data:/home/user/share/data:ro + - /dev:/dev + # Mount workspace + - ../../../../localization-devel-ws:/home/user/localization-ws/src/localization-devel-ws + command: /bin/bash -c " + source /opt/ros/humble/setup.bash && + source /home/user/localization-ws/install/setup.bash && + ros2 launch /home/user/localization-ws/src/localization-devel-ws/ekf/launch/b_running_launch.py" \ No newline at end of file diff --git a/docker/testBot/OdomComm/docker/compose-serial.yaml b/docker/testBot/OdomComm/docker/compose-serial.yaml new file mode 100644 index 0000000..f66f575 --- /dev/null +++ b/docker/testBot/OdomComm/docker/compose-serial.yaml @@ -0,0 +1,29 @@ +services: + ros1: + image: ros1:serial + container_name: ros1 + stdin_open: true + tty: true + network_mode: host + privileged: true + volumes: + # - ../comm-ws:/root/comm-ws + # - /dev/bus/usb:/dev/bus/usb + - /dev:/dev + stop_grace_period: 1s + # depends_on: + # ros-core: + # condition: service_healthy + # command: /bin/bash -c " + # chmod +x /root/comm-ws/devel/_setup_util.py && + # find /root/comm-ws/devel -type f -name '*.sh' -exec chmod +x {} \; && + # source /opt/ros/noetic/setup.bash && + # source /root/comm-ws/devel/setup.bash && + # cd /root/comm-ws && + # sudo chmod 777 /dev/ttyACM0 && + # roslaunch rosserial_server stm32.launch" + command: /bin/bash -c " + source /opt/ros/noetic/setup.bash && + source ~/ws/devel/setup.bash && + sudo chmod 777 /dev/ttyACM0 && + roslaunch odometry odometry_comm.launch" \ No newline at end of file diff --git a/docker/testBot/OdomComm/docker/compose.yml b/docker/testBot/OdomComm/docker/compose.yml new file mode 100644 index 0000000..86c7878 --- /dev/null +++ b/docker/testBot/OdomComm/docker/compose.yml @@ -0,0 +1,110 @@ +services: + ros-core: + image: osrf/ros:noetic-desktop-full + container_name: ros-core + command: "rosmaster --core" + network_mode: host + stop_grace_period: 1s + healthcheck: + # The healthcheck is required for ros1-bridge to wait until roscore is ready. + test: /ros_entrypoint.sh bash -c "rostopic list || exit 1" + interval: 3s + timeout: 10s + retries: 5 + + ros1-bridge: + build: + context: . + target: release + image: pomelo925/ttennis-humble:communication + container_name: ros2-ros1-bridge-ws + stop_grace_period: 1s + depends_on: + ros-core: + # The healthcheck is required for ros1-bridge to wait until roscore is ready. + condition: service_healthy + command: ./start-bridge.sh + stdin_open: true + tty: true + network_mode: host + working_dir: / + volumes: + # Mount local timezone into container. ( Readonly ) + # Reference: https://stackoverflow.com/questions/57607381/how-do-i-change-timezone-in-a-docker-container + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + # Direct Rendering Infrastructure + - /dev/dri:/dev/dri + # Shared Memory + - /dev/shm:/dev/shm + environment: + - ROS_DOMAIN_ID=42 + # - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp=value + + ros1: + image: jossiewang/eurobot2025-localization:rosserial-ttyusb0 + container_name: ros1 + stdin_open: true + tty: true + network_mode: host + privileged: true + volumes: + - /dev:/dev + stop_grace_period: 1s + depends_on: + ros-core: + condition: service_healthy + command: /bin/bash -c " + source /opt/ros/noetic/setup.bash && + source ~/ws/devel/setup.bash && + sudo chmod 777 /dev/ttyUSB0 && + roslaunch odometry odometry_comm.launch" + + ros2-localization-dev: + build: + context: . + dockerfile: Dockerfile + args: + USERNAME: user + image: jossiewang/eurobot2025-localization:testbot-release # TODO: make a ros-ws-built image + container_name: localization-2025-dev-ros2 + stdin_open: true + tty: true + privileged: true + stop_grace_period: 1s + restart: no + # entrypoint: ["./start.sh"] + + network_mode: host + working_dir: /home/user/localization-ws + environment: + - DISPLAY=${DISPLAY} + # Set ros2 environment variables. + # References: + # - https://docs.ros.org/en/humble/Concepts/Intermediate/About-Domain-ID.html + # - https://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Configuring-ROS2-Environment.html + # - ROS_LOCALHOST_ONLY=1 + - ROS_DOMAIN_ID=42 + - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp + - ROS_WS=/home/user/localization-ws + volumes: + # Mount local timezone into container. ( Readonly ) + # Reference: https://stackoverflow.com/questions/57607381/how-do-i-change-timezone-in-a-docker-container + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + # Mount X11 server + - /tmp/.X11-unix:/tmp/.X11-unix + # Direct Rendering Infrastructure + # - /dev/dri:/dev/dri + # Mount sound card to prevent Gazebo warning. + # - /dev/snd:/dev/snd + - /dev:/dev + # Mount workspace + - ../../../../localization-devel-ws:/home/user/localization-ws/src/localization-devel-ws + command: /bin/bash -c " + cd /home/user/localization-ws/src/localization-devel-ws && + sudo chmod +x usb.sh && + ./usb.sh && + source /opt/ros/humble/setup.bash && + source /home/user/localization-ws/install/setup.bash && + ros2 launch /home/user/localization-ws/src/localization-devel-ws/ekf/launch/run.launch" \ No newline at end of file diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/CMakeLists.txt b/docker/testBot/OdomComm/docker/odometry/odometry/CMakeLists.txt similarity index 90% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/CMakeLists.txt rename to docker/testBot/OdomComm/docker/odometry/odometry/CMakeLists.txt index 223d94e..e45df10 100644 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/CMakeLists.txt +++ b/docker/testBot/OdomComm/docker/odometry/odometry/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.0.2) -project(imu_drive) +project(odometry) ## Compile as C++11, supported in ROS Kinetic and newer # add_compile_options(-std=c++11) @@ -8,11 +8,11 @@ project(imu_drive) ## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) ## is used, also find other catkin packages find_package(catkin REQUIRED COMPONENTS + geometry_msgs + nav_msgs roscpp rospy - sensor_msgs std_msgs - std_srvs dynamic_reconfigure ) @@ -73,7 +73,7 @@ find_package(catkin REQUIRED COMPONENTS ## Generate added messages and services with any dependencies listed here # generate_messages( # DEPENDENCIES -# sensor_msgs# std_msgs +# geometry_msgs# nav_msgs# std_msgs # ) ################################################ @@ -92,7 +92,7 @@ find_package(catkin REQUIRED COMPONENTS ## Generate dynamic reconfigure parameters in the 'cfg' folder generate_dynamic_reconfigure_options( - cfg/imu_drive_param.cfg + cfg/odometry_param.cfg ) ################################### @@ -106,8 +106,8 @@ generate_dynamic_reconfigure_options( ## DEPENDS: system dependencies of this project that dependent projects also need catkin_package( # INCLUDE_DIRS include -# LIBRARIES imu_drive -# CATKIN_DEPENDS roscpp rospy sensor_msgs std_msgs std_srvs +# LIBRARIES odometry +# CATKIN_DEPENDS geometry_msgs nav_msgs roscpp rospy std_msgs # DEPENDS system_lib ) @@ -122,11 +122,6 @@ include_directories( ${catkin_INCLUDE_DIRS} ) -## Declare a C++ library -# add_library(${PROJECT_NAME} -# src/${PROJECT_NAME}/imu_drive.cpp -# ) - ## Add cmake target dependencies of the library ## as an example, code may need to be generated before libraries ## either from message generation or dynamic reconfigure @@ -135,7 +130,7 @@ include_directories( ## Declare a C++ executable ## With catkin_make all packages are built within a single CMake context ## The recommended prefix ensures that target names across packages don't collide -# add_executable(${PROJECT_NAME}_node src/imu_drive_node.cpp) +# add_executable(${PROJECT_NAME}_node src/odometry_node.cpp) ## Rename C++ executable without prefix ## The above recommended prefix causes long target names, the following renames the @@ -152,13 +147,14 @@ include_directories( # ${catkin_LIBRARIES} # ) -add_library(imu_drive lib/imu_drive.cpp) -target_link_libraries(imu_drive ${catkin_LIBRARIES}) -add_dependencies(imu_drive ${catkin_EXPORTED_TARGETS} ${PROJECT_NAME}_gencfg) - +## -- Declare library -- ## +add_library(odometry lib/odometry.cpp) +target_link_libraries(odometry ${catkin_LIBRARIES}) +add_dependencies(odometry ${catkin_EXPORTED_TARGETS} ${PROJECT_NAME}_gencfg) -add_executable(imu_drive_node src/imu_drive_node.cpp) -target_link_libraries(imu_drive_node imu_drive) +## -- Build a nodes -- ## +add_executable(odometry_node src/odometry_node.cpp) +target_link_libraries(odometry_node odometry) ############# ## Install ## @@ -207,7 +203,7 @@ target_link_libraries(imu_drive_node imu_drive) ############# ## Add gtest based cpp test target and link libraries -# catkin_add_gtest(${PROJECT_NAME}-test test/test_imu_drive.cpp) +# catkin_add_gtest(${PROJECT_NAME}-test test/test_odometry.cpp) # if(TARGET ${PROJECT_NAME}-test) # target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}) # endif() diff --git a/docker/testBot/OdomComm/docker/odometry/odometry/cfg/odometry_param.cfg b/docker/testBot/OdomComm/docker/odometry/odometry/cfg/odometry_param.cfg new file mode 100644 index 0000000..dca0f41 --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/odometry/cfg/odometry_param.cfg @@ -0,0 +1,29 @@ +#!/usr/bin/env python +PACKAGE = "odometry" +NAMESPACE = "odometry" +GENERATE_FILE = "odometry_param" + +from dynamic_reconfigure.parameter_generator_catkin import ParameterGenerator, int_t, double_t, bool_t, str_t + +gen = ParameterGenerator() + +gen.add("publish", bool_t, 0, "To publish or not", True) +gen.add("twist_topic", str_t, 0, "Topic to subscribe odometry information", "/Toposition") +gen.add("odom_topic", str_t, 0, "Topic to publish odometry information", "/odom") + +gen.add("fixed_frame", str_t, 0, "Parent frame", "odom") +gen.add("target_frame", str_t, 0, "Child frame", "base_footprint") + +gen.add("covariance_x", double_t, 0, "Covariance x", 0.05, 0, 1) +gen.add("covariance_y", double_t, 0, "Covariance y", 0.05, 0, 1) +gen.add("covariance_z", double_t, 0, "Covariance z", 0.1, 0, 1) +gen.add("covariance_vx", double_t, 0, "Covariance vx", 0.1, 0, 1) +gen.add("covariance_vy", double_t, 0, "Covariance vy", 0.1, 0, 1) +gen.add("covariance_vz", double_t, 0, "Covariance vz", 0.2, 0, 1) + +gen.add("covariance_multi_vx", double_t, 0, "Covariance multiplican for vx", 0.5, 0, 9) +gen.add("covariance_multi_vy", double_t, 0, "Covariance multiplican for vy", 0.5, 0, 9) +gen.add("covariance_multi_vz", double_t, 0, "Covariance multiplican for vz", 0.5, 0, 9) + + +exit(gen.generate(PACKAGE, NAMESPACE, GENERATE_FILE)) diff --git a/docker/testBot/OdomComm/docker/odometry/odometry/include/odometry/odometry.h b/docker/testBot/OdomComm/docker/odometry/odometry/include/odometry/odometry.h new file mode 100644 index 0000000..89fab9a --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/odometry/include/odometry/odometry.h @@ -0,0 +1,80 @@ +#pragma once + +#include "ros/ros.h" +#include "nav_msgs/Odometry.h" +#include "geometry_msgs/Twist.h" +#include "std_srvs/Empty.h" +#include "std_msgs/Float64.h" + +#include +#include +#include +#include "odometry/odometry_paramConfig.h" + +class Odometry { + +public : + + Odometry(ros::NodeHandle &nh, ros::NodeHandle &nh_local); + +private : + + /* Function - for initialize params */ + void Initialize(); + + /* Function - for update params */ + bool UpdateParams(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res); + + /* Function - for twist callback */ + void TwistCallback(const geometry_msgs::Twist::ConstPtr &msg); + + /* Function -> for geometry_msgs::Twist ( cmd_vel ) */ + void P_VelocityCallback(const nav_msgs::Odometry::ConstPtr &msg); + + /* Function -> for geometry_msgs::TwistWithCovariance ( ekf_pose ) */ + void VelocityCallback(const nav_msgs::Odometry::ConstPtr &msg); + + /* Function publish sth we need */ + void publish(); + + /* Function for dynamic configure callback */ + void DynamicParamCallback(odometry::odometry_paramConfig &config, uint32_t level); + + /* Function to set up dynamic configure function and server */ + void SetDynamicReconfigure(); + + /** -- Node Handles -- **/ + ros::NodeHandle nh_; + ros::NodeHandle nh_local_; + + /** -- Advertise -- **/ + ros::Subscriber twist_sub_; + ros::Subscriber vel_sub_; + ros::Publisher odom_pub_; + ros::ServiceServer param_srv_; // Service for update param ( call by other nodes ) + + /** -- Msgs to pub -- **/ + nav_msgs::Odometry odometry_output_; + nav_msgs::Odometry odometry_output_backup_; + double covariance_multi_[3]; // x, y, z + + /** -- Parameters -- **/ + bool p_active_; + bool p_publish_; + bool p_update_params_; + bool p_sub_from_nav_; + bool p_use_dynamic_reconf_; + bool p_use_stm_integral_; + + double p_covariance_; + double p_covariance_multi_; + + std::string p_fixed_frame_; + std::string p_target_frame_; + + std::string p_twist_topic_; + std::string p_odom_topic_; + +}; + + diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/odometry/launch/odometry_comm.launch b/docker/testBot/OdomComm/docker/odometry/odometry/launch/odometry_comm.launch similarity index 73% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/odometry/launch/odometry_comm.launch rename to docker/testBot/OdomComm/docker/odometry/odometry/launch/odometry_comm.launch index 25bab0d..d7f96c8 100644 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/odometry/odometry/launch/odometry_comm.launch +++ b/docker/testBot/OdomComm/docker/odometry/odometry/launch/odometry_comm.launch @@ -6,7 +6,7 @@ - + - + + require: - publishers: [ Toposition ] + publishers: [ odoo_googoogoo, driving_duaiduaiduai ] subscribers: [ cmd_vel ] diff --git a/docker/testBot/OdomComm/docker/odometry/odometry/lib/odometry.cpp b/docker/testBot/OdomComm/docker/odometry/odometry/lib/odometry.cpp new file mode 100644 index 0000000..ff8a1f6 --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/odometry/lib/odometry.cpp @@ -0,0 +1,359 @@ +#include "odometry/odometry.h" + +Odometry::Odometry (ros::NodeHandle &nh, ros::NodeHandle &nh_local){ + + this->nh_ = nh; + this->nh_local_ = nh_local; + this->Initialize(); +} + +void Odometry::Initialize(){ + + std_srvs::Empty empty; + + this->p_active_ = false; + ROS_INFO_STREAM("[Odometry] : inactive node"); + + if(this->UpdateParams(empty.request, empty.response)){ + ROS_INFO_STREAM("[Odometry] : Initialize param ok"); + } + else { + ROS_INFO_STREAM("[Odometry] : Initialize param failed"); + } + +} + +bool Odometry::UpdateParams(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res){ + + bool prev_active = p_active_; + + double covariance[6]; + double multi_covariance[3]; + + /* get param */ + if(this->nh_local_.param("active", p_active_, true)){ + ROS_INFO_STREAM("[Odometry] : active set to " << p_active_); + } + + if(this->nh_local_.param("publish", p_publish_, true)){ + ROS_INFO_STREAM("[Odometry] : publish set to " << p_publish_); + } + + if(this->nh_local_.param("twist_topic", p_twist_topic_, "/Toposition")){ + ROS_INFO_STREAM("[Odometry] : Current subscribe topic [ " << p_twist_topic_ << " ]"); + } + + if(this->nh_local_.param("odom_topic", p_odom_topic_, "/odom")){ + ROS_INFO_STREAM("[Odometry] : Current publish topic [ " << p_odom_topic_ << " ]"); + } + + if(this->nh_local_.param("fixed_frame", p_fixed_frame_, "odom")){ + ROS_INFO_STREAM("[Odometry] : Current fixed frame [ " << p_fixed_frame_ << " ]"); + } + + if(this->nh_local_.param("target_frame", p_target_frame_, "base_footprint")){ + ROS_INFO_STREAM("[Odometry] : Current target frame [ " << p_target_frame_ << " ]"); + } + + if(this->nh_local_.param("update_params", p_update_params_, false)){ + ROS_INFO_STREAM("[Odometry] : update params set to " << p_update_params_); + } + + if(this->nh_local_.param("covariance_x", covariance[0], 0.)){ + ROS_INFO_STREAM("[Odometry] : x covariance set to " << covariance[0]); + this->odometry_output_.twist.covariance[0] = covariance[0]; + } + + if(this->nh_local_.param("covariance_y", covariance[1], 0.)){ + ROS_INFO_STREAM("[Odometry] : y covariance set to " << covariance[1]); + this->odometry_output_.twist.covariance[7] = covariance[1]; + } + + if(this->nh_local_.param("covariance_z", covariance[2], 0.)){ + ROS_INFO_STREAM("[Odometry] : z covariance set to " << covariance[2]); + this->odometry_output_.twist.covariance[14] = covariance[2]; + } + + if(this->nh_local_.param("covariance_vx", covariance[3], 0.)){ + ROS_INFO_STREAM("[Odometry] : vx covariance set to " << covariance[3]); + this->odometry_output_.twist.covariance[21] = covariance[3]; + } + + if(this->nh_local_.param("covariance_vy", covariance[4], 0.)){ + ROS_INFO_STREAM("[Odometry] : vy covariance set to " << covariance[4]); + this->odometry_output_.twist.covariance[28] = covariance[4]; + } + + if(this->nh_local_.param("covariance_vz", covariance[5], 0.)){ + ROS_INFO_STREAM("[Odometry] : vz covariance set to " << covariance[5]); + this->odometry_output_.twist.covariance[35] = covariance[5]; + } + + if(this->nh_local_.param("covariance_multi_vx", multi_covariance[0], 0.)){ + ROS_INFO_STREAM("[Odometry] : vx covariance multiplicant set to " << multi_covariance[0]); + covariance_multi_[0] = multi_covariance[0]; + } + + if(this->nh_local_.param("covariance_multi_vy", multi_covariance[1], 0.)){ + ROS_INFO_STREAM("[Odometry] : vy covariance multiplicant set to " << multi_covariance[1]); + covariance_multi_[1] = multi_covariance[1]; + } + + if(this->nh_local_.param("covariance_multi_vz", multi_covariance[2], 0.)){ + ROS_INFO_STREAM("[Odometry] : vz covariance multiplicant set to " << multi_covariance[2]); + covariance_multi_[2] = multi_covariance[2]; + } + + if(this->nh_local_.param("using_nav_vel_cb", p_sub_from_nav_, 0.)){ + ROS_INFO_STREAM("[Odometry] : current subscribe from nav cmd_vel is set to " << p_sub_from_nav_); + } + + if(this->nh_local_.param("using_dynamic_reconf", p_use_dynamic_reconf_, true)){ + ROS_INFO_STREAM("[Odometry] : using dynamic reconfigure is set to " << p_use_dynamic_reconf_); + } + + if(this->nh_local_.param("use_stm_integral", p_use_stm_integral_, true)){ + ROS_INFO_STREAM("[Odometry] : use stm integral " << p_use_stm_integral_); + } + + if(p_active_ != prev_active) { + + if (p_active_) { + + ROS_INFO_STREAM("[Odometry] : active node"); + this->twist_sub_ = nh_.subscribe(p_twist_topic_, 10, &Odometry::TwistCallback, this); + this->odom_pub_ = nh_.advertise(p_odom_topic_, 10); + + if(this->p_sub_from_nav_){ + this->vel_sub_ = nh_.subscribe("cmd_vel", 10, &Odometry::P_VelocityCallback, this); + } + else{ + this->vel_sub_ = nh_.subscribe("local_filter", 10, &Odometry::VelocityCallback, this); + } + + if(this->p_update_params_){ + this->param_srv_ = nh_local_.advertiseService("params", &Odometry::UpdateParams, this); + } + + if(this->p_use_dynamic_reconf_){ + this->SetDynamicReconfigure(); + } + + } + else { + this->twist_sub_.shutdown(); + this->odom_pub_.shutdown(); + this->vel_sub_.shutdown(); + + if(this->p_update_params_){ + this->param_srv_.shutdown(); + } + } + } + + + /* -- Backup covariance -- */ + this->odometry_output_backup_ = this->odometry_output_; + + /* -- Set basic variables -- */ + this->odometry_output_.header.frame_id = this->p_fixed_frame_; + this->odometry_output_.child_frame_id = this->p_target_frame_; + + return true; + +} + +void Odometry::TwistCallback(const geometry_msgs::Twist::ConstPtr &msg){ + + static unsigned int sequence = 0; + sequence++; + + this->odometry_output_.header.seq = sequence; + this->odometry_output_.header.stamp = ros::Time::now(); + + if(p_use_stm_integral_){ + this->odometry_output_.pose.pose.position.x = msg->angular.x; + this->odometry_output_.pose.pose.position.y = msg->angular.y; + + tf2::Quaternion quaternion; + quaternion.setRPY(0, 0, msg->linear.z); + + this->odometry_output_.pose.pose.orientation = tf2::toMsg(quaternion); + } + + this->odometry_output_.twist.twist.linear.x = msg->linear.x; + this->odometry_output_.twist.twist.linear.y = msg->linear.y; + this->odometry_output_.twist.twist.angular.z = msg->angular.z; + this->odometry_output_.pose.covariance[0] = 0.1; + this->odometry_output_.pose.covariance[7] = 0.1; + this->odometry_output_.pose.covariance[14] = 0.1; + this->odometry_output_.pose.covariance[21] = 0.1; + this->odometry_output_.pose.covariance[28] = 0.1; + this->odometry_output_.pose.covariance[35] = 0.1; + + if(this->p_publish_) this->publish(); + +} + + +void Odometry::P_VelocityCallback(const nav_msgs::Odometry::ConstPtr &msg){ + + double covariance_multi[3]; + + covariance_multi[0] = this->covariance_multi_[0] * abs(msg->twist.twist.linear.x); + covariance_multi[1] = this->covariance_multi_[1] * abs(msg->twist.twist.linear.y); + covariance_multi[2] = this->covariance_multi_[2] * abs(msg->twist.twist.angular.z); + + this->odometry_output_.twist.covariance[0] = std::max(0.00000001, covariance_multi[0] + this->odometry_output_backup_.twist.covariance[0]); + this->odometry_output_.twist.covariance[7] = std::max(0.00000001, covariance_multi[1] + this->odometry_output_backup_.twist.covariance[7]); + this->odometry_output_.twist.covariance[14] = std::max(0.00000001, covariance_multi[2]+ this->odometry_output_backup_.twist.covariance[14]); + this->odometry_output_.twist.covariance[21] = std::max(0.00000001, covariance_multi[0] + this->odometry_output_backup_.twist.covariance[21]); + this->odometry_output_.twist.covariance[28] = std::max(0.00000001, covariance_multi[1] + this->odometry_output_backup_.twist.covariance[28]); + this->odometry_output_.twist.covariance[35] = std::max(0.00000001, covariance_multi[2] + this->odometry_output_backup_.twist.covariance[35]); + +} + + +void Odometry::VelocityCallback(const nav_msgs::Odometry::ConstPtr &msg){ + + boost::shared_ptr odometry_ptr(new nav_msgs::Odometry()); + + odometry_ptr->twist.twist.linear = msg->twist.twist.linear; + odometry_ptr->twist.twist.angular = msg->twist.twist.angular; + + this->P_VelocityCallback(odometry_ptr); + +} + +void Odometry::publish(){ + + this->odom_pub_.publish(this->odometry_output_); + +} + +void Odometry::DynamicParamCallback(odometry::odometry_paramConfig &config, uint32_t level){ + + /* get param */ + if(p_publish_ != config.publish){ + this->p_publish_ = config.publish; + ROS_INFO_STREAM("[Odometry] : publish set to " << p_publish_); + } + + if(p_twist_topic_ != config.twist_topic){ + + this->p_twist_topic_ = config.twist_topic; + + if(p_active_){ + this->twist_sub_ = nh_.subscribe(p_twist_topic_, 10, &Odometry::TwistCallback, this); + } + + ROS_INFO_STREAM("[Odometry] : Current subscribe topic [ " << p_twist_topic_ << " ]"); + } + + if(p_odom_topic_ != config.odom_topic){ + + this->p_odom_topic_ = config.odom_topic; + + if(p_active_){ + this->odom_pub_ = nh_.advertise(p_odom_topic_, 10); + } + + ROS_INFO_STREAM("[Odometry] : Current publish topic [ " << p_odom_topic_ << " ]"); + } + + if(p_fixed_frame_ != config.fixed_frame){ + + this->p_fixed_frame_ = config.fixed_frame; + this->odometry_output_.header.frame_id = this->p_fixed_frame_; + + ROS_INFO_STREAM("[Odometry] : Current fixed frame [ " << p_fixed_frame_ << " ]"); + } + + if(p_target_frame_ != config.target_frame){ + + this->p_target_frame_ = config.target_frame; + this->odometry_output_.child_frame_id = this->p_target_frame_; + + ROS_INFO_STREAM("[Odometry] : Current target frame [ " << p_target_frame_ << " ]"); + } + + if(this->odometry_output_backup_.twist.covariance[0] != config.covariance_x){ + + this->odometry_output_backup_.twist.covariance[0] = config.covariance_x; + + ROS_INFO_STREAM("[Odometry] : x covariance set to " << this->odometry_output_backup_.twist.covariance[0]); + } + + if(this->odometry_output_backup_.twist.covariance[7] != config.covariance_y){ + + this->odometry_output_backup_.twist.covariance[7] = config.covariance_y; + + ROS_INFO_STREAM("[Odometry] : y covariance set to " << this->odometry_output_backup_.twist.covariance[7]); + } + + if(this->odometry_output_backup_.twist.covariance[14] != config.covariance_z){ + + this->odometry_output_backup_.twist.covariance[14] = config.covariance_z; + + ROS_INFO_STREAM("[Odometry] : z covariance set to " << this->odometry_output_backup_.twist.covariance[14]); + } + + if(this->odometry_output_backup_.twist.covariance[21] != config.covariance_vx){ + + this->odometry_output_backup_.twist.covariance[21] = config.covariance_vx; + + ROS_INFO_STREAM("[Odometry] : vx covariance set to " << this->odometry_output_backup_.twist.covariance[21]); + } + + if(this->odometry_output_backup_.twist.covariance[28] != config.covariance_vy){ + + this->odometry_output_backup_.twist.covariance[28] = config.covariance_vy; + + ROS_INFO_STREAM("[Odometry] : vy covariance set to " << this->odometry_output_backup_.twist.covariance[28]); + } + + if(this->odometry_output_backup_.twist.covariance[35] != config.covariance_vz){ + + this->odometry_output_backup_.twist.covariance[35] = config.covariance_vz; + + ROS_INFO_STREAM("[Odometry] : vz covariance set to " << this->odometry_output_backup_.twist.covariance[35]); + } + + if(this->covariance_multi_[0] != config.covariance_multi_vx){ + + this->covariance_multi_[0] = config.covariance_multi_vx; + + ROS_INFO_STREAM("[Odometry] : vx covariance multiplicant set to " << this->covariance_multi_[0]); + } + + if(this->covariance_multi_[1] != config.covariance_multi_vy){ + + this->covariance_multi_[1] = config.covariance_multi_vy; + + ROS_INFO_STREAM("[Odometry] : vy covariance multiplicant set to " << this->covariance_multi_[1]); + } + + if(this->covariance_multi_[2] != config.covariance_multi_vz){ + + this->covariance_multi_[2] = config.covariance_multi_vz; + + ROS_INFO_STREAM("[Odometry] : vz covariance multiplicant set to " << this->covariance_multi_[2]); + } + + this->odometry_output_.twist.covariance = this->odometry_output_backup_.twist.covariance; + +} + +void Odometry::SetDynamicReconfigure(){ + + static dynamic_reconfigure::Server dynamic_param_srv_; + + dynamic_reconfigure::Server::CallbackType callback; + + // If the function is a class member : + // boost::bind(&function, class instance, _1, _2) + callback = boost::bind(&Odometry::DynamicParamCallback, this, _1, _2); + + // Set callback function to param server + dynamic_param_srv_.setCallback(callback); +} diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/package.xml b/docker/testBot/OdomComm/docker/odometry/odometry/package.xml similarity index 89% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/package.xml rename to docker/testBot/OdomComm/docker/odometry/odometry/package.xml index 730e53a..6679d91 100644 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/package.xml +++ b/docker/testBot/OdomComm/docker/odometry/odometry/package.xml @@ -1,8 +1,8 @@ - local_filter + odometry 0.0.0 - The local_filter package + The odometry package @@ -19,7 +19,7 @@ - + @@ -37,13 +37,13 @@ - message_generation + - message_runtime + @@ -54,7 +54,7 @@ roscpp rospy std_msgs - obstacle_detector + dynamic_reconfigure geometry_msgs nav_msgs roscpp @@ -65,7 +65,7 @@ roscpp rospy std_msgs - obstacle_detector + dynamic_reconfigure diff --git a/docker/testBot/OdomComm/docker/odometry/odometry/param/odometry_comm.yaml b/docker/testBot/OdomComm/docker/odometry/odometry/param/odometry_comm.yaml new file mode 100644 index 0000000..3144dda --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/odometry/param/odometry_comm.yaml @@ -0,0 +1,32 @@ +# Active this node +active: true + +# Set publish to this node +publish: true + +# Open param service +update_params: false + +# Covariance for "const" part +covariance_x: 0.0005 +covariance_y: 0.0005 +covariance_z: 0.01 +covariance_vx: 0.0005 +covariance_vy: 0.0005 +covariance_vz: 0.02 + +# Covariance for "multiple" part +covariance_multi_vx: 0.05 +covariance_multi_vy: 0.05 +covariance_multi_vz: 0.05 + +# Dynamic adjustment from : + # True: (ns)/cmd_vel + # False: (ns)/final_pose +using_nav_vel_cb: false + +# Open if STM has integral information +use_stm_integral: false + +# Open Dynamic reconfigure service +using_dynamic_reconf: false \ No newline at end of file diff --git a/docker/testBot/OdomComm/docker/odometry/odometry/src/odometry_node.cpp b/docker/testBot/OdomComm/docker/odometry/odometry/src/odometry_node.cpp new file mode 100644 index 0000000..2500ea2 --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/odometry/src/odometry_node.cpp @@ -0,0 +1,29 @@ +#include "odometry/odometry.h" + +int main(int argc, char** argv){ + + ros::init(argc, argv, "odometry_node"); + + ros::NodeHandle nh(""); + ros::NodeHandle nh_local("~"); + + try { + + ROS_INFO_STREAM("[Odometry] : Initializing odometry"); + Odometry odometry(nh, nh_local); + ros::spin(); + + } + catch (const char* s) { + + ROS_FATAL_STREAM("[Odometry] : " << s); + + } + catch (...) { + + ROS_FATAL_STREAM("[Odometry] : Unexpected error"); + + } + + return 0; +} diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_msgs/CHANGELOG.rst b/docker/testBot/OdomComm/docker/odometry/rosserial_msgs/CHANGELOG.rst new file mode 100644 index 0000000..ee8c98d --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_msgs/CHANGELOG.rst @@ -0,0 +1,105 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package rosserial_msgs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +0.9.2 (2021-04-02) +------------------ + +0.9.1 (2020-09-09) +------------------ + +0.9.0 (2020-08-25) +------------------ +* Fix Travis for Noetic + Python 3 +* Bump minimum CMake version to 3.7.2 (Melodic). +* Drop separate node for message service (`#446 `_) +* Contributors: Mike Purvis + +0.8.0 (2018-10-11) +------------------ + +0.7.7 (2017-11-29) +------------------ +* Fix catkin lint errors (`#296 `_) +* Contributors: Bei Chen Liu + +0.7.6 (2017-03-01) +------------------ + +0.7.5 (2016-11-22) +------------------ + +0.7.4 (2016-09-21) +------------------ + +0.7.3 (2016-08-05) +------------------ + +0.7.2 (2016-07-15) +------------------ + +0.7.1 (2015-07-06) +------------------ + +0.7.0 (2015-04-23) +------------------ + +0.6.3 (2014-11-05) +------------------ + +0.6.2 (2014-09-10) +------------------ + +0.6.1 (2014-06-30) +------------------ + +0.6.0 (2014-06-11) +------------------ +* Uncomment ID_TX_STOP constant, per `#111 `_ +* Contributors: Mike Purvis + +0.5.6 (2014-06-11) +------------------ +* Add Mike Purvis as maintainer to all but xbee. +* remove ID_TX_STOP from rosserial_msgs/msg/TopicInfo.msg, using hardcode modification. fix the dupilcated registration problem of subscriber +* modified rosserial +* Contributors: Mike Purvis, Moju Zhao + +0.5.5 (2014-01-14) +------------------ + +0.5.4 (2013-10-17) +------------------ + +0.5.3 (2013-09-21) +------------------ +* Add message info service and stub of node to provide it, for +rosserial_server support. + +0.5.2 (2013-07-17) +------------------ + +* Fix release version + +0.5.1 (2013-07-15) +------------------ + +0.4.5 (2013-07-02) +------------------ + +0.4.4 (2013-03-20) +------------------ + +0.4.3 (2013-03-13 14:08) +------------------------ + +0.4.2 (2013-03-13 01:15) +------------------------ + +0.4.1 (2013-03-09) +------------------ + +0.4.0 (2013-03-08) +------------------ +* Changed DEBUG log level to ROSDEBUG. +* initial catkin version on github diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_msgs/CMakeLists.txt b/docker/testBot/OdomComm/docker/odometry/rosserial_msgs/CMakeLists.txt new file mode 100644 index 0000000..c525612 --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_msgs/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.7.2) +project(rosserial_msgs) + +find_package(catkin REQUIRED COMPONENTS + message_generation +) + +add_message_files(FILES + Log.msg + TopicInfo.msg +) + +add_service_files(FILES + RequestParam.srv +) + +generate_messages() + +catkin_package(CATKIN_DEPENDS + message_runtime +) diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_msgs/msg/Log.msg b/docker/testBot/OdomComm/docker/odometry/rosserial_msgs/msg/Log.msg new file mode 100644 index 0000000..7239876 --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_msgs/msg/Log.msg @@ -0,0 +1,10 @@ + +#ROS Logging Levels +uint8 ROSDEBUG=0 +uint8 INFO=1 +uint8 WARN=2 +uint8 ERROR=3 +uint8 FATAL=4 + +uint8 level +string msg diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_msgs/msg/TopicInfo.msg b/docker/testBot/OdomComm/docker/odometry/rosserial_msgs/msg/TopicInfo.msg new file mode 100644 index 0000000..dafd6b0 --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_msgs/msg/TopicInfo.msg @@ -0,0 +1,21 @@ +# special topic_ids +uint16 ID_PUBLISHER=0 +uint16 ID_SUBSCRIBER=1 +uint16 ID_SERVICE_SERVER=2 +uint16 ID_SERVICE_CLIENT=4 +uint16 ID_PARAMETER_REQUEST=6 +uint16 ID_LOG=7 +uint16 ID_TIME=10 +uint16 ID_TX_STOP=11 + +# The endpoint ID for this topic +uint16 topic_id + +string topic_name +string message_type + +# MD5 checksum for this message type +string md5sum + +# size of the buffer message must fit in +int32 buffer_size diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_msgs/package.xml b/docker/testBot/OdomComm/docker/odometry/rosserial_msgs/package.xml new file mode 100644 index 0000000..9f8ac6d --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_msgs/package.xml @@ -0,0 +1,18 @@ + + rosserial_msgs + 0.9.2 + + Messages for automatic topic configuration using rosserial. + + Michael Ferguson + Paul Bouchier + Mike Purvis + BSD + http://ros.org/wiki/rosserial_msgs + + catkin + + message_generation + + message_runtime + diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_msgs/srv/RequestParam.srv b/docker/testBot/OdomComm/docker/odometry/rosserial_msgs/srv/RequestParam.srv new file mode 100644 index 0000000..ca605e8 --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_msgs/srv/RequestParam.srv @@ -0,0 +1,7 @@ +string name + +--- + +int32[] ints +float32[] floats +string[] strings diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_server/CHANGELOG.rst b/docker/testBot/OdomComm/docker/odometry/rosserial_server/CHANGELOG.rst new file mode 100644 index 0000000..e8d8efd --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_server/CHANGELOG.rst @@ -0,0 +1,130 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package rosserial_server +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +0.9.2 (2021-04-02) +------------------ +* Add python3-dev as build depend (`#544 `_) +* Contributors: Tobias Fischer + +0.9.1 (2020-09-09) +------------------ +* Add boost::thread dependency to rosserial_server (`#513 `_) +* Contributors: Mike Purvis + +0.9.0 (2020-08-25) +------------------ +* Only initialize embedded python interpreter once. (`#491 `_) +* Port 482 and 483 forward from Melodic branch (`#492 `_) +* Fix warning when using std_msgs/Empty (`#482 `_) +* Bump minimum CMake version to 3.7.2 (Melodic). +* Removed unused service client for message info service (`#481 `_) +* Call io_service.stop() when ros::ok() returns false (`#477 `_) +* Call Py_Finalize before throwing exception (`#476 `_) +* [Windows] use c++ signed trait to replace ssize_t for better portability. (`#463 `_) +* Port rosserial_server to Boost 1.71. (`#468 `_) +* rosserial_server: update install rules for binary targets (`#457 `_) +* Fix bug: assign the md5 for service (`#449 `_) +* Contributors: Hermann von Kleist, Johannes Meyer, Mike Purvis, Sean Yen, 趙 漠居(Zhao, Moju) + +0.8.0 (2018-10-11) +------------------ +* Fix compiling on boost > 1.66 (`#362 `_) + Reflective of changes made to boost::asio noted here: + http://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/net_ts.html +* Contributors: Fan Jiang + +0.7.7 (2017-11-29) +------------------ +* Fix catkin lint errors (`#296 `_) +* Contributors: Bei Chen Liu + +0.7.6 (2017-03-01) +------------------ + +0.7.5 (2016-11-22) +------------------ +* Fixing build errors for boost >=1.60 (`#226 `_) (`#250 `_) +* Contributors: Malte Splietker + +0.7.4 (2016-09-21) +------------------ +* Use catkin_EXPORTED_TARGETS to avoid CMake warning (`#246 `_) +* Fix AsyncReadBuffer for UDP socket case. (`#245 `_) +* Contributors: Mike Purvis + +0.7.3 (2016-08-05) +------------------ +* Avoid runaway async condition when port is bad. (`#236 `_) +* Add missing install rule for udp_socket_node +* Make the ~require param configurable from Session. (`#233 `_) +* Contributors: Mike Purvis + +0.7.2 (2016-07-15) +------------------ +* Implementation of native UDP rosserial server. (`#231 `_) +* Explicit session lifecycle for the serial server. (`#228 `_) + This is a long overdue change which will resolve some crashes when + USB serial devices return error states in the face of noise or other + interruptions. +* Support for VER1 protocol has been dropped. +* Handle log messages in rosserial_server +* Contributors: Mike Purvis, mkrauter + +0.7.1 (2015-07-06) +------------------ + +0.7.0 (2015-04-23) +------------------ +* Fill out description field in package.xml. +* Bugfix for checksum. + Publishing topics fails when messages are over 256 bytes in length due to checksum() function or'ing high and low byte instead of adding them. +* rosserial_server: Properly receive messages > 255 bytes. +* Contributors: Chad Attermann, Mike Purvis + +0.6.3 (2014-11-05) +------------------ +* Add more log output, don't end the session for certain write errors. +* Contributors: Mike Purvis + +0.6.2 (2014-09-10) +------------------ +* Bugfix for interrupted sessions. + This is a two-part fix for an issue causes a segfault when the device + disappears during operation, for example a ttyACM device which is unplugged. + The AsyncReadBuffer part avoids calling a callback after the object + owning it has destructed, and the SerialSession part avoids recreating + itself until the previous instance has finished the destructor and been + full destroyed. +* Add dependency on rosserial_msgs_gencpp, fixes `#133 `_ +* Make ServiceClient::handle public, to fix compilation bug on some platforms. +* Enabled registration of service clients +* Add namespaces to headers, swap ROS thread to foreground. +* Move headers to include path, rename to follow ROS style. + +0.6.1 (2014-06-30) +------------------ + +0.6.0 (2014-06-11) +------------------ + +0.5.6 (2014-06-11) +------------------ +* Fixed build error due to variable being read as a function due to missing parenthesis +* Add rosserial_python as dependency of rosserial_server +* Contributors: Mike Purvis, spaghetti- + +0.5.5 (2014-01-14) +------------------ +* Add support for require/publishers and require/subscribers parameters. +* Use stream logging in rosserial_server + +0.5.4 (2013-10-17) +------------------ + +0.5.3 (2013-09-21) +------------------ +* New package: rosserial_server +* Contains example launch file for serial configuration of server +* Working now with both Groovy and Hydro clients. +* Subscriber to correctly declare known md5 and topic type from client. diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_server/CMakeLists.txt b/docker/testBot/OdomComm/docker/odometry/rosserial_server/CMakeLists.txt new file mode 100644 index 0000000..6e57211 --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_server/CMakeLists.txt @@ -0,0 +1,77 @@ +cmake_minimum_required(VERSION 3.7.2) +project(rosserial_server) + +find_package(catkin REQUIRED COMPONENTS + roscpp + rosserial_msgs + std_msgs + topic_tools +) + +find_package(Boost REQUIRED COMPONENTS + system + thread +) + +find_package(PythonLibs REQUIRED) + +catkin_package( + INCLUDE_DIRS include + CATKIN_DEPENDS + roscpp + rosserial_msgs + std_msgs + topic_tools + LIBRARIES ${PROJECT_NAME}_lookup +) + +include_directories( + include + ${Boost_INCLUDE_DIRS} + ${catkin_INCLUDE_DIRS} +) + +add_library(${PROJECT_NAME}_lookup src/msg_lookup.cpp) +target_link_libraries(${PROJECT_NAME}_lookup ${PYTHON_LIBRARY}) +target_include_directories(${PROJECT_NAME}_lookup SYSTEM PRIVATE ${PYTHON_INCLUDE_DIR}) + +add_executable(${PROJECT_NAME}_serial_node src/serial_node.cpp) +target_link_libraries(${PROJECT_NAME}_serial_node ${catkin_LIBRARIES} ${PROJECT_NAME}_lookup) +set_target_properties(${PROJECT_NAME}_serial_node PROPERTIES OUTPUT_NAME serial_node PREFIX "") +add_dependencies(${PROJECT_NAME}_serial_node ${catkin_EXPORTED_TARGETS}) + +add_executable(${PROJECT_NAME}_socket_node src/socket_node.cpp) +target_link_libraries(${PROJECT_NAME}_socket_node ${catkin_LIBRARIES} ${PROJECT_NAME}_lookup) +set_target_properties(${PROJECT_NAME}_socket_node PROPERTIES OUTPUT_NAME socket_node PREFIX "") +add_dependencies(${PROJECT_NAME}_socket_node ${catkin_EXPORTED_TARGETS}) + +add_executable(${PROJECT_NAME}_udp_socket_node src/udp_socket_node.cpp) +target_link_libraries(${PROJECT_NAME}_udp_socket_node ${catkin_LIBRARIES} ${PROJECT_NAME}_lookup) +set_target_properties(${PROJECT_NAME}_udp_socket_node PROPERTIES OUTPUT_NAME udp_socket_node PREFIX "") +add_dependencies(${PROJECT_NAME}_udp_socket_node ${catkin_EXPORTED_TARGETS}) + +install( + TARGETS + ${PROJECT_NAME}_lookup + ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} + LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} + RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION} +) + +install( + TARGETS + ${PROJECT_NAME}_serial_node + ${PROJECT_NAME}_socket_node + ${PROJECT_NAME}_udp_socket_node + RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +) + +install( + DIRECTORY launch + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} +) + +install( + DIRECTORY include/${PROJECT_NAME}/ + DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} +) diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/async_read_buffer.h b/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/async_read_buffer.h new file mode 100644 index 0000000..586fcae --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/async_read_buffer.h @@ -0,0 +1,204 @@ +/** + * + * \file + * \brief Helper object for successive reads from a ReadStream. + * \author Mike Purvis + * \copyright Copyright (c) 2013, Clearpath Robotics, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Clearpath Robotics, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CLEARPATH ROBOTICS, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN 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. + * + * Please send comments, questions, or patches to code@clearpathrobotics.com + * + */ + +#ifndef ROSSERIAL_SERVER_ASYNC_READ_BUFFER_H +#define ROSSERIAL_SERVER_ASYNC_READ_BUFFER_H + +#include +#include +#include + +#include + +// ssize_t is POSIX-only type. Use make_signed for portable code. +#include // size_t +#include // std::make_signed +typedef std::make_signed::type signed_size_t; + +namespace rosserial_server +{ + +template +class AsyncReadBuffer +{ +public: + AsyncReadBuffer(AsyncReadStream& s, size_t capacity, + boost::function error_callback) + : stream_(s), read_requested_bytes_(0), error_callback_(error_callback) { + reset(); + mem_.resize(capacity); + ROS_ASSERT_MSG(error_callback_, "Bad error callback passed to read buffer."); + } + + /** + * @brief Commands a fixed number of bytes from the buffer. This may be fulfilled from existing + * buffer content, or following a hardware read if required. + */ + void read(size_t requested_bytes, boost::function callback) { + ROS_DEBUG_STREAM_NAMED("async_read", "Buffer read of " << requested_bytes << " bytes, " << + "wi: " << write_index_ << ", ri: " << read_index_); + + ROS_ASSERT_MSG(read_requested_bytes_ == 0, "Bytes requested is nonzero, is there an operation already pending?"); + ROS_ASSERT_MSG(callback, "Bad read success callback function."); + read_success_callback_ = callback; + read_requested_bytes_ = requested_bytes; + + if (read_requested_bytes_ > mem_.size()) + { + // Insufficient room in the buffer for the requested bytes, + ROS_ERROR_STREAM_NAMED("async_read", "Requested to read " << read_requested_bytes_ << + " bytes, but buffer capacity is only " << mem_.size() << "."); + error_callback_(boost::system::errc::make_error_code(boost::system::errc::no_buffer_space)); + return; + } + + // Number of bytes which must be transferred to satisfy the request. + signed_size_t transfer_bytes = read_requested_bytes_ - bytesAvailable(); + + if (transfer_bytes > 0) + { + // If we don't have enough headroom in the buffer, we'll have to shift what's currently in there to make room. + if (bytesHeadroom() < transfer_bytes) + { + memmove(&mem_[0], &mem_[read_index_], bytesAvailable()); + write_index_ = bytesAvailable(); + read_index_ = 0; + } + + // Initiate a read from hardware so that we have enough bytes to fill the user request. + ROS_DEBUG_STREAM_NAMED("async_read", "Requesting transfer of at least " << transfer_bytes << " byte(s)."); + boost::asio::async_read(stream_, + boost::asio::buffer(&mem_[write_index_], bytesHeadroom()), + boost::asio::transfer_at_least(transfer_bytes), + boost::bind(&AsyncReadBuffer::callback, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + else + { + // We have enough in the buffer already, can fill the request without going to hardware. + callSuccessCallback(); + } + } + +private: + void reset() + { + read_index_ = 0; + write_index_ = 0; + } + + inline size_t bytesAvailable() + { + return write_index_ - read_index_; + } + + inline size_t bytesHeadroom() + { + return mem_.size() - write_index_; + } + + /** + * @brief The internal callback which is called by the boost::asio::async_read invocation + * in the public read method above. + */ + void callback(const boost::system::error_code& error, size_t bytes_transferred) + { + if (error) + { + read_requested_bytes_ = 0; + read_success_callback_.clear(); + ROS_DEBUG_STREAM_NAMED("async_read", "Read operation failed with: " << error); + + if (error == boost::asio::error::operation_aborted) + { + // Special case for operation_aborted. The abort callback comes when the owning Session + // is in the middle of teardown, which means the callback is no longer valid. + } + else + { + error_callback_(error); + } + return; + } + + write_index_ += bytes_transferred; + ROS_DEBUG_STREAM_NAMED("async_read", "Successfully read " << bytes_transferred << " byte(s), now " << bytesAvailable() << " available."); + callSuccessCallback(); + } + + /** + * @brief Calls the user's callback. This is a separate function because it gets called from two + * places, depending whether or not an actual HW read is required to fill the request. + */ + void callSuccessCallback() + { + ROS_DEBUG_STREAM_NAMED("async_read", "Invoking success callback with buffer of requested size " << + read_requested_bytes_ << " byte(s)."); + + ros::serialization::IStream stream(&mem_[read_index_], read_requested_bytes_); + read_index_ += read_requested_bytes_; + + // Post the callback rather than executing it here so, so that we have a chance to do the cleanup + // below prior to it actually getting run, in the event that the callback queues up another read. +#if BOOST_VERSION >= 107000 + boost::asio::post(stream_.get_executor(), boost::bind(read_success_callback_, stream)); +#else + stream_.get_io_service().post(boost::bind(read_success_callback_, stream)); +#endif + + // Resetting these values clears our state so that we know there isn't a callback pending. + read_requested_bytes_ = 0; + read_success_callback_.clear(); + + if (bytesAvailable() == 0) + { + ROS_DEBUG_STREAM_NAMED("async_read", "Buffer is empty, resetting indexes to the beginning."); + reset(); + } + } + + AsyncReadStream& stream_; + std::vector mem_; + + size_t write_index_; + size_t read_index_; + boost::function error_callback_; + + boost::function read_success_callback_; + size_t read_requested_bytes_; +}; + +} // namespace + +#endif // ROSSERIAL_SERVER_ASYNC_READ_BUFFER_H diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/msg_lookup.h b/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/msg_lookup.h new file mode 100644 index 0000000..28e60b9 --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/msg_lookup.h @@ -0,0 +1,44 @@ +/** + * \author Mike Purvis + * \copyright Copyright (c) 2019, Clearpath Robotics, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Clearpath Robotics, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CLEARPATH ROBOTICS, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN 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. + * + * Please send comments, questions, or patches to code@clearpathrobotics.com + */ + +#include +#include + +namespace rosserial_server +{ + +struct MsgInfo +{ + std::string md5sum; + std::string full_text; +}; + +const MsgInfo lookupMessage(const std::string& message_type, const std::string submodule = "msg"); + +} // namespace rosserial_server diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/serial_session.h b/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/serial_session.h new file mode 100644 index 0000000..2a0bc15 --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/serial_session.h @@ -0,0 +1,118 @@ +/** + * + * \file + * \brief Single, reconnecting class for a serial rosserial session. + * \author Mike Purvis + * \copyright Copyright (c) 2013, Clearpath Robotics, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Clearpath Robotics, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CLEARPATH ROBOTICS, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN 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. + * + * Please send comments, questions, or patches to code@clearpathrobotics.com + * + */ + +#ifndef ROSSERIAL_SERVER_SERIAL_SESSION_H +#define ROSSERIAL_SERVER_SERIAL_SESSION_H + +#include +#include +#include + +#include + +#include "rosserial_server/session.h" + +namespace rosserial_server +{ + +class SerialSession : public Session +{ +public: + SerialSession(boost::asio::io_service& io_service, std::string port, int baud) + : Session(io_service), port_(port), baud_(baud), timer_(io_service) + { + ROS_INFO_STREAM("rosserial_server session configured for " << port_ << " at " << baud << "bps."); + + failed_connection_attempts_ = 0; + check_connection(); + } + +private: + void check_connection() + { + if (!is_active()) + { + attempt_connection(); + } + + // Every second, check again if the connection should be reinitialized, + // if the ROS node is still up. + if (ros::ok()) + { + timer_.expires_from_now(boost::posix_time::milliseconds(2000)); + timer_.async_wait(boost::bind(&SerialSession::check_connection, this)); + } + else + { + shutdown(); + } + } + + void attempt_connection() + { + ROS_DEBUG("Opening serial port."); + + boost::system::error_code ec; + socket().open(port_, ec); + if (ec) { + failed_connection_attempts_++; + if (failed_connection_attempts_ == 1) { + ROS_ERROR_STREAM("Unable to open port " << port_ << ": " << ec); + } else { + ROS_DEBUG_STREAM("Unable to open port " << port_ << " (" << failed_connection_attempts_ << "): " << ec); + } + return; + } + ROS_INFO_STREAM("Opened " << port_); + failed_connection_attempts_ = 0; + + typedef boost::asio::serial_port_base serial; + socket().set_option(serial::baud_rate(baud_)); + socket().set_option(serial::character_size(8)); + socket().set_option(serial::stop_bits(serial::stop_bits::one)); + socket().set_option(serial::parity(serial::parity::none)); + socket().set_option(serial::flow_control(serial::flow_control::none)); + + // Kick off the session. + start(); + } + + std::string port_; + int baud_; + boost::asio::deadline_timer timer_; + int failed_connection_attempts_; +}; + +} // namespace + +#endif // ROSSERIAL_SERVER_SERIAL_SESSION_H diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/session.h b/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/session.h new file mode 100644 index 0000000..e7f8ab7 --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/session.h @@ -0,0 +1,625 @@ +/** + * + * \file + * \brief Class representing a session between this node and a + * templated rosserial client. + * \author Mike Purvis + * \copyright Copyright (c) 2013, Clearpath Robotics, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Clearpath Robotics, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CLEARPATH ROBOTICS, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN 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. + * + * Please send comments, questions, or patches to code@clearpathrobotics.com + * + */ + +#ifndef ROSSERIAL_SERVER_SESSION_H +#define ROSSERIAL_SERVER_SESSION_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "rosserial_server/async_read_buffer.h" +#include "rosserial_server/topic_handlers.h" + +namespace rosserial_server +{ + + typedef std::vector Buffer; + typedef boost::shared_ptr BufferPtr; + + template + class Session : boost::noncopyable + { + public: + Session(boost::asio::io_service &io_service) + : io_service_(io_service), + socket_(io_service), + sync_timer_(io_service), + require_check_timer_(io_service), + ros_spin_timer_(io_service), + async_read_buffer_(socket_, buffer_max, + boost::bind(&Session::read_failed, this, + boost::asio::placeholders::error)) + { + active_ = false; + + timeout_interval_ = boost::posix_time::milliseconds(5000); + attempt_interval_ = boost::posix_time::milliseconds(1000); + require_check_interval_ = boost::posix_time::milliseconds(1000); + ros_spin_interval_ = boost::posix_time::milliseconds(10); + require_param_name_ = "~require"; + + unrecognised_topic_retry_threshold_ = ros::param::param("~unrecognised_topic_retry_threshold", 0); + + nh_.setCallbackQueue(&ros_callback_queue_); + + // Intermittent callback to service ROS callbacks. To avoid polling like this, + // CallbackQueue could in the future be extended with a scheme to monitor for + // callbacks on another thread, and then queue them up to be executed on this one. + ros_spin_timer_.expires_from_now(ros_spin_interval_); + ros_spin_timer_.async_wait(boost::bind(&Session::ros_spin_timeout, this, + boost::asio::placeholders::error)); + } + + Socket &socket() + { + return socket_; + } + + void start() + { + ROS_DEBUG("Starting session."); + + callbacks_[rosserial_msgs::TopicInfo::ID_PUBLISHER] = boost::bind(&Session::setup_publisher, this, _1); + callbacks_[rosserial_msgs::TopicInfo::ID_SUBSCRIBER] = boost::bind(&Session::setup_subscriber, this, _1); + callbacks_[rosserial_msgs::TopicInfo::ID_SERVICE_CLIENT + rosserial_msgs::TopicInfo::ID_PUBLISHER] = boost::bind(&Session::setup_service_client_publisher, this, _1); + callbacks_[rosserial_msgs::TopicInfo::ID_SERVICE_CLIENT + rosserial_msgs::TopicInfo::ID_SUBSCRIBER] = boost::bind(&Session::setup_service_client_subscriber, this, _1); + callbacks_[rosserial_msgs::TopicInfo::ID_LOG] = boost::bind(&Session::handle_log, this, _1); + callbacks_[rosserial_msgs::TopicInfo::ID_TIME] = boost::bind(&Session::handle_time, this, _1); + + active_ = true; + attempt_sync(); + read_sync_header(); + } + + void stop() + { + std::cout << "Sending Tx Stop Request..." << std::endl; + uint8_t overhead_bytes = 8; + uint16_t length = overhead_bytes; + BufferPtr buffer_ptr(new Buffer(length)); + ros::serialization::OStream stream(&buffer_ptr->at(0), buffer_ptr->size()); + stream << (uint16_t)0xfeff << (uint16_t)0x0000 << (uint16_t)0x0bff << (uint16_t)0xf400; + boost::asio::async_write(socket_, boost::asio::buffer(*buffer_ptr), + boost::bind(&Session::write_completion_cb, this, boost::asio::placeholders::error, buffer_ptr)); + std::cout << "Stop..." << std::endl; + + // Abort any pending ROS callbacks. + ros_callback_queue_.clear(); + + // Abort active session timer callbacks, if present. + sync_timer_.cancel(); + require_check_timer_.cancel(); + + // Reset the state of the session, dropping any publishers or subscribers + // we currently know about from this client. + callbacks_.clear(); + subscribers_.clear(); + publishers_.clear(); + services_.clear(); + + // Close the socket. + socket_.close(); + active_ = false; + } + + void shutdown() + { + if (is_active()) + { + stop(); + } + io_service_.stop(); + } + + bool is_active() + { + return active_; + } + + /** + * This is to set the name of the required topics parameter from the + * default of ~require. You might want to do this to avoid a conflict + * with something else in that namespace, or because you're embedding + * multiple instances of rosserial_server in a single process. + */ + void set_require_param(std::string param_name) + { + require_param_name_ = param_name; + } + + private: + /** + * Periodic function which handles calling ROS callbacks, executed on the same + * io_service thread to avoid a concurrency nightmare. + */ + void ros_spin_timeout(const boost::system::error_code &error) + { + ros_callback_queue_.callAvailable(); + + if (ros::ok()) + { + // Call again next interval. + ros_spin_timer_.expires_from_now(ros_spin_interval_); + ros_spin_timer_.async_wait(boost::bind(&Session::ros_spin_timeout, this, + boost::asio::placeholders::error)); + } + else + { + shutdown(); + } + } + + //// RECEIVING MESSAGES //// + // TODO: Total message timeout, implement primarily in ReadBuffer. + + void read_sync_header() + { + async_read_buffer_.read(1, boost::bind(&Session::read_sync_first, this, _1)); + } + + void read_sync_first(ros::serialization::IStream &stream) + { + uint8_t sync; + stream >> sync; + if (sync == 0xff) + { + async_read_buffer_.read(1, boost::bind(&Session::read_sync_second, this, _1)); + } + else + { + read_sync_header(); + } + } + + void read_sync_second(ros::serialization::IStream &stream) + { + uint8_t sync; + stream >> sync; + if (sync == 0xfe) + { + async_read_buffer_.read(5, boost::bind(&Session::read_id_length, this, _1)); + } + else + { + read_sync_header(); + } + } + + void read_id_length(ros::serialization::IStream &stream) + { + uint16_t topic_id, length; + uint8_t length_checksum; + + // Check header checksum byte for length field. + stream >> length >> length_checksum; + if (length_checksum + checksum(length) != 0xff) + { + uint8_t csl = checksum(length); + ROS_WARN("Bad message header length checksum. Dropping message from client. T%d L%d C%d %d", topic_id, length, length_checksum, csl); + read_sync_header(); + return; + } + else + { + stream >> topic_id; + } + ROS_DEBUG("Received message header with length %d and topic_id=%d", length, topic_id); + + // Read message length + checksum byte. + async_read_buffer_.read(length + 1, boost::bind(&Session::read_body, this, + _1, topic_id)); + } + + void read_body(ros::serialization::IStream &stream, uint16_t topic_id) + { + ROS_DEBUG("Received body of length %d for message on topic %d.", stream.getLength(), topic_id); + + ros::serialization::IStream checksum_stream(stream.getData(), stream.getLength()); + uint8_t msg_checksum = checksum(checksum_stream) + checksum(topic_id); + + if (msg_checksum != 0xff) + { + ROS_WARN("Rejecting message on topicId=%d, length=%d with bad checksum.", topic_id, stream.getLength()); + } + else + { + if (callbacks_.count(topic_id) == 1) + { + try + { + // stream includes the check sum byte. + ros::serialization::IStream msg_stream(stream.getData(), stream.getLength() - 1); + callbacks_[topic_id](msg_stream); + } + catch (ros::serialization::StreamOverrunException e) + { + if (topic_id < 100) + { + ROS_ERROR("Buffer overrun when attempting to parse setup message."); + ROS_ERROR_ONCE("Is this firmware from a pre-Groovy rosserial?"); + } + else + { + ROS_WARN("Buffer overrun when attempting to parse user message."); + } + } + } + else + { + ROS_WARN("Received message with unrecognized topicId (%d).", topic_id); + + if ((unrecognised_topic_retry_threshold_ > 0) && ++unrecognised_topics_ >= unrecognised_topic_retry_threshold_) + { + // The threshold for unrecognised topics has been exceeded. + // Attempt to request the topics from the client again + ROS_WARN("Unrecognised topic threshold exceeded. Requesting topics from client"); + attempt_sync(); + unrecognised_topics_ = 0; + } + } + } + + // Kickoff next message read. + read_sync_header(); + } + + void read_failed(const boost::system::error_code &error) + { + if (error == boost::system::errc::no_buffer_space) + { + // No worries. Begin syncing on a new message. + ROS_WARN("Overrun on receive buffer. Attempting to regain rx sync."); + read_sync_header(); + } + else if (error) + { + // When some other read error has occurred, stop the session, which destroys + // all known publishers and subscribers. + ROS_WARN_STREAM("Socket asio error, closing socket: " << error); + stop(); + } + } + + //// SENDING MESSAGES //// + + void write_message(Buffer &message, const uint16_t topic_id) + { + uint8_t overhead_bytes = 8; + uint16_t length = overhead_bytes + message.size(); + BufferPtr buffer_ptr(new Buffer(length)); + + uint8_t msg_checksum; + ros::serialization::IStream checksum_stream(message.size() > 0 ? &message[0] : NULL, message.size()); + + ros::serialization::OStream stream(&buffer_ptr->at(0), buffer_ptr->size()); + uint8_t msg_len_checksum = 255 - checksum(message.size()); + stream << (uint16_t)0xfeff << (uint16_t)message.size() << msg_len_checksum << topic_id; + msg_checksum = 255 - (checksum(checksum_stream) + checksum(topic_id)); + + memcpy(stream.advance(message.size()), &message[0], message.size()); + stream << msg_checksum; + + ROS_DEBUG_NAMED("async_write", "Sending buffer of %d bytes to client.", length); + boost::asio::async_write(socket_, boost::asio::buffer(*buffer_ptr), + boost::bind(&Session::write_completion_cb, this, boost::asio::placeholders::error, buffer_ptr)); + } + + void write_completion_cb(const boost::system::error_code &error, + BufferPtr buffer_ptr) + { + if (error) + { + if (error == boost::system::errc::io_error) + { + ROS_WARN_THROTTLE(1, "Socket write operation returned IO error."); + } + else if (error == boost::system::errc::no_such_device) + { + ROS_WARN_THROTTLE(1, "Socket write operation returned no device."); + } + else + { + ROS_WARN_STREAM_THROTTLE(1, "Unknown error returned during write operation: " << error); + } + stop(); + } + // Buffer is destructed when this function exits and buffer_ptr goes out of scope. + } + + //// SYNC WATCHDOG //// + void attempt_sync() + { + request_topics(); + set_sync_timeout(attempt_interval_); + } + + void set_sync_timeout(const boost::posix_time::time_duration &interval) + { + if (ros::ok()) + { + sync_timer_.cancel(); + sync_timer_.expires_from_now(interval); + sync_timer_.async_wait(boost::bind(&Session::sync_timeout, this, + boost::asio::placeholders::error)); + } + else + { + shutdown(); + } + } + + void sync_timeout(const boost::system::error_code &error) + { + if (error != boost::asio::error::operation_aborted) + { + ROS_DEBUG("Sync with device lost."); + stop(); + } + } + + //// HELPERS //// + void request_topics() + { + std::vector message(0); + ROS_DEBUG("Sending request topics message for VER2 protocol."); + write_message(message, rosserial_msgs::TopicInfo::ID_PUBLISHER); + + // Set timer for future point at which to verify the subscribers and publishers + // created by the client against the expected set given in the parameters. + require_check_timer_.expires_from_now(require_check_interval_); + require_check_timer_.async_wait(boost::bind(&Session::required_topics_check, this, + boost::asio::placeholders::error)); + } + + void required_topics_check(const boost::system::error_code &error) + { + if (error != boost::asio::error::operation_aborted) + { + if (ros::param::has(require_param_name_)) + { + if (!check_set(require_param_name_ + "/publishers", publishers_) || + !check_set(require_param_name_ + "/subscribers", subscribers_)) + { + ROS_WARN("Connected client failed to establish the publishers and subscribers dictated by require parameter. Re-requesting topics."); + request_topics(); + } + } + } + } + + template + bool check_set(std::string param_name, M map) + { + XmlRpc::XmlRpcValue param_list; + ros::param::get(param_name, param_list); + ROS_ASSERT(param_list.getType() == XmlRpc::XmlRpcValue::TypeArray); + for (int i = 0; i < param_list.size(); ++i) + { + ROS_ASSERT(param_list[i].getType() == XmlRpc::XmlRpcValue::TypeString); + std::string required_topic((std::string(param_list[i]))); + // Iterate through map of registered topics, to ensure that this one is present. + bool found = false; + for (typename M::iterator j = map.begin(); j != map.end(); ++j) + { + if (nh_.resolveName(j->second->get_topic()) == + nh_.resolveName(required_topic)) + { + found = true; + ROS_INFO_STREAM("Verified connection to topic " << required_topic << ", given in parameter " << param_name); + break; + } + } + if (!found) + { + ROS_WARN_STREAM("Missing connection to topic " << required_topic << ", required by parameter " << param_name); + return false; + } + } + return true; + } + + static uint8_t checksum(ros::serialization::IStream &stream) + { + uint8_t sum = 0; + for (uint16_t i = 0; i < stream.getLength(); ++i) + { + sum += stream.getData()[i]; + } + return sum; + } + + static uint8_t checksum(uint16_t val) + { + return (val >> 8) + val; + } + + //// RECEIVED MESSAGE HANDLERS //// + + void setup_publisher(ros::serialization::IStream &stream) + { + rosserial_msgs::TopicInfo topic_info; + ros::serialization::Serializer::read(stream, topic_info); + + PublisherPtr pub(new Publisher(nh_, topic_info)); + callbacks_[topic_info.topic_id] = boost::bind(&Publisher::handle, pub, _1); + publishers_[topic_info.topic_id] = pub; + + set_sync_timeout(timeout_interval_); + } + + void setup_subscriber(ros::serialization::IStream &stream) + { + rosserial_msgs::TopicInfo topic_info; + ros::serialization::Serializer::read(stream, topic_info); + + SubscriberPtr sub(new Subscriber(nh_, topic_info, + boost::bind(&Session::write_message, this, _1, topic_info.topic_id))); + subscribers_[topic_info.topic_id] = sub; + + set_sync_timeout(timeout_interval_); + } + + // When the rosserial client creates a ServiceClient object (and/or when it registers that object with the NodeHandle) + // it creates a publisher (to publish the service request message to us) and a subscriber (to receive the response) + // the service client callback is attached to the *subscriber*, so when we receive the service response + // and wish to send it over the socket to the client, + // we must attach the topicId that came from the service client subscriber message + + void setup_service_client_publisher(ros::serialization::IStream &stream) + { + rosserial_msgs::TopicInfo topic_info; + ros::serialization::Serializer::read(stream, topic_info); + + if (!services_.count(topic_info.topic_name)) + { + ROS_DEBUG("Creating service client for topic %s", topic_info.topic_name.c_str()); + ServiceClientPtr srv(new ServiceClient( + nh_, topic_info, boost::bind(&Session::write_message, this, _1, _2))); + services_[topic_info.topic_name] = srv; + callbacks_[topic_info.topic_id] = boost::bind(&ServiceClient::handle, srv, _1); + } + if (services_[topic_info.topic_name]->getRequestMessageMD5() != topic_info.md5sum) + { + ROS_WARN("Service client setup: Request message MD5 mismatch between rosserial client and ROS"); + } + else + { + ROS_DEBUG("Service client %s: request message MD5 successfully validated as %s", + topic_info.topic_name.c_str(), topic_info.md5sum.c_str()); + } + set_sync_timeout(timeout_interval_); + } + + void setup_service_client_subscriber(ros::serialization::IStream &stream) + { + rosserial_msgs::TopicInfo topic_info; + ros::serialization::Serializer::read(stream, topic_info); + + if (!services_.count(topic_info.topic_name)) + { + ROS_DEBUG("Creating service client for topic %s", topic_info.topic_name.c_str()); + ServiceClientPtr srv(new ServiceClient( + nh_, topic_info, boost::bind(&Session::write_message, this, _1, _2))); + services_[topic_info.topic_name] = srv; + callbacks_[topic_info.topic_id] = boost::bind(&ServiceClient::handle, srv, _1); + } + // see above comment regarding the service client callback for why we set topic_id here + services_[topic_info.topic_name]->setTopicId(topic_info.topic_id); + if (services_[topic_info.topic_name]->getResponseMessageMD5() != topic_info.md5sum) + { + ROS_WARN("Service client setup: Response message MD5 mismatch between rosserial client and ROS"); + } + else + { + ROS_DEBUG("Service client %s: response message MD5 successfully validated as %s", + topic_info.topic_name.c_str(), topic_info.md5sum.c_str()); + } + set_sync_timeout(timeout_interval_); + } + + void handle_log(ros::serialization::IStream &stream) + { + rosserial_msgs::Log l; + ros::serialization::Serializer::read(stream, l); + if (l.level == rosserial_msgs::Log::ROSDEBUG) + ROS_DEBUG("%s", l.msg.c_str()); + else if (l.level == rosserial_msgs::Log::INFO) + ROS_INFO("%s", l.msg.c_str()); + else if (l.level == rosserial_msgs::Log::WARN) + ROS_WARN("%s", l.msg.c_str()); + else if (l.level == rosserial_msgs::Log::ERROR) + ROS_ERROR("%s", l.msg.c_str()); + else if (l.level == rosserial_msgs::Log::FATAL) + ROS_FATAL("%s", l.msg.c_str()); + } + + void handle_time(ros::serialization::IStream &stream) + { + std_msgs::Time time; + time.data = ros::Time::now(); + + size_t length = ros::serialization::serializationLength(time); + std::vector message(length); + + ros::serialization::OStream ostream(&message[0], length); + ros::serialization::Serializer::write(ostream, time); + + write_message(message, rosserial_msgs::TopicInfo::ID_TIME); + + // The MCU requesting the time from the server is the sync notification. This + // call moves the timeout forward. + set_sync_timeout(timeout_interval_); + } + + boost::asio::io_service &io_service_; + Socket socket_; + AsyncReadBuffer async_read_buffer_; + enum + { + buffer_max = 1023 + }; + bool active_; + + ros::NodeHandle nh_; + ros::CallbackQueue ros_callback_queue_; + + boost::posix_time::time_duration timeout_interval_; + boost::posix_time::time_duration attempt_interval_; + boost::posix_time::time_duration require_check_interval_; + boost::posix_time::time_duration ros_spin_interval_; + boost::asio::deadline_timer sync_timer_; + boost::asio::deadline_timer require_check_timer_; + boost::asio::deadline_timer ros_spin_timer_; + std::string require_param_name_; + int unrecognised_topic_retry_threshold_{0}; + int unrecognised_topics_{0}; + + std::map> callbacks_; + std::map publishers_; + std::map subscribers_; + std::map services_; + }; + +} // namespace + +#endif // ROSSERIAL_SERVER_SESSION_H diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/tcp_server.h b/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/tcp_server.h new file mode 100644 index 0000000..1dff9c5 --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/tcp_server.h @@ -0,0 +1,92 @@ +/** + * + * \file + * \brief TCP server for rosserial + * \author Mike Purvis + * \copyright Copyright (c) 2013, Clearpath Robotics, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Clearpath Robotics, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CLEARPATH ROBOTICS, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN 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. + * + * Please send comments, questions, or patches to code@clearpathrobotics.com + * + */ + +#ifndef ROSSERIAL_SERVER_TCP_SERVER_H +#define ROSSERIAL_SERVER_TCP_SERVER_H + +#include +#include +#include + +#include + +#include "rosserial_server/session.h" + + +namespace rosserial_server +{ + +using boost::asio::ip::tcp; + +template< typename Session = rosserial_server::Session > +class TcpServer +{ +public: + TcpServer(boost::asio::io_service& io_service, short port) + : io_service_(io_service), + acceptor_(io_service, tcp::endpoint(tcp::v4(), port)) + { + start_accept(); + } + +private: + void start_accept() + { + Session* new_session = new Session(io_service_); + acceptor_.async_accept(new_session->socket(), + boost::bind(&TcpServer::handle_accept, this, new_session, + boost::asio::placeholders::error)); + } + + void handle_accept(Session* new_session, + const boost::system::error_code& error) + { + if (!error) + { + new_session->start(); + } + else + { + delete new_session; + } + + start_accept(); + } + + boost::asio::io_service& io_service_; + tcp::acceptor acceptor_; +}; + +} // namespace + +#endif // ROSSERIAL_SERVER_TCP_SERVER_H diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/topic_handlers.h b/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/topic_handlers.h new file mode 100644 index 0000000..737adb5 --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/topic_handlers.h @@ -0,0 +1,195 @@ +/** + * + * \file + * \brief Classes which manage the Publish and Subscribe relationships + * that a Session has with its client. + * \author Mike Purvis + * \copyright Copyright (c) 2013, Clearpath Robotics, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Clearpath Robotics, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CLEARPATH ROBOTICS, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN 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. + * + * Please send comments, questions, or patches to code@clearpathrobotics.com + * + */ + +#ifndef ROSSERIAL_SERVER_TOPIC_HANDLERS_H +#define ROSSERIAL_SERVER_TOPIC_HANDLERS_H + +#include +#include +#include +#include + +namespace rosserial_server +{ + +class Publisher { +public: + Publisher(ros::NodeHandle& nh, const rosserial_msgs::TopicInfo& topic_info) { + rosserial_server::MsgInfo msginfo; + try + { + msginfo = lookupMessage(topic_info.message_type); + } + catch (const std::exception& e) + { + ROS_WARN_STREAM("Unable to look up message: " << e.what()); + } + + if (!msginfo.md5sum.empty() && msginfo.md5sum != topic_info.md5sum) + { + ROS_WARN_STREAM("Message" << topic_info.message_type << "MD5 sum from client does not " + "match that in system. Will avoid using system's message definition."); + msginfo.full_text = ""; + } + message_.morph(topic_info.md5sum, topic_info.message_type, msginfo.full_text, "false"); + publisher_ = message_.advertise(nh, topic_info.topic_name, 1); + } + + void handle(ros::serialization::IStream stream) { + ros::serialization::Serializer::read(stream, message_); + publisher_.publish(message_); + } + + std::string get_topic() { + return publisher_.getTopic(); + } + +private: + ros::Publisher publisher_; + topic_tools::ShapeShifter message_; +}; + +typedef boost::shared_ptr PublisherPtr; + + +class Subscriber { +public: + Subscriber(ros::NodeHandle& nh, rosserial_msgs::TopicInfo& topic_info, + boost::function& buffer)> write_fn) + : write_fn_(write_fn) { + ros::SubscribeOptions opts; + opts.init( + topic_info.topic_name, 1, boost::bind(&Subscriber::handle, this, _1)); + opts.md5sum = topic_info.md5sum; + opts.datatype = topic_info.message_type; + subscriber_ = nh.subscribe(opts); + } + + std::string get_topic() { + return subscriber_.getTopic(); + } + +private: + void handle(const boost::shared_ptr& msg) { + size_t length = ros::serialization::serializationLength(*msg); + std::vector buffer(length); + + ros::serialization::OStream ostream(&buffer[0], length); + ros::serialization::Serializer::write(ostream, *msg); + + write_fn_(buffer); + } + + ros::Subscriber subscriber_; + boost::function& buffer)> write_fn_; +}; + +typedef boost::shared_ptr SubscriberPtr; + +class ServiceClient { +public: + ServiceClient(ros::NodeHandle& nh, rosserial_msgs::TopicInfo& topic_info, + boost::function& buffer, const uint16_t topic_id)> write_fn) + : write_fn_(write_fn) { + topic_id_ = -1; + + rosserial_server::MsgInfo srvinfo; + rosserial_server::MsgInfo reqinfo; + rosserial_server::MsgInfo respinfo; + try + { + srvinfo = lookupMessage(topic_info.message_type, "srv"); + reqinfo = lookupMessage(topic_info.message_type + "Request", "srv"); + respinfo = lookupMessage(topic_info.message_type + "Response", "srv"); + } + catch (const std::exception& e) + { + ROS_WARN_STREAM("Unable to look up service definition: " << e.what()); + } + service_md5_ = srvinfo.md5sum; + request_message_md5_ = reqinfo.md5sum; + response_message_md5_ = respinfo.md5sum; + + ros::ServiceClientOptions opts; + opts.service = topic_info.topic_name; + opts.md5sum = srvinfo.md5sum; + opts.persistent = false; // always false for now + service_client_ = nh.serviceClient(opts); + } + void setTopicId(uint16_t topic_id) { + topic_id_ = topic_id; + } + std::string getServiceMD5() { + return service_md5_; + } + std::string getRequestMessageMD5() { + return request_message_md5_; + } + std::string getResponseMessageMD5() { + return response_message_md5_; + } + + void handle(ros::serialization::IStream stream) { + // deserialize request message + ros::serialization::Serializer::read(stream, request_message_); + + // perform service call + // note that at present, at least for rosserial-windows a service call returns nothing, + // so we discard the return value of this call() invocation. + service_client_.call(request_message_, response_message_, service_md5_); + + // write service response over the wire + size_t length = ros::serialization::serializationLength(response_message_); + std::vector buffer(length); + ros::serialization::OStream ostream(&buffer[0], length); + ros::serialization::Serializer::write(ostream, response_message_); + write_fn_(buffer,topic_id_); + } + +private: + topic_tools::ShapeShifter request_message_; + topic_tools::ShapeShifter response_message_; + ros::ServiceClient service_client_; + boost::function& buffer, const uint16_t topic_id)> write_fn_; + std::string service_md5_; + std::string request_message_md5_; + std::string response_message_md5_; + uint16_t topic_id_; +}; + +typedef boost::shared_ptr ServiceClientPtr; + +} // namespace + +#endif // ROSSERIAL_SERVER_TOPIC_HANDLERS_H diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/udp_socket_session.h b/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/udp_socket_session.h new file mode 100644 index 0000000..6e38dae --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/udp_socket_session.h @@ -0,0 +1,94 @@ +/** + * + * \file + * \brief Reconnecting class for a UDP rosserial session. + * \author Mike Purvis + * \copyright Copyright (c) 2016, Clearpath Robotics, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Clearpath Robotics, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CLEARPATH ROBOTICS, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN 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. + * + * Please send comments, questions, or patches to code@clearpathrobotics.com + * + */ + +#ifndef ROSSERIAL_SERVER_UDP_SOCKET_SESSION_H +#define ROSSERIAL_SERVER_UDP_SOCKET_SESSION_H + +#include +#include +#include + +#include + +#include "rosserial_server/session.h" +#include "rosserial_server/udp_stream.h" + + +namespace rosserial_server +{ + +using boost::asio::ip::udp; + +class UdpSocketSession : public Session +{ +public: + UdpSocketSession(boost::asio::io_service& io_service, + udp::endpoint server_endpoint, + udp::endpoint client_endpoint) + : Session(io_service), timer_(io_service), + server_endpoint_(server_endpoint), client_endpoint_(client_endpoint) + { + ROS_INFO_STREAM("rosserial_server UDP session created between " << server_endpoint << " and " << client_endpoint); + check_connection(); + } + +private: + void check_connection() + { + if (!is_active()) + { + socket().open(server_endpoint_, client_endpoint_); + start(); + } + + // Every second, check again if the connection should be reinitialized, + // if the ROS node is still up. + if (ros::ok()) + { + timer_.expires_from_now(boost::posix_time::milliseconds(2000)); + timer_.async_wait(boost::bind(&UdpSocketSession::check_connection, this)); + } + else + { + shutdown(); + } + } + + boost::asio::deadline_timer timer_; + udp::endpoint server_endpoint_; + udp::endpoint client_endpoint_; +}; + +} // namespace + +#endif // ROSSERIAL_SERVER_UDP_SOCKET_SESSION_H diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/udp_stream.h b/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/udp_stream.h new file mode 100644 index 0000000..30034cb --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_server/include/rosserial_server/udp_stream.h @@ -0,0 +1,133 @@ +/** + * + * \file + * \brief Adapter which allows a single-ended UDP connection to + * present the stream interface. + * \author Mike Purvis + * \copyright Copyright (c) 2016, Clearpath Robotics, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Clearpath Robotics, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CLEARPATH ROBOTICS, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN 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. + * + * Please send comments, questions, or patches to code@clearpathrobotics.com + * + */ + +#ifndef ROSSERIAL_SERVER_UDP_STREAM_H +#define ROSSERIAL_SERVER_UDP_STREAM_H + +#include +#include +#include + +#include + +#include "rosserial_server/session.h" + + +namespace rosserial_server +{ + +using boost::asio::ip::udp; +#if BOOST_VERSION < 107000 +using boost::asio::handler_type; +#endif + + +class UdpStream : public udp::socket +{ +public: + explicit UdpStream(boost::asio::io_service& io_service) : udp::socket(io_service) + { + } + + void open(udp::endpoint server_endpoint, udp::endpoint client_endpoint) + { + boost::system::error_code ec; + const protocol_type protocol = server_endpoint.protocol(); + + udp::socket::open(protocol, ec); + boost::asio::detail::throw_error(ec, "open"); + + udp::socket::bind(server_endpoint, ec); + boost::asio::detail::throw_error(ec, "bind"); + + client_endpoint_ = client_endpoint; + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (boost::system::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + BOOST_ASIO_MOVE_ARG(WriteHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a WriteHandler. + BOOST_ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; +#if (BOOST_VERSION >= 106600) + // See: http://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/net_ts.html + boost::asio::async_completion init(handler); + + udp::socket::async_send_to( + buffers, client_endpoint_, 0, init.completion_handler); + + return init.result.get(); +#else + return this->get_service().async_send_to( + this->get_implementation(), buffers, client_endpoint_, 0, + BOOST_ASIO_MOVE_CAST(WriteHandler)(handler)); +#endif + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void (boost::system::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + BOOST_ASIO_MOVE_ARG(ReadHandler) handler) + { + // If you get an error on the following line it means that your handler does + // not meet the documented type requirements for a ReadHandler. + BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; +#if (BOOST_VERSION >= 106600) + // See: http://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/net_ts.html + boost::asio::async_completion init(handler); + + udp::socket::async_receive( + buffers, 0, init.completion_handler); + + return init.result.get(); +#else + return this->get_service().async_receive_from( + this->get_implementation(), buffers, client_endpoint_, 0, + BOOST_ASIO_MOVE_CAST(ReadHandler)(handler)); +#endif + } + +private: + udp::endpoint client_endpoint_; +}; + +} // namespace + +#endif // ROSSERIAL_SERVER_UDP_STREAM_H diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_server/launch/serial.launch b/docker/testBot/OdomComm/docker/odometry/rosserial_server/launch/serial.launch new file mode 100644 index 0000000..8928429 --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_server/launch/serial.launch @@ -0,0 +1,6 @@ + + + + + + diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_server/launch/socket.launch b/docker/testBot/OdomComm/docker/odometry/rosserial_server/launch/socket.launch new file mode 100644 index 0000000..cde949c --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_server/launch/socket.launch @@ -0,0 +1,3 @@ + + + diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_server/launch/udp_socket.launch b/docker/testBot/OdomComm/docker/odometry/rosserial_server/launch/udp_socket.launch new file mode 100644 index 0000000..9b79aed --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_server/launch/udp_socket.launch @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_server/package.xml b/docker/testBot/OdomComm/docker/odometry/rosserial_server/package.xml new file mode 100644 index 0000000..df73c39 --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_server/package.xml @@ -0,0 +1,23 @@ + + + rosserial_server + 0.9.2 + + A more performance- and stability-oriented server alternative implemented + in C++ to rosserial_python. + + + Mike Purvis + Mike Purvis + + BSD + + catkin + rosserial_msgs + std_msgs + roscpp + topic_tools + python3-dev + libboost-thread-dev + libboost-thread + diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_server/src/msg_lookup.cpp b/docker/testBot/OdomComm/docker/odometry/rosserial_server/src/msg_lookup.cpp new file mode 100644 index 0000000..b2c0e23 --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_server/src/msg_lookup.cpp @@ -0,0 +1,102 @@ +/** + * \author Mike Purvis + * \copyright Copyright (c) 2019, Clearpath Robotics, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Clearpath Robotics, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CLEARPATH ROBOTICS, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN 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. + * + * Please send comments, questions, or patches to code@clearpathrobotics.com + */ + +#include "rosserial_server/msg_lookup.h" +#include "Python.h" + +namespace rosserial_server +{ + +const MsgInfo lookupMessage(const std::string& message_type, const std::string submodule) +{ + // Lazy-initialize the embedded Python interpreter. Avoid calling the corresponding + // finalize method due to issues with importing cyaml in the second instance. The + // total memory cost of having this in-process is only about 5-6MB. + Py_Initialize(); + + MsgInfo msginfo; + size_t slash_pos = message_type.find('/'); + if (slash_pos == std::string::npos) + { + throw std::runtime_error("Passed message type string does not include a slash character."); + } + std::string module_name = message_type.substr(0, slash_pos); + std::string class_name = message_type.substr(slash_pos + 1, std::string::npos); + + PyObject* module = PyImport_ImportModule((module_name + "." + submodule).c_str()); + if (!module) + { + throw std::runtime_error("Unable to import message module " + module_name + "."); + } + PyObject* msg_class = PyObject_GetAttrString(module, class_name.c_str()); + if (!msg_class) + { + throw std::runtime_error("Unable to find message class " + class_name + + " in module " + module_name + "."); + } + Py_XDECREF(module); + + PyObject* full_text = PyObject_GetAttrString(msg_class, "_full_text"); + PyObject* md5sum = PyObject_GetAttrString(msg_class, "_md5sum"); + if (!md5sum) + { + throw std::runtime_error("Class for message " + message_type + " did not contain" + + "expected _md5sum attribute."); + } + Py_XDECREF(msg_class); + +#if PY_VERSION_HEX > 0x03000000 + if (full_text) + { + msginfo.full_text.assign(PyUnicode_AsUTF8(full_text)); + } + msginfo.md5sum.assign(PyUnicode_AsUTF8(md5sum)); +#else + if (full_text) + { + msginfo.full_text.assign(PyString_AsString(full_text)); + } + msginfo.md5sum.assign(PyString_AsString(md5sum)); +#endif + + // See https://github.com/ros/ros_comm/issues/344 + // and https://github.com/ros/gencpp/pull/14 + // Valid full_text returned, but it is empty, so insert single line + if (full_text && msginfo.full_text.empty()) + { + msginfo.full_text = "\n"; + } + + Py_XDECREF(full_text); + Py_XDECREF(md5sum); + + return msginfo; +} + +} // namespace rosserial_server diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_server/src/serial_node.cpp b/docker/testBot/OdomComm/docker/odometry/rosserial_server/src/serial_node.cpp new file mode 100644 index 0000000..8fcd890 --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_server/src/serial_node.cpp @@ -0,0 +1,56 @@ +/** + * + * \file + * \brief Main entry point for the serial node. + * \author Mike Purvis + * \copyright Copyright (c) 2013, Clearpath Robotics, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Clearpath Robotics, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CLEARPATH ROBOTICS, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN 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. + * + * Please send comments, questions, or patches to code@clearpathrobotics.com + * + */ + +#include +#include +#include + +#include + +#include "rosserial_server/serial_session.h" + + +int main(int argc, char* argv[]) +{ + ros::init(argc, argv, "rosserial_server_serial_node"); + + std::string port; + int baud; + ros::param::param("~port", port, "/dev/ttyACM0"); + ros::param::param("~baud", baud, 57600); + + boost::asio::io_service io_service; + rosserial_server::SerialSession serial_session(io_service, port, baud); + io_service.run(); + return 0; +} diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_server/src/socket_node.cpp b/docker/testBot/OdomComm/docker/odometry/rosserial_server/src/socket_node.cpp new file mode 100644 index 0000000..e803b6d --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_server/src/socket_node.cpp @@ -0,0 +1,56 @@ +/** + * + * \file + * \brief Main entry point for the socket server node. + * \author Mike Purvis + * \copyright Copyright (c) 2013, Clearpath Robotics, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Clearpath Robotics, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CLEARPATH ROBOTICS, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN 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. + * + * Please send comments, questions, or patches to code@clearpathrobotics.com + * + */ + +#include +#include +#include + +#include + +#include "rosserial_server/tcp_server.h" + + +int main(int argc, char* argv[]) +{ + ros::init(argc, argv, "rosserial_server_socket_node"); + + int port; + ros::param::param("~port", port, 11411); + + boost::asio::io_service io_service; + rosserial_server::TcpServer<> tcp_server(io_service, port); + + ROS_INFO_STREAM("Listening for rosserial TCP connections on port " << port); + io_service.run(); + return 0; +} diff --git a/docker/testBot/OdomComm/docker/odometry/rosserial_server/src/udp_socket_node.cpp b/docker/testBot/OdomComm/docker/odometry/rosserial_server/src/udp_socket_node.cpp new file mode 100644 index 0000000..1ff7718 --- /dev/null +++ b/docker/testBot/OdomComm/docker/odometry/rosserial_server/src/udp_socket_node.cpp @@ -0,0 +1,65 @@ +/** + * + * \file + * \brief Main entry point for the UDP socket server node. + * \author Mike Purvis + * \copyright Copyright (c) 2016, Clearpath Robotics, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Clearpath Robotics, Inc. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CLEARPATH ROBOTICS, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN 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. + * + * Please send comments, questions, or patches to code@clearpathrobotics.com + * + */ + +#include +#include +#include + +#include + +#include "rosserial_server/udp_socket_session.h" + +using boost::asio::ip::udp; +using boost::asio::ip::address; + + +int main(int argc, char* argv[]) +{ + ros::init(argc, argv, "rosserial_server_udp_socket_node"); + + int server_port; + int client_port; + std::string client_addr; + ros::param::param("~server_port", server_port, 11411); + ros::param::param("~client_port", client_port, 11411); + ros::param::param("~client_addr", client_addr, "127.0.0.1"); + + boost::asio::io_service io_service; + rosserial_server::UdpSocketSession udp_socket_session( + io_service, + udp::endpoint(udp::v4(), server_port), + udp::endpoint(address::from_string(client_addr), client_port)); + io_service.run(); + + return 0; +} diff --git a/docker/testBot/OdomComm/docker/start-bridge.sh b/docker/testBot/OdomComm/docker/start-bridge.sh new file mode 100644 index 0000000..1cc90c5 --- /dev/null +++ b/docker/testBot/OdomComm/docker/start-bridge.sh @@ -0,0 +1,14 @@ +#!/bin/bash -e + +if [ $# = 1 ]; then + export ROS_MASTER_URI=$1 +else + export ROS_MASTER_URI=http://localhost:11311 +fi + +# Normally, sourcing the setup.bash file is sufficient to set up the ROS environment. +# However, the ros1_bridge package can't be found by ROS 2 even if we source the file. Manual sourcing is required. +source /ros2_humble/install/setup.bash +source /ros2_humble/install/ros1_bridge/share/ros1_bridge/local_setup.bash + +ros2 run ros1_bridge dynamic_bridge \ No newline at end of file diff --git a/docker/testBot/OdomComm/docker/yellow-compose.yml b/docker/testBot/OdomComm/docker/yellow-compose.yml new file mode 100644 index 0000000..02aa317 --- /dev/null +++ b/docker/testBot/OdomComm/docker/yellow-compose.yml @@ -0,0 +1,102 @@ +services: + ros-core: + image: osrf/ros:noetic-desktop-full + container_name: ros-core + command: "rosmaster --core" + network_mode: host + stop_grace_period: 1s + healthcheck: + # The healthcheck is required for ros1-bridge to wait until roscore is ready. + test: /ros_entrypoint.sh bash -c "rostopic list || exit 1" + interval: 3s + timeout: 10s + retries: 5 + + ros1-bridge: + build: + context: . + target: release + image: pomelo925/ttennis-humble:communication + container_name: ros2-ros1-bridge-ws + stop_grace_period: 1s + depends_on: + ros-core: + # The healthcheck is required for ros1-bridge to wait until roscore is ready. + condition: service_healthy + command: ./start-bridge.sh + stdin_open: true + tty: true + network_mode: host + working_dir: / + volumes: + # Mount local timezone into container. ( Readonly ) + # Reference: https://stackoverflow.com/questions/57607381/how-do-i-change-timezone-in-a-docker-container + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + # Direct Rendering Infrastructure + - /dev/dri:/dev/dri + # Shared Memory + - /dev/shm:/dev/shm + environment: + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + # - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp=value + + ros1: + image: jossiewang/eurobot2025-localization:rosserial-chassis + container_name: ros1 + stdin_open: true + tty: true + network_mode: host + privileged: true + volumes: + - /dev:/dev + stop_grace_period: 1s + depends_on: + ros-core: + condition: service_healthy + command: /bin/bash -c " + source /opt/ros/noetic/setup.bash && + source ~/ws/devel/setup.bash && + roslaunch odometry odometry_comm.launch" + + ros2-localization-dev: + image: jossiewang/eurobot2025-localization2:master + container_name: localization-2025-dev-ros2 + stdin_open: true + tty: true + privileged: true + stop_grace_period: 1s + restart: no + # entrypoint: ["./start.sh"] + + network_mode: host + working_dir: /home/user/localization-ws + environment: + - DISPLAY=${DISPLAY} + # Set ros2 environment variables. + # References: + # - https://docs.ros.org/en/humble/Concepts/Intermediate/About-Domain-ID.html + # - https://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Configuring-ROS2-Environment.html + # - ROS_LOCALHOST_ONLY=1 + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp + - ROS_WS=/home/user/localization-ws + volumes: + # Mount local timezone into container. ( Readonly ) + # Reference: https://stackoverflow.com/questions/57607381/how-do-i-change-timezone-in-a-docker-container + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + # Mount X11 server + - /tmp/.X11-unix:/tmp/.X11-unix + # Direct Rendering Infrastructure + # - /dev/dri:/dev/dri + # Mount sound card to prevent Gazebo warning. + # - /dev/snd:/dev/snd + - /home/share/data:/home/user/share/data:ro + - /dev:/dev + # Mount workspace + - ../../../../localization-devel-ws:/home/user/localization-ws/src/localization-devel-ws + command: /bin/bash -c " + source /opt/ros/humble/setup.bash && + source /home/user/localization-ws/install/setup.bash && + ros2 launch /home/user/localization-ws/src/localization-devel-ws/ekf/launch/y_running_launch.py" \ No newline at end of file diff --git a/docker/testBot/OdomComm/pull_and_run.sh b/docker/testBot/OdomComm/pull_and_run.sh new file mode 100755 index 0000000..213ad95 --- /dev/null +++ b/docker/testBot/OdomComm/pull_and_run.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +## 0. clean container within same group +echo "=== [COMMUNICATION] Pull & Run ===" +echo "[COMMUNICATION] Remove Containers ..." +docker compose -p communication down --volumes --remove-orphans + +## 1. make scripts & library executable +find ../E.Communication -type f -name "*.sh" -exec sudo chmod +x {} \; + +## 2. environment setup +export DISPLAY=:0 +xhost +local:docker +cd docker + +## 3. build image +echo "[COMMUNICATION] Pull Images ..." +docker pull pomelo925/ttennis-humble:communication + +## 4. deployment +echo "[COMMUNICATION] Deploying..." +docker compose -p communication up +# docker compose -p communication up -d \ No newline at end of file diff --git a/docker/testBot/OdomComm/yellow_run.sh b/docker/testBot/OdomComm/yellow_run.sh new file mode 100755 index 0000000..285b983 --- /dev/null +++ b/docker/testBot/OdomComm/yellow_run.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +## 0. clean container within same group +#echo "=== [COMMUNICATION] Pull & Run ===" +#echo "[COMMUNICATION] Remove Containers ..." +#docker compose -p communication down --volumes --remove-orphans + +## 2. environment setup +#export DISPLAY=:0 +#xhost +local:docker +#cd docker + +## 3. build image +#echo "[COMMUNICATION] Pull Images ..." +#docker pull pomelo925/ttennis-humble:communication + +## 4. deployment +echo "[COMMUNICATION] Deploying..." +docker compose -p localization-yellow -f docker/yellow-compose.yml up diff --git a/docker/testBot/docker-compose.yaml b/docker/testBot/docker-compose.yaml index ac5ba76..bd296fb 100644 --- a/docker/testBot/docker-compose.yaml +++ b/docker/testBot/docker-compose.yaml @@ -1,28 +1,175 @@ -version: '3' +x-common-vars: &ros2-vars + stdin_open: true + tty: true + privileged: true + stop_grace_period: 1s + restart: no + network_mode: host + working_dir: /home/user/localization-ws + environment: + # - DISPLAY=${DISPLAY} + # Set ros2 environment variables. + # References: + # - https://docs.ros.org/en/humble/Concepts/Intermediate/About-Domain-ID.html + # - https://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Configuring-ROS2-Environment.html + # - ROS_LOCALHOST_ONLY=1 + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp + - ROS_WS=/home/user/localization-ws + volumes: + # Mount local timezone into container. ( Readonly ) + # Reference: https://stackoverflow.com/questions/57607381/how-do-i-change-timezone-in-a-docker-container + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + # Mount X11 server + - /tmp/.X11-unix:/tmp/.X11-unix + # Direct Rendering Infrastructure + # - /dev/dri:/dev/dri + # Mount sound card to prevent Gazebo warning. + # - /dev/snd:/dev/snd + - /dev:/dev + # Mount workspace + - ../../localization-devel-ws:/home/user/localization-ws/src/localization-devel-ws + services: - ros1-localization-dev: - build: + + localization-main: + profiles: ['localization_main'] + image: jossiewang/eurobot2025-localization2:master # OdomTF finished testing 30th April 2025(Odom, obs_pcl) + container_name: localization-main + <<: + - *ros2-vars + command: /bin/bash -c " + source /opt/ros/humble/setup.bash && + source /home/user/localization-ws/install/setup.bash && + sudo chmod 777 /dev/ttyUSB1 && + ros2 launch ${ROS2_LAUNCH_FILE}" + + + localization-ready-signal: + profiles: ['localization_ready_signal'] + image: jossiewang/eurobot2025-localization:ReadySignal # OdomTF finished testing 30th April 2025(Odom, obs_pcl) + container_name: localization-ready-signal + <<: + - *ros2-vars + command: /bin/bash -c " + source /opt/ros/humble/setup.bash && + source /home/user/localization-ws/install/setup.bash && + ros2 launch ${ROS2_LAUNCH_FILE}" + + localization-RunStarter: + profiles: ['localization_RunStarter'] + image: jossiewang/eurobot2025-localization2:master + container_name: localization-RunStarter + <<: + - *ros2-vars + command: /bin/bash -c " + source /opt/ros/humble/setup.bash && + source /home/user/localization-ws/install/setup.bash && + ros2 launch ${ROS2_LAUNCH_FILE}" + + localization-build-run: + profiles: ['build_and_run'] + image: jossiewang/eurobot2025-localization2:master + container_name: localization_build_run + <<: + - *ros2-vars + command: /bin/bash -c " + source /opt/ros/humble/setup.bash && + source /home/user/localization-ws/install/setup.bash && + cd /home/user/localization-ws && + colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release --packages-select healthcheck btcpp_ros2_interfaces && + source /home/user/localization-ws/install/setup.bash && + ros2 launch ${ROS2_LAUNCH_FILE}" + + localization-record: + profiles: ['record'] + image: jossiewang/eurobot2025-localization:local-ros2-wSensors + container_name: localization_record + <<: + - *ros2-vars + command: /bin/bash -c " + source /opt/ros/humble/setup.bash && + source /home/user/localization-ws/install/setup.bash && + ros2 bag record -o ${BAG_NAME} \ + /local_filter \ + /lidar_pose \ + /final_pose \ + /tf \ + /tf_static \ + /odom2map \ + /rival/final_pose \ + /raw_obstacles \ + /raw_obstacles_visualization_pcl \ + /candidates" + + localization-bash: + profiles: ['localization_bash'] + image: jossiewang/eurobot2025-localization:master + container_name: localization-bash + <<: + - *ros2-vars + command: /bin/bash + + ros-core: + profiles: ['communication'] + image: osrf/ros:noetic-desktop-full + container_name: ros-core + command: "rosmaster --core" + network_mode: host + stop_grace_period: 1s + healthcheck: + # The healthcheck is required for ros1-bridge to wait until roscore is ready. + test: /ros_entrypoint.sh bash -c "rostopic list || exit 1" + interval: 3s + timeout: 10s + retries: 5 + + ros1-bridge: + profiles: ['communication'] + build: context: . - dockerfile: Dockerfile - args: - USERNAME: user - image: jossiewang/eurobot2025-localization:testBot-ros1-ImuLidar - container_name: localization-2025-ros1-dev + target: release + image: pomelo925/ttennis-humble:communication + container_name: ros2-ros1-bridge-ws + stop_grace_period: 1s + depends_on: + ros-core: + # The healthcheck is required for ros1-bridge to wait until roscore is ready. + condition: service_healthy + command: ./start-bridge.sh stdin_open: true tty: true - privileged: true - stop_grace_period: 1s - restart: no network_mode: host - working_dir: /home/user/localization-ws-ros1 - environment: - - DISPLAY=${DISPLAY} - - ROS_WS=/home/user/localization-ws-ros1 + working_dir: / volumes: + # Mount local timezone into container. ( Readonly ) + # Reference: https://stackoverflow.com/questions/57607381/how-do-i-change-timezone-in-a-docker-container - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro - - /tmp/.X11-unix:/tmp/.X11-unix + # Direct Rendering Infrastructure + - /dev/dri:/dev/dri + # Shared Memory + - /dev/shm:/dev/shm + environment: + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + # - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp=value + + ros1: + profiles: ['communication'] + image: jossiewang/eurobot2025-localization:rosserial-chassis + container_name: ros1 + stdin_open: true + tty: true + network_mode: host + privileged: true + volumes: - /dev:/dev - # - ../../localization-ws-ros1/src/localization-devel-ws:/home/user/localization-ws-ros1/src/localization-devel-ws:rw - - ../../localization-ws-ros1/src/localization-devel-ws:/home/user/localization-ws-ros1/src/localization-devel-ws - command: /bin/bash -c "echo 'export ROS_HOSTNAME=192.168.50.10' >> ~/.bashrc && echo 'export ROS_MASTER_URI=http://192.168.50.10:11311' >> ~/.bashrc && /home/user/localization-ws-ros1/src/localization-devel-ws/usb.sh && /bin/bash" \ No newline at end of file + stop_grace_period: 1s + depends_on: + ros-core: + condition: service_healthy + command: /bin/bash -c " + source /opt/ros/noetic/setup.bash && + source ~/ws/devel/setup.bash && + roslaunch odometry odometry_comm.launch" \ No newline at end of file diff --git a/docker/testBot/phidgets_drivers/LICENSE b/docker/testBot/phidgets_drivers/LICENSE new file mode 100644 index 0000000..2863577 --- /dev/null +++ b/docker/testBot/phidgets_drivers/LICENSE @@ -0,0 +1,5 @@ +The libphidgets22 library is licensed under the GNU Lesser General Public Licence v3 or later. +See LICENSE.gplv3 and LICENSE.lgplv3 for the text of the license. + +The rest of the code in this repository is licensed under the 3-clause BSD license. +See LICENSE.bsd for the text of the license. diff --git a/docker/testBot/phidgets_drivers/LICENSE.bsd b/docker/testBot/phidgets_drivers/LICENSE.bsd new file mode 100644 index 0000000..d06df08 --- /dev/null +++ b/docker/testBot/phidgets_drivers/LICENSE.bsd @@ -0,0 +1,26 @@ +Copyright (c) 2019, Open Source Robotics Foundation +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +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. diff --git a/docker/testBot/phidgets_drivers/LICENSE.gplv3 b/docker/testBot/phidgets_drivers/LICENSE.gplv3 new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/docker/testBot/phidgets_drivers/LICENSE.gplv3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/docker/testBot/phidgets_drivers/LICENSE.lgplv3 b/docker/testBot/phidgets_drivers/LICENSE.lgplv3 new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/docker/testBot/phidgets_drivers/LICENSE.lgplv3 @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/docker/testBot/phidgets_drivers/README.md b/docker/testBot/phidgets_drivers/README.md new file mode 100644 index 0000000..ccb9d82 --- /dev/null +++ b/docker/testBot/phidgets_drivers/README.md @@ -0,0 +1,193 @@ +Phidgets drivers for ROS 2 +========================== + +Overview +-------- + +Drivers for various [Phidgets](https://www.phidgets.com) devices. This package includes: + +* `libphidget22`: a package which downloads and builds the Phidgets C API from + phidgets.com as an external project. + +* `phidgets_api`: a package that implements a C++ wrapper for the libphidgets22 + C API, providing some base Phidget helper functions and various classes for + different phidget devices. + +* `phidgets_msgs`: a package that contains custom messages and services for + interacting with the nodes. + +* ROS 2 nodes exposing the functionality of specific phidgets devices: + + * [`phidgets_accelerometer`](phidgets_accelerometer/README.md) + + * [`phidgets_analog_inputs`](phidgets_analog_inputs/README.md) + + * [`phidgets_analog_outputs`](phidgets_analog_outputs/README.md) + + * [`phidgets_digital_inputs`](phidgets_digital_inputs/README.md) + + * [`phidgets_digital_outputs`](phidgets_digital_outputs/README.md) + + * [`phidgets_gyroscope`](phidgets_gyroscope/README.md) + + * [`phidgets_high_speed_encoder`](phidgets_high_speed_encoder/README.md) + + * [`phidgets_ik`](phidgets_ik/README.md) + + * [`phidgets_magnetometer`](phidgets_magnetometer/README.md) + + * [`phidgets_motors`](phidgets_motors/README.md) + + * [`phidgets_spatial`](phidgets_spatial/README.md) + + * [`phidgets_temperature`](phidgets_temperature/README.md) + +Concerning Phidgets +------------------- + +Phidgets are typically plugged into USB on a host computer (though there are +wireless ones, they will be ignored here). In the "old-style" Phidgets, there +was one USB plug per device. So if you have a +[temperature Phidget](https://www.phidgets.com/?tier=3&catid=14&pcid=12&prodid=1042), +and an [accelerometer Phidget](https://www.phidgets.com/?tier=3&catid=10&pcid=8&prodid=1026), +they take up 2 USB plugs on the computer. These "old-style" Phidgets are still +around and still available for sale, but most of the new development and +sensors are in "new-style" Phidgets. In "new-style" Phidgets, a +[VINT hub](https://www.phidgets.com/?tier=3&catid=2&pcid=1&prodid=643) is +connected to the host computer via USB, and then the other Phidgets connect to +a port on the VINT hub. Most of the "old-style" Phidget functions (temperature, +acclerometer, etc.) are also available as "new-style" Phidgets, and most new +functionality is only available as VINT devices. + +### Identifying Phidgets devices ### + +All Phidgets that plug directly into a USB port (including the VINT hub) have a +unique serial number. This serial number is printed on the back of the device, +and is also printed out by the phidgets drivers when they start up. The serial +number can be specified as a parameter when the driver starts up; otherwise, the +default is to connect to *any* Phidgets that are of the correct type at startup. + +Uniquely identifying a "new-style" Phidget also requires one more piece of +information, which is the VINT hub port it is connected to. This also must be +provided as a parameter when starting up the driver. + +Note that there are "smart" and "simple" VINT hub devices. "Smart" devices have +their own microcontrollers on board and use a protocol to communicate with the +VINT hub. + +"Simple" VINT hub devices don't have a microcontroller. They just provide +or accept a voltage from the VINT hub port (which can act as a digital input, +digital output, or analog inputs). + +Whether the Phidget is "smart" or "simple" can be determined by looking at the +"Connection and Compatibility" portion of the webpage for the individual sensor. +If the device is "smart", then "is_hub_port_device" must be set to "false" +when launching a driver; if the device is "simple", then "is_hub_port_device" +must be set to "true". + +Installing +---------- + +### From packages ### + +Make sure you have ROS 2 Dashing or newer installed: https://docs.ros.org + +Then run: + + sudo apt-get install ros--phidgets-drivers + +### From source ### + +Make sure you have ROS 2 Dashing or newer installed: https://docs.ros.org + +Also make sure you have git installed: + + sudo apt-get install git-core + +Change directory to the source folder of your colcon workspace. +If, for instance, your workspace is `~/colcon_ws`, make sure there is +a `src/` folder within it, then execute: + + cd ~/colcon_ws/src + +Clone the repository ( may be `dashing`, ...) + + git clone -b https://github.com/ros-drivers/phidgets_drivers.git + +Install dependencies using rosdep: + + rosdep install phidgets_drivers + +Alternatively, if rosdep does not work, install the following packages: + + sudo apt-get install libusb-1.0-0 libusb-1.0-0-dev + +Compile your colcon workspace: + + cd ~/colcon_ws + colcon build + +### Udev rules setup ### + +**Note:** The following steps are only required when installing the package +from source. When installing a binary debian package of `phidgets_api` >= 0.7.8, +the udev rules are set up automatically. + +Make sure your colcon workspace has been successfully compiled. +To set up the udev rules for the Phidgets USB devices, run the following commands: + + sudo cp ~/colcon_ws/src/phidgets_drivers/phidgets_api/debian/udev /etc/udev/rules.d/99-phidgets.rules + sudo udevadm control --reload-rules + +Afterwards, disconnect the USB cable and plug it in again (or run `sudo udevadm trigger`). + +Running +------- + +You may notice that there are no executables installed by the various phidgets packages. +Instead, all functionality is available via [ROS 2 components](https://docs.ros.org/en/rolling/Concepts/About-Composition.html). +The reason for this is that when using phidget VINT hubs, only one process at a time can access the hub. +Since the hub may have multiple devices connected to it, we need to load multiple different phidgets drivers into the same process. +We do that through loading multiple components into a single ROS 2 component container. + +Luckily, to make things easier in the single device case, we have default launch files for running a single phidgets driver in a single process. +For instance, you can run the phidgets_spatial node by running: + + ros2 launch phidgets_spatial spatial-launch.py + +See the README files in the individual packages above for exact usage of each launch file. + +Developing +---------- + +To check formatting after modifying source code: + + python3 clang-check-style.py + +To reformat source code: + + find . -name '*.h' -or -name '*.hpp' -or -name '*.cpp' | xargs clang-format-6.0 -i -style=file $1 + + +pre-commit Formatting Checks +---------------------------- + +This repo has a [pre-commit](https://pre-commit.com/) check that runs in CI. +You can use this locally and set it up to run automatically before you commit +something. To install, use apt: + +```bash +sudo apt install pre-commit +``` + +To run over all the files in the repo manually: + +```bash +pre-commit run -a +``` + +To run pre-commit automatically before committing in the local repo, install the git hooks: + +```bash +pre-commit install +``` diff --git a/docker/testBot/phidgets_drivers/libphidget22/CHANGELOG.rst b/docker/testBot/phidgets_drivers/libphidget22/CHANGELOG.rst new file mode 100644 index 0000000..324fe84 --- /dev/null +++ b/docker/testBot/phidgets_drivers/libphidget22/CHANGELOG.rst @@ -0,0 +1,103 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package libphidget22 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +2.3.3 (2024-03-13) +------------------ +* Update to libphidget22 1.19 (`#176 `_) +* Contributors: Martin Günther + +2.3.2 (2023-11-27) +------------------ + +2.3.1 (2023-03-03) +------------------ +* Update to libphidget22 1.13. (`#160 `_) +* Contributors: Chris Lalancette + +2.3.0 (2022-04-13) +------------------ +* Update to libphidgets 20220203 (`#138 `_) +* Contributors: Chris Lalancette + +2.2.2 (2022-02-17) +------------------ +* Remove outdated patch file (`#112 `_) +* Update to libphidget22-1.7.20210816 (`#108 `_) + This is required to support new devices such as the MOT0109. + Fixes `#99 `_, fixes `#105 `_. + This is a forward-port of `#106 `_ to ROS2. +* Make sure libphidget22 library can be found. (`#97 `_) (`#100 `_) + In Foxy and later, we need to provide the .dsv hook so that + the library can be found. +* Contributors: Chris Lalancette, Martin Günther + +2.2.1 (2021-08-03) +------------------ +* Update to the latest libphidgets 1.6 library. (`#91 `_) +* Contributors: Chris Lalancette + +2.2.0 (2021-05-20) +------------------ + +2.1.0 (2021-03-29) +------------------ +* Compile libphidget22 with -fPIC (`#88 `_) +* Update to libphidget22 from 2020. +* Contributors: Chris Lalancette, Scott K Logan + +2.0.2 (2020-06-01) +------------------ + +2.0.1 (2019-12-05) +------------------ + +2.0.0 (2019-12-05) +------------------ +* Port libphidget22 vendor package to ament. +* Ignore all packages for ROS 2 port. +* Update maintainers in package.xml +* Merge pull request `#39 `_ from clalancette/add-libphidget22 +* Patch warnings in libphidget22. +* Fix up libphidget22 package.xml dependencies. +* Add in libphidget22 package. +* Contributors: Chris Lalancette, Martin Günther + +0.7.9 (2019-06-28) +------------------ + +0.7.8 (2019-05-06) +------------------ + +0.7.7 (2018-09-18) +------------------ + +0.7.6 (2018-08-09) +------------------ + +0.7.5 (2018-01-31) +------------------ + +0.7.4 (2017-10-04) +------------------ + +0.7.3 (2017-06-30) +------------------ + +0.7.2 (2017-06-02) +------------------ + +0.7.1 (2017-05-22) +------------------ + +0.7.0 (2017-02-17 17:40) +------------------------ + +0.2.3 (2017-02-17 12:11) +------------------------ + +0.2.2 (2015-03-23) +------------------ + +0.2.1 (2015-01-15) +------------------ diff --git a/docker/testBot/phidgets_drivers/libphidget22/CMakeLists.txt b/docker/testBot/phidgets_drivers/libphidget22/CMakeLists.txt new file mode 100644 index 0000000..d0640d5 --- /dev/null +++ b/docker/testBot/phidgets_drivers/libphidget22/CMakeLists.txt @@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 3.5) +project(libphidget22) + +find_package(ament_cmake REQUIRED) + +set(extra_c_flags "-g -O2 -Wno-incompatible-pointer-types -Wno-deprecated-declarations -Wno-format-truncation -fPIC") + +include(ExternalProject) +ExternalProject_Add(EP_${PROJECT_NAME} + URL https://www.phidgets.com/downloads/phidget22/libraries/linux/libphidget22/libphidget22-1.19.20240304.tar.gz + URL_MD5 9b059eaef8cb8ce70b8abd7e4d309d1d + + SOURCE_DIR ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-src + CONFIGURE_COMMAND + /configure + CFLAGS=${extra_c_flags} + --prefix=${CMAKE_CURRENT_BINARY_DIR}/libphidget22_install + --disable-ldconfig + BUILD_COMMAND $(MAKE) + INSTALL_COMMAND $(MAKE) install +) + +# The external project will install to the build folder, but we'll install that on make install. +# Note that we install lib and include separately so we can add the extra level of indirection +# to the include directory. +install( + DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR}/libphidget22_install/lib/ + DESTINATION + ${CMAKE_INSTALL_PREFIX}/opt/libphidget22/lib/ +) + +install( + DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR}/libphidget22_install/include/ + DESTINATION + ${CMAKE_INSTALL_PREFIX}/opt/libphidget22/include/libphidget22/ +) + +ament_environment_hooks(env_hook/libphidget22_library_path.sh) +set(ENV_VAR_NAME "LD_LIBRARY_PATH") +set(ENV_VAR_VALUE "opt/libphidget22/lib") +ament_environment_hooks(env_hook/libphidget22_library_path.dsv.in) + +ament_package(CONFIG_EXTRAS "cmake/libphidget22-extras.cmake.in") diff --git a/docker/testBot/phidgets_drivers/libphidget22/cmake/libphidget22-extras.cmake.in b/docker/testBot/phidgets_drivers/libphidget22/cmake/libphidget22-extras.cmake.in new file mode 100644 index 0000000..7a386fa --- /dev/null +++ b/docker/testBot/phidgets_drivers/libphidget22/cmake/libphidget22-extras.cmake.in @@ -0,0 +1,3 @@ +# TODO(clalancette): Does this work in general? +list(APPEND @PROJECT_NAME@_INCLUDE_DIRS "${@PROJECT_NAME@_DIR}/../../../opt/libphidget22/include") +list(APPEND @PROJECT_NAME@_LIBRARIES "${@PROJECT_NAME@_DIR}/../../../opt/libphidget22/lib/libphidget22.so") diff --git a/docker/testBot/phidgets_drivers/libphidget22/env_hook/libphidget22_library_path.dsv.in b/docker/testBot/phidgets_drivers/libphidget22/env_hook/libphidget22_library_path.dsv.in new file mode 100644 index 0000000..92145c4 --- /dev/null +++ b/docker/testBot/phidgets_drivers/libphidget22/env_hook/libphidget22_library_path.dsv.in @@ -0,0 +1 @@ +prepend-non-duplicate;@ENV_VAR_NAME@;@ENV_VAR_VALUE@ diff --git a/docker/testBot/phidgets_drivers/libphidget22/env_hook/libphidget22_library_path.sh b/docker/testBot/phidgets_drivers/libphidget22/env_hook/libphidget22_library_path.sh new file mode 100644 index 0000000..a6e0098 --- /dev/null +++ b/docker/testBot/phidgets_drivers/libphidget22/env_hook/libphidget22_library_path.sh @@ -0,0 +1,16 @@ +# copied from libphidget22/env_hook/libphidget22_library_path.sh + +# detect if running on Darwin platform +_UNAME=`uname -s` +_IS_DARWIN=0 +if [ "$_UNAME" = "Darwin" ]; then + _IS_DARWIN=1 +fi +unset _UNAME + +if [ $_IS_DARWIN -eq 0 ]; then + ament_prepend_unique_value LD_LIBRARY_PATH "$AMENT_CURRENT_PREFIX/opt/libphidget22/lib" +else + ament_prepend_unique_value DYLD_LIBRARY_PATH "$AMENT_CURRENT_PREFIX/opt/libphidget22/lib" +fi +unset _IS_DARWIN diff --git a/docker/testBot/phidgets_drivers/libphidget22/package.xml b/docker/testBot/phidgets_drivers/libphidget22/package.xml new file mode 100644 index 0000000..027d8de --- /dev/null +++ b/docker/testBot/phidgets_drivers/libphidget22/package.xml @@ -0,0 +1,29 @@ + + + + libphidget22 + 2.3.3 + This package wraps the libphidget22 to use it as a ROS dependency + + Martin Günther + Chris Lalancette + + LGPL + + http://ros.org/wiki/libphidget22 + https://github.com/ros-drivers/phidgets_drivers.git + https://github.com/ros-drivers/phidgets_drivers/issues + + Alexander Bubeck + + ament_cmake + + libusb-1.0-dev + libusb-1.0 + + + ament_cmake + + diff --git a/docker/testBot/phidgets_drivers/phidgets_api/CHANGELOG.rst b/docker/testBot/phidgets_drivers/phidgets_api/CHANGELOG.rst new file mode 100644 index 0000000..887aa45 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/CHANGELOG.rst @@ -0,0 +1,179 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package phidgets_api +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +2.3.3 (2024-03-13) +------------------ + +2.3.2 (2023-11-27) +------------------ +* added new parameters for spatial precision MOT0109 onwards +* added support for phidget spatial onboard orientation estimation +* Contributors: Malte kl. Piening, Martin Günther + +2.3.1 (2023-03-03) +------------------ +* adding support for analog outputs for ROS2 (`#145 `_) +* Contributors: Alexis Fetet + +2.3.0 (2022-04-13) +------------------ + +2.2.2 (2022-02-17) +------------------ +* spatial: Add attach + detach handlers +* Fix some clang-tidy warnings +* Fix typo in error message (`#104 `_) +* Contributors: Martin Günther + +2.2.1 (2021-08-03) +------------------ + +2.2.0 (2021-05-20) +------------------ + +2.1.0 (2021-03-29) +------------------ +* Switch header guards to _HPP SUFFIX. +* Remove unnecessary cstddef. +* Contributors: Chris Lalancette + +2.0.2 (2020-06-01) +------------------ +* Use '=default' for default destructors. (`#66 `_) +* Contributors: Chris Lalancette + +2.0.1 (2019-12-05) +------------------ +* Switch the buildtoo_depend to ament_cmake_ros. (`#65 `_) +* Contributors: Chris Lalancette + +2.0.0 (2019-12-05) +------------------ +* Remove unnecessary base-class initialization. +* Get rid of C-style casts. +* Make sure what() method is marked as override. +* Rename method parameter name to match implementation. +* Make sure to include cstddef for libphidget22.h include. +* Make sure to initialize class member variables. +* Reformat using clang +* Make sure to set the VoltageRange to AUTO at the beginning. +* Only publish motor_back_emf if supported by DC Motor device (`#53 `_) +* Print out the serial number when connecting. +* Port phidgets_api to ament. +* Ignore all packages for ROS 2 port. +* Update maintainers in package.xml +* Merge pull request `#39 `_ from clalancette/add-libphidget22 +* Add in try/catch blocks for connecting. +* Fixes from review. +* Implement data interval setting for analog inputs. +* Add in the license files and add to the headers. +* Completely remove libphidget21. +* Rewrite Motor Phidget to use libphidget22. +* Rewrite High Speed Encoder to use libphidget22. +* Rewrite IR to use libphidget22. +* Rewrite IMU using libphidget22. +* Add support for Phidgets Magnetometer sensors. +* Add support for Phidgets Gyroscope sensors. +* Add support for Phidgets Accelerometer sensors. +* Add in support for Phidgets Temperature sensors. +* Rewrite phidgets_ik on top of libphidget22 classes. +* Add in support for Phidgets Analog inputs. +* Add in support for Phidgets Digital Inputs. +* Add in support for Phidgets Digital Outputs. +* Add in libphidget22 helper functions. +* Rename Phidget class to Phidget21 class. +* Switch to C++14. +* Merge pull request `#36 `_ from clalancette/phidget-cleanup2 +* Run clang-format on the whole codebase. +* Switch to C++14 everywhere. +* Remove unused indexHandler from Encoder class. +* Change API from separate open/waitForAttachment to openAndWaitForAttachment. +* Small cleanups throughout the code. +* Push libphidgets API calls down to phidgets_api. +* Quiet down the to-be-overridden callbacks. +* Consistently use nullptr instead of 0. +* Make the phidget_api destructors virtual. +* Style cleanup. +* Move libusb dependency into the libphidget21 package.xml. +* Switch to package format 2. +* Cleanup spacing in all of the CMakeLists.txt +* Contributors: Chris Lalancette, Martin Günther, Peter Polidoro + +0.7.9 (2019-06-28) +------------------ +* Add missing OnInputChange handler (`#33 `_) +* Contributors: Kai Hermann + +0.7.8 (2019-05-06) +------------------ +* Install udev rules on binary package installation +* Contributors: Martin Günther + +0.7.7 (2018-09-18) +------------------ + +0.7.6 (2018-08-09) +------------------ + +0.7.5 (2018-01-31) +------------------ +* Add support for the phidgets_ik (Phidgets Interface Kit) +* Contributors: Russel Howe, James Sarrett, Martin Günther + +0.7.4 (2017-10-04) +------------------ +* Fix typo and doxygen docs +* Contributors: Jose Luis Blanco Claraco, Martin Günther + +0.7.3 (2017-06-30) +------------------ + +0.7.2 (2017-06-02) +------------------ + +0.7.1 (2017-05-22) +------------------ +* Set event handlers for motor + encoder APIs +* Added basic motor api +* Added basic encoder board api +* Contributors: Zach Anderson, Martin Günther + +0.7.0 (2017-02-17) +------------------ +* Use our own libphidget21 instead of external libphidgets +* Contributors: Martin Günther + +0.2.3 (2017-02-17) +------------------ +* Add IMU diagnostics (`#24 `_) +* Contributors: Mani Monajjemi, Keshav Iyengar, Martin Günther + +0.2.2 (2015-03-23) +------------------ +* phidgets_api: updated build/installation rules to use 3rd party libphdigets ROS package +* phidgets_api: updated package details +* phidgets_api: added copy of udev rule to package and updated path in script +* phidgets_api: updated path to libphidgets header file +* phidgets_api: removed license and header file of phidgets library +* Contributors: Murilo FM + +0.2.1 (2015-01-15) +------------------ +* phidgets_api: add libusb dependency + This caused Jenkins CI tests to fail. +* phidgets_api: fix case in CMakeLists +* phidgets_api: added GNU LGPLv3 copy (phidget21.h) +* phidgets_api: updated license and author information +* phidgets_api: added script to setup udev rules for Phidgets devices +* phidgets_api: added libphidget21 dependency as cmake external project +* phidgets_api: updated path to libphidget header file +* phidgets_api: added libphidget header file to package +* phidgets_api: removed phidgets_c_api dependency +* Deleted comments within files of all packages +* Catkinised packages +* added missing cmakelists +* added api, imu and ir +* removed deps directory +* initial commit +* Contributors: Ivan Dryanovski, Martin Günther, Murilo FM diff --git a/docker/testBot/phidgets_drivers/phidgets_api/CMakeLists.txt b/docker/testBot/phidgets_drivers/phidgets_api/CMakeLists.txt new file mode 100644 index 0000000..c3232bf --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/CMakeLists.txt @@ -0,0 +1,57 @@ +cmake_minimum_required(VERSION 3.5) + +project(phidgets_api) + +# Default to C++14 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 14) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +find_package(ament_cmake_ros REQUIRED) +find_package(libphidget22 REQUIRED) + +include_directories(include) + +add_library(phidgets_api src/accelerometer.cpp + src/analog_input.cpp + src/analog_inputs.cpp + src/analog_output.cpp + src/analog_outputs.cpp + src/digital_input.cpp + src/digital_inputs.cpp + src/digital_output.cpp + src/digital_outputs.cpp + src/encoder.cpp + src/encoders.cpp + src/gyroscope.cpp + src/ir.cpp + src/magnetometer.cpp + src/motor.cpp + src/motors.cpp + src/phidget22.cpp + src/spatial.cpp + src/temperature.cpp) + +ament_target_dependencies(phidgets_api + libphidget22 +) + +install(TARGETS phidgets_api + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) + +install(DIRECTORY include/ + DESTINATION include +) + +ament_export_dependencies(ament_cmake libphidget22) +ament_export_include_directories(include) +ament_export_libraries(phidgets_api) + +ament_package() diff --git a/docker/testBot/phidgets_drivers/phidgets_api/debian/udev b/docker/testBot/phidgets_drivers/phidgets_api/debian/udev new file mode 100644 index 0000000..52074cc --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/debian/udev @@ -0,0 +1,7 @@ +# Very old Phidgets +SUBSYSTEMS=="usb", ACTION=="add", ATTRS{idVendor}=="0925", ATTRS{idProduct}=="8101", MODE="666" +SUBSYSTEMS=="usb", ACTION=="add", ATTRS{idVendor}=="0925", ATTRS{idProduct}=="8104", MODE="666" +SUBSYSTEMS=="usb", ACTION=="add", ATTRS{idVendor}=="0925", ATTRS{idProduct}=="8201", MODE="666" + +# All current and future Phidgets - Vendor = 0x06c2, Product = 0x0030 - 0x00af +SUBSYSTEMS=="usb", ACTION=="add", ATTRS{idVendor}=="06c2", ATTRS{idProduct}=="00[3-a][0-f]", MODE="666" diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/accelerometer.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/accelerometer.hpp new file mode 100644 index 0000000..a0f5690 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/accelerometer.hpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_ACCELEROMETER_HPP +#define PHIDGETS_API_ACCELEROMETER_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class Accelerometer final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Accelerometer) + + explicit Accelerometer( + int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function data_handler); + + ~Accelerometer(); + + int32_t getSerialNumber() const noexcept; + + void getAcceleration(double &x, double &y, double &z, + double ×tamp) const; + + void setDataInterval(uint32_t interval_ms) const; + + void dataHandler(const double acceleration[3], double timestamp) const; + + private: + int32_t serial_number_; + std::function data_handler_; + PhidgetAccelerometerHandle accel_handle_{nullptr}; + + static void DataHandler(PhidgetAccelerometerHandle input_handle, void *ctx, + const double acceleration[3], double timestamp); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_ACCELEROMETER_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/analog_input.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/analog_input.hpp new file mode 100644 index 0000000..4664874 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/analog_input.hpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_ANALOG_INPUT_HPP +#define PHIDGETS_API_ANALOG_INPUT_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class AnalogInput final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(AnalogInput) + + explicit AnalogInput(int32_t serial_number, int hub_port, + bool is_hub_port_device, int channel, + std::function input_handler); + + ~AnalogInput(); + + int32_t getSerialNumber() const noexcept; + + double getSensorValue() const; + + void setDataInterval(uint32_t data_interval_ms) const; + + void voltageChangeHandler(double sensorValue) const; + + private: + int32_t serial_number_; + int channel_; + std::function input_handler_; + PhidgetVoltageInputHandle ai_handle_{nullptr}; + + static void VoltageChangeHandler(PhidgetVoltageInputHandle input_handle, + void *ctx, double sensorValue); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_ANALOG_INPUT_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/analog_inputs.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/analog_inputs.hpp new file mode 100644 index 0000000..a961932 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/analog_inputs.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_ANALOG_INPUTS_HPP +#define PHIDGETS_API_ANALOG_INPUTS_HPP + +#include +#include +#include + +#include "phidgets_api/analog_input.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class AnalogInputs final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(AnalogInputs) + + explicit AnalogInputs(int32_t serial_number, int hub_port, + bool is_hub_port_device, + std::function input_handler); + + ~AnalogInputs() = default; + + int32_t getSerialNumber() const noexcept; + + uint32_t getInputCount() const noexcept; + + double getSensorValue(int index) const; + + void setDataInterval(int index, uint32_t data_interval_ms) const; + + private: + uint32_t input_count_{0}; + std::vector> ais_; +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_ANALOG_INPUTS_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/analog_output.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/analog_output.hpp new file mode 100644 index 0000000..fec7564 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/analog_output.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_ANALOG_OUTPUT_HPP +#define PHIDGETS_API_ANALOG_OUTPUT_HPP + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class AnalogOutput final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(AnalogOutput) + + explicit AnalogOutput(int32_t serial_number, int hub_port, + bool is_hub_port_device, int channel); + + ~AnalogOutput(); + + void setOutputVoltage(double voltage) const; + + void setEnabledOutput(int enabled) const; + + private: + PhidgetVoltageOutputHandle ao_handle_; +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_ANALOG_OUTPUT_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/analog_outputs.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/analog_outputs.hpp new file mode 100644 index 0000000..d93fa5f --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/analog_outputs.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_ANALOG_OUTPUTS_HPP +#define PHIDGETS_API_ANALOG_OUTPUTS_HPP + +#include +#include + +#include "phidgets_api/analog_output.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class AnalogOutputs final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(AnalogOutputs) + + explicit AnalogOutputs(int32_t serial_number, int hub_port, + bool is_hub_port_device); + + ~AnalogOutputs(); + + uint32_t getOutputCount() const noexcept; + + void setOutputVoltage(int index, double voltage) const; + + void setEnabledOutput(int index, int enabled) const; + + private: + uint32_t output_count_; + std::vector> aos_; +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_ANALOG_INPUTS_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/digital_input.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/digital_input.hpp new file mode 100644 index 0000000..cca92b4 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/digital_input.hpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_DIGITAL_INPUT_HPP +#define PHIDGETS_API_DIGITAL_INPUT_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class DigitalInput +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(DigitalInput) + + explicit DigitalInput(int32_t serial_number, int hub_port, + bool is_hub_port_device, int channel, + std::function input_handler); + + ~DigitalInput(); + + int32_t getSerialNumber() const noexcept; + + bool getInputValue() const; + + void stateChangeHandler(int state) const; + + private: + int32_t serial_number_; + int channel_; + std::function input_handler_; + PhidgetDigitalInputHandle di_handle_{nullptr}; + + static void StateChangeHandler(PhidgetDigitalInputHandle input_handle, + void *ctx, int state); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_DIGITAL_INPUT_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/digital_inputs.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/digital_inputs.hpp new file mode 100644 index 0000000..5188c6f --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/digital_inputs.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_DIGITAL_INPUTS_HPP +#define PHIDGETS_API_DIGITAL_INPUTS_HPP + +#include +#include +#include + +#include "phidgets_api/digital_input.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class DigitalInputs +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(DigitalInputs) + + explicit DigitalInputs(int32_t serial_number, int hub_port, + bool is_hub_port_device, + std::function input_handler); + + ~DigitalInputs() = default; + + int32_t getSerialNumber() const noexcept; + + uint32_t getInputCount() const noexcept; + + bool getInputValue(int index) const; + + private: + uint32_t input_count_{0}; + std::vector> dis_; +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_DIGITAL_INPUTS_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/digital_output.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/digital_output.hpp new file mode 100644 index 0000000..937967b --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/digital_output.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_DIGITAL_OUTPUT_HPP +#define PHIDGETS_API_DIGITAL_OUTPUT_HPP + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class DigitalOutput final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(DigitalOutput) + + explicit DigitalOutput(int32_t serial_number, int hub_port, + bool is_hub_port_device, int channel); + + ~DigitalOutput(); + + int32_t getSerialNumber() const noexcept; + + void setOutputState(bool state) const; + + private: + int32_t serial_number_; + PhidgetDigitalOutputHandle do_handle_{nullptr}; +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_DIGITAL_OUTPUT_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/digital_outputs.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/digital_outputs.hpp new file mode 100644 index 0000000..b87a57b --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/digital_outputs.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_DIGITAL_OUTPUTS_HPP +#define PHIDGETS_API_DIGITAL_OUTPUTS_HPP + +#include +#include + +#include "phidgets_api/digital_output.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class DigitalOutputs final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(DigitalOutputs) + + explicit DigitalOutputs(int32_t serial_number, int hub_port, + bool is_hub_port_device); + + ~DigitalOutputs() = default; + + int32_t getSerialNumber() const noexcept; + + uint32_t getOutputCount() const noexcept; + + void setOutputState(int index, bool state) const; + + private: + uint32_t output_count_{0}; + std::vector> dos_; +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_DIGITAL_OUTPUTS_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/encoder.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/encoder.hpp new file mode 100644 index 0000000..6ed06ec --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/encoder.hpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_ENCODER_HPP +#define PHIDGETS_API_ENCODER_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class Encoder final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Encoder) + + explicit Encoder( + int32_t serial_number, int hub_port, bool is_hub_port_device, + int channel, + std::function position_change_handler); + + ~Encoder(); + + int32_t getSerialNumber() const noexcept; + + /** @brief Reads the current position of an encoder + */ + int64_t getPosition() const; + + /** @brief Sets the offset of an encoder such that current position is the + * specified value + * @param position The new value that should be returned by + * 'getPosition(index)' at the current position of the encoder*/ + void setPosition(int64_t position) const; + + /** @brief Gets the position of an encoder the last time an index pulse + * occured. An index pulse in this context refers to an input from the + * encoder the pulses high once every revolution. + */ + int64_t getIndexPosition() const; + + /** @brief Checks if an encoder is powered on and receiving events + */ + bool getEnabled() const; + + /** @brief Set the powered state of an encoder. If an encoder is not + * enabled, it will not be given power, and events and changes in position + * will not be captured. + * @param enabled The new powered state of the encoder*/ + void setEnabled(bool enabled) const; + + void positionChangeHandler(int position_change, double time, + int index_triggered); + + private: + int32_t serial_number_; + int channel_; + std::function position_change_handler_; + PhidgetEncoderHandle encoder_handle_{nullptr}; + + static void PositionChangeHandler(PhidgetEncoderHandle phid, void *ctx, + int position_change, double time, + int index_triggered); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_ENCODER_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/encoders.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/encoders.hpp new file mode 100644 index 0000000..54a59c2 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/encoders.hpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_ENCODERS_HPP +#define PHIDGETS_API_ENCODERS_HPP + +#include +#include +#include + +#include "phidgets_api/encoder.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class Encoders final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Encoders) + + explicit Encoders( + int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function position_change_handler); + + ~Encoders() = default; + + int32_t getSerialNumber() const noexcept; + + /**@brief Gets the number of encoder input channels supported by this board + */ + uint32_t getEncoderCount() const; + + /** @brief Reads the current position of an encoder + * @param index The index of the encoder to read */ + int64_t getPosition(int index) const; + + /** @brief Sets the offset of an encoder such that current position is the + * specified value + * @param index The index of the encoder to set + * @param position The new value that should be returned by + * 'getPosition(index)' at the current position of the encoder*/ + void setPosition(int index, int64_t position) const; + + /** @brief Gets the position of an encoder the last time an index pulse + * occured. An index pulse in this context refers to an input from the + * encoder the pulses high once every revolution. + * @param index The index of the encoder to read */ + int64_t getIndexPosition(int index) const; + + /** @brief Checks if an encoder is powered on and receiving events + * @param index The index of the encoder to check */ + bool getEnabled(int index) const; + + /** @brief Set the powered state of an encoder. If an encoder is not + * enabled, it will not be given power, and events and changes in position + * will not be captured. + * @param index The index of the encoder to change + * @param enabled The new powered state of the encoder*/ + void setEnabled(int index, bool enabled) const; + + private: + uint32_t encoder_count_{0}; + std::vector> encs_; +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_ENCODERS_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/gyroscope.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/gyroscope.hpp new file mode 100644 index 0000000..b0326a9 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/gyroscope.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_GYROSCOPE_HPP +#define PHIDGETS_API_GYROSCOPE_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class Gyroscope final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Gyroscope) + + explicit Gyroscope( + int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function data_handler); + + ~Gyroscope(); + + int32_t getSerialNumber() const noexcept; + + void getAngularRate(double &x, double &y, double &z, + double ×tamp) const; + + void setDataInterval(uint32_t interval_ms) const; + + void zero() const; + + void dataHandler(const double angular_rate[3], double timestamp) const; + + private: + int32_t serial_number_; + std::function data_handler_; + PhidgetGyroscopeHandle gyro_handle_{nullptr}; + + static void DataHandler(PhidgetGyroscopeHandle input_handle, void *ctx, + const double angular_rate[3], double timestamp); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_GYROSCOPE_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/ir.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/ir.hpp new file mode 100644 index 0000000..050af4b --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/ir.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_IR_HPP +#define PHIDGETS_API_IR_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class IR final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(IR) + + explicit IR(int32_t serial_number, + std::function code_handler); + + ~IR(); + + int32_t getSerialNumber() const noexcept; + + void codeHandler(const char *code, uint32_t bit_count, int is_repeat) const; + + private: + int32_t serial_number_; + std::function code_handler_; + PhidgetIRHandle ir_handle_{nullptr}; + + static void CodeHandler(PhidgetIRHandle ir, void *ctx, const char *code, + uint32_t bit_count, int is_repeat); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_IR_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/magnetometer.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/magnetometer.hpp new file mode 100644 index 0000000..f13684a --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/magnetometer.hpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_MAGNETOMETER_HPP +#define PHIDGETS_API_MAGNETOMETER_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class Magnetometer final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Magnetometer) + + explicit Magnetometer( + int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function data_handler); + + ~Magnetometer(); + + int32_t getSerialNumber() const noexcept; + + void setCompassCorrectionParameters(double cc_mag_field, double cc_offset0, + double cc_offset1, double cc_offset2, + double cc_gain0, double cc_gain1, + double cc_gain2, double cc_T0, + double cc_T1, double cc_T2, + double cc_T3, double cc_T4, + double cc_T5); + + void getMagneticField(double &x, double &y, double &z, + double ×tamp) const; + + void setDataInterval(uint32_t interval_ms) const; + + void dataHandler(const double magnetic_field[3], double timestamp) const; + + private: + int32_t serial_number_; + std::function data_handler_; + PhidgetMagnetometerHandle mag_handle_{nullptr}; + + static void DataHandler(PhidgetMagnetometerHandle input_handle, void *ctx, + const double magnetic_field[3], double timestamp); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_MAGNETOMETER_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/motor.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/motor.hpp new file mode 100644 index 0000000..aab3ad5 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/motor.hpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_MOTOR_HPP +#define PHIDGETS_API_MOTOR_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class Motor final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Motor) + + explicit Motor(int32_t serial_number, int hub_port, bool is_hub_port_device, + int channel, + std::function duty_cycle_change_handler, + std::function back_emf_change_handler); + + ~Motor(); + + int32_t getSerialNumber() const noexcept; + + double getDutyCycle() const; + void setDutyCycle(double duty_cycle) const; + double getAcceleration() const; + void setAcceleration(double acceleration) const; + bool backEMFSensingSupported() const; + double getBackEMF() const; + void setDataInterval(uint32_t data_interval_ms) const; + + double getBraking() const; + void setBraking(double braking) const; + + void dutyCycleChangeHandler(double duty_cycle) const; + + void backEMFChangeHandler(double back_emf) const; + + private: + int32_t serial_number_; + int channel_; + std::function duty_cycle_change_handler_; + std::function back_emf_change_handler_; + PhidgetDCMotorHandle motor_handle_{nullptr}; + bool back_emf_sensing_supported_; + + static void DutyCycleChangeHandler(PhidgetDCMotorHandle motor_handle, + void *ctx, double duty_cycle); + static void BackEMFChangeHandler(PhidgetDCMotorHandle motor_handle, + void *ctx, double back_emf); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_MOTOR_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/motors.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/motors.hpp new file mode 100644 index 0000000..c7a450d --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/motors.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_MOTORS_HPP +#define PHIDGETS_API_MOTORS_HPP + +#include +#include +#include + +#include "phidgets_api/motor.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class Motors final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Motors) + + explicit Motors(int32_t serial_number, int hub_port, + bool is_hub_port_device, + std::function duty_cycle_change_handler, + std::function back_emf_change_handler); + + ~Motors() = default; + + int32_t getSerialNumber() const noexcept; + + uint32_t getMotorCount() const noexcept; + double getDutyCycle(int index) const; + void setDutyCycle(int index, double duty_cycle) const; + double getAcceleration(int index) const; + void setAcceleration(int index, double acceleration) const; + bool backEMFSensingSupported(int index) const; + double getBackEMF(int index) const; + void setDataInterval(int index, uint32_t data_interval_ms) const; + + double getBraking(int index) const; + void setBraking(int index, double braking) const; + + private: + uint32_t motor_count_{0}; + std::vector> motors_; +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_MOTORS_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/phidget22.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/phidget22.hpp new file mode 100644 index 0000000..7c31a26 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/phidget22.hpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_PHIDGET22_HPP +#define PHIDGETS_API_PHIDGET22_HPP + +#include +#include + +#include + +#define PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Classname) \ + Classname(const Classname &) = delete; \ + void operator=(const Classname &) = delete; \ + Classname(Classname &&) = delete; \ + void operator=(Classname &&) = delete; + +namespace phidgets { + +class Phidget22Error final : public std::exception +{ + public: + explicit Phidget22Error(const std::string &msg, PhidgetReturnCode code); + + const char *what() const noexcept override; + + private: + std::string msg_; +}; + +namespace helpers { + +void openWaitForAttachment(PhidgetHandle handle, int32_t serial_number, + int hub_port, bool is_hub_port_device, int channel); + +void closeAndDelete(PhidgetHandle *handle) noexcept; + +} // namespace helpers +} // namespace phidgets + +#endif // PHIDGETS_API_PHIDGET22_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/spatial.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/spatial.hpp new file mode 100644 index 0000000..ffb1302 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/spatial.hpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_SPATIAL_HPP +#define PHIDGETS_API_SPATIAL_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +class Spatial final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Spatial) + + explicit Spatial( + int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function + data_handler, + std::function algorithm_data_handler, + std::function attach_handler = nullptr, + std::function detach_handler = nullptr); + + ~Spatial(); + + int32_t getSerialNumber() const noexcept; + + void setCompassCorrectionParameters(double cc_mag_field, double cc_offset0, + double cc_offset1, double cc_offset2, + double cc_gain0, double cc_gain1, + double cc_gain2, double cc_T0, + double cc_T1, double cc_T2, + double cc_T3, double cc_T4, + double cc_T5); + + void setSpatialAlgorithm(const std::string algorithm); + + void setAHRSParameters(double angularVelocityThreshold, + double angularVelocityDeltaThreshold, + double accelerationThreshold, double magTime, + double accelTime, double biasTime); + + void setAlgorithmMagnetometerGain(double magnetometer_gain); + + void setHeatingEnabled(bool heating_enabled); + + void setDataInterval(uint32_t interval_ms) const; + + void zero() const; + + void dataHandler(const double acceleration[3], const double angular_rate[3], + const double magnetic_field[3], double timestamp) const; + + void algorithmDataHandler(const double quaternion[4], + double timestamp) const; + + virtual void attachHandler(); + virtual void detachHandler(); + + private: + int32_t serial_number_; + std::function + data_handler_; + + std::function + algorithm_data_handler_; + + std::function attach_handler_; + std::function detach_handler_; + + PhidgetSpatialHandle spatial_handle_{nullptr}; + + static void DataHandler(PhidgetSpatialHandle input_handle, void *ctx, + const double acceleration[3], + const double angular_rate[3], + const double magnetic_field[3], double timestamp); + static void AlgorithmDataHandler(PhidgetSpatialHandle input_handle, + void *ctx, const double quaternion[4], + double timestamp); + static void AttachHandler(PhidgetHandle input_handle, void *ctx); + static void DetachHandler(PhidgetHandle input_handle, void *ctx); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_SPATIAL_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/temperature.hpp b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/temperature.hpp new file mode 100644 index 0000000..5d20404 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/include/phidgets_api/temperature.hpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_API_TEMPERATURE_HPP +#define PHIDGETS_API_TEMPERATURE_HPP + +#include + +#include + +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +enum class ThermocoupleType { + J_TYPE = 1, + K_TYPE = 2, + E_TYPE = 3, + T_TYPE = 4, +}; + +class Temperature final +{ + public: + PHIDGET22_NO_COPY_NO_MOVE_NO_ASSIGN(Temperature) + + explicit Temperature(int32_t serial_number, int hub_port, + bool is_hub_port_device, + std::function temperature_handler); + + ~Temperature(); + + int32_t getSerialNumber() const noexcept; + + void setThermocoupleType(ThermocoupleType type); + + double getTemperature() const; + + void setDataInterval(uint32_t interval_ms) const; + + void temperatureChangeHandler(double temperature) const; + + private: + int32_t serial_number_; + std::function temperature_handler_; + PhidgetTemperatureSensorHandle temperature_handle_{nullptr}; + + static void TemperatureChangeHandler( + PhidgetTemperatureSensorHandle temperature_handle, void *ctx, + double temperature); +}; + +} // namespace phidgets + +#endif // PHIDGETS_API_TEMPERATURE_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_api/package.xml b/docker/testBot/phidgets_drivers/phidgets_api/package.xml new file mode 100644 index 0000000..c1c096d --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/package.xml @@ -0,0 +1,26 @@ + + + phidgets_api + 2.3.3 + A C++ Wrapper for the Phidgets C API + + Martin Günther + Chris Lalancette + + BSD + + http://ros.org/wiki/phidgets_api + https://github.com/ros-drivers/phidgets_drivers.git + https://github.com/ros-drivers/phidgets_drivers/issues + + Tully Foote + Ivan Dryanovski + + ament_cmake_ros + + libphidget22 + + + ament_cmake + + diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/accelerometer.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/accelerometer.cpp new file mode 100644 index 0000000..869c498 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/accelerometer.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 +#include + +#include + +#include "phidgets_api/accelerometer.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +Accelerometer::Accelerometer( + int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function data_handler) + : serial_number_(serial_number), data_handler_(data_handler) +{ + PhidgetReturnCode ret = PhidgetAccelerometer_create(&accel_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to create Accelerometer handle", ret); + } + + helpers::openWaitForAttachment( + reinterpret_cast(accel_handle_), serial_number, hub_port, + is_hub_port_device, 0); + + ret = PhidgetAccelerometer_setOnAccelerationChangeHandler( + accel_handle_, DataHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set change handler for acceleration", + ret); + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(accel_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get serial number for acceleration", + ret); + } + } +} + +Accelerometer::~Accelerometer() +{ + PhidgetHandle handle = reinterpret_cast(accel_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t Accelerometer::getSerialNumber() const noexcept +{ + return serial_number_; +} + +void Accelerometer::getAcceleration(double &x, double &y, double &z, + double ×tamp) const +{ + double accel[3]; + PhidgetReturnCode ret = + PhidgetAccelerometer_getAcceleration(accel_handle_, &accel); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get acceleration", ret); + } + + x = accel[0]; + y = accel[1]; + z = accel[2]; + + double ts; + ret = PhidgetAccelerometer_getTimestamp(accel_handle_, &ts); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get acceleration timestamp", ret); + } + + timestamp = ts; +} + +void Accelerometer::setDataInterval(uint32_t interval_ms) const +{ + PhidgetReturnCode ret = + PhidgetAccelerometer_setDataInterval(accel_handle_, interval_ms); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set data interval", ret); + } +} + +void Accelerometer::dataHandler(const double acceleration[3], + double timestamp) const +{ + data_handler_(acceleration, timestamp); +} + +void Accelerometer::DataHandler(PhidgetAccelerometerHandle /* input_handle */, + void *ctx, const double acceleration[3], + double timestamp) +{ + (reinterpret_cast(ctx)) + ->dataHandler(acceleration, timestamp); +} + +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/analog_input.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/analog_input.cpp new file mode 100644 index 0000000..4298bce --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/analog_input.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 +#include + +#include + +#include "phidgets_api/analog_input.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +AnalogInput::AnalogInput(int32_t serial_number, int hub_port, + bool is_hub_port_device, int channel, + std::function input_handler) + : serial_number_(serial_number), + channel_(channel), + input_handler_(input_handler) +{ + PhidgetReturnCode ret = PhidgetVoltageInput_create(&ai_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create AnalogInput handle for channel " + + std::to_string(channel), + ret); + } + + helpers::openWaitForAttachment(reinterpret_cast(ai_handle_), + serial_number, hub_port, is_hub_port_device, + channel); + + if (!is_hub_port_device) + { + ret = + PhidgetVoltageInput_setVoltageRange(ai_handle_, VOLTAGE_RANGE_AUTO); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set analog input voltage range", + ret); + } + } + + ret = PhidgetVoltageInput_setOnVoltageChangeHandler( + ai_handle_, VoltageChangeHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to set change handler for AnalogInput channel " + + std::to_string(channel), + ret); + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(ai_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to get serial number for analog input channel " + + std::to_string(channel), + ret); + } + } +} + +AnalogInput::~AnalogInput() +{ + PhidgetHandle handle = reinterpret_cast(ai_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t AnalogInput::getSerialNumber() const noexcept +{ + return serial_number_; +} + +double AnalogInput::getSensorValue() const +{ + double sensor_value; + PhidgetReturnCode ret = + PhidgetVoltageInput_getSensorValue(ai_handle_, &sensor_value); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get analog sensor value", ret); + } + + return sensor_value; +} + +void AnalogInput::setDataInterval(uint32_t data_interval_ms) const +{ + PhidgetReturnCode ret = + PhidgetVoltageInput_setDataInterval(ai_handle_, data_interval_ms); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set analog data interval", ret); + } +} + +void AnalogInput::voltageChangeHandler(double sensorValue) const +{ + input_handler_(channel_, sensorValue); +} + +void AnalogInput::VoltageChangeHandler( + PhidgetVoltageInputHandle /* input_handle */, void *ctx, double sensorValue) +{ + (reinterpret_cast(ctx))->voltageChangeHandler(sensorValue); +} + +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/analog_inputs.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/analog_inputs.cpp new file mode 100644 index 0000000..a730b39 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/analog_inputs.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 +#include + +#include + +#include "phidgets_api/analog_input.hpp" +#include "phidgets_api/analog_inputs.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +AnalogInputs::AnalogInputs(int32_t serial_number, int hub_port, + bool is_hub_port_device, + std::function input_handler) +{ + PhidgetReturnCode ret; + + PhidgetVoltageInputHandle ai_handle; + + ret = PhidgetVoltageInput_create(&ai_handle); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create AnalogInput handle for determining channel " + "count", + ret); + } + + PhidgetHandle handle = reinterpret_cast(ai_handle); + + helpers::openWaitForAttachment(handle, serial_number, hub_port, + is_hub_port_device, 0); + + ret = Phidget_getDeviceChannelCount(handle, PHIDCHCLASS_VOLTAGEINPUT, + &input_count_); + + helpers::closeAndDelete(&handle); + + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get AnalogInput device channel count", + ret); + } + + ais_.resize(input_count_); + for (uint32_t i = 0; i < input_count_; ++i) + { + ais_[i] = std::make_unique( + serial_number, hub_port, is_hub_port_device, i, input_handler); + } +} + +int32_t AnalogInputs::getSerialNumber() const noexcept +{ + return ais_.at(0)->getSerialNumber(); +} + +uint32_t AnalogInputs::getInputCount() const noexcept +{ + return input_count_; +} + +double AnalogInputs::getSensorValue(int index) const +{ + return ais_.at(index)->getSensorValue(); +} + +void AnalogInputs::setDataInterval(int index, uint32_t data_interval_ms) const +{ + ais_.at(index)->setDataInterval(data_interval_ms); +} + +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/analog_output.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/analog_output.cpp new file mode 100644 index 0000000..6e1619f --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/analog_output.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/analog_output.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +AnalogOutput::AnalogOutput(int32_t serial_number, int hub_port, + bool is_hub_port_device, int channel) +{ + PhidgetReturnCode ret; + ret = PhidgetVoltageOutput_create(&ao_handle_); + + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create AnalogOutput handle for channel " + + std::to_string(channel), + ret); + } + + helpers::openWaitForAttachment(reinterpret_cast(ao_handle_), + serial_number, hub_port, is_hub_port_device, + channel); +} + +AnalogOutput::~AnalogOutput() +{ + PhidgetHandle handle = reinterpret_cast(ao_handle_); + helpers::closeAndDelete(&handle); +} + +void AnalogOutput::setOutputVoltage(double voltage) const +{ + PhidgetReturnCode ret = + PhidgetVoltageOutput_setVoltage(ao_handle_, voltage); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set analog output voltage", ret); + } +} + +void AnalogOutput::setEnabledOutput(int enabled) const +{ + PhidgetReturnCode ret = + PhidgetVoltageOutput_setEnabled(ao_handle_, enabled); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set analog output enabled", ret); + } +} + +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/analog_outputs.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/analog_outputs.cpp new file mode 100644 index 0000000..bb5cc89 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/analog_outputs.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 "phidgets_api/analog_output.hpp" +#include "phidgets_api/analog_outputs.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +AnalogOutputs::AnalogOutputs(int32_t serial_number, int hub_port, + bool is_hub_port_device) +{ + PhidgetReturnCode ret; + + PhidgetVoltageOutputHandle ao_handle; + + ret = PhidgetVoltageOutput_create(&ao_handle); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create AnalogOutput handle for determining channel " + "count", + ret); + } + + PhidgetHandle handle = reinterpret_cast(ao_handle); + + helpers::openWaitForAttachment(handle, serial_number, hub_port, + is_hub_port_device, 0); + + ret = Phidget_getDeviceChannelCount(handle, PHIDCHCLASS_VOLTAGEOUTPUT, + &output_count_); + + helpers::closeAndDelete(&handle); + + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get AnalogOutput device channel count", + ret); + } + + aos_.resize(output_count_); + for (uint32_t i = 0; i < output_count_; ++i) + { + aos_[i] = std::make_unique(serial_number, hub_port, + is_hub_port_device, i); + } +} + +AnalogOutputs::~AnalogOutputs() +{ +} + +uint32_t AnalogOutputs::getOutputCount() const noexcept +{ + return output_count_; +} + +void AnalogOutputs::setOutputVoltage(int index, double voltage) const +{ + aos_.at(index)->setOutputVoltage(voltage); +} + +void AnalogOutputs::setEnabledOutput(int index, int enabled) const +{ + aos_.at(index)->setEnabledOutput(enabled); +} + +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/digital_input.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/digital_input.cpp new file mode 100644 index 0000000..323cde3 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/digital_input.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/digital_input.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +DigitalInput::DigitalInput(int32_t serial_number, int hub_port, + bool is_hub_port_device, int channel, + std::function input_handler) + : serial_number_(serial_number), + channel_(channel), + input_handler_(input_handler) +{ + PhidgetReturnCode ret = PhidgetDigitalInput_create(&di_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create DigitalInput handle for channel " + + std::to_string(channel), + ret); + } + + helpers::openWaitForAttachment(reinterpret_cast(di_handle_), + serial_number, hub_port, is_hub_port_device, + channel); + + ret = PhidgetDigitalInput_setOnStateChangeHandler(di_handle_, + StateChangeHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to set change handler for DigitalInput channel " + + std::to_string(channel), + ret); + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(di_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to get serial number for digital input channel " + + std::to_string(channel), + ret); + } + } +} + +DigitalInput::~DigitalInput() +{ + PhidgetHandle handle = reinterpret_cast(di_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t DigitalInput::getSerialNumber() const noexcept +{ + return serial_number_; +} + +bool DigitalInput::getInputValue() const +{ + int state; + PhidgetReturnCode ret = PhidgetDigitalInput_getState(di_handle_, &state); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get digital input state", ret); + } + + return !!state; +} + +void DigitalInput::stateChangeHandler(int state) const +{ + input_handler_(channel_, state); +} + +void DigitalInput::StateChangeHandler( + PhidgetDigitalInputHandle /* input_handle */, void *ctx, int state) +{ + (reinterpret_cast(ctx))->stateChangeHandler(state); +} + +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/digital_inputs.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/digital_inputs.cpp new file mode 100644 index 0000000..fd404aa --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/digital_inputs.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/digital_input.hpp" +#include "phidgets_api/digital_inputs.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +DigitalInputs::DigitalInputs(int32_t serial_number, int hub_port, + bool is_hub_port_device, + std::function input_handler) +{ + PhidgetReturnCode ret; + + PhidgetDigitalInputHandle di_handle; + + ret = PhidgetDigitalInput_create(&di_handle); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create DigitalInput handle for determining channel " + "count", + ret); + } + + PhidgetHandle handle = reinterpret_cast(di_handle); + + helpers::openWaitForAttachment(handle, serial_number, hub_port, + is_hub_port_device, 0); + + ret = Phidget_getDeviceChannelCount(handle, PHIDCHCLASS_DIGITALINPUT, + &input_count_); + + helpers::closeAndDelete(&handle); + + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get DigitalInput device channel count", + ret); + } + + dis_.resize(input_count_); + for (uint32_t i = 0; i < input_count_; ++i) + { + dis_[i] = std::make_unique( + serial_number, hub_port, is_hub_port_device, i, input_handler); + } +} + +int32_t DigitalInputs::getSerialNumber() const noexcept +{ + return dis_.at(0)->getSerialNumber(); +} + +uint32_t DigitalInputs::getInputCount() const noexcept +{ + return input_count_; +} + +bool DigitalInputs::getInputValue(int index) const +{ + return dis_.at(index)->getInputValue(); +} + +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/digital_output.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/digital_output.cpp new file mode 100644 index 0000000..48daf40 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/digital_output.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/digital_output.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +DigitalOutput::DigitalOutput(int32_t serial_number, int hub_port, + bool is_hub_port_device, int channel) + : serial_number_(serial_number) +{ + PhidgetReturnCode ret; + + ret = PhidgetDigitalOutput_create(&do_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create DigitalOutput handle for channel " + + std::to_string(channel), + ret); + } + + helpers::openWaitForAttachment(reinterpret_cast(do_handle_), + serial_number, hub_port, is_hub_port_device, + channel); + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(do_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to get serial number for digital output channel " + + std::to_string(channel), + ret); + } + } +} + +DigitalOutput::~DigitalOutput() +{ + PhidgetHandle handle = reinterpret_cast(do_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t DigitalOutput::getSerialNumber() const noexcept +{ + return serial_number_; +} + +void DigitalOutput::setOutputState(bool state) const +{ + PhidgetReturnCode ret = PhidgetDigitalOutput_setState(do_handle_, state); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set state for DigitalOutput", ret); + } +} + +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/digital_outputs.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/digital_outputs.cpp new file mode 100644 index 0000000..5f96ebd --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/digital_outputs.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/digital_output.hpp" +#include "phidgets_api/digital_outputs.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +DigitalOutputs::DigitalOutputs(int32_t serial_number, int hub_port, + bool is_hub_port_device) +{ + PhidgetReturnCode ret; + + PhidgetDigitalOutputHandle do_handle; + + ret = PhidgetDigitalOutput_create(&do_handle); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create DigitalOutput handle for determining channel " + "count", + ret); + } + + PhidgetHandle handle = reinterpret_cast(do_handle); + + helpers::openWaitForAttachment(handle, serial_number, hub_port, + is_hub_port_device, 0); + + ret = Phidget_getDeviceChannelCount(handle, PHIDCHCLASS_DIGITALOUTPUT, + &output_count_); + + helpers::closeAndDelete(&handle); + + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get DigitalOutput device channel count", + ret); + } + + dos_.resize(output_count_); + for (uint32_t i = 0; i < output_count_; ++i) + { + dos_[i] = std::make_unique(serial_number, hub_port, + is_hub_port_device, i); + } +} + +int32_t DigitalOutputs::getSerialNumber() const noexcept +{ + return dos_.at(0)->getSerialNumber(); +} + +uint32_t DigitalOutputs::getOutputCount() const noexcept +{ + return output_count_; +} + +void DigitalOutputs::setOutputState(int index, bool state) const +{ + dos_.at(index)->setOutputState(state); +} + +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/encoder.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/encoder.cpp new file mode 100644 index 0000000..31731a5 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/encoder.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 "phidgets_api/encoder.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +Encoder::Encoder( + int32_t serial_number, int hub_port, bool is_hub_port_device, int channel, + std::function position_change_handler) + : serial_number_(serial_number), + channel_(channel), + position_change_handler_(position_change_handler) +{ + // create the handle + PhidgetReturnCode ret = PhidgetEncoder_create(&encoder_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to create Encoder handle", ret); + } + + helpers::openWaitForAttachment( + reinterpret_cast(encoder_handle_), serial_number, + hub_port, is_hub_port_device, channel); + + ret = PhidgetEncoder_setOnPositionChangeHandler( + encoder_handle_, PositionChangeHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to set change handler for Encoder channel " + + std::to_string(channel), + ret); + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(encoder_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to get serial number for encoder channel " + + std::to_string(channel), + ret); + } + } +} + +Encoder::~Encoder() +{ + PhidgetHandle handle = reinterpret_cast(encoder_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t Encoder::getSerialNumber() const noexcept +{ + return serial_number_; +} + +int64_t Encoder::getPosition() const +{ + int64_t position; + PhidgetReturnCode ret = + PhidgetEncoder_getPosition(encoder_handle_, &position); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get position for Encoder channel " + + std::to_string(channel_), + ret); + } + + return position; +} + +void Encoder::setPosition(int64_t position) const +{ + PhidgetReturnCode ret = + PhidgetEncoder_setPosition(encoder_handle_, position); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set position for Encoder channel " + + std::to_string(channel_), + ret); + } +} + +int64_t Encoder::getIndexPosition() const +{ + int64_t position; + PhidgetReturnCode ret = + PhidgetEncoder_getIndexPosition(encoder_handle_, &position); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to get index position for Encoder channel " + + std::to_string(channel_), + ret); + } + + return position; +} + +bool Encoder::getEnabled() const +{ + int enabled; + PhidgetReturnCode ret = + PhidgetEncoder_getEnabled(encoder_handle_, &enabled); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get enabled for Encoder channel " + + std::to_string(channel_), + ret); + } + + return enabled == PTRUE; +} + +void Encoder::setEnabled(bool enabled) const +{ + PhidgetReturnCode ret = + PhidgetEncoder_setEnabled(encoder_handle_, enabled ? PTRUE : PFALSE); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set enabled for Encoder channel " + + std::to_string(channel_), + ret); + } +} + +void Encoder::positionChangeHandler(int position_change, double time, + int index_triggered) +{ + position_change_handler_(channel_, position_change, time, index_triggered); +} + +void Encoder::PositionChangeHandler(PhidgetEncoderHandle /* phid */, void *ctx, + int position_change, double time, + int index_triggered) +{ + (reinterpret_cast(ctx)) + ->positionChangeHandler(position_change, time, index_triggered); +} + +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/encoders.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/encoders.cpp new file mode 100644 index 0000000..548c472 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/encoders.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/encoder.hpp" +#include "phidgets_api/encoders.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +Encoders::Encoders( + int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function position_change_handler) +{ + PhidgetReturnCode ret; + + PhidgetEncoderHandle enc_handle; + + ret = PhidgetEncoder_create(&enc_handle); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create Encoder handle for determining channel " + "count", + ret); + } + + PhidgetHandle handle = reinterpret_cast(enc_handle); + + helpers::openWaitForAttachment(handle, serial_number, hub_port, + is_hub_port_device, 0); + + ret = Phidget_getDeviceChannelCount(handle, PHIDCHCLASS_ENCODER, + &encoder_count_); + + helpers::closeAndDelete(&handle); + + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get Encoder device channel count", ret); + } + + encs_.resize(encoder_count_); + for (uint32_t i = 0; i < encoder_count_; ++i) + { + encs_[i] = std::make_unique(serial_number, hub_port, + is_hub_port_device, i, + position_change_handler); + } +} + +int32_t Encoders::getSerialNumber() const noexcept +{ + return encs_.at(0)->getSerialNumber(); +} + +uint32_t Encoders::getEncoderCount() const +{ + return encoder_count_; +} + +int64_t Encoders::getPosition(int index) const +{ + return encs_.at(index)->getPosition(); +} + +void Encoders::setPosition(int index, int64_t position) const +{ + return encs_.at(index)->setPosition(position); +} + +int64_t Encoders::getIndexPosition(int index) const +{ + return encs_.at(index)->getIndexPosition(); +} + +bool Encoders::getEnabled(int index) const +{ + return encs_.at(index)->getEnabled(); +} + +void Encoders::setEnabled(int index, bool enabled) const +{ + return encs_.at(index)->setEnabled(enabled); +} + +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/gyroscope.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/gyroscope.cpp new file mode 100644 index 0000000..5fca8a9 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/gyroscope.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 +#include + +#include + +#include "phidgets_api/gyroscope.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +Gyroscope::Gyroscope(int32_t serial_number, int hub_port, + bool is_hub_port_device, + std::function data_handler) + : serial_number_(serial_number), data_handler_(data_handler) +{ + PhidgetReturnCode ret = PhidgetGyroscope_create(&gyro_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to create Gyroscope handle", ret); + } + + helpers::openWaitForAttachment( + reinterpret_cast(gyro_handle_), serial_number, hub_port, + is_hub_port_device, 0); + + ret = PhidgetGyroscope_setOnAngularRateUpdateHandler(gyro_handle_, + DataHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set Gyroscope update handler", ret); + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(gyro_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get serial number for gyroscope", + ret); + } + } +} + +Gyroscope::~Gyroscope() +{ + PhidgetHandle handle = reinterpret_cast(gyro_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t Gyroscope::getSerialNumber() const noexcept +{ + return serial_number_; +} + +void Gyroscope::zero() const +{ + PhidgetReturnCode ret = PhidgetGyroscope_zero(gyro_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to calibrate Gyroscope", ret); + } +} + +void Gyroscope::getAngularRate(double &x, double &y, double &z, + double ×tamp) const +{ + double angular_rate[3]; + PhidgetReturnCode ret = + PhidgetGyroscope_getAngularRate(gyro_handle_, &angular_rate); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get angular rate from gyroscope", ret); + } + + x = angular_rate[0]; + y = angular_rate[1]; + z = angular_rate[2]; + + double ts; + ret = PhidgetGyroscope_getTimestamp(gyro_handle_, &ts); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get timestamp from gyroscope", ret); + } + + timestamp = ts; +} + +void Gyroscope::setDataInterval(uint32_t interval_ms) const +{ + PhidgetReturnCode ret = + PhidgetGyroscope_setDataInterval(gyro_handle_, interval_ms); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set data interval", ret); + } +} + +void Gyroscope::dataHandler(const double angular_rate[3], + double timestamp) const +{ + data_handler_(angular_rate, timestamp); +} + +void Gyroscope::DataHandler(PhidgetGyroscopeHandle /* input_handle */, + void *ctx, const double angular_rate[3], + double timestamp) +{ + (reinterpret_cast(ctx))->dataHandler(angular_rate, timestamp); +} + +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/ir.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/ir.cpp new file mode 100644 index 0000000..f80c0f3 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/ir.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 "phidgets_api/ir.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +IR::IR(int32_t serial_number, + std::function code_handler) + : serial_number_(serial_number), code_handler_(code_handler) +{ + // create the handle + PhidgetReturnCode ret = PhidgetIR_create(&ir_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to create IR handle", ret); + } + + helpers::openWaitForAttachment(reinterpret_cast(ir_handle_), + serial_number, 0, false, 0); + + // register ir data callback + ret = PhidgetIR_setOnCodeHandler(ir_handle_, CodeHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set code handler for ir", ret); + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(ir_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get serial number for IR", ret); + } + } +} + +IR::~IR() +{ + PhidgetHandle handle = reinterpret_cast(ir_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t IR::getSerialNumber() const noexcept +{ + return serial_number_; +} + +void IR::codeHandler(const char *code, uint32_t bit_count, int is_repeat) const +{ + code_handler_(code, bit_count, is_repeat); +} + +void IR::CodeHandler(PhidgetIRHandle /* ir */, void *ctx, const char *code, + uint32_t bit_count, int is_repeat) +{ + (reinterpret_cast(ctx))->codeHandler(code, bit_count, is_repeat); +} + +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/magnetometer.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/magnetometer.cpp new file mode 100644 index 0000000..69fcb89 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/magnetometer.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 +#include + +#include + +#include "phidgets_api/magnetometer.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +Magnetometer::Magnetometer( + int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function data_handler) + : serial_number_(serial_number), data_handler_(data_handler) +{ + PhidgetReturnCode ret = PhidgetMagnetometer_create(&mag_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to create Magnetometer handle", ret); + } + + helpers::openWaitForAttachment(reinterpret_cast(mag_handle_), + serial_number, hub_port, is_hub_port_device, + 0); + + ret = PhidgetMagnetometer_setOnMagneticFieldChangeHandler( + mag_handle_, DataHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set change handler for Magnetometer", + ret); + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(mag_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get serial number for magnetometer", + ret); + } + } +} + +Magnetometer::~Magnetometer() +{ + PhidgetHandle handle = reinterpret_cast(mag_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t Magnetometer::getSerialNumber() const noexcept +{ + return serial_number_; +} + +void Magnetometer::setCompassCorrectionParameters( + double cc_mag_field, double cc_offset0, double cc_offset1, + double cc_offset2, double cc_gain0, double cc_gain1, double cc_gain2, + double cc_T0, double cc_T1, double cc_T2, double cc_T3, double cc_T4, + double cc_T5) +{ + PhidgetReturnCode ret = PhidgetMagnetometer_setCorrectionParameters( + mag_handle_, cc_mag_field, cc_offset0, cc_offset1, cc_offset2, cc_gain0, + cc_gain1, cc_gain2, cc_T0, cc_T1, cc_T2, cc_T3, cc_T4, cc_T5); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set magnetometer correction parameters", + ret); + } +} + +void Magnetometer::getMagneticField(double &x, double &y, double &z, + double ×tamp) const +{ + double mag_field[3]; + PhidgetReturnCode ret = + PhidgetMagnetometer_getMagneticField(mag_handle_, &mag_field); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get magnetic field", ret); + } + + x = mag_field[0]; + y = mag_field[1]; + z = mag_field[2]; + + double ts; + ret = PhidgetMagnetometer_getTimestamp(mag_handle_, &ts); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get magnetic field timestamp", ret); + } + + timestamp = ts; +} + +void Magnetometer::setDataInterval(uint32_t interval_ms) const +{ + PhidgetReturnCode ret = + PhidgetMagnetometer_setDataInterval(mag_handle_, interval_ms); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set data interval", ret); + } +} + +void Magnetometer::dataHandler(const double magnetic_field[3], + double timestamp) const +{ + data_handler_(magnetic_field, timestamp); +} + +void Magnetometer::DataHandler(PhidgetMagnetometerHandle /* input_handle */, + void *ctx, const double magnetic_field[3], + double timestamp) +{ + (reinterpret_cast(ctx)) + ->dataHandler(magnetic_field, timestamp); +} + +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/motor.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/motor.cpp new file mode 100644 index 0000000..3b14270 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/motor.cpp @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/motor.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +Motor::Motor(int32_t serial_number, int hub_port, bool is_hub_port_device, + int channel, + std::function duty_cycle_change_handler, + std::function back_emf_change_handler) + : serial_number_(serial_number), + channel_(channel), + duty_cycle_change_handler_(duty_cycle_change_handler), + back_emf_change_handler_(back_emf_change_handler) +{ + PhidgetReturnCode ret = PhidgetDCMotor_create(&motor_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to create Motor handle for channel " + + std::to_string(channel), + ret); + } + + helpers::openWaitForAttachment( + reinterpret_cast(motor_handle_), serial_number, hub_port, + is_hub_port_device, channel); + + ret = PhidgetDCMotor_setOnVelocityUpdateHandler( + motor_handle_, DutyCycleChangeHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to set duty cycle update handler for Motor channel " + + std::to_string(channel), + ret); + } + + back_emf_sensing_supported_ = true; + ret = PhidgetDCMotor_setBackEMFSensingState(motor_handle_, 1); + if (ret == EPHIDGET_UNSUPPORTED) + { + back_emf_sensing_supported_ = false; + } else if (ret == EPHIDGET_OK) + { + ret = PhidgetDCMotor_setOnBackEMFChangeHandler( + motor_handle_, BackEMFChangeHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to set back EMF update handler for Motor channel " + + std::to_string(channel), + ret); + } + } else + { + throw Phidget22Error( + "Failed to set back EMF sensing state Motor channel " + + std::to_string(channel), + ret); + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(motor_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to get serial number for motor channel " + + std::to_string(channel), + ret); + } + } +} + +Motor::~Motor() +{ + PhidgetHandle handle = reinterpret_cast(motor_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t Motor::getSerialNumber() const noexcept +{ + return serial_number_; +} + +double Motor::getDutyCycle() const +{ + double duty_cycle; + PhidgetReturnCode ret = + PhidgetDCMotor_getVelocity(motor_handle_, &duty_cycle); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get duty cycle for Motor channel " + + std::to_string(channel_), + ret); + } + return duty_cycle; +} + +void Motor::setDutyCycle(double duty_cycle) const +{ + PhidgetReturnCode ret = + PhidgetDCMotor_setTargetVelocity(motor_handle_, duty_cycle); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set duty cycle for Motor channel " + + std::to_string(channel_), + ret); + } +} + +double Motor::getAcceleration() const +{ + double accel; + PhidgetReturnCode ret = + PhidgetDCMotor_getAcceleration(motor_handle_, &accel); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get acceleration for Motor channel " + + std::to_string(channel_), + ret); + } + return accel; +} + +void Motor::setAcceleration(double acceleration) const +{ + PhidgetReturnCode ret = + PhidgetDCMotor_setAcceleration(motor_handle_, acceleration); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set acceleration for Motor channel " + + std::to_string(channel_), + ret); + } +} + +bool Motor::backEMFSensingSupported() const +{ + return back_emf_sensing_supported_; +} + +double Motor::getBackEMF() const +{ + if (!back_emf_sensing_supported_) + { + throw Phidget22Error("Back EMF sensing not supported", + EPHIDGET_UNSUPPORTED); + } + double backemf; + PhidgetReturnCode ret = PhidgetDCMotor_getBackEMF(motor_handle_, &backemf); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get back EMF for Motor channel " + + std::to_string(channel_), + ret); + } + return backemf; +} + +void Motor::setDataInterval(uint32_t data_interval_ms) const +{ + PhidgetReturnCode ret = + PhidgetDCMotor_setDataInterval(motor_handle_, data_interval_ms); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set data interval for Motor channel " + + std::to_string(channel_), + ret); + } +} + +double Motor::getBraking() const +{ + double braking; + PhidgetReturnCode ret = + PhidgetDCMotor_getBrakingStrength(motor_handle_, &braking); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to get braking strength for Motor channel " + + std::to_string(channel_), + ret); + } + return braking; +} + +void Motor::setBraking(double braking) const +{ + PhidgetReturnCode ret = + PhidgetDCMotor_setTargetBrakingStrength(motor_handle_, braking); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to set braking strength for Motor channel " + + std::to_string(channel_), + ret); + } +} + +void Motor::dutyCycleChangeHandler(double duty_cycle) const +{ + duty_cycle_change_handler_(channel_, duty_cycle); +} + +void Motor::backEMFChangeHandler(double back_emf) const +{ + back_emf_change_handler_(channel_, back_emf); +} + +void Motor::DutyCycleChangeHandler(PhidgetDCMotorHandle /* motor_handle */, + void *ctx, double duty_cycle) +{ + (reinterpret_cast(ctx))->dutyCycleChangeHandler(duty_cycle); +} + +void Motor::BackEMFChangeHandler(PhidgetDCMotorHandle /* motor_handle */, + void *ctx, double back_emf) +{ + (reinterpret_cast(ctx))->backEMFChangeHandler(back_emf); +} + +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/motors.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/motors.cpp new file mode 100644 index 0000000..4cfe6ad --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/motors.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/motors.hpp" +#include "phidgets_api/phidget22.hpp" + +namespace phidgets { + +Motors::Motors(int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function duty_cycle_change_handler, + std::function back_emf_change_handler) +{ + PhidgetReturnCode ret; + + PhidgetDCMotorHandle motor_handle; + + ret = PhidgetDCMotor_create(&motor_handle); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error( + "Failed to create Motor handle for determining channel " + "count", + ret); + } + + PhidgetHandle handle = reinterpret_cast(motor_handle); + + helpers::openWaitForAttachment(handle, serial_number, hub_port, + is_hub_port_device, 0); + + ret = Phidget_getDeviceChannelCount(handle, PHIDCHCLASS_DCMOTOR, + &motor_count_); + + helpers::closeAndDelete(&handle); + + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get Motor device channel count", ret); + } + + motors_.resize(motor_count_); + for (uint32_t i = 0; i < motor_count_; ++i) + { + motors_[i] = std::make_unique( + serial_number, hub_port, is_hub_port_device, i, + duty_cycle_change_handler, back_emf_change_handler); + } +} + +int32_t Motors::getSerialNumber() const noexcept +{ + return motors_.at(0)->getSerialNumber(); +} + +uint32_t Motors::getMotorCount() const noexcept +{ + return motor_count_; +} + +double Motors::getDutyCycle(int index) const +{ + return motors_.at(index)->getDutyCycle(); +} + +void Motors::setDutyCycle(int index, double duty_cycle) const +{ + motors_.at(index)->setDutyCycle(duty_cycle); +} + +double Motors::getAcceleration(int index) const +{ + return motors_.at(index)->getAcceleration(); +} + +void Motors::setAcceleration(int index, double acceleration) const +{ + motors_.at(index)->setAcceleration(acceleration); +} + +bool Motors::backEMFSensingSupported(int index) const +{ + return motors_.at(index)->backEMFSensingSupported(); +} + +double Motors::getBackEMF(int index) const +{ + return motors_.at(index)->getBackEMF(); +} + +void Motors::setDataInterval(int index, uint32_t data_interval_ms) const +{ + motors_.at(index)->setDataInterval(data_interval_ms); +} + +double Motors::getBraking(int index) const +{ + return motors_.at(index)->getBraking(); +} + +void Motors::setBraking(int index, double braking) const +{ + motors_.at(index)->setBraking(braking); +} + +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/phidget22.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/phidget22.cpp new file mode 100644 index 0000000..243dc02 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/phidget22.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 "phidgets_api/phidget22.hpp" + +namespace phidgets { + +Phidget22Error::Phidget22Error(const std::string &msg, PhidgetReturnCode code) +{ + const char *error_ptr; + PhidgetReturnCode ret = Phidget_getErrorDescription(code, &error_ptr); + if (ret == EPHIDGET_OK) + { + msg_ = msg + ": " + std::string(error_ptr); + } else + { + msg_ = msg + ": Unknown error"; + } +} + +const char *Phidget22Error::what() const noexcept +{ + return msg_.c_str(); +} + +namespace helpers { + +void openWaitForAttachment(PhidgetHandle handle, int32_t serial_number, + int hub_port, bool is_hub_port_device, int channel) +{ + PhidgetReturnCode ret; + + ret = Phidget_setDeviceSerialNumber(handle, serial_number); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set device serial number", ret); + } + + ret = Phidget_setHubPort(handle, hub_port); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set device hub port", ret); + } + + ret = Phidget_setIsHubPortDevice(handle, is_hub_port_device); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set device is hub port device", ret); + } + + ret = Phidget_setChannel(handle, channel); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set device channel", ret); + } + + ret = Phidget_openWaitForAttachment(handle, PHIDGET_TIMEOUT_DEFAULT); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to open device", ret); + } +} + +void closeAndDelete(PhidgetHandle *handle) noexcept +{ + Phidget_close(*handle); + Phidget_delete(handle); +} + +} // namespace helpers +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/spatial.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/spatial.cpp new file mode 100644 index 0000000..a0738d6 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/spatial.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/phidget22.hpp" +#include "phidgets_api/spatial.hpp" + +namespace phidgets { + +Spatial::Spatial( + int32_t serial_number, int hub_port, bool is_hub_port_device, + std::function + data_handler, + std::function algorithm_data_handler, + std::function attach_handler, std::function detach_handler) + : serial_number_(serial_number), + data_handler_(std::move(data_handler)), + algorithm_data_handler_(std::move(algorithm_data_handler)), + attach_handler_(std::move(attach_handler)), + detach_handler_(std::move(detach_handler)) +{ + PhidgetReturnCode ret = PhidgetSpatial_create(&spatial_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to create Spatial handle", ret); + } + + helpers::openWaitForAttachment( + reinterpret_cast(spatial_handle_), serial_number, + hub_port, is_hub_port_device, 0); + + ret = PhidgetSpatial_setOnSpatialDataHandler(spatial_handle_, DataHandler, + this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set change handler for Spatial", ret); + } + + if (algorithm_data_handler_ != nullptr) + { + ret = PhidgetSpatial_setOnAlgorithmDataHandler( + spatial_handle_, AlgorithmDataHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set algorithm handler for Spatial", + ret); + } + } + + if (attach_handler_ != nullptr) + { + ret = Phidget_setOnAttachHandler( + reinterpret_cast(spatial_handle_), AttachHandler, + this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set attach handler for Spatial", + ret); + } + } + + if (detach_handler_ != nullptr) + { + ret = Phidget_setOnDetachHandler( + reinterpret_cast(spatial_handle_), DetachHandler, + this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set detach handler for Spatial", + ret); + } + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(spatial_handle_), &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get serial number for spatial", + ret); + } + } +} + +Spatial::~Spatial() +{ + auto handle = reinterpret_cast(spatial_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t Spatial::getSerialNumber() const noexcept +{ + return serial_number_; +} + +void Spatial::zero() const +{ + PhidgetReturnCode ret = PhidgetSpatial_zeroGyro(spatial_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to calibrate Gyroscope", ret); + } +} + +void Spatial::setCompassCorrectionParameters( + double cc_mag_field, double cc_offset0, double cc_offset1, + double cc_offset2, double cc_gain0, double cc_gain1, double cc_gain2, + double cc_T0, double cc_T1, double cc_T2, double cc_T3, double cc_T4, + double cc_T5) +{ + PhidgetReturnCode ret = PhidgetSpatial_setMagnetometerCorrectionParameters( + spatial_handle_, cc_mag_field, cc_offset0, cc_offset1, cc_offset2, + cc_gain0, cc_gain1, cc_gain2, cc_T0, cc_T1, cc_T2, cc_T3, cc_T4, cc_T5); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set magnetometer correction parameters", + ret); + } +} + +void Spatial::setSpatialAlgorithm(const std::string algorithm_name) +{ + Phidget_SpatialAlgorithm algorithm; + + if (algorithm_name.compare("none") == 0) + { + algorithm = SPATIAL_ALGORITHM_NONE; + } else if (algorithm_name.compare("ahrs") == 0) + { + algorithm = SPATIAL_ALGORITHM_AHRS; + } else if (algorithm_name.compare("imu") == 0) + { + algorithm = SPATIAL_ALGORITHM_IMU; + } else + { + throw std::invalid_argument("Unknown spatial algorithm name"); + } + + PhidgetReturnCode ret = + PhidgetSpatial_setAlgorithm(spatial_handle_, algorithm); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set spatial algorithm", ret); + } +} + +void Spatial::setAHRSParameters(double angularVelocityThreshold, + double angularVelocityDeltaThreshold, + double accelerationThreshold, double magTime, + double accelTime, double biasTime) +{ + PhidgetReturnCode ret = PhidgetSpatial_setAHRSParameters( + spatial_handle_, angularVelocityThreshold, + angularVelocityDeltaThreshold, accelerationThreshold, magTime, + accelTime, biasTime); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set AHRS parameters", ret); + } +} + +void Spatial::setAlgorithmMagnetometerGain(double magnetometer_gain) +{ + PhidgetReturnCode ret = PhidgetSpatial_setAlgorithmMagnetometerGain( + spatial_handle_, magnetometer_gain); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set algorithm magnetometer gain", ret); + } +} + +void Spatial::setHeatingEnabled(bool heating_enabled) +{ + PhidgetReturnCode ret = + PhidgetSpatial_setHeatingEnabled(spatial_handle_, heating_enabled); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set heating flag", ret); + } +} + +void Spatial::setDataInterval(uint32_t interval_ms) const +{ + PhidgetReturnCode ret = + PhidgetSpatial_setDataInterval(spatial_handle_, interval_ms); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set data interval", ret); + } +} + +void Spatial::dataHandler(const double acceleration[3], + const double angular_rate[3], + const double magnetic_field[3], + double timestamp) const +{ + data_handler_(acceleration, angular_rate, magnetic_field, timestamp); +} + +void Spatial::algorithmDataHandler(const double quaternion[4], + double timestamp) const +{ + algorithm_data_handler_(quaternion, timestamp); +} + +void Spatial::attachHandler() +{ + attach_handler_(); +} + +void Spatial::detachHandler() +{ + detach_handler_(); +} + +void Spatial::DataHandler(PhidgetSpatialHandle /* input_handle */, void *ctx, + const double acceleration[3], + const double angular_rate[3], + const double magnetic_field[3], double timestamp) +{ + (reinterpret_cast(ctx)) + ->dataHandler(acceleration, angular_rate, magnetic_field, timestamp); +} + +void Spatial::AlgorithmDataHandler(PhidgetSpatialHandle /* input_handle */, + void *ctx, const double quaternion[4], + double timestamp) +{ + ((Spatial *)ctx)->algorithmDataHandler(quaternion, timestamp); +} + +void Spatial::AttachHandler(PhidgetHandle /* input_handle */, void *ctx) +{ + ((Spatial *)ctx)->attachHandler(); +} + +void Spatial::DetachHandler(PhidgetHandle /* input_handle */, void *ctx) +{ + ((Spatial *)ctx)->detachHandler(); +} + +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_api/src/temperature.cpp b/docker/testBot/phidgets_drivers/phidgets_api/src/temperature.cpp new file mode 100644 index 0000000..98a69d1 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_api/src/temperature.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 + +#include "phidgets_api/phidget22.hpp" +#include "phidgets_api/temperature.hpp" + +namespace phidgets { + +Temperature::Temperature(int32_t serial_number, int hub_port, + bool is_hub_port_device, + std::function temperature_handler) + : serial_number_(serial_number), temperature_handler_(temperature_handler) +{ + PhidgetReturnCode ret = + PhidgetTemperatureSensor_create(&temperature_handle_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to create TemperatureSensor handle", ret); + } + + helpers::openWaitForAttachment( + reinterpret_cast(temperature_handle_), serial_number, + hub_port, is_hub_port_device, 0); + + ret = PhidgetTemperatureSensor_setOnTemperatureChangeHandler( + temperature_handle_, TemperatureChangeHandler, this); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set change handler for Temperature", + ret); + } + + if (serial_number_ == -1) + { + ret = Phidget_getDeviceSerialNumber( + reinterpret_cast(temperature_handle_), + &serial_number_); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get serial number for temperature", + ret); + } + } +} + +Temperature::~Temperature() +{ + PhidgetHandle handle = reinterpret_cast(temperature_handle_); + helpers::closeAndDelete(&handle); +} + +int32_t Temperature::getSerialNumber() const noexcept +{ + return serial_number_; +} + +void Temperature::setThermocoupleType(ThermocoupleType type) +{ + PhidgetReturnCode ret = PhidgetTemperatureSensor_setThermocoupleType( + temperature_handle_, + static_cast(type)); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set Temperature thermocouple type", + ret); + } +} + +double Temperature::getTemperature() const +{ + double current_temperature; + PhidgetReturnCode ret = PhidgetTemperatureSensor_getTemperature( + temperature_handle_, ¤t_temperature); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to get temperature", ret); + } + return current_temperature; +} + +void Temperature::setDataInterval(uint32_t interval_ms) const +{ + PhidgetReturnCode ret = PhidgetTemperatureSensor_setDataInterval( + temperature_handle_, interval_ms); + if (ret != EPHIDGET_OK) + { + throw Phidget22Error("Failed to set data interval", ret); + } +} + +void Temperature::temperatureChangeHandler(double temperature) const +{ + temperature_handler_(temperature); +} + +void Temperature::TemperatureChangeHandler( + PhidgetTemperatureSensorHandle /* temperature_handle */, void *ctx, + double temperature) +{ + (reinterpret_cast(ctx)) + ->temperatureChangeHandler(temperature); +} + +} // namespace phidgets diff --git a/docker/testBot/phidgets_drivers/phidgets_drivers/CHANGELOG.rst b/docker/testBot/phidgets_drivers/phidgets_drivers/CHANGELOG.rst new file mode 100644 index 0000000..7cd17cd --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_drivers/CHANGELOG.rst @@ -0,0 +1,108 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package phidgets_drivers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +2.3.3 (2024-03-13) +------------------ + +2.3.2 (2023-11-27) +------------------ + +2.3.1 (2023-03-03) +------------------ + +2.3.0 (2022-04-13) +------------------ + +2.2.2 (2022-02-17) +------------------ + +2.2.1 (2021-08-03) +------------------ + +2.2.0 (2021-05-20) +------------------ + +2.1.0 (2021-03-29) +------------------ + +2.0.2 (2020-06-01) +------------------ + +2.0.1 (2019-12-05) +------------------ + +2.0.0 (2019-12-05) +------------------ +* Port phidgets_drivers to ROS 2. +* Ignore all packages for ROS 2 port. +* Update maintainers in package.xml +* Merge pull request `#39 `_ from clalancette/add-libphidget22 +* Completely remove libphidget21. +* Rewrite Motor Phidget to use libphidget22. +* Rewrite IMU using libphidget22. +* Add support for Phidgets Magnetometer sensors. +* Add support for Phidgets Gyroscope sensors. +* Add support for Phidgets Accelerometer sensors. +* Add in support for Phidgets Temperature sensors. +* Add in support for Phidgets Analog inputs. +* Add in support for Phidgets Digital Inputs. +* Add in support for Phidgets Digital Outputs. +* Add in libphidget22 package. +* Merge pull request `#36 `_ from clalancette/phidget-cleanup2 +* Split custom messages into their own package. +* Add in phidgets_ik to the phidgets_drivers metapackage. +* Switch to package format 2. +* Contributors: Chris Lalancette, Martin Günther + +0.7.9 (2019-06-28) +------------------ + +0.7.8 (2019-05-06) +------------------ + +0.7.7 (2018-09-18) +------------------ + +0.7.6 (2018-08-09) +------------------ + +0.7.5 (2018-01-31) +------------------ + +0.7.4 (2017-10-04) +------------------ +* Add phidgets_high_speed_encoder to metapackage +* Contributors: Jose Luis Blanco-Claraco, Martin Günther + +0.7.3 (2017-06-30) +------------------ + +0.7.2 (2017-06-02) +------------------ + +0.7.1 (2017-05-22) +------------------ + +0.7.0 (2017-02-17) +------------------ +* Remove phidgets_ir package + It was a stub anyway. If somebody has such a device and cares to expose + the data via ROS topics, this can be revived. +* Contributors: Martin Günther + +0.2.3 (2017-02-17) +------------------ +* Update package.xml meta info +* Contributors: Martin Günther + +0.2.2 (2015-03-23) +------------------ + +0.2.1 (2015-01-15) +------------------ +* phidgets_drivers: removed phidgets_c_api dependency +* Updated version, maintainer and author information +* Deleted comments within files of all packages +* phidgets_drivers: converted stack into metapackage +* Contributors: Murilo FM diff --git a/docker/testBot/phidgets_drivers/phidgets_drivers/CMakeLists.txt b/docker/testBot/phidgets_drivers/phidgets_drivers/CMakeLists.txt new file mode 100644 index 0000000..017c1c2 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_drivers/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.5) +project(phidgets_drivers) +find_package(ament_cmake REQUIRED) +ament_package() diff --git a/docker/testBot/phidgets_drivers/phidgets_drivers/package.xml b/docker/testBot/phidgets_drivers/phidgets_drivers/package.xml new file mode 100644 index 0000000..5839d8e --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_drivers/package.xml @@ -0,0 +1,44 @@ + + + phidgets_drivers + 2.3.3 + API and ROS drivers for Phidgets devices + + Martin Günther + Chris Lalancette + + BSD, LGPL + + http://ros.org/wiki/phidgets_drivers + https://github.com/ros-drivers/phidgets_drivers.git + https://github.com/ros-drivers/phidgets_drivers/issues + + Phidgets Inc. + Tully Foote + Ivan Dryanovski + Martin Günther + Murilo FM + José-Luis Blanco Claraco + Chris Lalancette + + ament_cmake + + libphidget22 + phidgets_accelerometer + phidgets_api + phidgets_analog_inputs + phidgets_digital_inputs + phidgets_digital_outputs + phidgets_gyroscope + phidgets_ik + phidgets_high_speed_encoder + phidgets_magnetometer + phidgets_motors + phidgets_msgs + phidgets_spatial + phidgets_temperature + + + ament_cmake + + diff --git a/docker/testBot/phidgets_drivers/phidgets_msgs/CHANGELOG.rst b/docker/testBot/phidgets_drivers/phidgets_msgs/CHANGELOG.rst new file mode 100644 index 0000000..6c7c6c7 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_msgs/CHANGELOG.rst @@ -0,0 +1,90 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package phidgets_msgs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +2.3.3 (2024-03-13) +------------------ + +2.3.2 (2023-11-27) +------------------ + +2.3.1 (2023-03-03) +------------------ +* adding support for analog outputs for ROS2 (`#145 `_) +* Contributors: Alexis Fetet + +2.3.0 (2022-04-13) +------------------ +* Merge pull request `#132 `_ from mintar/feat-pre-commit-ros2 + [galactic] Add pre-commit, move from travis to GitHub actions, fix style +* Fix trailing whitespace +* Contributors: Martin Günther + +2.2.2 (2022-02-17) +------------------ + +2.2.1 (2021-08-03) +------------------ + +2.2.0 (2021-05-20) +------------------ + +2.1.0 (2021-03-29) +------------------ + +2.0.2 (2020-06-01) +------------------ + +2.0.1 (2019-12-05) +------------------ + +2.0.0 (2019-12-05) +------------------ +* Port phidgets_msgs to ROS 2. +* Ignore all packages for ROS 2 port. +* Update maintainers in package.xml +* Merge pull request `#39 `_ from clalancette/add-libphidget22 +* Switch to C++14. +* Merge pull request `#36 `_ from clalancette/phidget-cleanup2 +* Switch to C++14 everywhere. +* Split custom messages into their own package. +* Contributors: Chris Lalancette, Martin Günther + +0.7.9 (2019-06-28) +------------------ + +0.7.8 (2019-05-06) +------------------ + +0.7.7 (2018-09-18) +------------------ + +0.7.6 (2018-08-09) +------------------ + +0.7.5 (2018-01-31) +------------------ + +0.7.4 (2017-10-04) +------------------ + +0.7.3 (2017-06-30) +------------------ + +0.7.2 (2017-06-02) +------------------ + +0.7.1 (2017-05-22) +------------------ + +0.7.0 (2017-02-17 17:40) +------------------------ + +0.2.3 (2017-02-17 12:11) +------------------------ + +0.2.2 (2015-03-23) +------------------ + +0.2.1 (2015-01-15) +------------------ diff --git a/docker/testBot/phidgets_drivers/phidgets_msgs/CMakeLists.txt b/docker/testBot/phidgets_drivers/phidgets_msgs/CMakeLists.txt new file mode 100644 index 0000000..8dbe4c6 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_msgs/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.5) +project(phidgets_msgs) + +# Default to C++14 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 14) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +find_package(ament_cmake REQUIRED) +find_package(rosidl_default_generators REQUIRED) +find_package(std_msgs REQUIRED) + +set(msg_files + "msg/EncoderDecimatedSpeed.msg" +) + +set(srv_files + "srv/SetAnalogOutput.srv" + "srv/SetDigitalOutput.srv" +) + +rosidl_generate_interfaces(${PROJECT_NAME} + ${srv_files} + ${msg_files} + DEPENDENCIES + std_msgs +) + +ament_export_dependencies(rosidl_default_runtime) + +ament_package() diff --git a/docker/testBot/phidgets_drivers/phidgets_msgs/msg/EncoderDecimatedSpeed.msg b/docker/testBot/phidgets_drivers/phidgets_msgs/msg/EncoderDecimatedSpeed.msg new file mode 100644 index 0000000..4c9a8e9 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_msgs/msg/EncoderDecimatedSpeed.msg @@ -0,0 +1,4 @@ +# Encoder averaged speed for a channel in a Phidgets High-Speed Encoder board +std_msgs/Header header +# Averaged (sliding window) speed estimation [rad/s] +float64 avr_speed diff --git a/docker/testBot/phidgets_drivers/phidgets_msgs/package.xml b/docker/testBot/phidgets_drivers/phidgets_msgs/package.xml new file mode 100644 index 0000000..428a248 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_msgs/package.xml @@ -0,0 +1,28 @@ + + + + phidgets_msgs + 2.3.3 + Custom ROS messages for Phidgets drivers + + Martin Günther + Chris Lalancette + + BSD + + Chris Lalancette + + ament_cmake + + std_msgs + + rosidl_default_generators + + rosidl_default_runtime + + rosidl_interface_packages + + + ament_cmake + + diff --git a/docker/testBot/phidgets_drivers/phidgets_msgs/srv/SetAnalogOutput.srv b/docker/testBot/phidgets_drivers/phidgets_msgs/srv/SetAnalogOutput.srv new file mode 100644 index 0000000..c587d95 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_msgs/srv/SetAnalogOutput.srv @@ -0,0 +1,6 @@ +# Sets the state of an analog output. + +uint16 index # index of the output to be set +float64 voltage +--- +bool success diff --git a/docker/testBot/phidgets_drivers/phidgets_msgs/srv/SetDigitalOutput.srv b/docker/testBot/phidgets_drivers/phidgets_msgs/srv/SetDigitalOutput.srv new file mode 100644 index 0000000..5aa15d0 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_msgs/srv/SetDigitalOutput.srv @@ -0,0 +1,6 @@ +# Sets the state of a digital output. + +uint16 index # index of the output to be set +bool state +--- +bool success diff --git a/docker/testBot/phidgets_drivers/phidgets_spatial/CHANGELOG.rst b/docker/testBot/phidgets_drivers/phidgets_spatial/CHANGELOG.rst new file mode 100644 index 0000000..d79bd22 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_spatial/CHANGELOG.rst @@ -0,0 +1,212 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package phidgets_spatial +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +2.3.3 (2024-03-13) +------------------ +* Add support for VINT networkhub (`#172 `_) + This is a port of `#127 `_ to ROS2. + Closes `#135 `_. +* Contributors: Martin Günther + +2.3.2 (2023-11-27) +------------------ +* Only set magnetometer gain if param is set (`#169 `_) +* added new parameters for spatial precision MOT0109 onwards +* added support for phidget spatial onboard orientation estimation +* Contributors: Malte kl. Piening, Martin Günther + +2.3.1 (2023-03-03) +------------------ + +2.3.0 (2022-04-13) +------------------ + +2.2.2 (2022-02-17) +------------------ +* Fix behavior after USB reattachment (`#119 `_) + The Phidged Spatial never recovered after detaching and reattaching to + the USB port. This commit fixes that. +* Add attach + detach handlers +* Fix publishing of invalid mag readings (`#116 `_) +* Contributors: Martin Günther + +2.2.1 (2021-08-03) +------------------ +* Make the magnetometer corrections optional again. (`#95 `_) +* Update the ROS 2 readme files. (`#93 `_) +* Contributors: Chris Lalancette + +2.2.0 (2021-05-20) +------------------ +* Make sure to declare the type while declaring the parameter. (`#89 `_) +* Contributors: Chris Lalancette + +2.1.0 (2021-03-29) +------------------ +* Don't publish messages that jumped back in time. (`#86 `_) +* Log synchronization window details at DEBUG level (`#84 `_) +* Get rid of deprecation warnings in Foxy. (`#75 `_) +* Switch header guards to _HPP SUFFIX. +* Contributors: Chris Lalancette, Martin Günther, Michael Grupp + +2.0.2 (2020-06-01) +------------------ +* Release build fixes (`#67 `_) +* Contributors: Chris Lalancette + +2.0.1 (2019-12-05) +------------------ +* Switch the buildtoo_depend to ament_cmake_ros. (`#65 `_) +* Contributors: Chris Lalancette + +2.0.0 (2019-12-05) +------------------ +* Include file cleanup. +* Make sure exceptions get caught by reference. +* Switch from NULL to nullptr. +* Make sure to initialize class member variables. +* Update READMEs to use "Published Topics" and "Subscribed Topics". (`#59 `_) +* Change launch output to "both" so it logs as well. +* Make publish_rate a double. +* Print out the serial number when connecting. +* Update documentation to mention device dependent fields. +* Update FIXME comments for sleep. +* Port spatial to ROS 2. +* Ignore all packages for ROS 2 port. +* Fix wrong defaults for standard deviations (`#48 `_) +* Improve the IMU calibration service (`#47 `_) +* Update maintainers in package.xml +* Merge pull request `#39 `_ from clalancette/add-libphidget22 +* Resynchronize the times at periodic intervals. +* Add launch files for all drivers. +* Add in try/catch blocks for connecting. +* Fixes from review. +* Documentation updates to README.md +* Set the publish_rate to 0 by default. +* Add in the license files and add to the headers. +* Remove nodes in favor of nodelets. +* Finish removing launch file from phidgets_spatial. +* Rewrite IMU using libphidget22. +* Contributors: Chris Lalancette, Martin Günther + +0.7.9 (2019-06-28) +------------------ + +0.7.8 (2019-05-06) +------------------ + +0.7.7 (2018-09-18) +------------------ +* Add parameter use_imu_time (default true) (`#27 `_) + Setting use_imu_time to false will disable the imu time calibration and + always use the Host time, i.e. ros::Time::now(). +* Contributors: Jochen Sprickerhof + +0.7.6 (2018-08-09) +------------------ +* phidgets_imu: Ensure strictly ordered timestamps (`#26 `_) + Fixes `#17 `_. +* Contributors: Michael Grupp, Martin Günther + +0.7.5 (2018-01-31) +------------------ +* phidgets_imu: Add roslaunch_add_file_check +* phidgets_imu: Add diagnostic_aggregator dependency +* phidgets_imu: Add missing install rule for config +* update to use non deprecated pluginlib macro (`#19 `_) +* Contributors: Martin Günther, Mikael Arguedas + +0.7.4 (2017-10-04) +------------------ + +0.7.3 (2017-06-30) +------------------ + +0.7.2 (2017-06-02) +------------------ +* First release into Lunar +* phidgets_imu: Add use_magnetic_field_msg to launch + This is required in Jade: Since Jade, phidgets_imu publishes + MagneticField messages, but imu_filter_madgwick still subscribes by + default to Vector3Stamped messages. When running as nodelets, this can + produce a silent error. + In Kinetic, this is optional: imu_filter_madgwick now defaults to + MagneticField. + From Lunar on, it should be removed, because the use_magnetic_field_msg + param was removed from imu_filter_madgwick. +* Contributors: Martin Günther + +0.7.1 (2017-05-22) +------------------ +* phidgets_imu: add optional serial number parameter (`#7 `_) +* phidgets_imu: Add imu_filter_madgwick dependency + Closes `#9 `_. +* Contributors: Johan M. von Behren, Martin Günther + +0.7.0 (2017-02-17) +------------------ +* Publish MagneticField instead of Vector3Stamped +* Report mag data in Tesla, not Gauss + This is to conform with sensor_msgs/MagneticField, which requires the + data to be in Tesla. +* Contributors: Martin Günther + +0.2.3 (2017-02-17) +------------------ +* Add IMU diagnostics (`#24 `_) +* Set data rate after reattachment + This fixes a bug where after disconnecting and reconnecting the USB + cable, the data rate would be set to the default of 125 Hz (= period of + 8ms). By moving the setDataRate call to the attachHandler, the data rate + is correctly set after each reattachment. +* Contributors: Mani Monajjemi, Keshav Iyengar, Martin Günther + +0.2.2 (2015-03-23) +------------------ +* Merge pull request #18 from ccny-ros-pkg/libphidgets + Merge libphidgets branch into indigo +* set orientation_covariance[0] to -1 + from Imu.msg: + > If you have no estimate for one of the data elements (e.g. your IMU doesn't produce an orientation + > estimate), please set element 0 of the associated covariance matrix to -1. +* phidgets_imu: fixed issue #9 +* Contributors: Martin Günther, Murilo FM + +0.2.1 (2015-01-15) +------------------ +* add boost depends to CMakeLists + All non-catkin things that we expose in our headers should be added to + the DEPENDS, so that packages which depend on our package will also + automatically link against it. + Also see: http://answers.ros.org/question/58498/what-is-the-purpose-of-catkin_depends/\#58593 +* improve error output when setting compass corr params + The previous implementation didn't catch a number of error codes + (EPHIDGET_INVALIDARG, EPHIDGET_NOTATTACHED, EPHIDGET_UNEXPECTED), and + the new one is more elegant and consistent with the previous code anyway. +* Set compass correction params on the device + Tested with a Phidget Spatial 3/3/3 1044. +* phidgets_imu: install phidgets_imu_nodelet.xml +* phidgets_imu: not exporting nodelet as library anymore +* Updated version, maintainer and author information +* phidgets_imu: added install rule to launch files +* phidgets_imu: removed unnecessary dependency +* Deleted comments within files of all packages +* Catkinised packages +* Merge pull request #1 from uos/fix_imu_time_lag + fix IMU time lag +* add some hints to error message + I just spent 30 minutes trying to figure out why the IMU works on one + computer and doesn't on another one. Felt a little foolish when I found + out that the udev rules weren't installed; maybe providing some more + info in the error message helps others. +* use ros::Time::now() if time lag exceeds threshold +* added warning if IMU time lags behind ROS time +* renamed rate parameter to period +* added timestamp in imu data +* fixed cmakelists by including lib to compile on electric +* adding missing imu_ros h file +* adding missing imu_ros cpp file +* added api, imu and ir +* initial commit +* Contributors: Ivan Dryanovski, Martin Günther, Murilo FM diff --git a/docker/testBot/phidgets_drivers/phidgets_spatial/CMakeLists.txt b/docker/testBot/phidgets_drivers/phidgets_spatial/CMakeLists.txt new file mode 100644 index 0000000..63fb3fe --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_spatial/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.5) +project(phidgets_spatial) + +# Default to C++14 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 14) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +find_package(ament_cmake_ros REQUIRED) +find_package(phidgets_api REQUIRED) +find_package(rclcpp REQUIRED) +find_package(rclcpp_components REQUIRED) +find_package(sensor_msgs REQUIRED) +find_package(std_msgs REQUIRED) +find_package(std_srvs REQUIRED) + +include_directories(include) + +add_library(phidgets_spatial SHARED src/spatial_ros_i.cpp) +ament_target_dependencies(phidgets_spatial + phidgets_api + rclcpp + rclcpp_components + sensor_msgs + std_msgs + std_srvs +) + +rclcpp_components_register_nodes(phidgets_spatial + "phidgets::SpatialRosI") + +install(TARGETS phidgets_spatial + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) + +install(DIRECTORY include/ + DESTINATION include +) + +install(DIRECTORY launch + DESTINATION share/${PROJECT_NAME} +) + +ament_package() diff --git a/docker/testBot/phidgets_drivers/phidgets_spatial/README.md b/docker/testBot/phidgets_drivers/phidgets_spatial/README.md new file mode 100644 index 0000000..ad3f350 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_spatial/README.md @@ -0,0 +1,60 @@ +Phidgets spatial ROS 2 driver +============================= + +This is the ROS 2 driver for Phidgets spatial. + +Usage +----- + +To run this driver standalone, do the following: + + ros2 launch phidgets_spatial spatial-launch.py + +Published Topics +---------------- + +* `/imu/data_raw` (`sensor_msgs/Imu`) - The raw accelerometer and gyroscope data. +* `imu/is_calibrated` (`std_msgs/Bool`) - Whether the gyroscope has been calibrated; this will be done automatically at startup time, but can also be re-done at any time by calling the `imu/calibrate` service. +* `/imu/mag` (`sensor_msgs/MagneticField`) - The raw magnetometer data. + +Services +-------- + +* `imu/calibrate` (`std_srvs/Empty`) - Run calibration on the gyroscope. + +Parameters +---------- + +* `serial` (int) - The serial number of the phidgets spatial to connect to. If -1 (the default), connects to any spatial phidget that can be found. +* `hub_port` (int) - The phidgets VINT hub port to connect to. Only used if the spatial phidget is connected to a VINT hub. Defaults to 0. +* `frame_id` (string) - The header frame ID to use when publishing the message. Defaults to [REP-0145](http://www.ros.org/reps/rep-0145.html) compliant `imu_link`. +* `use_orientation` (bool) - Use the phidget spatials onboard orientation estimation; Just available on MOT0109 onwards. Set to false for older versions. Defaults to false. +* `spatial_algorithm` (string) - Name of the spatial algorithm used for orientation estimation (one of "none", "ahrs", "imu"); Just used if `use_orientation` is set to true. Defaults to `ahrs`. +* `ahrs_angular_velocity_threshold` (double) - Parameter for AHRS orientation estimation; Just used if `use_orientation` is set to true. +* `ahrs_angular_velocity_delta_threshold` (double) - Parameter for AHRS orientation estimation; Just used if `use_orientation` is set to true. +* `ahrs_acceleration_threshold` (double) - Parameter for AHRS orientation estimation; Just used if `use_orientation` is set to true. +* `ahrs_mag_time` (double) - Parameter for AHRS orientation estimation; Just used if `use_orientation` is set to true. +* `ahrs_accel_time` (double) - Parameter for AHRS orientation estimation; Just used if `use_orientation` is set to true. +* `ahrs_bias_time` (double) - Parameter for AHRS orientation estimation; Just used if `use_orientation` is set to true. +* `algorithm_magnetometer_gain` (double) - Gain of magnetometer in orientation estimation algorithm; Just used if `use_orientation` is set to true. Defaults to 0.005 +* `heating_enabled` (bool) - Use the internal heating element; Just available on MOT0109 onwards. Do not set this parameter for older versions. +* `linear_acceleration_stdev` (double) - The standard deviation to use for the linear acceleration when publishing the message. Defaults to 280 ug. +* `angular_velocity_stdev` (double) - The standard deviation to use for the angular velocity when publishing the message. Defaults to 0.095 deg/s. +* `magnetic_field_stdev` (double) - The standard deviation to use for the magnetic field when publishing the message. Defaults to 1.1 milligauss. +* `time_resynchronization_interval_ms` (int) - The number of milliseconds to wait between resynchronizing the time on the Phidgets spatial with the local time. Larger values have less "jumps", but will have more timestamp drift. Setting this to 0 disables resynchronization. Defaults to 5000 ms. +* `data_interval_ms` (int) - The number of milliseconds between acquisitions of data on the device (allowed values are dependent on the device). Defaults to 8 ms. +* `callback_delta_epsilon_ms` (int) - The number of milliseconds epsilon allowed between callbacks when attempting to resynchronize the time. If this is set to 1, then a difference of `data_interval_ms` plus or minus 1 millisecond will be considered viable for resynchronization. Higher values give the code more leeway to resynchronize, at the cost of potentially getting bad resynchronizations sometimes. Lower values can give better results, but can also result in never resynchronizing. Must be less than `data_interval_ms`. Defaults to 1 ms. +* `publish_rate` (double) - How often the driver will publish data on the ROS topic. If 0 (the default), it will publish every time there is an update from the device (so at the `data_interval_ms`). If positive, it will publish the data at that rate regardless of the acquisition interval. +* `cc_mag_field` (double) - Ambient magnetic field calibration value; see device's user guide for information on how to calibrate. +* `cc_offset0` (double) - Calibration offset value 0; see device's user guide for information on how to calibrate. +* `cc_offset1` (double) - Calibration offset value 1; see device's user guide for information on how to calibrate. +* `cc_offset2` (double) - Calibration offset value 2; see device's user guide for information on how to calibrate. +* `cc_gain0` (double) - Gain offset value 0; see device's user guide for information on how to calibrate. +* `cc_gain1` (double) - Gain offset value 1; see device's user guide for information on how to calibrate. +* `cc_gain2` (double) - Gain offset value 2; see device's user guide for information on how to calibrate. +* `cc_t0` (double) - T offset value 0; see device's user guide for information on how to calibrate. +* `cc_t1` (double) - T offset value 1; see device's user guide for information on how to calibrate. +* `cc_t2` (double) - T offset value 2; see device's user guide for information on how to calibrate. +* `cc_t3` (double) - T offset value 3; see device's user guide for information on how to calibrate. +* `cc_t4` (double) - T offset value 4; see device's user guide for information on how to calibrate. +* `cc_t5` (double) - T offset value 5; see device's user guide for information on how to calibrate. diff --git a/docker/testBot/phidgets_drivers/phidgets_spatial/include/phidgets_spatial/spatial_ros_i.hpp b/docker/testBot/phidgets_drivers/phidgets_spatial/include/phidgets_spatial/spatial_ros_i.hpp new file mode 100644 index 0000000..572a41c --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_spatial/include/phidgets_spatial/spatial_ros_i.hpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +#ifndef PHIDGETS_SPATIAL_SPATIAL_ROS_I_HPP +#define PHIDGETS_SPATIAL_SPATIAL_ROS_I_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "phidgets_api/spatial.hpp" + +namespace phidgets { + +const float G = 9.80665; + +class SpatialRosI final : public rclcpp::Node +{ + public: + explicit SpatialRosI(const rclcpp::NodeOptions& options); + + private: + std::unique_ptr spatial_; + std::string frame_id_; + std::mutex spatial_mutex_; + + rclcpp::Publisher::SharedPtr cal_publisher_; + rclcpp::Service::SharedPtr cal_srv_; + rclcpp::Publisher::SharedPtr imu_pub_; + rclcpp::Publisher::SharedPtr + magnetic_field_pub_; + void timerCallback(); + rclcpp::TimerBase::SharedPtr timer_; + double publish_rate_; + std::string server_name_; + std::string server_ip_; + + rclcpp::Time ros_time_zero_; + bool synchronize_timestamps_{true}; + uint64_t data_time_zero_ns_{0}; + uint64_t last_data_timestamp_ns_{0}; + uint64_t last_ros_stamp_ns_{0}; + int64_t time_resync_interval_ns_{0}; + int64_t data_interval_ns_{0}; + bool can_publish_{false}; + rclcpp::Time last_cb_time_; + int64_t cb_delta_epsilon_ns_{0}; + + void calibrate(); + + void calibrateService( + const std::shared_ptr req, + std::shared_ptr res); + + // Accelerometer + double linear_acceleration_variance_{0.0}; + double last_accel_x_{0.0}; + double last_accel_y_{0.0}; + double last_accel_z_{0.0}; + + // Gyroscope + double angular_velocity_variance_{0.0}; + double last_gyro_x_{0.0}; + double last_gyro_y_{0.0}; + double last_gyro_z_{0.0}; + + // Magnetometer + double magnetic_field_variance_{0.0}; + double last_mag_x_{0.0}; + double last_mag_y_{0.0}; + double last_mag_z_{0.0}; + + // Onboard orientation estimation results + double last_quat_w_; + double last_quat_x_; + double last_quat_y_; + double last_quat_z_; + + void publishLatest(); + + void spatialDataCallback(const double acceleration[3], + const double angular_rate[3], + const double magnetic_field[3], double timestamp); + void spatialAlgorithmDataCallback(const double quaternion[4], + double timestamp); + void attachCallback(); + void detachCallback(); +}; + +} // namespace phidgets + +#endif // PHIDGETS_SPATIAL_SPATIAL_ROS_I_HPP diff --git a/docker/testBot/phidgets_drivers/phidgets_spatial/launch/spatial-launch.py b/docker/testBot/phidgets_drivers/phidgets_spatial/launch/spatial-launch.py new file mode 100644 index 0000000..ab1a097 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_spatial/launch/spatial-launch.py @@ -0,0 +1,63 @@ +# Copyright 2019 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Launch a Phidgets spatial in a component container.""" + +import launch +from launch_ros.actions import ComposableNodeContainer +from launch_ros.descriptions import ComposableNode + + +def generate_launch_description(): + """Generate launch description with multiple components.""" + + params = { + # optional param use_orientation, default is false + 'use_orientation': False, + + # optional param spatial_algorithm, default is 'ahrs' + 'spatial_algorithm': 'ahrs', + + # optional ahrs parameters + 'ahrs_angular_velocity_threshold': 1.0, + 'ahrs_angular_velocity_delta_threshold': 0.1, + 'ahrs_acceleration_threshold': 0.1, + 'ahrs_mag_time': 10.0, + 'ahrs_accel_time': 10.0, + 'ahrs_bias_time': 1.25, + + # optional param algorithm_magnetometer_gain, default is 0.005 + # WARNING: do not set on PhidgetSpatial MOT0110 onwards (not supported)! + # 'algorithm_magnetometer_gain': 0.005, + + # optional param heating_enabled, not modified by default + 'heating_enabled': False, + } + + container = ComposableNodeContainer( + name='phidget_container', + namespace='', + package='rclcpp_components', + executable='component_container', + composable_node_descriptions=[ + ComposableNode( + package='phidgets_spatial', + plugin='phidgets::SpatialRosI', + name='phidgets_spatial', + parameters=[params]), + ], + output='both', + ) + + return launch.LaunchDescription([container]) diff --git a/docker/testBot/phidgets_drivers/phidgets_spatial/package.xml b/docker/testBot/phidgets_drivers/phidgets_spatial/package.xml new file mode 100644 index 0000000..7b6058e --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_spatial/package.xml @@ -0,0 +1,32 @@ + + + phidgets_spatial + 2.3.3 + Driver for the Phidgets Spatial 3/3/3 devices + + Martin Günther + Chris Lalancette + + BSD + + http://ros.org/wiki/phidgets_spatial + https://github.com/ros-drivers/phidgets_drivers.git + https://github.com/ros-drivers/phidgets_drivers/issues + + Ivan Dryanovski + + ament_cmake_ros + + phidgets_api + rclcpp + rclcpp_components + sensor_msgs + std_msgs + std_srvs + + launch + + + ament_cmake + + diff --git a/docker/testBot/phidgets_drivers/phidgets_spatial/src/spatial_ros_i.cpp b/docker/testBot/phidgets_drivers/phidgets_spatial/src/spatial_ros_i.cpp new file mode 100644 index 0000000..c232f41 --- /dev/null +++ b/docker/testBot/phidgets_drivers/phidgets_spatial/src/spatial_ros_i.cpp @@ -0,0 +1,617 @@ +/* + * Copyright (c) 2019, Open Source Robotics Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "phidgets_api/spatial.hpp" +#include "phidgets_spatial/spatial_ros_i.hpp" + +namespace phidgets { + +SpatialRosI::SpatialRosI(const rclcpp::NodeOptions &options) + : rclcpp::Node("phidgets_spatial_node", options) +{ + setvbuf(stdout, nullptr, _IONBF, BUFSIZ); + + RCLCPP_INFO(get_logger(), "Starting Phidgets Spatial"); + + bool use_orientation = this->declare_parameter( + "use_orientation", + false); // default do not use the onboard orientation + std::string spatial_algorithm = + this->declare_parameter("spatial_algorithm", "ahrs"); + + double ahrsAngularVelocityThreshold; + double ahrsAngularVelocityDeltaThreshold; + double ahrsAccelerationThreshold; + double ahrsMagTime; + double ahrsAccelTime; + double ahrsBiasTime; + + this->declare_parameter("ahrs_angular_velocity_threshold", + rclcpp::ParameterType::PARAMETER_DOUBLE); + this->declare_parameter("ahrs_angular_velocity_delta_threshold", + rclcpp::ParameterType::PARAMETER_DOUBLE); + this->declare_parameter("ahrs_acceleration_threshold", + rclcpp::ParameterType::PARAMETER_DOUBLE); + this->declare_parameter("ahrs_mag_time", + rclcpp::ParameterType::PARAMETER_DOUBLE); + this->declare_parameter("ahrs_accel_time", + rclcpp::ParameterType::PARAMETER_DOUBLE); + this->declare_parameter("ahrs_bias_time", + rclcpp::ParameterType::PARAMETER_DOUBLE); + + bool has_ahrs_params = + this->get_parameter("ahrs_angular_velocity_threshold", + ahrsAngularVelocityThreshold) && + this->get_parameter("ahrs_angular_velocity_delta_threshold", + ahrsAngularVelocityDeltaThreshold) && + this->get_parameter("ahrs_acceleration_threshold", + ahrsAccelerationThreshold) && + this->get_parameter("ahrs_mag_time", ahrsMagTime) && + this->get_parameter("ahrs_accel_time", ahrsAccelTime) && + this->get_parameter("ahrs_bias_time", ahrsBiasTime); + + double algorithm_magnetometer_gain; + bool set_algorithm_magnetometer_gain = true; + if (!this->get_parameter("algorithm_magnetometer_gain", + algorithm_magnetometer_gain)) + { + algorithm_magnetometer_gain = 0.0; + set_algorithm_magnetometer_gain = + false; // if parameter not set, do not call api (because this + // function is not available from MOT0110 onwards) + } + + bool heating_enabled; + bool set_heating_enabled = true; + if (!this->get_parameter("heating_enabled", heating_enabled)) + { + set_heating_enabled = + false; // if parameter not set, do not call api (because this + // function is just available from MOT0109 onwards) + } + + int serial_num = + this->declare_parameter("serial", -1); // default open any device + + int hub_port = this->declare_parameter( + "hub_port", 0); // only used if the device is on a VINT hub_port + + // As specified in http://www.ros.org/reps/rep-0145.html + frame_id_ = this->declare_parameter("frame_id", "imu_link"); + + double linear_acceleration_stdev = this->declare_parameter( + "linear_acceleration_stdev", + 280.0 * 1e-6 * + G); // 280 ug accelerometer white noise sigma, as per manual + linear_acceleration_variance_ = + linear_acceleration_stdev * linear_acceleration_stdev; + + // 0.095 deg/s gyroscope white noise sigma, as per manual + double angular_velocity_stdev = this->declare_parameter( + "angular_velocity_stdev", 0.095 * (M_PI / 180.0)); + angular_velocity_variance_ = + angular_velocity_stdev * angular_velocity_stdev; + + // 1.1 milligauss magnetometer white noise sigma, as per manual + double magnetic_field_stdev = + this->declare_parameter("magnetic_field_stdev", 1.1 * 1e-3 * 1e-4); + magnetic_field_variance_ = magnetic_field_stdev * magnetic_field_stdev; + + int time_resync_ms = + this->declare_parameter("time_resynchronization_interval_ms", 5000); + time_resync_interval_ns_ = + static_cast(time_resync_ms) * 1000 * 1000; + + int data_interval_ms = this->declare_parameter("data_interval_ms", 8); + data_interval_ns_ = data_interval_ms * 1000 * 1000; + + int cb_delta_epsilon_ms = + this->declare_parameter("callback_delta_epsilon_ms", 1); + cb_delta_epsilon_ns_ = cb_delta_epsilon_ms * 1000 * 1000; + if (cb_delta_epsilon_ms >= data_interval_ms) + { + throw std::runtime_error( + "Callback epsilon is larger than the data interval; this can never " + "work"); + } + + publish_rate_ = this->declare_parameter("publish_rate", 0.0); + if (publish_rate_ > 1000.0) + { + throw std::runtime_error("Publish rate must be <= 1000"); + } + + this->declare_parameter("server_name", + rclcpp::ParameterType::PARAMETER_STRING); + this->declare_parameter("server_ip", + rclcpp::ParameterType::PARAMETER_STRING); + if (this->get_parameter("server_name", server_name_) && + this->get_parameter("server_ip", server_ip_)) + { + PhidgetNet_addServer(server_name_.c_str(), server_ip_.c_str(), 5661, "", + 0); + + RCLCPP_INFO(get_logger(), "Using phidget server %s at IP %s", + server_name_.c_str(), server_ip_.c_str()); + } + + // compass correction params (see + // http://www.phidgets.com/docs/1044_User_Guide) + this->declare_parameter("cc_mag_field", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_offset0", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_offset1", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_offset2", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_gain0", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_gain1", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_gain2", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_t0", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_t1", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_t2", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_t3", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_t4", rclcpp::PARAMETER_DOUBLE); + this->declare_parameter("cc_t5", rclcpp::PARAMETER_DOUBLE); + + bool has_compass_params = false; + double cc_mag_field = 0.0; + double cc_offset0 = 0.0; + double cc_offset1 = 0.0; + double cc_offset2 = 0.0; + double cc_gain0 = 0.0; + double cc_gain1 = 0.0; + double cc_gain2 = 0.0; + double cc_T0 = 0.0; + double cc_T1 = 0.0; + double cc_T2 = 0.0; + double cc_T3 = 0.0; + double cc_T4 = 0.0; + double cc_T5 = 0.0; + + try + { + cc_mag_field = this->get_parameter("cc_mag_field").get_value(); + cc_offset0 = this->get_parameter("cc_offset0").get_value(); + cc_offset1 = this->get_parameter("cc_offset1").get_value(); + cc_offset2 = this->get_parameter("cc_offset2").get_value(); + cc_gain0 = this->get_parameter("cc_gain0").get_value(); + cc_gain1 = this->get_parameter("cc_gain1").get_value(); + cc_gain2 = this->get_parameter("cc_gain2").get_value(); + cc_T0 = this->get_parameter("cc_t0").get_value(); + cc_T1 = this->get_parameter("cc_t1").get_value(); + cc_T2 = this->get_parameter("cc_t2").get_value(); + cc_T3 = this->get_parameter("cc_t3").get_value(); + cc_T4 = this->get_parameter("cc_t4").get_value(); + cc_T5 = this->get_parameter("cc_t5").get_value(); + has_compass_params = true; + } catch (const rclcpp::exceptions::ParameterUninitializedException &) + { + } + + RCLCPP_INFO(get_logger(), + "Connecting to Phidgets Spatial serial %d, hub port %d ...", + serial_num, hub_port); + + // We take the mutex here and don't unlock until the end of the constructor + // to prevent a callback from trying to use the publisher before we are + // finished setting up. + std::lock_guard lock(spatial_mutex_); + + last_quat_w_ = 0.0; + last_quat_x_ = 0.0; + last_quat_y_ = 0.0; + last_quat_z_ = 0.0; + + try + { + std::function algorithm_data_handler = + nullptr; + if (use_orientation) + { + algorithm_data_handler = + std::bind(&SpatialRosI::spatialAlgorithmDataCallback, this, + std::placeholders::_1, std::placeholders::_2); + } + + spatial_ = std::make_unique( + serial_num, hub_port, false, + std::bind(&SpatialRosI::spatialDataCallback, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3, std::placeholders::_4), + algorithm_data_handler, + std::bind(&SpatialRosI::attachCallback, this), + std::bind(&SpatialRosI::detachCallback, this)); + + RCLCPP_INFO(get_logger(), "Connected to serial %d", + spatial_->getSerialNumber()); + + spatial_->setDataInterval(data_interval_ms); + + cal_publisher_ = this->create_publisher( + "imu/is_calibrated", rclcpp::SystemDefaultsQoS().transient_local()); + + calibrate(); + + if (use_orientation) + { + spatial_->setSpatialAlgorithm(spatial_algorithm); + + if (has_ahrs_params) + { + spatial_->setAHRSParameters(ahrsAngularVelocityThreshold, + ahrsAngularVelocityDeltaThreshold, + ahrsAccelerationThreshold, + ahrsMagTime, ahrsAccelTime, + ahrsBiasTime); + } + + if (set_algorithm_magnetometer_gain) + spatial_->setAlgorithmMagnetometerGain( + algorithm_magnetometer_gain); + } + + if (has_compass_params) + { + spatial_->setCompassCorrectionParameters( + cc_mag_field, cc_offset0, cc_offset1, cc_offset2, cc_gain0, + cc_gain1, cc_gain2, cc_T0, cc_T1, cc_T2, cc_T3, cc_T4, cc_T5); + } else + { + RCLCPP_INFO(get_logger(), "No compass correction params found."); + } + + if (set_heating_enabled) + { + spatial_->setHeatingEnabled(heating_enabled); + } + } catch (const Phidget22Error &err) + { + RCLCPP_ERROR(get_logger(), "Spatial: %s", err.what()); + throw; + } + + imu_pub_ = this->create_publisher("imu/data_raw", 1); + + cal_srv_ = this->create_service( + "imu/calibrate", + std::bind(&SpatialRosI::calibrateService, this, std::placeholders::_1, + std::placeholders::_2)); + + magnetic_field_pub_ = + this->create_publisher("imu/mag", 1); + + if (publish_rate_ > 0.0) + { + double pub_msec = 1000.0 / publish_rate_; + timer_ = this->create_wall_timer( + std::chrono::milliseconds(static_cast(pub_msec)), + std::bind(&SpatialRosI::timerCallback, this)); + } +} + +void SpatialRosI::calibrate() +{ + RCLCPP_INFO(get_logger(), + "Calibrating IMU, this takes around 2 seconds to finish. " + "Make sure that the device is not moved during this time."); + spatial_->zero(); + // The API call returns directly, so we "enforce" the recommended 2 sec + // here. See: https://github.com/ros-drivers/phidgets_drivers/issues/40 + + // FIXME: Ideally we'd use an rclcpp method that honors use_sim_time here, + // but that doesn't actually exist. Once + // https://github.com/ros2/rclcpp/issues/465 is solved, we can revisit this. + std::this_thread::sleep_for(std::chrono::seconds(2)); + RCLCPP_INFO(get_logger(), "Calibrating IMU done."); + + // publish message + auto is_calibrated_msg = std::make_unique(); + is_calibrated_msg->data = true; + cal_publisher_->publish(std::move(is_calibrated_msg)); +} + +void SpatialRosI::calibrateService( + const std::shared_ptr req, + std::shared_ptr res) +{ + (void)req; + (void)res; + calibrate(); +} + +void SpatialRosI::publishLatest() +{ + auto msg = std::make_unique(); + + auto mag_msg = std::make_unique(); + + // build covariance matrices + for (int i = 0; i < 3; ++i) + { + for (int j = 0; j < 3; ++j) + { + if (i == j) + { + int idx = j * 3 + i; + msg->linear_acceleration_covariance[idx] = + linear_acceleration_variance_; + msg->angular_velocity_covariance[idx] = + angular_velocity_variance_; + mag_msg->magnetic_field_covariance[idx] = + magnetic_field_variance_; + } + } + } + + // Fill out and send IMU message + msg->header.frame_id = frame_id_; + + uint64_t imu_diff_in_ns = last_data_timestamp_ns_ - data_time_zero_ns_; + uint64_t time_in_ns = ros_time_zero_.nanoseconds() + imu_diff_in_ns; + + if (time_in_ns < last_ros_stamp_ns_) + { + RCLCPP_WARN(get_logger(), + "Time went backwards (%lu < %lu)! Not publishing message.", + time_in_ns, last_ros_stamp_ns_); + return; + } + + last_ros_stamp_ns_ = time_in_ns; + + rclcpp::Time ros_time = rclcpp::Time(time_in_ns); + + msg->header.stamp = ros_time; + + // set linear acceleration + msg->linear_acceleration.x = last_accel_x_; + msg->linear_acceleration.y = last_accel_y_; + msg->linear_acceleration.z = last_accel_z_; + + // set angular velocities + msg->angular_velocity.x = last_gyro_x_; + msg->angular_velocity.y = last_gyro_y_; + msg->angular_velocity.z = last_gyro_z_; + + // set spatial algorithm orientation estimation + msg->orientation.w = last_quat_w_; + msg->orientation.x = last_quat_x_; + msg->orientation.y = last_quat_y_; + msg->orientation.z = last_quat_z_; + + imu_pub_->publish(std::move(msg)); + + // Fill out and publish magnetic message + mag_msg->header.frame_id = frame_id_; + + mag_msg->header.stamp = ros_time; + + mag_msg->magnetic_field.x = last_mag_x_; + mag_msg->magnetic_field.y = last_mag_y_; + mag_msg->magnetic_field.z = last_mag_z_; + + magnetic_field_pub_->publish(std::move(mag_msg)); +} + +void SpatialRosI::timerCallback() +{ + std::lock_guard lock(spatial_mutex_); + if (can_publish_) + { + publishLatest(); + } +} + +void SpatialRosI::spatialDataCallback(const double acceleration[3], + const double angular_rate[3], + const double magnetic_field[3], + double timestamp) +{ + // When publishing the message on the ROS network, we want to publish the + // time that the data was acquired in seconds since the Unix epoch. The + // data we have to work with is the time that the callback happened (on the + // local processor, in Unix epoch seconds), and the timestamp that the + // IMU gives us on the callback (from the processor on the IMU, in + // milliseconds since some arbitrary starting point). + // + // At a first approximation, we can apply the timestamp from the device to + // Unix epoch seconds by taking a common starting point on the IMU and the + // local processor, then applying the delta between this IMU timestamp and + // the "zero" IMU timestamp to the local processor starting point. + // + // There are several complications with the simple scheme above. The first + // is finding a proper "zero" point where the IMU timestamp and the local + // timestamp line up. Due to potential delays in servicing this process, + // along with USB delays, the delta timestamp from the IMU and the time when + // this callback gets called can be wildly different. Since we want the + // initial zero for both the IMU and the local time to be in the same time + // "window", we throw away data at the beginning until we see that the delta + // callback and delta timestamp are within reasonable bounds of each other. + // + // The second complication is that the time on the IMU drifts away from the + // time on the local processor. Taking the "zero" time once at the + // beginning isn't sufficient, and we have to periodically re-synchronize + // the times given the constraints above. Because we still have the + // arbitrary delays present as described above, it can take us several + // callbacks to successfully synchronize. We continue publishing data using + // the old "zero" time until successfully resynchronize, at which point we + // switch to the new zero point. + + std::lock_guard lock(spatial_mutex_); + + rclcpp::Time now = this->now(); + + // At the beginning of time, need to initialize last_cb_time for later use; + // last_cb_time is used to figure out the time between callbacks + if (last_cb_time_.nanoseconds() == 0) + { + last_cb_time_ = now; + // We need to initialize the ros_time_zero since rclcpp::Duration + // below won't let us subtract an essentially uninitialized + // rclcpp::Time from another one. However, we'll still do an initial + // synchronization since the default value of synchronize_timestamp + // is true. + ros_time_zero_ = now; + return; + } + + rclcpp::Duration time_since_last_cb = now - last_cb_time_; + uint64_t this_ts_ns = static_cast(timestamp * 1000.0 * 1000.0); + + if (synchronize_timestamps_) + { + // The only time it's safe to sync time between IMU and ROS Node is when + // the data that came in is within the data interval that data is + // expected. It's possible for data to come late because of USB issues + // or swapping, etc and we don't want to sync with data that was + // actually published before this time interval, so we wait until we get + // data that is within the data interval +/- an epsilon since we will + // have taken some time to process and/or a short delay (maybe USB + // comms) may have happened + if (time_since_last_cb.nanoseconds() >= + (data_interval_ns_ - cb_delta_epsilon_ns_) && + time_since_last_cb.nanoseconds() <= + (data_interval_ns_ + cb_delta_epsilon_ns_)) + { + ros_time_zero_ = now; + data_time_zero_ns_ = this_ts_ns; + synchronize_timestamps_ = false; + can_publish_ = true; + } else + { + RCLCPP_DEBUG( + get_logger(), + "Data not within acceptable window for synchronization: " + "expected between %ld and %ld, saw %ld", + data_interval_ns_ - cb_delta_epsilon_ns_, + data_interval_ns_ + cb_delta_epsilon_ns_, + time_since_last_cb.nanoseconds()); + } + } + + if (can_publish_) // Cannot publish data until IMU/ROS timestamps have been + // synchronized at least once + { + // Save off the values + last_accel_x_ = -acceleration[0] * G; + last_accel_y_ = -acceleration[1] * G; + last_accel_z_ = -acceleration[2] * G; + + last_gyro_x_ = angular_rate[0] * (M_PI / 180.0); + last_gyro_y_ = angular_rate[1] * (M_PI / 180.0); + last_gyro_z_ = angular_rate[2] * (M_PI / 180.0); + + if (magnetic_field[0] != PUNK_DBL) + { + // device reports data in Gauss, multiply by 1e-4 to convert to + // Tesla + last_mag_x_ = magnetic_field[0] * 1e-4; + last_mag_y_ = magnetic_field[1] * 1e-4; + last_mag_z_ = magnetic_field[2] * 1e-4; + } else + { + // data is PUNK_DBL ("unknown double"), which means the magnetometer + // did not return valid readings. When publishing at 250 Hz, this + // will happen in every second message, because the magnetometer can + // only sample at 125 Hz. It is still important to publish these + // messages, because a downstream node sometimes uses a + // TimeSynchronizer to get Imu and Magnetometer nodes. + double nan = std::numeric_limits::quiet_NaN(); + + last_mag_x_ = nan; + last_mag_y_ = nan; + last_mag_z_ = nan; + } + last_data_timestamp_ns_ = this_ts_ns; + + // Publish if we aren't publishing on a timer + if (publish_rate_ <= 0.0) + { + publishLatest(); + } + } + + // Determine if we need to resynchronize - time between IMU and ROS Node can + // drift, periodically resync to deal with this issue + rclcpp::Duration diff = now - ros_time_zero_; + if (time_resync_interval_ns_ > 0 && + diff.nanoseconds() >= time_resync_interval_ns_) + { + synchronize_timestamps_ = true; + } + + last_cb_time_ = now; +} + +void SpatialRosI::spatialAlgorithmDataCallback(const double quaternion[4], + double timestamp) +{ + (void)timestamp; + last_quat_w_ = quaternion[3]; + last_quat_x_ = quaternion[0]; + last_quat_y_ = quaternion[1]; + last_quat_z_ = quaternion[2]; +} + +void SpatialRosI::attachCallback() +{ + RCLCPP_INFO(get_logger(), "Phidget Spatial attached."); + + // Set data interval. This is in attachCallback() because it has to be + // repeated on reattachment. + spatial_->setDataInterval(data_interval_ns_ / 1000 / 1000); + + // Force resynchronization, because the device time is reset to 0 after + // reattachment. + synchronize_timestamps_ = true; + can_publish_ = false; + last_cb_time_ = rclcpp::Time(0); +} + +void SpatialRosI::detachCallback() +{ + RCLCPP_INFO(get_logger(), "Phidget Spatial detached."); +} + +} // namespace phidgets + +RCLCPP_COMPONENTS_REGISTER_NODE(phidgets::SpatialRosI) diff --git a/localization-devel-ws/Localization2025.json b/localization-devel-ws/Localization2025.json new file mode 100644 index 0000000..b2ef89e --- /dev/null +++ b/localization-devel-ws/Localization2025.json @@ -0,0 +1,277 @@ +{ + "configById": { + "RawMessages!3t28h31": { + "diffEnabled": false, + "diffMethod": "custom", + "diffTopicPath": "", + "showFullMessageForDiff": false, + "topicPath": "/final_pose", + "fontSize": 12 + }, + "Publish!15frgi1": { + "buttonText": "Publish", + "buttonTooltip": "", + "advancedView": true, + "value": "{\n \"pose\": {\n \"position\": {\n \"x\": 0.2,\n \"y\": 0.2,\n \"z\": 0\n },\n \"orientation\": {\n \"x\": 0,\n \"y\": 0,\n \"z\": 0,\n \"w\": 1\n }\n },\n \"covariance\": [\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0\n ]\n}", + "topicName": "/initial_pose", + "datatype": "geometry_msgs/msg/PoseWithCovariance" + }, + "CallService!1yj1d78": { + "requestPayload": "{}", + "layout": "vertical", + "timeoutSeconds": 10, + "serviceName": "/stop_motor" + }, + "3D!18i6zy7": { + "layers": { + "845139cb-26bc-40b3-8161-8ab60af4baf5": { + "visible": true, + "frameLocked": true, + "label": "Grid", + "instanceId": "845139cb-26bc-40b3-8161-8ab60af4baf5", + "layerId": "foxglove.Grid", + "divisions": 10, + "lineWidth": 1, + "color": "#248eff", + "position": [ + 0, + 0, + 0 + ], + "rotation": [ + 0, + 0, + 0 + ], + "order": 1, + "size": 10 + } + }, + "cameraState": { + "perspective": false, + "distance": 7.756232686968195, + "phi": 0.8308265290941991, + "thetaOffset": 0.31473824063803896, + "targetOffset": [ + 1.4733514080898287, + 0.7413208915944177, + 1.4656570248963758e-17 + ], + "target": [ + 0, + 0, + 0 + ], + "targetOrientation": [ + 0, + 0, + 0, + 1 + ], + "fovy": 45, + "near": 0.5, + "far": 5000, + "logDepth": false + }, + "followMode": "follow-position", + "scene": { + "labelScaleFactor": 1, + "meshUpAxis": "y_up", + "transforms": { + "labelSize": 0.1 + } + }, + "transforms": { + "frame:map": { + "visible": true + }, + "frame:marker_6": { + "visible": true + }, + "frame:cam_mid_link": { + "visible": false + }, + "frame:cam_left_link": { + "visible": false + }, + "frame:camera_base_link": { + "visible": false + }, + "frame:cam_right_link": { + "visible": false + }, + "frame:cam_right_color_frame": { + "visible": false + }, + "frame:cam_right_color_optical_frame": { + "visible": false + }, + "frame:cam_right_depth_frame": { + "visible": false + }, + "frame:cam_right_depth_optical_frame": { + "visible": false + }, + "frame:cam_left_color_frame": { + "visible": false + }, + "frame:cam_left_color_optical_frame": { + "visible": false + }, + "frame:cam_left_depth_frame": { + "visible": false + }, + "frame:cam_left_depth_optical_frame": { + "visible": false + }, + "frame:cam_mid_color_frame": { + "visible": false + }, + "frame:cam_mid_color_optical_frame": { + "visible": false + }, + "frame:cam_mid_depth_frame": { + "visible": false + }, + "frame:cam_mid_depth_optical_frame": { + "visible": false + }, + "frame:beacon_c": { + "visible": true + }, + "frame:beacon_b": { + "visible": true + }, + "frame:base_footprint": { + "visible": true + }, + "frame:laser": { + "visible": false + }, + "frame:beacon_a": { + "visible": true + } + }, + "topics": { + "/robot/scan": { + "visible": false, + "colorField": "range", + "colorMode": "flat", + "colorMap": "turbo", + "decayTime": 0 + }, + "/robot/followed_path": { + "visible": false, + "lineWidth": 0.18 + }, + "/robot/imu/orientation_filtered": { + "visible": true, + "type": "arrow" + }, + "/robot/lidar_bonbonbon": { + "visible": true, + "type": "arrow", + "color": "#ff6b6bff", + "showCovariance": true, + "covarianceColor": "#a0409aa3" + }, + "/robot/map": { + "visible": true + }, + "/robot/move_base/global_costmap/costmap": { + "visible": false + }, + "/robot/move_base/GlobalPlanner/potential": { + "visible": false + }, + "/robot/move_base/local_costmap/costmap": { + "visible": false + }, + "/robot/move_base/global_costmap/footprint": { + "visible": false + }, + "/robot/move_base/local_costmap/footprint": { + "visible": false + }, + "/robot/dock_exec_goal": { + "visible": false + }, + "/robot/local_goal": { + "visible": false + }, + "/robot/path_exec_goal": { + "visible": false + }, + "/robot/move_base/GlobalPlanner/plan": { + "visible": false + }, + "/circles": { + "visible": true, + "showOutlines": false, + "selectedIdVariable": "1", + "namespaces": { + "text": { + "visible": true + }, + "circles": { + "visible": true + } + } + }, + "/scan": { + "visible": false, + "colorField": "intensity", + "colorMode": "colormap", + "colorMap": "turbo" + }, + "/raw_obstacles_visualization": { + "visible": true + }, + "/final_pose": { + "visible": false + }, + "/lidar_pose": { + "visible": true, + "type": "arrow", + "arrowScale": [ + 0.3, + 0.05, + 0.15 + ] + } + }, + "publish": { + "type": "point", + "poseTopic": "/move_base_simple/goal", + "pointTopic": "/clicked_point", + "poseEstimateTopic": "/initialpose", + "poseEstimateXDeviation": 0.5, + "poseEstimateYDeviation": 0.5, + "poseEstimateThetaDeviation": 0.26179939 + }, + "imageMode": {} + } + }, + "globalVariables": { + "1": 6 + }, + "userNodes": {}, + "playbackConfig": { + "speed": 1 + }, + "layout": { + "direction": "row", + "first": { + "first": "RawMessages!3t28h31", + "second": { + "first": "Publish!15frgi1", + "second": "CallService!1yj1d78", + "direction": "column" + }, + "direction": "column", + "splitPercentage": 23.14814814814815 + }, + "second": "3D!18i6zy7", + "splitPercentage": 29.545454545454547 + } +} \ No newline at end of file diff --git a/localization-devel-ws/btcpp_ros2_interfaces/CMakeLists.txt b/localization-devel-ws/btcpp_ros2_interfaces/CMakeLists.txt new file mode 100644 index 0000000..5e3553e --- /dev/null +++ b/localization-devel-ws/btcpp_ros2_interfaces/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.8) +project(btcpp_ros2_interfaces) + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +# find dependencies +find_package(ament_cmake REQUIRED) +# uncomment the following section in order to fill in +# further dependencies manually. +# find_package( REQUIRED) + +find_package(rosidl_default_generators REQUIRED) + +rosidl_generate_interfaces(${PROJECT_NAME} + "srv/StartUpSrv.srv" +) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + # the following line skips the linter which checks for copyrights + # comment the line when a copyright and license is added to all source files + set(ament_cmake_copyright_FOUND TRUE) + # the following line skips cpplint (only works in a git repo) + # comment the line when this package is in a git repo and when + # a copyright and license is added to all source files + set(ament_cmake_cpplint_FOUND TRUE) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/LICENSE b/localization-devel-ws/btcpp_ros2_interfaces/LICENSE similarity index 99% rename from localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/LICENSE rename to localization-devel-ws/btcpp_ros2_interfaces/LICENSE index 261eeb9..d645695 100644 --- a/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/LICENSE +++ b/localization-devel-ws/btcpp_ros2_interfaces/LICENSE @@ -1,3 +1,4 @@ + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ diff --git a/localization-devel-ws/btcpp_ros2_interfaces/package.xml b/localization-devel-ws/btcpp_ros2_interfaces/package.xml new file mode 100644 index 0000000..264b78a --- /dev/null +++ b/localization-devel-ws/btcpp_ros2_interfaces/package.xml @@ -0,0 +1,22 @@ + + + + btcpp_ros2_interfaces + 0.0.0 + TODO: Package description + user + Apache-2.0 + + ament_cmake + + ament_lint_auto + ament_lint_common + + + ament_cmake + + + rosidl_default_generators + rosidl_default_runtime + rosidl_interface_packages + diff --git a/localization-devel-ws/btcpp_ros2_interfaces/srv/StartUpSrv.srv b/localization-devel-ws/btcpp_ros2_interfaces/srv/StartUpSrv.srv new file mode 100644 index 0000000..463c654 --- /dev/null +++ b/localization-devel-ws/btcpp_ros2_interfaces/srv/StartUpSrv.srv @@ -0,0 +1,6 @@ +# Start Service +int32 group +int32 state +--- +int32 group +bool success \ No newline at end of file diff --git a/localization-devel-ws/cust_foxglove_launch.py b/localization-devel-ws/cust_foxglove_launch.py new file mode 100644 index 0000000..f72f20d --- /dev/null +++ b/localization-devel-ws/cust_foxglove_launch.py @@ -0,0 +1,15 @@ +import launch +from launch_ros.actions import Node + +def generate_launch_description(): + return launch.LaunchDescription([ + Node( + package="foxglove_bridge", + executable="foxglove_bridge", + name="foxglove_bridge", + output="screen", + parameters=[{ + "topic_whitelist": ["^(?!/rosout$).*"] # Exclude /rosout + }] + ) + ]) diff --git a/localization-devel-ws/ekf/.gitignore b/localization-devel-ws/ekf/.gitignore new file mode 100644 index 0000000..0331f63 --- /dev/null +++ b/localization-devel-ws/ekf/.gitignore @@ -0,0 +1 @@ +baggy/ \ No newline at end of file diff --git a/localization-devel-ws/ekf/LICENSE b/localization-devel-ws/ekf/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/localization-devel-ws/ekf/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/src/ttt.txt b/localization-devel-ws/ekf/ekf/__init__.py similarity index 100% rename from localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/src/ttt.txt rename to localization-devel-ws/ekf/ekf/__init__.py diff --git a/localization-devel-ws/ekf/ekf/ekf_node.py b/localization-devel-ws/ekf/ekf/ekf_node.py new file mode 100644 index 0000000..09af4da --- /dev/null +++ b/localization-devel-ws/ekf/ekf/ekf_node.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python3 +import math +from geometry_msgs.msg import TransformStamped, PoseWithCovarianceStamped, PoseStamped +from nav_msgs.msg import Odometry +import numpy as np + +import rclpy +from rclpy.node import Node + +from tf2_ros.static_transform_broadcaster import StaticTransformBroadcaster + +def quaternion_from_euler(roll, pitch, yaw): + roll, pitch, yaw = roll / 2.0, pitch / 2.0, yaw / 2.0 + cy, sy = math.cos(yaw), math.sin(yaw) + cp, sp = math.cos(pitch), math.sin(pitch) + cr, sr = math.cos(roll), math.sin(roll) + return [sr * cp * cy - cr * sp * sy, + cr * sp * cy + sr * cp * sy, + cr * cp * sy - sr * sp * cy, + cr * cp * cy + sr * sp * sy] + +def euler_from_quaternion(x, y, z, w): + t3, t4 = +2.0 * (w * z + x * y), +1.0 - 2.0 * (y * y + z * z) + yaw = math.atan2(t3, t4) + return yaw + +def normalize_angle(angle): + return math.atan2(math.sin(angle), math.cos(angle)) + +def is_invalid_data(x, y): + return np.isnan(x) or np.isnan(y) + +class EKFFootprintBroadcaster(Node): + def __init__(self): + super().__init__('ekf') + + self.X = np.array([0.4, 1.7, 0.0]) # State vector: x, y, theta + self.P = np.eye(3) * 9 * 1e-4 + self.P[2, 2] = 0.003 + self.Q = np.eye(3) + self.R_gps = np.eye(3) * 1e-2 + self.R_gps[2, 2] = 0.09 + self.R_camera = np.eye(3) * 1e-2 + + self.last_odom_time = self.get_clock().now().nanoseconds / 1e9 + self.gps_time = self.get_clock().now().nanoseconds / 1e9 + self.claim_parameters() + self.tf_static_broadcaster = StaticTransformBroadcaster(self) + self.t = TransformStamped() + self.t.header.frame_id = self.parent_frame_id + self.t.child_frame_id = self.child_frame_id + self.final_pose = PoseWithCovarianceStamped() + self.final_pose.header.frame_id = self.parent_frame_id + self.cam_measurement = [-100, -100, -100] + self.init_topics() + + self.footprint_publish() + + self.fast_spin = False + self.init = False + + def claim_parameters(self): + self.declare_parameter('robot_parent_frame_id', 'map') + self.declare_parameter('robot_frame_id', 'base_footprint') + self.declare_parameter('update_rate', 1) + self.declare_parameter('q_linear', 1e-3) + self.declare_parameter('q_angular', 1e-2) + self.declare_parameter('r_camera_linear', 1e-2) + self.declare_parameter('r_camera_angular', 0.15) + self.declare_parameter('r_threshold_xy', 1e-3) + self.declare_parameter('r_threshold_theta', 1e-2) + self.declare_parameter('refresh_zone_xl', 0.0) + self.declare_parameter('refresh_zone_xr', 3.0) + self.declare_parameter('refresh_zone_yl', 0.0) + self.declare_parameter('refresh_zone_yr', 2.0) + self.declare_parameter('fast_spin_threshold', 3.0) + self.declare_parameter('fast_vx_threshold', 0.5) + self.declare_parameter('fast_vy_threshold', 0.5) + self.parent_frame_id = self.get_parameter('robot_parent_frame_id').value + self.child_frame_id = self.get_parameter('robot_frame_id').value + self.rate = self.get_parameter('update_rate').value + self.Q[0, 0] = self.get_parameter('q_linear').value + self.Q[1, 1] = self.get_parameter('q_linear').value + self.Q[2, 2] = self.get_parameter('q_angular').value + self.R_camera[0, 0] = self.get_parameter('r_camera_linear').value + self.R_camera[1, 1] = self.get_parameter('r_camera_linear').value + self.R_camera[2, 2] = self.get_parameter('r_camera_angular').value + self.r_threshold_xy = self.get_parameter('r_threshold_xy').value + self.r_threshold_theta = self.get_parameter('r_threshold_theta').value + self.refresh_zone_xl = self.get_parameter('refresh_zone_xl').value + self.refresh_zone_xr = self.get_parameter('refresh_zone_xr').value + self.refresh_zone_yl = self.get_parameter('refresh_zone_yl').value + self.refresh_zone_yr = self.get_parameter('refresh_zone_yr').value + self.fast_spin_threshold = self.get_parameter('fast_spin_threshold').value + self.fast_vx_threshold = self.get_parameter('fast_vx_threshold').value + self.fast_vy_threshold = self.get_parameter('fast_vy_threshold').value + def init_topics(self): + self.create_subscription(PoseWithCovarianceStamped, 'lidar_pose', self.gps_callback, 1) + self.create_subscription(PoseWithCovarianceStamped, 'initial_pose', self.init_callback,1) + self.create_subscription(Odometry, 'local_filter', self.local_callback, 1) + self.create_subscription(PoseStamped, 'camera_pose', self.camera_callback, 1) + self.ekf_pose_publisher = self.create_publisher(PoseWithCovarianceStamped, 'final_pose', 1) + + + def init_callback(self, msg): + + self.get_logger().info("Initial pose received, initializing EKF state.") + + self.X[0] = msg.pose.pose.position.x + self.X[1] = msg.pose.pose.position.y + + theta = euler_from_quaternion( + msg.pose.pose.orientation.x, + msg.pose.pose.orientation.y, + msg.pose.pose.orientation.z, + msg.pose.pose.orientation.w + ) + self.X[2] = theta + if msg.pose.covariance[0] > 0 and msg.pose.covariance[7] > 0 and msg.pose.covariance[35] > 0: + if msg.pose.covariance[0] < 1 and msg.pose.covariance[7] < 1 and msg.pose.covariance[35] < 1: + self.P[0, 0] = msg.pose.covariance[0] + self.P[1, 1] = msg.pose.covariance[7] + self.P[2, 2] = msg.pose.covariance[35] + + def gps_callback(self, msg): + if self.fast_spin: + return + self.gps_time = msg.header.stamp.sec + msg.header.stamp.nanosec * 1e-9 + current_time = self.get_clock().now().nanoseconds / 1e9 + if abs(current_time - self.gps_time) > 1.5: + return + + if is_invalid_data(msg.pose.pose.position.x, msg.pose.pose.position.y): + return + + theta = euler_from_quaternion( + msg.pose.pose.orientation.x, + msg.pose.pose.orientation.y, + msg.pose.pose.orientation.z, + msg.pose.pose.orientation.w + ) + + gps_measurement = np.array([msg.pose.pose.position.x, msg.pose.pose.position.y, theta]) + + # check refresh zone + if gps_measurement[0] < self.refresh_zone_xl or gps_measurement[0] > self.refresh_zone_xr or gps_measurement[1] < self.refresh_zone_yl or gps_measurement[1] > self.refresh_zone_yr: + if self.init: + return + + self.R_gps[0, 0] = msg.pose.covariance[0] + self.R_gps[1, 1] = msg.pose.covariance[7] + self.R_gps[2, 2] = msg.pose.covariance[35] + for i in range(2): + if self.R_gps[i, i] > self.r_threshold_xy : + self.R_gps[i, i] = self.r_threshold_xy + if self.R_gps[2, 2] > self.r_threshold_theta: + self.R_gps[2, 2] = self.r_threshold_theta + self.ekf_update(gps_measurement, self.R_gps) + self.init = True + + def camera_callback(self, msg): + self.cam_time = msg.header.stamp.sec + msg.header.stamp.nanosec * 1e-9 + if is_invalid_data(msg.pose.position.x, msg.pose.position.y): + return + + theta = euler_from_quaternion( + msg.pose.orientation.x, + msg.pose.orientation.y, + msg.pose.orientation.z, + msg.pose.orientation.w + ) + self.cam_measurement = np.array([msg.pose.position.x, msg.pose.position.y, theta]) + self.camera_update() + + def camera_update(self): + current_time = self.get_clock().now().nanoseconds / 1e9 + if abs(current_time - self.cam_time) > 1.5: + self.cam_measurement = [-100, -100, -100] + return + if self.cam_measurement[0]==-100: # Check if the measurement is valid + self.get_logger().warn("Invalid cam measurement data received.") + return + null_time = abs(current_time - self.gps_time) + if null_time > 0.2: + self.R_camera[2,2] = 1e-10 + + self.ekf_update(self.cam_measurement, self.R_camera) + self.R_camera[0,0] = 1e-2 + self.R_camera[1,1] = 1e-2 + self.R_camera[2,2] = 0.15 + + def local_callback(self, msg): + current_time = self.get_clock().now().nanoseconds / 1e9 + dt = current_time - self.last_odom_time + self.last_odom_time = current_time + + v_x = msg.twist.twist.linear.x + v_y = msg.twist.twist.linear.y + w = msg.twist.twist.angular.z + self.ekf_predict(v_x, v_y, w, dt) + + if w > self.fast_spin_threshold or v_x > self.fast_vx_threshold or v_y > self.fast_vy_threshold: + self.fast_spin = True + else: + self.fast_spin = False + + def ekf_predict(self, v_x, v_y, w, dt): + theta = self.X[2] + # c_theta = math.cos(theta) + # s_theta = math.sin(theta) + # c_delta = math.cos(w * dt) + # s_delta = math.sin(w * dt) + # if abs(w) > 1e-3: + # self.X[0] += (c_theta*s_delta - s_theta*(c_delta-1))*v_x / w - (s_theta*s_delta - c_theta*(c_delta-1))*v_y / w + # self.X[1] += (s_theta*s_delta - c_theta*(c_delta-1))*v_x / w + (c_theta*s_delta - s_theta*(c_delta-1))*v_y / w + # else: + self.X[0] += v_x * dt * math.cos(theta + w * dt) - v_y * dt * math.sin(theta + w * dt) + self.X[1] += v_x * dt *math.sin(theta + w * dt) + v_y * dt * math.cos(theta + w * dt) + + self.X[2] += w * dt + self.X[2] = normalize_angle(self.X[2]) + self.footprint_publish() + self.P = self.P + self.Q + + def ekf_update(self, z, R): + if np.any(np.isnan(z)): # Check if the measurement is valid + self.get_logger().warn("Invalid measurement data received.") + return + + K = self.P @ np.linalg.inv(self.P + R) + self.P = (np.eye(3) - K) @ self.P + residual = z - self.X + if abs(residual[2]) > math.pi: + residual[2] = normalize_angle(residual[2]) + self.X = self.X + K @ residual + + def footprint_publish(self): + self.final_pose.header.stamp = self.get_clock().now().to_msg() + self.t.header.stamp = self.get_clock().now().to_msg() + self.t.transform.translation.x = self.X[0] + self.t.transform.translation.y = self.X[1] + self.t.transform.translation.z = 0.0 + quat = quaternion_from_euler(0, 0, self.X[2]) + self.t.transform.rotation.x = quat[0] + self.t.transform.rotation.y = quat[1] + self.t.transform.rotation.z = quat[2] + self.t.transform.rotation.w = quat[3] + self.tf_static_broadcaster.sendTransform(self.t) + + + self.final_pose.pose.pose.position.x = self.X[0] + self.final_pose.pose.pose.position.y = self.X[1] + self.final_pose.pose.pose.position.z = 0.0 + self.final_pose.pose.pose.orientation.x = quat[0] + self.final_pose.pose.pose.orientation.y = quat[1] + self.final_pose.pose.pose.orientation.z = quat[2] + self.final_pose.pose.pose.orientation.w = quat[3] + self.final_pose.pose.covariance[0] = self.P[0, 0] + self.final_pose.pose.covariance[7] = self.P[1, 1] + self.final_pose.pose.covariance[35] = self.P[2, 2] + self.ekf_pose_publisher.publish(self.final_pose) + + +def main(args=None): + rclpy.init(args=args) + ekf = EKFFootprintBroadcaster() + rclpy.spin(ekf) # Keep the node running + rclpy.shutdown() # Shut down the ROS 2 client + +if __name__ == '__main__': + main() + diff --git a/localization-devel-ws/ekf/ekf/fake_cam.py b/localization-devel-ws/ekf/ekf/fake_cam.py new file mode 100644 index 0000000..ff75a1c --- /dev/null +++ b/localization-devel-ws/ekf/ekf/fake_cam.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +import rclpy +from rclpy.node import Node + +from nav_msgs.msg import Odometry +from builtin_interfaces.msg import Time + +class CameraPublisher(Node): + def __init__(self): + super().__init__('fake_cam_broadcast') + self.camera_publisher = self.create_publisher(Odometry, 'camera', 10) + self.get_logger().info("cam publisher init") + self.publish_camera() + + def publish_camera(self): + data_list = [ + {"time": 50.0, "position": (0.4806712233883248, 1.0932451234202643, 0), "orientation": (0, 0, 0.9999016629254438, -0.014023711310927924)}, + {"time": 50.5, "position": (0.4806712233883248, 1.0932451234202643, 0), "orientation": (0, 0, 0.9999016629254438, -0.014023711310927924)}, + {"time": 51, "position": (0.4806712233883248, 1.0932451234202643, 0), "orientation": (0, 0, 0.9999016629254438, -0.014023711310927924)}, + ] + + clock = self.get_clock() + start_time = clock.now().seconds_nanoseconds()[0] # Seconds since epoch + for data in data_list: + target_time = start_time + data["time"] + self.get_logger().info(f"Start time: {start_time}, Target time: {target_time}") + + current_time = clock.now().seconds_nanoseconds()[0] + delay = target_time - current_time + + if delay > 0: + self.get_logger().info(f"Sleeping for {delay} seconds to match target time") + self.create_timer(delay, lambda: None) + + odom_msg = Odometry() + odom_msg.header.stamp = Time(sec=int(target_time), nanosec=int((target_time % 1) * 1e9)) + odom_msg.header.frame_id = "robot/map" + + # Set position and orientation + odom_msg.pose.pose.position.x = data["position"][0] + odom_msg.pose.pose.position.y = data["position"][1] + odom_msg.pose.pose.position.z = data["position"][2] + odom_msg.pose.pose.orientation.x = data["orientation"][0] + odom_msg.pose.pose.orientation.y = data["orientation"][1] + odom_msg.pose.pose.orientation.z = data["orientation"][2] + odom_msg.pose.pose.orientation.w = data["orientation"][3] + + # Publish message + self.camera_pub.publish(odom_msg) + self.get_logger().info(f"Published camera data at target time {target_time}") + + self.get_logger().info("Finished publishing camera data") + +def main(args=None): + rclpy.init(args=args) + node = CameraPublisher() + try: + rclpy.spin(node) + except KeyboardInterrupt: + node.get_logger().info("Node interrupted by user, shutting down.") + finally: + node.destroy_node() + rclpy.shutdown() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/localization-devel-ws/ekf/ekf/test_br.py b/localization-devel-ws/ekf/ekf/test_br.py new file mode 100644 index 0000000..a11d8a9 --- /dev/null +++ b/localization-devel-ws/ekf/ekf/test_br.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +import math +from geometry_msgs.msg import TransformStamped, PoseWithCovarianceStamped +from nav_msgs.msg import Odometry +import numpy as np + +import rclpy +from rclpy.node import Node + +from tf2_ros.static_transform_broadcaster import StaticTransformBroadcaster +def quaternion_from_euler(ai, aj, ak): + ai /= 2.0 + aj /= 2.0 + ak /= 2.0 + ci = math.cos(ai) + si = math.sin(ai) + cj = math.cos(aj) + sj = math.sin(aj) + ck = math.cos(ak) + sk = math.sin(ak) + cc = ci*ck + cs = ci*sk + sc = si*ck + ss = si*sk + + q = np.empty((4, )) + q[0] = cj*sc - sj*cs + q[1] = cj*ss + sj*cc + q[2] = cj*cs - sj*sc + q[3] = cj*cc + sj*ss + + return q +class TestBroadcaster(Node): + def __init__(self): + super().__init__('ekf_br') + self.tf_static_broadcaster = StaticTransformBroadcaster(self) + + self.parent_frame_id = 'robot/map' + self.child_frame_id = 'robot/base_footprint' + + self.X = np.array([0.5, 0.4, 0.0]) + + self.ekf_pose = self.create_publisher(Odometry, 'final_pose', 10) + self.footprint_publish() + + def footprint_publish(self): + t = TransformStamped() + + t.header.stamp = self.get_clock().now().to_msg() + t.header.frame_id = self.parent_frame_id + t.child_frame_id = self.child_frame_id + t.transform.translation.x = self.X[0] + t.transform.translation.y = self.X[1] + t.transform.translation.z = 0.0 + quat = quaternion_from_euler(0, 0, self.X[2]) + t.transform.rotation.x = quat[0] + t.transform.rotation.y = quat[1] + t.transform.rotation.z = quat[2] + t.transform.rotation.w = quat[3] + self.tf_static_broadcaster.sendTransform(t) + +def main(args=None): + rclpy.init(args=args) + br = TestBroadcaster() + rclpy.spin(br) # Keep the node running + rclpy.shutdown() # Shut down the ROS 2 client + +if __name__ == '__main__': + main() diff --git a/localization-devel-ws/ekf/launch/b_running_launch.py b/localization-devel-ws/ekf/launch/b_running_launch.py new file mode 100644 index 0000000..0ae363b --- /dev/null +++ b/localization-devel-ws/ekf/launch/b_running_launch.py @@ -0,0 +1,194 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, GroupAction, IncludeLaunchDescription, SetLaunchConfiguration, TimerAction +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution +from launch.launch_description_sources import PythonLaunchDescriptionSource, AnyLaunchDescriptionSource +from launch_ros.actions import Node +from launch_ros.substitutions import FindPackageShare + + +def generate_launch_description(): + rival_name = LaunchConfiguration('rival_name') + side = LaunchConfiguration('side') + + + local_filter_launch_path = PathJoinSubstitution([ + FindPackageShare('local_filter'), + 'launch', + 'local_filter_whole.xml' + ]) + + # rplidar_launch = PathJoinSubstitution([ + # FindPackageShare('lidar_localization_pkg'), + # 'launch', + # 'firmware', + # 'rplidar_s3_launch.py' + # ]) + + ydlidar_launch = PathJoinSubstitution([ + FindPackageShare('lidar_localization_pkg'), + 'launch', + 'firmware', + 'ydlidar_launch.py' + ]) + + obstacle_extractor_launch = PathJoinSubstitution([ + FindPackageShare('lidar_localization_pkg'), + 'launch', + 'obstacle_extractor_launch.xml' + ]) + + healthcheck_node = Node( + package='healthcheck', + executable='healthcheck_node', + name='healthcheck_node', + output='screen' + ) + + ekf_node = Node( + package='ekf', + executable='ekf_node', + name='ekf_node', + output='screen', + parameters=[{ + 'use_cam': 0, + 'robot_parent_frame_id': 'map', + 'robot_frame_id': 'base_footprint', + '/use_sim_time': False, + 'q_linear': 1.2e-6, + 'q_angular': 1.7e-7, + 'r_camra_linear': 1e-2, + 'r_camra_angular': 0.1, + 'r_threshold_xy': 1e-2, + 'r_threshold_theta': 1e-1 + }], + remappings=[ + ('initalpose', ['initial_pose']) + ] + + ) + + lidar_node = Node( + package='lidar_localization_pkg', + executable='lidar_localization', + name='lidar_localization', + + arguments=['--ros-args', '--log-level', 'info'], + output='screen', + parameters=[{ + 'side': side, + 'debug_mode': False, + 'visualize_candidate': True, + 'likelihood_threshold': 0.8, + + 'consistency_threshold': 0.95, + 'lidar_multiplier': 0.987 + }] + ) + + local_filter_launch = IncludeLaunchDescription( + AnyLaunchDescriptionSource(local_filter_launch_path) + ) + + rival_node = Node( + package='rival_localization', + executable='rival_localization', + name='rival_localization', + output='screen', + parameters=[ + { + 'robot_name': 'robot', + 'frequency': 10.0, + 'x_max': 3.0, + 'x_min': 0.0, + 'y_max': 2.0, + 'y_min': 0.0, + 'vel_lpf_gain': 0.9, + 'locking_rad': 0.3, + 'lockrad_growing_rate': 0.3, + 'is_me': 0.3, + 'cam_weight': 6.0, + 'obs_weight': 10.0, + 'side_weight': 2.0, + 'cam_side_threshold': 0.2, + 'side_obs_threshold': 0.2, + 'obs_cam_threshold': 0.2, + 'crossed_areas': [ + 2.55, 3.0, 0.65, 1.1, + 2.4, 2.85, 1.55, 2.0, + 2.0, 3.0, 0.0, 0.15, + 1.0, 2.0, 0.0, 0.4, + 0.0, 1.0, 0.0, 0.15, + 0.0, 0.45, 0.65, 1.1, + 0.15, 0.6, 1.55, 2.0, + 1.05, 1.95, 1.50, 2.0 + ] + } + ], + remappings=[ + ('raw_pose', [rival_name, '/raw_pose']), + ('/ceiling_rival/pose','/vision/aruco/rival_pose') + ] + ) + + rival_obstacle_node = Node( + package='obstacle_detector', + executable='obstacle_extractor_node', + name='obstacle_detector_to_map', + parameters=[{ + 'frame_id': 'map', + 'active': True, + 'use_scan': True, + 'use_pcl': False, + 'use_split_and_merge': True, + 'circles_from_visibles': True, + 'discard_converted_segments': False, + 'transform_coordinates': True, + 'min_group_points': 5, + 'max_group_distance': 0.04, + 'distance_proportion': 0.00628, + 'max_split_distance': 0.02, + 'max_merge_separation': 0.25, + 'max_merge_spread': 0.02, + 'max_circle_radius': 0.2, + 'radius_enlargement': 0.05, + 'pose_array': True + }], + remappings=[ + ('raw_obstacles', '/obstacles_to_map'), + ('scan', '/scan'), + ('raw_obstacles_visualization_pcl', 'raw_obstacle_visualization_to_map_pcl') + ] + ) + + static_tf = Node( + package='tf2_ros', + executable='static_transform_publisher', + name='robot_to_laser', + output='screen', + arguments=[ + '--x', '0', '--y', '0', '--z', '0', + '--roll', '0', '--pitch', '0', '--yaw', '-1.580239', + '--frame-id', 'base_footprint', + '--child-frame-id', 'laser' + ] + ) + + ydlidar_include = IncludeLaunchDescription(PythonLaunchDescriptionSource(ydlidar_launch)) + obstacle_extractor_include = IncludeLaunchDescription(AnyLaunchDescriptionSource(obstacle_extractor_launch)) + + return LaunchDescription([ + DeclareLaunchArgument('rival_name', default_value='rival'), + DeclareLaunchArgument('side', default_value='0'), + + static_tf, + ydlidar_include, + obstacle_extractor_include, + + ekf_node, + healthcheck_node, + + # TimerAction(period=2.0, actions=[ekf_node]), + TimerAction(period=4.0, actions=[lidar_node]), + TimerAction(period=6.0, actions=[local_filter_launch]), + TimerAction(period=8.0, actions=[rival_node, rival_obstacle_node]) + ]) diff --git a/localization-devel-ws/ekf/launch/bag_test.launch b/localization-devel-ws/ekf/launch/bag_test.launch new file mode 100644 index 0000000..617a0dc --- /dev/null +++ b/localization-devel-ws/ekf/launch/bag_test.launch @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/localization-devel-ws/ekf/launch/blue.launch b/localization-devel-ws/ekf/launch/blue.launch new file mode 100644 index 0000000..07f8334 --- /dev/null +++ b/localization-devel-ws/ekf/launch/blue.launch @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/localization-devel-ws/ekf/launch/fake_cam.launch b/localization-devel-ws/ekf/launch/fake_cam.launch new file mode 100644 index 0000000..fd04253 --- /dev/null +++ b/localization-devel-ws/ekf/launch/fake_cam.launch @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/localization-devel-ws/ekf/launch/run.launch b/localization-devel-ws/ekf/launch/run.launch new file mode 100644 index 0000000..f40af4d --- /dev/null +++ b/localization-devel-ws/ekf/launch/run.launch @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/localization-devel-ws/ekf/launch/y_running_launch.py b/localization-devel-ws/ekf/launch/y_running_launch.py new file mode 100644 index 0000000..fb86b2e --- /dev/null +++ b/localization-devel-ws/ekf/launch/y_running_launch.py @@ -0,0 +1,195 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, GroupAction, IncludeLaunchDescription, SetLaunchConfiguration, TimerAction +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution +from launch.launch_description_sources import PythonLaunchDescriptionSource, AnyLaunchDescriptionSource +from launch_ros.actions import Node +from launch_ros.substitutions import FindPackageShare + + +def generate_launch_description(): + rival_name = LaunchConfiguration('rival_name') + side = LaunchConfiguration('side') + + local_filter_launch_path = PathJoinSubstitution([ + FindPackageShare('local_filter'), + 'launch', + 'local_filter_whole.xml' + ]) + + # rplidar_launch = PathJoinSubstitution([ + # FindPackageShare('lidar_localization_pkg'), + # 'launch', + # 'firmware', + # 'rplidar_s3_launch.py' + # ]) + + ydlidar_launch = PathJoinSubstitution([ + FindPackageShare('lidar_localization_pkg'), + 'launch', + 'firmware', + 'ydlidar_launch.py' + ]) + + obstacle_extractor_launch = PathJoinSubstitution([ + FindPackageShare('lidar_localization_pkg'), + 'launch', + 'obstacle_extractor_launch.xml' + ]) + + healthcheck_node = Node( + package='healthcheck', + executable='healthcheck_node', + name='healthcheck_node', + output='screen' + # ,remappings=[ + # ['/vision/aruco/robot/single/average_pose', '/vision/aruco/robot_pose'] + # ] + ) + + ekf_node = Node( + package='ekf', + executable='ekf_node', + name='ekf_node', + output='screen', + parameters=[{ + 'use_cam': 0, + 'robot_parent_frame_id': 'map', + 'robot_frame_id': 'base_footprint', + '/use_sim_time': False, + 'q_linear': 1.2e-6, + 'q_angular': 1.7e-7, + 'r_camra_linear': 1e-2, + 'r_camra_angular': 0.1, + 'r_threshold_xy': 1e-2, + 'r_threshold_theta': 1e-1 + }], + remappings=[ + ('initalpose', ['initial_pose']) + ] + + ) + + lidar_node = Node( + package='lidar_localization_pkg', + executable='lidar_localization', + name='lidar_localization', + arguments=['--ros-args', '--log-level', 'info'], + output='screen', + parameters=[{ + 'side': side, + 'debug_mode': False, + 'visualize_candidate': True, + 'likelihood_threshold': 0.8, + 'consistency_threshold': 0.95, + 'lidar_multiplier': 0.987 + }] + ) + + local_filter_launch = IncludeLaunchDescription( + AnyLaunchDescriptionSource(local_filter_launch_path) + ) + + rival_node = Node( + package='rival_localization', + executable='rival_localization', + name='rival_localization', + output='screen', + parameters=[rival_config_path], + remappings=[ + ('raw_pose', [rival_name, '/raw_pose']), + parameters=[ + { + 'robot_name': 'robot', + 'frequency': 10.0, + 'x_max': 3.0, + 'x_min': 0.0, + 'y_max': 2.0, + 'y_min': 0.0, + 'vel_lpf_gain': 0.9, + 'locking_rad': 0.3, + 'lockrad_growing_rate': 0.3, + 'is_me': 0.3, + 'cam_weight': 6.0, + 'obs_weight': 10.0, + 'side_weight': 2.0, + 'cam_side_threshold': 0.2, + 'side_obs_threshold': 0.2, + 'obs_cam_threshold': 0.2, + 'crossed_areas': [ + 2.55, 3.0, 0.65, 1.1, + 2.4, 2.85, 1.55, 2.0, + 2.0, 3.0, 0.0, 0.15, + 1.0, 2.0, 0.0, 0.4, + 0.0, 1.0, 0.0, 0.15, + 0.0, 0.45, 0.65, 1.1, + 0.15, 0.6, 1.55, 2.0, + 1.05, 1.95, 1.50, 2.0 + ] + } + ], + remappings=[ + ('raw_pose', [rival_name, '/raw_pose']), + ('/ceiling_rival/pose','/vision/aruco/rival_pose') + ] + ) + + rival_obstacle_node = Node( + package='obstacle_detector', + executable='obstacle_extractor_node', + name='obstacle_detector_to_map', + parameters=[{ + 'frame_id': 'map', + 'active': True, + 'use_scan': True, + 'use_pcl': False, + 'use_split_and_merge': True, + 'circles_from_visibles': True, + 'discard_converted_segments': False, + 'transform_coordinates': True, + 'min_group_points': 5, + 'max_group_distance': 0.04, + 'distance_proportion': 0.00628, + 'max_split_distance': 0.02, + 'max_merge_separation': 0.25, + 'max_merge_spread': 0.02, + 'max_circle_radius': 0.2, + 'radius_enlargement': 0.05, + 'pose_array': True + }], + remappings=[ + ('raw_obstacles', '/obstacles_to_map'), + ('scan', '/scan'), + ('raw_obstacles_visualization_pcl', 'raw_obstacle_visualization_to_map_pcl') + ] + ) + + static_tf = Node( + package='tf2_ros', + executable='static_transform_publisher', + name='robot_to_laser', + output='screen', + arguments=[ + '--x', '0', '--y', '0', '--z', '0', + '--roll', '0', '--pitch', '0', '--yaw', '-1.580239', + '--frame-id', 'base_footprint', + '--child-frame-id', 'laser' + ] + ) + + # rplidar_include = IncludeLaunchDescription(PythonLaunchDescriptionSource(rplidar_launch)) + ydlidar_include = IncludeLaunchDescription(PythonLaunchDescriptionSource(ydlidar_launch)) + obstacle_extractor_include = IncludeLaunchDescription(AnyLaunchDescriptionSource(obstacle_extractor_launch)) + + return LaunchDescription([ + DeclareLaunchArgument('rival_name', default_value='rival'), + DeclareLaunchArgument('side', default_value='0'), + static_tf, + ydlidar_include, + obstacle_extractor_include, + ekf_node, + healthcheck_node, + # TimerAction(period=2.0, actions=[ekf_node]), + TimerAction(period=4.0, actions=[lidar_node]), + TimerAction(period=6.0, actions=[local_filter_launch]), + TimerAction(period=8.0, actions=[rival_node, rival_obstacle_node]) + ]) diff --git a/localization-devel-ws/ekf/launch/yellow.launch b/localization-devel-ws/ekf/launch/yellow.launch new file mode 100644 index 0000000..9c7f222 --- /dev/null +++ b/localization-devel-ws/ekf/launch/yellow.launch @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/localization-devel-ws/ekf/package.xml b/localization-devel-ws/ekf/package.xml new file mode 100644 index 0000000..b7f798d --- /dev/null +++ b/localization-devel-ws/ekf/package.xml @@ -0,0 +1,27 @@ + + + + ekf + 0.0.0 + TODO: Package description + user + Apache-2.0 + + ros2launch + rclpy + std_msgs + nav_msgs + geometry_msgs + numpy + math + tf2 + + ament_copyright + ament_flake8 + ament_pep257 + python3-pytest + + + ament_python + + diff --git a/localization-devel-ws/ekf/resource/ekf b/localization-devel-ws/ekf/resource/ekf new file mode 100644 index 0000000..e69de29 diff --git a/localization-devel-ws/ekf/setup.cfg b/localization-devel-ws/ekf/setup.cfg new file mode 100644 index 0000000..10b4728 --- /dev/null +++ b/localization-devel-ws/ekf/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script_dir=$base/lib/ekf +[install] +install_scripts=$base/lib/ekf diff --git a/localization-devel-ws/ekf/setup.py b/localization-devel-ws/ekf/setup.py new file mode 100644 index 0000000..93b9771 --- /dev/null +++ b/localization-devel-ws/ekf/setup.py @@ -0,0 +1,35 @@ +import os # Import os module +from setuptools import find_packages, setup +from glob import glob + +package_name = 'ekf' + +launch_files = glob('launch/*.launch') + glob('launch/*.py') + +setup( + name=package_name, + version='0.0.0', + packages=find_packages(exclude=['test']), + data_files=[ + ('share/ament_index/resource_index/packages', + ['resource/' + package_name]), + ('share/' + package_name, + ['package.xml']), + (os.path.join('share', package_name, 'launch'), + launch_files), + ], + install_requires=['setuptools'], + zip_safe=True, + maintainer='user', + maintainer_email='zhen.cc.1207@gmail.com', + description='TODO: Package description', + license='Apache-2.0', + tests_require=['pytest'], + entry_points={ + 'console_scripts': [ + 'ekf_node = ekf.ekf_node:main', + 'test_br = ekf.test_br:main', + 'fake_cam = ekf.fake_cam:main', + ], + }, +) diff --git a/localization-devel-ws/ekf/test/test_copyright.py b/localization-devel-ws/ekf/test/test_copyright.py new file mode 100644 index 0000000..97a3919 --- /dev/null +++ b/localization-devel-ws/ekf/test/test_copyright.py @@ -0,0 +1,25 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_copyright.main import main +import pytest + + +# Remove the `skip` decorator once the source file(s) have a copyright header +@pytest.mark.skip(reason='No copyright header has been placed in the generated source file.') +@pytest.mark.copyright +@pytest.mark.linter +def test_copyright(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found errors' diff --git a/localization-devel-ws/ekf/test/test_flake8.py b/localization-devel-ws/ekf/test/test_flake8.py new file mode 100644 index 0000000..27ee107 --- /dev/null +++ b/localization-devel-ws/ekf/test/test_flake8.py @@ -0,0 +1,25 @@ +# Copyright 2017 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_flake8.main import main_with_errors +import pytest + + +@pytest.mark.flake8 +@pytest.mark.linter +def test_flake8(): + rc, errors = main_with_errors(argv=[]) + assert rc == 0, \ + 'Found %d code style errors / warnings:\n' % len(errors) + \ + '\n'.join(errors) diff --git a/localization-devel-ws/ekf/test/test_pep257.py b/localization-devel-ws/ekf/test/test_pep257.py new file mode 100644 index 0000000..b234a38 --- /dev/null +++ b/localization-devel-ws/ekf/test/test_pep257.py @@ -0,0 +1,23 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_pep257.main import main +import pytest + + +@pytest.mark.linter +@pytest.mark.pep257 +def test_pep257(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found code style errors / warnings' diff --git a/localization-devel-ws/global/lidar_localization_pkg/LICENSE b/localization-devel-ws/global/lidar_localization_pkg/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/localization-devel-ws/global/lidar_localization_pkg/config/lidar_localization.yml b/localization-devel-ws/global/lidar_localization_pkg/config/lidar_localization.yml new file mode 100644 index 0000000..f0dafea --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/config/lidar_localization.yml @@ -0,0 +1,7 @@ +lidar_localization: + ros__parameters: + side: 0 + debug_mode: false + visualize_candidate: true + likelihood_threshold: 0.001 + consistency_threshold: 0.85 \ No newline at end of file diff --git a/localization-devel-ws/global/lidar_localization_pkg/launch/calibrator_launch.xml b/localization-devel-ws/global/lidar_localization_pkg/launch/calibrator_launch.xml new file mode 100644 index 0000000..e319ef5 --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/launch/calibrator_launch.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/localization-devel-ws/global/lidar_localization_pkg/launch/debug_visualize_launch.xml b/localization-devel-ws/global/lidar_localization_pkg/launch/debug_visualize_launch.xml new file mode 100644 index 0000000..977f540 --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/launch/debug_visualize_launch.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/localization-devel-ws/global/lidar_localization_pkg/launch/firmware/rplidar_s3_launch.py b/localization-devel-ws/global/lidar_localization_pkg/launch/firmware/rplidar_s3_launch.py new file mode 100644 index 0000000..0a25767 --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/launch/firmware/rplidar_s3_launch.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 + +import os + +from ament_index_python.packages import get_package_share_directory +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.actions import LogInfo +from launch.substitutions import LaunchConfiguration +from launch_ros.actions import Node + + +def generate_launch_description(): + channel_type = LaunchConfiguration('channel_type', default='serial') + serial_port = LaunchConfiguration('serial_port', default='/dev/ttyUSB0') + serial_baudrate = LaunchConfiguration('serial_baudrate', default='1000000') + frame_id = LaunchConfiguration('frame_id', default='laser') + inverted = LaunchConfiguration('inverted', default='false') + angle_compensate = LaunchConfiguration('angle_compensate', default='true') + scan_mode = LaunchConfiguration('scan_mode', default='Standard') + + return LaunchDescription([ + DeclareLaunchArgument( + 'channel_type', + default_value=channel_type, + description='Specifying channel type of lidar'), + + DeclareLaunchArgument( + 'serial_port', + default_value=serial_port, + description='Specifying usb port to connected lidar'), + + DeclareLaunchArgument( + 'serial_baudrate', + default_value=serial_baudrate, + description='Specifying usb port baudrate to connected lidar'), + + DeclareLaunchArgument( + 'frame_id', + default_value=frame_id, + description='Specifying frame_id of lidar'), + + DeclareLaunchArgument( + 'inverted', + default_value=inverted, + description='Specifying whether or not to invert scan data'), + + DeclareLaunchArgument( + 'angle_compensate', + default_value=angle_compensate, + description='Specifying whether or not to enable angle_compensate of scan data'), + + DeclareLaunchArgument( + 'scan_mode', + default_value=scan_mode, + description='Specifying scan mode of lidar'), + + Node( + package='rplidar_ros', + executable='rplidar_node', + name='rplidar_node', + parameters=[{'channel_type':channel_type, + 'serial_port': serial_port, + 'serial_baudrate': serial_baudrate, + 'frame_id': frame_id, + 'inverted': inverted, + 'angle_compensate': angle_compensate, + 'scan_mode': scan_mode}], + output='screen'), + ]) + diff --git a/localization-devel-ws/global/lidar_localization_pkg/launch/firmware/ydlidar.yaml b/localization-devel-ws/global/lidar_localization_pkg/launch/firmware/ydlidar.yaml new file mode 100644 index 0000000..deb99bc --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/launch/firmware/ydlidar.yaml @@ -0,0 +1,23 @@ +ydlidar_ros2_driver_node: + ros__parameters: + port: /dev/lidar + frame_id: laser + ignore_array: "" + baudrate: 512000 + lidar_type: 1 + device_type: 0 + sample_rate: 18 + abnormal_check_count: 4 + fixed_resolution: true + reversion: true + inverted: true + auto_reconnect: true + isSingleChannel: false + intensity: false + support_motor_dtr: false + angle_max: 180.0 + angle_min: -180.0 + range_max: 4.0 + range_min: 0.1 + frequency: 10.0 + invalid_range_is_inf: false diff --git a/localization-devel-ws/global/lidar_localization_pkg/launch/firmware/ydlidar_launch.py b/localization-devel-ws/global/lidar_localization_pkg/launch/firmware/ydlidar_launch.py new file mode 100644 index 0000000..9ee4ca3 --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/launch/firmware/ydlidar_launch.py @@ -0,0 +1,48 @@ +#!/usr/bin/python3 +# Copyright 2020, EAIBOT +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch_ros.actions import LifecycleNode +from launch_ros.actions import Node +from launch.actions import DeclareLaunchArgument +from launch.substitutions import LaunchConfiguration +from launch.actions import LogInfo + +import lifecycle_msgs.msg +import os + + +def generate_launch_description(): + share_dir = get_package_share_directory('ydlidar_ros2_driver') + parameter_file = LaunchConfiguration('params_file') + node_name = 'ydlidar_ros2_driver_node' + + params_declare = DeclareLaunchArgument('params_file', + default_value='/home/user/localization-ws/src/localization-devel-ws/global/lidar_localization_pkg/launch/firmware/ydlidar.yaml', + description='Path to the ROS2 parameters file to use.') + + driver_node = LifecycleNode(package='ydlidar_ros2_driver', + executable='ydlidar_ros2_driver_node', + name='ydlidar_ros2_driver_node', + output='screen', + emulate_tty=True, + parameters=[parameter_file], + namespace='/', + ) + return LaunchDescription([ + params_declare, + driver_node + ]) diff --git a/localization-devel-ws/global/lidar_localization_pkg/launch/obstacle_extractor_launch.xml b/localization-devel-ws/global/lidar_localization_pkg/launch/obstacle_extractor_launch.xml new file mode 100644 index 0000000..169240d --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/launch/obstacle_extractor_launch.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/localization-devel-ws/global/lidar_localization_pkg/launch/radius_compensation_launch.xml b/localization-devel-ws/global/lidar_localization_pkg/launch/radius_compensation_launch.xml new file mode 100644 index 0000000..25ef3b3 --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/launch/radius_compensation_launch.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/localization-devel-ws/global/lidar_localization_pkg/launch/view_obstacles_launch.xml b/localization-devel-ws/global/lidar_localization_pkg/launch/view_obstacles_launch.xml new file mode 100644 index 0000000..f40baec --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/launch/view_obstacles_launch.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/localization-devel-ws/global/lidar_localization_pkg/lidar_localization_pkg/__init__.py b/localization-devel-ws/global/lidar_localization_pkg/lidar_localization_pkg/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/localization-devel-ws/global/lidar_localization_pkg/lidar_localization_pkg/lidar_calibrator.py b/localization-devel-ws/global/lidar_localization_pkg/lidar_localization_pkg/lidar_calibrator.py new file mode 100644 index 0000000..fe77956 --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/lidar_localization_pkg/lidar_calibrator.py @@ -0,0 +1,118 @@ +import rclpy +from rclpy.node import Node +from obstacle_detector.msg import Obstacles +import numpy as np +from geometry_msgs.msg import Point +from visualization_msgs.msg import Marker +from std_msgs.msg import ColorRGBA +from geometry_msgs.msg import Twist +from std_msgs.msg import Int64 + +class LidarCalibrator(Node): + def __init__(self): + super().__init__('lidar_calibrator') + self.pose_sub = self.create_subscription( + Point, + 'pose', + self.pose_callback, + 10) + + self.obstacle_sub = self.create_subscription( + Obstacles, + 'raw_obstacles', + self.obstacle_callback, + 10) + + self.distance_pub = self.create_publisher( + Twist, + 'distance', + 10) + + self.ready_sub = self.create_subscription( + Int64, + 'record', + self.ready_callback, + 10 + ) + + self.marker_pub = self.create_publisher( + Marker, + 'nearest_obstacle', + 10) + + self.target_pose = None + self.dis = Twist() + self.pose = np.array([2.83, 0.03]) + self.create_timer(1, self.distance_publisher) + self.ready=0 + def pose_callback(self, msg): + self.target_pose = np.array([msg.x, msg.y]) + self.get_logger().info(f'Target pose set to x: {msg.x:.2f}, y: {msg.y:.2f}') + + def obstacle_callback(self, msg): + if self.target_pose is None: + self.get_logger().warn("No target pose received yet. Skipping obstacle processing.") + return + + if msg.circles: + distances = [ + np.linalg.norm(np.array([obs.center.x, obs.center.y]) - self.target_pose) + for obs in msg.circles + ] + min_index = np.argmin(distances) + nearest_obs = msg.circles[min_index] + distance = distances[min_index] + + self.publish_marker(nearest_obs.center.x, nearest_obs.center.y, nearest_obs.radius) + + ideal_vector = np.array([self.target_pose[0] - self.pose[0], self.target_pose[1] - self.pose[1]]) + nearest_obs_vector = np.array([nearest_obs.center.x - self.pose[0] , nearest_obs.center.y - self.pose[1]]) + theta = np.arctan2(ideal_vector[1], ideal_vector[0]) + self.dis.linear.x = ideal_vector[0] + self.dis.linear.y = ideal_vector[1] + self.dis.linear.z = nearest_obs_vector[0] + self.dis.angular.x = nearest_obs_vector[1] + self.dis.angular.y = ideal_vector[0]-nearest_obs_vector[0] + self.dis.angular.z = ideal_vector[1]-nearest_obs_vector[1] + self.get_logger().info(f"wanted obs:{self.target_pose}, nearest_obs:{nearest_obs.center.x, nearest_obs.center.y}") + self.get_logger().info(f"ideal_vector: {ideal_vector}, nearest_obs_vector: {nearest_obs_vector}") + self.get_logger().info(f"theta: {theta}") + + def ready_callback(self, msg): + if msg.data==1: + self.ready=1 + else: + self.ready=0 + + def publish_marker(self, x, y, radius): + marker = Marker() + marker.header.frame_id = "map" + marker.header.stamp = self.get_clock().now().to_msg() + marker.ns = "nearest_obstacle" + marker.id = 0 + marker.type = Marker.SPHERE + marker.action = Marker.ADD + marker.pose.position.x = x + marker.pose.position.y = y + marker.pose.position.z = 0.0 + marker.scale.x = radius * 2 # 確保顯示的圓球與實際障礙物大小一致 + marker.scale.y = radius * 2 + marker.scale.z = radius * 2 + marker.color = ColorRGBA(r=0.0, g=1.0, b=0.0, a=1.0) + marker.lifetime.sec = 1 + + self.marker_pub.publish(marker) + + def distance_publisher(self): + if self.ready: + self.distance_pub.publish(self.dis) + +def main(args=None): + rclpy.init(args=args) + node = LidarCalibrator() + rclpy.spin(node) + node.destroy_node() + rclpy.shutdown() + +if __name__ == '__main__': + main() diff --git a/localization-devel-ws/global/lidar_localization_pkg/lidar_localization_pkg/lidar_member_function.py b/localization-devel-ws/global/lidar_localization_pkg/lidar_localization_pkg/lidar_member_function.py new file mode 100644 index 0000000..75bdaee --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/lidar_localization_pkg/lidar_member_function.py @@ -0,0 +1,816 @@ +import rclpy +from rclpy.node import Node + +from tf2_ros import Buffer, TransformListener, LookupException, ConnectivityException, ExtrapolationException + +from geometry_msgs.msg import PoseWithCovarianceStamped, PoseArray, Pose, Point +from obstacle_detector.msg import Obstacles +from visualization_msgs.msg import Marker, MarkerArray +from std_msgs.msg import ColorRGBA, String +from nav_msgs.msg import Odometry + +import numpy as np + +class LidarLocalization(Node): # inherit from Node + + def __init__(self): + super().__init__('lidar_localization_node') + + # Declare parameters + self.declare_parameter('side', 0) + self.declare_parameter('debug_mode', False) + self.declare_parameter('use_two_beacons', False) + self.declare_parameter('visualize_candidate', True) + self.declare_parameter('likelihood_threshold', 0.001) + self.declare_parameter('consistency_threshold', 0.9) + self.declare_parameter('robot_frame_id', 'base_footprint') + self.declare_parameter('robot_parent_frame_id', 'map') + self.declare_parameter('lidar_multiplier', 0.987) + self.declare_parameter('likelihood_threshold_two', 0.95) + self.declare_parameter('consistency_threshold_two', 0.99) + + + # Get parameters + self.side = self.get_parameter('side').get_parameter_value().integer_value + self.debug_mode = self.get_parameter('debug_mode').get_parameter_value().bool_value + self.p_use_two_beacons = self.get_parameter('use_two_beacons').get_parameter_value().bool_value + self.visualize_true = self.get_parameter('visualize_candidate').get_parameter_value().bool_value + self.likelihood_threshold = self.get_parameter('likelihood_threshold').get_parameter_value().double_value + self.consistency_threshold = self.get_parameter('consistency_threshold').get_parameter_value().double_value + self.robot_frame_id = self.get_parameter('robot_frame_id').get_parameter_value().string_value + self.robot_parent_frame_id = self.get_parameter('robot_parent_frame_id').get_parameter_value().string_value + + self.lidar_multiplier = self.get_parameter('lidar_multiplier').get_parameter_value().double_value + self.likelihood_threshold_two = self.get_parameter('likelihood_threshold_two').get_parameter_value().double_value + self.consistency_threshold_two = self.get_parameter('consistency_threshold_two').get_parameter_value().double_value + + + # Set the landmarks map based on the side + if self.side == 0: + self.landmarks_map = [ + np.array([-0.094, 0.052]), + np.array([-0.094, 1.948]), + np.array([3.094, 1.0]) + ] + elif self.side == 1: + self.landmarks_map = [ + np.array([3.094, 0.052]), + np.array([3.094, 1.948]), + np.array([-0.094, 1.0]) + ] + + # set debug mode + self.beacon_no = 0 + + # ros settings + self.lidar_pose_pub = self.create_publisher(PoseWithCovarianceStamped, 'lidar_pose', 10) + self.beacons_pub = self.create_publisher(PoseArray, '/beacons_guaguagua', 10) + + if self.visualize_true: + self.marker_array = MarkerArray() + self.marker_num_pre = np.array([0, 0, 0]) + self.marker_id = 0 + self.circles_pub = self.create_publisher(MarkerArray, 'candidates', 10) + + self.subscription = self.create_subscription( + Obstacles, + 'raw_obstacles', + self.obstacle_callback, + 10) + self.subscription = self.create_subscription( # if TF is not available + PoseWithCovarianceStamped, + 'final_pose', + self.pred_pose_callback, + 10 + ) + self.subscription = self.create_subscription( + String, + 'set_lidar_side', + self.set_lidar_side_callback, + 10 + ) + self.subscription = self.create_subscription( + Odometry, + 'local_filter', + self.local_callback, + 1 + ) + self.subscription = self.create_subscription( + Point, + 'lidar_param_update', + self.param_update_callback, + 10 + ) + self.subscription # prevent unused variable warning + + # tf2 buffer + self.tf_buffer = Buffer() + self.tf_listener = TransformListener(self.tf_buffer, self) + + # ros debug logger + self.get_logger().debug('Lidar Localization Node has been initialized') + + self.init_landmarks_map(self.landmarks_map) + self.robot_pose = [] + self.landmarks_set = [] + self.P_pred = np.array([[0.05**2, 0.0, 0.0], [0.0, 0.05**2, 0.0], [0.0, 0.0, 0.1]]) # TODO: tune the value, fixed for now + self.newPose = False + self.R = np.array([[0.001, 0.0], [0.0, 0.001]]) # measurement noise; TODO: tune the value + self.lidar_pose_msg = PoseWithCovarianceStamped() + self.predict_transform = None + + self.P_pred_linear = 0.8 # starting mode [0.8, 1.0, 0.8] -> [70cm, 35 deg, 80%] + self.P_pred_angular = 1.0 + self.use_two_beacons = self.p_use_two_beacons + + def obstacle_callback(self, msg): # main + self.get_logger().debug('obstacle detected') + self.obs_raw = [] + for obs in msg.circles: + self.obs_raw.append(np.array([obs.center.x, obs.center.y])) + self.obs_time = msg.header.stamp + # check if TF is available. If true, use the TF. If false, use the latest topic + try: + self.predict_transform = self.tf_buffer.lookup_transform( + self.robot_parent_frame_id, + self.robot_frame_id, + self.obs_time + ) + self.robot_pose = np.array([ + self.predict_transform.transform.translation.x, + self.predict_transform.transform.translation.y, + euler_from_quaternion( + self.predict_transform.transform.rotation.x, + self.predict_transform.transform.rotation.y, + self.predict_transform.transform.rotation.z, + self.predict_transform.transform.rotation.w + ) + ]) + except (LookupException, ConnectivityException, ExtrapolationException) as e: + self.get_logger().error(f'Could not transform {self.robot_parent_frame_id} to {self.robot_frame_id}: {e}') + self.get_logger().debug("now try to use the latest topic") + if self.newPose == False: + self.get_logger().error("no new predict topic, skip.") + return + + # main: data processing + self.landmarks_candidate = self.get_landmarks_candidate(self.landmarks_map, self.obs_raw) + + # if each of the three beacon has at least one candidate + landmarks_with_candidates = 0 + for i in range(3): + if len(self.landmarks_candidate[i]['obs_candidates']) > 0: + landmarks_with_candidates += 1 + if landmarks_with_candidates == 3: + self.landmarks_set = self.get_landmarks_set(self.landmarks_candidate) + elif landmarks_with_candidates == 2: + # self.get_logger().info("candidates only two beacons") + self.get_two_beacons() + return + else: + # self.get_logger().warn("less than two landmarks") + return + + # for the normal case, complete three landmarks + if len(self.landmarks_set) == 0: # when fail when checking consistency and likelihood + self.get_logger().debug("empty landmarks set") + self.get_two_beacons() + return + + self.get_lidar_pose(self.landmarks_set, self.landmarks_map) + + # clear used data + self.clear_data() + + def param_update_callback(self, msg): + self.P_pred_linear = msg.x + self.P_pred_angular = msg.y + self.likelihood_threshold = msg.z + + def pred_pose_callback(self, msg): + self.newPose = True + orientation = euler_from_quaternion(msg.pose.pose.orientation.x, msg.pose.pose.orientation.y, msg.pose.pose.orientation.z, msg.pose.pose.orientation.w) # raw, pitch, *yaw + # check orientation range + if orientation < 0: + orientation += 2 * np.pi + self.robot_pose = np.array([msg.pose.pose.position.x, msg.pose.pose.position.y, orientation]) + self.P_pred = np.array([ + [self.P_pred_linear, 0, 0], + [0, self.P_pred_linear, 0], + [0, 0, self.P_pred_angular] + ]) + + def local_callback(self, msg): + # get robot speed + self.robot_speed = np.array([msg.twist.twist.linear.x, msg.twist.twist.linear.y, msg.twist.twist.angular.z]) + if not self.p_use_two_beacons: return + # if robot spin larger than 0.3, do not use two beacons + if abs(self.robot_speed[2]) > 0.3: + self.use_two_beacons = False + else: + self.use_two_beacons = True + + + def set_lidar_side_callback(self, msg): + side = msg.data.lower() + if side in ['0', '1', 'yellow', 'blue']: + if side == '0' or side == 'yellow': + self.side = 0 + self.landmarks_map = [ + np.array([-0.094, 0.052]), + np.array([-0.094, 1.948]), + np.array([3.094, 1.0]) + ] + elif side == '1' or side == 'blue': + self.side = 1 + self.landmarks_map = [ + np.array([3.094, 0.052]), + np.array([3.094, 1.948]), + np.array([-0.094, 1.0]) + ] + self.init_landmarks_map(self.landmarks_map) + self.get_logger().debug(f"Set lidar side to {self.side}") + else: + self.get_logger().warn("Invalid side value") + + def init_landmarks_map(self, landmarks_map): + self.landmarks_map = landmarks_map + # calculate the geometry description of landmarks + self.geometry_description_map = {} + NUM_LANDMARKS = len(landmarks_map) + for i in range(NUM_LANDMARKS): + for j in range(i + 1, NUM_LANDMARKS): + if i == j: + continue + d_ij = np.linalg.norm(landmarks_map[i] - landmarks_map[j]) + self.geometry_description_map[(i, j)] = d_ij + + def clear_data(self): + self.obs_raw = [] + self.robot_pose = np.array([]) + self.landmarks_candidate = [] + self.landmarks_set = [] + self.newPose = False + self.predict_transform = None + + def get_obs_candidate(self, landmark, obs_raw): + obs_candidates = [] + x_r, y_r, phi_r = self.robot_pose + x_o, y_o = landmark + r_prime = np.sqrt((x_o - x_r) ** 2 + (y_o - y_r) ** 2) + # theta_rob = np.arctan2( + temp = np.arctan2(y_o - y_r, x_o - x_r) + theta_prime = angle_limit_checking(temp - phi_r) + + H = np.array([ + [-(x_o - x_r) / r_prime, -(y_o - y_r) / r_prime, 0], + [(y_o - y_r) / r_prime ** 2, -(x_o - x_r) / r_prime ** 2, -1] + ]) + S = H @ self.P_pred @ H.T + self.R + S_inv = np.linalg.inv(S) + + for obs in obs_raw: + r_z = np.sqrt(obs[0] ** 2 + obs[1] ** 2) + theta_z = np.arctan2(obs[1], obs[0]) + y = np.array([r_z - r_prime, angle_limit_checking(theta_z - theta_prime)]) + di_square = y.T @ S_inv @ y + likelihood = np.exp(-0.5 * di_square) + if likelihood > self.likelihood_threshold: + obs[0] = 0.987*obs[0] + obs[1] = 0.987*obs[1] + + if likelihood > self.likelihood_threshold: + obs_candidates.append({'position': obs, 'probability': likelihood}) + else: + self.get_logger.info("calibrated obs likelihood very bad") + if self.visualize_true: + self.visualize_candidates(obs, likelihood) + + return obs_candidates + + def get_landmarks_candidate(self, landmarks_map, obs_raw): + landmarks_candidate = [] + self.beacon_no = 0 + for landmark in landmarks_map: + self.beacon_no += 1 + self.marker_id = 0 + candidate = { + 'landmark': landmark, + 'obs_candidates': self.get_obs_candidate(landmark, obs_raw) + } + landmarks_candidate.append(candidate) + + if self.visualize_true: + self.remove_old_markers() + self.circles_pub.publish(self.marker_array) + # self.get_logger().debug("Published marker array") + self.marker_array.markers.clear() # clean up (is this enough?) + + return landmarks_candidate + + def get_landmarks_set(self, landmarks_candidate): + landmarks_set = [] + for i in range(len(landmarks_candidate[0]['obs_candidates'])): + for j in range(len(landmarks_candidate[1]['obs_candidates'])): + for k in range(len(landmarks_candidate[2]['obs_candidates'])): + set = { + 'beacons': { + 0: landmarks_candidate[0]['obs_candidates'][i]['position'], + 1: landmarks_candidate[1]['obs_candidates'][j]['position'], + 2: landmarks_candidate[2]['obs_candidates'][k]['position'] + } + } + # consistency of the set + set['consistency'] = self.get_geometry_consistency(set['beacons']) + if set['consistency'] < self.consistency_threshold: + self.get_logger().debug(f"Geometry consistency is less than {self.consistency_threshold}: {set['consistency']}") + continue + # probability of the set + set['probability_set'] = landmarks_candidate[0]['obs_candidates'][i]['probability'] * landmarks_candidate[1]['obs_candidates'][j]['probability'] * landmarks_candidate[2]['obs_candidates'][k]['probability'] + landmarks_set.append(set) + + + return landmarks_set + + def get_lidar_pose(self, landmarks_set, landmarks_map): + if not landmarks_set: + raise ValueError("landmarks_set is empty") + # prefer the set with more beacons + landmarks_set = sorted(landmarks_set, key=lambda x: len(x['beacons']), reverse=True) + # with the most beacon possible, prefer the set with the highest probability_set; TODO: better way to sort? + max_likelihood = max(set['probability_set'] for set in landmarks_set) + max_likelihood_idx = next(i for i, set in enumerate(landmarks_set) if set['probability_set'] == max_likelihood) + + lidar_pose = np.zeros(3) + lidar_cov = np.diag([0.05**2, 0.05**2, 0.05**2]) # what should the optimal value be? + + # If the most likely set has at least 3 beacons + if len(landmarks_set[max_likelihood_idx]['beacons']) >= 3: + beacons = [landmarks_set[max_likelihood_idx]['beacons'][i] for i in range(3)] + A = np.zeros((2, 2)) + b = np.zeros(2) + dist_beacon_robot = [np.linalg.norm(beacon) for beacon in beacons] + + A[0, 0] = 2 * (landmarks_map[0][0] - landmarks_map[2][0]) + A[0, 1] = 2 * (landmarks_map[0][1] - landmarks_map[2][1]) + A[1, 0] = 2 * (landmarks_map[1][0] - landmarks_map[2][0]) + A[1, 1] = 2 * (landmarks_map[1][1] - landmarks_map[2][1]) + + b[0] = (landmarks_map[0][0]**2 - landmarks_map[2][0]**2) + (landmarks_map[0][1]**2 - landmarks_map[2][1]**2) + (dist_beacon_robot[2]**2 - dist_beacon_robot[0]**2) + b[1] = (landmarks_map[1][0]**2 - landmarks_map[2][0]**2) + (landmarks_map[1][1]**2 - landmarks_map[2][1]**2) + (dist_beacon_robot[2]**2 - dist_beacon_robot[1]**2) + + try: + X = np.linalg.solve(A.T @ A, A.T @ b) + if X[0] < 0 or X[0] > 3 or X[1] < 0 or X[1] > 2: + return + lidar_pose[0] = X[0] + lidar_pose[1] = X[1] + + robot_sin = 0 + robot_cos = 0 + + for i in range(3): + theta = angle_limit_checking(np.arctan2(landmarks_map[i][1] - lidar_pose[1], landmarks_map[i][0] - lidar_pose[0]) - np.arctan2(beacons[i][1], beacons[i][0])) + robot_sin += np.sin(theta) + robot_cos += np.cos(theta) + + lidar_pose[2] = angle_limit_checking(np.arctan2(robot_sin, robot_cos)) + + lidar_pose = self.pose_compensation(lidar_pose) + + lidar_cov[0, 0] /= (max_likelihood/1.1) + lidar_cov[1, 1] /= (max_likelihood/1.1) + lidar_cov[2, 2] /= (max_likelihood/1.1) + + # publish the lidar pose + self.pub_lidar_pose(lidar_pose, lidar_cov) + self.publish_beacons(beacons) + # debug print the calculation time (from obstacle stamp to now) + if self.debug_mode: + obs_time_rclpy = rclpy.time.Time.from_msg(self.obs_time) + calc_time_ms = (self.get_clock().now() - obs_time_rclpy).nanoseconds * 1e-6 + self.get_logger().info(f"calculation time: {calc_time_ms:.2f} ms") + + except np.linalg.LinAlgError as e: + self.get_logger().warn("Linear algebra error: {}".format(e)) + + # use markerarray to show the landmarks it used + if self.visualize_true: + self.visualize_sets(beacons, max_likelihood, landmarks_set[max_likelihood_idx]['consistency']) + + else: + self.get_logger().debug("not enough beacons") + + return lidar_pose, lidar_cov + + def get_two_beacons(self): + + if self.use_two_beacons == False: + self.get_logger().debug("use_two_beacons is set to false") + return + + indices = [] + self.get_logger().debug("get two beacons") + + # get the index of the two beacons with candidates and obstacle probability larger than 0.8 + for i in range(3): + if any(candidate['probability'] > self.likelihood_threshold_two for candidate in self.landmarks_candidate[i]['obs_candidates']): + indices.append(i) + + self.get_logger().debug(f"indices: {indices}") + + if len(indices) == 2: + two_index = [indices[0], indices[1]] + self.get_set_two(two_index) + elif len(indices) == 3: + for i in range(3): # 01, 12, 20 + two_index = [indices[i], indices[(i + 1) % 3]] + self.get_set_two(two_index) + + # check if there is valid set + if len(self.landmarks_set) == 0: + self.get_logger().warn("no valid set") + return + + # use the set with the highest probability + max_likelihood = max(set['probability_set'] for set in self.landmarks_set) + max_likelihood_idx = next(i for i, set in enumerate(self.landmarks_set) if set['probability_set'] == max_likelihood) + beacons = [self.landmarks_set[max_likelihood_idx]['beacons'][0], self.landmarks_set[max_likelihood_idx]['beacons'][1]] + # calculate the lidar pose + lidar_pose = self.get_lidar_pose_two( + indices[0], + indices[1], + beacons[0][0], beacons[0][1], + beacons[1][0], beacons[1][1] + ) + # check if the lidar pose is in the map + if lidar_pose[0] < 0 or lidar_pose[0] > 3 or lidar_pose[1] < 0 or lidar_pose[1] > 2: + self.get_logger().debug("lidar pose is out of map") + return + lidar_cov = np.diag([0.05**2, 0.05**2, 0.05**2]) + lidar_cov[0, 0] /= max_likelihood/10 + lidar_cov[1, 1] /= max_likelihood/10 + lidar_cov[2, 2] /= max_likelihood/10 + # publish the lidar pose + self.pub_lidar_pose(lidar_pose, lidar_cov) + if self.visualize_true: + self.visualize_sets(beacons, max_likelihood, self.landmarks_set[max_likelihood_idx]['consistency']) + + # also print the likelihood + self.get_logger().debug(f"lidar_pose (two): {lidar_pose}") + self.get_logger().debug(f"lidar_pose (two) likelihood: {max_likelihood}") + + self.clear_data() + + def get_set_two(self, two_index): + + # nominal geometry + nominal_distance = np.linalg.norm(self.landmarks_map[two_index[0]] - self.landmarks_map[two_index[1]]) + self.get_logger().debug(f"nominal distance: {nominal_distance:.2f}") + angle1 = np.arctan2(self.landmarks_map[two_index[1]][1] - self.robot_pose[1], self.landmarks_map[two_index[1]][0] - self.robot_pose[0]) + angle0 = np.arctan2(self.landmarks_map[two_index[0]][1] - self.robot_pose[1], self.landmarks_map[two_index[0]][0] - self.robot_pose[0]) + nominal_angle = angle_limit_checking(angle1 - angle0) + # print nominal angle, display in degree + self.get_logger().debug(f"nominal angle: {nominal_angle * 180 / np.pi:.2f} degree") + + # get the sets + for i in range(len(self.landmarks_candidate[two_index[0]]['obs_candidates'])): + for j in range(len(self.landmarks_candidate[two_index[1]]['obs_candidates'])): + set = { + 'beacons': { + 0: self.landmarks_candidate[two_index[0]]['obs_candidates'][i]['position'], + 1: self.landmarks_candidate[two_index[1]]['obs_candidates'][j]['position'] + } + } + # geometry consistency + # 1. check the cross product: 'robot to beacon 0' cross 'robot to beacon 1' + cross = np.cross(set['beacons'][0], set['beacons'][1]) # TODO: check if beacon 0 and 1 will be in the order of beacon a, b and c + if self.side == 0: # yellow is clockwise(negative) + if cross > 0: + # self.get_logger().debug("cross product is positive") + continue + elif self.side == 1: # blue is counter-clockwise(positive) + if cross < 0: + # self.get_logger().debug("cross product is negative") + continue + # 2. check the distance between the two beacons + set['consistency'] = 1 - abs(np.linalg.norm(set['beacons'][0] - set['beacons'][1]) - nominal_distance) / nominal_distance + if set['consistency'] < self.consistency_threshold_two: + self.get_logger().debug(f"Geometry consistency is less than {self.consistency_threshold_two}: {set['consistency']}") + continue + # 3. check the angle between the two beacons + angle1 = np.arctan2(set['beacons'][1][1], set['beacons'][1][0]) + angle0 = np.arctan2(set['beacons'][0][1], set['beacons'][0][0]) + angle = angle_limit_checking(angle1 - angle0) + if abs(angle_limit_checking(angle - nominal_angle)) > np.pi / 18: # 10 degree + self.get_logger().debug(f"Angle difference is too large: {angle * 180 / np.pi:.2f} degree") + continue + # self.get_logger().info(f"angle1: {angle1 * 180 / np.pi:.2f} degree") + # self.get_logger().info(f"angle0: {angle0 * 180 / np.pi:.2f} degree") + # self.get_logger().info(f"cadidate angle: {angle * 180 / np.pi:.2f} degree") + # probability of the set + set['probability_set'] = self.landmarks_candidate[two_index[0]]['obs_candidates'][i]['probability'] * self.landmarks_candidate[two_index[1]]['obs_candidates'][j]['probability'] + if set['probability_set'] < self.likelihood_threshold_two: + self.get_logger().debug(f"Probability is less than {self.likelihood_threshold_two}: {set['probability_set']}") + continue + self.landmarks_set.append(set) + + def get_lidar_pose_two(self, landmark_index_1, landmark_index_2, bxr, byr, cxr, cyr): + + lidar_pose = np.zeros(3) + # the beacons position w.r.t. map + bxm = self.landmarks_map[landmark_index_1][0] + bym = self.landmarks_map[landmark_index_1][1] + cxm = self.landmarks_map[landmark_index_2][0] + cym = self.landmarks_map[landmark_index_2][1] + x = cxm - bxm + y = cym - bym + xr = cxr - bxr + yr = cyr - byr + # calculate the angle from robot to map + costheta = (xr*x + yr*y) / (xr**2 + yr**2) + sintheta = (xr*y - yr*x) / (xr**2 + yr**2) + lidar_pose[2] = np.arctan2(sintheta, costheta) + # find the linear translation from robot to map + # 1. transform beacon positions from w.r.t. robot to w.r.t. map + bx = costheta * bxr - sintheta * byr + by = sintheta * bxr + costheta * byr + cx = costheta * cxr - sintheta * cyr + cy = sintheta * cxr + costheta * cyr + # 2. linear translation from robot to map, take the average of the result from the two beacons + lidar_pose[0] = (bxm - bx)/2 + (cxm - cx)/2 + lidar_pose[1] = (bym - by)/2 + (cym - cy)/2 + + return lidar_pose + + def visualize_sets(self, beacons, max_likelihood, consistency): + marker_array = MarkerArray() + for i, beacon in enumerate(beacons): + marker = Marker() + marker.header.frame_id = "base_footprint" + marker.header.stamp = self.get_clock().now().to_msg() + marker.ns = "chosen_landmarks" + marker.type = Marker.SPHERE + marker.action = Marker.ADD + marker.scale.x = 0.15 + marker.scale.y = 0.15 + marker.scale.z = 0.01 + marker.pose.position.x = beacon[0] + marker.pose.position.y = beacon[1] + marker.pose.position.z = -0.1 + if len(beacons) == 3: + marker.color = ColorRGBA(r=0.0, g=0.5, b=0.5, a=1.0) + elif len(beacons) == 2: + marker.color = ColorRGBA(r=0.5, g=0.5, b=0.0, a=1.0) + marker.id = i + marker_array.markers.append(marker) + + if len(beacons) == 2: + # delete marker id 3 + marker_delete = Marker() + marker_delete.header.frame_id = "base_footprint" + marker_delete.ns = "chosen_landmarks" + marker_delete.id = 3 + marker_delete.type = Marker.SPHERE + marker_delete.action = Marker.DELETE + marker_array.markers.append(marker_delete) + + # add the max_likelihood, consistency to the marker array + marker = Marker() + marker.header.frame_id = "map" + marker.header.stamp = self.get_clock().now().to_msg() + marker.ns = "set_param" + marker.type = Marker.TEXT_VIEW_FACING + marker.action = Marker.ADD + marker.scale.z = 0.1 + marker.pose.position.x = 3.5 + marker.pose.position.y = 2.5 + marker.pose.position.z = 0.5 + marker.color = ColorRGBA(r=1.0, g=1.0, b=1.0, a=1.0) + marker.text = f"max_likelihood: {max_likelihood:.2f}, consistency: {consistency:.2f}" + marker.id = 10 + marker_array.markers.append(marker) + # publish the marker array + self.circles_pub.publish(marker_array) + self.get_logger().debug("Published marker array") + # clean up + marker_array.markers.clear() + + def visualize_candidates(self, obs, likelihood): + + self.marker_id += 1 + + # circles + marker = Marker() + marker.header.frame_id = "base_footprint" + marker.header.stamp = self.get_clock().now().to_msg() + marker.ns = f"candidates_circle{self.beacon_no}" + marker.type = Marker.SPHERE + marker.action = Marker.ADD + marker.scale.x = 0.1 + marker.scale.y = 0.1 + marker.scale.z = 0.01 + + marker.pose.position.x = obs[0] + marker.pose.position.y = obs[1] + marker.pose.position.z = 0.0 + if self.beacon_no == 1: + marker.color = ColorRGBA(r=1.0, g=0.0, b=0.0, a=likelihood) # Red for beacon 1 + elif self.beacon_no == 2: + marker.color = ColorRGBA(r=0.0, g=1.0, b=0.0, a=likelihood) # Green for beacon 2 + elif self.beacon_no == 3: + marker.color = ColorRGBA(r=0.0, g=0.0, b=1.0, a=likelihood) # Blue for beacon 3 + marker.id = self.marker_id + self.marker_array.markers.append(marker) + + # texts + text_marker = Marker() + text_marker.header.frame_id = "base_footprint" + text_marker.header.stamp = self.get_clock().now().to_msg() + text_marker.ns = f"candidates_text{self.beacon_no}" + text_marker.type = Marker.TEXT_VIEW_FACING + text_marker.action = Marker.ADD + text_marker.scale.z = 0.1 + text_marker.color = ColorRGBA(r=0.0, g=0.0, b=0.0, a=0.2) # White text + + text_marker.pose.position.x = obs[0] + 0.2 + text_marker.pose.position.y = obs[1] + text_marker.pose.position.z = 0.1 + text_marker.text = f"{likelihood:.2f}" + text_marker.id = self.marker_id + self.marker_array.markers.append(text_marker) + + def remove_old_markers(self): + # remove the old markers + num_old_markers = self.marker_num_pre[self.beacon_no - 1] - self.marker_id + for i in range(num_old_markers): + old_marker = Marker() + old_marker.header.frame_id = "base_footprint" + old_marker.header.stamp = self.get_clock().now().to_msg() + old_marker.ns = f"candidates_circle{self.beacon_no}" + old_marker.type = Marker.SPHERE + old_marker.action = Marker.DELETE + old_marker.id = i + 1 + self.marker_array.markers.append(old_marker) + + old_text_marker = Marker() + old_text_marker.header.frame_id = "base_footprint" + old_text_marker.header.stamp = self.get_clock().now().to_msg() + old_text_marker.ns = f"candidates_text{self.beacon_no}" + old_text_marker.type = Marker.TEXT_VIEW_FACING + old_text_marker.action = Marker.DELETE + old_text_marker.id = i + 1 + self.marker_array.markers.append(old_text_marker) + # update the marker number + self.marker_num_pre[self.beacon_no - 1] = self.marker_id + + def get_geometry_consistency(self, beacons): + geometry_description = {} + consistency = 1.0 + lenB = len(beacons) + + # lenB can be 2, 3 or 4 + # use the index of the beacons to calculate the distance between them + for i in beacons: + for j in beacons: + if i == j: + continue + geometry_description[(i, j)] = np.linalg.norm(beacons[i] - beacons[j]) + # self.get_logger().debug(f"Beacon {i} to Beacon {j} distance: {geometry_description[(i, j)]}") + if (i, j) in self.geometry_description_map: + expected_distance = self.geometry_description_map[(i, j)] + consistency *= 1 - np.abs(geometry_description[(i, j)] - expected_distance) / expected_distance + # if the index is not found in map, it is probably on the lower triangle of the matrix + + # check the landmark sequence is correct, clockwise for yellow, counter-clockwise for blue + if self.side == 0: + if np.cross(beacons[1] - beacons[0], beacons[2] - beacons[0]) > 0: + consistency = 0 + elif self.side == 1: + if np.cross(beacons[1] - beacons[0], beacons[2] - beacons[0]) < 0: + consistency = 0 + + return consistency + + def pose_compensation(self, lidar_pose): + # Use robot speed to compensate for the pose + try: + v_x, v_y, w = self.robot_speed + dt = (self.get_clock().now() - rclpy.time.Time.from_msg(self.obs_time)).nanoseconds * 1e-9 + theta = lidar_pose[2] + c_theta = np.cos(theta) + s_theta = np.sin(theta) + c_delta = np.cos(w * dt) + s_delta = np.sin(w * dt) + + if abs(w) > 1e-3: + lidar_pose[0] += (c_theta * s_delta - s_theta * (c_delta - 1)) * v_x / w - (s_theta * s_delta - c_theta * (c_delta - 1)) * v_y / w + lidar_pose[1] += (s_theta * s_delta - c_theta * (c_delta - 1)) * v_x / w + (c_theta * s_delta - s_theta * (c_delta - 1)) * v_y / w + else: + lidar_pose[0] += v_x * dt * np.cos(theta + w * dt) - v_y * dt * np.sin(theta + w * dt) + lidar_pose[1] += v_x * dt * np.sin(theta + w * dt) + v_y * dt * np.cos(theta + w * dt) + + lidar_pose[2] += w * dt + return lidar_pose + except Exception as e: + self.get_logger().error(f"Error during pose compensation: {e}") + return lidar_pose + + def publish_beacons(self, beacons): + pose_array = PoseArray() + pose_array.header.stamp = self.get_clock().now().to_msg() + pose_array.header.frame_id = "base_footprint" # Adjust the frame_id as needed + + for beacon in beacons: + pose = Pose() + pose.position.x = beacon[0] + pose.position.y = beacon[1] + pose.position.z = 0.0 + pose.orientation.x = 0.0 + pose.orientation.y = 0.0 + pose.orientation.z = 0.0 + pose.orientation.w = 1.0 + pose_array.poses.append(pose) + + self.beacons_pub.publish(pose_array) + self.get_logger().debug("Published beacons to /beacons_guaguagua") + + def pub_lidar_pose(self, lidar_pose, lidar_cov): + # publish the lidar pose + self.lidar_pose_msg.header.stamp = self.get_clock().now().to_msg() + self.lidar_pose_msg.header.frame_id = 'map' #TODO: param + self.lidar_pose_msg.pose.pose.position.x = lidar_pose[0] + self.lidar_pose_msg.pose.pose.position.y = lidar_pose[1] + self.lidar_pose_msg.pose.pose.position.z = 0.0 + self.lidar_pose_msg.pose.pose.orientation.x = 0.0 + self.lidar_pose_msg.pose.pose.orientation.y = 0.0 + self.lidar_pose_msg.pose.pose.orientation.z = np.sin(lidar_pose[2] / 2) + self.lidar_pose_msg.pose.pose.orientation.w = np.cos(lidar_pose[2] / 2) + self.lidar_pose_msg.pose.covariance = [ + lidar_cov[0, 0], 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, lidar_cov[1, 1], 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, lidar_cov[2, 2] + ] + self.lidar_pose_pub.publish(self.lidar_pose_msg) + + +def quaternion_from_euler(ai, aj, ak): + ai /= 2.0 + aj /= 2.0 + ak /= 2.0 + ci = np.cos(ai) + si = np.sin(ai) + cj = np.cos(aj) + sj = np.sin(aj) + ck = np.cos(ak) + sk = np.sin(ak) + cc = ci * ck + cs = ci * sk + sc = si * ck + ss = si * sk + q = np.empty((4,)) + q[0] = cj * sc - sj * cs + q[1] = cj * ss + sj * cc + q[2] = cj * cs - sj * sc + q[3] = cj * cc + sj * ss + return q + +def euler_from_quaternion(x, y, z, w): + t0 = +2.0 * (w * x + y * z) + t1 = +1.0 - 2.0 * (x * x + y * y) + roll_x = np.arctan2(t0, t1) + t2 = +2.0 * (w * y - z * x) + t2 = +1.0 if t2 > +1.0 else t2 + t2 = -1.0 if t2 < -1.0 else t2 + pitch_y = np.arcsin(t2) + t3 = +2.0 * (w * z + x * y) + t4 = +1.0 - 2.0 * (y * y + z * z) + yaw_z = np.arctan2(t3, t4) + return yaw_z # in radians + +def angle_limit_checking(theta): + while theta > np.pi: + theta -= 2 * np.pi + while theta <= -np.pi: + theta += 2 * np.pi + return theta + +def main(args=None): + rclpy.init(args=args) + + lidar_localization = LidarLocalization() + + rclpy.spin(lidar_localization) + + # Destroy the node explicitly + # (optional - otherwise it will be done automatically + # when the garbage collector destroys the node object) + lidar_localization.destroy_node() + rclpy.shutdown() + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/localization-devel-ws/global/lidar_localization_pkg/lidar_localization_pkg/pred_publisher.py b/localization-devel-ws/global/lidar_localization_pkg/lidar_localization_pkg/pred_publisher.py new file mode 100644 index 0000000..b4b2625 --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/lidar_localization_pkg/pred_publisher.py @@ -0,0 +1,75 @@ +import rclpy +from rclpy.node import Node +from std_msgs.msg import Float64 +from geometry_msgs.msg import PoseWithCovarianceStamped +from tf2_ros import TransformBroadcaster +from geometry_msgs.msg import TransformStamped + +class PredPublisher(Node): + def __init__(self): + super().__init__('pred_publisher') + self.pose_pred = PoseWithCovarianceStamped() + self.pose_pred.header.frame_id = "map" + self.pose_pred.pose.pose.position.x = 0.0 + self.pose_pred.pose.pose.position.y = 0.0 + self.pose_pred.pose.pose.position.z = 0.0 + self.pose_pred.pose.pose.orientation.x = 0.0 + self.pose_pred.pose.pose.orientation.y = 0.0 + self.pose_pred.pose.pose.orientation.z = 0.0 + self.pose_pred.pose.pose.orientation.w = 1.0 + self.pose_pred.pose.covariance = [0.0] * 36 + self.pose_pred.pose.covariance[0] = 0.0025 + self.pose_pred.pose.covariance[7] = 0.0025 + self.pose_pred.pose.covariance[35] = 0.25 + + self.pub = self.create_publisher(PoseWithCovarianceStamped, '/final_pose', 10) + self.sub = self.create_subscription(PoseWithCovarianceStamped, '/lidar_pose', self.param_callback, 10) + + self.br = TransformBroadcaster(self) + self.t = TransformStamped() + self.t.header.stamp = self.get_clock().now().to_msg() + self.t.header.frame_id = "map" + self.t.child_frame_id = "base_footprint" + self.t.transform.translation.x = 0.0 + self.t.transform.translation.y = 1.0 + self.t.transform.translation.z = 0.0 + self.t.transform.rotation.x = 0.0 + self.t.transform.rotation.y = 0.0 + self.t.transform.rotation.z = 0.0 + self.t.transform.rotation.w = 1.0 + self.timer = self.create_timer(0.01, self.publish_pose) # 100Hz + + + def param_callback(self, msg): + # self.get_logger().info('Received parameter: %s' % msg) + # Modify the pose_pred data based on the received parameter + self.pose_pred.pose.pose.position.x = msg.pose.pose.position.x + self.pose_pred.pose.pose.position.y = msg.pose.pose.position.y + self.pose_pred.pose.pose.position.z = msg.pose.pose.position.z + self.pose_pred.pose.pose.orientation.x = msg.pose.pose.orientation.x + self.pose_pred.pose.pose.orientation.y = msg.pose.pose.orientation.y + self.pose_pred.pose.pose.orientation.z = msg.pose.pose.orientation.z + self.pose_pred.pose.pose.orientation.w = msg.pose.pose.orientation.w + self.pose_pred.pose.covariance = msg.pose.covariance + self.publish_pose() + # Broadcast static transform from map to robot base + + def publish_pose(self): + self.pose_pred.header.stamp = self.get_clock().now().to_msg() + self.pub.publish(self.pose_pred) + self.t.header.stamp = self.get_clock().now().to_msg() + self.t.transform.translation.x = self.pose_pred.pose.pose.position.x + self.t.transform.translation.y = self.pose_pred.pose.pose.position.y + self.t.transform.translation.z = self.pose_pred.pose.pose.position.z + self.t.transform.rotation = self.pose_pred.pose.pose.orientation + self.br.sendTransform(self.t) + # self.get_logger().info('Publishing predicted pose') +def main(args=None): + rclpy.init(args=args) + pred_publisher = PredPublisher() + rclpy.spin(pred_publisher) + pred_publisher.destroy_node() + rclpy.shutdown() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/localization-devel-ws/global/lidar_localization_pkg/lidar_localization_pkg/probability_circle_publisher.py b/localization-devel-ws/global/lidar_localization_pkg/lidar_localization_pkg/probability_circle_publisher.py new file mode 100644 index 0000000..eedb438 --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/lidar_localization_pkg/probability_circle_publisher.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 + +import rclpy +from rclpy.node import Node +from visualization_msgs.msg import Marker, MarkerArray +from std_msgs.msg import ColorRGBA +from geometry_msgs.msg import Point +import random + +class ProbabilityCirclePublisher(Node): + def __init__(self): + super().__init__('probability_circle_publisher') + self.publisher = self.create_publisher(MarkerArray, 'circles', 10) + self.timer = self.create_timer(1.0, self.publish_circles) + self.get_logger().info("Probability Circle Publisher Node has been started") + + def publish_circles(self): + marker_array = MarkerArray() + + # Example: Generate random probabilities for a set of circles + num_circles = 10 + for i in range(num_circles): + probability = random.uniform(0.0, 1.0) # Random probability between 0 and 1 + + # Create a circle marker + marker = Marker() + marker.header.frame_id = "map" + marker.header.stamp = self.get_clock().now().to_msg() + marker.ns = "circles" + marker.id = i + marker.type = Marker.SPHERE + marker.action = Marker.ADD + + # Set the circle's position (random in this example) + marker.pose.position.x = random.uniform(-5.0, 5.0) + marker.pose.position.y = random.uniform(-5.0, 5.0) + marker.pose.position.z = 0.0 + marker.pose.orientation.w = 1.0 # Neutral orientation + + # Set the scale (size of the circle) + marker.scale.x = 1.0 # Diameter of the circle + marker.scale.y = 1.0 + marker.scale.z = 0.1 # Flat circle in Z-axis + + # Set the color based on the probability + color_intensity = float(probability) # Ensure probability is a float + marker.color = ColorRGBA(r=float(0), + g=float(128/255), + b=float(1), + a=color_intensity) # Transparency + + # Add the marker to the array + marker_array.markers.append(marker) + + # Create a text marker to display the probability + text_marker = Marker() + text_marker.header.frame_id = "map" + text_marker.header.stamp = self.get_clock().now().to_msg() + text_marker.ns = "text" + text_marker.id = num_circles + i # Ensure unique ID + text_marker.type = Marker.TEXT_VIEW_FACING + text_marker.action = Marker.ADD + + # Set the text position to match the circle's position + text_marker.pose.position.x = marker.pose.position.x + text_marker.pose.position.y = marker.pose.position.y + text_marker.pose.position.z = marker.pose.position.z + 0.5 # Slightly above the circle + text_marker.pose.orientation.w = 1.0 # Neutral orientation + + # Set the scale (size of the text) + text_marker.scale.z = 0.3 # Height of the text + + # Set the color of the text + text_marker.color = ColorRGBA(r=1.0, g=1.0, b=1.0, a=1.0) # White text + + # Set the text to display the probability + text_marker.text = f"{probability:.2f}" + + # Add the text marker to the array + marker_array.markers.append(text_marker) + + # Publish the marker array + self.publisher.publish(marker_array) + self.get_logger().info(f"Published {num_circles} circles with probabilities") + +def main(args=None): + rclpy.init(args=args) + node = ProbabilityCirclePublisher() + try: + rclpy.spin(node) + except KeyboardInterrupt: + pass + finally: + node.destroy_node() + rclpy.shutdown() + +if __name__ == '__main__': + main() diff --git a/localization-devel-ws/global/lidar_localization_pkg/package.xml b/localization-devel-ws/global/lidar_localization_pkg/package.xml new file mode 100644 index 0000000..f09813e --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/package.xml @@ -0,0 +1,29 @@ + + + + lidar_localization_pkg + 0.0.0 + Eurobot Localization utilizing LiDAR scan and triangulation + user + Apache-2.0 + + rclpy + ros2launch + + sensor_msgs + geometry_msgs + nav_msgs + tf2_ros + obstacle_detector + visualization_msgs + std_msgs + + ament_copyright + ament_flake8 + ament_pep257 + python3-pytest + + + ament_python + + diff --git a/localization-devel-ws/global/lidar_localization_pkg/resource/lidar_localization_pkg b/localization-devel-ws/global/lidar_localization_pkg/resource/lidar_localization_pkg new file mode 100644 index 0000000..e69de29 diff --git a/localization-devel-ws/global/lidar_localization_pkg/setup.cfg b/localization-devel-ws/global/lidar_localization_pkg/setup.cfg new file mode 100644 index 0000000..7fcbfcb --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script_dir=$base/lib/lidar_localization_pkg +[install] +install_scripts=$base/lib/lidar_localization_pkg diff --git a/localization-devel-ws/global/lidar_localization_pkg/setup.py b/localization-devel-ws/global/lidar_localization_pkg/setup.py new file mode 100644 index 0000000..93f9f8f --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/setup.py @@ -0,0 +1,67 @@ +import os +from glob import glob +from setuptools import setup, find_packages + +package_name = 'lidar_localization_pkg' + +launch_files = (glob('launch/*.launch') + + glob('launch/*.py') + + glob('launch/*.xml')) + +firmware_launch_files = (glob('launch/firmware/*.launch') + + glob('launch/firmware/*.py') + + glob('launch/firmware/*.xml')) + +config_files = glob('config/*.yml') + glob('config/*.yaml') + +setup( + name=package_name, + version='0.0.0', + packages=find_packages(exclude=['test']), + + data_files=[ + ('share/ament_index/resource_index/packages', + ['resource/' + package_name]), + (os.path.join('share', package_name), + ['package.xml']), + + (os.path.join('share', package_name, 'launch'), + launch_files), + (os.path.join('share', package_name, 'launch', 'firmware'), + firmware_launch_files), + + (os.path.join('share', package_name, 'config'), + config_files), + ], + + install_requires=[ + 'setuptools', + 'rclpy', + 'geometry_msgs', + 'obstacle_detector', + 'visualization_msgs', + 'std_msgs', + 'tf2_ros', + ], + zip_safe=True, + maintainer='jossiew621', + maintainer_email='jossiew621@gapp.nthu.edu.tw', + keywords=['scan', 'beacon', 'localization'], + classifiers=[ + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Apache-2.0 License', + 'Programming Language :: Python', + 'Topic :: Software Development', + ], + description='Eurobot localization using LiDAR scan, probability and triangulation', + license='Apache-2.0', + tests_require=['pytest'], + entry_points={ + 'console_scripts': [ + 'lidar_localization = lidar_localization_pkg.lidar_member_function:main', + 'circle_publisher = lidar_localization_pkg.probability_circle_publisher:main', + 'pred_publisher = lidar_localization_pkg.pred_publisher:main', + 'lidar_calibrator = lidar_localization_pkg.lidar_calibrator:main', + ], + }, +) diff --git a/localization-devel-ws/global/lidar_localization_pkg/test/test_copyright.py b/localization-devel-ws/global/lidar_localization_pkg/test/test_copyright.py new file mode 100644 index 0000000..97a3919 --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/test/test_copyright.py @@ -0,0 +1,25 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_copyright.main import main +import pytest + + +# Remove the `skip` decorator once the source file(s) have a copyright header +@pytest.mark.skip(reason='No copyright header has been placed in the generated source file.') +@pytest.mark.copyright +@pytest.mark.linter +def test_copyright(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found errors' diff --git a/localization-devel-ws/global/lidar_localization_pkg/test/test_flake8.py b/localization-devel-ws/global/lidar_localization_pkg/test/test_flake8.py new file mode 100644 index 0000000..27ee107 --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/test/test_flake8.py @@ -0,0 +1,25 @@ +# Copyright 2017 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_flake8.main import main_with_errors +import pytest + + +@pytest.mark.flake8 +@pytest.mark.linter +def test_flake8(): + rc, errors = main_with_errors(argv=[]) + assert rc == 0, \ + 'Found %d code style errors / warnings:\n' % len(errors) + \ + '\n'.join(errors) diff --git a/localization-devel-ws/global/lidar_localization_pkg/test/test_pep257.py b/localization-devel-ws/global/lidar_localization_pkg/test/test_pep257.py new file mode 100644 index 0000000..b234a38 --- /dev/null +++ b/localization-devel-ws/global/lidar_localization_pkg/test/test_pep257.py @@ -0,0 +1,23 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_pep257.main import main +import pytest + + +@pytest.mark.linter +@pytest.mark.pep257 +def test_pep257(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found code style errors / warnings' diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/.gitignore b/localization-devel-ws/global/obstacle_detector_2-humble-devel/.gitignore new file mode 100644 index 0000000..d766648 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/.gitignore @@ -0,0 +1,3 @@ +CMakeLists.txt.user +*~ +.vscode \ No newline at end of file diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/CMakeLists.txt b/localization-devel-ws/global/obstacle_detector_2-humble-devel/CMakeLists.txt new file mode 100644 index 0000000..f52454d --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/CMakeLists.txt @@ -0,0 +1,144 @@ +cmake_minimum_required(VERSION 3.5) +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 14) +endif() +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() +project(obstacle_detector) + +# set(CMAKE_CXX_FLAGS "-std=c++11 -fpermissive ${CMAKE_CXX_FLAGS} -Wfatal-errors\ ") + +find_package(ament_cmake REQUIRED) +find_package(rclcpp REQUIRED) +find_package(geometry_msgs REQUIRED) +find_package(sensor_msgs REQUIRED) +find_package(std_msgs REQUIRED) +find_package(nav_msgs REQUIRED) +find_package(visualization_msgs REQUIRED) +find_package(nav_msgs REQUIRED) +find_package(Eigen3 REQUIRED) +find_package(std_srvs REQUIRED) +find_package(tf2 REQUIRED) +find_package(tf2_ros REQUIRED) +find_package(tf2_geometry_msgs REQUIRED) +find_package(laser_geometry REQUIRED) +find_package(builtin_interfaces REQUIRED) +find_package(rosidl_default_generators REQUIRED) + +find_package(Armadillo REQUIRED) +find_package(Boost 1.54.0 REQUIRED system) + +rosidl_generate_interfaces(${PROJECT_NAME} + "msg/CircleObstacle.msg" + "msg/Obstacles.msg" + "msg/SegmentObstacle.msg" + DEPENDENCIES geometry_msgs std_msgs builtin_interfaces +) + +set(PROJECT_INCLUDE_DIRS include) +set(PROJECT_DEPENDS "rclcpp" "std_msgs" "nav_msgs" "std_srvs" "visualization_msgs" "geometry_msgs" "sensor_msgs" "Eigen3" "tf2" "tf2_geometry_msgs" "tf2_ros" "laser_geometry" "rosidl_default_generators") + +include_directories(include ${PROJECT_INCLUDE_DIRS} ${ARMADILLO_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) + +# +# Build libs +add_library(obstacle_extractor src/obstacle_extractor.cpp) +target_link_libraries(obstacle_extractor ${ARMADILLO_LIBRARIES}) +ament_target_dependencies(obstacle_extractor ${PROJECT_DEPENDS}) +# allow the project to link against its own messages +rosidl_get_typesupport_target(cpp_typesupport_target "${PROJECT_NAME}" "rosidl_typesupport_cpp") +target_link_libraries(obstacle_extractor "${cpp_typesupport_target}") + +add_library(obstacle_tracker src/obstacle_tracker.cpp) +target_link_libraries(obstacle_tracker ${ARMADILLO_LIBRARIES}) +ament_target_dependencies(obstacle_tracker ${PROJECT_DEPENDS}) +# allow the project to link against its own messages +rosidl_get_typesupport_target(cpp_typesupport_target "${PROJECT_NAME}" "rosidl_typesupport_cpp") +target_link_libraries(obstacle_tracker "${cpp_typesupport_target}") + +add_library(obstacle_publisher src/obstacle_publisher.cpp) +target_link_libraries(obstacle_publisher ${ARMADILLO_LIBRARIES}) +ament_target_dependencies(obstacle_publisher ${PROJECT_DEPENDS}) +# allow the project to link against its own messages +rosidl_get_typesupport_target(cpp_typesupport_target "${PROJECT_NAME}" "rosidl_typesupport_cpp") +target_link_libraries(obstacle_publisher "${cpp_typesupport_target}") + +# +# Build nodes +# +# add_executable(scans_merger_node src/nodes/scans_merger_node.cpp) +# target_link_libraries(scans_merger_node scans_merger) + +add_executable(obstacle_extractor_node src/nodes/obstacle_extractor_node.cpp) +target_link_libraries(obstacle_extractor_node obstacle_extractor) + +add_executable(obstacle_tracker_node src/nodes/obstacle_tracker_node.cpp) +target_link_libraries(obstacle_tracker_node obstacle_tracker) + +add_executable(obstacle_publisher_node src/nodes/obstacle_publisher_node.cpp) +target_link_libraries(obstacle_publisher_node obstacle_publisher) + +# +# Build nodelets +# +# add_library(${PROJECT_NAME}_nodelets +# src/nodelets/scans_merger_nodelet.cpp +# src/nodelets/obstacle_extractor_nodelet.cpp +# src/nodelets/obstacle_tracker_nodelet.cpp +# src/nodelets/obstacle_publisher_nodelet.cpp) +# target_link_libraries(${PROJECT_NAME}_nodelets scans_merger obstacle_extractor obstacle_tracker obstacle_publisher) + +# ament_export_targets(my_libraryTargets HAS_LIBRARY_TARGET) +# ament_export_dependencies(some_dependency) + + +# +# Install libraries +# +# install(TARGETS scans_merger obstacle_extractor obstacle_tracker obstacle_publisher ${PROJECT_NAME}_nodelets ${PROJECT_NAME}_gui +# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} +# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} +# RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}) + +install(TARGETS obstacle_extractor obstacle_tracker obstacle_publisher + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION lib/${PROJECT_NAME} +) + +# +# Install nodes +# +# install(TARGETS scans_merger_node obstacle_extractor_node obstacle_tracker_node obstacle_publisher_node +# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) +install(TARGETS obstacle_extractor_node obstacle_tracker_node obstacle_publisher_node + RUNTIME DESTINATION lib/${PROJECT_NAME} +) + +## Mark other files for installation (e.g. launch and bag files, etc.) +install( + DIRECTORY + launch + DESTINATION share/${PROJECT_NAME}/ +) + +# +# Install header files +# # +# install(DIRECTORY include/${PROJECT_NAME}/ +# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}) + +# # +# # Install nodelet and rviz plugins description +# # +# install(FILES nodelet_plugins.xml rviz_plugins.xml +# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) + +# +# Create folders and copy resources +# +# file(MAKE_DIRECTORY $ENV{HOME}/.local/share/icons/robor) +# file(COPY resources/play.png resources/stop.png DESTINATION $ENV{HOME}/.local/share/icons/robor) + +ament_package() \ No newline at end of file diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/LICENSE b/localization-devel-ws/global/obstacle_detector_2-humble-devel/LICENSE new file mode 100644 index 0000000..a66d417 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2022, Giulio Schiavi +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 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. diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/ORIGINAL_LICENSE b/localization-devel-ws/global/obstacle_detector_2-humble-devel/ORIGINAL_LICENSE new file mode 100644 index 0000000..ec63bbf --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/ORIGINAL_LICENSE @@ -0,0 +1,30 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ \ No newline at end of file diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/README.md b/localization-devel-ws/global/obstacle_detector_2-humble-devel/README.md new file mode 100644 index 0000000..9f87dc9 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/README.md @@ -0,0 +1,281 @@ +# ROS2 obstacle detector and tracker (ROS2 Humble) +This package provides utilities to detect and track obstacles from data provided by 2D laser scanners. Detected obstacles come in a form of line segments or circles. The package was designed for a robot equipped with two laser scanners therefore it contains several additional utilities. + +## Acknowledgements +The code in this repository is a ROS2 port of the code in [https://github.com/jk-ethz/obstacle_detector](https://github.com/jk-ethz/obstacle_detector), which itself is a fork of [https://github.com/tysik/obstacle_detector](https://github.com/tysik/obstacle_detector). You can find the original LICENSE [here](/ORIGINAL_LICENSE). Any changes I made that are not covered by the original LICENSE are covered by the LICENSE [here](/LICENSE). The README of the original package can be found below. + +## ROS2 Notes +The current port includes all core functionality (detection, tracking, publishing, setting of parameters in the launch files). The Rviz visualization of obstacles was moved to standard `MarkerArray` messages instead of the custom implementation of the ROS1 original package. RViz "live" control panels have not yet been ported. I have plans to move this functionality to `rqt dynamic_reconfigure`. + + +# Original README (obstacle_detector package) +The `obstacle_detector` package provides utilities to detect and track obstacles from data provided by 2D laser scanners. Detected obstacles come in a form of line segments or circles. The package was designed for a robot equipped with two laser scanners therefore it contains several additional utilities. The working principles of the method are described in an article provided in the `resources` folder. + +The package requires [Armadillo C++](http://arma.sourceforge.net) library for compilation and runtime. + +----------------------- +

+ Visual example of obstacle detector output. +
+ Fig. 1. Visual example of obstacle detector output. +

+ +----------------------- + +1. The nodes and nodelets + 1.1 The scans_merger + 1.2 The obstacle_extractor + 1.3 The obstacle_tracker + 1.4 The obstacle_publisher +2. The messages +3. Launch files +4. The displays + +## 1. The nodes and nodelets + +The package provides separate nodes/nodelets to perform separate tasks. In general solution the data is processed in a following manner: + +`two laser scans` -> `scans merger` -> `merged scan or pcl` -> `obstacle extractor` -> `obstacles` -> `obstacle tracker` -> `refined obstacles` + +For some scenarios the pure obstacle extraction directly from a laser scan (without tracking) might be sufficient. + +The nodes are configurable with the use of ROS parameter server. All of the nodes provide a private `params` service, which allows the process to get the latest parameters from the parameter server. + +All of the nodes can be in either active or sleep mode, triggered by setting the appropriate variable in the parameter server and calling `params` service. In the sleep mode, any subscribers or publishers are shut down and the node does nothing until activated again. + +For the ease of use it is recomended to use appropriate Rviz panels provided for the nodes with the package. The Rviz panels communicate via parameter server and service-client calls, therefore the names of the nodes must be preserved unchanged (cf. launch files for examples). + +### 1.1. The scans_merger node +**Note: for convenience, the ROS2 implementation of this node has been moved to a separate repository at [https://github.com/giuschio/scan_merger_2.git](https://github.com/giuschio/scan_merger_2.git).** + + +This node converts two messages of type `sensor_msgs/LaserScan` from topics `front_scan` and `rear_scan` into a single laser scan of the same type, published under topic `scan` and/or a point cloud of type `sensor_msgs/PointCloud`, published under topic `pcl`. The difference between both is that the resulting laser scan divides the area into finite number of circular sectors and put one point (or actually one range value) in each section occupied by some measured points, whereas the resulting point cloud simply copies all of the points obtained from sensors. + +The input laser scans are firstly rectified to incorporate the motion of the scanner in time (see `laser_geometry` package). Next, two PCLs obtained from the previous step are synchronized and transformed into the target coordinate frame at the current point in time. + +----------------------- +

+ Visual example of scans_merger output +
+ Fig. 2. Visual example of `scans_merger` output. +

+ +----------------------- + +The resulting messages contain geometric data described with respect to a specific coordinate frame (e.g. `robot`). Assuming that the coordinate frames attached to two laser scanners are called `front_scanner` and `rear_scanner`, both transformation from `robot` frame to `front_scanner` frame and from `robot` frame to `rear_scanner` frame must be provided. The node allows to artificially restrict measured points to some rectangular region around the `robot` frame as well as to limit the resulting laser scan range. The points falling behind this region will be discarded. + +Even if only one laser scanner is used, the node can be useful for simple data pre-processing, e.g. rectification, range restriction or recalculation of points to a different coordinate frame. The node uses the following set of local parameters: + +* `~active` (`bool`, default: `true`) - active/sleep mode, +* `~publish_scan` (`bool`, default: `false`) - publish the merged laser scan message, +* `~publish_pcl` (`bool`, default: `true`) - publish the merged point cloud message, +* `~ranges_num` (`int`, default: `1000`) - number of ranges (circular sectors) contained in the 360 deg laser scan message, +* `~min_scanner_range` (`double`, default: `0.05`) - minimal allowable range value for produced laser scan message, +* `~max_scanner_range` (`double`, default: `10.0`) - maximal allowable range value for produced laser scan message, +* `~min_x_range` (`double`, default: `-10.0`) - limitation for points coordinates (points with coordinates behind these limitations will be discarded), +* `~max_x_range` (`double`, default: `10.0`) - as above, +* `~min_y_range` (`double`, default: `-10.0`) - as above, +* `~max_y_range` (`double`, default: `10.0`) - as above, +* `~fixed_frame_id` (`string`, default: `map`) - name of the fixed coordinate frame used for scan rectification in time, +* `~target_frame_id` (`string`, default: `map`) - name of the coordinate frame used as the origin for the produced laser scan or point cloud. + +In the original implementation, the `~target_frame_id` is the `robot` frame. However, with a moving robot, this causes the Kalman filters later on to think that the points are moving even though the robot is moving w. r. t. the map. That's why we enhance the point cloud with "range" (distance) information in the `scans_merger` node. This means that even though the origin of the target frame does not match the lidar scan origin, we still preserve the distance of each point to the scanner that produced the point. For that reason, it is absolutely essential to run the scans through the `scans_merger` node, even if not merging scans and to set `publish_pcl` to `true`. + +The package comes with Rviz panel for this node (**not yet ported**). + +----------------------- +

+ Rviz panel for the scans_merger node. +
+ Fig. 3. Rviz panel for the `scans_merger` node. +

+ +----------------------- + +### 1.2. The obstacle_extractor node + +This node converts messages of type `sensor_msgs/LaserScan` from topic `scan` or messages of type `sensor_msgs/PointCloud` from topic `pcl` into obstacles which are published as messages of custom type `obstacles_detector/Obstacles` under topic `raw_obstacles`. The PCL message must be ordered in the angular fashion, because the algorithm exploits the polar nature of laser scanners. + +----------------------- +

+ Visual example of obstacle_extractor output. +
+ Fig. 4. Visual example of `obstacle_extractor` output. +

+ +----------------------- + +The input points are firstly grouped into subsets and marked as visible or not (if a group is in front of neighbouring groups, it is visible. Otherwise it is assumed to be occluded). The algorithm extracts segments from each points subset. Next, the segments are checked for possible merging between each other. The circular obstacles are then extracted from segments and also merged if possible. Resulting set of obstacles can be transformed to a dedicated coordinate frame. + +The node is configurable with the following set of local parameters: + +* `~active` (`bool`, default: `true`) - active/sleep mode, +* `~use_scan` (`bool`, default: `false`) - use laser scan messages, +* `~use_pcl` (`bool`, default: `true`) - use point cloud messages (if both scan and pcl are chosen, scans will have priority), +* `~use_split_and_merge` (`bool`, default: `true`) - choose wether to use Iterative End Point Fit (false) or Split And Merge (true) algorithm to detect segments, +* `~circles_from_visibles` (`bool`, default: `true`) - detect circular obstacles only from fully visible (not occluded) segments, +* `~discard_converted_segments` (`bool`, default: `true`) - do not publish segments, from which the circles were spawned, +* `~transform_coordinates` (`bool`, default: `true`) - transform the coordinates of obstacles to a frame described with `frame_id` parameter, +* `~min_group_points` (`int`, default: `5`) - minimum number of points comprising a group to be further processed, +* `~max_group_distance` (`double`, default: `0.1`) - if the distance between two points is greater than this value, start a new group, +* `~distance_proportion` (`double`, default: `0.00628`) - enlarge the allowable distance between points proportionally to the range of point (use scan angle increment in radians), +* `~max_split_distance` (`double`, default: `0.2`) - if a point in group lays further from a leading line than this value, split the group, +* `~max_merge_separation` (`double`, default: `0.2`) - if distance between obstacles is smaller than this value, consider merging them, +* `~max_merge_spread` (`double`, default: `0.2`) - merge two segments if all of their extreme points lay closer to the leading line than this value, +* `~max_circle_radius` (`double`, default: `0.6`) - if a circle would have greater radius than this value, skip it, +* `~radius_enlargement` (`double`, default: `0.25`) - artificially enlarge the circles radius by this value, +* `~frame_id` (`string`, default: `map`) - name of the coordinate frame used as origin for produced obstacles (used only if `transform_coordinates` flag is set to true). + +The package comes with Rviz panel for this node (**not yet ported**). + +----------------------- +

+ Rviz panel for the obstacle_extractor node. +
+ Fig. 5. Rviz panel for the `obstacle_extractor` node. +

+ +----------------------- + +### 1.3. The obstacle_tracker node + +This node tracks and filters the circular and line segment obstacles with the use of Kalman filter. It subscribes to messages of custom type `obstacle_detector/Obstacles` from topic `raw_obstacles` and publishes messages of the same type under topic `tracked_obstacles`. The tracking algorithm is applied to both, the circular obstacles and line segments. The tracked obstacles are supplemented with additional information on their velocity. + +----------------------- +

+ Visual example of obstacle_tracker output. +
+ Fig. 6. Visual example of `obstacle_tracker` output. +

+ +----------------------- + +The node works in a synchronous manner with the default rate of 100 Hz. If detected obstacles are published less often, the tracker will supersample them and smoothen their position and radius. The following set of local parameters can be used to tune the node: + +* `~active` (`bool`, default: `true`) - active/sleep mode, +* `~copy_segments` (`bool`, default: `true`) - copy detected segments into tracked obstacles message, +* `~loop_rate` (`double`, default: `100.0`) - the main loop rate in Hz, +* `~tracking_duration` (`double`, default: `2.0`) - the duration of obstacle tracking in the case of lack of incomming data (after this time from the last corresponding measurement the tracked obstacle will be removed from the list), +* `~min_correspondence_cost` (`double`, default `0.3`) - a threshold for correspondence test, +* `~std_correspondence_dev` (`double`, default `0.15`) - (experimental) standard deviation of the position ellipse in the correspondence test, +* `~process_variance` (`double`, default `0.01`) - variance of obstacles position and radius (parameter of Kalman Filter), +* `~process_rate_variance` (`double`, default `0.1`) - variance of rate of change of obstacles values (parameter of Kalman Filter), +* `~measurement_variance` (`double`, default `1.0`) - variance of measured obstacles values (parameter of Kalman Filter), +* `~frame_id` (`string`, default: `map`) - name of the coordinate frame in which the obstacles are described, + +The package comes with Rviz panel for this node (**not yet ported**). + +----------------------- +

+ Rviz panel for the obstacle_tracker node. +
+ Fig. 7. Rviz panel for the `obstacle_tracker` node. +

+ +----------------------- + +### 1.4. The obstacle_publisher node + +The auxiliary node which allows to publish a set of virtual, circular obstacles in the form of message of type `obstacles_detector/Obstacles` under topic `obstacles`. The node is mostly used for off-line tests. The following parameters are used to configure the node: + +* `~active` (`bool`, default: `true`) - active/sleep mode, +* `~reset` (`bool`, default: `false`) - reset time for obstacles motion calculation (used by dedicated Rviz panel), +* `~fusion_example` (`bool`, default: `false`) - produce obstacles showing fusion, +* `~fission_example` (`bool`, default: `false`) - produce obstacles showing fission, +* `~radius_margin` (`double`, default: `0.25`) - artificially enlarge the circles radius by this value in meters, +* `~loop_rate` (`double`, default: `10.0`) - the main loop rate in Hz, +* `~frame_id` (`string`, default: `map`) - name of the coordinate frame in which the obstacles are described. + +The following parameters are used to provide the node with a set of obstacles: + +* `~x_vector` (`std::vector`, default: `[]`) - the array of x-coordinates of obstacles center points, +* `~y_vector` (`std::vector`, default: `[]`) - the array of y-coordinates of obstacles center points, +* `~r_vector` (`std::vector`, default: `[]`) - the array of obstacles radii, +* `~x_vector` (`std::vector`, default: `[]`) - the array of velocities of obstacles center points in x direction, +* `~y_vector` (`std::vector`, default: `[]`) - the array of velocities of obstacles center points in y direction. + +The package comes with Rviz panel for this node (**not yet ported**). + +----------------------- +

+ Rviz panel for the obstacle_publisher node. +
+ Fig. 8. Rviz panel for the `obstacle_publisher` node. +

+ +----------------------- + +## 2. The messages + +The package provides three custom message types. All of their numerical values are provided in SI units. + +* `CircleObstacle` + - `geometry_msgs/Point center` - center of circular obstacle, + - `geometry_msgs/Vector3 velocity` - linear velocity of circular obstacle, + - `float64 radius` - radius of circular obstacle with added safety margin, + - `float64 true_radius` - measured radius of obstacle without the safety margin. +* `SegmentObstacle` + - `geometry_msgs/Point first_point` - first point of the segment (in counter-clockwise direction), + - `geometry_msgs/Point last_point` - end point of the segment. +* `Obstacles` + - `Header header` + - `obstacle_detector/SegmentObstacle[] segments` + - `obstacle_detector/CircleObstacle[] circles` + +## 3. The launch files + +Provided launch files are good examples of how to use `obstacle_detector` package. They give a full list of parameters used by each of provided nodes. +* `demo.launch` - Plays a rosbag with recorded scans and starts all of the nodes with Rviz configured with appropriate panels. +* `nodes_example.launch` - Runs all of the nodes with their parameters set to default values. +* `nodelets_example.launch` - Runs all of the nodelets with their parameters set to default values. + +## 4. The displays + +For better visual effects, appropriate Rviz display for `Obstacles` messages was prepared. Via its properties, one can change the colors of the obstacles. + +## 5. Other interesting forks of this repository + +- Lots of improvements: + + https://github.com/6RiverSystems/obstacle_detector/tree/6RS + + Melodic: + + https://github.com/6RiverSystems/obstacle_detector/tree/6RS-melodic + https://github.com/6RiverSystems/obstacle_detector/tree/feature/lidar-tracking + +- Rectangular obstacles: + + https://github.com/tysik/obstacle_detector/compare/master...icsl-Jeon:obstacle_detector:master + +- Dynamic map subtraction, robot base velocity compensation: + + https://github.com/tysik/obstacle_detector/compare/master...bangyii:obstacle_detector:master + +- Fix start/end of laser bug: + + https://github.com/tysik/obstacle_detector/compare/master...seiga-k:obstacle_detector:master + +- Obstacles to PoseArray conversion for better Visualization: + + https://github.com/tysik/obstacle_detector/compare/master...gaoyangu:obstacle_detector:rikirobot + +- More obstacle information (can it move?) publishing: + + https://github.com/tysik/obstacle_detector/compare/master...Xvdgeest:obstacle_detector:master + +- Publishing of Polygons in addition to Segments: + + https://github.com/tysik/obstacle_detector/compare/master...FelixTFD:obstacle_detector:master + +- Different circle merging logic: + + https://github.com/tysik/obstacle_detector/compare/master...ljburtz:obstacle_detector:circle_merge_logic + +- Cone detection: + + https://github.com/tysik/obstacle_detector/compare/master...Magnusgaertner:obstacle_detector:master + +Author: +_Mateusz Przybyla_ + diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/icons/classes/Obstacle Extractor.png b/localization-devel-ws/global/obstacle_detector_2-humble-devel/icons/classes/Obstacle Extractor.png new file mode 100644 index 0000000..bf97282 Binary files /dev/null and b/localization-devel-ws/global/obstacle_detector_2-humble-devel/icons/classes/Obstacle Extractor.png differ diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/icons/classes/Obstacle Publisher.png b/localization-devel-ws/global/obstacle_detector_2-humble-devel/icons/classes/Obstacle Publisher.png new file mode 100644 index 0000000..61a4902 Binary files /dev/null and b/localization-devel-ws/global/obstacle_detector_2-humble-devel/icons/classes/Obstacle Publisher.png differ diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/icons/classes/Obstacle Tracker 2.png b/localization-devel-ws/global/obstacle_detector_2-humble-devel/icons/classes/Obstacle Tracker 2.png new file mode 100644 index 0000000..cf74075 Binary files /dev/null and b/localization-devel-ws/global/obstacle_detector_2-humble-devel/icons/classes/Obstacle Tracker 2.png differ diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/icons/classes/Obstacle Tracker.png b/localization-devel-ws/global/obstacle_detector_2-humble-devel/icons/classes/Obstacle Tracker.png new file mode 100644 index 0000000..4c1c767 Binary files /dev/null and b/localization-devel-ws/global/obstacle_detector_2-humble-devel/icons/classes/Obstacle Tracker.png differ diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/icons/classes/Obstacles.png b/localization-devel-ws/global/obstacle_detector_2-humble-devel/icons/classes/Obstacles.png new file mode 100644 index 0000000..ed91d08 Binary files /dev/null and b/localization-devel-ws/global/obstacle_detector_2-humble-devel/icons/classes/Obstacles.png differ diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/icons/classes/Scans Merger.png b/localization-devel-ws/global/obstacle_detector_2-humble-devel/icons/classes/Scans Merger.png new file mode 100644 index 0000000..b1dff2c Binary files /dev/null and b/localization-devel-ws/global/obstacle_detector_2-humble-devel/icons/classes/Scans Merger.png differ diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/obstacle_extractor.h b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/obstacle_extractor.h new file mode 100644 index 0000000..56f1a3c --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/obstacle_extractor.h @@ -0,0 +1,172 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include "rclcpp/rclcpp.hpp" +#include "tf2/exceptions.h" +#include "tf2_ros/transform_listener.h" +#include "tf2_ros/buffer.h" + +#include "obstacle_detector/utilities/point.h" +#include "obstacle_detector/utilities/segment.h" +#include "obstacle_detector/utilities/circle.h" +#include "obstacle_detector/utilities/point_set.h" +#include "obstacle_detector/utilities/math_utilities.h" + +#include "geometry_msgs/msg/point.hpp" +#include "geometry_msgs/msg/pose.hpp" +#include "geometry_msgs/msg/vector3.hpp" +#include "sensor_msgs/msg/laser_scan.hpp" +#include "sensor_msgs/msg/point_cloud.hpp" +#include "sensor_msgs/msg/point_cloud2.hpp" +#include "sensor_msgs/point_cloud2_iterator.hpp" +#include "visualization_msgs/msg/marker.hpp" +#include "visualization_msgs/msg/marker_array.hpp" +#include "nav_msgs/msg/odometry.hpp" +#include "std_srvs/srv/empty.hpp" +#include "sensor_msgs/msg/point_field.hpp" + +#include "geometry_msgs/msg/pose_array.hpp" + + +#include +#include "obstacle_detector/msg/obstacles.hpp" +#include "obstacle_detector/msg/circle_obstacle.hpp" +#include "obstacle_detector/msg/segment_obstacle.hpp" + + +namespace obstacle_detector +{ + +class ObstacleExtractor +{ +public: + ObstacleExtractor(std::shared_ptr nh, std::shared_ptr nh_local); + ~ObstacleExtractor(); + +private: + void updateParamsUtil(); + void updateParams(const std::shared_ptr request_header, + const std::shared_ptr &req, + const std::shared_ptr &res); + void scanCallback(const sensor_msgs::msg::LaserScan& scan_msg); + void pclCallback(const sensor_msgs::msg::PointCloud& pcl_msg); + void pcl2Callback(sensor_msgs::msg::PointCloud2::SharedPtr pcl_msg); + void localCallback(const nav_msgs::msg::Odometry& local_msg); + + void initialize() { std_srvs::srv::Empty empt; updateParamsUtil(); } + + void processPoints(); + void groupPoints(); + void transformObstacles(); + void publishObstacles(); + // void publishVisualizationObstacles(); + void publishPointCloud2Obstacles(); + + void detectSegments(const PointSet& point_set); + void mergeSegments(); + bool compareSegments(const Segment& s1, const Segment& s2, Segment& merged_segment); + bool checkSegmentsProximity(const Segment& s1, const Segment& s2); + bool checkSegmentsCollinearity(const Segment& segment, const Segment& s1, const Segment& s2); + Point distortionCorrection(sensor_msgs::msg::LaserScan, double*, double, double); + void tailElimination(PointSet&); + bool vectorComparison(double, double, double, double, double, double); + void detectCircles(); + void mergeCircles(); + bool compareCircles(const Circle& c1, const Circle& c2, Circle& merged_circle); + + std::shared_ptr nh_; + std::shared_ptr nh_local_; + + rclcpp::Subscription::SharedPtr scan_sub_; + rclcpp::Subscription::SharedPtr pcl_sub_; + rclcpp::Subscription::SharedPtr pcl2_sub_; + rclcpp::Subscription::SharedPtr local_filter_sub; + rclcpp::Publisher::SharedPtr obstacles_pub_; + // rclcpp::Publisher::SharedPtr obstacles_vis_pub_; + rclcpp::Publisher::SharedPtr obstacles_vis_pcl_pub_; + rclcpp::Publisher::SharedPtr obstacles_pose_array_pub_; + rclcpp::Service::SharedPtr params_srv_; + + rclcpp::Time stamp_; + rclcpp::Time time_last_marker_published_; + int num_active_markers_ = 0; + double local_twist[3]={0.0}; // 0: vx, 1: vy, 2: w + double prev_scan_twist[3]={0.0}; + std::string base_frame_id_; + std::shared_ptr tf_listener_{nullptr}; + std::unique_ptr tf_buffer_; + + std::list input_points_; + std::list segments_; + std::list circles_; + + // Parameters + bool p_active_; + bool p_use_scan_; + bool p_use_pcl_; + bool p_use_pcl_2_; + + bool p_use_split_and_merge_; + bool p_circles_from_visibles_; + bool p_discard_converted_segments_; + bool p_transform_coordinates_; + + bool p_pose_array_; + int p_min_group_points_; + + double p_distance_proportion_; + double p_max_group_distance_; + double p_max_split_distance_; + double p_max_merge_separation_; + double p_max_merge_spread_; + double p_max_circle_radius_; + double p_radius_enlargement_; + + double p_min_x_limit_; + double p_max_x_limit_; + double p_min_y_limit_; + double p_max_y_limit_; + + double p_max_range_ = 3.6; + + double p_tail_threshold; + + std::string p_frame_id_; + std::string published_obstacles_frame_id_ = ""; +}; + +} // namespace obstacle_detector diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/obstacle_publisher.h b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/obstacle_publisher.h new file mode 100644 index 0000000..e1178ab --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/obstacle_publisher.h @@ -0,0 +1,98 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include "rclcpp/rclcpp.hpp" +#include "std_srvs/srv/empty.hpp" +#include "obstacle_detector/msg/obstacles.hpp" +#include "obstacle_detector/msg/circle_obstacle.hpp" +#include "obstacle_detector/msg/segment_obstacle.hpp" + +namespace obstacle_detector +{ + +class ObstaclePublisher +{ +public: + ObstaclePublisher(std::shared_ptr nh, std::shared_ptr nh_local); + ~ObstaclePublisher(); + +private: + void updateParamsUtil(); + void updateParams(const std::shared_ptr request_header, + const std::shared_ptr &req, + const std::shared_ptr &res); + void timerCallback(); + + void initialize() { std_srvs::srv::Empty empt; updateParamsUtil(); } + + void calculateObstaclesPositions(double dt); + void fusionExample(double t); + void fissionExample(double t); + void publishObstacles(); + void reset(); + + std::shared_ptr nh_; + std::shared_ptr nh_local_; + + rclcpp::Publisher::SharedPtr obstacles_pub_; + rclcpp::Service::SharedPtr params_srv_; + rclcpp::TimerBase::SharedPtr timer_; + + obstacle_detector::msg::Obstacles obstacles_; + double t_; + + // Parameters + bool p_active_; + bool p_reset_; + bool p_fusion_example_; + bool p_fission_example_; + + double p_loop_rate_; + double p_sampling_time_; + double p_radius_margin_; + + std::vector p_x_vector_; + std::vector p_y_vector_; + std::vector p_r_vector_; + + std::vector p_vx_vector_; + std::vector p_vy_vector_; + + std::string p_frame_id_; +}; + +} // namespace obstacle_detector diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/obstacle_tracker.h b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/obstacle_tracker.h new file mode 100644 index 0000000..03e11ce --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/obstacle_tracker.h @@ -0,0 +1,142 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include +#include +#include "rclcpp/rclcpp.hpp" +#include +#include "std_srvs/srv/empty.hpp" +#include "nav_msgs/msg/odometry.hpp" + +#include "visualization_msgs/msg/marker.hpp" +#include "visualization_msgs/msg/marker_array.hpp" + +#include "obstacle_detector/msg/obstacles.hpp" +#include "obstacle_detector/msg/circle_obstacle.hpp" +#include "obstacle_detector/msg/segment_obstacle.hpp" + +#include "obstacle_detector/utilities/tracked_circle_obstacle.h" +#include "obstacle_detector/utilities/tracked_segment_obstacle.h" +#include "obstacle_detector/utilities/math_utilities.h" + +namespace obstacle_detector +{ + +class ObstacleTracker { +public: + ObstacleTracker(std::shared_ptr nh, std::shared_ptr nh_local); + ~ObstacleTracker(); + +private: + void updateParamsUtil(); + void updateParams(const std::shared_ptr request_header, const std::shared_ptr &req, const std::shared_ptr &res); + void timerCallback(); + void obstaclesCallback(const obstacle_detector::msg::Obstacles::ConstSharedPtr& new_obstacles); + void obstaclesCallbackCircles(const obstacle_detector::msg::Obstacles::ConstSharedPtr& new_obstacles); + void obstaclesCallbackSegments(const obstacle_detector::msg::Obstacles::ConstSharedPtr& new_obstacles); + void odomCallback(const nav_msgs::msg::Odometry::ConstSharedPtr& msg); + + void initialize() { std_srvs::srv::Empty empt; updateParamsUtil(); } + + double obstacleCostFunction(const obstacle_detector::msg::CircleObstacle& new_obstacle, const obstacle_detector::msg::CircleObstacle& old_obstacle); + double obstacleCostFunction(const obstacle_detector::msg::SegmentObstacle& new_obstacle, const obstacle_detector::msg::SegmentObstacle& old_obstacle); + void calculateCostMatrix(const std::vector& new_obstacles, arma::mat& cost_matrix); + void calculateCostMatrix(const std::vector& new_obstacles, arma::mat& cost_matrix); + void calculateRowMinIndices(const arma::mat& cost_matrix, std::vector& row_min_indices, const int T, const int U); + void calculateColMinIndices(const arma::mat& cost_matrix, std::vector& col_min_indices, const int T, const int U); + + bool fusionObstacleUsed(const int idx, const std::vector& col_min_indices, const std::vector& used_new, const std::vector& used_old); + bool fusionObstaclesCorrespond(const int idx, const int jdx, const std::vector& col_min_indices, const std::vector& used_old); + bool fissionObstacleUsed(const int idx, const int T, const std::vector& row_min_indices, const std::vector& used_new, const std::vector& used_old); + bool fissionObstaclesCorrespond(const int idx, const int jdx, const std::vector& row_min_indices, const std::vector& used_new); + + void fuseObstacles(const std::vector& fusion_indices, const std::vector& col_min_indices, + std::vector& new_tracked, const obstacle_detector::msg::Obstacles::ConstSharedPtr& new_obstacles); + void fissureObstacle(const std::vector& fission_indices, const std::vector& row_min_indices, + std::vector& new_tracked, const obstacle_detector::msg::Obstacles::ConstSharedPtr& new_obstacles); + void fuseObstacles(const std::vector& fusion_indices, const std::vector& col_min_indices, + std::vector& new_tracked, const obstacle_detector::msg::Obstacles::ConstSharedPtr& new_obstacles); + void fissureObstacle(const std::vector& fission_indices, const std::vector& row_min_indices, + std::vector& new_tracked, const obstacle_detector::msg::Obstacles::ConstSharedPtr& new_obstacles); + + void updateObstacles(); + void publishObstacles(); + void publishVisualizationObstacles(); + visualization_msgs::msg::Marker getMarkerBase(uid_t uid); + visualization_msgs::msg::Marker getMarkerCircle(obstacle_detector::msg::CircleObstacle& ob); + visualization_msgs::msg::Marker getMarkerSegment(obstacle_detector::msg::SegmentObstacle& ob); + visualization_msgs::msg::Marker getMarkerText(uid_t uid); + visualization_msgs::msg::Marker getMarkerVelocityArrow(uid_t uid, double px, double py, double vx, double vy); + + std::shared_ptr nh_; + std::shared_ptr nh_local_; + + rclcpp::Subscription::SharedPtr obstacles_sub_; + rclcpp::Subscription::SharedPtr odom_sub_; + rclcpp::Publisher::SharedPtr obstacles_pub_; + rclcpp::Publisher::SharedPtr obstacles_vis_pub_; + rclcpp::Service::SharedPtr params_srv_; + rclcpp::TimerBase::SharedPtr timer_; + + double radius_margin_; + obstacle_detector::msg::Obstacles obstacles_; + nav_msgs::msg::Odometry odom_; + + std::vector tracked_circle_obstacles_; + std::vector untracked_circle_obstacles_; + std::vector tracked_segment_obstacles_; + std::vector untracked_segment_obstacles_; + + // Parameters + bool p_active_; + bool p_copy_segments_; + bool p_compensate_robot_velocity_; + + double p_tracking_duration_; + double p_loop_rate_; + double p_sampling_time_; + double p_sensor_rate_; + double p_min_correspondence_cost_; + double p_std_correspondence_dev_; + double p_process_variance_; + double p_process_rate_variance_; + double p_measurement_variance_; + + std::string p_frame_id_; +}; + +} // namespace obstacle_detector diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/circle.h b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/circle.h new file mode 100644 index 0000000..614284c --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/circle.h @@ -0,0 +1,69 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include "obstacle_detector/utilities/point.h" +#include "obstacle_detector/utilities/segment.h" + +namespace obstacle_detector +{ + +class Circle +{ +public: + Circle(const Point& p = Point(), const double r = 0.0) : center(p), radius(r) { } + + /* + * Create a circle by taking the segment as a base of equilateral + * triangle. The circle is circumscribed on this triangle. + */ + Circle(const Segment& s) { + radius = 0.5773502 * s.length(); // sqrt(3)/3 * length + center = (s.first_point + s.last_point - radius * s.normal()) / 2.0; + point_sets = s.point_sets; + } + + double distanceTo(const Point& p) { return (p - center).length() - radius; } + + friend std::ostream& operator<<(std::ostream& out, const Circle& c) + { out << "C: " << c.center << ", R: " << c.radius; return out; } + + Point center; + double radius; + std::vector point_sets; +}; + +} // namespace obstacle_detector diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/figure_fitting.h b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/figure_fitting.h new file mode 100644 index 0000000..d25897d --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/figure_fitting.h @@ -0,0 +1,210 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include + +#include "obstacle_detector/utilities/point.h" +#include "obstacle_detector/utilities/segment.h" +#include "obstacle_detector/utilities/circle.h" + +namespace obstacle_detector +{ + +/* + * Returns a total best fit approximation of + * segment based on given point set. The equation + * used for fitting is given by + * Ax + By = -C + * and the A, B, C parameters are normalized. + */ +Segment fitSegment(const PointSet& point_set) { + static int num_calls = 0; + num_calls++; + + int N = point_set.num_points; + assert(N >= 2); + + arma::mat input = arma::mat(N, 2).zeros(); // [x_i, y_i] + arma::vec output = arma::vec(N).ones(); // [-C] + arma::vec params = arma::vec(2).zeros(); // [A ; B] + + PointIterator point = point_set.begin; + for (int i = 0; i < N; ++i) { + input(i, 0) = point->x; + input(i, 1) = point->y; + std::advance(point, 1); + } + + // Find A and B coefficients from linear regression (assuming C = -1.0) + params = arma::pinv(input) * output; + + double A, B, C; + A = params(0); + B = params(1); + C = -1.0; + + // Find end points + Point p1 = *point_set.begin; + Point p2 = *point_set.end; + + Segment segment(p1, p2); + segment.point_sets.push_back(point_set); + + double D = (A * A + B * B); + + // Project end points on the line + if (D > 0.0) { + Point projected_p1, projected_p2; + + projected_p1.x = ( B * B * p1.x - A * B * p1.y - A * C) / D; + projected_p1.y = (-A * B * p1.x + A * A * p1.y - B * C) / D; + projected_p1.z = p1.z; + + projected_p2.x = ( B * B * p2.x - A * B * p2.y - A * C) / D; + projected_p2.y = (-A * B * p2.x + A * A * p2.y - B * C) / D; + projected_p2.z = p2.z; + + segment.first_point = projected_p1; + segment.last_point = projected_p2; + } + + return segment; +} + +Segment fitSegment(const std::vector& point_sets) { + static int num_calls = 0; + num_calls++; + + int N = 0; + for (PointSet ps : point_sets) + N += ps.num_points; + + assert(N >= 2); + + arma::mat input = arma::mat(N, 2).zeros(); // [x_i, y_i] + arma::vec output = arma::vec(N).ones(); // [-C] + arma::vec params = arma::vec(2).zeros(); // [A ; B] + + int n = 0; + for (PointSet ps : point_sets) { + PointIterator point = ps.begin; + for (int i = 0; i < ps.num_points; ++i) { + input(i + n, 0) = point->x; + input(i + n, 1) = point->y; + std::advance(point, 1); + } + + n += ps.num_points; + } + + // Find A and B coefficients from linear regression (assuming C = -1.0) + params = arma::pinv(input) * output; + + double A, B, C; + A = params(0); + B = params(1); + C = -1.0; + + // Find end points + Point p1 = *point_sets.front().begin; + Point p2 = *point_sets.back().end; + + Segment segment(p1, p2); + segment.point_sets = point_sets; + + double D = (A * A + B * B); + + // Project end points on the line + if (D > 0.0) { + Point projected_p1, projected_p2; + + projected_p1.x = ( B * B * p1.x - A * B * p1.y - A * C) / D; + projected_p1.y = (-A * B * p1.x + A * A * p1.y - B * C) / D; + projected_p1.z = p1.z; + + projected_p2.x = ( B * B * p2.x - A * B * p2.y - A * C) / D; + projected_p2.y = (-A * B * p2.x + A * A * p2.y - B * C) / D; + projected_p2.z = p2.z; + + segment.first_point = projected_p1; + segment.last_point = projected_p2; + } + + return segment; +} + +/* + * Returns a total best fit approximation of + * a circle based on given point set. The equation + * used for fitting is given by + * a1 * x + a2 * y + a3 = -(x^2 + y^2) + * where parameters a1, a2, a3 are obtained from + * circle equation + * (x-x0)^2 + (y-y0)^2 = r^2. + */ +Circle fitCircle(const std::list& point_set) +{ + int N = point_set.size(); + assert(N >= 3); + + arma::mat input = arma::mat(N, 3).zeros(); // [x_i, y_i, 1] + arma::vec output = arma::vec(N).zeros(); // [-(x_i^2 + y_i^2)] + arma::vec params = arma::vec(3).zeros(); // [a_1 ; a_2 ; a_3] + + int i = 0; + Point last_point; + for (const Point& point : point_set) { + input(i, 0) = point.x; + input(i, 1) = point.y; + input(i, 2) = 1.0; + + last_point = point; + + output(i) = -(pow(point.x, 2) + pow(point.y, 2)); + i++; + } + + // Find a_1, a_2 and a_3 coefficients from linear regression + params = arma::pinv(input) * output; + + Point center(-params(0) / 2.0, -params(1) / 2.0, 0., last_point.x); + double radius = sqrt((params(0) * params(0) + params(1) * params(1)) / 4.0 - params(2)); + + return Circle(center, radius); +} + +} // namespace obstacle_detector diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/kalman.h b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/kalman.h new file mode 100644 index 0000000..879af16 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/kalman.h @@ -0,0 +1,108 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include + +class KalmanFilter +{ +public: + KalmanFilter(uint dim_in, uint dim_out, uint dim_state) : l(dim_in), m(dim_out), n(dim_state) { + using arma::mat; + using arma::vec; + + A = mat(n,n).eye(); + B = mat(n,l).zeros(); + C = mat(m,n).zeros(); + + Q = mat(n,n).eye(); + R = mat(m,m).eye(); + P = mat(n,n).eye(); + + K = mat(n,m).eye(); + I = arma::eye(n,n); + + u = vec(l).zeros(); + q_pred = vec(n).zeros(); + q_est = vec(n).zeros(); + y = vec(m).zeros(); + } + + void predictState() { + q_pred = A * q_est + B * u; + P = A * P * trans(A) + Q; + } + + void correctState() { + K = P * trans(C) * inv(C * P * trans(C) + R); + q_est = q_pred + K * (y - C * q_pred); + P = (I - K * C) * P; + } + + void updateState() { + predictState(); + correctState(); + } + +public: + // System matrices: + arma::mat A; // State + arma::mat B; // Input + arma::mat C; // Output + + // Covariance matrices: + arma::mat Q; // Process + arma::mat R; // Measurement + arma::mat P; // Estimate error + + // Kalman gain matrix: + arma::mat K; + + // Identity matrix + arma::mat I; + + // Signals: + arma::vec u; // Input + arma::vec q_pred; // Predicted state + arma::vec q_est; // Estimated state + arma::vec y; // Measurement + +private: + // Dimensions: + uint l; // Input + uint m; // Output + uint n; // State +}; diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/math_utilities.h b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/math_utilities.h new file mode 100644 index 0000000..40ff5bc --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/math_utilities.h @@ -0,0 +1,116 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include "geometry_msgs/msg/point.hpp" +#include "geometry_msgs/msg/pose.hpp" +#include "geometry_msgs/msg/point32.hpp" +#include "tf2/convert.h" +#include "tf2/exceptions.h" +#include "tf2_geometry_msgs/tf2_geometry_msgs.hpp" + +#include "obstacle_detector/utilities/point.h" + +namespace obstacle_detector +{ + +inline double signum(double x) { return (x < 0.0) ? -1.0 : 1.0; } +inline double abs(double x) { return (x < 0.0) ? -x : x; } +inline double max(double x, double y) { return (x > y) ? x : y; } + +inline double length(const geometry_msgs::msg::Point& point) { + return sqrt(point.x * point.x + point.y * point.y); +} + +inline double squaredLength(const geometry_msgs::msg::Point& point) { + return point.x * point.x + point.y * point.y; +} + +inline double length(const geometry_msgs::msg::Vector3& vec) { + return sqrt(vec.x * vec.x + vec.y * vec.y); +} + +inline double squaredLength(const geometry_msgs::msg::Vector3& vec) { + return vec.x * vec.x + vec.y * vec.y; +} + +inline geometry_msgs::msg::Point transformPoint(const geometry_msgs::msg::Point& point, double x, double y, double theta) { + geometry_msgs::msg::Point p; + + p.x = point.x * cos(theta) - point.y * sin(theta) + x; + p.y = point.x * sin(theta) + point.y * cos(theta) + y; + p.z = point.z; + + return p; +} + +inline geometry_msgs::msg::Point32 transformPoint(const geometry_msgs::msg::Point32& point, double x, double y, double theta) { + geometry_msgs::msg::Point32 p; + + p.x = point.x * cos(theta) - point.y * sin(theta) + x; + p.y = point.x * sin(theta) + point.y * cos(theta) + y; + p.z = point.z; + + return p; +} + +inline Point transformPoint(const Point point, double x, double y, double theta) { + Point p; + + p.x = point.x * cos(theta) - point.y * sin(theta) + x; + p.y = point.x * sin(theta) + point.y * cos(theta) + y; + p.z = point.z; + + return p; +} + +inline Point transformPoint(const Point& point, const geometry_msgs::msg::TransformStamped& transform) { + geometry_msgs::msg::Pose pose; + pose.position.x = point.x; + pose.position.y = point.y; + pose.position.z = point.z; + tf2::doTransform(pose, pose, transform); + return {pose.position.x, pose.position.y, 0., pose.position.z}; +} + +inline bool checkPointInLimits(const geometry_msgs::msg::Point32& p, double x_min, double x_max, double y_min, double y_max) { + if ((p.x > x_max) || (p.x < x_min) || (p.y > y_max) || (p.y < y_min)) + return false; + else + return true; +} + +} // namespace obstacle_detector diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/point.h b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/point.h new file mode 100644 index 0000000..7c27afe --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/point.h @@ -0,0 +1,93 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include +#include + +namespace obstacle_detector +{ + +class Point +{ +public: + Point(double x = 0.0, double y = 0.0, double range = 0.0, double z = 0.0) : x(x), y(y), range(range), z(z) {} + Point(const Point& p) : x(p.x), y(p.y), range(p.range), z(p.z) {} + static Point fromPoolarCoords(const double r, const double phi) { return Point(r * cos(phi), r * sin(phi)); } + + double getRange() const { return range > 0.0 ? range : length(); } + double length() const { return sqrt(pow(x, 2.0) + pow(y, 2.0)); } + double lengthSquared() const { return pow(x, 2.0) + pow(y, 2.0); } + double angle() const { return atan2(y, x); } + double angleDeg() const { return 180.0 * atan2(y, x) / M_PI; } + double dot(const Point& p) const { return x * p.x + y * p.y; } + double cross(const Point& p) const { return x * p.y - y * p.x; } + + Point normalized() { return (length() > 0.0) ? *this / length() : *this; } + Point reflected(const Point& normal) const { return *this - 2.0 * normal * (normal.dot(*this)); } + Point perpendicular() const { return Point(-y, x, 0., z); } + + friend Point operator+ (const Point& p1, const Point& p2) { return Point(p1.x + p2.x, p1.y + p2.y, 0., p1.z); } + friend Point operator- (const Point& p1, const Point& p2) { return Point(p1.x - p2.x, p1.y - p2.y, 0., p1.z); } + friend Point operator* (const double f, const Point& p) { return Point(f * p.x, f * p.y, 0., p.z); } + friend Point operator* (const Point& p, const double f) { return Point(f * p.x, f * p.y, 0., p.z); } + friend Point operator/ (const Point& p, const double f) { return (f != 0.0) ? Point(p.x / f, p.y / f, 0., p.z) : Point(); } + + Point operator- () { return Point(-x, -y, 0, z); } + Point operator+ () { return Point( x, y, 0, z); } + + Point& operator= (const Point& p) { if (this != &p) { x = p.x; y = p.y; range = p.range; z = p.z;} return *this; } + Point& operator+= (const Point& p) { x += p.x; y += p.y; return *this; } + Point& operator-= (const Point& p) { x -= p.x; y -= p.y; return *this; } + + friend bool operator== (const Point& p1, const Point& p2) { return (p1.x == p2.x && p1.y == p2.y); } + friend bool operator!= (const Point& p1, const Point& p2) { return !(p1 == p2); } + friend bool operator< (const Point& p1, const Point& p2) { return (p1.lengthSquared() < p2.lengthSquared()); } + friend bool operator<= (const Point& p1, const Point& p2) { return (p1.lengthSquared() <= p2.lengthSquared()); } + friend bool operator> (const Point& p1, const Point& p2) { return (p1.lengthSquared() > p2.lengthSquared()); } + friend bool operator>= (const Point& p1, const Point& p2) { return (p1.lengthSquared() >= p2.lengthSquared()); } + friend bool operator! (const Point& p1) { return (p1.x == 0.0 && p1.y == 0.0); } + + friend std::ostream& operator<<(std::ostream& out, const Point& p) + { out << "(" << p.x << ", " << p.y << ", " << p.z << ") with range " << p.getRange() << " to origin"; return out; } + + double x; + double y; + double z; + double range = 0.0; // Distance w.r.t. lidar scan origin +}; + +} // namespace obstacle_detector diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/point_set.h b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/point_set.h new file mode 100644 index 0000000..b0ad481 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/point_set.h @@ -0,0 +1,58 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include + +#include "obstacle_detector/utilities/point.h" + +namespace obstacle_detector +{ + +typedef std::list::iterator PointIterator; + +class PointSet +{ +public: + PointSet() : num_points(0), is_visible(false) {} + + PointIterator begin, end; // The iterators point to the list of points existing somewhere else + int num_points; + bool is_visible; // The point set is not occluded by any other point set +}; + +} // namespace obstacle_detector + diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/segment.h b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/segment.h new file mode 100644 index 0000000..26ba730 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/segment.h @@ -0,0 +1,121 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include + +#include "obstacle_detector/utilities/point.h" +#include "obstacle_detector/utilities/point_set.h" + +namespace obstacle_detector +{ + +class Segment +{ +public: + Segment(const Point& p1 = Point(), const Point& p2 = Point()) { + // Swap if not counter-clockwise + if (p1.cross(p2) > 0.0) + first_point = p1, last_point = p2; + else + first_point = p2, last_point = p1; + } + + double length() const { + return (last_point - first_point).length(); + } + + double lengthSquared() const { + return (last_point - first_point).lengthSquared(); + } + + Point normal() const { + return (last_point - first_point).perpendicular().normalized(); + } + + Point projection(const Point& p) const { + Point a = last_point - first_point; + Point b = p - first_point; + return first_point + a.dot(b) * a / a.lengthSquared(); + } + + Point trueProjection(const Point& p) const { + Point a = last_point - first_point; + Point b = p - first_point; + Point c = p - last_point; + + double t = a.dot(b) / a.lengthSquared(); + + if (t < 0.0) + return (first_point); + else if (t > 1.0) + return (last_point); + else + return first_point + a.dot(b) * a / a.lengthSquared(); + } + + double distanceTo(const Point& p) const { + return (p - projection(p)).length(); + } + + double trueDistanceTo(const Point& p) const { + Point a = last_point - first_point; + Point b = p - first_point; + Point c = p - last_point; + + double t = a.dot(b) / a.lengthSquared(); + + if (t < 0.0) + return b.length(); + else if (t > 1.0) + return c.length(); + + Point projection = first_point + t * a; + return (p - projection).length(); + } + + + friend std::ostream& operator<<(std::ostream& out, const Segment& s) { + out << "p1: " << s.first_point << ", p2: " << s.last_point; + return out; + } + + Point first_point; + Point last_point; + std::vector point_sets; +}; + +} // namespace obstacle_detector diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/tracked_circle_obstacle.h b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/tracked_circle_obstacle.h new file mode 100644 index 0000000..06e20d9 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/tracked_circle_obstacle.h @@ -0,0 +1,186 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include "obstacle_detector/msg/obstacles.hpp" +#include "obstacle_detector/msg/circle_obstacle.hpp" +#include "obstacle_detector/msg/segment_obstacle.hpp" + +#include "obstacle_detector/utilities/tracked_obstacle.h" +#include "obstacle_detector/utilities/kalman.h" + +namespace obstacle_detector +{ + +class TrackedCircleObstacle { +public: + TrackedCircleObstacle(const obstacle_detector::msg::CircleObstacle& obstacle) : obstacle_(obstacle), kf_x_(0, 1, 2), kf_y_(0, 1, 2), kf_r_(0, 1, 2) { + fade_counter_ = s_fade_counter_size_; + setNewUid(); + initKF(); + } + + void predictState() { + kf_x_.predictState(); + kf_y_.predictState(); + kf_r_.predictState(); + + obstacle_.center.x = kf_x_.q_pred(0); + obstacle_.center.y = kf_y_.q_pred(0); + + obstacle_.velocity.x = kf_x_.q_pred(1); + obstacle_.velocity.y = kf_y_.q_pred(1); + + obstacle_.radius = kf_r_.q_pred(0); + + fade_counter_--; + } + + void correctState(const obstacle_detector::msg::CircleObstacle& new_obstacle) { + kf_x_.y(0) = new_obstacle.center.x; + kf_y_.y(0) = new_obstacle.center.y; + kf_r_.y(0) = new_obstacle.radius; + + kf_x_.correctState(); + kf_y_.correctState(); + kf_r_.correctState(); + + obstacle_.center.x = kf_x_.q_est(0); + obstacle_.center.y = kf_y_.q_est(0); + + obstacle_.velocity.x = kf_x_.q_est(1); + obstacle_.velocity.y = kf_y_.q_est(1); + + obstacle_.radius = kf_r_.q_est(0); + + fade_counter_ = s_fade_counter_size_; + } + + void updateState() { + kf_x_.predictState(); + kf_y_.predictState(); + kf_r_.predictState(); + + kf_x_.correctState(); + kf_y_.correctState(); + kf_r_.correctState(); + + obstacle_.center.x = kf_x_.q_est(0); + obstacle_.center.y = kf_y_.q_est(0); + + obstacle_.velocity.x = kf_x_.q_est(1); + obstacle_.velocity.y = kf_y_.q_est(1); + + obstacle_.radius = kf_r_.q_est(0); + + fade_counter_--; + } + + static void setSamplingTime(double tp) { + s_sampling_time_ = tp; + } + + static void setCounterSize(int size) { + s_fade_counter_size_ = size; + } + + static void setCovariances(double process_var, double process_rate_var, double measurement_var) { + s_process_variance_ = process_var; + s_process_rate_variance_ = process_rate_var; + s_measurement_variance_ = measurement_var; + } + + void setNewUid() { obstacle_.uid = uid_next_++; } + bool hasFaded() const { return ((fade_counter_ <= 0) ? true : false); } + const obstacle_detector::msg::CircleObstacle& getObstacle() const { return obstacle_; } + const KalmanFilter& getKFx() const { return kf_x_; } + const KalmanFilter& getKFy() const { return kf_y_; } + const KalmanFilter& getKFr() const { return kf_r_; } + +private: + void initKF() { + kf_x_.A(0, 1) = s_sampling_time_; + kf_y_.A(0, 1) = s_sampling_time_; + kf_r_.A(0, 1) = s_sampling_time_; + + kf_x_.C(0, 0) = 1.0; + kf_y_.C(0, 0) = 1.0; + kf_r_.C(0, 0) = 1.0; + + kf_x_.R(0, 0) = s_measurement_variance_; + kf_y_.R(0, 0) = s_measurement_variance_; + kf_r_.R(0, 0) = s_measurement_variance_; + + kf_x_.Q(0, 0) = s_process_variance_; + kf_r_.Q(0, 0) = s_process_variance_; + kf_y_.Q(0, 0) = s_process_variance_; + + kf_x_.Q(1, 1) = s_process_rate_variance_; + kf_y_.Q(1, 1) = s_process_rate_variance_; + kf_r_.Q(1, 1) = s_process_rate_variance_; + + kf_x_.q_pred(0) = obstacle_.center.x; + kf_r_.q_pred(0) = obstacle_.radius; + kf_y_.q_pred(0) = obstacle_.center.y; + + kf_x_.q_pred(1) = obstacle_.velocity.x; + kf_y_.q_pred(1) = obstacle_.velocity.y; + + kf_x_.q_est(0) = obstacle_.center.x; + kf_r_.q_est(0) = obstacle_.radius; + kf_y_.q_est(0) = obstacle_.center.y; + + kf_x_.q_est(1) = obstacle_.velocity.x; + kf_y_.q_est(1) = obstacle_.velocity.y; + } + + obstacle_detector::msg::CircleObstacle obstacle_; + + KalmanFilter kf_x_; + KalmanFilter kf_y_; + KalmanFilter kf_r_; + + int fade_counter_; + + // Common variables + static int s_fade_counter_size_; + static double s_sampling_time_; + static double s_process_variance_; + static double s_process_rate_variance_; + static double s_measurement_variance_; +}; + +} diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/tracked_obstacle.h b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/tracked_obstacle.h new file mode 100644 index 0000000..c0ca5d2 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/tracked_obstacle.h @@ -0,0 +1,7 @@ +#pragma once + +namespace obstacle_detector +{ + // Common variables + static uint64_t uid_next_ = 0; +}; diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/tracked_segment_obstacle.h b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/tracked_segment_obstacle.h new file mode 100644 index 0000000..fd436e6 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/include/obstacle_detector/utilities/tracked_segment_obstacle.h @@ -0,0 +1,210 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +/* + * Author: Mateusz Przybyla + */ + +#pragma once + +#include "obstacle_detector/msg/obstacles.hpp" +#include "obstacle_detector/msg/circle_obstacle.hpp" +#include "obstacle_detector/msg/segment_obstacle.hpp" + +#include "obstacle_detector/utilities/tracked_obstacle.h" +#include "obstacle_detector/utilities/kalman.h" + +namespace obstacle_detector +{ + +class TrackedSegmentObstacle { +public: + TrackedSegmentObstacle(const obstacle_detector::msg::SegmentObstacle& obstacle) : obstacle_(obstacle), kf_x1_(0, 1, 2), kf_y1_(0, 1, 2), kf_x2_(0, 1, 2), kf_y2_(0, 1, 2) { + fade_counter_ = s_fade_counter_size_; + setNewUid(); + initKF(); + } + + void predictState() { + kf_x1_.predictState(); + kf_y1_.predictState(); + kf_x2_.predictState(); + kf_y2_.predictState(); + + obstacle_.first_point.x = kf_x1_.q_pred(0); + obstacle_.first_point.y = kf_y1_.q_pred(0); + obstacle_.last_point.x = kf_x2_.q_pred(0); + obstacle_.last_point.y = kf_y2_.q_pred(0); + + obstacle_.first_velocity.x = kf_x1_.q_pred(1); + obstacle_.first_velocity.y = kf_y1_.q_pred(1); + obstacle_.last_velocity.x = kf_x2_.q_pred(1); + obstacle_.last_velocity.y = kf_y2_.q_pred(1); + + fade_counter_--; + } + + void correctState(const obstacle_detector::msg::SegmentObstacle& new_obstacle) { + kf_x1_.y(0) = new_obstacle.first_point.x; + kf_y1_.y(0) = new_obstacle.first_point.y; + kf_x2_.y(0) = new_obstacle.last_point.x; + kf_y2_.y(0) = new_obstacle.last_point.y; + + kf_x1_.correctState(); + kf_y1_.correctState(); + kf_x2_.correctState(); + kf_y2_.correctState(); + + obstacle_.first_point.x = kf_x1_.q_est(0); + obstacle_.first_point.y = kf_y1_.q_est(0); + obstacle_.last_point.x = kf_x2_.q_est(0); + obstacle_.last_point.y = kf_y2_.q_est(0); + + obstacle_.first_velocity.x = kf_x1_.q_est(1); + obstacle_.first_velocity.y = kf_y1_.q_est(1); + obstacle_.last_velocity.x = kf_x2_.q_est(1); + obstacle_.last_velocity.y = kf_y2_.q_est(1); + + fade_counter_ = s_fade_counter_size_; + } + + void updateState() { + kf_x1_.predictState(); + kf_y1_.predictState(); + kf_x2_.predictState(); + kf_y2_.predictState(); + + kf_x1_.correctState(); + kf_y1_.correctState(); + kf_x2_.correctState(); + kf_y2_.correctState(); + + obstacle_.first_point.x = kf_x1_.q_est(0); + obstacle_.first_point.y = kf_y1_.q_est(0); + obstacle_.last_point.x = kf_x2_.q_est(0); + obstacle_.last_point.y = kf_y2_.q_est(0); + + obstacle_.first_velocity.x = kf_x1_.q_est(1); + obstacle_.first_velocity.y = kf_y1_.q_est(1); + obstacle_.last_velocity.x = kf_x2_.q_est(1); + obstacle_.last_velocity.y = kf_y2_.q_est(1); + + fade_counter_--; + } + + static void setSamplingTime(double tp) { + s_sampling_time_ = tp; + } + + static void setCounterSize(int size) { + s_fade_counter_size_ = size; + } + + static void setCovariances(double process_var, double process_rate_var, double measurement_var) { + s_process_variance_ = process_var; + s_process_rate_variance_ = process_rate_var; + s_measurement_variance_ = measurement_var; + } + + void setNewUid() { obstacle_.uid = uid_next_++; } + bool hasFaded() const { return ((fade_counter_ <= 0) ? true : false); } + const obstacle_detector::msg::SegmentObstacle& getObstacle() const { return obstacle_; } + const KalmanFilter& getKFx1() const { return kf_x1_; } + const KalmanFilter& getKFy1() const { return kf_y1_; } + const KalmanFilter& getKFx2() const { return kf_x2_; } + const KalmanFilter& getKFy2() const { return kf_y2_; } + +private: + void initKF() { + kf_x1_.A(0, 1) = s_sampling_time_; + kf_y1_.A(0, 1) = s_sampling_time_; + kf_x2_.A(0, 1) = s_sampling_time_; + kf_y2_.A(0, 1) = s_sampling_time_; + + kf_x1_.C(0, 0) = 1.0; + kf_y1_.C(0, 0) = 1.0; + kf_x2_.C(0, 0) = 1.0; + kf_y2_.C(0, 0) = 1.0; + + kf_x1_.R(0, 0) = s_measurement_variance_; + kf_y1_.R(0, 0) = s_measurement_variance_; + kf_x2_.R(0, 0) = s_measurement_variance_; + kf_y2_.R(0, 0) = s_measurement_variance_; + + kf_x1_.Q(0, 0) = s_process_variance_; + kf_y1_.Q(0, 0) = s_process_variance_; + kf_x2_.Q(0, 0) = s_process_variance_; + kf_y2_.Q(0, 0) = s_process_variance_; + + kf_x1_.Q(1, 1) = s_process_rate_variance_; + kf_y1_.Q(1, 1) = s_process_rate_variance_; + kf_x2_.Q(1, 1) = s_process_rate_variance_; + kf_y2_.Q(1, 1) = s_process_rate_variance_; + + kf_x1_.q_pred(0) = obstacle_.first_point.x; + kf_y1_.q_pred(0) = obstacle_.first_point.y; + kf_x2_.q_pred(0) = obstacle_.last_point.x; + kf_y2_.q_pred(0) = obstacle_.last_point.y; + + kf_x1_.q_pred(1) = obstacle_.first_velocity.x; + kf_y1_.q_pred(1) = obstacle_.first_velocity.y; + kf_x2_.q_pred(1) = obstacle_.last_velocity.x; + kf_y2_.q_pred(1) = obstacle_.last_velocity.y; + + kf_x1_.q_est(0) = obstacle_.first_point.x; + kf_y1_.q_est(0) = obstacle_.first_point.y; + kf_x2_.q_est(0) = obstacle_.last_point.x; + kf_y2_.q_est(0) = obstacle_.last_point.y; + + kf_x1_.q_est(1) = obstacle_.first_velocity.x; + kf_y1_.q_est(1) = obstacle_.first_velocity.y; + kf_x2_.q_est(1) = obstacle_.last_velocity.x; + kf_y2_.q_est(1) = obstacle_.last_velocity.y; + } + + obstacle_detector::msg::SegmentObstacle obstacle_; + + KalmanFilter kf_x1_; + KalmanFilter kf_y1_; + KalmanFilter kf_x2_; + KalmanFilter kf_y2_; + + int fade_counter_; + + // Common variables + static int s_fade_counter_size_; + static double s_sampling_time_; + static double s_process_variance_; + static double s_process_rate_variance_; + static double s_measurement_variance_; +}; + +} diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/launch/demo.launch b/localization-devel-ws/global/obstacle_detector_2-humble-devel/launch/demo.launch new file mode 100644 index 0000000..5cebdbe --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/launch/demo.launch @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [-3.0, -2.5, -2.5, -1.0, -1.0, -0.5, 2.5, 0.2, 2.0, 4.5, 4.0, 1.5] + [1.5, 0.0, -2.5, 3.0, 1.0, -4.0, -3.0, -0.9, 0.0, 0.0, 2.0, 2.0] + [0.5, 0.5, 1.5, 0.5, 0.7, 0.5, 1.5, 0.7, 0.7, 1.0, 0.5, 1.0] + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + + + + + + + + diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/launch/nodes.launch b/localization-devel-ws/global/obstacle_detector_2-humble-devel/launch/nodes.launch new file mode 100644 index 0000000..4e87400 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/launch/nodes.launch @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [-3.0, -2.5, -2.5, -1.0, -1.0, -0.5, 2.5, 0.2, 2.0, 4.5, 4.0, 1.5] + [1.5, 0.0, -2.5, 3.0, 1.0, -4.0, -3.0, -0.9, 0.0, 0.0, 2.0, 2.0] + [0.5, 0.5, 1.5, 0.5, 0.7, 0.5, 1.5, 0.7, 0.7, 1.0, 0.5, 1.0] + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + + + + + + diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/launch/obstacle_extractor.launch b/localization-devel-ws/global/obstacle_detector_2-humble-devel/launch/obstacle_extractor.launch new file mode 100644 index 0000000..4d20620 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/launch/obstacle_extractor.launch @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/launch/obstacle_extractor_and_tracker.launch b/localization-devel-ws/global/obstacle_detector_2-humble-devel/launch/obstacle_extractor_and_tracker.launch new file mode 100644 index 0000000..7765d49 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/launch/obstacle_extractor_and_tracker.launch @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/launch/obstacle_publisher.launch b/localization-devel-ws/global/obstacle_detector_2-humble-devel/launch/obstacle_publisher.launch new file mode 100644 index 0000000..985b475 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/launch/obstacle_publisher.launch @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/msg/CircleObstacle.msg b/localization-devel-ws/global/obstacle_detector_2-humble-devel/msg/CircleObstacle.msg new file mode 100644 index 0000000..d6df499 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/msg/CircleObstacle.msg @@ -0,0 +1,7 @@ +uint64 uid # Unique identifier +geometry_msgs/Point center # Central point [m] +geometry_msgs/Vector3 velocity # Linear velocity [m/s] +float64 radius # Radius with added margin [m] +float64 true_radius # True measured radius [m] +string semclass # Semantic class +float64 confidence # Confidence in semantic class diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/msg/Obstacles.msg b/localization-devel-ws/global/obstacle_detector_2-humble-devel/msg/Obstacles.msg new file mode 100644 index 0000000..2680d9e --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/msg/Obstacles.msg @@ -0,0 +1,4 @@ +std_msgs/Header header + +obstacle_detector/SegmentObstacle[] segments +obstacle_detector/CircleObstacle[] circles diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/msg/SegmentObstacle.msg b/localization-devel-ws/global/obstacle_detector_2-humble-devel/msg/SegmentObstacle.msg new file mode 100644 index 0000000..030749d --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/msg/SegmentObstacle.msg @@ -0,0 +1,7 @@ +uint64 uid # Unique identifier +geometry_msgs/Point first_point # First point of the segment [m] +geometry_msgs/Point last_point # Last point of the segment [m] +geometry_msgs/Vector3 first_velocity # Linear velocity [m/s] +geometry_msgs/Vector3 last_velocity # Linear velocity [m/s] +string semclass # Semantic class +float64 confidence # Confidence in semantic class diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/package.xml b/localization-devel-ws/global/obstacle_detector_2-humble-devel/package.xml new file mode 100644 index 0000000..b887cac --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/package.xml @@ -0,0 +1,36 @@ + + + obstacle_detector + 1.0.0 + Detect obstacles in form of line segments and circles from 2D laser scans. + Mateusz Przybyla + Mateusz Przybyla + BSD + ros2launch + ament_cmake + + + + tf2 + tf2_geometry_msgs + tf2_ros + rviz + rclcpp + roslaunch + nodelet + std_msgs + std_srvs + sensor_msgs + geometry_msgs + laser_geometry + armadillo + nav_msgs + builtin_interfaces + rosidl_default_generators + Eigen3 + rosidl_interface_packages + + + ament_cmake + + diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/resources/ObstacleDetector.pdf b/localization-devel-ws/global/obstacle_detector_2-humble-devel/resources/ObstacleDetector.pdf new file mode 100644 index 0000000..a284f2b Binary files /dev/null and b/localization-devel-ws/global/obstacle_detector_2-humble-devel/resources/ObstacleDetector.pdf differ diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/resources/Using hokuyo_node with udev.txt b/localization-devel-ws/global/obstacle_detector_2-humble-devel/resources/Using hokuyo_node with udev.txt new file mode 100644 index 0000000..22ee924 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/resources/Using hokuyo_node with udev.txt @@ -0,0 +1,36 @@ +How to use multiple Hokuyo laser scanners and determine which is which? +---- + +When you plug in the usb Hokuyo laser scanner into computer running Ubuntu, it shows up as a link in /dev/ folders (e.g. /dev/ttyACM0). If you have two or more laser scanners connected, you cannot be sure which link is dedicated to which device. In order to make sure that the Hokuyo laser scanners connected to the computer are being properly identified, one can make use of the getID node provided by the hokuyo_node package and the udev rules. To do so, take the following steps: + +1. Create a file with an udev rule in the folder /etc/udev/rules.d: + +sudo gedit /etc/udev/rules.d/47-hokuyo.rules + +(the name and number is arbitrary but there should not be two rules with the same number). + +2. Fill the file with the following rule (just paste the following in the file): + +SUBSYSTEMS=="usb", KERNEL=="ttyACM[0-9]*", ACTION=="add", ATTRS{idVendor}=="15d1", ATTRS{idProduct}=="0000", ATTRS{manufacturer}=="Hokuyo Data Flex for USB", ATTRS{product}=="URG-Series USB Driver", MODE="666", PROGRAM="/etc/ros/run.sh hokuyo_node getID %N q", SYMLINK+="sensors/hokuyo_%c", GROUP="dialout" + +(mind that the user should be a member of dialout group: sudo adduser $USER dialout). + +3. Create a file named run.sh in the /etc/ros/ folder and provide it with executable rights: + +sudo touch /etc/ros/run.sh +sudo chmod +x /etc/ros/run.sh + +4. Fill the file with the following: + +#!/bin/sh +. /opt/ros/hydro/setup.sh +rosrun $@ + +(change the distribution of ROS to the one that you use - it was Hydromedusa in my case). + +5. Refresh the udev rules list with: + +sudo udevadm control --reload-rules + +From now on, after plugging in the usb Hokuyo laser scanner, the /dev/ folder should not only contain ttyACMn links, but also /sensors/hokuyo_ links, with which you can unambiguously determine which device are you using. Good luck! + diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/resources/obstacle_detector.rviz b/localization-devel-ws/global/obstacle_detector_2-humble-devel/resources/obstacle_detector.rviz new file mode 100644 index 0000000..cdf288c --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/resources/obstacle_detector.rviz @@ -0,0 +1,269 @@ +Panels: + - Class: rviz/Displays + Help Height: 0 + Name: Displays + Property Tree Widget: + Expanded: ~ + Splitter Ratio: 0.5 + Tree Height: 931 + - Class: rviz/Selection + Name: Selection + - Class: rviz/Views + Expanded: + - /Current View1 + Name: Views + Splitter Ratio: 0.5 + - Class: rviz/Time + Experimental: false + Name: Time + SyncMode: 0 + SyncSource: Merged PCL + - Class: Scans Merger + Name: Scans Merger + - Class: Obstacle Extractor + Name: Obstacle Extractor + - Class: Obstacle Tracker + Name: Obstacle Tracker + - Class: Obstacle Publisher + Name: Obstacle Publisher +Visualization Manager: + Class: "" + Displays: + - Alpha: 0.5 + Cell Size: 1 + Class: rviz/Grid + Color: 160; 160; 164 + Enabled: true + Line Style: + Line Width: 0.0299999993 + Value: Lines + Name: Grid + Normal Cell Count: 0 + Offset: + X: 0 + Y: 0 + Z: 0 + Plane: XY + Plane Cell Count: 10 + Reference Frame: map + Value: true + - Alpha: 1 + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 10 + Min Value: -10 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/LaserScan + Color: 255; 0; 0 + Color Transformer: FlatColor + Decay Time: 0 + Enabled: false + Invert Rainbow: false + Max Color: 255; 255; 255 + Max Intensity: 4096 + Min Color: 0; 0; 0 + Min Intensity: 0 + Name: Front Scanner + Position Transformer: XYZ + Queue Size: 10 + Selectable: false + Size (Pixels): 3 + Size (m): 0.00999999978 + Style: Points + Topic: /front_scan + Unreliable: true + Use Fixed Frame: true + Use rainbow: true + Value: false + - Alpha: 1 + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 10 + Min Value: -10 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/LaserScan + Color: 255; 255; 255 + Color Transformer: FlatColor + Decay Time: 0 + Enabled: false + Invert Rainbow: false + Max Color: 255; 255; 255 + Max Intensity: 4096 + Min Color: 0; 0; 0 + Min Intensity: 0 + Name: Rear Scanner + Position Transformer: XYZ + Queue Size: 10 + Selectable: false + Size (Pixels): 3 + Size (m): 0.00999999978 + Style: Points + Topic: /rear_scan + Unreliable: true + Use Fixed Frame: true + Use rainbow: true + Value: false + - Alpha: 1 + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 10 + Min Value: -10 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/LaserScan + Color: 85; 170; 255 + Color Transformer: FlatColor + Decay Time: 0 + Enabled: true + Invert Rainbow: false + Max Color: 255; 255; 255 + Max Intensity: 4096 + Min Color: 0; 0; 0 + Min Intensity: 0 + Name: Merged Scan + Position Transformer: XYZ + Queue Size: 10 + Selectable: false + Size (Pixels): 5 + Size (m): 0.00999999978 + Style: Points + Topic: /scan + Unreliable: true + Use Fixed Frame: true + Use rainbow: true + Value: true + - Alpha: 1 + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 10 + Min Value: -10 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/PointCloud + Color: 85; 170; 255 + Color Transformer: FlatColor + Decay Time: 0 + Enabled: true + Invert Rainbow: false + Max Color: 255; 255; 255 + Max Intensity: 4096 + Min Color: 0; 0; 0 + Min Intensity: 0 + Name: Merged PCL + Position Transformer: XYZ + Queue Size: 10 + Selectable: false + Size (Pixels): 5 + Size (m): 0.00999999978 + Style: Points + Topic: /pcl + Unreliable: true + Use Fixed Frame: true + Use rainbow: true + Value: true + - Circles color: 170; 0; 0 + Class: Obstacles + Enabled: true + Margin color: 0; 170; 0 + Name: Obstacles + Opacity: 0.75 + Segments color: 170; 170; 0 + Segments thickness: 0.0299999993 + Topic: /obstacles + Unreliable: true + Value: true + - Class: rviz/TF + Enabled: true + Frame Timeout: 15 + Frames: + All Enabled: true + front_scanner: + Value: true + map: + Value: true + rear_scanner: + Value: true + robot: + Value: true + Marker Scale: 1 + Name: TF + Show Arrows: true + Show Axes: true + Show Names: true + Tree: + map: + robot: + front_scanner: + {} + rear_scanner: + {} + Update Interval: 0 + Value: true + Enabled: true + Global Options: + Background Color: 48; 48; 48 + Fixed Frame: map + Frame Rate: 30 + Name: root + Tools: + - Class: rviz/Interact + Hide Inactive Objects: true + - Class: rviz/MoveCamera + - Class: rviz/Select + - Class: rviz/FocusCamera + - Class: rviz/Measure + - Class: rviz/SetInitialPose + Topic: /initialpose + - Class: rviz/SetGoal + Topic: /move_base_simple/goal + - Class: rviz/PublishPoint + Single click: true + Topic: /clicked_point + Value: true + Views: + Current: + Angle: 0 + Class: rviz/TopDownOrtho + Enable Stereo Rendering: + Stereo Eye Separation: 0.0599999987 + Stereo Focal Distance: 1 + Swap Stereo Eyes: false + Value: false + Name: Current View + Near Clip Distance: 0.00999999978 + Scale: 77.9355011 + Target Frame: map + Value: TopDownOrtho (rviz) + X: 0 + Y: 0 + Saved: ~ +Window Geometry: + Displays: + collapsed: false + Height: 1056 + Hide Left Dock: false + Hide Right Dock: false + Obstacle Extractor: + collapsed: false + Obstacle Publisher: + collapsed: false + Obstacle Tracker: + collapsed: false + QMainWindow State: 000000ff00000000fd00000004000000000000016a00000402fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000006400fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fc0000000000000402000002010100001dfa000000000100000004fb00000018005300630061006e00730020004d006500720067006500720100000000ffffffff0000011900fffffffb00000024004f00620073007400610063006c006500200045007800740072006100630074006f00720100000000ffffffff0000013600fffffffb00000020004f00620073007400610063006c006500200054007200610063006b006500720100000000ffffffff0000013b00fffffffb00000022004f00620073007400610063006c00650020004400650074006500630074006f00720100000000ffffffff0000000000000000000000010000016a00000402fc0200000004fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb00000022004f00620073007400610063006c00650020005200650063006f00720064006500720000000000000000d70000000000000000fc0000000000000402000001dd0100001dfa000000020100000003fb0000000a005600690065007700730100000000ffffffff0000010f00fffffffb000000100044006900730070006c0061007900730100000000ffffffff0000016a00fffffffb00000024004f00620073007400610063006c00650020005000750062006c0069007300680065007201000003ab0000016a0000016600fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e100000197000000030000073f0000003efc0100000002fb0000000800540069006d006500000000000000073f0000030000fffffffb0000000800540069006d006501000000000000045000000000000000000000045f0000040200000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730000000000ffffffff0000000000000000 + Scans Merger: + collapsed: false + Selection: + collapsed: false + Time: + collapsed: false + Views: + collapsed: false + Width: 1855 + X: 65 + Y: 24 diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/resources/play.png b/localization-devel-ws/global/obstacle_detector_2-humble-devel/resources/play.png new file mode 100644 index 0000000..1a7d96a Binary files /dev/null and b/localization-devel-ws/global/obstacle_detector_2-humble-devel/resources/play.png differ diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/resources/scans_demo.bag b/localization-devel-ws/global/obstacle_detector_2-humble-devel/resources/scans_demo.bag new file mode 100644 index 0000000..27c2fc8 Binary files /dev/null and b/localization-devel-ws/global/obstacle_detector_2-humble-devel/resources/scans_demo.bag differ diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/resources/stop.png b/localization-devel-ws/global/obstacle_detector_2-humble-devel/resources/stop.png new file mode 100644 index 0000000..dbdbb34 Binary files /dev/null and b/localization-devel-ws/global/obstacle_detector_2-humble-devel/resources/stop.png differ diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/src/nodes/obstacle_extractor_node.cpp b/localization-devel-ws/global/obstacle_detector_2-humble-devel/src/nodes/obstacle_extractor_node.cpp new file mode 100644 index 0000000..6b56435 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/src/nodes/obstacle_extractor_node.cpp @@ -0,0 +1,64 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/obstacle_extractor.h" + +using namespace obstacle_detector; + +int main(int argc, char** argv) { + + rclcpp::init(argc, argv); + auto extractor_node = rclcpp::Node::make_shared("obstacle_extractor"); + + try { + RCLCPP_INFO(extractor_node->get_logger(), "[Obstacle Extractor]: Initializing node"); + ObstacleExtractor od(extractor_node, extractor_node); + rclcpp::spin(extractor_node); + // ros::spin(); + } + catch (const char* s) { + RCLCPP_FATAL_STREAM(extractor_node->get_logger(), "[Obstacle Extractor]: " << s); + } + catch (const std::exception &exc) { + auto eptr = std::current_exception(); // capture + RCLCPP_FATAL_STREAM(extractor_node->get_logger(), "[Obstacle Extractor]: " << exc.what()); + } + catch (...){ + RCLCPP_FATAL_STREAM(extractor_node->get_logger(), "[Obstacle Extractor]: Unknown error"); + } + + rclcpp::shutdown(); + return 0; +} diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/src/nodes/obstacle_publisher_node.cpp b/localization-devel-ws/global/obstacle_detector_2-humble-devel/src/nodes/obstacle_publisher_node.cpp new file mode 100644 index 0000000..0e75e8e --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/src/nodes/obstacle_publisher_node.cpp @@ -0,0 +1,61 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/obstacle_publisher.h" + +using namespace obstacle_detector; + +int main(int argc, char** argv) { + + rclcpp::init(argc, argv); + auto publisher_node = rclcpp::Node::make_shared("obstacle_publisher"); + try { + RCLCPP_INFO(publisher_node->get_logger(), "[Obstacle Publisher]: Initializing node"); + ObstaclePublisher ot(publisher_node, publisher_node); + rclcpp::spin(publisher_node); + } + catch (const char* s) { + RCLCPP_FATAL_STREAM(publisher_node->get_logger(), "[Obstacle Publisher]: " << s); + } + catch (const std::exception &exc) { + auto eptr = std::current_exception(); // capture + RCLCPP_FATAL_STREAM(publisher_node->get_logger(), "[Obstacle Publisher]: " << exc.what()); + } + catch (...){ + RCLCPP_FATAL_STREAM(publisher_node->get_logger(), "[Obstacle Publisher]: Unknown error"); + } + rclcpp::shutdown(); + return 0; +} diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/src/nodes/obstacle_tracker_node.cpp b/localization-devel-ws/global/obstacle_detector_2-humble-devel/src/nodes/obstacle_tracker_node.cpp new file mode 100644 index 0000000..c426d2b --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/src/nodes/obstacle_tracker_node.cpp @@ -0,0 +1,60 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/obstacle_tracker.h" + +using namespace obstacle_detector; + +int main(int argc, char** argv) { + rclcpp::init(argc, argv); + auto tracker_node = rclcpp::Node::make_shared("obstacle_tracker"); + try { + RCLCPP_INFO(tracker_node->get_logger(), "[Obstacle Tracker]: Initializing node"); + ObstacleTracker ot(tracker_node, tracker_node); + rclcpp::spin(tracker_node); + } + catch (const char* s) { + RCLCPP_FATAL_STREAM(tracker_node->get_logger(), "[Obstacle Tracker]: " << s); + } + catch (const std::exception &exc) { + auto eptr = std::current_exception(); // capture + RCLCPP_FATAL_STREAM(tracker_node->get_logger(), "[Obstacle Tracker]: " << exc.what()); + } + catch (...){ + RCLCPP_FATAL_STREAM(tracker_node->get_logger(), "[Obstacle Tracker]: Unknown error"); + } + rclcpp::shutdown(); + return 0; +} diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/src/obstacle_extractor.cpp b/localization-devel-ws/global/obstacle_detector_2-humble-devel/src/obstacle_extractor.cpp new file mode 100644 index 0000000..e861689 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/src/obstacle_extractor.cpp @@ -0,0 +1,845 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/obstacle_extractor.h" +#include "obstacle_detector/utilities/figure_fitting.h" +#include "obstacle_detector/utilities/math_utilities.h" + +using namespace std; +using namespace obstacle_detector; + +ObstacleExtractor::ObstacleExtractor(std::shared_ptr nh, std::shared_ptr nh_local){ + nh_ = nh; + nh_local_ = nh_local; + p_active_ = false; +// params_srv_ = nh_->create_service("params", +// std::bind( +// &ObstacleExtractor::updateParams, +// this, +// std::placeholders::_1, +// std::placeholders::_2, +// std::placeholders::_3 +// )); + + tf_buffer_ = std::make_unique(nh_->get_clock()); + tf_listener_ = std::make_shared(*tf_buffer_); + time_last_marker_published_ = nh_->get_clock()->now() - rclcpp::Duration(10, 0); + initialize(); +} + +ObstacleExtractor::~ObstacleExtractor() { + +} + +void ObstacleExtractor::updateParamsUtil(){ + bool prev_active = p_active_; + nh_->declare_parameter("active", rclcpp::PARAMETER_BOOL); + nh_->declare_parameter("use_scan", rclcpp::PARAMETER_BOOL); + nh_->declare_parameter("use_pcl", rclcpp::PARAMETER_BOOL); + nh_->declare_parameter("use_pcl2", rclcpp::PARAMETER_BOOL); + nh_->declare_parameter("use_split_and_merge", rclcpp::PARAMETER_BOOL); + nh_->declare_parameter("circles_from_visibles", rclcpp::PARAMETER_BOOL); + nh_->declare_parameter("discard_converted_segments", rclcpp::PARAMETER_BOOL); + nh_->declare_parameter("transform_coordinates", rclcpp::PARAMETER_BOOL); + + nh_->declare_parameter("min_group_points", rclcpp::PARAMETER_INTEGER); + + nh_->declare_parameter("max_group_distance", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("distance_proportion", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("max_split_distance", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("max_merge_separation", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("max_merge_spread", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("max_circle_radius", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("radius_enlargement", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("min_x_limit", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("max_x_limit", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("min_y_limit", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("max_y_limit", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("frame_id", rclcpp::PARAMETER_STRING); + nh_->declare_parameter("max_range", rclcpp::PARAMETER_DOUBLE); + + nh_->declare_parameter("tail_threshold", rclcpp::PARAMETER_DOUBLE); + + nh_->declare_parameter("pose_array", rclcpp::PARAMETER_BOOL); + + nh_->get_parameter_or("active", p_active_, true); + nh_->get_parameter_or("use_scan", p_use_scan_, true); + nh_->get_parameter_or("use_pcl", p_use_pcl_, true); + nh_->get_parameter_or("use_pcl2", p_use_pcl_2_, true); + nh_->get_parameter_or("use_split_and_merge", p_use_split_and_merge_, true); + nh_->get_parameter_or("circles_from_visibles", p_circles_from_visibles_, true); + nh_->get_parameter_or("discard_converted_segments", p_discard_converted_segments_, true); + nh_->get_parameter_or("transform_coordinates", p_transform_coordinates_, true); + + nh_->get_parameter_or("min_group_points", p_min_group_points_, 5); + + nh_->get_parameter_or("max_group_distance", p_max_group_distance_, 0.1); + nh_->get_parameter_or("distance_proportion", p_distance_proportion_, 0.00628); + nh_->get_parameter_or("max_split_distance", p_max_split_distance_, 0.2); + nh_->get_parameter_or("max_merge_separation", p_max_merge_separation_, 0.2); + nh_->get_parameter_or("max_merge_spread", p_max_merge_spread_, 0.2); + nh_->get_parameter_or("max_circle_radius", p_max_circle_radius_, 0.6); + nh_->get_parameter_or("radius_enlargement", p_radius_enlargement_, 0.25); + nh_->get_parameter_or("min_x_limit", p_min_x_limit_, -10.0); + nh_->get_parameter_or("max_x_limit", p_max_x_limit_, 10.0); + nh_->get_parameter_or("min_y_limit", p_min_y_limit_, -10.0); + nh_->get_parameter_or("max_y_limit", p_max_y_limit_, 10.0); + nh_->get_parameter_or("frame_id", p_frame_id_, std::string{"map"}); + nh_->get_parameter_or("max_range", p_max_range_, 3.6); + + nh_->get_parameter_or("tail_threshold", p_tail_threshold, 0.5); + + nh_->get_parameter_or("pose_array", p_pose_array_, false); + + + if (p_active_ != prev_active) { + if (p_active_) { + if (p_use_scan_){ + rclcpp::QoS qos(rclcpp::KeepLast(10)); + qos.best_effort(); // ⬅️ 改成 best_effort + qos.durability_volatile(); + + scan_sub_ = nh_->create_subscription( + "scan", qos, std::bind(&ObstacleExtractor::scanCallback, this, std::placeholders::_1)); + + }else if (p_use_pcl_){ + RCLCPP_INFO_STREAM_ONCE(nh_->get_logger(), "Using PointCloud1 topic"); + pcl_sub_ = nh_->create_subscription( + "pcl", 10, std::bind(&ObstacleExtractor::pclCallback, this, std::placeholders::_1)); + }else if (p_use_pcl_2_){ + RCLCPP_INFO_STREAM_ONCE(nh_->get_logger(), "Using PointCloud2 topic"); + pcl2_sub_ = nh_->create_subscription( + "pcl2", 10, std::bind(&ObstacleExtractor::pcl2Callback, this, std::placeholders::_1)); + } + local_filter_sub = nh_->create_subscription( + "local_filter", 10, std::bind(&ObstacleExtractor::localCallback, this, std::placeholders::_1)); + + obstacles_pub_ = nh_->create_publisher("raw_obstacles", 10); + // obstacles_vis_pub_ = nh_->create_publisher("raw_obstacles_visualization", 10); + obstacles_vis_pcl_pub_ = nh_->create_publisher("raw_obstacles_visualization_pcl", 10); + if(p_pose_array_)obstacles_pose_array_pub_ = nh_->create_publisher("scan_obstacles", 10); + } + else { + // Send empty message + auto obstacles_msg = obstacle_detector::msg::Obstacles(); + obstacles_msg.header.frame_id = p_frame_id_; + obstacles_msg.header.stamp = nh_->get_clock()->now(); + obstacles_pub_->publish(obstacles_msg); + if (p_pose_array_){ + auto pose_array_msg = geometry_msgs::msg::PoseArray(); + pose_array_msg.header.frame_id = p_frame_id_; + pose_array_msg.header.stamp = nh_->get_clock()->now(); + obstacles_pose_array_pub_->publish(pose_array_msg); + } + + } + } +} + +void ObstacleExtractor::updateParams(const std::shared_ptr request_header, + const std::shared_ptr &req, + const std::shared_ptr &res) { + updateParamsUtil(); +} + +void ObstacleExtractor::scanCallback(const sensor_msgs::msg::LaserScan& scan_msg) { + base_frame_id_ = scan_msg.header.frame_id; + stamp_ = scan_msg.header.stamp; + + double phi = scan_msg.angle_min; + + double scan_twist[3]={0.0}; + for (int i=0;i<3;i++) { + scan_twist[i]=0.5*(local_twist[i]+prev_scan_twist[i]); + prev_scan_twist[i]=local_twist[i]; + } + + for (const float r : scan_msg.ranges) { + if (r >= scan_msg.range_min && r <= p_max_range_) + input_points_.push_back(distortionCorrection(scan_msg, scan_twist, r, phi)); + + phi += scan_msg.angle_increment; + } + + processPoints(); +} + +void ObstacleExtractor::pclCallback(const sensor_msgs::msg::PointCloud& pcl_msg) { + base_frame_id_ = pcl_msg.header.frame_id; + stamp_ = pcl_msg.header.stamp; + + const auto range_channel = std::find_if(pcl_msg.channels.begin(), pcl_msg.channels.end(), [] (const sensor_msgs::msg::ChannelFloat32& channel) { return channel.name == "range"; } ); + int i = 0; + for (const geometry_msgs::msg::Point32& point : pcl_msg.points) { + auto point_copy = Point(point.x, point.y, 0., point.z); + if (range_channel != pcl_msg.channels.end()) { + point_copy.range = range_channel->values.at(i++); + assert(point_copy.range >= 0.0); + RCLCPP_INFO_STREAM_ONCE(nh_->get_logger(), "Point cloud contains range information, an example value is " << point_copy.range); + } else { + RCLCPP_WARN_ONCE(nh_->get_logger(), "Point cloud does not contain range information, assuming point cloud origin aligns with lidar origin"); + } + input_points_.push_back(point_copy); + } + + processPoints(); +} + +void ObstacleExtractor::pcl2Callback(sensor_msgs::msg::PointCloud2::SharedPtr pcl_msg) { + base_frame_id_ = pcl_msg->header.frame_id; + stamp_ = pcl_msg->header.stamp; + + const size_t number_of_points = pcl_msg->height * pcl_msg->width; + sensor_msgs::PointCloud2Iterator iter_x(*pcl_msg, "x"); + sensor_msgs::PointCloud2Iterator iter_y(*pcl_msg, "y"); + sensor_msgs::PointCloud2Iterator iter_z(*pcl_msg, "z"); + for (size_t i = 0; i < number_of_points; ++i, ++iter_x, ++iter_y, ++iter_z){ + double point_x = (*iter_x); + double point_y = (*iter_y); + double point_z = (*iter_z); + auto point_copy = Point(point_x, point_y, 0., point_z); + input_points_.push_back(point_copy); + RCLCPP_WARN_ONCE(nh_->get_logger(), "Assuming point cloud origin aligns with lidar origin"); + // RCLCPP_WARN_STREAM( + // nh_->get_logger(), + // "px " << point_x << ", py " << point_y << ", pz " << point_z); + } + processPoints(); +} + +void ObstacleExtractor::localCallback(const nav_msgs::msg::Odometry& local_msg){ + local_twist[0]=local_msg.twist.twist.linear.x; + local_twist[1]=local_msg.twist.twist.linear.y; + local_twist[2]=local_msg.twist.twist.angular.z; +} + +Point ObstacleExtractor::distortionCorrection(sensor_msgs::msg::LaserScan scan_msg, double* twist, double r, double phi){ + double dt=scan_msg.scan_time; + double c=1-abs((phi-scan_msg.angle_min)/(scan_msg.angle_max-scan_msg.angle_min)); + + double d_theta=c*twist[2]*dt; + + Eigen::Matrix2d R_curr; + R_curr << cos(d_theta), -sin(d_theta), sin(d_theta), cos(d_theta); + + // Eigen::Vector2d curr2prev_in_curr_frame; + // curr2prev_in_curr_frame << (-c*twist[0]*dt), (-c*twist[1]*dt); + + Eigen::Vector2d prev2scan_in_prev_frame; + prev2scan_in_prev_frame << (r*cos(phi)), r*sin(phi); + + Eigen::Vector2d curr2scan_in_curr_frame; + curr2scan_in_curr_frame = /*curr2prev_in_curr_frame +*/ R_curr * prev2scan_in_prev_frame; + + return Point(curr2scan_in_curr_frame(0), curr2scan_in_curr_frame(1)); +} + +void ObstacleExtractor::processPoints() { + segments_.clear(); + circles_.clear(); + + groupPoints(); // Grouping points simultaneously detects segments + mergeSegments(); + + detectCircles(); + mergeCircles(); + + transformObstacles(); + publishObstacles(); + // publishVisualizationObstacles(); + publishPointCloud2Obstacles(); + + input_points_.clear(); +} + +void ObstacleExtractor::groupPoints() { + static double sin_dp = sin(2.0 * p_distance_proportion_); + + PointSet point_set; + point_set.begin = input_points_.begin(); + point_set.end = input_points_.begin(); + point_set.num_points = 1; + point_set.is_visible = true; + + for (PointIterator point = input_points_.begin()++; point != input_points_.end(); ++point) { + double range = (*point).getRange(); + double distance = (*point - *point_set.end).length(); + + if (distance < p_max_group_distance_ + range * p_distance_proportion_) { + point_set.end = point; + point_set.num_points++; + } + else { + double prev_range = (*point_set.end).getRange(); + + // Heron's equation + double p = (range + prev_range + distance) / 2.0; + double S = sqrt(p * (p - range) * (p - prev_range) * (p - distance)); + double sin_d = 2.0 * S / (range * prev_range); // Sine of angle between beams + + // TODO: This condition can be fulfilled if the point are on the opposite sides + // of the scanner (angle = 180 deg). Needs another check. + if (abs(sin_d) < sin_dp && range < prev_range) + point_set.is_visible = false; + + // tailElimination(point_set); + detectSegments(point_set); + + // Begin new point set + point_set.begin = point; + point_set.end = point; + point_set.num_points = 1; + point_set.is_visible = (abs(sin_d) > sin_dp || range < prev_range); + } + } + + // tailElimination(point_set); + detectSegments(point_set); +} +void ObstacleExtractor::tailElimination(PointSet& point_set) { + auto group_size = std::distance(point_set.begin, point_set.end); + if (group_size <= 6) return; + + PointIterator forward_iter = std::next(point_set.begin); + PointIterator inverse_iter = std::prev(point_set.end); + PointIterator middle_iter = point_set.begin; + std::advance(middle_iter, group_size / 2); + + int tail_num = 0; + int rtail_num = 0; + bool forward_done = false; + bool inverse_done = false; + + while (!forward_done || !inverse_done) { + if (!forward_done) { + if (forward_iter == middle_iter || std::next(forward_iter) == point_set.end) { + forward_done = true; + } else { + tail_num++; + auto prev = std::prev(forward_iter); + auto next = std::next(forward_iter); + + if (vectorComparison(prev->x, prev->y, + forward_iter->x, forward_iter->y, + next->x, next->y) && + tail_num >= 3) { + point_set.begin = std::next(forward_iter); + point_set.num_points -= tail_num; + group_size -= tail_num; + forward_done = true; + } else { + ++forward_iter; + } + } + } + + if (!inverse_done) { + if (inverse_iter == middle_iter || inverse_iter == point_set.begin) { + inverse_done = true; + } else { + rtail_num++; + auto prev = std::next(inverse_iter); + auto next = std::prev(inverse_iter); + + if (vectorComparison(prev->x, prev->y, + inverse_iter->x, inverse_iter->y, + next->x, next->y) && + rtail_num >= 3) { + point_set.end = std::prev(inverse_iter); + point_set.num_points -= rtail_num; + group_size -= rtail_num; + inverse_done = true; + } else { + --inverse_iter; + } + } + } + + if (group_size <= 6) break; + } +} +bool ObstacleExtractor::vectorComparison( + double prev_x, double prev_y, + double x, double y, + double next_x, double next_y){ + Eigen::Vector2d vec_prev; + Eigen::Vector2d vec_next; + + double length_prev = sqrt( pow(x - prev_x, 2) + pow(y - prev_y, 2)); + double length_next = sqrt( pow(next_x - x, 2) + pow(next_y - y, 2)); + + vec_prev << (x - prev_x) / length_prev, (y - prev_y) / length_prev; + vec_next << (next_x - x) / length_next, (next_y - y) / length_next; + + return vec_prev.dot(vec_next) < p_tail_threshold; +} + +void ObstacleExtractor::detectSegments(const PointSet& point_set) { + if (point_set.num_points < p_min_group_points_) + return; + + Segment segment(*point_set.begin, *point_set.end); // Use Iterative End Point Fit + + if (p_use_split_and_merge_) + segment = fitSegment(point_set); + + PointIterator set_divider; + double max_distance = 0.0; + double distance = 0.0; + + int split_index = 0; // Natural index of splitting point (counting from 1) + int point_index = 0; // Natural index of current point in the set + + // Seek the point of division + for (PointIterator point = point_set.begin; point != point_set.end; ++point) { + ++point_index; + + if ((distance = segment.distanceTo(*point)) >= max_distance) { + double r = (*point).getRange(); + + if (distance > p_max_split_distance_ + r * p_distance_proportion_) { + max_distance = distance; + set_divider = point; + split_index = point_index; + } + } + } + + // Split the set only if the sub-groups are not 'small' + if (max_distance > 0.0 && split_index > p_min_group_points_ && split_index < point_set.num_points - p_min_group_points_) { + set_divider = input_points_.insert(set_divider, *set_divider); // Clone the dividing point for each group + + PointSet subset1, subset2; + subset1.begin = point_set.begin; + subset1.end = set_divider; + subset1.num_points = split_index; + subset1.is_visible = point_set.is_visible; + + subset2.begin = ++set_divider; + subset2.end = point_set.end; + subset2.num_points = point_set.num_points - split_index; + subset2.is_visible = point_set.is_visible; + + detectSegments(subset1); + detectSegments(subset2); + } else { // Add the segment + if (!p_use_split_and_merge_) + segment = fitSegment(point_set); + + segments_.push_back(segment); + } +} + +void ObstacleExtractor::mergeSegments() { + for (auto i = segments_.begin(); i != segments_.end(); ++i) { + for (auto j = i; j != segments_.end(); ++j) { + Segment merged_segment; + + if (compareSegments(*i, *j, merged_segment)) { + auto temp_itr = segments_.insert(i, merged_segment); + segments_.erase(i); + segments_.erase(j); + i = --temp_itr; // Check the new segment against others + break; + } + } + } +} + +bool ObstacleExtractor::compareSegments(const Segment& s1, const Segment& s2, Segment& merged_segment) { + if (&s1 == &s2) + return false; + + // Segments must be provided counter-clockwise + if (s1.first_point.cross(s2.first_point) < 0.0) + return compareSegments(s2, s1, merged_segment); + + if (checkSegmentsProximity(s1, s2)) { + vector point_sets; + point_sets.insert(point_sets.end(), s1.point_sets.begin(), s1.point_sets.end()); + point_sets.insert(point_sets.end(), s2.point_sets.begin(), s2.point_sets.end()); + + Segment segment = fitSegment(point_sets); + + if (checkSegmentsCollinearity(segment, s1, s2)) { + merged_segment = segment; + return true; + } + } + + return false; +} + +bool ObstacleExtractor::checkSegmentsProximity(const Segment& s1, const Segment& s2) { + return (s1.trueDistanceTo(s2.first_point) < p_max_merge_separation_ || + s1.trueDistanceTo(s2.last_point) < p_max_merge_separation_ || + s2.trueDistanceTo(s1.first_point) < p_max_merge_separation_ || + s2.trueDistanceTo(s1.last_point) < p_max_merge_separation_); +} + +bool ObstacleExtractor::checkSegmentsCollinearity(const Segment& segment, const Segment& s1, const Segment& s2) { + return (segment.distanceTo(s1.first_point) < p_max_merge_spread_ && + segment.distanceTo(s1.last_point) < p_max_merge_spread_ && + segment.distanceTo(s2.first_point) < p_max_merge_spread_ && + segment.distanceTo(s2.last_point) < p_max_merge_spread_); +} + +void ObstacleExtractor::detectCircles() { + for (auto segment = segments_.begin(); segment != segments_.end(); ++segment) { + if (p_circles_from_visibles_) { + bool segment_is_visible = true; + for (const PointSet& ps : segment->point_sets) { + if (!ps.is_visible) { + segment_is_visible = false; + break; + } + } + if (!segment_is_visible) + continue; + } + + Circle circle(*segment); + circle.radius += p_radius_enlargement_; + + if (circle.radius < p_max_circle_radius_) { + circles_.push_back(circle); + + if (p_discard_converted_segments_) { + segment = segments_.erase(segment); + --segment; + } + } + } +} + +void ObstacleExtractor::mergeCircles() { + for (auto i = circles_.begin(); i != circles_.end(); ++i) { + for (auto j = i; j != circles_.end(); ++j) { + Circle merged_circle; + + if (compareCircles(*i, *j, merged_circle)) { + auto temp_itr = circles_.insert(i, merged_circle); + circles_.erase(i); + circles_.erase(j); + i = --temp_itr; + break; + } + } + } +} + +bool ObstacleExtractor::compareCircles(const Circle& c1, const Circle& c2, Circle& merged_circle) { + if (&c1 == &c2) + return false; + + // If circle c1 is fully inside c2 - merge and leave as c2 + if (c2.radius - c1.radius >= (c2.center - c1.center).length()) { + merged_circle = c2; + return true; + } + + // If circle c2 is fully inside c1 - merge and leave as c1 + if (c1.radius - c2.radius >= (c2.center - c1.center).length()) { + merged_circle = c1; + return true; + } + + // If circles intersect and are 'small' - merge + if (c1.radius + c2.radius >= (c2.center - c1.center).length()) { + Point center = c1.center + (c2.center - c1.center) * c1.radius / (c1.radius + c2.radius); + double radius = (c1.center - center).length() + c1.radius; + + Circle circle(center, radius); + circle.radius += max(c1.radius, c2.radius); + + if (circle.radius < p_max_circle_radius_) { + circle.point_sets.insert(circle.point_sets.end(), c1.point_sets.begin(), c1.point_sets.end()); + circle.point_sets.insert(circle.point_sets.end(), c2.point_sets.begin(), c2.point_sets.end()); + merged_circle = circle; + return true; + } + } + + return false; +} + +// void ObstacleExtractor::publishVisualizationObstacles(){ +// auto obstacles_vis_msg = visualization_msgs::msg::MarkerArray(); +// int id = 0; +// for (const Segment& s : segments_) { +// auto seg_marker = visualization_msgs::msg::Marker(); +// seg_marker.header.stamp = stamp_; +// seg_marker.header.frame_id = published_obstacles_frame_id_; +// seg_marker.action = visualization_msgs::msg::Marker::ADD; +// seg_marker.id = id++; +// seg_marker.ns = "raw_obstacles"; +// seg_marker.scale.x = 0.1; +// seg_marker.color.g = 1.0; +// seg_marker.color.a = 1.0; +// seg_marker.type = visualization_msgs::msg::Marker::LINE_STRIP; + +// auto seg_fp = geometry_msgs::msg::Point(); +// seg_fp.x = s.first_point.x; +// seg_fp.y = s.first_point.y; +// seg_fp.z = s.first_point.z; +// auto seg_lp = geometry_msgs::msg::Point(); +// seg_lp.x = s.last_point.x; +// seg_lp.y = s.last_point.y; +// seg_lp.z = s.last_point.z; +// seg_marker.points.push_back(seg_fp); +// seg_marker.points.push_back(seg_lp); + +// seg_marker.pose.orientation.w = 1.0; +// obstacles_vis_msg.markers.push_back(seg_marker); +// } + +// for (const Circle& c : circles_) { +// if (c.center.x > p_min_x_limit_ && c.center.x < p_max_x_limit_ && +// c.center.y > p_min_y_limit_ && c.center.y < p_max_y_limit_) { +// auto circ_marker = visualization_msgs::msg::Marker(); +// circ_marker.header.stamp = stamp_; +// circ_marker.header.frame_id = published_obstacles_frame_id_; +// circ_marker.action = visualization_msgs::msg::Marker::ADD; +// circ_marker.id = id++; +// circ_marker.ns = "raw_obstacles"; +// // fake a bigger obstacle radius for visualization purposes +// double rad = c.radius; +// if (rad < 0.2){rad = 0.2;} +// circ_marker.scale.x = rad; +// circ_marker.scale.y = rad; +// circ_marker.scale.z = 0.01; +// circ_marker.color.g = 1.0; +// circ_marker.color.a = 1.0; +// circ_marker.type = visualization_msgs::msg::Marker::CYLINDER; + +// circ_marker.pose.position.x = c.center.x; +// circ_marker.pose.position.y = c.center.y; +// circ_marker.pose.position.z = c.center.z; + +// circ_marker.pose.orientation.x = 0.0; +// circ_marker.pose.orientation.y = 0.0; +// circ_marker.pose.orientation.z = 0.0; +// circ_marker.pose.orientation.w = 1.0; +// obstacles_vis_msg.markers.push_back(circ_marker); +// } +// } + +// // clean up remaining ids +// while(id < num_active_markers_){ +// visualization_msgs::msg::Marker markerD; +// markerD.header.stamp = stamp_; +// markerD.header.frame_id = published_obstacles_frame_id_; +// markerD.ns = "raw_obstacles"; +// markerD.id = id++; +// markerD.action = visualization_msgs::msg::Marker::DELETE; +// obstacles_vis_msg.markers.push_back(markerD); +// } +// num_active_markers_ = id + 1; +// obstacles_vis_pub_->publish(obstacles_vis_msg); +// time_last_marker_published_ = nh_->get_clock()->now(); +// } + +void ObstacleExtractor::transformObstacles() { + if (p_transform_coordinates_) { + geometry_msgs::msg::TransformStamped m_transform; + if (base_frame_id_[0] == '/') base_frame_id_.erase(0, 1); + if (p_frame_id_[0] == '/') p_frame_id_.erase(0, 1); + + try { + m_transform = tf_buffer_->lookupTransform(p_frame_id_, base_frame_id_, tf2::TimePointZero); + } + catch (const tf2::TransformException & ex) { + RCLCPP_INFO( + nh_->get_logger(), "Could not transformmm %s to %s: %s", + p_frame_id_.c_str(), base_frame_id_.c_str(), ex.what()); + return; + } + + for (Segment& s : segments_) { + s.first_point = transformPoint(s.first_point, m_transform); + s.last_point = transformPoint(s.last_point, m_transform); + } + + for (Circle& c : circles_){ + c.center = transformPoint(c.center, m_transform); + } + + published_obstacles_frame_id_ = p_frame_id_; + } + else{ + published_obstacles_frame_id_ = base_frame_id_; + } + +} + +void ObstacleExtractor::publishPointCloud2Obstacles() { + sensor_msgs::msg::PointCloud2 obstacles_pcl_msg; + obstacles_pcl_msg.header.stamp = stamp_; + obstacles_pcl_msg.header.frame_id = published_obstacles_frame_id_; + obstacles_pcl_msg.height = 1; + obstacles_pcl_msg.is_dense = false; + obstacles_pcl_msg.is_bigendian = false; + + sensor_msgs::msg::PointField f_x, f_y, f_z, f_rgb; + + f_x.name = "x"; + f_x.offset = 0; + f_x.datatype = sensor_msgs::msg::PointField::FLOAT32; + f_x.count = 1; + + f_y.name = "y"; + f_y.offset = 4; + f_y.datatype = sensor_msgs::msg::PointField::FLOAT32; + f_y.count = 1; + + f_z.name = "z"; + f_z.offset = 8; + f_z.datatype = sensor_msgs::msg::PointField::FLOAT32; + f_z.count = 1; + + f_rgb.name = "rgb"; + f_rgb.offset = 12; + f_rgb.datatype = sensor_msgs::msg::PointField::FLOAT32; + f_rgb.count = 1; + + obstacles_pcl_msg.fields = {f_x, f_y, f_z, f_rgb}; + + obstacles_pcl_msg.point_step = 16; + + std::vector data; + + auto add_point = [&](float x, float y, float z, uint8_t r, uint8_t g, uint8_t b) { + uint32_t rgb_int = (r << 16) | (g << 8) | b; + float rgb; + std::memcpy(&rgb, &rgb_int, sizeof(float)); + std::array point = {x, y, z, rgb}; + for (float v : point) { + uint8_t* p = reinterpret_cast(&v); + data.insert(data.end(), p, p + sizeof(float)); + } + }; + + auto add_circle_outline = [&](const geometry_msgs::msg::Point& center, float radius, int num_points = 36) { + for (int i = 0; i < num_points; ++i) { + float theta = 2.0f * M_PI * i / num_points; + float x = center.x + radius * std::cos(theta); + float y = center.y + radius * std::sin(theta); + float z = center.z; + add_point(x, y, z, 255, 105, 180); + } + }; + + for (const Segment& s : segments_) { + add_point(s.first_point.x, s.first_point.y, s.first_point.z, 0, 0, 255); + add_point(s.last_point.x, s.last_point.y, s.last_point.z, 0, 0, 255); + } + + for (const Circle& c : circles_) { + if (c.center.x > p_min_x_limit_ && c.center.x < p_max_x_limit_ && + c.center.y > p_min_y_limit_ && c.center.y < p_max_y_limit_) { + geometry_msgs::msg::Point center; + center.x = c.center.x; + center.y = c.center.y; + center.z = c.center.z; + add_circle_outline(center, c.radius); + } + } + + obstacles_pcl_msg.data = std::move(data); + obstacles_pcl_msg.width = obstacles_pcl_msg.data.size() / obstacles_pcl_msg.point_step; + obstacles_pcl_msg.row_step = obstacles_pcl_msg.width * obstacles_pcl_msg.point_step; + + obstacles_vis_pcl_pub_->publish(obstacles_pcl_msg); +} + + +void ObstacleExtractor::publishObstacles() { + auto obstacles_msg = obstacle_detector::msg::Obstacles(); + obstacles_msg.header.stamp = stamp_; + obstacles_msg.header.frame_id = published_obstacles_frame_id_; + + for (const Segment& s : segments_) { + obstacle_detector::msg::SegmentObstacle segment; + segment.first_point.x = s.first_point.x; + segment.first_point.y = s.first_point.y; + segment.first_point.z = s.first_point.z; + segment.last_point.x = s.last_point.x; + segment.last_point.y = s.last_point.y; + segment.last_point.z = s.last_point.z; + + obstacles_msg.segments.push_back(segment); + } + + for (const Circle& c : circles_) { + if (c.center.x > p_min_x_limit_ && c.center.x < p_max_x_limit_ && + c.center.y > p_min_y_limit_ && c.center.y < p_max_y_limit_) { + obstacle_detector::msg::CircleObstacle circle; + + circle.center.x = c.center.x; + circle.center.y = c.center.y; + circle.center.z = c.center.z; + circle.velocity.x = 0.0; + circle.velocity.y = 0.0; + circle.radius = c.radius; + circle.true_radius = c.radius - p_radius_enlargement_; + + obstacles_msg.circles.push_back(circle); + } + } + obstacles_pub_->publish(obstacles_msg); + + if (p_pose_array_){ + auto obstacles_pose_array_msg = geometry_msgs::msg::PoseArray(); + obstacles_pose_array_msg.header.stamp = stamp_; + obstacles_pose_array_msg.header.frame_id = published_obstacles_frame_id_; + + for (const Circle& c : circles_) { + if (c.center.x > p_min_x_limit_ && c.center.x < p_max_x_limit_ && + c.center.y > p_min_y_limit_ && c.center.y < p_max_y_limit_) { + geometry_msgs::msg::Pose pose; + pose.position.x = c.center.x; + pose.position.y = c.center.y; + pose.position.z = c.center.z; + pose.orientation.x = 0.0; + pose.orientation.y = 0.0; + pose.orientation.z = 0.0; + pose.orientation.w = 1.0; + obstacles_pose_array_msg.poses.push_back(pose); + } + } + obstacles_pose_array_pub_->publish(obstacles_pose_array_msg); + } + +} diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/src/obstacle_publisher.cpp b/localization-devel-ws/global/obstacle_detector_2-humble-devel/src/obstacle_publisher.cpp new file mode 100644 index 0000000..b81ba72 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/src/obstacle_publisher.cpp @@ -0,0 +1,241 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/obstacle_publisher.h" + +using namespace std; +using namespace obstacle_detector; + +ObstaclePublisher::ObstaclePublisher(std::shared_ptr nh, std::shared_ptr nh_local) { + nh_ = nh; + nh_local_ = nh_local; + p_active_ = false; +// params_srv_ = nh_->create_service("params", +// std::bind( +// &ObstaclePublisher::updateParams, +// this, +// std::placeholders::_1, +// std::placeholders::_2, +// std::placeholders::_3 +// )); + + t_ = 0.0; + timer_ = nh_->create_wall_timer(1000ms, std::bind(&ObstaclePublisher::timerCallback, this)); + initialize(); +} + +ObstaclePublisher::~ObstaclePublisher() { +} + + +void ObstaclePublisher::updateParamsUtil() { + bool prev_active = p_active_; + + nh_->declare_parameter("active", rclcpp::PARAMETER_BOOL); + nh_->declare_parameter("reset", rclcpp::PARAMETER_BOOL); + // Before using compensate_robot_velocity, consider using pointcloud input from map frame instead of base_link frame + nh_->declare_parameter("fusion_example", rclcpp::PARAMETER_BOOL); + nh_->declare_parameter("fission_example", rclcpp::PARAMETER_BOOL); + nh_->declare_parameter("loop_rate", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("radius_margin", rclcpp::PARAMETER_DOUBLE); + + nh_->get_parameter_or("active", p_active_, true); + nh_->get_parameter_or("reset", p_reset_, false); + nh_->get_parameter_or("fusion_example", p_fusion_example_, false); + nh_->get_parameter_or("fission_example", p_fission_example_, false); + nh_->get_parameter_or("loop_rate", p_loop_rate_, 10.0); + nh_->get_parameter_or("radius_margin", p_radius_margin_, 0.25); + + nh_->declare_parameter("x_vector", rclcpp::PARAMETER_DOUBLE_ARRAY); + nh_->declare_parameter("y_vector", rclcpp::PARAMETER_DOUBLE_ARRAY); + nh_->declare_parameter("r_vector", rclcpp::PARAMETER_DOUBLE_ARRAY); + nh_->declare_parameter("vx_vector", rclcpp::PARAMETER_DOUBLE_ARRAY); + nh_->declare_parameter("vy_vector", rclcpp::PARAMETER_DOUBLE_ARRAY); + + nh_->get_parameter("x_vector", p_x_vector_); + nh_->get_parameter("y_vector", p_y_vector_); + nh_->get_parameter("r_vector", p_r_vector_); + nh_->get_parameter("vx_vector", p_vx_vector_); + nh_->get_parameter("vy_vector", p_vy_vector_); + + nh_->declare_parameter("frame_id", rclcpp::PARAMETER_STRING); + nh_->get_parameter_or("frame_id", p_frame_id_, string("map")); + + p_sampling_time_ = 1.0 / p_loop_rate_; + timer_ = nh_->create_wall_timer(p_sampling_time_ * 1s, std::bind(&ObstaclePublisher::timerCallback, this)); + + if (p_active_ != prev_active) { + if (p_active_) { + obstacles_pub_ = nh_->create_publisher("obstacles", 10); + } + // else { + // obstacle_pub_.shutdown(); + // timer_.stop(); + // } + } + + obstacles_.header.frame_id = p_frame_id_; + obstacles_.circles.clear(); + + if (p_x_vector_.size() != p_y_vector_.size() || p_x_vector_.size() != p_r_vector_.size() || + p_x_vector_.size() != p_vx_vector_.size() || p_x_vector_.size() != p_vy_vector_.size()) + return; + + for (int idx = 0; idx < p_x_vector_.size(); ++idx) { + obstacle_detector::msg::CircleObstacle circle; + circle.center.x = p_x_vector_[idx]; + circle.center.y = p_y_vector_[idx]; + circle.radius = p_r_vector_[idx]; + circle.true_radius = p_r_vector_[idx] - p_radius_margin_;; + + circle.velocity.x = p_vx_vector_[idx]; + circle.velocity.y = p_vy_vector_[idx]; + + obstacles_.circles.push_back(circle); + } + + if (p_reset_) + reset(); +} + +void ObstaclePublisher::updateParams(const std::shared_ptr request_header, + const std::shared_ptr &req, + const std::shared_ptr &res) { + updateParamsUtil(); +} + +void ObstaclePublisher::timerCallback() { + t_ += p_sampling_time_; + + calculateObstaclesPositions(p_sampling_time_); + + if (p_fusion_example_) + fusionExample(t_); + else if (p_fission_example_) + fissionExample(t_); + + if (obstacles_.circles.size() > 0) + publishObstacles(); +} + +void ObstaclePublisher::calculateObstaclesPositions(double dt) { + for (auto& circ : obstacles_.circles) { + circ.center.x += circ.velocity.x * dt; + circ.center.y += circ.velocity.y * dt; + } +} + +void ObstaclePublisher::fusionExample(double t) { + obstacle_detector::msg::CircleObstacle circ1, circ2; + + obstacles_.circles.clear(); + + if (t < 5.0) { + circ1.center.x = -1.20 + 0.2 * t; + circ1.center.y = 0.0; + circ1.radius = 0.20; + + circ2.center.x = 1.20 - 0.2 * t; + circ2.center.y = 0.0; + circ2.radius = 0.20; + + obstacles_.circles.push_back(circ1); + obstacles_.circles.push_back(circ2); + } + else if (t < 15.0) { + circ1.center.x = 0.0; + circ1.center.y = 0.0; + circ1.radius = 0.20 + 0.20 * exp(-(t - 5.0) / 1.0); + + obstacles_.circles.push_back(circ1); + } + else if (t > 20.0) + reset(); + + circ1.true_radius = circ1.radius; + circ2.true_radius = circ2.radius; +} + +void ObstaclePublisher::fissionExample(double t) { + obstacle_detector::msg::CircleObstacle circ1, circ2; + + obstacles_.circles.clear(); + + if (t < 5.0) { + circ1.center.x = 0.0; + circ1.center.y = 0.0; + circ1.radius = 0.20; + + obstacles_.circles.push_back(circ1); + } + else if (t < 6.0) { + circ1.center.x = 0.0; + circ1.center.y = 0.0; + circ1.radius = 0.20 + 0.20 * (1.0 - exp(-(t - 5.0) / 1.0)); + + obstacles_.circles.push_back(circ1); + } + else if (t < 16.0){ + circ1.center.x = -0.20 - 0.2 * (t - 6.0); + circ1.center.y = 0.0; + circ1.radius = 0.20; + + circ2.center.x = 0.20 + 0.2 * (t - 6.0); + circ2.center.y = 0.0; + circ2.radius = 0.20; + + obstacles_.circles.push_back(circ1); + obstacles_.circles.push_back(circ2); + } + else if (t > 20.0) + reset(); + + circ1.true_radius = circ1.radius; + circ2.true_radius = circ2.radius; +} + +void ObstaclePublisher::publishObstacles() { + obstacle_detector::msg::Obstacles obstacles_msg; + obstacles_msg = obstacles_; + + obstacles_msg.header.stamp = nh_->get_clock()->now(); + obstacles_pub_->publish(obstacles_msg); +} + +void ObstaclePublisher::reset() { + t_ = 0.0; + p_reset_ = false; +// nh_local_.setParam("reset", false); +} diff --git a/localization-devel-ws/global/obstacle_detector_2-humble-devel/src/obstacle_tracker.cpp b/localization-devel-ws/global/obstacle_detector_2-humble-devel/src/obstacle_tracker.cpp new file mode 100644 index 0000000..fe80173 --- /dev/null +++ b/localization-devel-ws/global/obstacle_detector_2-humble-devel/src/obstacle_tracker.cpp @@ -0,0 +1,890 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2017, Poznan University of Technology + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Poznan University of Technology nor the names + * of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * 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. + */ + +/* + * Author: Mateusz Przybyla + */ + +#include "obstacle_detector/obstacle_tracker.h" + +using namespace std::chrono_literals; +using namespace obstacle_detector; +using namespace arma; +using namespace std; + +ObstacleTracker::ObstacleTracker(std::shared_ptr nh, std::shared_ptr nh_local){ + nh_ = nh; + nh_local_ = nh_local; + p_active_ = false; +// params_srv_ = nh_->create_service("params", +// std::bind( +// &ObstacleTracker::updateParams, +// this, +// std::placeholders::_1, +// std::placeholders::_2, +// std::placeholders::_3 +// )); + + timer_ = nh_->create_wall_timer(1000ms, std::bind(&ObstacleTracker::timerCallback, this)); + initialize(); +} + +ObstacleTracker::~ObstacleTracker() { +} + +void ObstacleTracker::updateParamsUtil(){ + bool prev_active = p_active_; + + nh_->declare_parameter("active", rclcpp::PARAMETER_BOOL); + nh_->declare_parameter("copy_segments", rclcpp::PARAMETER_BOOL); + // Before using compensate_robot_velocity, consider using pointcloud input from map frame instead of base_link frame + nh_->declare_parameter("compensate_robot_velocity", rclcpp::PARAMETER_BOOL); + nh_->declare_parameter("sensor_rate", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("loop_rate", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("frame_id", rclcpp::PARAMETER_STRING); + + nh_->declare_parameter("tracking_duration", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("min_correspondence_cost", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("std_correspondence_dev", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("process_variance", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("process_rate_variance", rclcpp::PARAMETER_DOUBLE); + nh_->declare_parameter("measurement_variance", rclcpp::PARAMETER_DOUBLE); + + nh_->get_parameter_or("active", p_active_, true); + nh_->get_parameter_or("copy_segments", p_copy_segments_, true); + nh_->get_parameter_or("compensate_robot_velocity", p_compensate_robot_velocity_, false); + nh_->get_parameter_or("sensor_rate", p_sensor_rate_, 10.0); + nh_->get_parameter_or("loop_rate", p_loop_rate_, 100.0); + nh_->get_parameter_or("frame_id", p_frame_id_, string("map")); + + nh_->get_parameter_or("tracking_duration", p_tracking_duration_, 2.0); + nh_->get_parameter_or("min_correspondence_cost", p_min_correspondence_cost_, 0.3); + nh_->get_parameter_or("std_correspondence_dev", p_std_correspondence_dev_, 0.15); + nh_->get_parameter_or("process_variance", p_process_variance_, 0.01); + nh_->get_parameter_or("process_rate_variance", p_process_rate_variance_, 0.1); + nh_->get_parameter_or("measurement_variance", p_measurement_variance_, 1.0); + + p_sampling_time_ = 1.0 / p_loop_rate_; + obstacles_.header.frame_id = p_frame_id_; + + TrackedCircleObstacle::setSamplingTime(p_sampling_time_); + TrackedCircleObstacle::setCounterSize(static_cast(p_loop_rate_ * p_tracking_duration_)); + TrackedCircleObstacle::setCovariances(p_process_variance_, p_process_rate_variance_, p_measurement_variance_); + TrackedSegmentObstacle::setSamplingTime(p_sampling_time_); + TrackedSegmentObstacle::setCounterSize(static_cast(p_loop_rate_ * p_tracking_duration_)); + TrackedSegmentObstacle::setCovariances(p_process_variance_, p_process_rate_variance_, p_measurement_variance_); + + timer_ = nh_->create_wall_timer(p_sampling_time_ * 1s, std::bind(&ObstacleTracker::timerCallback, this)); + + if (p_active_ != prev_active) { + if (p_active_) { + if(p_compensate_robot_velocity_){ + odom_sub_ = nh_->create_subscription( + "/odom", 10, std::bind(&ObstacleTracker::odomCallback, this, std::placeholders::_1)); + } + obstacles_sub_ = nh_->create_subscription( + "raw_obstacles", 10, std::bind(&ObstacleTracker::obstaclesCallback, this, std::placeholders::_1)); + obstacles_pub_ = nh_->create_publisher("tracked_obstacles", 10); + obstacles_vis_pub_ = nh_->create_publisher("tracked_obstacles_visualization", 10); + } + else { + // Send empty message + auto obstacles_msg = obstacle_detector::msg::Obstacles(); + obstacles_msg.header.frame_id = obstacles_.header.frame_id; + obstacles_msg.header.stamp = nh_->get_clock()->now(); + obstacles_pub_->publish(obstacles_msg); + + tracked_circle_obstacles_.clear(); + untracked_circle_obstacles_.clear(); + tracked_segment_obstacles_.clear(); + untracked_segment_obstacles_.clear(); + } + } +} + +void ObstacleTracker::updateParams(const std::shared_ptr request_header, + const std::shared_ptr &req, + const std::shared_ptr &res) { + updateParamsUtil(); +} + +void ObstacleTracker::timerCallback() { + updateObstacles(); + publishObstacles(); + publishVisualizationObstacles(); +} + +void ObstacleTracker::odomCallback(const nav_msgs::msg::Odometry::ConstSharedPtr& msg) +{ + odom_ = *msg; +} + +void ObstacleTracker::obstaclesCallback(const obstacle_detector::msg::Obstacles::ConstSharedPtr& new_obstacles) { + obstaclesCallbackCircles(new_obstacles); + obstaclesCallbackSegments(new_obstacles); +} + +void ObstacleTracker::obstaclesCallbackCircles(const obstacle_detector::msg::Obstacles::ConstSharedPtr& new_obstacles) { + if (new_obstacles->circles.size() > 0){ + radius_margin_ = new_obstacles->circles[0].radius - new_obstacles->circles[0].true_radius; + } + + int N = new_obstacles->circles.size(); + int T = tracked_circle_obstacles_.size(); + int U = untracked_circle_obstacles_.size(); + + if (T + U == 0) { + untracked_circle_obstacles_.assign(new_obstacles->circles.begin(), new_obstacles->circles.end()); + return; + } + + mat cost_matrix; + calculateCostMatrix(new_obstacles->circles, cost_matrix); + + vector row_min_indices; + calculateRowMinIndices(cost_matrix, row_min_indices, T, U); + + vector col_min_indices; + calculateColMinIndices(cost_matrix, col_min_indices, T, U); + + vector used_old_obstacles; + vector used_new_obstacles; + + vector new_tracked_obstacles; + vector new_untracked_obstacles; + + // Check for fusion (only tracked obstacles) + for (int i = 0; i < T-1; ++i) { + if (fusionObstacleUsed(i, col_min_indices, used_new_obstacles, used_old_obstacles)) + continue; + + vector fusion_indices; + fusion_indices.push_back(i); + + for (int j = i+1; j < T; ++j) { + if (fusionObstaclesCorrespond(i, j, col_min_indices, used_old_obstacles)) + fusion_indices.push_back(j); + } + + if (fusion_indices.size() > 1) { + fuseObstacles(fusion_indices, col_min_indices, new_tracked_obstacles, new_obstacles); + + // Mark used old and new obstacles + used_old_obstacles.insert(used_old_obstacles.end(), fusion_indices.begin(), fusion_indices.end()); + used_new_obstacles.push_back(col_min_indices[i]); + } + } + + // Check for fission (only tracked obstacles) + for (int i = 0; i < N-1; ++i) { + if (fissionObstacleUsed(i, T, row_min_indices, used_new_obstacles, used_old_obstacles)) + continue; + + vector fission_indices; + fission_indices.push_back(i); + + for (int j = i+1; j < N; ++j) { + if (fissionObstaclesCorrespond(i, j, row_min_indices, used_new_obstacles)) + fission_indices.push_back(j); + } + + if (fission_indices.size() > 1) { + fissureObstacle(fission_indices, row_min_indices, new_tracked_obstacles, new_obstacles); + + // Mark used old and new obstacles + used_old_obstacles.push_back(row_min_indices[i]); + used_new_obstacles.insert(used_new_obstacles.end(), fission_indices.begin(), fission_indices.end()); + } + } + + // Check for other possibilities + for (int n = 0; n < N; ++n) { + if (find(used_new_obstacles.begin(), used_new_obstacles.end(), n) != used_new_obstacles.end()) + continue; + + if (row_min_indices[n] == -1) { + new_untracked_obstacles.push_back(new_obstacles->circles[n]); + } + else if (find(used_old_obstacles.begin(), used_old_obstacles.end(), row_min_indices[n]) == used_old_obstacles.end()) { + if (row_min_indices[n] >= 0 && row_min_indices[n] < T) { + tracked_circle_obstacles_[row_min_indices[n]].correctState(new_obstacles->circles[n]); + } + else if (row_min_indices[n] >= T) { + TrackedCircleObstacle to(untracked_circle_obstacles_[row_min_indices[n] - T]); + to.correctState(new_obstacles->circles[n]); + for (int i = 0; i < static_cast(p_loop_rate_ / p_sensor_rate_); ++i) + to.updateState(); + + new_tracked_obstacles.push_back(to); + } + + used_new_obstacles.push_back(n); + } + } + + // Remove tracked obstacles that are no longer existent due to fusion or fission and insert new ones + // Sort in descending order to remove from back of the list + sort(used_old_obstacles.rbegin(), used_old_obstacles.rend()); + for (int idx : used_old_obstacles){ + tracked_circle_obstacles_.erase(tracked_circle_obstacles_.begin() + idx); + } + + tracked_circle_obstacles_.insert(tracked_circle_obstacles_.end(), new_tracked_obstacles.begin(), new_tracked_obstacles.end()); + + // Remove old untracked obstacles and save new ones + untracked_circle_obstacles_.clear(); + untracked_circle_obstacles_.assign(new_untracked_obstacles.begin(), new_untracked_obstacles.end()); +} + +void ObstacleTracker::obstaclesCallbackSegments(const obstacle_detector::msg::Obstacles::ConstSharedPtr& new_obstacles) { + int N = new_obstacles->segments.size(); + int T = tracked_segment_obstacles_.size(); + int U = untracked_segment_obstacles_.size(); + + if (T + U == 0) { + untracked_segment_obstacles_.assign(new_obstacles->segments.begin(), new_obstacles->segments.end()); + return; + } + + mat cost_matrix; + calculateCostMatrix(new_obstacles->segments, cost_matrix); + + vector row_min_indices; + calculateRowMinIndices(cost_matrix, row_min_indices, T, U); + + vector col_min_indices; + calculateColMinIndices(cost_matrix, col_min_indices, T, U); + + vector used_old_obstacles; + vector used_new_obstacles; + + vector new_tracked_obstacles; + vector new_untracked_obstacles; + + // Check for fusion (only tracked obstacles) + for (int i = 0; i < T-1; ++i) { + if (fusionObstacleUsed(i, col_min_indices, used_new_obstacles, used_old_obstacles)) + continue; + + vector fusion_indices; + fusion_indices.push_back(i); + + for (int j = i+1; j < T; ++j) { + if (fusionObstaclesCorrespond(i, j, col_min_indices, used_old_obstacles)) + fusion_indices.push_back(j); + } + + if (fusion_indices.size() > 1) { + fuseObstacles(fusion_indices, col_min_indices, new_tracked_obstacles, new_obstacles); + + // Mark used old and new obstacles + used_old_obstacles.insert(used_old_obstacles.end(), fusion_indices.begin(), fusion_indices.end()); + used_new_obstacles.push_back(col_min_indices[i]); + } + } + + // Check for fission (only tracked obstacles) + for (int i = 0; i < N-1; ++i) { + if (fissionObstacleUsed(i, T, row_min_indices, used_new_obstacles, used_old_obstacles)) + continue; + + vector fission_indices; + fission_indices.push_back(i); + + for (int j = i+1; j < N; ++j) { + if (fissionObstaclesCorrespond(i, j, row_min_indices, used_new_obstacles)) + fission_indices.push_back(j); + } + + if (fission_indices.size() > 1) { + fissureObstacle(fission_indices, row_min_indices, new_tracked_obstacles, new_obstacles); + + // Mark used old and new obstacles + used_old_obstacles.push_back(row_min_indices[i]); + used_new_obstacles.insert(used_new_obstacles.end(), fission_indices.begin(), fission_indices.end()); + } + } + + // Check for other possibilities + for (int n = 0; n < N; ++n) { + if (find(used_new_obstacles.begin(), used_new_obstacles.end(), n) != used_new_obstacles.end()) + continue; + + if (row_min_indices[n] == -1) { + new_untracked_obstacles.push_back(new_obstacles->segments[n]); + } + else if (find(used_old_obstacles.begin(), used_old_obstacles.end(), row_min_indices[n]) == used_old_obstacles.end()) { + if (row_min_indices[n] >= 0 && row_min_indices[n] < T) { + tracked_segment_obstacles_[row_min_indices[n]].correctState(new_obstacles->segments[n]); + } + else if (row_min_indices[n] >= T) { + TrackedSegmentObstacle to(untracked_segment_obstacles_[row_min_indices[n] - T]); + to.correctState(new_obstacles->segments[n]); + for (int i = 0; i < static_cast(p_loop_rate_ / p_sensor_rate_); ++i) + to.updateState(); + + new_tracked_obstacles.push_back(to); + } + + used_new_obstacles.push_back(n); + } + } + + // Remove tracked obstacles that are no longer existent due to fusion or fission and insert new ones + // Sort in descending order to remove from back of the list + sort(used_old_obstacles.rbegin(), used_old_obstacles.rend()); + for (int idx : used_old_obstacles) + tracked_segment_obstacles_.erase(tracked_segment_obstacles_.begin() + idx); + + tracked_segment_obstacles_.insert(tracked_segment_obstacles_.end(), new_tracked_obstacles.begin(), new_tracked_obstacles.end()); + + // Remove old untracked obstacles and save new ones + untracked_segment_obstacles_.clear(); + untracked_segment_obstacles_.assign(new_untracked_obstacles.begin(), new_untracked_obstacles.end()); +} + +double ObstacleTracker::obstacleCostFunction(const obstacle_detector::msg::CircleObstacle& new_obstacle, const obstacle_detector::msg::CircleObstacle& old_obstacle) { + mat distribution = mat(2, 2).zeros(); + vec relative_position = vec(2).zeros(); + + double cost = 0.0; + double penalty = 1.0; + double tp = 1.0 / p_sensor_rate_; + + double direction = atan2(old_obstacle.velocity.y, old_obstacle.velocity.x); + + geometry_msgs::msg::Point new_center = transformPoint(new_obstacle.center, 0.0, 0.0, -direction); + geometry_msgs::msg::Point old_center = transformPoint(old_obstacle.center, 0.0, 0.0, -direction); + + distribution(0, 0) = pow(p_std_correspondence_dev_, 2.0) + squaredLength(old_obstacle.velocity) * pow(tp, 2.0); + distribution(1, 1) = pow(p_std_correspondence_dev_, 2.0); + + relative_position(0) = new_center.x - old_center.x - tp * length(old_obstacle.velocity); + relative_position(1) = new_center.y - old_center.y; + + cost = sqrt(pow(new_obstacle.center.x - old_obstacle.center.x, 2.0) + pow(new_obstacle.center.y - old_obstacle.center.y, 2.0) + pow(new_obstacle.radius - old_obstacle.radius, 2.0)); + + mat a = -0.5 * trans(relative_position) * distribution * relative_position; + penalty = exp(a(0, 0)); + + // TODO: Check values for cost/penalty in common situations + // return cost / penalty; + return cost / 1.0; +} + +double ObstacleTracker::obstacleCostFunction(const obstacle_detector::msg::SegmentObstacle& new_obstacle, const obstacle_detector::msg::SegmentObstacle& old_obstacle) { + return sqrt(pow(new_obstacle.first_point.x - old_obstacle.first_point.x, 2.0) + pow(new_obstacle.first_point.y - old_obstacle.first_point.y, 2.0) + pow(new_obstacle.last_point.x - old_obstacle.last_point.x, 2.0) + pow(new_obstacle.last_point.y - old_obstacle.last_point.y, 2.0)); +} + +void ObstacleTracker::calculateCostMatrix(const vector& new_obstacles, mat& cost_matrix) { + /* + * Cost between two obstacles represents their difference. + * The bigger the cost, the less similar they are. + * N rows of cost_matrix represent new obstacles. + * T+U columns of cost matrix represent old tracked and untracked obstacles. + */ + int N = new_obstacles.size(); + int T = tracked_circle_obstacles_.size(); + int U = untracked_circle_obstacles_.size(); + + cost_matrix = mat(N, T + U, fill::zeros); + + for (int n = 0; n < N; ++n) { + for (int t = 0; t < T; ++t) + cost_matrix(n, t) = obstacleCostFunction(new_obstacles[n], tracked_circle_obstacles_[t].getObstacle()); + + for (int u = 0; u < U; ++u) + cost_matrix(n, u + T) = obstacleCostFunction(new_obstacles[n], untracked_circle_obstacles_[u]); + } +} + +void ObstacleTracker::calculateCostMatrix(const vector& new_obstacles, mat& cost_matrix) { + /* + * Cost between two obstacles represents their difference. + * The bigger the cost, the less similar they are. + * N rows of cost_matrix represent new obstacles. + * T+U columns of cost matrix represent old tracked and untracked obstacles. + */ + int N = new_obstacles.size(); + int T = tracked_segment_obstacles_.size(); + int U = untracked_segment_obstacles_.size(); + + cost_matrix = mat(N, T + U, fill::zeros); + + for (int n = 0; n < N; ++n) { + for (int t = 0; t < T; ++t) + cost_matrix(n, t) = obstacleCostFunction(new_obstacles[n], tracked_segment_obstacles_[t].getObstacle()); + + for (int u = 0; u < U; ++u) + cost_matrix(n, u + T) = obstacleCostFunction(new_obstacles[n], untracked_segment_obstacles_[u]); + } +} + +void ObstacleTracker::calculateRowMinIndices(const mat& cost_matrix, vector& row_min_indices, const int T, const int U) { + /* + * Vector of row minimal indices keeps the indices of old obstacles (tracked and untracked) + * that have the minimum cost related to each of new obstacles, i.e. row_min_indices[n] + * keeps the index of old obstacle that has the minimum cost with n-th new obstacle. + */ + int N = cost_matrix.n_rows; + + row_min_indices.assign(N, -1); // Minimum index -1 means no correspondence has been found + + for (int n = 0; n < N; ++n) { + double min_cost = p_min_correspondence_cost_; + + for (int t = 0; t < T; ++t) { + if (cost_matrix(n, t) < min_cost) { + min_cost = cost_matrix(n, t); + row_min_indices[n] = t; + } + } + + for (int u = 0; u < U; ++u) { + if (cost_matrix(n, u + T) < min_cost) { + min_cost = cost_matrix(n, u + T); + row_min_indices[n] = u + T; + } + } + } +} + +void ObstacleTracker::calculateColMinIndices(const mat& cost_matrix, vector& col_min_indices, const int T, const int U) { + /* + * Vector of column minimal indices keeps the indices of new obstacles that has the minimum + * cost related to each of old (tracked and untracked) obstacles, i.e. col_min_indices[i] + * keeps the index of new obstacle that has the minimum cost with i-th old obstacle. + */ + int N = cost_matrix.n_rows; + + col_min_indices.assign(T + U, -1); // Minimum index -1 means no correspondence has been found + + for (int t = 0; t < T; ++t) { + double min_cost = p_min_correspondence_cost_; + + for (int n = 0; n < N; ++n) { + if (cost_matrix(n, t) < min_cost) { + min_cost = cost_matrix(n, t); + col_min_indices[t] = n; + } + } + } + + for (int u = 0; u < U; ++u) { + double min_cost = p_min_correspondence_cost_; + + for (int n = 0; n < N; ++n) { + if (cost_matrix(n, u + T) < min_cost) { + min_cost = cost_matrix(n, u + T); + col_min_indices[u + T] = n; + } + } + } +} + +bool ObstacleTracker::fusionObstacleUsed(const int idx, const vector &col_min_indices, const vector &used_new, const vector &used_old) { + /* + * This function returns true if: + * - idx-th old obstacle was already used + * - obstacle to which idx-th old obstacle corresponds was already used + * - there is no corresponding obstacle + */ + + return (find(used_old.begin(), used_old.end(), idx) != used_old.end() || + find(used_new.begin(), used_new.end(), col_min_indices[idx]) != used_new.end() || + col_min_indices[idx] < 0); +} + +bool ObstacleTracker::fusionObstaclesCorrespond(const int idx, const int jdx, const vector& col_min_indices, const vector& used_old) { + /* + * This function returns true if: + * - both old obstacles correspond to the same new obstacle + * - jdx-th old obstacle was not yet used + */ + + return (col_min_indices[idx] == col_min_indices[jdx] && + find(used_old.begin(), used_old.end(), jdx) == used_old.end()); +} + +bool ObstacleTracker::fissionObstacleUsed(const int idx, const int T, const vector& row_min_indices, const vector& used_new, const vector& used_old) { + /* + * This function returns true if: + * - idx-th new obstacle was already used + * - obstacle to which idx-th new obstacle corresponds was already used + * - there is no corresponding obstacle + * - obstacle to which idx-th new obstacle corresponds is untracked + */ + + return (find(used_new.begin(), used_new.end(), idx) != used_new.end() || + find(used_old.begin(), used_old.end(), row_min_indices[idx]) != used_old.end() || + row_min_indices[idx] < 0 || + row_min_indices[idx] >= T); +} + +bool ObstacleTracker::fissionObstaclesCorrespond(const int idx, const int jdx, const vector& row_min_indices, const vector& used_new) { + /* + * This function returns true if: + * - both new obstacles correspond to the same old obstacle + * - jdx-th new obstacle was not yet used + */ + + return (row_min_indices[idx] == row_min_indices[jdx] && + find(used_new.begin(), used_new.end(), jdx) == used_new.end()); +} + +void ObstacleTracker::fuseObstacles(const vector& fusion_indices, const vector &col_min_indices, + vector& new_tracked, const obstacle_detector::msg::Obstacles::ConstSharedPtr& new_obstacles) { + obstacle_detector::msg::CircleObstacle c; + + double sum_var_x = 0.0; + double sum_var_y = 0.0; + double sum_var_vx = 0.0; + double sum_var_vy = 0.0; + double sum_var_r = 0.0; + + for (int idx : fusion_indices) { + c.center.x += tracked_circle_obstacles_[idx].getObstacle().center.x / tracked_circle_obstacles_[idx].getKFx().P(0,0); + c.center.y += tracked_circle_obstacles_[idx].getObstacle().center.y / tracked_circle_obstacles_[idx].getKFy().P(0,0); + // assume z coordinate of obstacles never changes + c.center.z = tracked_circle_obstacles_[idx].getObstacle().center.z; + c.velocity.x += tracked_circle_obstacles_[idx].getObstacle().velocity.x / tracked_circle_obstacles_[idx].getKFx().P(1,1); + c.velocity.y += tracked_circle_obstacles_[idx].getObstacle().velocity.y / tracked_circle_obstacles_[idx].getKFy().P(1,1); + c.radius += tracked_circle_obstacles_[idx].getObstacle().radius / tracked_circle_obstacles_[idx].getKFr().P(0,0); + + sum_var_x += 1.0 / tracked_circle_obstacles_[idx].getKFx().P(0,0); + sum_var_y += 1.0 / tracked_circle_obstacles_[idx].getKFy().P(0,0); + sum_var_vx += 1.0 / tracked_circle_obstacles_[idx].getKFx().P(1,1); + sum_var_vy += 1.0 / tracked_circle_obstacles_[idx].getKFy().P(1,1); + sum_var_r += 1.0 / tracked_circle_obstacles_[idx].getKFr().P(0,0); + } + + c.center.x /= sum_var_x; + c.center.y /= sum_var_y; + c.velocity.x /= sum_var_vx; + c.velocity.y /= sum_var_vy; + c.radius /= sum_var_r; + + TrackedCircleObstacle to(c); + to.correctState(new_obstacles->circles[col_min_indices[fusion_indices.front()]]); + for (int i = 0; i < static_cast(p_loop_rate_ / p_sensor_rate_); ++i) + to.updateState(); + + new_tracked.push_back(to); +} + +void ObstacleTracker::fuseObstacles(const vector& fusion_indices, const vector &col_min_indices, + vector& new_tracked, const obstacle_detector::msg::Obstacles::ConstSharedPtr& new_obstacles) { + obstacle_detector::msg::SegmentObstacle c; + + double sum_var_x1 = 0.0; + double sum_var_y1 = 0.0; + double sum_var_x2 = 0.0; + double sum_var_y2 = 0.0; + double sum_var_vx1 = 0.0; + double sum_var_vy1 = 0.0; + double sum_var_vx2 = 0.0; + double sum_var_vy2 = 0.0; + + for (int idx : fusion_indices) { + c.first_point.x += tracked_segment_obstacles_[idx].getObstacle().first_point.x / tracked_segment_obstacles_[idx].getKFx1().P(0,0); + c.first_point.y += tracked_segment_obstacles_[idx].getObstacle().first_point.y / tracked_segment_obstacles_[idx].getKFy1().P(0,0); + // assume z coordinate of obstacles never changes + c.first_point.z = tracked_segment_obstacles_[idx].getObstacle().first_point.z; + c.last_point.x += tracked_segment_obstacles_[idx].getObstacle().last_point.x / tracked_segment_obstacles_[idx].getKFx2().P(0,0); + c.last_point.y += tracked_segment_obstacles_[idx].getObstacle().last_point.y / tracked_segment_obstacles_[idx].getKFy2().P(0,0); + c.last_point.z = tracked_segment_obstacles_[idx].getObstacle().last_point.z; + c.first_velocity.x += tracked_segment_obstacles_[idx].getObstacle().first_velocity.x / tracked_segment_obstacles_[idx].getKFx1().P(1,1); + c.first_velocity.y += tracked_segment_obstacles_[idx].getObstacle().first_velocity.y / tracked_segment_obstacles_[idx].getKFy1().P(1,1); + c.last_velocity.x += tracked_segment_obstacles_[idx].getObstacle().last_velocity.x / tracked_segment_obstacles_[idx].getKFx2().P(1,1); + c.last_velocity.y += tracked_segment_obstacles_[idx].getObstacle().last_velocity.y / tracked_segment_obstacles_[idx].getKFy2().P(1,1); + + sum_var_x1 += 1.0 / tracked_segment_obstacles_[idx].getKFx1().P(0,0); + sum_var_y1 += 1.0 / tracked_segment_obstacles_[idx].getKFy1().P(0,0); + sum_var_x2 += 1.0 / tracked_segment_obstacles_[idx].getKFx2().P(0,0); + sum_var_y2 += 1.0 / tracked_segment_obstacles_[idx].getKFy2().P(0,0); + sum_var_vx1 += 1.0 / tracked_segment_obstacles_[idx].getKFx1().P(1,1); + sum_var_vy1 += 1.0 / tracked_segment_obstacles_[idx].getKFy1().P(1,1); + sum_var_vx2 += 1.0 / tracked_segment_obstacles_[idx].getKFx2().P(1,1); + sum_var_vy2 += 1.0 / tracked_segment_obstacles_[idx].getKFy2().P(1,1); + } + + c.first_point.x /= sum_var_x1; + c.first_point.y /= sum_var_y1; + c.last_point.x /= sum_var_x2; + c.last_point.y /= sum_var_y2; + c.first_velocity.x /= sum_var_vx1; + c.first_velocity.y /= sum_var_vy1; + c.last_velocity.x /= sum_var_vx2; + c.last_velocity.y /= sum_var_vy2; + + TrackedSegmentObstacle to(c); + to.correctState(new_obstacles->segments[col_min_indices[fusion_indices.front()]]); + for (int i = 0; i < static_cast(p_loop_rate_ / p_sensor_rate_); ++i) + to.updateState(); + + new_tracked.push_back(to); +} + +void ObstacleTracker::fissureObstacle(const vector& fission_indices, const vector& row_min_indices, + vector& new_tracked, const obstacle_detector::msg::Obstacles::ConstSharedPtr& new_obstacles) { + // For each new obstacle taking part in fission create a tracked obstacle from the original old one and update it with the new one + for (int idx : fission_indices) { + TrackedCircleObstacle to = tracked_circle_obstacles_[row_min_indices[idx]]; + // Ensure the fissured obstacle gets a new id + to.setNewUid(); + to.correctState(new_obstacles->circles[idx]); + for (int i = 0; i < static_cast(p_loop_rate_ / p_sensor_rate_); ++i) + to.updateState(); + + new_tracked.push_back(to); + } +} + +void ObstacleTracker::fissureObstacle(const vector& fission_indices, const vector& row_min_indices, + vector& new_tracked, const obstacle_detector::msg::Obstacles::ConstSharedPtr& new_obstacles) { + // For each new obstacle taking part in fission create a tracked obstacle from the original old one and update it with the new one + for (int idx : fission_indices) { + TrackedSegmentObstacle to = tracked_segment_obstacles_[row_min_indices[idx]]; + // Ensure the fissured obstacle gets a new id + to.setNewUid(); + to.correctState(new_obstacles->segments[idx]); + for (int i = 0; i < static_cast(p_loop_rate_ / p_sensor_rate_); ++i) + to.updateState(); + + new_tracked.push_back(to); + } +} + +void ObstacleTracker::updateObstacles() { + for (int i = 0; i < tracked_circle_obstacles_.size(); ++i) { + if (!tracked_circle_obstacles_[i].hasFaded()) + tracked_circle_obstacles_[i].updateState(); + else + tracked_circle_obstacles_.erase(tracked_circle_obstacles_.begin() + i--); + } + + for (int i = 0; i < tracked_segment_obstacles_.size(); ++i) { + if (!tracked_segment_obstacles_[i].hasFaded()) + tracked_segment_obstacles_[i].updateState(); + else + tracked_segment_obstacles_.erase(tracked_segment_obstacles_.begin() + i--); + } +} + +void ObstacleTracker::publishObstacles() { + auto obstacles_msg = obstacle_detector::msg::Obstacles(); + + obstacles_.circles.clear(); + obstacles_.segments.clear(); + + for (auto& tracked_circle_obstacle : tracked_circle_obstacles_) { + obstacle_detector::msg::CircleObstacle ob = tracked_circle_obstacle.getObstacle(); + ob.true_radius = ob.radius - radius_margin_; + // Compensate robot velocity from obstacle velocity + // Velocities are in robot's frame, x forward y leftwards + if (p_compensate_robot_velocity_) + { + double distance = sqrt(pow(ob.center.x, 2) + pow(ob.center.y, 2)); + double angle = atan2(ob.center.y, ob.center.x); + ob.velocity.x += odom_.twist.twist.linear.x - odom_.twist.twist.angular.z * distance * sin(angle); + ob.velocity.y += odom_.twist.twist.linear.y + odom_.twist.twist.angular.z * distance * cos(angle); + } + obstacles_.circles.push_back(ob); + } + for (auto& tracked_segment_obstacle : tracked_segment_obstacles_) { + obstacle_detector::msg::SegmentObstacle ob = tracked_segment_obstacle.getObstacle(); + // Compensate robot velocity from obstacle velocity + // Velocities are in robot's frame, x forward y leftwards + if (p_compensate_robot_velocity_) + { + double distance_first = sqrt(pow(ob.first_point.x, 2) + pow(ob.first_point.y, 2)); + double distance_last = sqrt(pow(ob.last_point.x, 2) + pow(ob.last_point.y, 2)); + double angle_first = atan2(ob.first_point.y, ob.first_point.x); + double angle_last = atan2(ob.last_point.y, ob.last_point.x); + ob.first_velocity.x += odom_.twist.twist.linear.x - odom_.twist.twist.angular.z * distance_first * sin(angle_first); + ob.first_velocity.y += odom_.twist.twist.linear.y + odom_.twist.twist.angular.z * distance_first * cos(angle_first); + ob.last_velocity.x += odom_.twist.twist.linear.x - odom_.twist.twist.angular.z * distance_last * sin(angle_last); + ob.last_velocity.y += odom_.twist.twist.linear.y + odom_.twist.twist.angular.z * distance_last * cos(angle_last); + } + obstacles_.segments.push_back(ob); + } + + obstacles_msg = obstacles_; + obstacles_msg.header.stamp = nh_->get_clock()->now(); + + obstacles_pub_->publish(obstacles_msg); +} + + +visualization_msgs::msg::Marker ObstacleTracker::getMarkerBase(uid_t uid){ + visualization_msgs::msg::Marker marker; + marker.header.stamp = nh_->get_clock()->now(); + marker.header.frame_id = obstacles_.header.frame_id; + marker.id = uid; + marker.lifetime = rclcpp::Duration(p_sampling_time_ * 1s); + + marker.color.r = ((int)((uid + 1) * 0.2799979960764232 * 10000)%10) * 0.1; + marker.color.g = ((int)((uid + 1) * 0.18779357508452654 * 10000)%10) * 0.1; + marker.color.b = ((int)((uid + 1) * 0.5754993762271469 * 10000)%10) * 0.1; + marker.color.a = 1.0; + + marker.pose.orientation.x = 0.0; + marker.pose.orientation.y = 0.0; + marker.pose.orientation.z = 0.0; + marker.pose.orientation.w = 1.0; + + marker.action = visualization_msgs::msg::Marker::ADD; + return marker; +} + +visualization_msgs::msg::Marker ObstacleTracker::getMarkerCircle(obstacle_detector::msg::CircleObstacle& ob){ + auto circ_marker = getMarkerBase(ob.uid); + circ_marker.ns = "tracked_obstacles_blobs"; + auto scale = ob.true_radius; + // debug: min scale for better visualization + if (scale < 0.2) scale = 0.2; + circ_marker.scale.x = circ_marker.scale.y = scale; + circ_marker.scale.z = 0.01; + circ_marker.type = visualization_msgs::msg::Marker::CYLINDER; + + circ_marker.pose.position.x = ob.center.x; + circ_marker.pose.position.y = ob.center.y; + // tracked obstacles are a bit higher than the raw ones, so that they can be visualized together + circ_marker.pose.position.z = ob.center.z + 0.1; + return circ_marker; +} + +visualization_msgs::msg::Marker ObstacleTracker::getMarkerVelocityArrow(uid_t uid, + double px, + double py, + double vx, + double vy){ + auto arrow_marker = getMarkerBase(uid); + arrow_marker.ns = "tracked_obstacles_velocities"; + arrow_marker.type = visualization_msgs::msg::Marker::ARROW; + arrow_marker.scale.y = 0.07; // width + arrow_marker.scale.z = 0.01; // height + arrow_marker.color.r = arrow_marker.color.g = arrow_marker.color.b = arrow_marker.color.a = 1.0; + + arrow_marker.pose.position.x = px; + arrow_marker.pose.position.y = py; + double vel_magnitude = std::sqrt(vx*vx + vy*vy); + double alpha = std::atan2(vy, vx); + // assume rotation around the z-axis (0, 0, 1) + arrow_marker.scale.x = vel_magnitude; + arrow_marker.pose.orientation.z = std::sin(alpha/2); + arrow_marker.pose.orientation.w = std::cos(alpha/2); + return arrow_marker; +} + +visualization_msgs::msg::Marker ObstacleTracker::getMarkerSegment(obstacle_detector::msg::SegmentObstacle& ob){ + auto seg_marker = getMarkerBase(ob.uid); + seg_marker.ns = "tracked_obstacles_blobs"; + seg_marker.scale.x = 0.1; + seg_marker.type = visualization_msgs::msg::Marker::LINE_STRIP; + + auto seg_fp = geometry_msgs::msg::Point(); + seg_fp.x = ob.first_point.x; + seg_fp.y = ob.first_point.y; + seg_fp.z = ob.first_point.z + 0.1; + auto seg_lp = geometry_msgs::msg::Point(); + seg_lp.x = ob.last_point.x; + seg_lp.y = ob.last_point.y; + seg_lp.z = ob.last_point.z + 0.1; + seg_marker.points.push_back(seg_fp); + seg_marker.points.push_back(seg_lp); + return seg_marker; +} + +visualization_msgs::msg::Marker ObstacleTracker::getMarkerText(uid_t uid){ + auto text_marker = getMarkerBase(uid); + text_marker.ns = "tracked_obstacles_ids"; + text_marker.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING; + text_marker.text = std::to_string(uid); + text_marker.scale.x = text_marker.scale.y = text_marker.scale.z = 0.2; + text_marker.color.r = text_marker.color.g = text_marker.color.b = text_marker.color.a = 1.0; + // text id is above marker + text_marker.pose.position.z = 0.2; + return text_marker; +} + +void ObstacleTracker::publishVisualizationObstacles() { + auto obstacles_vis_msg = visualization_msgs::msg::MarkerArray(); + + //clean up previous markers + auto marker_d = getMarkerBase(0); + marker_d.action = visualization_msgs::msg::Marker::DELETEALL; + obstacles_vis_msg.markers.push_back(marker_d); + + for (auto& tracked_circle_obstacle : tracked_circle_obstacles_) { + obstacle_detector::msg::CircleObstacle ob = tracked_circle_obstacle.getObstacle(); + auto circ_marker = getMarkerCircle(ob); + auto text_marker = getMarkerText(ob.uid); + text_marker.pose.position.z += ob.center.z; + text_marker.pose.position.x = ob.center.x; + text_marker.pose.position.y = ob.center.y; + auto arrow_vel = getMarkerVelocityArrow(ob.uid, ob.center.x, ob.center.y, ob.velocity.x, ob.velocity.y); + arrow_vel.pose.position.z += ob.center.z; + obstacles_vis_msg.markers.push_back(circ_marker); + obstacles_vis_msg.markers.push_back(text_marker); + obstacles_vis_msg.markers.push_back(arrow_vel); + } + + for (auto& tracked_segment_obstacle : tracked_segment_obstacles_) { + obstacle_detector::msg::SegmentObstacle ob = tracked_segment_obstacle.getObstacle(); + auto seg_marker = getMarkerSegment(ob); + auto text_marker = getMarkerText(ob.uid); + double px = (ob.first_point.x + ob.last_point.x)/2.0; + double py = (ob.first_point.y + ob.last_point.y)/2.0; + double vx = (ob.first_velocity.x + ob.last_velocity.x)/2.0; + double vy = (ob.first_velocity.y + ob.last_velocity.y)/2.0; + text_marker.pose.position.z += ob.first_point.z; + text_marker.pose.position.x = px; + text_marker.pose.position.y = py; + auto arrow_vel = getMarkerVelocityArrow(ob.uid, px, py, vx, vy); + arrow_vel.pose.position.z += ob.first_point.z; + obstacles_vis_msg.markers.push_back(seg_marker); + obstacles_vis_msg.markers.push_back(text_marker); + } + obstacles_vis_pub_->publish(obstacles_vis_msg); +} + +// Ugly initialization of static members of tracked obstacles... +int TrackedCircleObstacle::s_fade_counter_size_ = 0; +double TrackedCircleObstacle::s_sampling_time_ = 100.0; +double TrackedCircleObstacle::s_process_variance_ = 0.0; +double TrackedCircleObstacle::s_process_rate_variance_ = 0.0; +double TrackedCircleObstacle::s_measurement_variance_ = 0.0; +int TrackedSegmentObstacle::s_fade_counter_size_ = 0; +double TrackedSegmentObstacle::s_sampling_time_ = 100.0; +double TrackedSegmentObstacle::s_process_variance_ = 0.0; +double TrackedSegmentObstacle::s_process_rate_variance_ = 0.0; +double TrackedSegmentObstacle::s_measurement_variance_ = 0.0; diff --git a/localization-devel-ws/health_check.sh b/localization-devel-ws/health_check.sh new file mode 100755 index 0000000..7c58be6 --- /dev/null +++ b/localization-devel-ws/health_check.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# Function to handle termination signals +cleanup() { + echo "Terminating script..." + exit 0 +} + +# Trap termination signals +trap cleanup SIGINT SIGTERM + +while true; do + # local_filer is the most important topic + topic="/local_filter" + output=$(timeout 2s ros2 topic hz "$topic" 2>&1) + if [[ "$output" == *"WARNING: topic [$topic] does not appear to be published yet"* ]]; then + echo "WARNING: topic [$topic] does not appear to be published yet. Checking sensors..." + # check wheel sensor + topic="/driving_duaiduaiduai" + output=$(timeout 2s ros2 topic hz "$topic" 2>&1) + if [[ "$output" == *"WARNING: topic [$topic] does not appear to be published yet"* ]]; then + echo "WARNING: topic [$topic] does not appear to be published yet. Please re-plug the wheel sensor." + fi + # check imu + topic="/imu/data_raw" + output=$(timeout 2s ros2 topic hz "$topic" 2>&1) + if [[ "$output" == *"WARNING: topic [$topic] does not appear to be published yet"* ]]; then + echo "WARNING: topic [$topic] does not appear to be published yet. Please re-plug the imu sensor, or check with 'lsusb'" + fi + fi + + # check /lidar_pose, if it is not published for 5 seconds, prompt the user to check whether the initial pose is right, or check LiDAR connection + # use topic echo, if any topic comes in, it's fine, if there's no topic, give warning + topic="/lidar_pose" + output=$(timeout 5s ros2 topic echo "$topic" 2>&1) + if [[ "$output" == *"data"* ]]; then + echo "LiDAR is working fine." + else echo "WARNING: topic [$topic] has no new msg. Please check the initial pose or LiDAR connection." + fi + sleep 5 +done \ No newline at end of file diff --git a/localization-devel-ws/healthcheck/healthcheck/__init__.py b/localization-devel-ws/healthcheck/healthcheck/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/localization-devel-ws/healthcheck/healthcheck/healthcheck.py b/localization-devel-ws/healthcheck/healthcheck/healthcheck.py new file mode 100644 index 0000000..a70cd43 --- /dev/null +++ b/localization-devel-ws/healthcheck/healthcheck/healthcheck.py @@ -0,0 +1,601 @@ +import rclpy +from rclpy.node import Node +from geometry_msgs.msg import PoseWithCovarianceStamped, PoseStamped, Point +from sensor_msgs.msg import Imu +from tf2_ros.buffer import Buffer +from tf2_ros.transform_listener import TransformListener +import math +import numpy as np +from datetime import datetime # Import for date and time +import os # Import for file operations + +from healthcheck.ready_signal_template import ReadySignal +import json # Import for JSON operations + + +def rpy_from_quaternion(x, y, z, w): + # yaw (z-axis rotation) + siny_cosp = 2 * (w * z + x * y) + cosy_cosp = 1 - 2 * (y * y + z * z) + yaw = math.atan2(siny_cosp, cosy_cosp) + + return yaw + +class HealthCheckNode(Node): + def __init__(self): + super().__init__('healthcheck_node') + + # Parameters V + self.declare_parameter('robot_frame_id', 'base_footprint') + self.declare_parameter('map_frame_id', 'map') + self.declare_parameter('rival_frame_id', 'rival/base_footprint') + self.declare_parameter('lidar_frame_id', 'laser') + # lidar param: [P_pred_linear, P_pred_angular, likelihood_threshold] + self.declare_parameter('lidar_param_position', [0.2, 20.0, 0.8]) # [34cm, 170deg, 80%] take position only, set rotation free. we might not need this now + self.declare_parameter('lidar_param_running', [0.1, 1.0, 0.8]) # [20cm, 35deg, 80%] + self.declare_parameter('lidar_param_rotate', [0.5, 1.0, 0.8]) # [40cm, 35deg, 80%] take rotation only, set position free. + + self.p_robot_frame_id = self.get_parameter('robot_frame_id').get_parameter_value().string_value + self.p_map_frame_id = self.get_parameter('map_frame_id').get_parameter_value().string_value + self.p_rival_frame_id = self.get_parameter('rival_frame_id').get_parameter_value().string_value + self.p_lidar_frame_id = self.get_parameter('lidar_frame_id').get_parameter_value().string_value + self.p_lidar_param_rotate = self.get_parameter('lidar_param_rotate').get_parameter_value().double_array_value + self.p_lidar_param_running = self.get_parameter('lidar_param_running').get_parameter_value().double_array_value + self.p_lidar_param_position = self.get_parameter('lidar_param_position').get_parameter_value().double_array_value + + self.get_init = False + self.odom_init = False + self.prev_camera_pose = None + + # Subscribers: final_pose, local_filter, lidar_pose, imu/data_cov, odom2map, rival/final_pose V + self.subscription = self.create_subscription( + PoseStamped, + 'odom2map', + self.odom2map_callback, + 10 + ) + self.subscription = self.create_subscription( + PoseWithCovarianceStamped, + 'lidar_pose', + self.lidar_pose_callback, + 10 + ) + self.subscription =self.create_subscription( + PoseStamped, + '/vision/aruco/robot/single/average_pose', + self.camera_pose_callback, + 10 + ) + self.subscription = self.create_subscription( + PoseWithCovarianceStamped, + 'initialpose', + self.init_pose_callback, + 10 + ) + self.subscription = self.create_subscription( + Imu, + 'imu/data_cov', + self.imu_cov_callback, + 10 + ) + self.subscription # prevent unused variable warning + + self.init_pub = self.create_publisher( + PoseWithCovarianceStamped, + 'initial_pose', + 10 + ) + self.camera_pose_pub = self.create_publisher( + PoseStamped, + 'camera_pose', + 10 + ) + self.lidar_param_pub = self.create_publisher( + Point, + 'lidar_param_update', + 10 + ) + + self.is_localization_ok = False + self.signal_timer = self.create_timer(0.1, self.signal_to_main) + # TF buffer + self.tf_buffer = Buffer() + self.tf_listener = TransformListener(self.tf_buffer, self) + + # Read the user input from button file + self.read_button() + + # # Create health report file V + self.create_health_report_file() + + # for main communication + self.ready_signal = ReadySignal() + + self.check_localization_ok() + + # Timer for health check (3 seconds interval) V + self.timer = self.create_timer(3.0, self.health_check_timer_callback) + self.timer2 = self.create_timer(1.0, self.check_final_pose) + self.timer3 = self.create_timer(0.1, self.sensor_check) + + self.wheel_slip_first = True + + # for slip estimation + self.slip_values = [] + self.new_lidar = False + self.new_odom = False + + self.point_msg = Point() + + def read_button(self): + """ + Read /home/user/share/data/button.json, detect which start-button is + pressed, and publish the corresponding initial pose. + """ + button_file_path = '/home/user/share/data/button.json' + + # ---------- 1. Load the file ---------- + try: + with open(button_file_path, 'r') as fp: + btn_data = json.load(fp) + except (OSError, json.JSONDecodeError) as e: + self.get_logger().error(f"[read_button] Cannot read {button_file_path}: {e}") + return + + states = btn_data.get("states", {}) + if not states: + self.get_logger().warn("[read_button] No 'states' field in JSON") + return + + # ---------- 2. Find the first button that is True ---------- + pressed_id = next((int(k) for k, v in states.items() if v), None) + if pressed_id is None: + self.get_logger().info("[read_button] No button is pressed. waiting for initial pose...") + return + + # ---------- 3. Map button id → (x, y, yaw) ---------- + # mind blue/yellow, panda or raccoon + start_lookup = { # x, y, z, x, y, z, w + 0: (1.20, 0.20, 0.00, 0.00, 0.00, 0.707, 0.707), + 1: (0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 1.00), + 10: (0.35, 1.70, 0.00, 0.00, 0.00, 0.707, -0.707), + 11: (2.70, 0.90, 0.00, 0.00, 0.00, 1.00, 0.00), + # 13: (1.28, 0.30, 0.00, 0.00, 0.00, 0.707, 0.707), # for Panda + 13: (1.28, 0.30, 0.00, 0.00, 0.00, 1.00, 0.00), # for Raccoon + # team blue + 19: (2.65, 1.70, 0.00, 0.00, 0.00, 0.707, -0.707), + 15: (0.30, 0.90, 0.00, 0.00, 0.00, 0.00, 1.00), + 17: (1.72, 0.30, 0.00, 0.00, 0.00, 0.707, 0.707), + } + if pressed_id not in start_lookup: + self.get_logger().error(f"[read_button] Button {pressed_id} not in lookup table") + return + + # wait until the ekf is launched + self.check_tf_ok() + self.get_init = False + self.odom_init = False + # ---------- 4. Build & publish initial pose ---------- + init_msg = PoseWithCovarianceStamped() + init_msg.header.stamp = self.get_clock().now().to_msg() + init_msg.header.frame_id = self.p_map_frame_id + init_msg.pose.pose.position.x = start_lookup[pressed_id][0] + init_msg.pose.pose.position.y = start_lookup[pressed_id][1] + init_msg.pose.pose.position.z = 0.0 + init_msg.pose.pose.orientation.x = start_lookup[pressed_id][3] + init_msg.pose.pose.orientation.y = start_lookup[pressed_id][4] + init_msg.pose.pose.orientation.z = start_lookup[pressed_id][5] + init_msg.pose.pose.orientation.w = start_lookup[pressed_id][6] + + self.init_pub.publish(init_msg) # should be published after ekf is launched + self.initial_pose = init_msg + + yaw = rpy_from_quaternion( + start_lookup[pressed_id][3], + start_lookup[pressed_id][4], + start_lookup[pressed_id][5], + start_lookup[pressed_id][6] + ) + self.get_logger().info(f"[read_button] Init pose published from button {pressed_id} " + f"→ ({start_lookup[pressed_id][0]:.2f}, {start_lookup[pressed_id][1]:.2f}, {yaw:.2f} rad)") + + # Kick-start the localisation pipeline + self.check_localization_ok() + + def create_health_report_file(self): + # Generate the filename based on the current date and timeV + now = datetime.now() + filename = now.strftime("%Y-%m-%d_%H-%M-%S_health_report.txt") + report_dir = '/home/user/localization-ws/src/localization-devel-ws/healthcheck/report' + + # Ensure the directory exists + os.makedirs(report_dir, exist_ok=True) + + # Full path to the report file + self.report_file_path = os.path.join(report_dir, filename) + + # Create the file and write the header + with open(self.report_file_path, 'w') as file: + file.write("Health Report\n") + file.write(f"Generated on: {now.strftime('%Y-%m-%d %H:%M:%S')}\n") + file.write("=" * 40 + "\n") + self.get_logger().info(f"Health report file created: {self.report_file_path}") + + def check_localization_ok(self): #onlyOnce + # Conditions to satisfy for localization ok + # 1. TF is published and without error; base_footprint and rival/base_footprint are published + self.check_tf_ok() + # 2. local_filter, odom2map and imu/data_cov are published(what;s the difference oddom2map and local_filter? can they be merged?) + if hasattr(self, 'odom2map') and hasattr(self, 'local_filter') and hasattr(self, 'imu_cov'): + self.get_logger().info("odom2map local_filter, and imu are published") + # 3. either initial pose or camera pose is published + if not hasattr(self, 'initial_pose') and not hasattr(self, 'camera_pose'): + self.get_logger().warn("need inital or camera pose to initialize...") + return False + if not hasattr(self, 'lidar_pose'): + # self.get_logger().warn("lidar_pose not available") + return False + # 4. lidar_pose is published and agree with either initial pose or camera pose (TODO) + if hasattr(self, 'lidar_pose') and hasattr(self, 'initial_pose'): + if np.linalg.norm( + np.array([self.lidar_pose.pose.pose.position.x - self.initial_pose.pose.pose.position.x, + self.lidar_pose.pose.pose.position.y - self.initial_pose.pose.pose.position.y]) + ) >0.5: + #recall lidar localization to calculate + self.get_logger().warn("lidar_pose and initial pose have a large difference, republishing initial pose") + self.init_pub.publish(self.initial_pose) + return False + elif hasattr(self, 'lidar_pose') and hasattr(self, 'camera_pose'): + if np.linalg.norm( + np.array([self.lidar_pose.pose.pose.position.x - self.camera_pose.pose.pose.position.x, + self.lidar_pose.pose.pose.position.y - self.camera_pose.pose.pose.position.y]) + ) > 0.2: + #recall lidar localization to calculate + self.get_logger().warn("lidar_pose and camera pose have a large difference, republishing camera pose") + self.camera_pose_pub.publish(self.camera_pose) + return False + self.get_init = True + #4. if localization ok, publish the initial pose from lidar_pose + if self.get_init and not self.odom_init: + self.init_pub.publish(self.lidar_pose) + self.odom2map.pose.pose.position = self.lidar_pose.pose.pose.position + self.odom2map.pose.pose.orientation = self.lidar_pose.pose.pose.orientation + self.get_logger().info("Initial pose published from lidar_pose") + self.odom_init = True + # 5. if localization ok, set the lidar_param to 'running' + self.point_msg.x = self.p_lidar_param_running[0] + self.point_msg.y = self.p_lidar_param_running[1] + self.point_msg.z = self.p_lidar_param_running[2] + self.lidar_param_pub.publish(self.point_msg) + self.get_logger().info("Lidar parameters set to running") + # 6. response to main (a service?) + self.is_localization_ok = True + return True + + def health_check_timer_callback(self): + self.dead_wheel_slip_estimation() + # self.check_lidar_delay() + + def dead_wheel_slip_estimation(self): #V + # Check for dead wheel slip estimation + # Check the availability of the odom2map and lidar_pose + if not self.get_init or not self.odom_init: + return False + + if not hasattr(self, 'odom2map') or not hasattr(self, 'lidar_pose'): + self.get_logger().warn("odom2map or lidar_pose not available") + return False + + if not self.new_odom or not self.new_lidar: + self.get_logger().warn("odom2map or lidar_pose not updated") + return False + + # Record the timestamp of the latest lidar_pose + lidar_time = self.lidar_pose.header.stamp + + # Use lookup_transform to get the transform between odom2map at the lidar_pose timestamp and now + odom_tf = self.tf_buffer.lookup_transform( + self.p_map_frame_id, + self.p_robot_frame_id, + lidar_time + ) + + if not self.wheel_slip_first: + try: + + # Calculate the displacement from the transform + odom_displacement_x = odom_tf.transform.translation.x - self.odom_x_prev + odom_displacement_y = odom_tf.transform.translation.y - self.odom_y_prev + + # Calculate the displacement from lidar_pose + lidar_displacement_x = self.lidar_pose.pose.pose.position.x - self.lidar_x_prev + lidar_displacement_y = self.lidar_pose.pose.pose.position.y - self.lidar_y_prev + + # Compare the displacements + slip_x = abs(odom_displacement_x - lidar_displacement_x) + slip_y = abs(odom_displacement_y - lidar_displacement_y) + slip_magnitude = math.sqrt(slip_x**2 + slip_y**2) # for analysis + self.slip_values.append(slip_magnitude) + + + # self.get_logger().info(f"Slip X: {slip_x}, Slip Y: {slip_y}, Magnitude: {slip_magnitude}") + + + if slip_x > 0.03 or slip_y > 0.03: # TODO: more test on this, it shouldn't be so frequent! + self.get_logger().warn(f"Dead wheel slip detected! Slip X: {slip_x}, Slip Y: {slip_y}") + with open(self.report_file_path, 'a') as file: + file.write(f"Dead wheel slip detected! Slip X: {slip_x}, Slip Y: {slip_y}\n") + + + except Exception as e: + self.get_logger().error(f"Error during slip estimation: {e}") + + # Update previous positions + self.lidar_x_prev = self.lidar_pose.pose.pose.position.x + self.lidar_y_prev = self.lidar_pose.pose.pose.position.y + self.odom_x_prev = odom_tf.transform.translation.x + self.odom_y_prev = odom_tf.transform.translation.y + self.wheel_slip_first = False + self.new_odom = False + self.new_lidar = False + return True + + + def check_tf_ok(self): #V + tf_retry_count = 0 + while rclpy.ok(): # is it safe to use while loop? it is a blocking function + tf_retry_count += 1 + self.get_clock().sleep_for(rclpy.duration.Duration(seconds=1)) + + tf_ok = True + + # check /base_footprint, /laser, /rival/base_footprint + try: + self.tf_buffer.can_transform( + self.p_robot_frame_id, + self.p_map_frame_id, + rclpy.time.Time() + ) + except Exception as e: + tf_ok = False + self.get_logger().warn(f"TF lookup failed: {e}") + try: + self.tf_buffer.can_transform( + + self.p_rival_frame_id, + self.p_map_frame_id, + rclpy.time.Time() + ) + except Exception as e: + tf_ok = False + self.get_logger().warn(f"TF lookup failed: {e}") + + if tf_ok: + return True + + self.get_logger().warn("[Lidar Localization]: TF not OK") + + if tf_retry_count % 20 == 0: + self.get_logger().error( + f"[Lidar Localization]: TF error after retry {tf_retry_count} times" + ) + + return False + + # def check_lidar_delay(self): + # # Check delay time for lidar, + # # compare the trend of orientation because that is the most obvoius and stable data + + # # Initialize a list to store the latest 50 orientation z data + # if not hasattr(self, 'orientation_z_data'): + # self.orientation_z_data = [] + + # # Append the latest orientation z data + # self.orientation_z_data.append(self.lidar_pose.pose.pose.orientation.z) + + # # Ensure the list only keeps the latest 50 entries + # if len(self.orientation_z_data) > 50: + # self.orientation_z_data.pop(0) + + # # Example: Log the orientation data for debugging + # self.get_logger().info(f"Latest orientation z data: {self.orientation_z_data}") + # return True + + def check_final_pose(self): #V + if not self.get_init or not self.odom_init: + return False + # compare odom2map, lidar_pose and camera_pose + # warn if any one of them has a large difference + if not hasattr(self, 'odom2map') or not (hasattr(self, 'lidar_pose') or hasattr(self, 'camera_pose')): + self.get_logger().warn("odom2map or (both lidar_pose and camera_pose) not available") + return False + + current_time = self.get_clock().now().nanoseconds / 1e9 + tolerance = 0.1 + + def is_valid_stamp(stamp): + t = stamp.sec + stamp.nanosec / 1e9 + return abs(current_time - t) < tolerance and t > 1e-3 + + if not is_valid_stamp(self.odom2map.header.stamp): + self.get_logger().warn("odom2map timestamp invalid or too old") + return False + + if not is_valid_stamp(self.lidar_pose.header.stamp): + self.get_logger().warn("lidar_pose timestamp invalid or too old") + return False + + lidar_yaw = rpy_from_quaternion( + self.lidar_pose.pose.pose.orientation.x, + self.lidar_pose.pose.pose.orientation.y, + self.lidar_pose.pose.pose.orientation.z, + self.lidar_pose.pose.pose.orientation.w + ) + odom_yaw = rpy_from_quaternion( + self.odom2map.pose.pose.orientation.x, + self.odom2map.pose.pose.orientation.y, + self.odom2map.pose.pose.orientation.z, + self.odom2map.pose.pose.orientation.w + ) + # 1. compare lidar and odom2map, if they agree, fine! + angle_diff = abs(math.atan2(math.sin(odom_yaw - lidar_yaw), math.cos(odom_yaw - lidar_yaw))) + + if np.linalg.norm( + np.array([self.odom2map.pose.pose.position.x - self.lidar_pose.pose.pose.position.x, + self.odom2map.pose.pose.position.y - self.lidar_pose.pose.pose.position.y]) + ) < 0.15 and abs(angle_diff) < 0.4: + self.point_msg.x = self.p_lidar_param_running[0] + self.point_msg.y = self.p_lidar_param_running[1] + self.point_msg.z = self.p_lidar_param_running[2] + self.lidar_param_pub.publish(self.point_msg) + self.get_logger().info("Lidar parameters set to running") + return True + self.get_logger().warn("odom2map and lidar_pose have a large difference") + + # 2. if there's no camera, just initialpub odom2map, and have lidar to try again + if not hasattr(self, 'camera_pose'): + self.get_logger().warn("camera_pose not available") + # self.init_pub.publish(self.odom2map) + return False + # 3. if there's a camera, and odom2map agree with cameram, we suspect that lidar is broken + if not is_valid_stamp(self.camera_pose.header.stamp): + self.get_logger().warn("camera_pose timestamp invalid or too old") + return False + + if np.linalg.norm( + np.array([self.odom2map.pose.pose.position.x - self.camera_pose.pose.pose.pose.position.x, + self.odom2map.pose.pose.position.y - self.camera_pose.pose.pose.pose.position.y]) + ) < 0.05: + self.get_logger().warn("odom2map and camera_pose are identical, republishing camera_pose") + msg = PoseStamped() + msg.header.stamp = self.camera_pose.header.stamp + msg.header.frame_id = self.p_map_frame_id + msg.pose.position = self.camera_pose.pose.pose.position + msg.pose.orientation = self.camera_pose.pose.pose.orientation + self.camera_pose_publication.publish(msg) + # and set lidar param to fix rotation but ignore position + self.point_msg.x = self.p_lidar_param_running[0] + self.point_msg.y = self.p_lidar_param_running[1] + self.point_msg.z = self.p_lidar_param_running[2] + self.lidar_param_pub.publish(self.point_msg) + # self.get_logger().info("Lidar parameters set to running") + return False + else: + self.get_logger().warn("odom2map and camera_pose have a large difference") + # 4. if there's a camera, and lidar and camera agree, we suspect that odom2map is broken + if np.linalg.norm( + np.array([self.lidar_pose.pose.pose.position.x - self.camera_pose.pose.pose.position.x, + self.lidar_pose.pose.pose.position.y - self.camera_pose.pose.pose.position.y]) + ) < 0.05: + self.get_logger().warn("lidar_pose and camera_pose are identical, republishing camera as initial") + msg = PoseStamped() + msg.header.stamp = self.camera_pose.header.stamp + msg.header.frame_id = self.p_map_frame_id + msg.pose.position = self.camera_pose.pose.pose.position + msg.pose.orientation = self.camera_pose.pose.pose.orientation + self.camera_pose_pub.publish(self.camera_pose) + # save the broken odom information in the report file + with open(self.report_file_path, 'a') as file: + file.write(f"odom2map: {self.odom2map.pose.position.x}, {self.odom2map.pose.position.y}\n") + file.write(f"lidar_pose: {self.lidar_pose.pose.pose.position.x}, {self.lidar_pose.pose.pose.position.y}\n") + file.write(f"camera_pose: {self.camera_pose.pose.pose.position.x}, {self.camera_pose.pose.pose.position.y}\n") + return False + else: + self.get_logger().warn("lidar_pose, odom2map and camera_pose are all different, Splendid!") + self.init_pub.publish(self.odom2map) + # all three are wrong, save the information in the report file + with open(self.report_file_path, 'a') as file: + file.write(f"odom2map: {self.odom2map.pose.position.x}, {self.odom2map.pose.position.y}\n") + file.write(f"lidar_pose: {self.lidar_pose.pose.pose.position.x}, {self.lidar_pose.pose.pose.position.y}\n") + file.write(f"camera_pose: {self.camera_pose.pose.pose.position.x}, {self.camera_pose.pose.pose.position.y}\n") + self.get_logger().warn("All three poses doesn't agree") + return False + + def sensor_check(self): + + if not hasattr(self, 'imu_cov'): + return + + current_time = self.get_clock().now().nanoseconds / 1e9 + # if current_time - self.start_time > 5: + # return + def is_valid_stamp(stamp, tolerance): + t = stamp.sec + stamp.nanosec / 1e9 + return abs(current_time - t) < tolerance and t > 1e-3 + + if not is_valid_stamp(self.imu_cov.header.stamp, 1e-2): + self.get_logger().warn("imu_cov timestamp invalid or too old") + # write in the report file + with open(self.report_file_path, 'a') as file: + # Record the time difference between current time and imu_cov stamp + imu_time = self.imu_cov.header.stamp.sec + self.imu_cov.header.stamp.nanosec / 1e9 + + time_diff = imu_time - current_time + + file.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Time difference current to imu_cov: {time_diff} seconds\n") + # return False + + if not hasattr(self, 'odom2map'): + return + if not is_valid_stamp(self.odom2map.header.stamp, 1e-2): + self.get_logger().warn("[sensor check] odom2map timestamp invalid or too old") + with open(self.report_file_path, 'a') as file: + # also record the time difference between imu_cov and odom2map + odom_time = self.odom2map.header.stamp.sec + self.odom2map.header.stamp.nanosec / 1e9 + imu_time = self.imu_cov.header.stamp.sec + self.imu_cov.header.stamp.nanosec / 1e9 + + time_diff = odom_time - imu_time + + file.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Time difference imu to odom: {time_diff} seconds\n") + # return False + + def odom2map_callback(self, msg): + self.odom2map = PoseWithCovarianceStamped() + self.odom2map.header.stamp = msg.header.stamp + self.odom2map.header.frame_id = self.p_map_frame_id + self.odom2map.pose.pose.position = msg.pose.position + self.odom2map.pose.pose.orientation = msg.pose.orientation + self.new_odom = True + + def lidar_pose_callback(self, msg): + self.lidar_pose = msg + self.new_lidar = True + if not self.get_init: + self.check_localization_ok() + + def camera_pose_callback(self, msg): + self.camera_pose = PoseWithCovarianceStamped() + self.camera_pose.header.stamp = msg.header.stamp + self.camera_pose.header.frame_id = self.p_map_frame_id + self.camera_pose.pose.pose.position = msg.pose.position + self.camera_pose.pose.pose.orientation = msg.pose.orientation + if not hasattr(self, 'initial_pose') and not self.get_init: + self.camera_pose_pub.publish(msg) + self.check_localization_ok() + + def init_pose_callback(self, msg): + self.get_init = False + self.odom_init = False + self.initial_pose = msg + self.init_pub.publish(msg) + self.get_logger().info("Initial pose published") + self.check_localization_ok() + + def imu_cov_callback(self, msg): + self.imu_cov = msg + + def signal_to_main(self): + if self.is_localization_ok: + self.ready_signal.sendReadySignal(4, 3) + self.signal_timer.cancel() + self.signal_timer = None + +def main(args=None): + rclpy.init(args=args) + node = HealthCheckNode() + rclpy.spin(node) + node.destroy_node() + rclpy.shutdown() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/localization-devel-ws/healthcheck/healthcheck/ready_signal_template.py b/localization-devel-ws/healthcheck/healthcheck/ready_signal_template.py new file mode 100644 index 0000000..3bda3fc --- /dev/null +++ b/localization-devel-ws/healthcheck/healthcheck/ready_signal_template.py @@ -0,0 +1,50 @@ +import rclpy +from rclpy.node import Node +from std_msgs.msg import String +from btcpp_ros2_interfaces.srv import StartUpSrv + + +class ReadySignal(Node): + def __init__(self): + super().__init__('main_ready') + + self.ready_sub = self.create_subscription( + String, + '/robot/startup/plan', + self.readyCallback, + 2 + ) + + self.ready_srv_client = self.create_client( + StartUpSrv, + '/robot/startup/ready_signal' + ) + + self.is_main_ready = False + + def readyCallback(self, msg): + if msg is not None and not self.is_main_ready: + self.is_main_ready = True + + def sendReadySignal(self, group_, state_): + if self.is_main_ready: + self.get_logger().info('send ready signal') + + request = StartUpSrv.Request() + request.group = group_ + request.state = state_ + + self.ready_srv_client.call_async(request) + + self.get_logger().info(f"response: success={state_}, group={group_}") + +def main(args=None): + rclpy.init(args=args) + node = ReadySignal() + rclpy.spin(node) + node.destroy_node() + rclpy.shutdown() + + +if __name__ == '__main__': + main() diff --git a/localization-devel-ws/healthcheck/package.xml b/localization-devel-ws/healthcheck/package.xml new file mode 100644 index 0000000..e7c77bb --- /dev/null +++ b/localization-devel-ws/healthcheck/package.xml @@ -0,0 +1,31 @@ + + + + healthcheck + 0.0.0 + TODO: Package description + user + TODO: License declaration + + rclpy + ros2launch + + sensor_msgs + nav_msgs + geometry_msgs + numpy + math + tf2 + obstacle_detector + + ament_copyright + ament_flake8 + ament_pep257 + python3-pytest + + + ament_python + + + custom_interfaces + diff --git a/localization-devel-ws/healthcheck/report/2025-05-06_23-18-39_health_report.txt b/localization-devel-ws/healthcheck/report/2025-05-06_23-18-39_health_report.txt new file mode 100644 index 0000000..379a9bc --- /dev/null +++ b/localization-devel-ws/healthcheck/report/2025-05-06_23-18-39_health_report.txt @@ -0,0 +1,44 @@ +Health Report +Generated on: 2025-05-06 23:18:39 +======================================== +Slip X: 0.001689023162824821, Slip Y: 0.0062816502061107116 +Slip X: 0.0005228058801090008, Slip Y: 0.004273270196505141 +Slip X: 0.00038703762466085756, Slip Y: 0.0003470105693175185 +Slip X: 0.001035187633970036, Slip Y: 0.0012121096146209265 +Slip X: 0.001380667640858757, Slip Y: 0.006391676041684224 +Slip X: 0.004428010235400182, Slip Y: 0.026718938788039326 +Slip X: 0.010751613274282479, Slip Y: 0.04314868724203036 +Slip X: 0.007916033277741485, Slip Y: 0.02735124354799079 +Slip X: 0.0012658930420782477, Slip Y: 0.0038814636029436134 +Slip X: 0.00034975676723852933, Slip Y: 0.0018262522094985023 +Slip X: 0.0014045900823652646, Slip Y: 0.005224780368828341 +Slip X: 0.001378204638387337, Slip Y: 0.007311440172593109 +Slip X: 0.000931222408264154, Slip Y: 0.0022668896257938 +Slip X: 0.0021660291928143893, Slip Y: 0.003439107549672471 +Slip X: 0.0020071272744681123, Slip Y: 0.010058392364050484 +Slip X: 0.00023039441248540182, Slip Y: 0.013305006511614259 +Slip X: 2.448446093539225e-05, Slip Y: 0.003930935686237813 +Slip X: 0.0007494556309804445, Slip Y: 0.00828611916539268 +Slip X: 0.016809855172615407, Slip Y: 0.047102861454918044 +Slip X: 0.020700466435874643, Slip Y: 0.04836239791008312 +Slip X: 0.004261006029612824, Slip Y: 0.010000502251647836 +Slip X: 0.0004674306320164856, Slip Y: 0.007240005780283276 +Slip X: 0.06905879659498032, Slip Y: 0.018612287855255927 +Slip X: 0.07278460777011642, Slip Y: 0.006644461542378188 +Slip X: 0.004779838635010636, Slip Y: 0.010749763508241417 +Slip X: 0.0017389467474819842, Slip Y: 0.005371927463985182 +Slip X: 0.002804518210128615, Slip Y: 0.002729514079606421 +Slip X: 0.022738691533337962, Slip Y: 0.050618544353317585 +Slip X: 0.009657033233905155, Slip Y: 0.035847057074623656 +Slip X: 0.014798479920274776, Slip Y: 0.008483454121212652 +Slip X: 0.00021862023880464543, Slip Y: 0.00041918759924519655 +Slip X: 0.0009568145213028068, Slip Y: 0.003021636605683886 +Slip X: 0.03750877728863827, Slip Y: 0.029655771201653858 +Slip X: 0.03871784348502805, Slip Y: 0.03084348497704026 +Slip X: 0.0009793935995691672, Slip Y: 0.013586373748309066 +Slip X: 0.0007132141428577432, Slip Y: 0.0034508866435754015 +Slip X: 0.004303784562915192, Slip Y: 0.008607013388819418 +Slip X: 0.002693196294106137, Slip Y: 0.0018060842851246317 +Slip X: 0.0011223508968631335, Slip Y: 0.0025324309128818 +Slip X: 0.0013793853511625542, Slip Y: 0.00021937723819798194 +Slip X: 0.001139026336803739, Slip Y: 0.0023948092340417837 diff --git a/localization-devel-ws/healthcheck/resource/healthcheck b/localization-devel-ws/healthcheck/resource/healthcheck new file mode 100644 index 0000000..e69de29 diff --git a/localization-devel-ws/healthcheck/setup.cfg b/localization-devel-ws/healthcheck/setup.cfg new file mode 100644 index 0000000..144ebb4 --- /dev/null +++ b/localization-devel-ws/healthcheck/setup.cfg @@ -0,0 +1,6 @@ +[develop] +script_dir=$base/lib/healthcheck +[install] +install_scripts=$base/lib/healthcheck +[options.package_data] +healthcheck=srv/*.srv diff --git a/localization-devel-ws/healthcheck/setup.py b/localization-devel-ws/healthcheck/setup.py new file mode 100644 index 0000000..efeb41c --- /dev/null +++ b/localization-devel-ws/healthcheck/setup.py @@ -0,0 +1,45 @@ +import os +from glob import glob +from setuptools import find_packages, setup + +package_name = 'healthcheck' + +setup( + name=package_name, + version='0.0.0', + packages=find_packages(exclude=['test']), + data_files=[ + # Install marker file in the package index + ('share/ament_index/resource_index/packages', + ['resource/' + package_name]), + # Include our package.xml file + (os.path.join('share', package_name), ['package.xml']), + # Include all launch files. + (os.path.join('share', package_name, 'launch'), glob(os.path.join('launch', '*launch.[pxy][yma]*'))), + (os.path.join('share', package_name, 'launch'), glob(os.path.join('launch/firmware', '*launch.[pxy][yma]*'))), + # Include all config files. + (os.path.join('share', package_name, 'config'), glob(os.path.join('config', '*.yml'))), + ], + install_requires=[ + 'setuptools', + 'rclpy', + 'geometry_msgs', + 'obstacle_detector', + 'visualization_msgs', + 'std_msgs', + 'tf2_ros', + 'custom_interfaces', + ], + zip_safe=True, + maintainer='user', + maintainer_email='jossiew621@gapp.nthu.edu.tw', + description='TODO: Package description', + license='TODO: License declaration', + tests_require=['pytest'], + entry_points={ + 'console_scripts': [ + 'healthcheck_node = healthcheck.healthcheck:main', + 'ready_signal_node = healthcheck.ready_signal_template:main' + ], + }, +) diff --git a/localization-devel-ws/healthcheck/test/test_copyright.py b/localization-devel-ws/healthcheck/test/test_copyright.py new file mode 100644 index 0000000..97a3919 --- /dev/null +++ b/localization-devel-ws/healthcheck/test/test_copyright.py @@ -0,0 +1,25 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_copyright.main import main +import pytest + + +# Remove the `skip` decorator once the source file(s) have a copyright header +@pytest.mark.skip(reason='No copyright header has been placed in the generated source file.') +@pytest.mark.copyright +@pytest.mark.linter +def test_copyright(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found errors' diff --git a/localization-devel-ws/healthcheck/test/test_flake8.py b/localization-devel-ws/healthcheck/test/test_flake8.py new file mode 100644 index 0000000..27ee107 --- /dev/null +++ b/localization-devel-ws/healthcheck/test/test_flake8.py @@ -0,0 +1,25 @@ +# Copyright 2017 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_flake8.main import main_with_errors +import pytest + + +@pytest.mark.flake8 +@pytest.mark.linter +def test_flake8(): + rc, errors = main_with_errors(argv=[]) + assert rc == 0, \ + 'Found %d code style errors / warnings:\n' % len(errors) + \ + '\n'.join(errors) diff --git a/localization-devel-ws/healthcheck/test/test_pep257.py b/localization-devel-ws/healthcheck/test/test_pep257.py new file mode 100644 index 0000000..b234a38 --- /dev/null +++ b/localization-devel-ws/healthcheck/test/test_pep257.py @@ -0,0 +1,23 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_pep257.main import main +import pytest + + +@pytest.mark.linter +@pytest.mark.pep257 +def test_pep257(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found code style errors / warnings' diff --git a/localization-devel-ws/local/imu/imu_drive/CMakeLists.txt b/localization-devel-ws/local/imu/imu_drive/CMakeLists.txt new file mode 100644 index 0000000..e24c2a7 --- /dev/null +++ b/localization-devel-ws/local/imu/imu_drive/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.8) +project(imu_drive) + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +# find dependencies +find_package(ament_cmake REQUIRED) +find_package(rclcpp REQUIRED) +find_package(std_msgs REQUIRED) +find_package(std_srvs REQUIRED) +find_package(geometry_msgs REQUIRED) +find_package(nav_msgs REQUIRED) +find_package(sensor_msgs REQUIRED) + +include_directories( + include + ${catkin_INCLUDE_DIRS} +) + +add_library(imu_drive_lib lib/imu_drive.cpp) +target_include_directories(imu_drive_lib PUBLIC include) +ament_target_dependencies(imu_drive_lib rclcpp std_srvs sensor_msgs geometry_msgs nav_msgs std_msgs) + +add_executable(imu_drive_node src/imu_drive_node.cpp) +ament_target_dependencies(imu_drive_node rclcpp std_srvs sensor_msgs geometry_msgs nav_msgs std_msgs) +target_link_libraries(imu_drive_node imu_drive_lib) + +install(TARGETS + imu_drive_lib + imu_drive_node + + DESTINATION lib/${PROJECT_NAME}) + +install(DIRECTORY + launch + config + DESTINATION share/${PROJECT_NAME} +) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + # the following line skips the linter which checks for copyrights + # comment the line when a copyright and license is added to all source files + set(ament_cmake_copyright_FOUND TRUE) + # the following line skips cpplint (only works in a git repo) + # comment the line when this package is in a git repo and when + # a copyright and license is added to all source files + set(ament_cmake_cpplint_FOUND TRUE) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/localization-devel-ws/local/imu/imu_drive/LICENSE b/localization-devel-ws/local/imu/imu_drive/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/localization-devel-ws/local/imu/imu_drive/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/localization-devel-ws/local/imu/imu_drive/config/imu_drive_firm.yaml b/localization-devel-ws/local/imu/imu_drive/config/imu_drive_firm.yaml new file mode 100644 index 0000000..259c211 --- /dev/null +++ b/localization-devel-ws/local/imu/imu_drive/config/imu_drive_firm.yaml @@ -0,0 +1,20 @@ +IMU_DRIVE: + ros__parameters: + # Set the usage of the node + active: true + publish: true + + # Covariance for "const" part + covariance_vx: 0.05 + covariance_vy: 0.05 + covariance_vz: 0.01 + covariance_ax: 0.11 + covariance_ay: 0.11 + covariance_az: 0.2 + + # Covariance for "multiple" part + cov_multi_vel: 0.02 + cov_multi_acl: 0.02 + + # Low pass filter -> believe previous + filter_prev: 0.1 \ No newline at end of file diff --git a/localization-devel-ws/local/imu/imu_drive/include/imu_drive/imu_drive.h b/localization-devel-ws/local/imu/imu_drive/include/imu_drive/imu_drive.h new file mode 100644 index 0000000..39cacb5 --- /dev/null +++ b/localization-devel-ws/local/imu/imu_drive/include/imu_drive/imu_drive.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include + +#include "rclcpp/rclcpp.hpp" +#include "rclcpp/clock.hpp" +#include "rclcpp/duration.hpp" +#include "rcl/time.h" + +#include "std_msgs/msg/float64.hpp" +#include "std_srvs/srv/empty.hpp" +#include "sensor_msgs/msg/imu.hpp" +#include "geometry_msgs/msg/twist.hpp" +#include "nav_msgs/msg/odometry.hpp" +#include "geometry_msgs/msg/twist_with_covariance.hpp" + +using std::placeholders::_1; + +class IMU : public rclcpp::Node{ + +public : + + IMU() : Node("imu_drive_node", rclcpp::NodeOptions()){ + + Initialize(); + } + +private : + + /* Function - for initialize params */ + void Initialize(); + + /* Function - for update params */ + void UpdateParams(); + + /* Function - for sensor_msgs::Imu ( /imu/data )*/ + void IMUdataCallback(const sensor_msgs::msg::Imu::ConstPtr &msg); + + /* Function publish sth we need */ + void publish(); + + /** -- Advertise -- **/ + rclcpp::Subscription::SharedPtr imu_sub_; + rclcpp::Publisher::SharedPtr imu_pub_; + + /** -- Msgs to pub -- **/ + sensor_msgs::msg::Imu imu_output_; + sensor_msgs::msg::Imu imu_output_backup_; + + /** -- For low pass filter -- **/ + geometry_msgs::msg::Vector3 prev_angular_velocity; + geometry_msgs::msg::Vector3 prev_linear_acceleration; + + /* Parameters */ + bool p_active_; + bool p_publish_; + bool p_update_params_; + + double p_covariance_; + double p_cov_multi_vel_; + double p_cov_multi_acl_; + double p_filter_prev_; + + std::string p_frame_; + + std::string p_imu_sub_topic_; + std::string p_imu_pub_topic_; +}; \ No newline at end of file diff --git a/localization-devel-ws/local/imu/imu_drive/launch/imu_drive_firm.xml b/localization-devel-ws/local/imu/imu_drive/launch/imu_drive_firm.xml new file mode 100644 index 0000000..0015644 --- /dev/null +++ b/localization-devel-ws/local/imu/imu_drive/launch/imu_drive_firm.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/localization-devel-ws/local/imu/imu_drive/lib/imu_drive.cpp b/localization-devel-ws/local/imu/imu_drive/lib/imu_drive.cpp new file mode 100644 index 0000000..e55ffa0 --- /dev/null +++ b/localization-devel-ws/local/imu/imu_drive/lib/imu_drive.cpp @@ -0,0 +1,105 @@ +#include "imu_drive/imu_drive.h" + +void IMU::Initialize(){ + + RCLCPP_INFO(this->get_logger(), "inactive node"); + + this->declare_parameter("active", false); + this->declare_parameter("publish", false); + this->declare_parameter("covariance_vx", 0); + this->declare_parameter("covariance_vy", 0); + this->declare_parameter("covariance_vz", 0); + this->declare_parameter("covariance_ax", 0); + this->declare_parameter("covariance_ay", 0); + this->declare_parameter("covariance_az", 0); + this->declare_parameter("cov_multi_vel", 0); + this->declare_parameter("cov_multi_acl", 0); + this->declare_parameter("filter_prev", 0); + + UpdateParams(); +} + +void IMU::UpdateParams(){ + + RCLCPP_INFO(this->get_logger(), "start to update parameters"); + + /* get param */ + p_active_ = this->get_parameter("active").get_value(); + RCLCPP_INFO(this->get_logger(), "active set to %d", p_active_); + + p_publish_ = this->get_parameter("publish").get_value(); + RCLCPP_INFO(this->get_logger(), "publish set to %d", p_publish_); + + p_imu_sub_topic_ = "sub_topic"; + RCLCPP_INFO(this->get_logger(), "Current subscribe topic [ %s ]", p_imu_sub_topic_.c_str()); + + p_imu_pub_topic_ = "pub_topic"; + RCLCPP_INFO(this->get_logger(), "Current publish topic [ %s ]", p_imu_pub_topic_.c_str()); + + p_covariance_ = this->get_parameter("covariance_vx").get_value(); + RCLCPP_INFO(this->get_logger(), "vx covariance set to %f", p_covariance_); + this->imu_output_.angular_velocity_covariance[0] = p_covariance_; + + p_covariance_ = this->get_parameter("covariance_vy").get_value(); + RCLCPP_INFO(this->get_logger(), "vy covariance set to %f", p_covariance_); + this->imu_output_.angular_velocity_covariance[4] = p_covariance_; + + p_covariance_ = this->get_parameter("covariance_vz").get_value(); + RCLCPP_INFO(this->get_logger(), "vz covariance set to %f", p_covariance_); + this->imu_output_.angular_velocity_covariance[8] = p_covariance_; + + p_covariance_ = this->get_parameter("covariance_ax").get_value(); + RCLCPP_INFO(this->get_logger(), "ax covariance set to %f", p_covariance_); + this->imu_output_.linear_acceleration_covariance[0] = p_covariance_; + + p_covariance_ = this->get_parameter("covariance_ay").get_value(); + RCLCPP_INFO(this->get_logger(), "ay covariance set to %f", p_covariance_); + this->imu_output_.linear_acceleration_covariance[4] = p_covariance_; + + p_covariance_ = this->get_parameter("covariance_az").get_value(); + RCLCPP_INFO(this->get_logger(), "az covariance set to %f", p_covariance_); + this->imu_output_.linear_acceleration_covariance[8] = p_covariance_; + + p_cov_multi_vel_ = this->get_parameter("cov_multi_vel").get_value(); + RCLCPP_INFO(this->get_logger(), "[Odometry] : gyroscope \"a\" is set to %f", p_cov_multi_vel_); + + p_cov_multi_acl_ = this->get_parameter("cov_multi_acl").get_value(); + RCLCPP_INFO(this->get_logger(), "[Odometry] : accel \"a\" is set to %f", p_cov_multi_acl_); + + p_filter_prev_ = this->get_parameter("filter_prev").get_value(); + RCLCPP_INFO(this->get_logger(), "[Odometry] : low pass filter constant is set to %f", p_filter_prev_); + + RCLCPP_INFO(this->get_logger(), "active node"); + + this->imu_sub_ = this->create_subscription(p_imu_sub_topic_, 10, std::bind(&IMU::IMUdataCallback, this, _1)); + this->imu_pub_ = this->create_publisher(p_imu_pub_topic_, 10); + + /* -- Backup covariance -- */ + this->imu_output_backup_ = this->imu_output_; + + /* -- Set basic variables -- */ + this->imu_output_.header.frame_id = this->p_frame_; +} + +void IMU::IMUdataCallback(const sensor_msgs::msg::Imu::ConstPtr &msg){ // from /imu/data + + rclcpp::Clock clock; + + this->imu_output_.header.stamp = clock.now(); + + this->imu_output_.angular_velocity = msg->angular_velocity; + + this->imu_output_.linear_acceleration = msg->linear_acceleration; + + this->imu_output_.angular_velocity=msg->angular_velocity; /* */ + + this->prev_angular_velocity = this->imu_output_.angular_velocity; + + if(this->p_publish_) + publish(); +} + +void IMU::publish(){ + + this->imu_pub_->publish(this->imu_output_); +} \ No newline at end of file diff --git a/localization-devel-ws/local/imu/imu_drive/package.xml b/localization-devel-ws/local/imu/imu_drive/package.xml new file mode 100644 index 0000000..a39c4fb --- /dev/null +++ b/localization-devel-ws/local/imu/imu_drive/package.xml @@ -0,0 +1,27 @@ + + + + imu_drive + 0.0.0 + imu_drive + user + Apache-2.0 + + ament_cmake + + rclcpp + std_msgs + std_srvs + geometry_msgs + nav_msgs + sensor_msgs + + ros2launch + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/localization-devel-ws/local/imu/imu_drive/src/imu_drive_node.cpp b/localization-devel-ws/local/imu/imu_drive/src/imu_drive_node.cpp new file mode 100644 index 0000000..5551211 --- /dev/null +++ b/localization-devel-ws/local/imu/imu_drive/src/imu_drive_node.cpp @@ -0,0 +1,28 @@ +#include "imu_drive/imu_drive.h" + +int main(int argc, char * argv[]){ + + rclcpp::init(argc, argv); + + RCLCPP_INFO(rclcpp::get_logger("IMU_DRIVE"), "Initializing imu drive node"); + + try { + + rclcpp::spin(std::make_shared()); + } + catch (const char* s) { + + RCLCPP_FATAL(rclcpp::get_logger("IMU DRIVE"), "%s", s); + + } + catch (...) { + + RCLCPP_FATAL(rclcpp::get_logger("IMU DRIVE"), "Unexpected error"); + } + + rclcpp::shutdown(); + + RCLCPP_INFO(rclcpp::get_logger("IMU DRIVE"), "Shutdown"); + + return 0; +} \ No newline at end of file diff --git a/localization-devel-ws/local/local_filter/CMakeLists.txt b/localization-devel-ws/local/local_filter/CMakeLists.txt new file mode 100644 index 0000000..568d898 --- /dev/null +++ b/localization-devel-ws/local/local_filter/CMakeLists.txt @@ -0,0 +1,76 @@ +cmake_minimum_required(VERSION 3.8) +project(local_filter) + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +# list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_BINARY_DIR}/cmake) + +# find dependencies +find_package(ament_cmake REQUIRED) +find_package(rclcpp REQUIRED) +find_package(nav_msgs REQUIRED) +find_package(sensor_msgs REQUIRED) +find_package(geometry_msgs REQUIRED) +include_directories( EIGEN3_INCLUDE_DIR ) +find_package(Eigen3 REQUIRED) +find_package(tf2 REQUIRED) +find_package(tf2_ros REQUIRED) +find_package(tf2_geometry_msgs REQUIRED) + +# add executables +add_executable(local_filter_LPF src/local_filter_LPF.cpp) +ament_target_dependencies( + local_filter_LPF + rclcpp nav_msgs + sensor_msgs + geometry_msgs + Eigen3 + tf2 + tf2_ros + tf2_geometry_msgs +) + + +# install executables +install(TARGETS + local_filter_LPF + DESTINATION lib/${PROJECT_NAME} +) + +install(DIRECTORY + include + param + DESTINATION include/${PROJECT_NAME} +) + +install(DIRECTORY + launch + DESTINATION share/${PROJECT_NAME} +) + +# install( +# # TARGETS local_filter_LPF +# EXPORT export_${PROJECT_NAME} +# LIBRARY DESTINATION lib +# ARCHIVE DESTINATION lib +# RUNTIME DESTINATION bin +# ) + +# ament_export_targets(export_${PROJECT_NAME} HAS_LIBRARY_TARGET) +# ament_export_dependencies(some_dependency) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + # the following line skips the linter which checks for copyrights + # comment the line when a copyright and license is added to all source files + set(ament_cmake_copyright_FOUND TRUE) + # the following line skips cpplint (only works in a git repo) + # comment the line when this package is in a git repo and when + # a copyright and license is added to all source files + set(ament_cmake_cpplint_FOUND TRUE) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/localization-devel-ws/local/local_filter/cmake/Findeigen.cmake b/localization-devel-ws/local/local_filter/cmake/Findeigen.cmake new file mode 100644 index 0000000..7e0e35c --- /dev/null +++ b/localization-devel-ws/local/local_filter/cmake/Findeigen.cmake @@ -0,0 +1,92 @@ +# +# NOTICE! This module is deprecated and the FindEigen3.cmake which is +# distributed with Eigen should now be used. +# + +############################################################################### +# +# CMake script for finding the Eigen library. +# +# http://eigen.tuxfamily.org/index.php?title=Main_Page +# +# Copyright (c) 2006, 2007 Montel Laurent, +# Copyright (c) 2008, 2009 Gael Guennebaud, +# Copyright (c) 2009 Benoit Jacob +# Redistribution and use is allowed according to the terms of the 2-clause BSD +# license. +# +# +# Input variables: +# +# - Eigen_ROOT_DIR (optional): When specified, header files and libraries +# will be searched for in `${Eigen_ROOT_DIR}/include` and +# `${Eigen_ROOT_DIR}/libs` respectively, and the default CMake search order +# will be ignored. When unspecified, the default CMake search order is used. +# This variable can be specified either as a CMake or environment variable. +# If both are set, preference is given to the CMake variable. +# Use this variable for finding packages installed in a nonstandard location, +# or for enforcing that one of multiple package installations is picked up. +# +# Cache variables (not intended to be used in CMakeLists.txt files) +# +# - Eigen_INCLUDE_DIR: Absolute path to package headers. +# +# +# Output variables: +# +# - Eigen_FOUND: Boolean that indicates if the package was found +# - Eigen_INCLUDE_DIRS: Paths to the necessary header files +# - Eigen_VERSION: Version of Eigen library found +# - Eigen_DEFINITIONS: Definitions to be passed on behalf of eigen +# +# +# Example usage: +# +# # Passing the version means Eigen_FOUND will only be TRUE if a +# # version >= the provided version is found. +# find_package(Eigen 3.1.2) +# if(NOT Eigen_FOUND) +# # Error handling +# endif() +# ... +# add_definitions(${Eigen_DEFINITIONS}) +# ... +# include_directories(${Eigen_INCLUDE_DIRS} ...) +# +############################################################################### + +find_package(PkgConfig) +pkg_check_modules(PC_EIGEN eigen3) +set(EIGEN_DEFINITIONS ${PC_EIGEN_CFLAGS_OTHER}) + +message(WARNING + "The FindEigen.cmake Module in the cmake_modules package is deprecated.\n" + "Please use the FindEigen3.cmake Module provided with Eigen. " + "Change instances of find_package(Eigen) to find_package(Eigen3). " + "Check the FindEigen3.cmake Module for the resulting CMake variable names.\n" +) + +find_path(EIGEN_INCLUDE_DIR Eigen/Core + HINTS ${PC_EIGEN_INCLUDEDIR} ${PC_EIGEN_INCLUDE_DIRS} + "${Eigen_ROOT_DIR}" "$ENV{EIGEN_ROOT_DIR}" + "${EIGEN_ROOT}" "$ENV{EIGEN_ROOT}" # Backwards Compatibility + PATHS "$ENV{PROGRAMFILES}/Eigen" "$ENV{PROGRAMW6432}/Eigen" + "$ENV{PROGRAMFILES}/Eigen 3.0.0" "$ENV{PROGRAMW6432}/Eigen 3.0.0" + PATH_SUFFIXES eigen3 include/eigen3 include) + +set(EIGEN_INCLUDE_DIRS ${EIGEN_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Eigen DEFAULT_MSG EIGEN_INCLUDE_DIR) + +mark_as_advanced(EIGEN_INCLUDE_DIR) + +if(EIGEN_FOUND) + message(STATUS "Eigen found (include: ${EIGEN_INCLUDE_DIRS})") +endif(EIGEN_FOUND) + + +set(Eigen_INCLUDE_DIRS ${EIGEN_INCLUDE_DIRS}) +set(Eigen_FOUND ${EIGEN_FOUND}) +set(Eigen_VERSION ${EIGEN_VERSION}) +set(Eigen_DEFINITIONS ${EIGEN_DEFINITIONS}) diff --git a/localization-devel-ws/local/local_filter/include/blank.txt b/localization-devel-ws/local/local_filter/include/blank.txt new file mode 100644 index 0000000..e69de29 diff --git a/localization-devel-ws/local/local_filter/launch/local_filter.xml b/localization-devel-ws/local/local_filter/launch/local_filter.xml new file mode 100644 index 0000000..2894e42 --- /dev/null +++ b/localization-devel-ws/local/local_filter/launch/local_filter.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/localization-devel-ws/local/local_filter/launch/local_filter_whole.xml b/localization-devel-ws/local/local_filter/launch/local_filter_whole.xml new file mode 100644 index 0000000..74ecda1 --- /dev/null +++ b/localization-devel-ws/local/local_filter/launch/local_filter_whole.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/localization-devel-ws/local/local_filter/package.xml b/localization-devel-ws/local/local_filter/package.xml new file mode 100644 index 0000000..2e485e3 --- /dev/null +++ b/localization-devel-ws/local/local_filter/package.xml @@ -0,0 +1,30 @@ + + + + local_filter + 0.0.0 + TODO: Package description + user + TODO: License declaration + ros2launch + + ament_cmake + + rclcpp + nav_msgs + sensor_msgs + geometry_msgs + Eigen3 + tf2 + tf2_ros + tf2_geometry_msgs + + ros2launch + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/localization-devel-ws/local/local_filter/param/local_filter.yaml b/localization-devel-ws/local/local_filter/param/local_filter.yaml new file mode 100644 index 0000000..aebf2a8 --- /dev/null +++ b/localization-devel-ws/local/local_filter/param/local_filter.yaml @@ -0,0 +1,117 @@ + +/**: + ros__parameters: + frequency: 100 + silent_tf_failure: false + sensor_timeout: 0.01 + two_d_mode: true + + smooth_lagged_data: false + history_length: 0.4 + + dynamic_process_noise_covariance: false + + predict_to_current_time: false + + print_diagnostics: false + + # debug + debug: false + debug_out_file: /path/to/debug/file.txt + + # frames + map_frame: map + odom_frame: odom + base_link_frame: base_footprint + world_frame: odom + transform_time_offset: 0.05 + transform_timeout: 0 + + # sensors + # config: [x, y, z, + # r, p, y, + # x., y., z., + # r., p., y., + # x.., y.., z..] + odom0: odom + odom0_config: [false, false, false, + false, false, false, + true, true, false, + false, false, true, + false, false, false] + odom0_queue_size: 20 + odom0_differential: false + odom0_relative: false + odom0_nodelay: true + + odom0_twist_rejection_threshold: 100 + + imu0: imu/data_cov + imu0_config: [false, false, false, + false, false, false, + false, false, false, + false, false, true, + true, true, false] + imu0_queue_size: 100 + imu0_differential: false + imu0_relative: false + imu0_nodelay: true + + imu0_twist_rejection_threshold: 1000000000000.0 + imu0_linear_acceleration_rejection_threshold: 1000000000000.0 + + imu0_remove_gravitational_acceleration: false + + + initial_state: [0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0] + + publish_tf: true + publish_acceleration: true + + # control + # acc_config: [x., y., z., r., p., y.] + use_control: false + stamped_control: false + control_timeout: 0.2 + control_config: [true, true, false, false, false, true] + acceleration_limits: [1.3, 0.0, 0.0, 0.0, 0.0, 3.4] + deceleration_limits: [1.3, 0.0, 0.0, 0.0, 0.0, 4.5] + acceleration_gains: [0.8, 0.0, 0.0, 0.0, 0.0, 0.9] + deceleration_gains: [1.0, 0.0, 0.0, 0.0, 0.0, 1.0] + + # config: [x, y, z, r, p, y, x., y., z., r., p., y., x.., y.., z..] + process_noise_covariance: [0.005, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0.005, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0.006, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0.25, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0.25, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.01, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.01, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + + initial_estimate_covariance: [0.01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0.01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0.1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-6, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9] diff --git a/localization-devel-ws/local/local_filter/param/local_filter_whole.yaml b/localization-devel-ws/local/local_filter/param/local_filter_whole.yaml new file mode 100644 index 0000000..0f870b5 --- /dev/null +++ b/localization-devel-ws/local/local_filter/param/local_filter_whole.yaml @@ -0,0 +1,196 @@ +# --------------------------- +# -------- Rosserial -------- +# --------------------------- +rosserial_server_dp: + ros__parameters: + port: "/dev/USB0-3" # not used? + +# --------------------------- +# ------ Local filter ndoe ------ +# --------------------------- +local_filter_LPF: + ros__parameters: + # Active this node + active: true + + # Set publish to this node + publish: true + + # Open param service + update_params: false + + # Covariance for "const" part + covariance_x: 0.001 + covariance_y: 0.001 + covariance_z: 0.001 + covariance_vx: 0.0042 + covariance_vy: 0.0042 + covariance_vz: 0.005 + + # Covariance for "multiple" part + covariance_multi_vx: 0. + covariance_multi_vy: 0. + covariance_multi_vz: 0. + + # Dynamic adjustment from : + # True: (ns)/cmd_vel + # False: (ns)/final_pose + using_nav_vel_cb: false + + # Open if STM has integral information + use_stm_integral: false + + # Open Dynamic reconfigure service + using_dynamic_reconf: true + +# ---------------------- +# ------ IMU node ------ +# ---------------------- +imu_node: + ros__parameters: + # Set the usage of the node + active: true + publish: true + + # Open param service + update_params: false + + # Covariance for "const" part + covariance_vx: 0.005 + covariance_vy: 0.005 + covariance_vz: 0.001 + covariance_ax: 0.1 + covariance_ay: 0.1 + covariance_az: 0.1 + + # Covariance for "multiple" part + cov_multi_vel: 0.2 + cov_multi_acl: 0.2 + + # Use which topic to dynamic adjust covariance + using_nav_vel_cb: false + + # Low pass filter -> beleive previous + filter_prev: 0.1 + + using_dynamic_reconf: true + +# ---------------------- +# ------ EKF node ------ +# ---------------------- +ekf_velocity: + ros__parameters: + frequency: 100 + silent_tf_failure: false + sensor_timeout: 0.01 + two_d_mode: true + + smooth_lagged_data: false + history_length: 0.4 + + dynamic_process_noise_covariance: false + + predict_to_current_time: false + + print_diagnostics: false + + # debug + debug: false + debug_out_file: /path/to/debug/file.txt + + # frames + map_frame: map + odom_frame: odom + base_link_frame: base_footprint + world_frame: odom + transform_time_offset: 0.0 + transform_timeout: 0 + + # sensors + # config: [x, y, z, + # r, p, y, + # x., y., z., + # r., p., y., + # x.., y.., z..] + odom0: odom + odom0_config: [false, false, false, + false, false, false, + true, true, false, + false, false, true, + false, false, false] + odom0_queue_size: 20 + odom0_differential: false + odom0_relative: false + odom0_nodelay: true + + odom0_twist_rejection_threshold: 100 + + imu0: imu/data_cov + imu0_config: [false, false, false, + false, false, false, + false, false, false, + false, false, true, + false, false, false] + imu0_queue_size: 100 + imu0_differential: false + imu0_relative: false + imu0_nodelay: true + + imu0_twist_rejection_threshold: 1000000000000.0 + imu0_linear_acceleration_rejection_threshold: 1000000000000.0 + + imu0_remove_gravitational_acceleration: false + + + initial_state: [0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0] + + publish_tf: true + publish_acceleration: true + + # control + # acc_config: [x., y., z., r., p., y.] + use_control: false + stamped_control: false + control_timeout: 0.2 + control_config: [true, true, false, false, false, true] + acceleration_limits: [1.3, 0.0, 0.0, 0.0, 0.0, 3.4] + deceleration_limits: [1.3, 0.0, 0.0, 0.0, 0.0, 4.5] + acceleration_gains: [0.8, 0.0, 0.0, 0.0, 0.0, 0.9] + deceleration_gains: [1.0, 0.0, 0.0, 0.0, 0.0, 1.0] + + # config: [x, y, z, r, p, y, x., y., z., r., p., y., x.., y.., z..] + process_noise_covariance: [1e-6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1e-6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1e-6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1e-2, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1e-2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + + initial_estimate_covariance: [0.01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0.01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0.1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-4, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-4, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9] diff --git a/localization-devel-ws/local/local_filter/src/local_filter_LPF.cpp b/localization-devel-ws/local/local_filter/src/local_filter_LPF.cpp new file mode 100644 index 0000000..2bb98de --- /dev/null +++ b/localization-devel-ws/local/local_filter/src/local_filter_LPF.cpp @@ -0,0 +1,317 @@ +#include "rclcpp/rclcpp.hpp" +// msg +#include "nav_msgs/msg/odometry.hpp" +#include "sensor_msgs/msg/imu.hpp" +#include "geometry_msgs/msg/pose_with_covariance_stamped.hpp" +#include "geometry_msgs/msg/pose_stamped.hpp" +// matrix calulation +#include +#include +// tf2 +#include +#include "geometry_msgs/msg/point_stamped.hpp" +#include +#include "tf2_geometry_msgs/tf2_geometry_msgs.hpp" +#include +#include +struct RobotState +{ + Eigen::Vector3d mu; + Eigen::Matrix3d sigma; +}; + +class GlobalFilterNode { +public: + GlobalFilterNode(std::shared_ptr nh) : + nh_(nh){ + // Initialize filter coefficients and initial values + rclcpp::Clock clock; + rclcpp::Time now=clock.now(); + prev_stamp_=now; + linear_x_ = 0.0; + linear_y_ = 0.0; + angular_z_ = 0.0; + robotstate_.mu[0] = 0; + robotstate_.mu[1] = 0; + robotstate_.mu[2] = 0; + robotstate_.sigma.setZero(); + robotstate_.sigma(0, 0) = 0.1; // x-x + robotstate_.sigma(1, 1) = 0.1; // y-y + robotstate_.sigma(2, 2) = 0.1; // theta-theta + + nh_->declare_parameter("LPF_alpha_x", 0.5); // filter coefficient + alpha_x=nh_->get_parameter("LPF_alpha_x").as_double(); + + nh_->declare_parameter("LPF_alpha_y", 0.5); // filter coefficient + alpha_y=nh_->get_parameter("LPF_alpha_y").as_double(); + + nh_->declare_parameter("linear_cov_max", 0.1); + linear_cov_max_=nh_->get_parameter("linear_cov_max").as_double(); + + nh_->declare_parameter("angular_cov_max", 0.05); + angular_cov_max_=nh_->get_parameter("angular_cov_max").as_double(); + + for(int i=0;i<3;i++){ + std::string str; + switch(i){ + case 0: str="vx"; break; + case 1: str="vy"; break; + case 2: str="vz"; break; + default: break; + } + nh_->declare_parameter("covariance_"+str, 0.); + cov_backup_[i]=nh_->get_parameter("covariance_"+str).as_double(); + } + + for(int i=0;i<3;i++){ + std::string str; + switch(i){ + case 1: str="vx"; break; + case 2: str="vy"; break; + case 3: str="vz"; break; + default: break; + } + nh_->declare_parameter("covariance_multi_"+str, 0.); + cov_multi_[i]=nh_->get_parameter("covariance_multi_"+str).as_double(); + } + + setpose_sub_ = nh_->create_subscription("initial_pose", 50, std::bind(&GlobalFilterNode::setposeCallback, this, std::placeholders::_1)); + odom_sub_ = nh_->create_subscription("odoo_googoogoo", 10, std::bind(&GlobalFilterNode::odomCallback, this, std::placeholders::_1)); + imu_sub_ = nh_->create_subscription("imu/data_cov", 10, std::bind(&GlobalFilterNode::imuCallback, this, std::placeholders::_1)); + + global_filter_pub_ = nh_->create_publisher("local_filter", 10); + odom2map_pub_=nh_->create_publisher("odom2map", 10); + static_broadcaster_ = std::make_shared(nh_); + + + + } + + void diff_model(double v, double w, double dt) + { + double theta = robotstate_.mu(2); + double s = sin(theta); double s_ = sin(theta + w * dt); + double c = cos(theta); double c_ = cos(theta + w * dt); + + if ((w < 0.00001) && (w > -0.00001)){ + robotstate_.mu = robotstate_.mu + Eigen::Vector3d{ v * c * dt, v * s * dt, 0.0 }; + }else{ + robotstate_.mu = robotstate_.mu + Eigen::Vector3d{ v * (-s + s_) / w, v * (c - c_) / w, w * dt }; + } + robotstate_.mu(2) = angleLimitChecking(robotstate_.mu(2)); + } + + void omni_model(double v_x, double v_y, double w, double dt) + { + /* ekf prediction function for omni wheel */ + Eigen::Vector3d d_state; + + d_state << (v_x * dt), (v_y * dt), (w * dt); + + double theta_ = robotstate_.mu(2); /* */ + double s__theta = sin(theta_); + double c__theta = cos(theta_); + // double s__delta = sin(d_state(2)); + // double c__delta = cos(d_state(2)); + Eigen::Matrix3d A; + A << 1, 0, 0, 0, 1, 0, 0, 0, 1; + + Eigen::Matrix3d B; + // if (abs(w)> 1e-3) + // B << (c__theta*s__delta - s__theta*(c__delta -1))/w, -(s__theta*s__delta - c__theta*(c__delta -1))/w, 0, (s__theta*s__delta - c__theta*(c__delta -1))/w, (c__theta*s__delta - s__theta*(c__delta -1))/w, 0, 0, 0, 1; + // else + B << c__theta, -s__theta, 0, s__theta, c__theta, 0, 0, 0, 1; + + Eigen::Matrix3d cov_past; + cov_past = robotstate_.sigma; + + /* Update robot state mean */ + robotstate_.mu = A * robotstate_.mu + B * d_state; + } + + void setposeCallback(const geometry_msgs::msg::PoseWithCovarianceStamped & pose_msg) + { + double x = pose_msg.pose.pose.position.x; + double y = pose_msg.pose.pose.position.y; + + init_pose.position.x=x; + init_pose.position.y=y; + init_pose.orientation.x=pose_msg.pose.pose.orientation.x; + init_pose.orientation.y=pose_msg.pose.pose.orientation.y; + init_pose.orientation.z=pose_msg.pose.pose.orientation.z; + init_pose.orientation.w=pose_msg.pose.pose.orientation.w; + + rclcpp::Clock clock; + rclcpp::Time now=clock.now(); + + tf2::Quaternion q; + tf2::fromMsg(pose_msg.pose.pose.orientation, q); + double roll, pitch, yaw; + tf2::Matrix3x3(q).getRPY(roll, pitch, yaw); + + robotstate_.mu(0) = x; + robotstate_.mu(1) = y; + robotstate_.mu(2) = yaw; + + robotstate_.sigma(0, 0) = pose_msg.pose.covariance[0]; // x-x + robotstate_.sigma(0, 1) = pose_msg.pose.covariance[1]; // x-y + robotstate_.sigma(0, 2) = pose_msg.pose.covariance[5]; // x-theta + robotstate_.sigma(1, 0) = pose_msg.pose.covariance[6]; // y-x + robotstate_.sigma(1, 1) = pose_msg.pose.covariance[7]; // y-y + robotstate_.sigma(1, 2) = pose_msg.pose.covariance[11]; // y-theta + robotstate_.sigma(2, 0) = pose_msg.pose.covariance[30]; // theta-x + robotstate_.sigma(2, 1) = pose_msg.pose.covariance[31]; // theta-y + robotstate_.sigma(2, 2) = pose_msg.pose.covariance[35]; // theta-theta + RCLCPP_INFO(nh_->get_logger(), "Received initial pose: x = %f, y = %f, theta = %f", robotstate_.mu(0), robotstate_.mu(1), robotstate_.mu(2)); + + // publish absolute coordinate + coord_odom2map.header.stamp= now; + coord_odom2map.header.frame_id= "map"; + coord_odom2map.pose.position.x=robotstate_.mu(0); + coord_odom2map.pose.position.y=robotstate_.mu(1); + + tf2::Quaternion q_; + q_.setRPY(0, 0, robotstate_.mu(2)); + coord_odom2map.pose.orientation.x=q_.getX(); + coord_odom2map.pose.orientation.y=q_.getY(); + coord_odom2map.pose.orientation.z=q_.getZ(); + coord_odom2map.pose.orientation.w=q_.getW(); + odom2map_pub_->publish(coord_odom2map); + + } + + void odomCallback(const geometry_msgs::msg::Twist & odom_msg) { + // get velocity data + // Apply low-pass filter to linear xy from odom + linear_x_ = alpha_x * odom_msg.linear.x + (1 - alpha_x) * linear_x_; + linear_y_ = alpha_y * odom_msg.linear.y + (1 - alpha_y) * linear_y_; + // angular_z_=odom_msg.angular.z; + + double cov_multi[3]; + cov_multi[0]=cov_multi_[0]*abs(odom_msg.linear.x); + cov_multi[1]=cov_multi_[1]*abs(odom_msg.linear.y); + cov_multi[2]=cov_multi_[2]*abs(odom_msg.angular.z); + linear_x_cov_=std::min(linear_cov_max_, std::max(1e-8, cov_multi[0]+cov_backup_[0])); + linear_y_cov_=std::min(linear_cov_max_, std::max(1e-8, cov_multi[1]+cov_backup_[1])); + cov_backup_[0]=linear_x_cov_; + cov_backup_[1]=linear_y_cov_; + } + + void imuCallback(const sensor_msgs::msg::Imu & imu_msg) { + angular_z_ = imu_msg.angular_velocity.z; + rclcpp::Time stamp=imu_msg.header.stamp; /* */ + double dt=stamp.seconds()-prev_stamp_.seconds(); /* */ + omni_model(linear_x_, linear_y_, angular_z_, dt); /* */ + local_filter_pub(imu_msg.header.stamp, std::min(angular_cov_max_, imu_msg.angular_velocity_covariance[8])); //cov_max + prev_stamp_ = imu_msg.header.stamp; + } + + void local_filter_pub(rclcpp::Time stamp, double imu_cov) + { + // Publish global_filter message + nav_msgs::msg::Odometry global_filter_msg; + global_filter_msg.header.stamp = stamp; // imu callback stamp + global_filter_msg.header.frame_id = "map"; + global_filter_msg.child_frame_id = "base_link"; + //velocity + global_filter_msg.twist.twist.linear.x = linear_x_; //filtered x velocity + global_filter_msg.twist.twist.linear.y = linear_y_; + global_filter_msg.twist.twist.angular.z = angular_z_; //raw imu data + //pose + global_filter_msg.pose.pose.position.x = robotstate_.mu(0); + global_filter_msg.pose.pose.position.y = robotstate_.mu(1); + tf2::Quaternion quaternion_; + quaternion_.setRPY( 0, 0, robotstate_.mu(2) ); + quaternion_ = quaternion_.normalize(); + global_filter_msg.pose.pose.orientation.z = quaternion_.getZ(); + global_filter_msg.pose.pose.orientation.w = quaternion_.getW(); + //covariance + global_filter_msg.twist.covariance[0] = linear_x_cov_; //x-x + global_filter_msg.twist.covariance[7] = linear_y_cov_; //y-y + global_filter_msg.twist.covariance[35] = imu_cov; //theta-theta + global_filter_pub_->publish(global_filter_msg); + // publish absolute coordinate + coord_odom2map.header.stamp= stamp; + coord_odom2map.header.frame_id= "map"; + coord_odom2map.pose.position.x=robotstate_.mu(0); + coord_odom2map.pose.position.y=robotstate_.mu(1); + + tf2::Quaternion q_; + q_.setRPY(0, 0, robotstate_.mu(2)); + coord_odom2map.pose.orientation.x=q_.getX(); + coord_odom2map.pose.orientation.y=q_.getY(); + coord_odom2map.pose.orientation.z=q_.getZ(); + coord_odom2map.pose.orientation.w=q_.getW(); + odom2map_pub_->publish(coord_odom2map); + // publish static transform + static_transform_stamped_.header.frame_id = "map"; + static_transform_stamped_.child_frame_id = "Odom"; + static_transform_stamped_.header.stamp = stamp; + static_transform_stamped_.transform.translation.x = robotstate_.mu(0); + static_transform_stamped_.transform.translation.y = robotstate_.mu(1); + static_transform_stamped_.transform.rotation.x = q_.getX(); + static_transform_stamped_.transform.rotation.y = q_.getY(); + static_transform_stamped_.transform.rotation.z = q_.getZ(); + static_transform_stamped_.transform.rotation.w = q_.getW(); + static_broadcaster_->sendTransform(static_transform_stamped_); + } + + double angleLimitChecking(double theta) + { + while (theta > M_PI) + { + theta -= M_PI * 2; + } + while (theta <= -M_PI) + { + theta += M_PI * 2; + } + return theta; + } + +private: + std::shared_ptr nh_; + rclcpp::Subscription::SharedPtr odom_sub_; + rclcpp::Subscription::SharedPtr imu_sub_; + rclcpp::Subscription::SharedPtr setpose_sub_; + rclcpp::Publisher::SharedPtr global_filter_pub_; + rclcpp::Publisher::SharedPtr odom2map_pub_; + std::shared_ptr static_broadcaster_; + //raw + double twist_x_; + double twist_y_; + double cov_backup_[3]; + double cov_multi_[3]; + geometry_msgs::msg::Pose init_pose; + geometry_msgs::msg::PoseStamped coord_odom2map; + geometry_msgs::msg::TransformStamped static_transform_stamped_; + //filtered + double alpha_x; // filter coefficient + double alpha_y; // filter coefficient + double linear_x_; + double linear_x_cov_; + double linear_y_; + double linear_y_cov_; + double angular_z_; + // double imu_cov_; + RobotState robotstate_; + rclcpp::Time prev_stamp_; + double linear_cov_max_; + double angular_cov_max_; +}; + +int main(int argc, char** argv) { + + rclcpp::init(argc, argv); + + auto exec=std::make_shared(); + std::shared_ptr nh = rclcpp::Node::make_shared("nh"); + GlobalFilterNode global_filter_node(nh); + + exec->add_node(nh); + exec->spin(); + + rclcpp::shutdown(); + + return 0; +} diff --git a/localization-devel-ws/local/wheel_odom/odom_drive/CMakeLists.txt b/localization-devel-ws/local/wheel_odom/odom_drive/CMakeLists.txt new file mode 100644 index 0000000..0fe43f5 --- /dev/null +++ b/localization-devel-ws/local/wheel_odom/odom_drive/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.8) +project(odom_drive) + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +# find dependencies +find_package(ament_cmake REQUIRED) +find_package(rclcpp REQUIRED) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + # the following line skips the linter which checks for copyrights + # comment the line when a copyright and license is added to all source files + set(ament_cmake_copyright_FOUND TRUE) + # the following line skips cpplint (only works in a git repo) + # comment the line when this package is in a git repo and when + # a copyright and license is added to all source files + set(ament_cmake_cpplint_FOUND TRUE) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/localization-devel-ws/local/wheel_odom/odom_drive/LICENSE b/localization-devel-ws/local/wheel_odom/odom_drive/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/localization-devel-ws/local/wheel_odom/odom_drive/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/localization-devel-ws/local/wheel_odom/odom_drive/package.xml b/localization-devel-ws/local/wheel_odom/odom_drive/package.xml new file mode 100644 index 0000000..298cb14 --- /dev/null +++ b/localization-devel-ws/local/wheel_odom/odom_drive/package.xml @@ -0,0 +1,20 @@ + + + + odom_drive + 0.0.0 + TODO: Package description + user + Apache-2.0 + + ament_cmake + + rclcpp + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/localization-devel-ws/record.launch b/localization-devel-ws/record.launch new file mode 100644 index 0000000..b07a8b9 --- /dev/null +++ b/localization-devel-ws/record.launch @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/localization-devel-ws/rival/imm_filter/CMakeLists.txt b/localization-devel-ws/rival/imm_filter/CMakeLists.txt new file mode 100644 index 0000000..a0651d8 --- /dev/null +++ b/localization-devel-ws/rival/imm_filter/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.8) +project(imm_filter) + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +# find dependencies +find_package(ament_cmake REQUIRED) +find_package(rclcpp REQUIRED) +find_package(geometry_msgs REQUIRED) +find_package(nav_msgs REQUIRED) +find_package(sensor_msgs REQUIRED) +find_package(std_msgs REQUIRED) +find_package(tf2 REQUIRED) +find_package(tf2_ros REQUIRED) + +include_directories( + include + ${catkin_INCLUDE_DIRS} +) + +add_library(imm_filter_lib lib/EKF.cpp lib/IMM.cpp lib/ModelGenerator.cpp) +target_include_directories(imm_filter_lib PUBLIC include) +ament_target_dependencies(imm_filter_lib rclcpp sensor_msgs geometry_msgs nav_msgs std_msgs tf2 tf2_ros) + +add_executable(imm_filter_node src/imm_filter_node.cpp) +ament_target_dependencies(imm_filter_node rclcpp sensor_msgs geometry_msgs nav_msgs std_msgs tf2 tf2_ros) +target_link_libraries(imm_filter_node imm_filter_lib) + +install(TARGETS + imm_filter_lib + imm_filter_node + DESTINATION lib/${PROJECT_NAME}) + +install(DIRECTORY + launch + DESTINATION share/${PROJECT_NAME} +) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + # the following line skips the linter which checks for copyrights + # comment the line when a copyright and license is added to all source files + set(ament_cmake_copyright_FOUND TRUE) + # the following line skips cpplint (only works in a git repo) + # comment the line when this package is in a git repo and when + # a copyright and license is added to all source files + set(ament_cmake_cpplint_FOUND TRUE) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/localization-devel-ws/rival/imm_filter/LICENSE b/localization-devel-ws/rival/imm_filter/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/localization-devel-ws/rival/imm_filter/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/localization-devel-ws/rival/imm_filter/include/imm_filter/EKF.h b/localization-devel-ws/rival/imm_filter/include/imm_filter/EKF.h new file mode 100644 index 0000000..e9b1132 --- /dev/null +++ b/localization-devel-ws/rival/imm_filter/include/imm_filter/EKF.h @@ -0,0 +1,119 @@ +#ifndef EKF_H_ +#define EKF_H_ +#include +#include +#include +#include +#include +#include +#include "rclcpp/rclcpp.hpp" + +class KFBase { + + public: + void predict(const double& stamp); + void update(const Eigen::VectorXd &z); + void updateOnce(const double& stamp, const Eigen::VectorXd* z = nullptr); + + virtual void init(const double &stamp, const Eigen::VectorXd &x) {} + + Eigen::VectorXd x() const {return this->x_;} + Eigen::MatrixXd P() const {return this->P_;} + Eigen::MatrixXd S() const {return this->S_;} + + void setStateCoveriance(const Eigen::MatrixXd& P) { + this->P_ = P; + } + + void setState(const Eigen::VectorXd& x) { + this->x_ = x; + } + + void setCurrentTimeStamp(const double& stamp) { + this->dt_ = stamp - this->current_time_stamp_; + /*std::cout <<"TimeStamp:"<< std::fixed << stamp << std::endl; + std::cout <<"dt_:"<< std::fixed << dt_ << std::endl; + std::cout <<"det(s):"<< std::fixed << this->S().determinant() << std::endl;*/ + this->current_time_stamp_ = stamp; + + if (this->dt_ < 0) + this->dt_ = 1e-4; + } + + double stamp() const {return this->current_time_stamp_;} + double likelihood() const {return this->likelihood_;} + virtual KFBase* clone() {return new KFBase(*this);} + private: + /* data */ + protected: + Eigen::MatrixXd F_; // 狀態轉移矩陣 + Eigen::MatrixXd H_; // 測量矩陣 + Eigen::MatrixXd Q_; // 系統協方差矩陣 + Eigen::MatrixXd R_; // 測量協方差矩陣 + Eigen::MatrixXd J_; // 雅可比陣 + Eigen::MatrixXd P_; // 過程協方差矩陣 + Eigen::MatrixXd S_; + Eigen::VectorXd x_; // 狀態向量 + Eigen::VectorXd z_; // 測量向量 + + std::vector angle_mask_; + double likelihood_; + double dt_; + double current_time_stamp_; + virtual void updatePrediction() {} + virtual void updateMeasurement() {} + + static double normalizeAngle(const double raw_angle) { //象限處理 + int n = 0; + double angle = 0; + n = raw_angle / (3.141592653 * 2); + angle = raw_angle - (3.141592653 * 2) * n; + + if (angle > 3.141592653) + angle = angle - (3.141592653 * 2); + + else if (angle <= -3.141592653) + angle = angle + (3.141592653 * 2); + + + return angle; + } +}; + +class CV : public KFBase { + + private: + void updatePrediction(); + void updateMeasurement(); + public: + void init(const double &stamp, const Eigen::VectorXd &x); + virtual CV* clone() {return new CV(*this);} + CV(); + ~CV(); +}; + +class CA : public KFBase { + + private: + void updatePrediction(); + void updateMeasurement(); + public: + void init(const double &stamp, const Eigen::VectorXd &x); + virtual CA* clone() {return new CA(*this);} + CA(); + ~CA(); +}; + +class CT : public KFBase { + + private: + void updatePrediction(); + void updateMeasurement(); + const double yaw_rate_; + public: + void init(const double &stamp, const Eigen::VectorXd &x); + virtual CT* clone() {return new CT(*this);} + CT(const double& yaw_rate); + ~CT(); +}; +#endif diff --git a/localization-devel-ws/rival/imm_filter/include/imm_filter/IMM.h b/localization-devel-ws/rival/imm_filter/include/imm_filter/IMM.h new file mode 100644 index 0000000..14560dd --- /dev/null +++ b/localization-devel-ws/rival/imm_filter/include/imm_filter/IMM.h @@ -0,0 +1,68 @@ +#ifndef IMM_H_ +#define IMM_H_ +#include +#include +#include +#include +#include +#include +#include "imm_filter/EKF.h" +#include "rclcpp/rclcpp.hpp" + +class IMM { + + public: + Eigen::VectorXd x_; + + void addModel(const std::shared_ptr& model); + + void init( + const Eigen::MatrixXd& transfer_prob, + const Eigen::VectorXd& model_prob, + const Eigen::VectorXd& x, + const double& dt); + + void stateInteraction(); + void updateState(const double& stamp, const Eigen::VectorXd* z = nullptr); + void updateModelProb(); + void estimateFusion(); + void updateOnce(const double& stamp, const Eigen::VectorXd* z = nullptr); + + Eigen::VectorXd x() const {return x_;} + + IMM(const IMM& imm) { + + transfer_prob_ = imm.transfer_prob_; + P_ = imm.P_; + X_ = imm.X_; + c_ = imm.c_; + model_prob_ = imm.model_prob_; + x_ = imm.x_; + model_num_ = imm.model_num_; + state_num_ = imm.state_num_; + + for (size_t i = 0; i < imm.models_.size(); i++) { + + std::shared_ptr m = std::shared_ptr(imm.models_[i]->clone()); + this->models_.push_back(m); + } + } + double stamp() const {return current_time_stamp_;} + IMM* clone() {return new IMM(*this);} + + IMM(); + ~IMM(); + + private: + std::vector> models_; + Eigen::MatrixXd transfer_prob_; + Eigen::MatrixXd P_; + Eigen::MatrixXd X_; + Eigen::VectorXd c_; + Eigen::VectorXd model_prob_; + + size_t model_num_; + size_t state_num_; + double current_time_stamp_; +}; +#endif \ No newline at end of file diff --git a/localization-devel-ws/rival/imm_filter/include/imm_filter/ModelGenerator.h b/localization-devel-ws/rival/imm_filter/include/imm_filter/ModelGenerator.h new file mode 100644 index 0000000..69eca72 --- /dev/null +++ b/localization-devel-ws/rival/imm_filter/include/imm_filter/ModelGenerator.h @@ -0,0 +1,19 @@ +#ifndef MODEL_GENERATOR_H_ +#define MODEL_GENERATOR_H_ +#include "imm_filter/IMM.h" +#include "imm_filter/EKF.h" + +class ModelGenerator{ + + private: + /* data */ + public: + ModelGenerator(); + ~ModelGenerator(); + + void generateIMMModel(const double &stamp, const Eigen::VectorXd &x, IMM &imm); + static std::shared_ptr generateCVModel(const double &stamp, const Eigen::VectorXd &x); + static std::shared_ptr generateCAModel(const double &stamp, const Eigen::VectorXd &x); + static std::shared_ptr generateCTModel(const double &stamp, const Eigen::VectorXd &x, const double& yaw_rate); +}; +#endif \ No newline at end of file diff --git a/localization-devel-ws/rival/imm_filter/include/imm_filter/imm_filter_node.h b/localization-devel-ws/rival/imm_filter/include/imm_filter/imm_filter_node.h new file mode 100644 index 0000000..d46b09a --- /dev/null +++ b/localization-devel-ws/rival/imm_filter/include/imm_filter/imm_filter_node.h @@ -0,0 +1,46 @@ +#ifndef RIVAL_LOCALIZATION_H_ +#define RIVAL_LOCALIZATION_H_ +#include "imm_filter/IMM.h" +#include "imm_filter/EKF.h" +#include "imm_filter/ModelGenerator.h" +#include "rclcpp/rclcpp.hpp" +#include "rclcpp/clock.hpp" +#include "geometry_msgs/msg/point.hpp" +#include "geometry_msgs/msg/vector3.hpp" +#include "nav_msgs/msg/odometry.hpp" +#include "tf2/LinearMath/Quaternion.h" +#include "tf2_ros/static_transform_broadcaster.h" +#include "tf2/LinearMath/Quaternion.h" + +using namespace std::placeholders; + +class filter : public rclcpp::Node{ + + public: + filter(); + + void obstacles_callback(const nav_msgs::msg::Odometry::ConstPtr& msg); + void IMM_publisher(double point_x, double point_y, double velocity_x, double velocity_y, rclcpp::Time time); + void broadcast_rival_tf(); + + bool first_; + + double sub_px_; + double sub_py_; + double sub_vx_; + double sub_vy_; + + private: + rclcpp::Subscription::SharedPtr imm_sub; + rclcpp::Publisher::SharedPtr imm_pub; + rclcpp::Time rival_stamp; + + std::string robot_name; + std::string rival_name; + + geometry_msgs::msg::TransformStamped rival_tf; + std::shared_ptr imm_br; + + IMM model_; +}; +#endif \ No newline at end of file diff --git a/localization-devel-ws/rival/imm_filter/launch/imm_filter.xml b/localization-devel-ws/rival/imm_filter/launch/imm_filter.xml new file mode 100644 index 0000000..4a53034 --- /dev/null +++ b/localization-devel-ws/rival/imm_filter/launch/imm_filter.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/localization-devel-ws/rival/imm_filter/lib/EKF.cpp b/localization-devel-ws/rival/imm_filter/lib/EKF.cpp new file mode 100644 index 0000000..52af910 --- /dev/null +++ b/localization-devel-ws/rival/imm_filter/lib/EKF.cpp @@ -0,0 +1,350 @@ +#include +#include +#include +#include +#include +#include "imm_filter/EKF.h" + +void KFBase::predict(const double& stamp) { + setCurrentTimeStamp(stamp); + updatePrediction(); + this->P_ = this->F_ * this->P_ * this->F_.transpose() + this->Q_; +} + +void KFBase::update(const Eigen::VectorXd &z) { + updateMeasurement(); + Eigen::MatrixXd S = this->H_ * this->P_ * this->H_.transpose() + this->R_; + Eigen::VectorXd v = z - this->z_; + + for (size_t i = 0; i < angle_mask_.size(); i++) { + if (angle_mask_[i] == true) + v(i) = normalizeAngle(v(i)); // 使角度在-pi~+pi之間 + } + double det = S.determinant(); + this->S_ = S; + S = S.inverse(); + this->likelihood_ = 1.0 / sqrt(2 * M_PI * fabs(det)) * exp(-0.5 * v.transpose() * S * v); // 假設模型殘差適用高斯分布,計算似然值 + + S = 0.5 * (S + S.transpose()); + Eigen::MatrixXd K = this->P_ * (this->H_.transpose() * S); + + + this->x_ = this->x_ + K * v; + Eigen::MatrixXd I; + I.setIdentity(6, 6); + Eigen::MatrixXd C = (I - K * this->H_); + this->P_ = C * this->P_ * C.transpose() + K * R_ * K.transpose(); + this->P_ = this->P_ + 0.0001 * I; +} + +void KFBase::updateOnce(const double& stamp, const Eigen::VectorXd* z) { + + if (z == nullptr) { + predict(stamp); + } + else { + predict(stamp); + update(*z); + } +} + +CV::CV() {} + +CV::~CV() {} + +void CV::init(const double &stamp, const Eigen::VectorXd &x) { + if (x.size() != 6) { + RCLCPP_WARN(rclcpp::get_logger("EKF"),"Dismatch between State and CV model."); + exit(1); + } + else + RCLCPP_INFO(rclcpp::get_logger("EKF"),"CV modle built"); + + this->current_time_stamp_ = stamp; + this->P_.setIdentity(6, 6); + this->R_.resize(4, 4); + this->R_ << 0.25, 0, 0, 0, + 0, 0.25, 0, 0, + 0, 0, 5, 0, + 0, 0, 0, 5; + this->x_ = x; // x, y, theta, v + this->F_.resize(6, 6); + this->H_.resize(4, 6); + this->H_ << 1, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 1, 0, 0; + this->angle_mask_ = {false, false, false, false}; + this->z_.resize(4); +} + +void CV::updatePrediction() { + Eigen::VectorXd xt; + xt.resize(6); + double vx = x_(2); + double vy = x_(3); + this->F_ << 1, 0, dt_, 0, 0, 0, + 0, 1, 0, dt_, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 1; + this->x_ = this->F_ * this->x_; + /*--------------------------------------------------------------------*\ + ** CALC Process Noice Q Matrix + \*--------------------------------------------------------------------*/ + { + double delta_1 = dt_; + double delta_2 = dt_ * dt_; + double delta_3 = dt_ * dt_ * dt_; + Eigen::Matrix G; + G << + 1 / 2.0 * delta_2, 0, + 0, 1 / 2.0 * delta_2, + dt_, 0, + 0, dt_, + 0, 0, + 0, 0; + Eigen::Matrix2d E; + E << 400, 0, 0, 400; + this->Q_ = G * E * G.transpose(); + } +} + +void CV::updateMeasurement() { +/* 觀測量為 x, y, theta, v + double vx = x_(2); + double vy = x_(3); + + + this->z_(0) = x_(0); + this->z_(1) = x_(1); + this->z_(2) = atan2(x_(3), x_(2)); + this->z_(3) = sqrt(x_(2) * x_(2) + x_(3) * x_(3)); + + this->H_ << 1, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, -vy/(vx*vx + vy*vy), vx/(vx*vx + vy*vy), 0, 0, + 0, 0, vx/sqrt(vx*vx + vy*vy), vy/sqrt(vx*vx + vy*vy), 0, 0; */ + + this->z_(0) = x_(0); + this->z_(1) = x_(1); + this->z_(2) = x_(2); + this->z_(3) = x_(3); +} + +CA::CA() {} + +CA::~CA() {} + +void CA::init(const double &stamp, const Eigen::VectorXd &x) { + if (x.size() != 6) { + RCLCPP_WARN(rclcpp::get_logger("EKF"),"Dismatch between State and CA model."); + exit(1); + } + else + RCLCPP_INFO(rclcpp::get_logger("EKF"),"CA modle built"); + + this->current_time_stamp_ = stamp; + this->P_.setIdentity(6, 6); + this->R_.resize(4, 4); + this->R_ << 0.25, 0, 0, 0, + 0, 0.25, 0, 0, + 0, 0, 5, 0, + 0, 0, 0, 5; + this->x_ = x; // x, y, theta, v + this->F_.resize(6, 6); + this->H_.resize(4, 6); + this->H_ << 1, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 1, 0, 0; + this->angle_mask_ = {false, false, false, false}; + this->z_.resize(4); +} + +void CA::updatePrediction() { + Eigen::VectorXd xt; + xt.resize(6); + double vx = x_(2); + double vy = x_(3); + double ax = x_(4); + double ay = x_(5); + this->F_ << 1, 0, dt_, 0, 1 / 2.0 * dt_ * dt_, 0, + 0, 1, 0, dt_, 0, 1 / 2.0 * dt_ * dt_, + 0, 0, 1, 0, dt_, 0, + 0, 0, 0, 1, 0, dt_, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 1; + this->x_ = this->F_ * this->x_; + /*--------------------------------------------------------------------*\ + ** CALC Process Noice Q Matrix + \*--------------------------------------------------------------------*/ + { + double delta_1 = dt_; + double delta_2 = dt_ * dt_; + double delta_3 = dt_ * dt_ * dt_; + Eigen::Matrix G; + G << + 1 / 6.0 * delta_3, 0, + 0, 1 / 6.0 * delta_3, + 1 / 2.0 * delta_2, 0, + 0, 1 / 2.0 * delta_2, + dt_, 0, + 0, dt_; + Eigen::Matrix2d E; + E << 400, 0, 0, 400; + this->Q_ = G * E * G.transpose(); + } + +} + +void CA::updateMeasurement() { +/* 觀測量為 x, y, theta, v + double vx = x_(2); + double vy = x_(3); + + + this->z_(0) = x_(0); + this->z_(1) = x_(1); + this->z_(2) = atan2(x_(3), x_(2)); + this->z_(3) = sqrt(x_(2) * x_(2) + x_(3) * x_(3)); + + this->H_ << 1, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, -vy/(vx*vx + vy*vy), vx/(vx*vx + vy*vy), 0, 0, + 0, 0, vx/sqrt(vx*vx + vy*vy), vy/sqrt(vx*vx + vy*vy), 0, 0; */ + // 觀測量為 x, y, vx, vy + this->z_(0) = x_(0); + this->z_(1) = x_(1); + this->z_(2) = x_(2); + this->z_(3) = x_(3); +} + + +CT::CT(const double& yaw_rate):yaw_rate_(yaw_rate) { + if (fabs(yaw_rate_) < 1e-3) + RCLCPP_WARN(rclcpp::get_logger("EKF"),"Yaw rate can't be zero in Const Turn rate model."); +} + +CT::~CT() {} + +void CT::init(const double &stamp, const Eigen::VectorXd &x) { + + if (x.size() != 6) { + RCLCPP_WARN(rclcpp::get_logger("EKF"),"Dismatch between State and CT model."); + exit(1); + } + else + RCLCPP_INFO(rclcpp::get_logger("EKF"),"CT modle built"); + + this->current_time_stamp_ = stamp; + this->P_.setIdentity(6, 6); + this->R_.resize(4, 4); + this->R_ << 0.25, 0, 0, 0, + 0, 0.25, 0, 0, + 0, 0, 5, 0, + 0, 0, 0, 5; + this->x_ = x; // x, y, theta, v + this->F_.resize(6, 6); + this->H_.resize(4, 6); + this->H_ << 1, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 1, 0, 0; + this->angle_mask_ = {false, false, false, false}; + this->z_.resize(4); +} + + +void CT::updatePrediction() { + Eigen::VectorXd xt; + xt.resize(6); + + double x = x_(0); + double y = x_(1); + double vx = x_(2); + double vy = x_(3); + double ax = x_(4); + double ay = x_(5); + double dA = yaw_rate_ * dt_; + double sdA = sin(dA); + double cdA = cos(dA); + double sdA_w = sdA / yaw_rate_; + double cdA_w = cdA / yaw_rate_; + + + this->F_ << 1, 0, sdA_w, (cdA - 1) / yaw_rate_, 0, 0, + 0, 1, (1 - cdA) / yaw_rate_, sdA_w, 0, 0, + 0, 0, cdA, -sdA, 0, 0, + 0, 0, sdA, cdA, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0; + this->x_ = this->F_ * this->x_; + /*--------------------------------------------------------------------*\ + ** CALC Process Noice Q Matrix + \*--------------------------------------------------------------------*/ + { + double delta_1 = dt_; + double delta_2 = dt_ * dt_; + double delta_3 = dt_ * dt_ * dt_; + double omega_1 = pow(yaw_rate_,-1); + double omega_2 = pow(yaw_rate_,-2); + double omega_3 = pow(yaw_rate_,-3); + + // TODO 增加角速度噪音項 + // double dx_dw = 2*vx*cdA*omega_3 + dt_*vy*omega_2 + dt_*vy*cdA*omega_2 - 2*vy*omega_3*sdA + dt_*vx*omega_2*sdA; + // double dy_dw = 2*vy*cdA*omega_3 - dt_*vx*omega_2 - dt_*vx*cdA*omega_2 + 2*vx*omega_3*sdA + dt_*vy*omega_2*sdA; + // double dvx_dw = -(vy*cdA*omega_2) + dt_*vx*cdA*omega_1 - vx*omega_2*sdA - dt_*vy*omega_1*sdA; + // double dvy_dw = vx*cdA*omega_2 + dt_*vy*cdA*omega_1 - vy*omega_2*sdA + dt_*vx*omega_1*sdA; + // Eigen::Matrix G; + // G << + // 1 / 2.0 * delta_2, 0, dx_dw, + // 0, 1 / 2.0 * delta_2, dy_dw, + // dt_, 0, dvx_dw, + // 0, dt_, dvy_dw, + // 0, 0, 0, + // 0, 0, 0; + + // Eigen::Matrix3d E; + // E << 40, 0, 0, + // 0, 40, 0, + // 0, 0, 1; + + Eigen::Matrix G; + G << + 1 / 2.0 * delta_2, 0, + 0, 1 / 2.0 * delta_2, + dt_, 0, + 0, dt_, + 0, 0, + 0, 0; + Eigen::Matrix2d E; + E << 400, 0, + 0, 400; + this->Q_ = G * E * G.transpose(); + } +} + +void CT::updateMeasurement() { +/* 觀測量為 x, y, theta, v + double vx = x_(2); + double vy = x_(3); + + + this->z_(0) = x_(0); + this->z_(1) = x_(1); + this->z_(2) = atan2(x_(3), x_(2)); + this->z_(3) = sqrt(x_(2) * x_(2) + x_(3) * x_(3)); + + this->H_ << 1, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, -vy/(vx*vx + vy*vy), vx/(vx*vx + vy*vy), 0, 0, + 0, 0, vx/sqrt(vx*vx + vy*vy), vy/sqrt(vx*vx + vy*vy), 0, 0; */ + + // 觀測量為 x, y, vx, vy + this->z_(0) = x_(0); + this->z_(1) = x_(1); + this->z_(2) = x_(2); + this->z_(3) = x_(3); +} \ No newline at end of file diff --git a/localization-devel-ws/rival/imm_filter/lib/IMM.cpp b/localization-devel-ws/rival/imm_filter/lib/IMM.cpp new file mode 100644 index 0000000..f58e3f3 --- /dev/null +++ b/localization-devel-ws/rival/imm_filter/lib/IMM.cpp @@ -0,0 +1,141 @@ +#include "imm_filter/IMM.h" + +IMM::IMM():model_num_(0),current_time_stamp_(-1){} + +IMM::~IMM(){} + +void IMM::addModel(const std::shared_ptr& model) { + + this->models_.push_back(model); + this->model_num_++; +} + +void IMM::init (const Eigen::MatrixXd& transfer_prob, + const Eigen::VectorXd& model_prob, + const Eigen::VectorXd& x, + const double& dt) { + + RCLCPP_INFO(rclcpp::get_logger("IMM"),"imm filter initializing..."); + + if (this->model_num_ == 0) { + RCLCPP_WARN(rclcpp::get_logger("IMM"),"No valid model."); + exit(1); + } + + if (transfer_prob.cols() != this->model_num_ || transfer_prob.rows() != this->model_num_){ + + RCLCPP_WARN(rclcpp::get_logger("IMM"),"Dimension of transfer probability matrix is not equal to number of models."); + exit(1); + } + if (model_prob.size() != this->model_num_){ + + RCLCPP_WARN(rclcpp::get_logger("IMM"),"Dimension of model probability vector is not equal to number of models."); + exit(1); + } + + this->state_num_ = x.size(); + this->X_.resize(this->state_num_, this->model_num_); + + Eigen::VectorXd ZERO = Eigen::VectorXd::Zero(6); + + for (size_t i = 0; i < model_num_; i++) + this->X_.col(i) = this->models_[i]->x(); + + this->transfer_prob_ = transfer_prob; + this->model_prob_ = model_prob; + this->x_ = x; + this->c_.resize(this->model_num_); + + RCLCPP_INFO(rclcpp::get_logger("IMM"),"imm filter initialized"); +} + +void IMM::stateInteraction() { + + this->c_ = Eigen::VectorXd::Zero(this->model_num_); + + for (size_t j = 0; j < this->model_num_; j++) { + + for (size_t i = 0; i < this->model_num_; i++) + this->c_(j) += this->transfer_prob_(i, j) * this->model_prob_(i); + } + + Eigen::MatrixXd U = Eigen::MatrixXd::Zero(this->model_num_, this->model_num_); + + for (size_t i = 0; i < model_num_; i++) + this->X_.col(i) = this->models_[i]->x(); + + Eigen::MatrixXd X = this->X_; + this->X_.fill(0); + + for (size_t j = 0; j < this->model_num_; j++) { + + for (size_t i = 0; i < this->model_num_; i++) { + + U(i, j) = 1.0 / this->c_(j) * this->transfer_prob_(i, j) * this->model_prob_(i); + this->X_.col(j) += X.col(i) * U(i, j); + } + } + + for (size_t i = 0; i < this->model_num_; i++) { + + Eigen::MatrixXd P = Eigen::MatrixXd::Zero(this->state_num_, this->state_num_); + + for (size_t j = 0; j < this->model_num_; j++) { + + Eigen::VectorXd s = X.col(i) - this->X_.col(j); + P += U(i,j) * (this->models_[i]->P() + s * s.transpose()); + } + + this->models_[i]->setStateCoveriance(P); + this->models_[i]->setState(this->X_.col(i)); + } +} + +void IMM::updateState(const double& stamp, const Eigen::VectorXd* z) { + + current_time_stamp_ = stamp; + + for (size_t i = 0; i < this->model_num_; i++) { + + this->models_[i]->predict(stamp); + + if (nullptr != z) + this->models_[i]->update(*z); + } +} + +void IMM::updateModelProb() { + + double c_sum = 0; + + for (size_t i = 0; i < this->model_num_; i++) + c_sum += this->models_[i]->likelihood() * this->c_(i); + + for (size_t i = 0; i < this->model_num_; i++) + this->model_prob_(i) = 1 / c_sum * this->models_[i]->likelihood() * this->c_(i); +} + +void IMM::estimateFusion() { + + this->x_ = this->X_ * this->model_prob_; + + for (size_t i = 0; i < this->model_num_; i++) { + Eigen::MatrixXd v = this->X_.col(i) - this->x_; + this->P_ = this->models_[i]->P() + v * v.transpose() * this->model_prob_[i]; + } +} + +void IMM::updateOnce(const double& stamp, const Eigen::VectorXd* z) { + + if (z == nullptr) { + stateInteraction(); + updateState(stamp); + estimateFusion(); + } + else { + stateInteraction(); + updateState(stamp, z); + updateModelProb(); + estimateFusion(); + } +} \ No newline at end of file diff --git a/localization-devel-ws/rival/imm_filter/lib/ModelGenerator.cpp b/localization-devel-ws/rival/imm_filter/lib/ModelGenerator.cpp new file mode 100644 index 0000000..8dd3874 --- /dev/null +++ b/localization-devel-ws/rival/imm_filter/lib/ModelGenerator.cpp @@ -0,0 +1,52 @@ +#include "imm_filter/ModelGenerator.h" + +ModelGenerator::ModelGenerator(){} + +ModelGenerator::~ModelGenerator(){} + +void ModelGenerator::generateIMMModel(const double &stamp, const Eigen::VectorXd &x, IMM &imm) { + + auto cv = generateCVModel(stamp, x); + auto ca = generateCAModel(stamp, x); + auto ct0 = generateCTModel(stamp, x, 0.1); + auto ct1 = generateCTModel(stamp, x, -0.1); + + imm.addModel(ca); + imm.addModel(cv); + imm.addModel(ct0); + imm.addModel(ct1); + + Eigen::MatrixXd transfer_prob = Eigen::MatrixXd::Zero(4,4); + + transfer_prob << 0.8, 0.1, 0.05, 0.05, + 0.2, 0.7, 0.05, 0.05, + 0.1, 0.1, 0.75, 0.05, + 0.1, 0.1, 0.05, 0.75; + + Eigen::VectorXd model_prob = Eigen::VectorXd::Zero(4); + + model_prob << 0.3, 0.2, 0.25, 0.25; + + imm.init(transfer_prob, model_prob, x, stamp); +} + +std::shared_ptr ModelGenerator::generateCVModel(const double &stamp, const Eigen::VectorXd &x) { + + std::shared_ptr cv_ptr = std::shared_ptr(new CV()); + cv_ptr->init(stamp, x); + return cv_ptr; +} + +std::shared_ptr ModelGenerator::generateCAModel(const double &stamp, const Eigen::VectorXd &x) { + + std::shared_ptr ca_ptr = std::shared_ptr(new CA()); + ca_ptr->init(stamp, x); + return ca_ptr; +} + +std::shared_ptr ModelGenerator::generateCTModel(const double &stamp, const Eigen::VectorXd &x, const double& yaw_rate) { + + std::shared_ptr ct_ptr = std::shared_ptr(new CT(yaw_rate)); + ct_ptr->init(stamp, x); + return ct_ptr; +} \ No newline at end of file diff --git a/localization-devel-ws/rival/imm_filter/package.xml b/localization-devel-ws/rival/imm_filter/package.xml new file mode 100644 index 0000000..bf3f647 --- /dev/null +++ b/localization-devel-ws/rival/imm_filter/package.xml @@ -0,0 +1,28 @@ + + + + imm_filter + 0.0.0 + IMM filter node + user + Apache-2.0 + + ament_cmake + + rclcpp + geometry_msgs + nav_msgs + sensor_msgs + std_msgs + tf2 + tf2_ros + + ros2launch + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/localization-devel-ws/rival/imm_filter/src/imm_filter_node.cpp b/localization-devel-ws/rival/imm_filter/src/imm_filter_node.cpp new file mode 100644 index 0000000..2469342 --- /dev/null +++ b/localization-devel-ws/rival/imm_filter/src/imm_filter_node.cpp @@ -0,0 +1,116 @@ +#include "imm_filter/imm_filter_node.h" + +filter::filter () : Node("imm_filter_node"){ + + IMM imm; + + model_ = imm; + first_ = true; + + robot_name = "robot"; + rival_name = "rival_final"; + + imm_sub = this->create_subscription("raw_pose", 10, std::bind(&filter::obstacles_callback, this, _1)); + imm_pub = this->create_publisher("final_pose", 10); + imm_br = std::make_shared(this); +} + +void filter::obstacles_callback(const nav_msgs::msg::Odometry::ConstPtr& msg){ + + rclcpp::Clock clock; + rclcpp::Time stamp = clock.now(); + rival_stamp = stamp; + + Eigen::VectorXd x; + Eigen::VectorXd z; + + sub_px_ = msg->pose.pose.position.x; + sub_py_ = msg->pose.pose.position.y; + sub_vx_ = msg->twist.twist.linear.x; + sub_vy_ = msg->twist.twist.linear.y; + + // RCLCPP_INFO(this->get_logger(),"Subscribe:"); + // RCLCPP_INFO(this->get_logger(),"center:( %f , %f )", sub_px_, sub_py_); + // RCLCPP_INFO(this->get_logger(),"velocity:( %f , %f )", sub_vx_, sub_vy_); + // RCLCPP_INFO(this->get_logger(),"-------------"); + + if (!first_) { + + z.resize(4); + + z << sub_px_, sub_py_, sub_vx_, sub_vy_; + + model_.updateOnce(stamp.seconds(), &z); + + IMM_publisher(model_.x_[0], model_.x_[1], model_.x_[2], model_.x_[3], stamp); + } + else { + ModelGenerator gen; + + x.resize(6); + + //initial state + x << sub_px_, sub_py_, sub_vx_, sub_vy_, 0, 0; + + gen.generateIMMModel(stamp.seconds(), x, model_); + + first_ = false; + } +} + +void filter::broadcast_rival_tf() { + + static rclcpp::Time stamp_prev; + + if (stamp_prev.nanoseconds() != rival_stamp.nanoseconds()) { + + geometry_msgs::msg::TransformStamped transformStamped; + + transformStamped.header.stamp = rival_stamp; + transformStamped.header.frame_id = "/map"; + transformStamped.child_frame_id = rival_name + "/base_footprint"; + + transformStamped.transform.translation.x = sub_px_; + transformStamped.transform.translation.y = sub_py_; + transformStamped.transform.translation.z = 0; + + tf2::Quaternion q; + q.setRPY(0, 0, 0); // Roll, Pitch, Yaw + transformStamped.transform.rotation.x = q.x(); + transformStamped.transform.rotation.y = q.y(); + transformStamped.transform.rotation.z = q.z(); + transformStamped.transform.rotation.w = q.w(); + + imm_br->sendTransform(transformStamped); + } + stamp_prev = rival_stamp; +} + +void filter::IMM_publisher(double point_x, double point_y, double velocity_x, double velocity_y, rclcpp::Time time){ + + nav_msgs::msg::Odometry odom; + + odom.pose.pose.position.x = point_x; + odom.pose.pose.position.y = point_y; + odom.twist.twist.linear.x = velocity_x; + odom.twist.twist.linear.y = velocity_y; + odom.header.stamp = time; + + broadcast_rival_tf(); + imm_pub->publish(odom); + + // RCLCPP_INFO(this->get_logger(),"Publish:"); + // RCLCPP_INFO(this->get_logger(),"center:( %f , %f )", point_x, point_y); + // RCLCPP_INFO(this->get_logger(),"velocity:( %f , %f )", velocity_x, velocity_y); + // RCLCPP_INFO(this->get_logger(),"time stamp: %f", time.seconds()); + // RCLCPP_INFO(this->get_logger(),"-------------"); +} + +int main(int argc, char * argv[]) { + + rclcpp::init(argc, argv); + rclcpp::spin(std::make_shared()); + rclcpp::shutdown(); + + return 0; +} \ No newline at end of file diff --git a/localization-devel-ws/rival/rival_localization/CMakeLists.txt b/localization-devel-ws/rival/rival_localization/CMakeLists.txt new file mode 100644 index 0000000..2a45200 --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/CMakeLists.txt @@ -0,0 +1,56 @@ +cmake_minimum_required(VERSION 3.8) +project(rival_localization) + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +# find dependencies +find_package(ament_cmake REQUIRED) +find_package(rclcpp REQUIRED) +find_package(tf2 REQUIRED) +find_package(tf2_ros REQUIRED) +find_package(nav_msgs REQUIRED) +find_package(sensor_msgs REQUIRED) +find_package(std_msgs REQUIRED) +find_package(geometry_msgs REQUIRED) +find_package(obstacle_detector REQUIRED) + +include_directories( + include + ${catkin_INCLUDE_DIRS} +) + +add_library(rival_lib lib/EKF.cpp lib/IMM.cpp lib/ModelGenerator.cpp) +target_include_directories(rival_lib PUBLIC include) +ament_target_dependencies(rival_lib rclcpp sensor_msgs geometry_msgs nav_msgs std_msgs tf2 tf2_ros) + +add_executable(rival_localization src/rival_localization.cpp) +ament_target_dependencies(rival_localization rclcpp tf2 tf2_ros geometry_msgs nav_msgs obstacle_detector sensor_msgs std_msgs) +target_link_libraries(rival_localization rival_lib) + +install(TARGETS + rival_lib + rival_localization + + DESTINATION lib/${PROJECT_NAME}) + +install(DIRECTORY + launch + config + DESTINATION share/${PROJECT_NAME} +) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + # the following line skips the linter which checks for copyrights + # comment the line when a copyright and license is added to all source files + set(ament_cmake_copyright_FOUND TRUE) + # the following line skips cpplint (only works in a git repo) + # comment the line when this package is in a git repo and when + # a copyright and license is added to all source files + set(ament_cmake_cpplint_FOUND TRUE) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/localization-devel-ws/rival/rival_localization/LICENSE b/localization-devel-ws/rival/rival_localization/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/localization-devel-ws/rival/rival_localization/bag/0211_odom_2/0211_odom_2_0.db3 b/localization-devel-ws/rival/rival_localization/bag/0211_odom_2/0211_odom_2_0.db3 new file mode 100644 index 0000000..6cf0cb7 Binary files /dev/null and b/localization-devel-ws/rival/rival_localization/bag/0211_odom_2/0211_odom_2_0.db3 differ diff --git a/localization-devel-ws/rival/rival_localization/bag/0211_odom_2/metadata.yaml b/localization-devel-ws/rival/rival_localization/bag/0211_odom_2/metadata.yaml new file mode 100644 index 0000000..c4b03e8 --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/bag/0211_odom_2/metadata.yaml @@ -0,0 +1,44 @@ +rosbag2_bagfile_information: + version: 5 + storage_identifier: sqlite3 + duration: + nanoseconds: 49709465044 + starting_time: + nanoseconds_since_epoch: 1739280652997093315 + message_count: 5973 + topics_with_message_count: + - topic_metadata: + name: /odoo_googoogoo + type: geometry_msgs/msg/Twist + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 4537 + - topic_metadata: + name: /obstacles_to_map + type: obstacle_detector/msg/Obstacles + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 498 + - topic_metadata: + name: /rival/final_pose + type: nav_msgs/msg/Odometry + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 469 + - topic_metadata: + name: /rival/raw_pose + type: nav_msgs/msg/Odometry + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 469 + compression_format: "" + compression_mode: "" + relative_file_paths: + - 0211_odom_2_0.db3 + files: + - path: 0211_odom_2_0.db3 + starting_time: + nanoseconds_since_epoch: 1739280652997093315 + duration: + nanoseconds: 49709465044 + message_count: 5973 \ No newline at end of file diff --git a/localization-devel-ws/rival/rival_localization/bag/0212_odom_1/0212_odom_1_0.db3 b/localization-devel-ws/rival/rival_localization/bag/0212_odom_1/0212_odom_1_0.db3 new file mode 100644 index 0000000..c9dc0e6 Binary files /dev/null and b/localization-devel-ws/rival/rival_localization/bag/0212_odom_1/0212_odom_1_0.db3 differ diff --git a/localization-devel-ws/rival/rival_localization/bag/0212_odom_1/metadata.yaml b/localization-devel-ws/rival/rival_localization/bag/0212_odom_1/metadata.yaml new file mode 100644 index 0000000..d44845a --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/bag/0212_odom_1/metadata.yaml @@ -0,0 +1,44 @@ +rosbag2_bagfile_information: + version: 5 + storage_identifier: sqlite3 + duration: + nanoseconds: 62873619197 + starting_time: + nanoseconds_since_epoch: 1739340894060783485 + message_count: 7628 + topics_with_message_count: + - topic_metadata: + name: /odoo_googoogoo + type: geometry_msgs/msg/Twist + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 5738 + - topic_metadata: + name: /obstacles_to_map + type: obstacle_detector/msg/Obstacles + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 630 + - topic_metadata: + name: /rival/final_pose + type: nav_msgs/msg/Odometry + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 630 + - topic_metadata: + name: /rival/raw_pose + type: nav_msgs/msg/Odometry + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 630 + compression_format: "" + compression_mode: "" + relative_file_paths: + - 0212_odom_1_0.db3 + files: + - path: 0212_odom_1_0.db3 + starting_time: + nanoseconds_since_epoch: 1739340894060783485 + duration: + nanoseconds: 62873619197 + message_count: 7628 \ No newline at end of file diff --git a/localization-devel-ws/rival/rival_localization/bag/0212_odom_2/0212_odom_2_0.db3 b/localization-devel-ws/rival/rival_localization/bag/0212_odom_2/0212_odom_2_0.db3 new file mode 100644 index 0000000..95c1045 Binary files /dev/null and b/localization-devel-ws/rival/rival_localization/bag/0212_odom_2/0212_odom_2_0.db3 differ diff --git a/localization-devel-ws/rival/rival_localization/bag/0212_odom_2/metadata.yaml b/localization-devel-ws/rival/rival_localization/bag/0212_odom_2/metadata.yaml new file mode 100644 index 0000000..6e50aa9 --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/bag/0212_odom_2/metadata.yaml @@ -0,0 +1,44 @@ +rosbag2_bagfile_information: + version: 5 + storage_identifier: sqlite3 + duration: + nanoseconds: 37360348799 + starting_time: + nanoseconds_since_epoch: 1739343865060300895 + message_count: 4529 + topics_with_message_count: + - topic_metadata: + name: /odoo_googoogoo + type: geometry_msgs/msg/Twist + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 3408 + - topic_metadata: + name: /obstacles_to_map + type: obstacle_detector/msg/Obstacles + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 373 + - topic_metadata: + name: /rival/final_pose + type: nav_msgs/msg/Odometry + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 374 + - topic_metadata: + name: /rival/raw_pose + type: nav_msgs/msg/Odometry + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 374 + compression_format: "" + compression_mode: "" + relative_file_paths: + - 0212_odom_2_0.db3 + files: + - path: 0212_odom_2_0.db3 + starting_time: + nanoseconds_since_epoch: 1739343865060300895 + duration: + nanoseconds: 37360348799 + message_count: 4529 \ No newline at end of file diff --git a/localization-devel-ws/rival/rival_localization/bag/0212_odom_3/0212_odom_3_0.db3 b/localization-devel-ws/rival/rival_localization/bag/0212_odom_3/0212_odom_3_0.db3 new file mode 100644 index 0000000..1fac2bc Binary files /dev/null and b/localization-devel-ws/rival/rival_localization/bag/0212_odom_3/0212_odom_3_0.db3 differ diff --git a/localization-devel-ws/rival/rival_localization/bag/0212_odom_3/metadata.yaml b/localization-devel-ws/rival/rival_localization/bag/0212_odom_3/metadata.yaml new file mode 100644 index 0000000..44bb760 --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/bag/0212_odom_3/metadata.yaml @@ -0,0 +1,38 @@ +rosbag2_bagfile_information: + version: 5 + storage_identifier: sqlite3 + duration: + nanoseconds: 25980897083 + starting_time: + nanoseconds_since_epoch: 1739347209283068694 + message_count: 785 + topics_with_message_count: + - topic_metadata: + name: /obstacles_to_map + type: obstacle_detector/msg/Obstacles + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 261 + - topic_metadata: + name: /rival/final_pose + type: nav_msgs/msg/Odometry + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 262 + - topic_metadata: + name: /rival/raw_pose + type: nav_msgs/msg/Odometry + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 262 + compression_format: "" + compression_mode: "" + relative_file_paths: + - 0212_odom_3_0.db3 + files: + - path: 0212_odom_3_0.db3 + starting_time: + nanoseconds_since_epoch: 1739347209283068694 + duration: + nanoseconds: 25980897083 + message_count: 785 \ No newline at end of file diff --git a/localization-devel-ws/rival/rival_localization/bag/0213_immtest_1/0213_immtest_1_0.db3 b/localization-devel-ws/rival/rival_localization/bag/0213_immtest_1/0213_immtest_1_0.db3 new file mode 100644 index 0000000..69452a2 Binary files /dev/null and b/localization-devel-ws/rival/rival_localization/bag/0213_immtest_1/0213_immtest_1_0.db3 differ diff --git a/localization-devel-ws/rival/rival_localization/bag/0213_immtest_1/metadata.yaml b/localization-devel-ws/rival/rival_localization/bag/0213_immtest_1/metadata.yaml new file mode 100644 index 0000000..024e277 --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/bag/0213_immtest_1/metadata.yaml @@ -0,0 +1,38 @@ +rosbag2_bagfile_information: + version: 5 + storage_identifier: sqlite3 + duration: + nanoseconds: 35725051205 + starting_time: + nanoseconds_since_epoch: 1739436135984056934 + message_count: 3983 + topics_with_message_count: + - topic_metadata: + name: /odoo_googoogoo + type: geometry_msgs/msg/Twist + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 3269 + - topic_metadata: + name: /rival/final_pose + type: nav_msgs/msg/Odometry + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 357 + - topic_metadata: + name: /rival/raw_pose + type: nav_msgs/msg/Odometry + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 357 + compression_format: "" + compression_mode: "" + relative_file_paths: + - 0213_immtest_1_0.db3 + files: + - path: 0213_immtest_1_0.db3 + starting_time: + nanoseconds_since_epoch: 1739436135984056934 + duration: + nanoseconds: 35725051205 + message_count: 3983 \ No newline at end of file diff --git a/localization-devel-ws/rival/rival_localization/bag/0219_1/0219_1_0.db3 b/localization-devel-ws/rival/rival_localization/bag/0219_1/0219_1_0.db3 new file mode 100644 index 0000000..638e5e9 Binary files /dev/null and b/localization-devel-ws/rival/rival_localization/bag/0219_1/0219_1_0.db3 differ diff --git a/localization-devel-ws/rival/rival_localization/bag/0219_1/metadata.yaml b/localization-devel-ws/rival/rival_localization/bag/0219_1/metadata.yaml new file mode 100644 index 0000000..e03d2f2 --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/bag/0219_1/metadata.yaml @@ -0,0 +1,50 @@ +rosbag2_bagfile_information: + version: 5 + storage_identifier: sqlite3 + duration: + nanoseconds: 38283099796 + starting_time: + nanoseconds_since_epoch: 1739966579824486305 + message_count: 8731 + topics_with_message_count: + - topic_metadata: + name: /final_pose + type: geometry_msgs/msg/PoseWithCovarianceStamped + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 3798 + - topic_metadata: + name: /driving_duaiduaiduai + type: geometry_msgs/msg/Twist + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 3786 + - topic_metadata: + name: /rival/final_pose + type: nav_msgs/msg/Odometry + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 382 + - topic_metadata: + name: /rival/raw_pose + type: nav_msgs/msg/Odometry + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 382 + - topic_metadata: + name: /robot/obstacles_to_map + type: obstacle_detector/msg/Obstacles + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 383 + compression_format: "" + compression_mode: "" + relative_file_paths: + - 0219_1_0.db3 + files: + - path: 0219_1_0.db3 + starting_time: + nanoseconds_since_epoch: 1739966579824486305 + duration: + nanoseconds: 38283099796 + message_count: 8731 \ No newline at end of file diff --git a/localization-devel-ws/rival/rival_localization/bag/0219_2/0219_2_0.db3 b/localization-devel-ws/rival/rival_localization/bag/0219_2/0219_2_0.db3 new file mode 100644 index 0000000..90c04b0 Binary files /dev/null and b/localization-devel-ws/rival/rival_localization/bag/0219_2/0219_2_0.db3 differ diff --git a/localization-devel-ws/rival/rival_localization/bag/0219_2/metadata.yaml b/localization-devel-ws/rival/rival_localization/bag/0219_2/metadata.yaml new file mode 100644 index 0000000..108c019 --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/bag/0219_2/metadata.yaml @@ -0,0 +1,50 @@ +rosbag2_bagfile_information: + version: 5 + storage_identifier: sqlite3 + duration: + nanoseconds: 65337052052 + starting_time: + nanoseconds_since_epoch: 1739968006704646559 + message_count: 14568 + topics_with_message_count: + - topic_metadata: + name: /driving_duaiduaiduai + type: geometry_msgs/msg/Twist + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 6324 + - topic_metadata: + name: /final_pose + type: geometry_msgs/msg/PoseWithCovarianceStamped + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 6388 + - topic_metadata: + name: /rival/final_pose + type: nav_msgs/msg/Odometry + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 601 + - topic_metadata: + name: /rival/raw_pose + type: nav_msgs/msg/Odometry + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 601 + - topic_metadata: + name: /robot/obstacles_to_map + type: obstacle_detector/msg/Obstacles + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 654 + compression_format: "" + compression_mode: "" + relative_file_paths: + - 0219_2_0.db3 + files: + - path: 0219_2_0.db3 + starting_time: + nanoseconds_since_epoch: 1739968006704646559 + duration: + nanoseconds: 65337052052 + message_count: 14568 \ No newline at end of file diff --git a/localization-devel-ws/rival/rival_localization/bag/0219_3/0219_3_0.db3 b/localization-devel-ws/rival/rival_localization/bag/0219_3/0219_3_0.db3 new file mode 100644 index 0000000..8a0a76a Binary files /dev/null and b/localization-devel-ws/rival/rival_localization/bag/0219_3/0219_3_0.db3 differ diff --git a/localization-devel-ws/rival/rival_localization/bag/0219_3/metadata.yaml b/localization-devel-ws/rival/rival_localization/bag/0219_3/metadata.yaml new file mode 100644 index 0000000..874dded --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/bag/0219_3/metadata.yaml @@ -0,0 +1,56 @@ +rosbag2_bagfile_information: + version: 5 + storage_identifier: sqlite3 + duration: + nanoseconds: 72787771964 + starting_time: + nanoseconds_since_epoch: 1739968488844688226 + message_count: 16499 + topics_with_message_count: + - topic_metadata: + name: /final_pose + type: geometry_msgs/msg/PoseWithCovarianceStamped + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 6501 + - topic_metadata: + name: /driving_duaiduaiduai + type: geometry_msgs/msg/Twist + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 7233 + - topic_metadata: + name: /rival/final_pose + type: nav_msgs/msg/Odometry + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 713 + - topic_metadata: + name: /rival/raw_pose + type: nav_msgs/msg/Odometry + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 713 + - topic_metadata: + name: /lidar_pose + type: geometry_msgs/msg/PoseWithCovarianceStamped + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 609 + - topic_metadata: + name: /robot/obstacles_to_map + type: obstacle_detector/msg/Obstacles + serialization_format: cdr + offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 730 + compression_format: "" + compression_mode: "" + relative_file_paths: + - 0219_3_0.db3 + files: + - path: 0219_3_0.db3 + starting_time: + nanoseconds_since_epoch: 1739968488844688226 + duration: + nanoseconds: 72787771964 + message_count: 16499 \ No newline at end of file diff --git a/localization-devel-ws/rival/rival_localization/config/rival_localization.yaml b/localization-devel-ws/rival/rival_localization/config/rival_localization.yaml new file mode 100644 index 0000000..9eb9e40 --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/config/rival_localization.yaml @@ -0,0 +1,25 @@ +# Obstacle Extractor +# To -> map +obstacle_detector_to_map: + ros__parameters: + active: true + use_scan: true + use_pcl: false + frame_id: map + + use_split_and_merge: false + circles_from_visibles: true + discard_converted_segments: true + transform_coordinates: true + + min_group_points: 5 + + max_group_distance: 0.1 + distance_proportion: 0.00628 + max_split_distance: 0.01 + max_merge_separation: 0.05 + max_merge_spread: 0.2 + max_circle_radius: 0.1 + radius_enlargement: 0.0 + + pose_array:true \ No newline at end of file diff --git a/localization-devel-ws/rival/rival_localization/config/side_obstacles.yaml b/localization-devel-ws/rival/rival_localization/config/side_obstacles.yaml new file mode 100644 index 0000000..ac6ddde --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/config/side_obstacles.yaml @@ -0,0 +1,23 @@ +# Obstacle Extractor (side) +side/fixed_obstacle_detector_to_map: + ros__parameters: + active: true + use_scan: true + use_pcl: false + frame_id: map + + use_split_and_merge: false + circles_from_visibles: true + discard_converted_segments: true + transform_coordinates: true + + min_group_points: 10 + + max_group_distance: 0.2 + distance_proportion: 0.00628 + + max_split_distance: 0.02 + max_merge_separation: 0.2 + max_merge_spread: 0.2 + max_circle_radius: 0.25 + radius_enlargement: 0.0 diff --git a/localization-devel-ws/rival/rival_localization/include/rival_localization/EKF.h b/localization-devel-ws/rival/rival_localization/include/rival_localization/EKF.h new file mode 100644 index 0000000..e9b1132 --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/include/rival_localization/EKF.h @@ -0,0 +1,119 @@ +#ifndef EKF_H_ +#define EKF_H_ +#include +#include +#include +#include +#include +#include +#include "rclcpp/rclcpp.hpp" + +class KFBase { + + public: + void predict(const double& stamp); + void update(const Eigen::VectorXd &z); + void updateOnce(const double& stamp, const Eigen::VectorXd* z = nullptr); + + virtual void init(const double &stamp, const Eigen::VectorXd &x) {} + + Eigen::VectorXd x() const {return this->x_;} + Eigen::MatrixXd P() const {return this->P_;} + Eigen::MatrixXd S() const {return this->S_;} + + void setStateCoveriance(const Eigen::MatrixXd& P) { + this->P_ = P; + } + + void setState(const Eigen::VectorXd& x) { + this->x_ = x; + } + + void setCurrentTimeStamp(const double& stamp) { + this->dt_ = stamp - this->current_time_stamp_; + /*std::cout <<"TimeStamp:"<< std::fixed << stamp << std::endl; + std::cout <<"dt_:"<< std::fixed << dt_ << std::endl; + std::cout <<"det(s):"<< std::fixed << this->S().determinant() << std::endl;*/ + this->current_time_stamp_ = stamp; + + if (this->dt_ < 0) + this->dt_ = 1e-4; + } + + double stamp() const {return this->current_time_stamp_;} + double likelihood() const {return this->likelihood_;} + virtual KFBase* clone() {return new KFBase(*this);} + private: + /* data */ + protected: + Eigen::MatrixXd F_; // 狀態轉移矩陣 + Eigen::MatrixXd H_; // 測量矩陣 + Eigen::MatrixXd Q_; // 系統協方差矩陣 + Eigen::MatrixXd R_; // 測量協方差矩陣 + Eigen::MatrixXd J_; // 雅可比陣 + Eigen::MatrixXd P_; // 過程協方差矩陣 + Eigen::MatrixXd S_; + Eigen::VectorXd x_; // 狀態向量 + Eigen::VectorXd z_; // 測量向量 + + std::vector angle_mask_; + double likelihood_; + double dt_; + double current_time_stamp_; + virtual void updatePrediction() {} + virtual void updateMeasurement() {} + + static double normalizeAngle(const double raw_angle) { //象限處理 + int n = 0; + double angle = 0; + n = raw_angle / (3.141592653 * 2); + angle = raw_angle - (3.141592653 * 2) * n; + + if (angle > 3.141592653) + angle = angle - (3.141592653 * 2); + + else if (angle <= -3.141592653) + angle = angle + (3.141592653 * 2); + + + return angle; + } +}; + +class CV : public KFBase { + + private: + void updatePrediction(); + void updateMeasurement(); + public: + void init(const double &stamp, const Eigen::VectorXd &x); + virtual CV* clone() {return new CV(*this);} + CV(); + ~CV(); +}; + +class CA : public KFBase { + + private: + void updatePrediction(); + void updateMeasurement(); + public: + void init(const double &stamp, const Eigen::VectorXd &x); + virtual CA* clone() {return new CA(*this);} + CA(); + ~CA(); +}; + +class CT : public KFBase { + + private: + void updatePrediction(); + void updateMeasurement(); + const double yaw_rate_; + public: + void init(const double &stamp, const Eigen::VectorXd &x); + virtual CT* clone() {return new CT(*this);} + CT(const double& yaw_rate); + ~CT(); +}; +#endif diff --git a/localization-devel-ws/rival/rival_localization/include/rival_localization/IMM.h b/localization-devel-ws/rival/rival_localization/include/rival_localization/IMM.h new file mode 100644 index 0000000..fe628a6 --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/include/rival_localization/IMM.h @@ -0,0 +1,68 @@ +#ifndef IMM_H_ +#define IMM_H_ +#include +#include +#include +#include +#include +#include +#include "rival_localization/EKF.h" +#include "rclcpp/rclcpp.hpp" + +class IMM { + + public: + Eigen::VectorXd x_; + + void addModel(const std::shared_ptr& model); + + void init( + const Eigen::MatrixXd& transfer_prob, + const Eigen::VectorXd& model_prob, + const Eigen::VectorXd& x, + const double& dt); + + void stateInteraction(); + void updateState(const double& stamp, const Eigen::VectorXd* z = nullptr); + void updateModelProb(); + void estimateFusion(); + void updateOnce(const double& stamp, const Eigen::VectorXd* z = nullptr); + + Eigen::VectorXd x() const {return x_;} + + IMM(const IMM& imm) { + + transfer_prob_ = imm.transfer_prob_; + P_ = imm.P_; + X_ = imm.X_; + c_ = imm.c_; + model_prob_ = imm.model_prob_; + x_ = imm.x_; + model_num_ = imm.model_num_; + state_num_ = imm.state_num_; + + for (size_t i = 0; i < imm.models_.size(); i++) { + + std::shared_ptr m = std::shared_ptr(imm.models_[i]->clone()); + this->models_.push_back(m); + } + } + double stamp() const {return current_time_stamp_;} + IMM* clone() {return new IMM(*this);} + + IMM(); + ~IMM(); + + private: + std::vector> models_; + Eigen::MatrixXd transfer_prob_; + Eigen::MatrixXd P_; + Eigen::MatrixXd X_; + Eigen::VectorXd c_; + Eigen::VectorXd model_prob_; + + size_t model_num_; + size_t state_num_; + double current_time_stamp_; +}; +#endif \ No newline at end of file diff --git a/localization-devel-ws/rival/rival_localization/include/rival_localization/ModelGenerator.h b/localization-devel-ws/rival/rival_localization/include/rival_localization/ModelGenerator.h new file mode 100644 index 0000000..ac71728 --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/include/rival_localization/ModelGenerator.h @@ -0,0 +1,19 @@ +#ifndef MODEL_GENERATOR_H_ +#define MODEL_GENERATOR_H_ +#include "rival_localization/IMM.h" +#include "rival_localization/EKF.h" + +class ModelGenerator{ + + private: + /* data */ + public: + ModelGenerator(); + ~ModelGenerator(); + + void generateIMMModel(const double &stamp, const Eigen::VectorXd &x, IMM &imm); + static std::shared_ptr generateCVModel(const double &stamp, const Eigen::VectorXd &x); + static std::shared_ptr generateCAModel(const double &stamp, const Eigen::VectorXd &x); + static std::shared_ptr generateCTModel(const double &stamp, const Eigen::VectorXd &x, const double& yaw_rate); +}; +#endif \ No newline at end of file diff --git a/localization-devel-ws/rival/rival_localization/include/rival_localization/rival_localization.h b/localization-devel-ws/rival/rival_localization/include/rival_localization/rival_localization.h new file mode 100644 index 0000000..51e2e71 --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/include/rival_localization/rival_localization.h @@ -0,0 +1,111 @@ +#ifndef RIVAL_LOCALIZATION_H_ +#define RIVAL_LOCALIZATION_H_ + +#pragma once + +#include +#include + +#include "rival_localization/IMM.h" +#include "rival_localization/EKF.h" +#include "rival_localization/ModelGenerator.h" + +#include "geometry_msgs/msg/point.hpp" +#include "geometry_msgs/msg/transform_stamped.hpp" +#include "geometry_msgs/msg/vector3.hpp" +#include "geometry_msgs/msg/pose_stamped.hpp" +#include "geometry_msgs/msg/pose_with_covariance_stamped.hpp" // Add this for robot pose +#include "nav_msgs/msg/odometry.hpp" +#include "obstacle_detector/msg/obstacles.hpp" +#include "rclcpp/clock.hpp" +#include "rclcpp/rclcpp.hpp" +#include "tf2/LinearMath/Quaternion.h" +#include "tf2_ros/static_transform_broadcaster.h" +#include "geometry_msgs/msg/pose_with_covariance_stamped.hpp" + +using std::placeholders::_1; + +typedef struct rivalState { + double x; + double y; +}RivalState; + +class Rival : public rclcpp::Node { + +public: + + Rival(); + +private: + void initialize(); + + void obstacles_callback(const obstacle_detector::msg::Obstacles::SharedPtr msg); + void side_obstacles_callback(const obstacle_detector::msg::Obstacles::SharedPtr msg); + void cam_callback(const geometry_msgs::msg::PoseStamped::SharedPtr msg); + void robot_pose_callback(const geometry_msgs::msg::PoseWithCovarianceStamped::SharedPtr msg); + + void publish_rival_raw(); + void publish_rival_final(); + void fusion(); + void broadcast_rival_tf(); + bool in_playArea_obs(geometry_msgs::msg::Point center); + bool in_crossed_area(geometry_msgs::msg::Point center); + bool within_lock(geometry_msgs::msg::Point pre, geometry_msgs::msg::Point cur, double dt); + geometry_msgs::msg::Vector3 lpf(double gain, geometry_msgs::msg::Vector3 pre, geometry_msgs::msg::Vector3 cur); + bool is_me(geometry_msgs::msg::Point center); + void timerCallback(); + void imm_filter(); + bool is_me(geometry_msgs::msg::Point center); + + rclcpp::Subscription::SharedPtr obstacles_sub; + rclcpp::Subscription::SharedPtr cam_sub; + rclcpp::Subscription::SharedPtr side_obstacle_sub; + rclcpp::Subscription::SharedPtr robot_pose_sub; + + rclcpp::Publisher::SharedPtr rival_raw_pub, rival_final_pub; + rclcpp::TimerBase::SharedPtr timer_; + + obstacle_detector::msg::Obstacles obstacle; + nav_msgs::msg::Odometry rival_output; + geometry_msgs::msg::Point obstacle_pose; + geometry_msgs::msg::Point side_obstacle_pose; + geometry_msgs::msg::Point rival_raw_pose; + geometry_msgs::msg::Point rival_final_pose; + geometry_msgs::msg::Point my_pose; + geometry_msgs::msg::Point cam_rival_pose; + geometry_msgs::msg::Point side_obstacle_pose; + geometry_msgs::msg::Point my_pose; + geometry_msgs::msg::Vector3 obstacle_vel; + geometry_msgs::msg::Vector3 rival_raw_vel; + geometry_msgs::msg::Vector3 rival_final_vel; + rclcpp::Time obstacle_stamp; + rclcpp::Time rival_stamp; + rclcpp::Time cam_stamp; + rclcpp::Clock clock; + + geometry_msgs::msg::TransformStamped rival_tf; + std::shared_ptr br; + + std::string robot_name; + std::string rival_name; + + double x_max, x_min, y_max, y_min; + double vel_lpf_gain; + double locking_rad, p_locking_rad, freq; + double lockrad_growing_rate; + double cam_weight, obs_weight, side_weight; + double cam_side_threshold, side_obs_threshold, obs_cam_threshold; + double p_is_me; + + bool obstacle_ok, rival_ok , initial, camera_ok, side_obstacle_ok; + + std::vector crossed_areas; + + std::vector crossed_areas; + + bool obstacle_ok, rival_ok , initial, camera_ok; + + IMM model; +}; +#endif + diff --git a/localization-devel-ws/rival/rival_localization/launch/rival_localization.xml b/localization-devel-ws/rival/rival_localization/launch/rival_localization.xml new file mode 100644 index 0000000..4607933 --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/launch/rival_localization.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/localization-devel-ws/rival/rival_localization/launch/side_rival.xml b/localization-devel-ws/rival/rival_localization/launch/side_rival.xml new file mode 100644 index 0000000..4eebe4f --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/launch/side_rival.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + diff --git a/localization-devel-ws/rival/rival_localization/launch/view_rplidar_s3_launch.py b/localization-devel-ws/rival/rival_localization/launch/view_rplidar_s3_launch.py new file mode 100644 index 0000000..0336b22 --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/launch/view_rplidar_s3_launch.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 + +import os + +from ament_index_python.packages import get_package_share_directory +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.actions import LogInfo +from launch.substitutions import LaunchConfiguration +from launch_ros.actions import Node + + +def generate_launch_description(): + channel_type = LaunchConfiguration('channel_type', default='serial') + serial_port = LaunchConfiguration('serial_port', default='/dev/ttyUSB0') + serial_baudrate = LaunchConfiguration('serial_baudrate', default='1000000') + frame_id = LaunchConfiguration('frame_id', default='side/laser') + inverted = LaunchConfiguration('inverted', default='false') + angle_compensate = LaunchConfiguration('angle_compensate', default='true') + scan_mode = LaunchConfiguration('scan_mode', default='DenseBoost') + topic_name = LaunchConfiguration('topic_name', default='side_scan') + + rviz_config_dir = os.path.join( + get_package_share_directory('rival_localization'), + 'rviz', + 'rplidar_ros.rviz') + + + return LaunchDescription([ + DeclareLaunchArgument( + 'channel_type', + default_value=channel_type, + description='Specifying channel type of lidar'), + + DeclareLaunchArgument( + 'serial_port', + default_value=serial_port, + description='Specifying usb port to connected lidar'), + + DeclareLaunchArgument( + 'serial_baudrate', + default_value=serial_baudrate, + description='Specifying usb port baudrate to connected lidar'), + + DeclareLaunchArgument( + 'frame_id', + default_value=frame_id, + description='Specifying frame_id of lidar'), + + DeclareLaunchArgument( + 'inverted', + default_value=inverted, + description='Specifying whether or not to invert scan data'), + + DeclareLaunchArgument( + 'angle_compensate', + default_value=angle_compensate, + description='Specifying whether or not to enable angle_compensate of scan data'), + + DeclareLaunchArgument( + 'scan_mode', + default_value=scan_mode, + description='Specifying scan mode of lidar'), + + Node( + package='rplidar_ros', + executable='rplidar_node', + name='rplidar_node_rival', + parameters=[{'channel_type':channel_type, + 'serial_port': serial_port, + 'serial_baudrate': serial_baudrate, + 'frame_id': frame_id, + 'inverted': inverted, + 'topic_name': topic_name, + 'angle_compensate': angle_compensate, + 'scan_mode': scan_mode + + }], + output='screen'), + + Node( + package='rviz2', + executable='rviz2', + name='rviz2', + arguments=['-d', rviz_config_dir], + output='screen'), + ]) + diff --git a/localization-devel-ws/rival/rival_localization/lib/EKF.cpp b/localization-devel-ws/rival/rival_localization/lib/EKF.cpp new file mode 100644 index 0000000..10a00de --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/lib/EKF.cpp @@ -0,0 +1,290 @@ +#include +#include +#include +#include +#include +#include "rival_localization/EKF.h" + +void KFBase::predict(const double& stamp) { + setCurrentTimeStamp(stamp); + updatePrediction(); + this->P_ = this->F_ * this->P_ * this->F_.transpose() + this->Q_; +} + +void KFBase::update(const Eigen::VectorXd &z) { + updateMeasurement(); + Eigen::MatrixXd S = this->H_ * this->P_ * this->H_.transpose() + this->R_; + Eigen::VectorXd v = z - this->z_; + + for (size_t i = 0; i < angle_mask_.size(); i++) { + if (angle_mask_[i] == true) + v(i) = normalizeAngle(v(i)); // 使角度在-pi~+pi之間 + } + double det = S.determinant(); + this->S_ = S; + S = S.inverse(); + this->likelihood_ = 1.0 / sqrt(2 * M_PI * fabs(det)) * exp(-0.5 * v.transpose() * S * v); // 假設模型殘差適用高斯分布,計算似然值 + + S = 0.5 * (S + S.transpose()); + Eigen::MatrixXd K = this->P_ * (this->H_.transpose() * S); + + + this->x_ = this->x_ + K * v; + Eigen::MatrixXd I; + I.setIdentity(6, 6); + Eigen::MatrixXd C = (I - K * this->H_); + this->P_ = C * this->P_ * C.transpose() + K * R_ * K.transpose(); + this->P_ = this->P_ + 0.0001 * I; +} + +void KFBase::updateOnce(const double& stamp, const Eigen::VectorXd* z) { + + if (z == nullptr) { + predict(stamp); + } + else { + predict(stamp); + update(*z); + } +} + +CV::CV() {} + +CV::~CV() {} + +void CV::init(const double &stamp, const Eigen::VectorXd &x) { + if (x.size() != 6) { + RCLCPP_WARN(rclcpp::get_logger("EKF"),"Dismatch between State and CV model."); + exit(1); + } + // else + // RCLCPP_INFO(rclcpp::get_logger("EKF"),"CV modle built"); + + this->current_time_stamp_ = stamp; + this->P_.setIdentity(6, 6); + this->R_.resize(4, 4); + this->R_ << 0.25, 0, 0, 0, + 0, 0.25, 0, 0, + 0, 0, 5, 0, + 0, 0, 0, 5; + this->x_ = x; + this->F_.resize(6, 6); + this->H_.resize(4, 6); + this->H_ << 1, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 1, 0, 0; + this->angle_mask_ = {false, false, false, false}; + this->z_.resize(4); +} + +void CV::updatePrediction() { + Eigen::VectorXd xt; + xt.resize(6); + double vx = x_(2); + double vy = x_(3); + this->F_ << 1, 0, dt_, 0, 0, 0, + 0, 1, 0, dt_, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 1; + this->x_ = this->F_ * this->x_; + /*--------------------------------------------------------------------*\ + ** CALC Process Noice Q Matrix + \*--------------------------------------------------------------------*/ + { + double delta_1 = dt_; + double delta_2 = dt_ * dt_; + double delta_3 = dt_ * dt_ * dt_; + Eigen::Matrix G; + G << + 1 / 2.0 * delta_2, 0, + 0, 1 / 2.0 * delta_2, + dt_, 0, + 0, dt_, + 0, 0, + 0, 0; + Eigen::Matrix2d E; + E << 400, 0, 0, 400; + this->Q_ = G * E * G.transpose(); + } +} + +void CV::updateMeasurement() { + + this->z_(0) = x_(0); + this->z_(1) = x_(1); + this->z_(2) = x_(2); + this->z_(3) = x_(3); +} + +CA::CA() {} + +CA::~CA() {} + +void CA::init(const double &stamp, const Eigen::VectorXd &x) { + if (x.size() != 6) { + RCLCPP_WARN(rclcpp::get_logger("EKF"),"Dismatch between State and CA model."); + exit(1); + } + // else + // RCLCPP_INFO(rclcpp::get_logger("EKF"),"CA modle built"); + + this->current_time_stamp_ = stamp; + this->P_.setIdentity(6, 6); + this->R_.resize(4, 4); + this->R_ << 0.25, 0, 0, 0, + 0, 0.25, 0, 0, + 0, 0, 5, 0, + 0, 0, 0, 5; + this->x_ = x; + this->F_.resize(6, 6); + this->H_.resize(4, 6); + this->H_ << 1, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 1, 0, 0; + this->angle_mask_ = {false, false, false, false}; + this->z_.resize(4); +} + +void CA::updatePrediction() { + Eigen::VectorXd xt; + xt.resize(6); + double vx = x_(2); + double vy = x_(3); + double ax = x_(4); + double ay = x_(5); + this->F_ << 1, 0, dt_, 0, 1 / 2.0 * dt_ * dt_, 0, + 0, 1, 0, dt_, 0, 1 / 2.0 * dt_ * dt_, + 0, 0, 1, 0, dt_, 0, + 0, 0, 0, 1, 0, dt_, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 1; + this->x_ = this->F_ * this->x_; + /*--------------------------------------------------------------------*\ + ** CALC Process Noice Q Matrix + \*--------------------------------------------------------------------*/ + { + double delta_1 = dt_; + double delta_2 = dt_ * dt_; + double delta_3 = dt_ * dt_ * dt_; + Eigen::Matrix G; + G << + 1 / 6.0 * delta_3, 0, + 0, 1 / 6.0 * delta_3, + 1 / 2.0 * delta_2, 0, + 0, 1 / 2.0 * delta_2, + dt_, 0, + 0, dt_; + Eigen::Matrix2d E; + E << 400, 0, 0, 400; + this->Q_ = G * E * G.transpose(); + } + +} + +void CA::updateMeasurement() { + + // 觀測量為 x, y, vx, vy + this->z_(0) = x_(0); + this->z_(1) = x_(1); + this->z_(2) = x_(2); + this->z_(3) = x_(3); +} + + +CT::CT(const double& yaw_rate):yaw_rate_(yaw_rate) { + if (fabs(yaw_rate_) < 1e-3) + RCLCPP_WARN(rclcpp::get_logger("EKF"),"Yaw rate can't be zero in Const Turn rate model."); +} + +CT::~CT() {} + +void CT::init(const double &stamp, const Eigen::VectorXd &x) { + + if (x.size() != 6) { + RCLCPP_WARN(rclcpp::get_logger("EKF"),"Dismatch between State and CT model."); + exit(1); + } + // else + // RCLCPP_INFO(rclcpp::get_logger("EKF"),"CT modle built"); + + this->current_time_stamp_ = stamp; + this->P_.setIdentity(6, 6); + this->R_.resize(4, 4); + this->R_ << 0.25, 0, 0, 0, + 0, 0.25, 0, 0, + 0, 0, 5, 0, + 0, 0, 0, 5; + this->x_ = x; + this->F_.resize(6, 6); + this->H_.resize(4, 6); + this->H_ << 1, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 1, 0, 0; + this->angle_mask_ = {false, false, false, false}; + this->z_.resize(4); +} + + +void CT::updatePrediction() { + Eigen::VectorXd xt; + xt.resize(6); + + double x = x_(0); + double y = x_(1); + double vx = x_(2); + double vy = x_(3); + double ax = x_(4); + double ay = x_(5); + double dA = yaw_rate_ * dt_; + double sdA = sin(dA); + double cdA = cos(dA); + double sdA_w = sdA / yaw_rate_; + double cdA_w = cdA / yaw_rate_; + + + this->F_ << 1, 0, sdA_w, (cdA - 1) / yaw_rate_, 0, 0, + 0, 1, (1 - cdA) / yaw_rate_, sdA_w, 0, 0, + 0, 0, cdA, -sdA, 0, 0, + 0, 0, sdA, cdA, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0; + this->x_ = this->F_ * this->x_; + /*--------------------------------------------------------------------*\ + ** CALC Process Noice Q Matrix + \*--------------------------------------------------------------------*/ + { + double delta_1 = dt_; + double delta_2 = dt_ * dt_; + double delta_3 = dt_ * dt_ * dt_; + double omega_1 = pow(yaw_rate_,-1); + double omega_2 = pow(yaw_rate_,-2); + double omega_3 = pow(yaw_rate_,-3); + + Eigen::Matrix G; + G << + 1 / 2.0 * delta_2, 0, + 0, 1 / 2.0 * delta_2, + dt_, 0, + 0, dt_, + 0, 0, + 0, 0; + Eigen::Matrix2d E; + E << 400, 0, + 0, 400; + this->Q_ = G * E * G.transpose(); + } +} + +void CT::updateMeasurement() { + + // 觀測量為 x, y, vx, vy + this->z_(0) = x_(0); + this->z_(1) = x_(1); + this->z_(2) = x_(2); + this->z_(3) = x_(3); +} \ No newline at end of file diff --git a/localization-devel-ws/rival/rival_localization/lib/IMM.cpp b/localization-devel-ws/rival/rival_localization/lib/IMM.cpp new file mode 100644 index 0000000..070284a --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/lib/IMM.cpp @@ -0,0 +1,146 @@ +#include "rival_localization/IMM.h" + +IMM::IMM():model_num_(0),current_time_stamp_(-1){} + +IMM::~IMM(){} + +void IMM::addModel(const std::shared_ptr& model) { + + this->models_.push_back(model); + this->model_num_++; +} + +void IMM::init (const Eigen::MatrixXd& transfer_prob, + const Eigen::VectorXd& model_prob, + const Eigen::VectorXd& x, + const double& dt) { + + // RCLCPP_INFO(rclcpp::get_logger("IMM"),"imm filter initializing..."); + + if (this->model_num_ == 0) { + RCLCPP_WARN(rclcpp::get_logger("IMM"),"No valid model."); + exit(1); + } + + if (transfer_prob.cols() != this->model_num_ || transfer_prob.rows() != this->model_num_){ + + RCLCPP_WARN(rclcpp::get_logger("IMM"),"Dimension of transfer probability matrix is not equal to number of models."); + exit(1); + } + if (model_prob.size() != this->model_num_){ + + RCLCPP_WARN(rclcpp::get_logger("IMM"),"Dimension of model probability vector is not equal to number of models."); + exit(1); + } + + this->state_num_ = x.size(); + this->X_.resize(this->state_num_, this->model_num_); + + Eigen::VectorXd ZERO = Eigen::VectorXd::Zero(6); + + for (size_t i = 0; i < model_num_; i++) + this->X_.col(i) = this->models_[i]->x(); + + this->transfer_prob_ = transfer_prob; + this->model_prob_ = model_prob; + this->x_ = x; + this->c_.resize(this->model_num_); + + stateInteraction(); + + // RCLCPP_INFO(rclcpp::get_logger("IMM"),"imm filter initialized"); +} + +void IMM::stateInteraction() { + + this->c_ = Eigen::VectorXd::Zero(this->model_num_); + + for (size_t j = 0; j < this->model_num_; j++) { + + for (size_t i = 0; i < this->model_num_; i++) + this->c_(j) += this->transfer_prob_(i, j) * this->model_prob_(i); + } + + Eigen::MatrixXd U = Eigen::MatrixXd::Zero(this->model_num_, this->model_num_); + + for (size_t i = 0; i < model_num_; i++) + this->X_.col(i) = this->models_[i]->x(); + + Eigen::MatrixXd X = this->X_; + this->X_.fill(0); + + for (size_t j = 0; j < this->model_num_; j++) { + + for (size_t i = 0; i < this->model_num_; i++) { + + U(i, j) = 1.0 / this->c_(j) * this->transfer_prob_(i, j) * this->model_prob_(i); + this->X_.col(j) += X.col(i) * U(i, j); + } + } + + for (size_t i = 0; i < this->model_num_; i++) { + + Eigen::MatrixXd P = Eigen::MatrixXd::Zero(this->state_num_, this->state_num_); + + for (size_t j = 0; j < this->model_num_; j++) { + + Eigen::VectorXd s = X.col(i) - this->X_.col(j); + P += U(i,j) * (this->models_[i]->P() + s * s.transpose()); + } + + this->models_[i]->setStateCoveriance(P); + this->models_[i]->setState(this->X_.col(i)); + } +} + +void IMM::updateState(const double& stamp, const Eigen::VectorXd* z) { + + current_time_stamp_ = stamp; + + for (size_t i = 0; i < this->model_num_; i++) { + + this->models_[i]->predict(stamp); + + if (nullptr != z) + this->models_[i]->update(*z); + } +} + +void IMM::updateModelProb() { + + double c_sum = 0; + + for (size_t i = 0; i < this->model_num_; i++) + c_sum += this->models_[i]->likelihood() * this->c_(i); + + for (size_t i = 0; i < this->model_num_; i++) + this->model_prob_(i) = 1 / c_sum * this->models_[i]->likelihood() * this->c_(i); +} + +void IMM::estimateFusion() { + + this->x_ = this->X_ * this->model_prob_; + + for (size_t i = 0; i < this->model_num_; i++) { + Eigen::MatrixXd v = this->X_.col(i) - this->x_; + this->P_ = this->models_[i]->P() + v * v.transpose() * this->model_prob_[i]; + } +} + +void IMM::updateOnce(const double& stamp, const Eigen::VectorXd* z) { + + if (z == nullptr) { + updateState(stamp); + stateInteraction(); + estimateFusion(); + } + else { + updateState(stamp, z); + updateModelProb(); + stateInteraction(); + estimateFusion(); + } + + // std::cout<<"ca: "< ModelGenerator::generateCVModel(const double &stamp, const Eigen::VectorXd &x) { + + std::shared_ptr cv_ptr = std::shared_ptr(new CV()); + cv_ptr->init(stamp, x); + return cv_ptr; +} + +std::shared_ptr ModelGenerator::generateCAModel(const double &stamp, const Eigen::VectorXd &x) { + + std::shared_ptr ca_ptr = std::shared_ptr(new CA()); + ca_ptr->init(stamp, x); + return ca_ptr; +} + +std::shared_ptr ModelGenerator::generateCTModel(const double &stamp, const Eigen::VectorXd &x, const double& yaw_rate) { + + std::shared_ptr ct_ptr = std::shared_ptr(new CT(yaw_rate)); + ct_ptr->init(stamp, x); + return ct_ptr; +} \ No newline at end of file diff --git a/localization-devel-ws/rival/rival_localization/package.xml b/localization-devel-ws/rival/rival_localization/package.xml new file mode 100644 index 0000000..e8b29b1 --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/package.xml @@ -0,0 +1,29 @@ + + + + rival_localization + 0.0.0 + rival_localization + user + Apache-2.0 + + ament_cmake + + rclcpp + tf2 + tf2_ros + sensor_msgs + std_msgs + nav_msgs + geometry_msgs + obstacle_detector + + ros2launch + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/localization-devel-ws/rival/rival_localization/rviz/rplidar_ros.rviz b/localization-devel-ws/rival/rival_localization/rviz/rplidar_ros.rviz new file mode 100644 index 0000000..f755e41 --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/rviz/rplidar_ros.rviz @@ -0,0 +1,162 @@ +Panels: + - Class: rviz_common/Displays + Help Height: 78 + Name: Displays + Property Tree Widget: + Expanded: + - /Global Options1 + - /Status1 + - /LaserScan1 + - /LaserScan1/Topic1 + Splitter Ratio: 0.5 + Tree Height: 576 + - Class: rviz_common/Selection + Name: Selection + - Class: rviz_common/Tool Properties + Expanded: + - /2D Goal Pose1 + - /Publish Point1 + Name: Tool Properties + Splitter Ratio: 0.5886790156364441 + - Class: rviz_common/Views + Expanded: + - /Current View1 + Name: Views + Splitter Ratio: 0.5 +Visualization Manager: + Class: "" + Displays: + - Alpha: 0.5 + Cell Size: 1 + Class: rviz_default_plugins/Grid + Color: 160; 160; 164 + Enabled: true + Line Style: + Line Width: 0.029999999329447746 + Value: Lines + Name: Grid + Normal Cell Count: 0 + Offset: + X: 0 + Y: 0 + Z: 0 + Plane: XY + Plane Cell Count: 10 + Reference Frame: + Value: true + - Alpha: 1 + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 0 + Min Value: 0 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz_default_plugins/LaserScan + Color: 255; 255; 255 + Color Transformer: Intensity + Decay Time: 0 + Enabled: true + Invert Rainbow: false + Max Color: 255; 255; 255 + Max Intensity: 47 + Min Color: 0; 0; 0 + Min Intensity: 0 + Name: LaserScan + Position Transformer: XYZ + Selectable: true + Size (Pixels): 3 + Size (m): 0.05000000074505806 + Style: Flat Squares + Topic: + Depth: 5 + Durability Policy: Volatile + Filter size: 10 + History Policy: Keep Last + Reliability Policy: System Default + Value: /scan + Use Fixed Frame: true + Use rainbow: true + Value: true + Enabled: true + Global Options: + Background Color: 48; 48; 48 + Fixed Frame: robot/laser + Frame Rate: 30 + Name: root + Tools: + - Class: rviz_default_plugins/Interact + Hide Inactive Objects: true + - Class: rviz_default_plugins/MoveCamera + - Class: rviz_default_plugins/Select + - Class: rviz_default_plugins/FocusCamera + - Class: rviz_default_plugins/Measure + Line color: 128; 128; 0 + - Class: rviz_default_plugins/SetInitialPose + Covariance x: 0.25 + Covariance y: 0.25 + Covariance yaw: 0.06853891909122467 + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /initialpose + - Class: rviz_default_plugins/SetGoal + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /goal_pose + - Class: rviz_default_plugins/PublishPoint + Single click: true + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /clicked_point + Transformation: + Current: + Class: rviz_default_plugins/TF + Value: true + Views: + Current: + Class: rviz_default_plugins/Orbit + Distance: 18.439373016357422 + Enable Stereo Rendering: + Stereo Eye Separation: 0.05999999865889549 + Stereo Focal Distance: 1 + Swap Stereo Eyes: false + Value: false + Focal Point: + X: 0 + Y: 0 + Z: 0 + Focal Shape Fixed Size: true + Focal Shape Size: 0.05000000074505806 + Invert Z Axis: false + Name: Current View + Near Clip Distance: 0.009999999776482582 + Pitch: 1.5697605609893799 + Target Frame: + Value: Orbit (rviz_default_plugins) + Yaw: 1.5603972673416138 + Saved: ~ +Window Geometry: + Displays: + collapsed: false + Height: 805 + Hide Left Dock: false + Hide Right Dock: false + QMainWindow State: 000000ff00000000fd000000040000000000000156000002cbfc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000002cb000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f000002cbfc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003d000002cb000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004420000003efc0100000002fb0000000800540069006d00650100000000000004420000000000000000fb0000000800540069006d0065010000000000000450000000000000000000000413000002cb00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 + Selection: + collapsed: false + Tool Properties: + collapsed: false + Views: + collapsed: false + Width: 1391 + X: 282 + Y: 63 diff --git a/localization-devel-ws/rival/rival_localization/src/rival_localization.cpp b/localization-devel-ws/rival/rival_localization/src/rival_localization.cpp new file mode 100644 index 0000000..674453f --- /dev/null +++ b/localization-devel-ws/rival/rival_localization/src/rival_localization.cpp @@ -0,0 +1,449 @@ +#include "rival_localization/rival_localization.h" + +Rival::Rival() : Node("rival_localization"){ + + IMM imm; + model = imm; + initial = true; + + initialize(); +} + +void Rival::initialize() { + + this->declare_parameter("robot_name", "robot"); + this->declare_parameter("rival_name", "rival"); + this->declare_parameter("frequency", 10.); + // for play area + this->declare_parameter("x_max", 3.); + this->declare_parameter("x_min", 0.); + this->declare_parameter("y_max", 2.); + this->declare_parameter("y_min", 0.); + // for obstacle tracking (within_lock) + this->declare_parameter("vel_lpf_gain", 0.9); + this->declare_parameter("locking_rad", 0.3); + this->declare_parameter("lockrad_growing_rate", 0.3); + // for is_me + this->declare_parameter("is_me", 0.3); + // weights for the three sensors (the weight will be normalized depending on the combination) + + this->declare_parameter("cam_weight", 2.0); + this->declare_parameter("obs_weight", 6.0); + this->declare_parameter("side_weight", 2.0); + + // threshold for comparing poses + this->declare_parameter("cam_side_threshold", 0.2); + this->declare_parameter("side_obs_threshold", 0.2); + this->declare_parameter("obs_cam_threshold", 0.2); + // a list of crossed areas, each area as [x_min, x_max, y_min, y_max] + // This is stored as a flat vector, but you can interpret it as an n x 4 matrix in your code + this->declare_parameter>( + "crossed_areas", + {2.25, 3.0, 0.0, 0.15, + 2.55, 3.0, 0.65, 1.1} + ); + + + robot_name = this->get_parameter("robot_name").get_value(); + rival_name = this->get_parameter("rival_name").as_string(); + freq = this->get_parameter("frequency").as_double(); + // for play area + x_max = this->get_parameter("x_max").as_double(); + x_min = this->get_parameter("x_min").as_double(); + y_max = this->get_parameter("y_max").as_double(); + y_min = this->get_parameter("y_min").as_double(); + // for obstacle tracking (within_lock) + vel_lpf_gain = this->get_parameter("vel_lpf_gain").as_double(); + p_locking_rad = this->get_parameter("locking_rad").as_double(); // but what if rival is moving?? should increase if rival's moving! + lockrad_growing_rate = this->get_parameter("lockrad_growing_rate").as_double(); // 5e-2 meter per second + // for is_me + p_is_me = this->get_parameter("is_me").as_double(); + // weights for the three sensors (the weight will be normalized depending on the combination) + cam_weight = this->get_parameter("cam_weight").as_double(); + obs_weight = this->get_parameter("obs_weight").as_double(); + side_weight = this->get_parameter("side_weight").as_double(); + // threshold for comparing poses + cam_side_threshold = this->get_parameter("cam_side_threshold").as_double(); + side_obs_threshold = this->get_parameter("side_obs_threshold").as_double(); + obs_cam_threshold = this->get_parameter("obs_cam_threshold").as_double(); + // a list of crossed areas, each area as [x_min, x_max, y_min, y_max] + crossed_areas = this->get_parameter("crossed_areas").as_double_array(); + + // RCLCPP_INFO(this->get_logger(),"robot_name: %s, rival_name: %s", robot_name.c_str(), rival_name.c_str()); + + obstacles_sub = this->create_subscription("obstacles_to_map", 10, std::bind(&Rival::obstacles_callback, this, _1)); + cam_sub = this->create_subscription("/ceiling_rival/pose", 10, std::bind(&Rival::cam_callback, this, _1)); + side_obstacle_sub = this->create_subscription("/side/side_obstacles_to_map", 10, std::bind(&Rival::side_obstacles_callback, this, _1)); + robot_pose_sub = this->create_subscription("final_pose", 10, std::bind(&Rival::robot_pose_callback, this, _1)); + rival_raw_pub = this->create_publisher("raw_pose", 10); + rival_final_pub = this->create_publisher("rhino_pose", 10); + + br = std::make_shared(this); + timer_ = this->create_wall_timer(std::chrono::duration(1.0 / freq), std::bind(&Rival::timerCallback, this)); + + obstacle_ok = false; + locking_rad = p_locking_rad; + my_pose.x = 10, my_pose.y = 10; +} + +bool Rival::in_playArea_obs(geometry_msgs::msg::Point center) { + + bool ok = true; + + if (center.x > x_max || center.x < x_min) ok = false; + if (center.y > y_max || center.y < y_min) ok = false; + + return ok; +} + +bool Rival::in_crossed_area(geometry_msgs::msg::Point center) { + + bool tf = false; + // given a n * 4 matrix, it represents the crossed area of rectangle areas + // if the center is in any of the areas, return false + for (size_t i = 0; i < crossed_areas.size(); i += 4) { + if (center.x > crossed_areas[i] && center.x < crossed_areas[i + 1] && center.y > crossed_areas[i + 2] && center.y < crossed_areas[i + 3]) { + tf = true; + break; + } + } + + return tf; +} + +bool Rival::within_lock(geometry_msgs::msg::Point pre, geometry_msgs::msg::Point cur, double dt) { + + bool ok = true; + + locking_rad = locking_rad + sqrt(pow(rival_final_vel.x, 2) + pow(rival_final_vel.y, 2)) * dt; + double distance = sqrt(pow((pre.x - cur.x), 2) + pow((pre.y - cur.y), 2)); + + if (distance > locking_rad) ok = false; + + return ok; +} + +bool Rival::is_me(geometry_msgs::msg::Point center) { + + bool tf = false; + if (sqrt(pow((center.x - my_pose.x), 2) + pow((center.y - my_pose.y), 2)) < p_is_me) tf = true; + + return tf; +} + +geometry_msgs::msg::Vector3 Rival::lpf(double gain, geometry_msgs::msg::Vector3 pre, geometry_msgs::msg::Vector3 cur) { + + geometry_msgs::msg::Vector3 out; + out.x = gain * cur.x + (1.0 - gain) * pre.x; + out.y = gain * cur.y + (1.0 - gain) * pre.y; + + return out; +} + +void Rival::imm_filter() { + + double px, py, vx, vy; + + px = rival_raw_pose.x; + py = rival_raw_pose.y; + vx = rival_raw_vel.x; + vy = rival_raw_vel.y; + + if (!initial) { + + Eigen::VectorXd z; + + z.resize(4); + + z << px, py, vx, vy; + + model.updateOnce(rival_stamp.seconds(), &z); + } + else { + + Eigen::VectorXd x; + ModelGenerator gen; + + x.resize(6); + + //initial state + x << px, py, vx, vy, 0, 0; + + gen.generateIMMModel(rival_stamp.seconds(), x, model); + + initial = false; + } + + rival_final_pose.x = model.x_[0]; + rival_final_pose.y = model.x_[1]; + rival_final_vel.x = model.x_[2]; + rival_final_vel.y = model.x_[3]; +} + +void Rival::cam_callback(const geometry_msgs::msg::PoseStamped::SharedPtr msg) { + + cam_rival_pose.x = msg->pose.position.x; + cam_rival_pose.y = msg->pose.position.y; + cam_rival_pose.z = msg->pose.position.z; + cam_stamp = msg->header.stamp; +} + +void Rival::obstacles_callback(const obstacle_detector::msg::Obstacles::SharedPtr msg) { + + static bool first = false; + static geometry_msgs::msg::Point obstacle_pose_pre; + static geometry_msgs::msg::Vector3 obstacle_vel_pre; + static rclcpp::Time obstacle_stamp_pre; + static rclcpp::Time Obstacles_stamp_now; + Obstacles_stamp_now = msg->header.stamp; + double dt = Obstacles_stamp_now.seconds() - obstacle_stamp_pre.seconds(); + double dt_cam = Obstacles_stamp_now.seconds() - cam_stamp.seconds(); + double min_distance = 400; + + for (const obstacle_detector::msg::CircleObstacle& circle : msg->circles) { + + if (!in_playArea_obs(circle.center)) continue; + + if (!within_lock(obstacle_pose_pre, circle.center, dt)) continue; + + if (!first){ + + obstacle_stamp = msg->header.stamp; + obstacle_pose = circle.center; + first = true; + continue; + } + + double distance_; + if (dt_cam > 1) { // if camera is unavailable, use the rival's previous pose + distance_ = sqrt(pow((circle.center.x - rival_final_pose.x), 2) + pow((circle.center.y - rival_final_pose.y), 2)); + } else { // if camera is available, find the one closest to the rival + distance_ = sqrt(pow((circle.center.x - cam_rival_pose.x), 2) + pow((circle.center.y - cam_rival_pose.y), 2)); + camera_ok = true; + } + + obstacle_ok = true; + + if (distance_ <= min_distance) { + + min_distance = distance_; + obstacle_stamp = msg->header.stamp; + obstacle_pose = circle.center; + } + } + + if(obstacle_stamp.seconds() != obstacle_stamp_pre.seconds()){ + + obstacle_vel.x = (obstacle_pose.x - obstacle_pose_pre.x) / (obstacle_stamp.seconds() - obstacle_stamp_pre.seconds()); + obstacle_vel.y = (obstacle_pose.y - obstacle_pose_pre.y) / (obstacle_stamp.seconds() - obstacle_stamp_pre.seconds()); + obstacle_pose_pre = obstacle_pose; + obstacle_vel_pre = obstacle_vel; + obstacle_stamp_pre = obstacle_stamp; + } + + if (!obstacle_ok){ + + if (dt == 0) return; + if (locking_rad > 3) return; + locking_rad += locking_rad + dt * lockrad_growing_rate; + } + else + locking_rad = p_locking_rad; + + return; +} + +void Rival::side_obstacles_callback(const obstacle_detector::msg::Obstacles::SharedPtr msg) { + + double max_radius = 0.0; + + for (const obstacle_detector::msg::CircleObstacle& circle : msg->circles) { + + if (!in_playArea_obs(circle.center)) continue; // check if the obstacle is in the play area + // RCLCPP_INFO(this->get_logger(),"in play area: %f, %f", circle.center.x, circle.center.y); + if (is_me(circle.center)) continue; // ignore the one closest to our robot + // RCLCPP_INFO(this->get_logger(),"is not me: %f, %f", circle.center.x, circle.center.y); + if (in_crossed_area(circle.center)) continue; // ignore the ones in columns area, or other specified areas + // RCLCPP_INFO(this->get_logger(),"not in crossed area: %f, %f", circle.center.x, circle.center.y); + if (circle.radius > max_radius) { // find the obstacle with the largest radius + max_radius = circle.radius; + side_obstacle_pose = circle.center; + } + } + if (max_radius < 0.15) return; // no obstacle found, maybe the rival is blocked by something + // RCLCPP_INFO(this->get_logger(),"max radius: %f", max_radius); + // RCLCPP_INFO(this->get_logger(),"side_obstacle: %f, %f", side_obstacle_pose.x, side_obstacle_pose.y); + side_obstacle_ok = true; +} + +void Rival::robot_pose_callback(const geometry_msgs::msg::PoseWithCovarianceStamped::SharedPtr msg) { + my_pose.x = msg->pose.pose.position.x; + my_pose.y = msg->pose.pose.position.y; +} + +void Rival::fusion() { + + rival_ok = false; + + double distance_cam_obs = 10; + double distance_side_obs = 10; + double distance_cam_side = 10; + + // compare the three poses, if two of them are ok + if(camera_ok && side_obstacle_ok){ + distance_cam_side = sqrt(pow((side_obstacle_pose.x - cam_rival_pose.x), 2) + pow((side_obstacle_pose.y - cam_rival_pose.y), 2)); + } + if(camera_ok && obstacle_ok){ + distance_cam_obs = sqrt(pow((obstacle_pose.x - cam_rival_pose.x), 2) + pow((obstacle_pose.y - cam_rival_pose.y), 2)); + } + if(side_obstacle_ok && obstacle_ok){ + distance_side_obs = sqrt(pow((side_obstacle_pose.x - obstacle_pose.x), 2) + pow((side_obstacle_pose.y - obstacle_pose.y), 2)); + } + + // param for fusing the three + if(distance_cam_side < cam_side_threshold && distance_cam_obs < obs_cam_threshold && distance_side_obs < side_obs_threshold){ // if all of them agree + double total_weight = cam_weight + obs_weight + side_weight; // normalize the weights + rival_raw_pose.x = cam_rival_pose.x * (cam_weight / total_weight) + obstacle_pose.x * (obs_weight / total_weight) + side_obstacle_pose.x * (side_weight / total_weight); + rival_raw_pose.y = cam_rival_pose.y * (cam_weight / total_weight) + obstacle_pose.y * (obs_weight / total_weight) + side_obstacle_pose.y * (side_weight / total_weight); + rival_ok = true; + } + else if(distance_cam_obs < obs_cam_threshold){ + double total_weight = cam_weight + obs_weight; + rival_raw_pose.x = cam_rival_pose.x * (cam_weight / total_weight) + obstacle_pose.x * (obs_weight / total_weight); + rival_raw_pose.y = cam_rival_pose.y * (cam_weight / total_weight) + obstacle_pose.y * (obs_weight / total_weight); + rival_ok = true; + } + else if(distance_side_obs < side_obs_threshold){ + double total_weight = obs_weight + side_weight; + rival_raw_pose.x = obstacle_pose.x * (obs_weight / total_weight) + side_obstacle_pose.x * (side_weight / total_weight); + rival_raw_pose.y = obstacle_pose.y * (obs_weight / total_weight) + side_obstacle_pose.y * (side_weight / total_weight); + rival_ok = true; + } + else if(distance_cam_side < cam_side_threshold){ + double total_weight = cam_weight + side_weight; + rival_raw_pose.x = cam_rival_pose.x * (cam_weight / total_weight) + side_obstacle_pose.x * (side_weight / total_weight); + rival_raw_pose.y = cam_rival_pose.y * (cam_weight / total_weight) + side_obstacle_pose.y * (side_weight / total_weight); + rival_ok = true; + } + else{ // if none of them agree, use single one: cam -> side -> obs (what should be the priority?) + if(camera_ok){ + rival_raw_pose = cam_rival_pose; + // RCLCPP_INFO(this->get_logger(),"cam ok"); + rival_ok = true; + } + else if(side_obstacle_ok){ + rival_raw_pose = side_obstacle_pose; + // RCLCPP_INFO(this->get_logger(),"side_obstacle ok"); + rival_ok = true; + } + else if(obstacle_ok){ + rival_raw_pose = obstacle_pose; + // RCLCPP_INFO(this->get_logger(),"obstacle ok"); + rival_ok = true; + } + } + // reset the flags + obstacle_ok = false; + camera_ok = false; + side_obstacle_ok = false; +} + + +void Rival::timerCallback() { + + static geometry_msgs::msg::Point rival_pose_pre; + static geometry_msgs::msg::Vector3 rival_vel_pre; + + fusion(); + + if(rival_ok){ + // RCLCPP_INFO(this->get_logger(),"rival ok"); + rival_stamp = clock.now(); + + rival_raw_vel = lpf(vel_lpf_gain, rival_vel_pre, rival_raw_vel); + + // 比較是否有 imm filter 的差異 + publish_rival_raw(); + publish_rival_final(); + + broadcast_rival_tf(); + rival_pose_pre = rival_final_pose; + rival_vel_pre = rival_final_vel; + rival_ok = false; + } +} + +void Rival::publish_rival_raw() { + + rival_output.header.stamp = rival_stamp; + rival_output.header.frame_id = "/map"; + rival_output.child_frame_id = rival_name + "/raw_pose"; + rival_output.pose.pose.position = rival_raw_pose; + rival_output.pose.pose.orientation.w = 1; + rival_output.twist.twist.linear = rival_raw_vel; + + rival_raw_pub->publish(rival_output); + + // RCLCPP_INFO(this->get_logger(),"raw Publish:"); + // RCLCPP_INFO(this->get_logger(),"center:( %f , %f )",rival_raw_pose.x, rival_raw_pose.y); + // RCLCPP_INFO(this->get_logger(),"velocity:( %f , %f )", rival_raw_vel.x, rival_raw_vel.y); + // RCLCPP_INFO(this->get_logger(),"time stamp: %f", rival_stamp.seconds()); + // RCLCPP_INFO(this->get_logger(),"-------------"); +} + +void Rival::publish_rival_final() { + + imm_filter(); + + rival_output.header.stamp = rival_stamp; + rival_output.header.frame_id = "/map"; + rival_output.child_frame_id = rival_name + "/final_pose"; + rival_output.pose.pose.position = rival_final_pose; + rival_output.pose.pose.orientation.w = 1; + rival_output.twist.twist.linear = rival_final_vel; + + rival_final_pub->publish(rival_output); + + // RCLCPP_INFO(this->get_logger(),"final Publish:"); + // RCLCPP_INFO(this->get_logger(),"center:( %f , %f )",rival_final_pose.x, rival_final_pose.y); + // RCLCPP_INFO(this->get_logger(),"velocity:( %f , %f )", rival_final_vel.x, rival_final_vel.y); + // RCLCPP_INFO(this->get_logger(),"time stamp: %f", rival_stamp.seconds()); + // RCLCPP_INFO(this->get_logger(),"-------------"); + +} + +void Rival::broadcast_rival_tf() { + + static rclcpp::Time stamp_prev; + + if (stamp_prev.nanoseconds() != rival_stamp.nanoseconds()) { + + geometry_msgs::msg::TransformStamped transformStamped; + + transformStamped.header.stamp = rival_stamp; + transformStamped.header.frame_id = "/map"; + transformStamped.child_frame_id = rival_name + "/base_footprint"; + transformStamped.transform.translation.x = rival_final_pose.x; + transformStamped.transform.translation.y = rival_final_pose.y; + transformStamped.transform.translation.z = rival_final_pose.z; + + tf2::Quaternion q; + q.setRPY(0, 0, 0); // Roll, Pitch, Yaw + transformStamped.transform.rotation.x = q.x(); + transformStamped.transform.rotation.y = q.y(); + transformStamped.transform.rotation.z = q.z(); + transformStamped.transform.rotation.w = q.w(); + + br->sendTransform(transformStamped); + } + stamp_prev = rival_stamp; +} + +int main(int argc, char * argv[]){ + + rclcpp::init(argc, argv); + rclcpp::spin(std::make_shared()); + rclcpp::shutdown(); + + return 0; +} \ No newline at end of file diff --git a/localization-devel-ws/usb.sh b/localization-devel-ws/usb.sh index b508089..e8723c8 100755 --- a/localization-devel-ws/usb.sh +++ b/localization-devel-ws/usb.sh @@ -27,10 +27,12 @@ else ls -l $DEVICE_PATH # IMU udev rules - PACKAGES_PATH=/home/user/localization-ws-ros1/src + PACKAGES_PATH=/home/user/localization-ws/src sudo /lib/systemd/systemd-udevd --daemon cd $PACKAGES_PATH/phidgets_drivers/phidgets_api sudo cp debian/udev /etc/udev/rules.d/99-phidgets.rules + sudo udevadm control --reload-rules + fi # RPLiDAR port diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/.gitignore b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/.gitignore deleted file mode 100644 index 2fdd8d0..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -.vscode/ -build/ -build/* -devel/* -.YDLidar-SDK/build/ -obstacle_detector/* -*.swp \ No newline at end of file diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/README.md b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/README.md deleted file mode 100644 index f66a21d..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/README.md +++ /dev/null @@ -1,143 +0,0 @@ -# Eurobot-2024-Localization - -> Eurobot localization workspace for 2024 - -- elaborate README allow a team to grow! -- clear commit messages are benefitial for all! - -## Install - -> with docker - -```bash -# Move to your ws -mkdir devel -mkdir build -mkdir src -cd src -git clone git@github.com:DIT-ROBOTICS/Eurobot-2024-Localization.git -git clone git@github.com:DIT-ROBOTICS/Eurobot-2024-Localization-envs.git -git clone git@github.com:DIT-ROBOTICS/eurobot_msgs.git -``` - -### Build - -```bash -sudo docker compose -f /home/localization/Eurobot-2024/src/Eurobot-2024-Localization-envs/compose-build.yaml up --build -``` - -### Run - -```bash -sudo docker compose -f /home/localization/Eurobot-2024/src/Eurobot-2024-Localization-envs/compose-run.yaml up -``` - -#### send ready signal to start - -> Publishing a ready signal is necessary for determining Blue/Yellow side. -> Without that, localization program will not start to work. - -Blue -```bash -rostopic pub /robot/startup/ready_signal geometry_msgs/PointStamped "{header: {frame_id: '0'}, point: {x: 2.7, y: 1.0, z: 180.0}}" -1 -``` -Yellow -```bash -rostopic pub /robot/startup/ready_signal geometry_msgs/PointStamped "{header: {frame_id: '1'}, point: {x: 0.3, y: 1.0, z: 0.0}}" -1 -``` - -> Modify the value in `point` if it's necessary. -> z: in degree - -### Develop - -```bash -cd Eurobot-2024-Localization-envs -docker compose -f docker-compose.yaml up -d -docker exec -it localization bash -# in the container -source Eurobot-2024-Localization/rosenv.sh -``` - -## Structure - -``` -. -└── Your Workspace - └── build - └── devel - └── src - ├── Eurobot-2024-Localization - │   ├── .YDLidar-SDK - │   ├── eurobot_localization - │   ├── rival_localization - │   ├── lidar - │   │   ├── lidar_localization - │   │   └── ydlidar_ros_driver - │   ├── local_filter - │   │   ├── imu - │   │   │   ├── imu_drive - │   │   │   └── phidgets_drivers - │   │   ├── local_filter - │   │   └── odometry - │   │      ├── odometry - │   │      ├── rosserial_msgs - │   │      └── rosserial_server - │   ├── simulation - │   └── vive - └── Eurobot-2024-Localization-envs - -``` - - -## Architecture -> Local filter ( IMU + Odometry ) + global filter ( LiDAR ) - -### Local filter (unupdated) - -- Place all of the required component in local filter -- Run with rosserial and imu firmmware -```bash=1 -roslaunch local_filter local_filter.launch -``` -- Run without rosserial but imu firmware -```bash=1 -roslaunch local_filter local_filter_no_comm.launch # no such file now! -``` -- Run without rosserial and imu firmware -```bash=1 -roslaunch local_filter local_filter_no_firmware.launch # no such file now! -``` - -### Global filter (unupdated) - -- Place LiDAR driver and triangle localization in lidar -- Place global filter in eurobot_localization -- Run with only triangle localization -```bash=1 -roslaunch lidar_localization lidar_localization_2023.launch # no such file now! -``` -- Run with only lidar driver and triangle localization -```bash=1 -roslaunch lidar_localization lidar_with_driver.launch -``` -- Run global filter with lidar driver -```bash=1 -roslaunch eurobot_localization global_ekf.launch -``` -- Run global filter without lidar driver -```bash=1 -roslaunch eurobot_localization global_ekf_without_lidar.launch # no such file now! -``` - -### Together - -- Run all of the localization component -```bash=1 -roslaunch eurobot_localization eurobot_localization.launch -``` - -### Local machine setup configure - -- Port name -- Static TF for laser frame and imu frame diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/RECORD.md b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/RECORD.md deleted file mode 100644 index 24d28fd..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/RECORD.md +++ /dev/null @@ -1,42 +0,0 @@ -# Eurobot-Localization -> Simulation on PC - -## Setup - -### ROS setup -```bash -roscore & -``` - -### Prepare simulation data -> Odometry raw data to topic [/Toposition] -> IMU raw data to topic [/imu/data_raw] - -- Publish odometry data -```bash -rostopic pub -r 50 /Toposition geometry_msgs/Twist "linear: - x: 0.0 - y: 0.0 - z: 0.0 -angular: - x: 0.0 - y: 0.0 - z: 0.0" -``` -- Publish imu data -```bash -rostopic pub -r 50 /imu/data sensor_msgs/Imu "header: - seq: 0 - stamp: {secs: 0, nsecs: 0} - frame_id: '' -orientation: {x: 0.0, y: 0.0, z: 0.0, w: 0.0} -orientation_covariance: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] -angular_velocity: {x: 0.0, y: 0.0, z: 0.0} -angular_velocity_covariance: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] -linear_acceleration: {x: 0.0, y: 0.0, z: 0.0} -linear_acceleration_covariance: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]" -``` -- Or just run simulation ( but need to kill by other step ) -```bash -./simulation.sh -``` diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/.clang-format b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/.clang-format deleted file mode 100644 index 0b61ea8..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/.clang-format +++ /dev/null @@ -1,65 +0,0 @@ ---- -BasedOnStyle: Google -AccessModifierOffset: -2 -ConstructorInitializerIndentWidth: 2 -AlignEscapedNewlinesLeft: false -AlignTrailingComments: true -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false -AllowShortFunctionsOnASingleLine: None -AlwaysBreakTemplateDeclarations: true -AlwaysBreakBeforeMultilineStrings: true -BreakBeforeBinaryOperators: false -BreakBeforeTernaryOperators: false -BreakConstructorInitializersBeforeComma: true -BinPackParameters: true -ColumnLimit: 120 -ConstructorInitializerAllOnOneLineOrOnePerLine: true -DerivePointerBinding: false -PointerBindsToType: true -ExperimentalAutoDetectBinPacking: false -IndentCaseLabels: true -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: None -ObjCSpaceBeforeProtocolList: true -PenaltyBreakBeforeFirstCallParameter: 19 -PenaltyBreakComment: 60 -PenaltyBreakString: 1 -PenaltyBreakFirstLessLess: 1000 -PenaltyExcessCharacter: 1000 -PenaltyReturnTypeOnItsOwnLine: 90 -SpacesBeforeTrailingComments: 2 -Cpp11BracedListStyle: false -Standard: Auto -IndentWidth: 4 -TabWidth: 4 -UseTab: Never -IndentFunctionDeclarationAfterType: false -SpacesInParentheses: false -SpacesInAngles: false -SpaceInEmptyParentheses: false -SpacesInCStyleCastParentheses: false -SpaceAfterControlStatementKeyword: true -SpaceBeforeAssignmentOperators: true -ContinuationIndentWidth: 4 -SortIncludes: false -SpaceAfterCStyleCast: false - -# Configure each individual brace in BraceWrapping -BreakBeforeBraces: Custom - -# Control of individual brace wrapping cases -BraceWrapping: { - AfterClass: 'true' - AfterControlStatement: 'true' - AfterEnum : 'true' - AfterFunction : 'true' - AfterNamespace : 'true' - AfterStruct : 'true' - AfterUnion : 'true' - BeforeCatch : 'true' - BeforeElse : 'true' - IndentBraces : 'false' -} -... diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/.gitignore b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/.gitignore deleted file mode 100644 index dbe9c82..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.vscode/ \ No newline at end of file diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/CMakeLists.txt b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/CMakeLists.txt deleted file mode 100644 index fd28794..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/CMakeLists.txt +++ /dev/null @@ -1,226 +0,0 @@ -cmake_minimum_required(VERSION 3.0.2) -project(eurobot_localization) - -## Compile as C++11, supported in ROS Kinetic and newer -# add_compile_options(-std=c++11) - -## Find catkin macros and libraries -## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) -## is used, also find other catkin packages -find_package(catkin REQUIRED COMPONENTS - geometry_msgs - nav_msgs - roscpp - rospy - std_msgs - obstacle_detector - startup_msgs -) - -## System dependencies are found with CMake's conventions -# find_package(Boost REQUIRED COMPONENTS system) - - -## Uncomment this if the package has a setup.py. This macro ensures -## modules and global scripts declared therein get installed -## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html -# catkin_python_setup() - -################################################ -## Declare ROS messages, services and actions ## -################################################ - -## To declare and build messages, services or actions from within this -## package, follow these steps: -## * Let MSG_DEP_SET be the set of packages whose message types you use in -## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...). -## * In the file package.xml: -## * add a build_depend tag for "message_generation" -## * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET -## * If MSG_DEP_SET isn't empty the following dependency has been pulled in -## but can be declared for certainty nonetheless: -## * add a exec_depend tag for "message_runtime" -## * In this file (CMakeLists.txt): -## * add "message_generation" and every package in MSG_DEP_SET to -## find_package(catkin REQUIRED COMPONENTS ...) -## * add "message_runtime" and every package in MSG_DEP_SET to -## catkin_package(CATKIN_DEPENDS ...) -## * uncomment the add_*_files sections below as needed -## and list every .msg/.srv/.action file to be processed -## * uncomment the generate_messages entry below -## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...) - -## Generate messages in the 'msg' folder -# add_message_files( -# FILES -# Message1.msg -# Message2.msg -# ) - -# Generate services in the 'srv' folder -# add_service_files( -# FILES -# ) - -## Generate actions in the 'action' folder -# add_action_files( -# FILES -# Action1.action -# Action2.action -# ) - -# Generate added messages and services with any dependencies listed here -generate_messages( - DEPENDENCIES - geometry_msgs - nav_msgs - std_msgs -) - -################################################ -## Declare ROS dynamic reconfigure parameters ## -################################################ - -## To declare and build dynamic reconfigure parameters within this -## package, follow these steps: -## * In the file package.xml: -## * add a build_depend and a exec_depend tag for "dynamic_reconfigure" -## * In this file (CMakeLists.txt): -## * add "dynamic_reconfigure" to -## find_package(catkin REQUIRED COMPONENTS ...) -## * uncomment the "generate_dynamic_reconfigure_options" section below -## and list every .cfg file to be processed - -## Generate dynamic reconfigure parameters in the 'cfg' folder -# generate_dynamic_reconfigure_options( -# cfg/DynReconf1.cfg -# cfg/DynReconf2.cfg -# ) - -################################### -## catkin specific configuration ## -################################### -## The catkin_package macro generates cmake config files for your package -## Declare things to be passed to dependent projects -## INCLUDE_DIRS: uncomment this if your package contains header files -## LIBRARIES: libraries you create in this project that dependent projects also need -## CATKIN_DEPENDS: catkin_packages dependent projects also need -## DEPENDS: system dependencies of this project that dependent projects also need -catkin_package( -# INCLUDE_DIRS include -# LIBRARIES eurobot_localization - CATKIN_DEPENDS geometry_msgs nav_msgs roscpp rospy std_msgs message_runtime -# DEPENDS system_lib -) - -########### -## Build ## -########### - -## Specify additional locations of header files -## Your package locations should be listed before other locations -include_directories( - include - ${catkin_INCLUDE_DIRS} -) - -## Declare a C++ library -# add_library(${PROJECT_NAME} -# src/${PROJECT_NAME}/eurobot_localization.cpp -# ) - -## Add cmake target dependencies of the library -## as an example, code may need to be generated before libraries -## either from message generation or dynamic reconfigure -# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) - - -## Declare a C++ executable -## With catkin_make all packages are built within a single CMake context -## The recommended prefix ensures that target names across packages don't collide -# add_executable(${PROJECT_NAME}_node src/eurobot_localization_node.cpp) -add_executable(ekf src/ekf.cpp) -add_executable(run src/run.cpp) -add_executable(uc_ekf src/uc_ekf.cpp) -# add_executable(rival_localization src/rival_localization.cpp) -# add_executable(rival_localization_plus src/rival_localization_plus.cpp) - -## Rename C++ executable without prefix -## The above recommended prefix causes long target names, the following renames the -## target back to the shorter version for ease of user use -## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node" -# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "") -target_link_libraries(ekf ${catkin_LIBRARIES}) -target_link_libraries(run ${catkin_LIBRARIES}) -target_link_libraries(uc_ekf ${catkin_LIBRARIES}) -# target_link_libraries(rival_localization ${catkin_LIBRARIES}) -# target_link_libraries(rival_localization_plus ${catkin_LIBRARIES}) - -## Add cmake target dependencies of the executable -## same as for the library above -# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) -add_dependencies(ekf ${catkin_EXPORTED_TARGETS}) -add_dependencies(run ${catkin_EXPORTED_TARGETS}) -add_dependencies(uc_ekf ${catkin_EXPORTED_TARGETS}) -# add_dependencies(rival_localization ${catkin_EXPORTED_TARGETS}) -# add_dependencies(rival_localization_plus ${catkin_EXPORTED_TARGETS}) - -## Specify libraries to link a library or executable target against -# target_link_libraries(${PROJECT_NAME}_node -# ${catkin_LIBRARIES} -# ) - -############# -## Install ## -############# - -# all install targets should use catkin DESTINATION variables -# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html - -## Mark executable scripts (Python etc.) for installation -## in contrast to setup.py, you can choose the destination -# catkin_install_python(PROGRAMS -# scripts/my_python_script -# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} -# ) - -## Mark executables for installation -## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html -# install(TARGETS ${PROJECT_NAME}_node -# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} -# ) - -## Mark libraries for installation -## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html -# install(TARGETS ${PROJECT_NAME} -# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} -# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} -# RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION} -# ) - -## Mark cpp header files for installation -# install(DIRECTORY include/${PROJECT_NAME}/ -# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} -# FILES_MATCHING PATTERN "*.h" -# PATTERN ".svn" EXCLUDE -# ) - -## Mark other files for installation (e.g. launch and bag files, etc.) -# install(FILES -# # myfile1 -# # myfile2 -# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} -# ) - -############# -## Testing ## -############# - -## Add gtest based cpp test target and link libraries -# catkin_add_gtest(${PROJECT_NAME}-test test/test_eurobot_localization.cpp) -# if(TARGET ${PROJECT_NAME}-test) -# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}) -# endif() - -## Add folders to be run by python nosetests -# catkin_add_nosetests(test) diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/README.md b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/README.md deleted file mode 100644 index 2261710..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/README.md +++ /dev/null @@ -1 +0,0 @@ -# eurobot_localization diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/config/eurobot_localization.yaml b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/config/eurobot_localization.yaml deleted file mode 100644 index 08af013..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/config/eurobot_localization.yaml +++ /dev/null @@ -1,118 +0,0 @@ - -################################ - -# use ready signal or not -# side and initial_pose only used when ready_signal_active is false - -localization: - ready_signal_active: false - side: "0" - initial_pose: - x: 2.7 - y: 1.0 - z: 180.0 - # side: "1" - # initial_pose: - # x: 0.3 - # y: 1.0 - # z: 0.0 - -################################ - -# ekf.cpp - -ekf: - - chassis_type: 1 # diff: 0, omni: 1 - odom_freq: 100 # Odometry = final_pose frequency - timer_frequency: 10 # gps frequency - velocity_lpf_gain: 0.5 - - # initial covariance - initial_cov_x: 0.0001 - initial_cov_x_y: 0.0001 - initial_cov_x_yaw: 0.0001 - initial_cov_y_x: 0.0001 - initial_cov_y_y: 0.0001 - initial_cov_y_yaw: 0.0001 - initial_cov_yaw_x: 0.0001 - initial_cov_yaw_y: 0.0001 - initial_cov_yaw_yaw: 0.0001 - - ################################ - - diff_drive: - - # differential drive model const for ucekf - predict_cov_a1: 1.5 - predict_cov_a2: 2.5 - predict_cov_a3: 1.5 - predict_cov_a4: 2.5 - - # measurement covariance for ucekf - update_cov_1: 0.1 - update_cov_2: 0.1 - update_cov_3: 0.1 - - omni_drive: - - # omnidrive model const for ucekf - predict_const_x: 0.5 - predict_const_y: 0.5 - predict_const_theta: 0.5 - - # measurement covariance for ucekf - update_cov_1: 0.1 - update_cov_2: 0.1 - update_cov_3: 0.1 - - # for ucekf - mini_likelihood: -10000.0 - mini_likelihood_update: 0.35 - max_obstacle_distance: 0.16 # Should be a little larger than "max_obstacle_distance" in global_filter_basic.yaml - -################################ - -# two sides - -Blue: - - # Blue - beacon_ax: -0.094 - beacon_ay: 0.052 - - beacon_bx: -0.094 - beacon_by: 1.948 - - beacon_cx: 3.094 - beacon_cy: 1.0 - - # y - # ^ - # | - # b|----------| - # | | - # | |c - # | | - # (0) a|----------| -->x - -Yellow: - - # Yellow - beacon_ax: 3.094 - beacon_ay: 0.052 - - beacon_bx: 3.094 - beacon_by: 1.948 - - beacon_cx: -0.094 - beacon_cy: 1.0 - - # y - # ^ - # | - # |----------|b - # | | - # c| | - # | | - # (0) |----------|a -->x diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/include/ekf.h b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/include/ekf.h deleted file mode 100644 index ec6e054..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/include/ekf.h +++ /dev/null @@ -1,191 +0,0 @@ -#include -#include -#include -// tf2 -// #include -#include -#include -#include -#include -#include -#include -// for matrix calculate -#include -#include -// msg -#include -#include -#include -#include -#include -#include "obstacle_detector/Obstacles.h" -#include -#include -#include - -struct RobotState -{ - Eigen::Vector3d mu; - Eigen::Matrix3d sigma; -}; - -class Ekf -{ - public: - Ekf(ros::NodeHandle& nh, ros::NodeHandle& nh_local) : nh_(nh), nh_local_(nh_local){}; - void initialize(); - - private: - // ekf - void predict_diff_ekf(double v, double w, double dt, Eigen::Matrix3d odom_sigma); - void predict_diff_ucekf(double v, double w, double dt); - void predict_omni_ekf(double v_x, double v_y, double w, double dt, Eigen::Matrix3d odom_sigma); - void predict_omni_ucekf(double v_x, double v_y, double w, double dt); - void update_landmark(); - void update_gps(Eigen::Vector3d gps_pose, Eigen::Matrix3d gps_cov); - - // several util function - double euclideanDistance(Eigen::Vector2d a, Eigen::Vector3d b); - double euclideanDistance(Eigen::Vector2d a, Eigen::Vector2d b); - double angleLimitChecking(double theta); - Eigen::Vector3d safeMinusTheta(Eigen::Vector3d a, Eigen::Vector3d b); - Eigen::Vector3d cartesianToPolar(Eigen::Vector2d point, Eigen::Vector3d origin); - std::tuple cartesianToPolarWithH(Eigen::Vector2d point, Eigen::Vector3d origin); - Eigen::Vector2d tfBasefpToMap(Eigen::Vector2d point, Eigen::Vector3d robot_pose); - double degToRad(double deg) - { - return deg * M_PI / 180.0; - } - - // for ros - void setposeCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& pose_msg); - void odomCallback(const nav_msgs::Odometry::ConstPtr& odom_msg); - void obstaclesCallback(const obstacle_detector::Obstacles::ConstPtr& obstacle_msg); - void gpsCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& pose_msg); - void beaconCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& pose_msg); - void publishEkfPose(const ros::Time& stamp); - void publishUpdateBeacon(const ros::Time& stamp); - void broadcastEkfTransform(const nav_msgs::Odometry::ConstPtr& odom_msg); - void updateTimerCallback(const ros::TimerEvent &e); - - // for beacon position in map std::list{ax, ay, bx, by, cx, cy} - double p_beacon_ax_; - double p_beacon_ay_; - double p_beacon_bx_; - double p_beacon_by_; - double p_beacon_cx_; - double p_beacon_cy_; - double p_beacon_dx_; - double p_beacon_dy_; - - // ready process - bool ready; - ros::ServiceClient ready_client; - ros::Subscriber ready_sub; - void readyCallback(const geometry_msgs::PointStamped::ConstPtr& msg); - ros::ServiceClient scan_client; - std_srvs::Empty scan_srv; - bool ready_signal_active; - - //fast spin srv - ros::ServiceServer fast_spin_server; - bool fast_spin(std_srvs::SetBool::Request &req, std_srvs::SetBool::Response &res); - bool is_spinning; - - // Timer frequency - double p_timer_frequency_; - - std::list beacon_in_map_; - - // for beacon piller detection - bool if_new_obstacles_; - std::list beacon_from_scan_; - - // for debug - std::vector update_beacon_; - - // for robot state - RobotState robotstate_; - RobotState vive_state_; - RobotState lidar_state_; - double p_odom_freq_; - double imu_w; - double dt_; - double t_last; - bool first_cb; - - int chassis_type; - - // Check update - bool update_lidar_; - bool update_vive_; - - // ekf parameter - // motion covariance - // diff - double p_a1_; - double p_a2_; - double p_a3_; - double p_a4_; - // omni - double p_const_x; - double p_const_y; - double p_const_theta; - Eigen::DiagonalMatrix P_omni_model_; - - // measure noise - double p_Q1_; - double p_Q2_; - double p_Q3_; - Eigen::DiagonalMatrix Q_; - // gps measurement - bool if_gps; - Eigen::Vector3d gps_mu; - Eigen::Matrix3d gps_sigma; - bool if_beacon; - Eigen::Vector3d beacon_mu; - Eigen::Matrix3d beacon_sigma; - - // set minimum likelihood value - double p_mini_likelihood_; - double p_mini_likelihood_update_; - double p_max_obstacle_distance_; - - // ros parameter - std::string p_robot_name_; - double p_initial_x_; - double p_initial_y_; - double p_initial_theta_deg_; - - double p_velocity_lpf_gain_; - - // ros node - ros::NodeHandle nh_; - ros::NodeHandle nh_local_; - - // initial pose - ros::Subscriber setpose_sub_; - // measurement data - ros::Subscriber odom_sub_; - ros::Subscriber imu_sub_; - ros::Subscriber raw_obstacles_sub_; - ros::Subscriber gps_sub_; - ros::Subscriber vive_sub_; - ros::Subscriber beacon_sub_; - - // Publisher - ros::Publisher ekf_pose_pub_; - tf2_ros::TransformBroadcaster br_; - - // for debug - ros::Publisher update_beacon_pub_; - ros::Publisher global_filter_pub_; - tf2_ros::Buffer tfBuffer; - - // Update timer - ros::Timer update_timer_; - - // for function time calculation - int count_; - double duration_; -}; diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/include/uc_ekf.h b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/include/uc_ekf.h deleted file mode 100644 index a8a1cec..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/include/uc_ekf.h +++ /dev/null @@ -1,121 +0,0 @@ -#include -#include -#include -// tf2 -// #include -#include -#include -#include -#include -// for matrix calculate -#include -#include -// msg -#include -#include -#include -#include -#include "obstacle_detector/Obstacles.h" - -struct RobotState -{ - Eigen::Vector3d mu; - Eigen::Matrix3d sigma; -}; - -class Ekf -{ - public: - Ekf(ros::NodeHandle& nh) : nh_(nh){}; - void initialize(); - - private: - // ekf - void update_landmark(); - - // several util function - double euclideanDistance(Eigen::Vector2d a, Eigen::Vector3d b); - double euclideanDistance(Eigen::Vector2d a, Eigen::Vector2d b); - double angleLimitChecking(double theta); - Eigen::Vector3d safeMinusTheta(Eigen::Vector3d a, Eigen::Vector3d b); - Eigen::Vector3d cartesianToPolar(Eigen::Vector2d point, Eigen::Vector3d origin); - std::tuple cartesianToPolarWithH(Eigen::Vector2d point, Eigen::Vector3d origin); - double degToRad(double deg) - { - return deg * M_PI / 180.0; - } - - // for ros - void setposeCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& pose_msg); - void odomCallback(const nav_msgs::Odometry::ConstPtr& odom_msg); - void imuCallback(const sensor_msgs::Imu::ConstPtr& imu_msg); - void obstaclesCallback(const obstacle_detector::Obstacles::ConstPtr& obstacle_msg); - void gpsCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& pose_msg); - void beaconCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& pose_msg); - void publishEkfPose(const ros::Time& stamp); - void publishUpdateBeacon(const ros::Time& stamp); - void broadcastEkfTransform(const nav_msgs::Odometry::ConstPtr& odom_msg); - - // for beacon position in map std::list{ax, ay, bx, by, cx, cy} - double p_beacon_ax_; - double p_beacon_ay_; - double p_beacon_bx_; - double p_beacon_by_; - double p_beacon_cx_; - double p_beacon_cy_; - std::list beacon_in_map_; - - // for beacon piller detection - bool if_new_obstacles_; - std::list beacon_from_scan_; - - // for debug - std::vector update_beacon_; - - // for robot state - RobotState robotstate_; - - // ekf parameter - // measure noise - double p_Q1_; - double p_Q2_; - double p_Q3_; - Eigen::DiagonalMatrix Q_; - // gps measurement - bool if_gps; - Eigen::Vector3d gps_mu; - Eigen::Matrix3d gps_sigma; - bool if_beacon; - Eigen::Vector3d beacon_mu; - Eigen::Matrix3d beacon_sigma; - Eigen::Vector2d Ekf::tfBasefpToMap(Eigen::Vector2d point, Eigen::Vector3d robot_pose); - - // set minimum likelihood value - double p_mini_likelihood_; - double p_mini_likelihood_update_; - double p_max_obstacle_distance_; - - // ros parameter - std::string p_robot_name_; - double p_initial_x_; - double p_initial_y_; - double p_initial_theta_deg_; - - // ros node - ros::NodeHandle nh_; - // initial pose - ros::Subscriber setpose_sub_; - // measurement data - ros::Subscriber odom_sub_; - ros::Subscriber raw_obstacles_sub_; - - // Publisher - ros::Publisher ekf_pose_pub_; - tf2_ros::TransformBroadcaster br_; - // for debug - ros::Publisher update_beacon_pub_; - - // for function time calculation - int count_; - double duration_; -}; diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/eurobot_localization.launch b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/eurobot_localization.launch deleted file mode 100644 index a28f4f6..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/eurobot_localization.launch +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/eurobot_localization_robots.launch b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/eurobot_localization_robots.launch deleted file mode 100644 index c84f6f0..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/eurobot_localization_robots.launch +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/gps/global_ekf.launch b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/gps/global_ekf.launch deleted file mode 100644 index 930d356..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/gps/global_ekf.launch +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/rival/rival_localization.launch b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/rival/rival_localization.launch deleted file mode 100644 index 7e15c3d..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/rival/rival_localization.launch +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/rival/rival_localization_plus.launch b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/rival/rival_localization_plus.launch deleted file mode 100644 index 4836a61..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/rival/rival_localization_plus.launch +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/run.launch b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/run.launch deleted file mode 100644 index 1c2a44b..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/run.launch +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/rviz/eurobot_localization_rviz.launch b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/rviz/eurobot_localization_rviz.launch deleted file mode 100644 index 7ad13f6..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/launch/rviz/eurobot_localization_rviz.launch +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/package.xml b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/package.xml deleted file mode 100644 index 832e0fb..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/package.xml +++ /dev/null @@ -1,76 +0,0 @@ - - - eurobot_localization - 0.0.0 - The eurobot_localization package - - - - - root - - - - - - TODO - - - - - - - - - - - - - - - - - - - - - - - - message_generation - - - - - - message_runtime - - - - - catkin - geometry_msgs - nav_msgs - roscpp - rospy - std_msgs - obstacle_detector - geometry_msgs - nav_msgs - roscpp - rospy - std_msgs - geometry_msgs - nav_msgs - roscpp - rospy - std_msgs - obstacle_detector - - - - - - - - diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/rviz/eurobot_localization.rviz b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/rviz/eurobot_localization.rviz deleted file mode 100644 index 9b5f7cd..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/rviz/eurobot_localization.rviz +++ /dev/null @@ -1,308 +0,0 @@ -Panels: - - Class: rviz/Displays - Help Height: 0 - Name: Displays - Property Tree Widget: - Expanded: - - /Grid1/Offset1 - - /Odometry1/Shape1 - - /Odometry2/Shape1 - Splitter Ratio: 0.6424418687820435 - Tree Height: 381 - - Class: rviz/Selection - Name: Selection - - Class: rviz/Tool Properties - Expanded: - - /2D Nav Goal1 - - /2D Nav Goal2 - Name: Tool Properties - Splitter Ratio: 0.5886790156364441 - - Class: rviz/Views - Expanded: - - /Current View1 - Name: Views - Splitter Ratio: 0.5 - - Class: rviz/Time - Name: Time - SyncMode: 0 - SyncSource: LaserScan -Preferences: - PromptSaveOnExit: true -Toolbars: - toolButtonStyle: 2 -Visualization Manager: - Class: "" - Displays: - - Alpha: 0.5 - Cell Size: 0.10000000149011612 - Class: rviz/Grid - Color: 160; 160; 164 - Enabled: true - Line Style: - Line Width: 0.029999999329447746 - Value: Lines - Name: Grid - Normal Cell Count: 0 - Offset: - X: 1.5 - Y: 1.5 - Z: 0 - Plane: XY - Plane Cell Count: 40 - Reference Frame: - Value: true - - Alpha: 0.5 - Cell Size: 0.5 - Class: rviz/Grid - Color: 160; 160; 164 - Enabled: true - Line Style: - Line Width: 0.029999999329447746 - Value: Lines - Name: Grid - Normal Cell Count: 10 - Offset: - X: 1.5 - Y: 1.5 - Z: 0 - Plane: XY - Plane Cell Count: 6 - Reference Frame: - Value: true - - Class: rviz/TF - Enabled: true - Filter (blacklist): "" - Filter (whitelist): "" - Frame Timeout: 15 - Frames: - All Enabled: false - rival/base_footprint: - Value: true - robot/base_footprint: - Value: true - robot/beacon1: - Value: true - robot/beacon2: - Value: true - robot/beacon3: - Value: true - robot/imu_link: - Value: false - robot/laser_frame: - Value: false - robot/map: - Value: true - robot/odom: - Value: false - robot/predict: - Value: true - Marker Alpha: 1 - Marker Scale: 1 - Name: TF - Show Arrows: true - Show Axes: true - Show Names: true - Tree: - robot/map: - rival/base_footprint: - {} - robot/beacon1: - {} - robot/beacon2: - {} - robot/beacon3: - {} - robot/odom: - robot/base_footprint: - robot/imu_link: - {} - robot/laser_frame: - {} - robot/predict: - {} - Update Interval: 0 - Value: true - - Circles color: 170; 0; 0 - Class: Obstacles - Enabled: true - Margin color: 0; 170; 0 - Name: Obstacles - Opacity: 0.75 - Queue Size: 10 - Segments color: 170; 170; 0 - Segments thickness: 0.029999999329447746 - Topic: /robot/obstacles_to_base - Unreliable: false - Value: true - - Circles color: 0; 0; 170 - Class: Obstacles - Enabled: true - Margin color: 0; 170; 0 - Name: Obstacles - Opacity: 0.75 - Queue Size: 10 - Segments color: 170; 170; 0 - Segments thickness: 0.029999999329447746 - Topic: /rival/obstacles_to_map - Unreliable: false - Value: true - - Alpha: 1 - Autocompute Intensity Bounds: true - Autocompute Value Bounds: - Max Value: 10 - Min Value: -10 - Value: true - Axis: Z - Channel Name: intensity - Class: rviz/LaserScan - Color: 255; 255; 255 - Color Transformer: Intensity - Decay Time: 0 - Enabled: true - Invert Rainbow: false - Max Color: 255; 255; 255 - Min Color: 0; 0; 0 - Name: LaserScan - Position Transformer: XYZ - Queue Size: 10 - Selectable: true - Size (Pixels): 3 - Size (m): 0.009999999776482582 - Style: Flat Squares - Topic: /robot/scan - Unreliable: false - Use Fixed Frame: true - Use rainbow: true - Value: true - - Angle Tolerance: 0.10000000149011612 - Class: rviz/Odometry - Covariance: - Orientation: - Alpha: 0.5 - Color: 255; 255; 127 - Color Style: Unique - Frame: Local - Offset: 1 - Scale: 1 - Value: true - Position: - Alpha: 0.30000001192092896 - Color: 204; 51; 204 - Scale: 1 - Value: true - Value: true - Enabled: true - Keep: 1 - Name: Odometry - Position Tolerance: 0.009999999776482582 - Queue Size: 10 - Shape: - Alpha: 1 - Axes Length: 1 - Axes Radius: 0.10000000149011612 - Color: 255; 25; 0 - Head Length: 0.10000000149011612 - Head Radius: 0.009999999776482582 - Shaft Length: 0.019999999552965164 - Shaft Radius: 0.009999999776482582 - Value: Arrow - Topic: /robot/final_pose - Unreliable: false - Value: true - - Angle Tolerance: 0.10000000149011612 - Class: rviz/Odometry - Covariance: - Orientation: - Alpha: 0.5 - Color: 255; 255; 127 - Color Style: Unique - Frame: Local - Offset: 1 - Scale: 1 - Value: true - Position: - Alpha: 0.30000001192092896 - Color: 204; 51; 204 - Scale: 1 - Value: true - Value: true - Enabled: true - Keep: 1 - Name: Odometry - Position Tolerance: 0.009999999776482582 - Queue Size: 10 - Shape: - Alpha: 1 - Axes Length: 1 - Axes Radius: 0.10000000149011612 - Color: 255; 25; 0 - Head Length: 0.10000000149011612 - Head Radius: 0.10000000149011612 - Shaft Length: 0.009999999776482582 - Shaft Radius: 0.10000000149011612 - Value: Arrow - Topic: /rival/final_pose - Unreliable: false - Value: true - Enabled: true - Global Options: - Background Color: 48; 48; 48 - Default Light: true - Fixed Frame: robot/map - Frame Rate: 30 - Name: root - Tools: - - Class: rviz/Interact - Hide Inactive Objects: true - - Class: rviz/MoveCamera - - Class: rviz/Select - - Class: rviz/FocusCamera - - Class: rviz/Measure - - Class: rviz/SetInitialPose - Theta std deviation: 0.2617993950843811 - Topic: /robot/initialpose - X std deviation: 0.5 - Y std deviation: 0.5 - - Class: rviz/SetGoal - Topic: /robot/rviz_path_goal - - Class: rviz/SetGoal - Topic: /robot/rviz_dock_goal - - Class: rviz/PublishPoint - Single click: true - Topic: /clicked_point - Value: true - Views: - Current: - Angle: -0.01500002946704626 - Class: rviz/TopDownOrtho - Enable Stereo Rendering: - Stereo Eye Separation: 0.05999999865889549 - Stereo Focal Distance: 1 - Swap Stereo Eyes: false - Value: false - Invert Z Axis: false - Name: Current View - Near Clip Distance: 0.009999999776482582 - Scale: 253.73431396484375 - Target Frame: - X: 1.5128852128982544 - Y: 1.0647945404052734 - Saved: ~ -Window Geometry: - Displays: - collapsed: true - Height: 858 - Hide Left Dock: true - Hide Right Dock: true - QMainWindow State: 000000ff00000000fd00000004000000000000020b000002b3fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000006b00fffffffb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c0061007900730000000044000001be000000eb00fffffffb0000001e0054006f006f006c002000500072006f00700065007200740069006500730000000209000000ee0000006b00fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f0000034afc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a0056006900650077007300000000440000034a000000b900fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004c80000003efc0100000002fb0000000800540069006d00650100000000000004c8000003a200fffffffb0000000800540069006d00650100000000000004500000000000000000000004c8000002b300000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 - Selection: - collapsed: false - Time: - collapsed: false - Tool Properties: - collapsed: true - Views: - collapsed: true - Width: 1224 - X: 417 - Y: 34 diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/rviz/eurobot_localization_robot1.rviz b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/rviz/eurobot_localization_robot1.rviz deleted file mode 100644 index a47a051..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/rviz/eurobot_localization_robot1.rviz +++ /dev/null @@ -1,498 +0,0 @@ -Panels: - - Class: rviz/Displays - Help Height: 78 - Name: Displays - Property Tree Widget: - Expanded: - - /TF1/Frames1 - - /Odometry1/Covariance1 - - /Odometry2/Covariance1 - - /TF2/Frames1 - - /MarkerArray3 - - /MarkerArray3/Status1 - Splitter Ratio: 0.5 - Tree Height: 793 - - Class: rviz/Selection - Name: Selection - - Class: rviz/Tool Properties - Expanded: - - /2D Pose Estimate1 - - /2D Nav Goal1 - - /Publish Point1 - Name: Tool Properties - Splitter Ratio: 0.5886790156364441 - - Class: rviz/Views - Expanded: - - /Current View1 - Name: Views - Splitter Ratio: 0.5 - - Class: rviz/Time - Name: Time - SyncMode: 0 - SyncSource: "" -Preferences: - PromptSaveOnExit: true -Toolbars: - toolButtonStyle: 2 -Visualization Manager: - Class: "" - Displays: - - Alpha: 0.5 - Cell Size: 0.10000000149011612 - Class: rviz/Grid - Color: 160; 160; 164 - Enabled: true - Line Style: - Line Width: 0.029999999329447746 - Value: Lines - Name: Grid - Normal Cell Count: 0 - Offset: - X: 0 - Y: 0 - Z: 0 - Plane: XY - Plane Cell Count: 100 - Reference Frame: - Value: true - - Circles color: 170; 0; 0 - Class: Obstacles - Enabled: true - Margin color: 0; 170; 0 - Name: Obstacles - Opacity: 0.75 - Queue Size: 10 - Segments color: 170; 170; 0 - Segments thickness: 0.029999999329447746 - Topic: /robot/detector_to_base - Unreliable: false - Value: true - - Class: rviz/TF - Enabled: true - Frame Timeout: 15 - Frames: - All Enabled: false - cam_a: - Value: true - cam_a_ref: - Value: true - cam_b: - Value: true - cam_b_ref: - Value: true - cam_c_ref: - Value: true - camera1_aligned_depth_to_color_frame: - Value: true - camera1_color_frame: - Value: true - camera1_color_optical_frame: - Value: true - camera1_depth_frame: - Value: true - camera1_depth_optical_frame: - Value: true - camera1_infra1_frame: - Value: true - camera1_infra1_optical_frame: - Value: true - camera1_infra2_frame: - Value: true - camera1_infra2_optical_frame: - Value: true - camera1_link: - Value: true - camera2_aligned_depth_to_color_frame: - Value: true - camera2_color_frame: - Value: true - camera2_color_optical_frame: - Value: true - camera2_depth_frame: - Value: true - camera2_depth_optical_frame: - Value: true - camera2_infra1_frame: - Value: true - camera2_infra1_optical_frame: - Value: true - camera2_infra2_frame: - Value: true - camera2_infra2_optical_frame: - Value: true - camera2_link: - Value: true - camera3_aligned_depth_to_color_frame: - Value: true - camera3_color_frame: - Value: true - camera3_color_optical_frame: - Value: true - camera3_depth_frame: - Value: true - camera3_depth_optical_frame: - Value: true - camera3_infra1_frame: - Value: true - camera3_infra1_optical_frame: - Value: true - camera3_infra2_frame: - Value: true - camera3_infra2_optical_frame: - Value: true - camera3_link: - Value: true - camera_color_frame_1: - Value: true - camera_color_frame_2: - Value: true - camera_color_frame_3: - Value: true - map: - Value: true - marker1_1: - Value: true - marker2_1: - Value: true - robot/base_footprint: - Value: true - robot/beacon1: - Value: false - robot/beacon2: - Value: false - robot/beacon3: - Value: false - robot/imu_link: - Value: false - robot/laser_frame: - Value: false - robot/map: - Value: true - robot/odom: - Value: true - robot/predict: - Value: true - robot/vive_frame: - Value: false - robot_base: - Value: false - robot_camera: - Value: false - robot2/map: - Value: false - survive4_LH0: - Value: true - survive4_LH1: - Value: true - survive4_LH2: - Value: true - survive4_map: - Value: true - survive4_map0: - Value: true - survive4_map1: - Value: true - survive4_map2: - Value: true - survive4_map_avg: - Value: true - survive4_world: - Value: true - trackerD: - Value: true - world_frame: - Value: true - Marker Alpha: 1 - Marker Scale: 1 - Name: TF - Show Arrows: true - Show Axes: true - Show Names: true - Tree: - map: - robot/map: - robot/beacon1: - {} - robot/beacon2: - {} - robot/beacon3: - {} - robot/odom: - robot/base_footprint: - robot/imu_link: - {} - robot/laser_frame: - {} - robot/vive_frame: - {} - robot/predict: - {} - robot2/map: - {} - Update Interval: 0 - Value: true - - Class: rviz/MarkerArray - Enabled: true - Marker Topic: /robot/obstacle_marker - Name: MarkerArray - Namespaces: - "": true - Queue Size: 100 - Value: true - - Circles color: 170; 0; 0 - Class: Obstacles - Enabled: false - Margin color: 0; 170; 0 - Name: Obstacles - Opacity: 0.75 - Queue Size: 10 - Segments color: 170; 170; 0 - Segments thickness: 0.029999999329447746 - Topic: /robot/obstacle_array - Unreliable: false - Value: false - - Circles color: 170; 0; 0 - Class: Obstacles - Enabled: true - Margin color: 0; 170; 0 - Name: Obstacles - Opacity: 0.75 - Queue Size: 10 - Segments color: 170; 170; 0 - Segments thickness: 0.029999999329447746 - Topic: /robot2/obstacles_to_base - Unreliable: false - Value: true - - Alpha: 1 - Axes Length: 1 - Axes Radius: 0.10000000149011612 - Class: rviz/PoseWithCovariance - Color: 255; 25; 0 - Covariance: - Orientation: - Alpha: 0.5 - Color: 255; 255; 127 - Color Style: Unique - Frame: Local - Offset: 1 - Scale: 1 - Value: true - Position: - Alpha: 0.30000001192092896 - Color: 204; 51; 204 - Scale: 1 - Value: true - Value: true - Enabled: true - Head Length: 0.30000001192092896 - Head Radius: 0.10000000149011612 - Name: PoseWithCovariance - Queue Size: 10 - Shaft Length: 1 - Shaft Radius: 0.05000000074505806 - Shape: Arrow - Topic: /robot/ekf_pose - Unreliable: false - Value: true - - Angle Tolerance: 0.10000000149011612 - Class: rviz/Odometry - Covariance: - Orientation: - Alpha: 0.5 - Color: 255; 255; 127 - Color Style: Unique - Frame: Local - Offset: 1 - Scale: 1 - Value: true - Position: - Alpha: 0.30000001192092896 - Color: 204; 51; 204 - Scale: 1 - Value: true - Value: true - Enabled: false - Keep: 100 - Name: Odometry - Position Tolerance: 0.10000000149011612 - Queue Size: 10 - Shape: - Alpha: 1 - Axes Length: 1 - Axes Radius: 0.10000000149011612 - Color: 255; 25; 0 - Head Length: 0.30000001192092896 - Head Radius: 0.10000000149011612 - Shaft Length: 1 - Shaft Radius: 0.05000000074505806 - Value: Arrow - Topic: /robot/odom - Unreliable: false - Value: false - - Angle Tolerance: 0.10000000149011612 - Class: rviz/Odometry - Covariance: - Orientation: - Alpha: 0.5 - Color: 255; 255; 127 - Color Style: Unique - Frame: Local - Offset: 1 - Scale: 1 - Value: true - Position: - Alpha: 0.30000001192092896 - Color: 204; 51; 204 - Scale: 1 - Value: true - Value: true - Enabled: false - Keep: 1 - Name: Odometry - Position Tolerance: 0.10000000149011612 - Queue Size: 10 - Shape: - Alpha: 1 - Axes Length: 1 - Axes Radius: 0.10000000149011612 - Color: 255; 25; 0 - Head Length: 0.30000001192092896 - Head Radius: 0.10000000149011612 - Shaft Length: 1 - Shaft Radius: 0.05000000074505806 - Value: Arrow - Topic: /robot/global_filter - Unreliable: false - Value: false - - Alpha: 0.699999988079071 - Class: rviz/Map - Color Scheme: costmap - Draw Behind: false - Enabled: true - Name: Map - Topic: /robot/move_base/global_costmap/costmap - Unreliable: false - Use Timestamp: false - Value: true - - Class: rviz/MarkerArray - Enabled: true - Marker Topic: /robot2/obstacle_marker - Name: MarkerArray - Namespaces: - {} - Queue Size: 100 - Value: true - - Alpha: 1 - Axes Length: 1 - Axes Radius: 0.10000000149011612 - Class: rviz/PoseWithCovariance - Color: 255; 25; 0 - Covariance: - Orientation: - Alpha: 0.5 - Color: 255; 255; 127 - Color Style: Unique - Frame: Local - Offset: 1 - Scale: 1 - Value: true - Position: - Alpha: 0.30000001192092896 - Color: 204; 51; 204 - Scale: 1 - Value: true - Value: true - Enabled: false - Head Length: 0.30000001192092896 - Head Radius: 0.10000000149011612 - Name: PoseWithCovariance - Queue Size: 10 - Shaft Length: 1 - Shaft Radius: 0.05000000074505806 - Shape: Arrow - Topic: /robot2/ekf_pose - Unreliable: false - Value: false - - Class: rviz/TF - Enabled: false - Frame Timeout: 15 - Frames: - All Enabled: false - Marker Alpha: 1 - Marker Scale: 1 - Name: TF - Show Arrows: true - Show Axes: true - Show Names: true - Tree: - {} - Update Interval: 0 - Value: false - - Class: rviz/MarkerArray - Enabled: true - Marker Topic: /robot/obstacle_marker - Name: MarkerArray - Namespaces: - "": true - Queue Size: 100 - Value: true - Enabled: true - Global Options: - Background Color: 48; 48; 48 - Default Light: true - Fixed Frame: robot/map - Frame Rate: 30 - Name: root - Tools: - - Class: rviz/Interact - Hide Inactive Objects: true - - Class: rviz/MoveCamera - - Class: rviz/Select - - Class: rviz/FocusCamera - - Class: rviz/Measure - - Class: rviz/SetInitialPose - Theta std deviation: 0.2617993950843811 - Topic: /robot/initialpose - X std deviation: 0.5 - Y std deviation: 0.5 - - Class: rviz/SetGoal - Topic: /robot/mission - - Class: rviz/PublishPoint - Single click: true - Topic: /clicked_point - Value: true - Views: - Current: - Angle: 28.24005889892578 - Class: rviz/TopDownOrtho - Enable Stereo Rendering: - Stereo Eye Separation: 0.05999999865889549 - Stereo Focal Distance: 1 - Swap Stereo Eyes: false - Value: false - Invert Z Axis: false - Name: Current View - Near Clip Distance: 0.009999999776482582 - Scale: -117.77022552490234 - Target Frame: - X: 2.7860774993896484 - Y: 0.6524925231933594 - Saved: ~ -Window Geometry: - Displays: - collapsed: false - Height: 1016 - Hide Left Dock: false - Hide Right Dock: false - QMainWindow State: 000000ff00000000fd000000040000000000000187000003a2fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003b000003a2000000c700fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c000002610000000100000307000003a2fc0200000004fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003b00000358000000a000fffffffb0000000800540069006d0065000000003b000003a20000003700fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000007800000003efc0100000001fb0000000800540069006d00650100000000000004500000000000000000000005f3000003a200000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 - Selection: - collapsed: false - Time: - collapsed: false - Tool Properties: - collapsed: false - Views: - collapsed: false - Width: 1920 - X: 0 - Y: 27 diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/rviz/robot1_dit_test.rviz b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/rviz/robot1_dit_test.rviz deleted file mode 100644 index 4414f6b..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/rviz/robot1_dit_test.rviz +++ /dev/null @@ -1,311 +0,0 @@ -Panels: - - Class: rviz/Displays - Help Height: 70 - Name: Displays - Property Tree Widget: - Expanded: - - /Global Options1 - - /TF1/Frames1 - - /Odometry1/Shape1 - - /Obstacles3 - - /PoseWithCovariance1/Covariance1 - - /PoseWithCovariance1/Covariance1/Position1 - - /PoseWithCovariance1/Covariance1/Orientation1 - Splitter Ratio: 0.5 - Tree Height: 704 - - Class: rviz/Selection - Name: Selection - - Class: rviz/Tool Properties - Expanded: - - /2D Pose Estimate1 - - /2D Nav Goal1 - - /Publish Point1 - Name: Tool Properties - Splitter Ratio: 0.5886790156364441 - - Class: rviz/Views - Expanded: - - /Current View1 - Name: Views - Splitter Ratio: 0.5 - - Class: rviz/Time - Experimental: false - Name: Time - SyncMode: 0 - SyncSource: "" -Preferences: - PromptSaveOnExit: true -Toolbars: - toolButtonStyle: 2 -Visualization Manager: - Class: "" - Displays: - - Alpha: 0.5 - Cell Size: 1 - Class: rviz/Grid - Color: 160; 160; 164 - Enabled: true - Line Style: - Line Width: 0.029999999329447746 - Value: Lines - Name: Grid - Normal Cell Count: 0 - Offset: - X: 0 - Y: 0 - Z: 0 - Plane: XY - Plane Cell Count: 10 - Reference Frame: - Value: true - - Alpha: 0.699999988079071 - Class: rviz/Map - Color Scheme: map - Draw Behind: false - Enabled: false - Name: Map - Topic: /map - Unreliable: false - Use Timestamp: false - Value: false - - Alpha: 1 - Buffer Length: 1 - Class: rviz/Path - Color: 25; 255; 0 - Enabled: false - Head Diameter: 0.30000001192092896 - Head Length: 0.20000000298023224 - Length: 0.30000001192092896 - Line Style: Billboards - Line Width: 0.014999999664723873 - Name: Path - Offset: - X: 0 - Y: 0 - Z: 0 - Pose Color: 255; 85; 255 - Pose Style: None - Queue Size: 10 - Radius: 0.029999999329447746 - Shaft Diameter: 0.10000000149011612 - Shaft Length: 0.10000000149011612 - Topic: /path_rviz - Unreliable: false - Value: false - - Class: rviz/TF - Enabled: true - Frame Timeout: 15 - Frames: - All Enabled: false - base_footprint: - Value: true - base_laser_link: - Value: true - base_link: - Value: true - landmark1: - Value: true - landmark2: - Value: true - landmark3: - Value: true - map: - Value: true - odom: - Value: true - Marker Alpha: 1 - Marker Scale: 1.5 - Name: TF - Show Arrows: true - Show Axes: true - Show Names: true - Tree: - map: - landmark1: - {} - landmark2: - {} - landmark3: - {} - odom: - base_footprint: - base_link: - base_laser_link: - {} - Update Interval: 0 - Value: true - - Circles color: 170; 0; 0 - Class: Obstacles - Enabled: true - Margin color: 0; 170; 0 - Name: Obstacles - Opacity: 0.75 - Queue Size: 10 - Segments color: 170; 170; 0 - Segments thickness: 0.029999999329447746 - Topic: /raw_obstacles - Unreliable: false - Value: true - - Angle Tolerance: 0.009999999776482582 - Class: rviz/Odometry - Covariance: - Orientation: - Alpha: 0.5 - Color: 255; 255; 127 - Color Style: Unique - Frame: Local - Offset: 1 - Scale: 1 - Value: true - Position: - Alpha: 0.30000001192092896 - Color: 204; 51; 204 - Scale: 1 - Value: true - Value: false - Enabled: true - Keep: 1 - Name: Odometry - Position Tolerance: 0.009999999776482582 - Queue Size: 10 - Shape: - Alpha: 0.5 - Axes Length: 0.20000000298023224 - Axes Radius: 0.019999999552965164 - Color: 255; 255; 0 - Head Length: 0.20000000298023224 - Head Radius: 0.07999999821186066 - Shaft Length: 0.30000001192092896 - Shaft Radius: 0.05000000074505806 - Value: Arrow - Topic: /ground_truth - Unreliable: false - Value: true - - Circles color: 170; 0; 0 - Class: Obstacles - Enabled: true - Margin color: 0; 170; 0 - Name: Obstacles - Opacity: 0.75 - Queue Size: 10 - Segments color: 170; 170; 0 - Segments thickness: 0.029999999329447746 - Topic: /robot/obstacles - Unreliable: false - Value: true - - Circles color: 0; 0; 255 - Class: Obstacles - Enabled: true - Margin color: 255; 255; 0 - Name: Obstacles - Opacity: 0.75 - Queue Size: 10 - Segments color: 170; 170; 0 - Segments thickness: 0.029999999329447746 - Topic: /update_beacon - Unreliable: false - Value: true - - Alpha: 0.5 - Axes Length: 1 - Axes Radius: 0.10000000149011612 - Class: rviz/PoseWithCovariance - Color: 255; 255; 0 - Covariance: - Orientation: - Alpha: 0.5 - Color: 255; 255; 127 - Color Style: Unique - Frame: Local - Offset: 1 - Scale: 1 - Value: true - Position: - Alpha: 0.30000001192092896 - Color: 204; 51; 204 - Scale: 1 - Value: true - Value: true - Enabled: true - Head Length: 0.30000001192092896 - Head Radius: 0.10000000149011612 - Name: PoseWithCovariance - Queue Size: 10 - Shaft Length: 0.5 - Shaft Radius: 0.05000000074505806 - Shape: Arrow - Topic: /robot/ekf_pose - Unreliable: false - Value: true - Enabled: true - Global Options: - Background Color: 48; 48; 48 - Default Light: true - Fixed Frame: map - Frame Rate: 30 - Name: root - Tools: - - Class: rviz/Interact - Hide Inactive Objects: true - - Class: rviz/MoveCamera - - Class: rviz/Select - - Class: rviz/FocusCamera - - Class: rviz/Measure - - Class: rviz/SetInitialPose - Theta std deviation: 0.2617993950843811 - Topic: /initialpose - X std deviation: 0.5 - Y std deviation: 0.5 - - Class: rviz/SetGoal - Topic: /move_base_simple/goal - - Class: rviz/PublishPoint - Single click: true - Topic: /clicked_point - Value: true - Views: - Current: - Angle: 9.603798389434814e-06 - Class: rviz/TopDownOrtho - Enable Stereo Rendering: - Stereo Eye Separation: 0.05999999865889549 - Stereo Focal Distance: 1 - Swap Stereo Eyes: false - Value: false - Invert Z Axis: false - Name: Current View - Near Clip Distance: 0.009999999776482582 - Scale: 94.04732513427734 - Target Frame: - X: 1.583926796913147 - Y: 2.862351179122925 - Saved: - - Angle: -1.0244548320770264e-07 - Class: rviz/TopDownOrtho - Enable Stereo Rendering: - Stereo Eye Separation: 0.05999999865889549 - Stereo Focal Distance: 1 - Swap Stereo Eyes: false - Value: false - Invert Z Axis: false - Name: TopDownOrtho - Near Clip Distance: 0.009999999776482582 - Scale: 195.27081298828125 - Target Frame: - X: 1.0763458013534546 - Y: 1.570979118347168 -Window Geometry: - Displays: - collapsed: false - Height: 993 - Hide Left Dock: false - Hide Right Dock: true - QMainWindow State: 000000ff00000000fd00000004000000000000015600000343fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d00000343000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f00000362fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003b00000362000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004f30000003efc0100000002fb0000000800540069006d00650100000000000004f3000002eb00fffffffb0000000800540069006d00650100000000000004500000000000000000000003970000034300000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 - Selection: - collapsed: false - Time: - collapsed: false - Tool Properties: - collapsed: false - Views: - collapsed: true - Width: 1267 - X: 196 - Y: 27 diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/rviz/stage.rviz b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/rviz/stage.rviz deleted file mode 100644 index 3dc2893..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/rviz/stage.rviz +++ /dev/null @@ -1,409 +0,0 @@ -Panels: - - Class: rviz/Displays - Help Height: 138 - Name: Displays - Property Tree Widget: - Expanded: - - /Global Options1 - - /Navigation1/TF1/Frames1 - - /Path1 - Splitter Ratio: 0.5 - Tree Height: 976 - - Class: rviz/Selection - Name: Selection - - Class: rviz/Tool Properties - Expanded: - - /2D Pose Estimate1 - - /2D Nav Goal1 - - /Publish Point1 - Name: Tool Properties - Splitter Ratio: 0.5886790156364441 - - Class: rviz/Views - Expanded: - - /Current View1 - Name: Views - Splitter Ratio: 0.5 - - Class: rviz/Time - Experimental: false - Name: Time - SyncMode: 0 - SyncSource: LaserScan -Preferences: - PromptSaveOnExit: true -Toolbars: - toolButtonStyle: 2 -Visualization Manager: - Class: "" - Displays: - - Alpha: 0.5 - Cell Size: 1 - Class: rviz/Grid - Color: 160; 160; 164 - Enabled: true - Line Style: - Line Width: 0.029999999329447746 - Value: Lines - Name: Grid - Normal Cell Count: 0 - Offset: - X: 0 - Y: 0 - Z: 0 - Plane: XY - Plane Cell Count: 10 - Reference Frame: - Value: true - - Alpha: 0.699999988079071 - Class: rviz/Map - Color Scheme: map - Draw Behind: false - Enabled: true - Name: Map - Topic: /map - Unreliable: false - Use Timestamp: false - Value: true - - Class: rviz/Group - Displays: - - Alpha: 1 - Buffer Length: 1 - Class: rviz/Path - Color: 25; 255; 0 - Enabled: true - Head Diameter: 0.30000001192092896 - Head Length: 0.20000000298023224 - Length: 0.30000001192092896 - Line Style: Billboards - Line Width: 0.014999999664723873 - Name: Path - Offset: - X: 0 - Y: 0 - Z: 0 - Pose Color: 255; 85; 255 - Pose Style: None - Queue Size: 10 - Radius: 0.029999999329447746 - Shaft Diameter: 0.10000000149011612 - Shaft Length: 0.10000000149011612 - Topic: /move_base/GlobalPlanner/plan - Unreliable: false - Value: true - - Class: rviz/TF - Enabled: true - Frame Timeout: 15 - Frames: - All Enabled: false - base_footprint: - Value: true - base_laser_link: - Value: true - base_link: - Value: true - beacon1: - Value: true - beacon2: - Value: true - beacon3: - Value: true - laser_frame: - Value: true - map: - Value: true - odom: - Value: true - Marker Alpha: 1 - Marker Scale: 1 - Name: TF - Show Arrows: true - Show Axes: true - Show Names: true - Tree: - map: - beacon1: - {} - beacon2: - {} - beacon3: - {} - odom: - base_footprint: - base_link: - base_laser_link: - {} - laser_frame: - {} - Update Interval: 0 - Value: true - - Alpha: 1 - Arrow Length: 0.05000000074505806 - Axes Length: 0.30000001192092896 - Axes Radius: 0.009999999776482582 - Class: rviz/PoseArray - Color: 255; 25; 0 - Enabled: true - Head Length: 0.07000000029802322 - Head Radius: 0.029999999329447746 - Name: Orientation Filter - Queue Size: 10 - Shaft Length: 0.23000000417232513 - Shaft Radius: 0.009999999776482582 - Shape: Arrow (Flat) - Topic: /orientation - Unreliable: false - Value: true - - Alpha: 1 - Axes Length: 1 - Axes Radius: 0.10000000149011612 - Class: rviz/Pose - Color: 173; 127; 168 - Enabled: true - Head Length: 0.05000000074505806 - Head Radius: 0.10000000149011612 - Name: Nav_goal - Queue Size: 10 - Shaft Length: 0.25 - Shaft Radius: 0.05000000074505806 - Shape: Arrow - Topic: /nav_goal - Unreliable: false - Value: true - - Alpha: 1 - Axes Length: 1 - Axes Radius: 0.10000000149011612 - Class: rviz/Pose - Color: 255; 25; 0 - Enabled: true - Head Length: 0.30000001192092896 - Head Radius: 0.10000000149011612 - Name: Local_goal - Queue Size: 10 - Shaft Length: 1 - Shaft Radius: 0.05000000074505806 - Shape: Arrow - Topic: /local_goal - Unreliable: false - Value: true - Enabled: true - Name: Navigation - - Class: rviz/Group - Displays: - - Circles color: 170; 0; 0 - Class: Obstacles - Enabled: true - Margin color: 0; 170; 0 - Name: Obstacles - Opacity: 0.75 - Queue Size: 10 - Segments color: 170; 170; 0 - Segments thickness: 0.029999999329447746 - Topic: /obstacles_to_base - Unreliable: false - Value: true - - Alpha: 1 - Autocompute Intensity Bounds: true - Autocompute Value Bounds: - Max Value: 10 - Min Value: -10 - Value: true - Axis: Z - Channel Name: intensity - Class: rviz/LaserScan - Color: 255; 255; 255 - Color Transformer: Intensity - Decay Time: 0 - Enabled: true - Invert Rainbow: false - Max Color: 255; 255; 255 - Min Color: 0; 0; 0 - Name: LaserScan - Position Transformer: XYZ - Queue Size: 10 - Selectable: true - Size (Pixels): 3 - Size (m): 0.05000000074505806 - Style: Flat Squares - Topic: /scan - Unreliable: false - Use Fixed Frame: true - Use rainbow: true - Value: true - - Circles color: 114; 159; 207 - Class: Obstacles - Enabled: false - Margin color: 252; 175; 62 - Name: Updated Beacon - Opacity: 0.75 - Queue Size: 10 - Segments color: 170; 170; 0 - Segments thickness: 0.029999999329447746 - Topic: /update_beacon - Unreliable: false - Value: false - - Alpha: 1 - Axes Length: 1 - Axes Radius: 0.10000000149011612 - Class: rviz/PoseWithCovariance - Color: 85; 170; 255 - Covariance: - Orientation: - Alpha: 0.5 - Color: 255; 255; 127 - Color Style: Unique - Frame: Local - Offset: 1 - Scale: 1 - Value: true - Position: - Alpha: 0.30000001192092896 - Color: 204; 51; 204 - Scale: 1 - Value: true - Value: true - Enabled: true - Head Length: 0.30000001192092896 - Head Radius: 0.10000000149011612 - Name: ekf_pose - Queue Size: 10 - Shaft Length: 0.5 - Shaft Radius: 0.05000000074505806 - Shape: Arrow - Topic: /ekf_pose - Unreliable: false - Value: true - Enabled: true - Name: Localization - - Class: rviz/MarkerArray - Enabled: true - Marker Topic: /obstacle_marker - Name: MarkerArray - Namespaces: - "": true - Queue Size: 100 - Value: true - - Alpha: 1 - Buffer Length: 1 - Class: rviz/Path - Color: 239; 41; 41 - Enabled: true - Head Diameter: 0.30000001192092896 - Head Length: 0.20000000298023224 - Length: 0.30000001192092896 - Line Style: Lines - Line Width: 0.029999999329447746 - Name: Path - Offset: - X: 0 - Y: 0 - Z: 0 - Pose Color: 255; 85; 255 - Pose Style: None - Queue Size: 10 - Radius: 0.029999999329447746 - Shaft Diameter: 0.10000000149011612 - Shaft Length: 0.10000000149011612 - Topic: /local_path - Unreliable: false - Value: true - Enabled: true - Global Options: - Background Color: 48; 48; 48 - Default Light: true - Fixed Frame: map - Frame Rate: 30 - Name: root - Tools: - - Class: rviz/Interact - Hide Inactive Objects: true - - Class: rviz/MoveCamera - - Class: rviz/Select - - Class: rviz/FocusCamera - - Class: rviz/Measure - - Class: rviz/SetInitialPose - Theta std deviation: 0.2617993950843811 - Topic: /initialpose - X std deviation: 0.5 - Y std deviation: 0.5 - - Class: rviz/SetGoal - Topic: /nav_goal - - Class: rviz/PublishPoint - Single click: true - Topic: /clicked_point - Value: true - Views: - Current: - Angle: -0.014996705576777458 - Class: rviz/TopDownOrtho - Enable Stereo Rendering: - Stereo Eye Separation: 0.05999999865889549 - Stereo Focal Distance: 1 - Swap Stereo Eyes: false - Value: false - Invert Z Axis: false - Name: Current View - Near Clip Distance: 0.009999999776482582 - Scale: 349.5602111816406 - Target Frame: - X: 0.9571438431739807 - Y: 1.5277671813964844 - Saved: - - Angle: 0 - Class: rviz/TopDownOrtho - Enable Stereo Rendering: - Stereo Eye Separation: 0.05999999865889549 - Stereo Focal Distance: 1 - Swap Stereo Eyes: false - Value: false - Invert Z Axis: false - Name: TopDownOrtho - Near Clip Distance: 0.009999999776482582 - Scale: 213.4537811279297 - Target Frame: - X: 0.9328301548957825 - Y: 0.8575417995452881 - - Angle: 0 - Class: rviz/TopDownOrtho - Enable Stereo Rendering: - Stereo Eye Separation: 0.05999999865889549 - Stereo Focal Distance: 1 - Swap Stereo Eyes: false - Value: false - Invert Z Axis: false - Name: TopDownOrtho - Near Clip Distance: 0.009999999776482582 - Scale: 213.4537811279297 - Target Frame: - X: 0.9328301548957825 - Y: 0.8575417995452881 - - Angle: -0.005000163801014423 - Class: rviz/TopDownOrtho - Enable Stereo Rendering: - Stereo Eye Separation: 0.05999999865889549 - Stereo Focal Distance: 1 - Swap Stereo Eyes: false - Value: false - Invert Z Axis: false - Name: TopDownOrtho - Near Clip Distance: 0.009999999776482582 - Scale: 213.4537811279297 - Target Frame: - X: 0.9140906929969788 - Y: 0.707626461982727 -Window Geometry: - Displays: - collapsed: false - Height: 1546 - Hide Left Dock: false - Hide Right Dock: true - QMainWindow State: 000000ff00000000fd00000004000000000000039400000502fc0200000007fb0000001200530065006c0065006300740069006f006e00000001e10000009b000000b000fffffffb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afc0000006e00000502000001bc01000039fa000000010100000002fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000000ffffffff0000010b00fffffffb000000100044006900730070006c006100790073010000000000000156000001f700fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f0000037afc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003d0000037a0000013200fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e1000001970000000300000b4000000060fc0100000002fb0000000800540069006d0065010000000000000b400000057100fffffffb0000000800540069006d00650100000000000004500000000000000000000007a00000050200000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 - Selection: - collapsed: false - Time: - collapsed: false - Tool Properties: - collapsed: false - Views: - collapsed: true - Width: 2880 - X: 3144 - Y: 349 diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/scripts/ekf.py b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/scripts/ekf.py deleted file mode 100755 index 67dc4bb..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/scripts/ekf.py +++ /dev/null @@ -1,258 +0,0 @@ -#!/usr/bin/python3 -import numpy as np -import time - -# for ros -import rospy -from obstacle_detector.msg import Obstacles -from nav_msgs.msg import Odometry -from geometry_msgs.msg import PoseWithCovarianceStamped -from tf import TransformBroadcaster -import tf.transformations as tf_t - -class EKF: - - def __init__(self): - # for beacon position ([x], [y]) - self.beacon_position = np.array([[0.05, 1.05, 1.95],\ - [ 3.1, -0.1, 3.1]]) - - # for robot initial state (x; y; phi(-pi,pi)) - self.mu_0 = np.array([[0.5],\ - [0.5],\ - [0.5*np.pi]]) - - # for robot state - self.mu_past = self.mu_0.copy() - self.mu = np.array([[0.0],\ - [0.0],\ - [0.0]]) - - self.sigma_past = np.zeros((3,3)) - self.sigma = np.zeros((3,3)) - - self.dt = 1/50 - - # for beacon pillar detection - self.if_new_obstacles = False - self.beacon_scan = np.nan - - # for ros - rospy.init_node('ekf_localization', anonymous=True) - self.odom_sub = rospy.Subscriber("odom", Odometry, self.odom_sub_callback) - self.obstacles_sub = rospy.Subscriber("raw_obstacles", Obstacles, self.obstacles_sub_callback) - self.ekf_pose_pub = rospy.Publisher("ekf_pose", PoseWithCovarianceStamped, queue_size=10) - self.ekf_pose_tfbr = TransformBroadcaster() - - self.map_frame_name = "map" - self.robot_frame_name = "base_footprint" - - # for time calculate - self.index = 0 - self.duration = 0.0 - - def ekf_localization(self, v, w): - begin = time.clock_gettime_ns(time.CLOCK_REALTIME) - # set motion covariance - a1 = 0.5 - a2 = 0.8 - a3 = 0.5 - a4 = 0.8 - # set a minimum likelihood value - mini_likelihood = 0.5 - # for convenience, ex: s_dt = sin(theta+wdt) - theta = self.mu_past[2, 0].copy() - s = np.sin(theta) - c = np.cos(theta) - s_dt = np.sin(theta + w*self.dt) - c_dt = np.cos(theta + w*self.dt) - - # ekf predict step: - if w < 1e-5 and w > -1e-5 : - G = np.array([[1, 0, -v*s*self.dt],\ - [0, 1, v*c*self.dt],\ - [0, 0, 1]]) - - V = np.array([[c*self.dt, 0],\ - [s*self.dt, 0],\ - [0 , 0]]) - - M = np.array([[a1*v**2 + a2*w**2, 0],\ - [ 0, a3*v**2 + a4*w**2]]) - - mu_bar = self.mu_past.copy() + np.array([[v*c*self.dt],\ - [v*s*self.dt],\ - [ 0]]) - - else: - G = np.array([[1, 0, (v*(-c+c_dt))/w],\ - [0, 1, (v*(-s+s_dt))/w],\ - [0, 0, 1]]) - - V = np.array([[(-s+s_dt)/w, v*(s-s_dt)/w**2 + v*self.dt*(c_dt)/w],\ - [ (c-c_dt)/w, -v*(c-c_dt)/w**2 + v*self.dt*(s_dt)/w],\ - [ 0, self.dt]]) - - M = np.array([[a1*v**2+a2*w**2, 0],\ - [ 0, a3*v**2+a4*w**2]]) - - mu_bar = self.mu_past.copy() + np.array([[v*(-s+s_dt)/w],\ - [ v*(c-c_dt)/w],\ - [ w*self.dt]]) - mu_bar[2,0] = self._angle_limit_checking(mu_bar[2,0]) - - sigma_bar = G@self.sigma_past@G.T + V@M@V.T - sigma_bar = self._fix_FP_issue(sigma_bar) - - # ekf update step: - Q = np.diag((0.001, 0.2, 0.02)) - if self.if_new_obstacles is True: - # for every obstacle (or beacon pillar), calculate how likely it is the correct pillar. - for landmark_scan in np.nditer(self.beacon_scan, flags=['external_loop'], order='F'): - landmark_scan = np.reshape(landmark_scan, (2,1)) - # transfer landmark_scan type from (x, y) to (r, phi) - z_i = self._cartesian_to_polar(landmark_scan, np.zeros((3,1))) - j_max = 0 - H_j_max = 0 - S_j_max = 0 - z_j_max = 0 - for k in range(self.beacon_position.shape[1]): - landmark_k = np.reshape(self.beacon_position[:,k], (2,1)) - z_k, H_k = self._cartesian_to_polar(landmark_k, mu_bar, cal_H=True) - S_k = H_k@sigma_bar@H_k.T + Q - S_k = self._fix_FP_issue(S_k) - try: - # original - # j_k = 1/np.sqrt(np.linalg.det(2*np.pi*S_k)) * np.exp(-0.5*(z_i-z_k).T@np.linalg.inv(S_k)@(z_i-z_k)) - - # ln(j_k()) version - j_k = -0.5*(z_i-z_k).T@np.linalg.inv(S_k)@(z_i-z_k) - np.log(np.sqrt(np.linalg.det(2*np.pi*S_k))) - if np.around(j_k, 10)[0,0] > np.around(j_max, 10): - j_max = j_k.copy() - H_j_max = H_k.copy() - S_j_max = S_k.copy() - z_j_max = z_k.copy() - except Exception as e: - rospy.logerr("%s", e) - # continue - # rospy.loginfo("H_k=%s, sigma_bar=%s, S_k=%s", H_k, sigma_bar, S_k) - # print("S_k-Q = ", H_k@sigma_bar@H_k.T, "H_k = ", H_k, "sigma_bar = ", sigma_bar) - if j_max > mini_likelihood and j_max != np.nan: - K_i = sigma_bar@H_j_max.T@np.linalg.inv(S_j_max) - mu_bar = mu_bar + K_i@(z_i-z_j_max) - sigma_bar = (np.eye(3) - self._fix_FP_issue(K_i@H_j_max))@sigma_bar - - self.mu = mu_bar.copy() - self.sigma = self._fix_FP_issue(sigma_bar.copy()) - self.mu_past = self.mu.copy() - self.sigma_past = self.sigma.copy() - # finish once ekf, change the flag - self.if_new_obstacles = False - end = time.clock_gettime_ns(time.CLOCK_REALTIME) - self.index += 1 - self.duration += end-begin - print("ekf cost:", self.duration/self.index*1e-9 , "sec") - - def _euclidean_distance(self, a, b): - return np.sqrt((b[1, 0]-a[1, 0])**2 + (b[0, 0]-a[0, 0])**2) - - def _angle_limit_checking(self, theta): - if theta > np.pi: - theta -= 2 * np.pi - elif theta <= -np.pi: - theta += 2 * np.pi - return theta - - # find polar coordinate for point a from ref point b - def _cartesian_to_polar(self, a, b, cal_H=False): - q_sqrt = self._euclidean_distance(a, b) - q = q_sqrt**2 - a_b_x = a[0, 0]-b[0, 0] - a_b_y = a[1, 0]-b[1, 0] - z_hat = np.array([[q_sqrt],\ - [np.arctan2(a_b_y, a_b_x) - b[2, 0]],\ - [1]]) - z_hat[1,0] = self._angle_limit_checking(z_hat[1,0]) - if cal_H: - H = np.array([[-(a_b_x/q_sqrt), -(a_b_y/q_sqrt), 0],\ - [ a_b_y/q, -(a_b_x/q), -1],\ - [ 0, 0, 0]]) - return (z_hat, H) - else: - return z_hat - - # rotate theta and translation (x, y) from the laser frame - def _tf_laser_to_map(self, mu_bar, landmark_scan): - s = np.sin(mu_bar[2,0]) - c = np.cos(mu_bar[2,0]) - landmark_scan = np.array([[c, -s],[s, c]])@landmark_scan + np.reshape(mu_bar[0:2, 0], (2,1)) - return landmark_scan - - # if value less than upper_bound then equal to 0 - def _fix_FP_issue(self, matrix, upper_bound=1e-10): - matrix[matrix("robot_name", p_robot_name_, ""); - nh_local_.param("chassis_type", chassis_type, 0); // diff: 0, omni: 1 - - double initial_cov[9]; - nh_local_.param("initial_cov_x", initial_cov[0], 0.0005); - nh_local_.param("initial_cov_x_y", initial_cov[1], 0.0005); - nh_local_.param("initial_cov_x_yaw", initial_cov[2], 0.0005); - nh_local_.param("initial_cov_y_x", initial_cov[3], 0.0005); - nh_local_.param("initial_cov_y_y", initial_cov[4], 0.0005); - nh_local_.param("initial_cov_y_yaw", initial_cov[5], 0.0005); - nh_local_.param("initial_cov_yaw_x", initial_cov[6], 0.0005); - nh_local_.param("initial_cov_yaw_y", initial_cov[7], 0.0005); - nh_local_.param("initial_cov_yaw_yaw", initial_cov[8], 0.0005); - - // for debug - update_beacon_ = { Eigen::Vector2d(0.0, 0.0), Eigen::Vector2d(0.0, 0.0), Eigen::Vector2d(0.0, 0.0) }; - - // for robot state - robotstate_.sigma << initial_cov[0], initial_cov[1], initial_cov[2], - initial_cov[3], initial_cov[4], initial_cov[5], - initial_cov[6], initial_cov[7], initial_cov[8]; - - // Odom callback frequency, get dt for calculation - nh_.param("odom_freq", p_odom_freq_, 100.0); - dt_ = 1.0 / p_odom_freq_; - - if(chassis_type == 0){ - // differential drive - // prediction - nh_local_.param("diff_drive/predict_cov_a1", p_a1_, 1.5); - nh_local_.param("diff_drive/predict_cov_a2", p_a2_, 2.5); - nh_local_.param("diff_drive/predict_cov_a3", p_a3_, 1.5); - nh_local_.param("diff_drive/predict_cov_a4", p_a4_, 2.5); - // measurement - nh_local_.param("diff_drive/update_cov_1", p_Q1_, 0.01); - nh_local_.param("diff_drive/update_cov_2", p_Q2_, 0.01); - nh_local_.param("diff_drive/update_cov_3", p_Q3_, 0.02); - Q_ = Eigen::Vector3d{ p_Q1_, p_Q2_, p_Q3_ }.asDiagonal(); - - }else if(chassis_type == 1){ - // omnidirectional drive - // prediction - nh_local_.param("omni_drive/predict_const_x", p_const_x, 2.0); - nh_local_.param("omni_drive/predict_const_y", p_const_y, 2.0); - nh_local_.param("omni_drive/predict_const_theta", p_const_theta, 0.3); - P_omni_model_ = Eigen::Vector3d{ p_const_x, p_const_y, p_const_theta }.asDiagonal(); - // measurement - nh_local_.param("omni_drive/update_cov_1", p_Q1_, 0.01); - nh_local_.param("omni_drive/update_cov_2", p_Q2_, 0.01); - nh_local_.param("omni_drive/update_cov_3", p_Q3_, 0.02); - Q_ = Eigen::Vector3d{ p_Q1_, p_Q2_, p_Q3_ }.asDiagonal(); - } - - // use log(j_k) - nh_local_.param("mini_likelihood", p_mini_likelihood_, -10000.0); - nh_local_.param("mini_likelihood_update", p_mini_likelihood_update_, 0.4); - // use j_k - // mini_likelihood_ = 0.0; - // mini_likelihood_update_ = 25.0; - - // for obstacle filtering - nh_local_.param("max_obstacle_distance", p_max_obstacle_distance_, 0.15); - nh_local_.param("timer_frequency", p_timer_frequency_, 10); - nh_local_.param("velocity_lpf_gain", p_velocity_lpf_gain_, 0.5); - - // for beacon piller detection - if_new_obstacles_ = false; - if_gps = false; - update_lidar_ = false; - beacon_from_scan_ = {}; - - // for ros - setpose_sub_ = nh_.subscribe("initialpose", 50, &Ekf::setposeCallback, this); - odom_sub_ = nh_.subscribe("odom", 50, &Ekf::odomCallback, this); //remapped to globle_filter - raw_obstacles_sub_ = nh_.subscribe("obstacles_to_base", 10, &Ekf::obstaclesCallback, this); - gps_sub_ = nh_.subscribe("lidar_bonbonbon", 10, &Ekf::gpsCallback, this); - ekf_pose_pub_ = nh_.advertise("final_pose", 10); - update_beacon_pub_ = nh_.advertise("update_beacon", 10); - update_timer_ = nh_.createTimer(ros::Duration(1.0), &Ekf::updateTimerCallback, this, false, false); - - fast_spin_server = nh_.advertiseService("fast_spin", &Ekf::fast_spin, this); - ready_client = nh_.serviceClient("startup/ready_signal"); - scan_client = nh_.serviceClient("start_scan"); - - // for time calculate - count_ = 0; - duration_ = 0.0; - first_cb = false; - t_last = 0.0; - - // Set timer period - update_timer_.setPeriod(ros::Duration(1 / p_timer_frequency_), false); - update_timer_.start(); - - // ready and start signal - ready = false; - nh_.param("localization/ready_signal_active", ready_signal_active, false); - if(ready_signal_active) { - ready_sub = nh_.subscribe("startup/ready_signal", 10, &Ekf::readyCallback, this); - }else{ - geometry_msgs::PointStamped initial_msg; - nh_.param("localization/side", initial_msg.header.frame_id, "0"); - nh_.param("localization/initial_pose/x", initial_msg.point.x, 0.3); - nh_.param("localization/initial_pose/y", initial_msg.point.y, 1.5); - nh_.param("localization/initial_pose/z", initial_msg.point.z, 0.0); - - ready = true; - bool lidar_open = false; - while(!lidar_open){ - lidar_open = scan_client.call(scan_srv); - ros::Duration(0.1).sleep(); - } - - robotstate_.mu(0) = initial_msg.point.x; - robotstate_.mu(1) = initial_msg.point.y; - robotstate_.mu(2) = degToRad(initial_msg.point.z); - - std::string side; - if(strcmp(initial_msg.header.frame_id.c_str(), "0") == 0) side = "Blue"; - else if(strcmp(initial_msg.header.frame_id.c_str(), "1") == 0) side = "Yellow"; - - ready &= nh_.param(side + "/beacon_ax", p_beacon_ax_, -0.094); - ready &= nh_.param(side + "/beacon_ay", p_beacon_ay_, 0.052); - ready &= nh_.param(side + "/beacon_bx", p_beacon_bx_, -0.094); - ready &= nh_.param(side + "/beacon_by", p_beacon_by_, 1.948); - ready &= nh_.param(side + "/beacon_cx", p_beacon_cx_, 3.094); - ready &= nh_.param(side + "/beacon_cy", p_beacon_cy_, 1.0); - - // for beacon position in map list{ax, ay, bx, by, cx, cy} - Eigen::Vector2d beacon_a{ p_beacon_ax_, p_beacon_ay_ }; - Eigen::Vector2d beacon_b{ p_beacon_bx_, p_beacon_by_ }; - Eigen::Vector2d beacon_c{ p_beacon_cx_, p_beacon_cy_ }; - beacon_in_map_ = { beacon_a, beacon_b, beacon_c }; - } - - is_spinning = false; -} - -void Ekf::readyCallback(const geometry_msgs::PointStamped::ConstPtr& msg){ - - ready = true; - static bool called_ready = false; - if (called_ready) return; - - ready &= scan_client.call(scan_srv); - - robotstate_.mu(0) = msg->point.x; - robotstate_.mu(1) = msg->point.y; - robotstate_.mu(2) = degToRad(msg->point.z); - - std::string side; - - if(strcmp(msg->header.frame_id.c_str(), "0") == 0) side = "Blue"; - else if(strcmp(msg->header.frame_id.c_str(), "1") == 0) side = "Yellow"; - - ready &= nh_.param(side + "/beacon_ax", p_beacon_ax_, -0.094); - ready &= nh_.param(side + "/beacon_ay", p_beacon_ay_, 0.052); - ready &= nh_.param(side + "/beacon_bx", p_beacon_bx_, -0.094); - ready &= nh_.param(side + "/beacon_by", p_beacon_by_, 1.948); - ready &= nh_.param(side + "/beacon_cx", p_beacon_cx_, 3.094); - ready &= nh_.param(side + "/beacon_cy", p_beacon_cy_, 1.0); - - // for beacon position in map list{ax, ay, bx, by, cx, cy} - Eigen::Vector2d beacon_a{ p_beacon_ax_, p_beacon_ay_ }; - Eigen::Vector2d beacon_b{ p_beacon_bx_, p_beacon_by_ }; - Eigen::Vector2d beacon_c{ p_beacon_cx_, p_beacon_cy_ }; - beacon_in_map_ = { beacon_a, beacon_b, beacon_c }; - - startup_msgs::StartUpSrv ready_srv; - ready_srv.request.group = 3; - if(ready_client.call(ready_srv)) ready &= ready_srv.response.success; - - if(!ready) ROS_WARN("[ekf]: ready not ok"); - called_ready = true; - -} - -void Ekf::predict_diff_ekf(double v, double w, double dt, Eigen::Matrix3d odom_sigma) -{ - double theta = robotstate_.mu(2); - double s = sin(theta); double s_ = sin(theta + w * dt); - double c = cos(theta); double c_ = cos(theta + w * dt); - - if ((w < 0.00001) && (w > -0.00001)){ - robotstate_.mu = robotstate_.mu + Eigen::Vector3d{ v * c * dt, v * s * dt, 0.0 }; - }else{ - robotstate_.mu = robotstate_.mu + Eigen::Vector3d{ v * (-s + s_) / w, v * (c - c_) / w, w * dt }; - } - robotstate_.mu(2) = angleLimitChecking(robotstate_.mu(2)); - - Eigen::Matrix3d R; - R << odom_sigma(0, 0)*c_, 0.0, 0.0, 0.0, odom_sigma(0, 0)*s_, 0.0, 0.0, 0.0, odom_sigma(2, 2); - - robotstate_.sigma = robotstate_.sigma + R; -} - -void Ekf::predict_diff_ucekf(double v, double w, double dt) -{ - double theta = robotstate_.mu(2); - double s = sin(theta); - double c = cos(theta); - double s_dt = sin(theta + w * dt); - double c_dt = cos(theta + w * dt); - // cout << "sin= " << s << ",cos= " << c << ", theta= " << theta << endl; - - // ekf predict step - Eigen::Matrix3d G; - Eigen::Matrix V; - Eigen::Matrix2d M; - - if ((w < 0.00001) && (w > -0.00001)) - { - G << 1.0, 0.0, -v * s * dt, 0.0, 1.0, c* v* dt, 0.0, 0.0, 1.0; - V << c * dt, 0.0, s* dt, 0.0, 0.0, 0.0; - robotstate_.mu = robotstate_.mu + Eigen::Vector3d{ v * c * dt, v * s * dt, 0.0 }; - } - else - { - G << 1.0, 0.0, (v * (-c + c_dt)) / w, 0.0, 1.0, (v * (-s + s_dt)) / w, 0.0, 0.0, 1.0; - - V << (-s + s_dt) / w, v* (s - s_dt) / pow(w, 2) + v * dt * (c_dt) / w, (c - c_dt) / w, - -v * (c - c_dt) / pow(w, 2) + v * dt * (s_dt) / w, 0.0, dt; - - robotstate_.mu = robotstate_.mu + Eigen::Vector3d{ v * (-s + s_dt) / w, v * (c - c_dt) / w, w * dt }; - robotstate_.mu(2) = angleLimitChecking(robotstate_.mu(2)); - } - - M << pow(p_a1_ * v, 2) + pow(p_a2_ * w, 2), 0.0, 0.0, pow(p_a3_ * v, 2) + pow(p_a4_ * w, 2); - robotstate_.sigma = G * robotstate_.sigma * G.transpose() + V * M * V.transpose(); -} - -void Ekf::predict_omni_ucekf(double v_x, double v_y, double w, double dt) -{ - /* ucekf prediction function for omni wheel */ - double d_x; - double d_y; - double d_theta; - if (dt == 0) { - d_x = v_x / p_odom_freq_; - d_y = v_y / p_odom_freq_; - d_theta = w / p_odom_freq_; - } - else { - d_x = v_x * dt; - d_y = v_y * dt; - d_theta = w * dt; - } - double theta = robotstate_.mu(2); - double theta_ = theta + d_theta / 2; - double s_theta = sin(theta); - double c_theta = cos(theta); - double s__theta = sin(theta_); - double c__theta = cos(theta_); - double var_x = 0; - double var_y = 0; - double var_theta = 0; - - double x_pre = 0; - double y_pre = 0; - double theta_pre = 0; - - Eigen::Matrix3d G; - Eigen::Matrix3d W; - Eigen::Vector3d stdev_vec; - Eigen::Vector3d error_vec; - Eigen::DiagonalMatrix cov_motion; - - Eigen::Vector3d state_past; - Eigen::Matrix3d cov_past; - state_past = robotstate_.mu; - cov_past = robotstate_.sigma; - - // Jacobian matrix for Ekf linearization - G << 1.0, 0.0, -d_x * s_theta - d_y * c_theta, 0.0, 1.0, d_x* c_theta - d_y * s_theta, 0.0, 0.0, 1.0; - - W << c__theta, -s__theta, -d_x * s__theta / 2 - d_y * c__theta / 2, s__theta, c__theta, - d_x* c__theta / 2 - d_y * s__theta / 2, 0.0, 0.0, 1.0; - - // calculate model covariance - error_vec << d_x, d_y, d_theta; - stdev_vec = P_omni_model_ * error_vec; - var_x = stdev_vec(0) * stdev_vec(0); - var_y = stdev_vec(1) * stdev_vec(1); - var_theta = stdev_vec(2) * stdev_vec(2); - cov_motion = Eigen::Vector3d{ var_x, var_y, var_theta }.asDiagonal(); - - // Mean of Prediction - x_pre = state_past(0) + d_x * c__theta - d_y * s__theta; - y_pre = state_past(1) + d_x * s__theta + d_y * c__theta; - theta_pre = state_past(2) + d_theta; - robotstate_.mu << x_pre, y_pre, theta_pre; - - // Covariance of Prediction - robotstate_.sigma = G * cov_past * G.transpose() + W * cov_motion * W.transpose(); - // cout << "predict sigma " << robotstate_.sigma << endl; -} - -void Ekf::predict_omni_ekf(double v_x, double v_y, double w, double dt, Eigen::Matrix3d odom_sigma) -{ - /* ekf prediction function for omni wheel */ - Eigen::Vector3d d_state; - - /* COMMENT : get current velocity vector matrix */ - if (dt == 0) d_state << (v_x / p_odom_freq_), - (v_y / p_odom_freq_), - (w / p_odom_freq_); - else d_state << (v_x * dt), - (v_y * dt), - (w * dt); - - double theta_ = robotstate_.mu(2) + d_state(2) / 2; - double s__theta = sin(theta_); - double c__theta = cos(theta_); - - Eigen::Matrix3d A; - A << 1, 0, 0, 0, 1, 0, 0, 0, 1; - - Eigen::Matrix3d B; - B << c__theta, -s__theta, 0, s__theta, c__theta, 0, 0, 0, 1; - - Eigen::Matrix3d cov_past; - cov_past = robotstate_.sigma; - - /* Update robot state mean */ - robotstate_.mu = A * robotstate_.mu + B * d_state; - - /* Update robot state covariance ( sigma ) */ - robotstate_.sigma = A * cov_past * A.transpose() + odom_sigma; -} - -void Ekf::update_landmark() -{ - // ekf update step: - if (if_new_obstacles_) - { - // ROS_INFO("update landmark"); - // cout << "beacon: " << endl; - // vector of the max j_max for each beacon - vector j_beacon_max = { p_mini_likelihood_, p_mini_likelihood_, p_mini_likelihood_ }; - - vector z_j_beacon_max = { Eigen::Vector3d(0.0, 0.0, 0.0), Eigen::Vector3d(0.0, 0.0, 0.0), - Eigen::Vector3d(0.0, 0.0, 0.0) }; - - vector z_i_beacon_max = { Eigen::Vector3d(0.0, 0.0, 0.0), Eigen::Vector3d(0.0, 0.0, 0.0), - Eigen::Vector3d(0.0, 0.0, 0.0) }; - // cout << z_j_beacon_max[0] << z_j_beacon_max[1] << z_j_beacon_max[2] << - // endl; - Eigen::Matrix3d zero_mat = Eigen::Matrix3d(Eigen::Vector3d{ 0.0, 0.0, 0.0 }.asDiagonal()); - vector H_j_beacon_max = { zero_mat, zero_mat, zero_mat }; - vector S_j_beacon_max = { zero_mat, zero_mat, zero_mat }; - - while (!beacon_from_scan_.empty()) - { // for all scanned beacon - Eigen::Vector2d landmark_scan = beacon_from_scan_.back(); - beacon_from_scan_.pop_back(); - // transfer landmark_scan type from (x, y) to (r, phi) - Eigen::Vector3d z_i = cartesianToPolar(landmark_scan, Eigen::Vector3d(0, 0, 0)); - // cout << "z_i: " << z_i << endl; - double j_max = p_mini_likelihood_; - int k_max = 0; - int k = 0; - Eigen::Vector3d z_j_max; - Eigen::Matrix3d H_j_max; - Eigen::Matrix3d S_j_max; - - // cout << "beacon: " << endl; - - for (Eigen::Vector2d landmark_k : beacon_in_map_) - { // for all beacon in map - double j_k; - Eigen::Vector3d z_k; - Eigen::Matrix3d H_k; - Eigen::Matrix3d S_k; - std::tie(z_k, H_k) = cartesianToPolarWithH(landmark_k, robotstate_.mu); - S_k = H_k * robotstate_.sigma * H_k.transpose() + Eigen::Matrix3d(Q_); - try - { - // original - // j_k = 1/sqrt((2*M_PI*S_k).determinant()) * - // exp(-0.5*(safeMinusTheta(z_i, - // z_k)).transpose()*S_k.inverse()*(safeMinusTheta(z_i, z_k))); - // ln(j_k()) version - // cout << "d_z " << safeMinusTheta(z_i, z_k)(1) << endl; - j_k = -0.5 * safeMinusTheta(z_i, z_k).transpose() * S_k.inverse() * (safeMinusTheta(z_i, z_k)) - - log(sqrt((2 * M_PI * S_k).determinant())); - // cout << j_k << endl; - if (j_k > j_max) - { - j_max = j_k; - k_max = k; - z_j_max = z_k; - H_j_max = H_k; - S_j_max = S_k; - } - } - catch (const std::exception& e) - { - ROS_ERROR("%s", e.what()); - // std::cerr << e.what() << '\n'; - } - k += 1; - } - // if(j_max > mini_likelihood_update_){ - // Eigen::Matrix3d K_i; - // K_i = robotstate_bar_.sigma*H_j_max.transpose()*S_j_max.inverse(); - // robotstate_bar_.mu += K_i*(z_i-z_j_max); - // robotstate_bar_.sigma = (Eigen::Matrix3d::Identity() - - // K_i*H_j_max)*robotstate_bar_.sigma; - // } - - // only update three time but now it might update wrong beacon pillar - // for the 3 beacon pillars, take out the largest 3 j value and z, H, S - // matrix - if (j_max > j_beacon_max[k_max]) - { - j_beacon_max[k_max] = j_max; - z_j_beacon_max[k_max] = z_j_max; - z_i_beacon_max[k_max] = z_i; - H_j_beacon_max[k_max] = H_j_max; - S_j_beacon_max[k_max] = S_j_max; - // for debug - update_beacon_[k_max] = landmark_scan; - } - } - for (int i = 0; i < 3; i++) - { - if (j_beacon_max[i] > p_mini_likelihood_update_) - { - // cout << "update, j = " << j_beacon_max[i] << endl; - Eigen::Matrix3d K_i; - K_i = robotstate_.sigma * H_j_beacon_max[i].transpose() * S_j_beacon_max[i].inverse(); - robotstate_.mu += K_i * (safeMinusTheta(z_i_beacon_max[i], z_j_beacon_max[i])); - robotstate_.sigma = (Eigen::Matrix3d::Identity() - K_i * H_j_beacon_max[i]) * robotstate_.sigma; - } - else - { - update_beacon_[i] = Eigen::Vector2d(-1, -1); - // cout << "didn't update, j = " << j_beacon_max[i] << endl; - } - } - } - // finish once ekf, change the flag - if_new_obstacles_ = false; -} - -void Ekf::update_gps(Eigen::Vector3d gps_pose, Eigen::Matrix3d gps_cov) -{ - /* Update function for basic ekf */ - if (if_gps == true) - { - Eigen::Vector3d cur_pose; - Eigen::Matrix3d cur_cov; - cur_pose = robotstate_.mu; - cur_cov = robotstate_.sigma; - - Eigen::Vector3d d_z; - - d_z << gps_pose(0) - cur_pose(0), gps_pose(1) - cur_pose(1), angleLimitChecking(gps_pose(2) - cur_pose(2)); - - Eigen::Matrix3d H; - Eigen::Matrix3d S; - Eigen::Matrix3d K; - Eigen::Vector3d mu; - Eigen::Matrix3d sigma; - - /* COMMENT : transpose from base to map ( not used ) */ - H << 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0; - - /* COMMENT : denominator for kalman gain ( previous + measurment covariance ) */ - S = H * cur_cov * H.transpose() + Eigen::Matrix3d(gps_cov); - - /* COMMENT : kalman gain K */ - K = cur_cov * H.transpose() * S.inverse(); - - /* COMMENT : update current pose with kalman gain */ - mu = cur_pose + K * d_z; - - /* COMMENT : update covariance = previous cov - kalman gain * transpose * previous cov - * = ( 1 - kalman gain * transpose ) * previous cov - */ - sigma = (Eigen::Matrix3d::Identity() - K * H) * cur_cov; - - robotstate_.mu = mu; - robotstate_.sigma = sigma; - } - if_gps = false; -} - -double Ekf::euclideanDistance(Eigen::Vector2d a, Eigen::Vector3d b) -{ - return sqrt(pow((b(0) - a(0)), 2) + pow((b(1) - a(1)), 2)); -} - -double Ekf::euclideanDistance(Eigen::Vector2d a, Eigen::Vector2d b) -{ - return sqrt(pow((b(0) - a(0)), 2) + pow((b(1) - a(1)), 2)); -} - -double Ekf::angleLimitChecking(double theta) -{ - while (theta > M_PI) - { - theta -= M_PI * 2; - } - while (theta <= -M_PI) - { - theta += M_PI * 2; - } - return theta; -} - -Eigen::Vector3d Ekf::safeMinusTheta(Eigen::Vector3d a, Eigen::Vector3d b) -{ - Eigen::Vector3d a_minus_b = a - b; - a_minus_b(1) = angleLimitChecking(a_minus_b(1)); - return a_minus_b; -} - -Eigen::Vector3d Ekf::cartesianToPolar(Eigen::Vector2d point, Eigen::Vector3d origin) -{ - // transpose point from cartesian to polar with given origin - double q_sqrt = euclideanDistance(point, origin); - double q = pow(q_sqrt, 2); - double dx = point(0) - origin(0); - double dy = point(1) - origin(1); - Eigen::Vector3d z; - z << q_sqrt, atan2(dy, dx) - origin(2), 1.0; - z(1) = angleLimitChecking(z(1)); - return z; -} - -std::tuple Ekf::cartesianToPolarWithH(Eigen::Vector2d point, Eigen::Vector3d origin) -{ - // transpose point from cartesian to polar with given origin - double q_sqrt = euclideanDistance(point, origin); - double q = pow(q_sqrt, 2); - double dx = point(0) - origin(0); - double dy = point(1) - origin(1); - Eigen::Vector3d z; - Eigen::Matrix3d H; - z << q_sqrt, atan2(dy, dx) - origin(2), 1.0; - z(1) = angleLimitChecking(z(1)); - H << -(dx / q_sqrt), -(dy / q_sqrt), 0, dy / q, -dx / q, -1, 0, 0, 0; - return std::make_tuple(z, H); -} - -Eigen::Vector2d Ekf::tfBasefpToMap(Eigen::Vector2d point, Eigen::Vector3d robot_pose) -{ - double s = sin(robot_pose(2)); - double c = cos(robot_pose(2)); - Eigen::Matrix2d rotation_mat; - rotation_mat << c, -s, s, c; - Eigen::Vector2d point_map = rotation_mat * point + Eigen::Vector2d{ robot_pose(0), robot_pose(1) }; - return point_map; -} - -void Ekf::setposeCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& pose_msg) -{ - double x = pose_msg->pose.pose.position.x; - double y = pose_msg->pose.pose.position.y; - - tf2::Quaternion q; - tf2::fromMsg(pose_msg->pose.pose.orientation, q); - tf2::Matrix3x3 qt(q); - double _, yaw; - qt.getRPY(_, _, yaw); - - robotstate_.mu(0) = x; - robotstate_.mu(1) = y; - robotstate_.mu(2) = yaw; - - robotstate_.sigma(0, 0) = pose_msg->pose.covariance[0]; // x-x - robotstate_.sigma(0, 1) = pose_msg->pose.covariance[1]; // x-y - robotstate_.sigma(0, 2) = pose_msg->pose.covariance[5]; // x-theta - robotstate_.sigma(1, 0) = pose_msg->pose.covariance[6]; // y-x - robotstate_.sigma(1, 1) = pose_msg->pose.covariance[7]; // y-y - robotstate_.sigma(1, 2) = pose_msg->pose.covariance[11]; // y-theta - robotstate_.sigma(2, 0) = pose_msg->pose.covariance[30]; // theta-x - robotstate_.sigma(2, 1) = pose_msg->pose.covariance[31]; // theta-y - robotstate_.sigma(2, 2) = pose_msg->pose.covariance[35]; // theta-theta - - // ROS_INFO_STREAM("set initial x at " << x << "\n"); - // ROS_INFO_STREAM("set initial y at " << y << "\n"); - // ROS_INFO_STREAM("set initial theta at " << yaw << "\n"); -} - -bool Ekf::fast_spin(std_srvs::SetBool::Request &req, std_srvs::SetBool::Response &res){ - is_spinning = req.data; - res.success = true; - return true; -} - - -void Ekf::odomCallback(const nav_msgs::Odometry::ConstPtr& odom_msg) -{ - const ros::Time stamp = odom_msg->header.stamp; - // const ros::Time stamp = ros::Time::now() + ros::Duration(0.2); - if (first_cb) - { - dt_ = odom_msg->header.stamp.toSec() - t_last; - p_odom_freq_ = 1 / dt_; - } - else - { - first_cb = true; - dt_ = 0.0; - } - t_last = odom_msg->header.stamp.toSec(); - // ROS_INFO("dt_ = %f", dt_); - - double v_x = odom_msg->twist.twist.linear.x; - double v_y = odom_msg->twist.twist.linear.y; - double w = odom_msg->twist.twist.angular.z; - - Eigen::Matrix3d odom_sigma; - odom_sigma(0, 0) = odom_msg->twist.covariance[0]; // x-x - odom_sigma(0, 1) = 0.0; // x-y - odom_sigma(0, 2) = 0.0; // x-theta - odom_sigma(1, 0) = 0.0; // y-x - // odom_sigma(0, 1) = odom_msg->twist.covariance[1]; // x-y - // odom_sigma(0, 2) = odom_msg->twist.covariance[5]; // x-theta - // odom_sigma(1, 0) = odom_msg->twist.covariance[6]; // y-x - odom_sigma(1, 1) = odom_msg->twist.covariance[7]; // y-y - odom_sigma(1, 2) = 0.0; // y-theta - odom_sigma(2, 0) = 0.0; // theta-x - odom_sigma(2, 1) = 0.0; // theta-y - // odom_sigma(1, 2) = odom_msg->twist.covariance[11]; // y-theta - // odom_sigma(2, 0) = odom_msg->twist.covariance[30]; // theta-x - // odom_sigma(2, 1) = odom_msg->twist.covariance[31]; // theta-y - odom_sigma(2, 2) = odom_msg->twist.covariance[35]; // theta-theta - - // for calculate time cost - // struct timespec tt1, tt2; - // clock_gettime(CLOCK_REALTIME, &tt1); - - if(chassis_type == 0){ - if(if_gps){ - predict_diff_ekf(v_x, w, dt_, odom_sigma); /* ekf prediction */ - if(!is_spinning){ - update_gps(gps_mu, gps_sigma); /* ekf update */ - } - if_gps = false; - }else{ - predict_diff_ucekf(v_x, w, dt_); /* ucekf prediction */ - if(!is_spinning){ - // update_landmark(); /* ucekf update */ - } - } - }else if(chassis_type == 1){ - if(if_gps){ - /* Update with ekf */ - predict_omni_ekf(v_x, v_y, w, dt_, odom_sigma); /* ekf prediction */ - if(!is_spinning){ - update_gps(gps_mu, gps_sigma); /* ekf update */ - } - if_gps = false; - }else{ - predict_omni_ucekf(v_x, v_y, w, dt_); /* ucekf prediction */ - if(!is_spinning){ - // update_landmark(); /* ucekf update */ - } - } - } - if(ready){ - publishEkfPose(stamp); // stamp = acturally when does tf been generated - publishUpdateBeacon(stamp); - broadcastEkfTransform(odom_msg); // stamp = odom.stamp so frequency = odom's frequency - } -} - -void Ekf::gpsCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& pose_msg) -{ - // ROS_INFO("gpsCallback"); - tf2::Quaternion q; - tf2::fromMsg(pose_msg->pose.pose.orientation, q); - tf2::Matrix3x3 qt(q); - double _, yaw; - qt.getRPY(_, _, yaw); - - lidar_state_.mu(0) = pose_msg->pose.pose.position.x; - lidar_state_.mu(1) = pose_msg->pose.pose.position.y; - lidar_state_.mu(2) = yaw; - - lidar_state_.sigma(0, 0) = pose_msg->pose.covariance[0]; // x-x - lidar_state_.sigma(0, 1) = pose_msg->pose.covariance[1]; // x-y - lidar_state_.sigma(0, 2) = pose_msg->pose.covariance[5]; // x-theta - lidar_state_.sigma(1, 0) = pose_msg->pose.covariance[6]; // y-x - lidar_state_.sigma(1, 1) = pose_msg->pose.covariance[7]; // y-y - lidar_state_.sigma(1, 2) = pose_msg->pose.covariance[11]; // y-theta - lidar_state_.sigma(2, 0) = pose_msg->pose.covariance[30]; // theta-x - lidar_state_.sigma(2, 1) = pose_msg->pose.covariance[31]; // theta-y - lidar_state_.sigma(2, 2) = pose_msg->pose.covariance[35]; // theta-theta - - update_lidar_ = true; -} - -void Ekf::updateTimerCallback(const ros::TimerEvent& e) { - - if (!update_lidar_) return; - - // Update the lidar data - if (update_lidar_) { - gps_mu(0) = lidar_state_.mu(0); - gps_mu(1) = lidar_state_.mu(1); - gps_mu(2) = lidar_state_.mu(2); - - gps_sigma(0, 0) = lidar_state_.sigma(0, 0); // x-x - gps_sigma(0, 1) = lidar_state_.sigma(0, 1); // x-y - gps_sigma(0, 2) = lidar_state_.sigma(0, 2); // x-theta - gps_sigma(1, 0) = lidar_state_.sigma(1, 0); // y-x - gps_sigma(1, 1) = lidar_state_.sigma(1, 1); // y-y - gps_sigma(1, 2) = lidar_state_.sigma(1, 2); // y-theta - gps_sigma(2, 0) = lidar_state_.sigma(2, 0); // theta-x - gps_sigma(2, 1) = lidar_state_.sigma(2, 1); // theta-y - gps_sigma(2, 2) = lidar_state_.sigma(2, 2); // theta-theta - } - - if_gps = true; - update_lidar_ = false; -} - -void Ekf::beaconCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& pose_msg) -{ - //this is not used. - tf2::Quaternion q; - tf2::fromMsg(pose_msg->pose.pose.orientation, q); - tf2::Matrix3x3 qt(q); - double _, yaw; - qt.getRPY(_, _, yaw); - - beacon_mu(0) = pose_msg->pose.pose.position.x; - beacon_mu(1) = pose_msg->pose.pose.position.y; - beacon_mu(2) = yaw; - - beacon_sigma(0, 0) = pose_msg->pose.covariance[0]; // x-x - beacon_sigma(0, 1) = pose_msg->pose.covariance[1]; // x-y - beacon_sigma(0, 2) = pose_msg->pose.covariance[5]; // x-theta - beacon_sigma(1, 0) = pose_msg->pose.covariance[6]; // y-x - beacon_sigma(1, 1) = pose_msg->pose.covariance[7]; // y-y - beacon_sigma(1, 2) = pose_msg->pose.covariance[11]; // y-theta - beacon_sigma(2, 0) = pose_msg->pose.covariance[30]; // theta-x - beacon_sigma(2, 1) = pose_msg->pose.covariance[31]; // theta-y - beacon_sigma(2, 2) = pose_msg->pose.covariance[35]; // theta-theta - if_beacon = true; -} - -void Ekf::obstaclesCallback(const obstacle_detector::Obstacles::ConstPtr& obstacle_msg) -{ - if_new_obstacles_ = false; - beacon_from_scan_.clear(); // drop out all element in list - - for (int i = 0; i < obstacle_msg->circles.size(); i++) - { - obstacle_detector::CircleObstacle circle = obstacle_msg->circles[i]; - Eigen::Vector2d xy(circle.center.x, circle.center.y); - Eigen::Vector2d xy_map = tfBasefpToMap(xy, robotstate_.mu); - // filter out those obstacles radius bigger than 0.1m - // if(circle.true_radius >= 0.1){ - // continue; - // } - // filter out those obstacles position do not close the beacon pillar on map - for (auto const& i : beacon_in_map_) - { - double distance = euclideanDistance(xy_map, i); - if (distance < p_max_obstacle_distance_) - { - // alst time in real world - // experience we take < 0.5 - beacon_from_scan_.push_back(xy); - } - } - // beacon_from_scan_.push_back(xy); - } - if_new_obstacles_ = true; -} - -void Ekf::publishEkfPose(const ros::Time& stamp) -{ - static RobotState prev_robot_state; - static geometry_msgs::Twist prev_velocity; - - nav_msgs::Odometry ekf_pose; - ekf_pose.header.stamp = stamp; - ekf_pose.header.frame_id = p_robot_name_ + "map"; - ekf_pose.pose.pose.position.x = robotstate_.mu(0); - ekf_pose.pose.pose.position.y = robotstate_.mu(1); - tf2::Quaternion q; - q.setRPY(0, 0, robotstate_.mu(2)); - ekf_pose.pose.pose.orientation.x = q.x(); - ekf_pose.pose.pose.orientation.y = q.y(); - ekf_pose.pose.pose.orientation.z = q.z(); - ekf_pose.pose.pose.orientation.w = q.w(); - ekf_pose.pose.covariance[0] = robotstate_.sigma(0, 0); // x-x - ekf_pose.pose.covariance[1] = robotstate_.sigma(0, 1); // x-y - ekf_pose.pose.covariance[5] = robotstate_.sigma(0, 2); // x-theta - ekf_pose.pose.covariance[6] = robotstate_.sigma(1, 0); // y-x - ekf_pose.pose.covariance[7] = robotstate_.sigma(1, 1); // y-y - ekf_pose.pose.covariance[11] = robotstate_.sigma(1, 2); // y-theta - ekf_pose.pose.covariance[30] = robotstate_.sigma(2, 0); // theta-x - ekf_pose.pose.covariance[31] = robotstate_.sigma(2, 1); // theta-y - ekf_pose.pose.covariance[35] = robotstate_.sigma(2, 2); // theta-theta - - ekf_pose.twist.twist.linear.x = ((robotstate_.mu(0) - prev_robot_state.mu(0)) / 0.01) * p_velocity_lpf_gain_ - + prev_velocity.linear.x * (1 - p_velocity_lpf_gain_); - - ekf_pose.twist.twist.linear.y = ((robotstate_.mu(1) - prev_robot_state.mu(1)) / 0.01) * p_velocity_lpf_gain_ - + prev_velocity.linear.y * (1 - p_velocity_lpf_gain_); - - ekf_pose.twist.twist.angular.z = ((robotstate_.mu(2) - prev_robot_state.mu(2)) / 0.01) * p_velocity_lpf_gain_ - + prev_velocity.angular.z * (1 - p_velocity_lpf_gain_); - - prev_robot_state = robotstate_; - prev_velocity = ekf_pose.twist.twist; - - ekf_pose_pub_.publish(ekf_pose); -} - -void Ekf::broadcastEkfTransform(const nav_msgs::Odometry::ConstPtr& odom_msg) -{ - tf2::Transform map_to_baseft(tf2::Quaternion(tf2::Vector3(0, 0, 1), robotstate_.mu(2)), - tf2::Vector3(robotstate_.mu(0), robotstate_.mu(1), 0)); - // tf2::Transform odom_to_baseft( - // tf2::Quaternion(0, 0, odom_msg->pose.pose.orientation.z, odom_msg->pose.pose.orientation.w), - // tf2::Vector3(odom_msg->pose.pose.position.x, odom_msg->pose.pose.position.y, 0)); - // tf2::Transform map_to_odom = map_to_baseft * odom_to_baseft.inverse(); - - // geometry_msgs::TransformStamped transformStamped; - // transformStamped.header.stamp = odom_msg->header.stamp; - // transformStamped.header.frame_id = p_robot_name_ + "map"; - // transformStamped.child_frame_id = p_robot_name_ + "odom"; - // transformStamped.transform = tf2::toMsg(map_to_odom); - // br_.sendTransform(transformStamped); - - // Directly broadcast the map to base_footprint transform - geometry_msgs::TransformStamped transformStamped; - transformStamped.header.stamp = odom_msg->header.stamp; - transformStamped.header.frame_id = p_robot_name_ + "map"; - transformStamped.child_frame_id = p_robot_name_ + "base_footprint"; - transformStamped.transform = tf2::toMsg(map_to_baseft); - br_.sendTransform(transformStamped); -} - -void Ekf::publishUpdateBeacon(const ros::Time& stamp) -{ - obstacle_detector::Obstacles update_obstacles; - for (Eigen::Vector2d o : update_beacon_) - { - if (o(0) == -1) - continue; - obstacle_detector::CircleObstacle circle; - circle.center.x = o(0); - circle.center.y = o(1); - circle.radius = 0.35; - circle.true_radius = 0.05; - update_obstacles.circles.push_back(circle); - } - update_obstacles.header.frame_id = p_robot_name_ + "base_footprint"; - update_beacon_pub_.publish(update_obstacles); -} - -int main(int argc, char** argv) -{ - ros::init(argc, argv, "ekf_localization"); - ros::NodeHandle nh; - ros::NodeHandle nh_local("~"); - Ekf ekf(nh, nh_local); - ekf.initialize(); - - while (ros::ok()) - { - ros::spin(); - } -} diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/src/run.cpp b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/src/run.cpp deleted file mode 100644 index af7694f..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/src/run.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include - -class Run { -public: - Run(ros::NodeHandle& nh, ros::NodeHandle& nh_); -private: - void pub_vel(double x, double y, double w); - void timerCallback(const ros::TimerEvent&); - - ros::Publisher vel_pub; - geometry_msgs::Twist cmd; - ros::Timer timer; - std::string robot_name; - double freq; - double acc; - double max_vel; - double max_dis; - int mode; /* 0: linear, 1: rotation */ -}; - -Run::Run(ros::NodeHandle& nh, ros::NodeHandle& nh_) { - bool ok = true; - ok &= nh_.getParam("robot_name", robot_name); - ok &= nh_.getParam("frequency", freq); - ok &= nh_.getParam("acceleration", acc); - ok &= nh_.getParam("max_velocity", max_vel); - ok &= nh_.getParam("max_distance", max_dis); - ok &= nh_.getParam("mode", mode); - - vel_pub = nh.advertise("cmd_vel", 10); - timer = nh.createTimer(ros::Duration(1.0 / freq), &Run::timerCallback, this); -} - -void Run::pub_vel(double x, double y, double w) { - geometry_msgs::Twist msg; - msg.linear.x = x; - msg.linear.y = y; - msg.angular.z = w; - vel_pub.publish(msg); -} - -void Run::timerCallback(const ros::TimerEvent& event) { - static double count = 0; - static double odom = 0; - double out = 0; - - out = acc * count; - if(out > max_vel) out = max_vel; - - odom += double(out / freq); - if(odom <= max_dis){ - if(mode == 0) pub_vel(out, 0, 0); - if(mode == 1) pub_vel(0, out, 0); - if(mode == 2) pub_vel(0, 0, out); - } - else{ - pub_vel(0, 0, 0); - } - - count += double(1.0 / freq); -} - -int main(int argc, char** argv) { - ros::init(argc, argv, "run"); - ros::NodeHandle nh(""); - ros::NodeHandle nh_local("~"); - - Run run(nh, nh_local); - - while (ros::ok()) { - ros::spinOnce(); - } - return 0; -} \ No newline at end of file diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/src/uc_ekf.cpp b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/src/uc_ekf.cpp deleted file mode 100644 index 0541446..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/eurobot_localization/src/uc_ekf.cpp +++ /dev/null @@ -1,436 +0,0 @@ -/** - * - * @file ekf.cpp - * @brief - * - * @code{.unparsed} - * _____ - * / /::\ ___ ___ - * / /:/\:\ / /\ / /\ - * / /:/ \:\ / /:/ / /:/ - * /__/:/ \__\:| /__/::\ / /:/ - * \ \:\ / /:/ \__\/\:\__ / /::\ - * \ \:\ /:/ \ \:\/\ /__/:/\:\ - * \ \:\/:/ \__\::/ \__\/ \:\ - * \ \::/ /__/:/ \ \:\ - * \__\/ \__\/ \__\/ - * @endcode - * - * @author guanlunlu (guanlunlu@gapp.nthu.edu.tw) - * @version 0.1 - * @date 2022-03-05 - * - */ - -#include "ekf.h" -using namespace std; - -void Ekf::initialize() -{ - // get parameter - nh_.param("/ekf/robot_name", p_robot_name_, ""); - nh_.param("/ekf/initial_x", p_initial_x_, 0.4); - nh_.param("/ekf/initial_y", p_initial_y_, 2.7); - nh_.param("/ekf/initial_theta", p_initial_theta_deg_, -90.0); - - nh_.param("/ekf/beacon_ax", p_beacon_ax_, 0.05); - nh_.param("/ekf/beacon_ay", p_beacon_ay_, 3.1); - nh_.param("/ekf/beacon_bx", p_beacon_bx_, 1.05); - nh_.param("/ekf/beacon_by", p_beacon_by_, -0.1); - nh_.param("/ekf/beacon_cx", p_beacon_cx_, 1.95); - nh_.param("/ekf/beacon_cy", p_beacon_cy_, 3.1); - // for beacon position in map list{ax, ay, bx, by, cx, cy} - Eigen::Vector2d beacon_a{ p_beacon_ax_, p_beacon_ay_ }; - Eigen::Vector2d beacon_b{ p_beacon_bx_, p_beacon_by_ }; - Eigen::Vector2d beacon_c{ p_beacon_cx_, p_beacon_cy_ }; - - beacon_in_map_ = { beacon_a, beacon_b, beacon_c }; - - // for debug - update_beacon_ = { Eigen::Vector2d(0.0, 0.0), Eigen::Vector2d(0.0, 0.0), Eigen::Vector2d(0.0, 0.0) }; - - // for robot state - robotstate_.mu << p_initial_x_, p_initial_y_, degToRad(p_initial_theta_deg_); - robotstate_.sigma << 0, 0, 0, 0, 0, 0, 0, 0, 0; - - nh_.param("/ekf/odom_freq", p_odom_freq_, 50.0); - dt_ = 1.0 / p_odom_freq_; - - // ekf parameter - // measurement - nh_.param("/ekf/update_cov_1", p_Q1_, 0.05); - nh_.param("/ekf/update_cov_2", p_Q2_, 0.05); - nh_.param("/ekf/update_cov_3", p_Q3_, 0.05); - Q_ = Eigen::Vector3d{ p_Q1_, p_Q2_, p_Q3_ }.asDiagonal(); - // use log(j_k) - nh_.param("/ekf/mini_likelihood", p_mini_likelihood_, -10000.0); - nh_.param("/ekf/mini_likelihood_update", p_mini_likelihood_update_, 0.4); - // use j_k - // mini_likelihood_ = 0.0; - // mini_likelihood_update_ = 25.0; - - // for obstacle filtering - nh_.param("/ekf/max_obstacle_distance", p_max_obstacle_distance_, 0.5); - - // for beacon piller detection - if_new_obstacles_ = false; - - // for ros - setpose_sub_ = nh_.subscribe("initialpose", 50, &Ekf::setposeCallback, this); - odom_sub_ = nh_.subscribe("global_filter", 50, &Ekf::odomCallback, this); - raw_obstacles_sub_ = nh_.subscribe("obstacles_to_base", 10, &Ekf::obstaclesCallback, this); - ekf_pose_pub_ = nh_.advertise("ekf_pose", 10); - update_beacon_pub_ = nh_.advertise("update_beacon", 10); - - // for time calculate - count_ = 0; - duration_ = 0.0; -} - -void Ekf::update_landmark() -{ - // ekf update step: - if (if_new_obstacles_) - { - ROS_INFO("update landmark"); - // cout << "beacon: " << endl; - // vector of the max j_max for each beacon - vector j_beacon_max = { p_mini_likelihood_, p_mini_likelihood_, p_mini_likelihood_ }; - - vector z_j_beacon_max = { Eigen::Vector3d(0.0, 0.0, 0.0), Eigen::Vector3d(0.0, 0.0, 0.0), - Eigen::Vector3d(0.0, 0.0, 0.0) }; - - vector z_i_beacon_max = { Eigen::Vector3d(0.0, 0.0, 0.0), Eigen::Vector3d(0.0, 0.0, 0.0), - Eigen::Vector3d(0.0, 0.0, 0.0) }; - // cout << z_j_beacon_max[0] << z_j_beacon_max[1] << z_j_beacon_max[2] << - // endl; - Eigen::Matrix3d zero_mat = Eigen::Matrix3d(Eigen::Vector3d{ 0.0, 0.0, 0.0 }.asDiagonal()); - vector H_j_beacon_max = { zero_mat, zero_mat, zero_mat }; - vector S_j_beacon_max = { zero_mat, zero_mat, zero_mat }; - - while (!beacon_from_scan_.empty()) - { // for all scanned beacon - Eigen::Vector2d landmark_scan = beacon_from_scan_.back(); - beacon_from_scan_.pop_back(); - // transfer landmark_scan type from (x, y) to (r, phi) - Eigen::Vector3d z_i = cartesianToPolar(landmark_scan, Eigen::Vector3d(0, 0, 0)); - // cout << "z_i: " << z_i << endl; - double j_max = p_mini_likelihood_; - int k_max = 0; - int k = 0; - Eigen::Vector3d z_j_max; - Eigen::Matrix3d H_j_max; - Eigen::Matrix3d S_j_max; - - // cout << "beacon: " << endl; - - for (Eigen::Vector2d landmark_k : beacon_in_map_) - { // for all beacon in map - double j_k; - Eigen::Vector3d z_k; - Eigen::Matrix3d H_k; - Eigen::Matrix3d S_k; - std::tie(z_k, H_k) = cartesianToPolarWithH(landmark_k, robotstate_.mu); - S_k = H_k * robotstate_.sigma * H_k.transpose() + Eigen::Matrix3d(Q_); - try - { - // original - // j_k = 1/sqrt((2*M_PI*S_k).determinant()) * - // exp(-0.5*(safeMinusTheta(z_i, - // z_k)).transpose()*S_k.inverse()*(safeMinusTheta(z_i, z_k))); - // ln(j_k()) version - // cout << "d_z " << safeMinusTheta(z_i, z_k)(1) << endl; - j_k = -0.5 * safeMinusTheta(z_i, z_k).transpose() * S_k.inverse() * (safeMinusTheta(z_i, z_k)) - - log(sqrt((2 * M_PI * S_k).determinant())); - // cout << j_k << endl; - if (j_k > j_max) - { - j_max = j_k; - k_max = k; - z_j_max = z_k; - H_j_max = H_k; - S_j_max = S_k; - } - } - catch (const std::exception& e) - { - ROS_ERROR("%s", e.what()); - // std::cerr << e.what() << '\n'; - } - k += 1; - } - // if(j_max > mini_likelihood_update_){ - // Eigen::Matrix3d K_i; - // K_i = robotstate_bar_.sigma*H_j_max.transpose()*S_j_max.inverse(); - // robotstate_bar_.mu += K_i*(z_i-z_j_max); - // robotstate_bar_.sigma = (Eigen::Matrix3d::Identity() - - // K_i*H_j_max)*robotstate_bar_.sigma; - // } - - // only update three time but now it might update wrong beacon pillar - // for the 3 beacon pillars, take out the largest 3 j value and z, H, S - // matrix - if (j_max > j_beacon_max[k_max]) - { - j_beacon_max[k_max] = j_max; - z_j_beacon_max[k_max] = z_j_max; - z_i_beacon_max[k_max] = z_i; - H_j_beacon_max[k_max] = H_j_max; - S_j_beacon_max[k_max] = S_j_max; - // for debug - update_beacon_[k_max] = landmark_scan; - } - } - for (int i = 0; i < 3; i++) - { - if (j_beacon_max[i] > p_mini_likelihood_update_) - { - // cout << "update, j = " << j_beacon_max[i] << endl; - Eigen::Matrix3d K_i; - K_i = robotstate_.sigma * H_j_beacon_max[i].transpose() * S_j_beacon_max[i].inverse(); - robotstate_.mu += K_i * (safeMinusTheta(z_i_beacon_max[i], z_j_beacon_max[i])); - robotstate_.sigma = (Eigen::Matrix3d::Identity() - K_i * H_j_beacon_max[i]) * robotstate_.sigma; - } - else - { - update_beacon_[i] = Eigen::Vector2d(-1, -1); - // cout << "didn't update, j = " << j_beacon_max[i] << endl; - } - } - } - // finish once ekf, change the flag - if_new_obstacles_ = false; -} - -double Ekf::euclideanDistance(Eigen::Vector2d a, Eigen::Vector3d b) -{ - return sqrt(pow((b(0) - a(0)), 2) + pow((b(1) - a(1)), 2)); -} - -double Ekf::euclideanDistance(Eigen::Vector2d a, Eigen::Vector2d b) -{ - return sqrt(pow((b(0) - a(0)), 2) + pow((b(1) - a(1)), 2)); -} - -double Ekf::angleLimitChecking(double theta) -{ - while (theta > M_PI) - { - theta -= M_PI * 2; - } - while (theta <= -M_PI) - { - theta += M_PI * 2; - } - return theta; -} - -Eigen::Vector3d Ekf::safeMinusTheta(Eigen::Vector3d a, Eigen::Vector3d b) -{ - Eigen::Vector3d a_minus_b = a - b; - a_minus_b(1) = angleLimitChecking(a_minus_b(1)); - return a_minus_b; -} - -Eigen::Vector3d Ekf::cartesianToPolar(Eigen::Vector2d point, Eigen::Vector3d origin) -{ - // transpose point from cartesian to polar with given origin - double q_sqrt = euclideanDistance(point, origin); - double q = pow(q_sqrt, 2); - double dx = point(0) - origin(0); - double dy = point(1) - origin(1); - Eigen::Vector3d z; - z << q_sqrt, atan2(dy, dx) - origin(2), 1.0; - z(1) = angleLimitChecking(z(1)); - return z; -} - -std::tuple Ekf::cartesianToPolarWithH(Eigen::Vector2d point, Eigen::Vector3d origin) -{ - // transpose point from cartesian to polar with given origin - double q_sqrt = euclideanDistance(point, origin); - double q = pow(q_sqrt, 2); - double dx = point(0) - origin(0); - double dy = point(1) - origin(1); - Eigen::Vector3d z; - Eigen::Matrix3d H; - z << q_sqrt, atan2(dy, dx) - origin(2), 1.0; - z(1) = angleLimitChecking(z(1)); - H << -(dx / q_sqrt), -(dy / q_sqrt), 0, dy / q, -dx / q, -1, 0, 0, 0; - return std::make_tuple(z, H); -} - -void Ekf::setposeCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& pose_msg) -{ - double x = pose_msg->pose.pose.position.x; - double y = pose_msg->pose.pose.position.y; - - tf2::Quaternion q; - tf2::fromMsg(pose_msg->pose.pose.orientation, q); - tf2::Matrix3x3 qt(q); - double _, yaw; - qt.getRPY(_, _, yaw); - - robotstate_.mu(0) = x; - robotstate_.mu(1) = y; - robotstate_.mu(2) = yaw; - - robotstate_.sigma(0, 0) = pose_msg->pose.covariance[0]; // x-x - robotstate_.sigma(0, 1) = pose_msg->pose.covariance[1]; // x-y - robotstate_.sigma(0, 2) = pose_msg->pose.covariance[5]; // x-theta - robotstate_.sigma(1, 0) = pose_msg->pose.covariance[6]; // y-x - robotstate_.sigma(1, 1) = pose_msg->pose.covariance[7]; // y-y - robotstate_.sigma(1, 2) = pose_msg->pose.covariance[11]; // y-theta - robotstate_.sigma(2, 0) = pose_msg->pose.covariance[30]; // theta-x - robotstate_.sigma(2, 1) = pose_msg->pose.covariance[31]; // theta-y - robotstate_.sigma(2, 2) = pose_msg->pose.covariance[35]; // theta-theta - - cout << "set initial x at " << x << endl; - cout << "set initial y at " << y << endl; - cout << "set initial theta at " << yaw << endl; -} - -void Ekf::odomCallback(const nav_msgs::Odometry::ConstPtr& pose_msg) -{ - const ros::Time stamp = ros::Time::now() + ros::Duration(0.2); - double x = pose_msg->pose.pose.position.x; - double y = pose_msg->pose.pose.position.y; - - tf2::Quaternion q; - tf2::fromMsg(pose_msg->pose.pose.orientation, q); - tf2::Matrix3x3 qt(q); - double _, yaw; - qt.getRPY(_, _, yaw); - - robotstate_.mu << x, y, yaw; - robotstate_.sigma(0, 0) = pose_msg->pose.covariance[0]; // x-x - robotstate_.sigma(0, 1) = pose_msg->pose.covariance[1]; // x-y - robotstate_.sigma(0, 2) = pose_msg->pose.covariance[5]; // x-theta - robotstate_.sigma(1, 0) = pose_msg->pose.covariance[6]; // y-x - robotstate_.sigma(1, 1) = pose_msg->pose.covariance[7]; // y-y - robotstate_.sigma(1, 2) = pose_msg->pose.covariance[11]; // y-theta - robotstate_.sigma(2, 0) = pose_msg->pose.covariance[30]; // theta-x - robotstate_.sigma(2, 1) = pose_msg->pose.covariance[31]; // theta-y - robotstate_.sigma(2, 2) = pose_msg->pose.covariance[35]; // theta-theta - - robotstate_.sigma(0, 0) = 0.5; // x-x - robotstate_.sigma(1, 1) = 0.5; // y-y - robotstate_.sigma(2, 2) = 0.5; // theta-theta - - // cout << "v: " << v << "w: " << w << endl; - // for calculate time cost - // struct timespec tt1, tt2; - // clock_gettime(CLOCK_REALTIME, &tt1); - - // predict_diff(v_x, w); - update_landmark(); - publishEkfPose(stamp); // stamp = acturally when does tf been generated - // ROS_INFO("update_landmark = %f %f %f", robotstate_.mu(0), robotstate_.mu(1), robotstate_.mu(2)); - // update_gps(gps_mu, gps_sigma); - - // clock_gettime(CLOCK_REALTIME, &tt2); - // count_ += 1; - // duration_ += (tt2.tv_nsec-tt1.tv_nsec)*1e-9; - // cout << "average time cost is " << duration_/count_ << "s" << endl; -} - -void Ekf::obstaclesCallback(const obstacle_detector::Obstacles::ConstPtr& obstacle_msg) -{ - if_new_obstacles_ = false; - beacon_from_scan_.clear(); // drop out all element in list - // cout << "obstacle to map frame is: " << endl; - for (int i = 0; i < obstacle_msg->circles.size(); i++) - { - obstacle_detector::CircleObstacle circle = obstacle_msg->circles[i]; - Eigen::Vector2d xy(circle.center.x, circle.center.y); - Eigen::Vector2d xy_map = tfBasefpToMap(xy, robotstate_.mu); - // filter out those obstacles radius bigger than 0.1m - // if(circle.true_radius >= 0.1){ - // continue; - // } - // filter out those obstacles position do not close the beacon pillar on map - for (auto const& i : beacon_in_map_) - { - double distance = euclideanDistance(xy_map, i); - if (distance < p_max_obstacle_distance_) - { // alst time in real world - // experience we take < 0.5 - // cout << xy_map << endl; - beacon_from_scan_.push_back(xy); - } - } - // cout << xy << endl; - // beacon_from_scan_.push_back(xy); - } - if_new_obstacles_ = true; -} - -Eigen::Vector2d Ekf::tfBasefpToMap(Eigen::Vector2d point, Eigen::Vector3d robot_pose) -{ - double s = sin(robot_pose(2)); - double c = cos(robot_pose(2)); - Eigen::Matrix2d rotation_mat; - rotation_mat << c, -s, s, c; - Eigen::Vector2d point_map = rotation_mat * point + Eigen::Vector2d{ robot_pose(0), robot_pose(1) }; - return point_map; -} - -void Ekf::publishEkfPose(const ros::Time& stamp) -{ - geometry_msgs::PoseWithCovarianceStamped ekf_pose; - ekf_pose.header.stamp = stamp; - ekf_pose.header.frame_id = "map"; - ekf_pose.pose.pose.position.x = robotstate_.mu(0); - ekf_pose.pose.pose.position.y = robotstate_.mu(1); - tf2::Quaternion q; - q.setRPY(0, 0, robotstate_.mu(2)); - ekf_pose.pose.pose.orientation.x = q.x(); - ekf_pose.pose.pose.orientation.y = q.y(); - ekf_pose.pose.pose.orientation.z = q.z(); - ekf_pose.pose.pose.orientation.w = q.w(); - ekf_pose.pose.covariance[0] = robotstate_.sigma(0, 0); // x-x - ekf_pose.pose.covariance[1] = robotstate_.sigma(0, 1); // x-y - ekf_pose.pose.covariance[5] = robotstate_.sigma(0, 2); // x-theta - ekf_pose.pose.covariance[6] = robotstate_.sigma(1, 0); // y-x - ekf_pose.pose.covariance[7] = robotstate_.sigma(1, 1); // y-y - ekf_pose.pose.covariance[11] = robotstate_.sigma(1, 2); // y-theta - ekf_pose.pose.covariance[30] = robotstate_.sigma(2, 0); // theta-x - ekf_pose.pose.covariance[31] = robotstate_.sigma(2, 1); // theta-y - ekf_pose.pose.covariance[35] = robotstate_.sigma(2, 2); // theta-theta - ekf_pose_pub_.publish(ekf_pose); -} - -void Ekf::publishUpdateBeacon(const ros::Time& stamp) -{ - obstacle_detector::Obstacles update_obstacles; - for (Eigen::Vector2d o : update_beacon_) - { - if (o(0) == -1) - continue; - obstacle_detector::CircleObstacle circle; - circle.center.x = o(0); - circle.center.y = o(1); - circle.radius = 0.35; - circle.true_radius = 0.05; - update_obstacles.circles.push_back(circle); - } - if (p_robot_name_ == "") - update_obstacles.header.frame_id = "base_footprint"; - else - update_obstacles.header.frame_id = p_robot_name_ + "/base_footprint"; - update_beacon_pub_.publish(update_obstacles); -} - -int main(int argc, char** argv) -{ - ros::init(argc, argv, "landmark_update"); - ros::NodeHandle nh; - ros::NodeHandle nh_local("~"); - Ekf ekf(nh, nh_local); - ekf.initialize(); - - while (ros::ok()) - { - ros::spin(); - } -} diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/install.sh b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/install.sh deleted file mode 100755 index 3c64acf..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/install.sh +++ /dev/null @@ -1,35 +0,0 @@ -# Install basic package -sudo apt update -y -sudo apt install ros-noetic-costmap-converter \ - ros-noetic-robot-localization \ - ros-noetic-imu-tools \ - libusb-1.0-0 libusb-1.0-0-dev -y - -PACKAGES_PATH=/home/localization/localization_ws/src/Eurobot-2024-Localization - -# Build LiDAR -rm -rf $PACKAGES_PATH/.YDLidar-SDK/build -mv $PACKAGES_PATH/.YDLidar-SDK $PACKAGES_PATH/YDLidar-SDK -mkdir $PACKAGES_PATH/YDLidar-SDK/build -cd $PACKAGES_PATH/YDLidar-SDK/build -cmake .. -make -sudo make install -mv $PACKAGES_PATH/YDLidar-SDK $PACKAGES_PATH/.YDLidar-SDK - -# Get obstacle_detector -cd $PACKAGES_PATH -cd .. -git clone https://github.com/tysik/obstacle_detector.git - -# Build libsurvive -cd $PACKAGES_PATH/Vive/libsurvive -sudo apt-get install build-essential zlib1g-dev libx11-dev libusb-1.0-0-dev freeglut3-dev liblapacke-dev libopenblas-dev libatlas-base-dev cmake -y -make - -# Build localization worksapce -source /opt/ros/noetic/setup.bash -cd $PACKAGES_PATH/../.. -catkin_make -source /home/localization/localization_ws/devel/setup.bash - diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/.gitignore b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/.gitignore deleted file mode 100644 index 722d5e7..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.vscode diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/CMakeLists.txt b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/CMakeLists.txt deleted file mode 100755 index 696bd15..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/CMakeLists.txt +++ /dev/null @@ -1,71 +0,0 @@ -cmake_minimum_required(VERSION 2.8.3) -project(lidar_localization) - -set(CMAKE_CXX_FLAGS "-std=c++11 -fpermissive ${CMAKE_CXX_FLAGS} -Wfatal-errors\ ") - -find_package( - catkin - REQUIRED - COMPONENTS - roscpp - roslaunch - tf2 - tf2_ros - nodelet - rviz - std_msgs - geometry_msgs - sensor_msgs - laser_geometry - obstacle_detector -) - -find_package(Armadillo REQUIRED) - -catkin_package( - INCLUDE_DIRS include - LIBRARIES ${PROJECT_NAME} ${PROJECT_NAME}_nodelets beacon_scan_tracker area_obstacles_extractor - CATKIN_DEPENDS roscpp rviz std_msgs geometry_msgs sensor_msgs tf2 tf2_ros laser_geometry nodelet obstacle_detector -) - -include_directories( - include - ${catkin_INCLUDE_DIRS} - ${ARMADILLO_INCLUDE_DIRS} -) - -# -# Build libs -# -add_library(lidar_localization src/lidar_localization.cpp) -target_link_libraries(lidar_localization ${catkin_LIBRARIES} ${ARMADILLO_LIBRARIES}) -add_dependencies(lidar_localization ${catkin_EXPORTED_TARGETS}) - -add_library(area_obstacles_extractor src/area_obstacles_extractor.cpp) -target_link_libraries(area_obstacles_extractor ${catkin_LIBRARIES}) -add_dependencies(area_obstacles_extractor ${catkin_EXPORTED_TARGETS}) - -add_library(beacon_scan_tracker src/beacon_scan_tracker.cpp) -target_link_libraries(beacon_scan_tracker ${catkin_LIBRARIES}) -add_dependencies(beacon_scan_tracker ${catkin_EXPORTED_TARGETS}) - -# -# Build nodes -# -add_executable(lidar_localization_node src/nodes/lidar_localization_node.cpp) -target_link_libraries(lidar_localization_node lidar_localization) - -add_executable(area_obstacles_extractor_node src/nodes/area_obstacles_extractor_node.cpp) -target_link_libraries(area_obstacles_extractor_node area_obstacles_extractor) - -add_executable(beacon_scan_tracker_node src/nodes/beacon_scan_tracker_node.cpp) -target_link_libraries(beacon_scan_tracker_node beacon_scan_tracker) - -add_executable(scan_adjust src/scan_adjust.cpp) -target_link_libraries(scan_adjust ${catkin_LIBRARIES}) - -# -# Build nodelets -# -add_library(${PROJECT_NAME}_nodelets src/nodelets/${PROJECT_NAME}_nodelet.cpp) -target_link_libraries(${PROJECT_NAME}_nodelets ${PROJECT_NAME}) \ No newline at end of file diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/README.md b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/README.md deleted file mode 100644 index d4f814f..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/README.md +++ /dev/null @@ -1 +0,0 @@ -# lidar localization diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/include/lidar_localization/area_obstacles_extractor.h b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/include/lidar_localization/area_obstacles_extractor.h deleted file mode 100644 index b9cb5e1..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/include/lidar_localization/area_obstacles_extractor.h +++ /dev/null @@ -1,249 +0,0 @@ -/** - * - * @file area_obstacles_extractor.h - * @brief - * - * @code{.unparsed} - * _____ - * / /::\ ___ ___ - * / /:/\:\ / /\ / /\ - * / /:/ \:\ / /:/ / /:/ - * /__/:/ \__\:| /__/::\ / /:/ - * \ \:\ / /:/ \__\/\:\__ / /::\ - * \ \:\ /:/ \ \:\/\ /__/:/\:\ - * \ \:\/:/ \__\::/ \__\/ \:\ - * \ \::/ /__/:/ \ \:\ - * \__\/ \__\/ \__\/ - * @endcode - * - * @author sunfu.chou (sunfu.chou@gmail.com) - * @version 0.1 - * @date 2021-07-02 - * - */ - -#pragma once - -// ROS library -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Customized utility header fiile -#include - -// TF2 -#include -#include -#include -#include - -// Cpp tools -#include -#include - - -namespace lidar_localization -{ - -/** - * @struct TrackedObstacles - * @brief A structure that tracked the obstacles - */ -typedef struct TrackedObstacles -{ - - obstacle_detector::CircleObstacle obstacle; - double trackedTime; - double trackedReload; - - double lpf_believe_current; - - TrackedObstacles(obstacle_detector::CircleObstacle obs, double t, double lpf_believe_current) : trackedTime(t), lpf_believe_current(lpf_believe_current) - { - trackedReload = trackedTime; - obstacle.radius = obs.radius; - obstacle.center = obs.center; - } - - // Operator overload to calculate error length - double operator()(obstacle_detector::CircleObstacle compare) - { - return length(compare.center, obstacle.center); - } - - // Update only time - void update(double dt) - { - trackedReload -= dt; - } - - // Update with curren obstacles - void update(obstacle_detector::CircleObstacle new_obstacle, double dt) - { - double current_velocity_x = (new_obstacle.center.x - obstacle.center.x) / dt; - double current_velocity_y = (new_obstacle.center.y - obstacle.center.y) / dt; - - obstacle.velocity.x = current_velocity_x * lpf_believe_current + obstacle.velocity.x * (1 - lpf_believe_current); - obstacle.velocity.y = current_velocity_y * lpf_believe_current + obstacle.velocity.y * (1 - lpf_believe_current); - - obstacle.center = new_obstacle.center; - trackedReload = trackedTime; - // TODO : calculate velocity - } - - bool isTimeout() - { - return (trackedReload <= 0); - } - -} TrackedObstacles; - - -/** - * @class LidarLocalization - * @brief A class that use obstacles to localize robot position - */ -class AreaObstaclesExtractor -{ -public: - /** - * @brief Construct for the class `Lidar Localization` - * - * @param nh the global node handler - * @param nh_local the local node handler, use for get params - */ - AreaObstaclesExtractor(ros::NodeHandle& nh, ros::NodeHandle& nh_local); - ~AreaObstaclesExtractor(); - -private: - /** - * @brief To get params and set to this node in the constructor - * - */ - void initialize() - { - std_srvs::Empty empt; - updateParams(empt.request, empt.response); - } - - /** - * @brief A service call that get params and set to this node - * - * @param req The service request - * @param res The service response - * @return true if the service call is succeeds, - * @return false otherwise - */ - bool updateParams(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res); - - /** - * @brief Topic `obstacles` callback function - * - * @param ptr The obstaacles data - */ - void obstacleCallback(const obstacle_detector::Obstacles::ConstPtr& ptr); - - /** - * @brief Topic `ally_obstacles` callback function - * - * @param ptr The obstaacles data - */ - void allyObstacleCallback(const obstacle_detector::Obstacles::ConstPtr& ptr); - - void recordObstacles(obstacle_detector::Obstacles&, double); - - /** - * @brief Do low pass filter for each obstacles' velocity - * - * @param curr current obstacles data - * @param prev previous obstacles data - */ - void pushMardedObstacles(ros::Time, obstacle_detector::CircleObstacle, int); - - /** - * @brief Topic `obstacles_to_map` publisher function - * - */ - void publishObstacles(); - - void publishHaveObstacles(); - - /** - * @brief Topic `obstaclefield_marker` publisher function - * - */ - void publishMarkers(); - - obstacle_detector::CircleObstacle mergeObstacle(obstacle_detector::CircleObstacle cir1, obstacle_detector::CircleObstacle cir2); - - bool checkBoundary(geometry_msgs::Point); - bool checkRobotpose(geometry_msgs::Point); - void robotPoseCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& ptr); - void allyRobotPoseCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& ptr); - - /* ros node */ - ros::NodeHandle nh_; - ros::NodeHandle nh_local_; - ros::ServiceServer params_srv_; - - /* ros inter-node */ - ros::Subscriber sub_obstacles_; - ros::Subscriber sub_robot_pose_; - ros::Subscriber sub_ally_robot_pose_; - ros::Subscriber sub_ally_obstacles_; - ros::Publisher pub_obstacles_array_; - ros::Publisher pub_have_obstacles_; - ros::Publisher pub_marker_; - - geometry_msgs::PoseWithCovarianceStamped input_robot_pose_; - geometry_msgs::PoseWithCovarianceStamped input_ally_robot_pose_; - obstacle_detector::Obstacles output_obstacles_array_; - std::queue prev_output_obstacles_array_; - obstacle_detector::Obstacles ally_obstacles_; - std_msgs::Bool output_have_obstacles_; - visualization_msgs::MarkerArray output_marker_array_; - - /* private variables */ - std::vector trackedObstacles; - - tf2_ros::Buffer tfBuffer; - - /* ros param */ - bool p_active_; - bool p_central_; - - double p_x_min_range_; - double p_x_max_range_; - double p_y_min_range_; - double p_y_max_range_; - std::vector p_excluded_x_; - std::vector p_excluded_y_; - std::vector p_excluded_radius_; - double p_marker_height_; - double p_avoid_min_distance_; - double p_avoid_max_distance_; - double p_obstacle_merge_d_; - double p_obstacle_vel_merge_d_; - double p_obstacle_error_; - double p_obstacle_lpf_cur_; - double p_sample_number_; - double p_timeout_; - - std::string p_parent_frame_; - std::string p_ally_obstacles_topic_; - - double p_ally_excluded_radius_; - - std::vector exclude_poses_; - -}; -} // namespace lidar_localization diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/include/lidar_localization/beacon_scan_tracker.h b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/include/lidar_localization/beacon_scan_tracker.h deleted file mode 100644 index 4556119..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/include/lidar_localization/beacon_scan_tracker.h +++ /dev/null @@ -1,141 +0,0 @@ -/** - * - * @file beacon_scan_tracker.h - * @brief - * - * @code{.unparsed} - * _____ - * / /::\ ___ ___ - * / /:/\:\ / /\ / /\ - * / /:/ \:\ / /:/ / /:/ - * /__/:/ \__\:| /__/::\ / /:/ - * \ \:\ / /:/ \__\/\:\__ / /::\ - * \ \:\ /:/ \ \:\/\ /__/:/\:\ - * \ \:\/:/ \__\::/ \__\/ \:\ - * \ \::/ /__/:/ \ \:\ - * \__\/ \__\/ \__\/ - * @endcode - * - * @author sunfu.chou (sunfu.chou@gmail.com) - * @version 0.1 - * @date 2021-07-12 - * - */ - -#pragma once - -#define _USE_MATH_DEFINES -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -namespace lidar_localization -{ -/** - * @class BeaconScanTracker - * @brief A class that use obstacles to localize robot position - */ -class BeaconScanTracker -{ -public: - /** - * @brief Construct for the class `Lidar Localization` - * - * @param nh the global node handler - * @param nh_local the local node handler, use for get params - */ - BeaconScanTracker(ros::NodeHandle& nh, ros::NodeHandle& nh_local); - ~BeaconScanTracker(); - -private: - /** - * @brief To get params and set to this node in the constructor - * - */ - void initialize() - { - std_srvs::Empty empt; - updateParams(empt.request, empt.response); - } - - /** - * @brief A service call that get params and set to this node - * - * @param req The service request - * @param res The service response - * @return true if the service call is succeeds, - * @return false otherwise - */ - bool updateParams(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res); - - /** - * @brief A blocking function to check if Tf is ok - * - * @return true if all tf is ok - * @return false never, instead, stuck in while loop - */ - bool checkTFOK(); - - /** - * @brief To loopup tf of beacons to **robot** - * - */ - void getBeacontoRobot(); - - /** - * @brief Topic `pcl_msg` callback function - * - * @param ptr The obstaacles data - */ - void pclCallback(const sensor_msgs::PointCloud::ConstPtr pcl_msg); - - /** - * @brief Topic `lidar_pose` publisher function - * - */ - void publishScan(); - - /** - * @brief Tf broadcasters that send three fixed beacons pose to **map** - * - */ - - /* ros node */ - ros::NodeHandle nh_; - ros::NodeHandle nh_local_; - ros::ServiceServer params_srv_; - - /* ros inter-node */ - ros::Subscriber sub_pcl_; - ros::Publisher pub_scan_; - tf2_ros::Buffer tf2_buffer_; - tf2_ros::TransformListener tf2_listener_; - tf2_ros::StaticTransformBroadcaster static_broadcaster_; - - sensor_msgs::PointCloud input_pcl_; - sensor_msgs::LaserScan output_scan_; - - /* private variables */ - geometry_msgs::Point beacon_to_robot_[3]; - /* ros param */ - bool p_active_; -}; -} // namespace lidar_localization diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/include/lidar_localization/lidar_localization.h b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/include/lidar_localization/lidar_localization.h deleted file mode 100755 index c4c6109..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/include/lidar_localization/lidar_localization.h +++ /dev/null @@ -1,299 +0,0 @@ -/** - * - * @file lidar_localization.h - * @brief - * - * @code{.unparsed} - * _____ - * / /::\ ___ ___ - * / /:/\:\ / /\ / /\ - * / /:/ \:\ / /:/ / /:/ - * /__/:/ \__\:| /__/::\ / /:/ - * \ \:\ / /:/ \__\/\:\__ / /::\ - * \ \:\ /:/ \ \:\/\ /__/:/\:\ - * \ \:\/:/ \__\::/ \__\/ \:\ - * \ \::/ /__/:/ \ \:\ - * \__\/ \__\/ \__\/ - * @endcode - * - * @author sunfu.chou (sunfu.chou@gmail.com) - * @version 0.1 - * @date 2021-05-02 - * - */ - -#pragma once - -#define _USE_MATH_DEFINES -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -// matrix calulation -#include -#include - -#define BEACON_NUMBER 3 - -namespace lidar_localization -{ - - -/** - * @struct ObstacleCircle - * @brief for restore obstacle circles - */ -typedef struct ObstacleCircle -{ - /* -- Obstacle center position to base_footprint -- */ - geometry_msgs::Point center; - - /* -- Obstacle radius -- */ - double radius; - - /* -- Obstacle Velocity -- */ - geometry_msgs::Vector3 velocity; - - /* -- Distance between beacons -- */ - double beacon_distance[BEACON_NUMBER]; - -} ObstacleCircle; - - - -/** - * @struct BeaconRef - * @brief for restore beacons - */ -typedef struct BeaconRef -{ - /* -- Real beacon center (obstacle) -- */ - geometry_msgs::Point real; - - /* -- Ideal beacon center (tf) -- */ - geometry_msgs::Point ideal; - - /* Min error */ - double beaconError; - -} BeaconRef; - - - -/** - * @class LidarLocalization - * @brief A class that use obstacles to localize robot position - */ -class LidarLocalization -{ -public: - /** - * @brief Construct for the class `Lidar Localization` - * - * @param nh the global node handler - * @param nh_local the local node handler, use for get params - */ - LidarLocalization(ros::NodeHandle& nh, ros::NodeHandle& nh_local); - ~LidarLocalization(); - -private: - /** - * @brief To get params and set to this node in the constructor - * - */ - void initialize() - { - std_srvs::Empty empt; - updateParams(empt.request, empt.response); - } - - /** - * @brief A service call that get params and set to this node - * - * @param req The service request - * @param res The service response - * @return true if the service call is succeeds, - * @return false otherwise - */ - bool updateParams(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res); - - /** - * @brief Topic `cmd_vel` callback function - * - * @param ptr The command velocity data - */ - void cmdvelCallback(const geometry_msgs::Twist::ConstPtr& ptr); - - /** - * @brief Topic `odom` callback function - * - * @param ptr The command velocity data - */ - void odomCallback(const nav_msgs::Odometry::ConstPtr& ptr); - - /** - * @brief Topic `obstacles` callback function - * - * @param ptr The obstaacles data - */ - void obstacleCallback(const obstacle_detector::Obstacles::ConstPtr& ptr); - - /** - * @brief Topic `obstacles` callback function - * - * @param ptr The obstaacles data - */ - void ekfposeCallback(const nav_msgs::Odometry::ConstPtr& ptr); - - /** - * @brief Topic `lidar_pose` publisher function - * - */ - void publishLocation(); - - /** - * @brief Tf broadcasters that send three fixed beacons pose to **map** - * - */ - void publishBeacons(); - - /** - * @brief A blocking function to check if Tf is ok - * - * @return true if all tf is ok - * @return false never, instead, stuck in while loop - */ - bool checkTFOK(); - - /** - * @brief Send tf of the beacons to **map** - * - */ - void setBeacontoMap(); - - /** - * @brief To loopup tf of beacons to **map** - * - */ - void getBeacontoMap(); - - /** - * @brief To loopup tf of beacons to **robot** - * - */ - void getBeacontoRobot(); - - /** - * @brief To find the nearest obstacles to beacon tf pose - * - */ - void findBeacon(); - - /** - * @brief To check the geometry of three obstacles is satisfy tf geometry - * - * @return true if geometry is valid - * @return false if at least one geometry is wrong - */ - bool validateBeaconGeometry(); - - /** - * @brief To get robot pose by beacon - * - */ - void getRobotPose(); - - /* Get beacon prediction function */ - void updateBeacons(); - - /* ros node */ - ros::NodeHandle nh_; - ros::NodeHandle nh_local_; - ros::ServiceServer params_srv_; - - /* ros inter-node */ - ros::Subscriber sub_obstacles_; - // ros::Subscriber sub_toposition_; - ros::Subscriber sub_odom_; - ros::Subscriber sub_ekfpose_; - ros::Publisher pub_location_; - ros::Publisher pub_beacon_; - tf2_ros::Buffer tf2_buffer_; - tf2_ros::TransformListener tf2_listener_; - tf2_ros::StaticTransformBroadcaster static_broadcaster_; - - std::vector realtime_circles_; - geometry_msgs::PoseWithCovarianceStamped output_robot_pose_; - geometry_msgs::PoseArray output_beacons_; - - /* private variables */ - geometry_msgs::Point beacon_to_map_[BEACON_NUMBER]; - BeaconRef beacons_[BEACON_NUMBER]; - BeaconRef predict_beacons_[BEACON_NUMBER]; - - geometry_msgs::Pose ekf_pose_; - - geometry_msgs::Point robot_to_map_vel_; - geometry_msgs::Point robot_to_map_vel_pre; - - bool failed_tf_; - - /* ros param */ - bool p_active_; - - double p_cov_x_; - double p_cov_y_; - double p_cov_yaw_; - double p_beacon_1_x_; - double p_beacon_1_y_; - double p_beacon_2_x_; - double p_beacon_2_y_; - double p_beacon_3_x_; - double p_beacon_3_y_; - double p_theta_; - - double p_beacon_tolerance_; - - double p_predict_magnification_; - - double p_threshold_; - double p_cov_dec_; - - std::string p_obstacle_topic_; - // std::string p_toposition_topic_; - std::string p_odom_topic_; - std::string p_beacon_parent_frame_id_; - std::string p_predict_frame_id_; - std::string p_beacon_frame_id_prefix_; - std::string p_robot_parent_frame_id_; - std::string p_robot_frame_id_; - std::string p_ekfpose_topic_; - int chassis_type; - - ros::Time stamp_get_obs; - - //ready process - ros::Subscriber ready_sub; - void readyCallback(const geometry_msgs::PointStamped::ConstPtr& msg); - bool ready_signal_active; -}; -} // namespace lidar_localization diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/include/lidar_localization/util/math_util.h b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/include/lidar_localization/util/math_util.h deleted file mode 100755 index 36223c0..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/include/lidar_localization/util/math_util.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include - -namespace lidar_localization -{ -template -double length(T& pose1, S& pose2) -{ - return sqrt(pow(pose1.x - pose2.x, 2.) + pow(pose1.y - pose2.y, 2.)); -} - -template -double length(T& pose1) -{ - return sqrt(pow(pose1.x, 2.) + pow(pose1.y, 2.)); -} - -double length(double x, double y) -{ - return sqrt(pow(x, 2.) + pow(y, 2.)); -} - -inline bool is_whthin_tolerance(double measure, double expect, double tolerance) -{ - return abs(measure - expect) < tolerance; -} - -inline double radian_to_degree(double r) -{ - return r / M_PI * 180; -} - -} // namespace lidar_localization diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/launch/lidar_driver/lidar_G6.launch b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/launch/lidar_driver/lidar_G6.launch deleted file mode 100644 index 45b0134..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/launch/lidar_driver/lidar_G6.launch +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/launch/lidar_localization_new.launch b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/launch/lidar_localization_new.launch deleted file mode 100644 index c4bbb85..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/launch/lidar_localization_new.launch +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/launch/lidar_with_driver.launch b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/launch/lidar_with_driver.launch deleted file mode 100644 index 97a8765..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/launch/lidar_with_driver.launch +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/launch/nodelets.launch b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/launch/nodelets.launch deleted file mode 100644 index 306c575..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/launch/nodelets.launch +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/nodelet_plugins.xml b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/nodelet_plugins.xml deleted file mode 100755 index 57bd873..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/nodelet_plugins.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Lidar Localization - - - - diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/package.xml b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/package.xml deleted file mode 100755 index 0e27f37..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/package.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - lidar_localization - 0.0.0 - The lidar_localization package - paul - Sunfu-Chou - - NONE - - catkin - obstacle_detector - - tf2 - tf2_ros - rviz - roscpp - roslaunch - nodelet - std_msgs - std_srvs - sensor_msgs - geometry_msgs - laser_geometry - obstacle_detector - - - - - - - diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/params/lidar_global.yaml b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/params/lidar_global.yaml deleted file mode 100644 index 6e7ed4b..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/params/lidar_global.yaml +++ /dev/null @@ -1,70 +0,0 @@ -# Obstacle Extractor -# To -> base_footprint -obstacle_detector_to_base: - active: true - use_scan: true - use_pcl: false - - use_split_and_merge: true - circles_from_visibles: true - discard_converted_segments: false - transform_coordinates: true - - min_group_points: 5 - - max_group_distance: 0.04 - distance_proportion: 0.00628 - max_split_distance: 0.02 - max_merge_separation: 0.25 - max_merge_spread: 0.02 - max_circle_radius: 0.2 - radius_enlargement: 0.05 - -# Obstacle Tracker -# To -> base_footprint -obstacle_tracker_to_base: - active: true - - loop_rate: 10.0 - tracking_duration: 2.0 - min_correspondence_cost: 3 - std_correspondence_dev: 0.15 - process_variance: 0.1 - process_rate_variance: 1 - measurement_variance: 0.1 - -# Triangular localization -lidar_localization: - active: true - - cov_x: 0.1 - cov_y: 0.1 - cov_yaw: 0.1 - - theta: 0 - - beacon_tolerance: 0.14 # Should be less than "max_obstacle_distance" in global_filter_basic.yaml - threshold: 0.24 - cov_dec: 10 - predict_magnification: 0.065 # Highly depends on rate of odometry ( /Toposition ) - - obstacle_topic: obstacles_to_base - odom_topic: local_filter - ekfpose_topic: ekf_pose - # beacon_parent_frame_id: map - # beacon_frame_id_prefix: beacon - # robot_parent_frame_id: map - # robot_frame_id: base_footprint - - chassis_type: 1 # diff: 0, omni: 1 - -# # not used. -# # Extract obstacles -# area_obstacles_extractor: -# x_max_range: 2.85 -# y_max_range: 1.85 -# x_min_range: 0.15 -# y_min_range: 0.15 -# obstacle_merge_d: 0.2 -# obstacle_vel_merge_d: 0.25 -# obstacle_error: 0.35 diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/params/lidar_loc_blue_params.yaml b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/params/lidar_loc_blue_params.yaml deleted file mode 100644 index 0d3fe11..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/params/lidar_loc_blue_params.yaml +++ /dev/null @@ -1,14 +0,0 @@ -beacon_1_x: -0.094 -beacon_1_y: 0.052 - -beacon_2_x: -0.094 -beacon_2_y: 1.948 - -beacon_3_x: 3.094 -beacon_3_y: 1.0 - -# 2|----------| -# | | -# | |3 -# | | -# (0) 1|-----c----| diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/params/lidar_loc_yellow_params.yaml b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/params/lidar_loc_yellow_params.yaml deleted file mode 100644 index 0752cc7..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/params/lidar_loc_yellow_params.yaml +++ /dev/null @@ -1,14 +0,0 @@ -beacon_1_x: 3.094 -beacon_1_y: 0.052 - -beacon_2_x: 3.094 -beacon_2_y: 1.948 - -beacon_3_x: -0.094 -beacon_3_y: 1.0 - -# |----------|2 -# | | -# 3| | -# | | -# (0) |-----c----|1 diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/area_obstacles_extractor.cpp b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/area_obstacles_extractor.cpp deleted file mode 100755 index f3d558a..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/area_obstacles_extractor.cpp +++ /dev/null @@ -1,372 +0,0 @@ -/** - * - * @file area_obstacles_extractor.cpp - * @brief - * - * @code{.unparsed} - * _____ - * / /::\ ___ ___ - * / /:/\:\ / /\ / /\ - * / /:/ \:\ / /:/ / /:/ - * /__/:/ \__\:| /__/::\ / /:/ - * \ \:\ / /:/ \__\/\:\__ / /::\ - * \ \:\ /:/ \ \:\/\ /__/:/\:\ - * \ \:\/:/ \__\::/ \__\/ \:\ - * \ \::/ /__/:/ \ \:\ - * \__\/ \__\/ \__\/ - * @endcode - * - * @author sunfu.chou (sunfu.chou@gmail.com) - * @version 0.1 - * @date 2021-07-02 - * - */ - -#include "lidar_localization/area_obstacles_extractor.h" -#include "obstacle_detector/CircleObstacle.h" - -using namespace std; -using namespace lidar_localization; - -AreaObstaclesExtractor::AreaObstaclesExtractor(ros::NodeHandle& nh, ros::NodeHandle& nh_local) : nh_(nh), nh_local_(nh_local) -{ - params_srv_ = nh_local_.advertiseService("params", &AreaObstaclesExtractor::updateParams, this); - initialize(); -} - -AreaObstaclesExtractor::~AreaObstaclesExtractor() -{ - nh_local_.deleteParam("active"); - nh_local_.deleteParam("p_x_min_range_"); - nh_local_.deleteParam("p_x_max_range_"); - nh_local_.deleteParam("p_y_min_range_"); - nh_local_.deleteParam("p_y_max_range_"); - nh_local_.deleteParam("obstacle_radius"); -} - -bool AreaObstaclesExtractor::updateParams(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res) -{ - bool prev_active = p_active_; - - nh_local_.param("active", p_active_, true); - nh_local_.param("central", p_central_, true); - - nh_local_.param("x_min_range", p_x_min_range_, 0.0); - nh_local_.param("x_max_range", p_x_max_range_, 2.0); - nh_local_.param("y_min_range", p_y_min_range_, 0.0); - nh_local_.param("y_max_range", p_y_max_range_, 3.0); - - nh_local_.param("parent_frame", p_parent_frame_, "map"); - nh_local_.param("ally_obstacles_topic", p_ally_obstacles_topic_, "/robot2/obstacle_array"); - - nh_local_.param("obstacle_height", p_marker_height_, 2); - nh_local_.param("avoid_min_distance", p_avoid_min_distance_, 0.1); - nh_local_.param("avoid_max_distance", p_avoid_max_distance_, 0.5); - nh_local_.param("ally_excluded_radius", p_ally_excluded_radius_, p_avoid_min_distance_); - nh_local_.param("obstacle_merge_d", p_obstacle_merge_d_, 0.1); - nh_local_.param("obstacle_vel_merge_d", p_obstacle_vel_merge_d_, 0.3); - nh_local_.param("obstacle_error", p_obstacle_error_, 0.1); - nh_local_.param("obstacle_lpf_cur", p_obstacle_lpf_cur_, 0.5); - nh_local_.param("sample_number", p_sample_number_, 10.0); - nh_local_.param("timeout", p_timeout_, 0.3); - - if (p_active_ != prev_active) - { - if (p_active_) - { - sub_obstacles_ = nh_.subscribe("obstacles_to_map", 10, &AreaObstaclesExtractor::obstacleCallback, this); - pub_obstacles_array_ = nh_.advertise("obstacle_array", 10); - if(p_central_) - { - sub_ally_obstacles_ = nh_.subscribe(p_ally_obstacles_topic_, 10, &AreaObstaclesExtractor::allyObstacleCallback, this); - sub_robot_pose_ = nh_.subscribe("robot_pose", 10, &AreaObstaclesExtractor::robotPoseCallback, this); - sub_ally_robot_pose_ = nh_.subscribe("ally_pose", 10, &AreaObstaclesExtractor::allyRobotPoseCallback, this); - pub_have_obstacles_ = nh_.advertise("have_obstacles", 10); - pub_marker_ = nh_.advertise("obstacle_marker", 10); - } - } - else - { - pub_obstacles_array_.shutdown(); - if(p_central_) - { - sub_robot_pose_.shutdown(); - pub_have_obstacles_.shutdown(); - pub_marker_.shutdown(); - } - } - } - - return true; -} - -void AreaObstaclesExtractor::obstacleCallback(const obstacle_detector::Obstacles::ConstPtr& ptr) -{ - static tf2_ros::TransformListener tfListener(tfBuffer); - - output_obstacles_array_.header.stamp = ptr->header.stamp; - output_obstacles_array_.header.frame_id = p_parent_frame_; - - // Clear all previous obstacles - output_obstacles_array_.circles.clear(); - output_marker_array_.markers.clear(); - - obstacle_detector::Obstacles obstacle_buffer; - - int id = 0; - - // Get tf transform from base_footprint to map - geometry_msgs::TransformStamped transformStamped; - try{ - transformStamped = tfBuffer.lookupTransform(p_parent_frame_, ptr->header.frame_id, ros::Time(0), ros::Duration(1.0)); - } - catch (tf2::TransformException &ex) { - ROS_WARN("%s", ex.what()); - return; - } - - for (const obstacle_detector::CircleObstacle& circle : ptr->circles) - { - obstacle_detector::CircleObstacle circle_msg; - circle_msg = circle; - - geometry_msgs::PointStamped obstacle_to_base; - obstacle_to_base.header.frame_id = ptr->header.frame_id; - obstacle_to_base.header.stamp = ptr->header.stamp; - obstacle_to_base.point = circle.center; - - geometry_msgs::PointStamped obstacle_to_map; - tf2::doTransform(obstacle_to_base, obstacle_to_map, transformStamped); - - circle_msg.center = obstacle_to_map.point; - - // Check obstacle boundary - if (checkBoundary(circle_msg.center)) - { - /* Push in boundary obstacles into obstacle buffer */ - obstacle_buffer.circles.push_back(circle_msg); - } - } - - // TODO : traverse all of the in boundary obstacles and merge the closest - - // TODO : tracked obstacles - // Track the obstacle - // 1. Traverse all of the obstacles with tracked obstacle - // 2. If matched -> use z to set obstacle flag and update with obstacle - // 3. Not matched -> update with only time - if(trackedObstacles.empty()) - { - // No tracked obstacles -> push in tracked obstacle vector - for(auto obstacle : obstacle_buffer.circles) - { - TrackedObstacles new_tracked(obstacle, p_timeout_, p_obstacle_lpf_cur_); - trackedObstacles.push_back(new_tracked); - } - } - else - { - bool lastIterate = false; - for(auto trackedTop = trackedObstacles.begin() ; trackedTop != trackedObstacles.end() ;) - { - bool tracked = false; - for(auto& obstacle : obstacle_buffer.circles) - { - if(length(trackedTop->obstacle.center, obstacle.center) < p_obstacle_vel_merge_d_) - { - tracked = true; - trackedTop->update(obstacle, 0.1); - obstacle.center.z = 1; - } - else if(trackedTop == trackedObstacles.end() - 1 && obstacle.center.z != 1) // Last chance failed in trackedObstacles - { - tracked = true; - lastIterate = true; - TrackedObstacles new_trakced(obstacle, p_timeout_, p_obstacle_lpf_cur_); - trackedObstacles.push_back(new_trakced); - } - } - - if(tracked == false) trackedTop->update(0.1); - - if(lastIterate == true) break; - - if(trackedTop->isTimeout()) trackedTop = trackedObstacles.erase(trackedTop); - else trackedTop++; - } - } - - // TODO : prevent dulplicate tracked obstacles - for(auto trackedTop = trackedObstacles.begin() ; trackedTop != trackedObstacles.end() ; ) - { - bool tooClose = false; - for(auto trackedDown = trackedTop + 1 ; trackedDown != trackedObstacles.end() ; trackedDown++) - { - if(trackedTop != trackedDown && length(trackedTop->obstacle.center, trackedDown->obstacle.center) < 0.01) - { - tooClose = true; - } - } - - if(tooClose) trackedTop = trackedObstacles.erase(trackedTop); - else trackedTop++; - } - - // ( if central ) TODO : Merge 2 robots' closest obstacles - if(p_central_) - { - for(auto trackedTop : trackedObstacles) - { - bool track = false; - for(auto& ally : ally_obstacles_.circles) - { - if(length(ally.center, trackedTop.obstacle.center) < p_obstacle_merge_d_) - { - track = true; - - obstacle_detector::CircleObstacle merged = mergeObstacle(ally, trackedTop.obstacle); - if(!checkRobotpose(merged.center)) - { - output_obstacles_array_.circles.push_back(merged); - pushMardedObstacles(ptr->header.stamp, merged, id++); - ally.center.z = 1; - continue; - } - - } - } - if(!track && !checkRobotpose(trackedTop.obstacle.center)) - { - output_obstacles_array_.circles.push_back(trackedTop.obstacle); - pushMardedObstacles(ptr->header.stamp, trackedTop.obstacle, id++); - - } - } - - for(auto& ally : ally_obstacles_.circles) - { - if(ally.center.z == 0 && !checkRobotpose(ally.center)) - { - output_obstacles_array_.circles.push_back(ally); - pushMardedObstacles(ptr->header.stamp, ally, id++); - } - } - } - else - { - for(auto trackedTop : trackedObstacles) - { - output_obstacles_array_.circles.push_back(trackedTop.obstacle); - pushMardedObstacles(ptr->header.stamp, trackedTop.obstacle, id++); - } - } - - // TODO : publish obstacles - publishObstacles(); - - if(p_central_) - { - publishMarkers(); - publishHaveObstacles(); - } -} - -obstacle_detector::CircleObstacle AreaObstaclesExtractor::mergeObstacle(obstacle_detector::CircleObstacle cir1, obstacle_detector::CircleObstacle cir2) -{ - obstacle_detector::CircleObstacle merged; - merged.center.x = (cir1.center.x + cir2.center.x) / 2; - merged.center.y = (cir1.center.y + cir2.center.y) / 2; - merged.velocity.x = (cir1.velocity.x + cir2.velocity.x) / 2; - merged.velocity.y = (cir1.velocity.y + cir2.velocity.y) / 2; - merged.radius = cir1.radius; - - return merged; -} - -void AreaObstaclesExtractor::allyObstacleCallback(const obstacle_detector::Obstacles::ConstPtr& ptr){ - ally_obstacles_ = *ptr; - - for(auto& ally_obs : ally_obstacles_.circles) - { - ally_obs.center.z = 0; - } -} - -void AreaObstaclesExtractor::publishObstacles() -{ - pub_obstacles_array_.publish(output_obstacles_array_); -} - -void AreaObstaclesExtractor::publishHaveObstacles() -{ - output_have_obstacles_.data = false; - if (output_obstacles_array_.circles.size()) - { - output_have_obstacles_.data = true; - } - pub_have_obstacles_.publish(output_have_obstacles_); -} - -void AreaObstaclesExtractor::pushMardedObstacles(ros::Time time, obstacle_detector::CircleObstacle circle, int id) -{ - // Mark the obstacles - visualization_msgs::Marker marker; - marker.header.frame_id = p_parent_frame_; - marker.header.stamp = time; - marker.id = id; - marker.type = visualization_msgs::Marker::CYLINDER; - marker.lifetime = ros::Duration(0.1); - marker.pose.position.x = circle.center.x; - marker.pose.position.y = circle.center.y; - marker.pose.position.z = p_marker_height_ / 2.0; - marker.pose.orientation.w = 1.0; - marker.color.r = 0.5; - marker.color.g = 1.0; - marker.color.b = 0.5; - marker.color.a = 1.0; - - marker.scale.x = circle.radius; - marker.scale.y = circle.radius; - marker.scale.z = p_marker_height_; - - output_marker_array_.markers.push_back(marker); -} - - -void AreaObstaclesExtractor::publishMarkers() -{ - pub_marker_.publish(output_marker_array_); -} - -bool AreaObstaclesExtractor::checkBoundary(geometry_msgs::Point p) -{ - bool ret = true; - - // playing area boundary - if (p.x < p_x_min_range_ || p.x > p_x_max_range_) - ret = false; - if (p.y < p_y_min_range_ || p.y > p_y_max_range_) - ret = false; - - return ret; -} - -bool AreaObstaclesExtractor::checkRobotpose(geometry_msgs::Point p) -{ - if(length(input_robot_pose_.pose.pose.position, p) < p_obstacle_error_) return true; - if(length(input_ally_robot_pose_.pose.pose.position, p) < p_obstacle_error_) - { - return true; - } - return false; -} - -void AreaObstaclesExtractor::robotPoseCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& ptr) -{ - input_robot_pose_ = *ptr; -} - -void AreaObstaclesExtractor::allyRobotPoseCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& ptr) -{ - input_ally_robot_pose_ = *ptr; -} diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/beacon_scan_tracker.cpp b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/beacon_scan_tracker.cpp deleted file mode 100644 index b1ee5dd..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/beacon_scan_tracker.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/** - * - * @file beacon_scan_tracker.cpp - * @brief - * - * @code{.unparsed} - * _____ - * / /::\ ___ ___ - * / /:/\:\ / /\ / /\ - * / /:/ \:\ / /:/ / /:/ - * /__/:/ \__\:| /__/::\ / /:/ - * \ \:\ / /:/ \__\/\:\__ / /::\ - * \ \:\ /:/ \ \:\/\ /__/:/\:\ - * \ \:\/:/ \__\::/ \__\/ \:\ - * \ \::/ /__/:/ \ \:\ - * \__\/ \__\/ \__\/ - * @endcode - * - * @author sunfu.chou (sunfu.chou@gmail.com) - * @version 0.1 - * @date 2021-07-12 - * - */ -#include "lidar_localization/beacon_scan_tracker.h" -#include "lidar_localization/util/math_util.h" - -using namespace lidar_localization; -using namespace std; - -BeaconScanTracker::BeaconScanTracker(ros::NodeHandle& nh, ros::NodeHandle& nh_local) - : nh_(nh), nh_local_(nh_local), tf2_listener_(tf2_buffer_) -{ - p_active_ = false; - params_srv_ = nh_local_.advertiseService("params", &BeaconScanTracker::updateParams, this); - initialize(); -} - -BeaconScanTracker::~BeaconScanTracker() -{ -} - -bool BeaconScanTracker::updateParams(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res) -{ - bool prev_active = p_active_; - - nh_local_.param("active", p_active_, true); - - if (p_active_ != prev_active) - { - sub_pcl_ = nh_.subscribe("pcl", 10, &BeaconScanTracker::pclCallback, this); - pub_scan_ = nh_.advertise("beacon_scan", 10); - } - else - { - sub_pcl_.shutdown(); - pub_scan_.shutdown(); - } - checkTFOK(); - return true; -} - -bool BeaconScanTracker::checkTFOK() -{ - int tf_retry_count = 0; - while (ros::ok()) - { - ++tf_retry_count; - ros::Duration(0.5).sleep(); - - bool tf_ok = true; - for (int i = 1; i <= 3; i++) - { - if (!tf2_buffer_.canTransform("map", "beacon" + std::to_string(i), ros::Time())) - { - tf_ok = false; - } - if (!tf2_buffer_.canTransform("base_footprint", "beacon" + std::to_string(i), ros::Time())) - { - tf_ok = false; - } - } - - if (tf_ok) - return true; - - ROS_WARN_STREAM("[Beacon Scan Tracker]: " - << "tf not ok"); - - if (tf_retry_count % 20 == 0) - { - ROS_ERROR_STREAM("[Beacon Scan Tracker]: " - << "tf error after retry " << tf_retry_count << " times"); - } - } - - return false; -} - -void BeaconScanTracker::getBeacontoRobot() -{ - bool tf_ok = true; - geometry_msgs::TransformStamped transform; - for (int i = 1; i <= 3; ++i) - { - try - { - transform = tf2_buffer_.lookupTransform("base_footprint", "beacon" + std::to_string(i), ros::Time()); - - beacon_to_robot_[i - 1].x = transform.transform.translation.x; - beacon_to_robot_[i - 1].y = transform.transform.translation.y; - } - catch (const tf2::TransformException& ex) - { - try - { - transform = tf2_buffer_.lookupTransform("base_footprint", "beacon" + std::to_string(i), ros::Time()); - - beacon_to_robot_[i - 1].x = transform.transform.translation.x; - beacon_to_robot_[i - 1].y = transform.transform.translation.y; - } - catch (const tf2::TransformException& ex) - { - ROS_WARN_STREAM(ex.what()); - tf_ok = false; - } - } - } - - if (!tf_ok) - { - ROS_WARN_STREAM("[Beacon Scan Tracker]: " - << "get beacon to robot tf failed"); - } -} -void BeaconScanTracker::pclCallback(const sensor_msgs::PointCloud::ConstPtr pcl_msg) -{ - getBeacontoRobot(); - ros::Time now = ros::Time::now(); - vector ranges; - ranges.assign(1440, nanf("")); - for (auto& point : pcl_msg->points) - { - for (int i = 0; i < 3; i++) - { - if (sqrt(pow(point.x - beacon_to_robot_[i].x, 2.) + pow(point.y - beacon_to_robot_[i].y, 2.)) < 0.12) - { - double range = sqrt(pow(point.x, 2.0) + pow(point.y, 2.0)); - double angle = atan2(point.y, point.x); - size_t idx = static_cast(1440 * (angle + M_PI) / (2.0 * M_PI)); - if (isnan(ranges[idx]) || range < ranges[idx]) - ranges[idx] = range; - } - } - } - - output_scan_.header.frame_id = "base_footprint"; - output_scan_.header.stamp = now; - output_scan_.angle_min = -M_PI; - output_scan_.angle_max = M_PI; - output_scan_.angle_increment = 2.0 * M_PI / (1440 - 1); - output_scan_.time_increment = 0.0; - output_scan_.scan_time = 0.1; - output_scan_.range_min = 0.1; - output_scan_.range_max = 3.6; - output_scan_.ranges.assign(ranges.begin(), ranges.end()); - - pub_scan_.publish(output_scan_); -} diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/lidar_localization.cpp b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/lidar_localization.cpp deleted file mode 100755 index a6157b5..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/lidar_localization.cpp +++ /dev/null @@ -1,652 +0,0 @@ -/** - * - * @file lidar_localization.cpp - * @brief - * - * @code{.unparsed} - * _____ - * / /::\ ___ ___ - * / /:/\:\ / /\ / /\ - * / /:/ \:\ / /:/ / /:/ - * /__/:/ \__\:| /__/::\ / /:/ - * \ \:\ / /:/ \__\/\:\__ / /::\ - * \ \:\ /:/ \ \:\/\ /__/:/\:\ - * \ \:\/:/ \__\::/ \__\/ \:\ - * \ \::/ /__/:/ \ \:\ - * \__\/ \__\/ \__\/ - * @endcode - * - * @author sunfu.chou (sunfu.chou@gmail.com) - * @version 0.1 - * @date 2021-05-11 - * - */ - -#include "lidar_localization/lidar_localization.h" - -using namespace std; -using namespace lidar_localization; -using namespace arma; - -LidarLocalization::LidarLocalization(ros::NodeHandle& nh, ros::NodeHandle& nh_local) - : nh_(nh), nh_local_(nh_local), tf2_listener_(tf2_buffer_) -{ - params_srv_ = nh_local_.advertiseService("params", &LidarLocalization::updateParams, this); - initialize(); -} - -LidarLocalization::~LidarLocalization() -{ - nh_local_.deleteParam("active"); - - nh_local_.deleteParam("cov_x"); - nh_local_.deleteParam("cov_y"); - nh_local_.deleteParam("cov_yaw"); - // nh_local_.deleteParam("beacon_ax"); - // nh_local_.deleteParam("beacon_ay"); - // nh_local_.deleteParam("beacon_bx"); - // nh_local_.deleteParam("beacon_by"); - // nh_local_.deleteParam("beacon_cx"); - // nh_local_.deleteParam("beacon_cy"); - nh_local_.deleteParam("theta"); - - nh_local_.deleteParam("beacon_tolerance"); - nh_local_.deleteParam("threshold"); - nh_local_.deleteParam("cov_dec"); - - nh_local_.deleteParam("obstacle_topic"); - // nh_local_.deleteParam("toposition_topic"); - nh_local_.deleteParam("odom_topic"); - nh_local_.deleteParam("ekfpose_topic"); - - nh_local_.deleteParam("beacon_parent_frame_id"); - nh_local_.deleteParam("robot_parent_frame_id"); - nh_local_.deleteParam("robot_frame_id"); - - nh_local_.deleteParam("beacon_frame_id_prefix"); -} - -bool LidarLocalization::updateParams(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res) -{ - bool get_param_ok = true; - bool prev_active = p_active_; - - get_param_ok &= nh_local_.param("active", p_active_, true); - - get_param_ok &= nh_local_.param("cov_x", p_cov_x_, 1e-1); - get_param_ok &= nh_local_.param("cov_y", p_cov_y_, 1e-1); - get_param_ok &= nh_local_.param("cov_yaw", p_cov_yaw_, 1e-1); - get_param_ok &= nh_local_.param("theta", p_theta_, 0); - - get_param_ok &= nh_local_.param("beacon_tolerance", p_beacon_tolerance_, 0.13); - get_param_ok &= nh_local_.param("threshold", p_threshold_, 0.24); - get_param_ok &= nh_local_.param("cov_dec", p_cov_dec_, 0.01); - get_param_ok &= nh_local_.param("predict_magnification", p_predict_magnification_, 0.1); - - get_param_ok &= nh_local_.param("obstacle_topic", p_obstacle_topic_, "obstacles"); - // get_param_ok &= nh_local_.param("toposition_topic", p_toposition_topic_, "Toposition"); - get_param_ok &= nh_local_.param("odom_topic", p_odom_topic_, "odom"); - get_param_ok &= nh_local_.param("ekfpose_topic", p_ekfpose_topic_, "ekf_pose"); - - get_param_ok &= nh_local_.param("beacon_parent_frame_id", p_beacon_parent_frame_id_, "map"); - get_param_ok &= nh_local_.param("robot_parent_frame_id", p_robot_parent_frame_id_, "map"); - get_param_ok &= nh_local_.param("robot_frame_id", p_robot_frame_id_, "base_footprint"); - get_param_ok &= nh_local_.param("beacon_predict_frame_id", p_predict_frame_id_, "predict"); - - get_param_ok &= nh_local_.param("beacon_frame_id_prefix", p_beacon_frame_id_prefix_, "beacon"); - - if (p_active_ != prev_active) - { - if (p_active_) - { - sub_obstacles_ = nh_.subscribe(p_obstacle_topic_, 10, &LidarLocalization::obstacleCallback, this); - // sub_toposition_ = nh_.subscribe(p_toposition_topic_, 10, &LidarLocalization::cmdvelCallback, this); - sub_odom_ = nh_.subscribe(p_odom_topic_, 10, &LidarLocalization::odomCallback, this); - sub_ekfpose_ = nh_.subscribe(p_ekfpose_topic_, 10, &LidarLocalization::ekfposeCallback, this); - pub_location_ = nh_.advertise("lidar_bonbonbon", 10); - pub_beacon_ = nh_.advertise("beacons", 10); - } - else - { - sub_obstacles_.shutdown(); - pub_location_.shutdown(); - pub_beacon_.shutdown(); - } - } - - get_param_ok &= nh_.param("localization/ready_signal_active", ready_signal_active, false); - if(ready_signal_active){ - ready_sub = nh_.subscribe("startup/ready_signal", 10, &LidarLocalization::readyCallback, this); - }else{ - geometry_msgs::PointStamped initial_msg; - get_param_ok &= nh_.param("localization/side", initial_msg.header.frame_id, "0"); - get_param_ok &= nh_.param("localization/initial_pose/x", initial_msg.point.x, 0.3); - get_param_ok &= nh_.param("localization/initial_pose/y", initial_msg.point.y, 1.5); - get_param_ok &= nh_.param("localization/initial_pose/z", initial_msg.point.z, 0.0); - - std::string side; - if(strcmp(initial_msg.header.frame_id.c_str(), "0") == 0) side = "Blue"; - else if(strcmp(initial_msg.header.frame_id.c_str(), "1") == 0) side = "Yellow"; - - get_param_ok &= nh_.param(side + "/beacon_ax", p_beacon_1_x_, -0.094); - get_param_ok &= nh_.param(side + "/beacon_ay", p_beacon_1_y_, 0.052); - get_param_ok &= nh_.param(side + "/beacon_bx", p_beacon_2_x_, -0.094); - get_param_ok &= nh_.param(side + "/beacon_by", p_beacon_2_y_, 1.948); - get_param_ok &= nh_.param(side + "/beacon_cx", p_beacon_3_x_, 3.094); - get_param_ok &= nh_.param(side + "/beacon_cy", p_beacon_3_y_, 1.0); - - /* Setup beacon position for triangular localization */ - setBeacontoMap(); - checkTFOK(); - getBeacontoMap(); - - } - - if (get_param_ok) - { - // ROS_INFO_STREAM("[Lidar Localization]: " - // << "set param ok"); - } - else - { - ROS_WARN_STREAM("[Lidar Localization]: " - << "set param failed"); - } - - return true; -} - -void LidarLocalization::readyCallback(const geometry_msgs::PointStamped::ConstPtr& msg){ - static bool received_ready = false; - if(received_ready) return; - std::string side = msg->header.frame_id; - if(strcmp(side.c_str(), "0") == 0) side = "Blue"; - else if(strcmp(side.c_str(), "1") == 0) side = "Yellow"; - - bool ok = true; - ok &= nh_.param(side + "/beacon_ax", p_beacon_1_x_, -0.094); - ok &= nh_.param(side + "/beacon_ay", p_beacon_1_y_, 0.052); - ok &= nh_.param(side + "/beacon_bx", p_beacon_2_x_, -0.094); - ok &= nh_.param(side + "/beacon_by", p_beacon_2_y_, 1.948); - ok &= nh_.param(side + "/beacon_cx", p_beacon_3_x_, 3.094); - ok &= nh_.param(side + "/beacon_cy", p_beacon_3_y_, 1.0); - - /* Setup beacon position for triangular localization */ - setBeacontoMap(); - checkTFOK(); - getBeacontoMap(); - - if(!ok) ROS_WARN("[Lidar Localization]: ready failed"); - received_ready = true; - -} - -// void LidarLocalization::cmdvelCallback(const geometry_msgs::Twist::ConstPtr& ptr) -// { -// robot_to_map_vel_.x = ptr->linear.x; -// robot_to_map_vel_.y = ptr->linear.y; -// robot_to_map_vel_.z = ptr->angular.z; -// } - -void LidarLocalization::odomCallback(const nav_msgs::Odometry::ConstPtr& ptr) -{ - robot_to_map_vel_.x = ptr->twist.twist.linear.x; - robot_to_map_vel_.y = ptr->twist.twist.linear.y; - robot_to_map_vel_.z = ptr->twist.twist.angular.z; -} - -void LidarLocalization::ekfposeCallback(const nav_msgs::Odometry::ConstPtr& ptr) -{ - this->ekf_pose_ = ptr->pose.pose; -} - -/* MAIN */ -void LidarLocalization::obstacleCallback(const obstacle_detector::Obstacles::ConstPtr& ptr) -{ - stamp_get_obs = ptr->header.stamp; - - /* Remove previous obstacle circles */ - realtime_circles_.clear(); - - /* Transform beacon to robot frame */ - getBeacontoRobot(); - - /* Get update prediction beacons */ - updateBeacons(); - - /* Restore the obstacle circles */ - for (const obstacle_detector::CircleObstacle& obstacle : ptr->circles) - { - ObstacleCircle obstaclecircle; - obstaclecircle.center = obstacle.center; - obstaclecircle.radius = obstacle.true_radius; - obstaclecircle.velocity = obstacle.velocity; - for (int i = 0 ; i < 3 ; i++) - { - obstaclecircle.beacon_distance[i] = length(obstaclecircle.center, predict_beacons_[i].ideal); - } - realtime_circles_.push_back(obstaclecircle); - } - - findBeacon(); - getRobotPose(); -} - -void LidarLocalization::updateBeacons() -{ - /* First get ekf_pose + cmd_vel */ - /* And broadcast the pose to map frame */ - geometry_msgs::TransformStamped transform; - ros::Time now = ros::Time::now(); - // transform.header.stamp = now; - transform.header.stamp = stamp_get_obs; - transform.header.frame_id = p_robot_parent_frame_id_; - transform.child_frame_id = p_predict_frame_id_; - - transform.transform.translation.x = ekf_pose_.position.x; - transform.transform.translation.y = ekf_pose_.position.y; - transform.transform.translation.z = 0; - - tf2::Quaternion q; - tf2::fromMsg(ekf_pose_.orientation, q); - tf2::Matrix3x3 qt(q); - double _, yaw; - qt.getRPY(_, _, yaw); - - yaw += robot_to_map_vel_.z * p_predict_magnification_; - tf2::Quaternion yaw_quaternion; - yaw_quaternion.setRPY(0, 0, yaw); - - transform.transform.rotation.x = yaw_quaternion.getX(); - transform.transform.rotation.y = yaw_quaternion.getY(); - transform.transform.rotation.z = yaw_quaternion.getZ(); - transform.transform.rotation.w = yaw_quaternion.getW(); - - failed_tf_ = !(ekf_pose_.orientation.x + ekf_pose_.orientation.y + ekf_pose_.orientation.z + ekf_pose_.orientation.w); - - if(!failed_tf_) static_broadcaster_.sendTransform(transform); - else return; - - /* Get beacon transform from the tf to map */ - bool tf_ok = true; - for (int i = 1; i <= 3; ++i) - { - try - { - transform = tf2_buffer_.lookupTransform(p_predict_frame_id_, p_beacon_frame_id_prefix_ + std::to_string(i), ros::Time()); - - predict_beacons_[i - 1].ideal.x = transform.transform.translation.x; - predict_beacons_[i - 1].ideal.y = transform.transform.translation.y; - } - catch (const tf2::TransformException& ex) - { - ROS_WARN_STREAM(ex.what()); - tf_ok = false; - } - } - - if (tf_ok) - { - // ROS_INFO_STREAM("[Lidar Localization]: " << "get beacon to map tf ok"); - } - else - { - ROS_WARN_STREAM("[Lidar Localization]: " << "get beacon to map tf failed"); - } -} - -void LidarLocalization::setBeacontoMap() -{ - geometry_msgs::TransformStamped transform; - ros::Time now = ros::Time::now(); - transform.header.stamp = now; - transform.header.frame_id = p_beacon_parent_frame_id_; - - transform.transform.translation.z = 0; - transform.transform.rotation.x = 0; - transform.transform.rotation.y = 0; - transform.transform.rotation.z = 0; - transform.transform.rotation.w = 1; - - transform.child_frame_id = p_beacon_frame_id_prefix_ + "1"; - transform.transform.translation.x = p_beacon_1_x_; - transform.transform.translation.y = p_beacon_1_y_; - static_broadcaster_.sendTransform(transform); - - transform.child_frame_id = p_beacon_frame_id_prefix_ + "2"; - transform.transform.translation.x = p_beacon_2_x_; - transform.transform.translation.y = p_beacon_2_y_; - static_broadcaster_.sendTransform(transform); - - transform.child_frame_id = p_beacon_frame_id_prefix_ + "3"; - transform.transform.translation.x = p_beacon_3_x_; - transform.transform.translation.y = p_beacon_3_y_; - static_broadcaster_.sendTransform(transform); - -// ROS_INFO_STREAM("[Lidar Localization]: " -// << "set beacon tf ok"); -} - -bool LidarLocalization::checkTFOK() -{ - int tf_retry_count = 0; - while (ros::ok()) - { - ++tf_retry_count; - ros::Duration(0.5).sleep(); - - bool tf_ok = true; - for (int i = 1; i <= 3; i++) - { - if (!tf2_buffer_.canTransform(p_beacon_parent_frame_id_, p_beacon_frame_id_prefix_ + std::to_string(i), - ros::Time())) - { - tf_ok = false; - } - if (!tf2_buffer_.canTransform(p_robot_frame_id_, p_beacon_frame_id_prefix_ + std::to_string(i), ros::Time())) - { - tf_ok = false; - } - } - - if (tf_ok) - return true; - - ROS_WARN_STREAM("[Lidar Localization]: " - << "tf not ok"); - - if (tf_retry_count % 20 == 0) - { - ROS_ERROR_STREAM("[Lidar Localization]: " - << "tf error after retry " << tf_retry_count << " times"); - } - } - - return false; -} - -void LidarLocalization::getBeacontoMap() -{ - bool tf_ok = true; - geometry_msgs::TransformStamped transform; - for (int i = 1; i <= 3; ++i) - { - try - { - transform = tf2_buffer_.lookupTransform(p_beacon_parent_frame_id_, p_beacon_frame_id_prefix_ + std::to_string(i), ros::Time()); - - beacon_to_map_[i - 1].x = transform.transform.translation.x; - beacon_to_map_[i - 1].y = transform.transform.translation.y; - } - catch (const tf2::TransformException& ex) - { - ROS_WARN_STREAM(ex.what()); - tf_ok = false; - } - } - - if (tf_ok) - { - // ROS_INFO_STREAM("[Lidar Localization]: " << "get beacon to map tf ok"); - } - else - { - ROS_WARN_STREAM("[Lidar Localization]: " << "get beacon to map tf failed"); - } -} - -void LidarLocalization::getBeacontoRobot() -{ - bool tf_ok = true; - geometry_msgs::TransformStamped transform; - for (int i = 1; i <= 3; ++i) - { - try - { - transform = tf2_buffer_.lookupTransform(p_robot_frame_id_, p_beacon_frame_id_prefix_ + std::to_string(i), ros::Time()); - - double x = transform.transform.translation.x; - double y = transform.transform.translation.y; - - beacons_[i - 1].ideal.x = x; - beacons_[i - 1].ideal.y = y; - } - catch (const tf2::TransformException& ex) - { - ROS_WARN_STREAM(ex.what()); - tf_ok = false; - } - } - - if (!tf_ok) - { - ROS_WARN_STREAM("[Lidar Localization]: " << "get beacon to robot tf failed"); - } -} - -void LidarLocalization::findBeacon() -{ - /* Iterate the three beacons */ - for (int i = 0; i < 3; ++i) - { - if (realtime_circles_.empty()) - { - ROS_WARN_STREAM_THROTTLE(2, "[Lidar Localization]: " << "no obstacle information"); - continue; - } - - /* Check the closest obstacle in all of the obstacles */ - double min_distance = realtime_circles_[0].beacon_distance[i]; - for (auto circle : realtime_circles_) - { - if (circle.beacon_distance[i] <= min_distance) - { - min_distance = circle.beacon_distance[i]; - beacons_[i].real.x = circle.center.x; - beacons_[i].real.y = circle.center.y; - beacons_[i].beaconError = min_distance; - } - } - } -} - -bool LidarLocalization::validateBeaconGeometry() -{ - double beacon_distance[3][3] = {}; - double real_beacon_distance[3][3] = {}; - for (int i = 0; i < 3; i++) - { - for (int j = 0; j < 3; j++) - { - beacon_distance[i][j] = length(beacons_[i].real, beacons_[j].real); - } - } - - for (int i = 0; i < 3; i++) - { - for (int j = 0; j < 3; j++) - { - real_beacon_distance[i][j] = length(beacon_to_map_[i], beacon_to_map_[j]); - } - } - - double tolerance = p_beacon_tolerance_ + robot_to_map_vel_.z * 0.08; - - if (is_whthin_tolerance(beacon_distance[0][1], real_beacon_distance[0][1], tolerance) && - is_whthin_tolerance(beacon_distance[0][2], real_beacon_distance[0][2], tolerance) && - is_whthin_tolerance(beacon_distance[1][2], real_beacon_distance[1][2], tolerance)) - { - return true; - } - else - { - // ROS_INFO_STREAM_THROTTLE(2, "[Lidar Localization] : current beacon tolerance " << tolerance); - // ROS_INFO_STREAM_THROTTLE(2, "reacon distance: " << real_beacon_distance[0][1] << ", " << real_beacon_distance[0][2] << ", " - // << real_beacon_distance[1][2]); - ROS_WARN_STREAM_THROTTLE(2, "beacon distance: " << beacon_distance[0][1] << ", " << beacon_distance[0][2] << ", " - << beacon_distance[1][2]); - ROS_WARN_STREAM_THROTTLE(2, "real_beacon distance: " << real_beacon_distance[0][1] << ", " << real_beacon_distance[0][2] << ", " - << real_beacon_distance[1][2]); - return false; - } -} - -void LidarLocalization::getRobotPose() -{ - if (!validateBeaconGeometry()) - { - ROS_WARN_STREAM_THROTTLE(2, "geometry error"); - return; - } - - vector dist_beacon_robot; - for (int i = 0; i < 3; ++i) - { - dist_beacon_robot.push_back(length(beacons_[i].real)); - } - - // least squares method to solve Ax=b - // i.e to solve (A^T)Ax=(A^T)b - mat A(2, 2); - vec b(2); - vec X(2); - - A(0, 0) = 2 * (beacon_to_map_[0].x - beacon_to_map_[2].x); - A(0, 1) = 2 * (beacon_to_map_[0].y - beacon_to_map_[2].y); - - A(1, 0) = 2 * (beacon_to_map_[1].x - beacon_to_map_[2].x); - A(1, 1) = 2 * (beacon_to_map_[1].y - beacon_to_map_[2].y); - - b(0) = (pow(beacon_to_map_[0].x, 2) - pow(beacon_to_map_[2].x, 2)) + - (pow(beacon_to_map_[0].y, 2) - pow(beacon_to_map_[2].y, 2)) + - (pow(dist_beacon_robot[2], 2) - pow(dist_beacon_robot[0], 2)); - b(1) = (pow(beacon_to_map_[1].x, 2) - pow(beacon_to_map_[2].x, 2)) + - (pow(beacon_to_map_[1].y, 2) - pow(beacon_to_map_[2].y, 2)) + - (pow(dist_beacon_robot[2], 2) - pow(dist_beacon_robot[1], 2)); - try - { - X = solve(A.t() * A, A.t() * b, solve_opts::no_approx); - - output_robot_pose_.pose.pose.position.x = X(0); - output_robot_pose_.pose.pose.position.y = X(1); - - double robot_yaw = 0; - double robot_sin = 0; - double robot_cos = 0; - - for (int i = 0; i < 3; i++) - { - double theta = atan2(beacon_to_map_[i].y - output_robot_pose_.pose.pose.position.y, - beacon_to_map_[i].x - output_robot_pose_.pose.pose.position.x) - - atan2(beacons_[i].real.y, beacons_[i].real.x); - - robot_sin += sin(theta); - robot_cos += cos(theta); - } - - robot_yaw = atan2(robot_sin, robot_cos) + p_theta_ / 180.0 * 3.1415926; - - /* - TODO: should know exactly how much the delay is - known: lidar publish stamp, obstacle stamp, imu rotation, odom linear x and y - better to also to with linear - and then publish with ros::Time::now() - */ - - ros::Time now = ros::Time::now(); - ros::Duration delay = now - stamp_get_obs; - - // the robotstate calculated with obstacles, which is a few ms ago - Eigen::Vector3d robotstate; - robotstate(0) = X(0); - robotstate(1) = X(1); - robotstate(2) = robot_yaw; - - /* prediction function for omni wheel */ - double v_x = robot_to_map_vel_.x; - double v_y = robot_to_map_vel_.y; - double w = robot_to_map_vel_.z; - double dt = delay.toSec(); - - Eigen::Vector3d d_state; - - d_state << (v_x * dt), (v_y * dt), (w * dt); - - double theta_ = robotstate(2) + d_state(2) / 2; - double s__theta = sin(theta_); - double c__theta = cos(theta_); - - Eigen::Matrix3d A; - A << 1, 0, 0, 0, 1, 0, 0, 0, 1; - - Eigen::Matrix3d B; - B << c__theta, -s__theta, 0, s__theta, c__theta, 0, 0, 0, 1; - - robotstate = A * robotstate + B * d_state; - - output_robot_pose_.pose.pose.position.x = robotstate(0); - output_robot_pose_.pose.pose.position.y = robotstate(1); - - tf2::Quaternion q; - q.setRPY(0., 0., robot_yaw); - output_robot_pose_.pose.pose.orientation = tf2::toMsg(q); - - publishLocation(); - } - catch (const std::runtime_error& ex) - { - ROS_WARN_STREAM(A); - ROS_WARN_STREAM(b); - ROS_WARN_STREAM(ex.what()); - } - // publishBeacons(); -} - -void LidarLocalization::publishLocation() -{ - ros::Time now = ros::Time::now(); - - output_robot_pose_.header.frame_id = p_robot_parent_frame_id_; - output_robot_pose_.header.stamp = now; - // output_robot_pose_.header.stamp = stamp_get_obs; - - double error_length = length(output_robot_pose_.pose.pose.position, ekf_pose_.position); - double cov_x = (error_length > p_threshold_) ? p_cov_x_ * p_cov_dec_ : p_cov_x_; - double cov_y = (error_length > p_threshold_) ? p_cov_y_ * p_cov_dec_ : p_cov_y_; - double cov_yaw = (error_length > p_threshold_) ? p_cov_yaw_ * p_cov_dec_ : p_cov_yaw_; - - double geometryError = 0; - for(int i = 0 ; i < 3 ; i++) geometryError += beacons_[i].beaconError; - - /* TODO : debug and test */ - // ROS_WARN_STREAM_THROTTLE(2, "[Lidar localization] : current beacons error " << geometryError); - - // clang-format off - // x y z pitch roll yaw - output_robot_pose_.pose.covariance = {cov_x, 0, 0, 0, 0, 0, - 0, cov_y, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, cov_yaw}; - // clang-format on - pub_location_.publish(output_robot_pose_); -} - -void LidarLocalization::publishBeacons() -{ - ros::Time now = ros::Time::now(); - output_beacons_.header.stamp = now; - output_beacons_.header.frame_id = p_robot_frame_id_; - output_beacons_.poses.clear(); - - for (int i = 0; i < 3; ++i) - { - geometry_msgs::Pose pose; - pose.position.x = beacons_[i].real.x; - pose.position.y = beacons_[i].real.y; - output_beacons_.poses.push_back(pose); - } - - pub_beacon_.publish(output_beacons_); -} diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/nodelets/lidar_localization_nodelet.cpp b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/nodelets/lidar_localization_nodelet.cpp deleted file mode 100755 index 46f418a..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/nodelets/lidar_localization_nodelet.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/** - * - * @file lidar_localization_nodelet.cpp - * @brief - * - * @code{.unparsed} - * _____ - * / /::\ ___ ___ - * / /:/\:\ / /\ / /\ - * / /:/ \:\ / /:/ / /:/ - * /__/:/ \__\:| /__/::\ / /:/ - * \ \:\ / /:/ \__\/\:\__ / /::\ - * \ \:\ /:/ \ \:\/\ /__/:/\:\ - * \ \:\/:/ \__\::/ \__\/ \:\ - * \ \::/ /__/:/ \ \:\ - * \__\/ \__\/ \__\/ - * @endcode - * - * @author sunfu.chou (sunfu.chou@gmail.com) - * @version 0.1 - * @date 2021-05-12 - * - */ - -#include -#include - -#include "lidar_localization/lidar_localization.h" - -namespace lidar_localization -{ -class LidarLocalizationNodelet : public nodelet::Nodelet -{ -public: - virtual void onInit() - { - ros::NodeHandle nh = getNodeHandle(); - ros::NodeHandle nh_local = getPrivateNodeHandle(); - - try - { - NODELET_INFO("[Lidar Localization]: Initializing nodelet"); - lidar_localization_instance = std::shared_ptr(new LidarLocalization(nh, nh_local)); - } - catch (const char* s) - { - NODELET_FATAL_STREAM("[Lidar Localization]: " << s); - } - catch (...) - { - NODELET_FATAL_STREAM("[Lidar Localization]: Unexpected error"); - } - } - - virtual ~LidarLocalizationNodelet() - { - NODELET_INFO("[Lidar Localization]: Shutdown"); - } - -private: - std::shared_ptr lidar_localization_instance; -}; - -} // namespace lidar_localization - -#include -PLUGINLIB_EXPORT_CLASS(lidar_localization::LidarLocalizationNodelet, nodelet::Nodelet) diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/nodes/area_obstacles_extractor_node.cpp b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/nodes/area_obstacles_extractor_node.cpp deleted file mode 100644 index cbdabd8..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/nodes/area_obstacles_extractor_node.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/** - * - * @file area_obstacles_extractor_node.cpp - * @brief - * - * @code{.unparsed} - * _____ - * / /::\ ___ ___ - * / /:/\:\ / /\ / /\ - * / /:/ \:\ / /:/ / /:/ - * /__/:/ \__\:| /__/::\ / /:/ - * \ \:\ / /:/ \__\/\:\__ / /::\ - * \ \:\ /:/ \ \:\/\ /__/:/\:\ - * \ \:\/:/ \__\::/ \__\/ \:\ - * \ \::/ /__/:/ \ \:\ - * \__\/ \__\/ \__\/ - * @endcode - * - * @author sunfu.chou (sunfu.chou@gmail.com) - * @version 0.1 - * @date 2021-07-03 - * - */ - -#include "lidar_localization/area_obstacles_extractor.h" - -using namespace lidar_localization; - -int main(int argc, char** argv) -{ - ros::init(argc, argv, "area_obstacles_extractor"); - ros::NodeHandle nh(""); - ros::NodeHandle nh_local("~"); - - try - { - ROS_INFO("[Area Obstacles Extractor]: Initializing node"); - AreaObstaclesExtractor area_obstacles_extractor_instance(nh, nh_local); - ros::spin(); - } - catch (const char* s) - { - ROS_FATAL_STREAM("[Area Obstacles Extractor]: " << s); - } - catch (...) - { - ROS_FATAL_STREAM("[Area Obstacles Extractor]: Unexpected error"); - } - - return 0; -} diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/nodes/beacon_scan_tracker_node.cpp b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/nodes/beacon_scan_tracker_node.cpp deleted file mode 100644 index 9eb9447..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/nodes/beacon_scan_tracker_node.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/** - * - * @file beacon_scan_tracker_node.cpp - * @brief - * - * @code{.unparsed} - * _____ - * / /::\ ___ ___ - * / /:/\:\ / /\ / /\ - * / /:/ \:\ / /:/ / /:/ - * /__/:/ \__\:| /__/::\ / /:/ - * \ \:\ / /:/ \__\/\:\__ / /::\ - * \ \:\ /:/ \ \:\/\ /__/:/\:\ - * \ \:\/:/ \__\::/ \__\/ \:\ - * \ \::/ /__/:/ \ \:\ - * \__\/ \__\/ \__\/ - * @endcode - * - * @author sunfu.chou (sunfu.chou@gmail.com) - * @version 0.1 - * @date 2021-07-12 - * - */ - -#include "lidar_localization/beacon_scan_tracker.h" - -using namespace lidar_localization; - -int main(int argc, char** argv) -{ - ros::init(argc, argv, "beacon_scan_tracker"); - ros::NodeHandle nh(""); - ros::NodeHandle nh_local("~"); - - try - { - ROS_INFO("[Beacon Scan Tracker]: Initializing node"); - BeaconScanTracker beacon_scan_tracker_instance(nh, nh_local); - ros::spin(); - } - catch (const char* s) - { - ROS_FATAL_STREAM("[Beacon Scan Tracker]: " << s); - } - catch (...) - { - ROS_FATAL_STREAM("[Beacon Scan Tracker]: Unexpected error"); - } - - return 0; -} diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/nodes/lidar_localization_node.cpp b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/nodes/lidar_localization_node.cpp deleted file mode 100755 index 2a30008..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/nodes/lidar_localization_node.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * lidar_localization_node.cpp - * - * _____ - * / /::\ ___ ___ - * / /:/\:\ / /\ / /\ - * / /:/ \:\ / /:/ / /:/ - * /__/:/ \__\:| /__/::\ / /:/ - * \ \:\ / /:/ \__\/\:\__ / /::\ - * \ \:\ /:/ \ \:\/\ /__/:/\:\ - * \ \:\/:/ \__\::/ \__\/ \:\ - * \ \::/ /__/:/ \ \:\ - * \__\/ \__\/ \__\/ - * - * Created on: Feb 08, 2021 - * Author: sunfu-chou - */ - -#include "lidar_localization/lidar_localization.h" - -using namespace lidar_localization; - -int main(int argc, char** argv) -{ - ros::init(argc, argv, "lidar_localization"); - ros::NodeHandle nh(""); - ros::NodeHandle nh_local("~"); - - try - { - ROS_INFO("[Lidar Localization]: Initializing node"); - LidarLocalization lidar_localization_instance(nh, nh_local); - ros::spin(); - } - catch (const char* s) - { - ROS_FATAL_STREAM("[Lidar Localization]: " << s); - } - catch (...) - { - ROS_FATAL_STREAM("[Lidar Localization]: Unexpected error"); - } - - return 0; -} diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/rx_to_odom.cpp b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/rx_to_odom.cpp deleted file mode 100755 index 7b55233..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/rx_to_odom.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include -#include -#include -#include -#include - -ros::Publisher pub; -nav_msgs::Odometry odom; -geometry_msgs::TransformStamped transform; - -bool p_active; -bool p_publish; - -double p_init_x; -double p_init_y; -double p_init_yaw; - -double p_cov_vx; -double p_cov_vyaw; -double p_delay; - -// v w x y yaw blabla -void callback(const std_msgs::Int32MultiArray::ConstPtr msg) -{ - if (msg->data.size() < 5) - { - return; - } - - ros::Time now = ros::Time::now(); - odom.header.stamp = now; - odom.header.frame_id = "odom"; - - odom.child_frame_id = "base_footprint"; - - odom.pose.pose.position.x = msg->data[2] / 1e6; - odom.pose.pose.position.y = msg->data[3] / 1e6; - - tf2::Quaternion q; - q.setRPY(0., 0., msg->data[4] / 1e6); - odom.pose.pose.orientation = tf2::toMsg(q); - - odom.twist.twist.linear.x = msg->data[0] / 1e3; - odom.twist.twist.angular.z = msg->data[1] / 1e3; - // clang-format off - odom.twist.covariance = {p_cov_vx, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, p_cov_vyaw}; - // clang-format on - if (p_publish) - pub.publish(odom); - - static tf2_ros::TransformBroadcaster tf2_broadcaster; - transform.header.stamp = now; - transform.header.frame_id = "odom"; - transform.child_frame_id = "base_footprint"; - transform.transform.translation.x = msg->data[2] / 1e6; - transform.transform.translation.y = msg->data[3] / 1e6; - transform.transform.rotation = tf2::toMsg(q); - - tf2_broadcaster.sendTransform(transform); -} - -int main(int argc, char** argv) -{ - ros::init(argc, argv, "rx_to_odom"); - - ros::NodeHandle nh; - ros::NodeHandle nh_lcoal("~"); - - ros::Subscriber sub = nh.subscribe("rxST1", 200, &callback); - pub = nh.advertise("odom", 200); - - nh_lcoal.param("active", p_active, true); - nh_lcoal.param("publish_topic", p_publish, true); - nh_lcoal.param("init_x", p_init_x, 1.0); - nh_lcoal.param("init_y", p_init_y, 1.5); - nh_lcoal.param("init_yaw", p_init_yaw, 0.0); - nh_lcoal.param("cov_vx", p_cov_vx, 1e-1); - nh_lcoal.param("cov_vyaw", p_cov_vyaw, 1e-1); - nh_lcoal.param("delay", p_delay, 0.1); - if (p_delay < 0.2) - p_delay = 0.2; - - const boost::shared_ptr init_array(new std_msgs::Int32MultiArray); - init_array->data.push_back(0); - init_array->data.push_back(0); - init_array->data.push_back((int)(p_init_x * 1e6)); - init_array->data.push_back((int)(p_init_y * 1e6)); - init_array->data.push_back((int)(p_init_yaw * 1e6)); - callback(init_array); // for initialization - - ros::Duration(p_delay).sleep(); - callback(init_array); - - ros::spin(); - return 0; -} diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/scan_adjust.cpp b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/scan_adjust.cpp deleted file mode 100644 index d566714..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/scan_adjust.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include -#include -#include -#include - -class Scan { -public: - Scan(ros::NodeHandle& nh, ros::NodeHandle& nh_); - -private: - void initialize(); - void scan_callback(const sensor_msgs::LaserScan::ConstPtr& msg); - void local_filte_callback(const nav_msgs::Odometry::ConstPtr& msg); - void imu_callback(const sensor_msgs::Imu::ConstPtr& msg); - ros::NodeHandle nh, nh_; - ros::Subscriber scan_sub; - ros::Subscriber local_filte_sub; - ros::Subscriber imu_sub; - ros::Publisher scan_pub; - - std::string robot_name; - int robot_angular_vel_topic; - - double lidar_frequency; - double lidar_angular_vel; - double robot_angular_vel; - - double stamp; - double stamp_pre; -}; - -Scan::Scan(ros::NodeHandle& nh_g, ros::NodeHandle& nh_l){ - nh = nh_g; - nh_ = nh_l; - initialize(); -} - -void Scan::initialize(){ - bool ok = true; - ok &= nh_.getParam("robot_name", robot_name); - ok &= nh_.getParam("lidar_frequency", lidar_frequency); - ok &= nh_.getParam("robot_angular_vel_topic", robot_angular_vel_topic); - - switch(robot_angular_vel_topic){ - case 0: - local_filte_sub = nh.subscribe("global_filter", 10, &Scan::local_filte_callback, this); - case 1: - imu_sub = nh.subscribe("imu/data_cov", 10, &Scan::imu_callback, this); - default: - imu_sub = nh.subscribe("imu/data_cov", 10, &Scan::imu_callback, this); - } - scan_sub = nh.subscribe("scan", 10, &Scan::scan_callback, this); - scan_pub = nh.advertise("scan_adjust", 10); - - lidar_angular_vel = -6.28318 * lidar_frequency; - stamp = 0; - stamp_pre = 0; -} - -void Scan::scan_callback(const sensor_msgs::LaserScan::ConstPtr& msg){ - sensor_msgs::LaserScan scan_output; - stamp_pre = stamp; - stamp = msg->header.stamp.toSec(); - scan_output = *msg; - // scan_output.angle_increment = - // (1 + robot_angular_vel / lidar_angular_vel) * msg->angle_increment; - scan_output.angle_min = msg->angle_min + robot_angular_vel * (stamp - stamp_pre); - scan_pub.publish(scan_output); -} - -void Scan::local_filte_callback(const nav_msgs::Odometry::ConstPtr& msg){ - robot_angular_vel = msg->twist.twist.angular.z; -} - -void Scan::imu_callback(const sensor_msgs::Imu::ConstPtr& msg){ - robot_angular_vel = msg->angular_velocity.z; -} - -int main(int argc, char** argv) -{ - ros::init(argc, argv, "scan_adjust"); - ros::NodeHandle nh(""); - ros::NodeHandle nh_local("~"); - - Scan scan(nh, nh_local); - - while (ros::ok()) { - ros::spinOnce(); - } - - return 0; -} diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/scan_rosbag.cpp b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/scan_rosbag.cpp deleted file mode 100644 index 956c50f..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/lidar/lidar_localization/src/scan_rosbag.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include -#include - -ros::Publisher pub; -ros::Subscriber sub; - -void scanCallback(const sensor_msgs::LaserScan::ConstPtr& scan) -{ - ros::Time now = ros::Time::now(); - sensor_msgs::LaserScan* now_scan = new sensor_msgs::LaserScan; - *now_scan = *scan; - now_scan->header.stamp = now; - pub.publish(*now_scan); -} - -int main(int argc, char** argv) -{ - ros::init(argc, argv, "scan_rosbag"); - - ros::NodeHandle nh; - - ros::Subscriber sub = nh.subscribe("scan", 200, &scanCallback); - pub = nh.advertise("scan_bag", 200); - - ros::spin(); - return 0; -} diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/README.md b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/README.md deleted file mode 100644 index 279f21e..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Local filter - -- Local filter ( with odometry + imu ) - -## With all firmware - -```xml -roslaunch local_filter local_filter.launch -``` - -## Without firmware - -```xml -roslaunch local_filter local_filter_no_firmware.launch -``` - -## Only without rosserial communication - -```xml -roslaunch local_filter local_filter_no_comm.launch -``` diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/cfg/imu_drive_param.cfg b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/cfg/imu_drive_param.cfg deleted file mode 100644 index fa2553a..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/cfg/imu_drive_param.cfg +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python3 - -PACKAGE = "imu_drive" -NAMESPACE = "imu_drive" -GENERATE_FILE = "imu_drive_param" - -from dynamic_reconfigure.parameter_generator_catkin import ParameterGenerator, int_t, double_t, bool_t, str_t - -gen = ParameterGenerator() - -gen.add("publish", bool_t, 0, "To publish or not", True) -gen.add("sub_topic", str_t, 0, "Topic to subscribe data", "/imu/data") -gen.add("pub_topic", str_t, 0, "Topic to publish data", "/imu/data_cov") - -gen.add("frame", str_t, 0, "Parent frame", "imu_link") - -gen.add("covariance_vx", double_t, 0, "Covariance vx ( gyroscope )", 0.05, 0, 1) -gen.add("covariance_vy", double_t, 0, "Covariance vy ( gyroscope )", 0.05, 0, 1) -gen.add("covariance_vz", double_t, 0, "Covariance vz ( gyroscope )", 0.1, 0, 1) -gen.add("covariance_ax", double_t, 0, "Covariance ax ( accel )", 0.11, 0, 1) -gen.add("covariance_ay", double_t, 0, "Covariance ay ( accel )", 0.11, 0, 1) -gen.add("covariance_az", double_t, 0, "Covariance az ( accel )", 0.2, 0, 1) - -gen.add("cov_multi_vel", double_t, 0, "Covariance multiplican for gyroscope", 0.2, 0, 9) -gen.add("cov_multi_acl", double_t, 0, "Covariance multiplican for accel", 0.2, 0, 9) - - -exit(gen.generate(PACKAGE, NAMESPACE, GENERATE_FILE)) diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/include/imu_drive/imu_drive.h b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/include/imu_drive/imu_drive.h deleted file mode 100644 index 007a453..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/include/imu_drive/imu_drive.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once - -#include "ros/ros.h" -#include "std_srvs/Empty.h" -#include "sensor_msgs/Imu.h" -#include "geometry_msgs/Twist.h" -#include "nav_msgs/Odometry.h" -#include "std_msgs/Float64.h" -#include "geometry_msgs/TwistWithCovariance.h" - -#include -#include "imu_drive/imu_drive_paramConfig.h" - -class IMU { - -public : - - IMU(ros::NodeHandle &nh, ros::NodeHandle &nh_local); - -private : - - /* Function - for initialize params */ - void Initialize(); - - /* Function - for update params */ - bool UpdateParams(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res); - - /* Function - for sensor_msgs::Imu ( /imu/data )*/ - void IMUdataCallback(const sensor_msgs::Imu::ConstPtr &msg); - - /* Function -> for geometry_msgs::Twist ( cmd_vel ) */ - void P_VelocityCallback(const nav_msgs::Odometry::ConstPtr &msg); - - /* Function -> for geometry_msgs::Twist ( cmd_vel ) */ - void P_AccelerationCallback(const geometry_msgs::Twist::ConstPtr &msg); - - /* Function -> for geometry_msgs::TwistWithCovariance ( ekf_pose ) */ - void VelocityCallback(const nav_msgs::Odometry::ConstPtr &msg); - - /* Function -> for geometry_msgs::TwistWithCovariance ( ekf_pose ) */ - void AccelerationCallback(const geometry_msgs::TwistWithCovariance::ConstPtr &msg); - - /* Set dynamic reconfigure */ - void SetDynamicReconfigure(); - - /* Callback */ - void DynamicParamCallback(imu_drive::imu_drive_paramConfig &config, uint32_t level); - - - /* Function publish sth we need */ - void publish(); - - /** -- Node Handles -- **/ - ros::NodeHandle nh_; - ros::NodeHandle nh_local_; - - /** -- Advertise -- **/ - ros::Subscriber imu_sub_; - ros::Subscriber vel_sub_; - ros::Publisher imu_pub_; - ros::ServiceServer param_srv_; // Service for update param ( call by other nodes ) - - /** -- Msgs to pub -- **/ - sensor_msgs::Imu imu_output_; - sensor_msgs::Imu imu_output_backup_; - - /** -- For low pass filter -- **/ - geometry_msgs::Vector3 prev_angular_velocity; - geometry_msgs::Vector3 prev_linear_acceleration; - - /* Parameters */ - bool p_active_; - bool p_publish_; - bool p_update_params_; - bool p_sub_from_nav_; - bool p_use_dynamic_reconf_; - - double p_covariance_; - double p_cov_multi_vel_; - double p_cov_multi_acl_; - double p_filter_prev_; - - std::string p_frame_; - - std::string p_imu_sub_topic_; - std::string p_imu_pub_topic_; -}; - - diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/launch/imu_drive_firm.launch b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/launch/imu_drive_firm.launch deleted file mode 100644 index d254efc..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/launch/imu_drive_firm.launch +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/launch/spatial.launch b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/launch/spatial.launch deleted file mode 100644 index 9261279..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/launch/spatial.launch +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - # optional param serial, default is -1 - - - # optional param hub_port, used if connected to a VINT hub - - - # optional param frame_id, default is "imu_link" - - - # optional param use_orientation, default is false - - - # optional param spatial_algorithm, default is "ahrs" - - - # optional ahrs parameters - - - # optional param algorithm_magnetometer_gain, default is 0.005 - - - # optional param heating_enabled, not modified by default - - - # optional param linear_acceleration_stdev, default is 280ug - - - # optional param angular_velocity_stdev, default is 0.095 deg/s - - - # optional param magnetic_field_stdev, default is 1.1 milligauss - - - # supported data rates: 4 8 16 24 32 40 ... 1000 (in ms) - - - # Reasynchronize - - - # optional param publish_rate, defaults to 0 - - - # optional param server_name, required to locate remote network servers - - - # optional param server_ip, required to locate remote network servers - - - # compass correction params (see http://www.phidgets.com/docs/1044_User_Guide) - - - - #### IMU Orientation Filter ############################################### - - - - - - - - - - - - - - - - - #### IMU static transform ############################################### - - - diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/lib/imu_drive.cpp b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/lib/imu_drive.cpp deleted file mode 100644 index 2ca61df..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/lib/imu_drive.cpp +++ /dev/null @@ -1,337 +0,0 @@ -#include "imu_drive/imu_drive.h" - -IMU::IMU (ros::NodeHandle &nh, ros::NodeHandle &nh_local){ - - this->nh_ = nh; - this->nh_local_ = nh_local; - this->Initialize(); -} - -void IMU::Initialize(){ - - std_srvs::Empty empty; - - this->p_active_ = false; - ROS_INFO_STREAM("[IMU DRIVE] : inactive node"); - - if(this->UpdateParams(empty.request, empty.response)){ - ROS_INFO_STREAM("[IMU DRIVE] : Initialize param ok"); - } - else { - ROS_INFO_STREAM("[IMU DRIVE] : Initialize param failed"); - } - -} - -bool IMU::UpdateParams(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res){ - - bool prev_active = p_active_; - - /* get param */ - if(this->nh_local_.param("active", p_active_, true)){ - ROS_INFO_STREAM("[IMU DRIVE] : active set to " << p_active_); - } - - if(this->nh_local_.param("publish", p_publish_, true)){ - ROS_INFO_STREAM("[IMU DRIVE] : publish set to " << p_publish_); - } - - if(this->nh_local_.param("sub_topic", p_imu_sub_topic_, "/imu/data")){ - ROS_INFO_STREAM("[IMU DRIVE] : Current subscribe topic [ " << p_imu_sub_topic_ << " ]"); - } - - if(this->nh_local_.param("pub_topic", p_imu_pub_topic_, "/imu/data_cov")){ - ROS_INFO_STREAM("[IMU DRIVE] : Current publish topic [ " << p_imu_pub_topic_ << " ]"); - } - - if(this->nh_local_.param("frame", p_frame_, "imu_link")){ - ROS_INFO_STREAM("[IMU DRIVE] : Current fixed frame [ " << p_frame_ << " ]"); - } - - if(this->nh_local_.param("update_params", p_update_params_, false)){ - ROS_INFO_STREAM("[IMU DRIVE] : update params set to " << p_update_params_); - } - - if(this->nh_local_.param("covariance_vx", p_covariance_, 0.)){ - ROS_INFO_STREAM("[IMU DRIVE] : vx covariance set to " << p_covariance_); - this->imu_output_.angular_velocity_covariance[0] = p_covariance_; - } - - if(this->nh_local_.param("covariance_vy", p_covariance_, 0.)){ - ROS_INFO_STREAM("[IMU DRIVE] : vy covariance set to " << p_covariance_); - this->imu_output_.angular_velocity_covariance[4] = p_covariance_; - } - - if(this->nh_local_.param("covariance_vz", p_covariance_, 0.)){ - ROS_INFO_STREAM("[IMU DRIVE] : vz covariance set to " << p_covariance_); - this->imu_output_.angular_velocity_covariance[8] = p_covariance_; - } - - if(this->nh_local_.param("covariance_ax", p_covariance_, 0.)){ - ROS_INFO_STREAM("[IMU DRIVE] : ax covariance set to " << p_covariance_); - this->imu_output_.linear_acceleration_covariance[0] = p_covariance_; - } - - if(this->nh_local_.param("covariance_ay", p_covariance_, 0.)){ - ROS_INFO_STREAM("[IMU DRIVE] : ay covariance set to " << p_covariance_); - this->imu_output_.linear_acceleration_covariance[4] = p_covariance_; - } - - if(this->nh_local_.param("covariance_az", p_covariance_, 0.)){ - ROS_INFO_STREAM("[IMU DRIVE] : az covariance set to " << p_covariance_); - this->imu_output_.linear_acceleration_covariance[8] = p_covariance_; - } - - if(this->nh_local_.param("using_nav_vel_cb", p_sub_from_nav_, 0.)){ - ROS_INFO_STREAM("[Odometry] : current subscribe from nav cmd_vel is set to " << p_sub_from_nav_); - } - - if(this->nh_local_.param("cov_multi_vel", p_cov_multi_vel_, 0.)){ - ROS_INFO_STREAM("[Odometry] : gyroscope \"a\" is set to " << p_cov_multi_vel_); - } - - if(this->nh_local_.param("cov_multi_acl", p_cov_multi_acl_, 0.)){ - ROS_INFO_STREAM("[Odometry] : accel \"a\" is set to " << p_cov_multi_acl_); - } - - if(this->nh_local_.param("using_dynamic_reconf", p_use_dynamic_reconf_, false)){ - ROS_INFO_STREAM("[Odometry] : using dynamic reconfigure is set to " << p_use_dynamic_reconf_); - } - - if(this->nh_local_.param("filter_prev", p_filter_prev_, 0.1)){ - ROS_INFO_STREAM("[Odometry] : low pass filter constant is set to " << p_filter_prev_); - } - - if(p_active_ != prev_active) { - - if (p_active_) { - - ROS_INFO_STREAM("[IMU DRIVE] : active node"); - this->imu_sub_ = nh_.subscribe(p_imu_sub_topic_, 10, &IMU::IMUdataCallback, this); - this->imu_pub_ = nh_.advertise(p_imu_pub_topic_, 10); - - if(this->p_sub_from_nav_){ - this->vel_sub_ = nh_.subscribe("cmd_vel", 10, &IMU::P_VelocityCallback, this); - } - else{ - this->vel_sub_ = nh_.subscribe("local_filter", 10, &IMU::VelocityCallback, this); - } - - if(this->p_update_params_){ - this->param_srv_ = nh_local_.advertiseService("params", &IMU::UpdateParams, this); - } - - if(this->p_use_dynamic_reconf_){ - this->SetDynamicReconfigure(); - } - - } - else { - this->imu_sub_.shutdown(); - this->imu_pub_.shutdown(); - this->vel_sub_.shutdown(); - - if(this->p_update_params_){ - this->param_srv_.shutdown(); - } - } - } - - /* -- Backup covariance -- */ - this->imu_output_backup_ = this->imu_output_; - - /* -- Set basic variables -- */ - this->imu_output_.header.frame_id = this->p_frame_; - - return true; - -} - -void IMU::IMUdataCallback(const sensor_msgs::Imu::ConstPtr &msg){ // from /imu/data - - static unsigned int sequence = 0; - sequence++; - - this->imu_output_.header.seq = sequence; - this->imu_output_.header.stamp = ros::Time::now(); - - this->imu_output_.orientation = msg->orientation; - - if(sequence != 1){ - this->imu_output_.angular_velocity.x = msg->angular_velocity.x * (1 - p_filter_prev_) + prev_angular_velocity.x * p_filter_prev_; - this->imu_output_.angular_velocity.y = msg->angular_velocity.y * (1 - p_filter_prev_) + prev_angular_velocity.y * p_filter_prev_; - this->imu_output_.angular_velocity.z = msg->angular_velocity.z * (1 - p_filter_prev_) + prev_angular_velocity.z * p_filter_prev_; - } - else this->imu_output_.angular_velocity = msg->angular_velocity; - - this->imu_output_.linear_acceleration = msg->linear_acceleration; - - this->prev_angular_velocity = this->imu_output_.angular_velocity; - - if(this->p_publish_) this->publish(); - -} - -void IMU::P_VelocityCallback(const nav_msgs::Odometry::ConstPtr &msg){ - - double slope[3]; - double slope_accel[3]; - - /* Rotation */ - slope[0] = abs(msg->twist.twist.angular.x) * p_cov_multi_vel_; - slope[1] = abs(msg->twist.twist.angular.y) * p_cov_multi_vel_; - slope[2] = abs(msg->twist.twist.angular.z) * p_cov_multi_vel_; - - /* Linear */ - slope_accel[0] = abs(msg->twist.twist.linear.x) * p_cov_multi_acl_; - slope_accel[1] = abs(msg->twist.twist.linear.y) * p_cov_multi_acl_; - slope_accel[2] = abs(msg->twist.twist.linear.z) * p_cov_multi_acl_; - - /* imu_output_ = slope * x + original_covariance */ - this->imu_output_.orientation_covariance[0] = 0.1; - this->imu_output_.orientation_covariance[4] = 0.1; - this->imu_output_.orientation_covariance[8] = 0.1; - - this->imu_output_.angular_velocity_covariance[0] = std::max(0.00000001, slope[0] + this->imu_output_backup_.angular_velocity_covariance[0]); - this->imu_output_.angular_velocity_covariance[4] = std::max(0.00000001, slope[1] + this->imu_output_backup_.angular_velocity_covariance[4]); - this->imu_output_.angular_velocity_covariance[8] = std::max(0.00000001, slope[2] + this->imu_output_backup_.angular_velocity_covariance[8]); - - this->imu_output_.linear_acceleration_covariance[0] = std::max(0.00000001, slope_accel[0] + this->imu_output_backup_.linear_acceleration_covariance[0]); - this->imu_output_.linear_acceleration_covariance[4] = std::max(0.00000001, slope_accel[1] + this->imu_output_backup_.linear_acceleration_covariance[4]); - this->imu_output_.linear_acceleration_covariance[8] = std::max(0.00000001, slope_accel[2] + this->imu_output_backup_.linear_acceleration_covariance[8]); - -} - -void IMU::VelocityCallback(const nav_msgs::Odometry::ConstPtr &msg){ - - boost::shared_ptr odometry_ptr(new nav_msgs::Odometry()); - - odometry_ptr->twist.twist.linear = msg->twist.twist.linear; - odometry_ptr->twist.twist.angular = msg->twist.twist.angular; - - - this->P_VelocityCallback(odometry_ptr); - -} - -void IMU::publish(){ - - this->imu_pub_.publish(this->imu_output_); - -} - -void IMU::DynamicParamCallback(imu_drive::imu_drive_paramConfig &config, uint32_t level){ - - /* get param */ - if(p_publish_ != config.publish){ - this->p_publish_ = config.publish; - ROS_INFO_STREAM("[IMU DRIVE] : publish set to " << p_publish_); - } - - if(p_imu_pub_topic_ != config.pub_topic){ - - this->p_imu_pub_topic_ = config.pub_topic; - - if(p_active_){ - this->imu_pub_ = nh_.advertise(p_imu_pub_topic_, 10); - } - - ROS_INFO_STREAM("[IMU DRIVE] : Current publish topic [ " << p_imu_pub_topic_ << " ]"); - } - - if(p_imu_sub_topic_ != config.sub_topic){ - - this->p_imu_sub_topic_ = config.sub_topic; - - if(p_active_){ - this->imu_sub_ = nh_.subscribe(p_imu_sub_topic_, 10, &IMU::IMUdataCallback, this); - } - - ROS_INFO_STREAM("[IMU DRIVE] : Current subscribe topic [ " << p_imu_sub_topic_ << " ]"); - } - - if(p_frame_ != config.frame){ - - this->p_frame_ = config.frame; - this->imu_output_.header.frame_id = this->p_frame_; - - ROS_INFO_STREAM("[IMU DRIVE] : Current frame [ " << p_frame_ << " ]"); - } - - if(this->imu_output_backup_.angular_velocity_covariance[0] != config.covariance_vx){ - - this->imu_output_backup_.angular_velocity_covariance[0] = config.covariance_vx; - - ROS_INFO_STREAM("[IMU DRIVE] : vx ( gyroscope ) covariance is set to " << this->imu_output_backup_.angular_velocity_covariance[0]); - } - - if(this->imu_output_backup_.angular_velocity_covariance[4] != config.covariance_vy){ - - this->imu_output_backup_.angular_velocity_covariance[4] = config.covariance_vy; - - ROS_INFO_STREAM("[IMU DRIVE] : vy ( gyroscope ) covariance is set to " << this->imu_output_backup_.angular_velocity_covariance[4]); - } - - if(this->imu_output_backup_.angular_velocity_covariance[8] != config.covariance_vz){ - - this->imu_output_backup_.angular_velocity_covariance[8] = config.covariance_vz; - - ROS_INFO_STREAM("[IMU DRIVE] : vz ( gyroscope ) covariance is set to " << this->imu_output_backup_.angular_velocity_covariance[8]); - } - - - if(this->imu_output_backup_.linear_acceleration_covariance[0] != config.covariance_ax){ - - this->imu_output_backup_.linear_acceleration_covariance[0] = config.covariance_ax; - - ROS_INFO_STREAM("[IMU DRIVE] : ax ( acceleration ) covariance is set to " << this->imu_output_backup_.linear_acceleration_covariance[0]); - } - - if(this->imu_output_backup_.linear_acceleration_covariance[4] != config.covariance_ay){ - - this->imu_output_backup_.linear_acceleration_covariance[4] = config.covariance_ay; - - ROS_INFO_STREAM("[IMU DRIVE] : ay ( acceleration ) covariance is set to " << this->imu_output_backup_.linear_acceleration_covariance[4]); - } - - if(this->imu_output_backup_.linear_acceleration_covariance[8] != config.covariance_az){ - - this->imu_output_backup_.linear_acceleration_covariance[8] = config.covariance_az; - - ROS_INFO_STREAM("[IMU DRIVE] : az ( acceleration ) covariance is set to " << this->imu_output_backup_.linear_acceleration_covariance[8]); - } - - if(this->p_cov_multi_vel_ != config.cov_multi_vel){ - - this->p_cov_multi_vel_ = config.cov_multi_vel; - - ROS_INFO_STREAM("[IMU DRIVE] : covariance multiplicant angular is set to " << this->p_cov_multi_vel_); - } - - - if(this->p_cov_multi_acl_ != config.cov_multi_acl){ - - this->p_cov_multi_acl_ = config.cov_multi_acl; - - ROS_INFO_STREAM("[IMU DRIVE] : covariance multiplicant linear is set to " << this->p_cov_multi_acl_); - } - - this->imu_output_.angular_velocity_covariance = this->imu_output_backup_.angular_velocity_covariance; - this->imu_output_.linear_acceleration_covariance = this->imu_output_backup_.linear_acceleration_covariance; - -} - - -void IMU::SetDynamicReconfigure(){ - ROS_INFO_STREAM("[IMU DRIVE] : "); - static dynamic_reconfigure::Server dynamic_param_srv_; - - dynamic_reconfigure::Server::CallbackType callback; - - // If the function is a class member : - // boost::bind(&function, class instance, _1, _2) - callback = boost::bind(&IMU::DynamicParamCallback, this, _1, _2); - - // Set callback function to param server - dynamic_param_srv_.setCallback(callback); -} diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/package.xml b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/package.xml deleted file mode 100644 index 66e7293..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/package.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - imu_drive - 0.0.0 - The imu_drive package - - - - - assume - - - - - - TODO - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - catkin - roscpp - rospy - sensor_msgs - std_msgs - std_srvs - roscpp - rospy - sensor_msgs - std_msgs - std_srvs - roscpp - rospy - sensor_msgs - std_msgs - std_srvs - - - - - - - - diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/param/imu_drive_firm.yaml b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/param/imu_drive_firm.yaml deleted file mode 100644 index bc66ca5..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/param/imu_drive_firm.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# Set the usage of the node -active: true -publish: true - -# Open param service -update_params: false - -# Covariance for "const" part -covariance_vx: 0.05 -covariance_vy: 0.05 -covariance_vz: 0.01 -covariance_ax: 0.11 -covariance_ay: 0.11 -covariance_az: 0.2 - -# Covariance for "multiple" part -cov_multi_vel: 0.02 -cov_multi_acl: 0.02 - -# Use which topic to dynamic adjust covariance -using_nav_vel_cb: false - -# Low pass filter -> beleive previous -filter_prev: 0.1 \ No newline at end of file diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/src/imu_drive_node.cpp b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/src/imu_drive_node.cpp deleted file mode 100644 index 22a4007..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/imu/imu_drive/src/imu_drive_node.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "imu_drive/imu_drive.h" - -int main(int argc, char** argv){ - - ros::init(argc, argv, "imu_drive_node"); - - ros::NodeHandle nh(""); - ros::NodeHandle nh_local("~"); - - try { - - ROS_INFO_STREAM("[IMU DRIVE] : Initializing imu drive node"); - IMU imu(nh, nh_local); - ros::spin(); - - } - catch (const char* s) { - - ROS_FATAL_STREAM("[IMU DRIVE] : " << s); - - } - catch (...) { - - ROS_FATAL_STREAM("[IMU DRIVE] : Unexpected error"); - - } - - return 0; -} diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/CMakeLists.txt b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/CMakeLists.txt deleted file mode 100644 index 050c467..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/CMakeLists.txt +++ /dev/null @@ -1,214 +0,0 @@ -cmake_minimum_required(VERSION 3.0.2) -project(local_filter) - -## Compile as C++11, supported in ROS Kinetic and newer -# add_compile_options(-std=c++11) - -## Find catkin macros and libraries -## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) -## is used, also find other catkin packages -find_package(catkin REQUIRED COMPONENTS - geometry_msgs - nav_msgs - roscpp - rospy - std_msgs - obstacle_detector - startup_msgs -) - -## System dependencies are found with CMake's conventions -# find_package(Boost REQUIRED COMPONENTS system) - - -## Uncomment this if the package has a setup.py. This macro ensures -## modules and global scripts declared therein get installed -## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html -# catkin_python_setup() - -################################################ -## Declare ROS messages, services and actions ## -################################################ - -## To declare and build messages, services or actions from within this -## package, follow these steps: -## * Let MSG_DEP_SET be the set of packages whose message types you use in -## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...). -## * In the file package.xml: -## * add a build_depend tag for "message_generation" -## * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET -## * If MSG_DEP_SET isn't empty the following dependency has been pulled in -## but can be declared for certainty nonetheless: -## * add a exec_depend tag for "message_runtime" -## * In this file (CMakeLists.txt): -## * add "message_generation" and every package in MSG_DEP_SET to -## find_package(catkin REQUIRED COMPONENTS ...) -## * add "message_runtime" and every package in MSG_DEP_SET to -## catkin_package(CATKIN_DEPENDS ...) -## * uncomment the add_*_files sections below as needed -## and list every .msg/.srv/.action file to be processed -## * uncomment the generate_messages entry below -## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...) - -## Generate messages in the 'msg' folder -# add_message_files( -# FILES -# Message1.msg -# Message2.msg -# ) - -## Generate services in the 'srv' folder -# add_service_files( -# FILES -# Service1.srv -# Service2.srv -# ) - -## Generate actions in the 'action' folder -# add_action_files( -# FILES -# Action1.action -# Action2.action -# ) - -## Generate added messages and services with any dependencies listed here -generate_messages( - DEPENDENCIES - geometry_msgs - nav_msgs - std_msgs -) - -################################################ -## Declare ROS dynamic reconfigure parameters ## -################################################ - -## To declare and build dynamic reconfigure parameters within this -## package, follow these steps: -## * In the file package.xml: -## * add a build_depend and a exec_depend tag for "dynamic_reconfigure" -## * In this file (CMakeLists.txt): -## * add "dynamic_reconfigure" to -## find_package(catkin REQUIRED COMPONENTS ...) -## * uncomment the "generate_dynamic_reconfigure_options" section below -## and list every .cfg file to be processed - -## Generate dynamic reconfigure parameters in the 'cfg' folder -# generate_dynamic_reconfigure_options( -# cfg/DynReconf1.cfg -# cfg/DynReconf2.cfg -# ) - -################################### -## catkin specific configuration ## -################################### -## The catkin_package macro generates cmake config files for your package -## Declare things to be passed to dependent projects -## INCLUDE_DIRS: uncomment this if your package contains header files -## LIBRARIES: libraries you create in this project that dependent projects also need -## CATKIN_DEPENDS: catkin_packages dependent projects also need -## DEPENDS: system dependencies of this project that dependent projects also need -catkin_package( -# INCLUDE_DIRS include -# LIBRARIES local_filter - CATKIN_DEPENDS geometry_msgs nav_msgs roscpp rospy std_msgs message_runtime -# DEPENDS system_lib -) - -########### -## Build ## -########### - -## Specify additional locations of header files -## Your package locations should be listed before other locations -include_directories( - include - ${catkin_INCLUDE_DIRS} -) - -## Declare a C++ library -# add_library(${PROJECT_NAME} -# src/${PROJECT_NAME}/local_filter.cpp -# ) - -## Add cmake target dependencies of the library -## as an example, code may need to be generated before libraries -## either from message generation or dynamic reconfigure -# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) - -## Declare a C++ executable -## With catkin_make all packages are built within a single CMake context -## The recommended prefix ensures that target names across packages don't collide -# add_executable(${PROJECT_NAME}_node src/local_filter_node.cpp) -add_executable(local_filter_LPF src/local_filter_LPF.cpp) - -## Rename C++ executable without prefix -## The above recommended prefix causes long target names, the following renames the -## target back to the shorter version for ease of user use -## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node" -# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "") -target_link_libraries(local_filter_LPF ${catkin_LIBRARIES}) -## Add cmake target dependencies of the executable -## same as for the library above -# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) -add_dependencies(local_filter_LPF ${catkin_EXPORTED_TARGETS}) - -## Specify libraries to link a library or executable target against -# target_link_libraries(${PROJECT_NAME}_node -# ${catkin_LIBRARIES} -# ) - -############# -## Install ## -############# - -# all install targets should use catkin DESTINATION variables -# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html - -## Mark executable scripts (Python etc.) for installation -## in contrast to setup.py, you can choose the destination -# catkin_install_python(PROGRAMS -# scripts/my_python_script -# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} -# ) - -## Mark executables for installation -## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html -# install(TARGETS ${PROJECT_NAME}_node -# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} -# ) - -## Mark libraries for installation -## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html -# install(TARGETS ${PROJECT_NAME} -# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} -# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} -# RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION} -# ) - -## Mark cpp header files for installation -# install(DIRECTORY include/${PROJECT_NAME}/ -# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} -# FILES_MATCHING PATTERN "*.h" -# PATTERN ".svn" EXCLUDE -# ) - -## Mark other files for installation (e.g. launch and bag files, etc.) -# install(FILES -# # myfile1 -# # myfile2 -# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} -# ) - -############# -## Testing ## -############# - -## Add gtest based cpp test target and link libraries -# catkin_add_gtest(${PROJECT_NAME}-test test/test_local_filter.cpp) -# if(TARGET ${PROJECT_NAME}-test) -# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}) -# endif() - -## Add folders to be run by python nosetests -# catkin_add_nosetests(test) diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/launch/local_filter.launch b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/launch/local_filter.launch deleted file mode 100644 index 52e87c9..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/launch/local_filter.launch +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/launch/local_filter_whole.launch b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/launch/local_filter_whole.launch deleted file mode 100644 index 1e43c92..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/launch/local_filter_whole.launch +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/param/local_filter.yaml b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/param/local_filter.yaml deleted file mode 100644 index 0ca05b3..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/param/local_filter.yaml +++ /dev/null @@ -1,114 +0,0 @@ -frequency: 100 -silent_tf_failure: false -sensor_timeout: 0.01 -two_d_mode: true - -smooth_lagged_data: false -history_length: 0.4 - -dynamic_process_noise_covariance: false - -predict_to_current_time: false - -print_diagnostics: false - -# debug -debug: false -debug_out_file: /path/to/debug/file.txt - -# frames -map_frame: map -odom_frame: odom -base_link_frame: base_footprint -world_frame: odom -transform_time_offset: 0.05 -transform_timeout: 0 - -# sensors -# config: [x, y, z, -# r, p, y, -# x., y., z., -# r., p., y., -# x.., y.., z..] -odom0: odom -odom0_config: [false, false, false, - false, false, false, - true, true, false, - false, false, true, - false, false, false] -odom0_queue_size: 20 -odom0_differential: false -odom0_relative: false -odom0_nodelay: true - -odom0_twist_rejection_threshold: 100 - -imu0: imu/data_cov -imu0_config: [false, false, false, - false, false, false, - false, false, false, - false, false, true, - true, true, false] -imu0_queue_size: 100 -imu0_differential: false -imu0_relative: false -imu0_nodelay: true - -imu0_twist_rejection_threshold: 1000000000000.0 -imu0_linear_acceleration_rejection_threshold: 1000000000000.0 - -imu0_remove_gravitational_acceleration: false - - -initial_state: [0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0] - -publish_tf: true -publish_acceleration: true - -# control -# acc_config: [x., y., z., r., p., y.] -use_control: false -stamped_control: false -control_timeout: 0.2 -control_config: [true, true, false, false, false, true] -acceleration_limits: [1.3, 0.0, 0.0, 0.0, 0.0, 3.4] -deceleration_limits: [1.3, 0.0, 0.0, 0.0, 0.0, 4.5] -acceleration_gains: [0.8, 0.0, 0.0, 0.0, 0.0, 0.9] -deceleration_gains: [1.0, 0.0, 0.0, 0.0, 0.0, 1.0] - -# config: [x, y, z, r, p, y, x., y., z., r., p., y., x.., y.., z..] -process_noise_covariance: [0.005, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0.005, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0.006, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0.25, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0.25, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.01, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.01, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - -initial_estimate_covariance: [0.01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0.01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0.1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-6, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9] diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/param/local_filter_whole.yaml b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/param/local_filter_whole.yaml deleted file mode 100644 index 6b8f8e9..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/param/local_filter_whole.yaml +++ /dev/null @@ -1,192 +0,0 @@ -# --------------------------- -# -------- Rosserial -------- -# --------------------------- -rosserial_server_dp: - port: "/dev/USB0-3" # not used? - -# --------------------------- -# ------ Odometry ndoe ------ -# --------------------------- -odometry_node: - # Active this node - active: true - - # Set publish to this node - publish: true - - # Open param service - update_params: false - - # Covariance for "const" part - covariance_x: 0.001 - covariance_y: 0.001 - covariance_z: 0.001 - covariance_vx: 0.0042 - covariance_vy: 0.0042 - covariance_vz: 0.005 - - # Covariance for "multiple" part - covariance_multi_vx: 0. - covariance_multi_vy: 0. - covariance_multi_vz: 0. - - # Dynamic adjustment from : - # True: (ns)/cmd_vel - # False: (ns)/final_pose - using_nav_vel_cb: false - - # Open if STM has integral information - use_stm_integral: false - - # Open Dynamic reconfigure service - using_dynamic_reconf: true - -# ---------------------- -# ------ IMU node ------ -# ---------------------- -imu_node: - # Set the usage of the node - active: true - publish: true - - # Open param service - update_params: false - - # Covariance for "const" part - covariance_vx: 0.005 - covariance_vy: 0.005 - covariance_vz: 0.001 - covariance_ax: 0.1 - covariance_ay: 0.1 - covariance_az: 0.1 - - # Covariance for "multiple" part - cov_multi_vel: 0.2 - cov_multi_acl: 0.2 - - # Use which topic to dynamic adjust covariance - using_nav_vel_cb: false - - # Low pass filter -> beleive previous - filter_prev: 0.1 - - using_dynamic_reconf: true - -# ---------------------- -# ------ EKF node ------ -# ---------------------- -ekf_velocity: - frequency: 100 - silent_tf_failure: false - sensor_timeout: 0.01 - two_d_mode: true - - smooth_lagged_data: false - history_length: 0.4 - - dynamic_process_noise_covariance: false - - predict_to_current_time: false - - print_diagnostics: false - - # debug - debug: false - debug_out_file: /path/to/debug/file.txt - - # frames - map_frame: map - odom_frame: odom - base_link_frame: base_footprint - world_frame: odom - transform_time_offset: 0.0 - transform_timeout: 0 - - # sensors - # config: [x, y, z, - # r, p, y, - # x., y., z., - # r., p., y., - # x.., y.., z..] - odom0: odom - odom0_config: [false, false, false, - false, false, false, - true, true, false, - false, false, true, - false, false, false] - odom0_queue_size: 20 - odom0_differential: false - odom0_relative: false - odom0_nodelay: true - - odom0_twist_rejection_threshold: 100 - - imu0: imu/data_cov - imu0_config: [false, false, false, - false, false, false, - false, false, false, - false, false, true, - false, false, false] - imu0_queue_size: 100 - imu0_differential: false - imu0_relative: false - imu0_nodelay: true - - imu0_twist_rejection_threshold: 1000000000000.0 - imu0_linear_acceleration_rejection_threshold: 1000000000000.0 - - imu0_remove_gravitational_acceleration: false - - - initial_state: [0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0] - - publish_tf: true - publish_acceleration: true - - # control - # acc_config: [x., y., z., r., p., y.] - use_control: false - stamped_control: false - control_timeout: 0.2 - control_config: [true, true, false, false, false, true] - acceleration_limits: [1.3, 0.0, 0.0, 0.0, 0.0, 3.4] - deceleration_limits: [1.3, 0.0, 0.0, 0.0, 0.0, 4.5] - acceleration_gains: [0.8, 0.0, 0.0, 0.0, 0.0, 0.9] - deceleration_gains: [1.0, 0.0, 0.0, 0.0, 0.0, 1.0] - - # config: [x, y, z, r, p, y, x., y., z., r., p., y., x.., y.., z..] - process_noise_covariance: [1e-6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1e-6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1e-6, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1e-2, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1e-2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-3, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-3, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - - initial_estimate_covariance: [0.01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0.01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0.1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-4, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-4, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1e-9] diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/src/local_filter_LPF.cpp b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/src/local_filter_LPF.cpp deleted file mode 100644 index 8580da8..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/local_filter/local_filter/src/local_filter_LPF.cpp +++ /dev/null @@ -1,195 +0,0 @@ -#include -#include -// msg -#include -#include -#include -#include -// matrix calulation -#include -#include -// tf2 -#include -#include -#include -#include -#include -#include - -struct RobotState -{ - Eigen::Vector3d mu; - Eigen::Matrix3d sigma; -}; - -class GlobalFilterNode { -public: - GlobalFilterNode(ros::NodeHandle& nh, ros::NodeHandle& nh_local) : nh_(nh), nh_local_(nh_local){ - // Initialize filter coefficients and initial values - linear_x_ = 0.0; - linear_y_ = 0.0; - angular_z_ = 0.0; - nh_local_.param("LPF_alpha_x", alpha_x, 0.5); //filter coefficient - nh_local_.param("LPF_alpha_y", alpha_y, 0.5); //filter coefficient - nh_local_.param("linear_cov_max", linear_cov_max_, 0.1); - nh_local_.param("angular_cov_max", angular_cov_max_, 0.05); - - setpose_sub_ = nh_.subscribe("initialpose", 50, &GlobalFilterNode::setposeCallback, this); - odom_sub_ = nh_.subscribe("odom", 10, &GlobalFilterNode::odomCallback, this); - imu_sub_ = nh_.subscribe("imu/data_cov", 10, &GlobalFilterNode::imuCallback, this); - - global_filter_pub_ = nh_.advertise("local_filter", 100); - } - - void diff_model(double v, double w, double dt) - { - double theta = robotstate_.mu(2); - double s = sin(theta); double s_ = sin(theta + w * dt); - double c = cos(theta); double c_ = cos(theta + w * dt); - - if ((w < 0.00001) && (w > -0.00001)){ - robotstate_.mu = robotstate_.mu + Eigen::Vector3d{ v * c * dt, v * s * dt, 0.0 }; - }else{ - robotstate_.mu = robotstate_.mu + Eigen::Vector3d{ v * (-s + s_) / w, v * (c - c_) / w, w * dt }; - } - robotstate_.mu(2) = angleLimitChecking(robotstate_.mu(2)); - } - - void omni_model(double v_x, double v_y, double w, double dt) - { - /* ekf prediction function for omni wheel */ - Eigen::Vector3d d_state; - - d_state << (v_x * dt), (v_y * dt), (w * dt); - - double theta_ = robotstate_.mu(2) + d_state(2) / 2; - double s__theta = sin(theta_); - double c__theta = cos(theta_); - - Eigen::Matrix3d A; - A << 1, 0, 0, 0, 1, 0, 0, 0, 1; - - Eigen::Matrix3d B; - B << c__theta, -s__theta, 0, s__theta, c__theta, 0, 0, 0, 1; - - Eigen::Matrix3d cov_past; - cov_past = robotstate_.sigma; - - /* Update robot state mean */ - robotstate_.mu = A * robotstate_.mu + B * d_state; - } - - void setposeCallback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& pose_msg) - { - double x = pose_msg->pose.pose.position.x; - double y = pose_msg->pose.pose.position.y; - - tf2::Quaternion q; - tf2::fromMsg(pose_msg->pose.pose.orientation, q); - tf2::Matrix3x3 qt(q); - double _, yaw; - qt.getRPY(_, _, yaw); - - robotstate_.mu(0) = x; - robotstate_.mu(1) = y; - robotstate_.mu(2) = yaw; - - robotstate_.sigma(0, 0) = pose_msg->pose.covariance[0]; // x-x - robotstate_.sigma(0, 1) = pose_msg->pose.covariance[1]; // x-y - robotstate_.sigma(0, 2) = pose_msg->pose.covariance[5]; // x-theta - robotstate_.sigma(1, 0) = pose_msg->pose.covariance[6]; // y-x - robotstate_.sigma(1, 1) = pose_msg->pose.covariance[7]; // y-y - robotstate_.sigma(1, 2) = pose_msg->pose.covariance[11]; // y-theta - robotstate_.sigma(2, 0) = pose_msg->pose.covariance[30]; // theta-x - robotstate_.sigma(2, 1) = pose_msg->pose.covariance[31]; // theta-y - robotstate_.sigma(2, 2) = pose_msg->pose.covariance[35]; // theta-theta - } - - void odomCallback(const nav_msgs::Odometry::ConstPtr& odom_msg) { - // get velocity data - twist_x_ = odom_msg->twist.twist.linear.x; - twist_y_ = odom_msg->twist.twist.linear.y; - // Apply low-pass filter to linear xy from odom - linear_x_ = alpha_x * odom_msg->twist.twist.linear.x + (1 - alpha_x) * linear_x_; - linear_y_ = alpha_y * odom_msg->twist.twist.linear.y + (1 - alpha_y) * linear_y_; - linear_x_cov_ = std::min(linear_cov_max_, odom_msg->twist.covariance[0]); - linear_y_cov_ = std::min(linear_cov_max_, odom_msg->twist.covariance[7]); - } - - void imuCallback(const sensor_msgs::Imu::ConstPtr& imu_msg) { - angular_z_ = imu_msg->angular_velocity.z; - local_filter_pub(imu_msg->header.stamp, std::min(angular_cov_max_, imu_msg->angular_velocity_covariance[8])); //cov_max - prev_stamp_ = imu_msg->header.stamp; - } - - void local_filter_pub(ros::Time stamp, double imu_cov) - { - // Publish global_filter message - nav_msgs::Odometry global_filter_msg; - global_filter_msg.header.stamp = stamp; // imu callback stamp - //velocity - global_filter_msg.twist.twist.linear.x = linear_x_; //filtered x velocity - global_filter_msg.twist.twist.linear.y = linear_y_; - global_filter_msg.twist.twist.angular.z = angular_z_; //raw imu data - //pose - global_filter_msg.pose.pose.position.x = robotstate_.mu(0); - global_filter_msg.pose.pose.position.y = robotstate_.mu(1); - tf2::Quaternion quaternion_; - quaternion_.setRPY( 0, 0, robotstate_.mu(2) ); - quaternion_ = quaternion_.normalize(); - global_filter_msg.pose.pose.orientation.z = quaternion_.getZ(); - global_filter_msg.pose.pose.orientation.w = quaternion_.getW(); - //covariance - global_filter_msg.twist.covariance[0] = linear_x_cov_; //x-x - global_filter_msg.twist.covariance[7] = linear_y_cov_; //y-y - global_filter_msg.twist.covariance[35] = imu_cov; //theta-theta - global_filter_pub_.publish(global_filter_msg); - } - - double angleLimitChecking(double theta) -{ - while (theta > M_PI) - { - theta -= M_PI * 2; - } - while (theta <= -M_PI) - { - theta += M_PI * 2; - } - return theta; -} - -private: - ros::NodeHandle nh_; - ros::NodeHandle nh_local_; - ros::Subscriber odom_sub_; - ros::Subscriber imu_sub_; - ros::Subscriber setpose_sub_; - ros::Publisher global_filter_pub_; - - //raw - double twist_x_; - double twist_y_; - //filtered - double alpha_x; // filter coefficient - double alpha_y; // filter coefficient - double linear_x_; - double linear_x_cov_; - double linear_y_; - double linear_y_cov_; - double angular_z_; - // double imu_cov_; - RobotState robotstate_; - ros::Time prev_stamp_; - double linear_cov_max_; - double angular_cov_max_; -}; - -int main(int argc, char** argv) { - ros::init(argc, argv, "global_filter_LPF"); - ros::NodeHandle nh; - ros::NodeHandle nh_local("~"); - GlobalFilterNode global_filter_node(nh, nh_local); - ros::spin(); - return 0; -} diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/localization.perspective b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/localization.perspective deleted file mode 100644 index c66bb69..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/localization.perspective +++ /dev/null @@ -1,230 +0,0 @@ -{ - "keys": {}, - "groups": { - "mainwindow": { - "keys": { - "geometry": { - "repr(QByteArray.hex)": "QtCore.QByteArray(b'01d9d0cb00030000000000000000003600000a0e0000064e000000000000008000000a0e0000064e00000000000000000b40000000000000008000000a0e0000064e')", - "type": "repr(QByteArray.hex)", - "pretty-print": " 6 N N @ N" - }, - "state": { - "repr(QByteArray.hex)": "QtCore.QByteArray(b'000000ff00000000fd000000010000000300000a0f00000544fc0100000005fb0000004c007200710074005f00740066005f0074007200650065005f005f0052006f0073005400660054007200650065005f005f0031005f005f0052006f007300540066005400720065006500550069000000000000000555000002e500fffffffb00000050007200710074005f0070006f00730065005f0076006900650077005f005f0050006f007300650056006900650077005f005f0031005f005f0047004c00560069006500770057006900640067006500740100000000000001020000000000000000fb00000042007200710074005f0070006c006f0074005f005f0050006c006f0074005f005f0031005f005f00440061007400610050006c006f0074005700690064006700650074010000000000000351000001bd00fffffffb00000042007200710074005f0070006c006f0074005f005f0050006c006f0074005f005f0032005f005f00440061007400610050006c006f0074005700690064006700650074010000035d00000324000001bd00fffffffb00000042007200710074005f0070006c006f0074005f005f0050006c006f0074005f005f0033005f005f00440061007400610050006c006f0074005700690064006700650074010000068d00000382000001bd00ffffff00000a0f0000000000000004000000040000000800000008fc00000001000000030000000100000036004d0069006e0069006d0069007a006500640044006f0063006b00570069006400670065007400730054006f006f006c0062006100720100000000ffffffff0000000000000000')", - "type": "repr(QByteArray.hex)", - "pretty-print": " Lrqt_tf_tree__RosTfTree__1__RosTfTreeUi Prqt_pose_view__PoseView__1__GLViewWidget Brqt_plot__Plot__1__DataPlotWidget Brqt_plot__Plot__2__DataPlotWidget Brqt_plot__Plot__3__DataPlotWidget 6MinimizedDockWidgetsToolbar " - } - }, - "groups": { - "toolbar_areas": { - "keys": { - "MinimizedDockWidgetsToolbar": { - "repr": "8", - "type": "repr" - } - }, - "groups": {} - } - } - }, - "pluginmanager": { - "keys": { - "running-plugins": { - "repr": "{'rqt_plot/Plot': [1, 2, 3], 'rqt_tf_tree/RosTfTree': [1]}", - "type": "repr" - } - }, - "groups": { - "plugin__rqt_plot__Plot__1": { - "keys": {}, - "groups": { - "dock_widget__DataPlotWidget": { - "keys": { - "dock_widget_title": { - "repr": "'MatPlot'", - "type": "repr" - }, - "dockable": { - "repr": "True", - "type": "repr" - }, - "parent": { - "repr": "None", - "type": "repr" - } - }, - "groups": {} - }, - "plugin": { - "keys": { - "autoscroll": { - "repr": "True", - "type": "repr" - }, - "plot_type": { - "repr": "1", - "type": "repr" - }, - "topics": { - "repr": "['/robot/lidar_bonbonbon/pose/pose/orientation/w', '/robot/final_pose/pose/pose/orientation/w']", - "type": "repr" - }, - "x_limits": { - "repr": "[152.47024941444397, 153.47024941444397]", - "type": "repr" - }, - "y_limits": { - "repr": "[-2.895536666724907, 2.5011818287775784]", - "type": "repr" - } - }, - "groups": {} - } - } - }, - "plugin__rqt_plot__Plot__2": { - "keys": {}, - "groups": { - "dock_widget__DataPlotWidget": { - "keys": { - "dock_widget_title": { - "repr": "'MatPlot (2)'", - "type": "repr" - }, - "dockable": { - "repr": "True", - "type": "repr" - }, - "parent": { - "repr": "None", - "type": "repr" - } - }, - "groups": {} - }, - "plugin": { - "keys": { - "autoscroll": { - "repr": "True", - "type": "repr" - }, - "plot_type": { - "repr": "1", - "type": "repr" - }, - "topics": { - "repr": "['/robot/local_filter/twist/twist/angular/z', '/robot/final_pose/twist/twist/angular/z']", - "type": "repr" - }, - "x_limits": { - "repr": "[152.21846270561218, 153.21846270561218]", - "type": "repr" - }, - "y_limits": { - "repr": "[-2.3665503928561904, 2.718727645972482]", - "type": "repr" - } - }, - "groups": {} - } - } - }, - "plugin__rqt_plot__Plot__3": { - "keys": {}, - "groups": { - "dock_widget__DataPlotWidget": { - "keys": { - "dock_widget_title": { - "repr": "'MatPlot (3)'", - "type": "repr" - }, - "dockable": { - "repr": "True", - "type": "repr" - }, - "parent": { - "repr": "None", - "type": "repr" - } - }, - "groups": {} - }, - "plugin": { - "keys": { - "autoscroll": { - "repr": "True", - "type": "repr" - }, - "plot_type": { - "repr": "1", - "type": "repr" - }, - "topics": { - "repr": "['/robot/final_pose/pose/pose/position/x', '/robot/final_pose/pose/pose/position/y', '/robot/lidar_bonbonbon/pose/pose/position/x', '/robot/lidar_bonbonbon/pose/pose/position/y']", - "type": "repr" - }, - "x_limits": { - "repr": "[152.05426049232483, 153.05426049232483]", - "type": "repr" - }, - "y_limits": { - "repr": "[-1.298986617348703, 1.5514280771665736]", - "type": "repr" - } - }, - "groups": {} - } - } - }, - "plugin__rqt_pose_view__PoseView__1": { - "keys": {}, - "groups": { - "plugin": { - "keys": { - "view_matrix": { - "repr": "'[[0.7071067690849304, 0.29883623123168945, -0.6408563852310181, 0.0], [-0.7071067690849304, 0.29883623123168945, -0.6408563852310181, 0.0], [0.0, 0.9063078165054321, 0.42261824011802673, 0.0], [0.0, -3.0, -17.5, 1.0]]'", - "type": "repr" - } - }, - "groups": {} - } - } - }, - "plugin__rqt_tf_tree__RosTfTree__1": { - "keys": {}, - "groups": { - "dock_widget__RosTfTreeUi": { - "keys": { - "dock_widget_title": { - "repr": "'TF Tree'", - "type": "repr" - }, - "dockable": { - "repr": "True", - "type": "repr" - }, - "parent": { - "repr": "None", - "type": "repr" - } - }, - "groups": {} - }, - "plugin": { - "keys": { - "auto_fit_graph_check_box_state": { - "repr": "True", - "type": "repr" - }, - "highlight_connections_check_box_state": { - "repr": "True", - "type": "repr" - } - }, - "groups": {} - } - } - } - } - } - } -} \ No newline at end of file diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/rosenv.sh b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/rosenv.sh deleted file mode 100755 index e4c7dc9..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/rosenv.sh +++ /dev/null @@ -1,6 +0,0 @@ -export ROS_HOSTNAME="192.168.50.11" -export ROS_MASTER_URI="http://192.168.50.11:11311" -source /opt/ros/noetic/setup.bash -source /home/localization/localization_ws/devel/setup.bash - -sudo chown localization:localization ~/.gazebo \ No newline at end of file diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/simulation.sh b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/simulation.sh deleted file mode 100755 index 5ef1117..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/simulation.sh +++ /dev/null @@ -1,20 +0,0 @@ -rostopic pub -r 50 /Toposition geometry_msgs/Twist "linear: - x: 0.0 - y: 0.0 - z: 0.0 -angular: - x: 0.0 - y: 0.0 - z: 0.0" & - -rostopic pub -r 50 /imu/data sensor_msgs/Imu "header: - seq: 0 - stamp: {secs: 0, nsecs: 0} - frame_id: '' -orientation: {x: 0.0, y: 0.0, z: 0.0, w: 0.0} -orientation_covariance: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] -angular_velocity: {x: 0.0, y: 0.0, z: 0.0} -angular_velocity_covariance: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] -linear_acceleration: {x: 0.0, y: 0.0, z: 0.0} -linear_acceleration_covariance: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]" & - diff --git a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/usb.sh b/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/usb.sh deleted file mode 100755 index e093326..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/Eurobot-2024-Localization-main/usb.sh +++ /dev/null @@ -1,15 +0,0 @@ -# USB driver setup - -PACKAGES_PATH=/home/localization/localization_ws/src/Eurobot-2024-Localization - -sudo chmod 777 /dev/ttyUSB0 -sudo chmod 777 /dev/ttyUSB1 - -sudo /lib/systemd/systemd-udevd --daemon -cd $PACKAGES_PATH -cd $PACKAGES_PATH/local_filter/imu/phidgets_drivers/phidgets_api -sudo cp debian/udev /etc/udev/rules.d/99-phidgets.rules - -cd $PACKAGES_PATH/Vive/libsurvive -sudo cp ./useful_files/81-vive.rules /etc/udev/rules.d/ -sudo udevadm control --reload-rules && udevadm trigger \ No newline at end of file diff --git a/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/.gitignore b/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/.gitignore deleted file mode 100644 index 259148f..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -# Prerequisites -*.d - -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app diff --git a/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/README.md b/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/README.md deleted file mode 100644 index 904f17f..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# eurobot_msgs -Required messages for eurobot 2024 diff --git a/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/navigation_msgs/CMakeLists.txt b/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/navigation_msgs/CMakeLists.txt deleted file mode 100644 index 7eedc5d..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/navigation_msgs/CMakeLists.txt +++ /dev/null @@ -1,209 +0,0 @@ -cmake_minimum_required(VERSION 3.0.2) -project(navigation_msgs) - -## Compile as C++11, supported in ROS Kinetic and newer -# add_compile_options(-std=c++11) - -## Find catkin macros and libraries -## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) -## is used, also find other catkin packages -find_package(catkin REQUIRED COMPONENTS - actionlib_msgs - actionlib - geometry_msgs - roscpp - std_msgs -) - -## System dependencies are found with CMake's conventions -# find_package(Boost REQUIRED COMPONENTS system) - - -## Uncomment this if the package has a setup.py. This macro ensures -## modules and global scripts declared therein get installed -## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html -# catkin_python_setup() - -################################################ -## Declare ROS messages, services and actions ## -################################################ - -## To declare and build messages, services or actions from within this -## package, follow these steps: -## * Let MSG_DEP_SET be the set of packages whose message types you use in -## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...). -## * In the file package.xml: -## * add a build_depend tag for "message_generation" -## * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET -## * If MSG_DEP_SET isn't empty the following dependency has been pulled in -## but can be declared for certainty nonetheless: -## * add a exec_depend tag for "message_runtime" -## * In this file (CMakeLists.txt): -## * add "message_generation" and every package in MSG_DEP_SET to -## find_package(catkin REQUIRED COMPONENTS ...) -## * add "message_runtime" and every package in MSG_DEP_SET to -## catkin_package(CATKIN_DEPENDS ...) -## * uncomment the add_*_files sections below as needed -## and list every .msg/.srv/.action file to be processed -## * uncomment the generate_messages entry below -## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...) - -## Generate messages in the 'msg' folder -# add_message_files( -# FILES -# Message1.msg -# Message2.msg -# ) - -## Generate services in the 'srv' folder -# add_service_files( -# FILES -# Service1.srv -# Service2.srv -# ) - -## Generate actions in the 'action' folder -add_action_files( - FILES - NavMission.action -) - -## Generate added messages and services with any dependencies listed here -generate_messages( - DEPENDENCIES - actionlib_msgs - geometry_msgs - std_msgs -) - -################################################ -## Declare ROS dynamic reconfigure parameters ## -################################################ - -## To declare and build dynamic reconfigure parameters within this -## package, follow these steps: -## * In the file package.xml: -## * add a build_depend and a exec_depend tag for "dynamic_reconfigure" -## * In this file (CMakeLists.txt): -## * add "dynamic_reconfigure" to -## find_package(catkin REQUIRED COMPONENTS ...) -## * uncomment the "generate_dynamic_reconfigure_options" section below -## and list every .cfg file to be processed - -## Generate dynamic reconfigure parameters in the 'cfg' folder -# generate_dynamic_reconfigure_options( -# cfg/DynReconf1.cfg -# cfg/DynReconf2.cfg -# ) - -################################### -## catkin specific configuration ## -################################### -## The catkin_package macro generates cmake config files for your package -## Declare things to be passed to dependent projects -## INCLUDE_DIRS: uncomment this if your package contains header files -## LIBRARIES: libraries you create in this project that dependent projects also need -## CATKIN_DEPENDS: catkin_packages dependent projects also need -## DEPENDS: system dependencies of this project that dependent projects also need -catkin_package( -# INCLUDE_DIRS include -# LIBRARIES navigation_msgs - CATKIN_DEPENDS actionlib_msgs geometry_msgs roscpp std_msgs -# DEPENDS system_lib -) - -########### -## Build ## -########### - -## Specify additional locations of header files -## Your package locations should be listed before other locations -include_directories( - include - ${catkin_INCLUDE_DIRS} -) - -## Declare a C++ library -# add_library(${PROJECT_NAME} -# src/${PROJECT_NAME}/navigation_msgs.cpp -# ) - -## Add cmake target dependencies of the library -## as an example, code may need to be generated before libraries -## either from message generation or dynamic reconfigure -# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) - -## Declare a C++ executable -## With catkin_make all packages are built within a single CMake context -## The recommended prefix ensures that target names across packages don't collide -# add_executable(${PROJECT_NAME}_node src/navigation_msgs_node.cpp) - -## Rename C++ executable without prefix -## The above recommended prefix causes long target names, the following renames the -## target back to the shorter version for ease of user use -## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node" -# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "") - -## Add cmake target dependencies of the executable -## same as for the library above -# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) - -## Specify libraries to link a library or executable target against -# target_link_libraries(${PROJECT_NAME}_node -# ${catkin_LIBRARIES} -# ) - -############# -## Install ## -############# - -# all install targets should use catkin DESTINATION variables -# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html - -## Mark executable scripts (Python etc.) for installation -## in contrast to setup.py, you can choose the destination -# catkin_install_python(PROGRAMS -# scripts/my_python_script -# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} -# ) - -## Mark executables for installation -## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html -# install(TARGETS ${PROJECT_NAME}_node -# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} -# ) - -## Mark libraries for installation -## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html -# install(TARGETS ${PROJECT_NAME} -# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} -# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} -# RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION} -# ) - -## Mark cpp header files for installation -# install(DIRECTORY include/${PROJECT_NAME}/ -# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} -# FILES_MATCHING PATTERN "*.h" -# PATTERN ".svn" EXCLUDE -# ) - -## Mark other files for installation (e.g. launch and bag files, etc.) -# install(FILES -# # myfile1 -# # myfile2 -# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} -# ) - -############# -## Testing ## -############# - -## Add gtest based cpp test target and link libraries -# catkin_add_gtest(${PROJECT_NAME}-test test/test_navigation_msgs.cpp) -# if(TARGET ${PROJECT_NAME}-test) -# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}) -# endif() - -## Add folders to be run by python nosetests -# catkin_add_nosetests(test) diff --git a/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/navigation_msgs/action/NavMission.action b/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/navigation_msgs/action/NavMission.action deleted file mode 100644 index d4763cc..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/navigation_msgs/action/NavMission.action +++ /dev/null @@ -1,8 +0,0 @@ -# goal -geometry_msgs/TwistStamped nav_goal ---- -# result -std_msgs/Bool outcome ---- -# feedback -std_msgs/Char progress \ No newline at end of file diff --git a/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/navigation_msgs/package.xml b/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/navigation_msgs/package.xml deleted file mode 100644 index 9c3a0fb..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/navigation_msgs/package.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - navigation_msgs - 0.0.0 - The navigation_msgs package - - - - - assume - - - - - - Apache-2.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - catkin - actionlib_msgs - geometry_msgs - roscpp - std_msgs - actionlib_msgs - geometry_msgs - roscpp - std_msgs - actionlib_msgs - geometry_msgs - roscpp - std_msgs - - - - - - - - diff --git a/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/startup_msgs/CMakeLists.txt b/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/startup_msgs/CMakeLists.txt deleted file mode 100644 index 7f46bbe..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/startup_msgs/CMakeLists.txt +++ /dev/null @@ -1,211 +0,0 @@ -cmake_minimum_required(VERSION 3.0.2) -project(startup_msgs) - -## Compile as C++11, supported in ROS Kinetic and newer -# add_compile_options(-std=c++11) - -## Find catkin macros and libraries -## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) -## is used, also find other catkin packages -find_package(catkin REQUIRED COMPONENTS - geometry_msgs - nav_msgs - roscpp - rospy - tf2 - tf2_ros - message_generation -) - -## System dependencies are found with CMake's conventions -# find_package(Boost REQUIRED COMPONENTS system) - - -## Uncomment this if the package has a setup.py. This macro ensures -## modules and global scripts declared therein get installed -## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html -# catkin_python_setup() - -################################################ -## Declare ROS messages, services and actions ## -################################################ - -## To declare and build messages, services or actions from within this -## package, follow these steps: -## * Let MSG_DEP_SET be the set of packages whose message types you use in -## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...). -## * In the file package.xml: -## * add a build_depend tag for "message_generation" -## * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET -## * If MSG_DEP_SET isn't empty the following dependency has been pulled in -## but can be declared for certainty nonetheless: -## * add a exec_depend tag for "message_runtime" -## * In this file (CMakeLists.txt): -## * add "message_generation" and every package in MSG_DEP_SET to -## find_package(catkin REQUIRED COMPONENTS ...) -## * add "message_runtime" and every package in MSG_DEP_SET to -## catkin_package(CATKIN_DEPENDS ...) -## * uncomment the add_*_files sections below as needed -## and list every .msg/.srv/.action file to be processed -## * uncomment the generate_messages entry below -## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...) - -## Generate messages in the 'msg' folder -# add_message_files( -# FILES -# Message1.msg -# Message2.msg -# ) - -## Generate services in the 'srv' folder -add_service_files( - DIRECTORY srv - FILES - StartUpSrv.srv -) - -## Generate actions in the 'action' folder -# add_action_files( -# FILES -# Action1.action -# Action2.action -# ) - -## Generate added messages and services with any dependencies listed here -generate_messages( - DEPENDENCIES - geometry_msgs - std_msgs -) - -################################################ -## Declare ROS dynamic reconfigure parameters ## -################################################ - -## To declare and build dynamic reconfigure parameters within this -## package, follow these steps: -## * In the file package.xml: -## * add a build_depend and a exec_depend tag for "dynamic_reconfigure" -## * In this file (CMakeLists.txt): -## * add "dynamic_reconfigure" to -## find_package(catkin REQUIRED COMPONENTS ...) -## * uncomment the "generate_dynamic_reconfigure_options" section below -## and list every .cfg file to be processed - -## Generate dynamic reconfigure parameters in the 'cfg' folder -# generate_dynamic_reconfigure_options( -# cfg/DynReconf1.cfg -# cfg/DynReconf2.cfg -# ) - -################################### -## catkin specific configuration ## -################################### -## The catkin_package macro generates cmake config files for your package -## Declare things to be passed to dependent projects -## INCLUDE_DIRS: uncomment this if your package contains header files -## LIBRARIES: libraries you create in this project that dependent projects also need -## CATKIN_DEPENDS: catkin_packages dependent projects also need -## DEPENDS: system dependencies of this project that dependent projects also need -catkin_package( -# INCLUDE_DIRS include -# LIBRARIES startup_msgs -# DEPENDS system_lib - CATKIN_DEPENDS message_runtime -) - -########### -## Build ## -########### - -## Specify additional locations of header files -## Your package locations should be listed before other locations -include_directories( -# include - ${catkin_INCLUDE_DIRS} -) - -## Declare a C++ library -# add_library(${PROJECT_NAME} -# src/${PROJECT_NAME}/startup_msgs.cpp -# ) - -## Add cmake target dependencies of the library -## as an example, code may need to be generated before libraries -## either from message generation or dynamic reconfigure -# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) - -## Declare a C++ executable -## With catkin_make all packages are built within a single CMake context -## The recommended prefix ensures that target names across packages don't collide -# add_executable(${PROJECT_NAME}_node src/startup_msgs_node.cpp) - -## Rename C++ executable without prefix -## The above recommended prefix causes long target names, the following renames the -## target back to the shorter version for ease of user use -## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node" -# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "") - -## Add cmake target dependencies of the executable -## same as for the library above -# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) - -## Specify libraries to link a library or executable target against -# target_link_libraries(${PROJECT_NAME}_node -# ${catkin_LIBRARIES} -# ) - -############# -## Install ## -############# - -# all install targets should use catkin DESTINATION variables -# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html - -## Mark executable scripts (Python etc.) for installation -## in contrast to setup.py, you can choose the destination -# catkin_install_python(PROGRAMS -# scripts/my_python_script -# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} -# ) - -## Mark executables for installation -## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html -# install(TARGETS ${PROJECT_NAME}_node -# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} -# ) - -## Mark libraries for installation -## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html -# install(TARGETS ${PROJECT_NAME} -# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} -# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} -# RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION} -# ) - -## Mark cpp header files for installation -# install(DIRECTORY include/${PROJECT_NAME}/ -# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} -# FILES_MATCHING PATTERN "*.h" -# PATTERN ".svn" EXCLUDE -# ) - -## Mark other files for installation (e.g. launch and bag files, etc.) -# install(FILES -# # myfile1 -# # myfile2 -# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} -# ) - -############# -## Testing ## -############# - -## Add gtest based cpp test target and link libraries -# catkin_add_gtest(${PROJECT_NAME}-test test/test_startup_msgs.cpp) -# if(TARGET ${PROJECT_NAME}-test) -# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}) -# endif() - -## Add folders to be run by python nosetests -# catkin_add_nosetests(test) diff --git a/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/startup_msgs/package.xml b/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/startup_msgs/package.xml deleted file mode 100644 index d8ebf56..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/startup_msgs/package.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - startup_msgs - 0.0.0 - The startup_msgs package - - - - - assume - - - - - - Apache-2.0 - - - - - - - - - - - - - - - - - - - - - - - - message_generation - - - - - - message_runtime - - - - - catkin - geometry_msgs - nav_msgs - roscpp - rospy - tf2 - tf2_ros - geometry_msgs - nav_msgs - roscpp - rospy - tf2 - tf2_ros - geometry_msgs - nav_msgs - roscpp - rospy - tf2 - tf2_ros - - - - - - - - diff --git a/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/startup_msgs/srv/StartUpSrv.srv b/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/startup_msgs/srv/StartUpSrv.srv deleted file mode 100644 index 022d5e5..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/eurobot_msgs-master/startup_msgs/srv/StartUpSrv.srv +++ /dev/null @@ -1,3 +0,0 @@ -int64 group ---- -bool success \ No newline at end of file diff --git a/localization-ws-ros1/src/localization-devel-ws/usb.sh b/localization-ws-ros1/src/localization-devel-ws/usb.sh deleted file mode 100755 index b508089..0000000 --- a/localization-ws-ros1/src/localization-devel-ws/usb.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -# IMU port - -# List all USB devices and find the IMU device -IMU_DEVICE=$(lsusb | grep -i "06c2:008d") # Phidgets Inc. (formerly GLAB) USB2.1 Hub - -if [ -z "$IMU_DEVICE" ]; then - echo "IMU device not found." -else - # Extract the bus and device numbers - BUS=$(echo $IMU_DEVICE | awk '{print $2}') - DEVICE=$(echo $IMU_DEVICE | awk '{print $4}' | sed 's/://') - - # Construct the device path - DEVICE_PATH="/dev/bus/usb/$BUS/$DEVICE" - - # Check the current permissions of the device - echo "Current permissions for $DEVICE_PATH:" - ls -l $DEVICE_PATH - - # Change the permissions to read and write for all users - sudo chmod 666 $DEVICE_PATH - - # Verify the permissions have been changed - echo "Updated permissions for $DEVICE_PATH:" - ls -l $DEVICE_PATH - - # IMU udev rules - PACKAGES_PATH=/home/user/localization-ws-ros1/src - sudo /lib/systemd/systemd-udevd --daemon - cd $PACKAGES_PATH/phidgets_drivers/phidgets_api - sudo cp debian/udev /etc/udev/rules.d/99-phidgets.rules -fi - -# RPLiDAR port - -# Get the device name from dmesg -DEVICE_NAME=$(sudo dmesg | grep -oP 'cp210x converter now attached to \KttyUSB[0-9]+' | tail -1) - -# Validate the DEVICE_NAME to ensure it matches the expected pattern -if [[ "$DEVICE_NAME" =~ ^ttyUSB[0-9]+$ ]]; then - DEVICE_PATH="/dev/$DEVICE_NAME" - - # Change the permissions to read and write for all users - sudo chmod 666 $DEVICE_PATH - - # Verify the permissions have been changed - echo "Updated permissions for $DEVICE_PATH:" - ls -l $DEVICE_PATH -else - echo "Invalid device name: $DEVICE_NAME" -fi \ No newline at end of file diff --git a/simulation-ws/.gitignore b/simulation-ws/.gitignore deleted file mode 100644 index 7831216..0000000 --- a/simulation-ws/.gitignore +++ /dev/null @@ -1,55 +0,0 @@ -# Compiled source # -################### -*.com -*.class -*.dll -*.exe -*.o -*.so - -# Packages # -############ -*.7z -*.dmg -*.gz -*.iso -*.jar -*.rar -*.tar -*.zip - -# Logs and databases # -###################### -*.log -*.sql -*.sqlite - -# OS generated files # -###################### -.DS_Store -Thumbs.db - -# Python # -########## -*.pyc -*.pyo -__pycache__/ - -# Virtual environments # -######################## -venv/ -env/ - -# Node.js # -########### -node_modules/ - -# ROS # -####### -build/ -install/ -log/ - -# vscode # -########## -.vscode/ \ No newline at end of file