A ROS 2 Jazzy simulation of the Clearpath Jackal J100 mobile robot equipped with an AR4 6-DOF robotic arm, a 360° 2D LiDAR, and a custom A* Smooth Planner for autonomous navigation. Runs in Gazebo Harmonic with SLAM Toolbox mapping, AMCL localisation, MoveIt 2 arm control, and Nav2.
Using WSL2, Ubuntu, etc.
- Clone the repository.
- cd into TPR
cd TPR - Run the bringup script
./run.sh - After building (which will take minutes) the main simulation will automatically run.
If building yourself, after it's done, one line launch with:
ros2 launch goal_manager_pkg tpr_full.launch.py
- Overview
- System Requirements
- Repository Structure
- Installation
- Quick Start
- Running the Simulation
- Available Worlds
- Architecture Overview
- Package Breakdown
- Troubleshooting
- Contributing
- License
This workspace provides a complete mobile manipulation and autonomous navigation simulation:
| Component | Technology |
|---|---|
| Physics simulation | Gazebo Harmonic |
| Mobile base | Clearpath Jackal J100 (differential drive) |
| Manipulator | Annin Robotics AR4 6-DOF arm + gripper |
| Sensor | 360° GPU LiDAR → /lidar/scan |
| Mapping | SLAM Toolbox (online async) |
| Localisation | AMCL |
| Navigation | A* Smooth Planner + Pure Pursuit controller |
| Arm planning | MoveIt 2 + OMPL |
| Hardware interface | ros2_control (gz_ros2_control + mock_components) |
| Requirement | Version |
|---|---|
| Ubuntu | 24.04 (Noble) |
| ROS 2 | Jazzy |
| Gazebo | Harmonic |
| Python | 3.12+ |
| RAM | 8 GB minimum, 16 GB recommended |
| GPU | Optional but recommended |
jackal_ar4_ws/src/
├── jackal_ar4_description/ ← Main robot package
│ ├── urdf/
│ │ ├── jackal_ar4.urdf.xacro # Robot URDF + LiDAR + Gazebo plugins
│ │ ├── jackal_ar4.ros2_control.xacro # Hardware interface config
│ │ └── ar4_arm.urdf.xacro # AR4 arm macro
│ └── launch/
│ ├── gazebo.launch.py # Full simulation (Gazebo + controllers + Nav2 + RViz)
│ └── jackal_ar4.launch # RViz-only (no Gazebo, for arm testing)
│
├── jackal_ar4_navigation/ ← Navigation stack
│ ├── config/
│ │ ├── nav2_params.yaml # Nav2 costmap + controller params
│ │ ├── slam_toolbox_mapping_params.yaml
│ │ ├── mapping.rviz # RViz for SLAM mapping
│ │ └── navigation.rviz # RViz for A* navigation
│ ├── maps/
│ │ ├── tpr_map.yaml # Saved map (metadata)
│ │ └── tpr_map.pgm # Saved map (image)
│ ├── launch/
│ │ ├── mapping.launch.py # SLAM-only launch
│ │ └── astar_navigation.launch.py # A* navigation launch
│ └── scripts/
│ └── twist_unstamper.py # Converts TwistStamped → Twist
│
├── jackal_ar4_moveit_config/ ← MoveIt 2 configuration
├── jackal_ar4_worlds/ ← Gazebo SDF worlds
├── jackal_ar4_goals/ ← Goal-sending scripts
├── a_star_smooth_planner/ ← A* planner + smoother + pure pursuit
├── ar4/ ← AR4 arm description + MoveIt
└── clearpath_common/ ← Jackal J100 base description
# ROS 2 Jazzy
sudo apt update && sudo apt install -y locales
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
sudo apt install -y software-properties-common curl
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key \
-o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] \
http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" \
| sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
sudo apt update && sudo apt install -y ros-jazzy-desktop
# Gazebo + navigation packages
sudo apt install -y ros-jazzy-ros-gz
sudo apt install -y ros-jazzy-slam-toolbox
sudo apt install -y ros-jazzy-nav2-bringup
sudo apt install -y ros-jazzy-topic-tools
sudo apt install -y ros-jazzy-teleop-twist-keyboardmkdir -p ~/tpr/jackal_ar4_ws/src
cd ~/tpr/jackal_ar4_ws
git clone https://github.com/Oyefusi-Samuel/TPR.git src/
source /opt/ros/jazzy/setup.bash
sudo rosdep init # first time only
rosdep update
rosdep install --from-paths src --ignore-src -r -y
colcon build --symlink-install
source install/setup.bashAdd to ~/.bashrc:
echo "source ~/tpr/jackal_ar4_ws/install/setup.bash" >> ~/.bashrc# T1 — Launch simulation
ros2 launch jackal_ar4_description gazebo.launch.py world:=tpr
# T2 — Drive with keyboard
ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -r cmd_vel:=/cmd_vel
# T3 — Map the world (while sim is running)
ros2 launch jackal_ar4_navigation mapping.launch.py
# Drive around to build the map, then save:
ros2 run nav2_map_server map_saver_cli -f ~/tpr/jackal_ar4_ws/src/jackal_ar4_navigation/maps/tpr_map
cd ~/tpr/jackal_ar4_ws && colcon build --symlink-install && source install/setup.bash
# T3 — Navigate with A* (after mapping, with sim running)
ros2 launch jackal_ar4_navigation astar_navigation.launch.py
# Use '2D Pose Estimate' then '2D Goal Pose' in RVizros2 launch jackal_ar4_description gazebo.launch.py world:=tprWait ~15–20 s for all nodes to start. Starts: Gazebo, robot, LiDAR, bridges, arm controllers, Nav2, MoveIt, RViz.
# Other world options
ros2 launch jackal_ar4_description gazebo.launch.py world:=room_with_walls
ros2 launch jackal_ar4_description gazebo.launch.py world:=tpr launch_rviz:=falseros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -r cmd_vel:=/cmd_velPress x several times to reduce speed before driving. Use i/, to move forward/backward, j/l to turn.
Run after the simulation is fully started:
ros2 launch jackal_ar4_navigation mapping.launch.pyRViz opens showing the map being built live. Drive around to cover the entire environment. When done:
# Save the map
ros2 run nav2_map_server map_saver_cli \
-f ~/tpr/jackal_ar4_ws/src/jackal_ar4_navigation/maps/tpr_map
# Verify the saved yaml has a relative image path
cat ~/tpr/jackal_ar4_ws/src/jackal_ar4_navigation/maps/tpr_map.yaml
# Should say: image: tpr_map.pgm (NOT an absolute path)
# Rebuild to install the map
cd ~/tpr/jackal_ar4_ws && colcon build --symlink-install && source install/setup.bashCritical: If the saved map looks completely blank (all white with no walls), SLAM didn't capture wall data. This happens when the robot barely moves or the LiDAR data wasn't flowing yet. Remap: kill the mapping launch, relaunch it after the sim is fully up, drive slowly around all walls, then save again.
Run after mapping is done and the simulation is running:
ros2 launch jackal_ar4_navigation astar_navigation.launch.pyRViz opens with: saved map (white=free, black=walls), LiDAR scan (red), AMCL particles, costmap, A* path (orange), smoothed path (green).
To navigate:
- Click 2D Pose Estimate → click on the map where the robot actually is → drag to set heading
- Wait for AMCL particles to converge around the robot (a few seconds)
- Click 2D Goal Pose → click anywhere on white (free) space
- The robot plans and drives autonomously
In the RViz window launched by gazebo.launch.py:
- Open the Motion Planning panel
- Drag the interactive end-effector marker to a target pose
- Click Plan then Execute
The arm moves in both RViz and Gazebo simultaneously.
| World | Description |
|---|---|
empty |
Flat ground plane |
empty_room |
Enclosed empty room |
room_with_walls |
Room with obstacles |
room_with_walls_star |
Star-shaped room |
turtlebot_arena |
Standard benchmark arena |
tpr |
Custom TPR environment |
Gazebo Harmonic
├── diff_drive plugin → /tf (odom→base_link), /odom
├── JointStatePublisher → /wheel_joint_states
└── gpu_lidar sensor → /lidar/scan
│
▼
ros_gz_bridge → ROS topics: /clock /tf /cmd_vel /odom /lidar/scan
│
├── topic_tools relay (/wheel_joint_states → /joint_states)
├── topic_tools relay (/cmd_vel_smoothed → /cmd_vel)
│
├── robot_state_publisher → TF tree (URDF joints)
│
├── ros2_control_node
│ └── arm_controller + ar_gripper_controller
│ └── MoveIt 2 move_group
│
└── Nav2 stack
├── velocity_smoother (/cmd_vel → /cmd_vel_smoothed)
└── controller_server → /cmd_vel
Navigation (A* mode):
/lidar/scan ──► SLAM Toolbox ──► tpr_map.yaml (offline)
/lidar/scan ──► AMCL ──────────► /amcl_pose
/lidar/scan ──► Costmap ────────► /costmap
/goal_pose ──► A* Planner ─────► /a_star/path
↓
A* Smoother ──────► /a_star/path/smooth
↓
Pure Pursuit ──────► /cmd_vel_stamped
↓
twist_unstamper ───► /cmd_vel ──► Gazebo diff_drive
| Package | Role |
|---|---|
jackal_ar4_description |
Robot URDF, LiDAR, Gazebo plugins, all launch files |
jackal_ar4_navigation |
Nav2 params, SLAM params, mapping/navigation launches, RViz configs |
jackal_ar4_moveit_config |
MoveIt SRDF, kinematics, OMPL config, controller mappings |
jackal_ar4_goals |
Python scripts for sending arm and nav goals programmatically |
jackal_ar4_worlds |
Gazebo SDF worlds (tpr, room_with_walls, etc.) |
a_star_smooth_planner |
A* planner (Python), path smoother (C++), pure pursuit (C++) |
ar4_description |
AR4 arm URDF macros + STL meshes |
clearpath_platform_description |
Jackal J100 base URDF macros + meshes |
| Problem | Fix |
|---|---|
| Controllers not active | Relaunch — spawners start at t=10–11s and may race on slow machines |
| Teleop not driving robot | Verify with ros2 topic echo /cmd_vel --once. The cmd_vel_relay must be running |
| Robot bouncing in RViz | map_to_odom static publisher must NOT have use_sim_time=True — already fixed |
| Meshes missing in Gazebo | Only use gazebo.launch.py — it sets GZ_SIM_RESOURCE_PATH automatically |
| Problem | Fix |
|---|---|
/lidar/scan not publishing |
Check bridge: ros2 topic hz /lidar/scan |
| No LiDAR in RViz | Add → LaserScan → /lidar/scan, Fixed Frame = lidar or map |
| Problem | Fix |
|---|---|
| SLAM exits immediately | use_lifecycle_manager: false must be in slam_toolbox_mapping_params.yaml |
| Map blank after saving | Remap — the robot didn't move enough or LiDAR wasn't flowing. Drive all walls |
| Map not visible in RViz | Map display must use Durability Policy: Transient Local — mapping.rviz already has this |
| Problem | Fix |
|---|---|
Failed to change state for node: map_server |
Race condition — lifecycle_manager now has 3s delay. If still fails, increase delay |
No map received! |
Either map is blank (remap) or obstacle_layer scan topic is wrong (/lidar/scan not /scan) |
| Map shows with no walls | Map was saved blank — remap the TPR world |
| AMCL won't converge | Use 2D Pose Estimate to give the robot its initial position on the map |
| GLSL shader error in RViz | Use Binary representation: true + Color Scheme: map — already set in navigation.rviz |
# Dependencies missing
sudo apt install -y ros-jazzy-slam-toolbox ros-jazzy-nav2-bringup ros-jazzy-topic-tools
rosdep install --from-paths src --ignore-src -r -y
# Always rebuild after changing config files
cd ~/tpr/jackal_ar4_ws && colcon build --symlink-install && source install/setup.bash- Fork → branch → change → test → PR
- Test with:
ros2 launch jackal_ar4_description gazebo.launch.py - Commit style: Conventional Commits (
feat:,fix:,docs:) - New worlds → add
.sdftojackal_ar4_worlds/worlds/ - New dependencies → add to
package.xml, never rawapt install
MIT License — see LICENSE.
Third-party components retain their original licenses:
ar4_description/ar4_moveit_config→src/ar4/ar4_description/LICENSEclearpath_common→src/clearpath_common/LICENSEa_star_smooth_planner→src/a_star_smooth_planner/LICENSE