ROS1 Cheatsheet
ROS Noetic
ROS Noetic (Robot Operating System) is the final and most stable release of ROS 1, a framework for building and connecting robot software. It’s designed for Ubuntu 20.04 with Python 3 support and comes packed with tools, libraries, and community packages. However as of writing it has gone out of support from the ROS community and you should start using/migrating to ROS2. All code samples will be written in Python.
Useful resources:
• ROS Wiki
• ROS Documentation
• ROS Answers (Community Q&A)
• ROS GitHub
Using apptainer
As I'm learning ROS, it was recommended to use containerisation software to run all the ROS tools in an isolated environment. For my setup, I'll be using Apptainer to manage and run everything inside a container:
apptainer run -B /run/user/$UID /<path_to_container.sif>
Anything in wrapped with "<>" are templates and should not be copied into the command but replaced accordingly to the context the command. For example in the above command "<path_to_container>" should be replaced with the actual path of the .sif file.
Assume that any command shown in this post is run within this container, code can be written in any IDE of your choice but the commands need to be run in the container.
ROS Environment Setup
In ROS, a workspace (ws) is a directory where you build, modify, and organise your ROS packages and projects. We will be running our custom nodes and everything in the packages here.
# Create and source your workspace
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws
catkin_make
source devel/setup.bash
Package Creation
Packages are the core building blocks that contain your code, nodes, launch files, and configuration, essentially everything needed for a specific function or feature in a robot system.
cd ~/catkin_ws/src
catkin_create_pkg <pkg_name> rospy <dep1> <...> <depn>
Where <dep1> <...> <depn> is all the packages needed for the package. Note I leave rospy there since we will be using Python and do not worry you can go back to this and add more dependencies in the packages.xml file that is created after running this command.
Full Package structure
my_package/
├── CMakeLists.txt
├── package.xml
├── src/
│ └── my_node.py
└── scripts/
└── talker.py
Configure CMakeLists.txt
For custom messages, services, actions please refer to the CMakeLists.txt that is automatically generated with catkin_create_pkg.
Quickish places to add:
find_package(catkin REQUIRED COMPONENTS rospy std_msgs message_generation)
add_<messages/services/actions>_files(
FILES
<file.srv>
)
generate_messages(
DEPENDENCIES
std_msgs
)
catkin_package(
CATKIN_DEPENDS message_runtime
)
Writing a Python Node
Example: talker.py
#!/usr/bin/env python3
import rospy
from std_msgs.msg import String
def talker():
pub = rospy.Publisher('chatter', String, queue_size=10)
rospy.init_node('talker', anonymous=True)
rate = rospy.Rate(10) # 10Hz
while not rospy.is_shutdown():
msg = "Hello ROS Noetic! %s" % rospy.get_time()
rospy.loginfo(msg)
pub.publish(msg)
rate.sleep()
if __name__ == '__main__':
try:
talker()
except rospy.ROSInterruptException:
pass
Subscribing to Topics
Example: listener.py
#!/usr/bin/env python3
import rospy
from std_msgs.msg import String
def callback(data):
rospy.loginfo("I heard %s", data.data)
def listener():
rospy.init_node('listener', anonymous=True)
rospy.Subscriber("chatter", String, callback)
rospy.spin()
if __name__ == '__main__':
listener()
Running Nodes
# Make executable
chmod +x scripts/<name>.py
# Build the workspace
cd ~/catkin_ws
catkin_make
# Source the workspace
source devel/setup.bash
# Run roscore
roscore
# In a new terminal
rosrun <pkg> <name>.py
Node Commands
# Check node info
rosnode info /talker
# List active nodes
rosnode list
# Kill a node
rosnode kill <node_name>
# Kill all nodes
rosnode kill -a
Topic commands
# List topics
rostopic list
# Echo topic messages
rostopic echo /chatter
# Info about a topic
rostopic info /chatter
ROS Launch
Example of a launch file:
<launch>
<include file="$(find <pkg>)/launch/<file.launch>" />
<node pkg="<pkg>" type="<node>" name="<name>">
<remap from="<from_topic>" to="<to_topic>" />
</node>
</launch>
Run it
roslaunch <pkg> <file.launch>
Rosbag
To record logs from a topic and then can replay it for debugging purposes
rosbag record /<topic>
rosbag play <file.bag>
ROS Plot
We can use rqt_plot to visualise the messages of the topic:
rosrun rqt_plot rqt_plot
Use rosmsg to see what lines you can add to the plot and visualise.
rosmsg info /<topic>
ROS Services
Services are synchronous (request/response) communication between nodes.
Create a Service Definition
Example File: srv/AddTwoInts.srv
int64 a
int64 b
---
int64 sum
Place inside:
<pkg>/srv/<file.srv>
Service Server Example
#!/usr/bin/env python
from your_package.srv import AddTwoInts, AddTwoIntsResponse
import rospy
def handle_add_two_ints(req):
rospy.loginfo(f"Adding {req.a} + {req.b}")
return AddTwoIntsResponse(req.a + req.b)
rospy.init_node('add_two_ints_server')
service = rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints)
rospy.loginfo("Ready to add two ints.")
rospy.spin()
Service Client Server
#!/usr/bin/env python
import rospy
from your_package.srv import AddTwoInts
rospy.init_node('add_two_ints_client')
rospy.wait_for_service('add_two_ints')
try:
add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts)
resp = add_two_ints(3, 5)
rospy.loginfo(f"Sum = {resp.sum}")
except rospy.ServiceException as e:
rospy.logerr(f"Service call failed: {e}")
Command-line Tools
rosservice list
rosservice info /add_two_ints
rosservice call /add_two_ints "a: 2 b: 3"
rossrv show your_package/AddTwoInts
ROS Actions
Actions are asynchronous, providing feedback and allowing preemption.
Create an Action Definition
Example File: action/Fibonacci.action
int32 order
---
int32[] sequence
---
int32[] feedback_sequence
Place inside:
<pkg>/action/<file.action>
Action Server
#!/usr/bin/env python
import rospy
import actionlib
from your_package.msg import FibonacciAction, FibonacciFeedback, FibonacciResult
class FibonacciActionServer:
def __init__(self):
self.server = actionlib.SimpleActionServer('fibonacci', FibonacciAction, self.execute, False)
self.server.start()
def execute(self, goal):
feedback = FibonacciFeedback()
result = FibonacciResult()
feedback.sequence = [0, 1]
for i in range(2, goal.order):
if self.server.is_preempt_requested():
rospy.loginfo("Goal preempted")
self.server.set_preempted()
return
feedback.sequence.append(feedback.sequence[-1] + feedback.sequence[-2])
self.server.publish_feedback(feedback)
rospy.sleep(1.0)
result.sequence = feedback.sequence
self.server.set_succeeded(result)
rospy.init_node('fibonacci_server')
server = FibonacciActionServer()
rospy.spin()
Action Client
#!/usr/bin/env python
import rospy
import actionlib
from your_package.msg import FibonacciAction, FibonacciGoal
def feedback_cb(feedback):
rospy.loginfo(f"Feedback: {feedback.sequence}")
rospy.init_node('fibonacci_client')
client = actionlib.SimpleActionClient('fibonacci', FibonacciAction)
client.wait_for_server()
goal = FibonacciGoal(order=5)
client.send_goal(goal, feedback_cb=feedback_cb)
client.wait_for_result()
result = client.get_result()
rospy.loginfo(f"Result: {result.sequence}")
Command-line Tools
rosaction list
rosaction info fibonacci
rosaction show your_package/Fibonacci
# Using the action client tool (rqt or actionlib_tools)
rostopic pub /fibonacci/goal your_package/FibonacciActionGoal "{goal: {order: 5}}"
rostopic echo /fibonacci/feedback
rostopic echo /fibonacci/result
Service vs Action Summary
| Feature | Service | Action |
|---|---|---|
| Communication Style | Synchronous (blocking) | Asynchronous (non-blocking) |
| Use Case | Quick request/response | Long-running task |
| Feedback | None | Periodic feedback available |
| Preemption | Not possible | Possible |
| Tools | rosservice |
actionlib, rostopic |
ROS Transfomers
TF2 lets you relate coordinate frames at any point in time.
TF tree
# visual
rosrun rqt_tf_tree rqt_tf_tree
# text only
rosrun tf2_tools view_frames.py
# numeric lookups
rosrun tf tf_echo /base_link /map
Broadcasting a static transform
# x y z roll pitch yaw frame_child frame_parent
rosrun tf2_ros static_transform_publisher 0 0 0 0 0 0 camera_link base_link
Broadcasting with Python
#!/usr/bin/env python3
import rospy
import tf_conversions
import tf2_ros
import geometry_msgs.msg
rospy.init_node('tf_broadcaster')
br = tf2_ros.TransformBroadcaster()
t = geometry_msgs.msg.TransformStamped()
rate = rospy.Rate(10)
while not rospy.is_shutdown():
t.header.stamp = rospy.Time.now()
t.header.frame_id = "base_link"
t.child_frame_id = "camera_link"
t.transform.translation.x = 0.1
t.transform.translation.y = 0.0
t.transform.translation.z = 0.2
q = tf_conversions.transformations.quaternion_from_euler(0, 0, 0.52)
t.transform.rotation.x = q[0]
t.transform.rotation.y = q[1]
t.transform.rotation.z = q[2]
t.transform.rotation.w = q[3]
br.sendTransform(t)
rate.sleep()
Listening in Python
#!/usr/bin/env python3
import rospy
import tf2_ros
import geometry_msgs.msg
rospy.init_node('tf_listener')
tfBuffer = tf2_ros.Buffer()
listener = tf2_ros.TransformListener(tfBuffer)
rate = rospy.Rate(10)
while not rospy.is_shutdown():
try:
transf = tfBuffer.lookup_transform('map', 'base_link', rospy.Time(0))
rospy.loginfo(f"x = {transf.transform.translation.x:.3f}")
except Exception as e:
rospy.logwarn(str(e))
rate.sleep()
Common patterns
| What you want | How to do it |
|---|---|
| “where is frame B relative to A now?” | tfBuffer.lookup_transform(A, B, now) |
| periodic static offsets | run static_transform_publisher once |
| convert Euler → quaternion | tf_conversions.transformations.quaternion_from_euler |
Typical frame names
| Frame | Meaning |
|---|---|
map |
global / world reference |
odom |
continuous local (drift allowed) |
base_link |
robot body |
base_footprint |
projected body on ground plane |
| sensors | eg lidar_link, camera_link |
rule of thumb: only broadcast frames you own, never stomp another node's frame.
Rviz Commands
# Running Stage simulator Rviz
rosrun rviz rviz -d `rospack find rosplan_stage_demo`/config/rosplan_stage_demo.rviz &
# Running Stage Simulator
roslaunch rosplan_stage_demo empty_stage_single_robot.launch &
Common Message Types
| Type | Import | Example Use |
|---|---|---|
std_msgs/String |
from std_msgs.msg import String |
Chat or text data |
geometry_msgs/Twist |
from geometry_msgs.msg import Twist |
Velocity commands |
sensor_msgs/Image |
from sensor_msgs.msg import Image |
Camera data |
nav_msgs/Odometry |
from nav_msgs.msg import Odometry |
Robot position data |
Workspace Maintenance
# Clean workspace
catkin_make clean
# Rebuild everything
catkin_make -DCMAKE_BUILD_TYPE=Release
# Re-source environment
source devel/setup.bash
Debugging Tips
# Check log files
roscd log
less latest/rosout.log
# Print debug messages
rospy.logdebug("Debug message")
rospy.loginfo("Info message")
rospy.logwarn("Warning message")
rospy.logerr("Error message")
- ← Previous
picoCTF - Reverse Engineering | Flag Hunters