├── .clang-format ├── .clang-tidy ├── .devcontainer ├── nouveau │ └── devcontainer.json └── nvidia │ └── devcontainer.json ├── .docker ├── Dockerfile └── entrypoints │ ├── dev.sh │ └── sim.sh ├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yaml │ ├── config.yaml │ ├── documentation.yaml │ └── feature-request.yaml ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml ├── mergify.yml └── workflows │ ├── ci.yaml │ ├── docker.yaml │ └── format.yaml ├── .gitignore ├── .markdownlint.json ├── .pre-commit-config.yaml ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── alpha.repos ├── alpha_bringup ├── CMakeLists.txt ├── LICENSE ├── launch │ ├── alpha.launch.py │ └── planning.launch.py └── package.xml ├── alpha_description ├── CMakeLists.txt ├── LICENSE ├── config │ ├── alpha_controllers.yaml │ └── initial_positions.yaml ├── meshes │ ├── LICENSE │ ├── M2-1-1.stl │ ├── M2-1-3.stl │ ├── M2.stl │ ├── M3-INLINE.stl │ ├── RS1-100-101-123.stl │ └── end_effectors │ │ ├── RS1-124.stl │ │ ├── RS1-130.stl │ │ └── RS1-139.stl ├── moveit2 │ ├── alpha.config.srdf.xacro │ ├── alpha.srdf.xacro │ ├── joint_limits.yaml │ ├── kinematics.yaml │ ├── moveit_controllers.yaml │ ├── ompl_planning.yaml │ ├── pilz_cartesian_limits.yaml │ └── planning_pipelines.yaml ├── package.xml ├── rviz │ ├── moveit.rviz │ └── view_alpha.rviz └── xacro │ ├── LICENSE │ ├── alpha.config.xacro │ ├── alpha.gazebo.xacro │ ├── alpha.ros2_control.xacro │ ├── alpha.urdf.xacro │ ├── alpha_platform.config.xacro │ ├── alpha_platform.urdf.xacro │ └── end_effectors │ └── standard_jaws.urdf.xacro ├── alpha_driver ├── CMakeLists.txt ├── LICENSE ├── include │ └── alpha_driver │ │ ├── cobs.hpp │ │ ├── crc.hpp │ │ ├── device_id.hpp │ │ ├── driver.hpp │ │ ├── mode.hpp │ │ ├── packet.hpp │ │ ├── packet_id.hpp │ │ └── serial_client.hpp ├── package.xml ├── src │ ├── cobs.cpp │ ├── crc.cpp │ ├── driver.cpp │ ├── packet.cpp │ └── serial_client.cpp └── test │ ├── test_cobs.cpp │ ├── test_crc.cpp │ └── test_packet.cpp ├── alpha_hardware ├── CMakeLists.txt ├── LICENSE ├── alpha_hardware.xml ├── include │ └── alpha_hardware │ │ └── hardware.hpp ├── package.xml └── src │ └── hardware.cpp ├── requirements-dev.txt └── setup.cfg /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Google 4 | ColumnLimit: 100 5 | MaxEmptyLinesToKeep: 1 6 | 7 | IndentWidth: 2 8 | TabWidth: 2 9 | UseTab: Never 10 | AccessModifierOffset: -2 11 | AlignAfterOpenBracket: AlwaysBreak 12 | ConstructorInitializerIndentWidth: 0 13 | ContinuationIndentWidth: 2 14 | DerivePointerAlignment: false 15 | PointerAlignment: Middle 16 | PackConstructorInitializers: Never 17 | 18 | # Configure brace wrapping cases 19 | BreakBeforeBraces: Custom 20 | BraceWrapping: 21 | AfterClass: true 22 | AfterEnum: true 23 | AfterFunction: true 24 | AfterNamespace: true 25 | AfterStruct: true 26 | AfterUnion: true 27 | BeforeCatch: true 28 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: > 3 | -*, 4 | abseil-*, 5 | bugprone-*, 6 | google-*, 7 | misc-*, 8 | modernize-*, 9 | performance-*, 10 | portability-*, 11 | readability-*, 12 | -google-readability-braces-around-statements, 13 | -google-readability-namespace-comments, 14 | -google-runtime-references, 15 | -misc-non-private-member-variables-in-classes, 16 | -modernize-return-braced-init-list, 17 | -modernize-use-trailing-return-type, 18 | -performance-move-const-arg, 19 | -readability-braces-around-statements, 20 | -readability-identifier-length, 21 | -readability-magic-numbers, 22 | -readability-named-parameter, 23 | -readability-redundant-declaration, 24 | -readability-function-cognitive-complexity, 25 | -bugprone-narrowing-conversions, 26 | -bugprone-easily-swappable-parameters, 27 | -bugprone-implicit-widening-of-multiplication-result, 28 | -modernize-avoid-bind, 29 | -modernize-use-nodiscard 30 | WarningsAsErrors: "*" 31 | CheckOptions: 32 | - key: readability-braces-around-statements.ShortStatementLines 33 | value: "2" 34 | - key: readability-identifier-naming.NamespaceCase 35 | value: lower_case 36 | - key: readability-identifier-naming.ClassCase 37 | value: CamelCase 38 | - key: readability-identifier-naming.StructCase 39 | value: CamelCase 40 | - key: readability-identifier-naming.TemplateParameterCase, 41 | value: CamelCase, 42 | - key: readability-identifier-naming.FunctionCase 43 | value: camelBack 44 | - key: readability-identifier-naming.MethodCase 45 | value: camelBack 46 | - key: readability-identifier-naming.VariableCase 47 | value: lower_case 48 | - key: readability-identifier-naming.ClassMemberCase 49 | value: lower_case 50 | - key: readability-identifier-naming.ClassMemberSuffix 51 | value: _ 52 | - key: readability-identifier-naming.PrivateMemberSuffix 53 | value: _ 54 | - key: readability-identifier-naming.ProtectedMemberSuffix 55 | value: _ 56 | - key: readability-identifier-naming.EnumConstantCase 57 | value: CamelCase 58 | - key: readability-identifier-naming.EnumConstantPrefix 59 | value: k 60 | - key: readability-identifier-naming.ConstexprVariableCase 61 | value: CamelCase 62 | - key: readability-identifier-naming.ConstexprVariablePrefix 63 | value: k 64 | - key: readability-identifier-naming.GlobalConstantCase 65 | value: CamelCase 66 | - key: readability-identifier-naming.GlobalConstantPrefix 67 | value: k 68 | - key: readability-identifier-naming.MemberConstantCase 69 | value: CamelCase 70 | - key: readability-identifier-naming.MemberConstantPrefix 71 | value: k 72 | - key: readability-identifier-naming.StaticConstantCase 73 | value: CamelCase 74 | - key: readability-identifier-naming.StaticConstantPrefix 75 | value: k 76 | - key: readability-implicit-bool-conversion.AllowIntegerConditions 77 | value: 1 78 | - key: readability-implicit-bool-conversion.AllowPointerConditions 79 | value: 1 80 | - key: readability-function-cognitive-complexity.IgnoreMacros 81 | value: 1 82 | -------------------------------------------------------------------------------- /.devcontainer/nouveau/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "dockerFile": "../../.docker/Dockerfile", 3 | "context": "../..", 4 | "build": { 5 | "args": { 6 | "WORKSPACE": "${containerWorkspaceFolder}", 7 | "ROS_DISTRO": "rolling" 8 | }, 9 | "target": "develop" 10 | }, 11 | "remoteUser": "alpha", 12 | "runArgs": [ 13 | "--network=host", 14 | "--cap-add=SYS_PTRACE", 15 | "--security-opt=seccomp:unconfined", 16 | "--security-opt=apparmor:unconfined", 17 | "--volume=/dev:/dev", 18 | "--privileged", 19 | "--volume=/run/user/1000:/run/user/1000" 20 | ], 21 | "containerEnv": { 22 | "DISPLAY": "${localEnv:DISPLAY}", 23 | "WAYLAND_DISPLAY": "${localEnv:WAYLAND_DISPLAY}", 24 | "XDG_RUNTIME_DIR": "${localEnv:XDG_RUNTIME_DIR}", 25 | "PULSE_SERVER": "${localEnv:PULSE_SERVER}" 26 | }, 27 | "customizations": { 28 | "vscode": { 29 | "settings": { 30 | "files.associations": { 31 | "*.repos": "yaml", 32 | "*.world": "xml", 33 | "*.xacro": "xml", 34 | "*.srdf": "xml", 35 | "*.rviz": "yaml", 36 | "*.config": "xml", 37 | "*.sdf": "xml" 38 | }, 39 | "terminal.integrated.defaultProfile.linux": "bash", 40 | "files.insertFinalNewline": true, 41 | "files.trimTrailingWhitespace": true, 42 | "editor.formatOnSave": true, 43 | "editor.tabSize": 2, 44 | "xml.format.maxLineWidth": 100, 45 | "json.format.enable": true, 46 | "python.linting.enabled": true, 47 | "python.linting.flake8Enabled": true, 48 | "python.linting.pylintEnabled": false, 49 | "python.linting.pydocstyleEnabled": true, 50 | "python.linting.mypyEnabled": true, 51 | "python.formatting.provider": "black", 52 | "autoDocstring.startOnNewLine": false, 53 | "autoDocstring.docstringFormat": "google-notypes", 54 | "isort.args": ["--profile", "black"], 55 | "isort.check": true, 56 | "python.autoComplete.extraPaths": [ 57 | "/opt/ros/rolling/lib/python3.10/site-packages/", 58 | "/opt/ros/rolling/local/lib/python3.10/dist-packages/", 59 | "${workspaceFolder}/install/" 60 | ], 61 | "python.analysis.extraPaths": [ 62 | "/opt/ros/rolling/lib/python3.10/site-packages/", 63 | "/opt/ros/rolling/local/lib/python3.10/dist-packages/", 64 | "${workspaceFolder}/install/" 65 | ], 66 | "C_Cpp.default.intelliSenseMode": "linux-gcc-x86", 67 | "C_Cpp.clang_format_fallbackStyle": "Google", 68 | "C_Cpp.codeAnalysis.clangTidy.enabled": true, 69 | "C_Cpp.codeAnalysis.clangTidy.runAutomatically": true, 70 | "clang-format.executable": "/usr/bin/clang-format-14", 71 | "[cpp]": { 72 | "editor.rulers": [100], 73 | "editor.tabSize": 2, 74 | "editor.defaultFormatter": "xaver.clang-format" 75 | }, 76 | "[python]": { 77 | "editor.tabSize": 4, 78 | "editor.rulers": [90], 79 | "editor.codeActionsOnSave": { 80 | "source.organizeImports": true 81 | } 82 | }, 83 | "[dockerfile]": { 84 | "editor.quickSuggestions": { 85 | "strings": true 86 | }, 87 | "editor.defaultFormatter": "ms-azuretools.vscode-docker", 88 | "editor.tabSize": 4 89 | }, 90 | "[json]": { 91 | "editor.defaultFormatter": "esbenp.prettier-vscode" 92 | }, 93 | "[xml]": { 94 | "editor.defaultFormatter": "redhat.vscode-xml" 95 | }, 96 | "[markdown]": { 97 | "editor.rulers": [80], 98 | "editor.defaultFormatter": "DavidAnson.vscode-markdownlint" 99 | }, 100 | "search.exclude": { 101 | "**/build": true, 102 | "**/install": true, 103 | "**/log": true 104 | } 105 | }, 106 | "extensions": [ 107 | "ms-azuretools.vscode-docker", 108 | "ms-python.python", 109 | "njpwerner.autodocstring", 110 | "cschlosser.doxdocgen", 111 | "ms-vscode.cpptools", 112 | "redhat.vscode-xml", 113 | "redhat.vscode-yaml", 114 | "josetr.cmake-language-support-vscode", 115 | "smilerobotics.urdf", 116 | "DavidAnson.vscode-markdownlint", 117 | "esbenp.prettier-vscode", 118 | "xaver.clang-format", 119 | "ms-python.isort", 120 | "ms-python.flake8", 121 | "ms-python.black-formatter" 122 | ] 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /.devcontainer/nvidia/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "dockerFile": "../../.docker/Dockerfile", 3 | "context": "../..", 4 | "build": { 5 | "args": { 6 | "WORKSPACE": "${containerWorkspaceFolder}", 7 | "ROS_DISTRO": "rolling" 8 | }, 9 | "target": "develop-nvidia" 10 | }, 11 | "remoteUser": "alpha", 12 | "runArgs": [ 13 | "--network=host", 14 | "--cap-add=SYS_PTRACE", 15 | "--security-opt=seccomp:unconfined", 16 | "--security-opt=apparmor:unconfined", 17 | "--volume=/tmp/.X11-unix:/tmp/.X11-unix", 18 | "--volume=/mnt/wslg:/mnt/wslg", 19 | "--gpus=all" 20 | ], 21 | "containerEnv": { 22 | "DISPLAY": "${localEnv:DISPLAY}", 23 | "WAYLAND_DISPLAY": "${localEnv:WAYLAND_DISPLAY}", 24 | "XDG_RUNTIME_DIR": "${localEnv:XDG_RUNTIME_DIR}", 25 | "PULSE_SERVER": "${localEnv:PULSE_SERVER}", 26 | "LIBGL_ALWAYS_SOFTWARE": "1", 27 | "QT_X11_NO_MITSHM": "1" 28 | }, 29 | "customizations": { 30 | "vscode": { 31 | "settings": { 32 | "files.associations": { 33 | "*.repos": "yaml", 34 | "*.world": "xml", 35 | "*.xacro": "xml", 36 | "*.srdf": "xml", 37 | "*.rviz": "yaml", 38 | "*.config": "xml", 39 | "*.sdf": "xml" 40 | }, 41 | "terminal.integrated.defaultProfile.linux": "bash", 42 | "files.insertFinalNewline": true, 43 | "files.trimTrailingWhitespace": true, 44 | "editor.formatOnSave": true, 45 | "editor.tabSize": 2, 46 | "xml.format.maxLineWidth": 100, 47 | "json.format.enable": true, 48 | "python.linting.enabled": true, 49 | "python.linting.flake8Enabled": true, 50 | "python.linting.pylintEnabled": false, 51 | "python.linting.pydocstyleEnabled": true, 52 | "python.linting.mypyEnabled": true, 53 | "python.formatting.provider": "black", 54 | "autoDocstring.startOnNewLine": false, 55 | "autoDocstring.docstringFormat": "google-notypes", 56 | "isort.args": ["--profile", "black"], 57 | "isort.check": true, 58 | "python.autoComplete.extraPaths": [ 59 | "/opt/ros/rolling/lib/python3.10/site-packages/", 60 | "/opt/ros/rolling/local/lib/python3.10/dist-packages/", 61 | "${workspaceFolder}/install/" 62 | ], 63 | "python.analysis.extraPaths": [ 64 | "/opt/ros/rolling/lib/python3.10/site-packages/", 65 | "/opt/ros/rolling/local/lib/python3.10/dist-packages/", 66 | "${workspaceFolder}/install/" 67 | ], 68 | "C_Cpp.default.intelliSenseMode": "linux-gcc-x86", 69 | "C_Cpp.clang_format_fallbackStyle": "Google", 70 | "C_Cpp.codeAnalysis.clangTidy.enabled": true, 71 | "C_Cpp.codeAnalysis.clangTidy.runAutomatically": true, 72 | "clang-format.executable": "/usr/bin/clang-format-14", 73 | "[cpp]": { 74 | "editor.rulers": [100], 75 | "editor.tabSize": 2, 76 | "editor.defaultFormatter": "xaver.clang-format" 77 | }, 78 | "[python]": { 79 | "editor.tabSize": 4, 80 | "editor.rulers": [90], 81 | "editor.codeActionsOnSave": { 82 | "source.organizeImports": true 83 | } 84 | }, 85 | "[dockerfile]": { 86 | "editor.quickSuggestions": { 87 | "strings": true 88 | }, 89 | "editor.defaultFormatter": "ms-azuretools.vscode-docker", 90 | "editor.tabSize": 4 91 | }, 92 | "[json]": { 93 | "editor.defaultFormatter": "esbenp.prettier-vscode" 94 | }, 95 | "[xml]": { 96 | "editor.defaultFormatter": "redhat.vscode-xml" 97 | }, 98 | "[markdown]": { 99 | "editor.rulers": [80], 100 | "editor.defaultFormatter": "DavidAnson.vscode-markdownlint" 101 | }, 102 | "search.exclude": { 103 | "**/build": true, 104 | "**/install": true, 105 | "**/log": true 106 | } 107 | }, 108 | "extensions": [ 109 | "ms-azuretools.vscode-docker", 110 | "ms-python.python", 111 | "njpwerner.autodocstring", 112 | "cschlosser.doxdocgen", 113 | "ms-vscode.cpptools", 114 | "redhat.vscode-xml", 115 | "redhat.vscode-yaml", 116 | "josetr.cmake-language-support-vscode", 117 | "smilerobotics.urdf", 118 | "DavidAnson.vscode-markdownlint", 119 | "esbenp.prettier-vscode", 120 | "xaver.clang-format", 121 | "ms-python.isort", 122 | "ms-python.flake8", 123 | "ms-python.black-formatter" 124 | ] 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /.docker/Dockerfile: -------------------------------------------------------------------------------- 1 | ############################################## 2 | # ci: Install core dependencies needed for CI 3 | ############################################## 4 | ARG ROS_DISTRO=rolling 5 | FROM ros:$ROS_DISTRO-ros-base as ci 6 | 7 | LABEL maintainer="Evan Palmer" 8 | LABEL maintainer-email="evanp922@gmail.com" 9 | 10 | ENV DEBIAN_FRONTEND=noninteractive 11 | WORKDIR /root/ws_alpha 12 | 13 | COPY . src/alpha 14 | 15 | # Install apt packages 16 | RUN apt-get -q update \ 17 | && apt-get -q -y upgrade \ 18 | && apt-get -q install --no-install-recommends -y \ 19 | git \ 20 | wget \ 21 | curl \ 22 | sudo \ 23 | clang \ 24 | clang-format-14 \ 25 | clang-tidy \ 26 | clang-tools \ 27 | software-properties-common \ 28 | && apt-get autoremove -y \ 29 | && apt-get clean -y \ 30 | && rm -rf /var/lib/apt/lists/* 31 | 32 | # Install the alpha ROS dependencies 33 | RUN apt-get -q update \ 34 | && apt-get -q -y upgrade \ 35 | && rosdep update \ 36 | && rosdep install -y --from-paths src --ignore-src --rosdistro ${ROS_DISTRO} --as-root=apt:false \ 37 | && rm -rf src \ 38 | && apt-get autoremove -y \ 39 | && apt-get clean -y \ 40 | && rm -rf /var/lib/apt/lists/* 41 | 42 | # Configure a new non-root user 43 | ARG USERNAME=alpha 44 | ARG USER_UID=1000 45 | ARG USER_GID=$USER_UID 46 | 47 | RUN groupadd --gid $USER_GID $USERNAME \ 48 | && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \ 49 | && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ 50 | && chmod 0440 /etc/sudoers.d/$USERNAME \ 51 | && echo "source /usr/share/bash-completion/completions/git" >> /home/$USERNAME/.bashrc 52 | 53 | ################################################################# 54 | # sim: install Gazebo and the tools needed for Gazebo simulation 55 | ################################################################# 56 | FROM ci as sim 57 | 58 | # Install Gazebo Garden: https://gazebosim.org/docs/garden/install_ubuntu 59 | RUN wget https://packages.osrfoundation.org/gazebo.gpg -O /usr/share/keyrings/pkgs-osrf-archive-keyring.gpg 60 | RUN echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] http://packages.osrfoundation.org/gazebo/ubuntu-stable $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/gazebo-stable.list > /dev/null 61 | RUN apt-get update \ 62 | && apt-get -y --quiet --no-install-recommends install \ 63 | gz-garden \ 64 | && apt-get autoremove -y \ 65 | && apt-get clean -y \ 66 | && rm -rf /var/lib/apt/lists/* 67 | 68 | # Extend the ros_entrypoint to source the simulation environment 69 | COPY .docker/entrypoints/sim.sh / 70 | RUN sed -i '/source "\/opt\/ros\/$ROS_DISTRO\/setup\.bash" --/a source /sim.sh' /ros_entrypoint.sh 71 | 72 | ################################################## 73 | # deps: Install all external project dependencies 74 | ################################################## 75 | FROM sim as deps 76 | 77 | ENV GZ_VERSION=garden 78 | ENV USER_WORKSPACE=/home/$USERNAME/ws_alpha/install 79 | ENV DEBIAN_FRONTEND=noninteractive 80 | WORKDIR $USER_WORKSPACE/.. 81 | 82 | COPY alpha.repos src/ 83 | 84 | RUN apt-get -q update \ 85 | && apt-get -q -y upgrade \ 86 | && vcs import src < src/alpha.repos \ 87 | && rosdep update \ 88 | && rosdep install -y --from-paths src --ignore-src --rosdistro ${ROS_DISTRO} --as-root=apt:false --skip-keys="gz-transport12 gz-sim7 gz-math7 gz-msgs9 gz-plugin2" \ 89 | && apt-get autoremove -y \ 90 | && apt-get clean -y \ 91 | && rm -rf /var/lib/apt/lists/* 92 | 93 | RUN . "/opt/ros/${ROS_DISTRO}/setup.sh" \ 94 | && colcon build \ 95 | # Update /ros_entrypoint.sh to source the workspace 96 | && sed -i "s#/opt/ros/\$ROS_DISTRO/setup.bash#$USER_WORKSPACE/setup.sh#g" /ros_entrypoint.sh \ 97 | && echo "source ${USER_WORKSPACE}/setup.sh" >> /home/$USERNAME/.bashrc 98 | 99 | #################################################### 100 | # develop: Setup the image for development purposes 101 | #################################################### 102 | FROM deps as develop 103 | 104 | ENV GZ_VERSION=garden 105 | ENV ROS_UNDERLAY /root/ws_alpha/install 106 | ENV DEBIAN_FRONTEND=noninteractive 107 | WORKDIR $ROS_UNDERLAY/.. 108 | 109 | COPY . src/alpha 110 | 111 | # Install development tools 112 | RUN apt-get -q update \ 113 | && apt-get -q -y upgrade \ 114 | && apt-get -q install --no-install-recommends -y \ 115 | iputils-ping \ 116 | net-tools \ 117 | gdb \ 118 | nano \ 119 | htop \ 120 | python3-pip \ 121 | python3-dev \ 122 | && apt-get autoremove -y \ 123 | && apt-get clean -y \ 124 | && rm -rf /var/lib/apt/lists/* 125 | 126 | # Install debugging/linting Python packages 127 | RUN python3 -m pip install -r $(pwd)/src/alpha/requirements-dev.txt 128 | 129 | RUN apt-get -q update \ 130 | && apt-get -q -y upgrade \ 131 | && vcs import src < src/alpha/alpha.repos \ 132 | && rosdep update \ 133 | && rosdep install -y --from-paths src --ignore-src --rosdistro ${ROS_DISTRO} --as-root=apt:false --skip-keys="gz-transport12 gz-sim7 gz-math7 gz-msgs9 gz-plugin2" \ 134 | && rm -rf src \ 135 | && apt-get autoremove -y \ 136 | && apt-get clean -y \ 137 | && rm -rf /var/lib/apt/lists/* 138 | 139 | COPY .docker/entrypoints/dev.sh / 140 | 141 | # WARNING: This is a temporary solution for disabling the setuputils installation warning 142 | ENV PYTHONWARNINGS="ignore" 143 | 144 | ARG WORKSPACE 145 | RUN echo "if [ -f ${WORKSPACE}/install/setup.bash ]; then source ${WORKSPACE}/install/setup.bash; fi" >> /home/$USERNAME/.bashrc \ 146 | && echo "if [ -f /opt/ros/${ROS_DISTRO}/setup.bash ]; then source /opt/ros/${ROS_DISTRO}/setup.bash; fi" >> /home/$USERNAME/.bashrc \ 147 | # Expose the environment variables to the non-root user 148 | && echo "if [ -f /sim.sh ]; then source /sim.sh; fi" >> /home/$USERNAME/.bashrc \ 149 | && echo "if [ -f /dev.sh ]; then source /dev.sh; fi" >> /home/$USERNAME/.bashrc 150 | 151 | ###################################################################################### 152 | # develop-nvidia: Setup the image for development purposes with NVIDIA driver support 153 | ###################################################################################### 154 | FROM deps as develop-nvidia 155 | 156 | # Install NVIDIA software 157 | RUN apt-get update \ 158 | && apt-get install -y -qq --no-install-recommends \ 159 | libglvnd0 \ 160 | libgl1 \ 161 | libglx0 \ 162 | libegl1 \ 163 | libxext6 \ 164 | libx11-6 165 | 166 | # Env vars for the nvidia-container-runtime. 167 | ENV NVIDIA_VISIBLE_DEVICES all 168 | ENV NVIDIA_DRIVER_CAPABILITIES graphics,utility,compute 169 | ENV QT_X11_NO_MITSHM 1 170 | 171 | ENV GZ_VERSION=garden 172 | ENV ROS_UNDERLAY /root/ws_alpha/install 173 | ENV DEBIAN_FRONTEND=noninteractive 174 | WORKDIR $ROS_UNDERLAY/.. 175 | 176 | COPY . src/alpha 177 | 178 | # Install development tools 179 | RUN apt-get -q update \ 180 | && apt-get -q -y upgrade \ 181 | && apt-get -q install --no-install-recommends -y \ 182 | iputils-ping \ 183 | net-tools \ 184 | gdb \ 185 | nano \ 186 | htop \ 187 | python3-pip \ 188 | python3-dev \ 189 | && apt-get autoremove -y \ 190 | && apt-get clean -y \ 191 | && rm -rf /var/lib/apt/lists/* 192 | 193 | # Install debugging/linting Python packages 194 | RUN python3 -m pip install -r $(pwd)/src/alpha/requirements-dev.txt 195 | 196 | RUN apt-get -q update \ 197 | && apt-get -q -y upgrade \ 198 | && vcs import src < src/alpha/alpha.repos \ 199 | && rosdep update \ 200 | && rosdep install -y --from-paths src --ignore-src --rosdistro ${ROS_DISTRO} --as-root=apt:false --skip-keys="gz-transport12 gz-sim7 gz-math7 gz-msgs9 gz-plugin2" \ 201 | && rm -rf src \ 202 | && apt-get autoremove -y \ 203 | && apt-get clean -y \ 204 | && rm -rf /var/lib/apt/lists/* 205 | 206 | COPY .docker/entrypoints/dev.sh / 207 | 208 | # WARNING: This is a temporary solution for disabling the setuputils installation warning 209 | ENV PYTHONWARNINGS="ignore" 210 | 211 | ARG WORKSPACE 212 | RUN echo "if [ -f ${WORKSPACE}/install/setup.bash ]; then source ${WORKSPACE}/install/setup.bash; fi" >> /home/$USERNAME/.bashrc \ 213 | && echo "if [ -f /opt/ros/${ROS_DISTRO}/setup.bash ]; then source /opt/ros/${ROS_DISTRO}/setup.bash; fi" >> /home/$USERNAME/.bashrc \ 214 | # Expose the environment variables to the non-root user 215 | && echo "if [ -f /sim.sh ]; then source /sim.sh; fi" >> /home/$USERNAME/.bashrc \ 216 | && echo "if [ -f /dev.sh ]; then source /dev.sh; fi" >> /home/$USERNAME/.bashrc 217 | -------------------------------------------------------------------------------- /.docker/entrypoints/dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export GZ_SIM_RESOURCE_PATH=/workspaces/alpha/alpha_description/meshes:/workspaces/alpha/alpha_description/gazebo/worlds:$GZ_SIM_RESOURCE_PATH 4 | -------------------------------------------------------------------------------- /.docker/entrypoints/sim.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Build ros_gz for Gazebo Garden 4 | export GZ_VERSION=garden 5 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # ignore everything 2 | * 3 | 4 | # except the following 5 | !alpha_bringup 6 | !alpha_description 7 | !alpha_driver 8 | !alpha_hardware 9 | !alpha.repos 10 | !requirements-dev.txt 11 | !.docker/entrypoints 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report a bug. 3 | title: "[BUG]: " 4 | labels: [bug, needs triage] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: > 10 | Thank you for taking the time to file a bug report! Before creating a new 11 | issue, please make sure to take a few minutes to check the issue tracker 12 | for existing issues about the bug. 13 | 14 | - type: textarea 15 | attributes: 16 | label: Issue Description 17 | description: > 18 | Please provide a clear and concise description of what the bug is. 19 | validations: 20 | required: true 21 | 22 | - type: textarea 23 | attributes: 24 | label: Steps to Reproduce 25 | description: > 26 | Please provide the steps that should be taken to reproduce the bug. 27 | validations: 28 | required: true 29 | 30 | - type: textarea 31 | attributes: 32 | label: Expected Behavior 33 | description: > 34 | Please describe or show an example of the expected behavior. 35 | validations: 36 | required: true 37 | 38 | - type: textarea 39 | attributes: 40 | label: Error Message 41 | description: > 42 | Please include the full error message, if any. 43 | placeholder: > 44 | << Full traceback starting from `Traceback: ...` >> 45 | render: bash 46 | 47 | - type: textarea 48 | attributes: 49 | label: Runtime Environment 50 | description: > 51 | Please provide a description of the environment in which the error 52 | occurred. 53 | placeholder: > 54 | Raspberry Pi 4 running Ubuntu 22.04 natively. 55 | validations: 56 | required: true 57 | 58 | - type: textarea 59 | attributes: 60 | label: Additional Context 61 | description: > 62 | Please provide any additional context needed to understand the bug. 63 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yaml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Alpha Driver Community Support 4 | url: https://github.com/evan-palmer/alpha/discussions 5 | about: Please check out our discussions channel for any questions related to the project! 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.yaml: -------------------------------------------------------------------------------- 1 | name: Documentation Improvement 2 | description: Report an issue related to the Alpha driver documentation. 3 | title: "[DOC]: " 4 | labels: [documentation, needs triage] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: > 10 | Thank you for taking the time to report a documentation issue! Before creating 11 | a new issue, please make sure to take a few minutes to check the issue 12 | tracker for existing issues similar to that being reported. 13 | 14 | - type: dropdown 15 | attributes: 16 | label: Documentation Change Type 17 | description: Please indicate what type of documentation issue you are reporting. 18 | options: 19 | - Adding new documentation to the Alpha driver documentation 20 | - Changing existing Alpha driver documentation 21 | - Removing existing Alpha driver documentation 22 | validations: 23 | required: true 24 | 25 | - type: textarea 26 | attributes: 27 | label: Documentation Location 28 | description: > 29 | Please provide the location of the documentation that should be modified. 30 | 31 | - type: textarea 32 | attributes: 33 | label: Documentation Problem 34 | description: > 35 | Please provide a description of how the documentation needs to be improved. 36 | validations: 37 | required: true 38 | 39 | - type: textarea 40 | attributes: 41 | label: Suggested Change 42 | description: > 43 | Please provide a description of the proposed change and why the proposed change 44 | improves the upon the existing documentation. 45 | validations: 46 | required: true 47 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yaml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest a new idea for the project. 3 | title: "[FEATURE]: " 4 | labels: [enhancement, needs triage] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: > 10 | Thank you for taking the time to request a new feature! Before creating 11 | a new issue, please make sure to take a few minutes to check the issue 12 | tracker for existing issues similar to the proposed feature. 13 | 14 | - type: dropdown 15 | attributes: 16 | label: Feature Type 17 | description: Please indicate what type of feature request you would like to propose. 18 | options: 19 | - Adding new functionality to the Alpha driver 20 | - Changing existing functionality in the Alpha driver 21 | - Removing existing functionality in the Alpha driver 22 | validations: 23 | required: true 24 | 25 | - type: textarea 26 | attributes: 27 | label: Problem Description 28 | description: > 29 | Please provide a clear and concise description of what problem 30 | the feature would solve. 31 | validations: 32 | required: true 33 | 34 | - type: textarea 35 | attributes: 36 | label: Feature Description 37 | description: > 38 | Please provide a description of the proposed feature, using pseudocode 39 | if relevant. 40 | validations: 41 | required: true 42 | 43 | - type: textarea 44 | attributes: 45 | label: Alternative Solutions 46 | description: > 47 | Please provide a description of any alternative solutions or features 48 | that would satisfy the feature request. 49 | validations: 50 | required: true 51 | 52 | - type: textarea 53 | attributes: 54 | label: Additional Context 55 | description: > 56 | Please provide any additional context (e.g., relevant GitHub issues, 57 | code examples, or references) needed to understand the feature request. 58 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Checklist 2 | 3 | - [ ] I have performed a thorough review of my code 4 | - [ ] I have sufficiently commented my code 5 | - [ ] The implementation follows the project style conventions 6 | - [ ] All project unit tests are passing 7 | - [ ] If relevant, documentation has been provided or updated to discuss the changes made 8 | - [ ] System integration tests were performed successfully 9 | 10 | ## Changes Made 11 | 12 | Please provide a description of all changes made in this PR and why the changes 13 | are needed. 14 | 15 | ## Associated Issues 16 | 17 | Please provide a list of all open issues that this PR will close or contribute 18 | toward closing. 19 | 20 | - Fixes # (issue) 21 | 22 | ## Testing 23 | 24 | Please provide a clear and concise description of the testing performed. 25 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/mergify.yml: -------------------------------------------------------------------------------- 1 | 2 | pull_request_rules: 3 | - name: backport to iron at reviewers discretion 4 | conditions: 5 | - base=main 6 | - "label=backport-iron" 7 | actions: 8 | backport: 9 | branches: 10 | - iron 11 | 12 | - name: backport to humble at reviewers discretion 13 | conditions: 14 | - base=main 15 | - "label=backport-humble" 16 | actions: 17 | backport: 18 | branches: 19 | - humble 20 | 21 | - name: ask to resolve conflict 22 | conditions: 23 | - conflict 24 | - author!=mergify[bot] 25 | actions: 26 | comment: 27 | message: This pull request is in conflict. Could you fix it @{{author}}? 28 | 29 | - name: development targets main branch 30 | conditions: 31 | - base!=main 32 | - author!=mergify[bot] 33 | actions: 34 | comment: 35 | message: | 36 | Please target the `main` branch for development, we will backport the changes to {{base}} for you if approved and if they don't break the API. 37 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | env: 10 | CLANG_TIDY: true 11 | 12 | jobs: 13 | test: 14 | name: Test Implementation 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | env: 21 | - IMAGE: rolling-ci 22 | ROS_DISTRO: rolling 23 | - IMAGE: humble-ci 24 | ROS_DISTRO: humble 25 | - IMAGE: iron-ci 26 | ROS_DISTRO: iron 27 | steps: 28 | - name: Checkout repository 29 | uses: actions/checkout@v4 30 | with: 31 | submodules: recursive 32 | 33 | - name: Log into registry 34 | uses: docker/login-action@v3.0.0 35 | with: 36 | registry: ghcr.io 37 | username: ${{ github.actor }} 38 | password: ${{ secrets.GITHUB_TOKEN }} 39 | 40 | - name: Run ROS Industrial CI 41 | uses: ros-industrial/industrial_ci@master 42 | env: 43 | DOCKER_IMAGE: ghcr.io/evan-palmer/alpha:${{ matrix.env.IMAGE }} 44 | CXXFLAGS: >- 45 | -Wall -Wextra -Wpedantic -Wwrite-strings -Wunreachable-code -Wpointer-arith -Wredundant-decls 46 | CC: ${{ env.CLANG_TIDY && 'clang' }} 47 | CXX: ${{ env.CLANG_TIDY && 'clang++' }} 48 | -------------------------------------------------------------------------------- /.github/workflows/docker.yaml: -------------------------------------------------------------------------------- 1 | name: Docker 2 | 3 | on: 4 | schedule: 5 | - cron: "0 17 * * 6" 6 | push: 7 | branches: 8 | - main 9 | pull_request: 10 | paths: 11 | - .docker/** 12 | - .github/workflows/docker.yaml 13 | workflow_dispatch: 14 | 15 | env: 16 | PUSH: ${{ (github.event_name != 'pull_request') && (github.repository == 'evan-palmer/alpha') }} 17 | 18 | jobs: 19 | ci: 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | ROS_DISTRO: [humble, rolling, iron] 24 | runs-on: ubuntu-latest 25 | permissions: 26 | packages: write 27 | contents: read 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v4 31 | 32 | - name: Log into registry 33 | if: env.PUSH == 'true' 34 | uses: docker/login-action@v3.0.0 35 | with: 36 | registry: ghcr.io 37 | username: ${{ github.actor }} 38 | password: ${{ secrets.GITHUB_TOKEN }} 39 | 40 | - name: Extract Docker metadata 41 | if: env.PUSH == 'true' 42 | id: meta 43 | uses: docker/metadata-action@v5.5.1 44 | with: 45 | images: ghcr.io/${{ github.repository }} 46 | tags: | 47 | type=raw,value=${{ matrix.ROS_DISTRO }}-${{ github.job }} 48 | 49 | - name: Build and push Docker image 50 | uses: docker/build-push-action@v5.1.0 51 | with: 52 | context: . 53 | file: .docker/Dockerfile 54 | build-args: ROS_DISTRO=${{ matrix.ROS_DISTRO }} 55 | target: ${{ github.job }} 56 | tags: ${{ steps.meta.outputs.tags }} 57 | labels: ${{ steps.meta.outputs.labels }} 58 | push: ${{ env.PUSH }} 59 | -------------------------------------------------------------------------------- /.github/workflows/format.yaml: -------------------------------------------------------------------------------- 1 | name: Formatting (pre-commit) 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | workflow_dispatch: 9 | 10 | jobs: 11 | pre-commit: 12 | name: Format 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v4 17 | 18 | - name: Setup Python 19 | uses: actions/setup-python@v5 20 | with: 21 | python-version: "3.10" 22 | 23 | - name: Install clang-format-14 24 | run: sudo apt-get install clang-format-14 25 | 26 | - name: Run pre-commit 27 | uses: pre-commit/action@v3.0.1 28 | id: precommit 29 | 30 | - name: Upload pre-commit changes 31 | if: failure() && steps.precommit.outcome == 'failure' 32 | uses: rhaschke/upload-git-patch-action@main 33 | with: 34 | name: pre-commit 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # ROS 35 | build/ 36 | install/ 37 | log/ 38 | 39 | # VSCode 40 | .vscode/ 41 | 42 | # python 43 | .mypy_cache/ 44 | __pycache__ 45 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "MD026": false, 3 | "MD013": false 4 | } 5 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/psf/black 3 | rev: 23.1.0 4 | hooks: 5 | - id: black 6 | 7 | - repo: https://github.com/timothycrosley/isort 8 | rev: 5.12.0 9 | hooks: 10 | - id: isort 11 | 12 | - repo: https://github.com/codespell-project/codespell 13 | rev: v2.2.4 14 | hooks: 15 | - id: codespell 16 | args: ["--write-changes"] 17 | 18 | - repo: https://github.com/igorshubovych/markdownlint-cli 19 | rev: v0.33.0 20 | hooks: 21 | - id: markdownlint 22 | args: 23 | - --fix 24 | 25 | - repo: local 26 | hooks: 27 | - id: clang-format 28 | name: clang-format 29 | entry: clang-format-14 30 | language: system 31 | files: \.(c|cc|cxx|cpp|h|hpp|hxx|)$ 32 | args: ["-fallback-style=Google", "-i"] 33 | 34 | - repo: https://github.com/pre-commit/pre-commit-hooks 35 | rev: v4.4.0 36 | hooks: 37 | - id: check-added-large-files 38 | - id: check-case-conflict 39 | - id: check-json 40 | - id: check-toml 41 | - id: check-yaml 42 | - id: check-xml 43 | - id: check-merge-conflict 44 | - id: check-symlinks 45 | - id: debug-statements 46 | - id: destroyed-symlinks 47 | - id: detect-private-key 48 | - id: end-of-file-fixer 49 | - id: mixed-line-ending 50 | - id: trailing-whitespace 51 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | evanp922@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | . 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | . Translations are available at 128 | . 129 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Evan Palmer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reach Alpha 5 ROS 2 Driver :mechanical_arm: 2 | 3 | The Alpha 5 driver is a collection of ROS 2 packages designed 4 | to support ROS 2 integration with the [Reach Alpha 5 manipulator](https://reachrobotics.com/products/manipulators/reach-alpha/). 5 | 6 | ## Deprecation Notice 7 | 8 | We have deprecated this repository in favor of our [reach](https://github.com/Robotic-Decision-Making-Lab/reach) 9 | repository, which integrates additional features and support for the 10 | Reach Bravo 7 manipulator. 11 | 12 | ## Main features 13 | 14 | The main features of the Alpha 5 driver are: 15 | 16 | - Integration of the [Reach System Serial Protocol](https://reach-robotics.github.io/reach_robotics_sdk/documentation/index.html#) 17 | for hardware communication 18 | - ros2_control integration for manipulator position, velocity, and joint 19 | trajectory control 20 | - Simulation support using Gazebo Garden 21 | - Visualization support using RViz2 22 | - Integration with MoveIt2 for motion planning 23 | 24 | ## Installation 25 | 26 | The Alpha 5 driver is currently supported on Linux, and is available for the ROS 27 | distributions Humble and Rolling. To install the Alpha 5 driver, first clone 28 | this project to the `src` directory of your ROS workspace, replacing 29 | `$ROS_DISTRO` with the desired ROS distribution or `main` for Rolling: 30 | 31 | ```bash 32 | git clone -b $ROS_DISTRO git@github.com:evan-palmer/alpha.git 33 | ``` 34 | 35 | After cloning the project, install the ROS dependencies using `rosdep`, again, 36 | replacing `$ROS_DISTRO` with the desired ROS distribution: 37 | 38 | ```bash 39 | rosdep update && \ 40 | rosdep install -y --from-paths src --ignore-src --rosdistro $ROS_DISTRO 41 | ``` 42 | 43 | ## Quick start 44 | 45 | A ROS 2 launch file has been provided to start the Alpha 5 driver. To launch the 46 | driver using default arguments, run 47 | 48 | ```bash 49 | ros2 launch alpha_bringup alpha.launch.py 50 | ``` 51 | 52 | A full description of the launch arguments and their respective default values 53 | can be obtained by running the following command: 54 | 55 | ```bash 56 | ros2 launch alpha_bringup alpha.launch.py --show-args 57 | ``` 58 | 59 | ## Getting help 60 | 61 | If you have questions regarding usage of the Alpha 5 driver or regarding 62 | contributing to this project, please ask a question on our 63 | [Discussions](https://github.com/evan-palmer/alpha/discussions) board! 64 | 65 | ## Disclaimer 66 | 67 | This is an independent project, and is not affiliated with or maintained by 68 | Reach Robotics. Please refer to the [Reach Robotics SDK](https://github.com/Reach-Robotics/reach_robotics_sdk/tree/master) 69 | for all official software. 70 | 71 | ## License 72 | 73 | Any proprietary documents or software owned by Reach Robotics and used within 74 | this project are made available with official permission from Reach 75 | Robotics and have been documented accordingly. All other software is is released 76 | under the MIT license. 77 | -------------------------------------------------------------------------------- /alpha.repos: -------------------------------------------------------------------------------- 1 | repositories: 2 | ros_gz: 3 | type: git 4 | url: https://github.com/gazebosim/ros_gz 5 | version: ros2 6 | 7 | gz-plugin: 8 | type: git 9 | url: https://github.com/gazebosim/gz-plugin.git 10 | version: gz-plugin2 11 | 12 | gz_ros2_control: 13 | type: git 14 | url: https://github.com/evan-palmer/gz_ros2_control.git 15 | version: patch-rolling-garden 16 | -------------------------------------------------------------------------------- /alpha_bringup/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(alpha_bringup) 3 | 4 | set(THIS_PACKAGE_INCLUDE_DEPENDS 5 | rclcpp 6 | rclpy 7 | ament_cmake 8 | ) 9 | 10 | foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS}) 11 | find_package(${Dependency} REQUIRED) 12 | endforeach() 13 | 14 | install( 15 | DIRECTORY launch 16 | DESTINATION share/${PROJECT_NAME} 17 | ) 18 | 19 | ament_package() 20 | -------------------------------------------------------------------------------- /alpha_bringup/LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in 9 | all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 14 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 17 | THE SOFTWARE. 18 | -------------------------------------------------------------------------------- /alpha_bringup/launch/planning.launch.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023, Evan Palmer 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | # THE SOFTWARE. 20 | 21 | from launch import LaunchDescription 22 | from launch.actions import DeclareLaunchArgument 23 | from launch.conditions import IfCondition 24 | from launch.substitutions import ( 25 | Command, 26 | FindExecutable, 27 | LaunchConfiguration, 28 | PathJoinSubstitution, 29 | ) 30 | from launch_ros.actions import Node 31 | from launch_ros.substitutions import FindPackageShare 32 | 33 | 34 | def generate_launch_description() -> LaunchDescription: 35 | """ 36 | Generate a launch description to run MoveIt with the Reach Alpha 5 manipulator. 37 | 38 | Returns: 39 | LaunchDescription: ROS 2 launch description 40 | """ 41 | # Declare arguments 42 | args = [ 43 | DeclareLaunchArgument( 44 | "description_package", 45 | default_value="alpha_description", 46 | description=( 47 | "The description package with the Alpha URDF files. This is typically" 48 | " not set, but is available in case another description package has" 49 | " been defined." 50 | ), 51 | ), 52 | DeclareLaunchArgument( 53 | "description_file", 54 | default_value="alpha.config.xacro", 55 | description="The URDF/XACRO description file with the Alpha.", 56 | ), 57 | DeclareLaunchArgument( 58 | "prefix", 59 | default_value="", 60 | description=( 61 | "The prefix of the joint names. This is useful for multi-robot setups." 62 | " If the prefix is changed, then the joint names in the controller" 63 | " configuration must be updated. Expected format '/'." 64 | ), 65 | ), 66 | DeclareLaunchArgument( 67 | "namespace", 68 | default_value="", 69 | description=( 70 | "The namespace of the launched nodes. This is useful for multi-robot" 71 | " setups. If the namespace is changed, then the namespace in the" 72 | " controller configuration must be updated. Expected format '/'." 73 | ), 74 | ), 75 | DeclareLaunchArgument( 76 | "use_rviz", 77 | default_value="true", 78 | description="Automatically start RViz2.", 79 | ), 80 | DeclareLaunchArgument( 81 | "use_sim", 82 | default_value="false", 83 | description="Start the Gazebo simulation.", 84 | ), 85 | ] 86 | 87 | # Initialize Arguments 88 | description_package = LaunchConfiguration("description_package") 89 | description_file = LaunchConfiguration("description_file") 90 | use_rviz = LaunchConfiguration("use_rviz") 91 | use_sim = LaunchConfiguration("use_sim") 92 | prefix = LaunchConfiguration("prefix") 93 | namespace = LaunchConfiguration("namespace") 94 | 95 | # Get the robot description using xacro 96 | robot_description = { 97 | "robot_description": Command( 98 | [ 99 | PathJoinSubstitution([FindExecutable(name="xacro")]), 100 | " ", 101 | PathJoinSubstitution( 102 | [FindPackageShare(description_package), "xacro", description_file] 103 | ), 104 | " ", 105 | "prefix:=", 106 | prefix, 107 | ] 108 | ) 109 | } 110 | 111 | # Get the robot semantic description using xacro 112 | robot_description_semantic = { 113 | "robot_description_semantic": Command( 114 | [ 115 | PathJoinSubstitution([FindExecutable(name="xacro")]), 116 | " ", 117 | PathJoinSubstitution( 118 | [ 119 | FindPackageShare(description_package), 120 | "moveit2", 121 | "alpha.config.srdf.xacro", 122 | ] 123 | ), 124 | " ", 125 | "prefix:=", 126 | prefix, 127 | " ", 128 | "description_package:=", 129 | description_package, 130 | " ", 131 | "namespace:=", 132 | namespace, 133 | ] 134 | ) 135 | } 136 | 137 | # Load the moveit configuration files 138 | robot_description_planning_joint_limits = PathJoinSubstitution( 139 | [ 140 | FindPackageShare(description_package), 141 | "moveit2", 142 | "joint_limits.yaml", 143 | ] 144 | ) 145 | 146 | robot_description_planning_cartesian_limits = PathJoinSubstitution( 147 | [ 148 | FindPackageShare(description_package), 149 | "moveit2", 150 | "pilz_cartesian_limits.yaml", 151 | ] 152 | ) 153 | 154 | robot_description_kinematics = PathJoinSubstitution( 155 | [FindPackageShare(description_package), "moveit2", "kinematics.yaml"] 156 | ) 157 | 158 | planning_pipelines_config = PathJoinSubstitution( 159 | [ 160 | FindPackageShare(description_package), 161 | "moveit2", 162 | "planning_pipelines.yaml", 163 | ] 164 | ) 165 | 166 | ompl_planning_config = PathJoinSubstitution( 167 | [ 168 | FindPackageShare(description_package), 169 | "moveit2", 170 | "ompl_planning.yaml", 171 | ] 172 | ) 173 | 174 | moveit_controllers = PathJoinSubstitution( 175 | [ 176 | FindPackageShare(description_package), 177 | "moveit2", 178 | "moveit_controllers.yaml", 179 | ] 180 | ) 181 | 182 | # Configure MoveIt2 settings 183 | trajectory_execution = { 184 | "moveit_manage_controllers": True, 185 | "trajectory_execution.allowed_execution_duration_scaling": 1.2, 186 | "trajectory_execution.allowed_goal_duration_margin": 0.5, 187 | "trajectory_execution.allowed_start_tolerance": 0.01, 188 | } 189 | 190 | planning_scene_monitor_parameters = { 191 | "publish_planning_scene": True, 192 | "publish_geometry_updates": True, 193 | "publish_state_updates": True, 194 | "publish_transforms_updates": True, 195 | } 196 | 197 | # Declare ROS 2 nodes 198 | move_group_node = Node( 199 | package="moveit_ros_move_group", 200 | executable="move_group", 201 | output="screen", 202 | namespace=namespace, 203 | parameters=[ 204 | robot_description, 205 | robot_description_semantic, 206 | robot_description_kinematics, 207 | planning_pipelines_config, 208 | trajectory_execution, 209 | robot_description_planning_cartesian_limits, 210 | robot_description_planning_joint_limits, 211 | moveit_controllers, 212 | ompl_planning_config, 213 | planning_scene_monitor_parameters, 214 | {"use_sim_time": use_sim}, 215 | ], 216 | ) 217 | 218 | rviz_node = Node( 219 | package="rviz2", 220 | executable="rviz2", 221 | name="rviz2", 222 | output="screen", 223 | arguments=[ 224 | "-d", 225 | PathJoinSubstitution( 226 | [FindPackageShare(description_package), "rviz", "moveit.rviz"] 227 | ), 228 | ], 229 | parameters=[ 230 | robot_description, 231 | robot_description_semantic, 232 | robot_description_planning_cartesian_limits, 233 | robot_description_planning_joint_limits, 234 | robot_description_kinematics, 235 | planning_pipelines_config, 236 | ompl_planning_config, 237 | {"use_sim_time": use_sim}, 238 | ], 239 | condition=IfCondition(use_rviz), 240 | ) 241 | 242 | nodes = [ 243 | move_group_node, 244 | rviz_node, 245 | ] 246 | 247 | return LaunchDescription(args + nodes) 248 | -------------------------------------------------------------------------------- /alpha_bringup/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | alpha_bringup 5 | 0.0.1 6 | Launch package for the Reach Alpha ROS 2 driver 7 | Evan Palmer 8 | MIT 9 | 10 | ament_cmake 11 | 12 | ament_lint_auto 13 | ament_lint_common 14 | 15 | alpha_description 16 | alpha_driver 17 | alpha_hardware 18 | 19 | moveit_ros_move_group 20 | moveit_kinematics 21 | moveit_planners 22 | moveit_simple_controller_manager 23 | moveit_configs_utils 24 | moveit_ros_visualization 25 | moveit_planners_ompl 26 | rviz2 27 | rviz_common 28 | rviz_default_plugins 29 | ros2_controllers 30 | ros2_control 31 | joint_trajectory_controller 32 | controller_manager 33 | joint_state_publisher 34 | robot_state_publisher 35 | xacro 36 | urdf 37 | tf2_ros 38 | 39 | 40 | ament_cmake 41 | 42 | 43 | -------------------------------------------------------------------------------- /alpha_description/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(alpha_description) 3 | 4 | set(THIS_PACKAGE_INCLUDE_DEPENDS 5 | ament_cmake 6 | ) 7 | 8 | foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS}) 9 | find_package(${Dependency} REQUIRED) 10 | endforeach() 11 | 12 | install( 13 | DIRECTORY config meshes moveit2 rviz xacro 14 | DESTINATION share/${PROJECT_NAME} 15 | ) 16 | 17 | ament_package() 18 | -------------------------------------------------------------------------------- /alpha_description/LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in 9 | all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 14 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 17 | THE SOFTWARE. 18 | -------------------------------------------------------------------------------- /alpha_description/config/alpha_controllers.yaml: -------------------------------------------------------------------------------- 1 | controller_manager: 2 | ros__parameters: 3 | update_rate: 225 # Hz 4 | 5 | forward_velocity_controller: 6 | type: forward_command_controller/ForwardCommandController 7 | 8 | forward_position_controller: 9 | type: forward_command_controller/ForwardCommandController 10 | 11 | joint_state_broadcaster: 12 | type: joint_state_broadcaster/JointStateBroadcaster 13 | 14 | feedback_joint_position_trajectory_controller: 15 | type: joint_trajectory_controller/JointTrajectoryController 16 | 17 | feedback_joint_velocity_trajectory_controller: 18 | type: joint_trajectory_controller/JointTrajectoryController 19 | 20 | forward_velocity_controller: 21 | ros__parameters: 22 | joints: 23 | - axis_a 24 | - axis_b 25 | - axis_c 26 | - axis_d 27 | - axis_e 28 | interface_name: velocity 29 | 30 | forward_position_controller: 31 | ros__parameters: 32 | joints: 33 | - axis_a 34 | - axis_b 35 | - axis_c 36 | - axis_d 37 | - axis_e 38 | interface_name: position 39 | 40 | feedback_joint_position_trajectory_controller: 41 | ros__parameters: 42 | joints: 43 | - axis_a 44 | - axis_b 45 | - axis_c 46 | - axis_d 47 | - axis_e 48 | command_interfaces: 49 | - position 50 | state_interfaces: 51 | - position 52 | - velocity 53 | 54 | feedback_joint_velocity_trajectory_controller: 55 | ros__parameters: 56 | joints: 57 | - axis_a 58 | - axis_b 59 | - axis_c 60 | - axis_d 61 | - axis_e 62 | command_interfaces: 63 | - velocity 64 | state_interfaces: 65 | - position 66 | - velocity 67 | -------------------------------------------------------------------------------- /alpha_description/config/initial_positions.yaml: -------------------------------------------------------------------------------- 1 | # Default initial positions for alpha's ros2_control fake system 2 | initial_positions: 3 | axis_a: 0 4 | axis_b: 0 5 | axis_c: 0 6 | axis_d: 1.5746 7 | axis_e: 0 8 | -------------------------------------------------------------------------------- /alpha_description/meshes/LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), limited 3 | exclusively to use with products produced by Reach Robotics Pty Ltd, subject 4 | to the following conditions: 5 | 6 | The Software may only be used in conjunction with products manufactured or 7 | developed by Reach Robotics Pty Ltd. 8 | 9 | Redistributions or use of the Software in any other context, including but not 10 | limited to, integration, combination, or use with other products or software, 11 | are strictly prohibited without prior written authorization from Reach Robotics 12 | Pty Ltd. 13 | 14 | All copies of the Software, in whole or in part, must retain this notice and 15 | the above copyright notice. 16 | 17 | THIS SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL REACH 20 | ROBOTICS PTY LTD BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER 21 | IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /alpha_description/meshes/M2-1-1.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Robotic-Decision-Making-Lab/alpha/5e1a6648b78584d207e79890bc3905b770892dcd/alpha_description/meshes/M2-1-1.stl -------------------------------------------------------------------------------- /alpha_description/meshes/M2-1-3.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Robotic-Decision-Making-Lab/alpha/5e1a6648b78584d207e79890bc3905b770892dcd/alpha_description/meshes/M2-1-3.stl -------------------------------------------------------------------------------- /alpha_description/meshes/M2.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Robotic-Decision-Making-Lab/alpha/5e1a6648b78584d207e79890bc3905b770892dcd/alpha_description/meshes/M2.stl -------------------------------------------------------------------------------- /alpha_description/meshes/M3-INLINE.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Robotic-Decision-Making-Lab/alpha/5e1a6648b78584d207e79890bc3905b770892dcd/alpha_description/meshes/M3-INLINE.stl -------------------------------------------------------------------------------- /alpha_description/meshes/RS1-100-101-123.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Robotic-Decision-Making-Lab/alpha/5e1a6648b78584d207e79890bc3905b770892dcd/alpha_description/meshes/RS1-100-101-123.stl -------------------------------------------------------------------------------- /alpha_description/meshes/end_effectors/RS1-124.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Robotic-Decision-Making-Lab/alpha/5e1a6648b78584d207e79890bc3905b770892dcd/alpha_description/meshes/end_effectors/RS1-124.stl -------------------------------------------------------------------------------- /alpha_description/meshes/end_effectors/RS1-130.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Robotic-Decision-Making-Lab/alpha/5e1a6648b78584d207e79890bc3905b770892dcd/alpha_description/meshes/end_effectors/RS1-130.stl -------------------------------------------------------------------------------- /alpha_description/meshes/end_effectors/RS1-139.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Robotic-Decision-Making-Lab/alpha/5e1a6648b78584d207e79890bc3905b770892dcd/alpha_description/meshes/end_effectors/RS1-139.stl -------------------------------------------------------------------------------- /alpha_description/moveit2/alpha.config.srdf.xacro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /alpha_description/moveit2/alpha.srdf.xacro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 40 | 42 | 44 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /alpha_description/moveit2/joint_limits.yaml: -------------------------------------------------------------------------------- 1 | /**: 2 | ros__parameters: 3 | robot_description_planning: 4 | default_velocity_scaling_factor: 0.1 5 | default_acceleration_scaling_factor: 0.1 6 | joint_limits: 7 | axis_a: 8 | has_velocity_limits: true 9 | max_velocity: 2.5 10 | has_acceleration_limits: true 11 | max_acceleration: 2.0 12 | axis_b: 13 | has_velocity_limits: true 14 | max_velocity: 1.0 15 | has_acceleration_limits: true 16 | max_acceleration: 2.0 17 | axis_c: 18 | has_velocity_limits: true 19 | max_velocity: 1.0 20 | has_acceleration_limits: true 21 | max_acceleration: 2.0 22 | axis_d: 23 | has_velocity_limits: true 24 | max_velocity: 1.0 25 | has_acceleration_limits: true 26 | max_acceleration: 2.0 27 | axis_e: 28 | has_velocity_limits: true 29 | max_velocity: 0.5 30 | has_acceleration_limits: true 31 | max_acceleration: 2.0 32 | -------------------------------------------------------------------------------- /alpha_description/moveit2/kinematics.yaml: -------------------------------------------------------------------------------- 1 | /**: 2 | ros__parameters: 3 | robot_description_kinematics: 4 | alpha: 5 | kinematics_solver: kdl_kinematics_plugin/KDLKinematicsPlugin 6 | kinematics_solver_search_resolution: 0.0050000000000000001 7 | kinematics_solver_timeout: 0.0050000000000000001 8 | -------------------------------------------------------------------------------- /alpha_description/moveit2/moveit_controllers.yaml: -------------------------------------------------------------------------------- 1 | # MoveIt uses this configuration for controller management 2 | /**: 3 | ros__parameters: 4 | moveit_controller_manager: moveit_simple_controller_manager/MoveItSimpleControllerManager 5 | 6 | moveit_simple_controller_manager: 7 | controller_names: 8 | - feedback_joint_position_trajectory_controller 9 | 10 | feedback_joint_position_trajectory_controller: 11 | type: FollowJointTrajectory 12 | action_ns: follow_joint_trajectory 13 | default: true 14 | joints: 15 | - axis_a 16 | - axis_b 17 | - axis_c 18 | - axis_d 19 | - axis_e 20 | -------------------------------------------------------------------------------- /alpha_description/moveit2/ompl_planning.yaml: -------------------------------------------------------------------------------- 1 | /**: 2 | ros__parameters: 3 | ompl: 4 | planner_configs: 5 | SBLkConfigDefault: 6 | type: geometric::SBL 7 | range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup() 8 | ESTkConfigDefault: 9 | type: geometric::EST 10 | range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0 setup() 11 | goal_bias: 0.05 # When close to goal select goal, with this probability. default: 0.05 12 | LBKPIECEkConfigDefault: 13 | type: geometric::LBKPIECE 14 | range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup() 15 | border_fraction: 0.9 # Fraction of time focused on boarder default: 0.9 16 | min_valid_path_fraction: 0.5 # Accept partially valid moves above fraction. default: 0.5 17 | BKPIECEkConfigDefault: 18 | type: geometric::BKPIECE 19 | range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup() 20 | border_fraction: 0.9 # Fraction of time focused on boarder default: 0.9 21 | failed_expansion_score_factor: 0.5 # When extending motion fails, scale score by factor. default: 0.5 22 | min_valid_path_fraction: 0.5 # Accept partially valid moves above fraction. default: 0.5 23 | KPIECEkConfigDefault: 24 | type: geometric::KPIECE 25 | range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup() 26 | goal_bias: 0.05 # When close to goal select goal, with this probability. default: 0.05 27 | border_fraction: 0.9 # Fraction of time focused on boarder default: 0.9 (0.0,1.] 28 | failed_expansion_score_factor: 0.5 # When extending motion fails, scale score by factor. default: 0.5 29 | min_valid_path_fraction: 0.5 # Accept partially valid moves above fraction. default: 0.5 30 | RRTkConfigDefault: 31 | type: geometric::RRT 32 | range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup() 33 | goal_bias: 0.05 # When close to goal select goal, with this probability? default: 0.05 34 | RRTConnectkConfigDefault: 35 | type: geometric::RRTConnect 36 | range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup() 37 | RRTstarkConfigDefault: 38 | type: geometric::RRTstar 39 | range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup() 40 | goal_bias: 0.05 # When close to goal select goal, with this probability? default: 0.05 41 | delay_collision_checking: 1 # Stop collision checking as soon as C-free parent found. default 1 42 | TRRTkConfigDefault: 43 | type: geometric::TRRT 44 | range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup() 45 | goal_bias: 0.05 # When close to goal select goal, with this probability? default: 0.05 46 | max_states_failed: 10 # when to start increasing temp. default: 10 47 | temp_change_factor: 2.0 # how much to increase or decrease temp. default: 2.0 48 | min_temperature: 10e-10 # lower limit of temp change. default: 10e-10 49 | init_temperature: 10e-6 # initial temperature. default: 10e-6 50 | frountier_threshold: 0.0 # dist new state to nearest neighbor to disqualify as frontier. default: 0.0 set in setup() 51 | frountierNodeRatio: 0.1 # 1/10, or 1 nonfrontier for every 10 frontier. default: 0.1 52 | k_constant: 0.0 # value used to normalize expression. default: 0.0 set in setup() 53 | PRMkConfigDefault: 54 | type: geometric::PRM 55 | max_nearest_neighbors: 10 # use k nearest neighbors. default: 10 56 | PRMstarkConfigDefault: 57 | type: geometric::PRMstar 58 | FMTkConfigDefault: 59 | type: geometric::FMT 60 | num_samples: 1000 # number of states that the planner should sample. default: 1000 61 | radius_multiplier: 1.1 # multiplier used for the nearest neighbors search radius. default: 1.1 62 | nearest_k: 1 # use Knearest strategy. default: 1 63 | cache_cc: 1 # use collision checking cache. default: 1 64 | heuristics: 0 # activate cost to go heuristics. default: 0 65 | extended_fmt: 1 # activate the extended FMT*: adding new samples if planner does not finish successfully. default: 1 66 | BFMTkConfigDefault: 67 | type: geometric::BFMT 68 | num_samples: 1000 # number of states that the planner should sample. default: 1000 69 | radius_multiplier: 1.0 # multiplier used for the nearest neighbors search radius. default: 1.0 70 | nearest_k: 1 # use the Knearest strategy. default: 1 71 | balanced: 0 # exploration strategy: balanced true expands one tree every iteration. False will select the tree with lowest maximum cost to go. default: 1 72 | optimality: 1 # termination strategy: optimality true finishes when the best possible path is found. Otherwise, the algorithm will finish when the first feasible path is found. default: 1 73 | heuristics: 1 # activates cost to go heuristics. default: 1 74 | cache_cc: 1 # use the collision checking cache. default: 1 75 | extended_fmt: 1 # Activates the extended FMT*: adding new samples if planner does not finish successfully. default: 1 76 | PDSTkConfigDefault: 77 | type: geometric::PDST 78 | STRIDEkConfigDefault: 79 | type: geometric::STRIDE 80 | range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup() 81 | goal_bias: 0.05 # When close to goal select goal, with this probability. default: 0.05 82 | use_projected_distance: 0 # whether nearest neighbors are computed based on distances in a projection of the state rather distances in the state space itself. default: 0 83 | degree: 16 # desired degree of a node in the Geometric Near-neightbor Access Tree (GNAT). default: 16 84 | max_degree: 18 # max degree of a node in the GNAT. default: 12 85 | min_degree: 12 # min degree of a node in the GNAT. default: 12 86 | max_pts_per_leaf: 6 # max points per leaf in the GNAT. default: 6 87 | estimated_dimension: 0.0 # estimated dimension of the free space. default: 0.0 88 | min_valid_path_fraction: 0.2 # Accept partially valid moves above fraction. default: 0.2 89 | BiTRRTkConfigDefault: 90 | type: geometric::BiTRRT 91 | range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup() 92 | temp_change_factor: 0.1 # how much to increase or decrease temp. default: 0.1 93 | init_temperature: 100 # initial temperature. default: 100 94 | frountier_threshold: 0.0 # dist new state to nearest neighbor to disqualify as frontier. default: 0.0 set in setup() 95 | frountier_node_ratio: 0.1 # 1/10, or 1 nonfrontier for every 10 frontier. default: 0.1 96 | cost_threshold: 1e300 # the cost threshold. Any motion cost that is not better will not be expanded. default: inf 97 | LBTRRTkConfigDefault: 98 | type: geometric::LBTRRT 99 | range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup() 100 | goal_bias: 0.05 # When close to goal select goal, with this probability. default: 0.05 101 | epsilon: 0.4 # optimality approximation factor. default: 0.4 102 | BiESTkConfigDefault: 103 | type: geometric::BiEST 104 | range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup() 105 | ProjESTkConfigDefault: 106 | type: geometric::ProjEST 107 | range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup() 108 | goal_bias: 0.05 # When close to goal select goal, with this probability. default: 0.05 109 | LazyPRMkConfigDefault: 110 | type: geometric::LazyPRM 111 | range: 0.0 # Max motion added to tree. ==> maxDistance_ default: 0.0, if 0.0, set on setup() 112 | LazyPRMstarkConfigDefault: 113 | type: geometric::LazyPRMstar 114 | SPARSkConfigDefault: 115 | type: geometric::SPARS 116 | stretch_factor: 3.0 # roadmap spanner stretch factor. multiplicative upper bound on path quality. It does not make sense to make this parameter more than 3. default: 3.0 117 | sparse_delta_fraction: 0.25 # delta fraction for connection distance. This value represents the visibility range of sparse samples. default: 0.25 118 | dense_delta_fraction: 0.001 # delta fraction for interface detection. default: 0.001 119 | max_failures: 1000 # maximum consecutive failure limit. default: 1000 120 | SPARStwokConfigDefault: 121 | type: geometric::SPARStwo 122 | stretch_factor: 3.0 # roadmap spanner stretch factor. multiplicative upper bound on path quality. It does not make sense to make this parameter more than 3. default: 3.0 123 | sparse_delta_fraction: 0.25 # delta fraction for connection distance. This value represents the visibility range of sparse samples. default: 0.25 124 | dense_delta_fraction: 0.001 # delta fraction for interface detection. default: 0.001 125 | max_failures: 5000 # maximum consecutive failure limit. default: 5000 126 | TrajOptDefault: 127 | type: geometric::TrajOpt 128 | 129 | alpha: 130 | planner_configs: 131 | - SBLkConfigDefault 132 | - ESTkConfigDefault 133 | - LBKPIECEkConfigDefault 134 | - BKPIECEkConfigDefault 135 | - KPIECEkConfigDefault 136 | - RRTkConfigDefault 137 | - RRTConnectkConfigDefault 138 | - RRTstarkConfigDefault 139 | - TRRTkConfigDefault 140 | - PRMkConfigDefault 141 | - PRMstarkConfigDefault 142 | - FMTkConfigDefault 143 | - BFMTkConfigDefault 144 | - PDSTkConfigDefault 145 | - STRIDEkConfigDefault 146 | - BiTRRTkConfigDefault 147 | - LBTRRTkConfigDefault 148 | - BiESTkConfigDefault 149 | - ProjESTkConfigDefault 150 | - LazyPRMkConfigDefault 151 | - LazyPRMstarkConfigDefault 152 | - SPARSkConfigDefault 153 | - SPARStwokConfigDefault 154 | - TrajOptDefault 155 | -------------------------------------------------------------------------------- /alpha_description/moveit2/pilz_cartesian_limits.yaml: -------------------------------------------------------------------------------- 1 | # Limits for the Pilz planner 2 | /**: 3 | ros__parameters: 4 | robot_description_planning: 5 | cartesian_limits: 6 | max_trans_vel: 1.0 7 | max_trans_acc: 2.25 8 | max_trans_dec: -5.0 9 | max_rot_vel: 1.57 10 | -------------------------------------------------------------------------------- /alpha_description/moveit2/planning_pipelines.yaml: -------------------------------------------------------------------------------- 1 | /**: 2 | ros__parameters: 3 | default_planning_pipeline: ompl 4 | planning_pipelines: [pilz, ompl] 5 | pilz: 6 | planning_plugin: pilz_industrial_motion_planner/CommandPlanner 7 | request_adapters: "" 8 | start_state_max_bounds_error: 0.1 9 | ompl: 10 | planning_plugin: ompl_interface/OMPLPlanner 11 | request_adapters: 12 | default_planner_request_adapters/AddTimeOptimalParameterization 13 | default_planner_request_adapters/FixWorkspaceBounds 14 | default_planner_request_adapters/FixStartStateBounds 15 | default_planner_request_adapters/FixStartStateCollision 16 | default_planner_request_adapters/FixStartStatePathConstraints 17 | start_state_max_bounds_error: 0.1 18 | -------------------------------------------------------------------------------- /alpha_description/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | alpha_description 5 | 0.0.1 6 | Description files for the Reach Alpha 5 manipulator 7 | Evan Palmer 8 | MIT 9 | 10 | ament_cmake 11 | 12 | ament_lint_auto 13 | ament_lint_common 14 | 15 | xacro 16 | urdf 17 | 18 | 19 | ament_cmake 20 | 21 | 22 | -------------------------------------------------------------------------------- /alpha_description/rviz/moveit.rviz: -------------------------------------------------------------------------------- 1 | Panels: 2 | - Class: rviz_common/Displays 3 | Help Height: 0 4 | Name: Displays 5 | Property Tree Widget: 6 | Expanded: 7 | - /MotionPlanning1 8 | - /MotionPlanning1/Planned Path1 9 | Splitter Ratio: 0.5 10 | Tree Height: 247 11 | - Class: rviz_common/Help 12 | Name: Help 13 | - Class: rviz_common/Views 14 | Expanded: 15 | - /Current View1 16 | Name: Views 17 | Splitter Ratio: 0.5 18 | Visualization Manager: 19 | Class: "" 20 | Displays: 21 | - Alpha: 0.5 22 | Cell Size: 1 23 | Class: rviz_default_plugins/Grid 24 | Color: 160; 160; 164 25 | Enabled: true 26 | Line Style: 27 | Line Width: 0.029999999329447746 28 | Value: Lines 29 | Name: Grid 30 | Normal Cell Count: 0 31 | Offset: 32 | X: 0 33 | Y: 0 34 | Z: 0 35 | Plane: XY 36 | Plane Cell Count: 10 37 | Reference Frame: 38 | Value: true 39 | - Acceleration_Scaling_Factor: 0.1 40 | Class: moveit_rviz_plugin/MotionPlanning 41 | Enabled: true 42 | Move Group Namespace: "" 43 | MoveIt_Allow_Approximate_IK: false 44 | MoveIt_Allow_External_Program: false 45 | MoveIt_Allow_Replanning: false 46 | MoveIt_Allow_Sensor_Positioning: false 47 | MoveIt_Planning_Attempts: 10 48 | MoveIt_Planning_Time: 5 49 | MoveIt_Use_Cartesian_Path: false 50 | MoveIt_Use_Constraint_Aware_IK: false 51 | MoveIt_Workspace: 52 | Center: 53 | X: 0 54 | Y: 0 55 | Z: 0 56 | Size: 57 | X: 2 58 | Y: 2 59 | Z: 2 60 | Name: MotionPlanning 61 | Planned Path: 62 | Color Enabled: false 63 | Interrupt Display: false 64 | Links: 65 | All Links Enabled: true 66 | Expand Joint Details: false 67 | Expand Link Details: false 68 | Expand Tree: false 69 | Link Tree Style: Links in Alphabetic Order 70 | base_link: 71 | Alpha: 1 72 | Show Axes: false 73 | Show Trail: false 74 | ee_base_link: 75 | Alpha: 1 76 | Show Axes: false 77 | Show Trail: false 78 | m1_link: 79 | Alpha: 1 80 | Show Axes: false 81 | Show Trail: false 82 | Value: true 83 | m2_1_1_link: 84 | Alpha: 1 85 | Show Axes: false 86 | Show Trail: false 87 | Value: true 88 | m2_1_2_link: 89 | Alpha: 1 90 | Show Axes: false 91 | Show Trail: false 92 | Value: true 93 | m2_joint_link: 94 | Alpha: 1 95 | Show Axes: false 96 | Show Trail: false 97 | Value: true 98 | m3_inline_link: 99 | Alpha: 1 100 | Show Axes: false 101 | Show Trail: false 102 | Value: true 103 | push_rod: 104 | Alpha: 1 105 | Show Axes: false 106 | Show Trail: false 107 | standard_jaws_base_link: 108 | Alpha: 1 109 | Show Axes: false 110 | Show Trail: false 111 | Value: true 112 | standard_jaws_rs1_130_link: 113 | Alpha: 1 114 | Show Axes: false 115 | Show Trail: false 116 | Value: true 117 | standard_jaws_rs1_139_link: 118 | Alpha: 1 119 | Show Axes: false 120 | Show Trail: false 121 | Value: true 122 | world: 123 | Alpha: 1 124 | Show Axes: false 125 | Show Trail: false 126 | Loop Animation: true 127 | Robot Alpha: 0.5 128 | Robot Color: 150; 50; 150 129 | Show Robot Collision: false 130 | Show Robot Visual: true 131 | Show Trail: false 132 | State Display Time: 0.05 s 133 | Trail Step Size: 1 134 | Trajectory Topic: display_planned_path 135 | Use Sim Time: false 136 | Planning Metrics: 137 | Payload: 1 138 | Show Joint Torques: false 139 | Show Manipulability: false 140 | Show Manipulability Index: false 141 | Show Weight Limit: false 142 | TextHeight: 0.07999999821186066 143 | Planning Request: 144 | Colliding Link Color: 255; 0; 0 145 | Goal State Alpha: 1 146 | Goal State Color: 250; 128; 0 147 | Interactive Marker Size: 0 148 | Joint Violation Color: 255; 0; 255 149 | Planning Group: alpha 150 | Query Goal State: true 151 | Query Start State: false 152 | Show Workspace: false 153 | Start State Alpha: 1 154 | Start State Color: 0; 255; 0 155 | Planning Scene Topic: monitored_planning_scene 156 | Robot Description: robot_description 157 | Scene Geometry: 158 | Scene Alpha: 1 159 | Scene Color: 50; 230; 50 160 | Scene Display Time: 0.009999999776482582 161 | Show Scene Geometry: true 162 | Voxel Coloring: Z-Axis 163 | Voxel Rendering: Occupied Voxels 164 | Scene Robot: 165 | Attached Body Color: 150; 50; 150 166 | Links: 167 | All Links Enabled: true 168 | Expand Joint Details: false 169 | Expand Link Details: false 170 | Expand Tree: false 171 | Link Tree Style: Links in Alphabetic Order 172 | base_link: 173 | Alpha: 1 174 | Show Axes: false 175 | Show Trail: false 176 | ee_base_link: 177 | Alpha: 1 178 | Show Axes: false 179 | Show Trail: false 180 | m1_link: 181 | Alpha: 1 182 | Show Axes: false 183 | Show Trail: false 184 | Value: true 185 | m2_1_1_link: 186 | Alpha: 1 187 | Show Axes: false 188 | Show Trail: false 189 | Value: true 190 | m2_1_2_link: 191 | Alpha: 1 192 | Show Axes: false 193 | Show Trail: false 194 | Value: true 195 | m2_joint_link: 196 | Alpha: 1 197 | Show Axes: false 198 | Show Trail: false 199 | Value: true 200 | m3_inline_link: 201 | Alpha: 1 202 | Show Axes: false 203 | Show Trail: false 204 | Value: true 205 | push_rod: 206 | Alpha: 1 207 | Show Axes: false 208 | Show Trail: false 209 | standard_jaws_base_link: 210 | Alpha: 1 211 | Show Axes: false 212 | Show Trail: false 213 | Value: true 214 | standard_jaws_rs1_130_link: 215 | Alpha: 1 216 | Show Axes: false 217 | Show Trail: false 218 | Value: true 219 | standard_jaws_rs1_139_link: 220 | Alpha: 1 221 | Show Axes: false 222 | Show Trail: false 223 | Value: true 224 | world: 225 | Alpha: 1 226 | Show Axes: false 227 | Show Trail: false 228 | Robot Alpha: 0.5 229 | Show Robot Collision: false 230 | Show Robot Visual: true 231 | Value: true 232 | Velocity_Scaling_Factor: 0.1 233 | Enabled: true 234 | Global Options: 235 | Background Color: 48; 48; 48 236 | Fixed Frame: world 237 | Frame Rate: 30 238 | Name: root 239 | Tools: 240 | - Class: rviz_default_plugins/Interact 241 | Hide Inactive Objects: true 242 | - Class: rviz_default_plugins/MoveCamera 243 | - Class: rviz_default_plugins/Select 244 | Transformation: 245 | Current: 246 | Class: rviz_default_plugins/TF 247 | Value: true 248 | Views: 249 | Current: 250 | Class: rviz_default_plugins/Orbit 251 | Distance: 1.3433176279067993 252 | Enable Stereo Rendering: 253 | Stereo Eye Separation: 0.05999999865889549 254 | Stereo Focal Distance: 1 255 | Swap Stereo Eyes: false 256 | Value: false 257 | Focal Point: 258 | X: -0.07226651906967163 259 | Y: 0.19666166603565216 260 | Z: 0.07979677617549896 261 | Focal Shape Fixed Size: true 262 | Focal Shape Size: 0.05000000074505806 263 | Invert Z Axis: false 264 | Name: Current View 265 | Near Clip Distance: 0.009999999776482582 266 | Pitch: 0.3000001907348633 267 | Target Frame: world 268 | Value: Orbit (rviz_default_plugins) 269 | Yaw: 4.74516487121582 270 | Saved: ~ 271 | Window Geometry: 272 | Displays: 273 | collapsed: false 274 | Height: 1016 275 | Help: 276 | collapsed: false 277 | Hide Left Dock: false 278 | Hide Right Dock: false 279 | MotionPlanning: 280 | collapsed: false 281 | MotionPlanning - Trajectory Slider: 282 | collapsed: false 283 | QMainWindow State: 000000ff00000000fd000000010000000000000224000003a2fc0200000005fb00000044004d006f00740069006f006e0050006c0061006e006e0069006e00670020002d0020005400720061006a006500630074006f0072007900200053006c00690064006500720000000000ffffffff0000003f00fffffffb000000100044006900730070006c006100790073010000003b00000132000000c700fffffffb0000001c004d006f00740069006f006e0050006c0061006e006e0069006e00670100000173000001a50000016900fffffffb0000000800480065006c0070000000029a0000006e0000006e00fffffffb0000000a00560069006500770073010000031e000000bf000000a000ffffff00000556000003a200000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 284 | Views: 285 | collapsed: false 286 | Width: 1920 287 | X: 1920 288 | Y: 27 289 | -------------------------------------------------------------------------------- /alpha_description/rviz/view_alpha.rviz: -------------------------------------------------------------------------------- 1 | Panels: 2 | - Class: rviz_common/Displays 3 | Help Height: 78 4 | Name: Displays 5 | Property Tree Widget: 6 | Expanded: 7 | - /Global Options1 8 | - /Status1 9 | - /RobotModel1 10 | Splitter Ratio: 0.5 11 | Tree Height: 725 12 | - Class: rviz_common/Selection 13 | Name: Selection 14 | - Class: rviz_common/Tool Properties 15 | Expanded: 16 | - /2D Goal Pose1 17 | - /Publish Point1 18 | Name: Tool Properties 19 | Splitter Ratio: 0.5886790156364441 20 | - Class: rviz_common/Views 21 | Expanded: 22 | - /Current View1 23 | Name: Views 24 | Splitter Ratio: 0.5 25 | - Class: rviz_common/Time 26 | Experimental: false 27 | Name: Time 28 | SyncMode: 0 29 | SyncSource: "" 30 | Visualization Manager: 31 | Class: "" 32 | Displays: 33 | - Alpha: 0.5 34 | Cell Size: 1 35 | Class: rviz_default_plugins/Grid 36 | Color: 160; 160; 164 37 | Enabled: true 38 | Line Style: 39 | Line Width: 0.029999999329447746 40 | Value: Lines 41 | Name: Grid 42 | Normal Cell Count: 0 43 | Offset: 44 | X: 0 45 | Y: 0 46 | Z: 0 47 | Plane: XY 48 | Plane Cell Count: 10 49 | Reference Frame: 50 | Value: true 51 | - Alpha: 1 52 | Class: rviz_default_plugins/RobotModel 53 | Collision Enabled: false 54 | Description File: "" 55 | Description Source: Topic 56 | Description Topic: 57 | Depth: 5 58 | Durability Policy: Volatile 59 | History Policy: Keep Last 60 | Reliability Policy: Reliable 61 | Value: /robot_description 62 | Enabled: true 63 | Links: 64 | All Links Enabled: false 65 | Expand Joint Details: false 66 | Expand Link Details: false 67 | Expand Tree: false 68 | Link Tree Style: Links in Alphabetic Order 69 | base_footprint: 70 | Alpha: 1 71 | Show Axes: false 72 | Show Trail: false 73 | base_link: 74 | Alpha: 1 75 | Show Axes: false 76 | Show Trail: false 77 | Value: true 78 | ee_base_link: 79 | Alpha: 1 80 | Show Axes: false 81 | Show Trail: false 82 | Value: true 83 | m1_link: 84 | Alpha: 1 85 | Show Axes: false 86 | Show Trail: false 87 | Value: true 88 | m2_1_1_link: 89 | Alpha: 1 90 | Show Axes: false 91 | Show Trail: false 92 | Value: true 93 | m2_1_2_link: 94 | Alpha: 1 95 | Show Axes: false 96 | Show Trail: false 97 | Value: true 98 | m2_joint_link: 99 | Alpha: 1 100 | Show Axes: false 101 | Show Trail: false 102 | Value: true 103 | m3_inline_link: 104 | Alpha: 1 105 | Show Axes: false 106 | Show Trail: false 107 | Value: true 108 | platform: 109 | Alpha: 1 110 | Show Axes: false 111 | Show Trail: false 112 | Value: false 113 | push_rod: 114 | Alpha: 1 115 | Show Axes: false 116 | Show Trail: false 117 | Value: true 118 | standard_jaws_base_link: 119 | Alpha: 1 120 | Show Axes: false 121 | Show Trail: false 122 | Value: true 123 | standard_jaws_rs1_130_link: 124 | Alpha: 1 125 | Show Axes: false 126 | Show Trail: false 127 | Value: true 128 | standard_jaws_rs1_139_link: 129 | Alpha: 1 130 | Show Axes: false 131 | Show Trail: false 132 | Value: true 133 | standard_jaws_tool: 134 | Alpha: 1 135 | Show Axes: false 136 | Show Trail: false 137 | world: 138 | Alpha: 1 139 | Show Axes: false 140 | Show Trail: false 141 | Mass Properties: 142 | Inertia: false 143 | Mass: false 144 | Name: RobotModel 145 | TF Prefix: "" 146 | Update Interval: 0 147 | Value: true 148 | Visual Enabled: true 149 | Enabled: true 150 | Global Options: 151 | Background Color: 188; 188; 188 152 | Fixed Frame: base_link 153 | Frame Rate: 30 154 | Name: root 155 | Tools: 156 | - Class: rviz_default_plugins/Interact 157 | Hide Inactive Objects: true 158 | - Class: rviz_default_plugins/MoveCamera 159 | - Class: rviz_default_plugins/Select 160 | - Class: rviz_default_plugins/FocusCamera 161 | - Class: rviz_default_plugins/Measure 162 | Line color: 128; 128; 0 163 | - Class: rviz_default_plugins/SetInitialPose 164 | Covariance x: 0.25 165 | Covariance y: 0.25 166 | Covariance yaw: 0.06853891909122467 167 | Topic: 168 | Depth: 5 169 | Durability Policy: Volatile 170 | History Policy: Keep Last 171 | Reliability Policy: Reliable 172 | Value: /initialpose 173 | - Class: rviz_default_plugins/SetGoal 174 | Topic: 175 | Depth: 5 176 | Durability Policy: Volatile 177 | History Policy: Keep Last 178 | Reliability Policy: Reliable 179 | Value: /goal_pose 180 | - Class: rviz_default_plugins/PublishPoint 181 | Single click: true 182 | Topic: 183 | Depth: 5 184 | Durability Policy: Volatile 185 | History Policy: Keep Last 186 | Reliability Policy: Reliable 187 | Value: /clicked_point 188 | Transformation: 189 | Current: 190 | Class: rviz_default_plugins/TF 191 | Value: true 192 | Views: 193 | Current: 194 | Class: rviz_default_plugins/Orbit 195 | Distance: 0.5630145072937012 196 | Enable Stereo Rendering: 197 | Stereo Eye Separation: 0.05999999865889549 198 | Stereo Focal Distance: 1 199 | Swap Stereo Eyes: false 200 | Value: false 201 | Focal Point: 202 | X: -0.029880372807383537 203 | Y: 0.012031642720103264 204 | Z: 0.030446186661720276 205 | Focal Shape Fixed Size: false 206 | Focal Shape Size: 0.05000000074505806 207 | Invert Z Axis: false 208 | Name: Current View 209 | Near Clip Distance: 0.009999999776482582 210 | Pitch: 0.2503986954689026 211 | Target Frame: 212 | Value: Orbit (rviz) 213 | Yaw: 4.703569412231445 214 | Saved: ~ 215 | Window Geometry: 216 | Displays: 217 | collapsed: false 218 | Height: 1016 219 | Hide Left Dock: false 220 | Hide Right Dock: true 221 | QMainWindow State: 000000ff00000000fd00000004000000000000015a0000035efc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003b0000035e000000c700fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f000002b4fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003b000002b4000000a000fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000007800000003efc0100000002fb0000000800540069006d00650100000000000007800000025300fffffffb0000000800540069006d00650100000000000004500000000000000000000006200000035e00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 222 | Selection: 223 | collapsed: false 224 | Time: 225 | collapsed: false 226 | Tool Properties: 227 | collapsed: false 228 | Views: 229 | collapsed: true 230 | Width: 1920 231 | X: 1920 232 | Y: 27 233 | -------------------------------------------------------------------------------- /alpha_description/xacro/LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), limited 3 | exclusively to use with products produced by Reach Robotics Pty Ltd, subject 4 | to the following conditions: 5 | 6 | The Software may only be used in conjunction with products manufactured or 7 | developed by Reach Robotics Pty Ltd. 8 | 9 | Redistributions or use of the Software in any other context, including but not 10 | limited to, integration, combination, or use with other products or software, 11 | are strictly prohibited without prior written authorization from Reach Robotics 12 | Pty Ltd. 13 | 14 | All copies of the Software, in whole or in part, must retain this notice and 15 | the above copyright notice. 16 | 17 | THIS SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL REACH 20 | ROBOTICS PTY LTD BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER 21 | IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /alpha_description/xacro/alpha.config.xacro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /alpha_description/xacro/alpha.gazebo.xacro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | /${namespace[:-1]} 18 | 19 | /${namespace}robot_state_publisher 20 | 21 | $(find ${description_package})/config/${controllers_file} 22 | 23 | 24 | 25 | 26 | 27 | Gazebo/Gray 28 | 29 | 30 | Gazebo/Gray 31 | 32 | 33 | 34 | Gazebo/Gray 35 | 36 | 37 | Gazebo/Gray 38 | 39 | 40 | Gazebo/Gray 41 | 42 | 43 | Gazebo/Gray 44 | 45 | 46 | Gazebo/Gray 47 | 48 | 49 | Gazebo/Gray 50 | 51 | 52 | Gazebo/Gray 53 | 54 | 55 | Gazebo/Gray 56 | 57 | 58 | Gazebo/Gray 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /alpha_description/xacro/alpha.ros2_control.xacro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | gz_ros2_control/GazeboSimSystem 23 | 24 | 25 | 26 | 28 | 29 | 30 | mock_components/GenericSystem 31 | 32 | 33 | alpha_hardware/AlphaHardware 34 | ${serial_port} 35 | ${state_update_frequency} 36 | 37 | 38 | 39 | 40 | 41 | 42 | 0 43 | 0.015 44 | 45 | 46 | -2.5 47 | 2.5 48 | 49 | 50 | ${initial_positions['axis_a']} 51 | 52 | 53 | 0.0 54 | 55 | 56 | 57 | 58 | 59 | 0 60 | 3.22 61 | 62 | 63 | -1.0 64 | 1.0 65 | 66 | 67 | ${initial_positions['axis_b']} 68 | 69 | 70 | 0.0 71 | 72 | 73 | 74 | 75 | 76 | 0 77 | 3.22 78 | 79 | 80 | -1.0 81 | 1.0 82 | 83 | 84 | ${initial_positions['axis_c']} 85 | 86 | 87 | 0.0 88 | 89 | 90 | 91 | 92 | 93 | 0 94 | 3.49 95 | 96 | 97 | -1.0 98 | 1.0 99 | 100 | 101 | ${initial_positions['axis_d']} 102 | 103 | 104 | 0.0 105 | 106 | 107 | 108 | 109 | 110 | 0 111 | 6.10 112 | 113 | 114 | -0.5 115 | 0.5 116 | 117 | 118 | ${initial_positions['axis_e']} 119 | 120 | 121 | 0.0 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /alpha_description/xacro/alpha.urdf.xacro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 284 | 285 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | -------------------------------------------------------------------------------- /alpha_description/xacro/alpha_platform.config.xacro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 30 | 31 | 32 | 38 | 39 | 40 | 41 | 42 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /alpha_description/xacro/alpha_platform.urdf.xacro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /alpha_description/xacro/end_effectors/standard_jaws.urdf.xacro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /alpha_driver/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(alpha_driver) 3 | 4 | # Default to C99 5 | if(NOT CMAKE_C_STANDARD) 6 | set(CMAKE_C_STANDARD 99) 7 | endif() 8 | 9 | # Default to C++ 17 10 | if(NOT CMAKE_CXX_STANDARD) 11 | set(CMAKE_CXX_STANDARD 17) 12 | endif() 13 | 14 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") 15 | add_compile_options(-Wall -Wextra -Wpedantic) 16 | endif() 17 | 18 | set(THIS_PACKAGE_INCLUDE_DEPENDS 19 | rclcpp 20 | ament_cmake 21 | ) 22 | 23 | foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS}) 24 | find_package(${Dependency} REQUIRED) 25 | endforeach() 26 | 27 | include_directories( 28 | include 29 | ) 30 | 31 | add_library(${PROJECT_NAME} SHARED 32 | src/cobs.cpp 33 | src/crc.cpp 34 | src/packet.cpp 35 | src/serial_client.cpp 36 | src/driver.cpp 37 | ) 38 | 39 | target_link_libraries(${PROJECT_NAME} ${rclcpp_LIBRARIES}) 40 | target_include_directories(${PROJECT_NAME} PRIVATE include) 41 | ament_target_dependencies(${PROJECT_NAME} ${THIS_PACKAGE_INCLUDE_DEPENDS}) 42 | 43 | install( 44 | DIRECTORY include/ 45 | DESTINATION include 46 | ) 47 | 48 | install( 49 | TARGETS ${PROJECT_NAME} 50 | EXPORT ${PROJECT_NAME} 51 | RUNTIME DESTINATION bin 52 | LIBRARY DESTINATION lib 53 | ARCHIVE DESTINATION lib 54 | INCLUDES DESTINATION include 55 | ) 56 | 57 | if(BUILD_TESTING) 58 | find_package(ament_cmake_gtest REQUIRED) 59 | find_package(ament_cmake_gmock REQUIRED) 60 | find_package(ament_lint_auto REQUIRED) 61 | 62 | # Run linters found in package.xml except the two below 63 | set(ament_cmake_copyright_FOUND TRUE) 64 | set(ament_cmake_uncrustify_FOUND TRUE) 65 | 66 | ament_lint_auto_find_test_dependencies() 67 | 68 | # Setup unit tests 69 | ament_add_gtest( 70 | test_crc 71 | test/test_crc.cpp 72 | ) 73 | target_link_libraries(test_crc ${PROJECT_NAME}) 74 | 75 | ament_add_gmock( 76 | test_cobs 77 | test/test_cobs.cpp 78 | ) 79 | target_link_libraries(test_cobs ${PROJECT_NAME}) 80 | 81 | ament_add_gmock( 82 | test_packet 83 | test/test_packet.cpp 84 | ) 85 | target_link_libraries(test_packet ${PROJECT_NAME}) 86 | endif() 87 | 88 | ament_export_targets(${PROJECT_NAME} HAS_LIBRARY_TARGET) 89 | ament_export_dependencies(${THIS_PACKAGE_INCLUDE_DEPENDS}) 90 | 91 | ament_package() 92 | -------------------------------------------------------------------------------- /alpha_driver/LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in 9 | all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 14 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 17 | THE SOFTWARE. 18 | -------------------------------------------------------------------------------- /alpha_driver/include/alpha_driver/cobs.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Evan Palmer 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #pragma once 22 | 23 | #include 24 | 25 | namespace alpha::driver 26 | { 27 | 28 | /** 29 | * @brief Encode serial data using the COBS algorithm. 30 | * 31 | * @remark This implementation has been inspired by the following source: 32 | * https://github.com/gbmhunter/SerialFiller/blob/d678acbf6d29de7042d48c6be8ecef556bb6d857/src/CobsTranscoder.cpp#L19 33 | * 34 | * @param data The serial data to encode. 35 | * @return The serial data that has been encoded with the COBS algorithm. 36 | */ 37 | std::vector cobsEncode(const std::vector & data); 38 | 39 | /** 40 | * @brief Decode serial data that has been encoded with the COBS algorithm. 41 | * 42 | * @remark This implementation has been inspired by the following source: 43 | * https://github.com/gbmhunter/SerialFiller/blob/d678acbf6d29de7042d48c6be8ecef556bb6d857/src/CobsTranscoder.cpp#L74 44 | * 45 | * @param data The serial data to decode. 46 | * @return The decoded serial data. 47 | */ 48 | std::vector cobsDecode(const std::vector & data); 49 | 50 | } // namespace alpha::driver 51 | -------------------------------------------------------------------------------- /alpha_driver/include/alpha_driver/crc.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Evan Palmer 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | namespace alpha::driver 28 | { 29 | 30 | /** 31 | * @brief The CRC initial value specified by the Reach protocol. 32 | */ 33 | const unsigned char kInitialValue = 0x00; 34 | 35 | /** 36 | * @brief The CRC final XOR value specified by the Reach protocol. 37 | */ 38 | const unsigned char kFinalXorValue = 0xFF; 39 | 40 | /** 41 | * @brief The Reach protocol specifies that the CRC input should be reflected. 42 | */ 43 | const bool kInputReflected = true; 44 | 45 | /** 46 | * @brief The Reach protocol specifies that the CRC result should be reflected. 47 | */ 48 | const bool kResultReflected = true; 49 | 50 | /** 51 | * @brief Lookup table used for the CRC calculation. 52 | * 53 | * @note This is pre-computed to speed up the CRC calculation. 54 | * 55 | * @remarks To recompute by hand, use the following parameters: 56 | * - Polynomial: 0x4D 57 | * - Initial Value: 0x00 58 | * - Final XOR Value: 0xFF 59 | * - Input Reflected: True 60 | * - Result Reflected: True 61 | * - Use Reflected Lookup Table: False \n 62 | * \n 63 | * For help computing the lookup table, you can use the handy-dandy calculator available 64 | * at this link below: http://www.sunshine2k.de/coding/javascript/crc/crc_js.html 65 | */ 66 | const std::array kCrc8LookupTable = { 67 | 0x00, 0x4D, 0x9A, 0xD7, 0x79, 0x34, 0xE3, 0xAE, 0xF2, 0xBF, 0x68, 0x25, 0x8B, 0xC6, 0x11, 0x5C, 68 | 0xA9, 0xE4, 0x33, 0x7E, 0xD0, 0x9D, 0x4A, 0x07, 0x5B, 0x16, 0xC1, 0x8C, 0x22, 0x6F, 0xB8, 0xF5, 69 | 0x1F, 0x52, 0x85, 0xC8, 0x66, 0x2B, 0xFC, 0xB1, 0xED, 0xA0, 0x77, 0x3A, 0x94, 0xD9, 0x0E, 0x43, 70 | 0xB6, 0xFB, 0x2C, 0x61, 0xCF, 0x82, 0x55, 0x18, 0x44, 0x09, 0xDE, 0x93, 0x3D, 0x70, 0xA7, 0xEA, 71 | 0x3E, 0x73, 0xA4, 0xE9, 0x47, 0x0A, 0xDD, 0x90, 0xCC, 0x81, 0x56, 0x1B, 0xB5, 0xF8, 0x2F, 0x62, 72 | 0x97, 0xDA, 0x0D, 0x40, 0xEE, 0xA3, 0x74, 0x39, 0x65, 0x28, 0xFF, 0xB2, 0x1C, 0x51, 0x86, 0xCB, 73 | 0x21, 0x6C, 0xBB, 0xF6, 0x58, 0x15, 0xC2, 0x8F, 0xD3, 0x9E, 0x49, 0x04, 0xAA, 0xE7, 0x30, 0x7D, 74 | 0x88, 0xC5, 0x12, 0x5F, 0xF1, 0xBC, 0x6B, 0x26, 0x7A, 0x37, 0xE0, 0xAD, 0x03, 0x4E, 0x99, 0xD4, 75 | 0x7C, 0x31, 0xE6, 0xAB, 0x05, 0x48, 0x9F, 0xD2, 0x8E, 0xC3, 0x14, 0x59, 0xF7, 0xBA, 0x6D, 0x20, 76 | 0xD5, 0x98, 0x4F, 0x02, 0xAC, 0xE1, 0x36, 0x7B, 0x27, 0x6A, 0xBD, 0xF0, 0x5E, 0x13, 0xC4, 0x89, 77 | 0x63, 0x2E, 0xF9, 0xB4, 0x1A, 0x57, 0x80, 0xCD, 0x91, 0xDC, 0x0B, 0x46, 0xE8, 0xA5, 0x72, 0x3F, 78 | 0xCA, 0x87, 0x50, 0x1D, 0xB3, 0xFE, 0x29, 0x64, 0x38, 0x75, 0xA2, 0xEF, 0x41, 0x0C, 0xDB, 0x96, 79 | 0x42, 0x0F, 0xD8, 0x95, 0x3B, 0x76, 0xA1, 0xEC, 0xB0, 0xFD, 0x2A, 0x67, 0xC9, 0x84, 0x53, 0x1E, 80 | 0xEB, 0xA6, 0x71, 0x3C, 0x92, 0xDF, 0x08, 0x45, 0x19, 0x54, 0x83, 0xCE, 0x60, 0x2D, 0xFA, 0xB7, 81 | 0x5D, 0x10, 0xC7, 0x8A, 0x24, 0x69, 0xBE, 0xF3, 0xAF, 0xE2, 0x35, 0x78, 0xD6, 0x9B, 0x4C, 0x01, 82 | 0xF4, 0xB9, 0x6E, 0x23, 0x8D, 0xC0, 0x17, 0x5A, 0x06, 0x4B, 0x9C, 0xD1, 0x7F, 0x32, 0xE5, 0xA8}; 83 | 84 | /** 85 | * @brief Reflect data about the center bit. 86 | * 87 | * @remark This implementation has been inspired by Micheal Barr's "CRC Series, 88 | * Part 3: CRC Implementation Code in C/C++" (2000), retrieved Jan 6, 2023: 89 | * https://barrgroup.com/Embedded-Systems/How-To/CRC-Calculation-C-Code 90 | * 91 | * @param data The data to reflect. 92 | * @param size The size of the data being reflected. 93 | * @return The reflected data. 94 | */ 95 | unsigned char reflect(std::uint64_t data, int size); 96 | 97 | /** 98 | * @brief Calculate the CRC value for a message using the CRC8 algorithm. 99 | * 100 | * @remark This implementation has been inspired by Micheal Barr's "CRC Series, 101 | * Part 3: CRC Implementation Code in C/C++" (2000), retrieved Jan 6, 2023: 102 | * https://barrgroup.com/Embedded-Systems/How-To/CRC-Calculation-C-Code 103 | * 104 | * @param data The serial data whose CRC should be calculated. 105 | * @param initial_value The CRC initial value. 106 | * @param final_xor_value The CRC final XOR value. 107 | * @param input_reflected Reflect the input. 108 | * @param result_reflected Reflect the result. 109 | * @param lookup_table The pre-computed lookup table used for CRC calculation. 110 | * @return The calculated CRC8 value for the provided serial data. 111 | */ 112 | unsigned char calculateCrc8( 113 | const std::vector & data, unsigned char initial_value, 114 | unsigned char final_xor_value, bool input_reflected, bool result_reflected, 115 | const std::array & lookup_table); 116 | 117 | /** 118 | * @brief Calculate the CRC value for a message using the Reach serial protocol specification. 119 | * 120 | * @remark This is a wrapper for the @ref calculateCrc8 method defined using the Reach 121 | * protocol. 122 | * 123 | * @param data The serial data whose CRC should be calculated using the Reach specification. 124 | * @return The CRC8 value calculated using the Reach serial protocol. 125 | */ 126 | unsigned char calculateReachCrc8(const std::vector & data); 127 | 128 | } // namespace alpha::driver 129 | -------------------------------------------------------------------------------- /alpha_driver/include/alpha_driver/device_id.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Evan Palmer 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #pragma once 22 | 23 | namespace alpha::driver 24 | { 25 | 26 | /** 27 | * @brief A unique identifier used to denote the manipulator's joints. 28 | */ 29 | enum class DeviceId : unsigned char 30 | { 31 | kLinearJaws = 0x01, 32 | kRotateEndEffector = 0x02, 33 | kBendElbow = 0x03, 34 | kBendShoulder = 0x04, 35 | kRotateBase = 0x05, 36 | kAllJoints = 0xFF, 37 | }; 38 | 39 | } // namespace alpha::driver 40 | -------------------------------------------------------------------------------- /alpha_driver/include/alpha_driver/driver.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Evan Palmer 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "alpha_driver/mode.hpp" 31 | #include "alpha_driver/packet.hpp" 32 | #include "alpha_driver/packet_id.hpp" 33 | #include "alpha_driver/serial_client.hpp" 34 | 35 | namespace alpha::driver 36 | { 37 | 38 | class Driver 39 | { 40 | public: 41 | /** 42 | * @brief Construct a new Driver object. 43 | */ 44 | Driver(); 45 | 46 | /** 47 | * @brief Start the driver. 48 | * 49 | * @note This will attempt to connect the serial client and start the heartbeat. 50 | * 51 | * @param serial_port The serial port that the manipulator is available at. 52 | * @param heartbeat_timeout The maximum time (s) between heartbeat messages before the connection 53 | * is considered timed out. This must be greater than 1 second; defaults to 3 seconds. 54 | */ 55 | void start(const std::string & serial_port, int heartbeat_timeout = 3); 56 | 57 | /** 58 | * @brief Stop the driver. 59 | * 60 | * @note This will stop the heartbeat and disconnect the serial client. 61 | */ 62 | void stop(); 63 | 64 | /** 65 | * @brief Set the mode of a device. 66 | * 67 | * @param mode The desired operating mode. 68 | * @param device The device whose mode should be configured. 69 | */ 70 | void setMode(Mode mode, DeviceId device) const; 71 | 72 | /** 73 | * @brief Set the velocity of a device. 74 | * 75 | * @note If the velocity setpoint is set above the velocity limit, the velocity will be set to the 76 | * limit. 77 | * 78 | * @param velocity The velocity setpoint. For rotational devices, the velocity should be provided 79 | * in rad/s. For linear devices, the velocity should be provided in mm/s. 80 | * @param device The device whose velocity should be set. 81 | */ 82 | void setVelocity(float velocity, DeviceId device) const; 83 | 84 | /** 85 | * @brief Set the position of a device. 86 | * 87 | * @note If the position setpoint is outside of the configured limits, the command is ignored. 88 | * 89 | * @param position The position setpoint. For rotational devices, this should be an angle in the 90 | * range [0, 2pi]. For linear devices, the velocity should be a distance in mm. 91 | * @param device device whose position should be set 92 | */ 93 | void setPosition(float position, DeviceId device) const; 94 | 95 | /** 96 | * @brief Set the relative position of a device. 97 | * 98 | * @note The actuator will move from its current position by the amount specified. 99 | * 100 | * @param relative_position The relative position setpoint. For rotational devices, this should be 101 | * an angle in the range [0, 2pi]. For linear devices, the velocity should be a distance in mm. 102 | * @param device device whose relative position should be set. 103 | */ 104 | void setRelativePosition(float relative_position, DeviceId device) const; 105 | 106 | /** 107 | * @brief Set the current setpoint of the motor windings. 108 | * 109 | * @note Demanding current that is out of range will set the current to its maximum. 110 | * 111 | * @param current The current setpoint (mAh). 112 | * @param device The device whose current should be set. 113 | */ 114 | void setCurrent(float current, DeviceId device) const; 115 | 116 | /** 117 | * @brief Request a packet from the Alpha manipulator. 118 | * 119 | * @remark This intended to be used in conjunction with the @ref subscribe method. 120 | * 121 | * @param packet_type The packet type that the manipulator should send. 122 | * @param device The device that should send the packet. 123 | */ 124 | void request(PacketId packet_type, DeviceId device) const; 125 | 126 | /** 127 | * @brief Request multiple packets from the Alpha manipulator. 128 | * 129 | * @note Up to to 10 packets may be requested at once. 130 | * 131 | * @param packet_types The vector of packet types that the manipulator should send. 132 | * @param device The device that should send the packets. 133 | */ 134 | void request(std::vector & packet_types, DeviceId device) const; 135 | 136 | /** 137 | * @brief Register a callback function to be executed when a packet of the specified type is 138 | * received from the manipulator. 139 | * 140 | * @note This is intended to be used in conjunction with the @ref request method. 141 | * 142 | * @param packet_type The packet type that should signal this function. 143 | * @param callback The function to execute when a packet of the given type is received from the 144 | * manipulator. 145 | */ 146 | void subscribe(PacketId packet_type, const std::function & callback); 147 | 148 | private: 149 | /** 150 | * @brief Send a float message. 151 | * 152 | * @note This is a helper method used to cast a float value to a vector of unsigned chars and 153 | * send the resulting data. 154 | * 155 | * @param value The float value to send. 156 | * @param packet_type The type of packet to construct. 157 | * @param device The device that should receive the message. 158 | */ 159 | void sendFloat(float value, PacketId packet_type, DeviceId device) const; 160 | 161 | /** 162 | * @brief Enable heartbeat messages from the Alpha manipulator. 163 | * 164 | * @note There isn't a single dedicated message available for heartbeat messages. Because of this, 165 | * we specify the Model Number packet as the heartbeat message. 166 | * 167 | * @remark While a more helpful message could be requested as the heartbeat (e.g., position, 168 | * velocity, mode, etc.), the interface would become less usable and a bit more confusing if some 169 | * state messages are broadcasted but others aren't. Furthermore, those messages may not be 170 | * desired by users. Therefore, we leave it up to the users to request state information. 171 | * 172 | * @param freq The frequency (Hz) that the heartbeat packets should be sent at. 173 | */ 174 | void enableHeartbeat(int freq); 175 | 176 | /** 177 | * @brief Disable heartbeat messages from the Alpha manipulator. 178 | * 179 | * @note This is equivalent to setting the heartbeat frequency to 0. 180 | */ 181 | void disableHeartbeat(); 182 | 183 | /** 184 | * @brief Set the frequency that heartbeat messages should be sent. 185 | * 186 | * @param freq The frequency (Hz) that the heartbeat messages should be sent at. 187 | */ 188 | void setHeartbeatFreq(int freq); 189 | 190 | /** 191 | * @brief Monitor the latest heartbeat timestamp to determine whether or not the connection has 192 | * timed out. 193 | * 194 | * @note This method is not designed to be a watchdog. If a timeout occurs, it is the user's 195 | * responsibility to respond. 196 | * 197 | * @param heartbeat_timeout_ms maximum allowable time between heartbeat messages before notifying 198 | * users that a timeout may have occurred 199 | */ 200 | void monitorHeartbeat(int heartbeat_timeout_ms) const; 201 | 202 | SerialClient client_; 203 | 204 | // Heartbeat monitor 205 | std::thread heartbeat_worker_; 206 | std::atomic running_{false}; 207 | mutable std::mutex last_heartbeat_lock_; 208 | std::chrono::time_point last_heartbeat_; 209 | }; 210 | 211 | } // namespace alpha::driver 212 | -------------------------------------------------------------------------------- /alpha_driver/include/alpha_driver/mode.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Evan Palmer 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #pragma once 22 | 23 | namespace alpha::driver 24 | { 25 | 26 | /** 27 | * @brief A possible operating mode for a device. 28 | */ 29 | enum class Mode : unsigned char 30 | { 31 | kStandby = 0x00, 32 | kDisable = 0x01, 33 | kPosition = 0x02, 34 | kVelocity = 0x03, 35 | kCurrent = 0x04, 36 | }; 37 | 38 | } // namespace alpha::driver 39 | -------------------------------------------------------------------------------- /alpha_driver/include/alpha_driver/packet.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Evan Palmer 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #pragma once 22 | 23 | #include 24 | 25 | #include "alpha_driver/device_id.hpp" 26 | #include "alpha_driver/packet_id.hpp" 27 | 28 | namespace alpha::driver 29 | { 30 | 31 | class Packet 32 | { 33 | public: 34 | /** 35 | * @brief Create a new Packet. 36 | * 37 | * @param packet_id The packet type. 38 | * @param device_id The ID of the device that the data targets. 39 | * @param data Unencoded serial data. 40 | */ 41 | Packet(PacketId packet_id, DeviceId device_id, std::vector data); 42 | 43 | /** 44 | * @brief Encode the packet's data using the Reach packet structure specification. 45 | * 46 | * @return Encoded serial data. 47 | */ 48 | std::vector encode() const; 49 | 50 | /** 51 | * @brief Decode a packet that has been encoded using the Reach communication specification. 52 | * 53 | * @param data The encoded serial data. 54 | * @return The packet obtained from the decoded serial data. 55 | */ 56 | static Packet decode(const std::vector & data); 57 | 58 | /** 59 | * @brief Get the unique packet identifier. 60 | * 61 | * @return The packet type. 62 | */ 63 | PacketId getPacketId() const; 64 | 65 | /** 66 | * @brief Get the unique device identifier. 67 | * 68 | * @return The ID of the device that the packet targets. 69 | */ 70 | DeviceId getDeviceId() const; 71 | 72 | /** 73 | * @brief Get the packet data. 74 | * 75 | * @note The packet serial data will never be encoded during the lifetime of the object unless 76 | * the packet is instantiated with the serial data already encoded. 77 | * 78 | * @return The packet serial data. 79 | */ 80 | std::vector getData() const; 81 | 82 | private: 83 | PacketId packet_id_; 84 | DeviceId device_id_; 85 | std::vector data_; 86 | }; 87 | 88 | } // namespace alpha::driver 89 | -------------------------------------------------------------------------------- /alpha_driver/include/alpha_driver/packet_id.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Evan Palmer 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #pragma once 22 | 23 | namespace alpha::driver 24 | { 25 | 26 | /** 27 | * @brief A unique identifier used to determine how to interpret the packet data. 28 | */ 29 | enum class PacketId : unsigned char 30 | { 31 | kMode = 0x01, 32 | kVelocity = 0X02, 33 | kPosition = 0x03, 34 | kCurrent = 0x05, 35 | kRelativePosition = 0x0E, 36 | kIndexedPosition = 0x0D, 37 | kRequest = 0x60, 38 | kSerialNumber = 0x61, 39 | kModelNumber = 0x62, 40 | kTemperature = 0x66, 41 | kSoftwareVersion = 0x6C, 42 | kKmEndPos = 0xA1, 43 | kKmEndVel = 0xA2, 44 | kKmEndVelLocal = 0xCB, 45 | kKmBoxObstacle02 = 0xA5, 46 | kKmBoxObstacle03 = 0xA6, 47 | kKmBoxObstacle04 = 0xA7, 48 | kKmBoxObstacle05 = 0xA8, 49 | kKmCylinderObstacle02 = 0xAB, 50 | kKmCylinderObstacle03 = 0xAC, 51 | kKmCylinderObstacle04 = 0xAD, 52 | kKmCylinderObstacle05 = 0xAE, 53 | kVoltage = 0x90, 54 | kSave = 0x50, 55 | kHeartbeatFreqency = 0x92, 56 | kHeartbeatSet = 0x91, 57 | kPositionLimits = 0x10, 58 | kVelocityLimits = 0x11, 59 | kCurrentLimits = 0x12, 60 | kAtiFtReading = 0xD8, 61 | kBootloader = 0xFF, 62 | kVoltageThresholdParameters = 0x99, 63 | }; 64 | 65 | } // namespace alpha::driver 66 | -------------------------------------------------------------------------------- /alpha_driver/include/alpha_driver/serial_client.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Evan Palmer 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "alpha_driver/packet.hpp" 31 | #include "alpha_driver/packet_id.hpp" 32 | 33 | namespace alpha::driver 34 | { 35 | 36 | class SerialClient 37 | { 38 | public: 39 | /** 40 | * @brief Attempt to establish a connection with the Alpha manipulator. 41 | * 42 | * @note This configures the serial port for R/W operation at a baudrate of 115200. 43 | * 44 | * @param device The full path to the serial device file (e.g., /dev/ttyUSB0). 45 | * @param polling_timeout_ms The timeout (ms) between serial port reads--used for VTIME. 46 | */ 47 | void connect(const std::string & device, int polling_timeout_ms = 500); 48 | 49 | /** 50 | * @brief Shutdown the serial client. 51 | */ 52 | void disconnect(); 53 | 54 | /** 55 | * @brief Send a packet over the serial connection. 56 | * 57 | * @note This method performs packet encoding. It is not necessary to encode the data before 58 | * calling this method. 59 | * 60 | * @param packet The message to send to the Reach Alpha manipulator. 61 | * @return True if the packet was sent successfully, false otherwise. 62 | */ 63 | bool send(const Packet & packet) const; 64 | 65 | /** 66 | * @brief Register a new callback function for a specified packet type. 67 | * 68 | * @param packet_type The type of packet that the callback should be registered to. 69 | * @param callback The function that should be executed when a message of a given type is 70 | * received. 71 | */ 72 | void registerCallback(PacketId packet_type, const std::function & callback); 73 | 74 | /** 75 | * @brief Indicates whether or not the serial client is currently active. 76 | * 77 | * @note To be considered 'active' there must be an open serial connection and a worker should be 78 | * polling the RX. 79 | * 80 | * @return True if the serial client is active, false otherwise. 81 | */ 82 | bool active() const; 83 | 84 | private: 85 | enum class PortState 86 | { 87 | kOpen, // The serial port is currently open 88 | kClosed // The serial port is currently closed 89 | }; 90 | 91 | /** 92 | * @brief Poll the serial line for incoming data. 93 | * 94 | * @note This method is executed by the RX thread. Furthermore, this method is 95 | * a blocking method that runs indefinitely. The main loop is terminated by 96 | * the atomic @ref running_ flag. 97 | */ 98 | void poll() const; 99 | 100 | /** 101 | * @brief Map used to store the callback functions for messages. 102 | * 103 | * @note Keys should be the ID of a message and the values are the callback functions to execute 104 | * when a message the respective packet ID is received. 105 | */ 106 | std::unordered_map>> callbacks_; 107 | 108 | // Serial port poller 109 | int handle_; 110 | std::atomic running_{false}; 111 | PortState port_status_ = PortState::kClosed; 112 | std::thread rx_worker_; 113 | }; 114 | 115 | } // namespace alpha::driver 116 | -------------------------------------------------------------------------------- /alpha_driver/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | alpha_driver 5 | 0.0.1 6 | Serial driver for the Reach Robotics Alpha manipulator 7 | Evan Palmer 8 | MIT 9 | 10 | ament_cmake 11 | 12 | rclcpp 13 | 14 | ament_cmake_gtest 15 | ament_cmake_gmock 16 | ament_lint_auto 17 | ament_lint_common 18 | 19 | 20 | ament_cmake 21 | 22 | 23 | -------------------------------------------------------------------------------- /alpha_driver/src/cobs.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Evan Palmer 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #include "alpha_driver/cobs.hpp" 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | namespace alpha::driver 28 | { 29 | 30 | std::vector cobsEncode(const std::vector & data) 31 | { 32 | // Initialize the encoded data with 0x00 prepended 33 | // this will be overwritten once the count to the next 0x00 is determined 34 | std::vector encoded_data = {0x00}; 35 | 36 | int block_start = 0; 37 | int current_block_size = 0; 38 | 39 | for (const unsigned char it : data) { 40 | if (it == 0x00) { 41 | // Save the total number of elements before the next 0x00 42 | encoded_data[block_start] = static_cast(current_block_size + 1); 43 | 44 | // Add a placeholder 45 | encoded_data.push_back(0x00); 46 | 47 | // Reset the counters 48 | block_start = encoded_data.size() - 1; 49 | current_block_size = 0; 50 | } else { 51 | // Copy over the data 52 | encoded_data.push_back(it); 53 | current_block_size++; 54 | 55 | // Handle the case where the block size is 254 or greater 56 | // Note that the Reach specification dictates that packets may not be larger 57 | // than 254 bytes including the footer; however, we handle this case as a sanity check 58 | if (current_block_size >= 254) { 59 | encoded_data[block_start] = static_cast(current_block_size + 1); 60 | 61 | // Add placeholder 62 | encoded_data.push_back(0x00); 63 | 64 | // Reset counters 65 | block_start = encoded_data.size() - 1; 66 | current_block_size = 0; 67 | } 68 | } 69 | } 70 | 71 | encoded_data[block_start] = static_cast(current_block_size + 1); 72 | encoded_data.push_back(0x00); 73 | 74 | return encoded_data; 75 | } 76 | 77 | std::vector cobsDecode(const std::vector & data) 78 | { 79 | std::vector decoded_data; 80 | std::vector::size_type encoded_data_pos = 0; 81 | 82 | while (encoded_data_pos < data.size()) { 83 | const int block_size = data[encoded_data_pos] - 1; 84 | encoded_data_pos++; 85 | 86 | for (int i = 0; i < block_size; ++i) { 87 | const unsigned char byte = data[encoded_data_pos]; 88 | 89 | if (byte == 0x00) { 90 | throw std::runtime_error("Failed to decode the encoded data."); 91 | } 92 | 93 | decoded_data.push_back(data[encoded_data_pos]); 94 | encoded_data_pos++; 95 | } 96 | 97 | if (data[encoded_data_pos] == 0x00) { 98 | break; 99 | } 100 | 101 | if (block_size < 0xFE) { 102 | decoded_data.push_back(0x00); 103 | } 104 | } 105 | 106 | return decoded_data; 107 | } 108 | 109 | } // namespace alpha::driver 110 | -------------------------------------------------------------------------------- /alpha_driver/src/crc.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Evan Palmer 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #include "alpha_driver/crc.hpp" 22 | 23 | namespace alpha::driver 24 | { 25 | 26 | unsigned char reflect(std::uint64_t data, int size) 27 | { 28 | std::uint64_t reflection = 0x00000000; 29 | 30 | for (int bit = 0; bit < size; ++bit) { 31 | if (data & 0x01) { 32 | reflection |= (1 << ((size - 1) - bit)); 33 | } 34 | 35 | data = (data >> 1); 36 | } 37 | 38 | return static_cast(reflection); 39 | } 40 | 41 | unsigned char calculateCrc8( 42 | const std::vector & data, unsigned char initial_value, 43 | unsigned char final_xor_value, bool input_reflected, bool result_reflected, 44 | const std::array & lookup_table) 45 | { 46 | unsigned char crc = initial_value; 47 | unsigned char value; 48 | 49 | const int width = (8 * sizeof(crc)); 50 | 51 | for (const unsigned char byte : data) { 52 | // Reflect the data 53 | if (input_reflected) { 54 | value = reflect(byte, 8); 55 | } else { 56 | value = byte; 57 | } 58 | 59 | value ^= crc >> (width - 8); 60 | crc = lookup_table[value] ^ (crc << 8); 61 | } 62 | 63 | // Reflect the result 64 | if (result_reflected) { 65 | crc = reflect(crc, width); 66 | } 67 | 68 | return crc ^ final_xor_value; 69 | } 70 | 71 | unsigned char calculateReachCrc8(const std::vector & data) 72 | { 73 | return calculateCrc8( 74 | data, kInitialValue, kFinalXorValue, kInputReflected, kResultReflected, kCrc8LookupTable); 75 | } 76 | 77 | } // namespace alpha::driver 78 | -------------------------------------------------------------------------------- /alpha_driver/src/driver.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Evan Palmer 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #include "alpha_driver/driver.hpp" 22 | 23 | #include 24 | 25 | #include "alpha_driver/device_id.hpp" 26 | #include "alpha_driver/packet_id.hpp" 27 | #include "rclcpp/rclcpp.hpp" 28 | 29 | using namespace std::chrono_literals; 30 | 31 | namespace alpha::driver 32 | { 33 | 34 | Driver::Driver() 35 | { 36 | subscribe(PacketId::kModelNumber, [this](const Packet &) -> void { 37 | const std::lock_guard lock(last_heartbeat_lock_); 38 | last_heartbeat_ = std::chrono::steady_clock::now(); 39 | }); 40 | } 41 | 42 | void Driver::start(const std::string & serial_port, int heartbeat_timeout) 43 | { 44 | if (heartbeat_timeout < 1) { 45 | throw std::invalid_argument("The heartbeat timeout must be greater than 1 second."); 46 | } 47 | 48 | // Attempt to connect the serial client 49 | // We don't expose the VTIME timeout to the user API to avoid usability concerns 50 | client_.connect(serial_port); 51 | 52 | // Disable any previous heartbeat configurations 53 | disableHeartbeat(); 54 | 55 | // Configure the new heartbeat request 56 | // The heartbeat has a minimum frequency of one message per second, so we set it to that 57 | enableHeartbeat(1); 58 | 59 | { 60 | const std::lock_guard lock(last_heartbeat_lock_); 61 | last_heartbeat_ = std::chrono::steady_clock::now(); 62 | } 63 | 64 | running_.store(true); 65 | 66 | // Start the thread that monitors heartbeats from the manipulator 67 | heartbeat_worker_ = std::thread(&Driver::monitorHeartbeat, this, heartbeat_timeout); 68 | } 69 | 70 | void Driver::stop() 71 | { 72 | disableHeartbeat(); 73 | running_.store(false); 74 | heartbeat_worker_.join(); 75 | client_.disconnect(); 76 | } 77 | 78 | void Driver::setMode(Mode mode, DeviceId device) const 79 | { 80 | if (!running_.load() || !client_.active()) { 81 | throw std::runtime_error( 82 | "Cannot send a message to the manipulator without an active connection!"); 83 | } 84 | 85 | const std::vector mode_setting = {static_cast(mode)}; 86 | 87 | const Packet packet(PacketId::kMode, device, mode_setting); 88 | 89 | client_.send(packet); 90 | } 91 | 92 | void Driver::setVelocity(float velocity, DeviceId device) const 93 | { 94 | sendFloat(velocity, PacketId::kVelocity, device); 95 | } 96 | 97 | void Driver::setPosition(float position, DeviceId device) const 98 | { 99 | sendFloat(position, PacketId::kPosition, device); 100 | } 101 | 102 | void Driver::setRelativePosition(float relative_position, DeviceId device) const 103 | { 104 | sendFloat(relative_position, PacketId::kRelativePosition, device); 105 | } 106 | 107 | void Driver::setCurrent(float current, DeviceId device) const 108 | { 109 | sendFloat(current, PacketId::kCurrent, device); 110 | } 111 | 112 | void Driver::request(PacketId packet_type, DeviceId device) const 113 | { 114 | if (!running_.load() || !client_.active()) { 115 | throw std::runtime_error( 116 | "Cannot send a request to the manipulator without an active connection!"); 117 | } 118 | 119 | const std::vector request_type = {static_cast(packet_type)}; 120 | 121 | const Packet packet(PacketId::kRequest, device, request_type); 122 | 123 | client_.send(packet); 124 | } 125 | 126 | void Driver::request(std::vector & packet_types, DeviceId device) const 127 | { 128 | if (packet_types.size() > 10) { 129 | throw std::invalid_argument("Cannot request more than 10 packets from a device at once."); 130 | } 131 | 132 | if (!running_.load() || !client_.active()) { 133 | throw std::runtime_error( 134 | "Cannot send a request to the manipulator without an active connection!"); 135 | } 136 | 137 | std::vector request_types(packet_types.size()); 138 | 139 | // Cast to unsigned char 140 | for (auto type : packet_types) { 141 | request_types.push_back(static_cast(type)); 142 | } 143 | 144 | const Packet packet(PacketId::kRequest, device, request_types); 145 | 146 | client_.send(packet); 147 | } 148 | 149 | void Driver::subscribe(PacketId packet_type, const std::function & callback) 150 | { 151 | client_.registerCallback(packet_type, callback); 152 | } 153 | 154 | void Driver::sendFloat(float value, PacketId packet_type, DeviceId device_id) const 155 | { 156 | if (!running_.load() || !client_.active()) { 157 | throw std::runtime_error( 158 | "Cannot send a message to the manipulator without an active connection!"); 159 | } 160 | 161 | // Convert the float into a vector of bytes 162 | std::vector reinterpretted_vector(sizeof(value)); 163 | std::memcpy(reinterpretted_vector.data(), &value, sizeof(value)); 164 | 165 | const Packet packet(packet_type, device_id, reinterpretted_vector); 166 | 167 | client_.send(packet); 168 | } 169 | 170 | void Driver::enableHeartbeat(int freq) 171 | { 172 | // We request the model number as the heartbeat because there isn't an official heartbeat message 173 | const std::vector heartbeat_config = { 174 | static_cast(alpha::driver::PacketId::kModelNumber)}; 175 | 176 | const Packet packet(PacketId::kHeartbeatSet, DeviceId::kAllJoints, heartbeat_config); 177 | 178 | client_.send(packet); 179 | 180 | setHeartbeatFreq(freq); 181 | } 182 | 183 | void Driver::disableHeartbeat() { setHeartbeatFreq(0); } 184 | 185 | void Driver::setHeartbeatFreq(int freq) 186 | { 187 | const std::vector heartbeat_frequency = {static_cast(freq)}; 188 | const Packet packet(PacketId::kHeartbeatFreqency, DeviceId::kAllJoints, heartbeat_frequency); 189 | client_.send(packet); 190 | } 191 | 192 | void Driver::monitorHeartbeat(int heartbeat_timeout) const 193 | { 194 | while (running_.load()) { 195 | // Make sure that the lock is properly scoped so that we don't accidentally keep the lock 196 | // forever 197 | { 198 | const std::lock_guard lock(last_heartbeat_lock_); 199 | 200 | if ( 201 | std::chrono::steady_clock::now() - last_heartbeat_ > 202 | std::chrono::seconds(heartbeat_timeout)) { 203 | RCLCPP_WARN( // NOLINT 204 | rclcpp::get_logger("AlphaDriver"), 205 | "Timeout occurred; the system has not received a heartbeat message in the last %d " 206 | "seconds", 207 | heartbeat_timeout); 208 | } 209 | } 210 | 211 | // We don't need to check more often than our timeout requires us to 212 | std::this_thread::sleep_for(std::chrono::seconds(heartbeat_timeout)); 213 | } 214 | } 215 | 216 | } // namespace alpha::driver 217 | -------------------------------------------------------------------------------- /alpha_driver/src/packet.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Evan Palmer 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #include "alpha_driver/packet.hpp" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "alpha_driver/cobs.hpp" 29 | #include "alpha_driver/crc.hpp" 30 | 31 | namespace alpha::driver 32 | { 33 | 34 | Packet::Packet(PacketId packet_id, DeviceId device_id, std::vector data) 35 | : packet_id_(packet_id), 36 | device_id_(device_id), 37 | data_(std::move(data)) 38 | { 39 | if (data_.empty()) { 40 | throw std::invalid_argument("Cannot create a packet with no data."); 41 | } 42 | } 43 | 44 | std::vector Packet::encode() const 45 | { 46 | std::vector data(data_); 47 | 48 | // Add the packet ID and the device ID 49 | // Note that we need to type cast to the underlying type because enum classes 50 | // don't implicitly cast to ints (which is a good thing) 51 | data.push_back(static_cast(packet_id_)); 52 | data.push_back(static_cast(device_id_)); 53 | 54 | // Length is the current buffer size plus two (length and CRC) 55 | data.push_back(data.size() + 2); 56 | 57 | // Calculate the CRC from the data and add it to the buffer 58 | data.push_back(calculateReachCrc8(data)); 59 | 60 | // Encode the data using COBS encoding 61 | std::vector encoded_data = cobsEncode(data); 62 | 63 | return encoded_data; 64 | } 65 | 66 | Packet Packet::decode(const std::vector & data) 67 | { 68 | if (data.empty()) { 69 | throw std::invalid_argument("An empty data packet was received for decoding."); 70 | } 71 | 72 | // Note that an exception will be raised if the decoding fails 73 | std::vector decoded_data = cobsDecode(data); 74 | 75 | if (decoded_data.empty()) { 76 | throw std::runtime_error("Decoded data is empty"); 77 | } 78 | 79 | // Pop the CRC and make sure that it is defined correctly 80 | const unsigned char actual_crc = decoded_data.back(); 81 | decoded_data.pop_back(); 82 | 83 | const unsigned char expected_crc = calculateReachCrc8(decoded_data); 84 | 85 | if (actual_crc != expected_crc) { 86 | throw std::runtime_error("The expected and actual CRC values do not match."); 87 | } 88 | 89 | // Pop the packet length to ensure that a packet of the correct size was 90 | // provided 91 | const auto length = static_cast::size_type>(decoded_data.back()); 92 | decoded_data.pop_back(); 93 | 94 | if ((decoded_data.size() + 2) != length) { 95 | throw std::runtime_error("The specified payload size is not equal to the actual payload size."); 96 | } 97 | 98 | // Get the device ID 99 | const unsigned char device_id = decoded_data.back(); 100 | decoded_data.pop_back(); 101 | 102 | // Get the packet ID 103 | const unsigned char packet_id = decoded_data.back(); 104 | decoded_data.pop_back(); 105 | 106 | return Packet(static_cast(packet_id), static_cast(device_id), decoded_data); 107 | } 108 | 109 | PacketId Packet::getPacketId() const { return packet_id_; } 110 | 111 | DeviceId Packet::getDeviceId() const { return device_id_; } 112 | 113 | std::vector Packet::getData() const { return data_; } 114 | 115 | } // namespace alpha::driver 116 | -------------------------------------------------------------------------------- /alpha_driver/src/serial_client.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Evan Palmer 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #include "alpha_driver/serial_client.hpp" 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "alpha_driver/packet.hpp" 35 | #include "rclcpp/rclcpp.hpp" 36 | 37 | using namespace std::chrono_literals; 38 | 39 | namespace alpha::driver 40 | { 41 | 42 | void SerialClient::connect(const std::string & device, const int polling_timeout_ms) 43 | { 44 | if (device.empty()) { 45 | throw std::invalid_argument("Attempted to open file using an unassigned file path."); 46 | } 47 | 48 | handle_ = open(device.c_str(), O_RDWR); 49 | 50 | if (handle_ == -1) { 51 | throw std::runtime_error( 52 | "Could not open specified serial device. Please verify that the device is not already being " 53 | "used and that you have configured read/write permissions."); 54 | } 55 | 56 | port_status_ = PortState::kOpen; 57 | 58 | struct termios tty; 59 | 60 | // Get the terminal existing settings 61 | if (tcgetattr(handle_, &tty) < 0) { 62 | throw std::runtime_error("Unable to get the current terminal configurations."); 63 | } 64 | 65 | // Set the baudrate to 115200 as defined by the Alpha Reach specification 66 | cfsetispeed(&tty, B115200); 67 | cfsetospeed(&tty, B115200); 68 | 69 | tty.c_cflag |= (CLOCAL | CREAD); // Enable the receiver and set the mode to local mode 70 | tty.c_cflag &= ~CSIZE; // Mask the character size bits 71 | tty.c_cflag |= CS8; // Use 8 data bits per byte 72 | tty.c_cflag &= ~PARENB; // Disable parity 73 | tty.c_cflag &= ~CSTOPB; // Only one stop bit is used 74 | tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control 75 | tty.c_lflag &= ~ICANON; // Disable canonical input 76 | tty.c_lflag &= ~ECHO; // Disable echo 77 | tty.c_lflag &= ~ECHOE; // Disable erasure 78 | tty.c_lflag &= ~ECHONL; // Disable new-line echo 79 | tty.c_lflag &= ~ISIG; // Disable signals 80 | tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off IO control 81 | tty.c_iflag &= 82 | ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | 83 | ICRNL); // Disable special handling of received bytes 84 | 85 | tty.c_oflag &= ~OPOST; // Disable output post-processing 86 | tty.c_oflag &= ~ONLCR; // Disable conversion of newline to carriage return 87 | 88 | tty.c_cc[VTIME] = 89 | static_cast(polling_timeout_ms / 100); // Set the timeout; convert to deciseconds 90 | tty.c_cc[VMIN] = 0; // We can't set this in a thread otherwise it may block indefinitely 91 | 92 | // Save the configurations 93 | if (tcsetattr(handle_, TCSANOW, &tty) != 0) { 94 | throw std::runtime_error("Unable to save the terminal configurations."); 95 | } 96 | 97 | // Indicate that we are now running 98 | // Used by the rx_worker_ main loop 99 | running_.store(true); 100 | 101 | // Start reading data from the serial port 102 | rx_worker_ = std::thread(&SerialClient::poll, this); 103 | 104 | // Give the RX thread a few ms to start up 105 | std::this_thread::sleep_for(10ms); 106 | 107 | // Final check to make sure that the client was properly connected 108 | if (!active()) { 109 | throw std::runtime_error( 110 | "An error occurred while attempting to establish a serial connection."); 111 | } 112 | } 113 | 114 | void SerialClient::disconnect() 115 | { 116 | running_.store(false); 117 | rx_worker_.join(); 118 | close(handle_); 119 | } 120 | 121 | bool SerialClient::send(const Packet & packet) const 122 | { 123 | std::vector encoded_data = packet.encode(); 124 | 125 | if (write(handle_, encoded_data.data(), encoded_data.size()) < 0) { 126 | RCLCPP_WARN( // NOLINT 127 | rclcpp::get_logger("SerialClient"), "Failed to write the encoded packet to the serial port."); 128 | 129 | return false; 130 | } 131 | 132 | return true; 133 | } 134 | 135 | void SerialClient::registerCallback( 136 | PacketId packet_type, const std::function & callback) 137 | { 138 | callbacks_[packet_type].push_back(callback); 139 | } 140 | 141 | bool SerialClient::active() const { return (running_.load() && port_status_ == PortState::kOpen); } 142 | 143 | void SerialClient::poll() const 144 | { 145 | std::array data; 146 | 147 | // Start by waiting for the end of a packet 148 | // Once we have reached the end of the packet then we can start processing data like normal 149 | while (running_.load()) { 150 | const int size = read(handle_, &data, 1); 151 | 152 | if (size > 0 && data[0] == 0) { 153 | break; 154 | } 155 | } 156 | 157 | // Create a buffer to store incoming data 158 | std::vector buffer; 159 | 160 | // Now we can start processing data 161 | while (running_.load()) { 162 | // Note that we have to read byte-by-byte. This is because the Reach protocol doesn't include 163 | // a header which defines the size of the packet. Instead we have to read until there is a 164 | // packet delimiter (0x00) and process that data. 165 | const int size = read(handle_, &data, 1); 166 | 167 | if (size < 0) { 168 | RCLCPP_WARN( // NOLINT 169 | rclcpp::get_logger("SerialClient"), 170 | "An error occurred while attempting to read a message from the serial port."); 171 | } else if (size > 0) { 172 | buffer.push_back(data[0]); 173 | 174 | // We have reached the end of the packet; now try to decode it 175 | if (data[0] == 0) { 176 | try { 177 | const Packet packet = Packet::decode(buffer); 178 | 179 | // We need to use the find method here instead of a normal [] indexing operation because 180 | // the [] does not have const overloading which we want for this to be thread-safe 181 | auto it = callbacks_.find(packet.getPacketId()); 182 | 183 | // If a callback exists for the message, execute it 184 | if (it != callbacks_.end()) { 185 | for (const auto & callback : it->second) { 186 | callback(packet); 187 | } 188 | } 189 | } 190 | catch (const std::exception & e) { 191 | RCLCPP_DEBUG(rclcpp::get_logger("SerialClient"), e.what()); // NOLINT 192 | } 193 | 194 | // Empty the buffer before we start reading the next packet 195 | buffer.clear(); 196 | } 197 | } 198 | } 199 | } 200 | 201 | } // namespace alpha::driver 202 | -------------------------------------------------------------------------------- /alpha_driver/test/test_cobs.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Evan Palmer 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #include 22 | 23 | #include "alpha_driver/cobs.hpp" 24 | 25 | namespace alpha::driver::test 26 | { 27 | 28 | TEST(CobsTest, EncodesData) 29 | { 30 | const std::vector data = {0x23, 0x00, 0xD4, 0x81, 0x00, 0xFA}; 31 | const std::vector encoded_data = {0x02, 0x23, 0x03, 0xD4, 0x81, 0x02, 0xFA, 0x00}; 32 | 33 | ASSERT_THAT(alpha::driver::cobsEncode(data), ::testing::ElementsAreArray(encoded_data)); 34 | } 35 | 36 | TEST(CobsTest, DecodesData) 37 | { 38 | const std::vector encoded_data = {0x02, 0x23, 0x03, 0xD4, 0x81, 0x02, 0xFA, 0x00}; 39 | const std::vector decoded_data = {0x23, 0x00, 0xD4, 0x81, 0x00, 0xFA}; 40 | 41 | ASSERT_THAT(alpha::driver::cobsDecode(encoded_data), ::testing::ElementsAreArray(decoded_data)); 42 | } 43 | 44 | } // namespace alpha::driver::test 45 | 46 | int main(int argc, char ** argv) 47 | { 48 | ::testing::InitGoogleTest(&argc, argv); 49 | const int result = RUN_ALL_TESTS(); 50 | 51 | return result; 52 | } 53 | -------------------------------------------------------------------------------- /alpha_driver/test/test_crc.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Evan Palmer 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #include 22 | 23 | #include "alpha_driver/crc.hpp" 24 | 25 | namespace alpha::driver::test 26 | { 27 | 28 | TEST(CrcTest, CalculatesReachCrc) 29 | { 30 | const std::vector message = {0xFF, 0x12, 0xAD, 0x23, 0x56}; 31 | const unsigned char expected_crc = 0xF3; 32 | 33 | EXPECT_EQ(alpha::driver::calculateReachCrc8(message), expected_crc); 34 | } 35 | 36 | } // namespace alpha::driver::test 37 | 38 | int main(int argc, char ** argv) 39 | { 40 | ::testing::InitGoogleTest(&argc, argv); 41 | const int result = RUN_ALL_TESTS(); 42 | 43 | return result; 44 | } 45 | -------------------------------------------------------------------------------- /alpha_driver/test/test_packet.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Evan Palmer 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #include 22 | 23 | #include "alpha_driver/crc.hpp" 24 | #include "alpha_driver/packet.hpp" 25 | #include "alpha_driver/packet_id.hpp" 26 | #include "alpha_driver/serial_client.hpp" 27 | 28 | namespace alpha::driver::test 29 | { 30 | 31 | TEST(PacketTest, TestPacketEncode) 32 | { 33 | const std::vector data = { 34 | static_cast(alpha::driver::PacketId::kPosition)}; 35 | 36 | // Create an encoded test packet using the Reach structure based off of the test 37 | // data 38 | const std::vector expected_encoding = {0x06, 0x03, 0x60, 0x01, 0x05, 0x52, 0x00}; 39 | 40 | // Construct a new packet using the data and the expected IDs 41 | auto packet = alpha::driver::Packet( 42 | alpha::driver::PacketId::kRequest, alpha::driver::DeviceId::kLinearJaws, data); 43 | 44 | ASSERT_THAT(packet.encode(), ::testing::ElementsAreArray(expected_encoding)); 45 | } 46 | 47 | TEST(PacketTest, TestPacketDecode) 48 | { 49 | const std::vector encoded_data = {0x09, 0x01, 0x02, 0x03, 0x04, 50 | 0x01, 0xFF, 0x08, 0x5D, 0x00}; 51 | 52 | const std::vector decoded_data = {0x01, 0x02, 0x03, 0x04}; 53 | 54 | const alpha::driver::Packet packet = alpha::driver::Packet::decode(encoded_data); 55 | 56 | ASSERT_THAT(packet.getData(), ::testing::ElementsAreArray(decoded_data)); 57 | } 58 | 59 | TEST(PacketTest, TestInvalidDecoding) 60 | { 61 | const std::vector decoded_data = {0x01, 0x02, 0x03, 0x04}; 62 | 63 | // Cannot decoded data that has already been decoded 64 | ASSERT_THROW(alpha::driver::Packet::decode(decoded_data), std::runtime_error); 65 | } 66 | 67 | TEST(PacketTest, TestInvalidPacketConstruction) 68 | { 69 | const std::vector empty_data = {}; 70 | 71 | ASSERT_THROW( 72 | alpha::driver::Packet( 73 | alpha::driver::PacketId::kVelocity, alpha::driver::DeviceId::kAllJoints, empty_data), 74 | std::invalid_argument); 75 | } 76 | 77 | } // namespace alpha::driver::test 78 | 79 | int main(int argc, char ** argv) 80 | { 81 | ::testing::InitGoogleTest(&argc, argv); 82 | const int result = RUN_ALL_TESTS(); 83 | 84 | return result; 85 | } 86 | -------------------------------------------------------------------------------- /alpha_hardware/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(alpha_hardware) 3 | 4 | # Default to C99 5 | if(NOT CMAKE_C_STANDARD) 6 | set(CMAKE_C_STANDARD 99) 7 | endif() 8 | 9 | # Default to C++ 17 10 | if(NOT CMAKE_CXX_STANDARD) 11 | set(CMAKE_CXX_STANDARD 17) 12 | endif() 13 | 14 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") 15 | add_compile_options(-Wall -Wextra -Wpedantic) 16 | endif() 17 | 18 | set(THIS_PACKAGE_INCLUDE_DEPENDS 19 | rclcpp 20 | ament_cmake 21 | alpha_driver 22 | rclcpp_lifecycle 23 | pluginlib 24 | hardware_interface 25 | ) 26 | 27 | foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS}) 28 | find_package(${Dependency} REQUIRED) 29 | endforeach() 30 | 31 | include_directories( 32 | include 33 | ) 34 | 35 | add_library(${PROJECT_NAME} SHARED 36 | src/hardware.cpp 37 | ) 38 | target_include_directories(${PROJECT_NAME} PRIVATE include) 39 | ament_target_dependencies(${PROJECT_NAME} ${THIS_PACKAGE_INCLUDE_DEPENDS}) 40 | 41 | pluginlib_export_plugin_description_file(hardware_interface alpha_hardware.xml) 42 | 43 | install( 44 | TARGETS ${PROJECT_NAME} 45 | DESTINATION lib/${PROJECT_NAME} 46 | ) 47 | 48 | install(TARGETS ${PROJECT_NAME} 49 | RUNTIME DESTINATION bin 50 | LIBRARY DESTINATION lib 51 | ARCHIVE DESTINATION lib 52 | ) 53 | 54 | install( 55 | DIRECTORY include/ 56 | DESTINATION include 57 | ) 58 | 59 | if(BUILD_TESTING) 60 | find_package(ament_lint_auto REQUIRED) 61 | 62 | # Run linters found in package.xml except the two below 63 | set(ament_cmake_copyright_FOUND TRUE) 64 | set(ament_cmake_uncrustify_FOUND TRUE) 65 | 66 | ament_lint_auto_find_test_dependencies() 67 | endif() 68 | 69 | ament_export_include_directories(include) 70 | ament_export_libraries(${PROJECT_NAME}) 71 | ament_export_dependencies(${THIS_PACKAGE_INCLUDE_DEPENDS}) 72 | 73 | ament_package() 74 | -------------------------------------------------------------------------------- /alpha_hardware/LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in 9 | all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 14 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 17 | THE SOFTWARE. 18 | -------------------------------------------------------------------------------- /alpha_hardware/alpha_hardware.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Reach Alpha manipulator plugin for ros2_control using a system hardware 7 | interface-type. 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /alpha_hardware/include/alpha_hardware/hardware.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Evan Palmer 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "alpha_driver/driver.hpp" 30 | #include "alpha_driver/packet.hpp" 31 | #include "hardware_interface/handle.hpp" 32 | #include "hardware_interface/hardware_info.hpp" 33 | #include "hardware_interface/system_interface.hpp" 34 | #include "hardware_interface/types/hardware_interface_return_values.hpp" 35 | #include "hardware_interface/visibility_control.h" 36 | #include "rclcpp/clock.hpp" 37 | #include "rclcpp/duration.hpp" 38 | #include "rclcpp/macros.hpp" 39 | #include "rclcpp/time.hpp" 40 | #include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp" 41 | #include "rclcpp_lifecycle/state.hpp" 42 | 43 | namespace alpha::hardware 44 | { 45 | 46 | class AlphaHardware : public hardware_interface::SystemInterface 47 | { 48 | public: 49 | RCLCPP_SHARED_PTR_DEFINITIONS(AlphaHardware) 50 | 51 | hardware_interface::CallbackReturn on_init( 52 | const hardware_interface::HardwareInfo & info) override; 53 | 54 | hardware_interface::CallbackReturn on_configure( 55 | const rclcpp_lifecycle::State & previous_state) override; 56 | 57 | hardware_interface::CallbackReturn on_cleanup( 58 | const rclcpp_lifecycle::State & previous_state) override; 59 | 60 | hardware_interface::return_type prepare_command_mode_switch( 61 | const std::vector & start_interfaces, 62 | const std::vector & stop_interfaces) override; 63 | 64 | hardware_interface::return_type perform_command_mode_switch( 65 | const std::vector & start_interfaces, 66 | const std::vector & stop_interfaces) override; 67 | 68 | std::vector export_state_interfaces() override; 69 | 70 | std::vector export_command_interfaces() override; 71 | 72 | hardware_interface::CallbackReturn on_activate( 73 | const rclcpp_lifecycle::State & previous_state) override; 74 | 75 | hardware_interface::CallbackReturn on_deactivate( 76 | const rclcpp_lifecycle::State & previous_state) override; 77 | 78 | hardware_interface::return_type read( 79 | const rclcpp::Time & time, const rclcpp::Duration & period) override; 80 | 81 | hardware_interface::return_type write( 82 | const rclcpp::Time & time, const rclcpp::Duration & period) override; 83 | 84 | private: 85 | enum class ControlMode 86 | { 87 | kVelocity, 88 | kPosition, 89 | }; 90 | 91 | /** 92 | * @brief Write the current position of the robot received from the serial client to the 93 | * respective asynchronous vector. 94 | * 95 | * @param packet The position packet that signaled the callback. 96 | */ 97 | void updatePositionCb(const alpha::driver::Packet & packet); 98 | 99 | /** 100 | * @brief Write the current velocity of the robot received from the serial client to the 101 | * respective asynchronous vector. 102 | * 103 | * @param packet The velocity packet that signaled the callback. 104 | */ 105 | void updateVelocityCb(const alpha::driver::Packet & packet); 106 | 107 | /** 108 | * @brief Asynchronously read the current state of the robot by polling the robot serial 109 | * interface. 110 | * 111 | * @param freq The frequency (Hz) that the interface should poll the current robot state at. 112 | */ 113 | void pollState(int freq) const; 114 | 115 | // Driver things 116 | alpha::driver::Driver driver_; 117 | std::thread state_request_worker_; 118 | std::atomic running_{false}; 119 | 120 | // ROS parameters 121 | std::string serial_port_; 122 | int state_update_freq_; 123 | 124 | // ros2_control command interfaces 125 | std::vector hw_commands_velocities_; 126 | std::vector hw_commands_positions_; 127 | 128 | // ros2_control state interfaces 129 | std::vector hw_states_positions_, async_states_positions_; 130 | std::vector hw_states_velocities_, async_states_velocities_; 131 | std::vector control_modes_; 132 | 133 | std::mutex access_async_states_; 134 | }; 135 | 136 | } // namespace alpha::hardware 137 | -------------------------------------------------------------------------------- /alpha_hardware/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | alpha_hardware 5 | 0.0.1 6 | Reach Alpha manipulator hardware interface for ros2_control 7 | Evan Palmer 8 | MIT 9 | 10 | ament_cmake 11 | 12 | rclcpp 13 | ros2_control 14 | ros2_controllers 15 | hardware_interface 16 | pluginlib 17 | alpha_driver 18 | 19 | ament_lint_auto 20 | ament_lint_common 21 | 22 | controller_manager 23 | xacro 24 | 25 | 26 | ament_cmake 27 | 28 | 29 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | # Development tools that are not required for project installation 2 | pre-commit 3 | mypy 4 | isort 5 | flake8 6 | black 7 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 88 3 | exclude = *.egg,build,install,temp,devel,.mypy_cache,__pycache__ 4 | select = E,W,F 5 | doctests = True 6 | verbose = 2 7 | ignore = 8 | E203 9 | W503 10 | 11 | [isort] 12 | profile=black 13 | 14 | [pydocstyle] 15 | ignore = D100 16 | convention = google 17 | --------------------------------------------------------------------------------