π¨Powering Bots Up Together

Table of contents
- π Robot Town Needs an Alarm Clock
- π§ Analogy: Launch File = Town Sunrise Schedule
- ποΈ Step 1: Set Up a New Package: town_starter
- π οΈ Step 2: Add Our New Bots β TrafficBot & LightBot
- π§ͺ Step 3: Launch File Basics (Wake-up Schedule)
- π Step 4: Update setup.py to Include the Launch File
- π§ͺ Step 5: Build the Whole Town Again
- π Step 6: Try Launching It (No Real Bots Yet!)
- π Mini Map: Where Are We?
- π Summary So Far
- π οΈ Building the Bots Weβll Launch
- π¦ TrafficBot: Keeps an Eye on Town Roads
- π‘ LightBot: Manages Street Lights
- π¦ Rebuild Robot Town!
- π Time to Launch the Whole Town!
- ποΈ Recap of Folder Structure
- π‘ Concept Recap
- π― Mini Project Review
- π Whatβs Next: The Robot Town Ledger β YAML & Launch Files Mastery

π Robot Town Needs an Alarm Clock
So far, you've been waking each bot up manually like:
ros2 run weather_bot announcer
ros2 run mood_bot feel
Imagine doing that for 10 bots every morning!
Thatβs like manually waking each robot in town by knocking on their doors one at a time. π«
Enter: Launch Files π
They're like the town alarm system β start all bots on a schedule, with one press of a button!
π§ Analogy: Launch File = Town Sunrise Schedule
Think of a launch file as Robot Town's daily startup plan.
At sunrise, it:
Rings every robotβs morning bell
Starts specific bots with predefined roles
Maybe even tells them what to do
Itβs written in Python and managed using launch
from launch_ros
.
ποΈ Step 1: Set Up a New Package: town_starter
Letβs create a dedicated package just to manage town-wide startup routines.
cd ~/robot_town_ws/src
ros2 pkg create --build-type ament_python --dependencies rclpy launch launch_ros town_starter
This gives:
town_starter/
βββ town_starter/
β βββ __init__.py
βββ setup.py
βββ package.xml
Letβs also create a launch/
folder inside town_starter/
:
mkdir -p town_starter/launch
touch town_starter/launch/start_town.py
π οΈ Step 2: Add Our New Bots β TrafficBot & LightBot
Weβll quickly stub out two new packages β weβll build their logic in Part 2.
ros2 pkg create --build-type ament_python --node-name light_control light_bot
ros2 pkg create --build-type ament_python --node-name traffic_manager traffic_bot
Folder structure now looks like this:
robot_town_ws/
βββ src/
βββ ...
βββ traffic_bot/
βββ light_bot/
βββ town_starter/
βββ launch/
β βββ start_town.py
π§ͺ Step 3: Launch File Basics (Wake-up Schedule)
Letβs write a Python-based launch file inside start_
town.py
. For now, itβll just print hello messages β weβll link to the real bots in Part 2.
# Import the LaunchDescription class to define what this launch file will do
from launch import LaunchDescription
# Import the Node action to describe nodes to be launched
from launch_ros.actions import Node
# Function required by ROS2 launch system; it returns the launch setup
def generate_launch_description():
return LaunchDescription([
# First node: Launches the 'light_control' executable from the 'light_bot' package
Node(
package='light_bot', # The name of the ROS2 package
executable='light_control', # The name of the executable to run
name='light_bot' # The name this node will take in the ROS2 graph
),
# Second node: Launches the 'traffic_manager' executable from the 'traffic_bot' package
Node(
package='traffic_bot', # The name of the ROS2 package
executable='traffic_manager', # The name of the executable to run
name='traffic_bot' # The name this node will take in the ROS2 graph
)
])
π§ Explanation:
Node(...)
tells ROS2 to start a nodepackage
is your botβs package nameexecutable
is what to run (as defined insetup.py
)name
is how ROS2 will refer to it in logs and tools
This file acts like:
ποΈ βStart light_bot and traffic_bot now.β
π Step 4: Update setup.py
to Include the Launch File
In town_starter/
setup.py
, add:
import os
from glob import glob
from setuptools import setup
# Define the name of the package
package_name = 'town_starter'
setup(
name=package_name, # Name of the package used for installation and discovery
...
data_files=[
# Install the package.xml file into the share directory of the package
('share/' + package_name, ['package.xml']),
# Install all .py files from the 'launch' folder into the launch folder in the share directory
# glob('launch/*.py') finds all Python launch files in the local 'launch' folder
('share/' + package_name + '/launch', glob('launch/*.py')),
],
...
)
This makes sure the launch file is installed correctly during the build.
π§ͺ Step 5: Build the Whole Town Again
From your root workspace:
cd ~/robot_town_ws
colcon build
source install/setup.bash
π Step 6: Try Launching It (No Real Bots Yet!)
We can test the launch file even though our bots donβt do anything yet.
ros2 launch town_starter start_town.py
Expected output (or error if bots not built yet):
[INFO] [launch_ros.actions.Node]: light_bot started
[INFO] [launch_ros.actions.Node]: traffic_bot started
π Launch file is in place β our bots will respond once their logic is wired in Part 2.
π Mini Map: Where Are We?
robot_town_ws/
βββ src/
βββ mayor_bot/
βββ door_bot/
βββ chatter_bot/
βββ ...
βββ traffic_bot/
βββ light_bot/
βββ town_starter/
βββ launch/
βββ start_town.py
π Summary So Far
Concept | What It Means in Robot Town |
launch file | Master schedule to start multiple bots |
Node(...) | Tells which bot to launch |
ros2 launch | Runs the schedule |
town_starter | Package that holds launch files |
π οΈ Building the Bots Weβll Launch
So far, we have created a launch file but our bots didnβt do anything yet.
Now letβs give TrafficBot and LightBot some life β small tasks that make the town feel alive!
π¦ TrafficBot: Keeps an Eye on Town Roads
Navigate to the traffic bot package:
cd ~/robot_town_ws/src/traffic_bot/traffic_bot
Create a file named traffic_
manager.py
:
touch traffic_manager.py
Paste the following inside:
import rclpy
from rclpy.node import Node
class TrafficBot(Node):
def __init__(self):
super().__init__('traffic_bot')
self.get_logger().info("π TrafficBot is monitoring intersections...")
def main(args=None):
rclpy.init(args=args)
bot = TrafficBot()
rclpy.spin(bot)
bot.destroy_node()
rclpy.shutdown()
Update __init__.py
:
from .traffic_manager import main
π setup.py
Changes for Executable
Inside traffic_bot/
setup.py
, update:
entry_points={
'console_scripts': [
'traffic_manager = traffic_bot.traffic_manager:main',
],
},
π‘ LightBot: Manages Street Lights
Now do the same for LightBot:
cd ~/robot_town_ws/src/light_bot/light_bot
touch light_control.py
Paste this:
import rclpy
from rclpy.node import Node
class LightBot(Node):
def __init__(self):
super().__init__('light_bot')
self.get_logger().info("π‘ LightBot is turning on the town street lights!")
def main(args=None):
rclpy.init(args=args)
bot = LightBot()
rclpy.spin(bot)
bot.destroy_node()
rclpy.shutdown()
Update __init__.py
:
from .light_control import main
And in light_bot/
setup.py
:
entry_points={
'console_scripts': [
'light_control = light_bot.light_control:main',
],
},
π¦ Rebuild Robot Town!
Back at the root of your workspace:
cd ~/robot_town_ws
colcon build
source install/setup.bash
π Time to Launch the Whole Town!
Now run your launch file again:
ros2 launch town_starter start_town.py
And voila! You should see output like:
[light_bot] π‘ LightBot is turning on the town street lights!
[traffic_bot] π TrafficBot is monitoring intersections...
Your launch file just woke up two bots β one looking at traffic, the other lighting the roads. ππ¦
ποΈ Recap of Folder Structure
Hereβs where we are now:
robot_town_ws/
βββ src/
βββ mayor_bot/
βββ door_bot/
βββ chatter_bot/
βββ light_bot/
β βββ light_bot/
β βββ __init__.py
β βββ light_control.py
βββ traffic_bot/
β βββ traffic_bot/
β βββ __init__.py
β βββ traffic_manager.py
βββ town_starter/
βββ launch/
β βββ start_town.py
π‘ Concept Recap
Term | Meaning in Robot Town |
Launch File | City-wide morning alarm system |
Node(...) | Schedule for which bot to start |
ros2 launch | The command that starts the daily routine |
LightBot & TrafficBot | Two bots now started by one command |
π§ Whatβs __init__.py
Doing in This Case?
When you write this in traffic_bot/traffic_bot/__init__.py
:
from .traffic_manager import main
You are making the main()
function from traffic_
manager.py
importable from the entire package.
π§© Why Are We Doing This?
Because of how ROS 2 uses entry points in the setup.py
file!
Hereβs an example you probably have in your setup.py
:
entry_points={
'console_scripts': [
'traffic = traffic_bot:main',
],
},
This tells ROS 2:
βHey, when someone runs
ros2 run traffic_bot traffic
, go into thetraffic_bot
package and run themain()
function.β
Now, for that to work:
The package (
traffic_bot
) must exposemain()
at the top level, and thatβs exactly what__init__.py
is doing.__init__.py
links the internal file (traffic_
manager.py
) to the package namespace, makingmain()
visible like this:traffic_bot.main()
Think of traffic_
manager.py
as the control room, and __init__.py
as the front desk of the TrafficBot Department.
When someone runs ros2 run traffic_bot traffic
, they donβt walk all the way to the control room β they ask the front desk, and the desk says, βYup, go right ahead, here's main()
from traffic_manager.β
TL;DR π
β
__init__.py
re-exports main()
β
Enables ros2 run traffic_bot traffic
to work
β
Keeps your code modular and clean
π― Mini Project Review
β Youβve:
Created a launch file that starts multiple bots
Written basic functionality for two bots
Connected everything in one powerful command
Robot Town is starting to feel like a real city!
π Whatβs Next: The Robot Town Ledger β YAML & Launch Files Mastery
Ready to become the master of Robot Townβs command center? Next, we dive into YAML β the secret language behind launch files β and how to wield it to organize and control your bots with ease.
Unlock the full power of launching and configuring your robot crew like a true mayor! ποΈβ¨π€
Subscribe to my newsletter
Read articles from gayatri kumar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by