Add aruco detector and hik camera driver source
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
build/
|
||||
install/
|
||||
log/
|
||||
.cache/
|
||||
@@ -0,0 +1,168 @@
|
||||
import sys
|
||||
import numpy as np
|
||||
import cv2
|
||||
import cv2.aruco as aruco
|
||||
import math
|
||||
from ctypes import *
|
||||
|
||||
# MVS 파이썬 SDK 경로 추가
|
||||
sys.path.append("/opt/MVS/Samples/64/Python/MvImport")
|
||||
from MvCameraControl_class import *
|
||||
|
||||
def rotation_matrix_to_quaternion(R):
|
||||
"""
|
||||
회전 행렬(Rotation Matrix)을 수치적으로 안정적인 쿼터니언(qx, qy, qz, qw)으로 변환
|
||||
"""
|
||||
tr = R[0, 0] + R[1, 1] + R[2, 2]
|
||||
|
||||
if tr > 0:
|
||||
S = math.sqrt(tr + 1.0) * 2
|
||||
qw = 0.25 * S
|
||||
qx = (R[2, 1] - R[1, 2]) / S
|
||||
qy = (R[0, 2] - R[2, 0]) / S
|
||||
qz = (R[1, 0] - R[0, 1]) / S
|
||||
elif (R[0, 0] > R[1, 1]) and (R[0, 0] > R[2, 2]):
|
||||
S = math.sqrt(1.0 + R[0, 0] - R[1, 1] - R[2, 2]) * 2
|
||||
qw = (R[2, 1] - R[1, 2]) / S
|
||||
qx = 0.25 * S
|
||||
qy = (R[0, 1] + R[1, 0]) / S
|
||||
qz = (R[0, 2] + R[2, 0]) / S
|
||||
elif R[1, 1] > R[2, 2]:
|
||||
S = math.sqrt(1.0 + R[1, 1] - R[0, 0] - R[2, 2]) * 2
|
||||
qw = (R[0, 2] - R[2, 0]) / S
|
||||
qx = (R[0, 1] + R[1, 0]) / S
|
||||
qy = 0.25 * S
|
||||
qz = (R[1, 2] + R[2, 1]) / S
|
||||
else:
|
||||
S = math.sqrt(1.0 + R[2, 2] - R[0, 0] - R[1, 1]) * 2
|
||||
qw = (R[1, 0] - R[0, 1]) / S
|
||||
qx = (R[0, 2] + R[2, 0]) / S
|
||||
qy = (R[1, 2] + R[2, 1]) / S
|
||||
qz = 0.25 * S
|
||||
|
||||
if tr <= 0:
|
||||
if (R[0, 0] > R[1, 1]) and (R[0, 0] > R[2, 2]): pass
|
||||
elif R[1, 1] > R[2, 2]: qw = (R[0, 2] - R[2, 0]) / S
|
||||
else: qw = (R[1, 0] - R[0, 1]) / S
|
||||
|
||||
return qx, qy, qz, qw
|
||||
|
||||
def main():
|
||||
# 1. 카메라 초기화
|
||||
deviceList = MV_CC_DEVICE_INFO_LIST()
|
||||
tlayerType = MV_GIGE_DEVICE | MV_USB_DEVICE
|
||||
ret = MvCamera.MV_CC_EnumDevices(tlayerType, deviceList)
|
||||
if ret != 0:
|
||||
print(f"카메라 검색 실패! ret[0x{ret:x}]")
|
||||
return
|
||||
|
||||
if deviceList.nDeviceNum == 0:
|
||||
print("연결된 카메라가 없습니다.")
|
||||
return
|
||||
|
||||
stDeviceImgLimit = cast(deviceList.pDeviceInfo[0], POINTER(MV_CC_DEVICE_INFO)).contents
|
||||
cam = MvCamera()
|
||||
cam.MV_CC_CreateHandle(stDeviceImgLimit)
|
||||
cam.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)
|
||||
|
||||
cam.MV_CC_SetEnumValue("ExposureAuto", 0)
|
||||
cam.MV_CC_SetFloatValue("ExposureTime", 50000.0)
|
||||
cam.MV_CC_SetBoolValue("AcquisitionFrameRateEnable", True)
|
||||
cam.MV_CC_SetFloatValue("AcquisitionFrameRate", 15.0)
|
||||
|
||||
ret = cam.MV_CC_StartGrabbing()
|
||||
|
||||
# 150mm 마커 크기 반영 (0.15미터)
|
||||
marker_size = 0.15
|
||||
|
||||
# 캘리브레이션 파라미터 (1440x1080 규격 원본 파라미터)
|
||||
fx, fy, cx, cy = 1203.078148, 1206.096396, 699.186863, 565.715472
|
||||
mtx = np.array([[fx, 0, cx], [0, fy, cy], [0, 0, 1]])
|
||||
dist = np.array([[-0.102740, 0.093985, -0.000759, -0.001804, 0.0]])
|
||||
|
||||
# 7x7 출력 마커 설정
|
||||
dictionary = aruco.getPredefinedDictionary(aruco.DICT_7X7_50)
|
||||
parameters = aruco.DetectorParameters()
|
||||
|
||||
try:
|
||||
while True:
|
||||
stOutFrame = MV_FRAME_OUT()
|
||||
ret = cam.MV_CC_GetImageBuffer(stOutFrame, 1000)
|
||||
if ret == 0:
|
||||
if stOutFrame.pBufAddr:
|
||||
addr = cast(stOutFrame.pBufAddr, c_void_p).value
|
||||
nWidth = stOutFrame.stFrameInfo.nWidth
|
||||
nHeight = stOutFrame.stFrameInfo.nHeight
|
||||
nFrameLen = stOutFrame.stFrameInfo.nFrameLen
|
||||
|
||||
pData = (c_ubyte * nFrameLen).from_address(addr)
|
||||
img_raw = np.frombuffer(pData, dtype=np.uint8).reshape(nHeight, nWidth)
|
||||
frame = cv2.cvtColor(img_raw, cv2.COLOR_BayerGB2BGR)
|
||||
|
||||
# ArUco 인식
|
||||
corners, ids, _ = aruco.detectMarkers(frame, dictionary, parameters=parameters)
|
||||
if ids is not None:
|
||||
# 꼭짓점 좌표 정밀도를 소수점 이하(Sub-pixel) 단위로 보정하는 연산
|
||||
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
||||
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001)
|
||||
for corner in corners:
|
||||
cv2.cornerSubPix(gray, corner, (5, 5), (-1, -1), criteria)
|
||||
|
||||
for i in range(len(ids)):
|
||||
marker_id = ids[i][0]
|
||||
rvec, tvec, _ = aruco.estimatePoseSingleMarkers(corners[i], marker_size, mtx, dist)
|
||||
|
||||
# 회전 변환 행렬 계산
|
||||
rmat, _ = cv2.Rodrigues(rvec)
|
||||
|
||||
# 💡 [수정] OpenCV 기준계와 매칭하기 위한 X축 180도 회전 보정 행렬
|
||||
R_flip = np.array([
|
||||
[1, 0, 0],
|
||||
[0, -1, 0],
|
||||
[0, 0, -1]
|
||||
], dtype=np.float32)
|
||||
|
||||
# 회전 행렬 교정 연산
|
||||
rmat_corrected = np.dot(rmat, R_flip)
|
||||
|
||||
# 보정된 회전 행렬 기반으로 쿼터니언 변환 계산
|
||||
qx, qy, qz, qw = rotation_matrix_to_quaternion(rmat_corrected)
|
||||
|
||||
# 시각화할 좌표축 벡터도 보정 처리
|
||||
rvec_corrected, _ = cv2.Rodrigues(rmat_corrected)
|
||||
|
||||
# 마커 '정중앙' 원점 추출 및 수치 맵핑
|
||||
tvec_center = tvec[0][0]
|
||||
x, y, z = tvec_center[0], tvec_center[1], tvec_center[2]
|
||||
|
||||
# 시각화 (마커 테두리선 및 3축 기둥 렌더링)
|
||||
aruco.drawDetectedMarkers(frame, corners)
|
||||
cv2.drawFrameAxes(frame, mtx, dist, rvec_corrected, tvec_center, 0.05)
|
||||
|
||||
# 텍스트 출력 위치 설정
|
||||
text_pos = (int(corners[i][0][0][0]), int(corners[i][0][0][1]) - 75)
|
||||
|
||||
# 텍스트 색상을 실제 시각화 축 색상과 1:1 일치 (BGR 포맷 기준)
|
||||
cv2.putText(frame, f"ID:{marker_id}", text_pos, cv2.FONT_HERSHEY_SIMPLEX, 0.45, (255, 255, 255), 2)
|
||||
cv2.putText(frame, f"X:{x:.2f}m (Red)", (text_pos[0], text_pos[1] + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 255), 2)
|
||||
cv2.putText(frame, f"Y:{y:.2f}m (Green)", (text_pos[0], text_pos[1] + 40), cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 255, 0), 2)
|
||||
cv2.putText(frame, f"Z:{z:.2f}m (Blue)", (text_pos[0], text_pos[1] + 60), cv2.FONT_HERSHEY_SIMPLEX, 0.45, (255, 0, 0), 2)
|
||||
|
||||
# 수치 안정성이 확보된 쿼터니언 정보 출력
|
||||
quat_text = f"Q [x:{qx:.3f}, y:{qy:.3f}, z:{qz:.3f}, w:{qw:.3f}]"
|
||||
cv2.putText(frame, quat_text, (text_pos[0], text_pos[1] + 80), cv2.FONT_HERSHEY_SIMPLEX, 0.42, (0, 255, 255), 2)
|
||||
|
||||
cv2.imshow("Hikrobot ArUco V2 (Pose with Units)", frame)
|
||||
|
||||
cam.MV_CC_FreeImageBuffer(stOutFrame)
|
||||
|
||||
if cv2.waitKey(1) & 0xFF == ord('q'):
|
||||
break
|
||||
finally:
|
||||
cam.MV_CC_StopGrabbing()
|
||||
cam.MV_CC_CloseDevice()
|
||||
cam.MV_CC_DestroyHandle()
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+258
@@ -0,0 +1,258 @@
|
||||
#!/usr/bin/env python3
|
||||
import rclpy
|
||||
from rclpy.node import Node
|
||||
from rclpy.qos import QoSProfile, ReliabilityPolicy, HistoryPolicy
|
||||
from sensor_msgs.msg import Image, CompressedImage
|
||||
from geometry_msgs.msg import PoseStamped, TransformStamped
|
||||
import tf2_ros
|
||||
import numpy as np
|
||||
import cv2
|
||||
import cv2.aruco as aruco
|
||||
import math
|
||||
|
||||
class ArUcoDetectorNode(Node):
|
||||
def __init__(self):
|
||||
super().__init__('aruco_detector_node')
|
||||
|
||||
# --- [Parameters] ---
|
||||
self.declare_parameter('image_topic', '/camera/image')
|
||||
self.declare_parameter('pose_topic', '/aruco_detector/pose')
|
||||
self.declare_parameter('marker_size', 0.15) # 150mm
|
||||
self.declare_parameter('camera_frame', 'camera_optical_link')
|
||||
self.declare_parameter('show_image', False) # Default to false since Web UI displays it
|
||||
|
||||
self.image_topic = self.get_parameter('image_topic').value
|
||||
self.pose_topic = self.get_parameter('pose_topic').value
|
||||
self.marker_size = self.get_parameter('marker_size').value
|
||||
self.camera_frame = self.get_parameter('camera_frame').value
|
||||
self.show_image = self.get_parameter('show_image').value
|
||||
|
||||
# Calibration parameters (from hikrobot camera calibration)
|
||||
self.fx = 1203.078148
|
||||
self.fy = 1206.096396
|
||||
self.cx = 699.186863
|
||||
self.cy = 565.715472
|
||||
|
||||
self.mtx = np.array([[self.fx, 0, self.cx], [0, self.fy, self.cy], [0, 0, 1]])
|
||||
self.dist = np.array([[-0.102740, 0.093985, -0.000759, -0.001804, 0.0]])
|
||||
|
||||
# ArUco Setup
|
||||
self.dictionary = aruco.getPredefinedDictionary(aruco.DICT_7X7_50)
|
||||
self.parameters = aruco.DetectorParameters()
|
||||
|
||||
# Publishers
|
||||
self.pose_pub = self.create_publisher(PoseStamped, self.pose_topic, 10)
|
||||
self.comp_image_pub = self.create_publisher(CompressedImage, '/aruco_detector/image/compressed', 10)
|
||||
self.image_pub = self.create_publisher(Image, '/aruco_detector/image', 10)
|
||||
|
||||
# TF Broadcaster
|
||||
self.tf_broadcaster = tf2_ros.TransformBroadcaster(self)
|
||||
|
||||
# Throttling time tracker
|
||||
self.last_process_time = self.get_clock().now()
|
||||
|
||||
# Subscriber with QoS Profile (depth=1, reliable) to avoid queue backlog
|
||||
qos_profile = QoSProfile(
|
||||
reliability=ReliabilityPolicy.RELIABLE,
|
||||
history=HistoryPolicy.KEEP_LAST,
|
||||
depth=1
|
||||
)
|
||||
self.create_subscription(Image, self.image_topic, self.image_callback, qos_profile)
|
||||
|
||||
self.get_logger().info('==========================================')
|
||||
self.get_logger().info(' ROS2 ARUCO DETECTOR HIKROBOT ')
|
||||
self.get_logger().info(f' Subscribing to: {self.image_topic}')
|
||||
self.get_logger().info(f' Publishing to: {self.pose_topic}')
|
||||
self.get_logger().info('==========================================')
|
||||
|
||||
def rotation_matrix_to_quaternion(self, R):
|
||||
tr = R[0, 0] + R[1, 1] + R[2, 2]
|
||||
if tr > 0:
|
||||
S = math.sqrt(tr + 1.0) * 2
|
||||
qw = 0.25 * S
|
||||
qx = (R[2, 1] - R[1, 2]) / S
|
||||
qy = (R[0, 2] - R[2, 0]) / S
|
||||
qz = (R[1, 0] - R[0, 1]) / S
|
||||
elif (R[0, 0] > R[1, 1]) and (R[0, 0] > R[2, 2]):
|
||||
S = math.sqrt(1.0 + R[0, 0] - R[1, 1] - R[2, 2]) * 2
|
||||
qw = (R[2, 1] - R[1, 2]) / S
|
||||
qx = 0.25 * S
|
||||
qy = (R[0, 1] + R[1, 0]) / S
|
||||
qz = (R[0, 2] + R[2, 0]) / S
|
||||
elif R[1, 1] > R[2, 2]:
|
||||
S = math.sqrt(1.0 + R[1, 1] - R[0, 0] - R[2, 2]) * 2
|
||||
qw = (R[0, 2] - R[2, 0]) / S
|
||||
qx = (R[0, 1] + R[1, 0]) / S
|
||||
qy = 0.25 * S
|
||||
qz = (R[1, 2] + R[2, 1]) / S
|
||||
else:
|
||||
S = math.sqrt(1.0 + R[2, 2] - R[0, 0] - R[1, 1]) * 2
|
||||
qw = (R[1, 0] - R[0, 1]) / S
|
||||
qx = (R[0, 2] + R[2, 0]) / S
|
||||
qy = (R[1, 2] + R[2, 1]) / S
|
||||
qz = 0.25 * S
|
||||
return qx, qy, qz, qw
|
||||
|
||||
def image_callback(self, msg):
|
||||
# Throttling to ~7Hz to prevent CPU starvation and queue backlog
|
||||
current_time = self.get_clock().now()
|
||||
dt = (current_time - self.last_process_time).nanoseconds / 1e9
|
||||
if dt < 0.14:
|
||||
return
|
||||
self.last_process_time = current_time
|
||||
|
||||
try:
|
||||
# Direct raw conversion to avoid cv_bridge dependencies
|
||||
if msg.encoding == 'rgb8':
|
||||
img_rgb = np.frombuffer(msg.data, dtype=np.uint8).reshape((msg.height, msg.width, 3))
|
||||
frame = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR)
|
||||
elif msg.encoding == 'bgr8':
|
||||
frame = np.frombuffer(msg.data, dtype=np.uint8).reshape((msg.height, msg.width, 3))
|
||||
else:
|
||||
self.get_logger().error(f'Unsupported image encoding: {msg.encoding}')
|
||||
return
|
||||
except Exception as e:
|
||||
self.get_logger().error(f'Image conversion failed: {e}')
|
||||
return
|
||||
|
||||
# Check if resize is needed to speed up ArUco detection (1440x1080 -> 360x270)
|
||||
h, w = frame.shape[:2]
|
||||
scale = 1.0
|
||||
if w > 800:
|
||||
scale = 0.25
|
||||
frame_resized = cv2.resize(frame, (int(w * scale), int(h * scale)))
|
||||
else:
|
||||
frame_resized = frame
|
||||
|
||||
# ArUco detection on resized frame for speed
|
||||
corners, ids, _ = aruco.detectMarkers(frame_resized, self.dictionary, parameters=self.parameters)
|
||||
|
||||
if ids is not None:
|
||||
# Rescale corners back to original resolution for accurate pose estimation and drawing
|
||||
if scale != 1.0:
|
||||
corners = [c / scale for c in corners]
|
||||
|
||||
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
||||
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001)
|
||||
for corner in corners:
|
||||
cv2.cornerSubPix(gray, corner, (5, 5), (-1, -1), criteria)
|
||||
|
||||
# Draw detected markers on the frame
|
||||
aruco.drawDetectedMarkers(frame, corners)
|
||||
|
||||
for i in range(len(ids)):
|
||||
marker_id = ids[i][0]
|
||||
|
||||
# Estimate pose
|
||||
rvec, tvec, _ = aruco.estimatePoseSingleMarkers(corners[i], self.marker_size, self.mtx, self.dist)
|
||||
|
||||
rmat, _ = cv2.Rodrigues(rvec)
|
||||
|
||||
# Flip to match OpenCV camera frame convention
|
||||
R_flip = np.array([
|
||||
[1, 0, 0],
|
||||
[0, -1, 0],
|
||||
[0, 0, -1]
|
||||
], dtype=np.float32)
|
||||
|
||||
rmat_corrected = np.dot(rmat, R_flip)
|
||||
qx, qy, qz, qw = self.rotation_matrix_to_quaternion(rmat_corrected)
|
||||
|
||||
tvec_center = tvec[0][0]
|
||||
x, y, z = tvec_center[0], tvec_center[1], tvec_center[2]
|
||||
|
||||
# Publish PoseStamped (embed marker_id in frame_id for web client)
|
||||
pose_msg = PoseStamped()
|
||||
pose_msg.header.stamp = msg.header.stamp
|
||||
pose_msg.header.frame_id = f'aruco_marker_{marker_id}'
|
||||
|
||||
pose_msg.pose.position.x = x
|
||||
pose_msg.pose.position.y = y
|
||||
pose_msg.pose.position.z = z
|
||||
|
||||
pose_msg.pose.orientation.x = qx
|
||||
pose_msg.pose.orientation.y = qy
|
||||
pose_msg.pose.orientation.z = qz
|
||||
pose_msg.pose.orientation.w = qw
|
||||
|
||||
self.pose_pub.publish(pose_msg)
|
||||
|
||||
# Broadcast TF Transform
|
||||
t = TransformStamped()
|
||||
t.header.stamp = msg.header.stamp
|
||||
t.header.frame_id = self.camera_frame
|
||||
t.child_frame_id = f'aruco_marker_{marker_id}'
|
||||
|
||||
t.transform.translation.x = x
|
||||
t.transform.translation.y = y
|
||||
t.transform.translation.z = z
|
||||
|
||||
t.transform.rotation.x = qx
|
||||
t.transform.rotation.y = qy
|
||||
t.transform.rotation.z = qz
|
||||
t.transform.rotation.w = qw
|
||||
|
||||
self.tf_broadcaster.sendTransform(t)
|
||||
|
||||
# Calculate Yaw angle in degrees from quaternion
|
||||
siny_cosp = 2.0 * (qw * qz + qx * qy)
|
||||
cosy_cosp = 1.0 - 2.0 * (qy * qy + qz * qz)
|
||||
yaw_deg = math.degrees(math.atan2(siny_cosp, cosy_cosp))
|
||||
|
||||
# Draw 3D axis and ID for Web UI view
|
||||
rvec_corrected, _ = cv2.Rodrigues(rmat_corrected)
|
||||
cv2.drawFrameAxes(frame, self.mtx, self.dist, rvec_corrected, tvec_center, 0.05)
|
||||
text_pos1 = (int(corners[i][0][0][0]), int(corners[i][0][0][1]) - 40)
|
||||
text_pos2 = (int(corners[i][0][0][0]), int(corners[i][0][0][1]) - 15)
|
||||
text_pos3 = (int(corners[i][0][0][0]), int(corners[i][0][0][1]) + 10)
|
||||
cv2.putText(frame, f"ID: {marker_id}", text_pos1, cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
|
||||
cv2.putText(frame, f"X:{x:.2f} Y:{y:.2f} Z:{z:.2f}", text_pos2, cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 2)
|
||||
cv2.putText(frame, f"Yaw: {yaw_deg:.1f}deg", text_pos3, cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 100, 255), 2)
|
||||
|
||||
# Scale down output image to 1/4th res (1440x1080 -> 360x270) to drastically reduce network bandwidth & browser UI load
|
||||
h_out, w_out = frame.shape[:2]
|
||||
frame_out = cv2.resize(frame, (int(w_out * 0.25), int(h_out * 0.25)))
|
||||
|
||||
# Publish compressed image for Web UI Dashboard
|
||||
try:
|
||||
_, jpeg_data = cv2.imencode('.jpg', frame_out)
|
||||
comp_msg = CompressedImage()
|
||||
comp_msg.header = msg.header
|
||||
comp_msg.format = 'jpeg'
|
||||
comp_msg.data = jpeg_data.tobytes()
|
||||
self.comp_image_pub.publish(comp_msg)
|
||||
except Exception as e:
|
||||
self.get_logger().error(f'Failed to publish compressed image: {e}')
|
||||
|
||||
# Publish raw image for web_video_server topic discovery
|
||||
try:
|
||||
raw_msg = Image()
|
||||
raw_msg.header = msg.header
|
||||
raw_msg.height = frame_out.shape[0]
|
||||
raw_msg.width = frame_out.shape[1]
|
||||
raw_msg.encoding = 'bgr8'
|
||||
raw_msg.is_bigendian = 0
|
||||
raw_msg.step = frame_out.shape[1] * 3
|
||||
raw_msg.data = frame_out.tobytes()
|
||||
self.image_pub.publish(raw_msg)
|
||||
except Exception as e:
|
||||
self.get_logger().error(f'Failed to publish raw image: {e}')
|
||||
|
||||
if self.show_image:
|
||||
cv2.imshow("ROS2 ArUco Detector", frame_out)
|
||||
cv2.waitKey(1)
|
||||
|
||||
def main(args=None):
|
||||
rclpy.init(args=args)
|
||||
node = ArUcoDetectorNode()
|
||||
try:
|
||||
rclpy.spin(node)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
cv2.destroyAllWindows()
|
||||
node.destroy_node()
|
||||
rclpy.shutdown()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: AlwaysBreak
|
||||
BraceWrapping:
|
||||
AfterClass: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterStruct: true
|
||||
BreakBeforeBraces: Custom
|
||||
ColumnLimit: 100
|
||||
ConstructorInitializerIndentWidth: 0
|
||||
ContinuationIndentWidth: 2
|
||||
DerivePointerAlignment: false
|
||||
PointerAlignment: Middle
|
||||
ReflowComments: false
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
Checks: '-*,
|
||||
performance-*,
|
||||
-performance-unnecessary-value-param,
|
||||
llvm-namespace-comment,
|
||||
modernize-redundant-void-arg,
|
||||
modernize-use-nullptr,
|
||||
modernize-use-default,
|
||||
modernize-use-override,
|
||||
modernize-loop-convert,
|
||||
modernize-make-shared,
|
||||
modernize-make-unique,
|
||||
misc-unused-parameters,
|
||||
readability-named-parameter,
|
||||
readability-redundant-smartptr-get,
|
||||
readability-redundant-string-cstr,
|
||||
readability-simplify-boolean-expr,
|
||||
readability-container-size-empty,
|
||||
readability-identifier-naming,
|
||||
'
|
||||
HeaderFilterRegex: ''
|
||||
CheckOptions:
|
||||
- key: llvm-namespace-comment.ShortNamespaceLines
|
||||
value: '10'
|
||||
- key: llvm-namespace-comment.SpacesBeforeComments
|
||||
value: '2'
|
||||
- key: misc-unused-parameters.StrictMode
|
||||
value: '1'
|
||||
- key: readability-braces-around-statements.ShortStatementLines
|
||||
value: '2'
|
||||
# type names
|
||||
- key: readability-identifier-naming.ClassCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.EnumCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.UnionCase
|
||||
value: CamelCase
|
||||
# method names
|
||||
- key: readability-identifier-naming.MethodCase
|
||||
value: camelBack
|
||||
# variable names
|
||||
- key: readability-identifier-naming.VariableCase
|
||||
value: lower_case
|
||||
# class member names
|
||||
- key: readability-identifier-naming.PrivateMemberCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.PrivateMemberSuffix
|
||||
value: '_'
|
||||
- key: readability-identifier-naming.ProtectedMemberCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.ProtectedMemberSuffix
|
||||
value: '_'
|
||||
# const static or global variables are UPPER_CASE
|
||||
- key: readability-identifier-naming.EnumConstantCase
|
||||
value: UPPER_CASE
|
||||
- key: readability-identifier-naming.StaticConstantCase
|
||||
value: UPPER_CASE
|
||||
- key: readability-identifier-naming.ClassConstantCase
|
||||
value: UPPER_CASE
|
||||
- key: readability-identifier-naming.GlobalVariableCase
|
||||
value: UPPER_CASE
|
||||
...
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
name: Build and Test
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: rostooling/setup-ros-docker:ubuntu-jammy-ros-humble-desktop-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Build hik_camera_ros2_driver
|
||||
uses: ros-tooling/action-ros-ci@v0.3
|
||||
with:
|
||||
package-name: hik_camera_ros2_driver
|
||||
target-ros2-distro: humble
|
||||
skip-tests: true
|
||||
|
||||
- name: Test hik_camera_ros2_driver
|
||||
run: |
|
||||
/usr/bin/bash .github/workflows/colcon_test.sh hik_camera_ros2_driver
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
source /opt/ros/humble/setup.sh
|
||||
cd ros_ws
|
||||
colcon test --packages-up-to "$1" --event-handlers console_cohesion+ --return-code-on-test-failure
|
||||
@@ -0,0 +1,20 @@
|
||||
build
|
||||
|
||||
devel
|
||||
|
||||
install
|
||||
|
||||
log/*
|
||||
|
||||
.catkin_workspace
|
||||
|
||||
.vscode
|
||||
|
||||
.cache
|
||||
|
||||
__pycache__
|
||||
|
||||
*~
|
||||
*.pcd
|
||||
*.gv
|
||||
*.pdf
|
||||
@@ -0,0 +1,97 @@
|
||||
# To use:
|
||||
#
|
||||
# pre-commit run -a
|
||||
#
|
||||
# Or:
|
||||
#
|
||||
# pre-commit install # (runs every time you commit in git)
|
||||
#
|
||||
# To update this file:
|
||||
#
|
||||
# pre-commit autoupdate
|
||||
#
|
||||
# See https://github.com/pre-commit/pre-commit
|
||||
|
||||
repos:
|
||||
# Standard hooks
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: check-ast
|
||||
- id: check-case-conflict
|
||||
- id: check-docstring-first
|
||||
- id: check-merge-conflict
|
||||
- id: check-symlinks
|
||||
- id: check-xml
|
||||
- id: check-yaml
|
||||
- id: debug-statements
|
||||
- id: end-of-file-fixer
|
||||
- id: mixed-line-ending
|
||||
- id: trailing-whitespace
|
||||
exclude_types: [rst]
|
||||
- id: fix-byte-order-marker
|
||||
|
||||
# Python hooks
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.19.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py36-plus]
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.8.4
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [ --fix ]
|
||||
- id: ruff-format
|
||||
|
||||
# CPP hooks
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: v14.0.3
|
||||
hooks:
|
||||
- id: clang-format
|
||||
args: ['-fallback-style=none', '-i']
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: ament_cppcheck
|
||||
name: ament_cppcheck
|
||||
description: Static code analysis of C/C++ files.
|
||||
entry: env AMENT_CPPCHECK_ALLOW_SLOW_VERSIONS=1 ament_cppcheck
|
||||
language: system
|
||||
files: \.(h\+\+|h|hh|hxx|hpp|cuh|c|cc|cpp|cu|c\+\+|cxx|tpp|txx)$
|
||||
|
||||
# Cmake hooks
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: ament_lint_cmake
|
||||
name: ament_lint_cmake
|
||||
description: Check format of CMakeLists.txt files.
|
||||
entry: ament_lint_cmake
|
||||
language: system
|
||||
files: CMakeLists\.txt$
|
||||
|
||||
# Docs - RestructuredText hooks
|
||||
- repo: https://github.com/PyCQA/doc8
|
||||
rev: v1.1.2
|
||||
hooks:
|
||||
- id: doc8
|
||||
args: ['--max-line-length=100', '--ignore=D001']
|
||||
exclude: CHANGELOG\.rst$
|
||||
|
||||
- repo: https://github.com/pre-commit/pygrep-hooks
|
||||
rev: v1.10.0
|
||||
hooks:
|
||||
- id: rst-backticks
|
||||
exclude: CHANGELOG\.rst$
|
||||
- id: rst-directive-colons
|
||||
- id: rst-inline-touching-normal
|
||||
|
||||
# Spellcheck in comments and docs
|
||||
# skipping of *.svg files is not working...
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.3.0
|
||||
hooks:
|
||||
- id: codespell
|
||||
args: ['--write-changes']
|
||||
exclude: CHANGELOG\.rst|\.(svg|pyc)$
|
||||
@@ -0,0 +1,68 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(hik_camera_ros2_driver)
|
||||
|
||||
## Use C++14
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
## By adding -Wall and -Werror, the compiler does not ignore warnings anymore,
|
||||
## enforcing cleaner code.
|
||||
add_definitions(-Wall -Werror)
|
||||
|
||||
## Export compile commands for clangd
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
find_package(ament_cmake_auto REQUIRED)
|
||||
ament_auto_find_build_dependencies()
|
||||
|
||||
ament_auto_add_library(${PROJECT_NAME} SHARED
|
||||
src/hik_camera_node.cpp
|
||||
)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC hikSDK/include)
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||
target_link_directories(${PROJECT_NAME} PUBLIC hikSDK/lib/amd64)
|
||||
install(
|
||||
DIRECTORY hikSDK/lib/amd64/
|
||||
DESTINATION lib
|
||||
)
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
||||
target_link_directories(${PROJECT_NAME} PUBLIC hikSDK/lib/arm64)
|
||||
install(
|
||||
DIRECTORY hikSDK/lib/arm64/
|
||||
DESTINATION lib
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported host system architecture: ${CMAKE_HOST_SYSTEM_PROCESSOR}!")
|
||||
endif()
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
FormatConversion
|
||||
MediaProcess
|
||||
MvCameraControl
|
||||
MVRender
|
||||
MvUsb3vTL
|
||||
)
|
||||
|
||||
rclcpp_components_register_node(${PROJECT_NAME}
|
||||
PLUGIN hik_camera_ros2_driver::HikCameraRos2DriverNode
|
||||
EXECUTABLE ${PROJECT_NAME}_node
|
||||
)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(ament_lint_auto REQUIRED)
|
||||
list(APPEND AMENT_LINT_AUTO_EXCLUDE
|
||||
ament_cmake_copyright
|
||||
ament_cmake_cpplint
|
||||
ament_cmake_uncrustify
|
||||
ament_cmake_flake8
|
||||
)
|
||||
ament_lint_auto_find_test_dependencies()
|
||||
endif()
|
||||
|
||||
ament_auto_package(
|
||||
INSTALL_TO_SHARE
|
||||
launch
|
||||
config
|
||||
)
|
||||
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,88 @@
|
||||
[](https://opensource.org/licenses/Apache-2.0)
|
||||
[](https://github.com/SMBU-PolarBear-Robotics-Team/hik_camera_ros2_driver/actions/workflows/ci.yml)
|
||||
|
||||
# hik_camera_ros2_driver
|
||||
|
||||
## Overview
|
||||
|
||||
The `hik_camera_ros2_driver` package provides a ROS 2 driver for controlling and interfacing with Hikvision cameras. It supports functionalities such as camera initialization, parameter configuration, and image publishing. This package is intended for applications requiring reliable and configurable image data acquisition in a ROS 2 environment.
|
||||
|
||||
### Executables
|
||||
|
||||
The package includes the `hik_camera_node`, which manages the camera and publishes image data along with camera information to ROS 2 topics.
|
||||
|
||||
### Subscribed Topics
|
||||
|
||||
None.
|
||||
|
||||
### Published Topics
|
||||
|
||||
- `<camera_topic>` (sensor_msgs/msg/Image)
|
||||
- The image data captured by the Hikvision camera.
|
||||
|
||||
- `<camera_topic>/camera_info` (sensor_msgs/msg/CameraInfo)
|
||||
- Camera calibration information.
|
||||
|
||||
### Parameters
|
||||
|
||||
- `exposure_time` (double, default: `5000`)
|
||||
- The camera exposure time in microseconds.
|
||||
|
||||
- `gain` (double, default: `camera`)
|
||||
- The gain setting for the camera.
|
||||
|
||||
- `acquisition_frame_rate` (double, default: `165`)
|
||||
- The acquisition frame rate in hz for the camera.
|
||||
|
||||
- `pixel_format` (string, default: `RGB8Packed`)
|
||||
- The pixel format for the image data. Supported values: `Mono8`, `Mono10`, `Mono12`, `RGB8Packed`, `BGR8Packed`, `YUV422_YUYV_Packed`, `YUV422Packed`, `BayerRG8`, `BayerRG10`, `BayerRG10Packed`, `BayerRG12`, `BayerRG12Packed`.
|
||||
|
||||
- `adc_bit_depth` (string, default: `Bits_8`)
|
||||
- The ADC bit depth for the camera. Supported values: `Bits_8`, `Bits_12`.
|
||||
|
||||
- `use_sensor_data_qos` (bool, default: true)
|
||||
- Whether to use the `sensor_data` QoS profile for image topic publication.
|
||||
|
||||
- `camera_name` (string, default: `camera`)
|
||||
- The name of the camera for identification purposes.
|
||||
|
||||
- `frame_id` (string, default: `<camera_name>_optical_frame`)
|
||||
- The frame_id assigned to the published image data.
|
||||
|
||||
- `camera_topic` (string, default: `<camera_name>/image`)
|
||||
- The topic name for publishing image and info data.
|
||||
|
||||
- `camera_info_url` (string, default: `package://hik_camera_ros2_driver/config/camera_info.yaml`)
|
||||
- The URL for the camera calibration information file.
|
||||
|
||||
### Usage
|
||||
|
||||
#### Installation
|
||||
|
||||
To use this package, build it from source or include it in your ROS 2 workspace. Ensure that all dependencies are installed. You **don't** need to install the Hikvision camera SDK and include its libraries in your environment.
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
#### Run
|
||||
|
||||
You can use the provided launch file for starting the camera node with default or custom parameters:
|
||||
|
||||
```bash
|
||||
ros2 launch hik_camera_ros2_driver hik_camera_launch.py
|
||||
```
|
||||
@@ -0,0 +1,26 @@
|
||||
image_width: 1440
|
||||
image_height: 1080
|
||||
camera_name: camera
|
||||
camera_matrix:
|
||||
rows: 3
|
||||
cols: 3
|
||||
data: [1802.660547, 0.000000, 724.336357,
|
||||
0.000000, 1805.170912, 554.682044,
|
||||
0.000000, 0.000000, 1.000000]
|
||||
distortion_model: plumb_bob
|
||||
distortion_coefficients:
|
||||
rows: 1
|
||||
cols: 5
|
||||
data: [-0.082454, 0.108549, -0.000202, -0.000127, 0.000000]
|
||||
rectification_matrix:
|
||||
rows: 3
|
||||
cols: 3
|
||||
data: [1.000000, 0.000000, 0.000000,
|
||||
0.000000, 1.000000, 0.000000,
|
||||
0.000000, 0.000000, 1.000000]
|
||||
projection_matrix:
|
||||
rows: 3
|
||||
cols: 4
|
||||
data: [1782.400391, 0.000000, 723.788187, 0.000000,
|
||||
0.000000, 1791.625488, 554.238035, 0.000000,
|
||||
0.000000, 0.000000, 1.000000, 0.000000]
|
||||
@@ -0,0 +1,15 @@
|
||||
/hik_camera_ros2_driver:
|
||||
ros__parameters:
|
||||
camera_info_url: "package://hik_camera_ros2_driver/config/camera_info.yaml"
|
||||
pixel_format: "BayerRG8" # Recommended Option: "RGB8Packed", "BayerRG8"
|
||||
adc_bit_depth: "Bits_8" # If using "BayerRG8", <adc_bit_depth> must be set as "Bits_8"; otherwise, it can be "Bits_8" or "Bits_12"
|
||||
use_sensor_data_qos: false
|
||||
camera_name: "camera"
|
||||
# frame_id: "optical_frame" # If not set, it will be set as <camera_name>_optical_frame
|
||||
# camera_topic: "image" # If not set, it will be set as <camera_name>/image
|
||||
|
||||
acquisition_frame_rate: 10.0 # Unit: Hz
|
||||
exposure_auto: true # Enable Auto Exposure (Continuous)
|
||||
exposure_time: 15000.0 # Unit: us (only used if exposure_auto: false)
|
||||
gain: 12.0 # Range: 0.0 ~ 16.9, Unit: dB
|
||||
camera_serial_number: "DA9492688"
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,135 @@
|
||||
|
||||
#ifndef _MV_ERROR_DEFINE_H_
|
||||
#define _MV_ERROR_DEFINE_H_
|
||||
|
||||
/********************************************************************/
|
||||
/// \~chinese
|
||||
/// \name 正确码定义
|
||||
/// @{
|
||||
/// \~english
|
||||
/// \name Definition of correct code
|
||||
/// @{
|
||||
#define MV_OK 0x00000000 ///< \~chinese 成功,无错误 \~english Succeeded, no error
|
||||
/// @}
|
||||
|
||||
/********************************************************************/
|
||||
/// \~chinese
|
||||
/// \name 通用错误码定义:范围0x80000000-0x800000FF
|
||||
/// @{
|
||||
/// \~english
|
||||
/// \name Definition of General error code
|
||||
/// @{
|
||||
#define MV_E_HANDLE \
|
||||
0x80000000 ///< \~chinese 错误或无效的句柄 \~english Error or invalid handle
|
||||
#define MV_E_SUPPORT \
|
||||
0x80000001 ///< \~chinese 不支持的功能 \~english Not supported function
|
||||
#define MV_E_BUFOVER 0x80000002 ///< \~chinese 缓存已满 \~english Buffer overflow
|
||||
#define MV_E_CALLORDER \
|
||||
0x80000003 ///< \~chinese 函数调用顺序错误 \~english Function calling order error
|
||||
#define MV_E_PARAMETER \
|
||||
0x80000004 ///< \~chinese 错误的参数 \~english Incorrect parameter
|
||||
#define MV_E_RESOURCE \
|
||||
0x80000006 ///< \~chinese 资源申请失败 \~english Applying resource failed
|
||||
#define MV_E_NODATA 0x80000007 ///< \~chinese 无数据 \~english No data
|
||||
#define MV_E_PRECONDITION \
|
||||
0x80000008 ///< \~chinese 前置条件有误,或运行环境已发生变化 \~english Precondition error, or running environment changed
|
||||
#define MV_E_VERSION \
|
||||
0x80000009 ///< \~chinese 版本不匹配 \~english Version mismatches
|
||||
#define MV_E_NOENOUGH_BUF \
|
||||
0x8000000A ///< \~chinese 传入的内存空间不足 \~english Insufficient memory
|
||||
#define MV_E_ABNORMAL_IMAGE \
|
||||
0x8000000B ///< \~chinese 异常图像,可能是丢包导致图像不完整 \~english Abnormal image, maybe incomplete image because of lost packet
|
||||
#define MV_E_LOAD_LIBRARY \
|
||||
0x8000000C ///< \~chinese 动态导入DLL失败 \~english Load library failed
|
||||
#define MV_E_NOOUTBUF \
|
||||
0x8000000D ///< \~chinese 没有可输出的缓存 \~english No Available Buffer
|
||||
#define MV_E_UNKNOW 0x800000FF ///< \~chinese 未知的错误 \~english Unknown error
|
||||
/// @}
|
||||
|
||||
/********************************************************************/
|
||||
/// \~chinese
|
||||
/// \name GenICam系列错误:范围0x80000100-0x800001FF
|
||||
/// @{
|
||||
/// \~english
|
||||
/// \name GenICam Series Error Codes: Range from 0x80000100 to 0x800001FF
|
||||
/// @{
|
||||
#define MV_E_GC_GENERIC 0x80000100 ///< \~chinese 通用错误 \~english General error
|
||||
#define MV_E_GC_ARGUMENT \
|
||||
0x80000101 ///< \~chinese 参数非法 \~english Illegal parameters
|
||||
#define MV_E_GC_RANGE \
|
||||
0x80000102 ///< \~chinese 值超出范围 \~english The value is out of range
|
||||
#define MV_E_GC_PROPERTY 0x80000103 ///< \~chinese 属性 \~english Property
|
||||
#define MV_E_GC_RUNTIME \
|
||||
0x80000104 ///< \~chinese 运行环境有问题 \~english Running environment error
|
||||
#define MV_E_GC_LOGICAL 0x80000105 ///< \~chinese 逻辑错误 \~english Logical error
|
||||
#define MV_E_GC_ACCESS \
|
||||
0x80000106 ///< \~chinese 节点访问条件有误 \~english Node accessing condition error
|
||||
#define MV_E_GC_TIMEOUT 0x80000107 ///< \~chinese 超时 \~english Timeout
|
||||
#define MV_E_GC_DYNAMICCAST \
|
||||
0x80000108 ///< \~chinese 转换异常 \~english Transformation exception
|
||||
#define MV_E_GC_UNKNOW \
|
||||
0x800001FF ///< \~chinese GenICam未知错误 \~english GenICam unknown error
|
||||
/// @}
|
||||
|
||||
/********************************************************************/
|
||||
/// \~chinese
|
||||
/// \name GigE_STATUS对应的错误码:范围0x80000200-0x800002FF
|
||||
/// @{
|
||||
/// \~english
|
||||
/// \name GigE_STATUS Error Codes: Range from 0x80000200 to 0x800002FF
|
||||
/// @{
|
||||
#define MV_E_NOT_IMPLEMENTED \
|
||||
0x80000200 ///< \~chinese 命令不被设备支持 \~english The command is not supported by device
|
||||
#define MV_E_INVALID_ADDRESS \
|
||||
0x80000201 ///< \~chinese 访问的目标地址不存在 \~english The target address being accessed does not exist
|
||||
#define MV_E_WRITE_PROTECT \
|
||||
0x80000202 ///< \~chinese 目标地址不可写 \~english The target address is not writable
|
||||
#define MV_E_ACCESS_DENIED \
|
||||
0x80000203 ///< \~chinese 设备无访问权限 \~english No permission
|
||||
#define MV_E_BUSY \
|
||||
0x80000204 ///< \~chinese 设备忙,或网络断开 \~english Device is busy, or network disconnected
|
||||
#define MV_E_PACKET \
|
||||
0x80000205 ///< \~chinese 网络包数据错误 \~english Network data packet error
|
||||
#define MV_E_NETER 0x80000206 ///< \~chinese 网络相关错误 \~english Network error
|
||||
#define MV_E_IP_CONFLICT \
|
||||
0x80000221 ///< \~chinese 设备IP冲突 \~english Device IP conflict
|
||||
/// @}
|
||||
|
||||
/********************************************************************/
|
||||
/// \~chinese
|
||||
/// \name USB_STATUS对应的错误码:范围0x80000300-0x800003FF
|
||||
/// @{
|
||||
/// \~english
|
||||
/// \name USB_STATUS Error Codes: Range from 0x80000300 to 0x800003FF
|
||||
/// @{
|
||||
#define MV_E_USB_READ 0x80000300 ///< \~chinese 读usb出错 \~english Reading USB error
|
||||
#define MV_E_USB_WRITE 0x80000301 ///< \~chinese 写usb出错 \~english Writing USB error
|
||||
#define MV_E_USB_DEVICE 0x80000302 ///< \~chinese 设备异常 \~english Device exception
|
||||
#define MV_E_USB_GENICAM 0x80000303 ///< \~chinese GenICam相关错误 \~english GenICam error
|
||||
#define MV_E_USB_BANDWIDTH \
|
||||
0x80000304 ///< \~chinese 带宽不足 该错误码新增 \~english Insufficient bandwidth, this error code is newly added
|
||||
#define MV_E_USB_DRIVER \
|
||||
0x80000305 ///< \~chinese 驱动不匹配或者未装驱动 \~english Driver mismatch or unmounted drive
|
||||
#define MV_E_USB_UNKNOW 0x800003FF ///< \~chinese USB未知的错误 \~english USB unknown error
|
||||
/// @}
|
||||
|
||||
/********************************************************************/
|
||||
/// \~chinese
|
||||
/// \name 升级时对应的错误码:范围0x80000400-0x800004FF
|
||||
/// @{
|
||||
/// \~english
|
||||
/// \name Upgrade Error Codes: Range from 0x80000400 to 0x800004FF
|
||||
/// @{
|
||||
#define MV_E_UPG_FILE_MISMATCH \
|
||||
0x80000400 ///< \~chinese 升级固件不匹配 \~english Firmware mismatches
|
||||
#define MV_E_UPG_LANGUSGE_MISMATCH \
|
||||
0x80000401 ///< \~chinese 升级固件语言不匹配 \~english Firmware language mismatches
|
||||
#define MV_E_UPG_CONFLICT \
|
||||
0x80000402 ///< \~chinese 升级冲突(设备已经在升级了再次请求升级即返回此错误) \~english Upgrading conflicted (repeated upgrading requests during device upgrade)
|
||||
#define MV_E_UPG_INNER_ERR \
|
||||
0x80000403 ///< \~chinese 升级时相机内部出现错误 \~english Camera internal error during upgrade
|
||||
#define MV_E_UPG_UNKNOW \
|
||||
0x800004FF ///< \~chinese 升级时未知错误 \~english Unknown error during upgrade
|
||||
/// @}
|
||||
|
||||
#endif //_MV_ERROR_DEFINE_H_
|
||||
@@ -0,0 +1,93 @@
|
||||
|
||||
#ifndef _MV_ISP_ERROR_DEFINE_H_
|
||||
#define _MV_ISP_ERROR_DEFINE_H_
|
||||
|
||||
/************************************************************************
|
||||
* 来自ISP算法库的错误码
|
||||
************************************************************************/
|
||||
// 通用类型
|
||||
#define MV_ALG_OK 0x00000000 //处理正确
|
||||
#define MV_ALG_ERR 0x10000000 //不确定类型错误
|
||||
|
||||
// 能力检查
|
||||
#define MV_ALG_E_ABILITY_ARG 0x10000001 //能力集中存在无效参数
|
||||
|
||||
// 内存检查
|
||||
#define MV_ALG_E_MEM_NULL 0x10000002 //内存地址为空
|
||||
#define MV_ALG_E_MEM_ALIGN 0x10000003 //内存对齐不满足要求
|
||||
#define MV_ALG_E_MEM_LACK 0x10000004 //内存空间大小不够
|
||||
#define MV_ALG_E_MEM_SIZE_ALIGN 0x10000005 //内存空间大小不满足对齐要求
|
||||
#define MV_ALG_E_MEM_ADDR_ALIGN 0x10000006 //内存地址不满足对齐要求
|
||||
|
||||
// 图像检查
|
||||
#define MV_ALG_E_IMG_FORMAT 0x10000007 //图像格式不正确或者不支持
|
||||
#define MV_ALG_E_IMG_SIZE 0x10000008 //图像宽高不正确或者超出范围
|
||||
#define MV_ALG_E_IMG_STEP 0x10000009 //图像宽高与step参数不匹配
|
||||
#define MV_ALG_E_IMG_DATA_NULL 0x1000000A //图像数据存储地址为空
|
||||
|
||||
// 输入输出参数检查
|
||||
#define MV_ALG_E_CFG_TYPE 0x1000000B //设置或者获取参数类型不正确
|
||||
#define MV_ALG_E_CFG_SIZE 0x1000000C //设置或者获取参数的输入、输出结构体大小不正确
|
||||
#define MV_ALG_E_PRC_TYPE 0x1000000D //处理类型不正确
|
||||
#define MV_ALG_E_PRC_SIZE 0x1000000E //处理时输入、输出参数大小不正确
|
||||
#define MV_ALG_E_FUNC_TYPE 0x1000000F //子处理类型不正确
|
||||
#define MV_ALG_E_FUNC_SIZE 0x10000010 //子处理时输入、输出参数大小不正确
|
||||
|
||||
// 运行参数检查
|
||||
#define MV_ALG_E_PARAM_INDEX 0x10000011 //index参数不正确
|
||||
#define MV_ALG_E_PARAM_VALUE 0x10000012 //value参数不正确或者超出范围
|
||||
#define MV_ALG_E_PARAM_NUM 0x10000013 //param_num参数不正确
|
||||
|
||||
// 接口调用检查
|
||||
#define MV_ALG_E_NULL_PTR 0x10000014 //函数参数指针为空
|
||||
#define MV_ALG_E_OVER_MAX_MEM 0x10000015 //超过限定的最大内存
|
||||
#define MV_ALG_E_CALL_BACK 0x10000016 //回调函数出错
|
||||
|
||||
// 算法库加密相关检查
|
||||
#define MV_ALG_E_ENCRYPT 0x10000017 //加密错误
|
||||
#define MV_ALG_E_EXPIRE 0x10000018 //算法库使用期限错误
|
||||
|
||||
// 内部模块返回的基本错误类型
|
||||
#define MV_ALG_E_BAD_ARG 0x10000019 //参数范围不正确
|
||||
#define MV_ALG_E_DATA_SIZE 0x1000001A //数据大小不正确
|
||||
#define MV_ALG_E_STEP 0x1000001B //数据step不正确
|
||||
|
||||
// cpu指令集支持错误码
|
||||
#define MV_ALG_E_CPUID 0x1000001C //cpu不支持优化代码中的指令集
|
||||
|
||||
#define MV_ALG_WARNING 0x1000001D //警告
|
||||
|
||||
#define MV_ALG_E_TIME_OUT 0x1000001E //算法库超时
|
||||
#define MV_ALG_E_LIB_VERSION 0x1000001F //算法版本号出错
|
||||
#define MV_ALG_E_MODEL_VERSION 0x10000020 //模型版本号出错
|
||||
#define MV_ALG_E_GPU_MEM_ALLOC 0x10000021 //GPU内存分配错误
|
||||
#define MV_ALG_E_FILE_NON_EXIST 0x10000022 //文件不存在
|
||||
#define MV_ALG_E_NONE_STRING 0x10000023 //字符串为空
|
||||
#define MV_ALG_E_IMAGE_CODEC 0x10000024 //图像解码器错误
|
||||
#define MV_ALG_E_FILE_OPEN 0x10000025 //打开文件错误
|
||||
#define MV_ALG_E_FILE_READ 0x10000026 //文件读取错误
|
||||
#define MV_ALG_E_FILE_WRITE 0x10000027 //文件写错误
|
||||
#define MV_ALG_E_FILE_READ_SIZE 0x10000028 //文件读取大小错误
|
||||
#define MV_ALG_E_FILE_TYPE 0x10000029 //文件类型错误
|
||||
#define MV_ALG_E_MODEL_TYPE 0x1000002A //模型类型错误
|
||||
#define MV_ALG_E_MALLOC_MEM 0x1000002B //分配内存错误
|
||||
#define MV_ALG_E_BIND_CORE_FAILED 0x1000002C //线程绑核失败
|
||||
|
||||
// 降噪特有错误码
|
||||
#define MV_ALG_E_DENOISE_NE_IMG_FORMAT 0x10402001 //噪声特性图像格式错误
|
||||
#define MV_ALG_E_DENOISE_NE_FEATURE_TYPE 0x10402002 //噪声特性类型错误
|
||||
#define MV_ALG_E_DENOISE_NE_PROFILE_NUM 0x10402003 //噪声特性个数错误
|
||||
#define MV_ALG_E_DENOISE_NE_GAIN_NUM 0x10402004 //噪声特性增益个数错误
|
||||
#define MV_ALG_E_DENOISE_NE_GAIN_VAL 0x10402005 //噪声曲线增益值输入错误
|
||||
#define MV_ALG_E_DENOISE_NE_BIN_NUM 0x10402006 //噪声曲线柱数错误
|
||||
#define MV_ALG_E_DENOISE_NE_INIT_GAIN 0x10402007 //噪声估计初始化增益设置错误
|
||||
#define MV_ALG_E_DENOISE_NE_NOT_INIT 0x10402008 //噪声估计未初始化
|
||||
#define MV_ALG_E_DENOISE_COLOR_MODE 0x10402009 //颜色空间模式错误
|
||||
#define MV_ALG_E_DENOISE_ROI_NUM 0x1040200a //图像ROI个数错误
|
||||
#define MV_ALG_E_DENOISE_ROI_ORI_PT 0x1040200b //图像ROI原点错误
|
||||
#define MV_ALG_E_DENOISE_ROI_SIZE 0x1040200c //图像ROI大小错误
|
||||
#define MV_ALG_E_DENOISE_GAIN_NOT_EXIST 0x1040200d //输入的相机增益不存在(增益个数已达上限)
|
||||
#define MV_ALG_E_DENOISE_GAIN_BEYOND_RANGE 0x1040200e //输入的相机增益不在范围内
|
||||
#define MV_ALG_E_DENOISE_NP_BUF_SIZE 0x1040200f //输入的噪声特性内存大小错误
|
||||
|
||||
#endif //_MV_ISP_ERROR_DEFINE_H_
|
||||
@@ -0,0 +1,247 @@
|
||||
|
||||
#ifndef _MV_PIXEL_TYPE_H_
|
||||
#define _MV_PIXEL_TYPE_H_
|
||||
|
||||
//#include "Base/GCTypes.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* GigE Vision (2.0.03) PIXEL FORMATS */
|
||||
/************************************************************************/
|
||||
|
||||
// Indicate if pixel is monochrome or RGB
|
||||
#define MV_GVSP_PIX_MONO 0x01000000
|
||||
#define MV_GVSP_PIX_RGB 0x02000000 // deprecated in version 1.1
|
||||
#define MV_GVSP_PIX_COLOR 0x02000000
|
||||
#define MV_GVSP_PIX_CUSTOM 0x80000000
|
||||
#define MV_GVSP_PIX_COLOR_MASK 0xFF000000
|
||||
|
||||
// Indicate effective number of bits occupied by the pixel (including padding).
|
||||
// This can be used to compute amount of memory required to store an image.
|
||||
#define MV_PIXEL_BIT_COUNT(n) ((n) << 16)
|
||||
|
||||
#define MV_GVSP_PIX_EFFECTIVE_PIXEL_SIZE_MASK 0x00FF0000
|
||||
#define MV_GVSP_PIX_EFFECTIVE_PIXEL_SIZE_SHIFT 16
|
||||
|
||||
// Pixel ID: lower 16-bit of the pixel formats
|
||||
#define MV_GVSP_PIX_ID_MASK 0x0000FFFF
|
||||
#define MV_GVSP_PIX_COUNT 0x46 // next Pixel ID available
|
||||
|
||||
enum MvGvspPixelType {
|
||||
// Undefined pixel type
|
||||
#ifdef WIN32
|
||||
PixelType_Gvsp_Undefined = 0xFFFFFFFF,
|
||||
#else
|
||||
PixelType_Gvsp_Undefined = -1,
|
||||
#endif
|
||||
// Mono buffer format defines
|
||||
PixelType_Gvsp_Mono1p = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(1) | 0x0037),
|
||||
PixelType_Gvsp_Mono2p = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(2) | 0x0038),
|
||||
PixelType_Gvsp_Mono4p = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(4) | 0x0039),
|
||||
PixelType_Gvsp_Mono8 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x0001),
|
||||
PixelType_Gvsp_Mono8_Signed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x0002),
|
||||
PixelType_Gvsp_Mono10 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0003),
|
||||
PixelType_Gvsp_Mono10_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0004),
|
||||
PixelType_Gvsp_Mono12 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0005),
|
||||
PixelType_Gvsp_Mono12_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0006),
|
||||
PixelType_Gvsp_Mono14 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0025),
|
||||
PixelType_Gvsp_Mono16 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0007),
|
||||
|
||||
// Bayer buffer format defines
|
||||
PixelType_Gvsp_BayerGR8 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x0008),
|
||||
PixelType_Gvsp_BayerRG8 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x0009),
|
||||
PixelType_Gvsp_BayerGB8 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x000A),
|
||||
PixelType_Gvsp_BayerBG8 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x000B),
|
||||
PixelType_Gvsp_BayerGR10 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x000C),
|
||||
PixelType_Gvsp_BayerRG10 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x000D),
|
||||
PixelType_Gvsp_BayerGB10 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x000E),
|
||||
PixelType_Gvsp_BayerBG10 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x000F),
|
||||
PixelType_Gvsp_BayerGR12 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0010),
|
||||
PixelType_Gvsp_BayerRG12 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0011),
|
||||
PixelType_Gvsp_BayerGB12 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0012),
|
||||
PixelType_Gvsp_BayerBG12 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0013),
|
||||
PixelType_Gvsp_BayerGR10_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0026),
|
||||
PixelType_Gvsp_BayerRG10_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0027),
|
||||
PixelType_Gvsp_BayerGB10_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0028),
|
||||
PixelType_Gvsp_BayerBG10_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0029),
|
||||
PixelType_Gvsp_BayerGR12_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x002A),
|
||||
PixelType_Gvsp_BayerRG12_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x002B),
|
||||
PixelType_Gvsp_BayerGB12_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x002C),
|
||||
PixelType_Gvsp_BayerBG12_Packed = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x002D),
|
||||
PixelType_Gvsp_BayerGR16 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x002E),
|
||||
PixelType_Gvsp_BayerRG16 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x002F),
|
||||
PixelType_Gvsp_BayerGB16 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0030),
|
||||
PixelType_Gvsp_BayerBG16 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0031),
|
||||
|
||||
// RGB Packed buffer format defines
|
||||
PixelType_Gvsp_RGB8_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(24) | 0x0014),
|
||||
PixelType_Gvsp_BGR8_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(24) | 0x0015),
|
||||
PixelType_Gvsp_RGBA8_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(32) | 0x0016),
|
||||
PixelType_Gvsp_BGRA8_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(32) | 0x0017),
|
||||
PixelType_Gvsp_RGB10_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x0018),
|
||||
PixelType_Gvsp_BGR10_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x0019),
|
||||
PixelType_Gvsp_RGB12_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x001A),
|
||||
PixelType_Gvsp_BGR12_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x001B),
|
||||
PixelType_Gvsp_RGB16_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x0033),
|
||||
PixelType_Gvsp_BGR16_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x004B),
|
||||
PixelType_Gvsp_RGBA16_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(64) | 0x0064),
|
||||
PixelType_Gvsp_BGRA16_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(64) | 0x0051),
|
||||
PixelType_Gvsp_RGB10V1_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(32) | 0x001C),
|
||||
PixelType_Gvsp_RGB10V2_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(32) | 0x001D),
|
||||
PixelType_Gvsp_RGB12V1_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(36) | 0X0034),
|
||||
PixelType_Gvsp_RGB565_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x0035),
|
||||
PixelType_Gvsp_BGR565_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0X0036),
|
||||
|
||||
// YUV Packed buffer format defines
|
||||
PixelType_Gvsp_YUV411_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(12) | 0x001E),
|
||||
PixelType_Gvsp_YUV422_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x001F),
|
||||
PixelType_Gvsp_YUV422_YUYV_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x0032),
|
||||
PixelType_Gvsp_YUV444_Packed = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(24) | 0x0020),
|
||||
PixelType_Gvsp_YCBCR8_CBYCR = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(24) | 0x003A),
|
||||
PixelType_Gvsp_YCBCR422_8 = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x003B),
|
||||
PixelType_Gvsp_YCBCR422_8_CBYCRY = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x0043),
|
||||
PixelType_Gvsp_YCBCR411_8_CBYYCRYY = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(12) | 0x003C),
|
||||
PixelType_Gvsp_YCBCR601_8_CBYCR = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(24) | 0x003D),
|
||||
PixelType_Gvsp_YCBCR601_422_8 = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x003E),
|
||||
PixelType_Gvsp_YCBCR601_422_8_CBYCRY = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x0044),
|
||||
PixelType_Gvsp_YCBCR601_411_8_CBYYCRYY = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(12) | 0x003F),
|
||||
PixelType_Gvsp_YCBCR709_8_CBYCR = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(24) | 0x0040),
|
||||
PixelType_Gvsp_YCBCR709_422_8 = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x0041),
|
||||
PixelType_Gvsp_YCBCR709_422_8_CBYCRY = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x0045),
|
||||
PixelType_Gvsp_YCBCR709_411_8_CBYYCRYY = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(12) | 0x0042),
|
||||
|
||||
// RGB Planar buffer format defines
|
||||
PixelType_Gvsp_RGB8_Planar = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(24) | 0x0021),
|
||||
PixelType_Gvsp_RGB10_Planar = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x0022),
|
||||
PixelType_Gvsp_RGB12_Planar = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x0023),
|
||||
PixelType_Gvsp_RGB16_Planar = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x0024),
|
||||
|
||||
// 自定义的图片格式
|
||||
PixelType_Gvsp_Jpeg = (MV_GVSP_PIX_CUSTOM | MV_PIXEL_BIT_COUNT(24) | 0x0001),
|
||||
|
||||
PixelType_Gvsp_Coord3D_ABC32f =
|
||||
(MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(96) | 0x00C0), //0x026000C0
|
||||
PixelType_Gvsp_Coord3D_ABC32f_Planar =
|
||||
(MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(96) | 0x00C1), //0x026000C1
|
||||
|
||||
// 该值被废弃,请参考PixelType_Gvsp_Coord3D_AC32f_64; the value is discarded
|
||||
PixelType_Gvsp_Coord3D_AC32f = (MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(40) | 0x00C2),
|
||||
// 该值被废弃; the value is discarded (已放入Chunkdata)
|
||||
PixelType_Gvsp_COORD3D_DEPTH_PLUS_MASK =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(28) | 0x0001),
|
||||
|
||||
PixelType_Gvsp_Coord3D_ABC32 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(96) | 0x3001), //0x82603001
|
||||
PixelType_Gvsp_Coord3D_AB32f =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(64) | 0x3002), //0x82403002
|
||||
PixelType_Gvsp_Coord3D_AB32 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(64) | 0x3003), //0x82403003
|
||||
PixelType_Gvsp_Coord3D_AC32f_64 =
|
||||
(MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(64) | 0x00C2), //0x024000C2
|
||||
PixelType_Gvsp_Coord3D_AC32f_Planar =
|
||||
(MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(64) | 0x00C3), //0x024000C3
|
||||
PixelType_Gvsp_Coord3D_AC32 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(64) | 0x3004), //0x82403004
|
||||
PixelType_Gvsp_Coord3D_A32f = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(32) | 0x00BD), //0x012000BD
|
||||
PixelType_Gvsp_Coord3D_A32 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(32) | 0x3005), //0x81203005
|
||||
PixelType_Gvsp_Coord3D_C32f = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(32) | 0x00BF), //0x012000BF
|
||||
PixelType_Gvsp_Coord3D_C32 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(32) | 0x3006), //0x81203006
|
||||
|
||||
PixelType_Gvsp_Coord3D_ABC16 =
|
||||
(MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x00B9), //0x023000B9
|
||||
PixelType_Gvsp_Coord3D_C16 = (MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x00B8), //0x011000B8
|
||||
|
||||
//无损压缩像素格式定义
|
||||
PixelType_Gvsp_HB_Mono8 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x0001),
|
||||
PixelType_Gvsp_HB_Mono10 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0003),
|
||||
PixelType_Gvsp_HB_Mono10_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0004),
|
||||
PixelType_Gvsp_HB_Mono12 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0005),
|
||||
PixelType_Gvsp_HB_Mono12_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0006),
|
||||
PixelType_Gvsp_HB_Mono16 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0007),
|
||||
PixelType_Gvsp_HB_BayerGR8 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x0008),
|
||||
PixelType_Gvsp_HB_BayerRG8 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x0009),
|
||||
PixelType_Gvsp_HB_BayerGB8 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x000A),
|
||||
PixelType_Gvsp_HB_BayerBG8 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x000B),
|
||||
PixelType_Gvsp_HB_BayerRBGG8 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(8) | 0x0046),
|
||||
PixelType_Gvsp_HB_BayerGR10 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x000C),
|
||||
PixelType_Gvsp_HB_BayerRG10 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x000D),
|
||||
PixelType_Gvsp_HB_BayerGB10 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x000E),
|
||||
PixelType_Gvsp_HB_BayerBG10 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x000F),
|
||||
PixelType_Gvsp_HB_BayerGR12 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0010),
|
||||
PixelType_Gvsp_HB_BayerRG12 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0011),
|
||||
PixelType_Gvsp_HB_BayerGB12 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0012),
|
||||
PixelType_Gvsp_HB_BayerBG12 =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(16) | 0x0013),
|
||||
PixelType_Gvsp_HB_BayerGR10_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0026),
|
||||
PixelType_Gvsp_HB_BayerRG10_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0027),
|
||||
PixelType_Gvsp_HB_BayerGB10_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0028),
|
||||
PixelType_Gvsp_HB_BayerBG10_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x0029),
|
||||
PixelType_Gvsp_HB_BayerGR12_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x002A),
|
||||
PixelType_Gvsp_HB_BayerRG12_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x002B),
|
||||
PixelType_Gvsp_HB_BayerGB12_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x002C),
|
||||
PixelType_Gvsp_HB_BayerBG12_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_MONO | MV_PIXEL_BIT_COUNT(12) | 0x002D),
|
||||
PixelType_Gvsp_HB_YUV422_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x001F),
|
||||
PixelType_Gvsp_HB_YUV422_YUYV_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(16) | 0x0032),
|
||||
PixelType_Gvsp_HB_RGB8_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(24) | 0x0014),
|
||||
PixelType_Gvsp_HB_BGR8_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(24) | 0x0015),
|
||||
PixelType_Gvsp_HB_RGBA8_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(32) | 0x0016),
|
||||
PixelType_Gvsp_HB_BGRA8_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(32) | 0x0017),
|
||||
PixelType_Gvsp_HB_RGB16_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x0033),
|
||||
PixelType_Gvsp_HB_BGR16_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(48) | 0x004B),
|
||||
PixelType_Gvsp_HB_RGBA16_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(64) | 0x0064),
|
||||
PixelType_Gvsp_HB_BGRA16_Packed =
|
||||
(MV_GVSP_PIX_CUSTOM | MV_GVSP_PIX_COLOR | MV_PIXEL_BIT_COUNT(64) | 0x0051),
|
||||
|
||||
};
|
||||
|
||||
//enum MvUsbPixelType
|
||||
//{
|
||||
//
|
||||
//};
|
||||
|
||||
//跨平台定义
|
||||
//Cross Platform Definition
|
||||
#ifdef WIN32
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#endif /* _MV_PIXEL_TYPE_H_ */
|
||||
@@ -0,0 +1,57 @@
|
||||
import os
|
||||
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument, SetEnvironmentVariable
|
||||
from launch.substitutions import LaunchConfiguration
|
||||
from launch_ros.actions import Node
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
# Get the package directory
|
||||
bringup_dir = get_package_share_directory("hik_camera_ros2_driver")
|
||||
params_file = LaunchConfiguration("params_file")
|
||||
log_level = LaunchConfiguration("log_level")
|
||||
|
||||
# Create the launch configuration variables
|
||||
stdout_linebuf_envvar = SetEnvironmentVariable(
|
||||
"RCUTILS_LOGGING_BUFFERED_STREAM", "1"
|
||||
)
|
||||
|
||||
colorized_output_envvar = SetEnvironmentVariable("RCUTILS_COLORIZED_OUTPUT", "1")
|
||||
|
||||
# Declare the launch arguments
|
||||
declare_params_file_cmd = DeclareLaunchArgument(
|
||||
"params_file",
|
||||
default_value=os.path.join(bringup_dir, "config", "camera_params.yaml"),
|
||||
description="The joystick configuration file path",
|
||||
)
|
||||
|
||||
declare_log_level_cmd = DeclareLaunchArgument(
|
||||
"log_level", default_value="info", description="log level"
|
||||
)
|
||||
|
||||
start_hik_camera_cmd = Node(
|
||||
name="hik_camera_ros2_driver",
|
||||
package="hik_camera_ros2_driver",
|
||||
executable="hik_camera_ros2_driver_node",
|
||||
parameters=[params_file],
|
||||
arguments=["--ros-args", "--log-level", log_level],
|
||||
output="screen",
|
||||
)
|
||||
|
||||
# Create the launch description and populate
|
||||
ld = LaunchDescription()
|
||||
|
||||
# Set environment variables
|
||||
ld.add_action(stdout_linebuf_envvar)
|
||||
ld.add_action(colorized_output_envvar)
|
||||
|
||||
# Declare the launch arguments
|
||||
ld.add_action(declare_params_file_cmd)
|
||||
ld.add_action(declare_log_level_cmd)
|
||||
|
||||
# Add the actions to launch the nodes
|
||||
ld.add_action(start_hik_camera_cmd)
|
||||
|
||||
return ld
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="3">
|
||||
<name>hik_camera_ros2_driver</name>
|
||||
<version>1.0.0</version>
|
||||
<description>hik-robot industrial camera driver ros2</description>
|
||||
<maintainer email="lihanchen2004@163.com">Lihan Chen</maintainer>
|
||||
<maintainer email="xie13318782539@163.com">Zikang Xie</maintainer>
|
||||
<license>Apache-2.0</license>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<depend>rclcpp</depend>
|
||||
<depend>rclcpp_components</depend>
|
||||
<depend>sensor_msgs</depend>
|
||||
<depend>image_transport</depend>
|
||||
<depend>image_transport_plugins</depend>
|
||||
<depend>camera_info_manager</depend>
|
||||
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
||||
@@ -0,0 +1,321 @@
|
||||
#include <string>
|
||||
|
||||
#include "MvCameraControl.h"
|
||||
#include "camera_info_manager/camera_info_manager.hpp"
|
||||
#include "image_transport/image_transport.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
|
||||
namespace hik_camera_ros2_driver
|
||||
{
|
||||
class HikCameraRos2DriverNode : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
explicit HikCameraRos2DriverNode(const rclcpp::NodeOptions & options)
|
||||
: Node("hik_camera_ros2_driver", options)
|
||||
{
|
||||
RCLCPP_INFO(this->get_logger(), "Starting HikCameraRos2DriverNode!");
|
||||
|
||||
initializeCamera();
|
||||
declareParameters();
|
||||
startCamera();
|
||||
|
||||
params_callback_handle_ = this->add_on_set_parameters_callback(
|
||||
std::bind(&HikCameraRos2DriverNode::dynamicParametersCallback, this, std::placeholders::_1));
|
||||
|
||||
capture_thread_ = std::thread(&HikCameraRos2DriverNode::captureLoop, this);
|
||||
}
|
||||
|
||||
~HikCameraRos2DriverNode() override
|
||||
{
|
||||
if (capture_thread_.joinable()) {
|
||||
capture_thread_.join();
|
||||
}
|
||||
if (camera_handle_) {
|
||||
MV_CC_StopGrabbing(camera_handle_);
|
||||
MV_CC_CloseDevice(camera_handle_);
|
||||
MV_CC_DestroyHandle(&camera_handle_);
|
||||
}
|
||||
RCLCPP_INFO(this->get_logger(), "HikCameraRos2DriverNode destroyed!");
|
||||
}
|
||||
|
||||
private:
|
||||
bool initializeCamera()
|
||||
{
|
||||
MV_CC_DEVICE_INFO_LIST device_list;
|
||||
|
||||
// enum device
|
||||
while (rclcpp::ok()) {
|
||||
n_ret_ = MV_CC_EnumDevices(MV_USB_DEVICE, &device_list);
|
||||
if (n_ret_ != MV_OK) {
|
||||
RCLCPP_ERROR(this->get_logger(), "Failed to enumerate devices, retrying...");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
} else if (device_list.nDeviceNum == 0) {
|
||||
RCLCPP_ERROR(this->get_logger(), "No camera found, retrying...");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
} else {
|
||||
RCLCPP_INFO(this->get_logger(), "Found camera count = %d", device_list.nDeviceNum);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
n_ret_ = MV_CC_CreateHandle(&camera_handle_, device_list.pDeviceInfo[0]);
|
||||
if (n_ret_ != MV_OK) {
|
||||
RCLCPP_ERROR(this->get_logger(), "Failed to create camera handle!");
|
||||
return false;
|
||||
}
|
||||
|
||||
n_ret_ = MV_CC_OpenDevice(camera_handle_);
|
||||
if (n_ret_ != MV_OK) {
|
||||
RCLCPP_ERROR(this->get_logger(), "Failed to open camera device!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get camera information
|
||||
n_ret_ = MV_CC_GetImageInfo(camera_handle_, &img_info_);
|
||||
if (n_ret_ != MV_OK) {
|
||||
RCLCPP_ERROR(this->get_logger(), "Failed to get camera image info!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Init convert param
|
||||
image_msg_.data.reserve(img_info_.nHeightMax * img_info_.nWidthMax * 3);
|
||||
convert_param_.nWidth = img_info_.nWidthValue;
|
||||
convert_param_.nHeight = img_info_.nHeightValue;
|
||||
convert_param_.enDstPixelType = PixelType_Gvsp_RGB8_Packed;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void declareParameters()
|
||||
{
|
||||
rcl_interfaces::msg::ParameterDescriptor param_desc;
|
||||
MVCC_FLOATVALUE f_value;
|
||||
param_desc.integer_range.resize(1);
|
||||
param_desc.integer_range[0].step = 1;
|
||||
|
||||
// Acquisition frame rate
|
||||
param_desc.description = "Acquisition frame rate in Hz";
|
||||
if (MV_CC_GetFloatValue(camera_handle_, "AcquisitionFrameRate", &f_value) == MV_OK) {
|
||||
param_desc.integer_range[0].from_value = f_value.fMin;
|
||||
param_desc.integer_range[0].to_value = f_value.fMax;
|
||||
} else {
|
||||
param_desc.integer_range[0].from_value = 1;
|
||||
param_desc.integer_range[0].to_value = 200;
|
||||
f_value.fCurValue = 10.0;
|
||||
}
|
||||
double acquisition_frame_rate =
|
||||
this->declare_parameter("acquisition_frame_rate", 10.0, param_desc);
|
||||
MV_CC_SetBoolValue(camera_handle_, "AcquisitionFrameRateEnable", true);
|
||||
MV_CC_SetFloatValue(camera_handle_, "AcquisitionFrameRate", acquisition_frame_rate);
|
||||
RCLCPP_INFO(this->get_logger(), "Acquisition frame rate: %f", acquisition_frame_rate);
|
||||
|
||||
// Exposure Auto & Exposure time
|
||||
bool exposure_auto = this->declare_parameter("exposure_auto", false);
|
||||
if (exposure_auto) {
|
||||
int auto_status = MV_CC_SetEnumValueByString(camera_handle_, "ExposureAuto", "Continuous");
|
||||
if (auto_status == MV_OK) {
|
||||
RCLCPP_INFO(this->get_logger(), "Exposure auto enabled (Continuous)");
|
||||
} else {
|
||||
RCLCPP_WARN(this->get_logger(), "Failed to set ExposureAuto to Continuous: 0x%x", auto_status);
|
||||
}
|
||||
} else {
|
||||
int auto_status = MV_CC_SetEnumValueByString(camera_handle_, "ExposureAuto", "Off");
|
||||
if (auto_status == MV_OK) {
|
||||
RCLCPP_INFO(this->get_logger(), "Exposure auto disabled (Off)");
|
||||
}
|
||||
|
||||
param_desc.description = "Exposure time in microseconds";
|
||||
if (MV_CC_GetFloatValue(camera_handle_, "ExposureTime", &f_value) == MV_OK) {
|
||||
param_desc.integer_range[0].from_value = f_value.fMin;
|
||||
param_desc.integer_range[0].to_value = f_value.fMax;
|
||||
} else {
|
||||
param_desc.integer_range[0].from_value = 10;
|
||||
param_desc.integer_range[0].to_value = 1000000;
|
||||
}
|
||||
double exposure_time = this->declare_parameter("exposure_time", 5000.0, param_desc);
|
||||
MV_CC_SetFloatValue(camera_handle_, "ExposureTime", exposure_time);
|
||||
RCLCPP_INFO(this->get_logger(), "Exposure time: %f", exposure_time);
|
||||
}
|
||||
|
||||
// Gain
|
||||
param_desc.description = "Gain";
|
||||
if (MV_CC_GetFloatValue(camera_handle_, "Gain", &f_value) == MV_OK) {
|
||||
param_desc.integer_range[0].from_value = f_value.fMin;
|
||||
param_desc.integer_range[0].to_value = f_value.fMax;
|
||||
f_value.fCurValue = f_value.fCurValue;
|
||||
} else {
|
||||
param_desc.integer_range[0].from_value = 0;
|
||||
param_desc.integer_range[0].to_value = 20;
|
||||
f_value.fCurValue = 12.0;
|
||||
}
|
||||
double gain = this->declare_parameter("gain", f_value.fCurValue, param_desc);
|
||||
MV_CC_SetFloatValue(camera_handle_, "Gain", gain);
|
||||
RCLCPP_INFO(this->get_logger(), "Gain: %f", gain);
|
||||
|
||||
int status;
|
||||
|
||||
// ADC Bit Depth
|
||||
param_desc.description = "ADC Bit Depth";
|
||||
param_desc.additional_constraints = "Supported values: Bits_8, Bits_12";
|
||||
std::string adc_bit_depth = this->declare_parameter("adc_bit_depth", "Bits_8", param_desc);
|
||||
status = MV_CC_SetEnumValueByString(camera_handle_, "ADCBitDepth", adc_bit_depth.c_str());
|
||||
if (status == MV_OK) {
|
||||
RCLCPP_INFO(this->get_logger(), "ADC Bit Depth set to %s", adc_bit_depth.c_str());
|
||||
} else {
|
||||
RCLCPP_ERROR(this->get_logger(), "Failed to set ADC Bit Depth, status = %d", status);
|
||||
}
|
||||
|
||||
// Pixel format
|
||||
param_desc.description = "Pixel Format";
|
||||
std::string pixel_format = this->declare_parameter("pixel_format", "RGB8Packed", param_desc);
|
||||
status = MV_CC_SetEnumValueByString(camera_handle_, "PixelFormat", pixel_format.c_str());
|
||||
if (status == MV_OK) {
|
||||
RCLCPP_INFO(this->get_logger(), "Pixel Format set to %s", pixel_format.c_str());
|
||||
} else {
|
||||
RCLCPP_ERROR(this->get_logger(), "Failed to set Pixel Format, status = %d", status);
|
||||
}
|
||||
}
|
||||
|
||||
void startCamera()
|
||||
{
|
||||
bool use_sensor_data_qos = this->declare_parameter("use_sensor_data_qos", true);
|
||||
camera_name_ = this->declare_parameter("camera_name", "camera");
|
||||
frame_id_ = this->declare_parameter("frame_id", camera_name_ + "_optical_frame");
|
||||
camera_topic_ = this->declare_parameter("camera_topic", camera_name_ + "/image");
|
||||
|
||||
auto qos = use_sensor_data_qos ? rmw_qos_profile_sensor_data : rmw_qos_profile_default;
|
||||
camera_pub_ = image_transport::create_camera_publisher(this, camera_topic_, qos);
|
||||
|
||||
MV_CC_StartGrabbing(camera_handle_);
|
||||
|
||||
// Load camera info
|
||||
camera_info_manager_ =
|
||||
std::make_unique<camera_info_manager::CameraInfoManager>(this, camera_name_);
|
||||
auto camera_info_url = this->declare_parameter(
|
||||
"camera_info_url", "package://hik_camera_ros2_driver/config/camera_info.yaml");
|
||||
if (camera_info_manager_->validateURL(camera_info_url)) {
|
||||
camera_info_manager_->loadCameraInfo(camera_info_url);
|
||||
camera_info_msg_ = camera_info_manager_->getCameraInfo();
|
||||
} else {
|
||||
RCLCPP_WARN(this->get_logger(), "Invalid camera info URL: %s", camera_info_url.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void captureLoop()
|
||||
{
|
||||
MV_FRAME_OUT out_frame;
|
||||
RCLCPP_INFO(this->get_logger(), "Publishing image!");
|
||||
|
||||
image_msg_.header.frame_id = frame_id_;
|
||||
image_msg_.encoding = "rgb8";
|
||||
|
||||
while (rclcpp::ok()) {
|
||||
n_ret_ = MV_CC_GetImageBuffer(camera_handle_, &out_frame, 1000);
|
||||
if (MV_OK == n_ret_) {
|
||||
convert_param_.pDstBuffer = image_msg_.data.data();
|
||||
convert_param_.nDstBufferSize = image_msg_.data.size();
|
||||
convert_param_.pSrcData = out_frame.pBufAddr;
|
||||
convert_param_.nSrcDataLen = out_frame.stFrameInfo.nFrameLen;
|
||||
convert_param_.enSrcPixelType = out_frame.stFrameInfo.enPixelType;
|
||||
|
||||
MV_CC_ConvertPixelType(camera_handle_, &convert_param_);
|
||||
|
||||
image_msg_.header.stamp = this->now();
|
||||
image_msg_.height = out_frame.stFrameInfo.nHeight;
|
||||
image_msg_.width = out_frame.stFrameInfo.nWidth;
|
||||
image_msg_.step = out_frame.stFrameInfo.nWidth * 3;
|
||||
image_msg_.data.resize(image_msg_.width * image_msg_.height * 3);
|
||||
|
||||
camera_info_msg_.header = image_msg_.header;
|
||||
camera_pub_.publish(image_msg_, camera_info_msg_);
|
||||
|
||||
MV_CC_FreeImageBuffer(camera_handle_, &out_frame);
|
||||
|
||||
static auto last_log_time = std::chrono::steady_clock::now();
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::seconds>(now - last_log_time).count() >= 3) {
|
||||
MVCC_FLOATVALUE f_value;
|
||||
MV_CC_GetFloatValue(camera_handle_, "ResultingFrameRate", &f_value);
|
||||
RCLCPP_DEBUG(this->get_logger(), "ResultingFrameRate: %f Hz", f_value.fCurValue);
|
||||
last_log_time = now;
|
||||
}
|
||||
|
||||
} else {
|
||||
RCLCPP_WARN(this->get_logger(), "Get buffer failed! nRet: [%x]", n_ret_);
|
||||
MV_CC_StopGrabbing(camera_handle_);
|
||||
MV_CC_StartGrabbing(camera_handle_);
|
||||
fail_count_++;
|
||||
}
|
||||
|
||||
if (fail_count_ > 5) {
|
||||
RCLCPP_FATAL(this->get_logger(), "Camera failed!");
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::SetParametersResult dynamicParametersCallback(
|
||||
const std::vector<rclcpp::Parameter> & parameters)
|
||||
{
|
||||
rcl_interfaces::msg::SetParametersResult result;
|
||||
result.successful = true;
|
||||
|
||||
for (const auto & param : parameters) {
|
||||
const auto & type = param.get_type();
|
||||
const auto & name = param.get_name();
|
||||
int status = MV_OK;
|
||||
|
||||
if (type == rclcpp::ParameterType::PARAMETER_DOUBLE) {
|
||||
if (name == "gain") {
|
||||
status = MV_CC_SetFloatValue(camera_handle_, "Gain", param.as_double());
|
||||
} else {
|
||||
result.successful = false;
|
||||
result.reason = "Unknown parameter: " + name;
|
||||
continue;
|
||||
}
|
||||
} else if (type == rclcpp::ParameterType::PARAMETER_INTEGER) {
|
||||
if (name == "exposure_time") {
|
||||
status = MV_CC_SetFloatValue(camera_handle_, "ExposureTime", param.as_int());
|
||||
} else {
|
||||
result.successful = false;
|
||||
result.reason = "Unknown parameter: " + name;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
result.successful = false;
|
||||
result.reason = "Unsupported parameter type for: " + name;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (status != MV_OK) {
|
||||
result.successful = false;
|
||||
result.reason = "Failed to set " + name + ", status = " + std::to_string(status);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void * camera_handle_ = nullptr;
|
||||
int n_ret_ = MV_OK;
|
||||
MV_IMAGE_BASIC_INFO img_info_;
|
||||
MV_CC_PIXEL_CONVERT_PARAM convert_param_;
|
||||
|
||||
sensor_msgs::msg::Image image_msg_;
|
||||
sensor_msgs::msg::CameraInfo camera_info_msg_;
|
||||
image_transport::CameraPublisher camera_pub_;
|
||||
std::unique_ptr<camera_info_manager::CameraInfoManager> camera_info_manager_;
|
||||
rclcpp::node_interfaces::OnSetParametersCallbackHandle::SharedPtr params_callback_handle_;
|
||||
|
||||
std::string camera_name_;
|
||||
std::string frame_id_;
|
||||
std::string camera_topic_;
|
||||
|
||||
std::thread capture_thread_;
|
||||
int fail_count_ = 0;
|
||||
};
|
||||
} // namespace hik_camera_ros2_driver
|
||||
|
||||
#include "rclcpp_components/register_node_macro.hpp"
|
||||
RCLCPP_COMPONENTS_REGISTER_NODE(hik_camera_ros2_driver::HikCameraRos2DriverNode)
|
||||
Reference in New Issue
Block a user