Creating a Custom ROS Message
1. Objective — What You Are Building and Why
In this lesson you will learn how to create your own custom message in ROS.
Until now, you have used standard ROS messages such as:
std_msgs/Stringgeometry_msgs/Twist
Those are very useful, but in real robotic applications you often need to send your own structured data.
For example, imagine a robot that wants to publish information such as:
- battery percentage
- current position
- current orientation
- whether the brushes are active
- a debug message
There is no single standard message that perfectly matches that exact need.
So instead of sending many separate topics, you can define one custom message that groups all the information together.
That is exactly what we will do in this lesson.
We will create a package dedicated to custom messages and services, and we will define a new message that we will later use in the final project, where we simulate the behavior of a Roomba-like robot using Turtlesim.
So this lesson is not just theory.
This custom message will become a useful building block for the project you will build later.
2. Code Explanation — What / How / Why
Why Create a Separate Package for Messages?
In ROS, it is a very common and very good practice to create a dedicated package for:
- custom messages
- custom services
Why?
Because messages are often used by many other packages.
So instead of mixing message definitions with the logic of one node, you keep them in a separate package and make them reusable.
That is why in this lesson we create a dedicated package called:
rumba_msg
This package will contain the definitions of our custom .msg files and, later if needed, also custom .srv files.
Step 1 — Create the Package
First, inside the src folder of your workspace, create the package:
catkin_create_pkg rumba_msg roscpp rospy std_msgs
This creates the package structure.
At this point, the package exists, but it is still a normal ROS package.
It does not yet know that it will contain custom messages.
So now we have to configure it.
Step 2 — Modify package.xml
Now we go into package.xml.
Here we must add the dependency:
message_generation
Why?
Because ROS needs a package that knows how to generate source code from our custom .msg definitions.
This is a very important concept.
When you write a custom message file such as:
BatteryStatus.msg
ROS does not use that file directly at runtime.
Instead, during the build process, ROS generates the code needed so that C++ and Python nodes can use that custom message type.
That is exactly what message_generation is for.
So in package.xml, you add the right dependency entries for message_generation, and later also message_runtime.
A simple way to think about it is:
message_generation→ needed while buildingmessage_runtime→ needed while running
Step 3 — Modify CMakeLists.txt
Now we move to CMakeLists.txt.
This is where we tell catkin exactly what to generate.
There are three very important parts here.
Part A — Add message_generation to the package dependencies
In the find_package(catkin REQUIRED COMPONENTS ...) section, add:
message_generation
This tells the build system:
this package contains custom messages, so prepare the tools needed to generate them.
Part B — Declare the message files
Then we add the block:
add_message_files( FILES BatteryStatus.msg)
This tells ROS:
inside this package, generate code for the file
BatteryStatus.msg.
This is one of the most important lines in the whole lesson.
Because without this line, ROS will not know which custom message files it must process.
Part C — Generate the messages
Then we enable message generation with:
generate_messages( DEPENDENCIES std_msgs)
This tells ROS to actually generate the message code.
Why do we mention
std_msgs
here?
Because some messages may depend on standard message types or standard message infrastructure.
Even if this example is simple, it is a good habit to declare the dependencies correctly.
Part D — Update catkin_package
Then inside catkin_package(...), we add something like:
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
This is important because other packages that want to use rumba_msg must know that this package exports message runtime support.
So the whole idea is:
- declare the custom message files
- generate them during build
- export them so other packages can use them
Step 4 — Create the msg Folder
Now inside the rumba_msg package, create a folder called:
msg
This is where ROS expects custom message definitions.
Inside this folder, create the file:
BatteryStatus.msg
Step 5 — Define the Custom Message
Now we write the fields of our message.
From your video, the custom message contains data like:
int64 percentage_battery float64 location_x float64 location_y float64 orientation_theta bool brushes_up string debug_message
Let’s explain what this means.
This custom message groups together several pieces of information that describe the state of our simulated robot.
So instead of publishing many different topics, we can publish a single message that contains:
- battery charge
- robot position
- robot orientation
- cleaning state
- some debugging text
This is exactly why custom messages are so useful.
You create a message structure that matches
your application.
Step 6 — Build the Workspace
Now that the message has been defined and the package has been configured, you go back to the workspace root and build:
catkin_make
This step is essential.
During
catkin_make
, ROS reads:
package.xmlCMakeLists.txt- your
.msgfile
and generates the code that will allow you to use rumba_msg/BatteryStatus inside your C++ and Python nodes.
Then remember to source the workspace again:
source devel/setup.bash
This is important because the workspace now contains newly generated message definitions.
Step 7 — Use the Custom Message in a Node
Once the custom message is generated, you can use it in your ROS nodes.
In the example shown in the video, we create a node that publishes a BatteryStatus message.
In C++, you include the generated header:
#include "rumba_msg/BatteryStatus.h"
Then you create a publisher for that message type:
ros::Publisher pub = nh.advertise<rumba_msg::BatteryStatus>("/rumba/battery_status", 1000);
And then, inside the loop, you fill the message fields, for example:
- battery percentage
- x and y position
- orientation
- brushes state
- debug message
This is where the power of custom messages becomes very clear.
You are no longer limited to standard message types.
You can define exactly the structure your application needs.
Why This Matters for the Final Project
This custom message will be used in the final project with Turtlesim, where we simulate the behavior of a Roomba-like robot.
That means this lesson is preparing one important piece of the final architecture.
In the upcoming project, the robot simulation will not only move.
It will also publish its own internal state using a custom message designed by you.
This is exactly how real robotics software is often built.
You create custom interfaces that match the logic of your system.
3. Demo
In the video demonstration, you will see the full workflow step by step.
We will:
- Create the package
rumba_msg - Modify
package.xml - Modify
CMakeLists.txt - Create the
msgfolder - Create
BatteryStatus.msg - Build the workspace with
catkin_make - Use the generated custom message inside a C++ node
This is an important lesson to follow actively.
Do not just watch it.
Try to reproduce every step while I do it, because custom messages are one of those ROS features that become very easy once you have done them yourself at least once.
4. Key Takeaways
After this lesson, you should clearly understand that ROS is not limited to standard message types.
You can create your own custom messages whenever your application needs a more specific data structure.
You learned that:
- a custom message is defined in a
.msgfile .msgfiles live inside amsg/folder- message packages should usually be kept separate from application logic
package.xmlandCMakeLists.txtmust be configured correctlymessage_generationis needed during buildmessage_runtimeis needed so other packages can use the generated messages
Most importantly, you now understand the workflow:
- create the package
- declare the message
- configure the build files
- build the workspace
- use the new message inside your nodes
This is a very important milestone, because now you are starting to design ROS interfaces that fit your own robotic application, not just generic examples.
And that is exactly the direction we want, because in the final project you will use this custom message in the Roomba + Turtlesim simulation.