1313# Custom import
1414from isaacsim .oceansim .utils .UWrenderer_utils import UW_render
1515
16+ '''
17+ Attention:
18+
19+ Before OceanSim extension being activated, the extension isaacsim.ros2.bridge should be activated, otherwise rclpy will
20+ fail to be loaded.
21+
22+ so, we suggest that make sure the extension isaacsim.ros2.bridge is being setup to "AUTOLOADED" in Window->Extension.
23+ '''
24+ import rclpy
25+ from sensor_msgs .msg import CompressedImage
26+ import time
27+ import cv2
1628
1729class UW_Camera (Camera ):
1830
@@ -64,7 +76,8 @@ def initialize(self,
6476 viewport : bool = True ,
6577 writing_dir : str = None ,
6678 UW_yaml_path : str = None ,
67- physics_sim_view = None ):
79+ physics_sim_view = None ,
80+ enable_ros2_pub = True , uw_img_topic = "/oceansim/robot/uw_img" , ros2_pub_frequency = 5 , ros2_pub_jpeg_quality = 50 ):
6881
6982 """Configure underwater rendering properties and initialize pipelines.
7083
@@ -77,7 +90,11 @@ def initialize(self,
7790 viewport (bool, optional): Enable viewport visualization. Defaults to True.
7891 writing_dir (str, optional): Directory to save rendered images. Defaults to None.
7992 UW_yaml_path (str, optional): Path to YAML file with water properties. Defaults to None.
80- physics_sim_view (_type_, optional): _description_. Defaults to None.
93+ physics_sim_view (_type_, optional): _description_. Defaults to None.
94+ enable_ros2_pub (bool, optional): Enable ROS2 communication. Defaults to True.
95+ uw_img_topic (str, optional): ROS2 topic name for UW image. Defaults to "/oceansim/robot/uw_img".
96+ ros2_pub_frequency (int, optional): ROS2 publish frequency. Defaults to 5.
97+ ros2_pub_jpeg_quality (int, optional): ROS2 publish jpeg quality. Defaults to 50.
8198
8299 """
83100 self ._id = 0
@@ -116,9 +133,90 @@ def initialize(self,
116133 if writing_dir is not None :
117134 self ._writing = True
118135 self ._writing_backend = rep .BackendDispatch ({"paths" : {"out_dir" : writing_dir }})
136+
137+ # ROS2 configuration
138+ self ._enable_ros2_pub = enable_ros2_pub
139+ self ._uw_img_topic = uw_img_topic
140+ self ._last_publish_time = 0.0
141+ self ._ros2_pub_frequency = ros2_pub_frequency # publish frequency, hz
142+ self ._ros2_pub_jpeg_quality = ros2_pub_jpeg_quality
143+ self ._setup_ros2_publisher ()
119144
120145 print (f'[{ self ._name } ] Initialized successfully. Data writing: { self ._writing } ' )
121146
147+ def _setup_ros2_publisher (self ):
148+ '''
149+ setup the publisher for uw image
150+ '''
151+ try :
152+ if not self ._enable_ros2_pub :
153+ return
154+
155+ # Initialize ROS2 context if not already done
156+ if not rclpy .ok ():
157+ rclpy .init ()
158+ print (f'[{ self ._name } ] ROS2 context initialized' )
159+
160+ # Create uw image publisher node
161+ node_name = f'oceansim_rob_uw_img_pub_{ self ._name .lower ()} ' .replace (' ' , '_' )
162+ self ._ros2_uw_img_node = rclpy .create_node (node_name )
163+ self ._uw_img_pub = self ._ros2_uw_img_node .create_publisher (
164+ CompressedImage ,
165+ self ._uw_img_topic ,
166+ 10
167+ )
168+
169+ except Exception as e :
170+ print (f'[{ self ._name } ] ROS2 uw image publisher setup failed: { e } ' )
171+
172+ def _ros2_publish_uw_img (self , uw_img ):
173+ """
174+ publish the uw image
175+ """
176+ try :
177+ if self ._uw_img_pub is None :
178+ return
179+
180+ # fps control
181+ current_time = time .time ()
182+ if current_time - self ._last_publish_time < (1.0 / self ._frequency ):
183+ return
184+
185+ # Convert the image
186+ uw_image_cpu = uw_img .numpy ()
187+ if uw_image_cpu .dtype != np .uint8 :
188+ uw_image_cpu = uw_image_cpu .astype (np .uint8 ) # UW_render return 'rgba'
189+ uw_image_bgr = cv2 .cvtColor (uw_image_cpu , cv2 .COLOR_RGBA2BGR )
190+ encode_param = [int (cv2 .IMWRITE_JPEG_QUALITY ), self ._ros2_pub_jpeg_quality ] # JPEG quality, default 90, can be 0-100
191+ result , compressed_img = cv2 .imencode ('.jpg' , uw_image_bgr , encode_param )
192+ if not result :
193+ print (f'[{ self ._name } ] Failed to compress image to JPEG' )
194+ return
195+
196+ # Create a ROS2 Image message
197+ msg = CompressedImage ()
198+ msg .header .stamp = self ._ros2_uw_img_node .get_clock ().now ().to_msg ()
199+ msg .header .frame_id = 'uw_image'
200+ msg .format = 'jpeg'
201+ msg .data = compressed_img .tobytes ()
202+
203+ # Publish the message
204+ self ._uw_img_pub .publish (msg )
205+
206+ rclpy .spin_once (self ._ros2_uw_img_node , timeout_sec = 0.0 )
207+
208+ self ._last_publish_time = current_time
209+
210+ # debug
211+ # self._ros2_uw_img_node.get_logger().info(
212+ # f'Published image: encoding={msg.encoding}, '
213+ # f'width={msg.width}, height={msg.height}, step={msg.step}, '
214+ # f'data_size={len(msg.data)}'
215+ # )
216+
217+ except Exception as e :
218+ print (f'[{ self ._name } ] ROS2 uw image publish failed: { e } ' )
219+
122220 def render (self ):
123221 """Process and display a single frame with underwater effects.
124222
@@ -150,6 +248,9 @@ def render(self):
150248 if self ._writing :
151249 self ._writing_backend .schedule (write_image , path = f'UW_image_{ self ._id } .png' , data = uw_image )
152250 print (f'[{ self ._name } ] [{ self ._id } ] Rendered image saved to { self ._writing_backend .output_dir } ' )
251+ if self ._enable_ros2_pub :
252+ self ._ros2_publish_uw_img (uw_image )
253+ pass
153254
154255 self ._id += 1
155256
0 commit comments