Setup & Build
Every piece of manipulation code runs inside a Docker container managed by the top-level ./run.sh script in RoBorregos/home2. The host only needs Docker, Git, and, on a CUDA machine, the NVIDIA Container Toolkit.
Do not install ROS / MoveIt / PCL on the host
All dependencies live inside the container. Anything you install on the host risks polluting future runs and confusing teammates. If you need a tool, add it to the Dockerfile.
Prerequisites
- Docker Engine + Docker Compose plugin
- Git
- NVIDIA Container Toolkit (only on CUDA hosts)
- At least 30 GB of free disk for the image and build artifacts
Cloning the repo
Did you forget --recursive?
Many vendored libraries (gpd, vamp, foam, cricket, xarm_ros2, pymoveit2, …) are submodules. If you cloned without --recursive, fix it now:
First run
From the root of the repo:
Behind the scenes:
flowchart LR
A[./run.sh manipulation] --> B{Detect env}
B -->|nvidia-smi works| C[cuda]
B -->|tegra release file| D[l4t<br/>Jetson]
B -->|else| E[cpu]
C --> F[Build base image<br/>roborregos/home2:cuda_base]
D --> F
E --> F
F --> G[Build manipulation image<br/>roborregos/home2:manipulation-cuda]
G --> H[docker compose up<br/>home2-manipulation]
H --> I[docker exec, shell inside container]
Inside the container, the entry script sources ROS, the cached frida_interfaces, GPD, and the CycloneDDS profile, then drops you at /workspace. Subsequent runs reuse the existing container instantly.
Bind mounts
Your local working tree is bind-mounted into /workspace/src. Edits on the host are visible immediately inside the container, Python changes need no rebuild.
Rebuilding
Inside the container, two equivalents exist:
A. The build alias in docker/manipulation/.bash_aliases:
expands to:
cd /workspace \
&& source frida_interfaces_cache/install/local_setup.bash \
&& colcon build --symlink-install \
--packages-up-to manipulation_general \
--packages-ignore realsense_gazebo_plugin xarm_gazebo frida_interfaces
B. The wider build used by ./run.sh manipulation --build (also includes the IKFast plugin and xarm_utils):
colcon build --symlink-install \
--packages-up-to manipulation_general xarm6_ikfast_plugin xarm_utils \
--packages-ignore realsense_gazebo_plugin xarm_gazebo frida_interfaces
frida_interfaces is built once in a separate stage and mounted at
/workspace/frida_interfaces_cache. That is why we ignore it in both
invocations.
Useful when iterating on one Python file. After the build, source install/setup.bash.
When you edit a file under frida_interfaces/manipulation/, rebuild the
interfaces cache from the host:
Then back inside the manipulation container:
GPD setup
GPD is a C++ library compiled from source. The container handles this on entry through docker/manipulation/setup_gpd.sh:
- If
manipulation/packages/gpd/build/is missing, runscmake .. && make && sudo make install. - If the build dir exists but
/usr/local/include/gpdis missing, recompiles + reinstalls. - Otherwise no-op.
GPD_INSTALL_DIR=/workspace/install/gpd is exported so arm_pkg's CMake finds the library via find_library(GPD_LIB …).
Build fails with Library GPD not found
Force the install manually:
CycloneDDS
We use CycloneDDS instead of FastDDS. There are two profiles:
| Profile | When | File |
|---|---|---|
| Sim / localhost only | Default when running everything on one machine | docker/manipulation/cyclonedds_sim.xml |
| Network (multi-host) | When the dev PC talks to the robot over LAN | cyclonedds.xml (repo root) |
Run once per machine:
Replace <INTERFACE> with the network interface name (eno1, wlp2s0, …). Find it via:
Why the sim profile uses localhost only
The full pick stack spawns 20+ ROS nodes. WiFi multicast loopback is unreliable, so the sim profile forces unicast peers on localhost. It also raises MaxAutoParticipantIndex to 120 because the default of 9 is too small.
Simulation (MuJoCo)
flowchart LR
A[./run.sh simulation] --> B[mujoco container]
B --> C[ros2 launch mujoco_spawn<br/>mujoco_sim_init.launch.py]
C --> D[MuJoCo + ros2_control]
E[./run.sh manipulation] --> F[manipulation container]
F --> G[ros2 launch pick_and_place<br/>use_sim_time:=true]
D <-->|joint states / commands<br/>via /clock| G
Open a second terminal and run:
point_cloud_topic matters
On the real robot the default /point_cloud works. In MuJoCo, use /filtered_cloud (the MoveIt self-filtered topic). Otherwise the cluster picks up parts of the robot's own meshes.
Touching vendored packages
The repo vendors several upstream projects. Treat them like third-party code. The complete list lives in .gitmodules at the repo root.
| Package | Source on main |
Notes |
|---|---|---|
gpd |
Submodule → RoBorregos/gpd (fork of atenpas/gpd). | Compiled by setup_gpd.sh. |
pymoveit2 |
Submodule → RoBorregos/pymoveit2. | Used by the motion planning wrappers. Prefer editing frida_pymoveit2 (in-tree) for FRIDA-specific changes. |
xarm_ros2 |
Submodule → RoBorregos/xarm_ros2 on branch xarm_servicios_estable. |
Fork of xArm-Developer/xarm_ros2 with our patches enabled. Rarely modify; touch only in a dedicated PR. |
mujoco_ros2_control |
Submodule → RoBorregos/mujoco_ros2_control on branch mujoco_servicios_estable. |
Touch only the FRIDA-specific MJCF scene files. |
xarm6_ikfast_plugin |
In-tree (regular package). | Generated IKFast plugin for the xArm 6 (merged in PR #856). Rebuilt only when DH params change. |
frida_pymoveit2 |
In-tree (regular package). | Our derivative of pymoveit2 with xArm-6 group names, IK frames, and named configurations. |
Don't bundle vendored changes with feature work
Vendored-package changes go in dedicated PRs so reviewers can scope them. Mixing them with task-manager edits makes diffs unreadable.
Validation
After your first build, run a quick sanity check:
# 1. ROS sees the workspace
ros2 pkg list | grep pick_and_place
# 2. The interfaces are sourced
ros2 interface show frida_interfaces/action/ManipulationAction
# 3. MoveIt launches cleanly
ros2 launch arm_pkg frida_moveit_config.launch.py show_rviz:=true
If RViz opens with the FRIDA URDF loaded and no red errors in the terminal, you are ready to move on to Running Tasks.
Troubleshooting
Container exits immediately
Run docker logs home2-manipulation to see the failure. The usual cause is a stale build/ from a different env. Run ./run.sh --clean and retry.
Library GPD not found. Check ENV variable GPD_INSTALL_DIR
GPD didn't install. Run . /home/ros/setup_gpd.sh && export GPD_INSTALL_DIR=/workspace/install/gpd, then build.
Could not import frida_interfaces.action
The interfaces cache is stale. On the host run ./run.sh frida_interfaces, then re-enter the manipulation container.
MoveIt: No motion plan found immediately on every plan
The octomap is polluted. Clear it:
Failed to load IKFast plugin
Build the plugin explicitly:
Spawning ros2_controllers timed out
The xArm driver isn't up. Wait until frida_moveit_config.launch.py reports the controllers are loaded before launching pick_and_place.
Sim sees the robot as a giant point-cloud cluster
You're consuming the raw cloud, not the self-filtered one. Add point_cloud_topic:=/filtered_cloud to the launch.
DDS nodes can't discover each other
Confirm CYCLONEDDS_URI points at the right XML:
- Sim / single host →
docker/manipulation/cyclonedds_sim.xml - Multi-host on LAN →
cyclonedds.xml(root)
Stop / clean lifecycle
| Command | Effect |
|---|---|
./run.sh --stop |
Stop containers, keep state. |
./run.sh --down |
Stop and remove containers + networks. |
./run.sh --clean |
Delete build/, log/, install/, and the interfaces cache. |
Once your environment runs cleanly, head to Running Tasks.