Site-specific instructions and updates for KAIST Mobile Robots
This page collects configuration details and ongoing updates for the robot fleet at KAIST. Use the general Setup and Operation guides for baseline procedures; this page covers what is specific to your deployment.
Quick reference for day-to-day operation. For full detail, see Running the Robots. If Wi-Fi or IPs change, follow Updating Wi-Fi (IP Addresses) and update the table below.
| Device | IP address |
|---|---|
| Central machine | 192.168.68.52 |
| Robot 1 | 192.168.68.54 |
| Robot 2 | — |
| Robot 3 | 192.168.68.53 |
| Robot 4 | 192.168.68.51 |
Jetson login: username/password = jetson.
cd ~/dds_robot_platform/dds conda activate dds ./start_scripts.sh
Alternate: use the robot display app (Start Docker / Start ROS) or SSH (commands below). Full options: Running the Robots.
ssh jetson@<robot_ip>
Example: ssh jetson@192.168.68.54 (Robot 1).
docker exec -it ros_noetic bash
roslaunch mattbot_bringup kaist.launch
In the roslaunch terminal: Ctrl+C. Then exit to leave the container, or from the Jetson host:
sudo docker stop ros_noetic
ifconfig
Look under wlan0 for inet.
sudo shutdown now -h
cd ~/dds_robot_platform/dds ./stop_scripts.sh
Or press Ctrl+C in the terminal where ./start_scripts.sh is running.
Follow these steps for the KAIST mobile robot configuration.
On your workstation, launch the DDS Robot GUI (from the Desktop (Stanford Logo)). Also, start the Docker Desktop application (also on desktop).
After Docker Desktop is running, click the Local Stack Docker Start button in the Robot GUI (bottom right of the application). If you need sensor data capture, use the Capture Start button directly under it (see step 3).
Open the windows Terminal application. At the top of the application click the drop down button and open a new "Ubuntu WSL 1" tab. (Alternatively, you can directly open an Ubuntu WSL 1 terminal by searching for "Ubuntu WSL 1" in the Windows search bar.)
Important: This Ubuntu terminal should be WSL1, make sure you do not use the plan Ubuntu Terminal (which is WSL2)!
Navigate to the dds_robot_platform/dds/ repository, activate the dds conda environment, and run ./start_scripts.sh
cd dds_robot_platform/dds conda activate dds ./start_scripts.sh
NOTE: This step is only necessary if you care about storing all the sensor data for later analysis (or for image testing).
In the Robot GUI Local Stack panel (bottom right), click the Capture Start button directly under the Local Stack Docker Start button. This starts the dds_robot_platform/capture Docker Compose stack.
The central machine setup is now complete. You can click the "Retry loading map" button in the Robot GUI to see the map. If robots are online, they should become available in the right hand side of the GUI.
Power on the robots, wait a few minutes, the display screen should power on and you should see the desktop. Turn on the switch for the microcontroller and motors (the switch at the bottom), the red LED should turn on to signify the electronics are on.
I have tried to make this as simple as possible and you can do this in multiple ways: by SSHing into the robot from a terminal, from the Robot GUI, or from the display app on the robot itself. Here I will walk you through all possible options.
SSH into the Jetson (username/password = jetson):
ssh jetson@<jetson_ip_address>
Then start the docker container:
sudo docker run --runtime nvidia --network=host -v ~/workspaces/catkin_ws:/workspace/catkin_ws -v ~/gemini_api:/gemini_code -v /dev/bus/usb:/dev/bus/usb -v /dev/video0:/dev/video0 -v /dev/video1:/dev/video1 -it --device=/dev/ttyUSB0 --device=/dev/spidev0.0 --rm --privileged --name ros_noetic ghcr.io/satomm1/ml_ros:latest bash -lc 'python3 /workspace/catkin_ws/src/startup_script.py & exec bash'
Then start the ROS node via the kaist.launch file:
roslaunch mattbot_bringup kaist.launch
Select the robot you want to start from the "saved addresses" dropdown under "Robot Startup" on the right hand side column. If the robot is powered on and the Robot GUI detects the robot, the little dot next to the robot should be yellow (not red).
Click the "Start Docker" button for the selected robot to start the required docker containers on the robots.
Click the "KAIST" checkbox to enable startup according to KAIST configuration.
Click the "Start ROS" button to start the ROS node. The LiDAR should start spinning and the robot will show up in the Robot Desktop GUI.
The robot display app should automatically start upon booting up. If it doesn't, or you accidentally exit out, double tap the "Start-Mattbot-Display.sh" icon on the Desktop.
First, start Docker. There should be a button that says "Start Docker". This may take a few seconds and a success message will print on the screen.
Second, start the ROS node. Simply click the "Start ROS" button in the display. The LiDAR should start spinning and the robot will show up in the Robot Desktop GUI.
After startup is complete and robots appear in the DDS Robot GUI, use the map on the central operator workstation to localize each robot and send navigation goals.
In the Robot GUI, click the robot you want to operate in the status panel (left column). The selected robot is used for all map clicks until you choose a different one.
In the map toolbar, select Set Initial Position. This tells the GUI you are seeding AMCL localization, not sending a navigation goal.
Click and drag on the map to indicate the robot’s actual location and orientation. This only needs to be in roughly the correct location. The robot will perform a spinning sequence to localize. The robot icon on the map should jump to that pose.
Make sure the robot’s position on the map matches where it is in the room. Navigation goals will fail or behave incorrectly if localization is wrong.
In the map toolbar, select Set Robot Goal.
With the correct robot selected, click and drag to set the goal location and orientation on the map. The robot should plan a path and drive to that point.
To send a new goal, stay in Set Robot Goal mode and click another point. Use Stop in the GUI if you need to halt motion before sending a new goal.
For manual click-to-goal testing, use the steps above. For scenario runs driven by Jaewoo’s application layer (patrol routes and automated goal commands), complete startup and localization first, then follow Running the Robots with Jaewoo’s Code.
You can stop robot software and power down using SSH, the central Robot GUI, or the touch display on each robot. Complete shutdown individually for each robot.
In the terminal where you ran roslaunch, press Ctrl+C to stop the ROS nodes.
If you started ROS via a different method, SSH into the Jetson if you are not already connected (username/password = jetson):
ssh jetson@<jetson_ip_address>
Then, connect to the docker container and stop ros nodes:
docker exec -it ros_noetic bash rosnode kill --all
Stop the Docker container by typing exit in the container shell. If you started the container in detached mode or from another session, run:
sudo docker stop ros_noetic
To power off the Jetson gracefully, run:
sudo shutdown now -h
Wait for the Jetson to fully power down, then turn off the electronics/motor switch and the 12V battery.
Select the robot from the left-hand column, and then click shutdown to shutdown ROS nodes only on that robot.
Use Shut down all if you want to stop ROS execution on every connected robot at once.
Then select the robot in the dropdown box on the right hand column. Under "More", click Power Off to shut down the Jetson gracefully. Wait until the robot is fully off before cutting power.
Turn off the electronics/motor switch and the 12V battery.
On the robot’s touch display, click Stop ROS to stop the running ROS nodes. The LiDAR should stop spinning.
Exit out of the display. From the desktop, tap on the upper right corner of the Ubuntu desktop and then click shutdown. Wait for the Jetson to fully power down.
After the Jetson has shut down, turn off the electronics/motor switch and the 12V battery.
Charge batteries before extended storage. See Operation Guide → Powering down for additional manual shutdown steps.
Shut down all robots first (see Shutdown Robots above), then stop the central operator workstation services in reverse order of startup.
Skip this step if you did not start the capture stack during startup.
In the Robot GUI Local Stack panel, click the Capture Stop button (directly under the Local Stack Docker controls).
In Windows Terminal, find your Ubuntu WSL 1 tab (the same WSL1 environment used at startup — not plain Ubuntu / WSL2).
End the scripts by pressing Ctrl + C:
Alternatively, open a new Ubuntu WSL 1 tab and navigate to the DDS scripts directory and run the stop script:
cd dds_robot_platform/dds ./stop_scripts.sh
In the Robot GUI, click the Local Stack Docker Stop button (bottom right of the application). This stops the GraphQL, InfluxDB, and related compose services started at startup.
Close the DDS Robot GUI application. You may quit Docker Desktop if you are finished for the day; leave it running if you plan to restart the stack soon.
Required software, script, and hardware updates for the KAIST deployment. Most recent updates appear at the top.
Install the tools needed to build and flash motor / microcontroller firmware, clone the project, and set the robot defines. Stop after step 3 until Matt finishes the remaining software updates.
Do this on the central Windows machine. You can alternatively install on a Mac, but only if it is an Arm-based Apple silicon Mac (M1/M2/M3/etc.).
If you did not install XC32 during the MPLAB X IDE setup, download and install it separately from the MPLAB XC32 compiler page.
On the central Windows machine, open a terminal (Command Prompt or PowerShell), navigate to the Desktop, then clone the robot repository:
cd %USERPROFILE%\Desktop git clone https://github.com/satomm1/Robot.git
In PowerShell, use cd $env:USERPROFILE\Desktop instead of cd %USERPROFILE%\Desktop.
Robot folder you just cloned, then open the Software folder.PIC32MZ2048EFH144.1.6.xxx or 1.5.xxx).v5.xx, or whichever XC32 version you installed).In the left-hand column (Projects / file navigator), expand FrameworkHeaders, then double-click ES_Configure.h to open the file.
Near the top of the file, double-check and update these #define values for the robot you are programming:
#define ROBOT_ID x — set x to the ID of the robot you are programming.#define PCB_REV 3 — make sure this is set to 3.#define MOTOR_TYPE 1 — make sure this is set to 1.#define WHEEL_RADIUS 0.041 — set this to 0.041.#define CAR 0 — should be 0.#define WHEEL_BASE 0.258572 — should be this value.#define NUM_SERVICES 8 — set this to 8.Save the file after making any changes.
Stop here until Matt finishes the remaining software updates. Do not continue with flashing or further MCU steps until then.
Copy the latest cafeteria map files onto each robot, generate the DDS map, then copy the map JSON onto the central machine for the GUI.
Download these three caus_cafeteria* files from mattbot/maps (save them to your Windows Downloads folder):
Open Windows Terminal, then open a Command Prompt tab. Navigate to your Downloads folder:
cd Downloads
From Downloads, scp the three files to each Jetson (replace <jetson_ip> with that robot’s IP — see Cheatsheet). Password is typically jetson. Repeat for every robot:
scp caus_cafeteria* jetson@<jetson_ip>:~/
SSH into each robot, then move the files from the home directory into the mattbot_mcl maps folder:
ssh jetson@<jetson_ip>
sudo mv caus_cafeteria* ~/workspaces/catkin_ws/src/mattbot_mcl/maps
Enter the running ROS container (start Docker on the robot first if it is not already running):
docker exec -it ros_noetic bash
Navigate to the map scripts folder and generate the DDS map (may take a few seconds):
roscd mattbot_mcl/scripts python3 generate_dds_map.py --map_file caus_cafeteria
Repeat for every robot.
This step only needs to be done once. Use any Jetson that already has the updated map. On the central machine, open an Ubuntu WSL 1 terminal:
cd ~/dds_robot_platform/dds scp jetson@<jetson_ip>:~/workspaces/catkin_ws/src/mattbot_mcl/map_json/current_map_mod.json user_map.json
If ./start_scripts.sh is currently running, restart it so the updated map is picked up (stop with Ctrl+C, then run ./start_scripts.sh again from ~/dds_robot_platform/dds).
If the previous map was already loaded in the Robot GUI, you may also need View → Reload in the top-left menu bar to see the updated map.
After updating the central machine (see Pull latest dds_robot_platform in One Time Updates), pull the latest robot-side software on each Jetson using the Robot GUI. This must be done once per robot — the GUI update button applies only to the currently selected robot.
The timestamp above is the most recent commit across mattbot_bringup, mattbot_dds, mattbot_capture, mattbot_mcl, and mattbot_navigation (noetic/main). If your robots were last updated before that time, run Software Update on each Jetson.
For each robot:
Repeat steps 1–5 for every robot in the fleet.
If an update fails, check that the Jetson host service is installed (see Previous updates → Install Jetson host service) and that the robot is reachable on the network.
On the central operator workstation, update the existing dds_robot_platform clone in both WSL environments used at KAIST: Ubuntu WSL 1 (DDS scripts) and plain Ubuntu / WSL2 (capture stack). Each distro has its own filesystem — run git pull in each one.
1. Ubuntu WSL 1 — open an Ubuntu WSL 1 tab in Windows Terminal (not plain Ubuntu / WSL2). If DNS/network lookups fail in WSL1, fix the resolver first, then pull:
sudo sh -c 'echo "nameserver 8.8.8.8" > /etc/resolv.conf' cd ~/dds_robot_platform git pull
2. Ubuntu (WSL2) — open a plain Ubuntu tab in Windows Terminal (WSL2, used for capture):
cd ~/dds_robot_platform git pull
If git pull fails because of local changes in either environment, discard them and pull again:
cd ~/dds_robot_platform git restore . git pull
After pulling updates: if the Local Stack Docker containers are already running, you must restart them. Stop them via the Robot GUI (Local Stack Docker Stop), then start them again with Start. If Capture was running, also Stop then Start Capture in the same Local Stack panel.
If ./start_scripts.sh is running in the Ubuntu WSL 1 terminal, stop it with Ctrl+C, then run ./start_scripts.sh again from ~/dds_robot_platform/dds.
On Robot 1, reseat the motor connectors and tidy the motor wiring. This may help with the robot’s tendency to veer right.
At each motor, press down on the plastic part of the wire connector where it plugs into the matching plastic connector on the motor (see the red circles/arrows in the photo). Press gently but firmly. The easiest way is to put one finger on either side of the wires and push straight down.
Inspect the six-wire bundles that run from each motor to the circuit board:
Jetson Orin Nano: boots to UEFI shell instead of Linux — applies to Robot 3 (and the same symptoms on any other Jetson).
On startup, instead of booting normally, you get a screen like this:
UEFI Interactive Shell v2.2
EDK II
UEFI v2.70 (EDK II, 0x00010000)
Mapping table
FS1: Alias(s):F1:
FS0: Alias(s):F0:
Press ESC in 5 seconds to skip startup.nsh or any other key to continue.
Shell>
This isn’t a specific “error code” — it means the board’s firmware (UEFI) tried to find a bootable operating system and couldn’t, so it dropped into this fallback shell instead. It’s usually fixable without reflashing anything. Work through the steps below in order — start at Fix 1, and only move to the next one if it doesn’t resolve the issue.
Safe and no risk. Robot 3’s Jetson is normally connected only to a touchscreen, so you need a keyboard to type at the UEFI shell.
Shell> prompt.If you have trouble reaching or staying at the Shell> prompt: when you see “Press ESC in 5 seconds to skip startup.nsh or any other key to continue,” press any key during that 5-second countdown — don’t let it run out. That takes you to the Shell> prompt, where you can type the commands below.
At the Shell> prompt, list the available filesystems:
map -r
You’ll see a few entries (fs0:, fs1:, fs2:, etc.) — some belong to the SD card, some to firmware volumes. Check each one:
fs0: ls
Repeat with fs1:, fs2:, etc. Look for a folder called EFI\BOOT\ containing a file named BOOTAA64.EFI. Once you find it, boot it directly (replace fs2: with whichever filesystem actually had it):
fs2:\EFI\BOOT\BOOTAA64.EFI
map -r again at the Shell> prompt to see if a new filesystem shows up.If the SD card still isn’t detected at all after this, move to Fix 5 (reflash) — that points to either a bad card or a deeper firmware issue.
This is the most common root cause when the board has both an SD card and an NVMe drive installed. The firmware checks both storage devices at boot and tries whichever is first in its list — if that happens to be the NVMe drive and it doesn’t have a valid, currently-bootable OS, the boot attempt fails and you land in the shell instead of falling back to the SD card.
Option A — from the boot menu:
Option B — from inside Linux (use this if Fix 1 got you booted):
sudo efibootmgr -v
This lists all boot entries, something like:
Boot000A* Jetson NVME: ... File(\EFI\BOOTAA64.EFI) Boot000B* Jetson eMMC: ... File(\EFI\BOOTAA64.EFI)
Reorder so the SD/eMMC entry boots first (swap in the actual entry numbers shown on your device):
sudo efibootmgr -o 000B,000A
This is written to persistent firmware storage, so it should survive future reboots.
This issue often shows up “out of nowhere” after the board has been working fine for a while. The most common trigger is the board losing power abruptly — a pulled cable, a dead battery, a brownout, or cutting power without a clean shutdown. That can reset the firmware’s saved boot order back to its defaults, which causes it to start trying the NVMe drive again.
Where possible:
sudo shutdown now) before removing power.If you’ve worked through Fixes 1–4 and:
map -r, or…then the SD card or the board’s underlying bootloader firmware likely needs to be reflashed from a host PC via NVIDIA SDK Manager. This is more involved and carries some risk if done incorrectly.
Stop here and reach out to Matt to help with this step.
On the central operator workstation, install the updated DDS Robot GUI (v0.1.2).
DDS.Robot.GUI.Setup.0.1.2.exe).After updating, start Docker Desktop and use Local Stack Docker Start (and Capture Start if needed) as in Running the Robots. You may need to re-enter the dds_robot_platform path and WSL distro in the Local Stack panel.
On the central operator workstation, reinstall the DDS Robot GUI from the latest Windows installer release.
After reinstalling, start Docker Desktop and use Local Stack Docker Start (and Capture Start if needed) as in Running the Robots. You may need to re-enter the dds_robot_platform path and WSL distro in the Local Stack panel.
If the lab Wi-Fi network, SSID, or DHCP leases have changed, update connectivity and IP configuration on every robot and on the central machine (Robot GUI saved addresses, CycloneDDS peer lists, and each Jetson’s robot_env.sh / cyclonedds.xml).
Follow the full procedure in Updating Wi-Fi (IP Addresses) — connect to the new network, record IPs, update the Robot GUI and central cyclonedds.xml, then update and test each robot.
The digital twin currently uses the API calls: robotPositions, robotGoals, etc.
However, I have added additional APIs that provide the transformed coordinates to the DT global map. The API calls in the DT must be updated
Your DT code also provides the ability to set the transformation between robot map and global map. However, since I already perform this in my code, the DT should not utilize this functionality. I recommend disabling it or setting it to be disabled by default. (To get it to work while testing I had to go to advanced mode->tools->calibration->disable the calibration profile)
On each Jetson host (outside Docker), install the robot on-screen display from mattbot_display. The touch-screen UI shows messages from the robot backend, speaks them with local Piper TTS, and provides Start Robot / Stop Robot controls via the existing host services on ports 8081 (Docker) and 8080 (ROS).
Prerequisites: Complete the Install Jetson host service and Replace Docker images (ml_ros and gemini) updates below first. The display expects robot-host-service on port 8081, the ghcr.io/satomm1/ml_ros:latest image, and the ghcr.io/satomm1/gemini:latest image for the voice backend.
1. Install system packages on the Jetson:
sudo apt update sudo apt install -y python3-tk alsa-utils curl wget git
2. Clone or update the repository at ~/mattbot_display:
cd ~ # First time on this Jetson: git clone https://github.com/satomm1/mattbot_display.git # If the repo already exists: cd ~/mattbot_display && git pull
If git pull fails because of local changes (e.g. modified tracked files), discard them and pull again:
cd ~/mattbot_display git restore . git pull
3. Update the robot backend at ~/gemini_api (the display reads messages from this service over HTTP). This repository is private — Git will prompt for credentials when you clone or pull. Ask Matt for the password you will need to access the updates (your GitHub username is fine for the username field):
First time (repo not on the Jetson yet):
cd ~ git clone https://github.com/satomm1/gemini_api.git
When prompted, enter the token above as the password.
Updates (repo already at ~/gemini_api):
cd ~/gemini_api git pull
If Git asks for credentials again on pull, use the same token as the password.
If git pull fails because of local changes, run git restore . in ~/gemini_api and pull again.
If clone or pull still fails, contact Matt (satomm@stanford.edu).
4. Run the installer from the repo root (installs Piper TTS, systemd service, desktop shortcut, and sudoers for restart):
cd ~/mattbot_display chmod +x scripts/install.sh sudo ./scripts/install.sh
The installer runs scripts/setup_piper.sh (downloads Piper and the voice model), enables the mattbot-display systemd unit, and creates a desktop shortcut Start-Mattbot-Display.sh. The service runs Python directly from ~/mattbot_display — after pulling code updates, double-click the desktop shortcut (or run ./scripts/restart_display.sh) to restart; no separate sync step is needed.
5. Verify the display service is running:
sudo systemctl status mattbot-display curl -s http://127.0.0.1:8081/status
After reboot, the display should start automatically when the graphical session is up. Use the on-screen Exit button to return to the desktop; start it again with the desktop shortcut.
Full configuration (backend URL, socket port, ALSA device, launch path) is documented in the mattbot_display README. Override install path with INSTALL_DIR=/opt/mattbot/display sudo ./scripts/install.sh if you do not use ~/mattbot_display.
On each Jetson (outside Docker), install the Jetson host service so the operator GUI Robot Startup panel can start/stop the ros_noetic container and power off the Jetson over HTTP on port 8081. ROS launch and software updates remain on port 8080 inside the container (startup_script.py).
1. Download the install script on the Jetson:
cd ~ wget "https://raw.githubusercontent.com/satomm1/Robot/main/Jetson/jetson-host-install.sh" chmod +x jetson-host-install.sh
2. Run the installer on the Jetson:
sudo ./jetson-host-install.sh
The installer writes /opt/robot/host_service.py and enables the robot-host-service systemd unit so the service starts on boot. Re-running the installer replaces the service file and restarts it.
If you see /usr/bin/env: 'bash\r': No such file or directory, the script has Windows (CRLF) line endings. On the Jetson run sed -i 's/\r$//' ~/jetson-host-install.sh and try again.
3. Verify the service is running:
curl -s http://127.0.0.1:8081/status
Expected response (example): {"host_service": true, "docker_running": false, "container": "ros_noetic"}
On each Jetson, remove the old Docker images and pull the updated ml_ros and gemini images from GitHub Container Registry (ghcr.io/satomm1). The pulls can take a long time — use tmux so they keep running if your SSH session disconnects.
Install tmux:
sudo apt update sudo apt install tmux
Start a tmux session (on the Jetson host, outside Docker):
tmux new -s docker-pull
Inside tmux, stop the ROS container if it is running, remove the old ml_ros image, and pull the new one:
sudo docker stop ros_noetic sudo docker rmi ros_ml:latest sudo docker pull ghcr.io/satomm1/ml_ros:latest
Verify the new ml_ros image is present:
sudo docker images ghcr.io/satomm1/ml_ros:latest
Do the same for the gemini backend container — stop it if running, remove the old local image, and pull ghcr.io/satomm1/gemini:latest:
sudo docker stop gemini sudo docker rmi gemini:latest sudo docker pull ghcr.io/satomm1/gemini:latest
Verify the new gemini image is present:
sudo docker images ghcr.io/satomm1/gemini:latest
When the pull finishes, detach from tmux without stopping it: press Ctrl+B, then D. To reattach later — or if your SSH terminal disconnects during the pull — run tmux attach -t docker-pull. Exit the session when done: type exit inside tmux.
If docker rmi fails because an image is still in use, stop the container first (sudo docker stop ros_noetic or sudo docker stop gemini) and retry. If the old image tag differs from ros_ml:latest or gemini:latest, remove the tag shown by sudo docker images. No docker login is required — both packages are public on GHCR.
On each Jetson, download these files into ~/workspaces/catkin_ws/src:
cd ~/workspaces/catkin_ws/src sudo wget -O robot_env.sh "https://raw.githubusercontent.com/satomm1/Robot/main/Jetson/robot_env.sh" sudo wget -O startup_script.py "https://raw.githubusercontent.com/satomm1/dds_robot_platform/main/robot/startup_script.py" sudo wget -O cyclonedds.xml "https://raw.githubusercontent.com/satomm1/Robot/main/Jetson/cyclonedds.xml"
Edit robot_env.sh on each robot (inside the Docker container this path is /workspace/catkin_ws/src/robot_env.sh):
sudo nano ~/workspaces/catkin_ws/src/robot_env.sh
Update these lines with the values below. Set ROS_IP and ROBOT_ID uniquely for each robot; the other values are the same for all KAIST units:
export ROS_IP= # this Jetson's IP address export ROBOT_ID= # unique ID for this robot (match MCU robot ID) export MCU_SPI=3 export CAMERA_TYPE=astra_pro_plus export ROBOT_HEIGHT=tall
Use the arrow keys to move the cursor. Type each value after the = on the lines above (leave export and the variable names unchanged).
Save and exit nano: press Ctrl+O, then Enter to confirm the filename, then Ctrl+X to close the editor.
Configure cyclonedds.xml on each robot (host path ~/workspaces/catkin_ws/src/cyclonedds.xml; inside Docker, /workspace/catkin_ws/src/cyclonedds.xml). This file disables multicast and uses an explicit peer list for WiFi mesh discovery. robot_env.sh sets CYCLONEDDS_URI to this path — leave that line unchanged.
sudo nano ~/workspaces/catkin_ws/src/cyclonedds.xml
You must update the <Peer Address="…"/> entries so they list the actual IP addresses of every robot and device that should participate in DDS discovery on your network:
ROS_IP in robot_env.sh).The default file assumes the wlan0 interface; change <NetworkInterface name="…"/> only if your WiFi interface has a different name (run ip link or ifconfig to check).
Save and exit nano: Ctrl+O, Enter, Ctrl+X.
See Setup Guide → ROS workspace for sourcing robot_env.sh in ~/.bashrc if not already configured.
Each Jetson must load the spidev kernel module at boot so the robot can communicate with the MCU over SPI. Perform this on every robot unit.
Edit /etc/modules-load.d/spidev.conf on the Jetson host (outside Docker):
sudo nano /etc/modules-load.d/spidev.conf
Add this line to the file (create the file if it does not exist):
spidev
Save and exit nano: Ctrl+O, Enter, then Ctrl+X. Reboot the Jetson for the change to take effect:
sudo reboot
Configure WiFi roaming so each Jetson can switch access points when signal is weak. The Intel AC9260 is already installed on your robots — download and run the setup script on every Jetson host (outside Docker).
Download the script:
cd ~ wget "https://raw.githubusercontent.com/satomm1/Robot/main/Jetson/wifi_roaming_setup.sh" chmod +x wifi_roaming_setup.sh
Run the installer:
sudo ~/wifi_roaming_setup.sh
Follow the prompts for your lab SSID, password, and regulatory domain (KR for Korea). Then reboot:
sudo reboot
After reboot, verify the services are running (replace wlan0 if your interface name differs):
sudo systemctl status wpa_supplicant@wlan0 custom_wifi.service
Assign a fixed DHCP reservation for each Jetson’s WiFi MAC on your router for a stable IP. Revert steps are in the Jetson README.
Required first: These features only work after you complete the current One Time Updates — install the DDS Robot GUI update, run the robot software updates on each Jetson (then stop/restart Docker on the robots), and git pull dds_robot_platform on the central machine. See One Time Updates.
The GUI includes a Capture checkbox that controls whether robot images are saved.
The Patrol checkbox puts the selected robot into patrol mode. The robot patrols between points you choose and save from the GUI.
Under Robot Startup on the right-hand side, select the desired robot, then check the Patrol checkbox.
Click Set Patrol. A panel opens on the left-hand side showing the patrol points for that robot.
Remove existing points or add new ones by clicking/dragging on the map. You can also set how long the robot should stay at each patrol point before moving to the next one.
When finished, click Send to Robot.
The next time you Start ROS for that robot, it will automatically patrol between the saved points. You still need to initialize the robot position (set initial pose) as usual before navigation will work correctly.
After completing Running the Robots (central machine startup, robot startup, and localization), use these quick checks to confirm communication and navigation are working before scenario runs or further debugging.
This page polls the capture API for the latest camera frame from each robot. Use it to verify that robot image streams are reaching the central machine.
On the central machine, download watch_robot_images.html from the dds_robot_platform docs folder (use Raw → Save As, or open the file from your local clone at dds_robot_platform/docs/watch_robot_images.html).
Open the HTML file in a browser on the central machine. At the top of the page, set the four robot ID fields to 1, 2, 3, and 4 (match the IDs used on your KAIST fleet).
Make sure the central stack is running, including Capture from the Robot GUI Local Stack panel (Capture Start, under Local Stack Docker Start), and start at least one robot as in Running the Robots. Live camera images for active robots should appear in the 2×2 grid on the page.
If a cell shows “no frame” or “unreachable,” check that that robot’s ROS stack is up, capture is running, and the robot ID matches the GUI / robot_env.sh configuration.
Use the Robot GUI to confirm localization and navigation end-to-end (see Robot Operation under Running the Robots):
If initial pose, goals, and motion all work, DDS communication, localization, and navigation are functioning as expected for day-to-day GUI control.
Use this procedure when the robots or central machine move to a new Wi-Fi network (new SSID, passwords, or DHCP leases). Complete the steps below on every robot and on the central machine so the Robot GUI and DDS discovery use the new addresses.
On each robot’s display, go to the Ubuntu desktop. In the upper-right corner, click the power/system menu, then open the Wi-Fi settings. Select the new Wi-Fi SSID and enter the password to connect.
Repeat for every robot in the fleet.
Plug a USB keyboard into the Jetson. Open a terminal and run:
ifconfig
Find the wlan0 section and note the address next to inet — that is the robot’s Wi-Fi IP. Write down both the robot number and this IP address.
Repeat for every robot so you have a full list (e.g. Robot 1 → 192.168.x.x, Robot 2 → …).
On the central machine, open the Robot GUI. In the right-hand Robot Startup panel:
If the robots are powered on and reachable, the new IPs should be recognized and the status dot next to each robot should be solid gray (not red, and not flashing gray).
On the central Windows machine, allow the new robot IPs through Windows Defender Firewall so ROS/DDS traffic is not blocked.
On the central machine, open an Ubuntu WSL 1 terminal. Pull the latest dds_robot_platform so you have update_wifi_ips.sh, then run the script. It prompts for the central machine IP and each robot IP, then updates cyclonedds.xml automatically:
cd ~/dds_robot_platform git pull cd dds ./update_wifi_ips.sh
Enter the central machine IP and every robot IP from step 2 when prompted.
If you are already running ./start_scripts.sh, restart it after the script finishes (stop with ./stop_scripts.sh or Ctrl+C, then run ./start_scripts.sh again from ~/dds_robot_platform/dds) so discovery picks up the new peers.
Repeat this step for every robot.
Start Docker on the robot if it is not already running (Robot GUI Start Docker, or Start Docker on the robot display app).
SSH into the Jetson from an Ubuntu WSL 1 terminal on the central machine (username/password = jetson):
ssh jetson@<robot_ip_address>
Enter the running ROS container:
docker exec -it ros_noetic bash
Download and run the Wi-Fi IP update script. It prompts for the central machine IP and each robot IP, then updates robot_env.sh and cyclonedds.xml automatically:
cd /workspace/catkin_ws/src wget -O update_wifi_ips.sh "https://raw.githubusercontent.com/satomm1/Robot/main/Jetson/update_wifi_ips.sh" chmod +x update_wifi_ips.sh ./update_wifi_ips.sh
Enter the same IP addresses you used in step 5 when prompted.
Still inside the container, with the electronics/motor switch on, run:
roslaunch mattbot_bringup minimal.launch
If everything is working, you should see a launch summary (Parameters, Nodes), messages about processes that were started, and an [INFO] message that includes the robot ID.
Press Ctrl+C to stop the test launch when finished. Repeat steps 6–7 for each remaining robot.
After every robot has been updated and tested locally, verify that the central machine can talk to the robots over the new Wi-Fi network.
cd ~/dds_robot_platform/dds conda activate dds ./start_scripts.sh
If the network configuration is correct, the robots should appear on the map in the Robot GUI. In the terminal where ./start_scripts.sh is running, you should also see messages indicating that the central machine has connected to the robots that were started.
Jaewoo’s robot control code runs on top of the standard KAIST stack. It sends automated navigation goals and patrol commands through the existing DDS/ROS infrastructure instead of manual map clicks in the Robot GUI.
Follow every step in Running the Robots before starting Jaewoo’s code:
Do not skip these steps. Jaewoo’s application layer expects the central DDS stack and all target robots to already be running.
In the Robot GUI, set the initial position for every robot you plan to control (see Robot Operation → Set initial position (GUI) in Running the Robots).
Confirm each robot’s icon on the map matches its real location and orientation before proceeding. Automated goal commands will fail or behave incorrectly if localization is wrong.
With startup and localization complete, run Jaewoo’s application layer on the central operator workstation. This code publishes goal and patrol commands over DDS to the running robots.
Contact Jaewoo for the current repository location, environment setup, and launch command for your scenario.
Keep the Robot GUI and central DDS stack running while Jaewoo’s code is active. Use the GUI Stop button or the shutdown procedures in Running the Robots when the scenario is finished.
Use this workflow to create a new SLAM map on a KAIST robot. Power on the robot first (see Running the Robots), then SSH into the Jetson and run the steps below.
Connect to the robot you will map (username/password = jetson):
ssh jetson@<jetson_ip_address>
On the Jetson, start the ROS container:
sudo docker run --runtime nvidia --network=host -v ~/workspaces/catkin_ws:/workspace/catkin_ws -v ~/gemini_api:/gemini_code -v /dev/bus/usb:/dev/bus/usb -v /dev/video0:/dev/video0 -v /dev/video1:/dev/video1 -it --device=/dev/ttyUSB0 --device=/dev/spidev0.0 --rm --privileged --name ros_noetic ghcr.io/satomm1/ml_ros:latest bash -lc 'python3 /workspace/catkin_ws/src/startup_script.py & exec bash'
Wait for the container shell to appear.
In the first terminal (inside the Docker container), launch SLAM mapping:
roslaunch mattbot_bringup sense_and_map.launch
Open a second terminal, SSH into the same Jetson again, and enter the running container:
docker exec -it ros_noetic bash
Start the keyboard teleop node:
roslaunch mattbot_teleop keyboard.launch
Focus the teleop terminal and use the keyboard to drive the robot through the entire area you want mapped.
Real-time map visualization: To watch the map build as you drive, use RViz on a separate machine on the same network as the Jetson. See Setup Guide → RVIZ visualization for configuration (ROS_MASTER_URI pointing at the Jetson, then rosrun rviz rviz).
When you have driven through the entire area, press Ctrl+C in the teleop terminal to exit keyboard control.
In the same terminal (still inside the Docker container), finalize the map — replace <map-name> with a short name for this space (e.g. lab_floor1):
rosrun mattbot_mcl finalize_map.py --name <map-name>
This saves the map and generates the files needed by the robots and the central operator computer. For large maps, this step can take a few minutes.
Press Ctrl+C in the first terminal (where sense_and_map.launch is running) to stop mapping once map saving has finished. Then stop the Docker container by typing exit.
On your operator workstation, open the DDS Robot GUI and start the local Docker stack (see Operation → Starting User GUI / DDS if needed).
The on-screen map should update with your new map once the sync completes.
Repeat these steps for each robot that should use the new map (on the central computer GUI):
Repeat for every other robot in the fleet that needs the updated map.
Complete these steps once on each operator workstation (not on the Jetson). After setup, use Running the Robots for day-to-day operation.
Prerequisites: Docker Desktop (daemon running), git, and Miniconda or Anaconda.
Pre-built installers are produced by GitHub Actions (recommended; no Node.js required).
gui-installer-windows-latest — unzip, run DDS Robot GUI Setup … .exe, launch from the Start menu. Unsigned builds may show SmartScreen — More info → Run anyway if you trust the source.gui-installer-macos-latest — open .dmg, drag DDS Robot GUI to Applications. First launch may need right-click → Open.gui-installer-ubuntu-latest — unzip .AppImage, chmod +x, run. Install FUSE / libfuse2 if the AppImage will not start.http://localhost:8000/graphql.
Clone the repository on your workstation:
git clone https://github.com/satomm1/dds_robot_platform.git cd dds_robot_platform
Windows: Clone into your WSL filesystem (e.g. under ~/ in Ubuntu) so docker compose and DDS scripts run where the README expects. Run the GUI from Windows (desktop app) or from source.
Copy and edit the DDS environment file:
cp dds/dds_env.sh.example dds/dds_env.sh nano dds/dds_env.sh
Set AGENT_ID, INFLUXDB_TOKEN, and any other operator variables. The example file also sets CYCLONEDDS_URI to dds/cyclonedds.xml. Save and exit nano: Ctrl+O, Enter, Ctrl+X.
Edit dds/cyclonedds.xml for your network:
nano dds/cyclonedds.xml
<NetworkInterface name="…"/> to the interface that reaches the robot fleet (run ip link; common names include wlan0, wlp2s0, eth0).<Peer Address="…"/> entries with the IP address of each KAIST robot and any other DDS participant on the network.Save and exit nano: Ctrl+O, Enter, Ctrl+X.
Pull images and start the Docker services (GraphQL, InfluxDB, Ignite) from the repo root:
docker compose pull docker compose up -d
Alternatively, use the GUI Local Stack panel → Docker → Start (requires compose.yaml and dds/dds_env.sh).
DDS Python scripts run on the host (WSL on Windows), not inside Docker. Create the dds conda environment from the dds_robot_platform repo root:
cd dds_robot_platform conda env create -f environment.yml conda activate dds
Note: This is the step I am least confident is fully covered by environment.yml. If scripts fail with missing-module errors, install the reported package into the dds env (e.g. pip install <package> with conda activate dds) and let me know so the requirements file can be updated.
Environment creation can take several minutes. If the environment already exists and you need to refresh it:
conda env update -f environment.yml --prune
Verify CycloneDDS is available:
conda activate dds
python -c "import cyclonedds; print('OK')"
Start DDS scripts from the dds directory (start_scripts.sh activates the dds conda env automatically when needed):
cd dds ./start_scripts.sh
Verify scripts are running:
pgrep -af python
You should see publisher/subscriber scripts such as entry_exit.py, heartbeat_publisher.py, and goal_publisher.py. Stop them with ./stop_scripts.sh when finished.
Full reference: dds_robot_platform README and Setup Guide → User GUI / communication.
Your robots are already programmed with microcontroller firmware. You should not need this section under normal operation — it is here only if the MCU must be re-flashed (for example after a board replacement or an emergency firmware update).
These steps run on a Windows, macOS, or Linux PC with a Microchip programming tool connected to the robot main board (not on the Jetson). Power the robot electronics on while programming.
Download and install MPLAB X IDE from Microchip on your PC.
During installation, when prompted to choose which compilers to install, you only need the XC32 compiler (32-bit MCU). You can skip the other compiler options unless you plan to use them for other projects.
Full firmware project files and additional programming notes are in the Robot repo → Software/MCU and the Software README.
On your PC, clone the Robot repository:
git clone https://github.com/satomm1/robot.git
Open MPLAB X IDE, then go to File → Open Project and select the robot/Software/MCU directory inside the cloned repo.
In the bottom-left window, under Packs, click PIC32MZ… (the label may show a different device name on your machine). In the popup:
PIC32MZ2048EFH144.In the file navigator (top-left window), open FrameworkHeaders/ES_Configure.h. Update these #define values for the robot you are flashing:
ROBOT_ID — set to the ID for this robot (must match the value in robot_env.sh on the Jetson).PCB_REV — 3MOTOR_TYPE — 1WHEEL_RADIUS — 0.040CAR — 1After saving the file, click Clean and Build at the top of MPLAB (broom icon). Fix any build errors before continuing.
Matt will supply an MPLAB Snap In-Circuit Debugger and Programmer when he visits. Only 6 pins need to be connected: the left 6 pins on the Snap programmer cable connect to the matching exposed header pins on the robot main circuit board.
Click the Program button at the top of MPLAB (down-arrow icon) to flash the firmware to the microcontroller. If MPLAB asks you to select a programmer, choose the Snap programmer.
If the Snap programmer does not appear in the list:
Wait for the operation to finish — MPLAB will report success or show an error if programming failed.
Flashing the other robots: You only need to change ROBOT_ID in FrameworkHeaders/ES_Configure.h for each robot, then repeat the same programming procedure (connect Snap, power on, click Program).
The KAIST robots should be converted from rear-wheel drive to front-wheel drive. However, do not make these changes until you have the Snap programmer and can reprogram the microcontroller (when Matt visits). Follow the steps below when performing this change on a robot unit.
Make sure the Jetson and all robot electronics are fully powered off before starting the mechanical change.
Unplug the HDMI and USB cables from the upper levels of the robot that connect to the Jetson. You may also need to disconnect the microphone wires so the upper assembly can be rotated freely.
Unscrew the four screws visible from the top of the 3rd layer platform. This loosens the top four layers of the robot. Rotate those layers 180 degrees, then reattach the four screws.
Reattach the HDMI and USB cables from the upper levels back to the Jetson.
Reprogram the MCU using the procedure in Flashing the MCU above. Use CAR set to 1 in FrameworkHeaders/ES_Configure.h when building and flashing the firmware.