Hello everyone,
I am trying to retrieve distance data from a Ping360 sonar using ROS 2 (Humble) and Python. My goal is to detect obstacles and navigate in a pool environment.
The Pool Dimensions: 4 meters (width) x 8 meters (length).
The Problem:
When I run my Python subscriber nodes, I see no output in the terminal. I also do not see any error messages; the code runs silently. I suspect I might be using the wrong driver parameters or initializing the connection incorrectly.
Here is my setup and the codes I am using:
1. Starting the Driver (Terminal 1)
I launch the Ping360 driver with the following command via USB serial:
ros2 run ping360_sonar ping360.py --ros-args -p device:=/dev/ttyUSB0 -p baudrate:=115200 -p publish_scan:=True -p scan_threshold:=200 -p gain:=0 -p range_max:=2
2. Python Scripts (Terminal 2)
I am testing with three different scripts to read the /scan topic. None of them are producing data output with the current driver settings.
Script A: 4-Directional Radar (Monitoring 0°, 90°, 180°, 270°)
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import LaserScan
from rclpy.qos import qos_profile_sensor_data
import math
import os
class PoolRadar(Node):
def __init__(self):
super().__init__('pool_radar_node')
self.subscription = self.create_subscription(
LaserScan, '/scan', self.scan_callback, qos_profile_sensor_data)
self.distances = {
'FRONT': float('inf'), 'REAR': float('inf'),
'RIGHT': float('inf'), 'LEFT': float('inf')
}
self.get_logger().info('--- 4-WAY RADAR STARTED ---')
def scan_callback(self, msg):
current_angle = msg.angle_min
data_changed = False
for r in msg.ranges:
# Filter noise < 0.2m
if r > 0.2 and r != float('inf'):
deg = int(math.degrees(current_angle) % 360)
# Check 4 Main Directions with tolerance
if 170 < deg < 190: # FRONT (180 deg)
self.distances['FRONT'] = r
data_changed = True
elif deg > 350 or deg < 10: # REAR (0 deg)
self.distances['REAR'] = r
data_changed = True
elif 260 < deg < 280: # RIGHT (270 deg)
self.distances['RIGHT'] = r
data_changed = True
elif 80 < deg < 100: # LEFT (90 deg)
self.distances['LEFT'] = r
data_changed = True
current_angle += msg.angle_increment
if data_changed:
self.refresh_screen()
def refresh_screen(self):
os.system('cls' if os.name == 'nt' else 'clear')
front = self.distances['FRONT']
rear = self.distances['REAR']
right = self.distances['RIGHT']
left = self.distances['LEFT']
def fmt(val): return f"{val:.2f}m" if val != float('inf') else "---"
print("\n" * 2)
print(f" FRONT (180°)")
print(f" {fmt(front)}")
print(f" |")
print(f"LEFT (90°) {fmt(left)} --O-- {fmt(right)} RIGHT (270°)")
print(f" |")
print(f" {fmt(rear)}")
print(f" REAR (0°)")
print("\n" * 2)
def main(args=None):
rclpy.init(args=args)
rclpy.spin(PoolRadar())
rclpy.shutdown()
if __name__ == '__main__': main()
Script B: Front Obstacle Avoidance
(Targeting 8m length, alert at 1.5m)
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import LaserScan
from rclpy.qos import qos_profile_sensor_data
import math
class ObstacleAvoidance(Node):
def __init__(self):
super().__init__('obstacle_node')
self.subscription = self.create_subscription(
LaserScan, '/scan', self.scan_callback, qos_profile_sensor_data)
# Stop limit set to 1.5m for 8m pool length
self.LIMIT = 1.5
self.get_logger().info(f'--- OBSTACLE AVOIDANCE (Limit: {self.LIMIT}m) ---')
def scan_callback(self, msg):
current_angle = msg.angle_min
for r in msg.ranges:
if r > 0.2 and r != float('inf'):
deg = int(math.degrees(current_angle) % 360)
# Focus only on FRONT (170-190 degrees)
if 170 < deg < 190:
if r < self.LIMIT:
print(f"\033[91m[STOP!] OBSTACLE DETECTED! Dist: {r:.2f}m\033[0m")
elif r < 3.0:
print(f"\033[93m[WARNING] Approaching... Dist: {r:.2f}m\033[0m")
else:
print(f"\033[92m[CLEAR] Path Clear. Dist: {r:.2f}m\033[0m")
current_angle += msg.angle_increment
def main(args=None):
rclpy.init(args=args)
rclpy.spin(ObstacleAvoidance())
rclpy.shutdown()
if __name__ == '__main__': main()
Script C: Right Wall Follower
(Targeting 4m width, keeping 1.0m distance from right wall)
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import LaserScan
from rclpy.qos import qos_profile_sensor_data
import math
class WallFollower(Node):
def __init__(self):
super().__init__('wall_follower_node')
self.subscription = self.create_subscription(
LaserScan, '/scan', self.scan_callback, qos_profile_sensor_data)
# Target: Keep 1.0m distance from the right wall
self.TARGET = 1.0
print(f"--- RIGHT WALL FOLLOWER (Target: {self.TARGET}m) ---")
def scan_callback(self, msg):
current_angle = msg.angle_min
for r in msg.ranges:
deg = int(math.degrees(current_angle) % 360)
# Focus on RIGHT sector (260-280 degrees)
if 260 < deg < 280:
if r > 0.2 and r != float('inf'):
error = r - self.TARGET
if abs(error) < 0.10:
print(f"\033[92m[STRAIGHT] Perfect Position ({r:.2f}m)\033[0m")
elif error > 0:
print(f"\033[93m[TURN RIGHT >] Too far from wall ({r:.2f}m)\033[0m")
else:
print(f"\033[91m[< TURN LEFT] Too close to wall ({r:.2f}m)\033[0m")
current_angle += msg.angle_increment
def main(args=None):
rclpy.init(args=args)
rclpy.spin(WallFollower())
rclpy.shutdown()
if __name__ == '__main__': main()
My Questions:
- Is the
scan_threshold:=200parameter in my launch command too high? Could this be filtering out all returns and causing the lack of data? - Should I adjust
gain:=0to a higher value for a pool environment? - Are there any specific ROS 2 QoS settings required for the Ping360 driver that I might be missing in my
ros2 runcommand?
Any advice on why I am receiving no data would be very helpful. Thank you!
