Skip to main content
A TurtleBot3 Burger carrying a Qualcomm Dragonwing single-board computer with a 360-degree LiDAR on top, on a workbench beside an AprilTag calibration mat
Dragonwing · Qualcomm
Robotics
Dragonwing Team·Jun 26, 2026·← All posts

The TurtleBot3 Burger is the classic “hello world” robot for ROS 2: a small differential-drive platform with a 360-degree LiDAR that is just capable enough to do real autonomous navigation. In this post we put one on top of a Qualcomm Dragonwing SBC, install ROS 2 Jazzy from scratch, bring the robot up, build a map of a room with Cartographer, and then let Nav2 drive the robot to a goal on its own. Everything here runs on the board itself. The Dragonwing SBC is the robot’s compute, so there is no laptop in the loop except, optionally, to watch the map come together in RViz.
This walkthrough targets a TurtleBot3 Burger with a pre-flashed OpenCR controller and an LDS-02 LiDAR, running ROS 2 Jazzy on Ubuntu. If your LiDAR or controller revision differs, adjust the model variables in the setup step accordingly.

What you will do

  1. Install ROS 2 Jazzy on the Dragonwing device.
  2. Build the TurtleBot3 workspace and wire up the serial devices.
  3. Bring the robot up and drive it by keyboard.
  4. Build a map of your space with Cartographer SLAM and save it.
  5. Hand the map to Nav2 and send the robot to a goal autonomously.
  6. View everything in RViz, on the board’s own screen or from another machine.

Prerequisites

Before you begin, make sure you have:
  • A Dragonwing device that has completed first-time setup:
  • Access to the device by SSH, or by a directly connected display, keyboard, and mouse. The setup guides above cover networking, serial console, display, and SSH access.
  • A TurtleBot3 Burger with the OpenCR board pre-flashed and an LDS-02 LiDAR.
  • Two free USB ports on the SBC: one for the OpenCR controller, one for the LiDAR.

Install ROS 2 Jazzy

This is a one-time step on a fresh board. It follows the standard ROS 2 Jazzy install for Ubuntu, so if you already have ROS 2 Jazzy on the device you can skip straight to setting up the TurtleBot3. The commands below mirror the official ROS 2 Jazzy installation guide exactly. First, add the ROS 2 apt repository:
sudo apt update
sudo apt install -y curl gnupg2 lsb-release ca-certificates software-properties-common locales
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8

sudo add-apt-repository universe -y
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
Then install the desktop bundle plus the build tooling:
sudo apt update
sudo apt install -y ros-jazzy-desktop \
  python3-colcon-common-extensions \
  python3-rosdep \
  python3-argcomplete
sudo rosdep init || true
rosdep update
Finally, source ROS in every new shell by appending it to ~/.bashrc:
echo 'source /opt/ros/jazzy/setup.bash' >> ~/.bashrc
source ~/.bashrc

Set up the TurtleBot3

With ROS in place, it is time to give the robot its software and tell it which hardware it has.

Plug in and check the hardware

The Burger talks to the SBC over two USB connections:
ConnectionDevice
OpenCR → SBCUSB → /dev/ttyACM0
LDS-02 LiDAR → SBCUSB → /dev/ttyUSB0
Confirm both devices enumerated before going any further:
ls /dev/ttyACM0 /dev/ttyUSB0

Build the TurtleBot3 workspace

Clone the three TurtleBot3 packages for Jazzy and build them with colcon. The full upstream instructions live in the ROBOTIS TurtleBot3 documentation.
mkdir -p ~/turtlebot3_ws/src
cd ~/turtlebot3_ws/src
git clone -b jazzy https://github.com/ROBOTIS-GIT/DynamixelSDK.git
git clone -b jazzy https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git
git clone -b jazzy https://github.com/ROBOTIS-GIT/turtlebot3.git

cd ~/turtlebot3_ws
source /opt/ros/jazzy/setup.bash
rosdep install --from-paths src --ignore-src -r -y
colcon build --symlink-install

Tell ROS about your robot

The TurtleBot3 stack is configured through environment variables. Append them to ~/.bashrc so every terminal picks them up:
echo 'source ~/turtlebot3_ws/install/setup.bash' >> ~/.bashrc
echo 'export TURTLEBOT3_MODEL=burger' >> ~/.bashrc
echo 'export OPENCR_PORT=/dev/ttyACM0' >> ~/.bashrc
echo 'export LDS_MODEL=LDS-02' >> ~/.bashrc
source ~/.bashrc
Here is what each one does:
VariableValuePurpose
TURTLEBOT3_MODELburgerSelects the robot URDF and config
OPENCR_PORT/dev/ttyACM0Serial port to the OpenCR controller
LDS_MODELLDS-02Selects the LiDAR driver

Grant serial permissions

Your user needs access to the serial ports, and ModemManager has to stay away from the OpenCR’s CDC ACM device. Add yourself to the dialout group, disable ModemManager, and install the TurtleBot3 udev rules:
sudo usermod -aG dialout $USER
sudo systemctl disable --now ModemManager

sudo cp $(ros2 pkg prefix turtlebot3_bringup)/share/turtlebot3_bringup/script/99-turtlebot3-cdc.rules \
  /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm trigger
Log out and back in (or reboot) after this step so the dialout group membership takes effect. If you need to keep working in the same session before logging out, see the serial-permission workaround in Troubleshooting.

Bring the robot up

Launch the bringup. This starts the OpenCR driver, the LiDAR driver, and the differential-drive controller:
ros2 launch turtlebot3_bringup robot.launch.py
A healthy bringup logs something like this:
[DynamixelSDKWrapper]: Succeeded to open the port(/dev/ttyACM0)!
[ld08_driver]: FOUND LDS-02
[ld08_driver]: LDS-02 started successfully
[turtlebot3_node]: Run!
[diff_drive_controller]: Run!

Drive it by keyboard

Leave bringup running and open a second terminal. The TurtleBot3 teleop node is the recommended way to drive the robot, including while you are mapping:
ros2 run turtlebot3_teleop teleop_keyboard
Use the W/A/S/D/X keys to move, and press S or X to stop before you close the window.

Build a map with Cartographer SLAM

Now for the fun part. With the robot driving, you can build an occupancy-grid map of the space using Cartographer. Keep bringup running in its own terminal, then start Cartographer:
ros2 launch turtlebot3_cartographer cartographer.launch.py use_sim_time:=false
RViz will fail to open if there is no display connected, and that is fine. Cartographer and the /map topic keep working headlessly while you drive. Now drive the robot around with keyboard teleop, or by publishing velocity commands. The more of the space you cover, the better the resulting map. When you are happy with the coverage, save the map:
ros2 run nav2_map_server map_saver_cli -t /map -f ~/my_map
That writes two files:
  • ~/my_map.pgm is the occupancy-grid image.
  • ~/my_map.yaml is the map metadata (resolution, origin).
Stop Cartographer once the map is saved.

Drive autonomously with Nav2

With a saved map in hand, hand it to Nav2 and let the robot navigate on its own. (Bringup still needs to be running.)
ros2 launch turtlebot3_navigation2 navigation2.launch.py \
  use_sim_time:=false \
  map:=$HOME/my_map.yaml
In RViz, set a 2D Pose Estimate so Nav2 knows where the robot is on the map, then send a navigation goal and watch it plan a path and drive there. No display? You can do the same thing headlessly by sending the goal as a ROS 2 action:
ros2 action send_goal /navigate_to_pose nav2_msgs/action/NavigateToPose \
  '{pose: {header: {frame_id: "map"}, pose: {position: {x: 1.0, y: 0.5}}}}'

Watch it in RViz

RViz is the easiest way to see the map and the robot’s plan. There are two ways to run it: on the board’s own screen, or from another machine on the network.

On the board’s screen over SSH

The GNOME desktop uses Wayland, but RViz2’s Ogre renderer needs GLX (X11). GNOME runs XWayland automatically, but the auth file (XAUTHORITY) has a random suffix that changes on every reboot. The cleanest fix is a small helper in ~/.bashrc that finds it for you:
cat >> ~/.bashrc << 'EOF'

export-display() {
  local xauth
  xauth=$(find /run/user/1000 -name '.mutter-Xwaylandauth.*' 2>/dev/null | head -1)
  export DISPLAY=:0
  export XAUTHORITY="$xauth"
  export XDG_RUNTIME_DIR=/run/user/1000
  echo "DISPLAY=$DISPLAY  XAUTHORITY=$XAUTHORITY"
}
EOF
source ~/.bashrc
Run export-display in any SSH session before you start RViz:
export-display
It prints the resolved environment:
DISPLAY=:0  XAUTHORITY=/run/user/1000/.mutter-Xwaylandauth.XXXXXX
Putting it together, mapping with RViz on the connected screen looks like this:
# Terminal 1 – bringup
ros2 launch turtlebot3_bringup robot.launch.py

# Terminal 2 – SLAM + RViz on the connected screen
export-display
ros2 launch turtlebot3_cartographer cartographer.launch.py use_sim_time:=false
If you open a terminal directly on the GNOME desktop rather than over SSH, DISPLAY and XAUTHORITY are already set. Run RViz normally without export-display.

From another machine on the network

To run RViz on a laptop or workstation on the same network:
  1. On both machines, set the same domain ID:
    export ROS_DOMAIN_ID=30
    
  2. Install ROS 2 Jazzy on the remote machine.
  3. On the remote machine, open RViz:
    rviz2
    
    Add a Map display and set the topic to /map. ROS 2 discovers the map data automatically over the network, so no extra configuration is needed.

Troubleshooting

A few things tend to trip people up the first time. Here are the usual suspects and their fixes.

/dev/ttyACM0 permission denied

You have not logged out since being added to the dialout group. Use this wrapper until you do:
sg dialout -c '. /opt/ros/jazzy/setup.sh; . ~/turtlebot3_ws/install/setup.sh; \
  export TURTLEBOT3_MODEL=burger OPENCR_PORT=/dev/ttyACM0 LDS_MODEL=LDS-02; \
  ros2 launch turtlebot3_bringup robot.launch.py'
After one logout and login, the wrapper is no longer needed.

LiDAR scan empty or Cartographer stuck

Queue waiting for data: (0, scan) usually means the wrong LDS model is set.
echo $LDS_MODEL    # must be LDS-02
ls /dev/ttyUSB0    # must exist
If LDS_MODEL=LDS-01, fix it:
sed -i 's/export LDS_MODEL=LDS-01/export LDS_MODEL=LDS-02/' ~/.bashrc
source ~/.bashrc

package 'ld08_driver' not found

sudo apt install -y ros-jazzy-ld08-driver

cmd_vel commands do nothing

ROS 2 Jazzy changed the cmd_vel topic type from geometry_msgs/Twist to geometry_msgs/TwistStamped. If you publish Twist directly (for example from a custom node or an older tutorial), add a header field:
from geometry_msgs.msg import TwistStamped
from builtin_interfaces.msg import Time

msg = TwistStamped()
msg.header.stamp = self.get_clock().now().to_msg()
msg.twist.linear.x = 0.2
msg.twist.angular.z = 0.0
publisher.publish(msg)
The built-in turtlebot3_teleop teleop_keyboard node already handles this correctly, so this only affects custom publishers.

Map saver fails silently

Always pass -t /map explicitly:
ros2 run nav2_map_server map_saver_cli -t /map -f ~/my_map

apt locked by a background process

sudo systemctl stop apt-daily.service apt-daily-upgrade.service packagekit.service
sudo pkill -f aptd || true
sudo apt update

Next steps

You now have a robot that maps a room and drives itself, all from a Dragonwing board. From here you can:
  • Run hardware-accelerated AI on the robot’s camera. The Depth Estimation on the NPU walkthrough builds a ROS 2 node that runs a quantized model on the Hexagon HTP NPU.
  • Explore the QRB ROS ecosystem for zero-copy transport, reference samples, Gazebo simulation, and benchmarking.
  • Start from the Robotics Workflow Overview for the full picture of robotics development on Dragonwing.