Defining Custom Topic Messages
To enable structured data exchange between nodes via ROS topics, custom message types must be defined and integrated into the build system.
- Define Message Interface
Create a
.msgfile in themsg/directory of your package, e.g.,Person.msg:
string name
uint8 sex
uint8 age
# Constants for sex
uint8 UNKNOWN = 0
uint8 MALE = 1
uint8 FEMALE = 2
- ** Declare Message Dependencies in
package.xml** Add the following entries to enable message generation at build time:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
- Configuring
CMakeLists.txtfor Message Compilation Ensure the following lines are included inCMakeLists.txt:
find_package(catkin REQUIRED COMPONENTS
...
message_generation
)
add_message_files(
FILES
Person.msg
)
generate_messages(
DEPENDENCIES std_msgs
)
catkin_package(
...
CATKIN_DEPENDS message_runtime ...
)
After running catkin_make, generated headers (e.g., Person.h) will appear under devel/include/learning_topic/.
- Generate Language-Specific Code
The
generate_messages()macro automatically produces C++ headers and Python modules for use in source code.
Implementing a Message Publisher
C++ Publisher Implementation
The following node advertises a topic using the custom Person message type.
#include <ros/ros.h>
#include "learning_topic/Person.h"
int main(int argc, char** argv) {
ros::init(argc, argv, "person_advertiser");
ros::NodeHandle nh;
ros::Publisher info_pub = nh.advertise<learning_topic::Person>("/user_profile", 10);
ros::Rate loop_time(1.0);
while (ros::ok()) {
learning_topic::Person profile;
profile.name = "Alice";
profile.age = 25;
profile.sex = learning_topic::Person::FEMALE;
info_pub.publish(profile);
ROS_INFO_STREAM("Broadcasting profile: " << profile.name
<< ", Age: " << profile.age
<< ", Sex: " << profile.sex);
loop_time.sleep();
}
return 0;
}
Python Publisher Implementation Equivalent behavior using Python:
import rospy
from learning_topic.msg import Person
def announce_profile():
rospy.init_node('profile_publisher', anonymous=True)
pupil_pub = rospy.Publisher('/user_profile', Person, queue_size=10)
tick_rate = rospy.Rate(10)
while not rospy.is_shutdown():
data = Person()
data.name = "Bob"
data.age = 30
data.sex = Person.MALE
pupil_pub.publish(data)
rospy.loginfo("Updated profile → name: %s, age: %d, sex: %d",
data.name, data.age, data.sex)
tick_rate.sleep()
if __name__ == '__main__':
try:
announce_profile()
except rospy.ROSInterruptException:
pass
Note: Message constants like
MALE,FEMALE, etc., map directly to numeric values defined in the.msgfile—ensure consistency between definitions and usage.
Implementing a Message Subscriber
C++ Subscriber Logic A subscriber node is configured to process incoming messages with a callback.
#include <ros/ros.h>
#include "learning_topic/Person.h"
void handle_user_data(const learning_topic::Person::ConstPtr& info) {
ROS_INFO("Received user data → Name: %s, Age: %d, Sex: %d",
info->name.c_str(), info->age, info->sex);
}
int main(int argc, char** argv) {
ros::init(argc, argv, "profile_listener");
ros::NodeHandle nh;
ros::Subscriber profile_sub = nh.subscribe("/user_profile", 10, handle_user_data);
ros::spin();
return 0;
}
Python Subscriber Logic Simplified handling via Python callback:
import rospy
from learning_topic.msg import Person
def on_profile_received(msg):
rospy.loginfo("Subscribed profile: %s (%d years, sex=%d)",
msg.name, msg.age, msg.sex)
def listen_to_profiles():
rospy.init_node('profile_watchdog', anonymous=True)
rospy.Subscriber('/user_profile', Person, on_profile_received)
rospy.spin()
if __name__ == '__main__':
listen_to_profiles()
Build Configuration for Executables
In CMakeLists.txt, declare executable targets and link against required libraries and message generation dependencies:
add_executable(profile_advertiser src/profile_advertiser.cpp)
target_link_libraries(profile_advertiser ${catkin_LIBRARIES})
add_dependencies(profile_advertiser ${PROJECT_NAME}_generate_messages_cpp)
add_executable(profile_listener src/profile_listener.cpp)
target_link_libraries(profile_listener ${catkin_LIBRARIES})
add_dependencies(profile_listener ${PROJECT_NAME}_generate_messages_cpp)
Execution Workflow
To launch the system:
$ cd ~/catkin_ws
$ catkin_make
$ source devel/setup.bash
$ roscore
In separate terminals:
$ rosrun learning_topic profile_listener
$ rosrun learning_topic profile_advertiser
msg exchange will begin, with the advertiser sending structured Person messages and the listener logging received content.