diff --git a/.github/workflows/happypose_ros_build_and_test.yaml b/.github/workflows/happypose_ros_build_and_test.yaml index 12e8687..686c239 100644 --- a/.github/workflows/happypose_ros_build_and_test.yaml +++ b/.github/workflows/happypose_ros_build_and_test.yaml @@ -1,12 +1,13 @@ -name: "Humble: Build and Test" +--- +name: 'Humble: Build and Test' -on: +on : push: branches: - - "main" + - main pull_request: branches: - - "*" + - '*' jobs: test_happypose_ros: @@ -16,61 +17,61 @@ jobs: HAPPYPOSE_DATA_DIR: /tmp/local_data steps: - - name: Install EGL mesa - required for Panda3D renderer - run: | - sudo apt-get update - sudo apt-get install -qqy libegl1-mesa libegl1-mesa-dev + - name: Install EGL mesa - required for Panda3D renderer + run: | + sudo apt-get update + sudo apt-get install -qqy libegl1-mesa libegl1-mesa-dev - - name: Install Python C headers and remove Blinker version conflicting with HappyPose - run: | - sudo apt-get update - sudo apt-get install -qqy python3-dev - sudo apt purge -qqy python3-blinker + - name: Install Python C headers and remove Blinker version conflicting with HappyPose + run: | + sudo apt-get update + sudo apt-get install -qqy python3-dev + sudo apt purge -qqy python3-blinker - - name: Caching of the HappyPose installation and data - uses: actions/cache@v4 - with: - path: /tmp/local_data - key: data + - name: Caching of the HappyPose installation and data + uses: actions/cache@v4 + with: + path: /tmp/local_data + key: data - - name: Update pip - run: pip install -U pip + - name: Update pip + run: pip install -U pip - - name: Download HappyPose source - working-directory: /tmp - run: | - git clone --branch dev --recurse-submodules https://github.com/agimus-project/happypose.git + - name: Download HappyPose source + working-directory: /tmp + run: | + git clone --branch dev --recurse-submodules https://github.com/agimus-project/happypose.git - - name: Build and install HappyPose - working-directory: /tmp/happypose - run: pip install ".[cpu,pypi,evaluation,multiview]" --extra-index-url https://download.pytorch.org/whl/cpu + - name: Build and install HappyPose + working-directory: /tmp/happypose + run: pip install ".[cpu,pypi,evaluation,multiview]" --extra-index-url https://download.pytorch.org/whl/cpu - - name: Download pre-trained models required for tests - run: | - mkdir -p /tmp/local_data - python -m happypose.toolbox.utils.download \ - --bop_dataset ycbv \ - --cosypose_models \ - detector-bop-ycbv-pbr--970850 \ - coarse-bop-ycbv-pbr--724183 \ - refiner-bop-ycbv-pbr--604090 + - name: Download pre-trained models required for tests + run: | + mkdir -p /tmp/local_data + python -m happypose.toolbox.utils.download \ + --bop_dataset ycbv \ + --cosypose_models \ + detector-bop-ycbv-pbr--970850 \ + coarse-bop-ycbv-pbr--724183 \ + refiner-bop-ycbv-pbr--604090 - - name: Unzip HappyPose YCBV models - working-directory: /tmp/local_data/bop_datasets/ycbv - run: | - unzip -n -qq ycbv_base.zip - unzip -n -qq ycbv_models.zip + - name: Unzip HappyPose YCBV models + working-directory: /tmp/local_data/bop_datasets/ycbv + run: | + unzip -n -qq ycbv_base.zip + unzip -n -qq ycbv_models.zip - - name: Remove incompatible PyTest version - run: pip uninstall -y pytest + - name: Remove incompatible PyTest version + run: pip uninstall -y pytest - - name: Install ROS 2 Humble - uses: ros-tooling/setup-ros@v0.7 - with: - required-ros-distributions: humble + - name: Install ROS 2 Humble + uses: ros-tooling/setup-ros@v0.7 + with: + required-ros-distributions: humble - - name: Build and test happypose_ros - uses: ros-tooling/action-ros-ci@v0.3 - with: - package-name: happypose_ros - target-ros2-distro: humble + - name: Build and test happypose_ros + uses: ros-tooling/action-ros-ci@v0.3 + with: + package-name: happypose_ros + target-ros2-distro: humble diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9c4cbe5..0db0d39 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,4 @@ +--- repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.6.5 @@ -29,5 +30,10 @@ repos: rev: v2.3.0 hooks: - id: codespell - args: ['--write-changes'] + args: [--write-changes] exclude: resources/happypose_ros_diagram.drawio.svg +- repo: https://github.com/jumanjihouse/pre-commit-hook-yamlfmt + rev: 0.2.3 + hooks: + - id: yamlfmt + args: [--mapping, '2', --sequence, '2', --offset, '0', --colons, --width, '86'] diff --git a/happypose_examples/config/camera_info.yaml b/happypose_examples/config/camera_info.yaml index 7358a70..1dc5ca0 100644 --- a/happypose_examples/config/camera_info.yaml +++ b/happypose_examples/config/camera_info.yaml @@ -1,20 +1,21 @@ -image_width: 640 -image_height: 480 -camera_name: webcam -camera_matrix: +--- +image_width : 640 +image_height : 480 +camera_name : webcam +camera_matrix : rows: 3 cols: 3 data: [1066.778, 0.0, 312.9869, 0.0, 1067.487, 241.3109, 0.0, 0.0, 1.0] -distortion_model: plumb_bob +distortion_model : plumb_bob distortion_coefficients: rows: 1 cols: 5 data: [0.0, 0.0, 0.0, 0.0, 0.0] -rectification_matrix: +rectification_matrix : rows: 3 cols: 3 data: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0] -projection_matrix: +projection_matrix : rows: 3 cols: 4 data: [1.0, 0.0, 320.0, 0.0, 0.0, 1.0, 240.0, 0.0, 0.0, 0.0, 1.0, 0.0] diff --git a/happypose_examples/config/cosypose_params.yaml b/happypose_examples/config/cosypose_params.yaml index 8be0727..95bb8f2 100644 --- a/happypose_examples/config/cosypose_params.yaml +++ b/happypose_examples/config/cosypose_params.yaml @@ -1,3 +1,4 @@ +--- /**: ros__parameters: # Device to which the models will be loaded. Supported options are 'cpu' and 'cuda:x' where 'x' is the GPU number. @@ -38,7 +39,7 @@ # List of camera names to subscribe to. Those names are internal to happypose_ros node # and can be chosen arbitrarily as long as they are valid YAML keys to be used later - camera_names: ["cam_1"] + camera_names: [cam_1] cameras: # Timeout, after which a frame from a camera is considered too old. Value '0.0' disables timeout timeout: 0.0 diff --git a/happypose_examples/config/cosypose_params_multiview.yaml b/happypose_examples/config/cosypose_params_multiview.yaml index ee2498c..ed0c2de 100644 --- a/happypose_examples/config/cosypose_params_multiview.yaml +++ b/happypose_examples/config/cosypose_params_multiview.yaml @@ -1,3 +1,4 @@ +--- /**: ros__parameters: # Device to which the models will be loaded. Supported options are 'cpu' and 'cuda:x' where 'x' is the GPU number. @@ -52,7 +53,7 @@ # List of camera names to subscribe to. Those names are internal to happypose_ros node # and can be chosen arbitrarily as long as they are valid YAML keys to be used later - camera_names: ["cam_1", "cam_2", "cam_3"] + camera_names: [cam_1, cam_2, cam_3] cameras: # Timeout, after which a frame from a camera is considered too old. Value '0.0' disables timeout timeout: 0.0 diff --git a/happypose_msgs/examples/meshcat_viz.ipynb b/happypose_msgs/examples/meshcat_viz.ipynb index fc09f59..57bff9a 100644 --- a/happypose_msgs/examples/meshcat_viz.ipynb +++ b/happypose_msgs/examples/meshcat_viz.ipynb @@ -28,7 +28,7 @@ "\n", "python_version = f\"python{sys.version_info.major}.{sys.version_info.minor}\"\n", "dist_package_path = Path(\"local\") / \"lib\" / python_version / \"dist-packages\"\n", - "ros_path = Path(\"/opt\") / \"ros\" / os.environ['ROS_DISTRO'] / dist_package_path\n", + "ros_path = Path(\"/opt\") / \"ros\" / os.environ[\"ROS_DISTRO\"] / dist_package_path\n", "colson_ws_path = my_colcon_ws_path / \"install\" / \"happypose_msgs\" / dist_package_path\n", "sys.path.append(ros_path.as_posix())\n", "sys.path.append(colson_ws_path.as_posix())" diff --git a/happypose_ros/happypose_ros/happypose_ros_parameters.yaml b/happypose_ros/happypose_ros/happypose_ros_parameters.yaml index 7d3f488..9d1bb66 100644 --- a/happypose_ros/happypose_ros/happypose_ros_parameters.yaml +++ b/happypose_ros/happypose_ros/happypose_ros_parameters.yaml @@ -1,200 +1,195 @@ +--- happypose_ros: - device: { - type: string, - default_value: "cpu", - description: "Device to which the models will be loaded. Supported options are 'cpu' and 'cuda:x' where 'x' is the GPU number.", - validation: { - "custom_validators::torch_check_device": null - }, - read_only: true, - } - - verbose_info_logs: { - type: bool, - default_value: false, - description: "Extended verbosity on info logs. Show logs with number of detections and more.", - } - - time_stamp_strategy: { - type: string, - default_value: "oldest", - description: "Which image time stamp to use in final detection message.", - validation: { - one_of<>: [[ "average", "newest", "oldest" ]], - } - } + device: + type: string + default_value: cpu + description: Device to which the models will be loaded. Supported options are 'cpu' + and 'cuda:x' where 'x' is the GPU number. + validation: + custom_validators::torch_check_device: + + read_only: true + + verbose_info_logs: + type: bool + default_value: false + description: Extended verbosity on info logs. Show logs with number of detections and + more. + + time_stamp_strategy: + type: string + default_value: oldest + description: Which image time stamp to use in final detection message. + validation: + one_of<>: [[average, newest, oldest]] visualization: - publish_markers: { - type: bool, - default_value: false, - description: "Publish detected objects as markers to visualize detections in RViz.", - read_only: true, - } + publish_markers: + type: bool + default_value: false + description: Publish detected objects as markers to visualize detections in RViz. + read_only: true + markers: - dynamic_opacity: { - type: bool, - default_value: false, - description: "Change opacity of published markers based on their prediction score.", - } - lifetime: { - type: double, - default_value: 10.0, - description: "Lifetime of a published marker.", - validation: { - gt<>: [ 0.0 ], - } - } - - pose_estimator_type: { - type: string, - default_value: "cosypose", - description: "Specifies which pose estimator to use in the pipeline.", - validation: { - one_of<>: [[ "cosypose" ]], - }, - read_only: true, - } + dynamic_opacity: + type: bool + default_value: false + description: Change opacity of published markers based on their prediction score. + + lifetime: + type: double + default_value: 10.0 + description: Lifetime of a published marker. + validation: + gt<>: [0.0] + + pose_estimator_type: + type: string + default_value: cosypose + description: Specifies which pose estimator to use in the pipeline. + validation: + one_of<>: [[cosypose]] + + read_only: true + cosypose: - dataset_name: { - type: string, - default_value: "", - description: "Name of a dataset used during training.", - validation: { - one_of<>: [[ "hope", "tless", "ycbv" ]], - }, - read_only: true, - } + dataset_name: + type: string + default_value: '' + description: Name of a dataset used during training. + validation: + one_of<>: [[hope, tless, ycbv]] + + read_only: true + renderer: - renderer_type: { - type: string, - default_value: "panda3d", - description: "Specifies which renderer to use in the pipeline.", - validation: { - one_of<>: [[ "panda3d", "bullet" ]], - }, - read_only: true, - } - n_workers: { - type: int, - default_value: 8, - description: "Number of CPU cores to use during rendering.", - validation: { - gt_eq<>: 1, - }, - read_only: true, - } - gpu_renderer: { - type: bool, - default_value: true, - description: "Render objects on a GPU.", - read_only: true, - } + renderer_type: + type: string + default_value: panda3d + description: Specifies which renderer to use in the pipeline. + validation: + one_of<>: [[panda3d, bullet]] + + read_only: true + + n_workers: + type: int + default_value: 8 + description: Number of CPU cores to use during rendering. + validation: + gt_eq<>: 1 + + read_only: true + + gpu_renderer: + type: bool + default_value: true + description: Render objects on a GPU. + read_only: true inference: detector: - detection_th: { - type: double, - default_value: 0.7, - description: "Detection threshold of an object used by detector.", - validation: { - bounds<>: [0.0, 1.0], - } - } + detection_th: + type: double + default_value: 0.7 + description: Detection threshold of an object used by detector. + validation: + bounds<>: [0.0, 1.0] + pose_estimator: - n_refiner_iterations: { - type: int, - default_value: 3, - description: "Number of iterations for the refiner.", - validation: { - gt_eq<>: [ 1 ], - } - } - n_coarse_iterations: { - type: int, - default_value: 1, - description: "Number of iterations for the coarse estimate.", - validation: { - gt_eq<>: [ 1 ] , - } - } + n_refiner_iterations: + type: int + default_value: 3 + description: Number of iterations for the refiner. + validation: + gt_eq<>: [1] + + n_coarse_iterations: + type: int + default_value: 1 + description: Number of iterations for the coarse estimate. + validation: + gt_eq<>: [1] + multiview: - ransac_n_iter: { - type: int, - default_value: 2000, - description: "Number of ransac iterations when matching views.", - } - ransac_dist_threshold: { - type: double, - default_value: 0.2, - description: "Threshold (in metter) on the symmetric distance (Mean Symmetry-Aware Surface Distance) used consider a tentative match as an inlier during RANSAC iterations.", - } - ba_n_iter: { - type: int, - default_value: 100, - description: "Number of steps in the final bundle adjustment refinement.", - } - labels_to_keep: { - type: string_array, - description: "Labels of detected objects to keep. If not specified, all objects are kept.", + ransac_n_iter: + type: int + default_value: 2000 + description: Number of ransac iterations when matching views. + + ransac_dist_threshold: + type: double + default_value: 0.2 + description: Threshold (in metter) on the symmetric distance (Mean Symmetry-Aware + Surface Distance) used consider a tentative match as an inlier during RANSAC + iterations. + + ba_n_iter: + type: int + default_value: 100 + description: Number of steps in the final bundle adjustment refinement. + + labels_to_keep: + type: string_array + description: Labels of detected objects to keep. If not specified, all objects are + kept. # Walkaround to obtain "empty" string array - default_value: [""], - validation: { - unique<>: null, - } - } - camera_names: { - type: string_array, - description: "List of names of cameras to subscribe.", - read_only: true, - validation: { - size_gt<>: [0], - unique<>: null, - } - } + default_value: [''] + validation: + unique<>: + + camera_names: + type: string_array + description: List of names of cameras to subscribe. + read_only: true + validation: + size_gt<>: [0] + unique<>: + cameras: - timeout: { - type: double, - default_value: 0.0, - description: "Timeout, after which a frame from a camera is considered too old. Value '0.0' disables timeout.", - validation: { - gt_eq<>: [0.0], - } - } - n_min_cameras: { - type: int, - default_value: 1, - description: "Minimum number of valid camera views to start pose estimation pipeline.", - validation: { - gt_eq<>: [1], - }, - read_only: true, - } + timeout: + type: double + default_value: 0.0 + description: Timeout, after which a frame from a camera is considered too old. Value + '0.0' disables timeout. + validation: + gt_eq<>: [0.0] + + n_min_cameras: + type: int + default_value: 1 + description: Minimum number of valid camera views to start pose estimation pipeline. + validation: + gt_eq<>: [1] + + read_only: true + __map_camera_names: - compressed: { - type: bool, - default_value: false, - description: "Expect compressed image messages from given camera.", - read_only: true, - } - leading: { - type: bool, - default_value: false, - description: "Consider the camera to be leading. If a camera is leading, its frame_id is used as a reference. Only one camera can be leading, and it can't publish TF at the same time.", - read_only: true, - } - publish_tf: { - type: bool, - default_value: false, - description: "Publish TF of a given camera relative to the leading camera.", - read_only: true, - } - k_matrix: { - type: double_array, - default_value: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - description: "Camera intrinsic matrix. If values are valid intrinsic matrix, overwrites values from info ROS topic.", - validation: { - fixed_size<>: [9], + compressed: + type: bool + default_value: false + description: Expect compressed image messages from given camera. + read_only: true + + leading: + type: bool + default_value: false + description: Consider the camera to be leading. If a camera is leading, its frame_id + is used as a reference. Only one camera can be leading, and it can't publish TF + at the same time. + read_only: true + + publish_tf: + type: bool + default_value: false + description: Publish TF of a given camera relative to the leading camera. + read_only: true + + k_matrix: + type: double_array + default_value: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + description: Camera intrinsic matrix. If values are valid intrinsic matrix, overwrites + values from info ROS topic. + validation: + fixed_size<>: [9] lower_element_bounds<>: [0.0] - } - } diff --git a/happypose_ros/test/test_multi_view.yaml b/happypose_ros/test/test_multi_view.yaml index d445d40..23d2562 100644 --- a/happypose_ros/test/test_multi_view.yaml +++ b/happypose_ros/test/test_multi_view.yaml @@ -1,3 +1,4 @@ +--- /**: ros__parameters: verbose_info_logs: true @@ -8,7 +9,7 @@ renderer: n_workers: 1 - camera_names: ["cam_1", "cam_2", "cam_3"] + camera_names: [cam_1, cam_2, cam_3] cameras: timeout: 0.0 n_min_cameras: 3 @@ -16,8 +17,7 @@ leading: true publish_tf: false compressed: false - k_matrix: - [1066.778, 0.0, 312.9869, 0.0, 1067.487, 241.3109, 0.0, 0.0, 1.0] + k_matrix: [1066.778, 0.0, 312.9869, 0.0, 1067.487, 241.3109, 0.0, 0.0, 1.0] cam_2: compressed: true cam_3: diff --git a/happypose_ros/test/test_single_view.yaml b/happypose_ros/test/test_single_view.yaml index 71d14f3..50ab4b3 100644 --- a/happypose_ros/test/test_single_view.yaml +++ b/happypose_ros/test/test_single_view.yaml @@ -1,3 +1,4 @@ +--- /**: ros__parameters: # device is set from launch file @@ -10,7 +11,7 @@ renderer: n_workers: 1 - camera_names: ["cam_1"] + camera_names: [cam_1] cameras: timeout: 0.0 n_min_cameras: 1