[RDK X5][132GS]自行标定

[问题分类]:自行标定
[板卡类型]:[RDK X5][132GS]

X5 132GS,请官方提供一份详细的自行标定内参和外参的教程,我下载了RDK STUDIO,但其经常提供错误的解决方法。而且目前使用的标定文件和实际生成深度图时的标定文件不一致。这是运行bash run_stereo.sh:[mipi_cam-2] camera_calibration_file_path: /opt/tros/humble/lib/mipi_cam/config/calib_params.yaml打印的标定文件,但

如图中config文件夹中根本没有/calib_params.yaml,所以不知道生成深度图时使用的什么标定文件。目前运行bash run_stereo.sh,是可以看到网页实时深度图

深度距离基本上也是对的,

sunrise@ubuntu:~$ bash run_stereo.sh
/opt/tros/humble
/opt/tros/humble
[INFO] [launch]: All log files can be found below /home/sunrise/.ros/log/2026-09-24-11-02-47-262895-ubuntu-5185
[INFO] [launch]: Default logging verbosity is set to INFO
config_file_path is /opt/tros/humble/lib/mipi_cam/config/
Hobot shm pkg enables zero-copy with fastrtps profiles file: /opt/tros/humble/lib/hobot_shm/config/shm_fastdds.xml
Hobot shm pkg sets RMW_FASTRTPS_USE_QOS_FROM_XML: 1
env of RMW_FASTRTPS_USE_QOS_FROM_XML is 1 , ignore env setting
launch webserver
pwd_path is /home/sunrise
webserver_path is /opt/tros/humble/lib/websocket/webservice
launch webserver cmd is ./sbin/nginx -p .
nginx: [alert] could not open error log file: open() “./logs/error.log” failed (13: Permission denied)
config_file_path is /opt/tros/humble/lib/mipi_cam/config/
env of RMW_FASTRTPS_USE_QOS_FROM_XML is 1 , ignore env setting
env of RMW_FASTRTPS_USE_QOS_FROM_XML is 1 , ignore env setting
webserver has launch
config_file_path is /opt/tros/humble/lib/mipi_cam/config/
env of RMW_FASTRTPS_USE_QOS_FROM_XML is 1 , ignore env setting
env of RMW_FASTRTPS_USE_QOS_FROM_XML is 1 , ignore env setting
env of RMW_FASTRTPS_USE_QOS_FROM_XML is 1 , ignore env setting
env of RMW_FASTRTPS_USE_QOS_FROM_XML is 1 , ignore env setting
webserver has launch
[INFO] [stereonet_model_node-1]: process started with pid [5213]
[INFO] [mipi_cam-2]: process started with pid [5215]
[INFO] [hobot_codec_republish-3]: process started with pid [5217]
[INFO] [websocket-4]: process started with pid [5219]
[hobot_codec_republish-3] [WARN] [1790218968.556772862] [hobot_codec_encoder]: Parameters:
[hobot_codec_republish-3] sub_topic: /StereoNetNode/stereonet_visual
[hobot_codec_republish-3] pub_topic: /image_jpeg
[hobot_codec_republish-3] channel: 1
[hobot_codec_republish-3] in_mode: ros
[hobot_codec_republish-3] out_mode: ros
[hobot_codec_republish-3] in_format: bgr8
[hobot_codec_republish-3] out_format: jpeg
[hobot_codec_republish-3] jpg_quality: 60.00
[hobot_codec_republish-3] input_framerate: 30
[hobot_codec_republish-3] output_framerate: -1
[hobot_codec_republish-3] dump_output: false
[hobot_codec_republish-3] dump_file_prefix: ./dump_codec_output
[hobot_codec_republish-3] dump_frame_count: -1 (unlimited)
[hobot_codec_republish-3] [WARN] [1790218968.563683539] [HobotCodecImpl]: platform x5
[mipi_cam-2] [WARN] [1790218969.320602515] [mipi_node]:
[mipi_cam-2] node params:
[mipi_cam-2] config_path: /opt/tros/humble/lib/mipi_cam/config/
[mipi_cam-2] video_device_name: default
[mipi_cam-2] channel: 2
[mipi_cam-2] channel2: 0
[mipi_cam-2] camera_info_url:
[mipi_cam-2] camera_calibration_file_path: /opt/tros/humble/lib/mipi_cam/config/calib_params.yaml
[mipi_cam-2] out_format_name: nv12
[mipi_cam-2] gdc_bin_file:
[mipi_cam-2] image_width: 640
[mipi_cam-2] image_height: 352
[mipi_cam-2] sub_image_width: 1920
[mipi_cam-2] sub_image_height: 1080
[mipi_cam-2] framerate: 30
[mipi_cam-2] rotation: 90.000000
[mipi_cam-2] device_mode: dual
[mipi_cam-2] dual_combine: 1
[mipi_cam-2] lpwm_enable: true
[mipi_cam-2] gdc_enable: true
[mipi_cam-2] frame_ts_type: realtime
[mipi_cam-2] frame_id: default_cam
[mipi_cam-2] link_type: 0
[mipi_cam-2] link_port: 0
[mipi_cam-2] io_method_name: ros
[mipi_cam-2] cal_alpha: 0.000
[websocket-4] [WARN] [1790218969.439652155] [websocket]:
[websocket-4] Parameter:
[websocket-4] image_topic: /image_jpeg
[websocket-4] image_type: mjpeg
[websocket-4] only_show_image: 1
[websocket-4] output_fps: 0
[stereonet_model_node-1] [WARN] [1790218969.974435649] [StereoNetNode]: => ===================== init StereoNetNode =====================
[stereonet_model_node-1]
[stereonet_model_node-1] [WARN] [1790218969.979998783] [StereoNetNode]:
[stereonet_model_node-1] stereonet_model_file_path: /opt/tros/humble/share/hobot_stereonet/config/DStereoV2.4_int16.bin
[stereonet_model_node-1] stereo_image_topic: /image_combine_raw
[stereonet_model_node-1] camera_info_topic: /image_combine_raw/right/camera_info
[stereonet_model_node-1] depth_image_topic: ~/stereonet_depth
[stereonet_model_node-1] depth_camera_info_topic: ~/stereonet_depth/camera_info
[stereonet_model_node-1] rectify_left_image_topic: ~/rectify_left_image
[stereonet_model_node-1] rectify_right_image_topic: ~/rectify_right_image
[stereonet_model_node-1] publish_rectify_bgr: 0
[stereonet_model_node-1] [origin_left_image_topic, origin_right_image_topic, publish_origin_enable]: [~/origin_left_image, ~/origin_right_image, 1]
[stereonet_model_node-1] [pointcloud2_topic, publish_pcd_enabled]: [~/stereonet_pointcloud2, 1]
[stereonet_model_node-1] [visual_image_topic, publish_visual_enabled]: [~/stereonet_visual, 1]
[stereonet_model_node-1] [stereonet_frame_id, stereonet_frame_id_right]: [camera_link, camera_link_right]
[stereonet_model_node-1] uncertainty_th: -0.1
[stereonet_model_node-1] [camera_fx, camera_fy, camera_cx, camera_cy, baseline, doffs]: [0, 0, 0, 0, 0(m)0]
[stereonet_model_node-1] [pointcloud_downsample_step, pointcloud_height_min, pointcloud_height_max, pointcloud_depth_max]: [2, -5(m), 5(m), 5(m)]
[stereonet_model_node-1] [use_local_image_flag, local_image_dir, image_sleep]: [0, ./offline, 0]
[stereonet_model_node-1] [save_result_flag, save_dir, save_freq, save_total, save_stereo_flag, save_origin_flag, save_disp_flag, save_uncert_flag, save_depth_flag, save_visual_flag, save_pcd_flag]: [0, ./result, 1, -1, 1, 0, 1, 0, 1, 1, 0]
[stereonet_model_node-1] [calib_method, stereo_calib_file_path]: [none, calib.yaml]
[stereonet_model_node-1] [speckle_filter_enable, max_speckle_size, max_disp_diff]: [0, 100, 1]
[stereonet_model_node-1] [pcl_filter_enable, grid_size, grid_min_point_count]: [0, 0.1, 5]
[stereonet_model_node-1] [render_type, render_perf, depth_decimal_num, render_max_disp, render_z_near, render_z_range]: [distance, 1, 2, 80, -1(m), 3(m)]
[stereonet_model_node-1] left_img_mask_enable: 0
[stereonet_model_node-1] [measure_mode, roi_size, gt_depth]: [0, 10, 0(mm)]
[stereonet_model_node-1] [epipolar_mode, feature_epipolar_mode, epipolar_img, chessboard_per_rows, chessboard_per_cols, chessboard_square_size]: [0, 0, rect, 20, 11, 0.06(m)]
[stereonet_model_node-1] feature_epipolar_mode: 0
[stereonet_model_node-1] post_version: auto
[stereonet_model_node-1] [infer_thread_num, save_thread_num, max_save_task]: [2, 4, 50]
[stereonet_model_node-1]
[stereonet_model_node-1] => ==================================================================
[stereonet_model_node-1]
[stereonet_model_node-1] [BPU_PLAT]BPU Platform Version(1.3.6)! soc info(x5)
[stereonet_model_node-1] [HBRT] set log level as 0. version = 3.15.55.0
[stereonet_model_node-1] [DNN] Runtime version = 1.24.5_(3.15.55 HBRT)
[mipi_cam-2] [WARN] [1790218970.212120261] [mipi_cap]: i2c bus: 4, EEPROM FLAG: SZYGSJKJ
[mipi_cam-2]
[mipi_cam-2] [WARN] [1790218970.251028747] [mipi_cap]: => ================== left awb otp data ==================
[mipi_cam-2] [WARN] [1790218970.251112624] [mipi_cap]: left_awb_otp_data_.awb_golden_data[0].rg_ratio: 1401
[mipi_cam-2] [WARN] [1790218970.251137292] [mipi_cap]: left_awb_otp_data_.awb_golden_data[0].bg_ratio: 340
[mipi_cam-2] [WARN] [1790218970.251158126] [mipi_cap]: left_awb_otp_data_.awb_golden_data[1].rg_ratio: 1094
[mipi_cam-2] [WARN] [1790218970.251177501] [mipi_cap]: left_awb_otp_data_.awb_golden_data[1].bg_ratio: 403
[mipi_cam-2] [WARN] [1790218970.251196960] [mipi_cap]: left_awb_otp_data_.awb_golden_data[2].rg_ratio: 880
[mipi_cam-2] [WARN] [1790218970.251215794] [mipi_cap]: left_awb_otp_data_.awb_golden_data[2].bg_ratio: 548
[mipi_cam-2] [WARN] [1790218970.251234461] [mipi_cap]: left_awb_otp_data_.awb_data[0].rg_ratio: 1420
[mipi_cam-2] [WARN] [1790218970.251253045] [mipi_cap]: left_awb_otp_data_.awb_data[0].bg_ratio: 342
[mipi_cam-2] [WARN] [1790218970.251272462] [mipi_cap]: left_awb_otp_data_.awb_data[1].rg_ratio: 1108
[mipi_cam-2] [WARN] [1790218970.251291088] [mipi_cap]: left_awb_otp_data_.awb_data[1].bg_ratio: 407
[mipi_cam-2] [WARN] [1790218970.251309589] [mipi_cap]: left_awb_otp_data_.awb_data[2].rg_ratio: 891
[mipi_cam-2] [WARN] [1790218970.251328756] [mipi_cap]: left_awb_otp_data_.awb_data[2].bg_ratio: 557
[mipi_cam-2] [WARN] [1790218970.251381341] [mipi_cap]: => ================== right awb otp data ==================
[mipi_cam-2] [WARN] [1790218970.251408050] [mipi_cap]: right_awb_otp_data_.awb_golden_data[0].rg_ratio: 1401
[mipi_cam-2] [WARN] [1790218970.251427842] [mipi_cap]: right_awb_otp_data_.awb_golden_data[0].bg_ratio: 340
[mipi_cam-2] [WARN] [1790218970.251447259] [mipi_cap]: right_awb_otp_data_.awb_golden_data[1].rg_ratio: 1094
[mipi_cam-2] [WARN] [1790218970.251465718] [mipi_cap]: right_awb_otp_data_.awb_golden_data[1].bg_ratio: 403
[mipi_cam-2] [WARN] [1790218970.251485052] [mipi_cap]: right_awb_otp_data_.awb_golden_data[2].rg_ratio: 880
[mipi_cam-2] [WARN] [1790218970.251503386] [mipi_cap]: right_awb_otp_data_.awb_golden_data[2].bg_ratio: 548
[mipi_cam-2] [WARN] [1790218970.251522053] [mipi_cap]: right_awb_otp_data_.awb_data[0].rg_ratio: 1333
[mipi_cam-2] [WARN] [1790218970.251540637] [mipi_cap]: right_awb_otp_data_.awb_data[0].bg_ratio: 342
[mipi_cam-2] [WARN] [1790218970.251559471] [mipi_cap]: right_awb_otp_data_.awb_data[1].rg_ratio: 1046
[mipi_cam-2] [WARN] [1790218970.251577888] [mipi_cap]: right_awb_otp_data_.awb_data[1].bg_ratio: 407
[mipi_cam-2] [WARN] [1790218970.251596555] [mipi_cap]: right_awb_otp_data_.awb_data[2].rg_ratio: 841
[mipi_cam-2] [WARN] [1790218970.251614931] [mipi_cap]: right_awb_otp_data_.awb_data[2].bg_ratio: 555
[mipi_cam-2] [WARN] [1790218970.251633890] [mipi_cap]: => ========================================================
[mipi_cam-2] [WARN] [1790218970.274340354] [mipi_cap]: Target FOV 0.00° out of valid range [103.13°, 133.01°]
[mipi_cam-2] [WARN] [1790218970.274449399] [mipi_cap]: Use default alpha=0.0 (target FOV invalid)
[stereonet_model_node-1] [A][DNN][packed_model.cpp:247]Model [HorizonRT] The model builder version = 1.24.3
[stereonet_model_node-1] [WARN] [1790218970.342301617] [StereoNetNode]: => ============ init model start ============
[stereonet_model_node-1] [WARN] [1790218970.342500706] [StereoNetNode]: => model name: DStereoV2.4.0
[stereonet_model_node-1] [WARN] [1790218970.342554583] [StereoNetNode]: => input_count: 2
[stereonet_model_node-1] [WARN] [1790218970.342597459] [StereoNetNode]: => output_count: 4
[stereonet_model_node-1] [WARN] [1790218970.342667295] [StereoNetNode]: => model_input_h: 352, model_input_w: 640
[stereonet_model_node-1] [WARN] [1790218970.342758631] [StereoNetNode]: => ----- prepare_input_tensor -----
[stereonet_model_node-1] [WARN] [1790218970.342821299] [StereoNetNode]: => input tensor type is HB_DNN_IMG_TYPE_NV12
[stereonet_model_node-1] [WARN] [1790218970.343112891] [StereoNetNode]: => input[0].memsize: 337920
[stereonet_model_node-1] [WARN] [1790218970.343835579] [StereoNetNode]: => ----- prepare_output_tensor -----
[stereonet_model_node-1] [WARN] [1790218970.343978042] [StereoNetNode]: => output tensor type is HB_DNN_TENSOR_TYPE_S32
[stereonet_model_node-1] [WARN] [1790218970.344086587] [StereoNetNode]: => output[0].memsize: 506880
[stereonet_model_node-1] [WARN] [1790218970.345501837] [StereoNetNode]: => ============ init model end ============
[mipi_cam-2] [WARN] [1790218970.520529813] [mipi_cap]: => ================== all awb otp data ==================
[mipi_cam-2] [WARN] [1790218970.520620233] [mipi_cap]: pdata.awb_golden_data[0].rg_ratio: 1401
[mipi_cam-2] [WARN] [1790218970.520648692] [mipi_cap]: pdata.awb_golden_data[0].bg_ratio: 340
[mipi_cam-2] [WARN] [1790218970.520671859] [mipi_cap]: pdata.awb_golden_data[1].rg_ratio: 1094
[mipi_cam-2] [WARN] [1790218970.520692485] [mipi_cap]: pdata.awb_golden_data[1].bg_ratio: 403
[mipi_cam-2] [WARN] [1790218970.520821655] [mipi_cap]: pdata.awb_golden_data[2].rg_ratio: 880
[mipi_cam-2] [WARN] [1790218970.520856448] [mipi_cap]: pdata.awb_golden_data[2].bg_ratio: 548
[mipi_cam-2] [WARN] [1790218970.520877740] [mipi_cap]: pdata.awb_data[0].rg_ratio: 1333
[mipi_cam-2] [WARN] [1790218970.520897991] [mipi_cap]: pdata.awb_data[0].bg_ratio: 342
[mipi_cam-2] [WARN] [1790218970.520917616] [mipi_cap]: pdata.awb_data[1].rg_ratio: 1046
[mipi_cam-2] [WARN] [1790218970.520937450] [mipi_cap]: pdata.awb_data[1].bg_ratio: 407
[mipi_cam-2] [WARN] [1790218970.520956451] [mipi_cap]: pdata.awb_data[2].rg_ratio: 841
[mipi_cam-2] [WARN] [1790218970.520976077] [mipi_cap]: pdata.awb_data[2].bg_ratio: 555
[mipi_cam-2] [WARN] [1790218970.520995410] [mipi_cap]: pdata.awb_data[0].r: 1
[mipi_cam-2] [WARN] [1790218970.521015078] [mipi_cap]: pdata.awb_data[0].gr: 1
[mipi_cam-2] [WARN] [1790218970.521034203] [mipi_cap]: pdata.awb_data[0].gb: 1
[mipi_cam-2] [WARN] [1790218970.521053204] [mipi_cap]: pdata.awb_data[0].b: 1
[mipi_cam-2] [WARN] [1790218970.521072538] [mipi_cap]: pdata.awb_data[0].r: 0
[mipi_cam-2] [WARN] [1790218970.521091538] [mipi_cap]: pdata.awb_data[0].gr: 0
[mipi_cam-2] [WARN] [1790218970.521110289] [mipi_cap]: pdata.awb_data[0].gb: 0
[mipi_cam-2] [WARN] [1790218970.521129539] [mipi_cap]: pdata.awb_data[0].b: 0
[mipi_cam-2] [WARN] [1790218970.521149165] [mipi_cap]: => ================== all awb otp data ==================
[mipi_cam-2] [WARN] [1790218970.550509451] [mipi_cap]: X5 start gdc rotation and cal.
[mipi_cam-2]
[mipi_cam-2] [WARN] [1790218971.409421828] [mipi_cap]: => ================== all awb otp data ==================
[mipi_cam-2] [WARN] [1790218971.409627667] [mipi_cap]: pdata.awb_golden_data[0].rg_ratio: 1401
[mipi_cam-2] [WARN] [1790218971.409730087] [mipi_cap]: pdata.awb_golden_data[0].bg_ratio: 340
[mipi_cam-2] [WARN] [1790218971.409822006] [mipi_cap]: pdata.awb_golden_data[1].rg_ratio: 1094
[mipi_cam-2] [WARN] [1790218971.409922592] [mipi_cap]: pdata.awb_golden_data[1].bg_ratio: 403
[mipi_cam-2] [WARN] [1790218971.410009636] [mipi_cap]: pdata.awb_golden_data[2].rg_ratio: 880
[mipi_cam-2] [WARN] [1790218971.410096097] [mipi_cap]: pdata.awb_golden_data[2].bg_ratio: 548
[mipi_cam-2] [WARN] [1790218971.410182016] [mipi_cap]: pdata.awb_data[0].rg_ratio: 1420
[mipi_cam-2] [WARN] [1790218971.410266227] [mipi_cap]: pdata.awb_data[0].bg_ratio: 342
[mipi_cam-2] [WARN] [1790218971.410427898] [mipi_cap]: pdata.awb_data[1].rg_ratio: 1108
[mipi_cam-2] [WARN] [1790218971.410486317] [mipi_cap]: pdata.awb_data[1].bg_ratio: 407
[mipi_cam-2] [WARN] [1790218971.410530776] [mipi_cap]: pdata.awb_data[2].rg_ratio: 891
[mipi_cam-2] [WARN] [1790218971.410574861] [mipi_cap]: pdata.awb_data[2].bg_ratio: 557
[mipi_cam-2] [WARN] [1790218971.410619196] [mipi_cap]: pdata.awb_data[0].r: 1
[mipi_cam-2] [WARN] [1790218971.410662863] [mipi_cap]: pdata.awb_data[0].gr: 1
[mipi_cam-2] [WARN] [1790218971.410706615] [mipi_cap]: pdata.awb_data[0].gb: 1
[mipi_cam-2] [WARN] [1790218971.410749199] [mipi_cap]: pdata.awb_data[0].b: 1
[mipi_cam-2] [WARN] [1790218971.410791409] [mipi_cap]: pdata.awb_data[0].r: 0
[mipi_cam-2] [WARN] [1790218971.410833577] [mipi_cap]: pdata.awb_data[0].gr: 0
[mipi_cam-2] [WARN] [1790218971.410875453] [mipi_cap]: pdata.awb_data[0].gb: 0
[mipi_cam-2] [WARN] [1790218971.410917204] [mipi_cap]: pdata.awb_data[0].b: 0
[mipi_cam-2] [WARN] [1790218971.410959455] [mipi_cap]: => ================== all awb otp data ==================
[mipi_cam-2] [WARN] [1790218971.424934772] [mipi_cap]: X5 start gdc rotation and cal.
[mipi_cam-2]
[mipi_cam-2] [WARN] [1790218972.112263861] [mipi_cam]: [init]->cap sc132gs-1280p init success.
[mipi_cam-2]
[stereonet_model_node-1] [WARN] [1790218972.216383508] [StereoNetNode]: => sub rectified [fx, fy, cx, cy, baseline(m), doffs] : [253.962128, 164.328436, 324.022324, 172.271168, 0.079944, 0.000000]
[stereonet_model_node-1] [WARN] [1790218972.219018333] [StereoNetNode]: => receive left camera info
[stereonet_model_node-1] [WARN] [1790218972.231408813] [StereoNetNode]: => receive stereo image, format: nv12, stamp: 1790218972.156598272, latency: 74.79 ms
[stereonet_model_node-1] [WARN] [1790218972.233019692] [StereoNetNode]: => HFOV: 103.127°, VFOV: 93.9284°

我需要官方提供详细的自行标定内参和外参的教程,请求尽快提供,可以自行标定后需要能够采矫正后的左图(一帧)和深度图(一帧)。请求尽快解决

针对特定的问题,请参考特定领域问题的发帖模板继续提供相关信息: forum.d-robotics.cc/t/topic/28613

标定文件那个疑惑先解开:calib_params.yaml 默认就不存在,它是给用户自备标定文件预留的占位路径。132GS 模组的出厂标定存在模组 EEPROM 里,mipi_cam 启动时自动读取——所以你现在没放任何标定文件,深度图用的就是 EEPROM 里的出厂标定,距离基本准确是正常的,这条警告可忽略(参考官方 FAQ Q8:8.6 TROS/ROS 开发 | RDK X3/X5 DOC )。

自行标定有官方工具和文档,不需要 RDK Studio:

流程简述:

  1. 用与 run_stereo.sh 完全一致的相机参数采集标定数据(同分辨率、同 dual_combine),录 /image_combine_raw 抽帧:
source /opt/tros/humble/setup.bash
ros2 run mipi_cam mipi_cam --ros-args -p device_mode:="dual" -p out_format:="nv12" -p dual_combine:=2 -p framerate:=10.0 --log-level warn
  1. 用 stereo_calib(或 AMR 指南里的 kalibr docker)对棋盘格数据标定,得到含左右内参 + 外参(R/T、基线)的 yaml。

  2. 让自己的标定生效:关闭 GDC,并把 camera_calibration_file_path 指向你的文件(改 run_stereo.sh 里 mipi_cam 的启动参数):

-p gdc_enable:=false -p camera_calibration_file_path:=/path/to/your_calib.yaml

两个注意点:

  • 加载后日志打印的内参/基线可能和你标定的原始值不一致,这是 stereoRectify 重算新虚拟相机参数的正常行为(可用 cal_alpha 调视野/黑边取舍),不代表标定没生效。
  • 验收别看日志数值,看极线误差(官方标准 <1 px)+ 已知距离物体实测深度。

GitHub - D-Robotics/stereo_calib · GitHub具体怎么使用呢,下载源码后在自己电脑操作吗?我是win10,ssh连接X5 132GS采图吗,能详细说清楚吗?

分工是这样的:采图在 X5 板端(SSH 进去操作),stereo_calib 的标定计算在 PC 上跑,源码不用放到板子上编译。具体路径:

1. 板端采图:准备一块棋盘格(方格边长 ≥10cm,行列数不同),SSH 到 X5 启动双目出流,参数要和你后面 run_stereo.sh 实际用的完全一致(同分辨率、同 dual_combine、同 GDC 开关状态):

source /opt/tros/humble/setup.bash
ros2 run mipi_cam mipi_cam --ros-args -p device_mode:="dual" \
  -p out_format:="nv12" -p dual_combine:=2 -p framerate:=10.0

然后录 bag(ros2 bag record /image_combine_raw)或按 stereo_calib 仓库 README 提供的采图方式存图,缓慢移动标定板覆盖各个距离和视野角落,别糊、别出画。

2. 传回 PC:Win10 用 WinSCP 或 scp 把图像/bag 拉到本地。

3. PC 上标定:stereo_calib 是离线标定工具,Win10 建议在 WSL2(Ubuntu)里跑,具体依赖和脚本入口以仓库 README 为准。标完看两个东西:输出的 rectify 矫正图左右同一物体是否行对齐(官方验收标准极线误差 <1px),以及生成的标定 yaml。

4. 回灌板端:yaml 拷回 X5,run_stereo.sh 里设 calib_method=custom、stereo_calib_file_path=你的yaml路径。

两个坑提前说:

  • 标定图和实际深度链路不是同一套参数(分辨率/GDC 状态不一致),板端会出现几十像素级的巨误差,这是社区实测踩过的;
  • yaml 里 T 的方向是左目→右目、单位米,如果标出负的 base_line 通常是左右顺序标反了。

参考:

好的,明白了一些。还想问下source /opt/tros/humble/setup.bash
ros2 run mipi_cam mipi_cam --ros-args -p device_mode:=“dual”
-p out_format:=“nv12” -p dual_combine:=2 -p framerate:=10.0这个是输出图像,那怎么控制保存自己想要的那一帧图像呢?(比如棋盘格放好了怎么操作保存这帧图片,保存好20张图片是在另一个ssh终端输入source /opt/tros/humble/setup.bash
ros2 bag record /image_combine_raw
m然后自动生成一个包含20张左右图的新文件夹吗?)。另外相机的原始分辨率是19201080吗?这个分辨率和config里SC132gs_dual_calibration.yaml里的12801088不一样,哪个是真实且可正常输出的分辨率呢?把采集图片的文件夹可以直接拖到本地电脑吗?(我之前试过是可以直接拖的)。我下载GitHub - D-Robotics/stereo_calib · GitHub源码可以直接在win10系统跑吗?还有“标定图和实际深度链路不是同一套参数(分辨率/GDC 状态不一致),板端会出现几十像素级的巨误差”是什么意思?

几个问题分开答:

1. 怎么"摆好棋盘格存一帧"

ros2 bag record 不是你想的那样:它是连续录制,把 10fps 的所有帧都写进一个 bag 文件夹(里面是 .db3 数据库文件,不是图片),不会自动帮你挑 20 张,事后还得写脚本抽帧,比较绕。

推荐直接在板端跑一个小脚本,订阅 /image_combine_raw,摆好板子按一次回车存一张:

# save_frame.py,板端运行: python3 save_frame.py
import threading, cv2, rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image
from cv_bridge import CvBridge

class Grabber(Node):
    def __init__(self):
        super().__init__('frame_grabber')
        self.frame = None
        self.count = 0
        self.create_subscription(Image, '/image_combine_raw',
            lambda m: setattr(self, 'frame', CvBridge().imgmsg_to_cv2(m, 'bgr8')), 10)
    def save(self):
        if self.frame is None:
            print('还没收到图像'); return
        self.count += 1
        cv2.imwrite(f'calib_{self.count:02d}.png', self.frame)
        print('已保存', self.count, '张')

rclpy.init()
node = Grabber()
threading.Thread(target=rclpy.spin, args=(node,), daemon=True).start()
while input('摆好棋盘格按回车存一张,q 退出: ').strip() != 'q':
    node.save()

另一个终端正常启动 mipi_cam 出流即可。存 20 张左右、覆盖不同距离和视野四角。

2. 分辨率到底是多少

SC132GS 单路 sensor 原生是 1280×1080(官方规格:GS130W 产品简介),config 里的 1280×1088 是高度对齐后的实际采集分辨率,这个是真实输出,以它为准。

你看到的 1920×1080 来自 run_stereo.sh 日志里的 sub_image_width/height——那是深度链路里图像经过 GDC 矫正 + 旋转 + 左右拼接之后的尺寸,不是 sensor 原始分辨率。

3. 文件夹拖到本地

可以。你之前拖成功说明 SFTP 通道(WinSCP/MobaXterm 之类)没问题,继续这么用即可。

4. stereo_calib 能不能直接在 Win10 跑

不保证。它是按 Linux 环境开发的,具体依赖以仓库 README 为准;Win10 上最稳的方式是装 WSL2(Ubuntu)在里面跑。原生 Windows 能不能直接跑通没人给你打包票,别在这上面耗时间。

5. "标定图和深度链路不是同一套参数"是什么意思

标定参数只对"采集时一模一样处理状态的图像"有效。你 run_stereo.sh 里 gdc_enable:=true,深度算法收到的图是被 GDC 硬件矫正/旋转过的,EEPROM 里的出厂标定对应的也是矫正后的图。如果你采标定图时关了 GDC(或分辨率、dual_combine 不一致),标出来的内参外参描述的是原始图,喂给处理矫正图的深度链路,图和参数对不上,视差整体错位,深度就会差出几十个像素。所以采图命令的每个参数(分辨率、dual_combine、gdc_enable、rotation)必须和你实际出深度用的 run_stereo.sh 完全一致;想用自己标的原始图参数,就要在深度链路里同步关掉 GDC 并用 custom 方式加载你的 yaml。

参考:双目 MIPI 图像采集

好的,1.这个脚本要怎么跑呢?要先保存到哪吗?而且脚本里的bgr8和出流的-p out_format:=
“nv12”不一致能行吗?。2..所以运行这个脚本采集的图片是两张12801088的图横拼在一起的大图是吗?我需要不拼在一起的左右图left_00*.bmp/jpg/png right_00*.bmp/jpg/png*(单独的左右图的分辨率是多少呢?*),要运行怎样的脚本呢?3..另外是否可以把采集的原始两张图用我自己写的标定代码计算出内外参?如果可以最终回灌板端的yaml文件的内容格式是怎样的呢?(我尝试输出符合能回灌的yaml文件)。5.我明白了,GDC硬件矫正/旋转是什么意思呢?如何确保标定采图时的每个参数(dual_combine、gdc_enable、rotation等等的参数是什么意思呢?)都和后续出图的参数一致呢?

  1. 脚本怎么跑:存板端家目录即可(nano ~/save_calib.py 粘贴,或 WinSCP 拖上去),SSH 里先启动出流节点,再开第二个终端跑脚本。bgr8 和 nv12 不冲突:话题里流的确实是 nv12,脚本里的 bgr8 是 cv_bridge 转换的目标格式(存盘用),转换在脚本内部完成。只要订阅后用 imgmsg_to_cv2(msg, 'bgr8') 转,就能正常出图。

  2. 拼接与左右分离:dual_combine:=1 时 /image_combine_raw 就是一张左右横拼大图,左半=左目、右半=右目,单目分辨率 = 拼图宽度÷2 × 拼图高度。实际数值别猜,出流后查一下:

ros2 topic echo /image_combine_raw --no-arr --once | grep -E "width|height"

(dual_combine:=0 时会分成 /image_raw 和 /image_right_raw 两个话题,但标定采图建议和深度链路一样用 combine 路径再切半,保证一致。)

存左右图的脚本(每 2 秒自动存一对 left_XXXX.png/right_XXXX.png,摆好棋盘格等它拍,Ctrl+C 结束):

#!/usr/bin/env python3
# ~/save_calib.py  用法: python3 save_calib.py [保存目录]
import os, sys, cv2, rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image
from cv_bridge import CvBridge

OUT = sys.argv[1] if len(sys.argv) > 1 else os.path.expanduser('~/calib_data')
os.makedirs(OUT + '/left', exist_ok=True)
os.makedirs(OUT + '/right', exist_ok=True)

class Saver(Node):
    def __init__(self):
        super().__init__('calib_saver')
        self.br = CvBridge()
        self.n = 0
        self.latest = None
        self.create_subscription(Image, '/image_combine_raw', self.cb, 10)
        self.create_timer(2.0, self.save)   # 每2秒存一对
    def cb(self, msg):
        self.latest = msg
    def save(self):
        if self.latest is None:
            self.get_logger().warn('还没收到图像,确认出流节点在跑')
            return
        img = self.br.imgmsg_to_cv2(self.latest, 'bgr8')
        h, w = img.shape[:2]
        cv2.imwrite(f'{OUT}/left/left_{self.n:04d}.png', img[:, :w//2])
        cv2.imwrite(f'{OUT}/right/right_{self.n:04d}.png', img[:, w//2:])
        self.get_logger().info(f'saved {self.n:04d}, {w}x{h} -> 单目 {w//2}x{h}')
        self.n += 1

rclpy.init()
node = Saver()
try:
    rclpy.spin(node)
except KeyboardInterrupt:
    pass
  1. 自己的标定代码 + 回灌 yaml:可以,采下来的原始图用 OpenCV stereoCalibrate 或 kalibr 都行。回灌格式不要自己发明——板端现成有模板:
cat /opt/tros/humble/lib/mipi_cam/config/SC132gs_dual_calibration.yaml

照抄这个文件的字段结构,只替换数值:左右内参 K(单位像素,对应你采图时的分辨率)、畸变系数、左右外参 R/T(T 单位米、方向左目→右目,标出负基线通常是左右顺序反了)。然后 run_stereo.sh 里设 calib_method=custom、stereo_calib_file_path=你的yaml。注意:你是在 GDC/旋转处理后的图上标的,回灌运行时 gdc_enable、rotation 必须保持和采图时一样,否则内参对不上。

  1. 参数含义与一致性:
  • gdc_enable:GDC 是 X5 的硬件几何畸变校正单元,开启后按标定数据(132GS 出厂标定在模组 EEPROM,启动自动加载)在 sensor 原始分辨率上直接把镜头畸变校掉,你拿到的图已经不是原始畸变图;
  • rotation:硬件旋转输出图(0/90/180/270),GS130WI 默认 90 是由 sensor 装配方向决定的;
  • dual_combine:0=左右分开两个话题,1=横拼成一张 /image_combine_raw 给 StereoNet 用。

保证一致的方法很简单:先跑一次 bash run_stereo.sh,把它打印的 [mipi_cam-2] node params 那段(out_format / image_width / image_height / rotation / dual_combine / gdc_enable)原样抄进你的采图命令,一个都别改。你现在这套是 rotation:90、dual_combine:1、gdc_enable:true、nv12,采图就用同样的值。

参考:GS130WI 快速开始 https://developer.d-robotics.cc/accessories_stereo_camera_doc/stereo_camera_gs130wi/quick_start 、AMR 开发指南 3.5 传感器标定 https://developer.d-robotics.cc/rdk_x_doc/Application_case/amr 、mipi_cam 节点参数仓库 https://github.com/D-Robotics/hobot_mipi_cam

好的,还有些问题请教一下

所以要自己标定就不能开gdc_enable对吧?我运行source /opt/tros/humble/setup.bash
ros2 run mipi_cam mipi_cam --ros-args -p device_mode:=“dual”
-p out_format:=“nv12” -p dual_combine:=0 -p framerate:=10.0 -p gdc_enable:=false“”“

输出的image_width: 1088******
image_height: 1280
sub_image_width: 960
****sub_image_height: 540是什么意思呢?图像宽高真实是什么呢?

sunrise@ubuntu:~$ ros2 topic echo /image_combine_raw --no-arr --once | grep -E “width|height”
-bash: ros2: 未找到命令
这是我在出流后输入你写的查找宽高的输出,没能找到,怎么解决呢?

下面是运行source /opt/tros/humble/setup.bash
ros2 run mipi_cam mipi_cam --ros-args -p device_mode:=“dual”
-p out_format:=“nv12” -p dual_combine:=0 -p framerate:=10.0 -p gdc_enable:=false“”“的输出,可以讲解一下其中重要的指令吗?

[WARN] [1790240609.257982057] [mipi_node]:
node params:
config_path: /opt/tros/humble/lib/mipi_cam/config/
video_device_name:
channel: 0
channel2: 2
camera_info_url:
camera_calibration_file_path:
out_format_name: nv12
gdc_bin_file:
image_width: 1088
image_height: 1280
sub_image_width: 960
sub_image_height: 540
framerate: 10
rotation: 0.000000
device_mode: dual
dual_combine: 0
lpwm_enable: false
gdc_enable: false
frame_ts_type: sensor
frame_id: default_cam
link_type: 0
link_port: 0
io_method_name: ros
cal_alpha: 0.000
[INFO] [1790240609.259739517] [mipi_cap]: this board support mipi:
[INFO] [1790240609.259810350] [mipi_cap]: host 0
[INFO] [1790240609.259843267] [mipi_cap]: host 2
[INFO] [1790240609.260162684] [mipi_cam]: cal_file:
index: 0 sensor_name: sc230ai-30fps config_file:linear_1920x1080_raw10_30fps_1lane.c
index: 1 sensor_name: sc132gs-1280p config_file:linear_1088x1280_raw10_30fps_1lane.c
index: 2 sensor_name: ov5647 config_file:linear_1920x1080_raw10_30fps_2lane.c
index: 3 sensor_name: imx219-30fps config_file:linear_1920x1080_raw10_30fps_2lane.c
index: 4 sensor_name: imx477-1920x1080-50fps config_file:linear_1920x1080_raw12_50fps_2lane.c
index: 5 sensor_name: imx415-20fps-2lane config_file:linear_3840x2160_raw10_30fps_2lane.c
Searching camera sensor on device: /proc/device-tree/soc/cam/vcon@0 i2c bus: 6 mipi rx phy: 0
WARN: Sensor Name: sc230ai-30fps, Expected Chip ID: 0xCB34, Actual Chip ID Read: 0x00
WARN: Sensor Name: sc230ai-30fps, Expected Chip ID: 0xCB34, Actual Chip ID Read: 0x132
WARN: Sensor Name: sc132gs-1280p, Expected Chip ID: 0x132, Actual Chip ID Read: 0x00
INFO: vp_sensor_detect_2 Support sensor index:0, sensor_name:sc132gs-1280p on mipi rx csi 0, i2c addr 0x32, config_file:linear_1088x1280_raw10_30fps_1lane.c
Searching camera sensor on device: /proc/device-tree/soc/cam/vcon@2 i2c bus: 4 mipi rx phy: 2
WARN: Sensor Name: sc230ai-30fps, Expected Chip ID: 0xCB34, Actual Chip ID Read: 0x00
WARN: Sensor Name: sc230ai-30fps, Expected Chip ID: 0xCB34, Actual Chip ID Read: 0x00
WARN: Sensor Name: sc132gs-1280p, Expected Chip ID: 0x132, Actual Chip ID Read: 0x00
WARN: Sensor Name: sc132gs-1280p, Expected Chip ID: 0x132, Actual Chip ID Read: 0x00
INFO: vp_sensor_detect_2 Support sensor index:2, sensor_name:sc132gs-1280p on mipi rx csi 2, i2c addr 0x33, config_file:linear_1088x1280_raw10_30fps_1lane.c
Searching camera sensor on device: /proc/device-tree/soc/cam/vcon@2 i2c bus: 4 mipi rx phy: 2
WARN: Sensor Name: sc132gs-1280p, Expected Chip ID: 0x132, Actual Chip ID Read: 0x00
WARN: Sensor Name: sc132gs-1280p, Expected Chip ID: 0x132, Actual Chip ID Read: 0x00
INFO: vp_sensor_fixed_mipi_host_1 Found sensor_name:sc132gs-1280p on mipi rx csi 2, i2c addr 0x33, config_file:linear_1088x1280_raw10_30fps_1lane.c
[WARN] [1790240610.149475624] [mipi_cap]: i2c bus: 4, EEPROM FLAG: SZYGSJKJ

[INFO] [1790240610.151712210] [mipi_cap]: ====EepromDrobotHead======

bus: 4
flag: SZYGSJKJ
camType: 1
cal_tpye: 0
ver_main: 1
ver_min: 1
angle: 1
d_num: 8

[INFO] [1790240610.180630157] [mipi_cap]: ====m_d_info_l======

width: 1280
height: 1088
fx: 666.418884
fx: 666.418884
fy: 666.292725
cy: 540.605835

[INFO] [1790240610.181011449] [mipi_cap]: ====m_d_info_r======

width: 1280
height: 1088
fx: 666.285339
fx: 666.285339
fy: 666.128235
cy: 541.465759

[INFO] [1790240610.181342657] [mipi_cap]: ====r_t_info======

r11: 0.999994
r12: 0.003309
r13: -0.000336
r21: -0.003309
r22: 0.999993
r23: -0.001552
r31: 0.000330
r32: 0.001553
r33: 0.999999
tx: -79.943871
ty: 0.258484
tz: 0.054341

[INFO] [1790240610.201174428] [mipi_cap]: csi2 ignore mclk ex attr, because not config mclk.

[INFO] [1790240610.201866679] [mipi_cap]: lpwm_enable: 0

[INFO] [1790240610.201918096] [mipi_cap]: lpwm_index: 0, trigger_source: 0, trigger_mode: 0, period: 33333, offset: 10, duty_time: 100, threshold: 0, adjust_step: 0

[INFO] [1790240610.201961512] [mipi_cap]: lpwm_index: 1, trigger_source: 0, trigger_mode: 0, period: 33333, offset: 10, duty_time: 100, threshold: 0, adjust_step: 0

[INFO] [1790240610.202003762] [mipi_cap]: lpwm_index: 2, trigger_source: 0, trigger_mode: 0, period: 33333, offset: 10, duty_time: 100, threshold: 0, adjust_step: 0

[INFO] [1790240610.202043679] [mipi_cap]: lpwm_index: 3, trigger_source: 0, trigger_mode: 0, period: 33333, offset: 10, duty_time: 100, threshold: 0, adjust_step: 0

Searching camera sensor on device: /proc/device-tree/soc/cam/vcon@0 i2c bus: 6 mipi rx phy: 0
WARN: Sensor Name: sc132gs-1280p, Expected Chip ID: 0x132, Actual Chip ID Read: 0x00
INFO: vp_sensor_fixed_mipi_host_1 Found sensor_name:sc132gs-1280p on mipi rx csi 0, i2c addr 0x32, config_file:linear_1088x1280_raw10_30fps_1lane.c

[INFO] [1790240611.048537403] [mipi_cap]: csi0 ignore mclk ex attr, because not config mclk.

[INFO] [1790240611.050976739] [mipi_cap]: lpwm_enable: 0

[INFO] [1790240611.051142489] [mipi_cap]: lpwm_index: 0, trigger_source: 0, trigger_mode: 0, period: 33333, offset: 10, duty_time: 100, threshold: 0, adjust_step: 0

[INFO] [1790240611.051304864] [mipi_cap]: lpwm_index: 1, trigger_source: 0, trigger_mode: 0, period: 33333, offset: 10, duty_time: 100, threshold: 0, adjust_step: 0

[INFO] [1790240611.051459073] [mipi_cap]: lpwm_index: 2, trigger_source: 0, trigger_mode: 0, period: 33333, offset: 10, duty_time: 100, threshold: 0, adjust_step: 0

[INFO] [1790240611.051819156] [mipi_cap]: lpwm_index: 3, trigger_source: 0, trigger_mode: 0, period: 33333, offset: 10, duty_time: 100, threshold: 0, adjust_step: 0

[WARN] [1790240611.744712799] [mipi_cam]: [init]->cap sc132gs-1280p init success.

[INFO] [1790240611.744892132] [mipi_node]: [MipiCamNode::init]->Initing ‘/opt/tros/humble/lib/mipi_cam/config/’ at 1088x1280 via ros at 10 FPS
[INFO] [1790240611.744991007] [mipi_cap]: get calibration camera info
[INFO] [1790240611.760022481] [mipi_cam]: [start]->w:h=1088:1280.

[INFO] [1790240611.760445732] [mipi_node]: starting timer 100

=== Detected IIO Devices ===
Device: iio:device0 | Name: 34190000.adc

这是我写入save_frame.py后运行他的输出:感觉会缺很多依赖,要怎么安装这么多依赖呢?
sunrise@ubuntu:~$ ls ~/save_frame.py
/home/sunrise/save_frame.py
sunrise@ubuntu:~$ python3 save_frame.py
Traceback (most recent call last):
File “/home/sunrise/save_frame.py”, line 3, in
import threading, cv2, rclpy
ModuleNotFoundError: No module named ‘rclpy’,

5.下面是我修改的save_frame.py,想保存左右两图且回车才保存,能帮我看下修改的有问题吗?
#!/usr/bin/env python3 import threading, cv2, rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image
from cv_bridge import CvBridge

OUT = sys.argv[1] if len(sys.argv) > 1 else os.path.expanduser(‘~/calib_data’)

os.makedirs(OUT, exist_ok=True)

class Grabber(Node):
def init(self):
super().init(‘frame_grabber’)
self.frame = None
self.count = 0
self.create_subscription(Image, ‘/image_combine_raw’,
lambda m: setattr(self, ‘frame’, CvBridge().imgmsg_to_cv2(m, ‘bgr8’)), 10)
def save(self):
if self.frame is None:
print(‘还没收到图像’); return
self.count += 1

=== 从Saver脚本提取的切分保存逻辑,仅这一块替换 ===

img = self.frame
h, w = img.shape[:2]
cv2.imwrite(f’{OUT}/left_{self.count:04d}.png’, img[:, :w//2])
cv2.imwrite(f’{OUT}/right_{self.count:04d}.png’, img[:, w//2:])
print(f’saved {self.count:04d}, {w}x{h} → 单目 {w//2}x{h}')

rclpy.init()
node = Grabber()
threading.Thread(target=rclpy.spin, args=(node,), daemon=True).start()
while input('摆好棋盘格按回车存一张,q 退出: ').strip() != ‘q’:
node.save()

最后提醒下我是X5 132GS哈

1. GDC 与真实宽高

不是"自标定就必须关",原则是标定图状态 = 以后用图状态。你要拿原始畸变图自己标(推荐路线),现在 gdc_enable:=false 是对的;回灌时也要同样关 GDC、同 rotation。若想沿用 run_stereo.sh 默认链路(GDC 开),那采图也得开着 GDC 标,两边别混。

参数含义:

  • image_width: 1088 / image_height: 1280:sensor 实际输出帧尺寸(宽1088×高1280,竖幅;sc132gs 原生 1280×1080,1088 是对齐后的行宽)。关 GDC 后单目真实图像就是它,dual_combine:=0 时分 /image_raw、/image_right_raw 两个话题发布。你这套(GDC 关 + combine 0)直接得到左右两张原始图,正好采标定图用。
  • sub_image_width: 960 / sub_image_height: 540:矫正链路里的子图尺寸参数,gdc_enable:=false 时不参与输出,是默认值,忽略即可。真实宽高别猜,出流后实测:
source /opt/tros/humble/setup.bash
ros2 topic echo /image_raw --no-arr --once | grep -E "width|height"

2. ros2: 未找到命令

source 只对当前终端窗口生效,新开 SSH 窗口就没了。在执行 ros2 命令的那个终端里先跑一遍 source /opt/tros/humble/setup.bash 再 echo。另外注意:dual_combine:=0 时没有 /image_combine_raw,要 echo /image_raw(左)或 /image_right_raw(右)。

3. 日志重点

  • 一串 WARN: Expected Chip ID...:驱动在逐个探测它支持的 sensor,不匹配的报警是正常噪音,不用管。关键看两行 Found sensor sc132gs-1280p ... csi 0 和 ... csi 2——左右目都被识别到了。
  • EEPROM FLAG: SZYGSJKJ 之后的段落:从模组 EEPROM 读出的出厂标定。m_d_info_l/r 是左右目内参(对应 1280×1088 的 fx/fy/cx/cy),r_t_info 是外参:R 接近单位阵(两目基本平行),tx: -79.94 就是基线 ≈80mm,和 GS130W 官方标称一致(产品简介)。开 GDC 时硬件矫正用的就是这份数据;你关了 GDC,它只是打印出来不生效。
  • lpwm_*、mclk、csi2 ignore...:外部触发同步/时钟配置,lpwm_enable: false 时没用到,忽略。

最后一个细节:你贴的命令里有中文全角引号(“dual”、结尾的 “””),从论坛复制常带出来,实际敲命令一律用英文 "。

参考:双目 MIPI 图像采集 、hobot_mipi_cam 参数仓库

GitHub - D-Robotics/gs130_camera_sdk: RDK Stereo Camera SDK for GS130W/GS130WI series

可以试用下双目的SDK

好的,上面有两个问题没有回复哈,然后还有一个新问题

4.这是我写入save_frame.py后运行他的输出:感觉会缺很多依赖,要怎么安装这么多依赖呢?
sunrise@ubuntu:~$ ls ~/save_frame.py
/home/sunrise/save_frame.py
sunrise@ubuntu:~$ python3 save_frame.py
Traceback (most recent call last):
File “/home/sunrise/save_frame.py”, line 3, in
import threading, cv2, rclpy
ModuleNotFoundError: No module named ‘rclpy’,

5.下面是我修改的save_frame.py,想保存左右两图且回车才保存,能帮我看下修改的有问题吗?
import threading, cv2, rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
OUT = sys.argv[1] if len(sys.argv) > 1 else os.path.expanduser(‘~/calib_data’)
os.makedirs(OUT, exist_ok=True)
class Grabber(Node):
def init(self):
super().init(‘frame_grabber’)
self.frame = None
self.count = 0
self.create_subscription(Image, ‘/image_combine_raw’,
lambda m: setattr(self, ‘frame’, CvBridge().imgmsg_to_cv2(m, ‘bgr8’)), 10)
def save(self):
if self.frame is None:
print(‘还没收到图像’); return
self.count += 1
# === 从Saver脚本提取的切分保存逻辑,仅这一块替换 ===
img = self.frame
h, w = img.shape[:2]
cv2.imwrite(f’{OUT}/left_{self.count:04d}.png’, img[:, :w//2])
cv2.imwrite(f’{OUT}/right_{self.count:04d}.png’, img[:, w//2:])
print(f’saved {self.count:04d}, {w}x{h} → 单目 {w//2}x{h}')

rclpy.init()
node = Grabber()
threading.Thread(target=rclpy.spin, args=(node,), daemon=True).start()
while input('摆好棋盘格按回车存一张,q 退出: ').strip() != ‘q’:
node.save()

1.这是我source了:sunrise@ubuntu:~$ source /opt/tros/humble/setup.bash
ros2 topic echo /image_raw --no-arr --once | grep -E “width|height”
Could not determine the type for the passed topic
但报错

我是132GS,而且这个跟我的需求好像不一样吧

三个问题逐个说:

4. No module named 'rclpy'——不用 pip 装一堆依赖

rclpy、cv_bridge 都是 ROS 环境自带的,报错只是因为运行 python3 的那个终端没有 source 环境。同一个终端里先 source 再跑:

source /opt/tros/humble/setup.bash
python3 save_frame.py

如果 source 后仍报 rclpy 找不到,先确认 ls /opt/tros/ 下有没有 humble 目录(没有说明板子还没装 ToF/ROS 环境,需要先装)。cv2 是系统自带的 OpenCV,一般也随环境带出来;真缺再 pip3 install opencv-python。

5. 你改的脚本有一个硬伤:用了 sys 和 os 但没 import

第 4 行 OUT = sys.argv[1] ... 和 os.makedirs 会直接 NameError。把首行改成:

import sys, os, threading, cv2, rclpy

其他小建议:

  • CvBridge() 每帧回调都新建一次,放到 __init__ 里复用更好:self.bridge = CvBridge(),回调里用 self.bridge.imgmsg_to_cv2(...)。
  • 左右对半切的逻辑本身没问题;只要确认 132GS 的 combine 图是左右并排、且宽度为偶数即可(保存后打开一张肉眼核对下左右顺序)。

1. Could not determine the type for the passed topic

这个报错基本就是当前环境里不存在 /image_raw 这个 topic——要么相机节点没在跑,要么 topic 名不对。注意你脚本里订阅的是 /image_combine_raw,echo 却用的 /image_raw,两边名字不一致。先看实际有哪些:

source /opt/tros/humble/setup.bash
ros2 topic list

确认相机 launch 已启动、列表里有目标 topic 后,再对列表里的实际名字 echo:

ros2 topic echo /image_combine_raw --no-arr --once | grep -E "width|height"

如果 ros2 topic list 是空的,就是相机节点没起来,先把取流 launch 跑起来再试。

先把型号疑惑解开:132GS 和 SC132GS、GS130W/GS130WI 说的就是同一套东西,不存在文档给错型号。你自己贴的日志可以证明:

[init]->cap sc132gs-1280p init success
config_file: linear_1088x1280_raw10_30fps_1lane.c
  • SC132GS 是 sensor 型号(思特威),GS130W/GS130WI 是官方搭载这颗 sensor 的双目模组产品名,板端标定模板也叫 SC132gs_dual_calibration.yaml;
  • 所以 GS130W/GS130WI 手册里的标定流程对你的模组直接适用。

对齐你的需求(自行标定内外参 → 用自己的代码算 → 回灌 yaml → 采矫正左图和深度图各一帧),路径没变,按 132GS 再收敛一遍:

  1. 采图:用和 run_stereo.sh 完全一致的参数出流,订阅 /image_combine_raw 切半存 left/right。你运行脚本报 No module named 'rclpy',是因为那个终端没 source 环境,先执行再跑脚本:
source /opt/tros/humble/setup.bash
python3 ~/save_calib.py

你改的脚本还有两处硬伤:开头缺 import os, sys;def init / super().init 是 __init__ 粘贴时丢了两侧双下划线,会直接报错。

  1. 分辨率别搞混:gdc_enable:=false、rotation:=0 时原始输出是 1088(宽)×1280(高) 的竖图;rotation:=90 后变 1280×1088 横图,正好对应 EEPROM 出厂标定打印的 width:1280 height:1088。你采图走哪条链路,标定和回灌就必须配套哪条(内参 K 按实际存图分辨率填)。

  2. 回灌:照抄模板结构,只换数值(T 单位米、方向左目→右目):

cat /opt/tros/humble/lib/mipi_cam/config/SC132gs_dual_calibration.yaml

然后 run_stereo.sh 里设 calib_method=custom、stereo_calib_file_path=你的yaml,gdc_enable/rotation 保持和采图时一致。

  1. 取结果帧:跑起 run_stereo.sh 后,矫正左图在 /StereoNetNode/rectify_left_image,深度图在 /StereoNetNode/stereonet_depth(就是你日志里打印的这两个 topic),各订一帧存盘即可。

如果「跟我的需求不一样」指的不是型号问题,而是某份文档或某一步教程,麻烦指出具体是哪个链接、你期望的输出是什么,我按那个对齐。

参考:GS130W 产品简介 产品简介 | RDK Accessories DOC 、AMR 开发指南 3.5 传感器标定 6.1 AMR 开发指南 | RDK X3/X5 DOC

  1. 可用SDK 直接取图
  2. 132GS 就是 130WI/130W 的传感器
  3. 可以用下RDK Studio 直接帮助开发 RDK Studio
  4. 也可以用自己的Claude Code Codex 等Agent 工具接入MCP https://cdn.jsdelivr.net/npm/rdk-docs-mcp@latest/install.md