Elephant Robotics myAGV 2023 · ROS2 · Python 3.10+ · Ubuntu 20.04+
A 4-phase robotics lab covering SLAM, autonomous navigation, LLM-powered task planning, and cobot-assisted delivery.
| Guide | Description |
|---|---|
| lab_guide/LAB_GUIDE_DAY2.md | Day 2 — simulation exercises for Phases 2 & 3 |
| lab_guide/DAY2_REAL_ROBOT_GUIDE.md | Day 2 — sim-to-real instructions for Phases 2 & 3 |
myagv_lab/
├── myagv_lab/
│ ├── sim_layer.py ← Unified sim/real abstraction layer
│ ├── phase1_slam/
│ │ └── slam_node.py ← Occupancy-grid SLAM + exploration
│ ├── phase2_nav/
│ │ ├── nav_node.py ← NavigationManager + SIM_WAYPOINTS/REAL_WAYPOINTS (sim + Nav2)
│ │ └── nav_viz.py ← Live matplotlib map + trail visualiser
│ ├── phase3_human_cobot/
│ │ ├── human_cobot.py ← HumanCobot — drop-in for the cobot arm
│ │ ├── human_cobot_manager.py ← Full LLM-PDDL pipeline, human at load/unload
│ │ └── human_cobot_viz.py ← Two-panel live visualiser
│ └── phase4_delivery/
│ ├── mission_manager.py ← Top-level LLM-PDDL pipeline orchestrator
│ ├── mission_viz.py ← Two-panel live mission visualiser
│ ├── cobot_interface.py ← ROS2 node for real cobot arm
│ └── pddl_planner/
│ ├── domain.py ← PDDL domain (single source of truth)
│ ├── llm_translator.py ← NL → PDDL via DeepSeek API
│ ├── pddl_solver.py ← pyperplan wrapper → list[PlanStep]
│ └── primitive_executor.py ← PlanStep → robot primitives
├── langfuse_client/
│ ├── client.py ← Langfuse-instrumented LLM client
│ └── students.py ← Student roster + validation
├── config/
│ ├── slam_params.yaml ← slam_toolbox config (real robot)
│ └── nav2_params.yaml ← Nav2 config (real robot)
├── launch/
│ ├── slam_launch.py ← Phase 1 real-robot launch
│ ├── nav2_launch.py ← Phase 2 real-robot launch
│ ├── human_cobot_launch.py ← Phase 3 real-robot launch
│ └── full_demo_launch.py ← Phase 4 full demo launch
├── maps/ ← Saved maps (generated by Phase 1)
├── lab_guide/
│ ├── LAB_GUIDE_DAY2.md ← Day 2 simulation exercises
│ └── DAY2_REAL_ROBOT_GUIDE.md ← Day 2 sim-to-real instructions
├── tests/
│ └── test_all_phases.py ← Pytest suite (sim only, 54 tests)
├── Dockerfile ← sim and robot build targets
├── docker-compose.yml ← sim / test / myagv_slam services
├── .env.example ← API key template (copy to .env)
├── requirements.txt
├── run_tests.sh ← Test runner (uses project venv)
├── pytest.ini
├── setup.py
└── package.xml
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtPhases 3 and 4 call the DeepSeek LLM API via Langfuse for observability. You need four shared keys from the instructor plus your own student ID.
Add the following to ~/.bashrc so they persist across sessions:
# --- myAGV Lab API keys ---
export STUDENT_ID="yourlastname" # your last name, lowercase
export DEEPSEEK_API_KEY="sk-..." # provided by instructor
export LANGFUSE_PUBLIC_KEY="pk-lf-..." # provided by instructor
export LANGFUSE_SECRET_KEY="sk-lf-..." # provided by instructor
export LANGFUSE_BASE_URL="https://cloud.langfuse.com"Reload and verify:
source ~/.bashrc
echo "Student : $STUDENT_ID"
echo "DeepSeek : ${DEEPSEEK_API_KEY:0:8}..."
echo "Langfuse : ${LANGFUSE_BASE_URL}"Phases 1 and 2 require no API keys. Phases 3 and 4 also work without them by passing
--fallbackto skip the LLM step.
# 1. Activate the virtual environment
source .venv/bin/activate
# 2. Phase 1 — SLAM
python3 -m myagv_lab.phase1_slam.slam_node --sim
# 3. Phase 2 — Navigation (interactive)
python3 -m myagv_lab.phase2_nav.nav_node --sim
# 4. Phase 3 — Human-in-the-loop delivery (no API key needed)
python3 -m myagv_lab.phase3_human_cobot.human_cobot_manager --sim --fallback
# With live visualiser
python3 -m myagv_lab.phase3_human_cobot.human_cobot_manager --sim --fallback --viz
# With LLM translation (requires STUDENT_ID + API keys)
python3 -m myagv_lab.phase3_human_cobot.human_cobot_manager --sim \
--task "Deliver package_A to the delivery area and return home."
# 5. Phase 4 — Full LLM-PDDL pipeline (no API key needed)
python3 -m myagv_lab.phase4_delivery.mission_manager --sim --fallback
# With live visualiser
python3 -m myagv_lab.phase4_delivery.mission_manager --sim --fallback --viz
# With LLM translation (requires STUDENT_ID + API keys)
python3 -m myagv_lab.phase4_delivery.mission_manager --sim \
--task "Deliver package_A to the delivery area and return home."
# 6. Run all tests
./run_tests.sh tests/test_all_phases.py -vSame full LLM → PDDL → pyperplan pipeline as Phase 4, but load-package and
deliver-package steps pause for a human helper instead of commanding the cobot arm.
Natural language task
│
▼ llm_translator.py (DeepSeek API)
PDDL domain + problem
│
▼ pddl_solver.py (pyperplan BFS / A*)
[navigate, load-package, deliver-package, …]
│
▼ primitive_executor.py ← HumanCobot
AGV navigation (autonomous) + ★ human prompts at load/unload steps
When load-package fires:
★ HUMAN ACTION REQUIRED
Please place package_A onto the AGV platform at the loading area.
Press Enter when done…
In Phase 4 these steps are replaced by automated cobot arm commands.
Fallback scenarios (no API key needed):
python3 -m myagv_lab.phase3_human_cobot.human_cobot_manager --sim --fallback --scenario deliver_A
python3 -m myagv_lab.phase3_human_cobot.human_cobot_manager --sim --fallback --scenario deliver_AB
python3 -m myagv_lab.phase3_human_cobot.human_cobot_manager --sim --fallback --scenario recharge_then_deliverpython3 -m myagv_lab.phase4_delivery.mission_manager --sim --fallback --scenario deliver_A
python3 -m myagv_lab.phase4_delivery.mission_manager --sim --fallback --scenario deliver_AB
python3 -m myagv_lab.phase4_delivery.mission_manager --sim --fallback --scenario recharge_then_deliverStudent types a task in natural language
│
▼ llm_translator.py (DeepSeek API via Langfuse)
PDDL domain + problem
│
▼ pddl_solver.py (pyperplan BFS / A*)
[navigate, load-package, deliver-package, …]
│
▼ primitive_executor.py
Nav2 goals + /cobot_command topics
│
▼
Physical robot motion
The real myAGV 2023 runs inside a Docker container (myagv_slam) with ROS2 Galactic.
See lab_guide/DAY2_REAL_ROBOT_GUIDE.md for
the full step-by-step instructions.
Quick reference:
| Step | Action |
|---|---|
| 1 | SSH into the robot: ssh ubuntu@<robot-IP> |
| 2 | Enter the container: sudo docker exec -it myagv_slam bash |
| 3 | Source: source /opt/ros/galactic/setup.bash && source /ws/install/setup.bash |
| 4 | Phase 1: ros2 launch myagv_lab slam_launch.py |
| 5 | Phase 2: ros2 launch myagv_lab nav2_launch.py (+ set 2D Pose Estimate in RViz2) |
| 6 | Phase 3: ros2 launch myagv_lab human_cobot_launch.py use_fallback:=true |
| 7 | Phase 4: ros2 launch myagv_lab full_demo_launch.py use_fallback:=true |
Update REAL_WAYPOINTS in phase2_nav/nav_node.py with the real lab coordinates
(read from RViz2 using "Publish Point" after Phase 1) — leave SIM_WAYPOINTS
untouched, since the sim tests depend on it.
| Variable | Required for | Description |
|---|---|---|
MYAGV_USE_SIM |
All phases | 1 = simulation (default), 0 = real ROS2 robot |
STUDENT_ID |
Phases 3 & 4 LLM | Your last name in lowercase — tags your traces in Langfuse |
DEEPSEEK_API_KEY |
Phases 3 & 4 LLM | Shared DeepSeek API key (instructor provides) |
LANGFUSE_PUBLIC_KEY |
Phases 3 & 4 LLM | Shared Langfuse public key (instructor provides) |
LANGFUSE_SECRET_KEY |
Phases 3 & 4 LLM | Shared Langfuse secret key (instructor provides) |
LANGFUSE_BASE_URL |
Phases 3 & 4 LLM | Langfuse server URL (instructor provides) |
LANGFUSE_HOSTis accepted as an alias forLANGFUSE_BASE_URL.
Phase 2
- Add a new waypoint
"inspection_area"toSIM_WAYPOINTSinnav_node.pyand navigate to it. - Write a scripted route that visits 4 waypoints in sequence.
- Test graceful failure: try navigating to an unknown location and observe the error.
Phase 3
- Run all three fallback scenarios; compare the plans printed in the terminal.
- Explore the PDDL domain in
pddl_planner/domain.py— what are the preconditions forload-package? - Add
print()statements between the pipeline stages to trace data transformations. - Try your own task description (LLM mode):
--task "..."and observe the generated plan.
Phase 4
- Trace the pipeline — add
print()between each stage; observe data transformations. - Modify the task — try different natural language descriptions; observe how the PDDL problem changes.
- Add a new location — add
"inspection_area"toSIM_WAYPOINTSand the PDDL domain. - Extend the domain — add a new
inspectaction todomain.pyand a handler inprimitive_executor.py. - Failure handling — make
navigate()retry once on failure. - Real deployment — follow the Sim-to-Real Transition table above.
A prebuilt Docker image provides a consistent environment on any laptop and matches the on-robot container structure.
| Service | Target | ROS2 | Use case |
|---|---|---|---|
sim |
sim |
No | Student laptops — simulation only |
test |
sim |
No | CI — runs the full pytest suite headlessly |
myagv_slam |
robot |
ROS2 Galactic | Real myAGV 2023 (matches on-robot container name) |
cp .env.example .env
nano .env # fill in STUDENT_ID and API keysdocker compose build sim
docker compose run --rm sim # shell
docker compose run --rm sim python3 -m myagv_lab.phase2_nav.nav_node --sim # Phase 2
docker compose run --rm sim python3 -m myagv_lab.phase3_human_cobot.human_cobot_manager --sim --fallback # Phase 3
docker compose run --rm test # all testsMatplotlib GUI on Linux: works automatically via X11 forwarding. On macOS: run
xhost +localhostfirst and setDISPLAY=host.docker.internal:0in.env.
docker compose build myagv_slam # build (first time only)
docker compose run --rm myagv_slam # interactive shell
docker compose run --rm myagv_slam \
ros2 launch myagv_lab human_cobot_launch.py use_fallback:=true # Phase 3
docker compose run --rm myagv_slam \
ros2 launch myagv_lab full_demo_launch.py use_fallback:=true # Phase 4Default
ROS_DISTRO=galactic. For newer setups:--build-arg ROS_DISTRO=lyrical.
docker buildx build --platform linux/amd64,linux/arm64 --target sim -t myagv-lab:sim --push .