Article
roboticsrobot-perceptionros2lidarpoint-cloud-processingobstacle-avoidancepython
Process LiDAR Data for Obstacle Detection with ROS 2
Use Robot Operating System (ROS 2) to build a fundamental Robot Perception skill. This guide shows how to subscribe to simulated LiDAR data, process it in Python to find the closest obstacle, and log the distance for basic navigation decisions.
intermediate30 min4 steps
The play
- Create a ROS 2 Workspace and PackageAll ROS 2 code lives in workspaces. First, source your ROS 2 installation. Then, create a directory for your workspace and a Python package inside it for our perception node. This organizes your project and makes it buildable.
- Write the LiDAR Subscriber NodeCreate a Python file for your node (`simple_lidar_processor/simple_lidar_processor/lidar_node.py`). This script will initialize a ROS 2 node, subscribe to the `/scan` topic (where 2D LiDAR data is published), and define a callback function to handle incoming messages.
- Implement Closest Point LogicModify the `listener_callback` function. The `LaserScan` message contains a `ranges` array with distance measurements. Iterate through this array to find the minimum valid distance, which represents the closest obstacle in the robot's view.
- Build and Run the Node with a SimulatorFirst, build your workspace to make the node executable. Then, in one terminal, launch a Gazebo simulation with a robot that has a LiDAR (e.g., TurtleBot3). In a second terminal, source your workspace and run your perception node to see it process the simulated sensor data.
Starter code
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import LaserScan
import math
class LidarProcessor(Node):
"""
A simple ROS 2 node that subscribes to LaserScan data and finds the closest obstacle.
"""
def __init__(self):
super().__init__('lidar_processor_node')
self.subscription = self.create_subscription(
LaserScan,
'scan', # Standard topic for 2D LiDAR
self.listener_callback,
10) # QoS profile depth
self.get_logger().info('LiDAR Processor node started. Subscribing to /scan topic...')
def listener_callback(self, msg):
"""
Callback function for the /scan topic subscription.
Processes the incoming LaserScan message to find the minimum distance.
"""
# The 'ranges' field is an array of distance measurements.
# We filter out 'inf' and 'nan' values which can occur for no-return points.
valid_ranges = [r for r in msg.ranges if not math.isinf(r) and not math.isnan(r)]
if not valid_ranges:
self.get_logger().info('No valid obstacle readings.')
return
# Find the minimum distance from the valid readings
min_distance = min(valid_ranges)
self.get_logger().info(f'Closest obstacle detected at: {min_distance:.2f} meters')
def main(args=None):
"""
Main function to initialize and run the ROS 2 node.
"""
rclpy.init(args=args)
lidar_processor_node = LidarProcessor()
try:
rclpy.spin(lidar_processor_node)
except KeyboardInterrupt:
pass
finally:
# Destroy the node explicitly
lidar_processor_node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()