reachy2_sdk.sensors.lidar
Reachy Lidar module.
Handles all specific methods to the Lidar sensor.
1"""Reachy Lidar module. 2 3Handles all specific methods to the Lidar sensor. 4""" 5 6import logging 7from typing import Optional 8 9import cv2 10import grpc 11import numpy as np 12import numpy.typing as npt 13from google.protobuf.wrappers_pb2 import BoolValue, FloatValue 14from reachy2_sdk_api.mobile_base_lidar_pb2 import ( 15 LidarObstacleDetectionEnum, 16 LidarSafety, 17) 18from reachy2_sdk_api.mobile_base_lidar_pb2_grpc import MobileBaseLidarServiceStub 19 20from ..parts.part import Part 21 22 23class Lidar: 24 """LIDAR class for mobile base SDK.""" 25 26 def __init__(self, initial_state: LidarSafety, grpc_channel: grpc.Channel, part: Part) -> None: 27 """Initialize the LIDAR class.""" 28 self._logger = logging.getLogger(__name__) 29 self._stub = MobileBaseLidarServiceStub(grpc_channel) 30 self._part = part 31 32 self._safety_enabled: bool = initial_state.safety_on.value 33 self._safety_distance: float = initial_state.safety_distance.value 34 self._critical_distance: float = initial_state.critical_distance.value 35 36 self._obstacle_detection_status: str = LidarObstacleDetectionEnum.Name(initial_state.obstacle_detection_status.status) 37 38 def __repr__(self) -> str: 39 """Clean representation of a Reachy.""" 40 return f"""<Lidar safety_enabled={self.safety_enabled}>""" 41 42 def get_map(self) -> Optional[npt.NDArray[np.uint8]]: 43 """Retrieve the current map of the environment using lidar data. 44 45 Returns: 46 The current map of the environment as an image (NumPy array) if the lidar map is successfully 47 retrieved. Returns `None` if no lidar map is retrieved. 48 """ 49 compressed_map = self._stub.GetLidarMap(self._part._part_id) 50 if compressed_map.data == b"": 51 self._logger.error("No lidar map retrieved. (Note that the lidar map is not available in FAKE mode)") 52 return None 53 np_data = np.frombuffer(compressed_map.data, np.uint8) 54 img = cv2.imdecode(np_data, cv2.IMREAD_COLOR) 55 return img.astype(np.uint8) 56 57 @property 58 def safety_slowdown_distance(self) -> float: 59 """Get the safety distance from obstacles in meters for the mobile base. 60 61 The mobile base's speed is slowed down if the direction of speed matches the direction of 62 at least one LIDAR point within the safety distance range. 63 """ 64 return float(self._safety_distance) 65 66 @safety_slowdown_distance.setter 67 def safety_slowdown_distance(self, value: float) -> None: 68 """Set the safety distance for a Lidar sensor. 69 70 Args: 71 value: The safety distance to set for the LidarSafety object. This value specifies 72 the distance at which a safety slowdown should be initiated. 73 """ 74 self._stub.SetZuuuSafety( 75 LidarSafety( 76 safety_on=BoolValue(value=self.safety_enabled), 77 safety_distance=FloatValue(value=value), 78 critical_distance=FloatValue(value=self.safety_critical_distance), 79 ) 80 ) 81 82 @property 83 def safety_critical_distance(self) -> float: 84 """Get the critical distance in meters of the mobile base from obstacles. 85 86 The mobile base's speed is reduced to zero if the direction of speed matches the direction 87 of at least one LIDAR point within the critical distance range. If at least one point is 88 within the critical distance, even movements that move away from the obstacles are slowed 89 down to the "safety_zone" speed. 90 """ 91 return float(self._critical_distance) 92 93 @safety_critical_distance.setter 94 def safety_critical_distance(self, value: float) -> None: 95 """Set the critical distance for a Lidar safety feature. 96 97 Args: 98 value: The critical distance in meters for safety. This value specifies the distance 99 at which the mobile base should stop if moving in the direction of an obstacle. 100 If at least one point is within the critical distance, even movements that move 101 away from the obstacles are slowed down to the "safety_zone" speed. 102 """ 103 self._stub.SetZuuuSafety( 104 LidarSafety( 105 safety_on=BoolValue(value=self.safety_enabled), 106 safety_distance=FloatValue(value=self.safety_slowdown_distance), 107 critical_distance=FloatValue(value=value), 108 ) 109 ) 110 111 @property 112 def safety_enabled(self) -> bool: 113 """Get the current status of the safety feature. 114 115 Returns: 116 A boolean indicating whether the safety feature is enabled. If `True`, the safety feature is enabled. 117 """ 118 return self._safety_enabled 119 120 @safety_enabled.setter 121 def safety_enabled(self, value: bool) -> None: 122 """Set the safety status for the Lidar device. 123 124 Args: 125 value: A boolean indicating whether the safety features are enabled or disabled. If `True`, the safety feature 126 is enabled. 127 """ 128 self._stub.SetZuuuSafety( 129 LidarSafety( 130 safety_on=BoolValue(value=value), 131 safety_distance=FloatValue(value=self.safety_slowdown_distance), 132 critical_distance=FloatValue(value=self.safety_critical_distance), 133 ) 134 ) 135 136 @property 137 def obstacle_detection_status(self) -> str: 138 """Get the status of the lidar obstacle detection. 139 140 Returns: 141 The status of the lidar obstacle detection, which can be one of the following values: 142 NO_OBJECT_DETECTED, OBJECT_DETECTED_SLOWDOWN, OBJECT_DETECTED_STOP, or DETECTION_ERROR. 143 """ 144 return self._obstacle_detection_status 145 146 def reset_safety_default_distances(self) -> None: 147 """Reset default distance values for safety detection. 148 149 The reset values include: 150 - safety_critical_distance 151 - safety_slowdown_distance. 152 """ 153 self._stub.ResetDefaultSafetyDistances(self._part._part_id) 154 155 def _update_with(self, new_lidar_state: LidarSafety) -> None: 156 """Update lidar information with a new state received from a gRPC server. 157 158 Args: 159 new_lidar_state: Contains information about the lidar state received from the gRPC server. 160 """ 161 self._safety_enabled = new_lidar_state.safety_on.value 162 self._safety_distance = new_lidar_state.safety_distance.value 163 self._critical_distance = new_lidar_state.critical_distance.value 164 165 self._obstacle_detection_status = LidarObstacleDetectionEnum.Name(new_lidar_state.obstacle_detection_status.status)
24class Lidar: 25 """LIDAR class for mobile base SDK.""" 26 27 def __init__(self, initial_state: LidarSafety, grpc_channel: grpc.Channel, part: Part) -> None: 28 """Initialize the LIDAR class.""" 29 self._logger = logging.getLogger(__name__) 30 self._stub = MobileBaseLidarServiceStub(grpc_channel) 31 self._part = part 32 33 self._safety_enabled: bool = initial_state.safety_on.value 34 self._safety_distance: float = initial_state.safety_distance.value 35 self._critical_distance: float = initial_state.critical_distance.value 36 37 self._obstacle_detection_status: str = LidarObstacleDetectionEnum.Name(initial_state.obstacle_detection_status.status) 38 39 def __repr__(self) -> str: 40 """Clean representation of a Reachy.""" 41 return f"""<Lidar safety_enabled={self.safety_enabled}>""" 42 43 def get_map(self) -> Optional[npt.NDArray[np.uint8]]: 44 """Retrieve the current map of the environment using lidar data. 45 46 Returns: 47 The current map of the environment as an image (NumPy array) if the lidar map is successfully 48 retrieved. Returns `None` if no lidar map is retrieved. 49 """ 50 compressed_map = self._stub.GetLidarMap(self._part._part_id) 51 if compressed_map.data == b"": 52 self._logger.error("No lidar map retrieved. (Note that the lidar map is not available in FAKE mode)") 53 return None 54 np_data = np.frombuffer(compressed_map.data, np.uint8) 55 img = cv2.imdecode(np_data, cv2.IMREAD_COLOR) 56 return img.astype(np.uint8) 57 58 @property 59 def safety_slowdown_distance(self) -> float: 60 """Get the safety distance from obstacles in meters for the mobile base. 61 62 The mobile base's speed is slowed down if the direction of speed matches the direction of 63 at least one LIDAR point within the safety distance range. 64 """ 65 return float(self._safety_distance) 66 67 @safety_slowdown_distance.setter 68 def safety_slowdown_distance(self, value: float) -> None: 69 """Set the safety distance for a Lidar sensor. 70 71 Args: 72 value: The safety distance to set for the LidarSafety object. This value specifies 73 the distance at which a safety slowdown should be initiated. 74 """ 75 self._stub.SetZuuuSafety( 76 LidarSafety( 77 safety_on=BoolValue(value=self.safety_enabled), 78 safety_distance=FloatValue(value=value), 79 critical_distance=FloatValue(value=self.safety_critical_distance), 80 ) 81 ) 82 83 @property 84 def safety_critical_distance(self) -> float: 85 """Get the critical distance in meters of the mobile base from obstacles. 86 87 The mobile base's speed is reduced to zero if the direction of speed matches the direction 88 of at least one LIDAR point within the critical distance range. If at least one point is 89 within the critical distance, even movements that move away from the obstacles are slowed 90 down to the "safety_zone" speed. 91 """ 92 return float(self._critical_distance) 93 94 @safety_critical_distance.setter 95 def safety_critical_distance(self, value: float) -> None: 96 """Set the critical distance for a Lidar safety feature. 97 98 Args: 99 value: The critical distance in meters for safety. This value specifies the distance 100 at which the mobile base should stop if moving in the direction of an obstacle. 101 If at least one point is within the critical distance, even movements that move 102 away from the obstacles are slowed down to the "safety_zone" speed. 103 """ 104 self._stub.SetZuuuSafety( 105 LidarSafety( 106 safety_on=BoolValue(value=self.safety_enabled), 107 safety_distance=FloatValue(value=self.safety_slowdown_distance), 108 critical_distance=FloatValue(value=value), 109 ) 110 ) 111 112 @property 113 def safety_enabled(self) -> bool: 114 """Get the current status of the safety feature. 115 116 Returns: 117 A boolean indicating whether the safety feature is enabled. If `True`, the safety feature is enabled. 118 """ 119 return self._safety_enabled 120 121 @safety_enabled.setter 122 def safety_enabled(self, value: bool) -> None: 123 """Set the safety status for the Lidar device. 124 125 Args: 126 value: A boolean indicating whether the safety features are enabled or disabled. If `True`, the safety feature 127 is enabled. 128 """ 129 self._stub.SetZuuuSafety( 130 LidarSafety( 131 safety_on=BoolValue(value=value), 132 safety_distance=FloatValue(value=self.safety_slowdown_distance), 133 critical_distance=FloatValue(value=self.safety_critical_distance), 134 ) 135 ) 136 137 @property 138 def obstacle_detection_status(self) -> str: 139 """Get the status of the lidar obstacle detection. 140 141 Returns: 142 The status of the lidar obstacle detection, which can be one of the following values: 143 NO_OBJECT_DETECTED, OBJECT_DETECTED_SLOWDOWN, OBJECT_DETECTED_STOP, or DETECTION_ERROR. 144 """ 145 return self._obstacle_detection_status 146 147 def reset_safety_default_distances(self) -> None: 148 """Reset default distance values for safety detection. 149 150 The reset values include: 151 - safety_critical_distance 152 - safety_slowdown_distance. 153 """ 154 self._stub.ResetDefaultSafetyDistances(self._part._part_id) 155 156 def _update_with(self, new_lidar_state: LidarSafety) -> None: 157 """Update lidar information with a new state received from a gRPC server. 158 159 Args: 160 new_lidar_state: Contains information about the lidar state received from the gRPC server. 161 """ 162 self._safety_enabled = new_lidar_state.safety_on.value 163 self._safety_distance = new_lidar_state.safety_distance.value 164 self._critical_distance = new_lidar_state.critical_distance.value 165 166 self._obstacle_detection_status = LidarObstacleDetectionEnum.Name(new_lidar_state.obstacle_detection_status.status)
LIDAR class for mobile base SDK.
27 def __init__(self, initial_state: LidarSafety, grpc_channel: grpc.Channel, part: Part) -> None: 28 """Initialize the LIDAR class.""" 29 self._logger = logging.getLogger(__name__) 30 self._stub = MobileBaseLidarServiceStub(grpc_channel) 31 self._part = part 32 33 self._safety_enabled: bool = initial_state.safety_on.value 34 self._safety_distance: float = initial_state.safety_distance.value 35 self._critical_distance: float = initial_state.critical_distance.value 36 37 self._obstacle_detection_status: str = LidarObstacleDetectionEnum.Name(initial_state.obstacle_detection_status.status)
Initialize the LIDAR class.
43 def get_map(self) -> Optional[npt.NDArray[np.uint8]]: 44 """Retrieve the current map of the environment using lidar data. 45 46 Returns: 47 The current map of the environment as an image (NumPy array) if the lidar map is successfully 48 retrieved. Returns `None` if no lidar map is retrieved. 49 """ 50 compressed_map = self._stub.GetLidarMap(self._part._part_id) 51 if compressed_map.data == b"": 52 self._logger.error("No lidar map retrieved. (Note that the lidar map is not available in FAKE mode)") 53 return None 54 np_data = np.frombuffer(compressed_map.data, np.uint8) 55 img = cv2.imdecode(np_data, cv2.IMREAD_COLOR) 56 return img.astype(np.uint8)
Retrieve the current map of the environment using lidar data.
Returns:
The current map of the environment as an image (NumPy array) if the lidar map is successfully retrieved. Returns
None
if no lidar map is retrieved.
58 @property 59 def safety_slowdown_distance(self) -> float: 60 """Get the safety distance from obstacles in meters for the mobile base. 61 62 The mobile base's speed is slowed down if the direction of speed matches the direction of 63 at least one LIDAR point within the safety distance range. 64 """ 65 return float(self._safety_distance)
Get the safety distance from obstacles in meters for the mobile base.
The mobile base's speed is slowed down if the direction of speed matches the direction of at least one LIDAR point within the safety distance range.
83 @property 84 def safety_critical_distance(self) -> float: 85 """Get the critical distance in meters of the mobile base from obstacles. 86 87 The mobile base's speed is reduced to zero if the direction of speed matches the direction 88 of at least one LIDAR point within the critical distance range. If at least one point is 89 within the critical distance, even movements that move away from the obstacles are slowed 90 down to the "safety_zone" speed. 91 """ 92 return float(self._critical_distance)
Get the critical distance in meters of the mobile base from obstacles.
The mobile base's speed is reduced to zero if the direction of speed matches the direction of at least one LIDAR point within the critical distance range. If at least one point is within the critical distance, even movements that move away from the obstacles are slowed down to the "safety_zone" speed.
112 @property 113 def safety_enabled(self) -> bool: 114 """Get the current status of the safety feature. 115 116 Returns: 117 A boolean indicating whether the safety feature is enabled. If `True`, the safety feature is enabled. 118 """ 119 return self._safety_enabled
Get the current status of the safety feature.
Returns:
A boolean indicating whether the safety feature is enabled. If
True
, the safety feature is enabled.
137 @property 138 def obstacle_detection_status(self) -> str: 139 """Get the status of the lidar obstacle detection. 140 141 Returns: 142 The status of the lidar obstacle detection, which can be one of the following values: 143 NO_OBJECT_DETECTED, OBJECT_DETECTED_SLOWDOWN, OBJECT_DETECTED_STOP, or DETECTION_ERROR. 144 """ 145 return self._obstacle_detection_status
Get the status of the lidar obstacle detection.
Returns:
The status of the lidar obstacle detection, which can be one of the following values: NO_OBJECT_DETECTED, OBJECT_DETECTED_SLOWDOWN, OBJECT_DETECTED_STOP, or DETECTION_ERROR.
147 def reset_safety_default_distances(self) -> None: 148 """Reset default distance values for safety detection. 149 150 The reset values include: 151 - safety_critical_distance 152 - safety_slowdown_distance. 153 """ 154 self._stub.ResetDefaultSafetyDistances(self._part._part_id)
Reset default distance values for safety detection.
The reset values include:
- safety_critical_distance
- safety_slowdown_distance.