├── .colcon
├── build
│ └── .gitkeep
├── install
│ └── .gitkeep
└── log
│ └── .gitkeep
├── .docker
├── Dockerfile
└── entrypoint.sh
├── .github
└── workflows
│ ├── lint.yaml
│ └── ros2.yaml
├── .gitignore
├── .pre-commit-config.yaml
├── .readthedocs.yaml
├── LICENSE
├── README.md
├── dependencies.repos
├── docker-compose.yml
├── docs
├── COLCON_IGNORE
├── Makefile
├── make.bat
├── requirements.txt
└── source
│ ├── conf.py
│ ├── demos
│ ├── docs
│ │ └── panda_collecting_toycars.rst
│ └── index.rst
│ └── index.rst
├── grab2_behavior_tree
├── CMakeLists.txt
├── behavior_trees
│ ├── bt_groot2.btproj
│ ├── collect_cubes.xml
│ ├── collect_toys.xml
│ ├── lookaround_demo.xml
│ ├── stack_cubes.xml
│ └── target_ik_demo.xml
├── include
│ └── grab2_behavior_tree
│ │ ├── detect_object.hpp
│ │ ├── get_grasp.hpp
│ │ ├── get_grasp_hardcoded.hpp
│ │ ├── get_trajectory_hardcoded.hpp
│ │ ├── grip.hpp
│ │ ├── move.hpp
│ │ ├── plan.hpp
│ │ └── utils.hpp
├── launch
│ ├── bt_demo.launch.py
│ └── grab2_engine.launch.py
├── package.xml
├── plugins
│ ├── detect_object.cpp
│ ├── get_grasp.cpp
│ ├── get_grasp_hardcoded.cpp
│ ├── get_trajectory_hardcoded.cpp
│ ├── grip.cpp
│ ├── move.cpp
│ └── plan.cpp
└── src
│ └── main.cpp
├── grab2_controller
├── CMakeLists.txt
├── config
│ ├── panda
│ │ ├── initial_positions.yaml
│ │ └── ros2_controllers.yaml
│ ├── rviz2
│ │ ├── 3d_detection_cfg.yaml
│ │ └── rviz2_config.rviz
│ └── ur
│ │ ├── initial_positions.yaml
│ │ └── ros2_controllers.yaml
├── description
│ ├── panda
│ │ ├── panda.ros2_control.xacro
│ │ └── panda.urdf.xacro
│ ├── robotiq_2f_140
│ │ ├── README.md
│ │ ├── meshes
│ │ │ ├── collision
│ │ │ │ ├── robotiq_arg2f_140_inner_finger.stl
│ │ │ │ ├── robotiq_arg2f_140_inner_knuckle.stl
│ │ │ │ ├── robotiq_arg2f_140_outer_finger.stl
│ │ │ │ ├── robotiq_arg2f_140_outer_knuckle.stl
│ │ │ │ ├── robotiq_arg2f_base_link.stl
│ │ │ │ └── robotiq_arg2f_coupling.stl
│ │ │ └── visual
│ │ │ │ ├── robotiq_arg2f_140_inner_finger.stl
│ │ │ │ ├── robotiq_arg2f_140_inner_knuckle.stl
│ │ │ │ ├── robotiq_arg2f_140_outer_finger.stl
│ │ │ │ ├── robotiq_arg2f_140_outer_knuckle.stl
│ │ │ │ ├── robotiq_arg2f_base_link.stl
│ │ │ │ └── robotiq_arg2f_coupling.stl
│ │ └── robotiq_2f_140.xacro
│ └── ur
│ │ ├── ur.ros2_control.xacro
│ │ └── ur.urdf.xacro
├── launch
│ ├── franka_controllers.launch.py
│ ├── ur10e_suction_gripper_controllers.launch.py
│ └── ur5e_robotiq_2f_140_controllers.launch.py
└── package.xml
├── grab2_curobo_planner
├── config
│ ├── table.yaml
│ └── toybox.yaml
├── grab2_curobo_planner
│ ├── __init__.py
│ ├── curobo
│ │ ├── __init__.py
│ │ └── motion_generation.py
│ └── planning_server.py
├── launch
│ └── planning_server.launch.py
├── package.xml
├── resource
│ └── grab2_curobo_planner
├── setup.cfg
├── setup.py
└── test
│ ├── test_copyright.py
│ ├── test_flake8.py
│ └── test_pep257.py
├── grab2_grasp_generator
├── CMakeLists.txt
├── config
│ └── isaac_grasp.yaml
├── include
│ └── grab2_grasp_generator
│ │ ├── grasp_generator.hpp
│ │ └── visibility_control.h
├── package.xml
├── src
│ ├── grasp_generator.cpp
│ └── main.cpp
└── test
│ ├── CMakeLists.txt
│ └── test_grasp_generator.cpp
├── grab2_msgs
├── CMakeLists.txt
├── action
│ ├── ComputePlanThroughPoses.action
│ ├── ComputePlanToPose.action
│ └── GetIsaacGrasp.action
└── package.xml
├── grab2_planner
├── CMakeLists.txt
├── config
│ └── panda_move_group.rviz
├── include
│ └── grab2_planner
│ │ └── planner_server.hpp
├── launch
│ └── planning_server.launch.py
├── package.xml
└── src
│ ├── main.cpp
│ └── planner_server.cpp
├── grab2_ros_common
├── CMakeLists.txt
├── include
│ └── grab2_ros_common
│ │ └── node.hpp
└── package.xml
└── isaac_sim_worlds
├── assets
├── objects
│ ├── plate.usd
│ └── toy_car.usd
├── robots
│ ├── suction_gripper.usd
│ └── ur10e_short_suction.usd
└── worlds
│ └── toybox_world
│ ├── Textures
│ ├── Mat_Box_baseColor.jpg
│ ├── Mat_Box_metallicRoughness_metal_scale0.jpg
│ ├── Mat_Box_metallicRoughness_rough.jpg
│ ├── Mat_Box_normal.jpg
│ ├── Mat_Light_baseColor.png
│ ├── Mat_Light_emissive.jpg
│ ├── Mat_Light_metallicRoughness_metal_scale0.jpg
│ ├── Mat_Light_metallicRoughness_rough.jpg
│ ├── Mat_Room_baseColor.jpg
│ ├── Mat_Room_metallicRoughness_metal.jpg
│ ├── Mat_Room_metallicRoughness_rough.jpg
│ ├── Mat_Room_normal.jpg
│ ├── Mat_Rug_baseColor_scale1.jpg
│ ├── Mat_Rug_metallicRoughness_metal_scale0.jpg
│ ├── Mat_Rug_metallicRoughness_rough.jpg
│ ├── Mat_Rug_normal.jpg
│ ├── Mat_Toys_baseColor.png
│ ├── Mat_Toys_emissive.jpg
│ ├── Mat_Toys_metallicRoughness_metal.jpg
│ ├── Mat_Toys_metallicRoughness_rough.jpg
│ └── Mat_Toys_normal.jpg
│ └── world.usd
├── isaac_py.sh
├── panda_bin_cubes.py
├── panda_toybox.py
└── ur10e_stack_plates.py
/.colcon/build/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/.colcon/build/.gitkeep
--------------------------------------------------------------------------------
/.colcon/install/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/.colcon/install/.gitkeep
--------------------------------------------------------------------------------
/.colcon/log/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/.colcon/log/.gitkeep
--------------------------------------------------------------------------------
/.docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nvcr.io/nvidia/pytorch:23.08-py3 AS base
2 |
3 | ENV DEBIAN_FRONTEND=noninteractive
4 |
5 | # Install language
6 | RUN apt-get update && apt-get install -y --no-install-recommends \
7 | locales \
8 | && locale-gen en_US.UTF-8 \
9 | && update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 \
10 | && rm -rf /var/lib/apt/lists/*
11 | ENV LANG=en_US.UTF-8
12 |
13 | # Install timezone
14 | RUN ln -fs /usr/share/zoneinfo/UTC /etc/localtime \
15 | && export DEBIAN_FRONTEND=noninteractive \
16 | && apt-get update \
17 | && apt-get install -y --no-install-recommends tzdata \
18 | && dpkg-reconfigure --frontend noninteractive tzdata \
19 | && rm -rf /var/lib/apt/lists/*
20 |
21 | RUN apt-get update && apt-get -y upgrade \
22 | && rm -rf /var/lib/apt/lists/*
23 |
24 | # Install ROS2
25 | RUN apt-get update && apt-get install -y --no-install-recommends \
26 | curl \
27 | software-properties-common \
28 | && add-apt-repository universe \
29 | && export ROS_APT_SOURCE_VERSION=$(curl -s https://api.github.com/repos/ros-infrastructure/ros-apt-source/releases/latest | grep -F "tag_name" | awk -F\" '{print $4}') \
30 | && curl -L -o /tmp/ros2-apt-source.deb "https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. /etc/os-release && echo $VERSION_CODENAME)_all.deb" \
31 | && dpkg -i /tmp/ros2-apt-source.deb \
32 | && apt-get update && apt-get install -y --no-install-recommends \
33 | python3-argcomplete \
34 | ros-humble-desktop \
35 | && rm -rf /var/lib/apt/lists/*
36 |
37 | # Deal with getting tons of debconf messages
38 | # See: https://github.com/phusion/baseimage-docker/issues/58
39 | RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
40 |
41 | # add GL:
42 | RUN apt-get update && apt-get install -y --no-install-recommends \
43 | pkg-config \
44 | libglvnd-dev \
45 | libgl1-mesa-dev \
46 | libegl1-mesa-dev \
47 | libgles2-mesa-dev && \
48 | rm -rf /var/lib/apt/lists/*
49 |
50 | # Env vars for the nvidia-container-runtime.
51 | ENV NVIDIA_VISIBLE_DEVICES all
52 | ENV NVIDIA_DRIVER_CAPABILITIES graphics,utility,compute
53 | ENV QT_X11_NO_MITSHM=1
54 |
55 | ENV ROS_DISTRO=humble
56 | ENV AMENT_PREFIX_PATH=/opt/ros/humble
57 | ENV COLCON_PREFIX_PATH=/opt/ros/humble
58 | ENV LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:/opt/ros/humble/lib/x86_64-linux-gnu:/opt/ros/humble/lib"
59 | ENV PATH=/opt/ros/humble/bin:$PATH
60 | ENV PYTHONPATH=/opt/ros/humble/local/lib/python3.10/dist-packages:/opt/ros/humble/lib/python3.10/site-packages
61 | ENV ROS_PYTHON_VERSION=3
62 | ENV ROS_VERSION=2
63 | ENV ROS_AUTOMATIC_DISCOVERY_RANGE=SUBNET
64 | ENV DEBIAN_FRONTEND=
65 |
66 | RUN apt-get update && apt-get install -y \
67 | tzdata \
68 | software-properties-common \
69 | && dpkg-reconfigure -f noninteractive tzdata \
70 | && add-apt-repository -y ppa:git-core/ppa \
71 | && apt-get update && apt-get install -y \
72 | curl \
73 | lsb-core \
74 | wget \
75 | build-essential \
76 | cmake \
77 | git \
78 | git-lfs \
79 | iputils-ping \
80 | make \
81 | openssh-server \
82 | openssh-client \
83 | libeigen3-dev \
84 | libssl-dev \
85 | python3-pip \
86 | python3-ipdb \
87 | python3-tk \
88 | python3-wstool \
89 | sudo git bash unattended-upgrades \
90 | apt-utils \
91 | terminator \
92 | glmark2 \
93 | && rm -rf /var/lib/apt/lists/*
94 |
95 | # push defaults to bashrc:
96 | RUN apt-get update && apt-get install --reinstall -y \
97 | libmpich-dev \
98 | hwloc-nox libmpich12 mpich \
99 | && rm -rf /var/lib/apt/lists/*
100 |
101 | # This is required to enable mpi lib access:
102 | ENV PATH="${PATH}:/opt/hpcx/ompi/bin"
103 | ENV LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:/opt/hpcx/ompi/lib"
104 |
105 | ENV TORCH_CUDA_ARCH_LIST "7.0+PTX"
106 | ENV LD_LIBRARY_PATH="/usr/local/lib:${LD_LIBRARY_PATH}"
107 |
108 | # Get CuRobo
109 | # COPY curobo /pkgs/curobo # If you have local curobo package, you can copy it directly
110 | RUN mkdir /pkgs && cd /pkgs && git clone https://github.com/NVlabs/curobo.git
111 | RUN cd /pkgs/curobo && pip3 install .[dev,usd] --no-build-isolation
112 |
113 | # Setup ROS2 Workspace
114 | RUN mkdir -p /ros2_ws/src
115 | WORKDIR /ros2_ws
116 | COPY dependencies.repos ./src
117 |
118 | # Install ROS2 dependencies
119 | RUN apt-get update && apt-get install -y --no-install-recommends \
120 | python3-vcstool \
121 | ros-${ROS_DISTRO}-rviz2 \
122 | ros-${ROS_DISTRO}-xacro \
123 | ros-${ROS_DISTRO}-ros2-control \
124 | ros-${ROS_DISTRO}-behaviortree-cpp \
125 | ros-${ROS_DISTRO}-tf-transformations \
126 | ros-${ROS_DISTRO}-rmw-cyclonedds-cpp \
127 | ros-${ROS_DISTRO}-hardware-interface \
128 | ros-${ROS_DISTRO}-controller-manager \
129 | ros-${ROS_DISTRO}-gripper-controllers \
130 | ros-${ROS_DISTRO}-position-controllers \
131 | ros-${ROS_DISTRO}-joint-state-broadcaster \
132 | ros-${ROS_DISTRO}-topic-based-ros2-control \
133 | && rm -rf /var/lib/apt/lists/*
134 |
135 | RUN vcs import src < src/dependencies.repos
136 |
137 | ###########################################
138 | # Develop image
139 | ###########################################
140 | FROM base AS dev
141 |
142 | ENV DEBIAN_FRONTEND=noninteractive
143 | RUN apt-get update && apt-get install -y --no-install-recommends \
144 | bash-completion \
145 | build-essential \
146 | cmake \
147 | gdb \
148 | git \
149 | openssh-client \
150 | python3-argcomplete \
151 | python3-pip \
152 | ros-dev-tools \
153 | ros-${ROS_DISTRO}-ament-* \
154 | vim \
155 | && rm -rf /var/lib/apt/lists/*
156 |
157 | RUN rosdep init || echo "rosdep already initialized"
158 |
159 | ARG USERNAME=ros
160 | ARG USER_UID=1000
161 | ARG USER_GID=$USER_UID
162 |
163 | # Check if "ubuntu" user exists, delete it if it does, then create the desired user
164 | RUN if getent passwd ubuntu > /dev/null 2>&1; then \
165 | userdel -r ubuntu && \
166 | echo "Deleted existing ubuntu user"; \
167 | fi && \
168 | groupadd --gid $USER_GID $USERNAME && \
169 | useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME && \
170 | echo "Created new user $USERNAME"
171 |
172 | # Add sudo support for the non-root user
173 | RUN apt-get update && apt-get install -y sudo \
174 | && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME\
175 | && chmod 0440 /etc/sudoers.d/$USERNAME \
176 | && rm -rf /var/lib/apt/lists/*
177 |
178 | # Set up autocompletion for user
179 | RUN apt-get update && apt-get install -y --no-install-recommends git-core bash-completion \
180 | && echo "if [ -f /opt/ros/${ROS_DISTRO}/setup.bash ]; then source /opt/ros/${ROS_DISTRO}/setup.bash; fi" >> /home/$USERNAME/.bashrc \
181 | && echo "if [ -f /usr/share/colcon_argcomplete/hook/colcon-argcomplete.bash ]; then source /usr/share/colcon_argcomplete/hook/colcon-argcomplete.bash; fi" >> /home/$USERNAME/.bashrc \
182 | && rm -rf /var/lib/apt/lists/*
183 |
184 | ENV DEBIAN_FRONTEND=
185 | ENV AMENT_CPPCHECK_ALLOW_SLOW_VERSIONS=1
186 |
--------------------------------------------------------------------------------
/.docker/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | # Re-install MPICH - Required for CuRobo
5 | sudo apt-get update
6 | sudo apt-get -y install --reinstall -y \
7 | libmpich-dev hwloc-nox libmpich12 mpich
8 |
9 | # Install ROS2 dependencies
10 | rosdep update --rosdistro=${ROS_DISTRO}
11 | rosdep install --from-paths src --ignore-src -r -y --rosdistro=${ROS_DISTRO}
12 |
13 | # Build
14 | colcon build --symlink-install
15 |
16 | # Source workspace
17 | if [ -f /ros2_ws/install/setup.bash ]
18 | then
19 | source /ros2_ws/install/setup.bash
20 | fi
21 |
22 | # Execute the command passed into this entrypoint
23 | exec "$@"
24 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yaml:
--------------------------------------------------------------------------------
1 | name: Lint
2 |
3 | on:
4 | pull_request:
5 |
6 | jobs:
7 | pre-commit:
8 | name: pre-commit
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v5
12 | - uses: actions/setup-python@v5
13 | - uses: pre-commit/action@v3.0.1
14 |
15 | action-ros-lint:
16 | name: ament_${{ matrix.linter }}
17 | runs-on: ubuntu-latest
18 | container:
19 | image: rostooling/setup-ros-docker:ubuntu-noble-ros-rolling-ros-base-latest
20 | strategy:
21 | fail-fast: false
22 | matrix:
23 | linter:
24 | [uncrustify, cpplint, lint_cmake, xmllint, flake8, pep257]
25 | steps:
26 | - uses: actions/checkout@v5
27 | - uses: ros-tooling/action-ros-lint@0.1.4
28 | with:
29 | linter: ${{ matrix.linter }}
30 | package-name: "*"
31 |
--------------------------------------------------------------------------------
/.github/workflows/ros2.yaml:
--------------------------------------------------------------------------------
1 | name: ROS2 CI
2 |
3 | on:
4 | pull_request:
5 | types: [opened, synchronize, reopened, ready_for_review]
6 |
7 | push:
8 | branches: [main]
9 |
10 | jobs:
11 | build-and-test:
12 | name: build-and-test-against-${{ matrix.ros-distro }}
13 | runs-on: ${{ matrix.os }}
14 | strategy:
15 | fail-fast: false
16 | matrix:
17 | include:
18 | - os: [ubuntu-24.04]
19 | ros-distro: "kilted"
20 | - os: [ubuntu-24.04]
21 | ros-distro: "jazzy"
22 | - os: [ubuntu-22.04]
23 | ros-distro: "humble"
24 | steps:
25 | - name: Install yaml-cpp deps
26 | run: sudo apt-get install -y libyaml-cpp-dev
27 |
28 | - name: Checkout
29 | uses: actions/checkout@v5
30 |
31 | - name: Setup ROS 2
32 | uses: ros-tooling/setup-ros@0.7.15
33 | with:
34 | required-ros-distributions: ${{ matrix.ros-distro }}
35 |
36 | - name: Build and Test
37 | uses: ros-tooling/action-ros-ci@0.4.5
38 | with:
39 | target-ros2-distro: ${{ matrix.ros-distro }}
40 | vcs-repo-file-url: ${GITHUB_WORKSPACE}/dependencies.repos
41 | colcon-defaults: |
42 | {
43 | "test": {
44 | "parallel-workers" : 1
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/__pycache__/
2 | .hypothesis/*
3 | .thumbs/
4 |
5 | .colcon/
6 | BehaviorTree.ROS2/
7 |
8 | # Sphinx documentation
9 | docs/build/
10 | docs/venv/
11 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | # To use:
2 | #
3 | # pre-commit run -a
4 | #
5 | # Or:
6 | #
7 | # pre-commit install # (runs every time you commit in git)
8 | #
9 | # To update this file:
10 | #
11 | # pre-commit autoupdate
12 | #
13 | # To Install pre-commit
14 | #
15 | # pip install pre-commit --break-system-packages
16 | #
17 | # See https://github.com/pre-commit/pre-commit
18 | repos:
19 | # Standard hooks
20 | - repo: https://github.com/pre-commit/pre-commit-hooks
21 | rev: v6.0.0
22 | hooks:
23 | - id: check-added-large-files
24 | - id: check-ast
25 | - id: check-builtin-literals
26 | - id: check-case-conflict
27 | - id: check-docstring-first
28 | - id: check-executables-have-shebangs
29 | - id: check-json
30 | - id: check-merge-conflict
31 | - id: check-symlinks
32 | - id: check-toml
33 | - id: check-vcs-permalinks
34 | - id: check-yaml
35 | - id: debug-statements
36 | - id: destroyed-symlinks
37 | - id: detect-private-key
38 | - id: end-of-file-fixer
39 | - id: fix-byte-order-marker
40 | - id: forbid-new-submodules
41 | - id: mixed-line-ending
42 | - id: name-tests-test
43 | - id: requirements-txt-fixer
44 | - id: sort-simple-yaml
45 | - id: trailing-whitespace
46 |
47 | # Autoformats Python code.
48 | - repo: https://github.com/psf/black.git
49 | rev: 25.1.0
50 | hooks:
51 | - id: black
52 | args: ["--skip-string-normalization"]
53 |
54 | # Finds spelling issues in code.
55 | - repo: https://github.com/codespell-project/codespell
56 | rev: v2.4.1
57 | hooks:
58 | - id: codespell
59 | args: ["--write-changes", "-L", "reacher"] # Provide a comma-separated list of misspelled words that codespell should ignore (for example: '-L', 'word1,word2,word3').
60 | exclude: \.(svg|pyc|stl|dae|lock)$
61 |
62 | - repo: https://github.com/pre-commit/mirrors-clang-format
63 | rev: v21.1.0
64 | hooks:
65 | - id: clang-format
66 | files: \.(c|cc|cxx|cpp|frag|glsl|h|hpp|hxx|ih|ispc|ipp|java|m|proto|vert)$
67 | # -i arg is included by default by the hook
68 | args: ["-fallback-style=none"]
69 |
70 | - repo: https://github.com/adrienverge/yamllint
71 | rev: v1.37.1
72 | hooks:
73 | - id: yamllint
74 | args:
75 | [
76 | "--no-warnings",
77 | "--config-data",
78 | "{extends: default, rules: {line-length: disable, braces: {max-spaces-inside: 1}, indentation: disable}}",
79 | ]
80 | types: [text]
81 | files: \.(yml|yaml)$
82 |
83 | # Checks links in markdown files.
84 | - repo: https://github.com/tcort/markdown-link-check
85 | rev: v3.13.7
86 | hooks:
87 | - id: markdown-link-check
88 |
89 | # CPP formatting
90 | - repo: https://github.com/pre-commit/mirrors-clang-format
91 | rev: v21.1.0
92 | hooks:
93 | - id: clang-format
94 | types_or: [c++, c]
95 | args: ['-fallback-style=none', '-i']
96 |
97 | exclude: '(.colcon)/.*'
98 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | # Read the Docs configuration file
2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
3 |
4 | # Required
5 | version: 2
6 |
7 | # Set the OS, Python version, and other tools you might need
8 | build:
9 | os: ubuntu-24.04
10 | tools:
11 | python: "3.12"
12 |
13 | # Build documentation in the "docs/" directory with Sphinx
14 | sphinx:
15 | configuration: docs/source/conf.py
16 |
17 | # Optionally build your docs in additional formats such as PDF and ePub
18 | formats:
19 | - pdf
20 | - epub
21 |
22 | # Optionally, but recommended,
23 | # declare the Python requirements required to build your documentation
24 | # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
25 | python:
26 | install:
27 | - requirements: docs/requirements.txt
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 ElSayed ElSheikh
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 | # Grab2 Behavior Demos
2 |
3 | [](https://github.com/elsayedelsheikh/grab2/actions/workflows/ros2.yaml)
4 |
5 | This repository provides manipulation demos
6 |
7 | - Using BehaviorTree.CPP, Moveit2, Nvidia cuRobo, and ROS2 with Nvidia Isaac sim.
8 | - You can use this package with any simulator that supports topic based ROS2 control.
9 | - Docker containers are available as well.
10 |
11 | Available demos:
12 |
13 | - **Isaac Sim:**
14 | Check [Start Isaac Sim world](#2-start-the-isaac-sim-simulation-world)
15 | - **No Isaac Sim:**
16 | Run [mock hardware demo](#3-start-the-behavior-demo)
17 |
18 |
19 |
20 | See the sections below for detailed instructions on each option.
21 |
22 | ## Project Overview
23 |
24 | This project demonstrates robot manipulation using a modular tech stack:
25 |
26 | - **BehaviorTree.CPP v4.6** for task-level logic and decision making
27 | - **Nvidia CuRobo** for fast, efficient, real-time trajectory planning
28 | - **ROS2** with **ros2_control** and `joint_trajectory_controller` for motion execution
29 |
30 | The behavior tree manages the decision-making flow, Nvidia CuRobo handles motion planning, and ROS2 `ros2_control` executes the robot's motion.
31 |
32 |
33 |
34 | ### Demo Scenarios
35 |
36 | - **Simple Room:**
37 | Franka robot, a bin, and three cubes on a table
38 | - **Toybox:**
39 | Panda robot with car toys scattered on the ground
40 |
41 | ### Available Behavior Demos
42 |
43 | - `collect_cubes`
44 | - `collect_toys`
45 | - `lookaround_demo`
46 | - `target_ik_demo`
47 |
48 | For more information, see `grab2_behavior_tree/behavior_trees`.
49 |
50 | ### Available Planners
51 |
52 | - Moveit2
53 | - Nvidia CuRobo
54 | - Did you write one? Send a pull request to add it to this list.
55 |
56 | ### Available BehaviorTree Plugins
57 |
58 | - See `grab2_behavior_tree/plugins`.
59 | - Did you write one? Send a pull request to add it to this list.
60 |
61 | ## Requirements
62 |
63 | - NVIDIA GPU with driver version 550+ is required for simulation and demo containers.
64 | - [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/) must be installed.
65 | - Ensure [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) is set up for GPU access in containers.
66 |
67 | ## Running Demos with Docker Compose
68 |
69 | This project uses Docker Compose to manage simulation and demo containers
70 | defined in `docker-compose.yml`.
71 |
72 | ---
73 |
74 | ### 1. Build the Containers
75 |
76 | Build all required images:
77 |
78 | ```sh
79 | docker compose build
80 | ```
81 |
82 | ---
83 |
84 | ### 2. Start the Isaac Sim Simulation World
85 |
86 | You can start the Isaac Sim simulation world in two ways:
87 |
88 | - **Using Docker Compose:**
89 | Launch the simulation environment in a container with:
90 |
91 | ```sh
92 | docker compose up isaac-simple-room
93 | ```
94 |
95 | or/ for the toybox world:
96 |
97 | ```sh
98 | docker compose up isaac-toybox
99 | ```
100 |
101 | - **Using a Local Isaac Sim Installation (version 4.5):**
102 | If you have Isaac Sim installed locally, you can run the simulation script directly:
103 |
104 | ```sh
105 | cd isaac_sim_worlds
106 | ./isaac_py.sh panda_bin_cubes.py
107 | ```
108 |
109 | or/ for the toybox world:
110 |
111 | ```sh
112 | cd isaac_sim_worlds
113 | ./isaac_py.sh panda_toybox.py
114 | ```
115 |
116 | ---
117 |
118 | ### 3. Start the Behavior Demo
119 |
120 | In a separate terminal, start the behavior demo container:
121 |
122 | - Simple Room Demo (with Isaac Sim):
123 |
124 | ```sh
125 | docker compose up simple-room-demo
126 | ```
127 |
128 | - Toybox Demo:
129 |
130 | ```sh
131 | docker compose up toybox-demo
132 | ```
133 |
134 | - Simple Room Demo (no Isaac Sim, mock hardware):
135 | (No Isaac Sim required; can be run standalone, without objects to grab)
136 |
137 | ```sh
138 | docker compose up simple-room-demo-no-isaac
139 | ```
140 |
141 | ---
142 |
143 | ### 4. Stopping the Demos
144 |
145 | To stop all running containers, press `Ctrl+C` in the terminal or run:
146 |
147 | ```sh
148 | docker compose down
149 | ```
150 |
--------------------------------------------------------------------------------
/dependencies.repos:
--------------------------------------------------------------------------------
1 | # List of repositories to use within your workspace
2 | # See https://github.com/dirk-thomas/vcstool
3 | repositories:
4 | BehaviorTree.ROS2:
5 | type: git
6 | url: https://github.com/elsayedelsheikh/BehaviorTree.ROS2.git
7 | version: deprecate_ament_target_deps
8 | topic_based_ros2_control:
9 | type: git
10 | url: https://github.com/PickNikRobotics/topic_based_ros2_control.git
11 | version: main
12 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | isaac-sim:
3 | container_name: isaacsim450
4 | image: nvcr.io/nvidia/isaac-sim:4.5.0
5 | environment:
6 | - ACCEPT_EULA=Y
7 | - PRIVACY_CONSENT=Y
8 | - ROS_DOMAIN_ID=0
9 | - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
10 | - LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/isaac-sim/exts/isaacsim.ros2.bridge/humble/lib
11 | entrypoint: bash
12 | working_dir: /isaac_sim_worlds
13 | volumes:
14 | - ~/docker/isaac-sim/cache/kit:/isaac-sim/kit/cache:rw
15 | - ~/docker/isaac-sim/cache/ov:/root/.cache/ov:rw
16 | - ~/docker/isaac-sim/cache/pip:/root/.cache/pip:rw
17 | - ~/docker/isaac-sim/cache/glcache:/root/.cache/nvidia/GLCache:rw
18 | - ~/docker/isaac-sim/cache/computecache:/root/.nv/ComputeCache:rw
19 | - ~/docker/isaac-sim/logs:/root/.nvidia-omniverse/logs:rw
20 | - ~/docker/isaac-sim/data:/root/.local/share/ov/data:rw
21 | - ~/docker/isaac-sim/documents:/root/Documents:rw
22 | - ./isaac_sim_worlds:/isaac_sim_worlds:ro
23 | deploy:
24 | resources:
25 | reservations:
26 | devices:
27 | - driver: nvidia
28 | count: all
29 | capabilities: [gpu]
30 | network_mode: host
31 |
32 | isaac-simple-room:
33 | extends: isaac-sim
34 | command: ["./isaac_py.sh", "panda_bin_cubes.py"]
35 |
36 | isaac-toybox:
37 | extends: isaac-sim
38 | command: ["./isaac_py.sh", "panda_toybox.py"]
39 |
40 | grab2:
41 | container_name: grab2
42 | image: grab2
43 | build:
44 | context: .
45 | dockerfile: .docker/Dockerfile
46 | target: dev
47 | args:
48 | - USERNAME=${USERNAME:-ros}
49 | - USER_UID=${UID:-1000}
50 | - USER_GID=${UID:-1000}
51 | # Interactive Shell
52 | stdin_open: true
53 | tty: true
54 | # Networking
55 | network_mode: host
56 | ipc: host
57 | environment:
58 | - ROS_DOMAIN_ID=0
59 | - RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
60 | # Allows graphical programs in the container.
61 | - DISPLAY=${DISPLAY}
62 | - QT_X11_NO_MITSHM=1
63 | - NVIDIA_DRIVER_CAPABILITIES=all
64 | working_dir: /ros2_ws
65 | user: ${USERNAME:-ros}
66 | volumes:
67 | - ./.docker/entrypoint.sh:/entrypoint.sh:ro
68 | - ./grab2_behavior_tree:/ros2_ws/src/grab2_behavior_tree:ro
69 | - ./grab2_controller:/ros2_ws/src/grab2_controller:ro
70 | - ./grab2_msgs:/ros2_ws/src/grab2_msgs:ro
71 | - ./grab2_curobo_planner:/ros2_ws/src/grab2_curobo_planner:ro
72 | - ./.colcon/build/:/ros2_ws/build/:rw
73 | - ./.colcon/install/:/ros2_ws/install/:rw
74 | - ./.colcon/log/:/ros2_ws/log/:rw
75 | # Allows graphical programs in the container.
76 | - /tmp/.X11-unix:/tmp/.X11-unix:rw
77 | - ${XAUTHORITY:-$HOME/.Xauthority}:/root/.Xauthority
78 | deploy:
79 | resources:
80 | reservations:
81 | devices:
82 | - driver: nvidia
83 | count: all
84 | capabilities: [gpu]
85 | entrypoint: ["/entrypoint.sh"]
86 |
87 | simple-room-demo:
88 | extends: grab2
89 | command: >
90 | ros2 launch grab2_behavior_tree bt_demo.launch.py
91 | robot:=panda
92 | world:=table
93 | behavior:=collect_cubes
94 |
95 | simple-room-demo-no-isaac:
96 | extends: grab2
97 | command: >
98 | ros2 launch grab2_behavior_tree bt_demo.launch.py
99 | robot:=panda
100 | world:=table
101 | behavior:=collect_cubes
102 | hardware:=mock_components
103 |
104 | toybox-demo:
105 | extends: grab2
106 | command: >
107 | ros2 launch grab2_behavior_tree bt_demo.launch.py
108 | robot:=panda
109 | world:=toybox
110 | behavior:=collect_toys
111 |
--------------------------------------------------------------------------------
/docs/COLCON_IGNORE:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/docs/COLCON_IGNORE
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = source
9 | BUILDDIR = build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=source
11 | set BUILDDIR=build
12 |
13 | %SPHINXBUILD% >NUL 2>NUL
14 | if errorlevel 9009 (
15 | echo.
16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
17 | echo.installed, then set the SPHINXBUILD environment variable to point
18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
19 | echo.may add the Sphinx directory to PATH.
20 | echo.
21 | echo.If you don't have Sphinx installed, grab it from
22 | echo.https://www.sphinx-doc.org/
23 | exit /b 1
24 | )
25 |
26 | if "%1" == "" goto help
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | esbonio
2 | sphinx
3 | sphinx-autobuild
4 | sphinx-copybutton
5 | sphinx_rtd_theme
6 | sphinxcontrib-youtube
7 |
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # For the full list of built-in configuration values, see the documentation:
4 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
5 |
6 | # -- Project information -----------------------------------------------------
7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
8 |
9 | project = 'grab2'
10 | copyright = '2025, ElSayed ElSheikh'
11 | author = 'ElSayed ElSheikh'
12 | release = '0.0.1'
13 |
14 | # -- General configuration ---------------------------------------------------
15 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
16 |
17 | extensions = [
18 | "sphinx.ext.autodoc",
19 | "sphinx.ext.autosummary",
20 | "sphinx_rtd_theme",
21 | "sphinx_copybutton",
22 | "sphinxcontrib.youtube",
23 | ]
24 |
25 | templates_path = ['_templates']
26 | exclude_patterns = []
27 |
28 |
29 | # -- Options for HTML output -------------------------------------------------
30 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
31 |
32 | html_theme = 'sphinx_rtd_theme'
33 | html_static_path = ['_static']
34 |
--------------------------------------------------------------------------------
/docs/source/demos/docs/panda_collecting_toycars.rst:
--------------------------------------------------------------------------------
1 | Collecting toys with Franka Panda
2 | #################################
3 |
4 | In this demo, we will be using Franka Panda to clean up the playground after kids had missed up the place.
5 |
6 | So Franka will look for the toycars using the camera fixed at the end-effector, `lookaround_demo` behavior, whenever a toycar is detected, panda will try to grasp it, lift it up and throw it in the toybox and continues until there are no toys left on the ground.
7 |
--------------------------------------------------------------------------------
/docs/source/demos/index.rst:
--------------------------------------------------------------------------------
1 | Demos
2 | ######
3 |
4 | .. toctree::
5 | :maxdepth: 1
6 |
7 | docs/panda_collecting_toycars
8 |
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | .. grab2 documentation master file, created by
2 | sphinx-quickstart on Thu Sep 25 00:31:48 2025.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | grab2 documentation
7 | ===================
8 |
9 | grab2 brings together ROS 2, NVIDIA cuRobo, BehaviorTree.CPP and NVIDIA Isaac Sim; a practical setup for a typical manipulation pipeline from perception to motion planning and behavior orchestration.
10 |
11 | Setup
12 | =====
13 |
14 | Supported Robots
15 | ----------------
16 |
17 | +----------------------------------------------------------------+-------------+
18 | | Robot | Status |
19 | +================================================================+=============+
20 | | Franka panda with 2-finger girpper | Supported |
21 | +----------------------------------------------------------------+-------------+
22 | | Universal Robot (UR5) with 2-finger gripper "`robotiq_2f_140`" | Supported |
23 | +----------------------------------------------------------------+-------------+
24 | | Universal Robot (UR10) with suction gripper | Supported |
25 | +----------------------------------------------------------------+-------------+
26 | | Tiago (Mobile manipulator) | In progress |
27 | +----------------------------------------------------------------+-------------+
28 |
29 | Supported Motion Planners
30 | -------------------------
31 |
32 | * Moveit2
33 | * NVIDIA cuRobo
34 |
35 | Supported Simulators
36 | -------------------------
37 |
38 | * NVIDIA Isaac Sim >= 4.5 (any simulator that supports ROS2 topic based control)
39 | * Gazebo
40 |
41 | Grab2 Demos
42 | ===========
43 |
44 | .. toctree::
45 | :maxdepth: 1
46 |
47 | demos/index
48 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.8)
2 | project(grab2_behavior_tree)
3 |
4 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
5 | add_compile_options(-Wall -Wextra -Wpedantic)
6 | endif()
7 |
8 | # find dependencies
9 | find_package(ament_cmake REQUIRED)
10 | find_package(rclcpp REQUIRED)
11 | find_package(rclcpp_action REQUIRED)
12 | find_package(rclcpp_components REQUIRED)
13 | find_package(ament_index_cpp REQUIRED)
14 | find_package(std_msgs REQUIRED)
15 | find_package(builtin_interfaces REQUIRED)
16 | find_package(geometry_msgs REQUIRED)
17 | find_package(tf2_geometry_msgs REQUIRED)
18 | find_package(vision_msgs REQUIRED)
19 | find_package(vision_msgs_rviz_plugins REQUIRED)
20 | find_package(control_msgs REQUIRED)
21 | find_package(trajectory_msgs REQUIRED)
22 | find_package(grab2_msgs REQUIRED)
23 | find_package(behaviortree_cpp REQUIRED)
24 | find_package(behaviortree_ros2 REQUIRED)
25 |
26 | set(CMAKE_CXX_STANDARD 17)
27 |
28 | set(THIS_PACK_DEPS
29 | rclcpp
30 | rclcpp_action
31 | rclcpp_components
32 | std_msgs
33 | builtin_interfaces
34 | geometry_msgs
35 | tf2_geometry_msgs
36 | vision_msgs
37 | trajectory_msgs
38 | ament_index_cpp
39 | control_msgs
40 | grab2_msgs
41 | behaviortree_cpp
42 | behaviortree_ros2
43 | )
44 |
45 | # Libraries
46 | add_library(grab2_get_trajectory_hardcoded SHARED plugins/get_trajectory_hardcoded.cpp)
47 | add_library(grab2_get_grasp_hardcoded SHARED plugins/get_grasp_hardcoded.cpp)
48 | add_library(grab2_detect_object SHARED plugins/detect_object.cpp)
49 | add_library(grab2_get_grasp SHARED plugins/get_grasp.cpp)
50 | add_library(grab2_plan SHARED plugins/plan.cpp)
51 | add_library(grab2_move SHARED plugins/move.cpp)
52 | add_library(grab2_grip SHARED plugins/grip.cpp)
53 |
54 | list(APPEND plugin_libs
55 | grab2_get_trajectory_hardcoded
56 | grab2_get_grasp_hardcoded
57 | grab2_detect_object
58 | grab2_get_grasp
59 | grab2_plan
60 | grab2_move
61 | grab2_grip
62 | )
63 |
64 | foreach(bt_plugin ${plugin_libs})
65 | ament_target_dependencies(${bt_plugin} ${THIS_PACK_DEPS})
66 | target_compile_definitions(${bt_plugin} PRIVATE BT_PLUGIN_EXPORT)
67 | target_include_directories(${bt_plugin}
68 | PRIVATE
69 | $
70 | $)
71 | endforeach()
72 |
73 | # Executables
74 | add_executable(grab2_engine src/main.cpp)
75 | ament_target_dependencies(grab2_engine ${THIS_PACK_DEPS})
76 | target_include_directories(grab2_engine PUBLIC
77 | $
78 | $)
79 |
80 | # Install Libs & Exec
81 | install(
82 | TARGETS
83 | ${plugin_libs}
84 | grab2_engine
85 | ARCHIVE DESTINATION lib
86 | LIBRARY DESTINATION lib
87 | RUNTIME DESTINATION lib/${PROJECT_NAME}
88 | )
89 |
90 | # Install directories
91 | install(
92 | DIRECTORY include/
93 | DESTINATION include
94 | )
95 |
96 | install(
97 | DIRECTORY behavior_trees launch
98 | DESTINATION share/${PROJECT_NAME}
99 | )
100 |
101 |
102 | if(BUILD_TESTING)
103 | find_package(ament_lint_auto REQUIRED)
104 |
105 | set(ament_cmake_copyright_FOUND TRUE)
106 | ament_lint_auto_find_test_dependencies()
107 | endif()
108 |
109 | ament_package()
110 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/behavior_trees/bt_groot2.btproj:
--------------------------------------------------------------------------------
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 |
39 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/behavior_trees/collect_cubes.xml:
--------------------------------------------------------------------------------
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 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/behavior_trees/collect_toys.xml:
--------------------------------------------------------------------------------
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 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/behavior_trees/lookaround_demo.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/behavior_trees/stack_cubes.xml:
--------------------------------------------------------------------------------
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 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/behavior_trees/target_ik_demo.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/include/grab2_behavior_tree/detect_object.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #ifndef GRAB2_BEHAVIOR_TREE__DETECT_OBJECT_HPP_
4 | #define GRAB2_BEHAVIOR_TREE__DETECT_OBJECT_HPP_
5 |
6 | #include
7 | #include
8 |
9 | #include "behaviortree_ros2/bt_topic_sub_node.hpp"
10 |
11 | #include "geometry_msgs/msg/pose.hpp"
12 | #include "geometry_msgs/msg/pose_stamped.hpp"
13 | #include "vision_msgs/msg/detection3_d_array.hpp"
14 |
15 | namespace grab2_behavior_tree
16 | {
17 |
18 |
19 | class DetectObject : public BT::RosTopicSubNode
20 | {
21 | public:
22 | explicit DetectObject(
23 | const std::string & name,
24 | const BT::NodeConfig & conf,
25 | const BT::RosNodeParams & params
26 | );
27 |
28 | static BT::PortsList providedPorts()
29 | {
30 | return
31 | {
32 | BT::OutputPort("class", "Detected object class_id"),
33 | BT::OutputPort("pose", "Detected object pose")
34 | };
35 | }
36 |
37 | BT::NodeStatus onTick(const std::shared_ptr & last_msg)
38 | override;
39 | };
40 |
41 | } // namespace grab2_behavior_tree
42 |
43 | #endif // GRAB2_BEHAVIOR_TREE__DETECT_OBJECT_HPP_
44 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/include/grab2_behavior_tree/get_grasp.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #ifndef GRAB2_BEHAVIOR_TREE__GET_GRASP_HPP_
4 | #define GRAB2_BEHAVIOR_TREE__GET_GRASP_HPP_
5 |
6 | #include
7 |
8 | #include "behaviortree_cpp/bt_factory.h"
9 | #include "geometry_msgs/msg/pose_stamped.hpp"
10 | #include "grab2_behavior_tree/utils.hpp"
11 |
12 | namespace grab2_behavior_tree
13 | {
14 |
15 | class GetGrasp : public BT::SyncActionNode
16 | {
17 | public:
18 | explicit GetGrasp(
19 | const std::string & name,
20 | const BT::NodeConfig & conf
21 | );
22 |
23 | static BT::PortsList providedPorts()
24 | {
25 | return
26 | {
27 | BT::InputPort("object_pose"),
28 | BT::OutputPort("pregrasp"),
29 | BT::OutputPort("grasp")
30 | }
31 | ;
32 | }
33 | BT::NodeStatus tick() override;
34 | };
35 |
36 | } // namespace grab2_behavior_tree
37 |
38 | #endif // GRAB2_BEHAVIOR_TREE__GET_GRASP_HPP_
39 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/include/grab2_behavior_tree/get_grasp_hardcoded.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #ifndef GRAB2_BEHAVIOR_TREE__GET_GRASP_HARDCODED_HPP_
4 | #define GRAB2_BEHAVIOR_TREE__GET_GRASP_HARDCODED_HPP_
5 |
6 | #include
7 | #include
8 |
9 | #include "behaviortree_cpp/bt_factory.h"
10 | #include "geometry_msgs/msg/pose.hpp"
11 | #include "geometry_msgs/msg/pose_stamped.hpp"
12 | #include "grab2_behavior_tree/utils.hpp"
13 |
14 | namespace grab2_behavior_tree
15 | {
16 |
17 | class GetGraspHardcoded : public BT::SyncActionNode
18 | {
19 | public:
20 | explicit GetGraspHardcoded(
21 | const std::string & name,
22 | const BT::NodeConfig & conf
23 | );
24 |
25 | static BT::PortsList providedPorts()
26 | {
27 | return
28 | {
29 | BT::OutputPort("pregrasp"),
30 | BT::OutputPort("grasp")
31 | }
32 | ;
33 | }
34 | BT::NodeStatus tick() override;
35 |
36 | private:
37 | static int counter_;
38 | std::vector grasp_poses_;
39 | };
40 |
41 | } // namespace grab2_behavior_tree
42 |
43 | #endif // GRAB2_BEHAVIOR_TREE__GET_GRASP_HARDCODED_HPP_
44 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/include/grab2_behavior_tree/get_trajectory_hardcoded.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #ifndef GRAB2_BEHAVIOR_TREE__GET_TRAJECTORY_HARDCODED_HPP_
4 | #define GRAB2_BEHAVIOR_TREE__GET_TRAJECTORY_HARDCODED_HPP_
5 |
6 | #include
7 | #include "behaviortree_cpp/bt_factory.h"
8 | #include "trajectory_msgs/msg/joint_trajectory.hpp"
9 |
10 | namespace grab2_behavior_tree
11 | {
12 |
13 | class SetJointTrajectoryGoal : public BT::SyncActionNode
14 | {
15 | public:
16 | explicit SetJointTrajectoryGoal(
17 | const std::string & name,
18 | const BT::NodeConfig & conf
19 | );
20 |
21 | static BT::PortsList providedPorts()
22 | {
23 | return
24 | {
25 | BT::OutputPort("trajectory")
26 | }
27 | ;
28 | }
29 | BT::NodeStatus tick() override;
30 | };
31 |
32 | } // namespace grab2_behavior_tree
33 |
34 | #endif // GRAB2_BEHAVIOR_TREE__GET_TRAJECTORY_HARDCODED_HPP_
35 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/include/grab2_behavior_tree/grip.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #ifndef GRAB2_BEHAVIOR_TREE__GRIP_HPP_
4 | #define GRAB2_BEHAVIOR_TREE__GRIP_HPP_
5 |
6 | #include
7 | #include
8 | #include "behaviortree_ros2/bt_action_node.hpp"
9 |
10 | #include "control_msgs/action/gripper_command.hpp"
11 |
12 | #include "grab2_behavior_tree/utils.hpp"
13 |
14 | namespace grab2_behavior_tree
15 | {
16 |
17 | using GripperCommand = control_msgs::action::GripperCommand;
18 |
19 | class Grip : public BT::RosActionNode
20 | {
21 | public:
22 | explicit Grip(
23 | const std::string & name,
24 | const BT::NodeConfig & conf,
25 | const BT::RosNodeParams & params);
26 |
27 | static BT::PortsList providedPorts()
28 | {
29 | return providedBasicPorts(
30 | {
31 | BT::InputPort("pose", "0.04", "Double: Default value is (0.04): open")
32 | });
33 | }
34 |
35 | bool setGoal(BT::RosActionNode::Goal & goal) override;
36 | BT::NodeStatus onResultReceived(const WrappedResult & wr) override;
37 | BT::NodeStatus onFeedback(const std::shared_ptr feedback) override;
38 | };
39 |
40 | } // namespace grab2_behavior_tree
41 |
42 | #endif // GRAB2_BEHAVIOR_TREE__GRIP_HPP_
43 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/include/grab2_behavior_tree/move.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #ifndef GRAB2_BEHAVIOR_TREE__MOVE_HPP_
4 | #define GRAB2_BEHAVIOR_TREE__MOVE_HPP_
5 |
6 | #include
7 | #include "behaviortree_ros2/bt_action_node.hpp"
8 |
9 | #include "trajectory_msgs/msg/joint_trajectory.hpp"
10 | #include "control_msgs/action/follow_joint_trajectory.hpp"
11 |
12 | #include "grab2_behavior_tree/utils.hpp"
13 |
14 | namespace grab2_behavior_tree
15 | {
16 |
17 | using FollowTrajectory = control_msgs::action::FollowJointTrajectory;
18 |
19 | class Move : public BT::RosActionNode
20 | {
21 | public:
22 | explicit Move(
23 | const std::string & name,
24 | const BT::NodeConfig & conf,
25 | const BT::RosNodeParams & params
26 | );
27 |
28 | static BT::PortsList providedPorts()
29 | {
30 | return providedBasicPorts(
31 | {
32 | BT::InputPort("trajectory")
33 | }
34 | );
35 | }
36 |
37 | bool setGoal(BT::RosActionNode::Goal & goal) override;
38 | BT::NodeStatus onResultReceived(const WrappedResult & wr) override;
39 |
40 | // Note that cancelGoal() is done automatically.
41 | void onHalt() override;
42 |
43 | // BT::NodeStatus onFeedback(const std::shared_ptr /*feedback*/) override
44 | // {
45 | // return BT::NodeStatus::RUNNING;
46 | // }
47 | };
48 |
49 | } // namespace grab2_behavior_tree
50 |
51 | #endif // GRAB2_BEHAVIOR_TREE__MOVE_HPP_
52 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/include/grab2_behavior_tree/plan.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #ifndef GRAB2_BEHAVIOR_TREE__PLAN_HPP_
4 | #define GRAB2_BEHAVIOR_TREE__PLAN_HPP_
5 |
6 | #include
7 | #include "behaviortree_ros2/bt_action_node.hpp"
8 |
9 | #include "geometry_msgs/msg/pose.hpp"
10 | #include "geometry_msgs/msg/pose_stamped.hpp"
11 | #include "trajectory_msgs/msg/joint_trajectory.hpp"
12 | #include "grab2_msgs/action/compute_plan_to_pose.hpp"
13 |
14 | #include "grab2_behavior_tree/utils.hpp"
15 |
16 | namespace grab2_behavior_tree
17 | {
18 |
19 | using PlanToGoal = grab2_msgs::action::ComputePlanToPose;
20 |
21 | class Plan : public BT::RosActionNode
22 | {
23 | public:
24 | explicit Plan(
25 | const std::string & name,
26 | const BT::NodeConfig & conf,
27 | const BT::RosNodeParams & params
28 | );
29 |
30 | static BT::PortsList providedPorts()
31 | {
32 | return providedBasicPorts(
33 | {
34 | BT::InputPort("target_ik"),
35 | BT::OutputPort("trajectory")
36 | }
37 | );
38 | }
39 |
40 | bool setGoal(BT::RosActionNode::Goal & goal) override;
41 | BT::NodeStatus onResultReceived(const WrappedResult & wr) override;
42 | };
43 |
44 | } // namespace grab2_behavior_tree
45 |
46 | #endif // GRAB2_BEHAVIOR_TREE__PLAN_HPP_
47 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/include/grab2_behavior_tree/utils.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #ifndef GRAB2_BEHAVIOR_TREE__UTILS_HPP_
4 | #define GRAB2_BEHAVIOR_TREE__UTILS_HPP_
5 |
6 | #include
7 | #include "behaviortree_cpp/behavior_tree.h"
8 |
9 | #include "geometry_msgs/msg/pose.hpp"
10 | #include "geometry_msgs/msg/pose_stamped.hpp"
11 |
12 | namespace grab2_behavior_tree
13 | {
14 | enum class AnsiColor
15 | {
16 | RESET,
17 | RED,
18 | GREEN,
19 | YELLOW,
20 | BLUE,
21 | CYAN,
22 | MAGENTA,
23 | WHITE
24 | };
25 |
26 | inline const char * getAnsiCode(AnsiColor color = AnsiColor::RESET)
27 | {
28 | switch (color) {
29 | case AnsiColor::RESET: return "\033[0m";
30 | case AnsiColor::RED: return "\033[31m";
31 | case AnsiColor::GREEN: return "\033[32m";
32 | case AnsiColor::YELLOW: return "\033[33m";
33 | case AnsiColor::BLUE: return "\033[34m";
34 | case AnsiColor::CYAN: return "\033[36m";
35 | case AnsiColor::MAGENTA: return "\033[35m";
36 | case AnsiColor::WHITE: return "\033[37m";
37 | default: return "\033[0m";
38 | }
39 | }
40 |
41 | inline std::string colorize(const std::string & msg, AnsiColor color)
42 | {
43 | return getAnsiCode(color) + msg + getAnsiCode();
44 | }
45 |
46 | } // namespace grab2_behavior_tree
47 |
48 | namespace BT
49 | {
50 |
51 | template<>
52 | inline geometry_msgs::msg::Pose convertFromString(const StringView key)
53 | {
54 | // three real numbers separated by semicolons
55 | auto parts = BT::splitString(key, ';');
56 | if (parts.size() != 7) {
57 | throw std::runtime_error("invalid number of fields for Pose attribute)");
58 | } else {
59 | geometry_msgs::msg::Pose pose;
60 | pose.position.x = BT::convertFromString(parts[0]);
61 | pose.position.y = BT::convertFromString(parts[1]);
62 | pose.position.z = BT::convertFromString(parts[2]);
63 | pose.orientation.x = BT::convertFromString(parts[3]);
64 | pose.orientation.y = BT::convertFromString(parts[4]);
65 | pose.orientation.z = BT::convertFromString(parts[5]);
66 | pose.orientation.w = BT::convertFromString(parts[6]);
67 | return pose;
68 | }
69 | }
70 |
71 | template<>
72 | inline geometry_msgs::msg::PoseStamped convertFromString(const StringView key)
73 | {
74 | // three real numbers separated by semicolons
75 | auto parts = BT::splitString(key, ';');
76 | if (parts.size() != 7) {
77 | throw std::runtime_error("invalid number of fields for Pose attribute)");
78 | } else {
79 | geometry_msgs::msg::PoseStamped pose_stamped;
80 | pose_stamped.header.frame_id = "world";
81 | pose_stamped.pose.position.x = BT::convertFromString(parts[0]);
82 | pose_stamped.pose.position.y = BT::convertFromString(parts[1]);
83 | pose_stamped.pose.position.z = BT::convertFromString(parts[2]);
84 | pose_stamped.pose.orientation.x = BT::convertFromString(parts[3]);
85 | pose_stamped.pose.orientation.y = BT::convertFromString(parts[4]);
86 | pose_stamped.pose.orientation.z = BT::convertFromString(parts[5]);
87 | pose_stamped.pose.orientation.w = BT::convertFromString(parts[6]);
88 | return pose_stamped;
89 | }
90 | }
91 |
92 | } // namespace BT
93 |
94 | #endif // GRAB2_BEHAVIOR_TREE__UTILS_HPP_
95 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/launch/bt_demo.launch.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from ament_index_python.packages import get_package_share_directory
4 | from launch import LaunchDescription
5 | from launch.actions import (
6 | DeclareLaunchArgument,
7 | IncludeLaunchDescription,
8 | OpaqueFunction,
9 | TimerAction,
10 | )
11 | from launch.launch_description_sources import PythonLaunchDescriptionSource
12 | from launch.substitutions import LaunchConfiguration
13 |
14 |
15 | def get_launch_nodes(context, *args, **kwargs):
16 | robot = LaunchConfiguration('robot').perform(context)
17 | world = LaunchConfiguration('world').perform(context)
18 | behavior_tree = LaunchConfiguration('behavior').perform(context)
19 |
20 | # Launch ROS2 Control
21 | launch_controller = IncludeLaunchDescription(
22 | PythonLaunchDescriptionSource(
23 | os.path.join(
24 | get_package_share_directory('grab2_controller'),
25 | 'launch',
26 | f'{robot}_controllers.launch.py',
27 | )
28 | ),
29 | launch_arguments={
30 | 'hardware': LaunchConfiguration('hardware'),
31 | 'world': world,
32 | }.items(),
33 | )
34 |
35 | # Prepare configs
36 | # Franka equipped with finger gripper
37 | if robot == 'franka':
38 | robot_arg = 'franka'
39 | jc_action_arg = '/panda_arm_controller/follow_joint_trajectory'
40 | gc_action_arg = '/panda_hand_controller/gripper_cmd'
41 |
42 | # UR5e equipped with finger gripper 'Robotiq_2f_140'
43 | elif robot == 'ur5e_robotiq_gripper':
44 | robot_arg = 'ur5e_robotiq_2f_140'
45 | jc_action_arg = '/ur_arm_controller/follow_joint_trajectory'
46 | gc_action_arg = '/robotiq_gripper_controller/gripper_cmd'
47 |
48 | # UR10e equipped with suction gripper
49 | elif robot == 'ur10e_suction_gripper':
50 | robot_arg = 'ur10e'
51 | jc_action_arg = '/ur_arm_controller/follow_joint_trajectory'
52 |
53 | # Launch Planning Server
54 | launch_planner = IncludeLaunchDescription(
55 | PythonLaunchDescriptionSource(
56 | os.path.join(
57 | get_package_share_directory('grab2_curobo_planner'),
58 | 'launch',
59 | 'planning_server.launch.py',
60 | )
61 | ),
62 | launch_arguments={
63 | 'robot': robot_arg,
64 | 'world': world,
65 | }.items(),
66 | )
67 |
68 | launch_bt_engine = IncludeLaunchDescription(
69 | PythonLaunchDescriptionSource(
70 | os.path.join(
71 | get_package_share_directory('grab2_behavior_tree'),
72 | 'launch',
73 | 'grab2_engine.launch.py',
74 | )
75 | ),
76 | launch_arguments={
77 | 'behavior_tree': behavior_tree,
78 | 'jc_action': jc_action_arg,
79 | 'gc_action': gc_action_arg,
80 | }.items(),
81 | )
82 |
83 | # Delay behavior execution till Planner warms up
84 | delayed_bt_engine = TimerAction(period=60.0, actions=[launch_bt_engine])
85 |
86 | return [launch_controller, launch_planner, delayed_bt_engine]
87 |
88 |
89 | def generate_launch_description():
90 | robot_model_declaration = DeclareLaunchArgument(
91 | 'robot',
92 | default_value='franka',
93 | description='Simulated Robot -- '
94 | 'possible values: [franka, ur10e_suction_gripper, ur5e_robotiq_gripper]',
95 | )
96 |
97 | world_model_declaration = DeclareLaunchArgument(
98 | 'world',
99 | default_value='table',
100 | description='Simulation world -- possible values: [table, toybox]',
101 | )
102 |
103 | behavior_declaration = DeclareLaunchArgument(
104 | 'behavior',
105 | default_value='collect_cubes',
106 | description='Behavior xml file -- Check behavior_trees/',
107 | )
108 |
109 | ros2_control_hardware_type = DeclareLaunchArgument(
110 | 'hardware',
111 | default_value='isaac',
112 | description=(
113 | 'ROS2 control hardware interface type to use for the launch file -- '
114 | 'possible values: [mock_components, isaac]'
115 | ),
116 | )
117 |
118 | return LaunchDescription(
119 | [
120 | robot_model_declaration,
121 | world_model_declaration,
122 | behavior_declaration,
123 | ros2_control_hardware_type,
124 | OpaqueFunction(function=get_launch_nodes),
125 | ]
126 | )
127 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/launch/grab2_engine.launch.py:
--------------------------------------------------------------------------------
1 | from launch import LaunchDescription
2 | from launch.actions import DeclareLaunchArgument
3 | from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
4 | from launch_ros.actions import Node
5 | from launch_ros.substitutions import FindPackageShare
6 |
7 |
8 | def generate_launch_description():
9 | behavior_tree_declaration = DeclareLaunchArgument(
10 | 'behavior_tree',
11 | default_value='collect_cubes',
12 | description='Behavior Tree XML file, Check behavior_trees directory for examples',
13 | )
14 |
15 | jc_action_declaration = DeclareLaunchArgument(
16 | 'jc_action',
17 | default_value='/panda_arm_controller/follow_joint_trajectory',
18 | description='Joint Trajectory Controller action name',
19 | )
20 |
21 | gc_action_declaration = DeclareLaunchArgument(
22 | 'gc_action',
23 | default_value='/panda_hand_controller/gripper_cmd',
24 | description='Gripper Controller action name',
25 | )
26 |
27 | bt_xml_file = PathJoinSubstitution(
28 | [
29 | FindPackageShare('grab2_behavior_tree'),
30 | 'behavior_trees',
31 | [LaunchConfiguration('behavior_tree'), '.xml'],
32 | ]
33 | )
34 |
35 | task_planner_node = Node(
36 | package='grab2_behavior_tree',
37 | executable='grab2_engine',
38 | name='grab2_engine',
39 | output='screen',
40 | parameters=[
41 | {
42 | 'behavior_tree': bt_xml_file,
43 | 'jc_action': LaunchConfiguration('jc_action'),
44 | 'gc_action': LaunchConfiguration('gc_action'),
45 | }
46 | ],
47 | )
48 |
49 | return LaunchDescription(
50 | [
51 | behavior_tree_declaration,
52 | jc_action_declaration,
53 | gc_action_declaration,
54 | task_planner_node,
55 | ]
56 | )
57 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | grab2_behavior_tree
5 | 0.0.0
6 | TODO: Package description
7 | ros
8 | TODO: License declaration
9 |
10 | ament_cmake
11 |
12 | rclcpp
13 | rclcpp_action
14 | rclcpp_components
15 | std_msgs
16 | builtin_interfaces
17 | geometry_msgs
18 | tf2_geometry_msgs
19 | vision_msgs
20 | vision_msgs_rviz_plugins
21 | trajectory_msgs
22 | ament_index_cpp
23 | control_msgs
24 | grab2_msgs
25 | behaviortree_cpp
26 | behaviortree_ros2
27 |
28 | ament_lint_auto
29 | ament_lint_common
30 |
31 |
32 | ament_cmake
33 |
34 |
35 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/plugins/detect_object.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #include // for M_PI
4 | #include "vision_msgs/msg/detection3_d.hpp"
5 | #include "grab2_behavior_tree/detect_object.hpp"
6 |
7 | #include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
8 |
9 |
10 | namespace grab2_behavior_tree
11 | {
12 | DetectObject::DetectObject(
13 | const std::string & name,
14 | const BT::NodeConfig & conf,
15 | const BT::RosNodeParams & params
16 | )
17 | : BT::RosTopicSubNode(name, conf, params)
18 | {}
19 |
20 |
21 | BT::NodeStatus
22 | DetectObject::onTick(const std::shared_ptr & last_msg)
23 | {
24 | if (last_msg) {
25 | if (!last_msg->detections.empty()) {
26 | RCLCPP_INFO(logger(), "[%s] Found toy", name().c_str());
27 | auto & detection = last_msg->detections.back();
28 | // Set grasp
29 | geometry_msgs::msg::PoseStamped target;
30 | target.header.frame_id = last_msg->header.frame_id;
31 | target.pose = detection.bbox.center;
32 |
33 | setOutput("class", "car"); // TODO(ElSayed): Use /semantics topic to get class_id
34 | setOutput("pose", target);
35 | return BT::NodeStatus::SUCCESS;
36 |
37 | } else {
38 | RCLCPP_INFO(logger(), "[%s] Can't find any toys", name().c_str());
39 | }
40 | }
41 | return BT::NodeStatus::FAILURE;
42 | }
43 |
44 | } // namespace grab2_behavior_tree
45 |
46 | #include "behaviortree_ros2/plugins.hpp"
47 | CreateRosNodePlugin(grab2_behavior_tree::DetectObject, "DetectObject") // NOLINT
48 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/plugins/get_grasp.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #include // for M_PI
4 | #include "grab2_behavior_tree/get_grasp.hpp"
5 |
6 | #include "tf2/utils.hpp"
7 | #include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
8 |
9 | namespace grab2_behavior_tree
10 | {
11 |
12 | GetGrasp::GetGrasp(
13 | const std::string & name,
14 | const BT::NodeConfig & conf
15 | )
16 | : BT::SyncActionNode(name, conf)
17 | {
18 | }
19 |
20 | BT::NodeStatus
21 | GetGrasp::tick()
22 | {
23 | // Get object pose
24 | geometry_msgs::msg::PoseStamped object_pose;
25 | getInput("object_pose", object_pose);
26 |
27 | // Generate Grasp Pose
28 | // TODO(ElSayed): Get Grasp pose from a yaml file that corresponds to the object type
29 | geometry_msgs::msg::PoseStamped grasp_pose(object_pose);
30 |
31 | // Position
32 | grasp_pose.pose.position.z += 0.12;
33 |
34 | // Orientation
35 | double yaw = tf2::getYaw(grasp_pose.pose.orientation);
36 | tf2::Quaternion target_quat;
37 | target_quat.setRPY(M_PI, 0, yaw);
38 | grasp_pose.pose.orientation = tf2::toMsg(target_quat);
39 |
40 | // Set grasp pose
41 | setOutput("grasp", grasp_pose);
42 |
43 | // Set pre-grasp pose
44 | grasp_pose.pose.position.z += 0.1;
45 | setOutput("pregrasp", grasp_pose);
46 |
47 |
48 | return BT::NodeStatus::SUCCESS;
49 | }
50 |
51 | } // namespace grab2_behavior_tree
52 |
53 | BT_REGISTER_NODES(factory)
54 | {
55 | factory.registerNodeType("GetGrasp");
56 | }
57 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/plugins/get_grasp_hardcoded.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #include "grab2_behavior_tree/get_grasp_hardcoded.hpp"
4 |
5 | namespace grab2_behavior_tree
6 | {
7 |
8 | // Initialize counter
9 | int GetGraspHardcoded::counter_ = 0;
10 |
11 | GetGraspHardcoded::GetGraspHardcoded(
12 | const std::string & name,
13 | const BT::NodeConfig & conf
14 | )
15 | : BT::SyncActionNode(name, conf)
16 | {
17 | // Cubes
18 | geometry_msgs::msg::PoseStamped pose_stamped;
19 | pose_stamped.header.frame_id = "world";
20 |
21 | pose_stamped.pose.position.x = -0.34;
22 | pose_stamped.pose.position.y = -0.2;
23 | pose_stamped.pose.position.z = 0.15;
24 | pose_stamped.pose.orientation.x = 1.0;
25 | pose_stamped.pose.orientation.y = 0.0;
26 | pose_stamped.pose.orientation.z = 0.0;
27 | pose_stamped.pose.orientation.w = 0.0;
28 |
29 | // 1st Cube
30 | grasp_poses_.push_back(pose_stamped);
31 |
32 | // 2nd Cube
33 | pose_stamped.pose.position.y += -0.1;
34 | grasp_poses_.push_back(pose_stamped);
35 |
36 | // 3rd Cube
37 | pose_stamped.pose.position.y += -0.1;
38 | grasp_poses_.push_back(pose_stamped);
39 | }
40 |
41 | BT::NodeStatus
42 | GetGraspHardcoded::tick()
43 | {
44 | if (counter_ > 2) {
45 | return BT::NodeStatus::FAILURE;
46 | }
47 | // Set grasp pose
48 | setOutput("grasp", grasp_poses_[counter_]);
49 |
50 | // Set pregrasp pose
51 | geometry_msgs::msg::PoseStamped ps(grasp_poses_[counter_]);
52 | ps.pose.position.z = 0.2;
53 | setOutput("pregrasp", ps);
54 |
55 | // Increment to next target
56 | counter_++;
57 |
58 | return BT::NodeStatus::SUCCESS;
59 | }
60 |
61 | } // namespace grab2_behavior_tree
62 |
63 | BT_REGISTER_NODES(factory)
64 | {
65 | factory.registerNodeType("GetGraspHardcoded");
66 | }
67 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/plugins/get_trajectory_hardcoded.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #include "builtin_interfaces/msg/duration.hpp"
4 | #include "trajectory_msgs/msg/joint_trajectory_point.hpp"
5 | #include "grab2_behavior_tree/get_trajectory_hardcoded.hpp"
6 |
7 | namespace grab2_behavior_tree
8 | {
9 |
10 | SetJointTrajectoryGoal::SetJointTrajectoryGoal(
11 | const std::string & name,
12 | const BT::NodeConfig & conf
13 | )
14 | : BT::SyncActionNode(name, conf)
15 | {}
16 |
17 | BT::NodeStatus
18 | SetJointTrajectoryGoal::tick()
19 | {
20 | // Initialize JointTrajectory
21 | trajectory_msgs::msg::JointTrajectory jt;
22 | jt.header.frame_id = "panda_link0";
23 | jt.joint_names = {
24 | "panda_joint1",
25 | "panda_joint2",
26 | "panda_joint3",
27 | "panda_joint4",
28 | "panda_joint5",
29 | "panda_joint6",
30 | "panda_joint7"
31 | };
32 |
33 | // Initialize Duration
34 | builtin_interfaces::msg::Duration duration;
35 |
36 | // Initialize Point
37 | trajectory_msgs::msg::JointTrajectoryPoint pt;
38 | pt.positions = {
39 | 0.0,
40 | -0.7841,
41 | 0.0,
42 | -2.3571,
43 | 0.0,
44 | 1.787,
45 | 0.7672
46 | };
47 |
48 | // 1st Point
49 | duration.sec = 1.0;
50 | pt.positions[0] = 1.57;
51 | pt.time_from_start = duration;
52 | jt.points.push_back(pt);
53 |
54 | // Second Point
55 | duration.sec = 3.0;
56 | pt.positions[0] = 0.0;
57 | pt.time_from_start = duration;
58 | jt.points.push_back(pt);
59 |
60 | // Set output
61 | setOutput("trajectory", jt);
62 |
63 | return BT::NodeStatus::SUCCESS;
64 | }
65 |
66 | } // namespace grab2_behavior_tree
67 |
68 | BT_REGISTER_NODES(factory)
69 | {
70 | factory.registerNodeType("GetTrajectoryHardcoded");
71 | }
72 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/plugins/grip.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #include "grab2_behavior_tree/grip.hpp"
4 |
5 | namespace grab2_behavior_tree
6 | {
7 |
8 | Grip::Grip(
9 | const std::string & name, const BT::NodeConfig & conf,
10 | const BT::RosNodeParams & params)
11 | : BT::RosActionNode(name, conf, params)
12 | {}
13 |
14 | bool
15 | Grip::setGoal(BT::RosActionNode::Goal & goal)
16 | {
17 | goal.command.max_effort = 50.0;
18 | getInput("pose", goal.command.position);
19 | return true;
20 | }
21 |
22 | BT::NodeStatus
23 | Grip::onResultReceived(const WrappedResult & wr)
24 | {
25 | if (wr.result->reached_goal) {
26 | RCLCPP_INFO(logger(), "[%s] Gripping SUCCESS", name().c_str());
27 | return BT::NodeStatus::SUCCESS;
28 | }
29 | return BT::NodeStatus::FAILURE;
30 | }
31 |
32 | BT::NodeStatus
33 | Grip::onFeedback(const std::shared_ptr feedback)
34 | {
35 | RCLCPP_INFO(logger(), "[%s] Grip State:", name().c_str());
36 | RCLCPP_INFO(logger(), "[%s] Position: %.3f", name().c_str(), feedback->position);
37 | RCLCPP_INFO(logger(), "[%s] Effort: %.3f", name().c_str(), feedback->effort);
38 | RCLCPP_INFO(logger(), "[%s] Stalled: %s", name().c_str(), feedback->stalled ? "true" : "false");
39 | RCLCPP_INFO(
40 | logger(), "[%s] Reached Goal: %s",
41 | name().c_str(), feedback->reached_goal ? "true" : "false");
42 | return BT::NodeStatus::RUNNING;
43 | }
44 |
45 | } // namespace grab2_behavior_tree
46 |
47 | #include "behaviortree_ros2/plugins.hpp"
48 | CreateRosNodePlugin(grab2_behavior_tree::Grip, "Grip"); // NOLINT
49 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/plugins/move.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #include "grab2_behavior_tree/move.hpp"
4 |
5 | namespace grab2_behavior_tree
6 | {
7 | Move::Move(
8 | const std::string & name,
9 | const BT::NodeConfig & conf,
10 | const BT::RosNodeParams & params
11 | )
12 | : BT::RosActionNode(name, conf, params)
13 | {}
14 |
15 | bool
16 | Move::setGoal(BT::RosActionNode::Goal & goal)
17 | {
18 | getInput("trajectory", goal.trajectory);
19 | return true;
20 | }
21 |
22 | BT::NodeStatus
23 | Move::onResultReceived(const WrappedResult & wr)
24 | {
25 | if (wr.result->error_code == wr.result->SUCCESSFUL) {
26 | RCLCPP_INFO(
27 | logger(), "[%s] Execution %s",
28 | name().c_str(),
29 | colorize("SUCCESS", AnsiColor::GREEN).c_str()
30 | );
31 | return BT::NodeStatus::SUCCESS;
32 | }
33 | return BT::NodeStatus::FAILURE;
34 | }
35 | void
36 | Move::onHalt()
37 | {
38 | RCLCPP_INFO(
39 | logger(), "[%s] Execution %s",
40 | name().c_str(),
41 | colorize("Stop", AnsiColor::YELLOW).c_str()
42 | );
43 | }
44 |
45 | } // namespace grab2_behavior_tree
46 |
47 | #include "behaviortree_ros2/plugins.hpp"
48 | CreateRosNodePlugin(grab2_behavior_tree::Move, "Move") // NOLINT
49 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/plugins/plan.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #include "grab2_behavior_tree/plan.hpp"
4 |
5 | namespace grab2_behavior_tree
6 | {
7 | Plan::Plan(
8 | const std::string & name,
9 | const BT::NodeConfig & conf,
10 | const BT::RosNodeParams & params
11 | )
12 | : BT::RosActionNode(name, conf, params)
13 | {}
14 |
15 | bool
16 | Plan::setGoal(BT::RosActionNode::Goal & goal)
17 | {
18 | // Set Header
19 | goal.header.stamp = now();
20 | goal.header.frame_id = "panda_link0";
21 |
22 | // Set Goal Pose
23 | getInput("target_ik", goal.goal);
24 |
25 | RCLCPP_DEBUG(logger(), "Goal Frame: [%s]", goal.goal.header.frame_id.c_str());
26 | RCLCPP_DEBUG(logger(), "Goal Position (x): [%f]", goal.goal.pose.position.x);
27 | RCLCPP_DEBUG(logger(), "Goal Position (y): [%f]", goal.goal.pose.position.y);
28 | RCLCPP_DEBUG(logger(), "Goal Position (z): [%f]", goal.goal.pose.position.z);
29 |
30 | return true;
31 | }
32 |
33 | BT::NodeStatus
34 | Plan::onResultReceived(const WrappedResult & wr)
35 | {
36 | // wr.result->error_code == wr.result-> SUCCESSFUL
37 | if (wr.code == rclcpp_action::ResultCode::SUCCEEDED) {
38 | RCLCPP_INFO(logger(), "[%s] Planning SUCCESS", name().c_str());
39 | setOutput("trajectory", wr.result->trajectory);
40 | return BT::NodeStatus::SUCCESS;
41 | } else {
42 | return BT::NodeStatus::FAILURE;
43 | }
44 | return BT::NodeStatus::SUCCESS;
45 | }
46 |
47 | } // namespace grab2_behavior_tree
48 |
49 | #include "behaviortree_ros2/plugins.hpp"
50 | CreateRosNodePlugin(grab2_behavior_tree::Plan, "Plan") // NOLINT
51 |
--------------------------------------------------------------------------------
/grab2_behavior_tree/src/main.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #include "rclcpp/rclcpp.hpp"
4 | #include "behaviortree_cpp/bt_factory.h" // BehaviorTreeFactory
5 | #include "behaviortree_cpp/utils/shared_library.h" // loader
6 | #include "behaviortree_cpp/loggers/groot2_publisher.h"
7 |
8 | #include "behaviortree_ros2/plugins.hpp" // RegisterRosNode
9 |
10 | BT::NodeStatus SaySomethingSimple(BT::TreeNode & self);
11 |
12 | int main(int argc, char ** argv)
13 | {
14 | rclcpp::init(argc, argv);
15 | auto node = std::make_shared("task_planner");
16 |
17 | BT::BehaviorTreeFactory factory;
18 | BT::SharedLibrary loader;
19 |
20 | // Register Native BT Nodes
21 | factory.registerFromPlugin(loader.getOSName("grab2_get_grasp"));
22 | factory.registerFromPlugin(loader.getOSName("grab2_get_grasp_hardcoded"));
23 | factory.registerFromPlugin(loader.getOSName("grab2_get_trajectory_hardcoded"));
24 |
25 | // Declare Parameters
26 | node->declare_parameter("behavior_tree", "");
27 | node->declare_parameter("jc_action", "/panda_arm_controller/follow_joint_trajectory");
28 | node->declare_parameter("gc_action", "/panda_hand_controller/gripper_cmd");
29 |
30 | // Get Parameters
31 | std::string jc_action = node->get_parameter("jc_action").as_string();
32 | std::string gc_action = node->get_parameter("gc_action").as_string();
33 |
34 | // Register ROS2 BT Nodes
35 | RegisterRosNode(
36 | factory,
37 | loader.getOSName("grab2_move"),
38 | {node, jc_action});
39 | RegisterRosNode(
40 | factory,
41 | loader.getOSName("grab2_grip"),
42 | {node, gc_action});
43 | RegisterRosNode(
44 | factory,
45 | loader.getOSName("grab2_plan"),
46 | {node, "/plan_to_goal"});
47 | RegisterRosNode(
48 | factory,
49 | loader.getOSName("grab2_detect_object"),
50 | {node, "/eef_camera/bbox_3d"});
51 |
52 | // SaySomething BT Node
53 | BT::PortsList say_something_ports = {BT::InputPort("message")};
54 | factory.registerSimpleAction("SaySomething", SaySomethingSimple, say_something_ports);
55 |
56 | // Create tree
57 | std::string bt_xml = node->get_parameter("behavior_tree").as_string();
58 | auto tree = factory.createTreeFromFile(bt_xml);
59 |
60 | BT::Groot2Publisher publisher(tree); // Port Defaults to 1667
61 |
62 | tree.tickWhileRunning();
63 | return 0;
64 | }
65 |
66 | BT::NodeStatus SaySomethingSimple(BT::TreeNode & self)
67 | {
68 | auto msg = self.getInput("message");
69 | if (!msg) {
70 | throw BT::RuntimeError("missing required input [message]: ", msg.error());
71 | }
72 |
73 | std::cout << "Robot says: " << msg.value() << std::endl;
74 | return BT::NodeStatus::SUCCESS;
75 | }
76 |
--------------------------------------------------------------------------------
/grab2_controller/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.8)
2 | project(grab2_controller)
3 |
4 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
5 | add_compile_options(-Wall -Wextra -Wpedantic)
6 | endif()
7 |
8 | # find dependencies
9 | find_package(ament_cmake REQUIRED)
10 |
11 | install(DIRECTORY launch description config DESTINATION share/${PROJECT_NAME})
12 |
13 | if(BUILD_TESTING)
14 | find_package(ament_lint_auto REQUIRED)
15 | # the following line skips the linter which checks for copyrights
16 | # comment the line when a copyright and license is added to all source files
17 | set(ament_cmake_copyright_FOUND TRUE)
18 | # the following line skips cpplint (only works in a git repo)
19 | # comment the line when this package is in a git repo and when
20 | # a copyright and license is added to all source files
21 | set(ament_cmake_cpplint_FOUND TRUE)
22 | ament_lint_auto_find_test_dependencies()
23 | endif()
24 |
25 | ament_package()
26 |
--------------------------------------------------------------------------------
/grab2_controller/config/panda/initial_positions.yaml:
--------------------------------------------------------------------------------
1 | # Default initial positions for the panda arm's ros2_control fake system
2 | initial_positions:
3 | panda_joint1: 0.0
4 | panda_joint2: -0.785
5 | panda_joint3: 0.0
6 | panda_joint4: -2.356
7 | panda_joint5: 0.0
8 | panda_joint6: 1.571
9 | panda_joint7: 0.785
10 |
--------------------------------------------------------------------------------
/grab2_controller/config/panda/ros2_controllers.yaml:
--------------------------------------------------------------------------------
1 | # This config file is used by ros2_control
2 | controller_manager:
3 | ros__parameters:
4 | update_rate: 100 # Hz
5 |
6 | joint_state_broadcaster:
7 | type: joint_state_broadcaster/JointStateBroadcaster
8 |
9 | panda_arm_controller:
10 | type: joint_trajectory_controller/JointTrajectoryController
11 |
12 | panda_hand_controller:
13 | type: position_controllers/GripperActionController
14 |
15 | panda_arm_controller:
16 | ros__parameters:
17 | command_interfaces:
18 | - position
19 | state_interfaces:
20 | - position
21 | - velocity
22 | joints:
23 | - panda_joint1
24 | - panda_joint2
25 | - panda_joint3
26 | - panda_joint4
27 | - panda_joint5
28 | - panda_joint6
29 | - panda_joint7
30 |
31 | panda_hand_controller:
32 | ros__parameters:
33 | joint: panda_finger_joint1
34 |
--------------------------------------------------------------------------------
/grab2_controller/config/rviz2/3d_detection_cfg.yaml:
--------------------------------------------------------------------------------
1 | ## Car Class
2 | '0':
3 | r: 0
4 | g: 255
5 | b: 0
6 |
--------------------------------------------------------------------------------
/grab2_controller/config/ur/initial_positions.yaml:
--------------------------------------------------------------------------------
1 | # Default initial positions for the ur10e's ros2_control fake system
2 | initial_positions:
3 | shoulder_pan_joint: 0.0
4 | shoulder_lift_joint: -1.57
5 | elbow_joint: 0.0
6 | wrist_1_joint: -1.57
7 | wrist_2_joint: 0.0
8 | wrist_3_joint: 0.0
9 |
--------------------------------------------------------------------------------
/grab2_controller/config/ur/ros2_controllers.yaml:
--------------------------------------------------------------------------------
1 | # This config file is used by ros2_control
2 | controller_manager:
3 | ros__parameters:
4 | update_rate: 100 # Hz
5 |
6 | joint_state_broadcaster:
7 | type: joint_state_broadcaster/JointStateBroadcaster
8 |
9 | ur_arm_controller:
10 | type: joint_trajectory_controller/JointTrajectoryController
11 |
12 | robotiq_gripper_controller:
13 | type: position_controllers/GripperActionController
14 |
15 | ur_arm_controller:
16 | ros__parameters:
17 | command_interfaces:
18 | - position
19 | state_interfaces:
20 | - position
21 | - velocity
22 | joints:
23 | - shoulder_pan_joint
24 | - shoulder_lift_joint
25 | - elbow_joint
26 | - wrist_1_joint
27 | - wrist_2_joint
28 | - wrist_3_joint
29 |
30 | robotiq_gripper_controller:
31 | ros__parameters:
32 | joint: finger_joint
33 |
--------------------------------------------------------------------------------
/grab2_controller/description/panda/panda.ros2_control.xacro:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | mock_components/GenericSystem
11 |
12 |
13 | topic_based_ros2_control/TopicBasedSystem
14 | /isaac_joint_commands
15 | /isaac_joint_states
16 |
17 |
18 |
19 |
20 |
21 | ${initial_positions['panda_joint1']}
22 |
23 |
24 | 0.0
25 |
26 |
27 |
28 |
29 |
30 | ${initial_positions['panda_joint2']}
31 |
32 |
33 | 0.0
34 |
35 |
36 |
37 |
38 |
39 | ${initial_positions['panda_joint3']}
40 |
41 |
42 | 0.0
43 |
44 |
45 |
46 |
47 |
48 | ${initial_positions['panda_joint4']}
49 |
50 |
51 | 0.0
52 |
53 |
54 |
55 |
56 |
57 | ${initial_positions['panda_joint5']}
58 |
59 |
60 | 0.0
61 |
62 |
63 |
64 |
65 |
66 | ${initial_positions['panda_joint6']}
67 |
68 |
69 | 0.0
70 |
71 |
72 |
73 |
74 |
75 | ${initial_positions['panda_joint7']}
76 |
77 |
78 | 0.0
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | 0.0
87 |
88 |
89 | 0.0
90 |
91 |
92 |
93 | panda_finger_joint1
94 | 1
95 |
96 | 0.0
97 |
98 |
99 | 0.0
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/grab2_controller/description/panda/panda.urdf.xacro:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/grab2_controller/description/robotiq_2f_140/README.md:
--------------------------------------------------------------------------------
1 | # Robotiq 140mm 2-Finger-Adaptive-Gripper
2 |
3 | This is the README file from Robotiq's package for the 2F-140 gripper.
4 |
5 | Commit `4e3196c1c6a83f54acfa3dd8cf02b575ebba3e53` is used
6 | from [Robotiq's ROS Industrial Github repository](https://github.com/ros-industrial/robotiq)
7 |
8 | This package contains the 140mm stroke gripper URDF files from robotiq series **C3**.
9 |
10 | ## Gripper Visual
11 |
12 | 
13 |
--------------------------------------------------------------------------------
/grab2_controller/description/robotiq_2f_140/meshes/collision/robotiq_arg2f_140_inner_finger.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/grab2_controller/description/robotiq_2f_140/meshes/collision/robotiq_arg2f_140_inner_finger.stl
--------------------------------------------------------------------------------
/grab2_controller/description/robotiq_2f_140/meshes/collision/robotiq_arg2f_140_inner_knuckle.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/grab2_controller/description/robotiq_2f_140/meshes/collision/robotiq_arg2f_140_inner_knuckle.stl
--------------------------------------------------------------------------------
/grab2_controller/description/robotiq_2f_140/meshes/collision/robotiq_arg2f_140_outer_finger.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/grab2_controller/description/robotiq_2f_140/meshes/collision/robotiq_arg2f_140_outer_finger.stl
--------------------------------------------------------------------------------
/grab2_controller/description/robotiq_2f_140/meshes/collision/robotiq_arg2f_140_outer_knuckle.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/grab2_controller/description/robotiq_2f_140/meshes/collision/robotiq_arg2f_140_outer_knuckle.stl
--------------------------------------------------------------------------------
/grab2_controller/description/robotiq_2f_140/meshes/collision/robotiq_arg2f_base_link.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/grab2_controller/description/robotiq_2f_140/meshes/collision/robotiq_arg2f_base_link.stl
--------------------------------------------------------------------------------
/grab2_controller/description/robotiq_2f_140/meshes/collision/robotiq_arg2f_coupling.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/grab2_controller/description/robotiq_2f_140/meshes/collision/robotiq_arg2f_coupling.stl
--------------------------------------------------------------------------------
/grab2_controller/description/robotiq_2f_140/meshes/visual/robotiq_arg2f_140_inner_finger.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/grab2_controller/description/robotiq_2f_140/meshes/visual/robotiq_arg2f_140_inner_finger.stl
--------------------------------------------------------------------------------
/grab2_controller/description/robotiq_2f_140/meshes/visual/robotiq_arg2f_140_inner_knuckle.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/grab2_controller/description/robotiq_2f_140/meshes/visual/robotiq_arg2f_140_inner_knuckle.stl
--------------------------------------------------------------------------------
/grab2_controller/description/robotiq_2f_140/meshes/visual/robotiq_arg2f_140_outer_finger.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/grab2_controller/description/robotiq_2f_140/meshes/visual/robotiq_arg2f_140_outer_finger.stl
--------------------------------------------------------------------------------
/grab2_controller/description/robotiq_2f_140/meshes/visual/robotiq_arg2f_140_outer_knuckle.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/grab2_controller/description/robotiq_2f_140/meshes/visual/robotiq_arg2f_140_outer_knuckle.stl
--------------------------------------------------------------------------------
/grab2_controller/description/robotiq_2f_140/meshes/visual/robotiq_arg2f_base_link.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/grab2_controller/description/robotiq_2f_140/meshes/visual/robotiq_arg2f_base_link.stl
--------------------------------------------------------------------------------
/grab2_controller/description/robotiq_2f_140/meshes/visual/robotiq_arg2f_coupling.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/grab2_controller/description/robotiq_2f_140/meshes/visual/robotiq_arg2f_coupling.stl
--------------------------------------------------------------------------------
/grab2_controller/description/ur/ur.ros2_control.xacro:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | mock_components/GenericSystem
11 |
12 |
13 | topic_based_ros2_control/TopicBasedSystem
14 | /isaac_joint_commands
15 | /isaac_joint_states
16 |
17 |
18 |
19 |
20 |
21 | ${initial_positions['shoulder_pan_joint']}
22 |
23 |
24 | 0.0
25 |
26 |
27 |
28 |
29 |
30 | ${initial_positions['shoulder_lift_joint']}
31 |
32 |
33 | 0.0
34 |
35 |
36 |
37 |
38 |
39 | ${initial_positions['elbow_joint']}
40 |
41 |
42 | 0.0
43 |
44 |
45 |
46 |
47 |
48 | ${initial_positions['wrist_1_joint']}
49 |
50 |
51 | 0.0
52 |
53 |
54 |
55 |
56 |
57 | ${initial_positions['wrist_2_joint']}
58 |
59 |
60 | 0.0
61 |
62 |
63 |
64 |
65 |
66 | ${initial_positions['wrist_3_joint']}
67 |
68 |
69 | 0.0
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/grab2_controller/description/ur/ur.urdf.xacro:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/grab2_controller/launch/franka_controllers.launch.py:
--------------------------------------------------------------------------------
1 | from launch import LaunchDescription
2 | from launch.actions import DeclareLaunchArgument
3 | from launch.conditions import IfCondition
4 | from launch.substitutions import (
5 | Command,
6 | EqualsSubstitution,
7 | FindExecutable,
8 | LaunchConfiguration,
9 | PathJoinSubstitution,
10 | )
11 | from launch_ros.actions import Node
12 | from launch_ros.substitutions import FindPackageShare
13 |
14 |
15 | def generate_launch_description():
16 | # Launch configuration variables
17 | use_rviz = LaunchConfiguration('use_rviz')
18 |
19 | # Command-line arguments
20 | declare_description_package = DeclareLaunchArgument(
21 | 'description_package', default_value='grab2_controller'
22 | )
23 |
24 | declare_use_rviz_cmd = DeclareLaunchArgument(
25 | 'use_rviz', default_value='True', description='Whether to start RVIZ'
26 | )
27 |
28 | declare_ros2_control_hardware_type = DeclareLaunchArgument(
29 | 'hardware',
30 | default_value='isaac',
31 | description=(
32 | 'ROS2 control hardware interface type to use for the launch file -- '
33 | 'possible values: [mock_components, isaac]'
34 | ),
35 | )
36 |
37 | declare_sim_world = DeclareLaunchArgument(
38 | 'world',
39 | default_value='table',
40 | description='Simulation world -- possible values: [table, toybox]',
41 | )
42 |
43 | robot_xacro_path = PathJoinSubstitution(
44 | [
45 | FindPackageShare(LaunchConfiguration('description_package')),
46 | 'description',
47 | 'panda',
48 | 'panda.urdf.xacro',
49 | ]
50 | )
51 |
52 | ros2_controllers_path = PathJoinSubstitution(
53 | [
54 | FindPackageShare(LaunchConfiguration('description_package')),
55 | 'config',
56 | 'panda',
57 | 'ros2_controllers.yaml',
58 | ]
59 | )
60 |
61 | rviz_config_file = PathJoinSubstitution(
62 | [
63 | FindPackageShare(LaunchConfiguration('description_package')),
64 | 'config',
65 | 'rviz2',
66 | 'rviz2_config.rviz',
67 | ]
68 | )
69 |
70 | robot_description_content = Command(
71 | [
72 | PathJoinSubstitution([FindExecutable(name='xacro')]),
73 | ' ',
74 | robot_xacro_path,
75 | ' ',
76 | 'ros2_control_hardware_type:=',
77 | LaunchConfiguration('hardware'),
78 | ' ',
79 | ]
80 | )
81 |
82 | # Publish Robot State
83 | robot_state_publisher = Node(
84 | package='robot_state_publisher',
85 | executable='robot_state_publisher',
86 | name='robot_state_publisher',
87 | output='both',
88 | parameters=[{'robot_description': robot_description_content}],
89 | )
90 |
91 | ros2_control_node = Node(
92 | package='controller_manager',
93 | executable='ros2_control_node',
94 | parameters=[ros2_controllers_path],
95 | remappings=[
96 | ('/controller_manager/robot_description', '/robot_description'),
97 | ],
98 | output='screen',
99 | )
100 |
101 | joint_state_broadcaster_spawner = Node(
102 | package='controller_manager',
103 | executable='spawner',
104 | arguments=[
105 | 'joint_state_broadcaster',
106 | '--controller-manager',
107 | '/controller_manager',
108 | ],
109 | )
110 |
111 | panda_arm_controller_spawner = Node(
112 | package='controller_manager',
113 | executable='spawner',
114 | arguments=['panda_arm_controller', '-c', '/controller_manager'],
115 | )
116 |
117 | panda_hand_controller_spawner = Node(
118 | package='controller_manager',
119 | executable='spawner',
120 | arguments=['panda_hand_controller', '-c', '/controller_manager'],
121 | )
122 |
123 | rviz_node = Node(
124 | package='rviz2',
125 | executable='rviz2',
126 | name='rviz2',
127 | output='log',
128 | arguments=['-d', rviz_config_file],
129 | condition=IfCondition(use_rviz),
130 | )
131 |
132 | world2robot_tf_toybox = Node(
133 | package='tf2_ros',
134 | executable='static_transform_publisher',
135 | name='static_transform_publisher',
136 | output='log',
137 | arguments=[
138 | '0.0', # translation
139 | '0.25',
140 | '0.0',
141 | '0.0', # rotation (RPY)
142 | '0.0',
143 | '0.0',
144 | 'world',
145 | 'panda_link0',
146 | ],
147 | condition=IfCondition(
148 | EqualsSubstitution(LaunchConfiguration('world'), 'toybox')
149 | ),
150 | )
151 |
152 | world2robot_tf_table = Node(
153 | package='tf2_ros',
154 | executable='static_transform_publisher',
155 | name='static_transform_publisher',
156 | output='log',
157 | arguments=[
158 | '0.0', # translation
159 | '-0.640',
160 | '0.0',
161 | '1.571', # rotation (RPY)
162 | '0.0',
163 | '0.0',
164 | 'world',
165 | 'panda_link0',
166 | ],
167 | condition=IfCondition(
168 | EqualsSubstitution(LaunchConfiguration('world'), 'table')
169 | ),
170 | )
171 |
172 | return LaunchDescription(
173 | [
174 | declare_description_package,
175 | declare_use_rviz_cmd,
176 | declare_ros2_control_hardware_type,
177 | declare_sim_world,
178 | rviz_node,
179 | robot_state_publisher,
180 | ros2_control_node,
181 | joint_state_broadcaster_spawner,
182 | panda_arm_controller_spawner,
183 | panda_hand_controller_spawner,
184 | world2robot_tf_toybox,
185 | world2robot_tf_table,
186 | ]
187 | )
188 |
--------------------------------------------------------------------------------
/grab2_controller/launch/ur10e_suction_gripper_controllers.launch.py:
--------------------------------------------------------------------------------
1 | from launch import LaunchDescription
2 | from launch.actions import DeclareLaunchArgument
3 | from launch.conditions import IfCondition
4 | from launch.substitutions import (
5 | Command,
6 | EqualsSubstitution,
7 | FindExecutable,
8 | LaunchConfiguration,
9 | PathJoinSubstitution,
10 | )
11 | from launch_ros.actions import Node
12 | from launch_ros.substitutions import FindPackageShare
13 |
14 |
15 | def generate_launch_description():
16 | # Command-line arguments
17 | description_package = DeclareLaunchArgument(
18 | 'description_package', default_value='grab2_controller'
19 | )
20 |
21 | ros2_control_hardware_type = DeclareLaunchArgument(
22 | 'hardware',
23 | default_value='isaac',
24 | description=(
25 | 'ROS2 control hardware interface type to use for the launch file -- '
26 | 'possible values: [mock_components, isaac]'
27 | ),
28 | )
29 |
30 | sim_world_declare = DeclareLaunchArgument(
31 | 'world',
32 | default_value='table',
33 | description='Simulation world -- possible values: [table, toybox]',
34 | )
35 |
36 | robot_xacro_path = PathJoinSubstitution(
37 | [
38 | FindPackageShare(LaunchConfiguration('description_package')),
39 | 'description',
40 | 'ur',
41 | 'ur.urdf.xacro',
42 | ]
43 | )
44 |
45 | ros2_controllers_path = PathJoinSubstitution(
46 | [
47 | FindPackageShare(LaunchConfiguration('description_package')),
48 | 'config',
49 | 'ur',
50 | 'ros2_controllers.yaml',
51 | ]
52 | )
53 |
54 | rviz_config_file = PathJoinSubstitution(
55 | [
56 | FindPackageShare(LaunchConfiguration('description_package')),
57 | 'config',
58 | 'rviz2',
59 | 'rviz2_config.rviz',
60 | ]
61 | )
62 |
63 | robot_description_content = Command(
64 | [
65 | PathJoinSubstitution([FindExecutable(name='xacro')]),
66 | ' ',
67 | robot_xacro_path,
68 | ' ',
69 | 'ros2_control_hardware_type:=',
70 | LaunchConfiguration('hardware'),
71 | ' ',
72 | ]
73 | )
74 |
75 | # Publish Robot State
76 | robot_state_publisher = Node(
77 | package='robot_state_publisher',
78 | executable='robot_state_publisher',
79 | name='robot_state_publisher',
80 | output='both',
81 | parameters=[{'robot_description': robot_description_content}],
82 | )
83 |
84 | ros2_control_node = Node(
85 | package='controller_manager',
86 | executable='ros2_control_node',
87 | parameters=[ros2_controllers_path],
88 | remappings=[
89 | ('/controller_manager/robot_description', '/robot_description'),
90 | ],
91 | output='screen',
92 | )
93 |
94 | joint_state_broadcaster_spawner = Node(
95 | package='controller_manager',
96 | executable='spawner',
97 | arguments=[
98 | 'joint_state_broadcaster',
99 | '--controller-manager',
100 | '/controller_manager',
101 | ],
102 | )
103 |
104 | ur10e_controller_spawner = Node(
105 | package='controller_manager',
106 | executable='spawner',
107 | arguments=['ur_arm_controller', '-c', '/controller_manager'],
108 | )
109 |
110 | rviz_node = Node(
111 | package='rviz2',
112 | executable='rviz2',
113 | name='rviz2',
114 | output='log',
115 | arguments=['-d', rviz_config_file],
116 | )
117 |
118 | world2robot_tf_toybox = Node(
119 | package='tf2_ros',
120 | executable='static_transform_publisher',
121 | name='static_transform_publisher',
122 | output='log',
123 | arguments=[
124 | '0.0', # translation
125 | '0.25',
126 | '0.0',
127 | '0.0', # rotation (RPY)
128 | '0.0',
129 | '0.0',
130 | 'world',
131 | 'base_link',
132 | ],
133 | condition=IfCondition(
134 | EqualsSubstitution(LaunchConfiguration('world'), 'toybox')
135 | ),
136 | )
137 |
138 | world2robot_tf_table = Node(
139 | package='tf2_ros',
140 | executable='static_transform_publisher',
141 | name='static_transform_publisher',
142 | output='log',
143 | arguments=[
144 | '0.0', # translation
145 | '-0.640',
146 | '0.0',
147 | '0.0', # rotation (RPY)
148 | '0.0',
149 | '0.0',
150 | 'world',
151 | 'base_link',
152 | ],
153 | condition=IfCondition(
154 | EqualsSubstitution(LaunchConfiguration('world'), 'table')
155 | ),
156 | )
157 |
158 | return LaunchDescription(
159 | [
160 | sim_world_declare,
161 | description_package,
162 | ros2_control_hardware_type,
163 | rviz_node,
164 | robot_state_publisher,
165 | ros2_control_node,
166 | joint_state_broadcaster_spawner,
167 | ur10e_controller_spawner,
168 | world2robot_tf_toybox,
169 | world2robot_tf_table,
170 | ]
171 | )
172 |
--------------------------------------------------------------------------------
/grab2_controller/launch/ur5e_robotiq_2f_140_controllers.launch.py:
--------------------------------------------------------------------------------
1 | from launch import LaunchDescription
2 | from launch.actions import DeclareLaunchArgument
3 | from launch.conditions import IfCondition
4 | from launch.substitutions import (
5 | Command,
6 | EqualsSubstitution,
7 | FindExecutable,
8 | LaunchConfiguration,
9 | PathJoinSubstitution,
10 | )
11 | from launch_ros.actions import Node
12 | from launch_ros.substitutions import FindPackageShare
13 |
14 |
15 | def generate_launch_description():
16 | # Command-line arguments
17 | description_package = DeclareLaunchArgument(
18 | 'description_package', default_value='grab2_controller'
19 | )
20 |
21 | ros2_control_hardware_type = DeclareLaunchArgument(
22 | 'hardware',
23 | default_value='isaac',
24 | description=(
25 | 'ROS2 control hardware interface type to use for the launch file -- '
26 | 'possible values: [mock_components, isaac]'
27 | ),
28 | )
29 |
30 | sim_world_declare = DeclareLaunchArgument(
31 | 'world',
32 | default_value='table',
33 | description='Simulation world -- possible values: [table, toybox]',
34 | )
35 |
36 | robot_xacro_path = PathJoinSubstitution(
37 | [
38 | FindPackageShare(LaunchConfiguration('description_package')),
39 | 'description',
40 | 'ur',
41 | 'ur.urdf.xacro',
42 | ]
43 | )
44 |
45 | ros2_controllers_path = PathJoinSubstitution(
46 | [
47 | FindPackageShare(LaunchConfiguration('description_package')),
48 | 'config',
49 | 'ur',
50 | 'ros2_controllers.yaml',
51 | ]
52 | )
53 |
54 | rviz_config_file = PathJoinSubstitution(
55 | [
56 | FindPackageShare(LaunchConfiguration('description_package')),
57 | 'config',
58 | 'rviz2',
59 | 'rviz2_config.rviz',
60 | ]
61 | )
62 |
63 | robot_description_content = Command(
64 | [
65 | PathJoinSubstitution([FindExecutable(name='xacro')]),
66 | ' ',
67 | robot_xacro_path,
68 | ' ',
69 | 'ur_type:=',
70 | 'ur5e',
71 | ' ',
72 | 'use_robotiq_gripper:=',
73 | 'true',
74 | ' ',
75 | 'ros2_control_hardware_type:=',
76 | LaunchConfiguration('hardware'),
77 | ' ',
78 | ]
79 | )
80 |
81 | # Publish Robot State
82 | robot_state_publisher = Node(
83 | package='robot_state_publisher',
84 | executable='robot_state_publisher',
85 | name='robot_state_publisher',
86 | output='both',
87 | parameters=[{'robot_description': robot_description_content}],
88 | )
89 |
90 | ros2_control_node = Node(
91 | package='controller_manager',
92 | executable='ros2_control_node',
93 | parameters=[ros2_controllers_path],
94 | remappings=[
95 | ('/controller_manager/robot_description', '/robot_description'),
96 | ],
97 | output='screen',
98 | )
99 |
100 | joint_state_broadcaster_spawner = Node(
101 | package='controller_manager',
102 | executable='spawner',
103 | arguments=[
104 | 'joint_state_broadcaster',
105 | '--controller-manager',
106 | '/controller_manager',
107 | ],
108 | )
109 |
110 | ur5e_arm_controller_spawner = Node(
111 | package='controller_manager',
112 | executable='spawner',
113 | arguments=['ur_arm_controller', '-c', '/controller_manager'],
114 | )
115 |
116 | robotiq_gripper_controller_spawner = Node(
117 | package='controller_manager',
118 | executable='spawner',
119 | arguments=['robotiq_gripper_controller', '-c', '/controller_manager'],
120 | )
121 |
122 | rviz_node = Node(
123 | package='rviz2',
124 | executable='rviz2',
125 | name='rviz2',
126 | output='log',
127 | arguments=['-d', rviz_config_file],
128 | )
129 |
130 | world2robot_tf_toybox = Node(
131 | package='tf2_ros',
132 | executable='static_transform_publisher',
133 | name='static_transform_publisher',
134 | output='log',
135 | arguments=[
136 | '0.0', # translation
137 | '0.25',
138 | '0.0',
139 | '0.0', # rotation (RPY)
140 | '0.0',
141 | '0.0',
142 | 'world',
143 | 'base_link',
144 | ],
145 | condition=IfCondition(
146 | EqualsSubstitution(LaunchConfiguration('world'), 'toybox')
147 | ),
148 | )
149 |
150 | world2robot_tf_table = Node(
151 | package='tf2_ros',
152 | executable='static_transform_publisher',
153 | name='static_transform_publisher',
154 | output='log',
155 | arguments=[
156 | '0.0', # translation
157 | '-0.640',
158 | '0.0',
159 | '0.0', # rotation (RPY)
160 | '0.0',
161 | '0.0',
162 | 'world',
163 | 'base_link',
164 | ],
165 | condition=IfCondition(
166 | EqualsSubstitution(LaunchConfiguration('world'), 'table')
167 | ),
168 | )
169 |
170 | return LaunchDescription(
171 | [
172 | sim_world_declare,
173 | description_package,
174 | ros2_control_hardware_type,
175 | rviz_node,
176 | robot_state_publisher,
177 | ros2_control_node,
178 | joint_state_broadcaster_spawner,
179 | ur5e_arm_controller_spawner,
180 | robotiq_gripper_controller_spawner,
181 | world2robot_tf_toybox,
182 | world2robot_tf_table,
183 | ]
184 | )
185 |
--------------------------------------------------------------------------------
/grab2_controller/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | grab2_controller
5 | 0.0.0
6 | TODO: Package description
7 | sayed
8 | TODO: License declaration
9 |
10 | ament_cmake
11 |
12 | xacro
13 | rviz2
14 | tf2_ros
15 | tf_transformations
16 | ros2_control
17 | ros2_controllers
18 | hardware_interface
19 | controller_manager
20 | position_controllers
21 | joint_trajectory_controller
22 | robot_state_publisher
23 | joint_state_broadcaster
24 | topic_based_ros2_control
25 | ur_description
26 | moveit_resources_panda_description
27 |
28 | ament_lint_auto
29 | ament_lint_common
30 |
31 |
32 | ament_cmake
33 |
34 |
35 |
--------------------------------------------------------------------------------
/grab2_curobo_planner/config/table.yaml:
--------------------------------------------------------------------------------
1 | cuboid:
2 | table:
3 | dims: [5.0, 5.0, 0.2] # x, y, z
4 | pose: [0.0, 0.0, -0.1, 1, 0, 0, 0.0] # x, y, z, qx, qy, qz, qw
5 |
--------------------------------------------------------------------------------
/grab2_curobo_planner/config/toybox.yaml:
--------------------------------------------------------------------------------
1 | cuboid:
2 | floor:
3 | dims: [2.5, 2.5, 0.2] # x, y, z
4 | pose: [0.0, 0.0, -0.1, 0, 0, 0, 1.0] # x, y, z, qx, qy, qz, qw
5 | color: [0.6, 0.6, 0.8, 1.0]
6 | toy_box_w1: # Front: Height = 0.2 + 0.3
7 | dims: [0.82, 0.1, 0.6] # x, y, z
8 | pose: [0.54, -0.16, 0.2, 0.99938, 0.0, 0.0, 0.03533] # x, y, z, qw, qx, qy, qz
9 | color: [1.0, 0.0, 0.0, 1.0]
10 | toy_box_w2: # Back: Height = 0.25 + 0.325
11 | dims: [1.0, 0.1, 0.65] # x, y, z
12 | pose: [0.54, -0.6, 0.25, 0.99938, 0.0, 0.0, 0.03533] # x, y, z, qw, qx, qy, qz
13 | color: [0.0, 0.0, 1.0, 1.0]
14 | toy_box_w3: # Side: Height = 0.2 + 0.3
15 | dims: [1.0, 0.1, 0.6] # x, y, z
16 | pose: [0.11, -0.68, 0.2, 0.70711, 0.0, 0.0, 0.70711] # x, y, z, qw, qx, qy, qz
17 | color: [0.0, 0.0, 1.0, 1.0]
18 |
--------------------------------------------------------------------------------
/grab2_curobo_planner/grab2_curobo_planner/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/grab2_curobo_planner/grab2_curobo_planner/__init__.py
--------------------------------------------------------------------------------
/grab2_curobo_planner/grab2_curobo_planner/curobo/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/grab2_curobo_planner/grab2_curobo_planner/curobo/__init__.py
--------------------------------------------------------------------------------
/grab2_curobo_planner/grab2_curobo_planner/curobo/motion_generation.py:
--------------------------------------------------------------------------------
1 | from functools import wraps
2 | from typing import Dict, List
3 |
4 | from curobo.geom.sdf.world import CollisionCheckerType
5 | from curobo.geom.types import Cuboid
6 | from curobo.types.base import TensorDeviceType
7 | from curobo.types.math import Pose
8 | from curobo.types.robot import JointState
9 | from curobo.wrap.reacher.motion_gen import MotionGen, MotionGenConfig
10 | import numpy as np
11 | import torch
12 |
13 |
14 | def requires_warmup(func):
15 | @wraps(func)
16 | def wrapper(self, *args, **kwargs):
17 | if not getattr(self, '_warmed_up', False):
18 | raise RuntimeError(
19 | f"Cannot call '{func.__name__}' before calling warmup()."
20 | )
21 | return func(self, *args, **kwargs)
22 |
23 | return wrapper
24 |
25 |
26 | class CuRoboMotionGen:
27 | _warmed_up = False
28 |
29 | def __init__(
30 | self,
31 | robot_config='franka.yml',
32 | world_config='collision_table.yml',
33 | interpolation_dt=0.02,
34 | collision_activation_distance=0.02,
35 | ):
36 | self.tensor_args = TensorDeviceType(device=torch.device('cuda:0'))
37 | motion_gen_cfg = MotionGenConfig.load_from_robot_config(
38 | robot_config,
39 | world_config,
40 | self.tensor_args,
41 | trajopt_tsteps=34,
42 | interpolation_steps=5000,
43 | num_ik_seeds=50,
44 | num_trajopt_seeds=6,
45 | grad_trajopt_iters=500,
46 | trajopt_dt=0.5,
47 | interpolation_dt=interpolation_dt,
48 | evaluate_interpolated_trajectory=True,
49 | js_trajopt_dt=0.5,
50 | js_trajopt_tsteps=34,
51 | collision_checker_type=CollisionCheckerType.VOXEL,
52 | collision_activation_distance=collision_activation_distance,
53 | )
54 |
55 | self.motion_gen = MotionGen(motion_gen_cfg)
56 | self.world_collision = self.motion_gen.world_coll_checker
57 |
58 | def warmup(self):
59 | self.motion_gen.warmup()
60 | self._warmed_up = True
61 |
62 | @property
63 | def joint_names(self) -> List[str]:
64 | """Get the joint names of the robot."""
65 | return self.motion_gen.joint_names
66 |
67 | @property
68 | def base_link(self) -> List[str]:
69 | """Get the base link name of the robot."""
70 | return self.motion_gen.kinematics.base_link
71 |
72 | def get_world_collision_voxels(self):
73 | voxels = self.world_collision.get_occupancy_in_bounding_box(
74 | Cuboid(
75 | name='test',
76 | pose=[0.0, 0.0, 0.0, 1, 0, 0, 0], # x, y, z, qw, qx, qy, qz
77 | dims=[2.0, 2.0, 2.0],
78 | ),
79 | voxel_size=0.05,
80 | )
81 | xyzr_tensor = voxels.xyzr_tensor.clone()
82 | xyzr_tensor[..., 3] = voxels.feature_tensor
83 |
84 | return xyzr_tensor
85 |
86 | @requires_warmup
87 | def check_goal_feasible(self, target_pose: List) -> bool:
88 | """
89 | Check if a target pose is feasible (i.e., within the robot's reachable workspace).
90 |
91 | Args:
92 | target_pose (List): A list containing the target position [x, y, z]
93 | followed by orientation as quaternion [qw, qx, qy, qz].
94 |
95 | Returns
96 | -------
97 | bool: True if the target pose is reachable (IK solution exists), False otherwise.
98 |
99 | """
100 | t_position = target_pose[:3]
101 | t_orientation = target_pose[3:]
102 |
103 | goal = Pose(
104 | position=self.tensor_args.to_device([t_position]),
105 | quaternion=self.tensor_args.to_device([t_orientation]),
106 | )
107 |
108 | ik_sol = self.motion_gen.ik_solver.solve_single(goal)
109 | return ik_sol.success.item()
110 |
111 | @requires_warmup
112 | def compute_kinematics(self, joint_state: List[float]) -> Dict[str, np.ndarray]:
113 | """
114 | Compute the forward kinematics for a given joint state.
115 |
116 | Args:
117 | joint_state (List[float]): Joint positions for the robot.
118 |
119 | Returns
120 | -------
121 | Dict[str, np.ndarray]: A dictionary mapping the link name
122 | to its [x, y, z, qw, qx, qy, qz] pose.
123 |
124 | """
125 | state = JointState.from_position(
126 | self.tensor_args.to_device([joint_state]),
127 | joint_names=self.motion_gen.joint_names,
128 | )
129 |
130 | k = self.motion_gen.compute_kinematics(state)
131 |
132 | pose = k.ee_pos_seq.cpu().numpy() # shape: (1, 3)
133 | quat = k.ee_quat_seq.cpu().numpy() # shape: (1, 4)
134 |
135 | pose_quat = np.concatenate([pose, quat], axis=1).flatten() # shape: (7,)
136 |
137 | return {k.link_names[0]: pose_quat}
138 |
139 | @requires_warmup
140 | def plan(self, initial_state: List, target_pose: List) -> np.ndarray:
141 | """
142 | Generate a trajectory from initial_state (jointState) to a Cartesian target_pose.
143 |
144 | Args:
145 | initial_state (List): Initial joint positions, length (num_joints)
146 | target_pose (List): A list containing the target position [x, y, z]
147 | followed by orientation as quaternion [qw, qx, qy, qz].
148 |
149 | Returns
150 | -------
151 | np.ndarray: Planned trajectory with shape (N, num_joints)
152 | where N is the number of steps(trajectory_points).
153 |
154 | """
155 | # zero rows, num_joints cols
156 | plan = np.empty((0, len(self.motion_gen.joint_names)))
157 | q_start = JointState.from_position(
158 | self.tensor_args.to_device([initial_state]),
159 | joint_names=self.motion_gen.joint_names,
160 | )
161 |
162 | t_position = target_pose[:3]
163 | t_orientation = target_pose[3:] # w, x, y, z
164 |
165 | q_goal = Pose(
166 | position=self.tensor_args.to_device([t_position]),
167 | quaternion=self.tensor_args.to_device([t_orientation]),
168 | )
169 |
170 | result = self.motion_gen.plan_single(q_start, q_goal)
171 |
172 | if result.success.item():
173 | plan = result.get_interpolated_plan().position.cpu().numpy()
174 |
175 | return plan # Shape (N, num_joints)
176 |
--------------------------------------------------------------------------------
/grab2_curobo_planner/launch/planning_server.launch.py:
--------------------------------------------------------------------------------
1 | from launch import LaunchDescription
2 | from launch.actions import DeclareLaunchArgument, OpaqueFunction
3 | from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
4 | from launch_ros.actions import Node
5 | from launch_ros.substitutions import FindPackageShare
6 |
7 |
8 | def get_launch_nodes(context, *args, **kwargs):
9 | robot = LaunchConfiguration('robot').perform(context)
10 |
11 | world_config_file = PathJoinSubstitution(
12 | [
13 | FindPackageShare('grab2_curobo_planner'),
14 | 'config',
15 | [LaunchConfiguration('world'), '.yaml'],
16 | ]
17 | )
18 |
19 | # Prepare configs
20 | # Franka equipped with finger gripper
21 | if robot == 'franka':
22 | state_topic_name = '/panda_arm_controller/controller_state'
23 |
24 | # For UR5e with 2-finger gripper or/ UR10e with suction gripper
25 | else:
26 | state_topic_name = '/ur_arm_controller/controller_state'
27 |
28 | planning_server_node = Node(
29 | package='grab2_curobo_planner',
30 | executable='planning_server',
31 | name='planning_server',
32 | output='both',
33 | parameters=[
34 | {
35 | 'robot_config': [LaunchConfiguration('robot'), '.yml'],
36 | 'world_config': world_config_file,
37 | 'state_topic': state_topic_name,
38 | }
39 | ],
40 | )
41 | return [planning_server_node]
42 |
43 |
44 | def generate_launch_description():
45 | # Command-line arguments
46 | robot_model_declaration = DeclareLaunchArgument(
47 | 'robot',
48 | default_value='franka',
49 | description=(
50 | 'Robot model used with cuRobo -- ' 'Supported robots: [franka, ur10e]'
51 | ),
52 | )
53 |
54 | world_model_declaration = DeclareLaunchArgument(
55 | 'world',
56 | default_value='table',
57 | description=(
58 | 'Static World Collision model for cuRobo -- '
59 | 'Available configs: [table, toybox]'
60 | ),
61 | )
62 |
63 | return LaunchDescription(
64 | [
65 | robot_model_declaration,
66 | world_model_declaration,
67 | OpaqueFunction(function=get_launch_nodes),
68 | ]
69 | )
70 |
--------------------------------------------------------------------------------
/grab2_curobo_planner/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | grab2_curobo_planner
5 | 0.0.0
6 | TODO: Package description
7 | sayed
8 | TODO: License declaration
9 |
10 | rclpy
11 | tf2_ros
12 | std_msgs
13 | control_msgs
14 | trajectory_msgs
15 | grab2_msgs
16 | tf2_geometry_msgs
17 | visualization_msgs
18 |
19 | ament_copyright
20 | ament_flake8
21 | ament_pep257
22 | python3-pytest
23 |
24 |
25 | ament_python
26 |
27 |
28 |
--------------------------------------------------------------------------------
/grab2_curobo_planner/resource/grab2_curobo_planner:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/grab2_curobo_planner/resource/grab2_curobo_planner
--------------------------------------------------------------------------------
/grab2_curobo_planner/setup.cfg:
--------------------------------------------------------------------------------
1 | [develop]
2 | script_dir=$base/lib/grab2_curobo_planner
3 | [install]
4 | install_scripts=$base/lib/grab2_curobo_planner
5 |
--------------------------------------------------------------------------------
/grab2_curobo_planner/setup.py:
--------------------------------------------------------------------------------
1 | from glob import glob
2 | import os
3 |
4 | from setuptools import find_packages, setup
5 |
6 | package_name = 'grab2_curobo_planner'
7 |
8 | setup(
9 | name=package_name,
10 | version='0.0.0',
11 | packages=find_packages(exclude=['test']),
12 | data_files=[
13 | ('share/ament_index/resource_index/packages', ['resource/' + package_name]),
14 | ('share/' + package_name, ['package.xml']),
15 | (os.path.join('share', package_name, 'launch'), glob('launch/*.launch.py')),
16 | (os.path.join('share', package_name, 'config'), glob('config/*.yaml')),
17 | ],
18 | install_requires=['setuptools'],
19 | zip_safe=True,
20 | maintainer='sayed',
21 | maintainer_email='elsayed.elsheikh97@gmail.com',
22 | description='TODO: Package description',
23 | license='TODO: License declaration',
24 | tests_require=['pytest'],
25 | entry_points={
26 | 'console_scripts': [
27 | 'planning_server = grab2_curobo_planner.planning_server:main'
28 | ],
29 | },
30 | )
31 |
--------------------------------------------------------------------------------
/grab2_curobo_planner/test/test_copyright.py:
--------------------------------------------------------------------------------
1 | # Copyright 2015 Open Source Robotics Foundation, Inc.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | from ament_copyright.main import main
16 | import pytest
17 |
18 |
19 | # Remove the `skip` decorator once the source file(s) have a copyright header
20 | @pytest.mark.skip(
21 | reason='No copyright header has been placed in the generated source file.'
22 | )
23 | @pytest.mark.copyright
24 | @pytest.mark.linter
25 | def test_copyright():
26 | rc = main(argv=['.', 'test'])
27 | assert rc == 0, 'Found errors'
28 |
--------------------------------------------------------------------------------
/grab2_curobo_planner/test/test_flake8.py:
--------------------------------------------------------------------------------
1 | # Copyright 2017 Open Source Robotics Foundation, Inc.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | from ament_flake8.main import main_with_errors
16 | import pytest
17 |
18 |
19 | @pytest.mark.flake8
20 | @pytest.mark.linter
21 | def test_flake8():
22 | rc, errors = main_with_errors(argv=[])
23 | assert rc == 0, 'Found %d code style errors / warnings:\n' % len(
24 | errors
25 | ) + '\n'.join(errors)
26 |
--------------------------------------------------------------------------------
/grab2_curobo_planner/test/test_pep257.py:
--------------------------------------------------------------------------------
1 | # Copyright 2015 Open Source Robotics Foundation, Inc.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | from ament_pep257.main import main
16 | import pytest
17 |
18 |
19 | @pytest.mark.linter
20 | @pytest.mark.pep257
21 | def test_pep257():
22 | rc = main(argv=['.', 'test'])
23 | assert rc == 0, 'Found code style errors / warnings'
24 |
--------------------------------------------------------------------------------
/grab2_grasp_generator/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.8)
2 | project(grab2_grasp_generator)
3 |
4 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
5 | add_compile_options(-Wall -Wextra -Wpedantic)
6 | endif()
7 |
8 | # find dependencies
9 | find_package(ament_cmake REQUIRED)
10 | find_package(ament_cmake_ros REQUIRED)
11 | find_package(rclcpp REQUIRED)
12 | find_package(rclcpp_action REQUIRED)
13 | find_package(rclcpp_components REQUIRED)
14 | find_package(geometry_msgs REQUIRED)
15 | find_package(moveit_msgs REQUIRED)
16 | find_package(grab2_ros_common REQUIRED)
17 | find_package(grab2_msgs REQUIRED)
18 | find_package(tf2 REQUIRED)
19 | find_package(tf2_ros REQUIRED)
20 | find_package(tf2_geometry_msgs REQUIRED)
21 | find_package(yaml-cpp REQUIRED)
22 | find_package(ament_index_cpp REQUIRED)
23 |
24 | set(executable_name grasp_generator)
25 | set(library_name ${executable_name}_core)
26 |
27 | add_library(${library_name} src/grasp_generator.cpp)
28 | add_library(grab2_grasp_generator::grasp_generator ALIAS ${library_name})
29 | target_compile_features(${library_name} PUBLIC c_std_99 cxx_std_17) # Require C99 and C++17
30 |
31 | target_include_directories(${library_name} PUBLIC
32 | $
33 | $)
34 |
35 | target_link_libraries(${library_name} PUBLIC
36 | yaml-cpp
37 | rclcpp::rclcpp
38 | rclcpp_action::rclcpp_action
39 | grab2_ros_common::grab2_ros_common
40 | tf2::tf2
41 | tf2_ros::tf2_ros
42 | ${tf2_geometry_msgs_TARGETS}
43 | ament_index_cpp::ament_index_cpp
44 | ${std_msgs_TARGETS}
45 | ${control_msgs_TARGETS}
46 | ${geometry_msgs_TARGETS}
47 | ${trajectory_msgs_TARGETS}
48 | ${grab2_msgs_TARGETS}
49 | ${visualization_msgs_TARGETS}
50 | ${moveit_msgs_TARGETS}
51 | )
52 |
53 | # Causes the visibility macros to use dllexport rather than dllimport,
54 | # which is appropriate when building the dll but not consuming it.
55 | target_compile_definitions(${library_name} PRIVATE "GRAB2_GRASP_GENERATOR_BUILDING_LIBRARY")
56 |
57 | add_executable(${executable_name}
58 | src/main.cpp
59 | )
60 |
61 | target_link_libraries(${executable_name} PRIVATE ${library_name} rclcpp::rclcpp)
62 |
63 | install(
64 | DIRECTORY include/
65 | DESTINATION include/${PROJECT_NAME}
66 | )
67 |
68 | install(
69 | TARGETS ${library_name}
70 | EXPORT export_${PROJECT_NAME}
71 | ARCHIVE DESTINATION lib
72 | LIBRARY DESTINATION lib
73 | RUNTIME DESTINATION bin
74 | )
75 |
76 | install(TARGETS ${executable_name}
77 | RUNTIME DESTINATION lib/${PROJECT_NAME}
78 | )
79 |
80 | install(
81 | DIRECTORY config
82 | DESTINATION share/${PROJECT_NAME}
83 | )
84 |
85 | if(BUILD_TESTING)
86 | find_package(ament_lint_auto REQUIRED)
87 | find_package(ament_cmake_gtest REQUIRED)
88 |
89 | # the following line skips the linter which checks for copyrights
90 | set(ament_cmake_copyright_FOUND TRUE)
91 | # the following line skips linters
92 | # set(ament_cmake_cpplint_FOUND TRUE)
93 | # set(ament_cmake_uncrustify_FOUND TRUE)
94 | ament_lint_auto_find_test_dependencies()
95 |
96 | ament_find_gtest()
97 | add_subdirectory(test)
98 | endif()
99 |
100 | ament_export_include_directories(
101 | "include/${PROJECT_NAME}"
102 | )
103 | ament_export_libraries(
104 | ${library_name}
105 | )
106 | ament_export_targets(
107 | export_${PROJECT_NAME}
108 | )
109 |
110 | ament_package()
111 |
--------------------------------------------------------------------------------
/grab2_grasp_generator/config/isaac_grasp.yaml:
--------------------------------------------------------------------------------
1 | format: isaac_grasp
2 | format_version: 1.0
3 |
4 | object_frame_link: /World/mug
5 | gripper_frame_link: /World/panda_hand
6 |
7 | grasps:
8 | grasp_0:
9 | confidence: 1.0
10 | position: [-0.04346, 0.06759, 0.19895]
11 | orientation: {w: 0.00332, xyz: [0.98453, 0.16837, 0.04837]}
12 | cspace_position:
13 | panda_finger_joint1: 0.00943 # gripper closed
14 | pregrasp_cspace_position:
15 | panda_finger_joint1: 0.04 # gripper open
16 | grasp_1:
17 | confidence: 0.74
18 | position: [-0.04346, 0.06759, 0.19895]
19 | orientation: {w: 0.00332, xyz: [0.98453, 0.16837, 0.04837]}
20 | cspace_position:
21 | panda_finger_joint1: 0.00943
22 | pregrasp_cspace_position:
23 | panda_finger_joint1: 0.04
24 |
--------------------------------------------------------------------------------
/grab2_grasp_generator/include/grab2_grasp_generator/grasp_generator.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #ifndef GRAB2_GRASP_GENERATOR__GRASP_GENERATOR_HPP_
4 | #define GRAB2_GRASP_GENERATOR__GRASP_GENERATOR_HPP_
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #include "rclcpp/rclcpp.hpp"
13 | #include "rclcpp_action/rclcpp_action.hpp"
14 |
15 | #include "grab2_ros_common/node.hpp"
16 | #include "grab2_msgs/action/get_isaac_grasp.hpp"
17 | #include "grab2_grasp_generator/visibility_control.h"
18 |
19 | #include "moveit_msgs/msg/grasp.hpp"
20 | #include "geometry_msgs/msg/pose.hpp"
21 | #include "geometry_msgs/msg/pose_stamped.hpp"
22 |
23 | namespace grab2_grasp_generator
24 | {
25 |
26 | class GraspGenerator : public grab2::Node
27 | {
28 | public:
29 | using GraspVector = std::vector;
30 | using ActionGetGrasp = grab2_msgs::action::GetIsaacGrasp;
31 |
32 | /**
33 | * @brief A constructor of grab2_grasp_generator::GraspGenerator
34 | * @param options Additional options for node creation
35 | */
36 | explicit GraspGenerator(const rclcpp::NodeOptions & options = rclcpp::NodeOptions());
37 |
38 | /**
39 | * @brief Handles the getGraspFromYAML action request.
40 | *
41 | * This method reads grasp configurations from a specified YAML file,
42 | * transforms each grasp pose from the object's local frame to the reference
43 | * (world) frame using the provided object pose, and returns the transformed
44 | * grasps in the action response.
45 | *
46 | * @param goal_handle The goal handle for the action request.
47 | */
48 | void getGraspFromYAML(const std::shared_ptr> goal_handle);
49 |
50 | /**
51 | * @brief Method to parse the yaml file containing the grasps
52 | *
53 | * @param file_path The full path to the isaac_grasp YAML file
54 | * @return std::optional The parsed grasps or std::nullopt on failure
55 | */
56 | std::optional parseYAML(const std::string & file_path);
57 |
58 | /**
59 | * @brief Method to transform the grasp pose from object frame
60 | * to planning or/ reference frame such as world frame
61 | *
62 | * @param grasp The grasp message to be transformed
63 | * @param world_to_object The pose of the object in the world frame
64 | */
65 | void transfromGraspFrame(
66 | moveit_msgs::msg::Grasp & grasp,
67 | const geometry_msgs::msg::PoseStamped & world_to_object) const;
68 |
69 | private:
70 | // Action server that provides the grasps for a given object
71 | rclcpp_action::Server::SharedPtr action_server_get_grasp_;
72 | };
73 |
74 | } // namespace grab2_grasp_generator
75 |
76 | #endif // GRAB2_GRASP_GENERATOR__GRASP_GENERATOR_HPP_
77 |
--------------------------------------------------------------------------------
/grab2_grasp_generator/include/grab2_grasp_generator/visibility_control.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #ifndef GRAB2_GRASP_GENERATOR__VISIBILITY_CONTROL_H_
4 | #define GRAB2_GRASP_GENERATOR__VISIBILITY_CONTROL_H_
5 |
6 | // This logic was borrowed (then namespaced) from the examples on the gcc wiki:
7 | // https://gcc.gnu.org/wiki/Visibility
8 |
9 | #if defined _WIN32 || defined __CYGWIN__
10 | #ifdef __GNUC__
11 | #define GRAB2_GRASP_GENERATOR_EXPORT __attribute__ ((dllexport))
12 | #define GRAB2_GRASP_GENERATOR_IMPORT __attribute__ ((dllimport))
13 | #else
14 | #define GRAB2_GRASP_GENERATOR_EXPORT __declspec(dllexport)
15 | #define GRAB2_GRASP_GENERATOR_IMPORT __declspec(dllimport)
16 | #endif
17 | #ifdef GRAB2_GRASP_GENERATOR_BUILDING_LIBRARY
18 | #define GRAB2_GRASP_GENERATOR_PUBLIC GRAB2_GRASP_GENERATOR_EXPORT
19 | #else
20 | #define GRAB2_GRASP_GENERATOR_PUBLIC GRAB2_GRASP_GENERATOR_IMPORT
21 | #endif
22 | #define GRAB2_GRASP_GENERATOR_PUBLIC_TYPE GRAB2_GRASP_GENERATOR_PUBLIC
23 | #define GRAB2_GRASP_GENERATOR_LOCAL
24 | #else
25 | #define GRAB2_GRASP_GENERATOR_EXPORT __attribute__ ((visibility("default")))
26 | #define GRAB2_GRASP_GENERATOR_IMPORT
27 | #if __GNUC__ >= 4
28 | #define GRAB2_GRASP_GENERATOR_PUBLIC __attribute__ ((visibility("default")))
29 | #define GRAB2_GRASP_GENERATOR_LOCAL __attribute__ ((visibility("hidden")))
30 | #else
31 | #define GRAB2_GRASP_GENERATOR_PUBLIC
32 | #define GRAB2_GRASP_GENERATOR_LOCAL
33 | #endif
34 | #define GRAB2_GRASP_GENERATOR_PUBLIC_TYPE
35 | #endif
36 |
37 | #endif // GRAB2_GRASP_GENERATOR__VISIBILITY_CONTROL_H_
38 |
--------------------------------------------------------------------------------
/grab2_grasp_generator/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | grab2_grasp_generator
5 | 0.0.1
6 | Grab2 grasp generator package
7 | ElSayed ElSheikh
8 | Apache-2.0
9 |
10 | ament_cmake_ros
11 |
12 | rclcpp
13 | rclcpp_action
14 | rclcpp_components
15 | geometry_msgs
16 | moveit_msgs
17 | grab2_msgs
18 | grab2_ros_common
19 | tf2_ros
20 | yaml-cpp
21 |
22 | ament_lint_auto
23 | ament_lint_common
24 | ament_lint_gtest
25 |
26 |
27 | ament_cmake
28 |
29 |
30 |
--------------------------------------------------------------------------------
/grab2_grasp_generator/src/grasp_generator.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #include "grab2_grasp_generator/grasp_generator.hpp"
4 |
5 | #include "tf2/LinearMath/Transform.hpp"
6 | #include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
7 | #include "trajectory_msgs/msg/joint_trajectory.hpp"
8 |
9 | #include "yaml-cpp/yaml.h"
10 | #include "ament_index_cpp/get_package_share_directory.hpp"
11 |
12 | namespace grab2_grasp_generator
13 | {
14 |
15 | GraspGenerator::GraspGenerator(const rclcpp::NodeOptions & options)
16 | : Node("grasp_generator", options)
17 | {
18 | this->action_server_get_grasp_ = create_action_server(
19 | "get_isaac_grasp",
20 | std::bind(&GraspGenerator::getGraspFromYAML, this, std::placeholders::_1)
21 | );
22 |
23 | RCLCPP_INFO(this->get_logger(), "GraspGenerator initialized");
24 | }
25 |
26 | void
27 | GraspGenerator::getGraspFromYAML(const std::shared_ptr> goal_handle)
28 | {
29 | RCLCPP_INFO(this->get_logger(), "Executing getGraspFromYAML action");
30 |
31 | const auto goal = goal_handle->get_goal();
32 | auto result = std::make_shared();
33 |
34 | // Validate input
35 | const std::string & filename = goal->isaac_grasp;
36 | if (filename.empty()) {
37 | RCLCPP_ERROR(this->get_logger(), "Empty grasp file name provided");
38 | goal_handle->abort(result);
39 | return;
40 | }
41 |
42 | // Construct file path
43 | std::string pkg_dir =
44 | ament_index_cpp::get_package_share_directory("grab2_grasp_generator");
45 | std::string file_path =
46 | pkg_dir + "/config/" + filename + ".yaml";
47 |
48 | // Check if file exists to parse it
49 | if (!std::filesystem::exists(file_path)) {
50 | RCLCPP_ERROR(this->get_logger(), "YAML file doesn't exist: %s", file_path.c_str());
51 | goal_handle->abort(result);
52 | return;
53 | }
54 |
55 | // Parse YAML file
56 | RCLCPP_INFO(
57 | this->get_logger(), "Loading grasps from: %s",
58 | (filename + ".yaml").c_str());
59 |
60 | auto grasps_opt = parseYAML(file_path);
61 |
62 | if (!grasps_opt) {
63 | RCLCPP_ERROR(this->get_logger(), "Failed to parse YAML file");
64 | goal_handle->abort(result);
65 | return;
66 | }
67 |
68 | auto grasps = std::move(*grasps_opt); // Target Gripper pose in Object frame
69 |
70 | // Transform all grasps to world frame (detected object frame)
71 | const auto & world_to_object = goal->object_pose; // Object pose in World frame
72 | for (auto & grasp : grasps) {
73 | transfromGraspFrame(grasp, world_to_object);
74 | }
75 |
76 | // Return result
77 | result->grasps = std::move(grasps);
78 | RCLCPP_INFO(this->get_logger(), "Returning %zu grasps", result->grasps.size());
79 |
80 | for (const auto & grasp : result->grasps) {
81 | RCLCPP_DEBUG(
82 | this->get_logger(),
83 | " Grasp '%s': [%.3f, %.3f, %.3f] quality=%.2f",
84 | grasp.id.c_str(),
85 | grasp.grasp_pose.pose.position.x,
86 | grasp.grasp_pose.pose.position.y,
87 | grasp.grasp_pose.pose.position.z,
88 | grasp.grasp_quality);
89 | }
90 |
91 | goal_handle->succeed(result);
92 | }
93 |
94 | void
95 | GraspGenerator::transfromGraspFrame(
96 | moveit_msgs::msg::Grasp & grasp,
97 | const geometry_msgs::msg::PoseStamped & world_to_object) const
98 | {
99 | // Store original pose for debug logging
100 | const auto object_to_gripper = grasp.grasp_pose.pose;
101 |
102 | // Get Target Gripper pose in World frame
103 | tf2::Transform tf_world_to_object, tf_object_to_gripper;
104 | tf2::fromMsg(world_to_object.pose, tf_world_to_object);
105 | tf2::fromMsg(grasp.grasp_pose.pose, tf_object_to_gripper);
106 |
107 | // Target Gripper pose
108 | tf2::Transform tf_world_to_gripper = tf_world_to_object * tf_object_to_gripper;
109 |
110 | // Update grasp pose
111 | grasp.grasp_pose.header.frame_id = world_to_object.header.frame_id;
112 | tf2::toMsg(tf_world_to_gripper, grasp.grasp_pose.pose);
113 |
114 | RCLCPP_DEBUG(this->get_logger(), "Grasp '%s' transformed:", grasp.id.c_str());
115 | RCLCPP_DEBUG(
116 | this->get_logger(),
117 | " Object frame Pos: [%.3f, %.3f, %.3f]",
118 | object_to_gripper.position.x,
119 | object_to_gripper.position.y,
120 | object_to_gripper.position.z);
121 | RCLCPP_DEBUG(
122 | this->get_logger(),
123 | " Reference/World frame Pos: [%.3f, %.3f, %.3f]",
124 | grasp.grasp_pose.pose.position.x,
125 | grasp.grasp_pose.pose.position.y,
126 | grasp.grasp_pose.pose.position.z);
127 | }
128 |
129 | std::optional
130 | GraspGenerator::parseYAML(const std::string & file_path)
131 | {
132 | GraspVector grasps;
133 |
134 | try {
135 | YAML::Node config = YAML::LoadFile(file_path);
136 | RCLCPP_DEBUG(this->get_logger(), "Parsing YAML: %s", file_path.c_str());
137 |
138 | // TODO(sayed): Gripper frame - This should be used to check if this is the right gripper
139 | // auto isaac_grip_frame = config["gripper_frame_link"].as();
140 | // size_t pos = isaac_grip_frame.find_last_of('/');
141 | // std::string grip_frame;
142 | // if (pos != std::string::npos) {
143 | // grip_frame = isaac_grip_frame.substr(pos + 1);
144 | // } else {
145 | // grip_frame = isaac_grip_frame;
146 | // }
147 |
148 | if (!config["grasps"]) {
149 | RCLCPP_ERROR(this->get_logger(), "YAML file doesn't contain 'grasps' field");
150 | return std::nullopt;
151 | }
152 |
153 | const YAML::Node & yaml_grasps = config["grasps"];
154 |
155 | for (auto grasp_it = yaml_grasps.begin(); grasp_it != yaml_grasps.end(); ++grasp_it) {
156 | // Construct Grasp Message
157 | moveit_msgs::msg::Grasp grasp_msg;
158 |
159 | // Grasp id
160 | grasp_msg.id = grasp_it->first.as();
161 | const YAML::Node & grasp = grasp_it->second;
162 |
163 | // Grasp quality or/ confidence
164 | grasp_msg.grasp_quality = grasp["confidence"].as();
165 |
166 | // Grasp grasp_pose in Object frame
167 | // grasp_msg.grasp_pose.header.frame_id = ; // This should be Object frame
168 | std::vector pos = grasp["position"].as>();
169 | grasp_msg.grasp_pose.pose.position.x = pos[0];
170 | grasp_msg.grasp_pose.pose.position.y = pos[1];
171 | grasp_msg.grasp_pose.pose.position.z = pos[2];
172 |
173 | double w = grasp["orientation"]["w"].as();
174 | std::vector xyz = grasp["orientation"]["xyz"].as>();
175 | grasp_msg.grasp_pose.pose.orientation.x = xyz[0];
176 | grasp_msg.grasp_pose.pose.orientation.y = xyz[1];
177 | grasp_msg.grasp_pose.pose.orientation.z = xyz[2];
178 | grasp_msg.grasp_pose.pose.orientation.w = w;
179 |
180 | // Add the grasp to the vector
181 | grasps.push_back(grasp_msg);
182 | }
183 | } catch (const YAML::Exception & e) {
184 | RCLCPP_ERROR(this->get_logger(), "YAML error: %s", e.what());
185 | return std::nullopt;
186 | } catch (const std::exception & e) {
187 | RCLCPP_ERROR(this->get_logger(), "Exception: %s", e.what());
188 | return std::nullopt;
189 | }
190 |
191 | if (grasps.empty()) {
192 | RCLCPP_ERROR(this->get_logger(), "No grasps found in the YAML file");
193 | return std::nullopt;
194 | }
195 |
196 | return grasps;
197 | }
198 |
199 |
200 | } // namespace grab2_grasp_generator
201 |
--------------------------------------------------------------------------------
/grab2_grasp_generator/src/main.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #include
4 |
5 | #include "grab2_grasp_generator/grasp_generator.hpp"
6 | #include "rclcpp/rclcpp.hpp"
7 |
8 | int main(int argc, char ** argv)
9 | {
10 | rclcpp::init(argc, argv);
11 | auto node = std::make_shared();
12 | rclcpp::spin(node->get_node_base_interface());
13 | rclcpp::shutdown();
14 |
15 | return 0;
16 | }
17 |
--------------------------------------------------------------------------------
/grab2_grasp_generator/test/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | ament_add_gtest(test_grasp_generator
2 | test_grasp_generator.cpp
3 | )
4 |
5 | target_link_libraries(test_grasp_generator
6 | ${library_name}
7 | rclcpp::rclcpp
8 | )
9 |
--------------------------------------------------------------------------------
/grab2_grasp_generator/test/test_grasp_generator.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include "rclcpp/rclcpp.hpp"
10 | #include "grab2_grasp_generator/grasp_generator.hpp"
11 | #include "grab2_msgs/action/get_isaac_grasp.hpp"
12 | #include "geometry_msgs/msg/pose_stamped.hpp"
13 | #include "moveit_msgs/msg/grasp.hpp"
14 |
15 | class GraspGeneratorTestFixture : public ::testing::Test
16 | {
17 | protected:
18 | void SetUp() override
19 | {
20 | rclcpp::init(0, nullptr);
21 | node_ = std::make_shared(rclcpp::NodeOptions());
22 |
23 | // Create temporary test directory
24 | test_dir_ = std::filesystem::temp_directory_path() / "grasp_generator_test";
25 | std::filesystem::create_directories(test_dir_);
26 | }
27 |
28 | void TearDown() override
29 | {
30 | node_.reset();
31 |
32 | // Clean up test files
33 | if (std::filesystem::exists(test_dir_)) {
34 | std::filesystem::remove_all(test_dir_);
35 | }
36 |
37 | rclcpp::shutdown();
38 | }
39 |
40 | // Helper method to create a test YAML file
41 | void createTestYAMLFile(const std::string & filename, const std::string & content)
42 | {
43 | std::ofstream file(test_dir_ / filename);
44 | file << content;
45 | file.close();
46 | }
47 |
48 | // Helper method to create a valid grasp YAML
49 | std::string createValidGraspYAML()
50 | {
51 | return
52 | R"(
53 | format: isaac_grasp
54 | format_version: 1.0
55 |
56 | object_frame_link: /World/mug
57 | gripper_frame_link: /World/panda_hand
58 |
59 | grasps:
60 | grasp_0:
61 | confidence: 0.95
62 | position: [0.1, 0.2, 0.3]
63 | orientation: {w: 1.0, xyz: [0.0, 0.0, 0.0]}
64 | cspace_position:
65 | panda_finger_joint1: 0.001
66 | pregrasp_cspace_position:
67 | panda_finger_joint1: 0.04
68 | grasp_1:
69 | confidence: 0.85
70 | position: [0.15, 0.25, 0.35]
71 | orientation: {w: 0.00332, xyz: [0.98453, 0.16837, 0.04837]}
72 | cspace_position:
73 | panda_finger_joint1: 0.01
74 | pregrasp_cspace_position:
75 | panda_finger_joint1: 0.035
76 | )";
77 | }
78 |
79 | std::shared_ptr node_;
80 | std::filesystem::path test_dir_;
81 | };
82 |
83 | TEST_F(GraspGeneratorTestFixture, testNodeCreation)
84 | {
85 | ASSERT_NE(node_, nullptr);
86 | EXPECT_STREQ(node_->get_name(), "grasp_generator");
87 | }
88 |
89 | TEST_F(GraspGeneratorTestFixture, testParseValidYAML)
90 | {
91 | std::string yaml_content = createValidGraspYAML();
92 | std::string filename = "test_grasps.yaml";
93 | createTestYAMLFile(filename, yaml_content);
94 |
95 | auto grasps_opt = node_->parseYAML((test_dir_ / filename).string());
96 |
97 | ASSERT_TRUE(grasps_opt.has_value());
98 | auto grasps = grasps_opt.value();
99 |
100 | ASSERT_EQ(grasps.size(), 2u);
101 |
102 | // Check first grasp
103 | EXPECT_EQ(grasps[0].id, "grasp_0");
104 | EXPECT_DOUBLE_EQ(grasps[0].grasp_quality, 0.95);
105 | EXPECT_DOUBLE_EQ(grasps[0].grasp_pose.pose.position.x, 0.1);
106 | EXPECT_DOUBLE_EQ(grasps[0].grasp_pose.pose.position.y, 0.2);
107 | EXPECT_DOUBLE_EQ(grasps[0].grasp_pose.pose.position.z, 0.3);
108 | EXPECT_DOUBLE_EQ(grasps[0].grasp_pose.pose.orientation.w, 1.0);
109 |
110 | // Check second grasp
111 | EXPECT_EQ(grasps[1].id, "grasp_1");
112 | EXPECT_DOUBLE_EQ(grasps[1].grasp_quality, 0.85);
113 | EXPECT_DOUBLE_EQ(grasps[1].grasp_pose.pose.position.x, 0.15);
114 | }
115 |
116 | TEST_F(GraspGeneratorTestFixture, testParseYAMLMissingGraspsField)
117 | {
118 | std::string yaml_content = R"(
119 | gripper_frame_link: "/World/panda_hand"
120 | )";
121 | std::string filename = "missing_grasps.yaml";
122 | createTestYAMLFile(filename, yaml_content);
123 |
124 | auto grasps_opt = node_->parseYAML((test_dir_ / filename).string());
125 |
126 | EXPECT_FALSE(grasps_opt.has_value());
127 | }
128 |
129 | TEST_F(GraspGeneratorTestFixture, testParseYAMLEmptyGrasps)
130 | {
131 | std::string yaml_content = R"(
132 | gripper_frame_link: "/World/robotiq_2f_140"
133 | grasps: {}
134 | )";
135 | std::string filename = "empty_grasps.yaml";
136 | createTestYAMLFile(filename, yaml_content);
137 |
138 | auto grasps_opt = node_->parseYAML((test_dir_ / filename).string());
139 |
140 | EXPECT_FALSE(grasps_opt.has_value());
141 | }
142 |
143 | TEST_F(GraspGeneratorTestFixture, testParseInvalidYAML)
144 | {
145 | std::string yaml_content = "invalid: yaml: content: [[["; // Malformed YAML
146 | std::string filename = "invalid.yaml";
147 | createTestYAMLFile(filename, yaml_content);
148 |
149 | auto grasps_opt = node_->parseYAML((test_dir_ / filename).string());
150 |
151 | EXPECT_FALSE(grasps_opt.has_value());
152 | }
153 |
154 | TEST_F(GraspGeneratorTestFixture, testParseNonExistentFile)
155 | {
156 | auto grasps_opt = node_->parseYAML("/nonexistent/path/to/file.yaml");
157 |
158 | EXPECT_FALSE(grasps_opt.has_value());
159 | }
160 |
161 | TEST_F(GraspGeneratorTestFixture, testTransformGraspFrameIdentity)
162 | {
163 | moveit_msgs::msg::Grasp grasp;
164 | grasp.id = "test_grasp";
165 | grasp.grasp_pose.pose.position.x = 1.0;
166 | grasp.grasp_pose.pose.position.y = 2.0;
167 | grasp.grasp_pose.pose.position.z = 3.0;
168 | grasp.grasp_pose.pose.orientation.w = 1.0;
169 | grasp.grasp_pose.pose.orientation.x = 0.0;
170 | grasp.grasp_pose.pose.orientation.y = 0.0;
171 | grasp.grasp_pose.pose.orientation.z = 0.0;
172 |
173 | geometry_msgs::msg::PoseStamped world_to_object;
174 | world_to_object.header.frame_id = "world";
175 | world_to_object.pose.position.x = 0.0;
176 | world_to_object.pose.position.y = 0.0;
177 | world_to_object.pose.position.z = 0.0;
178 | world_to_object.pose.orientation.w = 1.0;
179 | world_to_object.pose.orientation.x = 0.0;
180 | world_to_object.pose.orientation.y = 0.0;
181 | world_to_object.pose.orientation.z = 0.0;
182 |
183 | node_->transfromGraspFrame(grasp, world_to_object);
184 |
185 | EXPECT_EQ(grasp.grasp_pose.header.frame_id, "world");
186 | EXPECT_DOUBLE_EQ(grasp.grasp_pose.pose.position.x, 1.0);
187 | EXPECT_DOUBLE_EQ(grasp.grasp_pose.pose.position.y, 2.0);
188 | EXPECT_DOUBLE_EQ(grasp.grasp_pose.pose.position.z, 3.0);
189 | }
190 |
191 | TEST_F(GraspGeneratorTestFixture, testTransformGraspFrameTranslation)
192 | {
193 | moveit_msgs::msg::Grasp grasp;
194 | grasp.id = "test_grasp";
195 | grasp.grasp_pose.pose.position.x = 1.0;
196 | grasp.grasp_pose.pose.position.y = 0.0;
197 | grasp.grasp_pose.pose.position.z = 0.0;
198 | grasp.grasp_pose.pose.orientation.w = 1.0;
199 | grasp.grasp_pose.pose.orientation.x = 0.0;
200 | grasp.grasp_pose.pose.orientation.y = 0.0;
201 | grasp.grasp_pose.pose.orientation.z = 0.0;
202 |
203 | geometry_msgs::msg::PoseStamped world_to_object;
204 | world_to_object.header.frame_id = "world";
205 | world_to_object.pose.position.x = 5.0;
206 | world_to_object.pose.position.y = 3.0;
207 | world_to_object.pose.position.z = 2.0;
208 | world_to_object.pose.orientation.w = 1.0;
209 | world_to_object.pose.orientation.x = 0.0;
210 | world_to_object.pose.orientation.y = 0.0;
211 | world_to_object.pose.orientation.z = 0.0;
212 |
213 | node_->transfromGraspFrame(grasp, world_to_object);
214 |
215 | EXPECT_EQ(grasp.grasp_pose.header.frame_id, "world");
216 | EXPECT_DOUBLE_EQ(grasp.grasp_pose.pose.position.x, 6.0);
217 | EXPECT_DOUBLE_EQ(grasp.grasp_pose.pose.position.y, 3.0);
218 | EXPECT_DOUBLE_EQ(grasp.grasp_pose.pose.position.z, 2.0);
219 | }
220 |
221 | TEST_F(GraspGeneratorTestFixture, testTransformGraspFrameRotation)
222 | {
223 | moveit_msgs::msg::Grasp grasp;
224 | grasp.id = "test_grasp";
225 | grasp.grasp_pose.pose.position.x = 1.0;
226 | grasp.grasp_pose.pose.position.y = 0.0;
227 | grasp.grasp_pose.pose.position.z = 0.0;
228 | grasp.grasp_pose.pose.orientation.w = 1.0;
229 | grasp.grasp_pose.pose.orientation.x = 0.0;
230 | grasp.grasp_pose.pose.orientation.y = 0.0;
231 | grasp.grasp_pose.pose.orientation.z = 0.0;
232 |
233 | geometry_msgs::msg::PoseStamped world_to_object;
234 | world_to_object.header.frame_id = "world";
235 | world_to_object.pose.position.x = 0.0;
236 | world_to_object.pose.position.y = 0.0;
237 | world_to_object.pose.position.z = 0.0;
238 | // 90 degrees around Z axis
239 | world_to_object.pose.orientation.w = 0.707107;
240 | world_to_object.pose.orientation.x = 0.0;
241 | world_to_object.pose.orientation.y = 0.0;
242 | world_to_object.pose.orientation.z = 0.707107;
243 |
244 | node_->transfromGraspFrame(grasp, world_to_object);
245 |
246 | EXPECT_EQ(grasp.grasp_pose.header.frame_id, "world");
247 | EXPECT_NEAR(grasp.grasp_pose.pose.position.x, 0.0, 1e-5);
248 | EXPECT_NEAR(grasp.grasp_pose.pose.position.y, 1.0, 1e-5);
249 | EXPECT_NEAR(grasp.grasp_pose.pose.position.z, 0.0, 1e-5);
250 | }
251 |
252 | TEST_F(GraspGeneratorTestFixture, testParseMultipleGraspsWithQualities)
253 | {
254 | std::string yaml_content =
255 | R"(
256 | object_frame_link: /World/toy
257 | gripper_frame_link: "/World/robotiq_2f_140"
258 | grasps:
259 | high_quality:
260 | confidence: 0.99
261 | position: [0.0, 0.0, 0.0]
262 | orientation:
263 | w: 1.0
264 | xyz: [0.0, 0.0, 0.0]
265 | cspace_position:
266 | joint1: 0.5
267 | pregrasp_cspace_position:
268 | joint1: 0.0
269 | low_quality:
270 | confidence: 0.50
271 | position: [1.0, 1.0, 1.0]
272 | orientation:
273 | w: 1.0
274 | xyz: [0.0, 0.0, 0.0]
275 | cspace_position:
276 | joint1: 0.3
277 | pregrasp_cspace_position:
278 | joint1: 0.0
279 | )";
280 | std::string filename = "quality_test.yaml";
281 | createTestYAMLFile(filename, yaml_content);
282 |
283 | auto grasps_opt = node_->parseYAML((test_dir_ / filename).string());
284 |
285 | ASSERT_TRUE(grasps_opt.has_value());
286 | auto grasps = grasps_opt.value();
287 |
288 | ASSERT_EQ(grasps.size(), 2u);
289 |
290 | // Find grasps by ID
291 | moveit_msgs::msg::Grasp high_quality_grasp, low_quality_grasp;
292 | for (const auto & grasp : grasps) {
293 | if (grasp.id == "high_quality") {
294 | high_quality_grasp = grasp;
295 | } else if (grasp.id == "low_quality") {
296 | low_quality_grasp = grasp;
297 | }
298 | }
299 |
300 | EXPECT_DOUBLE_EQ(high_quality_grasp.grasp_quality, 0.99);
301 | EXPECT_DOUBLE_EQ(low_quality_grasp.grasp_quality, 0.50);
302 | }
303 |
304 | int main(int argc, char ** argv)
305 | {
306 | testing::InitGoogleTest(&argc, argv);
307 | return RUN_ALL_TESTS();
308 | }
309 |
--------------------------------------------------------------------------------
/grab2_msgs/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.8)
2 | project(grab2_msgs)
3 |
4 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
5 | add_compile_options(-Wall -Wextra -Wpedantic)
6 | endif()
7 |
8 | # find dependencies
9 | find_package(ament_cmake REQUIRED)
10 | find_package(builtin_interfaces REQUIRED)
11 | find_package(action_msgs REQUIRED)
12 | find_package(std_msgs REQUIRED)
13 | find_package(control_msgs REQUIRED)
14 | find_package(geometry_msgs REQUIRED)
15 | find_package(moveit_msgs REQUIRED)
16 | find_package(trajectory_msgs REQUIRED)
17 | find_package(rosidl_default_generators REQUIRED)
18 |
19 | rosidl_generate_interfaces(${PROJECT_NAME}
20 | "action/ComputePlanToPose.action"
21 | "action/ComputePlanThroughPoses.action"
22 | "action/GetIsaacGrasp.action"
23 | DEPENDENCIES builtin_interfaces std_msgs control_msgs geometry_msgs moveit_msgs trajectory_msgs
24 | )
25 |
26 | ament_package()
27 |
--------------------------------------------------------------------------------
/grab2_msgs/action/ComputePlanThroughPoses.action:
--------------------------------------------------------------------------------
1 | #Goal Definition
2 | std_msgs/Header header
3 | geometry_msgs/PoseStamped[] goals
4 | float64 interpolation_dt 0.1 # Optional (Seconds): used while forming trajectory msg
5 | ---
6 | #Result Definition: modified copy of control_msgs/action/FollowJointTrajectory
7 | trajectory_msgs/JointTrajectory trajectory
8 |
9 | int32 error_code
10 | int32 SUCCESSFUL = 0
11 | int32 INVALID_GOAL = -1
12 | int32 INVALID_JOINTS = -2
13 | int32 OLD_HEADER_TIMESTAMP = -3
14 |
15 | # Human readable description of the error code. Contains complementary
16 | # information that is especially useful when execution fails, for instance:
17 | # - INVALID_GOAL: The reason for the invalid goal (e.g., the requested
18 | # trajectory is in the past).
19 | # - INVALID_JOINTS: The mismatch between the expected controller joints
20 | # and those provided in the goal.
21 | string error_string
22 |
23 | ---
24 | #Feedback Definition
25 |
--------------------------------------------------------------------------------
/grab2_msgs/action/ComputePlanToPose.action:
--------------------------------------------------------------------------------
1 | #Goal Definition
2 | std_msgs/Header header
3 | geometry_msgs/PoseStamped goal
4 | float64 interpolation_dt 0.1 # Optional (Seconds): used while forming trajectory msg
5 | ---
6 | #Result Definition: modified copy of control_msgs/action/FollowJointTrajectory
7 | trajectory_msgs/JointTrajectory trajectory
8 |
9 | int32 error_code
10 | int32 SUCCESSFUL = 0
11 | int32 INVALID_GOAL = -1
12 | int32 INVALID_JOINTS = -2
13 | int32 OLD_HEADER_TIMESTAMP = -3
14 |
15 | # Human readable description of the error code. Contains complementary
16 | # information that is especially useful when execution fails, for instance:
17 | # - INVALID_GOAL: The reason for the invalid goal (e.g., the requested
18 | # trajectory is in the past).
19 | # - INVALID_JOINTS: The mismatch between the expected controller joints
20 | # and those provided in the goal.
21 | string error_string
22 |
23 | ---
24 | #Feedback Definition
25 |
--------------------------------------------------------------------------------
/grab2_msgs/action/GetIsaacGrasp.action:
--------------------------------------------------------------------------------
1 | # Goal Definition
2 |
3 | # Object Pose
4 | geometry_msgs/PoseStamped object_pose
5 |
6 | # Path to the isaac_grasp YAML file
7 | string isaac_grasp
8 | ---
9 | # Result Definition
10 |
11 | # All grasps
12 | moveit_msgs/Grasp[] grasps
13 | ---
14 |
--------------------------------------------------------------------------------
/grab2_msgs/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | grab2_msgs
5 | 0.0.0
6 | TODO: Package description
7 | sayed
8 | TODO: License declaration
9 |
10 | ament_cmake
11 | rosidl_default_generators
12 |
13 | builtin_interfaces
14 | std_msgs
15 | geometry_msgs
16 | action_msgs
17 | control_msgs
18 | moveit_msgs
19 | trajectory_msgs
20 |
21 | rosidl_interface_packages
22 |
23 |
24 | ament_cmake
25 |
26 |
27 |
--------------------------------------------------------------------------------
/grab2_planner/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.8)
2 | project(grab2_planner)
3 |
4 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
5 | add_compile_options(-Wall -Wextra -Wpedantic)
6 | endif()
7 |
8 | # find dependencies
9 | find_package(ament_cmake REQUIRED)
10 | find_package(ament_cmake_ros REQUIRED)
11 | find_package(rclcpp REQUIRED)
12 | find_package(rclcpp_action REQUIRED)
13 | find_package(rclcpp_components REQUIRED)
14 | find_package(rclcpp_lifecycle REQUIRED)
15 | find_package(pluginlib REQUIRED)
16 | find_package(std_msgs REQUIRED)
17 | find_package(grab2_msgs REQUIRED)
18 | find_package(grab2_ros_common REQUIRED)
19 | find_package(control_msgs REQUIRED)
20 | find_package(geometry_msgs REQUIRED)
21 | find_package(trajectory_msgs REQUIRED)
22 | find_package(tf2 REQUIRED)
23 | find_package(tf2_ros REQUIRED)
24 | find_package(tf2_geometry_msgs REQUIRED)
25 | find_package(visualization_msgs REQUIRED)
26 | find_package(moveit_msgs REQUIRED)
27 | find_package(moveit_visual_tools REQUIRED)
28 | find_package(moveit_ros_planning REQUIRED)
29 | find_package(moveit_ros_planning_interface REQUIRED)
30 |
31 | set(executable_name planner_server)
32 | set(library_name ${executable_name}_core)
33 |
34 | add_library(${library_name} SHARED
35 | src/planner_server.cpp
36 | )
37 |
38 | target_include_directories(${library_name}
39 | PUBLIC
40 | $
41 | $
42 | )
43 |
44 | target_link_libraries(${library_name} PUBLIC
45 | rclcpp::rclcpp
46 | rclcpp_action::rclcpp_action
47 | grab2_ros_common::grab2_ros_common
48 | tf2_ros::tf2_ros
49 | ${std_msgs_TARGETS}
50 | ${control_msgs_TARGETS}
51 | ${geometry_msgs_TARGETS}
52 | ${trajectory_msgs_TARGETS}
53 | ${grab2_msgs_TARGETS}
54 | ${tf2_geometry_msgs_TARGETS}
55 | ${visualization_msgs_TARGETS}
56 | ${moveit_msgs_TARGETS}
57 | ${moveit_visual_tools_TARGETS}
58 | ${moveit_ros_planning_TARGETS}
59 | ${moveit_ros_planning_interface_TARGETS}
60 | pluginlib::pluginlib
61 | ${rcl_interfaces_TARGETS}
62 | )
63 | target_link_libraries(${library_name} PRIVATE
64 | ${lifecycle_msgs_TARGETS}
65 | rclcpp_components::component
66 | tf2::tf2
67 | )
68 |
69 | add_executable(${executable_name}
70 | src/main.cpp
71 | )
72 |
73 | target_link_libraries(${executable_name} PRIVATE ${library_name} rclcpp::rclcpp)
74 |
75 | install(
76 | TARGETS ${library_name}
77 | EXPORT ${library_name}
78 | ARCHIVE DESTINATION lib
79 | LIBRARY DESTINATION lib
80 | RUNTIME DESTINATION bin
81 | )
82 |
83 | install(TARGETS ${executable_name}
84 | RUNTIME DESTINATION lib/${PROJECT_NAME}
85 | )
86 |
87 | install(
88 | DIRECTORY include/
89 | DESTINATION include/${PROJECT_NAME}
90 | )
91 |
92 | install(
93 | DIRECTORY launch config
94 | DESTINATION share/${PROJECT_NAME}
95 | )
96 |
97 | if(BUILD_TESTING)
98 | find_package(ament_lint_auto REQUIRED)
99 |
100 | # the following line skips the linter which checks for copyrights
101 | set(ament_cmake_copyright_FOUND TRUE)
102 | ament_lint_auto_find_test_dependencies()
103 | endif()
104 |
105 | ament_export_include_directories(include/${PROJECT_NAME})
106 | ament_export_libraries(${library_name})
107 |
108 | ament_export_dependencies(
109 | std_msgs
110 | grab2_msgs
111 | control_msgs
112 | geometry_msgs
113 | trajectory_msgs
114 | visualization_msgs
115 | pluginlib
116 | rclcpp
117 | rclcpp_lifecycle
118 | tf2_ros
119 | moveit_ros_planning_interface
120 | grab2_ros_common
121 | )
122 |
123 | ament_export_targets(${library_name})
124 | ament_package()
125 |
--------------------------------------------------------------------------------
/grab2_planner/include/grab2_planner/planner_server.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #ifndef GRAB2_PLANNER__PLANNER_SERVER_HPP_
4 | #define GRAB2_PLANNER__PLANNER_SERVER_HPP_
5 |
6 | #include
7 | #include
8 |
9 | #include "rclcpp/version.h"
10 | #include "rclcpp/rclcpp.hpp"
11 | #include "rclcpp_action/rclcpp_action.hpp"
12 |
13 | // For Jazzy and Later Support
14 | #if RCLCPP_VERSION_GTE(28, 0, 0)
15 | #include "moveit/move_group_interface/move_group_interface.hpp"
16 | #else
17 | #include "moveit/move_group_interface/move_group_interface.h"
18 | #endif
19 |
20 | #include "grab2_ros_common/node.hpp"
21 | #include "geometry_msgs/msg/pose.hpp"
22 | #include "grab2_msgs/action/compute_plan_to_pose.hpp"
23 | #include "grab2_msgs/action/compute_plan_through_poses.hpp"
24 |
25 | namespace grab2_planner
26 | {
27 |
28 | class PlannerServer : public grab2::Node
29 | {
30 | public:
31 | using ActionToPose = grab2_msgs::action::ComputePlanToPose;
32 | using ActionThroughPoses = grab2_msgs::action::ComputePlanThroughPoses;
33 |
34 | PlannerServer();
35 |
36 | /**
37 | * @brief The action server callback which calls planner to get the trajectory
38 | * ComputePlanToPose
39 | */
40 | void computePlan(
41 | const std::shared_ptr> goal_handle);
42 |
43 | /**
44 | * @brief The action server callback which calls planner to get the trajectory
45 | * ComputePlanThroughPoses
46 | */
47 | void computePlanThroughPoses(
48 | const std::shared_ptr> goal_handle);
49 |
50 | private:
51 | void initialize();
52 |
53 | bool initialized_;
54 | std::string planning_group_;
55 | rclcpp_action::Server::SharedPtr action_server_pose_;
56 | rclcpp_action::Server::SharedPtr action_server_poses_;
57 | moveit::planning_interface::MoveGroupInterfaceUniquePtr move_group_interface_;
58 | };
59 |
60 | } // namespace grab2_planner
61 |
62 | #endif // GRAB2_PLANNER__PLANNER_SERVER_HPP_
63 |
--------------------------------------------------------------------------------
/grab2_planner/launch/planning_server.launch.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from ament_index_python.packages import get_package_share_directory
4 | from launch import LaunchDescription
5 | from launch_ros.actions import Node
6 | from moveit_configs_utils import MoveItConfigsBuilder
7 |
8 |
9 | def generate_launch_description():
10 | # Load the robot configuration
11 | moveit_config = (
12 | MoveItConfigsBuilder('moveit_resources_panda')
13 | .robot_description(file_path='config/panda.urdf.xacro')
14 | .robot_description_semantic(file_path='config/panda.srdf')
15 | .trajectory_execution(file_path='config/gripper_moveit_controllers.yaml')
16 | .planning_pipelines(pipelines=['ompl', 'pilz_industrial_motion_planner'])
17 | .to_moveit_configs()
18 | )
19 |
20 | # RViz for visualization
21 | rviz_config_file = os.path.join(
22 | get_package_share_directory('grab2_planner'),
23 | 'config',
24 | 'panda_move_group.rviz',
25 | )
26 |
27 | # Launch RViz
28 | rviz_node = Node(
29 | package='rviz2',
30 | executable='rviz2',
31 | name='rviz2',
32 | output='log',
33 | arguments=['-d', rviz_config_file],
34 | parameters=[
35 | moveit_config.robot_description,
36 | moveit_config.robot_description_semantic,
37 | moveit_config.robot_description_kinematics,
38 | moveit_config.planning_pipelines,
39 | moveit_config.joint_limits,
40 | ],
41 | )
42 |
43 | # Start Moveit MoveGroup
44 | move_group_node = Node(
45 | package='moveit_ros_move_group',
46 | executable='move_group',
47 | output='screen',
48 | parameters=[moveit_config.to_dict()],
49 | arguments=['--ros-args', '--log-level', 'info'],
50 | )
51 |
52 | # Motion Generation node
53 | planner_server_node = Node(
54 | name='planner_server',
55 | package='grab2_planner',
56 | executable='planner_server',
57 | output='screen',
58 | parameters=[moveit_config.to_dict()],
59 | )
60 |
61 | return LaunchDescription([rviz_node, move_group_node, planner_server_node])
62 |
--------------------------------------------------------------------------------
/grab2_planner/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | grab2_planner
5 | 0.0.0
6 | TODO: Package description
7 | sayed
8 | TODO: License declaration
9 |
10 | ament_cmake_ros
11 |
12 | rclcpp
13 | rclcpp_action
14 | rclcpp_components
15 | tf2_ros
16 | std_msgs
17 | control_msgs
18 | geometry_msgs
19 | trajectory_msgs
20 | grab2_msgs
21 | tf2_geometry_msgs
22 | visualization_msgs
23 | moveit_msgs
24 | moveit_ros_planning
25 | moveit_ros_planning_interface
26 | moveit_visual_tools
27 | moveit_configs_utils
28 | moveit_resources_panda_moveit_config
29 | grab2_ros_common
30 |
31 | ament_lint_auto
32 | ament_lint_common
33 |
34 |
35 | ament_cmake
36 |
37 |
38 |
--------------------------------------------------------------------------------
/grab2_planner/src/main.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #include
4 |
5 | #include "grab2_planner/planner_server.hpp"
6 | #include "rclcpp/rclcpp.hpp"
7 |
8 | int main(int argc, char ** argv)
9 | {
10 | rclcpp::init(argc, argv);
11 | auto node = std::make_shared();
12 | rclcpp::spin(node->get_node_base_interface());
13 | rclcpp::shutdown();
14 |
15 | return 0;
16 | }
17 |
--------------------------------------------------------------------------------
/grab2_planner/src/planner_server.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #include "grab2_planner/planner_server.hpp"
4 |
5 | namespace grab2_planner
6 | {
7 |
8 | PlannerServer::PlannerServer()
9 | : Node("planner_server",
10 | rclcpp::NodeOptions().automatically_declare_parameters_from_overrides(true)),
11 | initialized_(false)
12 | {
13 | this->action_server_pose_ = create_action_server(
14 | "compute_plan_to_pose",
15 | std::bind(&PlannerServer::computePlan, this, std::placeholders::_1)
16 | );
17 |
18 | this->action_server_poses_ = create_action_server(
19 | "compute_plan_through_poses",
20 | std::bind(&PlannerServer::computePlanThroughPoses, this, std::placeholders::_1)
21 | );
22 |
23 | RCLCPP_INFO(this->get_logger(), "PlannerServer constructed");
24 | }
25 |
26 | void
27 | PlannerServer::initialize()
28 | {
29 | if (!initialized_) {
30 | // Get planning group from parameters
31 | this->get_parameter_or("planning_group", planning_group_, std::string("panda_arm"));
32 |
33 | // Create MoveGroupInterface
34 | move_group_interface_ =
35 | std::make_unique(
36 | this->shared_from_this(),
37 | planning_group_);
38 |
39 | initialized_ = true;
40 | RCLCPP_INFO(
41 | this->get_logger(),
42 | "PlannerServer initialized with planning group: %s",
43 | planning_group_.c_str());
44 | }
45 | }
46 |
47 | void
48 | PlannerServer::computePlan(const std::shared_ptr> goal_handle)
49 | {
50 | RCLCPP_INFO(this->get_logger(), "Executing ComputePlanToPose");
51 |
52 | const auto goal = goal_handle->get_goal();
53 | auto result = std::make_shared();
54 |
55 | if (!initialized_) {
56 | initialize();
57 | }
58 |
59 | // Planning logic
60 | const auto target = goal->goal.pose;
61 |
62 | moveit::planning_interface::MoveGroupInterface::Plan plan_msg;
63 | move_group_interface_->setPoseTarget(target);
64 |
65 | auto const success = static_cast(move_group_interface_->plan(plan_msg));
66 |
67 | if (success) {
68 | // For Jazzy and Later Support
69 | #if RCLCPP_VERSION_GTE(28, 0, 0)
70 | result->trajectory = plan_msg.trajectory.joint_trajectory;
71 | #else
72 | result->trajectory = plan_msg.trajectory_.joint_trajectory;
73 | #endif
74 |
75 | goal_handle->succeed(result);
76 | } else {
77 | result->error_string = "Failed to plan";
78 | goal_handle->abort(result);
79 | RCLCPP_ERROR(this->get_logger(), "Planning failed!");
80 | }
81 | }
82 |
83 | void PlannerServer::computePlanThroughPoses(
84 | const std::shared_ptr> goal_handle)
85 | {
86 | RCLCPP_INFO(this->get_logger(), "Executing ComputePlanThroughPoses");
87 |
88 | const auto goal = goal_handle->get_goal();
89 | auto result = std::make_shared();
90 |
91 | // Check if we need to initialize
92 | if (!initialized_) {
93 | initialize();
94 | }
95 |
96 | // TODO(ElSayed): Implement your planning logic here
97 | // Example:
98 | // - Use move_group_interface_ to plan through goal->target_poses
99 | // - Fill result->trajectory
100 |
101 | // For now, just abort
102 | goal_handle->abort(result);
103 | }
104 |
105 | } // namespace grab2_planner
106 |
107 | #include "rclcpp_components/register_node_macro.hpp"
108 |
109 | // Register the component with class_loader.
110 | // This acts as a sort of entry point, allowing the component to be discoverable when its library
111 | // is being loaded into a running process.
112 | // RCLCPP_COMPONENTS_REGISTER_NODE(grab2_planner::PlannerServer)
113 |
--------------------------------------------------------------------------------
/grab2_ros_common/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.8)
2 | project(grab2_ros_common)
3 |
4 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
5 | add_compile_options(-Wall -Wextra -Wpedantic)
6 | endif()
7 |
8 | # find dependencies
9 | find_package(ament_cmake REQUIRED)
10 | find_package(rclcpp REQUIRED)
11 | find_package(rclcpp_action REQUIRED)
12 | find_package(tf2 REQUIRED)
13 | find_package(tf2_ros REQUIRED)
14 | find_package(tf2_geometry_msgs REQUIRED)
15 | find_package(tf2_msgs REQUIRED)
16 | find_package(grab2_msgs REQUIRED)
17 | find_package(geometry_msgs REQUIRED)
18 | find_package(moveit_msgs REQUIRED)
19 |
20 | add_library(${PROJECT_NAME} INTERFACE)
21 | target_include_directories(${PROJECT_NAME}
22 | INTERFACE
23 | "$"
24 | "$"
25 | )
26 |
27 | target_link_libraries(${PROJECT_NAME} INTERFACE
28 | ${geometry_msgs_TARGETS}
29 | ${grab2_msgs_TARGETS}
30 | rclcpp::rclcpp
31 | tf2_ros::tf2_ros
32 | rclcpp_action::rclcpp_action
33 | tf2_geometry_msgs::tf2_geometry_msgs
34 | )
35 |
36 | install(DIRECTORY include/
37 | DESTINATION include/${PROJECT_NAME}
38 | )
39 |
40 | install(TARGETS ${PROJECT_NAME}
41 | EXPORT ${PROJECT_NAME}
42 | ARCHIVE DESTINATION lib
43 | LIBRARY DESTINATION lib
44 | RUNTIME DESTINATION bin
45 | )
46 |
47 | ament_export_include_directories(include/${PROJECT_NAME})
48 |
49 | if(BUILD_TESTING)
50 | find_package(ament_lint_auto REQUIRED)
51 |
52 | # the following line skips the linter which checks for copyrights
53 | set(ament_cmake_copyright_FOUND TRUE)
54 | ament_lint_auto_find_test_dependencies()
55 | endif()
56 |
57 | ament_export_dependencies(
58 | geometry_msgs
59 | rclcpp
60 | rclcpp_action
61 | grab2_msgs
62 | tf2
63 | tf2_geometry_msgs
64 | tf2_ros
65 | )
66 |
67 | ament_export_targets(${PROJECT_NAME})
68 | ament_package()
69 |
--------------------------------------------------------------------------------
/grab2_ros_common/include/grab2_ros_common/node.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2025, ElSayed ElSheikh
2 |
3 | #ifndef GRAB2_ROS_COMMON__NODE_HPP_
4 | #define GRAB2_ROS_COMMON__NODE_HPP_
5 |
6 | #include
7 | #include
8 |
9 | #include "rclcpp/rclcpp.hpp"
10 | #include "rclcpp_action/rclcpp_action.hpp"
11 |
12 | namespace grab2
13 | {
14 |
15 | class Node : public rclcpp::Node
16 | {
17 | public:
18 | Node(
19 | const std::string & node_name,
20 | const rclcpp::NodeOptions & options = rclcpp::NodeOptions()
21 | )
22 | : rclcpp::Node(node_name, options)
23 | {}
24 |
25 | Node(
26 | const std::string & node_name,
27 | const std::string & ns,
28 | const rclcpp::NodeOptions & options = rclcpp::NodeOptions()
29 | )
30 | : rclcpp::Node(node_name, ns, options)
31 | {}
32 |
33 | // ROS2 Action Server
34 | template
35 | using GoalHandle = rclcpp_action::ServerGoalHandle;
36 |
37 | template
38 | using ExecuteCallback = std::function>)>;
39 |
40 | template
41 | typename rclcpp_action::Server::SharedPtr create_action_server(
42 | const std::string & name,
43 | ExecuteCallback execute_callback)
44 | {
45 | auto handle_goal = [this, name](
46 | const rclcpp_action::GoalUUID & uuid,
47 | std::shared_ptr goal) -> rclcpp_action::GoalResponse
48 | {
49 | (void)uuid;
50 | (void)goal;
51 | RCLCPP_INFO(this->get_logger(), "Received goal request for %s", name.c_str());
52 | return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
53 | };
54 |
55 | auto handle_cancel = [this, name](
56 | const std::shared_ptr> goal_handle) -> rclcpp_action::CancelResponse
57 | {
58 | (void)goal_handle;
59 | RCLCPP_INFO(this->get_logger(), "Received request to cancel goal for %s", name.c_str());
60 | return rclcpp_action::CancelResponse::ACCEPT;
61 | };
62 |
63 | auto handle_accepted = [this, execute_callback](
64 | const std::shared_ptr> goal_handle)
65 | {
66 | std::thread{[execute_callback, goal_handle]() {
67 | execute_callback(goal_handle);
68 | }}.detach();
69 | };
70 |
71 | return rclcpp_action::create_server(
72 | this->get_node_base_interface(),
73 | this->get_node_clock_interface(),
74 | this->get_node_logging_interface(),
75 | this->get_node_waitables_interface(),
76 | name,
77 | handle_goal,
78 | handle_cancel,
79 | handle_accepted);
80 | }
81 | };
82 |
83 | } // namespace grab2
84 |
85 | #endif // GRAB2_ROS_COMMON__NODE_HPP_
86 |
--------------------------------------------------------------------------------
/grab2_ros_common/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | grab2_ros_common
5 | 0.0.0
6 | TODO: Package description
7 | sayed
8 | TODO: License declaration
9 |
10 | ament_cmake
11 |
12 | rclcpp
13 | rclcpp_action
14 | tf2
15 | tf2_ros
16 | tf2_geometry_msgs
17 | tf2_msgs
18 | grab2_msgs
19 | geometry_msgs
20 | moveit_msgs
21 |
22 | ament_lint_auto
23 | ament_lint_common
24 |
25 |
26 | ament_cmake
27 |
28 |
29 |
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/objects/plate.usd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/objects/plate.usd
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/objects/toy_car.usd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/objects/toy_car.usd
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/robots/suction_gripper.usd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/robots/suction_gripper.usd
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/robots/ur10e_short_suction.usd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/robots/ur10e_short_suction.usd
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Box_baseColor.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Box_baseColor.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Box_metallicRoughness_metal_scale0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Box_metallicRoughness_metal_scale0.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Box_metallicRoughness_rough.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Box_metallicRoughness_rough.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Box_normal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Box_normal.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Light_baseColor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Light_baseColor.png
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Light_emissive.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Light_emissive.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Light_metallicRoughness_metal_scale0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Light_metallicRoughness_metal_scale0.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Light_metallicRoughness_rough.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Light_metallicRoughness_rough.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Room_baseColor.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Room_baseColor.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Room_metallicRoughness_metal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Room_metallicRoughness_metal.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Room_metallicRoughness_rough.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Room_metallicRoughness_rough.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Room_normal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Room_normal.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Rug_baseColor_scale1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Rug_baseColor_scale1.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Rug_metallicRoughness_metal_scale0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Rug_metallicRoughness_metal_scale0.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Rug_metallicRoughness_rough.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Rug_metallicRoughness_rough.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Rug_normal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Rug_normal.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Toys_baseColor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Toys_baseColor.png
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Toys_emissive.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Toys_emissive.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Toys_metallicRoughness_metal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Toys_metallicRoughness_metal.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Toys_metallicRoughness_rough.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Toys_metallicRoughness_rough.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Toys_normal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/Textures/Mat_Toys_normal.jpg
--------------------------------------------------------------------------------
/isaac_sim_worlds/assets/worlds/toybox_world/world.usd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elsayedelsheikh/grab2/1a308a68997cd4eac40fb946b0c465f2a709189d/isaac_sim_worlds/assets/worlds/toybox_world/world.usd
--------------------------------------------------------------------------------
/isaac_sim_worlds/isaac_py.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Set possible Isaac Sim paths
4 | ISAAC_SIM_PATHS=("$HOME/isaacsim" "/isaac-sim")
5 |
6 | # Get absolute path of the current script directory
7 | CUR_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
8 |
9 | # Prepend the script directory to all arguments
10 | NEW_ARGS=""
11 | for arg in "$@"; do
12 | NEW_ARGS="${NEW_ARGS} ${CUR_SCRIPT_DIR}/${arg}"
13 | done
14 |
15 | # Look for a valid Isaac Sim installation
16 | for ISAAC_SIM_PATH in "${ISAAC_SIM_PATHS[@]}"; do
17 | if [[ -f "$ISAAC_SIM_PATH/python.sh" ]]; then
18 | "$ISAAC_SIM_PATH/python.sh" $NEW_ARGS
19 | exit 0
20 | fi
21 | done
22 |
23 | echo "No valid Isaac Sim installation found under home or root directory."
24 | exit 1
25 |
--------------------------------------------------------------------------------
/isaac_sim_worlds/panda_bin_cubes.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | import carb
4 | import numpy as np
5 |
6 | # USD Assets
7 | # Nvidia Isaac Server Assets
8 | ROBOT_USD_PATH = '/Isaac/Robots/Franka/franka_alt_fingers.usd'
9 | BACKGROUND_USD_PATH = '/Isaac/Environments/Simple_Room/simple_room.usd'
10 |
11 | # Prim Paths
12 | ROBOT_PRIM = '/World/Franka'
13 | CAMERA_PRIM = f'{ROBOT_PRIM}/panda_hand/geometry/realsense/realsense_camera'
14 |
15 |
16 | from isaacsim import SimulationApp # noqa E402 isort: skip
17 |
18 | simulation_app = SimulationApp({'renderer': 'RayTracedLighting', 'headless': False})
19 |
20 | # More imports that need to compare after we create the app
21 | from omni.isaac.core import SimulationContext # noqa E402 isort: skip
22 | from omni.isaac.core.utils import ( # noqa E402 isort: skip
23 | extensions,
24 | nucleus,
25 | prims,
26 | rotations,
27 | stage,
28 | viewports,
29 | )
30 | from omni.isaac.core_nodes.scripts.utils import ( # noqa E402 isort: skip
31 | set_target_prims,
32 | )
33 | from pxr import Gf, UsdGeom # noqa E402 isort: skip
34 | from isaacsim.core.prims import RigidPrim # noqa E402 isort: skip
35 | from isaacsim.core.api.objects import DynamicCuboid # noqa E402 isort: skip
36 |
37 | # enable ROS2 bridge extension
38 | extensions.enable_extension('isaacsim.ros2.bridge')
39 | extensions.enable_extension('isaacsim.core.nodes')
40 |
41 | # Action Graphs
42 | from isaacsim.ros2.bridge.scripts.og_shortcuts.og_rtx_sensors import ( # noqa E402 isort: skip
43 | Ros2CameraGraph,
44 | )
45 | from isaacsim.ros2.bridge.scripts.og_shortcuts.og_utils import ( # noqa E402 isort: skip
46 | Ros2JointStatesGraph,
47 | Ros2TfPubGraph,
48 | )
49 |
50 | simulation_context = SimulationContext(stage_units_in_meters=1.0)
51 |
52 | # Locate Isaac Sim assets folder to load environment and robot stages
53 | assets_root_path = nucleus.get_assets_root_path()
54 | if assets_root_path is None:
55 | carb.log_error('Could not find Isaac Sim assets folder')
56 | simulation_app.close()
57 | sys.exit()
58 |
59 | # Preparing stage
60 | viewports.set_camera_view(eye=np.array([1.2, 1.2, 0.8]), target=np.array([0, 0, 0.5]))
61 |
62 | # Loading the simple_room environment
63 | stage.add_reference_to_stage(
64 | assets_root_path + BACKGROUND_USD_PATH, '/World/background'
65 | )
66 |
67 | # Loading the Robot
68 | prims.create_prim(
69 | ROBOT_PRIM,
70 | 'Xform',
71 | position=np.array([0, -0.64, 0]),
72 | orientation=rotations.gf_rotation_to_np_array(Gf.Rotation(Gf.Vec3d(0, 0, 1), 90)),
73 | usd_path=assets_root_path + ROBOT_USD_PATH,
74 | )
75 |
76 | # Loading Cubes
77 | for i in range(3):
78 | DynamicCuboid(
79 | prim_path=f'/World/Objects/cube_{i}',
80 | scale=np.array([0.0515, 0.0515, 0.0515]),
81 | size=1.0,
82 | color=np.array([1, 0, 0]),
83 | )
84 | cubes_view = RigidPrim(prim_paths_expr='/World/Objects/cube_[0-2]')
85 |
86 | cube_pose = np.array([-0.34, -0.4, 0.1])
87 | cube_pose_offset = np.array([0.0, 0.1, 0.0])
88 | cubes_view.set_world_poses(
89 | positions=np.array(
90 | [cube_pose, cube_pose + 1 * cube_pose_offset, cube_pose + 2 * cube_pose_offset]
91 | )
92 | )
93 |
94 | # Bin
95 | prims.create_prim(
96 | '/World/bin',
97 | 'Xform',
98 | position=np.array([0.35, -0.32, 0.09]),
99 | usd_path=assets_root_path + '/Isaac/Props/KLT_Bin/small_KLT.usd',
100 | )
101 |
102 | simulation_app.update()
103 |
104 | # Camera
105 | # Fix camera settings since the defaults in the realsense model are inaccurate
106 | realsense_prim = UsdGeom.Camera(stage.get_current_stage().GetPrimAtPath(CAMERA_PRIM))
107 | realsense_prim.GetHorizontalApertureAttr().Set(20.955)
108 | realsense_prim.GetVerticalApertureAttr().Set(15.7)
109 | realsense_prim.GetFocalLengthAttr().Set(18.8)
110 | realsense_prim.GetFocusDistanceAttr().Set(400)
111 |
112 | # Create Camera Action Graph
113 | CAMERA_GRAPH_PATH = '/World/Graphs/Camera'
114 | camera_graph = Ros2CameraGraph()
115 | camera_graph._og_path = CAMERA_GRAPH_PATH
116 | camera_graph._camera_prim = CAMERA_PRIM
117 | camera_graph._frame_id = 'realsense_camera'
118 |
119 | # Topics
120 | camera_graph._node_namespace = 'eef_camera'
121 | camera_graph._rgb_topic = 'image_raw'
122 | camera_graph._depth_topic = 'image_depth'
123 |
124 | param_check = camera_graph._check_params()
125 | if param_check:
126 | print('Creating Articualtion Graph')
127 | camera_graph.make_graph()
128 | else:
129 | carb.log_error('Check Articualtion Graph parameters')
130 |
131 | simulation_app.update()
132 |
133 | # Create Tf Action Graph
134 | # You can add any prim_path to the following list to publish their tf with respect to /World
135 | tf_target_prims = [
136 | CAMERA_PRIM,
137 | ]
138 |
139 | TF_GRAPH_PATH = '/World/Graphs/Transforms'
140 | tf_graph = Ros2TfPubGraph()
141 | tf_graph._og_path = TF_GRAPH_PATH
142 |
143 | param_check = tf_graph._check_params()
144 | if param_check:
145 | print('Creating Transforms Graph')
146 | tf_graph.make_graph()
147 | else:
148 | carb.log_error('Check Transforms Graph parameters')
149 |
150 | set_target_prims(
151 | primPath=TF_GRAPH_PATH + '/PublisherTF',
152 | inputName='inputs:targetPrims',
153 | targetPrimPaths=tf_target_prims,
154 | )
155 |
156 | simulation_app.update()
157 |
158 | # Create Articulation Action Graph
159 | robot_graph = Ros2JointStatesGraph()
160 | robot_graph._og_path = '/World/Graphs/Articulation'
161 | robot_graph._art_root_path = ROBOT_PRIM
162 | robot_graph._publisher = True
163 | robot_graph._pub_topic = 'isaac_joint_states'
164 | robot_graph._subscriber = True
165 | robot_graph._sub_topic = 'isaac_joint_commands'
166 |
167 | param_check = robot_graph._check_params()
168 | if param_check:
169 | print('Creating Articualtion Graph')
170 | robot_graph.make_graph()
171 | else:
172 | carb.log_error('Check Articualtion Graph parameters')
173 |
174 | simulation_app.update()
175 | simulation_context.play()
176 |
177 | while simulation_app.is_running():
178 | simulation_context.step(render=True)
179 |
180 | simulation_context.stop()
181 | simulation_app.close()
182 |
--------------------------------------------------------------------------------
/isaac_sim_worlds/panda_toybox.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 |
4 | import carb
5 | import numpy as np
6 |
7 | # USD Assets
8 | # Nvidia Isaac Server Assets
9 | ROBOT_USD_PATH = '/Isaac/Robots/Franka/franka_alt_fingers.usd'
10 |
11 | # User Assets
12 | USER_PATH = (
13 | os.getcwd()
14 | ) # Make sure you're running the script from the correct directory
15 | TOY_CAR_USD_PATH = os.path.join(USER_PATH, 'assets', 'objects', 'toy_car.usd')
16 | BACKGROUND_USD_PATH = os.path.join(
17 | USER_PATH, 'assets', 'worlds', 'toybox_world', 'world.usd'
18 | )
19 |
20 | # Prim Paths
21 | ROBOT_PRIM = '/World/Franka'
22 | CAMERA_PRIM = f'{ROBOT_PRIM}/panda_hand/geometry/realsense/realsense_camera'
23 |
24 | from isaacsim import SimulationApp # noqa E402 isort: skip
25 |
26 | simulation_app = SimulationApp({'renderer': 'RayTracedLighting', 'headless': False})
27 |
28 | # More imports required after app creation
29 | from omni.isaac.core import SimulationContext # noqa E402 isort: skip
30 | from omni.isaac.core.utils import ( # noqa E402 isort: skip
31 | extensions,
32 | nucleus,
33 | prims,
34 | rotations,
35 | stage,
36 | viewports,
37 | )
38 | from omni.isaac.core_nodes.scripts.utils import ( # noqa E402 isort: skip
39 | set_target_prims,
40 | )
41 | from pxr import Sdf, Gf, UsdGeom, UsdShade # noqa E402 isort: skip
42 |
43 | # enable ROS2 bridge extension
44 | extensions.enable_extension('isaacsim.ros2.bridge')
45 | extensions.enable_extension('isaacsim.core.nodes')
46 |
47 | # Action Graphs
48 | import omni.graph.core as og # noqa E402 isort: skip
49 | from isaacsim.ros2.bridge.scripts.og_shortcuts.og_rtx_sensors import ( # noqa E402 isort: skip
50 | Ros2CameraGraph,
51 | )
52 | from isaacsim.ros2.bridge.scripts.og_shortcuts.og_utils import ( # noqa E402 isort: skip
53 | Ros2JointStatesGraph,
54 | Ros2TfPubGraph,
55 | )
56 |
57 | simulation_context = SimulationContext(stage_units_in_meters=1.0)
58 |
59 | # Setup Stage
60 | # Locate Isaac Sim assets directory to load robot
61 | assets_root_path = nucleus.get_assets_root_path()
62 | if assets_root_path is None:
63 | carb.log_error('Could not find Isaac Sim assets directory')
64 | simulation_app.close()
65 | sys.exit()
66 |
67 | # Preparing stage
68 | viewports.set_camera_view(eye=np.array([2.0, 1.35, 1.8]), target=np.array([0, 0, 0.3]))
69 |
70 | # Loading the background environment
71 | stage.add_reference_to_stage(BACKGROUND_USD_PATH, '/World/background')
72 |
73 | # Loading the Robot
74 | prims.create_prim(
75 | ROBOT_PRIM,
76 | 'Xform',
77 | position=np.array([0.0, 0.25, 0]),
78 | usd_path=assets_root_path + ROBOT_USD_PATH,
79 | )
80 |
81 | # Loading the Toys
82 | prims.create_prim(
83 | '/Toys/car_0',
84 | 'Xform',
85 | position=np.array([0.0, 0.7, 0.04]),
86 | usd_path=TOY_CAR_USD_PATH,
87 | )
88 | prims.create_prim(
89 | '/Toys/car_1',
90 | 'Xform',
91 | position=np.array([0.18, 0.7, 0.04]),
92 | orientation=rotations.gf_rotation_to_np_array(Gf.Rotation(Gf.Vec3d(0, 0, 1), 45)),
93 | usd_path=TOY_CAR_USD_PATH,
94 | )
95 | prims.create_prim(
96 | '/Toys/car_2',
97 | 'Xform',
98 | position=np.array([0.26, 0.43, 0.04]),
99 | orientation=rotations.gf_rotation_to_np_array(Gf.Rotation(Gf.Vec3d(1, 0, 1), -90)),
100 | usd_path=TOY_CAR_USD_PATH,
101 | )
102 | prims.create_prim(
103 | '/Toys/car_3',
104 | 'Xform',
105 | position=np.array([0.40, 0.43, 0.04]),
106 | orientation=rotations.gf_rotation_to_np_array(Gf.Rotation(Gf.Vec3d(0, 0, 1), -37)),
107 | usd_path=TOY_CAR_USD_PATH,
108 | )
109 |
110 | # Let's change colors
111 | cars = {
112 | 'car_1': [0.0, 1.0, 0.0], # Green
113 | 'car_2': [1.0, 0.0, 0.0], # Red
114 | 'car_3': [1.0, 1.0, 0.0], # Yellow
115 | }
116 |
117 |
118 | def change_color(stage, material_path, rgb_values):
119 | pbr_shader = UsdShade.Shader(stage.get_current_stage().GetPrimAtPath(material_path))
120 | color = pbr_shader.CreateInput('diffuseColor', Sdf.ValueTypeNames.Color3f)
121 | color.Set(Gf.Vec3f(rgb_values))
122 |
123 |
124 | for car_name, color in cars.items():
125 | material = f'/Toys/{car_name}/Car/Materials/Body/pbr_shader'
126 | change_color(stage, material, color)
127 |
128 | simulation_app.update()
129 |
130 | # Camera
131 | # Fix camera settings since the defaults in the realsense model are inaccurate
132 | realsense_prim = UsdGeom.Camera(stage.get_current_stage().GetPrimAtPath(CAMERA_PRIM))
133 | realsense_prim.GetHorizontalApertureAttr().Set(20.955)
134 | realsense_prim.GetVerticalApertureAttr().Set(15.7)
135 | realsense_prim.GetFocalLengthAttr().Set(18.8)
136 | realsense_prim.GetFocusDistanceAttr().Set(400)
137 |
138 | # Create Camera Action Graph
139 | CAMERA_GRAPH_PATH = '/World/Graphs/Camera'
140 | camera_graph = Ros2CameraGraph()
141 | camera_graph._og_path = CAMERA_GRAPH_PATH
142 | camera_graph._camera_prim = CAMERA_PRIM
143 | camera_graph._frame_id = 'realsense_camera'
144 |
145 | # Topics
146 | camera_graph._node_namespace = 'eef_camera'
147 | camera_graph._rgb_topic = 'image_raw'
148 | camera_graph._depth_topic = 'image_depth'
149 | camera_graph._bbox3d_pub = True
150 | camera_graph.__bbox3d_topic = 'bbox3d'
151 |
152 | param_check = camera_graph._check_params()
153 | if param_check:
154 | print('Creating Articualtion Graph')
155 | camera_graph.make_graph()
156 | else:
157 | carb.log_error('Check Articualtion Graph parameters')
158 |
159 | # Issue: Even though frameId is set to camera frame,
160 | # bbox3d array is published with respect to world
161 | # Change bbox3d frameId to world so that it appears correctly in rviz2
162 | BBOX3D_NODE_PATH = f'{CAMERA_GRAPH_PATH}/Bbox3dPublish'
163 | try:
164 | frameid_attr = og.Controller.attribute(f'{BBOX3D_NODE_PATH}.inputs:frameId')
165 | semantics_attr = og.Controller.attribute(
166 | f'{BBOX3D_NODE_PATH}.inputs:enableSemanticLabels'
167 | )
168 | og.Controller.set(frameid_attr, 'world')
169 | og.Controller.set(
170 | semantics_attr, False
171 | ) # Change this if you need semantics published
172 | except Exception as e:
173 | print('Error accessing attribute:', e)
174 |
175 | simulation_app.update()
176 |
177 | # Create Tf Action Graph
178 | # You can add any prim_path to the following list to publish their tf with respect to /World
179 | tf_target_prims = [
180 | CAMERA_PRIM,
181 | ]
182 |
183 | TF_GRAPH_PATH = '/World/Graphs/Transforms'
184 | tf_graph = Ros2TfPubGraph()
185 | tf_graph._og_path = TF_GRAPH_PATH
186 |
187 | param_check = tf_graph._check_params()
188 | if param_check:
189 | print('Creating Transforms Graph')
190 | tf_graph.make_graph()
191 | else:
192 | carb.log_error('Check Transforms Graph parameters')
193 |
194 | set_target_prims(
195 | primPath=TF_GRAPH_PATH + '/PublisherTF',
196 | inputName='inputs:targetPrims',
197 | targetPrimPaths=tf_target_prims,
198 | )
199 |
200 | simulation_app.update()
201 |
202 | # Create Articulation Action Graph
203 | robot_graph = Ros2JointStatesGraph()
204 | robot_graph._og_path = '/World/Graphs/Articulation'
205 | robot_graph._art_root_path = ROBOT_PRIM
206 | robot_graph._publisher = True
207 | robot_graph._pub_topic = 'isaac_joint_states'
208 | robot_graph._subscriber = True
209 | robot_graph._sub_topic = 'isaac_joint_commands'
210 |
211 | param_check = robot_graph._check_params()
212 | if param_check:
213 | print('Creating Articualtion Graph')
214 | robot_graph.make_graph()
215 | else:
216 | carb.log_error('Check Articualtion Graph parameters')
217 |
218 | simulation_app.update()
219 | simulation_context.play()
220 |
221 | # Simulation Loop
222 | while simulation_app.is_running():
223 | simulation_context.step(render=True)
224 |
225 | simulation_context.stop()
226 | simulation_app.close()
227 |
--------------------------------------------------------------------------------
/isaac_sim_worlds/ur10e_stack_plates.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 |
4 | import carb
5 | import numpy as np
6 |
7 | # USD Assets
8 | # Nvidia Isaac Server Assets
9 | BACKGROUND_USD_PATH = '/Isaac/Environments/Simple_Room/simple_room.usd'
10 |
11 | # User Assets
12 | USER_PATH = (
13 | os.getcwd()
14 | ) # Make sure you're running the script from the correct directory
15 | PLATE_USD_PATH = os.path.join(USER_PATH, 'assets', 'objects', 'plate.usd')
16 | ROBOT_USD_PATH = os.path.join(USER_PATH, 'assets', 'robots', 'ur10e_short_suction.usd')
17 |
18 | # Prim Paths
19 | ROBOT_PRIM = '/World/UR10e'
20 | SUCTION_GRIPPER = f'{ROBOT_PRIM}/ee_link'
21 | CAMERA_PRIM = f'{SUCTION_GRIPPER}/Camera'
22 |
23 | from isaacsim import SimulationApp # noqa E402 isort: skip
24 |
25 | simulation_app = SimulationApp({'renderer': 'RayTracedLighting', 'headless': False})
26 |
27 | # More imports that need to compare after we create the app
28 | from omni.isaac.core import SimulationContext # noqa E402 isort: skip
29 | from omni.isaac.core.utils import ( # noqa E402 isort: skip
30 | extensions,
31 | rotations,
32 | prims,
33 | stage,
34 | viewports,
35 | )
36 | from omni.isaac.core_nodes.scripts.utils import ( # noqa E402 isort: skip
37 | set_target_prims,
38 | )
39 | from pxr import Gf, UsdGeom # noqa E402 isort: skip
40 | from isaacsim.storage.native import get_assets_root_path # noqa E402 isort: skip
41 | from isaacsim.core.prims import XFormPrim # noqa E402 isort: skip
42 |
43 | # enable ROS2 bridge extension
44 | extensions.enable_extension('isaacsim.ros2.bridge')
45 | extensions.enable_extension('isaacsim.core.nodes')
46 |
47 | # Action Graphs
48 | from isaacsim.ros2.bridge.scripts.og_shortcuts.og_rtx_sensors import ( # noqa E402 isort: skip
49 | Ros2CameraGraph,
50 | )
51 | from isaacsim.ros2.bridge.scripts.og_shortcuts.og_utils import ( # noqa E402 isort: skip
52 | Ros2JointStatesGraph,
53 | Ros2TfPubGraph,
54 | )
55 |
56 | simulation_context = SimulationContext(stage_units_in_meters=1.0)
57 |
58 | # Locate Isaac Sim assets folder to load environment and robot stages
59 | nvidia_assets_root_path = get_assets_root_path()
60 | if nvidia_assets_root_path is None:
61 | carb.log_error('Could not find Isaac Sim assets folder')
62 | simulation_app.close()
63 | sys.exit()
64 |
65 | # Preparing stage
66 | viewports.set_camera_view(eye=np.array([1.2, 1.2, 0.8]), target=np.array([0, 0, 0.5]))
67 |
68 | # Loading the simple_room environment
69 | stage.add_reference_to_stage(
70 | nvidia_assets_root_path + BACKGROUND_USD_PATH, '/World/background'
71 | )
72 |
73 | # Loading the Robot
74 | prims.create_prim(
75 | ROBOT_PRIM,
76 | 'Xform',
77 | position=np.array([0, -0.64, 0]),
78 | usd_path=ROBOT_USD_PATH,
79 | )
80 |
81 | # Loading Objects
82 | for i in range(4):
83 | prims.create_prim(
84 | f'/World/Objects/plate_{i}',
85 | 'Xform',
86 | usd_path=PLATE_USD_PATH,
87 | )
88 |
89 | objects_view = XFormPrim(prim_paths_expr='/World/Objects/plate_[0-3]')
90 |
91 | object_pose = np.array([-0.37, -0.5, 0.1])
92 | object_pose_offset = np.array([0.0, 0.3, 0.0])
93 | objects_view.set_world_poses(
94 | positions=np.array(
95 | [
96 | object_pose,
97 | object_pose + 1 * object_pose_offset,
98 | object_pose + 2 * object_pose_offset,
99 | object_pose + 3 * object_pose_offset,
100 | ]
101 | )
102 | )
103 |
104 | simulation_app.update()
105 |
106 | # Camera
107 | # Fix camera settings
108 | # camera_prim = UsdGeom.Camera(stage.get_current_stage().GetPrimAtPath(CAMERA_PRIM))
109 | # camera_prim.GetHorizontalApertureAttr().Set(20.955)
110 | # camera_prim.GetVerticalApertureAttr().Set(15.7)
111 | # camera_prim.GetFocalLengthAttr().Set(18.8)
112 | # camera_prim.GetFocusDistanceAttr().Set(400)
113 |
114 | # # Create Camera Action Graph
115 | # CAMERA_GRAPH_PATH = '/World/Graphs/Camera'
116 | # camera_graph = Ros2CameraGraph()
117 | # camera_graph._og_path = CAMERA_GRAPH_PATH
118 | # camera_graph._camera_prim = CAMERA_PRIM
119 | # camera_graph._frame_id = 'realsense_camera'
120 |
121 | # # Topics
122 | # camera_graph._node_namespace = 'eef_camera'
123 | # camera_graph._rgb_topic = 'image_raw'
124 | # camera_graph._depth_topic = 'image_depth'
125 |
126 | # param_check = camera_graph._check_params()
127 | # if param_check:
128 | # print('Creating Articualtion Graph')
129 | # camera_graph.make_graph()
130 | # else:
131 | # carb.log_error('Check Articualtion Graph parameters')
132 |
133 | # simulation_app.update()
134 |
135 | # Create Tf Action Graph
136 | # You can add any prim_path to the following list to publish their tf with respect to /World
137 | # tf_target_prims = [
138 | # CAMERA_PRIM,
139 | # ]
140 |
141 | # TF_GRAPH_PATH = '/World/Graphs/Transforms'
142 | # tf_graph = Ros2TfPubGraph()
143 | # tf_graph._og_path = TF_GRAPH_PATH
144 |
145 | # param_check = tf_graph._check_params()
146 | # if param_check:
147 | # print('Creating Transforms Graph')
148 | # tf_graph.make_graph()
149 | # else:
150 | # carb.log_error('Check Transforms Graph parameters')
151 |
152 | # set_target_prims(
153 | # primPath=TF_GRAPH_PATH + '/PublisherTF',
154 | # inputName='inputs:targetPrims',
155 | # targetPrimPaths=tf_target_prims,
156 | # )
157 |
158 | # simulation_app.update()
159 |
160 | # Create Articulation Action Graph
161 | robot_graph = Ros2JointStatesGraph()
162 | robot_graph._og_path = '/World/Graphs/Articulation'
163 | robot_graph._art_root_path = ROBOT_PRIM + '/root_joint'
164 | robot_graph._publisher = True
165 | robot_graph._pub_topic = 'isaac_joint_states'
166 | robot_graph._subscriber = True
167 | robot_graph._sub_topic = 'isaac_joint_commands'
168 |
169 | param_check = robot_graph._check_params()
170 | if param_check:
171 | print('Creating Articualtion Graph')
172 | robot_graph.make_graph()
173 | else:
174 | carb.log_error('Check Articualtion Graph parameters')
175 | simulation_app.update()
176 |
177 | simulation_context.play()
178 | while simulation_app.is_running():
179 | simulation_context.step(render=True)
180 |
181 | simulation_context.stop()
182 | simulation_app.close()
183 |
--------------------------------------------------------------------------------