@@ -72,18 +72,25 @@ class _HesaiModelConfig:
7272
7373 name : str
7474 elevation_deg : list [float ] = field (repr = False )
75+ azimuth_offset_deg : list [float ] = field (repr = False )
7576 cos_el : np .ndarray = field (init = False , repr = False , compare = False )
7677 sin_el : np .ndarray = field (init = False , repr = False , compare = False )
78+ azimuth_offset_rad : np .ndarray = field (init = False , repr = False , compare = False )
7779
7880 def __post_init__ (self ) -> None :
7981 el_rad = np .radians (np .array (self .elevation_deg , dtype = np .float32 ))
8082 object .__setattr__ (self , "cos_el" , np .cos (el_rad ))
8183 object .__setattr__ (self , "sin_el" , np .sin (el_rad ))
84+ object .__setattr__ (
85+ self , "azimuth_offset_rad" ,
86+ np .radians (np .array (self .azimuth_offset_deg , dtype = np .float32 )),
87+ )
8288
8389
8490# XT32: 32 channels, 1° spacing from +15° to -16°
8591# https://www.hesaitech.com/product/xt16-32-32m/
8692_XT32_ELEVATION_DEG : list [float ] = [float (15 - i ) for i in range (32 )]
93+ _XT32_AZIMUTH_OFFSET_DEG : list [float ] = [0.0 ] * 32
8794
8895# OT128: 128 channels, non-uniform spacing from +14.985° to -24.765°
8996# Per Hesai OT128 User Manual (O01-en-260410), Appendix A / Angle Correction File:
@@ -107,12 +114,36 @@ def __post_init__(self) -> None:
107114 - 14.879 , - 15.237 , - 15.593 , - 15.948 , - 16.299 , - 16.651 , - 17.0 , - 17.347 ,
108115 - 17.701 , - 18.386 , - 19.063 , - 19.73 , - 20.376 , - 21.653 , - 23.044 , - 24.765 ,
109116]
117+ _OT128_AZIMUTH_OFFSET_DEG : list [float ] = [
118+ 0.186 , 0.185 , 1.335 , 1.343 , 0.148 , 0.147 , 0.146 , 0.146 ,
119+ 1.335 , 1.336 , 1.337 , 1.338 , 1.339 , 1.34 , 1.341 , 1.342 ,
120+ 0.128 , 0.128 , 0.127 , 0.127 , 0.107 , 0.106 , 0.105 , 0.105 ,
121+ - 3.118 , 1.315 , 4.529 , - 3.121 , 1.316 , 4.532 , - 3.124 , 1.317 ,
122+ 4.536 , - 3.127 , 1.317 , 4.539 , - 3.13 , 1.318 , 4.542 , - 3.133 ,
123+ 0.103 , 2.935 , - 1.517 , 0.103 , 2.937 , - 1.519 , 0.103 , 2.939 ,
124+ - 1.52 , 0.103 , 2.941 , - 1.521 , 0.102 , 2.943 , - 1.523 , 0.102 ,
125+ 2.945 , - 1.524 , 0.102 , 2.946 , - 1.526 , 0.102 , 2.948 , - 1.526 ,
126+ 1.324 , 4.57 , - 3.155 , 1.325 , 4.573 , - 3.157 , 1.326 , 4.575 ,
127+ - 3.159 , 1.326 , 4.578 , - 3.161 , 1.327 , 4.581 , - 3.163 , 1.328 ,
128+ 4.583 , - 3.165 , 1.329 , 4.586 , - 3.167 , 1.329 , 4.588 , - 3.168 ,
129+ 0.102 , 0.103 , 0.103 , 0.103 , 0.104 , 0.104 , 0.104 , 0.104 ,
130+ 1.337 , 1.337 , 1.338 , 1.339 , 1.34 , 1.341 , 1.341 , 1.342 ,
131+ 0.108 , 0.108 , 0.109 , 0.109 , 0.13 , 0.131 , 0.131 , 0.132 ,
132+ 1.384 , 1.384 , 1.385 , 1.385 , 1.386 , 1.386 , 1.387 , 1.387 ,
133+ 0.151 , 0.153 , 0.154 , 0.156 , 1.388 , 1.408 , 0.196 , 0.286 ,
134+ ]
110135# fmt: on
111136
112137# Model lookup by sensor type name.
113138HESAI_MODELS : dict [str , _HesaiModelConfig ] = {
114- "XT32" : _HesaiModelConfig (name = "XT32" , elevation_deg = _XT32_ELEVATION_DEG ),
115- "OT128" : _HesaiModelConfig (name = "OT128" , elevation_deg = _OT128_ELEVATION_DEG ),
139+ "XT32" : _HesaiModelConfig (
140+ name = "XT32" , elevation_deg = _XT32_ELEVATION_DEG ,
141+ azimuth_offset_deg = _XT32_AZIMUTH_OFFSET_DEG ,
142+ ),
143+ "OT128" : _HesaiModelConfig (
144+ name = "OT128" , elevation_deg = _OT128_ELEVATION_DEG ,
145+ azimuth_offset_deg = _OT128_AZIMUTH_OFFSET_DEG ,
146+ ),
116147}
117148
118149
@@ -262,13 +293,15 @@ def _decode_packet(
262293 if not np .any (valid ):
263294 return _EMPTY_POINTS
264295
265- azimuths_rad = np .radians (azimuths_raw .astype (np .float32 ) / 100.0 )
296+ # Per-channel azimuth: block azimuth + channel-specific offset
297+ block_az_rad = np .radians (azimuths_raw .astype (np .float32 ) / 100.0 )
298+ azimuths_rad = block_az_rad [:, np .newaxis ] + config .azimuth_offset_rad
266299
267300 # Hesai native frame: x = d*cos(el)*sin(az), y = d*cos(el)*cos(az), z = d*sin(el)
268301 # The TF tree (hesai_top -> base_link) handles the frame conversion.
269302 xy_dist = distances * cos_el
270- x = xy_dist * np .sin (azimuths_rad [:, np . newaxis ] )
271- y = xy_dist * np .cos (azimuths_rad [:, np . newaxis ] )
303+ x = xy_dist * np .sin (azimuths_rad )
304+ y = xy_dist * np .cos (azimuths_rad )
272305 z = distances * sin_el
273306
274307 return np .stack ([x [valid ], y [valid ], z [valid ], reflectivities [valid ]], axis = 0 )
0 commit comments