Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 31 additions & 10 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
ARG BASE_IMAGE=ros:jazzy-perception
FROM ${BASE_IMAGE}

# Build arguments for the new user.
ARG USER_NAME=dev
ARG USER_ID=1000
ARG GROUP_ID=1000

# Remove the default "ubuntu" user so that UID 1000 is free.
RUN userdel -r ubuntu || true

Expand All @@ -11,7 +16,15 @@ ENV QT_X11_NO_MITSHM=1
ENV TERM=xterm-256color

# Install common APT packages, then conditionally install Mapviz on x86.
RUN apt update && \
RUN set -e \
&& apt update -o Acquire::AllowInsecureRepositories=true || true \
&& apt install -y --no-install-recommends curl gnupg ca-certificates \
&& curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key \
| gpg --dearmor --yes --batch --no-tty \
--output /usr/share/keyrings/ros2-latest-archive-keyring.gpg \
&& cp /usr/share/keyrings/ros2-latest-archive-keyring.gpg \
/usr/share/keyrings/ros-archive-keyring.gpg \
&& apt update && \
apt install -y \
libasio-dev \
apt-utils \
Expand All @@ -28,15 +41,13 @@ RUN apt update && \
iproute2 \
can-utils \
net-tools \
python3-tk \
python3-matplotlib \
python3-geopandas \
python3-pyproj \
ros-jazzy-xacro \
ros-jazzy-navigation2 \
ros-jazzy-nav2-bringup \
ros-jazzy-teleop-twist-keyboard \
ros-jazzy-launch-xml \
python3-venv \
python3-pip \
sudo && \
\
# Detect device architecture.
Expand All @@ -55,11 +66,6 @@ RUN apt update && \
# Clean up apt cache.
rm -rf /var/lib/apt/lists/*

# Build arguments for the new user.
ARG USER_NAME=dev
ARG USER_ID=1000
ARG GROUP_ID=1000

# Create a new group and user with the specified UID/GID, and grant passwordless sudo.
RUN groupadd --gid ${GROUP_ID} ${USER_NAME} || echo "Group ${USER_NAME} already exists" && \
useradd --uid ${USER_ID} --gid ${GROUP_ID} --shell /bin/bash --create-home ${USER_NAME} && \
Expand All @@ -73,6 +79,16 @@ RUN echo "alias ls='ls --color=auto'" >> /etc/bash.bashrc && \

# Switch to the new "dev" user.
USER ${USER_NAME}
WORKDIR /home/${USER_NAME}

# Create virtual environment for Python packages
RUN python3 -m venv nav2_env && \
/bin/bash -c "source nav2_env/bin/activate && pip install --upgrade pip && pip install 'setuptools<69' 'pip<24'"

# Install Python packages from requirements.txt
COPY requirements.txt ./
RUN /bin/bash -c "source nav2_env/bin/activate && \
pip install -r requirements.txt"

# Make interactive shells auto-source ROS and ROS workspace
RUN echo 'source /opt/ros/jazzy/setup.bash' >> ~/.bashrc \
Expand All @@ -81,5 +97,10 @@ RUN echo 'source /opt/ros/jazzy/setup.bash' >> ~/.bashrc \
# Ensure the user's local bin is in PATH.
ENV PATH=/home/${USER_NAME}/.local/bin:${PATH}

# Expose venv to every subsequent shell
ENV VIRTUAL_ENV=/home/${USER_NAME}/nav2_env
ENV PATH=/home/${USER_NAME}/nav2_env/bin:$PATH
ENV PYTHONPATH=/home/dev/nav2_env/lib/python3.12/site-packages:$PYTHONPATH

# Set DISPLAY for X11 forwarding.
ENV DISPLAY=:0
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"ms-toolsai.jupyter"
],
"settings": {
"python.pythonPath": "/usr/bin/python3"
"python.pythonPath": "/home/dev/nav2_env/bin/python"
},
"remoteUser": "dev",
"updateRemoteUserUID": false
Expand Down
56 changes: 36 additions & 20 deletions .devcontainer/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: '3.8'

services:
vrtk:
services:
builder:
build:
context: .
dockerfile: Dockerfile
Expand All @@ -10,31 +10,47 @@ services:
USER_ID: "${HOST_UID}"
GROUP_ID: "${HOST_GID}"
BASE_IMAGE: "ros:jazzy-perception"
container_name: ros2_vrtk_container
image: ros2_dev:latest
command: /bin/true # Do nothing, just build
restart: "no"

runner:
image: ros2_dev:latest
container_name: ros2_runner
volumes:
- ..:/home/dev/ros_ws
- /tmp/.X11-unix:/tmp/.X11-unix
working_dir: /home/dev/ros_ws
network_mode: host
environment:
- DISPLAY=${DISPLAY}
restart: always
command: >
bash -lc "
set -e && \
source /home/dev/nav2_env/bin/activate && \
source /opt/ros/jazzy/setup.bash && \
colcon build --symlink-install --cmake-args -DBUILD_TESTING=OFF && \
source install/setup.bash && \
exec ros2 launch nav2_tutorial all_nodes.launch.py
"
restart: unless-stopped
stdin_open: true
tty: true
entrypoint:
- bash
- -lc
- |
set -e

# source ROS
source /opt/ros/jazzy/setup.bash

# ensure the workspace is built or latest build is sourced
colcon build --cmake-args -DBUILD_TESTING=OFF

# source the workspace
source install/setup.bash

# launch everything in one go
exec ros2 launch nav2_tutorial all_nodes.launch.py
vrtk:
build:
context: ..
dockerfile: Dockerfile
args:
USER_NAME: "dev"
USER_ID: "${HOST_UID}"
GROUP_ID: "${HOST_GID}"
BASE_IMAGE: "ros:jazzy-perception"
volumes:
- ..:/home/dev/ros_ws
- /tmp/.X11-unix:/tmp/.X11-unix
network_mode: host
environment:
- DISPLAY=${DISPLAY}
stdin_open: true
tty: true
11 changes: 11 additions & 0 deletions .devcontainer/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
catkin_pkg
empy==3.3.4
lark
numpy
pyyaml
tk
matplotlib
geopandas
pyproj
dash
dash-bootstrap-components
158 changes: 143 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ converter:
Build the ROS2 workspace.
```bash
source /opt/ros/jazzy/setup.bash
colcon build --cmake-args -DBUILD_TESTING=OFF
colcon build --symlink-install --cmake-args -DBUILD_TESTING=OFF
```


Expand All @@ -93,9 +93,9 @@ sudo ip link set can0 up type can bitrate 500000
candump can0
```

Alternatively, the user can also directly execute the provided script start_can.sh:
Alternatively, the user can also directly execute the provided script setup-can.sh:
```bash
sudo ./scripts/start_can.sh
sudo ./scripts/setup-can.sh
```

Example output from the can0 port:
Expand Down Expand Up @@ -177,7 +177,7 @@ waypoints:
### Manual Logger (`gps_keylogger.py`)

```bash
ros2 run nav2_tutorial gps_keylogger.py [optional_output.yaml]
ros2 run nav2_tutorial gps_keylogger [optional_output.yaml]
```

* Press `'f'` to log a waypoint
Expand All @@ -190,7 +190,7 @@ Waypoints are saved immediately and safely to disk.
### Periodic Logger (`gps_periodic_logger.py`)

```bash
ros2 run nav2_tutorial gps_periodic_logger.py [optional_output.yaml] -i 0.2
ros2 run nav2_tutorial gps_periodic_logger [optional_output.yaml] -i 0.2
```

* Logs waypoints automatically every 0.2s (default)
Expand All @@ -209,12 +209,12 @@ nav2_tutorial/src/nav2_tutorial/trajectories/gps_waypoints_<timestamp>.yaml

## 📈 Visualizing Logged Trajectories

To quickly visualize GPS waypoint logs, use the `visualize_gps_yaml.py` script:
To quickly visualize GPS waypoint logs, use the `visualize_gps` script:

### Example:
```bash
python3 utils/visualize_gps_yaml.py path/to/gps_waypoints.yaml # simple 2D plot
python3 utils/visualize_gps_yaml.py path/to/gps_waypoints.yaml --map # map overlay (if supported)
ros2 run nav2_tutorial visualize_gps path/to/gps_waypoints.yaml # simple 2D plot
ros2 run nav2_tutorial visualize_gps path/to/gps_waypoints.yaml --map # map overlay (if supported)
```

### Requirements for Map Overlay:
Expand All @@ -235,19 +235,19 @@ ros2 launch nav2_tutorial gps_waypoint_follower.launch.py
```
Note: You can use the `all_nodes.launch.py` file to achieve this. For navigation, this repository provides three waypoint following methods: Precise, Smooth, and Interactive. We can only choose one method each time we execute it.

* Precise GPS Waypoint Follower
The `precise_wp_follower` script streams all the logged points from a YAML file and makes the robot follow them point-by-point, stopping at every waypoint when the accuracy threshold is met.
* Smooth GPS Waypoint Follower (Recommended):
The `smooth_wp_follower` script streams all the logged points from a YAML file and divides them in segments, pre-computing a smooth trajectory for the robot to follow. This ensure constant speed throughout the waypoint following task.
```bash
ros2 run nav2_tutorial precise_wp_follower <optional: /path/to/file> <optional: --last> <optional: --reverse>
ros2 run nav2_tutorial smooth_wp_follower <optional: /path/to/file> <optional: --last> <optional: --reverse>
```

* Smooth GPS Waypoint Follower
The `smooth_wp_follower` script streams all the logged points from a YAML file and divides them in segments, pre-computing a smooth trajectory for the robot to follow. This ensure constant speed throughout the waypoint following task.
* Precise GPS Waypoint Follower:
The `precise_wp_follower` script streams all the logged points from a YAML file and makes the robot follow them point-by-point, stopping at every waypoint when the accuracy threshold is met. Note: Avoid combining this method with the periodic logger at high rates. Logging too frequently creates waypoints that are very close together, which can cause the robot to stop and start excessively at each point, leading to unstable behavior.
```bash
ros2 run nav2_tutorial smooth_wp_follower <optional: /path/to/file> <optional: --last> <optional: --reverse>
ros2 run nav2_tutorial precise_wp_follower <optional: /path/to/file> <optional: --last> <optional: --reverse>
```

* Interactive GPS Waypoint Follower
* Interactive GPS Waypoint Follower:
The `interactive_wp_follower` script listens to the mapviz topic for the next waypoint objective.
```bash
ros2 run nav2_tutorial interactive_wp_follower
Expand All @@ -258,3 +258,131 @@ For launching the graphical interface, you can run the following command (note t
ros2 launch nav2_tutorial mapviz.launch.py
```
Or alternatively use the `--mapviz` flag on the `gps_waypoint_follower.launch.py` script.


# Debugging trajectories
To analyze the trajectories performed by the robot and determine if any issues have occured, the user can perform a recording of all topics by running `ros2 bag record --all`. Then, launch RViz2 with the provided configuration file to easily visualize the debug topics containing the waypoint coordinates, the global and local plans, the costmaps, among other things: `ros2 run rviz2 rviz2 -d /home/dev/ros_ws/src/nav2_tutorial/config/nav2_plan_viz.rviz`.


# Dashboard
In addition, this repository provides an interactive dashboard to control all of these processes. It can be started as follows:
```bash
ros2 run nav2_tutorial dashboard
```

# Recommendations
We suggest employing tmux for controlling the robot via SSH, as intermittent disconnections may kill the running process. Next you'll find some basic steps to get familiar with tmux:

### Installing tmux
To install tmux on your device, please run the following commands:
```bash
sudo apt update && apt install tmux
```

### Start a tmux session
After you SSH into the device (using a terminal or VSCode's Remote-SSH), start a new tmux session as:
```bash
tmux new -s mysession
```
where "mysession" is simply the name given to this tmux session. Note that the prompt will change slightly once inside the tmux session. Inside, you can interact with it as a normal terminal and run any process.

### Detach from tmux
At any point, the user can "detach" from tmux and the process will keep running in the background. To do this, please press: 'Ctrl + b' (release both) then press 'd'. This detaches from the session and brings you back to the normal shell while the program continues running.

### Reconnect later
If the SSH connection drops or you log in again later, list running tmux sessions as:
```bash
tmux ls
```

The user will be prompted with something like:
```bash
mysession: 1 windows (created Thu Jul 3 14:32:51 2025) [80x24]
```

To reattach to the tmux session, simply use:
```bash
tmux attach -t mysession
```
You’re now back inside the process, exactly where you left off.


# 📖 Helper Scripts Overview

This project uses a set of bash scripts to manage the Docker ROS2 environment. Here’s what each script does:

---

### 🛠 **`build-container.sh`**

* **Purpose:** Builds the Docker image using the `builder` service.
* **What it does:**

* Stops any running containers.
* Rebuilds the Docker image from the Dockerfile.
* **Usage:**

```bash
bash scripts/build-container.sh
```

---

### 🚀 **`start-container.sh`**

* **Purpose:** Starts the `runner` container and optionally attaches to its shell.
* **What it does:**

* Starts the `runner` container in detached mode (keeps `all_nodes.launch.py` running).
* Drops you into a shell inside the container automatically.
* **Usage:**

```bash
bash scripts/start-container.sh
```

This will:

* Start `all_nodes.launch.py` (if not already running).
* Attach you to a shell inside the container.

---

### 🔄 **`restart-container.sh`**

* **Purpose:** Restarts the `runner` container cleanly.
* **What it does:**

* Stops the `runner` container.
* Starts it again, automatically relaunching `all_nodes.launch.py`.
* Attaches to a shell in the container after restart.
* **Usage:**

```bash
bash scripts/restart-container.sh
```

---

### 📜 **`logs-container.sh`**

* **Purpose:** Streams logs from the `runner` container.
* **What it does:**

* Shows all output from `all_nodes.launch.py` and other processes in the container.
* **Usage:**

```bash
bash scripts/logs-container.sh
```

---

## 📝 Notes

* The `runner` container is designed to persist and automatically restart on system reboot.
* To get a shell inside the container without restarting it:

```bash
docker compose exec runner bash
```
Loading