Skip to content

Commit 8230dd5

Browse files
fmricolukicdarkoo
andauthored
Range and qos (#198)
* Range Finder Signed-off-by: Francisco Martin Rico <[email protected]> * Fix QoS Signed-off-by: Francisco Martin Rico <[email protected]> * Unused import * Add docs * Fix deep code errors and avoid unnecessary call Signed-off-by: Francisco Martin Rico <[email protected]> * Fix deep code errors and avoid unnecessary call Signed-off-by: Francisco Martin Rico <[email protected]> * Geometric wheels values Signed-off-by: Francisco Martin Rico <[email protected]> * CI fix Co-authored-by: Darko Lukic <[email protected]>
1 parent 38d88e0 commit 8230dd5

File tree

9 files changed

+145
-24
lines changed

9 files changed

+145
-24
lines changed

docs/source/API-Devices.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ We created a collection of Python modules that read a Webots node description an
3737
:show-inheritance:
3838

3939

40+
### RangeFinder Device
41+
42+
.. automodule:: webots_ros2_core.devices.range_finder_device
43+
:members:
44+
:show-inheritance:
45+
46+
4047
### Distance Sensor Device
4148

4249
.. automodule:: webots_ros2_core.devices.distance_sensor_device

webots_ros2_core/webots_ros2_core/devices/camera_device.py

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
from sensor_msgs.msg import Image, CameraInfo
1818
from rclpy.time import Time
19-
from rclpy.qos import DurabilityPolicy, HistoryPolicy, QoSProfile
19+
from rclpy.qos import DurabilityPolicy, HistoryPolicy, QoSProfile, QoSReliabilityPolicy, qos_profile_sensor_data
2020
from .sensor_device import SensorDevice
2121

2222

@@ -50,36 +50,41 @@ def __init__(self, node, device_key, wb_device, params=None):
5050
self._image_publisher = self._node.create_publisher(
5151
Image,
5252
self._topic_name + '/image_raw',
53-
1
53+
qos_profile_sensor_data
5454
)
5555
self._camera_info_publisher = self._node.create_publisher(
5656
CameraInfo,
5757
self._topic_name + '/camera_info',
5858
QoSProfile(
5959
depth=1,
60+
reliability=QoSReliabilityPolicy.RELIABLE,
6061
durability=DurabilityPolicy.TRANSIENT_LOCAL,
6162
history=HistoryPolicy.KEEP_LAST,
6263
)
6364
)
6465

6566
# CameraInfo data
66-
msg = CameraInfo()
67-
msg.header.stamp = Time(seconds=self._node.robot.getTime()).to_msg()
68-
msg.height = self._wb_device.getHeight()
69-
msg.width = self._wb_device.getWidth()
70-
msg.distortion_model = 'plumb_bob'
71-
msg.d = [0.0, 0.0, 0.0, 0.0, 0.0]
72-
msg.k = [
73-
self._wb_device.getFocalLength(), 0.0, self._wb_device.getWidth() / 2,
74-
0.0, self._wb_device.getFocalLength(), self._wb_device.getHeight() / 2,
67+
self.__message_info = CameraInfo()
68+
self.__message_info.header.stamp = Time(seconds=self._node.robot.getTime()).to_msg()
69+
self.__message_info.height = self._wb_device.getHeight()
70+
self.__message_info.width = self._wb_device.getWidth()
71+
self.__message_info.distortion_model = 'plumb_bob'
72+
focal_length = self._wb_device.getFocalLength()
73+
if focal_length == 0:
74+
focal_length = 570.34 # Identical to Orbbec Astra
75+
self.__message_info.d = [0.0, 0.0, 0.0, 0.0, 0.0]
76+
self.__message_info.r = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]
77+
self.__message_info.k = [
78+
focal_length, 0.0, self._wb_device.getWidth() / 2,
79+
0.0, focal_length, self._wb_device.getHeight() / 2,
7580
0.0, 0.0, 1.0
7681
]
77-
msg.p = [
78-
self._wb_device.getFocalLength(), 0.0, self._wb_device.getWidth() / 2, 0.0,
79-
0.0, self._wb_device.getFocalLength(), self._wb_device.getHeight() / 2, 0.0,
82+
self.__message_info.p = [
83+
focal_length, 0.0, self._wb_device.getWidth() / 2, 0.0,
84+
0.0, focal_length, self._wb_device.getHeight() / 2, 0.0,
8085
0.0, 0.0, 1.0, 0.0
8186
]
82-
self._camera_info_publisher.publish(msg)
87+
self._camera_info_publisher.publish(self.__message_info)
8388

8489
# Load parameters
8590
camera_period_param = node.declare_parameter(wb_device.getName() + '_period', self._timestep)
@@ -89,10 +94,13 @@ def step(self):
8994
stamp = super().step()
9095
if not stamp:
9196
return
92-
9397
# Publish camera data
9498
if self._image_publisher.get_subscription_count() > 0 or self._always_publish:
9599
self._wb_device.enable(self._timestep)
100+
image = self._wb_device.getImage()
101+
102+
if image is None:
103+
return
96104

97105
# Image data
98106
msg = Image()
@@ -107,8 +115,11 @@ def step(self):
107115
# Both, `bytearray` and `array.array`, implement Python buffer protocol, so we should not see unpredictable
108116
# behavior.
109117
# deepcode ignore W0212: Avoid conversion from `bytearray` to `array.array`.
110-
msg._data = self._wb_device.getImage()
118+
msg._data = image
111119
msg.encoding = 'bgra8'
112120
self._image_publisher.publish(msg)
121+
122+
self.__message_info.header.stamp = Time(seconds=self._node.robot.getTime()).to_msg()
123+
self._camera_info_publisher.publish(self.__message_info)
113124
else:
114125
self._wb_device.disable()

webots_ros2_core/webots_ros2_core/devices/device_manager.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"""Auto discover Webots devices and publish suitable ROS2 topics."""
1818

1919
from .camera_device import CameraDevice
20+
from .range_finder_device import RangeFinderDevice
2021
from .led_device import LEDDevice
2122
from .lidar_device import LidarDevice
2223
from .distance_sensor_device import DistanceSensorDevice
@@ -45,6 +46,8 @@ def __init__(self, node, config=None):
4546
# Create ROS2 wrapped device
4647
if wb_device.getNodeType() == Node.CAMERA:
4748
device = CameraDevice(node, device_key, wb_device, self.__config.get(device_key, None))
49+
if wb_device.getNodeType() == Node.RANGE_FINDER:
50+
device = RangeFinderDevice(node, device_key, wb_device, self.__config.get(device_key, None))
4851
elif wb_device.getNodeType() == Node.LED:
4952
device = LEDDevice(node, device_key, wb_device, self.__config.get(device_key, None))
5053
elif wb_device.getNodeType() == Node.LIDAR:

webots_ros2_core/webots_ros2_core/devices/distance_sensor_device.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
"""Webots DistanceSensor device wrapper for ROS2."""
1616

17+
from rclpy.qos import qos_profile_sensor_data
1718
from sensor_msgs.msg import Range
1819
from webots_ros2_core.math.interpolation import interpolate_lookup_table
1920
from .sensor_device import SensorDevice
@@ -46,7 +47,8 @@ def __init__(self, node, device_key, wb_device, params=None):
4647

4748
# Create topics
4849
if not self._disable:
49-
self._publisher = self._node.create_publisher(Range, self._topic_name, 1)
50+
self._publisher = self._node.create_publisher(Range, self._topic_name,
51+
qos_profile_sensor_data)
5052

5153
def __get_max_value(self):
5254
table = self._wb_device.getLookupTable()

webots_ros2_core/webots_ros2_core/devices/imu_device.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
"""Webots Accelerometer, Gyro and InertialUnit devices wrapper for ROS2."""
1616

17+
from rclpy.qos import qos_profile_sensor_data
1718
from sensor_msgs.msg import Imu
1819
from webots_ros2_core.math.interpolation import interpolate_lookup_table
1920
from .sensor_device import SensorDevice
@@ -52,7 +53,8 @@ def __init__(self, node, device_key, wb_device, params=None):
5253
# Create topics
5354
self._publisher = None
5455
if not self._disable:
55-
self._publisher = self._node.create_publisher(Imu, self._topic_name, 1)
56+
self._publisher = self._node.create_publisher(Imu, self._topic_name,
57+
qos_profile_sensor_data)
5658

5759
def __enable_imu(self):
5860
for wb_device in self._wb_device:

webots_ros2_core/webots_ros2_core/devices/lidar_device.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
"""Lidar device."""
1616

17+
from rclpy.qos import qos_profile_sensor_data
1718
from sensor_msgs.msg import LaserScan, PointCloud2, PointField
1819
from tf2_ros import StaticTransformBroadcaster
1920
from geometry_msgs.msg import TransformStamped
@@ -62,9 +63,11 @@ def __init__(self, node, device_key, wb_device, params=None):
6263
# Create topics
6364
if wb_device.getNumberOfLayers() > 1:
6465
wb_device.enablePointCloud()
65-
self.__publisher = node.create_publisher(PointCloud2, self._topic_name, 1)
66+
self.__publisher = node.create_publisher(PointCloud2, self._topic_name,
67+
qos_profile_sensor_data)
6668
else:
67-
self.__publisher = node.create_publisher(LaserScan, self._topic_name, 1)
69+
self.__publisher = node.create_publisher(LaserScan, self._topic_name,
70+
qos_profile_sensor_data)
6871
self.__static_broadcaster = StaticTransformBroadcaster(node)
6972
transform_stamped = TransformStamped()
7073
transform_stamped.header.frame_id = self._frame_id

webots_ros2_core/webots_ros2_core/devices/light_sensor_device.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
"""Webots LightSensor device wrapper for ROS2."""
1616

17+
from rclpy.qos import qos_profile_sensor_data
1718
from sensor_msgs.msg import Illuminance
1819
from webots_ros2_core.math.interpolation import interpolate_lookup_table
1920
from .sensor_device import SensorDevice
@@ -48,7 +49,8 @@ def __init__(self, node, device_key, wb_device, params=None):
4849
# Create topics
4950
self._publisher = None
5051
if not self._disable:
51-
self._publisher = self._node.create_publisher(Illuminance, self._topic_name, 1)
52+
self._publisher = self._node.create_publisher(Illuminance, self._topic_name,
53+
qos_profile_sensor_data)
5254

5355
def __get_variance(self, raw_value):
5456
table = self._wb_device.getLookupTable()
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Copyright 2021 Intelligent Robotics Labs URJC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""RangeFinder device."""
16+
17+
import numpy as np
18+
from sensor_msgs.msg import Image
19+
from rclpy.qos import qos_profile_sensor_data
20+
from .sensor_device import SensorDevice
21+
22+
23+
class RangeFinderDevice(SensorDevice):
24+
"""
25+
ROS2 wrapper for Webots RangeFinder node.
26+
27+
Creates suitable ROS2 interface based on Webots
28+
[RangeFinder](https://cyberbotics.com/doc/reference/rangefinder) node instance:
29+
30+
It allows the following functinalities:
31+
- Publishes raw depth image of type `sensor_msgs/Image`
32+
33+
Args:
34+
node (WebotsNode): The ROS2 node.
35+
device_key (str): Unique identifier of the device used for configuration.
36+
wb_device (RangeFinder): Webots node of type RangeFinder.
37+
38+
Kwargs:
39+
params (dict): Inherited from `SensorDevice`
40+
41+
"""
42+
43+
def __init__(self, node, device_key, wb_device, params=None):
44+
super().__init__(node, device_key, wb_device, params)
45+
self._image_publisher = None
46+
47+
# Create topics
48+
if not self._disable:
49+
self._image_publisher = self._node.create_publisher(
50+
Image,
51+
self._topic_name + '/image_depth',
52+
qos_profile_sensor_data
53+
)
54+
55+
# Load parameters
56+
camera_period_param = node.declare_parameter(wb_device.getName() + '_period', self._timestep)
57+
self._camera_period = camera_period_param.value
58+
59+
def step(self):
60+
stamp = super().step()
61+
if not stamp:
62+
return
63+
# Publish camera data
64+
if self._image_publisher.get_subscription_count() > 0 or self._always_publish:
65+
self._wb_device.enable(self._timestep)
66+
67+
# Image data
68+
msg = Image()
69+
msg.header.stamp = stamp
70+
msg.header.frame_id = self._frame_id
71+
msg.height = self._wb_device.getHeight()
72+
msg.width = self._wb_device.getWidth()
73+
msg.is_bigendian = False
74+
msg.step = self._wb_device.getWidth() * 4
75+
76+
if self._wb_device.getRangeImage() is None:
77+
return
78+
image_array = np.array(self._wb_device.getRangeImage(), dtype="float32")
79+
80+
# We pass `data` directly to we avoid using `data` setter.
81+
# Otherwise ROS2 converts data to `array.array` which slows down the simulation as it copies memory internally.
82+
# Both, `bytearray` and `array.array`, implement Python buffer protocol, so we should not see unpredictable
83+
# behavior.
84+
# deepcode ignore W0212: Avoid conversion from `bytearray` to `array.array`.
85+
msg._data = image_array.tobytes()
86+
msg.encoding = '32FC1'
87+
88+
self._image_publisher.publish(msg)
89+
90+
else:
91+
self._wb_device.disable()

webots_ros2_tiago/resource/tiago.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ webots_driver:
77
robot_base_frame: base_link
88
synchronization: false
99
use_joint_state_publisher: true
10-
wheel_distance: 0.404
11-
wheel_radius: 0.1955
10+
wheel_distance: 0.5
11+
wheel_radius: 0.12

0 commit comments

Comments
 (0)