
    *ih6                         d Z ddlZddlmZ ddlmZmZ ddlZddl	Z
ddlmZmZ de
j        ddfd	Z G d
 de          ZdS )au	  Intel RealSense camera implementation (e.g. D405).

Recording mode opens by serial number and records to .bag via the SDK recorder.
Playback mode opens a .bag file and reads color + depth frames;
use ``RealSenseCamera.from_bag()`` to create a playback instance.

.. warning:: **For dynamic tasks, prefer ZED cameras over RealSense.**
   RealSense cameras have known synchronization limitations that make them
   less suitable for capturing fast robot motion.  See *Synchronization issues*
   below for details.

Intrinsics note
---------------
RealSense cameras store per-device calibration on-board.  get_intrinsics()
reads the color stream's Brown-Conrady coefficients directly from the SDK,
which is the same model OpenCV uses, so the values can be passed straight
to cv2 functions without any conversion.

Synchronization issues
----------------------
RealSense cameras (especially the D405) have several properties that make
multi-camera synchronization harder than with ZED cameras:

**Timestamp reliability**
    The ``global_time_enabled`` option is supposed to stamp frames with the
    host wall-clock time, but support is inconsistent across D4xx firmware
    versions.  When it is not supported, the SDK reports hardware-relative
    timestamps (milliseconds since device boot) that must be converted to
    wall-clock time.  ``_measure_clock_offset()`` compensates for this by
    measuring the offset between the first frame timestamp and
    ``time.time_ns()`` at recording start, but this is only an approximation.

**Frame extraction drain-loop bug**
    The RealSense SDK pre-buffers frames during bag playback when
    ``set_real_time(False)`` is set.  A naive "drain the queue to get the
    latest frame" loop (appropriate for live preview) will consume every
    other buffered frame, halving the apparent FPS to ~15 fps even when the
    bag was recorded at 30 fps.  ``grab()`` therefore skips the drain loop
    in playback mode (``_is_playback = True``).

**Recommendation**
    Use ZED cameras for scene and wrist cameras in dynamic manipulation
    tasks.  ZED timestamps (``sl.TIME_REFERENCE.IMAGE``) are wall-clock
    Unix nanoseconds, consistent across cameras and with the robot
    controller clock, making multi-camera alignment straightforward.
    RealSense cameras are best suited for static or slow-motion scenes
    where their close-range depth quality is the primary requirement.
    N)Path)OptionalTuple   )CameraCameraFramedevicereturnc                     |                                  D ]K}|                    t          j        j                  r%|                    t          j        j        d           LdS )z7Enable global timestamp on all sensors that support it.r   N)query_sensorssupportsrsoptionglobal_time_enabled
set_option)r	   sensors     6/home/robot-lab/raiden_cmu/raiden/cameras/realsense.py_enable_global_timer   <   sa    &&(( @ @??29899 	@bi;Q???@ @    c                   ^   e Zd ZdZd\  ZZd\  ZZddedede	fdZ
edefd	            Zedefd
            Zedefd            ZddZddZddZdeddfdZddZddZdefdZde	fdZdefdZdeej        ej        ee	e	f         f         fdZedededd fd            Z de!fdZ"dS ) RealSenseCamerau/   Intel RealSense D4xx camera – records to .bag)i  i     camera_nameserial_numberfpsc                     || _         || _        || _        d | _        d | _        d | _        d| _        d | _        | j        | _	        | j
        | _        d | _        d S )NMbP?)_name_serial_fps	_pipeline_config_profile_depth_scale_latest_frames_COLOR_W_color_w_COLOR_H_color_h_clock_offset_ns)selfr   r   r   s       r   __init__zRealSenseCamera.__init__I   s^     
$	04,07;#("!]!]
 04r   r
   c                     | j         S N)r   r+   s    r   namezRealSenseCamera.name_   s
    zr   c                 *    t          | j                  S r.   )strr   r/   s    r   r   zRealSenseCamera.serial_numberc   s    4<   r   c                     dS )Nbag r/   s    r   recording_extensionz#RealSenseCamera.recording_extensiong   s    ur   cfg	rs.configNc                 ,   | j                             |          | _        || _        | j                            t
          j        j                                                  }|	                                | _
        |                                | _        dS )z>Start the pipeline and record the negotiated color resolution.N)r!   startr#   r"   
get_streamr   streamcoloras_video_stream_profilewidthr'   heightr)   )r+   r7   color_streams      r   _start_pipelinezRealSenseCamera._start_pipelineo   sy    ,,S11}//IO
 

!
!
#
# 	 %**,,$++--r   c                    t          j                    | _        t          j                    }|                    | j                   |                    t           j        j        | j	        | j
        t           j        j        | j                   |                    t           j        j        | j        | j        t           j        j        | j                   |                     |           t'          d| j         d| j         d| j         d| j         d	           | j                                        }t3          |           |                                }|                                | _        d S )N  [z] opened: BGR8    × @ r   )r   pipeliner!   configenable_devicer   enable_streamr<   r=   r&   r(   formatbgr8r    depth_DEPTH_W_DEPTH_Hz16rB   printr   r'   r)   r#   
get_devicer   first_depth_sensorget_depth_scaler$   )r+   r7   r	   depth_sensors       r   openzRealSenseCamera.openy   sY   ikk$,'''IOT]DM29>49	
 	
 	
 	IOT]DM29=$)	
 	
 	
 	S!!!F$* F FMF F%)]F F7;yF F F	
 	
 	
 ))++F###0022(88::r   c                 f    | j         r)| j                                          d | _         d | _        d S d S r.   )r!   stopr#   r/   s    r   closezRealSenseCamera.close   s<    > 	!N!!!!DN DMMM	! 	!r   pathc                    | j         r| j                                          t          j                    }|                    | j                   |                    t          j        j        | j	        | j
        t          j        j        | j                   |                    t          j        j        | j        | j        t          j        j        | j                   |                    t'          |                     |                     |           t+          | j                                                   t1          d| j         d| j         d| j         d| j         d	           |                                  dS )z,Restart pipeline with bag recorder attached.rD   z] recording: BGR8 rE   rF   r   N)r!   rX   r   rH   rI   r   rJ   r<   r=   r&   r(   rK   rL   r    rM   rN   rO   rP   enable_record_to_filer2   rB   r   r#   rR   rQ   r   r'   r)   _measure_clock_offset)r+   rZ   r7   s      r   start_recordingzRealSenseCamera.start_recording   sl   > 	"N!!!ikk$,'''IOT]DM29>49	
 	
 	
 	IOT]DM29=$)	
 	
 	
 	!!#d)),,,S!!!DM4466777F$* F FMF F%)]F F7;yF F F	
 	
 	
 	""$$$$$r   c                     	 | j                             d          }t          j                    }|                                }|r0t          |                                dz            }||z
  | _        dS dS # t          $ r d| _        Y dS w xY w)ab  Grab the first available frame and compute RS-to-wall-clock offset.

        ``_clock_offset_ns`` is the additive correction such that:
            wall_ns = int(frame.get_timestamp() * 1_000_000) + _clock_offset_ns

        This works whether or not global_time_enabled is supported by the
        device: if it is supported the stored timestamps are already wall-clock
        and the offset will be ~0; if not, the offset captures the difference
        between the RealSense hardware clock and the system clock at the
        moment recording begins, which is stable enough over a 10-60 s episode.
        i  
timeout_ms@B N)	r!   wait_for_framestimetime_nsget_color_frameintget_timestampr*   	Exception)r+   frameswall_nscolor_framers_nss        r   r]   z%RealSenseCamera._measure_clock_offset   s    	)^33t3DDFlnnG 0022K 8K5577)CDD(/%%%%8 8  	) 	) 	)$(D!!!!	)s   A2A8 8BBc                 P   | j         r| j                                          t          j                    }|                    | j                   |                    t          j        j        | j	        | j
        t          j        j        | j                   |                    t          j        j        | j        | j        t          j        j        | j                   || _        | j                             |          | _        t+          | j                                                   dS )z;Restart pipeline without recorder to finalize the bag file.N)r!   rX   r   rH   rI   r   rJ   r<   r=   r'   r)   rK   rL   r    rM   rN   rO   rP   r"   r:   r#   r   rR   )r+   r7   s     r   stop_recordingzRealSenseCamera.stop_recording   s    > 	"N!!! ikk$,'''IOT]DM29>49	
 	
 	
 	IOT]DM29=$)	
 	
 	
 ,,S11DM446677777r   c                     	 | j                             d          }t          | dd          s%	 | j                             d          \  }}|sn|}$|| _        dS # t
          $ r Y dS w xY w)Ni  r`   _is_playbackFTr   )r!   rc   getattrtry_wait_for_framesr%   RuntimeError)r+   rj   oknewers       r   grabzRealSenseCamera.grab   s    	^33s3CCF477 
## $ B Ba B P PIB "F	#
 #)D4 	 	 	55	s   AA 
A*)A*c                     | j         ?| j                                         }|r$t          |                                dz            S ddl} |j                    S )a(  Return the capture timestamp of the most recently grabbed frame.

        With global_time_enabled the RealSense SDK stamps frames with system
        wall-clock time, so this is on the same clock as time.time_ns() and
        can be used directly to align robot data with camera frames.
        Nrb   r   )r%   rf   rg   rh   rd   re   )r+   framerd   s      r   get_current_timestamp_nsz(RealSenseCamera.get_current_timestamp_ns   s_     *'7799E >5..009<===t|~~r   c                 .   | j         t          d          | j         }t          | dd           }||                    |          }|                                }|                                }t          j        |                                          }t          j        |                                          }|| j	        z  
                    t          j                  }t          |                                dz            }t          |||          S )Nz'No frames available. Call grab() first._alignrb   )r=   rM   timestamp_ns)r%   rt   rr   processrf   get_depth_framenp
asanyarrayget_datar$   astypefloat32rg   rh   r   )	r+   rj   alignrl   depth_framer=   	depth_rawrM   r}   s	            r   	get_framezRealSenseCamera.get_frame   s    &HIII$h--]]6**F,,..,,..k224455M+"6"6"8"899	T..66rzBB ;4466BCCe,OOOOr   c                    | j         t          d          | j                             t          j        j                                                  }|                                }t          j	        |j
        d|j        gd|j        |j        gg dgt          j                  }t          j	        |j        dd         t          j                  }|j        |j        f}|||fS )a  Return on-device calibrated intrinsics for the color stream.

        RealSense stores per-unit calibration on-board.  The distortion model
        is Brown-Conrady (identical to OpenCV's), so coefficients are directly
        usable with cv2 functions.
        Nz&Camera is not open. Call open() first.        )r   r   g      ?)dtype   )r#   rt   r;   r   r<   r=   r>   get_intrinsicsr   arrayfxppxfyppyfloat64coeffsr?   r@   )r+   rA   intrcamera_matrixdist_coeffs
image_sizes         r   r   zRealSenseCamera.get_intrinsics	  s     = GHHH}//IO
 

!
!
#
# 	 **,,gsDH%TWdh'?Q*
 
 

 ht{2A2bjAAAj$+.
k:55r   bag_pathc                    |                      |           }||_        d|_        d|_        d|_        d|_        t          j                    |_        t          j	                    |_
        t          j	                            |j
        t          |          d           |j                            |j
                  |_        |j                                                                        }|                    d           	 |j                                                                        }|                                |_        n# t(          $ r Y nw xY wt          j        t          j        j                  |_        d|_        |S )z/Open a .bag file for frame-by-frame extraction. r   r   NF)repeat_playbackT)__new__r   r   r    r$   r%   r   rG   r!   rH   r"   enable_device_from_filer2   r:   r#   rR   as_playbackset_real_timerS   rT   ri   r   r<   r=   r|   rq   )clsr   r   camplaybackdss         r   from_bagzRealSenseCamera.from_bag%  sQ    kk#	 !ikk
	))KX 	* 	
 	
 	
 }**3;77 <**,,88::u%%%	((**==??B!1133C 	 	 	D	 Xbio..

s   AE 
EEc                    | j         si S | j                             t          j        j                                                  }|                                }| j        d| j        |j	        |j
        |j        |j        |j        |j        |j        d         |j        d         |j        d         |j        d         |j        d         dS )z6Return camera intrinsics and metadata as a plain dict.	RealSenser   r            )r   modelr   r?   r@   r   r   cxcyk1k2p1p2k3)r#   r;   r   r<   r=   r>   r   r   r    r?   r@   r   r   r   r   r   )r+   rA   r   s      r   get_camera_infozRealSenseCamera.get_camera_infoF  s    } 	I}//IO
 

!
!
#
# 	 **,,!\ 9Zk''((+a.+a.+a.+a.+a.
 
 	
r   )r   )r7   r8   r
   N)r
   N)#__name__
__module____qualname____doc__r&   r(   rN   rO   r2   rg   r,   propertyr0   r   r6   rB   rV   rY   r   r^   r]   ro   boolrw   rz   r   r   r   r   ndarrayr   classmethodr   dictr   r5   r   r   r   r   C   s2       99!Hh!Hh4 4C 4 4# 4 4 4 4, c    X !s ! ! ! X! S    X. . . .; ; ; ;(! ! ! !%D %T % % % %,) ) ) ),8 8 8 8&d    &#    P; P P P P*6bj"*eCHo&M N 6 6 6 68 3 $ ;L    [@
 
 
 
 
 
 
r   r   )r   rd   pathlibr   typingr   r   numpyr   pyrealsense2r   baser   r   r	   r   r   r5   r   r   <module>r      s   / /b        " " " " " " " "         % % % % % % % %@	 @d @ @ @ @Z
 Z
 Z
 Z
 Z
f Z
 Z
 Z
 Z
 Z
r   