[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Build](https://github.com/SMBU-PolarBear-Robotics-Team/hik_camera_ros2_driver/actions/workflows/ci.yml/badge.svg)](https://github.com/SMBU-PolarBear-Robotics-Team/hik_camera_ros2_driver/actions/workflows/ci.yml)

# hik_camera_ros2_driver

## 개요

`hik_camera_ros2_driver` 패키지는 Hikvision 카메라를 제어하고 연동하기 위한 ROS 2 드라이버다. 카메라 초기화, 파라미터 설정, 이미지 발행 등의 기능을 제공한다. ROS 2 환경에서 신뢰성 있고 설정 가능한 이미지 데이터 취득이 필요한 애플리케이션을 위한 패키지다.

### 실행 파일

이 패키지는 `hik_camera_node`를 포함하며, 카메라를 관리하고 이미지 데이터와 카메라 정보를 ROS 2 토픽으로 발행한다.

### 구독 토픽

없음. (단, `sync_role: "slave"`일 때는 마스터 카메라의 `ae_exposure_time_us`/`ae_gain_db` 토픽을 구독한다 — 아래 `sync_role` 참조)

### 발행 토픽

- `<camera_topic>` (sensor_msgs/msg/Image)
  - Hikvision 카메라가 캡처한 이미지 데이터.

- `<camera_topic>/camera_info` (sensor_msgs/msg/CameraInfo)
  - 카메라 캘리브레이션 정보.

- `<camera_name>/ae_exposure_time_us` (std_msgs/msg/Float64)
  - 매 프레임 실제로 적용된 노출시간[µs]. `sync_role`과 무관하게 항상 발행되므로, 다른 노드가 관찰용으로 구독해도 된다.

- `<camera_name>/ae_gain_db` (std_msgs/msg/Float64)
  - 매 프레임 실제로 적용된 게인[dB]. 위와 동일하게 항상 발행된다.

### 파라미터

- `exposure_auto` (bool, 기본값: `false`)
  - Continuous 자동 노출을 켠다. `true`면 카메라가 노출을 자동으로 제어하고 `exposure_time`은 무시된다.

- `exposure_time` (double, 기본값: `5000`)
  - 수동 노출시간[µs]. `exposure_auto`가 `false`일 때만 사용된다.

- `exposure_auto_target_brightness` (int, 기본값: `128`, 범위: `0-255`)
  - 자동 노출의 목표 밝기. `exposure_auto`가 `true`일 때만 적용된다. 런타임에 변경 가능:
    ```bash
    ros2 param set /hik_camera_ros2_driver exposure_auto_target_brightness 100
    ```

- `exposure_auto_min` (double, 기본값: `100.0`)
  - 자동 노출 하한[µs]. `exposure_auto`가 `true`일 때만 적용된다. 런타임에 변경 가능.

- `exposure_auto_max` (double, 기본값: `10000.0`)
  - 자동 노출 상한[µs]. `exposure_auto`가 `true`일 때만 적용된다. 런타임에 변경 가능.

- `gain` (double)
  - 수동 게인[dB]. `gain_auto`가 `false`일 때 사용된다. 런타임에 변경 가능:
    ```bash
    ros2 param set /hik_camera_ros2_driver gain 2.0
    ```

- `gain_auto` (bool, 기본값: `false`)
  - 자동 게인을 켠다. `use_software_ae`가 `false`면 카메라 온보드 `Continuous` 모드로, `true`면 아래
    소프트웨어 AE/AG 루프로 조절된다. 런타임에 변경 가능.

- `gain_auto_max_db` (double, 기본값: `12.0`)
  - 자동 게인 상한[dB]. `gain` 파라미터 자체의 하드웨어 범위와는 별개 값으로, 하드웨어 실측 상한
    (이 카메라는 약 16.9 dB)보다 낮게 잡아 노이즈를 제한하는 용도.

- `use_software_ae` (bool, 기본값: `false`)
  - 카메라 온보드 `ExposureAuto`/`GainAuto`는 항상 캡처된 프레임 **전체**를 측광한다 — 이 SDK에는
    캡처 AOI와 별개로 측광에만 쓰는 ROI를 지정하는 GenICam 노드가 없다. `true`면 온보드 auto를 끄고,
    대신 `ae_roi_top_ratio`로 정한 ROI의 퍼센타일 밝기를 매 프레임 소프트웨어에서 직접 계산해
    `ExposureTime`/`Gain`에 바로 써넣는다. 실제로 뭔가 조절되게 하려면 `exposure_auto`와/또는
    `gain_auto`도 함께 `true`여야 한다 (아니면 `exposure_time`/`gain` 값에 고정됨). 런타임에 변경 가능.

- `ae_roi_top_ratio` (double, 기본값: `0.0`)
  - `use_software_ae`에서 측광 시 상단부터 제외할 프레임 높이 비율. `0.5`면 하단 절반만 측광한다
    (예: 하늘 제외).

- `ae_target_percentile` / `ae_target_dn` (기본값: `70.0` / `130`)
  - 소프트웨어 AE/AG의 목표값: ROI 내 이 퍼센타일이 이 DN에 도달하도록 노출/게인을 조절한다.

- `ae_saturation_percentile` / `ae_saturation_dn` (기본값: `98.0` / `245`)
  - 하드 제약: 위 목표에 도달하지 못했더라도, 이 퍼센타일이 이 DN을 넘어설 정도로는 절대 밝게 하지
    않는다.

- `ae_step_gain` (double, 기본값: `0.5`)
  - 소프트웨어 AE/AG 루프의 프레임당 보정 댐핑(0~1).

- `sync_role` (string, 기본값: `"independent"`)
  - `"independent"`: 이 카메라가 자기 노출/게인을 스스로 결정한다 (기본값, 기존 동작과 동일).
  - `"master"`: SDK가 매 프레임 주는 실제 적용값(AE 모드와 무관하게 항상 유효)을
    `<camera_name>/ae_exposure_time_us`, `<camera_name>/ae_gain_db`로 발행한다.
  - `"slave"`: 자기 자신의 자동 노출/게인 로직을 완전히 무시하고, `sync_master_camera_ns`가
    발행하는 값을 그대로 적용한다. 카메라 여러 대 중 하나(예: 가운데 카메라)가 나머지의
    노출/게인을 결정하게 하고 싶을 때 사용한다.

- `sync_master_camera_ns` (string, 기본값: `""`)
  - `sync_role`이 `"slave"`일 때, 구독할 마스터 카메라의 `camera_name` (예: `"cam2"`).

  > **동작 요약:** 마스터(예: `cam2`)의 `exposure_auto`/`gain_auto`(및 `use_software_ae`)가 전부
  > `false`이면, 마스터는 `exposure_time`/`gain`에 고정된 값으로 구동되고 그 고정값이 그대로
  > 슬레이브(`cam1`, `cam3`)에 방송되어 **3대 전부 같은 고정 노출/게인**으로 구동된다.
  > 마스터에서 `exposure_auto`/`gain_auto`(또는 `use_software_ae`)를 켜면, 마스터가 그때그때
  > 계산한 실제 적용값이 프레임마다 방송되고 **슬레이브는 그 값을 그대로 추종**한다. 이때 슬레이브
  > 자신의 `exposure_auto`/`gain_auto`/`use_software_ae` 값은 (설정되어 있더라도) 완전히
  > 무시된다 — 노출/게인 자동 조절 여부는 오직 마스터 쪽 설정만으로 결정된다.

- `acquisition_frame_rate` (double, 기본값: `165`)
  - 카메라의 취득 프레임률[Hz].

- `pixel_format` (string, 기본값: `RGB8Packed`)
  - 이미지 데이터의 픽셀 포맷. 지원값: `Mono8`, `Mono10`, `Mono12`, `RGB8Packed`, `BGR8Packed`,
    `YUV422_YUYV_Packed`, `YUV422Packed`, `BayerRG8`, `BayerRG10`, `BayerRG10Packed`, `BayerRG12`,
    `BayerRG12Packed`.

- `adc_bit_depth` (string, 기본값: `Bits_8`)
  - 카메라의 ADC 비트 심도. 지원값: `Bits_8`, `Bits_12`.

- `use_sensor_data_qos` (bool, 기본값: true)
  - 이미지 토픽 발행에 `sensor_data` QoS 프로파일을 쓸지 여부.

- `camera_name` (string, 기본값: `camera`)
  - 카메라 식별용 이름.

- `frame_id` (string, 기본값: `<camera_name>_optical_frame`)
  - 발행되는 이미지 데이터에 붙는 frame_id.

- `camera_topic` (string, 기본값: `<camera_name>/image`)
  - 이미지·정보 데이터를 발행할 토픽 이름.

- `camera_info_url` (string, 기본값: `package://hik_camera_ros2_driver/config/camera_info.yaml`)
  - 카메라 캘리브레이션 정보 파일의 URL.

- `trigger_enable` (bool, 기본값: `false`)
  - 하드웨어 트리거 모드(LINE0)를 켠다. `true`면 프레임률이 트리거 신호로 제어된다.

- `use_trigger_timestamp` (bool, 기본값: `false`)
  - 시스템 시간 대신, LiDAR 드라이버가 기록한 공유 메모리 타임스탬프를 사용한다.

- `serial_number` (string, 기본값: `""`)
  - 시리얼 번호로 특정 카메라를 선택한다. 비어 있으면 처음 검출된 카메라를 사용한다.

- `enable_interval_log` (bool, 기본값: `false`)
  - 프레임별 타임스탬프 간격 로그(`[TS]`)를 출력한다. 10프레임마다 평균/최소/최대/지터 요약도
    함께 출력한다. 런타임에 토글 가능:
    ```bash
    ros2 param set /hik_camera_ros2_driver enable_interval_log true
    ```

### 사용법

#### 설치

이 패키지를 사용하려면 소스로 빌드하거나 ROS 2 워크스페이스에 포함시키면 된다. 의존 패키지가 모두
설치되어 있는지 확인할 것. Hikvision 카메라 SDK를 별도로 설치하고 그 라이브러리를 환경에 포함시킬
필요는 **없다**.

```bash
mkdir -p ~/ros_ws/src
cd ~/ros_ws/src
```

```bash
git clone https://github.com/SMBU-PolarBear-Robotics-Team/hik_camera_ros2_driver.git
```

```bash
cd ~/ros_ws
rosdep install -r --from-paths src --ignore-src --rosdistro $ROS_DISTRO -y
```

```bash
colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release
```

#### 실행

제공된 launch 파일로 기본값 또는 커스텀 파라미터를 사용해 카메라 노드를 실행할 수 있다:

```bash
ros2 launch hik_camera_ros2_driver hik_camera_launch.py
```
