Blog 3 – Turtlebot 3

2/8/26

Introduction

The TurtleBot series was created to democratize robotics—its core objective was to provide an affordable, standardized, ROS‑native mobile robot that anyone could learn from, modify, and use for research. Its significance lies in becoming the world’s most widely adopted open‑source robotics platform for education and prototyping, shaping how modern ROS developers learn and build.

Each version aligned with major ROS milestones:

  • TurtleBot1 (2010–2011): First ROS‑ready consumer robot.
  • TurtleBot2 (2012): More robust base (Kobuki).
  • TurtleBot3 (2017): Modular, customizable, ROS1→ROS2 bridge.
  • TurtleBot4 (2022+): Fully ROS2‑native, modern sensors, Create 3 base.

With built‑in LiDAR, depth cameras (Turtlebot 4), and ROS2 support, modern TurtleBots allow students and researchers to work on:

  • SLAM
  • Navigation (Nav2)
  • Multi‑robot systems
  • Perception and AI
  • Human‑robot interaction

All without needing expensive industrial hardware.

TurtleBot line remains significant because it:

  • Provides a reference architecture for ROS2‑native mobile robots.
  • Demonstrates best practices in sensor integration, power design, and software stack organization.
  • Offers a baseline platform for benchmarking navigation, SLAM, and perception algorithms.
  • Continues to evolve with ROS2 Humble, Jazzy, and Gazebo Sim support

I purchased a Turtlebot 3 a few years ago for the reasons described above. After using it for a while it has sat on a shelf until recent work on robotics has again made it relevant. This blog describes the (rather long and tedious) process of updating the software and some testing of the available functions.

Setup Process

The following describes the process of setting up the Turtlebot 3. The document was generated by Sonnet 4.5 (Claude) as summary of the actions taken.

OpenCR 1.0 Board Specifications:

ComponentSpecification
MCUSTM32F746ZGT6 (ARM Cortex-M7, 216MHz)
Flash1MB
SRAM320KB
IMUMPU9250 (3-axis gyro, 3-axis accel, 3-axis mag)
Motor Ports4x Dynamixel TTL (3-pin)
Power7-14V input (battery), 5V/3A output
USBMicro-B (programming/communication)
Buttons2x push buttons (SW1, SW2)
LEDs4x user LEDs
GPIOArduino-compatible headers

TurtleBot3 Setup Documentation

Raspberry Pi 4 | Ubuntu 22.04 | ROS2 Humble

1. Overview

This document describes the complete setup process for TurtleBot3 on a Raspberry Pi 4 running Ubuntu 22.04 with ROS2 Humble. The setup includes system configuration, ROS2 installation, TurtleBot3 packages, and hardware configuration.

Final Working Configuration

ComponentStatusNotes
Ubuntu 22.04✓ WorkingOn Raspberry Pi 4
ROS2 Humble Desktop✓ WorkingFull install with RViz2
TurtleBot3 Packages✓ WorkingBuilt from source
OpenCR Firmware✓ UpdatedFlashed via x86 PC
LiDAR (LDS-01)✓ Working/dev/ttyUSB0
Motors✓ WorkingVia OpenCR /dev/ttyACM0
Keyboard Teleop✓ WorkingFull motor control

2. ROS2 Humble Installation

Step 2.1: Configure Locale

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

export LANG=en_US.UTF-8

Step 2.2: Add ROS2 Repository

sudo apt install -y software-properties-common

sudo add-apt-repository -y universe

sudo apt update && sudo apt install -y curl gnupg lsb-release

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 $(source /etc/os-release && echo $UBUNTU_CODENAME) main” | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null

Step 2.3: Install ROS2 Humble

sudo apt update && sudo apt upgrade -y

sudo apt install -y ros-humble-desktop

sudo apt install -y ros-dev-tools python3-colcon-common-extensions python3-rosdep

Step 2.4: Initialize rosdep

sudo rosdep init

rosdep update

Step 2.5: Add ROS2 to bashrc

echo “source /opt/ros/humble/setup.bash” >> ~/.bashrc

3. TurtleBot3 Package Installation

Step 3.1: Install Dependencies from apt

sudo apt install -y ros-humble-cartographer ros-humble-cartographer-ros

sudo apt install -y ros-humble-navigation2 ros-humble-nav2-bringup

sudo apt install -y ros-humble-dynamixel-sdk ros-humble-turtlebot3-msgs ros-humble-turtlebot3

Step 3.2: Create Workspace and Clone Packages

Important: Always source ROS2 before building.

source /opt/ros/humble/setup.bash

mkdir -p ~/turtlebot3_ws/src

cd ~/turtlebot3_ws/src

git clone -b humble https://github.com/ROBOTIS-GIT/DynamixelSDK.git

git clone -b humble https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git

git clone -b humble https://github.com/ROBOTIS-GIT/turtlebot3.git

git clone -b ros2-devel https://github.com/ROBOTIS-GIT/hls_lfcd_lds_driver.git

Step 3.3: Build Workspace

cd ~/turtlebot3_ws

rosdep install –from-paths src –ignore-src -r -y

colcon build –symlink-install –parallel-workers 2

Step 3.4: Configure Environment Variables

Add the following lines to ~/.bashrc:

source ~/turtlebot3_ws/install/setup.bash

export TURTLEBOT3_MODEL=burger

export LDS_MODEL=LDS-01

export ROS_DOMAIN_ID=30

export OPENCR_PORT=/dev/ttyACM0

4. OpenCR Firmware Update

Critical: The OpenCR board must be flashed with ROS2 Humble firmware. ARM64 (Raspberry Pi) flash tools are NOT available. You must use an x86 PC.

On an x86 PC (Windows/Mac/Linux):

wget https://github.com/ROBOTIS-GIT/OpenCR-Binaries/raw/master/turtlebot3/ROS2/latest/opencr_update.tar.bz2

tar -xvf opencr_update.tar.bz2

cd opencr_update

./update.sh /dev/ttyACM0 burger.opencr

Note: Replace ‘burger’ with ‘waffle’ or ‘waffle_pi’ if applicable.

5. TurtleBot3 Operation Commands

5.1 Basic Operation

Launch Robot (Terminal 1)

ros2 launch turtlebot3_bringup robot.launch.py

Keyboard Control (Terminal 2)

ros2 run turtlebot3_teleop teleop_keyboard

RViz Visualization (Terminal 3)

ros2 launch turtlebot3_bringup rviz2.launch.py

5.2 SLAM Mapping

Start SLAM with Cartographer

ros2 launch turtlebot3_cartographer cartographer.launch.py

Save Map

ros2 run nav2_map_server map_saver_cli -f ~/maps/my_map

5.3 Autonomous Navigation

ros2 launch turtlebot3_navigation2 navigation2.launch.py map:=~/maps/my_map.yaml

5.4 Debugging Commands

CommandPurpose
ros2 topic listList all active topics
ros2 topic echo /scanView LiDAR data
ros2 topic echo /cmd_velView velocity commands
ros2 topic echo /odomView odometry data
ros2 node listList active nodes

6. Issues Encountered and Solutions

IssueCauseSolution
turtlebot3_simulations clone failedWrong branch: humble-develUse branch: humble
Build failed – ament_cmake not foundROS2 not sourced before buildRun: source /opt/ros/humble/setup.bash
Gazebo package not foundOSRF repository not addedAdd OSRF Gazebo repository
Environment variables emptybashrc not properly sourcedManually add to ~/.bashrc
OpenCR flash failed on ARM64No ARM64 binaries availableFlash from x86 PC
Motors not movingOpenCR has old firmwareFlash ROS2 Humble firmware

7. System Information Commands

InformationCommand
RAM usagefree -h
SD card storagedf -h
RPi modelcat /proc/device-tree/model
CPU infocat /proc/cpuinfo | grep Model
Check serial portsls /dev/ttyACM* /dev/ttyUSB*

Turtlebot 3 Demos

Within the configuration process (next section) are 10 demos which look at different aspects of the Turtlebot 3 (Burger). These demos and some of the results are described in this section. Note that the results are sometimes continuous and a lot of text. They are shortened in the following as illustration. Command lines are indicated by >.

Demo 1 – Bringup – >ros2 launch turtlebot3_bringup robot.launch.py

[INFO] [launch]: All log files can be found below
/home/dennismiller/.ros/log/2026-02-07-17-25-26-278663-dennismiller-desktop-3651
[INFO] [launch]: Default logging verbosity is set to INFO
urdf_file_name : turtlebot3_burger.urdf ….

Demo 2 – Keyboard teleop

>ros2 launch turtlebot3_bringup robot.launch.py

>ros2 run turtlebot3_teleop teleop_keyboard

Control Your TurtleBot3!
—————————
Moving around:
         w
    a    s    d
         x

w/x : increase/decrease linear velocity (Burger : ~ 0.22, Waffle and
Waffle Pi : ~ 0.26)
a/d : increase/decrease angular velocity (Burger : ~ 2.84, Waffle and
Waffle Pi : ~ 1.82)

space key, s : force stop

CTRL-C to quit

currently:    linear velocity 0.01     angular velocity 0.0
currently:    linear velocity 0.02     angular velocity 0.0
currently:    linear velocity 0.03     angular velocity 0.0
currently:    linear velocity 0.04     angular velocity 0.0

Demo 3 – SLAM mapping

>ros2 launch turtlebot3_bringup robot.launch.py

>ros2 launch turtlebot3_cartographer cartographer.launch.py

[INFO] [launch]: All log files can be found below
/home/dennismiller/.ros/log/2026-02-07-19-56-40-958956-dennismiller-desktop-5068
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [cartographer_node-1]: process started with pid [5070]
[INFO]

Demo 4 – Save map

>mkdir -p ~/maps

>ros2 run nav2_map_server map_saver_cli -f ~/maps/my_first_map

Saves two files:

  • ~/maps/my_first_map.yaml — map metadata
  • ~/maps/my_first_map.pgm — map image (grayscale)

View the map image – eog ~/maps/my_first_map.pgm

Demo 5 – Autonomous navigation

>ros2 launch turtlebot3_bringup rviz2.launch.py

>ros2 launch turtlebot3_navigation2 navigation2.launch.pymap:=$HOME/maps/my_first_map.yaml

This command starts navigation and calls the SLAM map from the prior demo. As this opened, the responsiveness of the RPI 4 was very low; it was difficult to determine if selections were used. In the Rviz header, ‘2D Pose Estimate’ was selected and then the current position selected. ‘2D Goal Pose’ is was selected and then the desire position on the map was selected. A path was calculated as shown. The Turtlebots wheels were driven in an attempt to move it to the selected location. While the basic idea worked the combination of the slow response, selection uncertainty and physical setup didn’t result in the robot moving as shown.

RViz Navigation Explained

What you’re seeing:

ColorWhat it is
Green particlesRobot’s estimated positions (localization uncertainty)
Blue linePlanned global path to goal
Red lines/areasCostmap – obstacles and inflation zones (keep-out areas)
Black areasWalls/obstacles from your saved map
Gray areasFree space
White areasUnknown space

Demo 6 – ROS Topic list

>ros2 topic list

battery_state
/cmd_vel
/imu
/joint_states
/magnetic_field
/odom
/parameter_events
/robot_description
/rosout
/scan
/sensor_state
/tf
/tf_static

>dennismiller@dennismiller-desktop:~$ ros2 topic info /scan
Type: sensor_msgs/msg/LaserScan
Publisher count: 1
Subscription count: 0


>dennismiller@dennismiller-desktop:~$ ros2 topic hz /scan
WARNING: topic [/scan] does not appear to be published yet
average rate: 4.985
    min: 0.200s max: 0.201s std dev: 0.00030s

window: 6 (Note: ‘window’ inidicates the number of messages  sampled; it’s cummulative)
average rate: 4.984
    min: 0.200s max: 0.201s std dev: 0.00033s

window: 11
average rate: 4.984
    min: 0.199s max: 0.202s std dev: 0.00054s

…..

Demo 7 – LIDAR data

>ros2 launch turtlebot3_bringup rviz2.launch.py

>ros2 topic echo /scan –once

header:
  stamp:
    sec: 1770571607
    nanosec: 524285835
  frame_id: base_scan
angle_min: 0.0
angle_max: 6.2657318115234375
angle_increment: 0.01745329238474369
time_increment: 0.0005592841189354658
scan_time: 0.20134228467941284
range_min: 0.11999999731779099
range_max: 3.5
ranges:
– 0.4399999976158142
– 0.43299999833106995
– 0.4300000071525574

…..

LIDAR is publishing at 5 Hz (normal for LDS-01)

Key fields:

FieldMeaning
angle_min / angle_maxScan coverage (0 to 6.28 = 360°)
range_min / range_maxValid distance range (0.12m to 3.5m)
rangesArray of distances for each angle
intensitiesSignal strength at each angle

Demo 8 – Odometry data

>ros2 launch turtlebot3_bringup robot.launch.py

>ros2 topic echo /odom –once

stamp: sec: 1770572248 nanosec: 212764219

 frame_id: odom

child_frame_id: base_footprint

pose:

pose:

position: x: 15.919378026437194  y: 0.33155781798783285 z: 0.0

orientation: x: 0.0 y: 0.0 z: 0.013453528466268046 w: 0.9999094971905244

covariance: – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0.0 – 0

Covariance is a 6×6 matrix (36 values) representing uncertainty in the measurement. It tells other nodes “how confident” the odometry is. 0.0 is ‘don’t know’/’not provided’, ~.001 high confidence, ~1+ low confidence.

Running the wheels with teleop resulted in :

Field                            Value                           Meaning

position.x                    15.92 m                       Robot traveled ~16 meters

forwardposition.y       0.33 m                         Slight drift sideways

twist.linear.x               0.086 m/s                    Currently moving forward slowly

twist.angular.z            -0.003 rad/s                 Tiny right turn

Key fields:

FieldMeaning
pose.position.xDistance traveled forward (meters)
pose.position.yDistance traveled sideways (meters)
pose.orientationRobot heading (quaternion)
twist.linear.xCurrent forward velocity (m/s)
twist.angular.zCurrent rotation rate (rad/s)

Demo 9 – Rviz visualization

Stop ROS and restart – >ros2 launch turtlebot3_bringup robot.launch.py

>ros2 launch turtlebot3_bringup rviz2.launch.py

Rviz with LIDAR data points; color is intensity

Demo 10 – System Status

# ROS2 version: >echo $TURTLEBOT3_MODEL – humble

# Active nodes: >ros2 node list –

# Active topics: >ros2 topic list –

# Active services: >ros2 service list –

# Sensor states: >ros2 topic echo /sensor_state –once –

# RPI 4 RAM: >free -h –

# RPI 4 SD Storage: >df -h –

# RPI 4 Temperature: >cat /sys/class/thermal/thermal_zone0/temp (divide by 1000 for C) –

# Serial Ports: >ls /dev/ttyACM0 /dev/ttyUSB0 –

ROS2 nodes:

>ros2 launch turtlebot3_bringup robot.launch.py

>ros2 node list

NodePurpose
/turtlebot3_nodeMotor control, OpenCR communication
/hlds_laser_publisherLiDAR driver (LDS-01)
/robot_state_publisherPublishes robot URDF/transforms

When Running Teleop

NodePurpose
/teleop_keyboardKeyboard input → /cmd_vel

When Running SLAM (Cartographer)

NodePurpose
/cartographer_nodeSLAM processing
/cartographer_occupancy_grid_nodeGenerates map grid

When Running Navigation

NodePurpose
/bt_navigatorBehavior tree executor
/controller_serverPath following
/planner_serverPath planning
/map_serverServes the saved map
/amclLocalization (particle filter)
/lifecycle_managerManages node states

When Running RViz

NodePurpose
/rviz2Visualization

Logitech Controller

Add Xbox/Microsoft Controller

Step 1: Install Xbox driver and joystick packages

bash

# Install Xbox controller driver

>sudo apt install xboxdrv joystick jstest-gtk

# Install ROS2 joystick packages

>sudo apt install ros-humble-joy ros-humble-teleop-twist-joy

Step 2: Connect controller and verify

bash

# Plug in Xbox controller via USB, then check

>ls /dev/input/js*

# Should show /dev/input/js0

# Test the controller

>jstest /dev/input/js0

# Or with GUI

>jstest-gtk

Step 3: Set permissions

bash

# Add user to input group

>sudo usermod -aG input $USER

# Create udev rule for controller

>echo ‘KERNEL==”js*”, MODE=”0666″‘ | sudo tee /etc/udev/rules.d/99-joystick.rules

>sudo udevadm control –reload-rules

>sudo udevadm trigger

Logout and login for group changes.

Step 4: Test with ROS2

Terminal 1 – Launch robot:

bash

>ros2 launch turtlebot3_bringup robot.launch.py

Terminal 2 – Launch joystick node:

bash

>ros2 run joy joy_node

Terminal 3 – Check joystick data:

bash

>ros2 topic echo /joy

Move sticks and press buttons – you should see values change.

Step 5: Launch teleop with joystick

Terminal 2 – Replace joy_node with full teleop:

bash

>ros2 launch teleop_twist_joy teleop-launch.py joy_config:=’xbox’

Controller Mapping (Xbox):

ControlAction
Left stick up/downForward/backward
Right stick left/rightTurn left/right
LB (left bumper)Hold to enable movement (deadman switch)
RB (right bumper)Hold for turbo speed

Step 6: Create a launch script

bash

>cat > ~/turtlebot3_demos/xbox_teleop.sh << ‘EOF’

#!/bin/bash

>source /opt/ros/humble/setup.bash

>source ~/turtlebot3_ws/install/setup.bash

>echo “Xbox Controller Teleop for TurtleBot3”

>echo “”

>echo “Controls:”

>echo ”  Left stick    : Forward/Backward”

>echo ”  Right stick   : Turn Left/Right”

>echo ”  LB (hold)     : Enable movement”

>echo ”  RB (hold)     : Turbo mode”

>echo “”

>echo “Make sure bringup is running first!”

>echo “”

>ros2 launch teleop_twist_joy teleop-launch.py joy_config:=’xbox’

>EOF

>chmod +x ~/turtlebot3_demos/xbox_teleop.sh

Duplicate SD card On Linux PC

# Insert SD card into PC, find the device name

>lsblk

# Usually shows as /dev/sdb or /dev/mmcblk0

# Make sure to identify the CORRECT device!

# Create image (replace sdX with your device)

>sudo dd if=/dev/sdX of=~/turtlebot3_backup.img bs=4M status=progress

# Compress it (optional, saves space)

>gzip ~/turtlebot3_backup.img

To restore to a new SD card:

# Decompress if needed

>gunzip ~/turtlebot3_backup.img.gz

# Write to new SD card (replace sdX)

>sudo dd if=~/turtlebot3_backup.img of=/dev/sdX bs=4M status=progress

Simulation with Gazebo

While technically feasible to run the Turtlebot 3 as a Gazebo simulation, the computational power required is really beyond the RPI 4B with 4 Gb of RAM. I didn’t try to run it.

Configuration Using Python Commands

The entire configuration process was done through a Python script that ran the required commands. For avid Python coders this is likely an obvious process. For me as a sometimes Python coder it was … amazing. The Python script follows.

#!/bin/bash
#===============================================================================
# TurtleBot3 Setup Script for Raspberry Pi 4 with Ubuntu 22.04 and ROS2
Humble
#
# This script performs:
#   1. RPi 4 System Characteristics Detection
#   2. ROS2 Humble Installation
#   3. TurtleBot3 Packages and Demo Scripts Installation
#
# Usage: chmod +x turtlebot3_rpi4_setup.sh && ./turtlebot3_rpi4_setup.sh
#===============================================================================

set -e  # Exit on error

# Colors for output
RED=’\033[0;31m’
GREEN=’\033[0;32m’
YELLOW=’\033[1;33m’
BLUE=’\033[0;34m’
NC=’\033[0m’ # No Color

print_header() {
     echo -e
“\n${BLUE}============================================================${NC}”
     echo -e “${BLUE}$1${NC}”
     echo -e
“${BLUE}============================================================${NC}\n”
}

print_success() {
     echo -e “${GREEN}✓ $1${NC}”
}

print_warning() {
     echo -e “${YELLOW}⚠ $1${NC}”
}

print_error() {
     echo -e “${RED}✗ $1${NC}”
}

#===============================================================================
# PART 1: RASPBERRY PI 4 SYSTEM CHARACTERISTICS
#===============================================================================
detect_rpi4_characteristics() {
     print_header “PART 1: RASPBERRY PI 4 SYSTEM CHARACTERISTICS”

     OUTPUT_FILE=”$HOME/rpi4_characteristics.txt”

     echo “Raspberry Pi 4 System Characteristics Report” > “$OUTPUT_FILE”
     echo “Generated: $(date)” >> “$OUTPUT_FILE”
     echo “=============================================” >> “$OUTPUT_FILE”

     # CPU Information
     echo -e “\n${GREEN}— CPU Information —${NC}”
     echo -e “\nCPU Information:” >> “$OUTPUT_FILE”

     if [ -f /proc/cpuinfo ]; then
         CPU_MODEL=$(grep “Model” /proc/cpuinfo | head -1 | cut -d: -f2
| xargs)
         CPU_HARDWARE=$(grep “Hardware” /proc/cpuinfo | head -1 | cut
-d: -f2 | xargs)
         CPU_REVISION=$(grep “Revision” /proc/cpuinfo | head -1 | cut
-d: -f2 | xargs)
         CPU_CORES=$(nproc)

         echo ”  Model: $CPU_MODEL”
         echo ”  Hardware: $CPU_HARDWARE”
         echo ”  Revision: $CPU_REVISION”
         echo ”  CPU Cores: $CPU_CORES”

         echo ”  Model: $CPU_MODEL” >> “$OUTPUT_FILE”
         echo ”  Hardware: $CPU_HARDWARE” >> “$OUTPUT_FILE”
         echo ”  Revision: $CPU_REVISION” >> “$OUTPUT_FILE”
         echo ”  CPU Cores: $CPU_CORES” >> “$OUTPUT_FILE”
     fi

     # CPU Architecture
     ARCH=$(uname -m)
     echo ”  Architecture: $ARCH”
     echo ”  Architecture: $ARCH” >> “$OUTPUT_FILE”

     # CPU Frequency
     if [ -f /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq ]; then
         MAX_FREQ=$(cat
/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq)
         MAX_FREQ_MHZ=$((MAX_FREQ / 1000))
         echo ”  Max CPU Frequency: ${MAX_FREQ_MHZ} MHz”
         echo ”  Max CPU Frequency: ${MAX_FREQ_MHZ} MHz” >> “$OUTPUT_FILE”
     fi

     # Memory Information
     echo -e “\n${GREEN}— Memory Information —${NC}”
     echo -e “\nMemory Information:” >> “$OUTPUT_FILE”

     TOTAL_MEM=$(free -h | grep Mem | awk ‘{print $2}’)
     USED_MEM=$(free -h | grep Mem | awk ‘{print $3}’)
     FREE_MEM=$(free -h | grep Mem | awk ‘{print $4}’)

     echo ”  Total RAM: $TOTAL_MEM”
     echo ”  Used RAM: $USED_MEM”
     echo ”  Free RAM: $FREE_MEM”

     echo ”  Total RAM: $TOTAL_MEM” >> “$OUTPUT_FILE”
     echo ”  Used RAM: $USED_MEM” >> “$OUTPUT_FILE”
     echo ”  Free RAM: $FREE_MEM” >> “$OUTPUT_FILE”

     # Storage Information
     echo -e “\n${GREEN}— Storage Information —${NC}”
     echo -e “\nStorage Information:” >> “$OUTPUT_FILE”

     df -h / | tail -1 | while read filesystem size used avail use_pct
mounted; do
         echo ”  Root Filesystem: $filesystem”
         echo ”  Total Size: $size”
         echo ”  Used: $used ($use_pct)”
         echo ”  Available: $avail”

         echo ”  Root Filesystem: $filesystem” >> “$OUTPUT_FILE”
         echo ”  Total Size: $size” >> “$OUTPUT_FILE”
         echo ”  Used: $used ($use_pct)” >> “$OUTPUT_FILE”
         echo ”  Available: $avail” >> “$OUTPUT_FILE”
     done

     # Temperature
     echo -e “\n${GREEN}— Temperature —${NC}”
     echo -e “\nTemperature:” >> “$OUTPUT_FILE”

     if [ -f /sys/class/thermal/thermal_zone0/temp ]; then
         TEMP=$(cat /sys/class/thermal/thermal_zone0/temp)
         TEMP_C=$(echo “scale=1; $TEMP / 1000” | bc)
         echo ”  CPU Temperature: ${TEMP_C}°C”
         echo ”  CPU Temperature: ${TEMP_C}°C” >> “$OUTPUT_FILE”
     fi

     # GPU Memory (if vcgencmd available)
     if command -v vcgencmd &> /dev/null; then
         echo -e “\n${GREEN}— GPU Information —${NC}”
         echo -e “\nGPU Information:” >> “$OUTPUT_FILE”

         GPU_MEM=$(vcgencmd get_mem gpu 2>/dev/null || echo “N/A”)
         echo ”  $GPU_MEM”
         echo ”  $GPU_MEM” >> “$OUTPUT_FILE”
     fi

     # OS Information
     echo -e “\n${GREEN}— Operating System —${NC}”
     echo -e “\nOperating System:” >> “$OUTPUT_FILE”

     if [ -f /etc/os-release ]; then
         source /etc/os-release
         echo ”  OS: $PRETTY_NAME”
         echo ”  Kernel: $(uname -r)”

         echo ”  OS: $PRETTY_NAME” >> “$OUTPUT_FILE”
         echo ”  Kernel: $(uname -r)” >> “$OUTPUT_FILE”
     fi

     # Network Interfaces
     echo -e “\n${GREEN}— Network Interfaces —${NC}”
     echo -e “\nNetwork Interfaces:” >> “$OUTPUT_FILE”

     ip -o link show | while read num iface rest; do
         IFACE_NAME=$(echo $iface | tr -d ‘:’)
         if [[ “$IFACE_NAME” != “lo” ]]; then
             IP_ADDR=$(ip -4 addr show $IFACE_NAME 2>/dev/null | grep
inet | awk ‘{print $2}’ | head -1)
             if [ -n “$IP_ADDR” ]; then
                 echo ”  $IFACE_NAME: $IP_ADDR”
                 echo ”  $IFACE_NAME: $IP_ADDR” >> “$OUTPUT_FILE”
             fi
         fi
     done

     # USB Devices
     echo -e “\n${GREEN}— USB Devices —${NC}”
     echo -e “\nUSB Devices:” >> “$OUTPUT_FILE”

     if command -v lsusb &> /dev/null; then
         lsusb | while read line; do
             echo ”  $line”
             echo ”  $line” >> “$OUTPUT_FILE”
         done
     fi

     # Serial Ports (important for TurtleBot)
     echo -e “\n${GREEN}— Serial Ports (for OpenCR) —${NC}”
     echo -e “\nSerial Ports:” >> “$OUTPUT_FILE”

     ls -la /dev/ttyACM* /dev/ttyUSB* 2>/dev/null | while read line; do
         echo ”  $line”
         echo ”  $line” >> “$OUTPUT_FILE”
     done || echo ”  No serial ports detected”

     print_success “Characteristics saved to: $OUTPUT_FILE”
}

#===============================================================================
# PART 2: ROS2 HUMBLE INSTALLATION
#===============================================================================
install_ros2_humble() {
     print_header “PART 2: ROS2 HUMBLE INSTALLATION”

     # Check if ROS2 is already installed
     if [ -d “/opt/ros/humble” ]; then
         print_warning “ROS2 Humble appears to be already installed at
/opt/ros/humble”
         read -p “Do you want to reinstall? (y/N): ” REINSTALL
         if [[ ! “$REINSTALL” =~ ^[Yy]$ ]]; then
             print_success “Skipping ROS2 installation”
             return
         fi
     fi

     echo “Setting up locale…”
     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
     export LANG=en_US.UTF-8
     print_success “Locale configured”

     echo “Adding ROS2 repository…”
     sudo apt install -y software-properties-common
     sudo add-apt-repository -y universe

     sudo apt update && sudo apt install -y curl gnupg lsb-release
     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 $(source /etc/os-release &&
echo $UBUNTU_CODENAME) main” | \
         sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
     print_success “ROS2 repository added”

     echo “Updating package lists…”
     sudo apt update
     sudo apt upgrade -y

     echo “Installing ROS2 Humble…”
     # For RPi without display, use ros-base. With display, use desktop
     read -p “Install with GUI tools (desktop) or minimal (ros-base)?
[desktop/base]: ” ROS_TYPE

     if [[ “$ROS_TYPE” == “desktop” ]]; then
         sudo apt install -y ros-humble-desktop
         print_success “ROS2 Humble Desktop installed”
     else
         sudo apt install -y ros-humble-ros-base
         print_success “ROS2 Humble Base installed”
     fi

     # Install development tools
     echo “Installing development tools…”
     sudo apt install -y ros-dev-tools python3-colcon-common-extensions
python3-rosdep
     print_success “Development tools installed”

     # Initialize rosdep
     if [ ! -f /etc/ros/rosdep/sources.list.d/20-default.list ]; then
         echo “Initializing rosdep…”
         sudo rosdep init
     fi
     rosdep update
     print_success “rosdep initialized”

     # Add ROS2 to bashrc
     if ! grep -q “source /opt/ros/humble/setup.bash” ~/.bashrc; then
         echo “source /opt/ros/humble/setup.bash” >> ~/.bashrc
         print_success “ROS2 added to .bashrc”
     fi

     # Source for current session
     source /opt/ros/humble/setup.bash

     print_success “ROS2 Humble installation complete!”
}

#===============================================================================
# PART 3: TURTLEBOT3 PACKAGES AND DEMOS
#===============================================================================
install_turtlebot3() {
     print_header “PART 3: TURTLEBOT3 PACKAGES AND DEMOS”

     # Source ROS2
     source /opt/ros/humble/setup.bash

     # Determine TurtleBot3 model
     echo “Which TurtleBot3 model do you have?”
     echo ”  1) burger”
     echo ”  2) waffle”
     echo ”  3) waffle_pi”
     read -p “Enter choice [1-3]: ” TB3_CHOICE

     case $TB3_CHOICE in
         1) TB3_MODEL=”burger” ;;
         2) TB3_MODEL=”waffle” ;;
         3) TB3_MODEL=”waffle_pi” ;;
         *) TB3_MODEL=”burger” ;;
     esac

     # Determine LiDAR model
     echo “Which LiDAR model do you have?”
     echo ”  1) LDS-01 (older TurtleBot3)”
     echo ”  2) LDS-02 (newer TurtleBot3)”
     read -p “Enter choice [1-2]: ” LDS_CHOICE

     case $LDS_CHOICE in
         1) LDS_MODEL=”LDS-01″ ;;
         2) LDS_MODEL=”LDS-02″ ;;
         *) LDS_MODEL=”LDS-01″ ;;
     esac

     # Install TurtleBot3 dependencies
     echo “Installing TurtleBot3 dependencies…”
     sudo apt install -y \
         ros-humble-cartographer \
         ros-humble-cartographer-ros \
         ros-humble-navigation2 \
         ros-humble-nav2-bringup \
         ros-humble-dynamixel-sdk \
         ros-humble-turtlebot3-msgs \
         ros-humble-turtlebot3

     print_success “TurtleBot3 base packages installed”

     # Create TurtleBot3 workspace
     echo “Creating TurtleBot3 workspace…”
     mkdir -p ~/turtlebot3_ws/src
     cd ~/turtlebot3_ws/src

     # Clone TurtleBot3 packages from source for latest updates
     echo “Cloning TurtleBot3 source packages…”

     if [ ! -d “DynamixelSDK” ]; then
         git clone -b humble https://github.com/ROBOTIS-GIT/DynamixelSDK.git
     fi

     if [ ! -d “turtlebot3_msgs” ]; then
         git clone -b humble
https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git
     fi

     if [ ! -d “turtlebot3” ]; then
         git clone -b humble https://github.com/ROBOTIS-GIT/turtlebot3.git
     fi

     if [ ! -d “turtlebot3_simulations” ]; then
         git clone -b humble-devel
https://github.com/ROBOTIS-GIT/turtlebot3_simulations.git
     fi

     # Install LiDAR driver based on model
     echo “Installing LiDAR driver…”
     if [ ! -d “ld08_driver” ] && [ “$LDS_MODEL” == “LDS-02” ]; then
         git clone -b ros2-devel
https://github.com/ROBOTIS-GIT/ld08_driver.git
     elif [ ! -d “hls_lfcd_lds_driver” ] && [ “$LDS_MODEL” == “LDS-01”
]; then
         git clone -b ros2-devel
https://github.com/ROBOTIS-GIT/hls_lfcd_lds_driver.git
     fi

     print_success “Source packages cloned”

     # Install dependencies with rosdep
     echo “Installing dependencies with rosdep…”
     cd ~/turtlebot3_ws
     rosdep install –from-paths src –ignore-src -r -y || true

     # Build workspace
     echo “Building TurtleBot3 workspace (this may take a while on RPi)…”
     cd ~/turtlebot3_ws
     colcon build –symlink-install –parallel-workers 2

     print_success “TurtleBot3 workspace built”

     # Add TurtleBot3 environment to bashrc
     echo “Configuring environment…”

     # Remove old entries if they exist
     sed -i ‘/TURTLEBOT3_MODEL/d’ ~/.bashrc
     sed -i ‘/LDS_MODEL/d’ ~/.bashrc
     sed -i ‘/ROS_DOMAIN_ID.*TURTLEBOT/d’ ~/.bashrc
     sed -i ‘/turtlebot3_ws/d’ ~/.bashrc

     # Add new entries
     cat >> ~/.bashrc << EOF

# TurtleBot3 Configuration
source ~/turtlebot3_ws/install/setup.bash
export TURTLEBOT3_MODEL=$TB3_MODEL
export LDS_MODEL=$LDS_MODEL
export ROS_DOMAIN_ID=30  # Match this with Remote PC
export OPENCR_PORT=/dev/ttyACM0
export OPENCR_MODEL=$TB3_MODEL
EOF

     print_success “Environment configured in .bashrc”

     # Set up udev rules for OpenCR and LiDAR
     echo “Setting up udev rules for hardware…”

     # OpenCR udev rule
     sudo bash -c ‘cat > /etc/udev/rules.d/99-opencr.rules << EOF
ATTRS{idVendor}==”0483″, ATTRS{idProduct}==”5740″,
ENV{ID_MM_DEVICE_IGNORE}=”1″, MODE=”0666″, GROUP=”dialout”,
SYMLINK+=”opencr”
EOF’

     # LiDAR udev rule
     if [ “$LDS_MODEL” == “LDS-01” ]; then
         sudo bash -c ‘cat > /etc/udev/rules.d/99-lds01.rules << EOF
KERNEL==”ttyUSB*”, ATTRS{idVendor}==”0483″, ATTRS{idProduct}==”5740″,
MODE=”0666″, GROUP=”dialout”, SYMLINK+=”ttyLDS”
EOF’
     else
         sudo bash -c ‘cat > /etc/udev/rules.d/99-lds02.rules << EOF
KERNEL==”ttyUSB*”, ATTRS{idVendor}==”10c4″, ATTRS{idProduct}==”ea60″,
MODE=”0666″, GROUP=”dialout”, SYMLINK+=”ttyLDS”
EOF’
     fi

     # Reload udev rules
     sudo udevadm control –reload-rules
     sudo udevadm trigger

     # Add user to dialout group
     sudo usermod -aG dialout $USER

     print_success “udev rules configured”

     # Create demo launch scripts
     create_demo_scripts

     print_success “TurtleBot3 installation complete!”
}

#===============================================================================
# CREATE DEMO SCRIPTS
#===============================================================================
create_demo_scripts() {
     print_header “Creating TurtleBot3 Demo Scripts”

     DEMO_DIR=”$HOME/turtlebot3_demos”
     mkdir -p “$DEMO_DIR”

     # Demo 1: Bringup (run on TurtleBot)
     cat > “$DEMO_DIR/01_bringup.sh” << ‘EOF’
#!/bin/bash
# TurtleBot3 Bringup – Run this on the TurtleBot3 (RPi)
# This starts the robot’s core systems: motors, sensors, etc.

source /opt/ros/humble/setup.bash
source ~/turtlebot3_ws/install/setup.bash

echo “Starting TurtleBot3 Bringup…”
echo “Model: $TURTLEBOT3_MODEL”
echo “LiDAR: $LDS_MODEL”
echo “”
echo “Press Ctrl+C to stop”

ros2 launch turtlebot3_bringup robot.launch.py
EOF
     chmod +x “$DEMO_DIR/01_bringup.sh”

     # Demo 2: Teleop Keyboard
     cat > “$DEMO_DIR/02_teleop_keyboard.sh” << ‘EOF’
#!/bin/bash
# Keyboard Teleoperation – Control TurtleBot3 with keyboard
# Run this on Remote PC or TurtleBot

source /opt/ros/humble/setup.bash
source ~/turtlebot3_ws/install/setup.bash

echo “Keyboard Teleoperation for TurtleBot3”
echo “”
echo “Controls:”
echo ”  w/x : increase/decrease linear velocity”
echo ”  a/d : increase/decrease angular velocity”
echo ”  space/s : stop”
echo “”

ros2 run turtlebot3_teleop teleop_keyboard
EOF
     chmod +x “$DEMO_DIR/02_teleop_keyboard.sh”

     # Demo 3: SLAM (Cartographer)
     cat > “$DEMO_DIR/03_slam_cartographer.sh” << ‘EOF’
#!/bin/bash
# SLAM with Cartographer – Create a map while driving
# Run on Remote PC while bringup is running on TurtleBot

source /opt/ros/humble/setup.bash
source ~/turtlebot3_ws/install/setup.bash

echo “Starting SLAM with Cartographer…”
echo “Make sure bringup is running on TurtleBot3”
echo “”

ros2 launch turtlebot3_cartographer cartographer.launch.py
EOF
     chmod +x “$DEMO_DIR/03_slam_cartographer.sh”

     # Demo 4: Save Map
     cat > “$DEMO_DIR/04_save_map.sh” << ‘EOF’
#!/bin/bash
# Save the map created by SLAM
# Run this after exploring the environment

source /opt/ros/humble/setup.bash

MAP_NAME=${1:-“my_map”}
echo “Saving map as: $MAP_NAME”

ros2 run nav2_map_server map_saver_cli -f ~/maps/$MAP_NAME

echo “Map saved to ~/maps/$MAP_NAME.yaml and ~/maps/$MAP_NAME.pgm”
EOF
     chmod +x “$DEMO_DIR/04_save_map.sh”

     # Demo 5: Navigation
     cat > “$DEMO_DIR/05_navigation.sh” << ‘EOF’
#!/bin/bash
# Autonomous Navigation – Navigate using a saved map
# Run on Remote PC

source /opt/ros/humble/setup.bash
source ~/turtlebot3_ws/install/setup.bash

MAP_FILE=${1:-“$HOME/maps/my_map.yaml”}

echo “Starting Navigation with map: $MAP_FILE”
echo “Make sure bringup is running on TurtleBot3”
echo “”

ros2 launch turtlebot3_navigation2 navigation2.launch.py map:=$MAP_FILE
EOF
     chmod +x “$DEMO_DIR/05_navigation.sh”

     # Demo 6: Check Topics
     cat > “$DEMO_DIR/06_check_topics.sh” << ‘EOF’
#!/bin/bash
# List all active ROS2 topics
# Useful for debugging

source /opt/ros/humble/setup.bash

echo “Active ROS2 Topics:”
echo “===================”
ros2 topic list

echo “”
echo “To see topic data, use: ros2 topic echo /topic_name”
echo “To see topic info, use: ros2 topic info /topic_name”
EOF
     chmod +x “$DEMO_DIR/06_check_topics.sh”

     # Demo 7: View LiDAR Data
     cat > “$DEMO_DIR/07_view_lidar.sh” << ‘EOF’
#!/bin/bash
# View LiDAR scan data
# Run while bringup is active

source /opt/ros/humble/setup.bash

echo “Viewing LiDAR data from /scan topic”
echo “Press Ctrl+C to stop”
echo “”

ros2 topic echo /scan
EOF
     chmod +x “$DEMO_DIR/07_view_lidar.sh”

     # Demo 8: View Odometry
     cat > “$DEMO_DIR/08_view_odom.sh” << ‘EOF’
#!/bin/bash
# View odometry data
# Run while bringup is active

source /opt/ros/humble/setup.bash

echo “Viewing Odometry data from /odom topic”
echo “Press Ctrl+C to stop”
echo “”

ros2 topic echo /odom
EOF
     chmod +x “$DEMO_DIR/08_view_odom.sh”

     # Demo 9: RViz Visualization (for Remote PC with display)
     cat > “$DEMO_DIR/09_rviz.sh” << ‘EOF’
#!/bin/bash
# Launch RViz for visualization
# Run on Remote PC with display

source /opt/ros/humble/setup.bash
source ~/turtlebot3_ws/install/setup.bash

echo “Launching RViz…”

ros2 launch turtlebot3_bringup rviz2.launch.py
EOF
     chmod +x “$DEMO_DIR/09_rviz.sh”

     # Demo 10: System Status
     cat > “$DEMO_DIR/10_system_status.sh” << ‘EOF’
#!/bin/bash
# Check ROS2 system status

source /opt/ros/humble/setup.bash

echo “ROS2 System Status”
echo “==================”

echo “”
echo “ROS2 Version:”
ros2 –version

echo “”
echo “Active Nodes:”
ros2 node list

echo “”
echo “Active Topics:”
ros2 topic list

echo “”
echo “Active Services:”
ros2 service list
EOF
     chmod +x “$DEMO_DIR/10_system_status.sh”

     # Create README for demos
     cat > “$DEMO_DIR/README.md” << EOF
# TurtleBot3 Demo Scripts

## Quick Start

### On TurtleBot3 (Raspberry Pi):
1. First, run bringup:
    \`\`\`
    ./01_bringup.sh
    \`\`\`

### On Remote PC (or another terminal):
2. Control with keyboard:
    \`\`\`
    ./02_teleop_keyboard.sh
    \`\`\`

## Available Demos

| Script | Description | Run On |
|——–|————-|——–|
| 01_bringup.sh | Start robot systems | TurtleBot3 |
| 02_teleop_keyboard.sh | Keyboard control | Remote PC |
| 03_slam_cartographer.sh | Create map while driving | Remote PC |
| 04_save_map.sh | Save SLAM map | Remote PC |
| 05_navigation.sh | Autonomous navigation | Remote PC |
| 06_check_topics.sh | List ROS2 topics | Any |
| 07_view_lidar.sh | View LiDAR data | Any |
| 08_view_odom.sh | View odometry | Any |
| 09_rviz.sh | Visual monitoring | Remote PC (GUI) |
| 10_system_status.sh | Check system status | Any |

## Network Setup

Make sure TurtleBot3 and Remote PC are on the same network and have
matching:
– ROS_DOMAIN_ID (default: 30)
– Same ROS2 version (Humble)

## Troubleshooting

1. **Can’t connect to robot**: Check ROS_DOMAIN_ID matches
2. **No LiDAR data**: Check USB connection and udev rules
3. **Motors not moving**: Check OpenCR connection (/dev/ttyACM0)

## Environment Variables

Current configuration (in ~/.bashrc):
– TURTLEBOT3_MODEL=$TB3_MODEL
– LDS_MODEL=$LDS_MODEL
– ROS_DOMAIN_ID=30
EOF

     # Create maps directory
     mkdir -p ~/maps

     print_success “Demo scripts created in: $DEMO_DIR”
     echo “”
     echo “Available demo scripts:”
     ls -la “$DEMO_DIR”/*.sh
}

#===============================================================================
# MAIN MENU
#===============================================================================
main_menu() {
     clear
     echo -e “${BLUE}”
     echo “╔══════════════════════════════════════════════════════════════╗”
     echo “║     TurtleBot3 Setup for Raspberry Pi 4 + Ubuntu 22.04      ║”
     echo “║                    with ROS2 Humble                          ║”
     echo “╚══════════════════════════════════════════════════════════════╝”
     echo -e “${NC}”
     echo “”
     echo “Select an option:”
     echo “”
     echo ”  1) Run ALL steps (recommended for fresh install)”
     echo ”  2) Part 1 only: Detect RPi4 characteristics”
     echo ”  3) Part 2 only: Install ROS2 Humble”
     echo ”  4) Part 3 only: Install TurtleBot3 packages & demos”
     echo ”  5) Exit”
     echo “”
     read -p “Enter choice [1-5]: ” CHOICE

     case $CHOICE in
         1)
             detect_rpi4_characteristics
             install_ros2_humble
             install_turtlebot3
             ;;
         2)
             detect_rpi4_characteristics
             ;;
         3)
             install_ros2_humble
             ;;
         4)
             install_turtlebot3
             ;;
         5)
             echo “Exiting…”
             exit 0
             ;;
         *)
             echo “Invalid choice”
             exit 1
             ;;
     esac
}

#===============================================================================
# FINAL SUMMARY
#===============================================================================
print_final_summary() {
     print_header “SETUP COMPLETE!”

     echo “Summary of installed components:”
     echo “”
     echo ”  1. RPi4 Characteristics: ~/rpi4_characteristics.txt”
     echo ”  2. ROS2 Humble: /opt/ros/humble/”
     echo ”  3. TurtleBot3 Workspace: ~/turtlebot3_ws/”
     echo ”  4. Demo Scripts: ~/turtlebot3_demos/”
     echo “”
     echo “Next steps:”
     echo “”
     echo ”  1. Log out and log back in (or reboot) for group changes”
     echo ”  2. Connect your TurtleBot3 hardware”
     echo ”  3. Run: cd ~/turtlebot3_demos && ./01_bringup.sh”
     echo “”
     echo “For keyboard control, open another terminal and run:”
     echo ”  cd ~/turtlebot3_demos && ./02_teleop_keyboard.sh”
     echo “”
     print_success “Happy robotics!”
}

#===============================================================================
# RUN SCRIPT
#===============================================================================
main_menu
print_final_summary