Skip to content

Commit fd007ec

Browse files
committed
Initial wireless sonar sensor framework.
1 parent fa6e766 commit fd007ec

File tree

4 files changed

+128
-0
lines changed

4 files changed

+128
-0
lines changed

ros_ws/src/sonar/package.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0"?>
2+
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
3+
<package format="3">
4+
<name>sonar</name>
5+
<version>0.0.0</version>
6+
<description>Wireless sonar sensor driver</description>
7+
<maintainer email="bertrik@sikken.nl">Bertrik Sikken</maintainer>
8+
<license>MIT</license>
9+
10+
<depend>std_msgs</depend>
11+
<depend>rclcpp</depend>
12+
<depend>bleak</depend>
13+
14+
<test_depend>python3-pytest</test_depend>
15+
16+
<export>
17+
<build_type>ament_python</build_type>
18+
</export>
19+
</package>

ros_ws/src/sonar/setup.cfg

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[develop]
2+
script_dir=$base/lib/lidar
3+
[install]
4+
install_scripts=$base/lib/lidar

ros_ws/src/sonar/setup.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from setuptools import find_packages, setup
2+
3+
package_name = 'sonar'
4+
5+
setup(
6+
name=package_name,
7+
version='0.0.0',
8+
packages=find_packages(exclude=['test']),
9+
data_files=[
10+
('share/ament_index/resource_index/packages',
11+
['resource/' + package_name]),
12+
('share/' + package_name, ['package.xml']),
13+
],
14+
install_requires=['setuptools'],
15+
zip_safe=True,
16+
maintainer='bertrik',
17+
maintainer_email='bertrik@sikken.nl',
18+
description='Wireless sonar sensor driver',
19+
license='MIT',
20+
tests_require=['pytest'],
21+
entry_points={
22+
'console_scripts': [
23+
],
24+
},
25+
)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/usr/bin/env python3
2+
# Copyright 2025 Bertrik Sikken <bertrik@sikken.nl>
3+
import math
4+
import threading
5+
6+
import rclpy
7+
import serial
8+
from rclpy.node import Node
9+
from sensor_msgs.msg import Range
10+
11+
12+
class Sonar:
13+
def __init__(self, device):
14+
self.serial = serial.Serial(device, baudrate=230400, timeout=None)
15+
16+
def open(self) -> None:
17+
if not self.serial.is_open:
18+
self.serial.open()
19+
20+
def poll(self) -> bytes | None:
21+
return None
22+
23+
def close(self) -> None:
24+
self.serial.close()
25+
26+
27+
class SonarNode(Node):
28+
def __init__(self):
29+
super().__init__('sonar')
30+
self.declare_parameter('device', value='/dev/ttyUSB0')
31+
self.declare_parameter('topic', value='/sonar')
32+
self.declare_parameter('frame_id', value='sonar')
33+
34+
device = self.get_parameter('device').get_parameter_value().string_value
35+
self.sonar = Sonar(device)
36+
37+
self.frame_id = self.get_parameter('frame_id').get_parameter_value().string_value
38+
39+
topic = self.get_parameter('topic').get_parameter_value().string_value
40+
self.publisher = self.create_publisher(Range, topic, 10)
41+
42+
# initialise Range message
43+
self.msg = Range()
44+
self.msg.header.frame_id = self.frame_id
45+
self.msg.radiation_type = Range.ULTRASOUND
46+
self.msg.field_of_view = math.radians(45)
47+
self.msg.min_range = 0.3 # estimated
48+
self.msg.max_range = 60.0 # 200 feet actually
49+
50+
# create and start listen thread
51+
self.thread = threading.Thread(target=self.node_task())
52+
self.thread.start()
53+
54+
def node_task(self) -> None:
55+
self.sonar.open()
56+
while rclpy.ok():
57+
frame = self.sonar.poll()
58+
if frame:
59+
self.process_frame(frame)
60+
self.sonar.close()
61+
62+
def process_frame(self, frame: bytes) -> None:
63+
now = self.get_clock().now()
64+
65+
# publish message
66+
self.msg.range = 12.34
67+
self.msg.header.stamp = now.to_msg()
68+
self.publisher.publish(self.msg)
69+
70+
71+
def main(args=None):
72+
rclpy.init(args=args)
73+
sonar_node = SonarNode()
74+
rclpy.spin(sonar_node)
75+
sonar_node.destroy_node()
76+
rclpy.shutdown()
77+
78+
79+
if __name__ == '__main__':
80+
main()

0 commit comments

Comments
 (0)