From 1a674188ee10535db94d96feb07bc63da110d514 Mon Sep 17 00:00:00 2001 From: lgv Date: Wed, 17 Jun 2026 09:36:26 +0800 Subject: [PATCH] feat : add touch ui --- ....sync-conflict-20260318-013404-55JW2V4.iml | 11 + ....sync-conflict-20260318-013404-55JW2V4.xml | 7 + ....sync-conflict-20260318-013404-55JW2V4.xml | 103 +++ ....sync-conflict-20260326-143838-55JW2V4.xml | 247 ++++++ ....sync-conflict-20260402-100016-55JW2V4.xml | 272 +++++++ README.md | 10 +- clients/__pycache__/__init__.cpython-39.pyc | Bin 142 -> 142 bytes .../__pycache__/_path_setup.cpython-39.pyc | Bin 499 -> 499 bytes .../__pycache__/base_client.cpython-39.pyc | Bin 1906 -> 2066 bytes .../__pycache__/camera_client.cpython-39.pyc | Bin 0 -> 2799 bytes ...get_rgb_image_stream_client.cpython-39.pyc | Bin 0 -> 2648 bytes .../__pycache__/hand_client.cpython-39.pyc | Bin 2313 -> 2313 bytes clients/__pycache__/hlc_client.cpython-39.pyc | Bin 0 -> 2026 bytes .../__pycache__/movej_client.cpython-39.pyc | Bin 7078 -> 7055 bytes .../torque_off_client.cpython-39.pyc | Bin 1406 -> 1406 bytes .../torque_on_client.cpython-39.pyc | Bin 1415 -> 1415 bytes clients/base_client.py | 8 +- clients/camera_client.py | 67 ++ clients/gui_camera_client..py | 727 ------------------ clients/hand_client.py | 2 +- clients/hlc_client.py | 66 ++ clients/joint_states.csv | 51 -- clients/press_client.py | 54 -- .../cmvr/__pycache__/__init__.cpython-39.pyc | Bin 149 -> 149 bytes .../api/__pycache__/__init__.cpython-39.pyc | Bin 153 -> 153 bytes .../camera_command_pb2.cpython-39.pyc | Bin 0 -> 6292 bytes .../camera_service_pb2_grpc.cpython-39.pyc | Bin 0 -> 9684 bytes .../api/__pycache__/common_pb2.cpython-39.pyc | Bin 2175 -> 1956 bytes .../dexhand_command_pb2.cpython-39.pyc | Bin 6883 -> 5226 bytes .../dexhand_service_pb2_grpc.cpython-39.pyc | Bin 6966 -> 7887 bytes .../hlc_command_pb2.cpython-39.pyc | Bin 1444 -> 1407 bytes .../hlc_service_pb2_grpc.cpython-39.pyc | Bin 0 -> 3108 bytes .../humanoid_robot_command_pb2.cpython-39.pyc | Bin 5397 -> 5687 bytes ...noid_robot_service_pb2_grpc.cpython-39.pyc | Bin 6691 -> 10228 bytes generated/cmvr/api/biohead_command_pb2.py | 50 +- generated/cmvr/api/biohead_service_pb2.py | 4 +- .../cmvr/api/biohead_service_pb2_grpc.py | 346 +++++++++ .../cmvr/api/humanoid_robot_command_pb2.py | 120 +-- .../cmvr/api/humanoid_robot_service_pb2.py | 4 +- .../api/humanoid_robot_service_pb2_grpc.py | 86 +++ generated/cmvr/api/system_command_pb2.py | 12 +- generated/cmvr/api/system_service_pb2.py | 4 +- generated/cmvr/api/system_service_pb2_grpc.py | 43 ++ generated/cmvr/config/__init__.py | 0 .../cmvr/config/camera_config/__init__.py | 0 .../config/camera_config/camera_config_pb2.py | 46 ++ .../camera_config/camera_config_pb2_grpc.py | 24 + .../cmvr/config/dexhand_config/__init__.py | 0 .../dexhand_config/dexhand_config_pb2.py | 38 + .../dexhand_config/dexhand_config_pb2_grpc.py | 24 + .../cmvr/config/microphone_config/__init__.py | 0 .../microphone_config_pb2.py | 38 + .../microphone_config_pb2_grpc.py | 24 + .../config/pinocchio_dls_ik_config_pb2.py | 34 + .../pinocchio_dls_ik_config_pb2_grpc.py | 24 + .../pinocchio_qp_ik_solver_config_pb2.py | 36 + .../pinocchio_qp_ik_solver_config_pb2_grpc.py | 24 + .../cmvr/config/speaker_config/__init__.py | 0 .../speaker_config/speaker_conifg_pb2.py | 38 + .../speaker_config/speaker_conifg_pb2_grpc.py | 24 + .../touch_screen_app_config/__init__.py | 0 .../touch_screen_app_config_pb2.py | 52 ++ .../touch_screen_app_config_pb2_grpc.py | 24 + generated_apriltags/1.0x1.0cm/1.ai | Bin 0 -> 1612 bytes generated_apriltags/1.0x1.0cm/10.ai | Bin 0 -> 1595 bytes generated_apriltags/1.0x1.0cm/11.ai | Bin 0 -> 1621 bytes generated_apriltags/1.0x1.0cm/12.ai | Bin 0 -> 1603 bytes generated_apriltags/1.0x1.0cm/13.ai | Bin 0 -> 1604 bytes generated_apriltags/1.0x1.0cm/14.ai | Bin 0 -> 1636 bytes generated_apriltags/1.0x1.0cm/15.ai | Bin 0 -> 1604 bytes generated_apriltags/1.0x1.0cm/16.ai | Bin 0 -> 1608 bytes generated_apriltags/1.0x1.0cm/17.ai | Bin 0 -> 1598 bytes generated_apriltags/1.0x1.0cm/18.ai | Bin 0 -> 1599 bytes generated_apriltags/1.0x1.0cm/19.ai | Bin 0 -> 1612 bytes generated_apriltags/1.0x1.0cm/2.ai | Bin 0 -> 1590 bytes generated_apriltags/1.0x1.0cm/20.ai | Bin 0 -> 1618 bytes generated_apriltags/1.0x1.0cm/3.ai | Bin 0 -> 1609 bytes generated_apriltags/1.0x1.0cm/4.ai | Bin 0 -> 1594 bytes generated_apriltags/1.0x1.0cm/5.ai | Bin 0 -> 1612 bytes generated_apriltags/1.0x1.0cm/6.ai | Bin 0 -> 1603 bytes generated_apriltags/1.0x1.0cm/7.ai | Bin 0 -> 1583 bytes generated_apriltags/1.0x1.0cm/8.ai | Bin 0 -> 1614 bytes generated_apriltags/1.0x1.0cm/9.ai | Bin 0 -> 1629 bytes generated_apriltags/1.5x1.5cm/1.ai | Bin 0 -> 1609 bytes generated_apriltags/1.5x1.5cm/10.ai | Bin 0 -> 1591 bytes generated_apriltags/1.5x1.5cm/11.ai | Bin 0 -> 1618 bytes generated_apriltags/1.5x1.5cm/12.ai | Bin 0 -> 1599 bytes generated_apriltags/1.5x1.5cm/13.ai | Bin 0 -> 1597 bytes generated_apriltags/1.5x1.5cm/14.ai | Bin 0 -> 1632 bytes generated_apriltags/1.5x1.5cm/15.ai | Bin 0 -> 1600 bytes generated_apriltags/1.5x1.5cm/16.ai | Bin 0 -> 1606 bytes generated_apriltags/1.5x1.5cm/17.ai | Bin 0 -> 1591 bytes generated_apriltags/1.5x1.5cm/18.ai | Bin 0 -> 1595 bytes generated_apriltags/1.5x1.5cm/19.ai | Bin 0 -> 1608 bytes generated_apriltags/1.5x1.5cm/2.ai | Bin 0 -> 1585 bytes generated_apriltags/1.5x1.5cm/20.ai | Bin 0 -> 1615 bytes generated_apriltags/1.5x1.5cm/3.ai | Bin 0 -> 1603 bytes generated_apriltags/1.5x1.5cm/4.ai | 72 ++ generated_apriltags/1.5x1.5cm/5.ai | Bin 0 -> 1608 bytes generated_apriltags/1.5x1.5cm/6.ai | Bin 0 -> 1599 bytes generated_apriltags/1.5x1.5cm/7.ai | Bin 0 -> 1581 bytes generated_apriltags/1.5x1.5cm/8.ai | Bin 0 -> 1609 bytes generated_apriltags/1.5x1.5cm/9.ai | 73 ++ generated_apriltags/2.0x2.0cm/1.ai | Bin 0 -> 1608 bytes generated_apriltags/2.0x2.0cm/10.ai | 73 ++ generated_apriltags/2.0x2.0cm/11.ai | 75 ++ generated_apriltags/2.0x2.0cm/12.ai | 72 ++ generated_apriltags/2.0x2.0cm/13.ai | Bin 0 -> 1602 bytes generated_apriltags/2.0x2.0cm/14.ai | 74 ++ generated_apriltags/2.0x2.0cm/15.ai | Bin 0 -> 1601 bytes generated_apriltags/2.0x2.0cm/16.ai | Bin 0 -> 1607 bytes generated_apriltags/2.0x2.0cm/17.ai | Bin 0 -> 1595 bytes generated_apriltags/2.0x2.0cm/18.ai | 74 ++ generated_apriltags/2.0x2.0cm/19.ai | 72 ++ generated_apriltags/2.0x2.0cm/2.ai | Bin 0 -> 1588 bytes generated_apriltags/2.0x2.0cm/20.ai | Bin 0 -> 1617 bytes generated_apriltags/2.0x2.0cm/3.ai | Bin 0 -> 1608 bytes generated_apriltags/2.0x2.0cm/4.ai | Bin 0 -> 1593 bytes generated_apriltags/2.0x2.0cm/5.ai | 74 ++ generated_apriltags/2.0x2.0cm/6.ai | Bin 0 -> 1599 bytes generated_apriltags/2.0x2.0cm/7.ai | Bin 0 -> 1582 bytes generated_apriltags/2.0x2.0cm/8.ai | Bin 0 -> 1610 bytes generated_apriltags/2.0x2.0cm/9.ai | 76 ++ generated_apriltags_svg_check/1.0x1.0cm/1.svg | 191 +++++ generated_apriltags_svg_check/1.5x1.5cm/1.svg | 191 +++++ protos/cmvr/api/biohead_command.proto | 93 +++ protos/cmvr/api/biohead_service.proto | 25 + protos/cmvr/api/humanoid_robot_command.proto | 42 + protos/cmvr/api/humanoid_robot_service.proto | 3 + protos/cmvr/api/system_command.proto | 11 + protos/cmvr/api/system_service.proto | 2 + .../config/camera_config/camera_config.proto | 60 ++ .../dexhand_config/dexhand_config.proto | 15 + .../microphone_config/microphone_config.proto | 15 + .../cmvr/config/pinocchio_dls_ik_config.proto | 0 .../pinocchio_qp_ik_solver_config.proto | 17 + .../speaker_config/speaker_conifg.proto | 11 + .../touch_screen_app_config.proto | 122 +++ requirements.txt | 6 +- .../generate_apriltag_single.cpython-39.pyc | Bin 0 -> 12548 bytes scripts/apriltags_border_20x15cm_A4.pdf | Bin 6786 -> 5849 bytes ....sync-conflict-20260318-013404-55JW2V4.pdf | Bin 0 -> 7312 bytes scripts/generate_apriltag_batch.py | 162 ++++ scripts/generate_apriltag_single.py | 471 +++++++++--- scripts/generate_apriltags_border.py | 10 +- ...r.sync-conflict-20260318-013404-55JW2V4.py | 384 +++++++++ scripts/generated_apriltags.zip | Bin 0 -> 158359 bytes scripts/generated_apriltags/1.0x1.0cm/1.ai | Bin 0 -> 1612 bytes scripts/generated_apriltags/1.0x1.0cm/1.png | Bin 0 -> 544 bytes scripts/generated_apriltags/1.0x1.0cm/10.ai | Bin 0 -> 1595 bytes scripts/generated_apriltags/1.0x1.0cm/10.png | Bin 0 -> 533 bytes scripts/generated_apriltags/1.0x1.0cm/11.ai | Bin 0 -> 1621 bytes scripts/generated_apriltags/1.0x1.0cm/11.png | Bin 0 -> 522 bytes scripts/generated_apriltags/1.0x1.0cm/12.ai | Bin 0 -> 1603 bytes scripts/generated_apriltags/1.0x1.0cm/12.png | Bin 0 -> 544 bytes scripts/generated_apriltags/1.0x1.0cm/13.ai | Bin 0 -> 1604 bytes scripts/generated_apriltags/1.0x1.0cm/13.png | Bin 0 -> 531 bytes scripts/generated_apriltags/1.0x1.0cm/14.ai | Bin 0 -> 1636 bytes scripts/generated_apriltags/1.0x1.0cm/14.png | Bin 0 -> 545 bytes scripts/generated_apriltags/1.0x1.0cm/15.ai | Bin 0 -> 1604 bytes scripts/generated_apriltags/1.0x1.0cm/15.png | Bin 0 -> 539 bytes scripts/generated_apriltags/1.0x1.0cm/16.ai | Bin 0 -> 1608 bytes scripts/generated_apriltags/1.0x1.0cm/16.png | Bin 0 -> 534 bytes scripts/generated_apriltags/1.0x1.0cm/17.ai | Bin 0 -> 1598 bytes scripts/generated_apriltags/1.0x1.0cm/17.png | Bin 0 -> 538 bytes scripts/generated_apriltags/1.0x1.0cm/18.ai | Bin 0 -> 1599 bytes scripts/generated_apriltags/1.0x1.0cm/18.png | Bin 0 -> 540 bytes scripts/generated_apriltags/1.0x1.0cm/19.ai | Bin 0 -> 1612 bytes scripts/generated_apriltags/1.0x1.0cm/19.png | Bin 0 -> 530 bytes scripts/generated_apriltags/1.0x1.0cm/2.ai | Bin 0 -> 1590 bytes scripts/generated_apriltags/1.0x1.0cm/2.png | Bin 0 -> 538 bytes scripts/generated_apriltags/1.0x1.0cm/20.ai | Bin 0 -> 1618 bytes scripts/generated_apriltags/1.0x1.0cm/20.png | Bin 0 -> 536 bytes scripts/generated_apriltags/1.0x1.0cm/3.ai | Bin 0 -> 1609 bytes scripts/generated_apriltags/1.0x1.0cm/3.png | Bin 0 -> 534 bytes scripts/generated_apriltags/1.0x1.0cm/4.ai | Bin 0 -> 1594 bytes scripts/generated_apriltags/1.0x1.0cm/4.png | Bin 0 -> 535 bytes scripts/generated_apriltags/1.0x1.0cm/5.ai | Bin 0 -> 1612 bytes scripts/generated_apriltags/1.0x1.0cm/5.png | Bin 0 -> 534 bytes scripts/generated_apriltags/1.0x1.0cm/6.ai | Bin 0 -> 1603 bytes scripts/generated_apriltags/1.0x1.0cm/6.png | Bin 0 -> 533 bytes scripts/generated_apriltags/1.0x1.0cm/7.ai | Bin 0 -> 1583 bytes scripts/generated_apriltags/1.0x1.0cm/7.png | Bin 0 -> 536 bytes scripts/generated_apriltags/1.0x1.0cm/8.ai | Bin 0 -> 1614 bytes scripts/generated_apriltags/1.0x1.0cm/8.png | Bin 0 -> 538 bytes scripts/generated_apriltags/1.0x1.0cm/9.ai | Bin 0 -> 1629 bytes scripts/generated_apriltags/1.0x1.0cm/9.png | Bin 0 -> 530 bytes scripts/generated_apriltags/1.5x1.5cm/1.ai | Bin 0 -> 1609 bytes scripts/generated_apriltags/1.5x1.5cm/1.png | Bin 0 -> 746 bytes scripts/generated_apriltags/1.5x1.5cm/10.ai | Bin 0 -> 1591 bytes scripts/generated_apriltags/1.5x1.5cm/10.png | Bin 0 -> 737 bytes scripts/generated_apriltags/1.5x1.5cm/11.ai | Bin 0 -> 1618 bytes scripts/generated_apriltags/1.5x1.5cm/11.png | Bin 0 -> 728 bytes scripts/generated_apriltags/1.5x1.5cm/12.ai | Bin 0 -> 1599 bytes scripts/generated_apriltags/1.5x1.5cm/12.png | Bin 0 -> 741 bytes scripts/generated_apriltags/1.5x1.5cm/13.ai | Bin 0 -> 1597 bytes scripts/generated_apriltags/1.5x1.5cm/13.png | Bin 0 -> 739 bytes scripts/generated_apriltags/1.5x1.5cm/14.ai | Bin 0 -> 1632 bytes scripts/generated_apriltags/1.5x1.5cm/14.png | Bin 0 -> 752 bytes scripts/generated_apriltags/1.5x1.5cm/15.ai | Bin 0 -> 1600 bytes scripts/generated_apriltags/1.5x1.5cm/15.png | Bin 0 -> 726 bytes scripts/generated_apriltags/1.5x1.5cm/16.ai | Bin 0 -> 1606 bytes scripts/generated_apriltags/1.5x1.5cm/16.png | Bin 0 -> 737 bytes scripts/generated_apriltags/1.5x1.5cm/17.ai | Bin 0 -> 1591 bytes scripts/generated_apriltags/1.5x1.5cm/17.png | Bin 0 -> 734 bytes scripts/generated_apriltags/1.5x1.5cm/18.ai | Bin 0 -> 1595 bytes scripts/generated_apriltags/1.5x1.5cm/18.png | Bin 0 -> 741 bytes scripts/generated_apriltags/1.5x1.5cm/19.ai | Bin 0 -> 1608 bytes scripts/generated_apriltags/1.5x1.5cm/19.png | Bin 0 -> 733 bytes scripts/generated_apriltags/1.5x1.5cm/2.ai | Bin 0 -> 1585 bytes scripts/generated_apriltags/1.5x1.5cm/2.png | Bin 0 -> 735 bytes scripts/generated_apriltags/1.5x1.5cm/20.ai | Bin 0 -> 1615 bytes scripts/generated_apriltags/1.5x1.5cm/20.png | Bin 0 -> 732 bytes scripts/generated_apriltags/1.5x1.5cm/3.ai | Bin 0 -> 1603 bytes scripts/generated_apriltags/1.5x1.5cm/3.png | Bin 0 -> 745 bytes scripts/generated_apriltags/1.5x1.5cm/4.ai | 71 ++ scripts/generated_apriltags/1.5x1.5cm/4.png | Bin 0 -> 738 bytes scripts/generated_apriltags/1.5x1.5cm/5.ai | Bin 0 -> 1608 bytes scripts/generated_apriltags/1.5x1.5cm/5.png | Bin 0 -> 735 bytes scripts/generated_apriltags/1.5x1.5cm/6.ai | Bin 0 -> 1599 bytes scripts/generated_apriltags/1.5x1.5cm/6.png | Bin 0 -> 730 bytes scripts/generated_apriltags/1.5x1.5cm/7.ai | Bin 0 -> 1581 bytes scripts/generated_apriltags/1.5x1.5cm/7.png | Bin 0 -> 735 bytes scripts/generated_apriltags/1.5x1.5cm/8.ai | Bin 0 -> 1609 bytes scripts/generated_apriltags/1.5x1.5cm/8.png | Bin 0 -> 748 bytes scripts/generated_apriltags/1.5x1.5cm/9.ai | 72 ++ scripts/generated_apriltags/1.5x1.5cm/9.png | Bin 0 -> 738 bytes scripts/generated_apriltags/2.0x2.0cm/1.ai | Bin 0 -> 1608 bytes scripts/generated_apriltags/2.0x2.0cm/1.png | Bin 0 -> 961 bytes scripts/generated_apriltags/2.0x2.0cm/10.ai | 72 ++ scripts/generated_apriltags/2.0x2.0cm/10.png | Bin 0 -> 950 bytes scripts/generated_apriltags/2.0x2.0cm/11.ai | 74 ++ scripts/generated_apriltags/2.0x2.0cm/11.png | Bin 0 -> 956 bytes scripts/generated_apriltags/2.0x2.0cm/12.ai | 71 ++ scripts/generated_apriltags/2.0x2.0cm/12.png | Bin 0 -> 960 bytes scripts/generated_apriltags/2.0x2.0cm/13.ai | Bin 0 -> 1602 bytes scripts/generated_apriltags/2.0x2.0cm/13.png | Bin 0 -> 956 bytes scripts/generated_apriltags/2.0x2.0cm/14.ai | 73 ++ scripts/generated_apriltags/2.0x2.0cm/14.png | Bin 0 -> 956 bytes scripts/generated_apriltags/2.0x2.0cm/15.ai | Bin 0 -> 1601 bytes scripts/generated_apriltags/2.0x2.0cm/15.png | Bin 0 -> 956 bytes scripts/generated_apriltags/2.0x2.0cm/16.ai | Bin 0 -> 1607 bytes scripts/generated_apriltags/2.0x2.0cm/16.png | Bin 0 -> 949 bytes scripts/generated_apriltags/2.0x2.0cm/17.ai | Bin 0 -> 1595 bytes scripts/generated_apriltags/2.0x2.0cm/17.png | Bin 0 -> 960 bytes scripts/generated_apriltags/2.0x2.0cm/18.ai | 73 ++ scripts/generated_apriltags/2.0x2.0cm/18.png | Bin 0 -> 960 bytes scripts/generated_apriltags/2.0x2.0cm/19.ai | 71 ++ scripts/generated_apriltags/2.0x2.0cm/19.png | Bin 0 -> 954 bytes scripts/generated_apriltags/2.0x2.0cm/2.ai | Bin 0 -> 1588 bytes scripts/generated_apriltags/2.0x2.0cm/2.png | Bin 0 -> 958 bytes scripts/generated_apriltags/2.0x2.0cm/20.ai | Bin 0 -> 1617 bytes scripts/generated_apriltags/2.0x2.0cm/20.png | Bin 0 -> 952 bytes scripts/generated_apriltags/2.0x2.0cm/3.ai | Bin 0 -> 1608 bytes scripts/generated_apriltags/2.0x2.0cm/3.png | Bin 0 -> 953 bytes scripts/generated_apriltags/2.0x2.0cm/4.ai | Bin 0 -> 1593 bytes scripts/generated_apriltags/2.0x2.0cm/4.png | Bin 0 -> 958 bytes scripts/generated_apriltags/2.0x2.0cm/5.ai | 73 ++ scripts/generated_apriltags/2.0x2.0cm/5.png | Bin 0 -> 957 bytes scripts/generated_apriltags/2.0x2.0cm/6.ai | Bin 0 -> 1599 bytes scripts/generated_apriltags/2.0x2.0cm/6.png | Bin 0 -> 960 bytes scripts/generated_apriltags/2.0x2.0cm/7.ai | Bin 0 -> 1582 bytes scripts/generated_apriltags/2.0x2.0cm/7.png | Bin 0 -> 951 bytes scripts/generated_apriltags/2.0x2.0cm/8.ai | Bin 0 -> 1610 bytes scripts/generated_apriltags/2.0x2.0cm/8.png | Bin 0 -> 959 bytes scripts/generated_apriltags/2.0x2.0cm/9.ai | 75 ++ scripts/generated_apriltags/2.0x2.0cm/9.png | Bin 0 -> 959 bytes ui/__pycache__/__init__.cpython-39.pyc | Bin 137 -> 137 bytes ui/__pycache__/app.cpython-39.pyc | Bin 37393 -> 24497 bytes ui/__pycache__/camera_page.cpython-39.pyc | Bin 0 -> 16282 bytes ui/__pycache__/plot_page.cpython-39.pyc | Bin 0 -> 12406 bytes ui/__pycache__/rgb_image_page.cpython-39.pyc | Bin 0 -> 20508 bytes ui/__pycache__/run_action.cpython-39.pyc | Bin 2471 -> 2471 bytes ui/app.py | 203 ++--- ui/camera_page.py | 605 +++++++++++++++ ui/plot_page.py | 396 ++++++++++ ui/rgb_image_page.py | 710 +++++++++++++++++ 277 files changed, 6808 insertions(+), 1141 deletions(-) create mode 100644 .idea/grpc_client.sync-conflict-20260318-013404-55JW2V4.iml create mode 100644 .idea/misc.sync-conflict-20260318-013404-55JW2V4.xml create mode 100644 .idea/workspace.sync-conflict-20260318-013404-55JW2V4.xml create mode 100644 .idea/workspace.sync-conflict-20260326-143838-55JW2V4.xml create mode 100644 .idea/workspace.sync-conflict-20260402-100016-55JW2V4.xml create mode 100644 clients/__pycache__/camera_client.cpython-39.pyc create mode 100644 clients/__pycache__/get_rgb_image_stream_client.cpython-39.pyc create mode 100644 clients/__pycache__/hlc_client.cpython-39.pyc create mode 100644 clients/camera_client.py delete mode 100644 clients/gui_camera_client..py create mode 100644 clients/hlc_client.py delete mode 100644 clients/joint_states.csv delete mode 100644 clients/press_client.py create mode 100644 generated/cmvr/api/__pycache__/camera_command_pb2.cpython-39.pyc create mode 100644 generated/cmvr/api/__pycache__/camera_service_pb2_grpc.cpython-39.pyc create mode 100644 generated/cmvr/api/__pycache__/hlc_service_pb2_grpc.cpython-39.pyc create mode 100644 generated/cmvr/config/__init__.py create mode 100644 generated/cmvr/config/camera_config/__init__.py create mode 100644 generated/cmvr/config/camera_config/camera_config_pb2.py create mode 100644 generated/cmvr/config/camera_config/camera_config_pb2_grpc.py create mode 100644 generated/cmvr/config/dexhand_config/__init__.py create mode 100644 generated/cmvr/config/dexhand_config/dexhand_config_pb2.py create mode 100644 generated/cmvr/config/dexhand_config/dexhand_config_pb2_grpc.py create mode 100644 generated/cmvr/config/microphone_config/__init__.py create mode 100644 generated/cmvr/config/microphone_config/microphone_config_pb2.py create mode 100644 generated/cmvr/config/microphone_config/microphone_config_pb2_grpc.py create mode 100644 generated/cmvr/config/pinocchio_dls_ik_config_pb2.py create mode 100644 generated/cmvr/config/pinocchio_dls_ik_config_pb2_grpc.py create mode 100644 generated/cmvr/config/pinocchio_qp_ik_solver_config_pb2.py create mode 100644 generated/cmvr/config/pinocchio_qp_ik_solver_config_pb2_grpc.py create mode 100644 generated/cmvr/config/speaker_config/__init__.py create mode 100644 generated/cmvr/config/speaker_config/speaker_conifg_pb2.py create mode 100644 generated/cmvr/config/speaker_config/speaker_conifg_pb2_grpc.py create mode 100644 generated/cmvr/config/touch_screen_app_config/__init__.py create mode 100644 generated/cmvr/config/touch_screen_app_config/touch_screen_app_config_pb2.py create mode 100644 generated/cmvr/config/touch_screen_app_config/touch_screen_app_config_pb2_grpc.py create mode 100644 generated_apriltags/1.0x1.0cm/1.ai create mode 100644 generated_apriltags/1.0x1.0cm/10.ai create mode 100644 generated_apriltags/1.0x1.0cm/11.ai create mode 100644 generated_apriltags/1.0x1.0cm/12.ai create mode 100644 generated_apriltags/1.0x1.0cm/13.ai create mode 100644 generated_apriltags/1.0x1.0cm/14.ai create mode 100644 generated_apriltags/1.0x1.0cm/15.ai create mode 100644 generated_apriltags/1.0x1.0cm/16.ai create mode 100644 generated_apriltags/1.0x1.0cm/17.ai create mode 100644 generated_apriltags/1.0x1.0cm/18.ai create mode 100644 generated_apriltags/1.0x1.0cm/19.ai create mode 100644 generated_apriltags/1.0x1.0cm/2.ai create mode 100644 generated_apriltags/1.0x1.0cm/20.ai create mode 100644 generated_apriltags/1.0x1.0cm/3.ai create mode 100644 generated_apriltags/1.0x1.0cm/4.ai create mode 100644 generated_apriltags/1.0x1.0cm/5.ai create mode 100644 generated_apriltags/1.0x1.0cm/6.ai create mode 100644 generated_apriltags/1.0x1.0cm/7.ai create mode 100644 generated_apriltags/1.0x1.0cm/8.ai create mode 100644 generated_apriltags/1.0x1.0cm/9.ai create mode 100644 generated_apriltags/1.5x1.5cm/1.ai create mode 100644 generated_apriltags/1.5x1.5cm/10.ai create mode 100644 generated_apriltags/1.5x1.5cm/11.ai create mode 100644 generated_apriltags/1.5x1.5cm/12.ai create mode 100644 generated_apriltags/1.5x1.5cm/13.ai create mode 100644 generated_apriltags/1.5x1.5cm/14.ai create mode 100644 generated_apriltags/1.5x1.5cm/15.ai create mode 100644 generated_apriltags/1.5x1.5cm/16.ai create mode 100644 generated_apriltags/1.5x1.5cm/17.ai create mode 100644 generated_apriltags/1.5x1.5cm/18.ai create mode 100644 generated_apriltags/1.5x1.5cm/19.ai create mode 100644 generated_apriltags/1.5x1.5cm/2.ai create mode 100644 generated_apriltags/1.5x1.5cm/20.ai create mode 100644 generated_apriltags/1.5x1.5cm/3.ai create mode 100644 generated_apriltags/1.5x1.5cm/4.ai create mode 100644 generated_apriltags/1.5x1.5cm/5.ai create mode 100644 generated_apriltags/1.5x1.5cm/6.ai create mode 100644 generated_apriltags/1.5x1.5cm/7.ai create mode 100644 generated_apriltags/1.5x1.5cm/8.ai create mode 100644 generated_apriltags/1.5x1.5cm/9.ai create mode 100644 generated_apriltags/2.0x2.0cm/1.ai create mode 100644 generated_apriltags/2.0x2.0cm/10.ai create mode 100644 generated_apriltags/2.0x2.0cm/11.ai create mode 100644 generated_apriltags/2.0x2.0cm/12.ai create mode 100644 generated_apriltags/2.0x2.0cm/13.ai create mode 100644 generated_apriltags/2.0x2.0cm/14.ai create mode 100644 generated_apriltags/2.0x2.0cm/15.ai create mode 100644 generated_apriltags/2.0x2.0cm/16.ai create mode 100644 generated_apriltags/2.0x2.0cm/17.ai create mode 100644 generated_apriltags/2.0x2.0cm/18.ai create mode 100644 generated_apriltags/2.0x2.0cm/19.ai create mode 100644 generated_apriltags/2.0x2.0cm/2.ai create mode 100644 generated_apriltags/2.0x2.0cm/20.ai create mode 100644 generated_apriltags/2.0x2.0cm/3.ai create mode 100644 generated_apriltags/2.0x2.0cm/4.ai create mode 100644 generated_apriltags/2.0x2.0cm/5.ai create mode 100644 generated_apriltags/2.0x2.0cm/6.ai create mode 100644 generated_apriltags/2.0x2.0cm/7.ai create mode 100644 generated_apriltags/2.0x2.0cm/8.ai create mode 100644 generated_apriltags/2.0x2.0cm/9.ai create mode 100644 generated_apriltags_svg_check/1.0x1.0cm/1.svg create mode 100644 generated_apriltags_svg_check/1.5x1.5cm/1.svg create mode 100644 protos/cmvr/config/camera_config/camera_config.proto create mode 100644 protos/cmvr/config/dexhand_config/dexhand_config.proto create mode 100644 protos/cmvr/config/microphone_config/microphone_config.proto create mode 100644 protos/cmvr/config/pinocchio_dls_ik_config.proto create mode 100644 protos/cmvr/config/pinocchio_qp_ik_solver_config.proto create mode 100644 protos/cmvr/config/speaker_config/speaker_conifg.proto create mode 100644 protos/cmvr/config/touch_screen_app_config/touch_screen_app_config.proto create mode 100644 scripts/__pycache__/generate_apriltag_single.cpython-39.pyc create mode 100644 scripts/apriltags_border_20x15cm_A4.sync-conflict-20260318-013404-55JW2V4.pdf create mode 100644 scripts/generate_apriltag_batch.py create mode 100644 scripts/generate_apriltags_border.sync-conflict-20260318-013404-55JW2V4.py create mode 100644 scripts/generated_apriltags.zip create mode 100644 scripts/generated_apriltags/1.0x1.0cm/1.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/1.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/10.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/10.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/11.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/11.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/12.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/12.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/13.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/13.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/14.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/14.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/15.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/15.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/16.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/16.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/17.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/17.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/18.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/18.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/19.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/19.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/2.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/2.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/20.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/20.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/3.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/3.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/4.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/4.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/5.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/5.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/6.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/6.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/7.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/7.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/8.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/8.png create mode 100644 scripts/generated_apriltags/1.0x1.0cm/9.ai create mode 100644 scripts/generated_apriltags/1.0x1.0cm/9.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/1.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/1.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/10.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/10.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/11.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/11.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/12.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/12.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/13.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/13.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/14.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/14.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/15.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/15.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/16.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/16.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/17.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/17.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/18.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/18.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/19.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/19.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/2.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/2.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/20.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/20.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/3.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/3.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/4.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/4.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/5.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/5.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/6.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/6.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/7.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/7.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/8.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/8.png create mode 100644 scripts/generated_apriltags/1.5x1.5cm/9.ai create mode 100644 scripts/generated_apriltags/1.5x1.5cm/9.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/1.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/1.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/10.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/10.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/11.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/11.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/12.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/12.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/13.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/13.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/14.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/14.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/15.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/15.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/16.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/16.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/17.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/17.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/18.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/18.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/19.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/19.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/2.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/2.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/20.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/20.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/3.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/3.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/4.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/4.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/5.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/5.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/6.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/6.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/7.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/7.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/8.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/8.png create mode 100644 scripts/generated_apriltags/2.0x2.0cm/9.ai create mode 100644 scripts/generated_apriltags/2.0x2.0cm/9.png create mode 100644 ui/__pycache__/camera_page.cpython-39.pyc create mode 100644 ui/__pycache__/plot_page.cpython-39.pyc create mode 100644 ui/__pycache__/rgb_image_page.cpython-39.pyc create mode 100644 ui/camera_page.py create mode 100644 ui/plot_page.py create mode 100644 ui/rgb_image_page.py diff --git a/.idea/grpc_client.sync-conflict-20260318-013404-55JW2V4.iml b/.idea/grpc_client.sync-conflict-20260318-013404-55JW2V4.iml new file mode 100644 index 0000000..1675d09 --- /dev/null +++ b/.idea/grpc_client.sync-conflict-20260318-013404-55JW2V4.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.sync-conflict-20260318-013404-55JW2V4.xml b/.idea/misc.sync-conflict-20260318-013404-55JW2V4.xml new file mode 100644 index 0000000..accb38f --- /dev/null +++ b/.idea/misc.sync-conflict-20260318-013404-55JW2V4.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.sync-conflict-20260318-013404-55JW2V4.xml b/.idea/workspace.sync-conflict-20260318-013404-55JW2V4.xml new file mode 100644 index 0000000..d286775 --- /dev/null +++ b/.idea/workspace.sync-conflict-20260318-013404-55JW2V4.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1773704484327 + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.sync-conflict-20260326-143838-55JW2V4.xml b/.idea/workspace.sync-conflict-20260326-143838-55JW2V4.xml new file mode 100644 index 0000000..9d758e2 --- /dev/null +++ b/.idea/workspace.sync-conflict-20260326-143838-55JW2V4.xml @@ -0,0 +1,247 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + "customColor": "", + "associatedIndex": 3 +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + C:\Users\lgv\AppData\Roaming\Subversion + + + + + 1773737578904 + + + + \ No newline at end of file diff --git a/.idea/workspace.sync-conflict-20260402-100016-55JW2V4.xml b/.idea/workspace.sync-conflict-20260402-100016-55JW2V4.xml new file mode 100644 index 0000000..78431af --- /dev/null +++ b/.idea/workspace.sync-conflict-20260402-100016-55JW2V4.xml @@ -0,0 +1,272 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + "customColor": "", + "associatedIndex": 3 +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + C:\Users\lgv\AppData\Roaming\Subversion + + + + + 1773737578904 + + + + \ No newline at end of file diff --git a/README.md b/README.md index 002e6fd..bfae643 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,13 @@ ## Setup ```bash -pip install -r requirements.txt +python -m pip install -r requirements.txt +``` + +Use the same Python interpreter you run the clients with. For example: + +```bash +C:\Users\lgv\miniconda3\envs\grpc_client\python.exe -m pip install -r requirements.txt ``` ## Generate gRPC code @@ -29,9 +35,9 @@ python -m clients.get_pose_client python -m clients.torque_on_client python -m clients.torque_off_client python -m clients.torque_key_client -python -m clients.press_client python -m clients.save_joint_to_csv python -m clients.http_server +python -m clients.hlc_client ``` ## UI (PyQt) diff --git a/clients/__pycache__/__init__.cpython-39.pyc b/clients/__pycache__/__init__.cpython-39.pyc index e6ee043e6749dfb863a68c87759c7cf24e10f5f1..775872aac8ba1b449aee4fca130a82c784838d9f 100644 GIT binary patch delta 19 YcmeBU>|^9k|^9k_10u=Mm-^Jpj;G3L1IZpd~r$9 zN`@l7$=;0do&rE&O_p2iAc>sJq#`CDzX)Vd5lE(p50&5ta&K|iSFD@xs z$xy^I*_$!mlMg7Y$#RPwB$1PuRKx`27jb|HkW3K|D!~in-r}&y%}*)KNws4H@`_o2 J1P>zzGXP@t95Da@ diff --git a/clients/__pycache__/base_client.cpython-39.pyc b/clients/__pycache__/base_client.cpython-39.pyc index a76c039d34ff1a0d084f03c0075aaa4f574835fc..3dc12679bc636c243dd16013e21d133c8665d5ca 100644 GIT binary patch delta 689 zcmZ8dO=}cE5bf&j`P`Yw8aGDNi;@t>VDJ#XKt)^-5k)HOTjCe4A zV6B6PAWVLO;K|=0coPih&8r6wo~@d!8f~aoudA!NU%wyOt9I5;(^$}2Xult>1mo<> z@)GR^q5vZbX;Pz&w*`eVz6rKOp-pHa%ASc$i?VN$J0j0;7FI7^rNIg+(G!&IK;A>ch5I?|T9F^|LTJJ%BWbh}PgK0c1SA#a)Ex^Z0 zp1dNNPZo1U(WNMl5jasp1v@N5I#ufl_lzK&a5MMuv%dVoX4INXdw`Nn7^eTgL1MeD z-f%Q@J(f})rb4L+1SIh6=zx6@td4GMGiz%_jbq)2gWC^f4g&6uyie)T#uiA!me6S?_3Kv)S^o26t~s) zFtaVX-cI3Iujm<~c-%XCk^fc%edh)nkKKR&v+O6^Rn8Fz&=5lhBxG<15*&MP=clM_ zS$+51U-@gC>*8cpB4)^oh&DG8EzBij;Hr9FsDMc9)gb>G}4m(XX5A6}| Sn8heK-~RjD-Ynhf5n^Fs zX>aKt*a@~47D4?0aaPfY3p2B4=gc{~TZ`Vqu#`##jMjMX@#dRz7Cwy)5?W_5NEUOM zkV4w05L?ob?kS6f^rTPKmccU1C$SF-FoR{twrN{Jm6h2AyW}STtjg*RUc)%J_z0sI z@d?P_0x#I1vBPsdj5Vu!xIsX|5mdj1w?MsUkh}`g>Le*CE-fQ=xPlMXz>N0m0eRS0 zL@p+DvYz@yA&ohN#E<_flWds@rg=Gi@Qhh8WZmlsm&B-*skh*PXn3tELGCKVU3(Ef z_*^3PD|&yexKY}XcxO+{M%0eolGf|nds<}-`75=UU7AuSMa6s=0|5cF0f!JeC_)@@ zZgb|T%BPeEWwicF^w8N@&7A@Vr`_CqtDzWKWg36TO;ej1DT<0i$%DZ6+LbxWd#a0y RYR4OQGHTxRdlFyW{!eN;U%3DP diff --git a/clients/__pycache__/camera_client.cpython-39.pyc b/clients/__pycache__/camera_client.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5d8ac4d27b0d6c5aa007c98a87f3dcc224c65395 GIT binary patch literal 2799 zcmb_e&2QX96rUN}YwuUG*;ceERHRTrU0?;Rgb=Dq)kP^BSgLHr!HQ&gJ+qrl?X^4h z7B$*($w~=_9=P|gIp$yB&*11Q2gDI^Lsi9lV<*{7CBDcS_O?kp+V0(#k5rsN*sWnP&QUJ>P8&1r~+v%+bY*wx=b2hy=w zX0^Q_@OpvBtZ(CV7>&HZrdj#HFceAZg)uNSF-pc#xUrXRCBQFlM7=0&2SYJR?|O-V zsiyCRLVB(rg`qd%Zrr;8vloilM99ZOA2#USa0fE>;Zp@AqY+N|sLFgR0dog>lENT} zLpgC6r;f%M*QUhLg&|5@e@?lV39KR*6{R%g;65;44$ZS>|;x&$+DDR)BJlJJ<5}T0fF&`G;-}X>H@)-L+xp4aC~U z{dUsw8K`{Jkk+8Lp(hO(QZl0xdO#<{Wt?=$A??^&rh!GAm4$7)>A08a35?(&JrO}a zGyN@ZG!nrfk+{`gpMClHrXN0*n;(DleIy?yUhIoaG_LC_$86@WWV7%=&bHzwS=Akm zhN+9YC+AQ#r8C{RI1jlsCj{;)bD=yqqv7>6r!|wrs__x`F%rx{fr_U&$C)@hAF~N6Rn z)CYSIlnkqA8VYt(%A)RIF z=Hc)*NG7N`q32JdotJwZXyqmNjcv-e;N`4z(%e(OX z1j}L+&xyTuC`|}QNdK=f`-|8;a0~wno;3;*ayf;1d0Udb^;^adZCM>RS%Gs3ssO^zGy2tjk zzRoqSPpzl=KHJ9f1qpqCCy3GUVaY)tP+^K-HZUtW0oDwVyCk`ur{zAIY5Sl(VtmHFgMyUm zGh-hTm6AtA>K@C$VUx>Xp|EsQWdJ|k^cr_q!3{tAVrm8FD<>suwJp3JD>g!!_939 zm{dhsIRnEsPD+Zz43kRR50FxRypEL8^87+n7Jfp(1P^jjskNMbKOT{FDSm4uFE6eb!8LQWHl&S>Cg{Fx)t$EgILMyCDu8) zhQk#cFw`X;cbVRcq98N+LFA4E(x^A?w~kLPya45=+qvSvZmD~gn}~EA zOUzN{_)8BiTgCA%(YBd|H-MHG52XpL3P}l9Pa^R^Q^%@O%(-yJzio9%_$Sbrbt^ad YZOrEcL(ZZNR)eR+YH&Ehqcv{+4fEHgjQ{`u literal 0 HcmV?d00001 diff --git a/clients/__pycache__/get_rgb_image_stream_client.cpython-39.pyc b/clients/__pycache__/get_rgb_image_stream_client.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a01f28d2ad6c156cd73418a3af8d9e1a0e26a9c4 GIT binary patch literal 2648 zcmb_eOK;pZ5GEfgefb zdqEsU{#bZP|2B+XDo0bPo{R!mpnuyNsw9A^8gj;C912jC#bQC{F4S}YjU=23!kM7l z5KI^|!cAF`mM~vXQ4!Y6+@;(u`?hdqCU?pn!ZvwTEK5t)WPM0vW5BMED?~O$^MxT= zVrf?4El~%9tmK#O%WUKRz3(G`D0`WbepDdbxi#E)(76_9xYq`;y4KNHdj+WW8L@W2 zK<}f1v;nmPHC=`#BXc^X2Xsn2CPT{Unf^Xko8VceD7Vn?sp-YS{Nih|WtjyppG# zJk9IgXgtbbe9})ZprRT9-MM}STw#;;0zVvPRY-llBN0%LJ}L_%-ki<*p$sH2jJLKx^2zvDLbG@_lT;4i0h~-{CKldc-D8)7gFW+hI~8$2UV*u zsW7T|3v}&Flme$j=gq6xkqdLD6DLpCRUEm77RaqFy^Oku(rC$|!Q;srUgSTLUcvD$ zjHC@{2&+*GK8H5w&c|;w%Yx`@UVvsmEwvYX3Iv*Bn@!2wKwl8_IR!(IZF9Ra#lI!k za|XU)bX}_60e-Nujez1w=Gr++`h2yQ`6??srZkSivs0+EYC^;73oPm~j2s%Og|lci zZmZIUrwsscuQIQQ-H(7XMa>Dl_!i)cBL4?)bqRKKZFL2He5IGg$y>>8|;Lo z0u}q{3F!@JrYxxmD%gztV(gJUy2tj6fhi1O&YWlFKHJ7T0ReJ=`MS~#K;*T8k=BE% zVd$jJy6X#=Po1iBC-kGf@YimF%s;r&&f1&Ycz8~>Xx|Crzz@?kAc}>~XoT>-gqp5H zvv2H@^z*{a_u1Uo2OtRdIr|A>Ju~N(eQSgg||#@_TxCrD}ylhGleRj z_F)95@L7dKijnP5lQ{;X%TCK|+2Bqu8Dq3X*-OpmuBhw4m0Q`M)Ln zp=>8A&f@-L&_2E+@FAk_k^Dk=XSVfw$4g~4NfbseKc4Bsqo=$*7220OcnP=tWTfEH qN3@432PwQd^aZ9BI}cgz+$l`Dh5?;oW_IW@Yrto*2HZCA8UFzRVUjWc literal 0 HcmV?d00001 diff --git a/clients/__pycache__/hand_client.cpython-39.pyc b/clients/__pycache__/hand_client.cpython-39.pyc index 5fa591ae9d8c7384ebb7c70516a848dba1c325cb..22c543f83d00f36e8ee9f5c13f498be9103b4585 100644 GIT binary patch delta 200 zcmeAa>J;KlJ;Klf86((P0pQmRGGJ-8RCo?s#q(~5?7^LGCYjRG0F|uh;BNuXH F008?^GSvV8 diff --git a/clients/__pycache__/hlc_client.cpython-39.pyc b/clients/__pycache__/hlc_client.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..047f6654033e1d08585a795f1f4a4bfc4225a6ac GIT binary patch literal 2026 zcmZuyOK%%D5GJ{AX=S_4qv<1skOnApsG0`tB?!`>ZkwEpA`VbsVSrgJX-mq!!rgTf zVSTc%`2|e@`@S1MbR0qtSCjf;J3r!b7npc5dEi2)pB4~4fn`s@CJ4#BdUYPvrJZXoR!cBWnMK( z@-nWc6|^@W6_cXgNoO*zzmF>ks$NoLS&{Q{avKLs(}bfY&c#x78C8{3N3#SLn%w4x zs!S5xZM`_qxDU0u2OvpA1&NrTktLY0;LNskq$})KR5-#twNGf|iBS6R1tNH5iJs`6 zI#EyfXp0UWrb&0o#TVUwcu=Te5~t}bKO836ks8P4Yz)Kpx|59)1{+@#lr5+`P^&F~ znw-&=p3|0aCdeLnO?O9@w!o5TTwQa-+F)%@lUrY7tZ1X@ zkq)x>IR`2vGJ(6eDrgqkWa9l8gq2&|)}32tc1zBj);O+L92-grk zM)(BbQ-tdXeE{v$IA;_n^um9K8Smp*?Wryxx{vf)ZmWGItFp-9LcZ^0HH101k>MBd zdvx1)*Jayui!L_b`M2AhsgGdr?x?SQ&ht2voa-LvSs|Jf<&g7dO`I-!JkCXtaIWw& z=@9ZuJxelKPYak(5zS$d49W)>K&{YIKDC)|F}gJXw!USI zQTYBgV-o0I6{I6hGyy&Wp(em{vUmc4qJmt%+Q+nJ!aad7!`}n`TXXw2@;h5< ze4}yJG=b5$bMHde(q9PB#mD=zw~}r_ItL2$cHdyo-rN8BdAo`aB`Er!E6vG=*7?JS<U jjTV?}j$!>E#Av$NSK<=mWECkLM!(I0QhtogECS*Hj2;!x delta 102 zcmeA-UuMpm$ji&c00e>S8Z+NadEFi|g2mpl& B8RY-~ diff --git a/clients/__pycache__/torque_off_client.cpython-39.pyc b/clients/__pycache__/torque_off_client.cpython-39.pyc index 0b80783d3c471891b3516ac2f524af81b99069de..16f213ddf24b7fa193f065944173a8fc444e86a3 100644 GIT binary patch delta 22 ccmeyz^^c1;k(ZZ?0SHpWc4ofV$Xmz?07%~kTL1t6 delta 22 ccmeyz^^c1;k(ZZ?0SI2nG-Te}$Xmz?089i1qyPW_ diff --git a/clients/__pycache__/torque_on_client.cpython-39.pyc b/clients/__pycache__/torque_on_client.cpython-39.pyc index 956d3479d8521337464f2b0818c6bb3c07da1cc2..37003800f7d36c5589b5eea7843942c5cca0e23d 100644 GIT binary patch delta 22 ccmZqYZs+Dra0|05ypPb^rhX delta 22 ccmZqYZs+Dra0|061?2xBvhE diff --git a/clients/base_client.py b/clients/base_client.py index 995770b..eb4b629 100644 --- a/clients/base_client.py +++ b/clients/base_client.py @@ -17,9 +17,14 @@ class RobotClientBase: self.retries = retries self.channel = None self.stub = None + self.stub_hand = None print(f"Attempting to connect to gRPC server: {self.address}") self._connect_with_retry() + def _init_stubs(self, channel): + self.stub = humanoid_robot_service_pb2_grpc.HumanoidRobotServiceStub(channel) + self.stub_hand = dexhand_service_pb2_grpc.DexHandServiceStub(channel) + def _connect_with_retry(self): attempt = 0 while attempt <= self.retries: @@ -27,8 +32,7 @@ class RobotClientBase: try: self.channel = grpc.insecure_channel(self.address) grpc.channel_ready_future(self.channel).result(timeout=self.timeout) - self.stub = humanoid_robot_service_pb2_grpc.HumanoidRobotServiceStub(self.channel) - self.stub_hand = dexhand_service_pb2_grpc.DexHandServiceStub(self.channel) + self._init_stubs(self.channel) print(f"Successfully connected to gRPC server: {self.address}") return except grpc.FutureTimeoutError: diff --git a/clients/camera_client.py b/clients/camera_client.py new file mode 100644 index 0000000..53cdb2f --- /dev/null +++ b/clients/camera_client.py @@ -0,0 +1,67 @@ +import time +from typing import Callable, Optional + +from google.protobuf.timestamp_pb2 import Timestamp + +from clients._path_setup import ensure_paths + +ensure_paths() + +from clients.base_client import RobotClientBase +from cmvr.api import camera_command_pb2 as pb +from cmvr.api import camera_service_pb2_grpc as rpc +from cmvr.api import common_pb2 + + +class CameraClient(RobotClientBase): + """Client for CameraService RGB image RPCs.""" + + def _init_stubs(self, channel): + self.stub = rpc.CameraServiceStub(channel) + + @staticmethod + def _make_header(device_id: str): + header = common_pb2.CommandHeader.Request() + header.device_id = device_id + timestamp = Timestamp() + timestamp.GetCurrentTime() + header.timestamp.CopyFrom(timestamp) + return header + + def start_camera(self, device_id: str): + request = pb.StartCameraCommand.Request(header=self._make_header(device_id)) + return self.stub.StartCamera(request, timeout=self.timeout) + + def stop_camera(self, device_id: str): + request = pb.StopCameraCommand.Request(header=self._make_header(device_id)) + return self.stub.StopCamera(request, timeout=self.timeout) + + def get_rgb_image(self, device_id: str): + request = pb.GetRGBImageCommand.Request(header=self._make_header(device_id)) + return self.stub.GetRGBImage(request, timeout=self.timeout) + + def get_rgb_image_stream( + self, + device_id: str, + stop_requested: Optional[Callable[[], bool]] = None, + request_interval: float = 0.001, + ): + should_stop = stop_requested or (lambda: False) + + def request_gen(): + while not should_stop(): + yield pb.GetRGBImageStreamCommand.Request( + header=self._make_header(device_id), + eof=False, + ) + time.sleep(request_interval) + + yield pb.GetRGBImageStreamCommand.Request( + header=self._make_header(device_id), + eof=True, + ) + + return self.stub.GetRGBImageStream(request_gen()) + + +GetRGBImageStreamClient = CameraClient diff --git a/clients/gui_camera_client..py b/clients/gui_camera_client..py deleted file mode 100644 index 6ef05b4..0000000 --- a/clients/gui_camera_client..py +++ /dev/null @@ -1,727 +0,0 @@ -# camera_player_qt.py -import sys -import cv2 -import av -import numpy as np -import grpc -import threading -import time -from datetime import datetime -from pathlib import Path -from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, - QHBoxLayout, QLabel, QPushButton, QLineEdit, - QFileDialog, QMessageBox, QStatusBar, QGroupBox, - QGridLayout, QComboBox, QSpinBox, QCheckBox, QFrame) -from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer -from PyQt5.QtGui import QImage, QPixmap - -# 导入生æˆçš„protobufæ¨¡å— -from cmvr.api import camera_service_pb2_grpc -from cmvr.api import camera_command_pb2 -from cmvr.api.common_pb2 import CommandHeader -from google.protobuf.timestamp_pb2 import Timestamp - - -class StreamThread(QThread): - """æµæŽ¥æ”¶çº¿ç¨‹""" - frame_received = pyqtSignal(object, int, int) # frame, width, height - status_update = pyqtSignal(dict) - error_occurred = pyqtSignal(str) - keyframe_received = pyqtSignal() - - # 支æŒçš„ç¼–ç æ ¼å¼ - CODECS = { - 'H.265 (HEVC)': 'hevc', - 'H.264 (AVC)': 'h264' - } - - def __init__(self, device_id, server_addr, codec='hevc', wait_for_keyframe=True): - super().__init__() - self.device_id = device_id - self.server_addr = server_addr - self.codec_name = codec - self.wait_for_keyframe = wait_for_keyframe - self.running = False - self.streaming = False - - # gRPC - self.channel = None - self.stub = None - - # è§£ç å™¨ - self.codec = None - self._init_decoder() - - # 统计 - self.frame_count = 0 - self.fps_counter = 0 - self.fps_time = time.time() - self.fps = 0 - self.keyframe_count = 0 - - def _init_decoder(self): - """åˆå§‹åŒ–è§£ç å™¨""" - try: - # æ ¹æ®é€‰æ‹©çš„ç¼–ç åˆ›å»ºè§£ç å™¨ - self.codec = av.CodecContext.create(self.codec_name, "r") - self.codec.thread_count = 1 - - # è®¾ç½®ä½Žå»¶è¿Ÿæ¨¡å¼ - if hasattr(self.codec, 'flags'): - try: - self.codec.flags |= av.codec.context.AV_CODEC_FLAG_LOW_DELAY - except: - pass - - print(f"✓ {self.codec_name.upper()}è§£ç å™¨åˆå§‹åŒ–æˆåŠŸ") - except Exception as e: - print(f"✗ è§£ç å™¨åˆå§‹åŒ–失败: {e}") - self.codec = None - - def _create_header(self): - """创建请求头""" - timestamp = Timestamp() - timestamp.GetCurrentTime() - return CommandHeader.Request( - device_id=self.device_id, - timestamp=timestamp - ) - - def connect(self): - """连接æœåС噍""" - try: - self.channel = grpc.insecure_channel(self.server_addr) - grpc.channel_ready_future(self.channel).result(timeout=3) - self.stub = camera_service_pb2_grpc.CameraServiceStub(self.channel) - return True - except Exception as e: - self.error_occurred.emit(f"连接失败: {e}") - return False - - def start_camera(self): - """å¯åŠ¨æ‘„åƒå¤´""" - try: - request = camera_command_pb2.StartCameraCommand.Request( - header=self._create_header() - ) - response = self.stub.StartCamera(request, timeout=3) - return response.header.success - except Exception as e: - self.error_occurred.emit(f"å¯åŠ¨æ‘„åƒå¤´å¤±è´¥: {e}") - return False - - def stop_camera(self): - """åœæ­¢æ‘„åƒå¤´""" - try: - request = camera_command_pb2.StopCameraCommand.Request( - header=self._create_header() - ) - self.stub.StopCamera(request, timeout=3) - except: - pass - - def run(self): - """线程主函数""" - if not self.connect(): - return - - if not self.start_camera(): - return - - self.running = True - self.streaming = True - self.frame_count = 0 - self.fps_counter = 0 - self.fps_time = time.time() - keyframe_received = False - - def request_gen(): - while self.streaming: - yield camera_command_pb2.GetRGBImageStreamCommand.Request( - header=self._create_header(), - eof=False - ) - time.sleep(0.001) - yield camera_command_pb2.GetRGBImageStreamCommand.Request( - header=self._create_header(), - eof=True - ) - - try: - responses = self.stub.GetRGBImageStream(request_gen()) - - for response in responses: - if not self.streaming: - break - - if response.header.success: - frame_data = response.color_frame.data - is_keyframe = response.color_frame.is_key_frame - - # 等待首个关键帧 - if self.wait_for_keyframe and not keyframe_received and not is_keyframe: - continue - - if is_keyframe and not keyframe_received: - keyframe_received = True - self.keyframe_count += 1 - self.keyframe_received.emit() - - # è§£ç  - if self.codec and frame_data: - packet = av.Packet(frame_data) - frames = self.codec.decode(packet) - - if frames: - # 转æ¢ä¸ºRGB (å·²ç»æ˜¯RGBæ ¼å¼) - img = frames[0].to_ndarray(format='rgb24') - - # 更新统计 - self.fps_counter += 1 - self.frame_count += 1 - - # 计算FPS - now = time.time() - if now - self.fps_time >= 1.0: - self.fps = self.fps_counter - self.fps_counter = 0 - self.fps_time = now - - # å‘é€çŠ¶æ€æ›´æ–° - self.status_update.emit({ - 'fps': self.fps, - 'frames': self.frame_count, - 'keyframes': self.keyframe_count - }) - - # å‘é€å¸§åˆ°ä¸»çº¿ç¨‹ - h, w = img.shape[:2] - self.frame_received.emit(img, w, h) - - else: - print(f"æµé”™è¯¯: {response.header.error_message}") - - except Exception as e: - self.error_occurred.emit(f"æµé”™è¯¯: {e}") - finally: - self.stop_camera() - if self.channel: - self.channel.close() - self.running = False - self.streaming = False - - def stop(self): - """åœæ­¢çº¿ç¨‹""" - self.streaming = False - self.wait(2000) # 等待最多2ç§’ - - -class CameraPlayerQt(QMainWindow): - """基于Qt的摄åƒå¤´æ‹‰æµæ’­æ”¾å™¨""" - - def __init__(self): - super().__init__() - self.setWindowTitle("æ‘„åƒå¤´æ‹‰æµæ’­æ”¾å™¨ (Qt版)") - self.setMinimumSize(1000, 800) - - # 状æ€å˜é‡ - self.connected = False - self.streaming = False - self.recording = False - self.stream_thread = None - self.video_writer = None - - # 录制目录 - self.record_path = Path("./recordings") - self.record_path.mkdir(exist_ok=True) - - # 创建UI - self._create_ui() - - # çŠ¶æ€æ  - self.status_bar = QStatusBar() - self.setStatusBar(self.status_bar) - self.status_bar.showMessage("就绪") - - # 定时器用于更新显示 - self.display_timer = QTimer() - self.display_timer.timeout.connect(self._update_display) - self.display_timer.start(30) # 约33fps - - # 当å‰å¸§ç¼“å­˜ - self.current_frame = None - self.frame_width = 0 - self.frame_height = 0 - - def _create_ui(self): - """创建UI""" - central_widget = QWidget() - self.setCentralWidget(central_widget) - main_layout = QVBoxLayout(central_widget) - - # ========== 连接设置区域 ========== - conn_group = QGroupBox("连接设置") - conn_layout = QGridLayout() - - # 第一行:设备IDå’Œç¼–ç æ ¼å¼ - conn_layout.addWidget(QLabel("设备ID:"), 0, 0) - self.device_id = QLineEdit("right_hand_cam") - conn_layout.addWidget(self.device_id, 0, 1) - - conn_layout.addWidget(QLabel("ç¼–ç æ ¼å¼:"), 0, 2) - self.codec_combo = QComboBox() - self.codec_combo.addItems(['H.265 (HEVC)', 'H.264 (AVC)']) - self.codec_combo.setCurrentIndex(0) - self.codec_combo.setMinimumWidth(120) - conn_layout.addWidget(self.codec_combo, 0, 3) - - # 等待关键帧选项 - self.wait_keyframe = QCheckBox("等待关键帧") - self.wait_keyframe.setChecked(True) - conn_layout.addWidget(self.wait_keyframe, 0, 4) - - # 第二行:æœåŠ¡å™¨åœ°å€ - conn_layout.addWidget(QLabel("æœåС噍IP:"), 1, 0) - self.server_ip = QLineEdit("192.168.0.222") - conn_layout.addWidget(self.server_ip, 1, 1) - - conn_layout.addWidget(QLabel("端å£:"), 1, 2) - self.server_port = QLineEdit("50052") - self.server_port.setMaximumWidth(80) - conn_layout.addWidget(self.server_port, 1, 3) - - # 超时设置 - conn_layout.addWidget(QLabel("è¶…æ—¶(ç§’):"), 1, 4) - self.timeout_spin = QSpinBox() - self.timeout_spin.setRange(1, 10) - self.timeout_spin.setValue(3) - self.timeout_spin.setMaximumWidth(60) - conn_layout.addWidget(self.timeout_spin, 1, 5) - - # 第三行:录制目录 - conn_layout.addWidget(QLabel("录制目录:"), 2, 0) - self.record_dir = QLineEdit(str(self.record_path)) - conn_layout.addWidget(self.record_dir, 2, 1, 1, 4) - - self.browse_btn = QPushButton("æµè§ˆ") - self.browse_btn.clicked.connect(self._browse_dir) - conn_layout.addWidget(self.browse_btn, 2, 5) - - # 第四行:控制按钮 - btn_layout = QHBoxLayout() - - self.connect_btn = QPushButton("连接") - self.connect_btn.clicked.connect(self._connect) - self.connect_btn.setMinimumWidth(80) - btn_layout.addWidget(self.connect_btn) - - self.preview_btn = QPushButton("预览") - self.preview_btn.clicked.connect(self._toggle_preview) - self.preview_btn.setEnabled(False) - self.preview_btn.setMinimumWidth(80) - btn_layout.addWidget(self.preview_btn) - - self.record_btn = QPushButton("录制") - self.record_btn.clicked.connect(self._toggle_recording) - self.record_btn.setEnabled(False) - self.record_btn.setMinimumWidth(80) - btn_layout.addWidget(self.record_btn) - - self.disconnect_btn = QPushButton("æ–­å¼€") - self.disconnect_btn.clicked.connect(self._disconnect) - self.disconnect_btn.setEnabled(False) - self.disconnect_btn.setMinimumWidth(80) - btn_layout.addWidget(self.disconnect_btn) - - btn_layout.addStretch() - - conn_layout.addLayout(btn_layout, 3, 0, 1, 6) - - conn_group.setLayout(conn_layout) - main_layout.addWidget(conn_group) - - # ========== çŠ¶æ€æ˜¾ç¤ºåŒºåŸŸ ========== - status_group = QGroupBox("状æ€ä¿¡æ¯") - status_layout = QHBoxLayout() - - self.conn_status = QLabel("◠未连接") - self.conn_status.setStyleSheet("color: red;") - status_layout.addWidget(self.conn_status) - - status_layout.addWidget(QLabel("|")) - - self.codec_status = QLabel("ç¼–ç : H.265") - status_layout.addWidget(self.codec_status) - - status_layout.addWidget(QLabel("|")) - - self.fps_status = QLabel("FPS: 0") - status_layout.addWidget(self.fps_status) - - status_layout.addWidget(QLabel("|")) - - self.frame_status = QLabel("帧数: 0") - status_layout.addWidget(self.frame_status) - - status_layout.addWidget(QLabel("|")) - - self.resolution_status = QLabel("分辨率: -") - status_layout.addWidget(self.resolution_status) - - status_layout.addWidget(QLabel("|")) - - self.keyframe_status = QLabel("关键帧: 等待") - self.keyframe_status.setStyleSheet("color: orange;") - status_layout.addWidget(self.keyframe_status) - - status_layout.addWidget(QLabel("|")) - - self.record_status = QLabel("录制: 未开始") - self.record_status.setStyleSheet("color: gray;") - status_layout.addWidget(self.record_status) - - status_layout.addStretch() - status_group.setLayout(status_layout) - main_layout.addWidget(status_group) - - # ========== 视频预览区域 ========== - video_group = QGroupBox("视频预览") - video_layout = QVBoxLayout() - - self.video_label = QLabel() - self.video_label.setAlignment(Qt.AlignCenter) - self.video_label.setMinimumSize(640, 480) - self.video_label.setStyleSheet("background-color: black; border: 1px solid gray;") - video_layout.addWidget(self.video_label) - - video_group.setLayout(video_layout) - main_layout.addWidget(video_group, 1) # 1è¡¨ç¤ºæ‹‰ä¼¸å› å­ - - # ========== æ“作说明 ========== - info_frame = QFrame() - info_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) - info_layout = QHBoxLayout(info_frame) - - info_label = QLabel("æ“作说明: â‘ é€‰æ‹©ç¼–ç æ ¼å¼ ②连接 ③预览 ④录制 (按'åœæ­¢é¢„览'结æŸ)") - info_label.setStyleSheet("color: gray; padding: 5px;") - info_layout.addWidget(info_label) - - main_layout.addWidget(info_frame) - - def _browse_dir(self): - """æµè§ˆç›®å½•""" - directory = QFileDialog.getExistingDirectory(self, "选择录制目录", - self.record_dir.text()) - if directory: - self.record_dir.setText(directory) - self.record_path = Path(directory) - self.record_path.mkdir(exist_ok=True) - - def _get_server_addr(self): - """èŽ·å–æœåŠ¡å™¨åœ°å€""" - return f"{self.server_ip.text()}:{self.server_port.text()}" - - def _get_codec(self): - """获å–ç¼–ç æ ¼å¼""" - codec_map = { - 'H.265 (HEVC)': 'hevc', - 'H.264 (AVC)': 'h264' - } - return codec_map[self.codec_combo.currentText()] - - def _connect(self): - """连接æœåС噍""" - codec = self._get_codec() - self.status_bar.showMessage(f"正在连接æœåС噍 (ç¼–ç : {codec.upper()})...") - - # æ›´æ–°ç¼–ç çŠ¶æ€æ˜¾ç¤º - self.codec_status.setText(f"ç¼–ç : {self.codec_combo.currentText()}") - - # 创建æµçº¿ç¨‹ - self.stream_thread = StreamThread( - self.device_id.text(), - self._get_server_addr(), - codec=codec, - wait_for_keyframe=self.wait_keyframe.isChecked() - ) - - # è¿žæŽ¥ä¿¡å· - self.stream_thread.frame_received.connect(self._on_frame_received) - self.stream_thread.status_update.connect(self._on_status_update) - self.stream_thread.error_occurred.connect(self._on_error) - self.stream_thread.keyframe_received.connect(self._on_keyframe_received) - - # 连接测试 - if self.stream_thread.connect(): - self.connected = True - self.conn_status.setText("◠已连接") - self.conn_status.setStyleSheet("color: green;") - self.connect_btn.setEnabled(False) - self.preview_btn.setEnabled(True) - self.disconnect_btn.setEnabled(True) - self.codec_combo.setEnabled(False) - self.wait_keyframe.setEnabled(False) - self.status_bar.showMessage("æœåŠ¡å™¨è¿žæŽ¥æˆåŠŸ", 3000) - else: - self.stream_thread = None - - def _disconnect(self): - """断开连接""" - # åœæ­¢é¢„览 - if self.streaming: - self._stop_preview() - - # åœæ­¢å½•制 - if self.recording: - self._stop_recording() - - # 清ç†çº¿ç¨‹ - if self.stream_thread: - self.stream_thread.stop() - self.stream_thread = None - - self.connected = False - self.conn_status.setText("◠未连接") - self.conn_status.setStyleSheet("color: red;") - self.connect_btn.setEnabled(True) - self.preview_btn.setEnabled(False) - self.preview_btn.setText("预览") - self.record_btn.setEnabled(False) - self.record_btn.setText("录制") - self.disconnect_btn.setEnabled(False) - self.codec_combo.setEnabled(True) - self.wait_keyframe.setEnabled(True) - - # æ¸…ç©ºç”»é¢ - self.video_label.clear() - self.current_frame = None - - # é‡ç½®çŠ¶æ€ - self.keyframe_status.setText("关键帧: 等待") - self.keyframe_status.setStyleSheet("color: orange;") - self.fps_status.setText("FPS: 0") - self.frame_status.setText("帧数: 0") - self.resolution_status.setText("分辨率: -") - - self.status_bar.showMessage("已断开连接", 3000) - - def _toggle_preview(self): - """切æ¢é¢„览""" - if not self.streaming: - self._start_preview() - else: - self._stop_preview() - - def _start_preview(self): - """开始预览""" - if not self.stream_thread: - return - - self.streaming = True - self.stream_thread.streaming = True - self.stream_thread.start() - - self.preview_btn.setText("åœæ­¢") - self.record_btn.setEnabled(True) - - if self.wait_keyframe.isChecked(): - self.keyframe_status.setText("关键帧: 等待中...") - self.keyframe_status.setStyleSheet("color: orange;") - else: - self.keyframe_status.setText("关键帧: ä¸ç­‰å¾…") - self.keyframe_status.setStyleSheet("color: gray;") - - self.status_bar.showMessage("预览已å¯åЍ...") - - def _stop_preview(self): - """åœæ­¢é¢„览""" - if self.stream_thread: - self.stream_thread.streaming = False - self.stream_thread.wait(2000) - - self.streaming = False - self.preview_btn.setText("预览") - self.record_btn.setEnabled(False) - - # åœæ­¢å½•制 - if self.recording: - self._stop_recording() - - self.keyframe_status.setText("关键帧: 等待") - self.keyframe_status.setStyleSheet("color: orange;") - - # æ¸…ç©ºç”»é¢ - self.video_label.clear() - self.current_frame = None - - self.status_bar.showMessage("é¢„è§ˆå·²åœæ­¢", 3000) - - def _toggle_recording(self): - """切æ¢å½•制""" - if not self.recording: - self._start_recording() - else: - self._stop_recording() - - def _start_recording(self): - """开始录制""" - if not self.streaming: - QMessageBox.warning(self, "警告", "请先å¯åŠ¨é¢„è§ˆ") - return - - # 更新录制目录 - self.record_path = Path(self.record_dir.text()) - self.record_path.mkdir(parents=True, exist_ok=True) - - # ç”Ÿæˆæ–‡ä»¶å - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - codec_name = self.codec_combo.currentText().replace(' ', '_') - filename = self.record_path / f"rec_{self.device_id.text()}_{codec_name}_{timestamp}.mp4" - - # 获å–ç”»é¢å°ºå¯¸ - if self.current_frame is not None: - height, width = self.current_frame.shape[:2] - else: - width, height = 640, 480 - - # 创建VideoWriter - fourcc = cv2.VideoWriter_fourcc(*'mp4v') - self.video_writer = cv2.VideoWriter(str(filename), fourcc, 30.0, (width, height)) - - if self.video_writer.isOpened(): - self.recording = True - self.record_btn.setText("åœæ­¢") - self.record_status.setText("◠录制中") - self.record_status.setStyleSheet("color: red;") - self.status_bar.showMessage(f"开始录制: {filename.name}", 3000) - else: - QMessageBox.critical(self, "错误", "无法创建视频文件") - - def _stop_recording(self): - """åœæ­¢å½•制""" - if self.video_writer: - self.video_writer.release() - self.video_writer = None - - self.recording = False - self.record_btn.setText("录制") - self.record_status.setText("录制: 未开始") - self.record_status.setStyleSheet("color: gray;") - self.status_bar.showMessage("å½•åˆ¶å·²åœæ­¢", 3000) - - def _on_frame_received(self, frame, width, height): - """收到新帧""" - self.current_frame = frame - self.frame_width = width - self.frame_height = height - - # 录制 - if self.recording and self.video_writer: - # 转æ¢ä¸ºBGR用于录制 - frame_bgr = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) - self.video_writer.write(frame_bgr) - - def _update_display(self): - """更新显示""" - if self.current_frame is not None: - # 直接使用QImage,é¿å…è½¬æ¢ - bytes_per_line = 3 * self.frame_width - qimage = QImage(self.current_frame.data, self.frame_width, - self.frame_height, bytes_per_line, QImage.Format_RGB888) - - # ç¼©æ”¾ä»¥é€‚åº”æ ‡ç­¾å¤§å° - label_size = self.video_label.size() - pixmap = QPixmap.fromImage(qimage).scaled( - label_size, Qt.KeepAspectRatio, Qt.SmoothTransformation) - - self.video_label.setPixmap(pixmap) - - # 更新分辨率显示 - self.resolution_status.setText(f"分辨率: {self.frame_width}x{self.frame_height}") - - def _on_status_update(self, stats): - """çŠ¶æ€æ›´æ–°""" - self.fps_status.setText(f"FPS: {stats['fps']}") - self.frame_status.setText(f"帧数: {stats['frames']}") - - def _on_keyframe_received(self): - """收到关键帧""" - if self.wait_keyframe.isChecked(): - self.keyframe_status.setText("关键帧: 已接收") - self.keyframe_status.setStyleSheet("color: green;") - - def _on_error(self, msg): - """错误处ç†""" - QMessageBox.critical(self, "错误", msg) - self.status_bar.showMessage(f"错误: {msg}") - - def closeEvent(self, event): - """窗å£å…³é—­äº‹ä»¶""" - # åœæ­¢é¢„览 - if self.streaming: - self._stop_preview() - - # åœæ­¢å½•制 - if self.recording: - self._stop_recording() - - # 断开连接 - if self.connected: - self._disconnect() - - event.accept() - - -def main(): - app = QApplication(sys.argv) - - # è®¾ç½®åº”ç”¨æ ·å¼ - app.setStyle('Fusion') - - # 设置全局样å¼è¡¨ - app.setStyleSheet(""" - QGroupBox { - font-weight: bold; - border: 1px solid gray; - border-radius: 5px; - margin-top: 10px; - padding-top: 10px; - } - QGroupBox::title { - subcontrol-origin: margin; - left: 10px; - padding: 0 5px 0 5px; - } - QPushButton { - padding: 5px 10px; - } - QLineEdit { - padding: 3px; - } - QComboBox { - padding: 3px; - } - """) - - window = CameraPlayerQt() - window.show() - - print("\n" + "=" * 60) - print("æ‘„åƒå¤´æ‹‰æµæ’­æ”¾å™¨ (Qt版) å¯åЍ") - print("=" * 60) - print("支æŒç¼–ç :") - print(" • H.265 (HEVC) - 高压缩率") - print(" • H.264 (AVC) - 兼容性好") - print("\næ“作步骤:") - print("1. é€‰æ‹©ç¼–ç æ ¼å¼") - print("2. 输入设备IDã€æœåС噍IP和端å£") - print("3. 点击ã€è¿žæŽ¥ã€‘按钮") - print("4. 点击ã€é¢„览】按钮开始观看") - print("5. 点击ã€å½•制】按钮开始/åœæ­¢å½•制") - print("=" * 60 + "\n") - - sys.exit(app.exec_()) - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/clients/hand_client.py b/clients/hand_client.py index 75e0951..729203f 100644 --- a/clients/hand_client.py +++ b/clients/hand_client.py @@ -104,7 +104,7 @@ if __name__ == "__main__": # ] # client.set_angles(hand_open, device_id="hand2") - client.set_angles(hand_open, device_id="hand2") + client.set_angles(hand_touch, device_id="hand2") # client.set_angles(handshake, device_id="hand1") # Close client diff --git a/clients/hlc_client.py b/clients/hlc_client.py new file mode 100644 index 0000000..65f6a51 --- /dev/null +++ b/clients/hlc_client.py @@ -0,0 +1,66 @@ +from google.protobuf.timestamp_pb2 import Timestamp + +from clients._path_setup import ensure_paths + +ensure_paths() + +from clients.base_client import RobotClientBase +from cmvr.api import common_pb2 +from cmvr.api import hlc_command_pb2 as pb +from cmvr.api import hlc_service_pb2_grpc as rpc + + +class HlcClient(RobotClientBase): + """Client for calling cmvr.api.HlcService.""" + + def _init_stubs(self, channel): + self.stub = rpc.HlcServiceStub(channel) + + @staticmethod + def _make_header(device_id: str): + header = common_pb2.CommandHeader.Request() + header.device_id = device_id + timestamp = Timestamp() + timestamp.GetCurrentTime() + header.timestamp.CopyFrom(timestamp) + return header + + def touch( + self, + u: int, + v: int, + max_force: float, + device_id="hc01", + timeout=None, + ): + request = pb.Touch.Request() + request.header.CopyFrom(self._make_header(device_id)) + request.u = u + request.v = v + request.max_force = max_force + + rpc_timeout = self.timeout if timeout is None else timeout + response = self.stub.touch(request, timeout=rpc_timeout) + return response + + +def main(): + client = HlcClient() + try: + response = client.touch( + u=12, + v=34, + max_force=10.0, + device_id="hc01", + ) + print("touch RPC call completed") + print(f"success: {response.header.success}") + print(f"error_message: {response.header.error_message}") + print(f"timestamp: {response.header.timestamp.seconds}") + return response + finally: + client.close() + + +if __name__ == "__main__": + main() diff --git a/clients/joint_states.csv b/clients/joint_states.csv deleted file mode 100644 index 600a5be..0000000 --- a/clients/joint_states.csv +++ /dev/null @@ -1,51 +0,0 @@ -R_SHOULDER_P,R_SHOULDER_R,R_SHOULDER_Y,R_ELBOW_R,R_WRIST_P,R_WRIST_Y,R_WRIST_R -42.3871312049,94.4262521308,-69.8990748368,-35.2088867644,-16.7676098745,-5.0042133827,1.9505775177 -38.4358423623,74.7161029078,-69.9082421615,-35.1792075506,-16.7656045222,16.3947289414,-3.6198327581 -74.883406584,73.4828685496,-69.8867562442,-5.0052447067,-15.4131949426,15.065925223,14.4940560476 -61.7429060315,50.3894035464,-69.9068670628,-20.3281415008,-15.3997877302,35.9769366887,1.1899187489 -59.4242031304,67.0953058663,-69.8938609209,-21.9081872124,-15.423221704,19.7719713691,4.0812929663 -90.7017399835,66.3198647864,-69.8943765829,7.3872594442,-15.469745877,18.6678816978,20.4932679373 -43.3590395128,62.9293297379,-73.6622043394,-35.8108362239,-15.1957001636,20.2325721406,0 -40.5923409116,88.5308028978,-73.6077160531,-31.2608064855,-11.9376329573,-4.7227192179,-12.7598273933 -23.8609419698,84.6395600321,-73.65269324,-33.7536312605,-15.2801541426,-3.2523949241,-33.0631025258 -17.1967552631,63.8605007466,-76.6419286488,-33.6005942334,-26.570975045,1.7080444831,-40.4801557753 -112.3846401909,69.7356099778,-80.6124115775,39.6581332267,-8.4428768859,-0.4859828018,9.1025231955 --5.7770761525,76.506481426,-80.5607307844,-61.0971825964,0.409550232,5.9197426435,-25.0002685454 --8.8597036819,77.9207131517,-80.5670906159,-33.145436561,0.2532473454,5.9258159961,-61.5692425238 --36.6110290806,76.1685509185,-80.3678158947,-72.2165745265,11.1447166646,-0.0391903132,-52.2577616205 --35.3651323551,36.5073237197,-79.0065827651,-73.8037822106,52.7478124227,12.1613156805,-32.3100640957 --5.3251270437,44.1961881472,-81.8030751716,-71.2213468364,52.7241492657,10.7124200082,-8.0924558984 -2.8886240199,76.6163747311,-84.1251585237,-69.7672372481,74.4535163503,7.928589969,-1.7550270223 -10.4090961515,84.9616196088,-84.1484779059,-60.456730373,-18.5233817419,-21.8610327859,0 -54.5989244672,82.6571833568,-84.1363312007,-15.5816445344,-18.5294550945,-10.4057729963,-7.731091417 -81.1793533158,80.2358637145,-84.1327788623,11.5328382751,-18.4099360985,-10.3396536667,-3.3667000042 -38.2612048264,74.9317069261,-84.1652082735,-34.3789573981,-32.3599687196,0,0 -45.9865093697,73.6787628197,-42.7689502796,-25.1068959911,21.0002209945,17.5849341693,12.4229855056 -22.7002440684,64.4069878916,-62.7950857265,-31.455039178,7.9418825899,33.6965073683,-22.0264457013 --7.8064353671,69.4039247102,-64.5624886372,-46.9387079294,5.7706590252,20.9282574954,-42.2467565451 --2.1254442368,74.2813998286,-71.1825002979,-13.416838097,-4.5865844458,22.1780503339,-61.8393921242 -5.4559906041,77.0098821448,-71.1713849167,-48.8421310206,0.1305770815,11.0492046002,-28.0768227221 -8.1414437899,69.7912441798,-109.3110080989,-62.2787361616,26.0631052554,-26.4386790901,-18.1101072843 -8.1236820983,39.0906758264,-91.0631617607,-54.527763109,26.2233042549,12.5954458019,-19.292806765 -11.0263435842,28.5577634954,-91.6200194418,-56.0312616592,24.2097586755,5.6390506197,0 -36.628332406,62.8089513052,-98.8949218623,-43.4181687572,24.2570276936,2.887306217,-1.8566697351 -24.7188316764,87.9824822878,-99.1789943371,-41.2523309958,-8.5307113159,-24.3634832519,-10.7646737591 -72.4000929083,79.991210736,-99.1548155182,0.9364995161,-8.3708560911,-10.9260186742,-2.1646918458 -23.3924916765,86.2120999966,-73.4446522646,-46.4581109308,6.0181194969,8.9699407617,-2.1167925741 -30.0970719078,53.0039818529,-97.8485290411,-46.4597152127,5.9310872079,8.2670934344,-8.9634663386 -29.1964395496,32.4169207245,-113.7501132082,-48.2284359262,0.5494665255,3.410932346,-18.0847825497 -47.142795496,53.1601701478,-116.6299136781,-33.5294328753,-21.6354593019,-0.8102196181,-14.3094490461 -52.5930565222,86.0146587404,-67.4882403222,-27.2043225917,-5.4967279034,-0.6699022541,-2.348095636 -5.689127131,79.3010830718,-87.5470343635,-64.8126993063,-6.6456228742,-13.3742673328,-10.9887575528 --36.490879831,85.2079914607,-76.8147327198,-59.0912000599,3.7261737249,-13.9641273829,-40.7579257144 --43.7821688445,66.2628554858,-75.3971778389,-55.7114939138,2.5531572309,-13.9655024816,-48.4137304772 --18.0355654751,62.0254315203,-77.0001418623,-56.2921293434,-15.9961794991,-23.3165747686,-20.1152876799 --25.9848392206,67.5677668642,-69.5084322121,-71.3935206538,20.9554156949,11.0399799797,-15.5277865016 --34.3248128865,59.3506353495,-64.9300983585,-78.7725868015,26.2126472399,7.4840320158,-12.2919500578 --26.2237053253,93.5622317757,-72.0755123174,-77.1128999564,26.2140796344,12.1362201291,-17.8720433204 -43.6226573943,57.1218295265,-87.7357093655,-23.230974874,23.3228773044,17.0774654501,-0.0433729051 -20.543000674,69.8960954563,-85.5268997694,-25.9198658066,77.8772256551,22.5696096911,-8.9078321367 --0.940166446,22.5041779109,-94.648101389,-58.6892701666,70.7468231905,20.9154232408,-8.9639247048 -0.1180293058,33.0162345782,-92.8632805614,-67.671758704,61.8440903782,13.0295759233,0 -41.6477291703,62.5570217627,-96.0662292278,-22.0864916783,52.5155926283,11.0115039773,-3.3080864217 --9.3205909323,58.9835412902,-86.0922372259,-65.5880830905,52.532036517,11.5678459964,-14.0276111066 diff --git a/clients/press_client.py b/clients/press_client.py deleted file mode 100644 index 87bf02e..0000000 --- a/clients/press_client.py +++ /dev/null @@ -1,54 +0,0 @@ -from clients._path_setup import ensure_paths - -ensure_paths() - -from google.protobuf import timestamp_pb2 - -from clients.base_client import RobotClientBase -from cmvr.api import hlc_command_pb2 as pb - - -class TouchClient(RobotClientBase): - """Client to send Touch commands""" - - def send(self, u: int, v: int, max_force: int, device_id="hc01"): - """ - u, v: target coordinates - max_force: maximum touch force - device_id: device ID - """ - # Construct request - req = pb.Touch.Request() - req.header.device_id = device_id - ts = timestamp_pb2.Timestamp() - ts.GetCurrentTime() - req.header.timestamp.CopyFrom(ts) - - req.u = u - req.v = v - req.max_force = max_force - - # Call RPC - try: - resp = self.stub.touch(req, timeout=self.timeout) - success = getattr(resp.header, "success", None) - error_msg = getattr(resp.header, "error_message", "") - timestamp_sec = getattr(resp.header.timestamp, "seconds", 0) - - print("Touch RPC call succeeded") - print(f"Success: {success}") - print(f"Error message: {error_msg}") - print(f"Timestamp: {timestamp_sec}") - except Exception as e: - print("Touch RPC call failed:", e) - - -if __name__ == "__main__": - # Initialize client - client = TouchClient() - - # Send Touch command - client.send(u=12, v=13, max_force=1300, device_id="hc01") - - # Close client - client.close() diff --git a/generated/cmvr/__pycache__/__init__.cpython-39.pyc b/generated/cmvr/__pycache__/__init__.cpython-39.pyc index b1b4d0bb73b89e69575f36a3b12912b2f5885cb5..d3c793c55f89d82f8118246d75bf2a408a3d37c9 100644 GIT binary patch delta 19 ZcmbQrIF*q*k(ZZ?0SHpWc24B(0sth;1Qq}Q delta 19 ZcmbQrIF*q*k(ZZ?0SKNmHcaI10stnF1WNz_ diff --git a/generated/cmvr/api/__pycache__/__init__.cpython-39.pyc b/generated/cmvr/api/__pycache__/__init__.cpython-39.pyc index a78f275564e1ff525566ac1806877b25327ec772..3b26f31a62748fc502ca16bed6c3c8f43713d01b 100644 GIT binary patch delta 19 ZcmbQqIFpe(k(ZZ?0SHpWc24B(0{|sp1R?+c delta 19 ZcmbQqIFpe(k(ZZ?0SKNmHcaI10{|x_1Xln6 diff --git a/generated/cmvr/api/__pycache__/camera_command_pb2.cpython-39.pyc b/generated/cmvr/api/__pycache__/camera_command_pb2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7e87604f3110e00c4603ac606d93d8e2a6d19da GIT binary patch literal 6292 zcmd5=OKcm*8Q$faLrS7nk5JTW%{Z-{2W?AjnmTS8ilk)4kt~yx+HBQsmz<%zHpyjn zS59Q~)}p<%K`-sCfP84tAUX8b2YM)SFWOsyy#^>MR5 zN0cbX5x_Ae&T$lQLP>HQYm-h&Ns9uD?+>x~z2SQ!G)@yVNeN9oiUFV8&pZ;8QSg^; zOPw)g9JnZQ`xA8Nk*G|vXTB>aS(e_PVv{WUC)L^C%0n{E0h;Ez84t;H z7SJ5$<~<}+8PEmJ&Y>+LnO+5SjdKega%~qmhqj1h`UOB=40MQO`Z91|;@m49a&5oN zIkZJ2)9Zj<4RnZPdIPwxaPBn^xwc>B9NHq1>Fa=sfew*Omw+pAuIwS1Rsbz?ZpB0H zd~b3N=Zi?DtAO6%+$|5eao^+|#zo}Xu5k`+5y^BN&~46bcu1yq0983x^N>vIfHngi zBAIRh_chLa-9xVJTY)VinJUoxjX;M;rrW@MlXKtlkZY@Q4&x${>9+xCfew*OM{2_5 z#>4p))@7z6>se7ZOY7p7`Q!{$BPITM|e2f>^v~9Xg&Q( zQ|rKp)TYttXkA+1!va%+gwa)djRouHDM28Qw~!L(umBEo6Mfg*h3hi^7m*~FO$b(o z@kwbC!Et6qS-g~%hDh`so!WP^A!tUWVG_H`^qsqQHq6^Ekchq4W7!C_h8JcFeyBp= z&qZ|*p)X8jGaChUQW_^|-BRzfJ++0wvoT;3`CHytxJ80QHeV@S%Lr1EL^rR!3{aF@ zaxoL~$;C_<;WF=!aJaYt(rA8*WJ)}Vt6kgFyO!RxBtk;Xclqo>9Pja|aol>BPs_!7 zd{QAvBC)2yS}hBP9ljJ=3T4FnZ$*-F9oKBwGSVa&hNczImSN|AB$CmZZS>q+{{P2w011Mnt*$Jsc3^Xy$AEq+o+U%lC6a=J)G)nU91oGy z!dQQKgJVJ}5TXY+DZeXH3%S1V=xv<;L?q)d*D~wbcb}r!QXJ3u&-N!FK3iDXGM}t< z9&7uDC$+Y)3kLIokBEUK|_jShN{~IdaK*ik23bk6fvstOt_3xqb`>32hMY&w5l$VO7Tly!c8dD)q)r;we z+k>i!GJQgiA4ZuusWb5W5f0)z=tKhD?G1NEzV~DVI&#N50_Pa{h>;rjPNVf0&JEwJ z)`}|?-|yeCXZnO5-?6TuV&)`8;Qk*dnLtUovQfWzD8R>2=$k#UF9@-Q8oo%!?E7>0 zD~fm!eiP#)YW^(#3E!Y@kYjy8kXKQ|gB;fcGx-;a4y{uaQdKRluCMs9-jBmN&B8^w zIJllibwS+wD1Gj+(jfeYs2yC%qh9qTsM0CV4y^T2g&&uX&fgHnL_x#~`kNYD-CDg` zDX#gmy_1C5YACvJs>lzi{}Oci2Pn>;E)GK1!L_^f`m268cmoQSFxf-<>X0Ic{Co6u z@o~x^EJLUMjxY1QT07)a{~44d+_3$fa!3(`{UiF~VNX-~VJ}~B;;KrGrm8w4JBG2- zW^U~QDl{hC`GS*D`;{0Ijsd~R3^2SlW5Rug^pX0E33mq4M`|=q_DGxpe4a3~tF=um z**FQ+tKc|uTky>fB@?!Rbt4#2)hz&Rx%D3>RyI1C-gROdn@ex5mYjH*wY1%~4O4=c zDs4DHg~o*33C;{YDqyy@3Pl_Kx9L`V@LA!E7AdVU(uu(cGThA7)s6b?suSO7 z8x5^(!2o?Uw}iE(HMe3=?9xhiw^I(PSeE1V*Nxr)2{O;XawK)LR#8i8KT6FRQQHR8 z2PhObY$$SB&Tva-ZEC5FJ+%uDIA=_?f1LlXGq)0^e% z+n9{)t1rD{nD;HM*JRr}X0NF>+dAvo+dF>I&XqyFwvRtv3%xxjaie3<-8Op-7ti_# kE)g*u{32pZ4EeuLBP0<{hlfKk=pPmn(3gQ9dFp=u20h`}M*si- literal 0 HcmV?d00001 diff --git a/generated/cmvr/api/__pycache__/camera_service_pb2_grpc.cpython-39.pyc b/generated/cmvr/api/__pycache__/camera_service_pb2_grpc.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dfa98bfd74e27039610ad4e3a14a00792b0df420 GIT binary patch literal 9684 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 TrX{nVYM&=FQ?E{~>6iZvI?0Gl literal 0 HcmV?d00001 diff --git a/generated/cmvr/api/__pycache__/common_pb2.cpython-39.pyc b/generated/cmvr/api/__pycache__/common_pb2.cpython-39.pyc index 33fbda6cfd9226849c3c5ef3b13d3f546a140982..ce4d60530b442f8e81a1398b03bff07e5f0ce2cf 100644 GIT binary patch delta 953 zcmZuu%WB&|6qV#ea%|ah9=7w6<3%?i;6-eIY)DuGka zU@QVROS!=k&J0;2PYUlCqe#TTGRc$TkON&DtdKm;LYIIpak=`54a-;}d9;Req=@HN z7_^QTDCz-}@gl&bdy*m8U?GkiFg3$K8O*WRo*RJI|2%C{kJ?!V#+Uv%y**SlUkcdvqq*|96R68tM;iEy98$GYUC|Eo z9Zfk?4z$s4dd5eptv=MyKNka2j4V=)>h22;YullZOdokZQ zsT~w_B^Fe2VRoG;7U)oftsvbm^Ur#5O4V9QQ*Rs}HDjUe_?GE}Fz61IYV@t@Bn-_q zsd`xRx~@vX^T6&zeh|jD`mx`cBEdHKA&eK$NlQZtyvp5_RxBaMH!;D;@AN+iZ}#ck i!WxtrR=nRcEYEUx``1h=nGh3Mjt4!g5w7{@(M8YhmOmnKd6x?L71L@Vuq^MVMD(~W4mCF!oT7GmUNo>b1-aTvQR zwU_V#IDm59LyHg>#EB1p#K)K`2ebzyZk$+n#&yc>3XlBvZ=N^LGh=^W{-<)+HBDW? z>v!^%_p+iWzsbp~mw}Uy@s57@_U`>T33R7O6ewOASBhgY!Ze=*C8vzG6=}Pyu>2Kq zs!*M*vciVq)DqH>>QJAou_96j zI6Gj@FK@xl6~ULu5Iqf%TuiyK3{N7A)Y`nU){&~uvsi8yOsuw>R7+@b`xSoc<}GGg zV;wW+*DJY`q~1wFGLyI2>QnU_!oOqKD}9I6p+?_jYp8py0htc5wroi18*zFl#49WeP7C;oar_dro=P>qR6fb@x z8$bU==IUIOJMhdK!GSl1(fP;+VOn7jxKs83Tn1cdefaVWqFCg+0Bq=vo{FLsPRHKq zvCG}SDcodU)JDNGe$SnG_eNMEoYL9wgO)1vjy zsT+V(p&k2i&$17CUE6wM9oSBZS`QBo+ee)TcDrNuMSY4p2JDTz#_LhEZ-4g49-KJM zH|$;eeY^8X+xpZo&~w!7?H{-M?L)B&)3f09nv0*Z+jFsfbLPi9jKfjrA6_5%39egc z>4fLy9+dlV>fz$}(po?(p2YGX7#*7E$nl7as!d4;Vj0jj6{`2z&+RZF0jQY zUi^{sqb#OH6O=45mLrqT7@1O5C;ttDs6>64U(r=Vz2=;jK6B{#GhfqXcIvy1%aQ*d F{{ncDrE-8MoJ~Zso+m zNs1UkdEkLJe)%70Ux5h;5D5MSUf2Ud^6Q?;0~$#1FbRosuAP!f#LD&Oe9!rwbK~6P z$53>}3)KXW*8_zuJAL$OkDCvATI-5W!kbwu6gCe zjO2P>Iq@P1pOk;-zpvLW^S?+k+BS2R3MUI@|^2<^X|U%_`} zKTQ=E=+{7}_f`{OqlKc}t9_G$E&PmzDi3?hLuhyS8IisPq@?hX{P(r?Og#LBDpUWP zQ)}YpmMF>eTkQ0h7lJek)%G9!RFM9HG@QOWFgtBW66M5*+JD}t*|36kGr@N=d#1T( z=!;FuY@0is2aB}P)~$wSSa=GZu<9L3C%3H`lxS_e>Q&a<^Scel7uq?Yl3CX%+^buw zYvrQDmGlQ%$7nm8+IqLHk{u_!TR*TCwI;QO@CZ&~Xn&cQ?>do^C>3kvs$8i#+@4|X zXhsW0_b9iDKa6!a+uEL9Xb`c{sh9NDu0@+|(`q?xUN)P3B9wWCmNTYqNupYW!`Pc5 zIdRo6HKG&MY}!{do1r<_k~L?4l)1lxAH4NBEQ9{&$u;N?`3HK}v%yF>5Dg3ugdjic T4?}JQF592IZpX8i;2r%J(q_@d literal 6883 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 MdijWZrT^9EKT6|Pm;e9( diff --git a/generated/cmvr/api/__pycache__/dexhand_service_pb2_grpc.cpython-39.pyc b/generated/cmvr/api/__pycache__/dexhand_service_pb2_grpc.cpython-39.pyc index 3d840e8c08e568e3d01065984d9f474a3096f672..3b4d603918da096c00fa62c4550cd3b5d860b3a3 100644 GIT binary patch literal 7887 zcmd5>-H#j96`vVC3o7)biqvPM@^@5yxawO4QlBdIrP2yYf9HC9;AbzRj)-9VjaCR)k* zjIOa{J;n7#x^A+WdZxDwT&kIE2m96XC+7TVs>NxH@&JMS{b`Uzv zCYJF6%L%Q)JJzm#viFL0ts7b)y~5jEI3Z@aKBJc`xAm?#?R31;LsEz>P)VfNv_iFW z+hV-K+bppBb}yrr_57D#l6tjw(pqnFC*W4Mvm+dQ@wVR;5n(N#b(@?7L0_R4dgm-( zSj@lK9-kYhs2h;k_GeKZVPI4LP!~sX;FuSQ&l>&w!J2=y0q%> zp>_&o1tm!8n%37Fu=r4K7>z_Dsp^?Vis<~PL@PfoG=I+*6+1vk70Pb4|s4t#OZ z37vaam$0+E9_IhaP0)&B%wyGCnp`AhgC;B3CEV z80^k5U-)x=5ovEACm7p{_0ubw$|(--dqPRHd}$ranKI`yuO zPLF6B^nP`yb8NH8Q$w1z?X|towjU)g`Evj*mDCO0#LxKBoO$r=-m#&j?Ros_kct{k z1d!BrTHLl}*0!S)3hlgY-{?Bc0VZw3nqAu#RS>KsGX_r47U-=7Il`*v3b-vwg6{alI00zBc6pw zV>vegFnp*@Wd$X80-$evtihM#tolUPsh4CrwZa*uQ>?BbVa#OtL@{QY;~HPPE5`otKCgWGvFay0OK-A#v`+lvtFT*!W*^ASJq!! zy|P-{TD>T9G?nqt@tT1w)%@`C-nUkT@WtJPcp5~=^uWu*W_>JlE1XoxGG=X{1f&}+ zQPi_~&)Q!;RoWBk{P^xd0(~TH@dJRGc#fJc5}^GQ0c9{`EN`Pk)ESDpCk~))%q{rA zcc)PNh)6k*vg5$o6O^5}FJ;P+9y)Cn=;Gg+HszuZowmn8*E`>uHgzf-I&HMWZ%okk z^nG?cDt8I$6qU6LzPQ;pAMC&YKrl1p9lnFdUfbw6lL_itQF={DGdW2dQ z0gAmMn|h2|%CNpiEoEI#q9v_ZJh>kM$_SsLxsS$43v3k=dhU=`dDLNSG)TM*mH>DBC*@-pBwg>GD`2;PrzrV9XpAtoU`VM9WR{=C5 zsV6BW{VIuw^Z~T~mUNDt- zS(=%umqKreWms1A(&!y!IhI#F6TOF6ffZFRgWj^bTV#@(*qGUxiO4mLPlvB~012N}z@FJP$- zyDifpNbD^J$@j&QM7z03>WXmuyX&?GHl^%Sp){t>GxoUqY(U17@oZ!!|IfqmSKvuo_qid zM{)h3FiZ~jo+hmh^Jy8TC5nTla9b4V{B&xjC*4*o zoIjlNkRtefbo_l{QXPU6dhN$PUSJlfK{>4-M&)dOh`GZA3`p^U3uzO=EvVqaRV zt!`YvrJVid>c-~f*K0CokN$YH>v=V4fU1{A7+e6_LATTK1+M?k<5N+e4Wh9Jv;ZDdG^1587id! literal 6966 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 delta 822 zcmZ8eJ#W)M81~t2?AXpnlQf~B(6myAhzeL35KBcHAV~cnb`~!uC-=4_#Bn$~NM$KM z0pztqA~7Ns1~$a+xRt4hfnNaJx$cL!SAOn!pZCu1-E)79kLISA%NY_M?dOjN@3NBg zLnPOS#gi@mg$?v!^U(?N4c9~xm@{iGxjBM}Hp=@2x5%{xq0JVFK1Xf|O2abAE=z6& zD#ImWa8^y2$yp7uvl}D}+Hm<0CEQ4Pl~^QqD$kWfHB+^Ap`Jj6)BLGASKK<}W;dZe zN4#~yhAY?jt%NJX+obSHevjx1DMAUal0~TC1}Sk|BW0*^Ug5Y#mY|mCHO`B+)U3W; zeF|fsUIe5up+OY%gHa>CgC3MMAHvW(f+k`Hg~Ji_qu58wU%nw3)!Bj*O#3gH)`8bk z2qUIfNFe;8=LC=ljIO=Dg8T+jwcY}-Z36~KDZg|?02`HvC5d=5y*el z{CO33S}(dS+i?q*xqYy|XSdkOrMCyEwY%q@I=lt$XMH93Q>;vYqi*n0o~ diff --git a/generated/cmvr/api/__pycache__/hlc_service_pb2_grpc.cpython-39.pyc b/generated/cmvr/api/__pycache__/hlc_service_pb2_grpc.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b8251843107932a5e2defa59524f720d5f7ce249 GIT binary patch literal 3108 zcmbVOPj4Kz73XkfXJ`LNT1$@Px~(%UdRf3;IYr?XMiSUiR*r*uYe;H~E`$=JAtlXb zcDNxqQMA}xYTt@{fffdwW4}T_NDsXB)N3yVjHK>+oYiV&Czo=-H~cqzZ{GX;9+|AH zGzpYHxBs3!SS942*jX+SbRIy}{{X?skZ_OF+#7oCO@|cTe(sNhVa+2v7>2?d)rS$U z4I9&&Fc;>{ackJ}h-isOgwN=(@q1Ei21K;a|L75XczxLT11acTA{y#1VueQ{`YYj$ z7d~%p5#ACZ^x7|cXs=-V1%=kCTjNHbd4KXA5x&Md&k65{)o1>24f@ykI`r3Fzr#29 z_2*=G4aVQ$n=roV#@9LBC*7@Se<#mGVdA9Vu@>rcq2e@8v=%x}rBXsqWWlrIBsMah zC~4$Td3=Y9!29%o7$ z1}bzW3zlh?%QK-4rrm>65ud0@8c&k+)8s_NS)omm=b$3fabn{6H}Na)_H;KsDot$g z6Hy427+94`jvvM8_;a1AhmsLo*oe-kY4qTuem4bu5c%!>P>VhaXt4H@zJ{ z%|)U`Tux3@0=u2dQdLHcB`8nq|EjEAEfA} zq7eE0_@Q$|Z>Q?_7K>ip+{TdgAY{GE zU)(mbOivHGzFk-1vr+(PAeLE@XH%iuQMf1GHrwsoQ;J!m3+{RY^y91 zbDHwy5iPxo)dN5hd|D)9!I*6_Rv{bOZN@$;lYBl? zXW$#~i$YX7>zBk9jv;D(1XTwhA}^qBDCj6h`5kIiSZ;CNDje&GU$dj(!u}+xUd1n!#4MU zL#5uqk(J*L9zS{d>;9Ae;GqAJZQ)WbOp@i=ZVaS(e7RR&DXF@?x(@c)`W(0b0||=0 zFmHNr16FP0pP(xVUG+vcdfs{hEI1&%Bq-nSbmY z4((Po4o4b+gcS^&X~2c+7X-e-)r|#(F9|%|_>N4_Z7YPhy|u(*hMcJg&LE%gT&T(s zE3i_*Nlw%niggs%P;@}pcdve;nd4iQUhH9SB%Ch>QOA69ZExYsTiInX-whB#?yONZ z#1>?!jkm4TcI^$FOR>YS*%%eQ4FYDQJIsW-2dgxOnb6P+kni6HuY-EvfuwCu-G!MH zrV0ukr9X&}CTTK143E%}f1`I?l$yZt5;eumKiqqf;Ffe*q0d z&WO(7009)efu0$i`Lo(An1!?YESfdkak5&W1n*b}T~D>(-ECB{Zi_D_kaFEomMctd z>W#>v>MF7r<8D*o*=0kMs1spqT~6=>1b)slFiYV`0J=jK*S3?wz2eG91eXnwmP*(b z9GVk=CSFhM>RAFA_E;*2e8SId-GE85G&TYtB|HIbj&GNwLpFX4RmUJmD};oMk4wt) zb~OU>-_y>epDU7gqsrBT-2s$t_+5;*ZMYYiEUmEo{rrCq4Cq63Si$^EeRtukuAM0V zqi{`kqe>)>WYJlo3OL`ijV`FD@QUEvZ#N(9J>6lC`h))7Lr8_}cm2Kn$G;ia7F*ot z;2d#_E~YjYGmv(fE+-SIAn5`PfE+zmnk>&uCbYt|Zd;H`Vcw-t+ukc7U}I7BLU}Y2 zsd0yl!c69X?*3pY2#ZMA=>6(s`w-37_dqzV&^8S`;0>i-+gpX&gg%ALOJ7sk{4e#R SZ)wo{hQ1COold72t^E%zwnZrb literal 0 HcmV?d00001 diff --git a/generated/cmvr/api/__pycache__/humanoid_robot_command_pb2.cpython-39.pyc b/generated/cmvr/api/__pycache__/humanoid_robot_command_pb2.cpython-39.pyc index 1856ba58addf754b67a7a124eea38075a0969733..dd8c021661f8fde43073de1a9ef1866cb358320d 100644 GIT binary patch literal 5687 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 Qlg84MeZ+52`hI@@17psghX4Qo diff --git a/generated/cmvr/api/__pycache__/humanoid_robot_service_pb2_grpc.cpython-39.pyc b/generated/cmvr/api/__pycache__/humanoid_robot_service_pb2_grpc.cpython-39.pyc index 1caa4b6e884ab575004757694283811fca78c3af..f05c297ec500790c9355738d3df282c501586c2d 100644 GIT binary patch literal 10228 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 Y+s|mSfQ}C`JENqRQe*Fq{n&i_e|bg#jQ{`u diff --git a/generated/cmvr/api/biohead_command_pb2.py b/generated/cmvr/api/biohead_command_pb2.py index 0c44d0b..2b1f7f3 100644 --- a/generated/cmvr/api/biohead_command_pb2.py +++ b/generated/cmvr/api/biohead_command_pb2.py @@ -25,7 +25,7 @@ _sym_db = _symbol_database.Default() from cmvr.api import common_pb2 as cmvr_dot_api_dot_common__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1e\x63mvr/api/biohead_command.proto\x12\x08\x63mvr.api\x1a\x15\x63mvr/api/common.proto\"\xdd\x08\n\x10\x46\x61\x63ialExpression\x12\x33\n\x07\x65yebrow\x18\x03 \x01(\x0b\x32\".cmvr.api.FacialExpression.Eyebrow\x12\x31\n\x06\x65yelid\x18\x04 \x01(\x0b\x32!.cmvr.api.FacialExpression.Eyelid\x12\x33\n\x07\x65yeball\x18\x05 \x01(\x0b\x32\".cmvr.api.FacialExpression.Eyeball\x12-\n\x04nose\x18\x06 \x01(\x0b\x32\x1f.cmvr.api.FacialExpression.Nose\x12/\n\x05mouth\x18\x07 \x01(\x0b\x32 .cmvr.api.FacialExpression.Mouth\x12+\n\x03jaw\x18\x08 \x01(\x0b\x32\x1e.cmvr.api.FacialExpression.Jaw\x1ai\n\x07\x45yebrow\x12\x16\n\x0eleft_outside_y\x18\x01 \x01(\x02\x12\x15\n\rleft_inside_y\x18\x02 \x01(\x02\x12\x17\n\x0fright_outside_y\x18\x03 \x01(\x02\x12\x16\n\x0eright_inside_y\x18\x04 \x01(\x02\x1a\x62\n\x06\x45yelid\x12\x14\n\x0cleft_upper_y\x18\x01 \x01(\x02\x12\x14\n\x0cleft_lower_y\x18\x02 \x01(\x02\x12\x15\n\rright_upper_y\x18\x03 \x01(\x02\x12\x15\n\rright_lower_y\x18\x04 \x01(\x02\x1aK\n\x07\x45yeball\x12\x0e\n\x06left_x\x18\x01 \x01(\x02\x12\x0e\n\x06left_y\x18\x02 \x01(\x02\x12\x0f\n\x07right_x\x18\x03 \x01(\x02\x12\x0f\n\x07right_y\x18\x04 \x01(\x02\x1a\'\n\x04Nose\x12\x0e\n\x06left_y\x18\x01 \x01(\x02\x12\x0f\n\x07right_y\x18\x02 \x01(\x02\x1a\xbc\x03\n\x05Mouth\x12\x13\n\x0bupper_lip_y\x18\x01 \x01(\x02\x12\x13\n\x0bupper_lip_z\x18\x02 \x01(\x02\x12\x13\n\x0blower_lip_y\x18\x03 \x01(\x02\x12\x13\n\x0blower_lip_z\x18\x04 \x01(\x02\x12:\n\x08left_lip\x18\x05 \x01(\x0b\x32(.cmvr.api.FacialExpression.Mouth.LeftLip\x12<\n\tright_lip\x18\x06 \x01(\x0b\x32).cmvr.api.FacialExpression.Mouth.RightLip\x1aq\n\x07LeftLip\x12\x0f\n\x07upper_x\x18\x01 \x01(\x02\x12\x0f\n\x07upper_y\x18\x02 \x01(\x02\x12\x10\n\x08\x63orner_x\x18\x03 \x01(\x02\x12\x10\n\x08\x63orner_y\x18\x04 \x01(\x02\x12\x0f\n\x07lower_x\x18\x05 \x01(\x02\x12\x0f\n\x07lower_y\x18\x06 \x01(\x02\x1ar\n\x08RightLip\x12\x0f\n\x07upper_x\x18\x01 \x01(\x02\x12\x0f\n\x07upper_y\x18\x02 \x01(\x02\x12\x10\n\x08\x63orner_x\x18\x03 \x01(\x02\x12\x10\n\x08\x63orner_y\x18\x04 \x01(\x02\x12\x0f\n\x07lower_x\x18\x05 \x01(\x02\x12\x0f\n\x07lower_y\x18\x06 \x01(\x02\x1a\x1b\n\x03Jaw\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\"\xf0\x01\n\x13SetFacialExpression\x1aj\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12.\n\nexpression\x18\x02 \x01(\x0b\x32\x1a.cmvr.api.FacialExpression\x1am\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\x12\x14\n\x0c\x65xecution_id\x18\x02 \x01(\t\x12\x19\n\x11\x65xecution_time_ms\x18\x03 \x01(\x02\"\xf8\x01\n\x16StreamFacialExpression\x1aq\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12(\n\x04\x65xpr\x18\x02 \x01(\x0b\x32\x1a.cmvr.api.FacialExpression\x12\x0b\n\x03\x65of\x18\x03 \x01(\x08\x1ak\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\x12-\n\texpr_diff\x18\x02 \x01(\x0b\x32\x1a.cmvr.api.FacialExpression\"\x84\x02\n\tGetStatus\x1a:\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x1a\xba\x01\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\x12\x11\n\tis_moving\x18\x02 \x01(\x08\x12\x17\n\x0flast_request_id\x18\x03 \x01(\t\x12\x19\n\x11\x63urrent_positions\x18\x04 \x03(\x02\x12\x18\n\x10\x63\x61mera_recording\x18\x05 \x01(\x08\x12\x1b\n\x13\x61\x63tive_recording_id\x18\x06 \x01(\t\"\xa4\x01\n\rEmergencyStop\x1a:\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x1aW\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\x12\x19\n\x11stopped_processes\x18\x02 \x01(\tb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1e\x63mvr/api/biohead_command.proto\x12\x08\x63mvr.api\x1a\x15\x63mvr/api/common.proto\"\xdd\x08\n\x10\x46\x61\x63ialExpression\x12\x33\n\x07\x65yebrow\x18\x03 \x01(\x0b\x32\".cmvr.api.FacialExpression.Eyebrow\x12\x31\n\x06\x65yelid\x18\x04 \x01(\x0b\x32!.cmvr.api.FacialExpression.Eyelid\x12\x33\n\x07\x65yeball\x18\x05 \x01(\x0b\x32\".cmvr.api.FacialExpression.Eyeball\x12-\n\x04nose\x18\x06 \x01(\x0b\x32\x1f.cmvr.api.FacialExpression.Nose\x12/\n\x05mouth\x18\x07 \x01(\x0b\x32 .cmvr.api.FacialExpression.Mouth\x12+\n\x03jaw\x18\x08 \x01(\x0b\x32\x1e.cmvr.api.FacialExpression.Jaw\x1ai\n\x07\x45yebrow\x12\x16\n\x0eleft_outside_y\x18\x01 \x01(\x02\x12\x15\n\rleft_inside_y\x18\x02 \x01(\x02\x12\x17\n\x0fright_outside_y\x18\x03 \x01(\x02\x12\x16\n\x0eright_inside_y\x18\x04 \x01(\x02\x1a\x62\n\x06\x45yelid\x12\x14\n\x0cleft_upper_y\x18\x01 \x01(\x02\x12\x14\n\x0cleft_lower_y\x18\x02 \x01(\x02\x12\x15\n\rright_upper_y\x18\x03 \x01(\x02\x12\x15\n\rright_lower_y\x18\x04 \x01(\x02\x1aK\n\x07\x45yeball\x12\x0e\n\x06left_x\x18\x01 \x01(\x02\x12\x0e\n\x06left_y\x18\x02 \x01(\x02\x12\x0f\n\x07right_x\x18\x03 \x01(\x02\x12\x0f\n\x07right_y\x18\x04 \x01(\x02\x1a\'\n\x04Nose\x12\x0e\n\x06left_y\x18\x01 \x01(\x02\x12\x0f\n\x07right_y\x18\x02 \x01(\x02\x1a\xbc\x03\n\x05Mouth\x12\x13\n\x0bupper_lip_y\x18\x01 \x01(\x02\x12\x13\n\x0bupper_lip_z\x18\x02 \x01(\x02\x12\x13\n\x0blower_lip_y\x18\x03 \x01(\x02\x12\x13\n\x0blower_lip_z\x18\x04 \x01(\x02\x12:\n\x08left_lip\x18\x05 \x01(\x0b\x32(.cmvr.api.FacialExpression.Mouth.LeftLip\x12<\n\tright_lip\x18\x06 \x01(\x0b\x32).cmvr.api.FacialExpression.Mouth.RightLip\x1aq\n\x07LeftLip\x12\x0f\n\x07upper_x\x18\x01 \x01(\x02\x12\x0f\n\x07upper_y\x18\x02 \x01(\x02\x12\x10\n\x08\x63orner_x\x18\x03 \x01(\x02\x12\x10\n\x08\x63orner_y\x18\x04 \x01(\x02\x12\x0f\n\x07lower_x\x18\x05 \x01(\x02\x12\x0f\n\x07lower_y\x18\x06 \x01(\x02\x1ar\n\x08RightLip\x12\x0f\n\x07upper_x\x18\x01 \x01(\x02\x12\x0f\n\x07upper_y\x18\x02 \x01(\x02\x12\x10\n\x08\x63orner_x\x18\x03 \x01(\x02\x12\x10\n\x08\x63orner_y\x18\x04 \x01(\x02\x12\x0f\n\x07lower_x\x18\x05 \x01(\x02\x12\x0f\n\x07lower_y\x18\x06 \x01(\x02\x1a\x1b\n\x03Jaw\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\"\xf0\x01\n\x13SetFacialExpression\x1aj\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12.\n\nexpression\x18\x02 \x01(\x0b\x32\x1a.cmvr.api.FacialExpression\x1am\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\x12\x14\n\x0c\x65xecution_id\x18\x02 \x01(\t\x12\x19\n\x11\x65xecution_time_ms\x18\x03 \x01(\x02\"\xf8\x01\n\x16StreamFacialExpression\x1aq\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12(\n\x04\x65xpr\x18\x02 \x01(\x0b\x32\x1a.cmvr.api.FacialExpression\x12\x0b\n\x03\x65of\x18\x03 \x01(\x08\x1ak\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\x12-\n\texpr_diff\x18\x02 \x01(\x0b\x32\x1a.cmvr.api.FacialExpression\"\x84\x02\n\tGetStatus\x1a:\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x1a\xba\x01\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\x12\x11\n\tis_moving\x18\x02 \x01(\x08\x12\x17\n\x0flast_request_id\x18\x03 \x01(\t\x12\x19\n\x11\x63urrent_positions\x18\x04 \x03(\x02\x12\x18\n\x10\x63\x61mera_recording\x18\x05 \x01(\x08\x12\x1b\n\x13\x61\x63tive_recording_id\x18\x06 \x01(\t\"\xa4\x01\n\rEmergencyStop\x1a:\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x1aW\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\x12\x19\n\x11stopped_processes\x18\x02 \x01(\t\"\x86\x01\n\nSpeakStart\x1a:\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x1a<\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"\x85\x01\n\tSpeakStop\x1a:\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x1a<\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"\x81\x01\n\x05Happy\x1a:\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x1a<\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"\x84\x01\n\x08Surprise\x1a:\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x1a<\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"\x8b\x01\n\x0f\x45xpressionTired\x1a:\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x1a<\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"\x8b\x01\n\x0f\x45xpressionAngry\x1a:\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x1a<\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"\x8d\x01\n\x11\x45xpressionSadness\x1a:\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x1a<\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"\x8a\x01\n\x0e\x45xpressionYawn\x1a:\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x1a<\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedbackb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -74,4 +74,52 @@ if not _descriptor._USE_C_DESCRIPTORS: _globals['_EMERGENCYSTOP_REQUEST']._serialized_end=1269 _globals['_EMERGENCYSTOP_FEEDBACK']._serialized_start=2022 _globals['_EMERGENCYSTOP_FEEDBACK']._serialized_end=2109 + _globals['_SPEAKSTART']._serialized_start=2112 + _globals['_SPEAKSTART']._serialized_end=2246 + _globals['_SPEAKSTART_REQUEST']._serialized_start=1211 + _globals['_SPEAKSTART_REQUEST']._serialized_end=1269 + _globals['_SPEAKSTART_FEEDBACK']._serialized_start=1319 + _globals['_SPEAKSTART_FEEDBACK']._serialized_end=1379 + _globals['_SPEAKSTOP']._serialized_start=2249 + _globals['_SPEAKSTOP']._serialized_end=2382 + _globals['_SPEAKSTOP_REQUEST']._serialized_start=1211 + _globals['_SPEAKSTOP_REQUEST']._serialized_end=1269 + _globals['_SPEAKSTOP_FEEDBACK']._serialized_start=1319 + _globals['_SPEAKSTOP_FEEDBACK']._serialized_end=1379 + _globals['_HAPPY']._serialized_start=2385 + _globals['_HAPPY']._serialized_end=2514 + _globals['_HAPPY_REQUEST']._serialized_start=1211 + _globals['_HAPPY_REQUEST']._serialized_end=1269 + _globals['_HAPPY_FEEDBACK']._serialized_start=1319 + _globals['_HAPPY_FEEDBACK']._serialized_end=1379 + _globals['_SURPRISE']._serialized_start=2517 + _globals['_SURPRISE']._serialized_end=2649 + _globals['_SURPRISE_REQUEST']._serialized_start=1211 + _globals['_SURPRISE_REQUEST']._serialized_end=1269 + _globals['_SURPRISE_FEEDBACK']._serialized_start=1319 + _globals['_SURPRISE_FEEDBACK']._serialized_end=1379 + _globals['_EXPRESSIONTIRED']._serialized_start=2652 + _globals['_EXPRESSIONTIRED']._serialized_end=2791 + _globals['_EXPRESSIONTIRED_REQUEST']._serialized_start=1211 + _globals['_EXPRESSIONTIRED_REQUEST']._serialized_end=1269 + _globals['_EXPRESSIONTIRED_FEEDBACK']._serialized_start=1319 + _globals['_EXPRESSIONTIRED_FEEDBACK']._serialized_end=1379 + _globals['_EXPRESSIONANGRY']._serialized_start=2794 + _globals['_EXPRESSIONANGRY']._serialized_end=2933 + _globals['_EXPRESSIONANGRY_REQUEST']._serialized_start=1211 + _globals['_EXPRESSIONANGRY_REQUEST']._serialized_end=1269 + _globals['_EXPRESSIONANGRY_FEEDBACK']._serialized_start=1319 + _globals['_EXPRESSIONANGRY_FEEDBACK']._serialized_end=1379 + _globals['_EXPRESSIONSADNESS']._serialized_start=2936 + _globals['_EXPRESSIONSADNESS']._serialized_end=3077 + _globals['_EXPRESSIONSADNESS_REQUEST']._serialized_start=1211 + _globals['_EXPRESSIONSADNESS_REQUEST']._serialized_end=1269 + _globals['_EXPRESSIONSADNESS_FEEDBACK']._serialized_start=1319 + _globals['_EXPRESSIONSADNESS_FEEDBACK']._serialized_end=1379 + _globals['_EXPRESSIONYAWN']._serialized_start=3080 + _globals['_EXPRESSIONYAWN']._serialized_end=3218 + _globals['_EXPRESSIONYAWN_REQUEST']._serialized_start=1211 + _globals['_EXPRESSIONYAWN_REQUEST']._serialized_end=1269 + _globals['_EXPRESSIONYAWN_FEEDBACK']._serialized_start=1319 + _globals['_EXPRESSIONYAWN_FEEDBACK']._serialized_end=1379 # @@protoc_insertion_point(module_scope) diff --git a/generated/cmvr/api/biohead_service_pb2.py b/generated/cmvr/api/biohead_service_pb2.py index 01f389d..e78f428 100644 --- a/generated/cmvr/api/biohead_service_pb2.py +++ b/generated/cmvr/api/biohead_service_pb2.py @@ -25,7 +25,7 @@ _sym_db = _symbol_database.Default() from cmvr.api import biohead_command_pb2 as cmvr_dot_api_dot_biohead__command__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1e\x63mvr/api/biohead_service.proto\x12\x08\x63mvr.api\x1a\x1e\x63mvr/api/biohead_command.proto2\x85\x03\n\x0e\x42ioHeadService\x12`\n\rSetExpression\x12%.cmvr.api.SetFacialExpression.Request\x1a&.cmvr.api.SetFacialExpression.Feedback\"\x00\x12k\n\x10StreamExpression\x12(.cmvr.api.StreamFacialExpression.Request\x1a).cmvr.api.StreamFacialExpression.Feedback(\x01\x30\x01\x12N\n\x0fGetSystemStatus\x12\x1b.cmvr.api.GetStatus.Request\x1a\x1c.cmvr.api.GetStatus.Feedback\"\x00\x12T\n\rEmergencyStop\x12\x1f.cmvr.api.EmergencyStop.Request\x1a .cmvr.api.EmergencyStop.Feedback\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1e\x63mvr/api/biohead_service.proto\x12\x08\x63mvr.api\x1a\x1e\x63mvr/api/biohead_command.proto2\x94\x08\n\x0e\x42ioHeadService\x12`\n\rSetExpression\x12%.cmvr.api.SetFacialExpression.Request\x1a&.cmvr.api.SetFacialExpression.Feedback\"\x00\x12k\n\x10StreamExpression\x12(.cmvr.api.StreamFacialExpression.Request\x1a).cmvr.api.StreamFacialExpression.Feedback(\x01\x30\x01\x12N\n\x0fGetSystemStatus\x12\x1b.cmvr.api.GetStatus.Request\x1a\x1c.cmvr.api.GetStatus.Feedback\"\x00\x12T\n\rEmergencyStop\x12\x1f.cmvr.api.EmergencyStop.Request\x1a .cmvr.api.EmergencyStop.Feedback\"\x00\x12K\n\nSpeakStart\x12\x1c.cmvr.api.SpeakStart.Request\x1a\x1d.cmvr.api.SpeakStart.Feedback\"\x00\x12H\n\tSpeakStop\x12\x1b.cmvr.api.SpeakStop.Request\x1a\x1c.cmvr.api.SpeakStop.Feedback\"\x00\x12<\n\x05Happy\x12\x17.cmvr.api.Happy.Request\x1a\x18.cmvr.api.Happy.Feedback\"\x00\x12\x45\n\x08Surprise\x12\x1a.cmvr.api.Surprise.Request\x1a\x1b.cmvr.api.Surprise.Feedback\"\x00\x12Z\n\x0f\x45xpressionTired\x12!.cmvr.api.ExpressionTired.Request\x1a\".cmvr.api.ExpressionTired.Feedback\"\x00\x12Z\n\x0f\x45xpressionAngry\x12!.cmvr.api.ExpressionAngry.Request\x1a\".cmvr.api.ExpressionAngry.Feedback\"\x00\x12`\n\x11\x45xpressionSadness\x12#.cmvr.api.ExpressionSadness.Request\x1a$.cmvr.api.ExpressionSadness.Feedback\"\x00\x12W\n\x0e\x45xpressionYawn\x12 .cmvr.api.ExpressionYawn.Request\x1a!.cmvr.api.ExpressionYawn.Feedback\"\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -33,5 +33,5 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cmvr.api.biohead_service_pb if not _descriptor._USE_C_DESCRIPTORS: DESCRIPTOR._loaded_options = None _globals['_BIOHEADSERVICE']._serialized_start=77 - _globals['_BIOHEADSERVICE']._serialized_end=466 + _globals['_BIOHEADSERVICE']._serialized_end=1121 # @@protoc_insertion_point(module_scope) diff --git a/generated/cmvr/api/biohead_service_pb2_grpc.py b/generated/cmvr/api/biohead_service_pb2_grpc.py index 4313022..0ddb589 100644 --- a/generated/cmvr/api/biohead_service_pb2_grpc.py +++ b/generated/cmvr/api/biohead_service_pb2_grpc.py @@ -55,6 +55,46 @@ class BioHeadServiceStub(object): request_serializer=cmvr_dot_api_dot_biohead__command__pb2.EmergencyStop.Request.SerializeToString, response_deserializer=cmvr_dot_api_dot_biohead__command__pb2.EmergencyStop.Feedback.FromString, _registered_method=True) + self.SpeakStart = channel.unary_unary( + '/cmvr.api.BioHeadService/SpeakStart', + request_serializer=cmvr_dot_api_dot_biohead__command__pb2.SpeakStart.Request.SerializeToString, + response_deserializer=cmvr_dot_api_dot_biohead__command__pb2.SpeakStart.Feedback.FromString, + _registered_method=True) + self.SpeakStop = channel.unary_unary( + '/cmvr.api.BioHeadService/SpeakStop', + request_serializer=cmvr_dot_api_dot_biohead__command__pb2.SpeakStop.Request.SerializeToString, + response_deserializer=cmvr_dot_api_dot_biohead__command__pb2.SpeakStop.Feedback.FromString, + _registered_method=True) + self.Happy = channel.unary_unary( + '/cmvr.api.BioHeadService/Happy', + request_serializer=cmvr_dot_api_dot_biohead__command__pb2.Happy.Request.SerializeToString, + response_deserializer=cmvr_dot_api_dot_biohead__command__pb2.Happy.Feedback.FromString, + _registered_method=True) + self.Surprise = channel.unary_unary( + '/cmvr.api.BioHeadService/Surprise', + request_serializer=cmvr_dot_api_dot_biohead__command__pb2.Surprise.Request.SerializeToString, + response_deserializer=cmvr_dot_api_dot_biohead__command__pb2.Surprise.Feedback.FromString, + _registered_method=True) + self.ExpressionTired = channel.unary_unary( + '/cmvr.api.BioHeadService/ExpressionTired', + request_serializer=cmvr_dot_api_dot_biohead__command__pb2.ExpressionTired.Request.SerializeToString, + response_deserializer=cmvr_dot_api_dot_biohead__command__pb2.ExpressionTired.Feedback.FromString, + _registered_method=True) + self.ExpressionAngry = channel.unary_unary( + '/cmvr.api.BioHeadService/ExpressionAngry', + request_serializer=cmvr_dot_api_dot_biohead__command__pb2.ExpressionAngry.Request.SerializeToString, + response_deserializer=cmvr_dot_api_dot_biohead__command__pb2.ExpressionAngry.Feedback.FromString, + _registered_method=True) + self.ExpressionSadness = channel.unary_unary( + '/cmvr.api.BioHeadService/ExpressionSadness', + request_serializer=cmvr_dot_api_dot_biohead__command__pb2.ExpressionSadness.Request.SerializeToString, + response_deserializer=cmvr_dot_api_dot_biohead__command__pb2.ExpressionSadness.Feedback.FromString, + _registered_method=True) + self.ExpressionYawn = channel.unary_unary( + '/cmvr.api.BioHeadService/ExpressionYawn', + request_serializer=cmvr_dot_api_dot_biohead__command__pb2.ExpressionYawn.Request.SerializeToString, + response_deserializer=cmvr_dot_api_dot_biohead__command__pb2.ExpressionYawn.Feedback.FromString, + _registered_method=True) class BioHeadServiceServicer(object): @@ -91,6 +131,56 @@ class BioHeadServiceServicer(object): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def SpeakStart(self, request, context): + """å¼€å§‹è¯´è¯ + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def SpeakStop(self, request, context): + """åœæ­¢è¯´è¯ + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Happy(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Surprise(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ExpressionTired(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ExpressionAngry(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ExpressionSadness(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ExpressionYawn(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_BioHeadServiceServicer_to_server(servicer, server): rpc_method_handlers = { @@ -114,6 +204,46 @@ def add_BioHeadServiceServicer_to_server(servicer, server): request_deserializer=cmvr_dot_api_dot_biohead__command__pb2.EmergencyStop.Request.FromString, response_serializer=cmvr_dot_api_dot_biohead__command__pb2.EmergencyStop.Feedback.SerializeToString, ), + 'SpeakStart': grpc.unary_unary_rpc_method_handler( + servicer.SpeakStart, + request_deserializer=cmvr_dot_api_dot_biohead__command__pb2.SpeakStart.Request.FromString, + response_serializer=cmvr_dot_api_dot_biohead__command__pb2.SpeakStart.Feedback.SerializeToString, + ), + 'SpeakStop': grpc.unary_unary_rpc_method_handler( + servicer.SpeakStop, + request_deserializer=cmvr_dot_api_dot_biohead__command__pb2.SpeakStop.Request.FromString, + response_serializer=cmvr_dot_api_dot_biohead__command__pb2.SpeakStop.Feedback.SerializeToString, + ), + 'Happy': grpc.unary_unary_rpc_method_handler( + servicer.Happy, + request_deserializer=cmvr_dot_api_dot_biohead__command__pb2.Happy.Request.FromString, + response_serializer=cmvr_dot_api_dot_biohead__command__pb2.Happy.Feedback.SerializeToString, + ), + 'Surprise': grpc.unary_unary_rpc_method_handler( + servicer.Surprise, + request_deserializer=cmvr_dot_api_dot_biohead__command__pb2.Surprise.Request.FromString, + response_serializer=cmvr_dot_api_dot_biohead__command__pb2.Surprise.Feedback.SerializeToString, + ), + 'ExpressionTired': grpc.unary_unary_rpc_method_handler( + servicer.ExpressionTired, + request_deserializer=cmvr_dot_api_dot_biohead__command__pb2.ExpressionTired.Request.FromString, + response_serializer=cmvr_dot_api_dot_biohead__command__pb2.ExpressionTired.Feedback.SerializeToString, + ), + 'ExpressionAngry': grpc.unary_unary_rpc_method_handler( + servicer.ExpressionAngry, + request_deserializer=cmvr_dot_api_dot_biohead__command__pb2.ExpressionAngry.Request.FromString, + response_serializer=cmvr_dot_api_dot_biohead__command__pb2.ExpressionAngry.Feedback.SerializeToString, + ), + 'ExpressionSadness': grpc.unary_unary_rpc_method_handler( + servicer.ExpressionSadness, + request_deserializer=cmvr_dot_api_dot_biohead__command__pb2.ExpressionSadness.Request.FromString, + response_serializer=cmvr_dot_api_dot_biohead__command__pb2.ExpressionSadness.Feedback.SerializeToString, + ), + 'ExpressionYawn': grpc.unary_unary_rpc_method_handler( + servicer.ExpressionYawn, + request_deserializer=cmvr_dot_api_dot_biohead__command__pb2.ExpressionYawn.Request.FromString, + response_serializer=cmvr_dot_api_dot_biohead__command__pb2.ExpressionYawn.Feedback.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( 'cmvr.api.BioHeadService', rpc_method_handlers) @@ -233,3 +363,219 @@ class BioHeadService(object): timeout, metadata, _registered_method=True) + + @staticmethod + def SpeakStart(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/cmvr.api.BioHeadService/SpeakStart', + cmvr_dot_api_dot_biohead__command__pb2.SpeakStart.Request.SerializeToString, + cmvr_dot_api_dot_biohead__command__pb2.SpeakStart.Feedback.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def SpeakStop(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/cmvr.api.BioHeadService/SpeakStop', + cmvr_dot_api_dot_biohead__command__pb2.SpeakStop.Request.SerializeToString, + cmvr_dot_api_dot_biohead__command__pb2.SpeakStop.Feedback.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Happy(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/cmvr.api.BioHeadService/Happy', + cmvr_dot_api_dot_biohead__command__pb2.Happy.Request.SerializeToString, + cmvr_dot_api_dot_biohead__command__pb2.Happy.Feedback.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Surprise(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/cmvr.api.BioHeadService/Surprise', + cmvr_dot_api_dot_biohead__command__pb2.Surprise.Request.SerializeToString, + cmvr_dot_api_dot_biohead__command__pb2.Surprise.Feedback.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ExpressionTired(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/cmvr.api.BioHeadService/ExpressionTired', + cmvr_dot_api_dot_biohead__command__pb2.ExpressionTired.Request.SerializeToString, + cmvr_dot_api_dot_biohead__command__pb2.ExpressionTired.Feedback.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ExpressionAngry(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/cmvr.api.BioHeadService/ExpressionAngry', + cmvr_dot_api_dot_biohead__command__pb2.ExpressionAngry.Request.SerializeToString, + cmvr_dot_api_dot_biohead__command__pb2.ExpressionAngry.Feedback.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ExpressionSadness(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/cmvr.api.BioHeadService/ExpressionSadness', + cmvr_dot_api_dot_biohead__command__pb2.ExpressionSadness.Request.SerializeToString, + cmvr_dot_api_dot_biohead__command__pb2.ExpressionSadness.Feedback.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ExpressionYawn(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/cmvr.api.BioHeadService/ExpressionYawn', + cmvr_dot_api_dot_biohead__command__pb2.ExpressionYawn.Request.SerializeToString, + cmvr_dot_api_dot_biohead__command__pb2.ExpressionYawn.Feedback.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/generated/cmvr/api/humanoid_robot_command_pb2.py b/generated/cmvr/api/humanoid_robot_command_pb2.py index b16f942..c8c95a5 100644 --- a/generated/cmvr/api/humanoid_robot_command_pb2.py +++ b/generated/cmvr/api/humanoid_robot_command_pb2.py @@ -25,67 +25,81 @@ _sym_db = _symbol_database.Default() from cmvr.api import common_pb2 as cmvr_dot_api_dot_common__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%cmvr/api/humanoid_robot_command.proto\x12\x08\x63mvr.api\x1a\x15\x63mvr/api/common.proto\"8\n\x08JointCmd\x12\x12\n\njoint_name\x18\x01 \x01(\t\x12\x0b\n\x03rad\x18\x02 \x01(\x01\x12\x0b\n\x03vel\x18\x03 \x01(\x01\"M\n\x06Pose3D\x12\t\n\x01x\x18\x01 \x01(\x01\x12\t\n\x01y\x18\x02 \x01(\x01\x12\t\n\x01z\x18\x03 \x01(\x01\x12\n\n\x02rx\x18\x04 \x01(\x01\x12\n\n\x02ry\x18\x05 \x01(\x01\x12\n\n\x02rz\x18\x06 \x01(\x01\"\xbd\x01\n\x05MoveJ\x1av\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12 \n\x04\x63mds\x18\x02 \x03(\x0b\x32\x12.cmvr.api.JointCmd\x12\x0b\n\x03vel\x18\x03 \x01(\x01\x12\x0b\n\x03\x61\x63\x63\x18\x04 \x01(\x01\x1a<\n\x08Response\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"\xc7\x01\n\x07GetPose\x1a^\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12\x11\n\tbase_link\x18\x02 \x01(\t\x12\x0f\n\x07\x65\x65_link\x18\x03 \x01(\t\x1a\\\n\x08Response\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\x12\x1e\n\x04pose\x18\x02 \x01(\x0b\x32\x10.cmvr.api.Pose3D\"\xd3\x01\n\x05MoveL\x1a\x8b\x01\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12\x0f\n\x07\x65\x65_link\x18\x02 \x01(\t\x12$\n\ntargetPose\x18\x03 \x01(\x0b\x32\x10.cmvr.api.Pose3D\x12\x0b\n\x03vel\x18\x04 \x01(\x01\x12\x0b\n\x03\x61\x63\x63\x18\x05 \x01(\x01\x1a<\n\x08Response\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"\xe2\x01\n\x06SpeedJ\x1a\x99\x01\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12\x12\n\njoint_name\x18\x02 \x01(\t\x12\x0b\n\x03vel\x18\x03 \x01(\x01\x12\x0b\n\x03\x61\x63\x63\x18\x04 \x01(\x01\x12/\n\x03\x64ir\x18\x05 \x01(\x0e\x32\".cmvr.api.RobotJointIndexDirection\x1a<\n\x08Response\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"\x87\x02\n\x06SpeedL\x1a\xbe\x01\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12\x0f\n\x07\x65\x65_link\x18\x02 \x01(\t\x12\x0b\n\x03vel\x18\x03 \x01(\x01\x12\x0b\n\x03\x61\x63\x63\x18\x04 \x01(\x01\x12/\n\x03\x64ir\x18\x05 \x01(\x0e\x32\".cmvr.api.RobotJointIndexDirection\x12&\n\x04\x63\x61rt\x18\x06 \x01(\x0e\x32\x18.cmvr.api.RobotCartesian\x1a<\n\x08Response\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"a\n\nJointState\x12\x0c\n\x04name\x18\x01 \x03(\t\x12\x10\n\x08position\x18\x02 \x03(\x01\x12\x10\n\x08velocity\x18\x03 \x03(\x01\x12\x0e\n\x06\x65\x66\x66ort\x18\x04 \x03(\x01\x12\x11\n\ttimestamp\x18\x05 \x01(\x01\"f\n\rJointResponse\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\x12#\n\x05state\x18\x02 \x03(\x0b\x32\x14.cmvr.api.JointState\"?\n\x0cJointRequest\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\"\x9e\x01\n\x0e\x43\x61librateZeroQ\x1aN\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12\x12\n\njoint_name\x18\x02 \x01(\t\x1a<\n\x08Response\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"\xb1\x01\n\x06ServoJ\x1ai\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12 \n\x04\x63mds\x18\x02 \x03(\x0b\x32\x12.cmvr.api.JointCmd\x12\x0b\n\x03vel\x18\x03 \x01(\x01\x1a<\n\x08Response\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback*=\n\x0eRobotCartesian\x12\x05\n\x01X\x10\x00\x12\x05\n\x01Y\x10\x01\x12\x05\n\x01Z\x10\x02\x12\x06\n\x02RX\x10\x03\x12\x06\n\x02RY\x10\x04\x12\x06\n\x02RZ\x10\x05*\xbf\x01\n\x18RobotJointIndexDirection\x12\x0b\n\x07\x46ORWARD\x10\x00\x12\x0c\n\x08\x42\x41\x43KWARD\x10\x01\x12\x0e\n\nX_POSITIVE\x10\x02\x12\x0e\n\nX_NEGATIVE\x10\x03\x12\x0e\n\nY_POSITIVE\x10\x04\x12\x0e\n\nY_NEGATIVE\x10\x05\x12\x0e\n\nZ_POSITIVE\x10\x06\x12\x0e\n\nZ_NEGATIVE\x10\x07\x12\x0c\n\x08ROTATE_X\x10\x08\x12\x0c\n\x08ROTATE_Y\x10\t\x12\x0c\n\x08ROTATE_Z\x10\nb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%cmvr/api/humanoid_robot_command.proto\x12\x08\x63mvr.api\x1a\x15\x63mvr/api/common.proto\"8\n\x08JointCmd\x12\x12\n\njoint_name\x18\x01 \x01(\t\x12\x0b\n\x03rad\x18\x02 \x01(\x01\x12\x0b\n\x03vel\x18\x03 \x01(\x01\"M\n\x06Pose3D\x12\t\n\x01x\x18\x01 \x01(\x01\x12\t\n\x01y\x18\x02 \x01(\x01\x12\t\n\x01z\x18\x03 \x01(\x01\x12\n\n\x02rx\x18\x04 \x01(\x01\x12\n\n\x02ry\x18\x05 \x01(\x01\x12\n\n\x02rz\x18\x06 \x01(\x01\"\xe4\x01\n\x12TransformMatrix4x4\x12\x0b\n\x03m00\x18\x01 \x01(\x01\x12\x0b\n\x03m01\x18\x02 \x01(\x01\x12\x0b\n\x03m02\x18\x03 \x01(\x01\x12\x0b\n\x03m03\x18\x04 \x01(\x01\x12\x0b\n\x03m10\x18\x05 \x01(\x01\x12\x0b\n\x03m11\x18\x06 \x01(\x01\x12\x0b\n\x03m12\x18\x07 \x01(\x01\x12\x0b\n\x03m13\x18\x08 \x01(\x01\x12\x0b\n\x03m20\x18\t \x01(\x01\x12\x0b\n\x03m21\x18\n \x01(\x01\x12\x0b\n\x03m22\x18\x0b \x01(\x01\x12\x0b\n\x03m23\x18\x0c \x01(\x01\x12\x0b\n\x03m30\x18\r \x01(\x01\x12\x0b\n\x03m31\x18\x0e \x01(\x01\x12\x0b\n\x03m32\x18\x0f \x01(\x01\x12\x0b\n\x03m33\x18\x10 \x01(\x01\"\xbd\x01\n\x05MoveJ\x1av\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12 \n\x04\x63mds\x18\x02 \x03(\x0b\x32\x12.cmvr.api.JointCmd\x12\x0b\n\x03vel\x18\x03 \x01(\x01\x12\x0b\n\x03\x61\x63\x63\x18\x04 \x01(\x01\x1a<\n\x08Response\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"\xc7\x01\n\x07GetPose\x1a^\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12\x11\n\tbase_link\x18\x02 \x01(\t\x12\x0f\n\x07\x65\x65_link\x18\x03 \x01(\t\x1a\\\n\x08Response\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\x12\x1e\n\x04pose\x18\x02 \x01(\x0b\x32\x10.cmvr.api.Pose3D\"\xd3\x01\n\x05MoveL\x1a\x8b\x01\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12\x0f\n\x07\x65\x65_link\x18\x02 \x01(\t\x12$\n\ntargetPose\x18\x03 \x01(\x0b\x32\x10.cmvr.api.Pose3D\x12\x0b\n\x03vel\x18\x04 \x01(\x01\x12\x0b\n\x03\x61\x63\x63\x18\x05 \x01(\x01\x1a<\n\x08Response\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"\xe2\x01\n\x06SpeedJ\x1a\x99\x01\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12\x12\n\njoint_name\x18\x02 \x01(\t\x12\x0b\n\x03vel\x18\x03 \x01(\x01\x12\x0b\n\x03\x61\x63\x63\x18\x04 \x01(\x01\x12/\n\x03\x64ir\x18\x05 \x01(\x0e\x32\".cmvr.api.RobotJointIndexDirection\x1a<\n\x08Response\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"\x87\x02\n\x06SpeedL\x1a\xbe\x01\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12\x0f\n\x07\x65\x65_link\x18\x02 \x01(\t\x12\x0b\n\x03vel\x18\x03 \x01(\x01\x12\x0b\n\x03\x61\x63\x63\x18\x04 \x01(\x01\x12/\n\x03\x64ir\x18\x05 \x01(\x0e\x32\".cmvr.api.RobotJointIndexDirection\x12&\n\x04\x63\x61rt\x18\x06 \x01(\x0e\x32\x18.cmvr.api.RobotCartesian\x1a<\n\x08Response\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"a\n\nJointState\x12\x0c\n\x04name\x18\x01 \x03(\t\x12\x10\n\x08position\x18\x02 \x03(\x01\x12\x10\n\x08velocity\x18\x03 \x03(\x01\x12\x0e\n\x06\x65\x66\x66ort\x18\x04 \x03(\x01\x12\x11\n\ttimestamp\x18\x05 \x01(\x01\"f\n\rJointResponse\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\x12#\n\x05state\x18\x02 \x03(\x0b\x32\x14.cmvr.api.JointState\"?\n\x0cJointRequest\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\"\x9e\x01\n\x0e\x43\x61librateZeroQ\x1aN\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12\x12\n\njoint_name\x18\x02 \x01(\t\x1a<\n\x08Response\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"\xb1\x01\n\x06ServoJ\x1ai\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12 \n\x04\x63mds\x18\x02 \x03(\x0b\x32\x12.cmvr.api.JointCmd\x12\x0b\n\x03vel\x18\x03 \x01(\x01\x1a<\n\x08Response\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"\xdb\x01\n\rGetPoseMatrix\x1a^\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12\x11\n\tbase_link\x18\x02 \x01(\t\x12\x0f\n\x07\x65\x65_link\x18\x03 \x01(\t\x1aj\n\x08Response\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\x12,\n\x06matrix\x18\x02 \x01(\x0b\x32\x1c.cmvr.api.TransformMatrix4x4\"\x8b\x02\n\x18\x43omputeForwardKinematics\x1a\x82\x01\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12\x11\n\tbase_link\x18\x02 \x01(\t\x12\x0f\n\x07\x65\x65_link\x18\x03 \x01(\t\x12\"\n\x06joints\x18\x04 \x03(\x0b\x32\x12.cmvr.api.JointCmd\x1aj\n\x08Response\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\x12,\n\x06matrix\x18\x02 \x01(\x0b\x32\x1c.cmvr.api.TransformMatrix4x4*=\n\x0eRobotCartesian\x12\x05\n\x01X\x10\x00\x12\x05\n\x01Y\x10\x01\x12\x05\n\x01Z\x10\x02\x12\x06\n\x02RX\x10\x03\x12\x06\n\x02RY\x10\x04\x12\x06\n\x02RZ\x10\x05*\xbf\x01\n\x18RobotJointIndexDirection\x12\x0b\n\x07\x46ORWARD\x10\x00\x12\x0c\n\x08\x42\x41\x43KWARD\x10\x01\x12\x0e\n\nX_POSITIVE\x10\x02\x12\x0e\n\nX_NEGATIVE\x10\x03\x12\x0e\n\nY_POSITIVE\x10\x04\x12\x0e\n\nY_NEGATIVE\x10\x05\x12\x0e\n\nZ_POSITIVE\x10\x06\x12\x0e\n\nZ_NEGATIVE\x10\x07\x12\x0c\n\x08ROTATE_X\x10\x08\x12\x0c\n\x08ROTATE_Y\x10\t\x12\x0c\n\x08ROTATE_Z\x10\nb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cmvr.api.humanoid_robot_command_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: DESCRIPTOR._loaded_options = None - _globals['_ROBOTCARTESIAN']._serialized_start=1923 - _globals['_ROBOTCARTESIAN']._serialized_end=1984 - _globals['_ROBOTJOINTINDEXDIRECTION']._serialized_start=1987 - _globals['_ROBOTJOINTINDEXDIRECTION']._serialized_end=2178 + _globals['_ROBOTCARTESIAN']._serialized_start=2646 + _globals['_ROBOTCARTESIAN']._serialized_end=2707 + _globals['_ROBOTJOINTINDEXDIRECTION']._serialized_start=2710 + _globals['_ROBOTJOINTINDEXDIRECTION']._serialized_end=2901 _globals['_JOINTCMD']._serialized_start=74 _globals['_JOINTCMD']._serialized_end=130 _globals['_POSE3D']._serialized_start=132 _globals['_POSE3D']._serialized_end=209 - _globals['_MOVEJ']._serialized_start=212 - _globals['_MOVEJ']._serialized_end=401 - _globals['_MOVEJ_REQUEST']._serialized_start=221 - _globals['_MOVEJ_REQUEST']._serialized_end=339 - _globals['_MOVEJ_RESPONSE']._serialized_start=341 - _globals['_MOVEJ_RESPONSE']._serialized_end=401 - _globals['_GETPOSE']._serialized_start=404 - _globals['_GETPOSE']._serialized_end=603 - _globals['_GETPOSE_REQUEST']._serialized_start=415 - _globals['_GETPOSE_REQUEST']._serialized_end=509 - _globals['_GETPOSE_RESPONSE']._serialized_start=511 - _globals['_GETPOSE_RESPONSE']._serialized_end=603 - _globals['_MOVEL']._serialized_start=606 - _globals['_MOVEL']._serialized_end=817 - _globals['_MOVEL_REQUEST']._serialized_start=616 - _globals['_MOVEL_REQUEST']._serialized_end=755 - _globals['_MOVEL_RESPONSE']._serialized_start=341 - _globals['_MOVEL_RESPONSE']._serialized_end=401 - _globals['_SPEEDJ']._serialized_start=820 - _globals['_SPEEDJ']._serialized_end=1046 - _globals['_SPEEDJ_REQUEST']._serialized_start=831 - _globals['_SPEEDJ_REQUEST']._serialized_end=984 - _globals['_SPEEDJ_RESPONSE']._serialized_start=341 - _globals['_SPEEDJ_RESPONSE']._serialized_end=401 - _globals['_SPEEDL']._serialized_start=1049 - _globals['_SPEEDL']._serialized_end=1312 - _globals['_SPEEDL_REQUEST']._serialized_start=1060 - _globals['_SPEEDL_REQUEST']._serialized_end=1250 - _globals['_SPEEDL_RESPONSE']._serialized_start=341 - _globals['_SPEEDL_RESPONSE']._serialized_end=401 - _globals['_JOINTSTATE']._serialized_start=1314 - _globals['_JOINTSTATE']._serialized_end=1411 - _globals['_JOINTRESPONSE']._serialized_start=1413 - _globals['_JOINTRESPONSE']._serialized_end=1515 - _globals['_JOINTREQUEST']._serialized_start=1517 - _globals['_JOINTREQUEST']._serialized_end=1580 - _globals['_CALIBRATEZEROQ']._serialized_start=1583 - _globals['_CALIBRATEZEROQ']._serialized_end=1741 - _globals['_CALIBRATEZEROQ_REQUEST']._serialized_start=831 - _globals['_CALIBRATEZEROQ_REQUEST']._serialized_end=909 - _globals['_CALIBRATEZEROQ_RESPONSE']._serialized_start=341 - _globals['_CALIBRATEZEROQ_RESPONSE']._serialized_end=401 - _globals['_SERVOJ']._serialized_start=1744 - _globals['_SERVOJ']._serialized_end=1921 - _globals['_SERVOJ_REQUEST']._serialized_start=221 - _globals['_SERVOJ_REQUEST']._serialized_end=326 - _globals['_SERVOJ_RESPONSE']._serialized_start=341 - _globals['_SERVOJ_RESPONSE']._serialized_end=401 + _globals['_TRANSFORMMATRIX4X4']._serialized_start=212 + _globals['_TRANSFORMMATRIX4X4']._serialized_end=440 + _globals['_MOVEJ']._serialized_start=443 + _globals['_MOVEJ']._serialized_end=632 + _globals['_MOVEJ_REQUEST']._serialized_start=452 + _globals['_MOVEJ_REQUEST']._serialized_end=570 + _globals['_MOVEJ_RESPONSE']._serialized_start=572 + _globals['_MOVEJ_RESPONSE']._serialized_end=632 + _globals['_GETPOSE']._serialized_start=635 + _globals['_GETPOSE']._serialized_end=834 + _globals['_GETPOSE_REQUEST']._serialized_start=646 + _globals['_GETPOSE_REQUEST']._serialized_end=740 + _globals['_GETPOSE_RESPONSE']._serialized_start=742 + _globals['_GETPOSE_RESPONSE']._serialized_end=834 + _globals['_MOVEL']._serialized_start=837 + _globals['_MOVEL']._serialized_end=1048 + _globals['_MOVEL_REQUEST']._serialized_start=847 + _globals['_MOVEL_REQUEST']._serialized_end=986 + _globals['_MOVEL_RESPONSE']._serialized_start=572 + _globals['_MOVEL_RESPONSE']._serialized_end=632 + _globals['_SPEEDJ']._serialized_start=1051 + _globals['_SPEEDJ']._serialized_end=1277 + _globals['_SPEEDJ_REQUEST']._serialized_start=1062 + _globals['_SPEEDJ_REQUEST']._serialized_end=1215 + _globals['_SPEEDJ_RESPONSE']._serialized_start=572 + _globals['_SPEEDJ_RESPONSE']._serialized_end=632 + _globals['_SPEEDL']._serialized_start=1280 + _globals['_SPEEDL']._serialized_end=1543 + _globals['_SPEEDL_REQUEST']._serialized_start=1291 + _globals['_SPEEDL_REQUEST']._serialized_end=1481 + _globals['_SPEEDL_RESPONSE']._serialized_start=572 + _globals['_SPEEDL_RESPONSE']._serialized_end=632 + _globals['_JOINTSTATE']._serialized_start=1545 + _globals['_JOINTSTATE']._serialized_end=1642 + _globals['_JOINTRESPONSE']._serialized_start=1644 + _globals['_JOINTRESPONSE']._serialized_end=1746 + _globals['_JOINTREQUEST']._serialized_start=1748 + _globals['_JOINTREQUEST']._serialized_end=1811 + _globals['_CALIBRATEZEROQ']._serialized_start=1814 + _globals['_CALIBRATEZEROQ']._serialized_end=1972 + _globals['_CALIBRATEZEROQ_REQUEST']._serialized_start=1062 + _globals['_CALIBRATEZEROQ_REQUEST']._serialized_end=1140 + _globals['_CALIBRATEZEROQ_RESPONSE']._serialized_start=572 + _globals['_CALIBRATEZEROQ_RESPONSE']._serialized_end=632 + _globals['_SERVOJ']._serialized_start=1975 + _globals['_SERVOJ']._serialized_end=2152 + _globals['_SERVOJ_REQUEST']._serialized_start=452 + _globals['_SERVOJ_REQUEST']._serialized_end=557 + _globals['_SERVOJ_RESPONSE']._serialized_start=572 + _globals['_SERVOJ_RESPONSE']._serialized_end=632 + _globals['_GETPOSEMATRIX']._serialized_start=2155 + _globals['_GETPOSEMATRIX']._serialized_end=2374 + _globals['_GETPOSEMATRIX_REQUEST']._serialized_start=646 + _globals['_GETPOSEMATRIX_REQUEST']._serialized_end=740 + _globals['_GETPOSEMATRIX_RESPONSE']._serialized_start=2268 + _globals['_GETPOSEMATRIX_RESPONSE']._serialized_end=2374 + _globals['_COMPUTEFORWARDKINEMATICS']._serialized_start=2377 + _globals['_COMPUTEFORWARDKINEMATICS']._serialized_end=2644 + _globals['_COMPUTEFORWARDKINEMATICS_REQUEST']._serialized_start=2406 + _globals['_COMPUTEFORWARDKINEMATICS_REQUEST']._serialized_end=2536 + _globals['_COMPUTEFORWARDKINEMATICS_RESPONSE']._serialized_start=2268 + _globals['_COMPUTEFORWARDKINEMATICS_RESPONSE']._serialized_end=2374 # @@protoc_insertion_point(module_scope) diff --git a/generated/cmvr/api/humanoid_robot_service_pb2.py b/generated/cmvr/api/humanoid_robot_service_pb2.py index c466cd2..822a742 100644 --- a/generated/cmvr/api/humanoid_robot_service_pb2.py +++ b/generated/cmvr/api/humanoid_robot_service_pb2.py @@ -26,7 +26,7 @@ from cmvr.api import common_pb2 as cmvr_dot_api_dot_common__pb2 from cmvr.api import humanoid_robot_command_pb2 as cmvr_dot_api_dot_humanoid__robot__command__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%cmvr/api/humanoid_robot_service.proto\x12\x08\x63mvr.api\x1a\x15\x63mvr/api/common.proto\x1a%cmvr/api/humanoid_robot_command.proto2\xc5\x05\n\x14HumanoidRobotService\x12N\n\ttorqueOff\x12\x1f.cmvr.api.CommandHeader.Request\x1a .cmvr.api.CommandHeader.Feedback\x12M\n\x08torqueOn\x12\x1f.cmvr.api.CommandHeader.Request\x1a .cmvr.api.CommandHeader.Feedback\x12:\n\x05moveJ\x12\x17.cmvr.api.MoveJ.Request\x1a\x18.cmvr.api.MoveJ.Response\x12:\n\x05moveL\x12\x17.cmvr.api.MoveL.Request\x1a\x18.cmvr.api.MoveL.Response\x12=\n\x06speedJ\x12\x18.cmvr.api.SpeedJ.Request\x1a\x19.cmvr.api.SpeedJ.Response\x12=\n\x06speedL\x12\x18.cmvr.api.SpeedL.Request\x1a\x19.cmvr.api.SpeedL.Response\x12@\n\rgetJointState\x12\x16.cmvr.api.JointRequest\x1a\x17.cmvr.api.JointResponse\x12@\n\x07getPose\x12\x19.cmvr.api.GetPose.Request\x1a\x1a.cmvr.api.GetPose.Response\x12U\n\x0e\x63\x61librateZeroQ\x12 .cmvr.api.CalibrateZeroQ.Request\x1a!.cmvr.api.CalibrateZeroQ.Response\x12=\n\x06servoJ\x12\x18.cmvr.api.ServoJ.Request\x1a\x19.cmvr.api.ServoJ.Responseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%cmvr/api/humanoid_robot_service.proto\x12\x08\x63mvr.api\x1a\x15\x63mvr/api/common.proto\x1a%cmvr/api/humanoid_robot_command.proto2\x8e\x07\n\x14HumanoidRobotService\x12N\n\ttorqueOff\x12\x1f.cmvr.api.CommandHeader.Request\x1a .cmvr.api.CommandHeader.Feedback\x12M\n\x08torqueOn\x12\x1f.cmvr.api.CommandHeader.Request\x1a .cmvr.api.CommandHeader.Feedback\x12:\n\x05moveJ\x12\x17.cmvr.api.MoveJ.Request\x1a\x18.cmvr.api.MoveJ.Response\x12:\n\x05moveL\x12\x17.cmvr.api.MoveL.Request\x1a\x18.cmvr.api.MoveL.Response\x12=\n\x06speedJ\x12\x18.cmvr.api.SpeedJ.Request\x1a\x19.cmvr.api.SpeedJ.Response\x12=\n\x06speedL\x12\x18.cmvr.api.SpeedL.Request\x1a\x19.cmvr.api.SpeedL.Response\x12@\n\rgetJointState\x12\x16.cmvr.api.JointRequest\x1a\x17.cmvr.api.JointResponse\x12@\n\x07getPose\x12\x19.cmvr.api.GetPose.Request\x1a\x1a.cmvr.api.GetPose.Response\x12U\n\x0e\x63\x61librateZeroQ\x12 .cmvr.api.CalibrateZeroQ.Request\x1a!.cmvr.api.CalibrateZeroQ.Response\x12=\n\x06servoJ\x12\x18.cmvr.api.ServoJ.Request\x1a\x19.cmvr.api.ServoJ.Response\x12R\n\rgetPoseMatrix\x12\x1f.cmvr.api.GetPoseMatrix.Request\x1a .cmvr.api.GetPoseMatrix.Response\x12s\n\x18\x63omputeForwardKinematics\x12*.cmvr.api.ComputeForwardKinematics.Request\x1a+.cmvr.api.ComputeForwardKinematics.Responseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -34,5 +34,5 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cmvr.api.humanoid_robot_ser if not _descriptor._USE_C_DESCRIPTORS: DESCRIPTOR._loaded_options = None _globals['_HUMANOIDROBOTSERVICE']._serialized_start=114 - _globals['_HUMANOIDROBOTSERVICE']._serialized_end=823 + _globals['_HUMANOIDROBOTSERVICE']._serialized_end=1024 # @@protoc_insertion_point(module_scope) diff --git a/generated/cmvr/api/humanoid_robot_service_pb2_grpc.py b/generated/cmvr/api/humanoid_robot_service_pb2_grpc.py index cd15fc7..6536b92 100644 --- a/generated/cmvr/api/humanoid_robot_service_pb2_grpc.py +++ b/generated/cmvr/api/humanoid_robot_service_pb2_grpc.py @@ -85,6 +85,16 @@ class HumanoidRobotServiceStub(object): request_serializer=cmvr_dot_api_dot_humanoid__robot__command__pb2.ServoJ.Request.SerializeToString, response_deserializer=cmvr_dot_api_dot_humanoid__robot__command__pb2.ServoJ.Response.FromString, _registered_method=True) + self.getPoseMatrix = channel.unary_unary( + '/cmvr.api.HumanoidRobotService/getPoseMatrix', + request_serializer=cmvr_dot_api_dot_humanoid__robot__command__pb2.GetPoseMatrix.Request.SerializeToString, + response_deserializer=cmvr_dot_api_dot_humanoid__robot__command__pb2.GetPoseMatrix.Response.FromString, + _registered_method=True) + self.computeForwardKinematics = channel.unary_unary( + '/cmvr.api.HumanoidRobotService/computeForwardKinematics', + request_serializer=cmvr_dot_api_dot_humanoid__robot__command__pb2.ComputeForwardKinematics.Request.SerializeToString, + response_deserializer=cmvr_dot_api_dot_humanoid__robot__command__pb2.ComputeForwardKinematics.Response.FromString, + _registered_method=True) class HumanoidRobotServiceServicer(object): @@ -150,6 +160,18 @@ class HumanoidRobotServiceServicer(object): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def getPoseMatrix(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def computeForwardKinematics(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_HumanoidRobotServiceServicer_to_server(servicer, server): rpc_method_handlers = { @@ -203,6 +225,16 @@ def add_HumanoidRobotServiceServicer_to_server(servicer, server): request_deserializer=cmvr_dot_api_dot_humanoid__robot__command__pb2.ServoJ.Request.FromString, response_serializer=cmvr_dot_api_dot_humanoid__robot__command__pb2.ServoJ.Response.SerializeToString, ), + 'getPoseMatrix': grpc.unary_unary_rpc_method_handler( + servicer.getPoseMatrix, + request_deserializer=cmvr_dot_api_dot_humanoid__robot__command__pb2.GetPoseMatrix.Request.FromString, + response_serializer=cmvr_dot_api_dot_humanoid__robot__command__pb2.GetPoseMatrix.Response.SerializeToString, + ), + 'computeForwardKinematics': grpc.unary_unary_rpc_method_handler( + servicer.computeForwardKinematics, + request_deserializer=cmvr_dot_api_dot_humanoid__robot__command__pb2.ComputeForwardKinematics.Request.FromString, + response_serializer=cmvr_dot_api_dot_humanoid__robot__command__pb2.ComputeForwardKinematics.Response.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( 'cmvr.api.HumanoidRobotService', rpc_method_handlers) @@ -483,3 +515,57 @@ class HumanoidRobotService(object): timeout, metadata, _registered_method=True) + + @staticmethod + def getPoseMatrix(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/cmvr.api.HumanoidRobotService/getPoseMatrix', + cmvr_dot_api_dot_humanoid__robot__command__pb2.GetPoseMatrix.Request.SerializeToString, + cmvr_dot_api_dot_humanoid__robot__command__pb2.GetPoseMatrix.Response.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def computeForwardKinematics(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/cmvr.api.HumanoidRobotService/computeForwardKinematics', + cmvr_dot_api_dot_humanoid__robot__command__pb2.ComputeForwardKinematics.Request.SerializeToString, + cmvr_dot_api_dot_humanoid__robot__command__pb2.ComputeForwardKinematics.Response.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/generated/cmvr/api/system_command_pb2.py b/generated/cmvr/api/system_command_pb2.py index e76b9d5..c60017f 100644 --- a/generated/cmvr/api/system_command_pb2.py +++ b/generated/cmvr/api/system_command_pb2.py @@ -25,15 +25,15 @@ _sym_db = _symbol_database.Default() from cmvr.api import common_pb2 as cmvr_dot_api_dot_common__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x63mvr/api/system_command.proto\x12\x08\x63mvr.api\x1a\x15\x63mvr/api/common.proto\"J\n\nDeviceList\x12\x11\n\tdevice_id\x18\x01 \x01(\t\x12)\n\x0b\x64\x65vice_type\x18\x02 \x01(\x0e\x32\x14.cmvr.api.DeviceType\"\xd5\x01\n\x14GetSystemInfoCommand\x1a\t\n\x07Request\x1a\xb1\x01\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\x12\x13\n\x0bsystem_name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\x12\n\n\x02os\x18\x05 \x01(\t\x12\x16\n\x0ekernel_version\x18\x06 \x01(\t\x12\x14\n\x0c\x61rchitecture\x18\x07 \x01(\t\"\xf8\x01\n\x16GetSystemStatusCommand\x1a\t\n\x07Request\x1a\xd2\x01\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\x12\x11\n\tcpu_usage\x18\x02 \x01(\x02\x12\x14\n\x0cmem_total_mb\x18\x03 \x01(\x02\x12\x13\n\x0bmem_used_mb\x18\x04 \x01(\x02\x12\x15\n\rdisk_total_gb\x18\x05 \x01(\x02\x12\x14\n\x0c\x64isk_used_gb\x18\x06 \x01(\x02\x12)\n\x0b\x64\x65vice_list\x18\x07 \x03(\x0b\x32\x14.cmvr.api.DeviceList\"\xb6\x01\n\x13UpdateParamsCommand\x1a\x61\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12%\n\x06params\x18\x02 \x03(\x0b\x32\x15.cmvr.api.ConfigParam\x1a<\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback*}\n\nDeviceType\x12\x07\n\x03\x41GV\x10\x00\x12\x0b\n\x07\x42\x61ttery\x10\x01\x12\n\n\x06\x43\x61mera\x10\x02\x12\x0b\n\x07\x44\x65xHand\x10\x03\x12\x0b\n\x07Gripper\x10\x04\x12\x0e\n\nMicrophone\x10\x05\x12\t\n\x05Robot\x10\x06\x12\x0b\n\x07Speaker\x10\x07\x12\x0b\n\x07Unknown\x10\x14\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x63mvr/api/system_command.proto\x12\x08\x63mvr.api\x1a\x15\x63mvr/api/common.proto\"J\n\nDeviceList\x12\x11\n\tdevice_id\x18\x01 \x01(\t\x12)\n\x0b\x64\x65vice_type\x18\x02 \x01(\x0e\x32\x14.cmvr.api.DeviceType\"\xd5\x01\n\x14GetSystemInfoCommand\x1a\t\n\x07Request\x1a\xb1\x01\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\x12\x13\n\x0bsystem_name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\x12\n\n\x02os\x18\x05 \x01(\t\x12\x16\n\x0ekernel_version\x18\x06 \x01(\t\x12\x14\n\x0c\x61rchitecture\x18\x07 \x01(\t\"\xf8\x01\n\x16GetSystemStatusCommand\x1a\t\n\x07Request\x1a\xd2\x01\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\x12\x11\n\tcpu_usage\x18\x02 \x01(\x02\x12\x14\n\x0cmem_total_mb\x18\x03 \x01(\x02\x12\x13\n\x0bmem_used_mb\x18\x04 \x01(\x02\x12\x15\n\rdisk_total_gb\x18\x05 \x01(\x02\x12\x14\n\x0c\x64isk_used_gb\x18\x06 \x01(\x02\x12)\n\x0b\x64\x65vice_list\x18\x07 \x03(\x0b\x32\x14.cmvr.api.DeviceList\"\xb6\x01\n\x13UpdateParamsCommand\x1a\x61\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x12%\n\x06params\x18\x02 \x03(\x0b\x32\x15.cmvr.api.ConfigParam\x1a<\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback\"\x8a\x01\n\x0eStopAllCommand\x1a:\n\x07Request\x12/\n\x06header\x18\x01 \x01(\x0b\x32\x1f.cmvr.api.CommandHeader.Request\x1a<\n\x08\x46\x65\x65\x64\x62\x61\x63k\x12\x30\n\x06header\x18\x01 \x01(\x0b\x32 .cmvr.api.CommandHeader.Feedback*}\n\nDeviceType\x12\x07\n\x03\x41GV\x10\x00\x12\x0b\n\x07\x42\x61ttery\x10\x01\x12\n\n\x06\x43\x61mera\x10\x02\x12\x0b\n\x07\x44\x65xHand\x10\x03\x12\x0b\n\x07Gripper\x10\x04\x12\x0e\n\nMicrophone\x10\x05\x12\t\n\x05Robot\x10\x06\x12\x0b\n\x07Speaker\x10\x07\x12\x0b\n\x07Unknown\x10\x14\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cmvr.api.system_command_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: DESCRIPTOR._loaded_options = None - _globals['_DEVICETYPE']._serialized_start=794 - _globals['_DEVICETYPE']._serialized_end=919 + _globals['_DEVICETYPE']._serialized_start=935 + _globals['_DEVICETYPE']._serialized_end=1060 _globals['_DEVICELIST']._serialized_start=66 _globals['_DEVICELIST']._serialized_end=140 _globals['_GETSYSTEMINFOCOMMAND']._serialized_start=143 @@ -54,4 +54,10 @@ if not _descriptor._USE_C_DESCRIPTORS: _globals['_UPDATEPARAMSCOMMAND_REQUEST']._serialized_end=730 _globals['_UPDATEPARAMSCOMMAND_FEEDBACK']._serialized_start=179 _globals['_UPDATEPARAMSCOMMAND_FEEDBACK']._serialized_end=239 + _globals['_STOPALLCOMMAND']._serialized_start=795 + _globals['_STOPALLCOMMAND']._serialized_end=933 + _globals['_STOPALLCOMMAND_REQUEST']._serialized_start=633 + _globals['_STOPALLCOMMAND_REQUEST']._serialized_end=691 + _globals['_STOPALLCOMMAND_FEEDBACK']._serialized_start=179 + _globals['_STOPALLCOMMAND_FEEDBACK']._serialized_end=239 # @@protoc_insertion_point(module_scope) diff --git a/generated/cmvr/api/system_service_pb2.py b/generated/cmvr/api/system_service_pb2.py index af84fab..4dea91f 100644 --- a/generated/cmvr/api/system_service_pb2.py +++ b/generated/cmvr/api/system_service_pb2.py @@ -25,7 +25,7 @@ _sym_db = _symbol_database.Default() from cmvr.api import system_command_pb2 as cmvr_dot_api_dot_system__command__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x63mvr/api/system_service.proto\x12\x08\x63mvr.api\x1a\x1d\x63mvr/api/system_command.proto2\xbe\x02\n\rSystemService\x12\x62\n\rGetSystemInfo\x12&.cmvr.api.GetSystemInfoCommand.Request\x1a\'.cmvr.api.GetSystemInfoCommand.Feedback\"\x00\x12h\n\x0fGetSystemStatus\x12(.cmvr.api.GetSystemStatusCommand.Request\x1a).cmvr.api.GetSystemStatusCommand.Feedback\"\x00\x12_\n\x0cUpdateParams\x12%.cmvr.api.UpdateParamsCommand.Request\x1a&.cmvr.api.UpdateParamsCommand.Feedback\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x63mvr/api/system_service.proto\x12\x08\x63mvr.api\x1a\x1d\x63mvr/api/system_command.proto2\x90\x03\n\rSystemService\x12\x62\n\rGetSystemInfo\x12&.cmvr.api.GetSystemInfoCommand.Request\x1a\'.cmvr.api.GetSystemInfoCommand.Feedback\"\x00\x12h\n\x0fGetSystemStatus\x12(.cmvr.api.GetSystemStatusCommand.Request\x1a).cmvr.api.GetSystemStatusCommand.Feedback\"\x00\x12_\n\x0cUpdateParams\x12%.cmvr.api.UpdateParamsCommand.Request\x1a&.cmvr.api.UpdateParamsCommand.Feedback\"\x00\x12P\n\x07StopAll\x12 .cmvr.api.StopAllCommand.Request\x1a!.cmvr.api.StopAllCommand.Feedback\"\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -33,5 +33,5 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cmvr.api.system_service_pb2 if not _descriptor._USE_C_DESCRIPTORS: DESCRIPTOR._loaded_options = None _globals['_SYSTEMSERVICE']._serialized_start=75 - _globals['_SYSTEMSERVICE']._serialized_end=393 + _globals['_SYSTEMSERVICE']._serialized_end=475 # @@protoc_insertion_point(module_scope) diff --git a/generated/cmvr/api/system_service_pb2_grpc.py b/generated/cmvr/api/system_service_pb2_grpc.py index 215234d..7262550 100644 --- a/generated/cmvr/api/system_service_pb2_grpc.py +++ b/generated/cmvr/api/system_service_pb2_grpc.py @@ -49,6 +49,11 @@ class SystemServiceStub(object): request_serializer=cmvr_dot_api_dot_system__command__pb2.UpdateParamsCommand.Request.SerializeToString, response_deserializer=cmvr_dot_api_dot_system__command__pb2.UpdateParamsCommand.Feedback.FromString, _registered_method=True) + self.StopAll = channel.unary_unary( + '/cmvr.api.SystemService/StopAll', + request_serializer=cmvr_dot_api_dot_system__command__pb2.StopAllCommand.Request.SerializeToString, + response_deserializer=cmvr_dot_api_dot_system__command__pb2.StopAllCommand.Feedback.FromString, + _registered_method=True) class SystemServiceServicer(object): @@ -72,6 +77,12 @@ class SystemServiceServicer(object): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def StopAll(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_SystemServiceServicer_to_server(servicer, server): rpc_method_handlers = { @@ -90,6 +101,11 @@ def add_SystemServiceServicer_to_server(servicer, server): request_deserializer=cmvr_dot_api_dot_system__command__pb2.UpdateParamsCommand.Request.FromString, response_serializer=cmvr_dot_api_dot_system__command__pb2.UpdateParamsCommand.Feedback.SerializeToString, ), + 'StopAll': grpc.unary_unary_rpc_method_handler( + servicer.StopAll, + request_deserializer=cmvr_dot_api_dot_system__command__pb2.StopAllCommand.Request.FromString, + response_serializer=cmvr_dot_api_dot_system__command__pb2.StopAllCommand.Feedback.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( 'cmvr.api.SystemService', rpc_method_handlers) @@ -181,3 +197,30 @@ class SystemService(object): timeout, metadata, _registered_method=True) + + @staticmethod + def StopAll(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/cmvr.api.SystemService/StopAll', + cmvr_dot_api_dot_system__command__pb2.StopAllCommand.Request.SerializeToString, + cmvr_dot_api_dot_system__command__pb2.StopAllCommand.Feedback.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/generated/cmvr/config/__init__.py b/generated/cmvr/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/generated/cmvr/config/camera_config/__init__.py b/generated/cmvr/config/camera_config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/generated/cmvr/config/camera_config/camera_config_pb2.py b/generated/cmvr/config/camera_config/camera_config_pb2.py new file mode 100644 index 0000000..d43d179 --- /dev/null +++ b/generated/cmvr/config/camera_config/camera_config_pb2.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: cmvr/config/camera_config/camera_config.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'cmvr/config/camera_config/camera_config.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n-cmvr/config/camera_config/camera_config.proto\x12\x0b\x63mvr.config\"\xdc\x02\n\x15RealSenseCameraConfig\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0cserialNumber\x18\x02 \x01(\t\x12\r\n\x05width\x18\x03 \x01(\x05\x12\x0e\n\x06height\x18\x04 \x01(\x05\x12\x0b\n\x03\x66ps\x18\x05 \x01(\x05\x12\r\n\x05\x63odec\x18\x06 \x01(\t\x12,\n\x0b\x63\x61mera_mode\x18\x07 \x01(\x0e\x32\x17.cmvr.config.CameraMode\x12,\n\x0bstream_mode\x18\x08 \x01(\x0e\x32\x17.cmvr.config.StreamMode\x12*\n\nalign_mode\x18\t \x01(\x0e\x32\x16.cmvr.config.AlignMode\x12\x13\n\x0b\x62uffer_size\x18\n \x01(\x05\x12\x0c\n\x04sync\x18\x0b \x01(\x08\x12\x0e\n\x06\x65nable\x18\x0c \x01(\x08\x12\x14\n\x0c\x65ncode_width\x18\r \x01(\x05\x12\x15\n\rencode_height\x18\x0e \x01(\x05\"\x93\x02\n\x0fUVCCameraConfig\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0b\n\x03usb\x18\x02 \x01(\t\x12\r\n\x05width\x18\x03 \x01(\x05\x12\x0e\n\x06height\x18\x04 \x01(\x05\x12\x0b\n\x03\x66ps\x18\x05 \x01(\x05\x12\r\n\x05\x63odec\x18\x06 \x01(\t\x12,\n\x0b\x63\x61mera_mode\x18\x07 \x01(\x0e\x32\x17.cmvr.config.CameraMode\x12,\n\x0bstream_mode\x18\x08 \x01(\x0e\x32\x17.cmvr.config.StreamMode\x12\x13\n\x0b\x62uffer_size\x18\t \x01(\x05\x12\x0e\n\x06\x65nable\x18\n \x01(\x08\x12\x14\n\x0c\x65ncode_width\x18\x0b \x01(\x05\x12\x15\n\rencode_height\x18\x0c \x01(\x05\"\x80\x01\n\x0c\x43\x61meraConfig\x12\x31\n\x0buvc_cameras\x18\x01 \x03(\x0b\x32\x1c.cmvr.config.UVCCameraConfig\x12=\n\x11realsense_cameras\x18\x02 \x03(\x0b\x32\".cmvr.config.RealSenseCameraConfig*:\n\nCameraMode\x12\x15\n\x11\x43\x41MERA_MODE_PHOTO\x10\x00\x12\x15\n\x11\x43\x41MERA_MODE_VIDEO\x10\x01*N\n\nStreamMode\x12\x13\n\x0fSTREAM_MODE_RGB\x10\x00\x12\x14\n\x10STREAM_MODE_RGBD\x10\x01\x12\x15\n\x11STREAM_MODE_DEPTH\x10\x02*7\n\tAlignMode\x12\x14\n\x10\x41LIGN_MODE_COLOR\x10\x00\x12\x14\n\x10\x41LIGN_MODE_DEPTH\x10\x01\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cmvr.config.camera_config.camera_config_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_CAMERAMODE']._serialized_start=822 + _globals['_CAMERAMODE']._serialized_end=880 + _globals['_STREAMMODE']._serialized_start=882 + _globals['_STREAMMODE']._serialized_end=960 + _globals['_ALIGNMODE']._serialized_start=962 + _globals['_ALIGNMODE']._serialized_end=1017 + _globals['_REALSENSECAMERACONFIG']._serialized_start=63 + _globals['_REALSENSECAMERACONFIG']._serialized_end=411 + _globals['_UVCCAMERACONFIG']._serialized_start=414 + _globals['_UVCCAMERACONFIG']._serialized_end=689 + _globals['_CAMERACONFIG']._serialized_start=692 + _globals['_CAMERACONFIG']._serialized_end=820 +# @@protoc_insertion_point(module_scope) diff --git a/generated/cmvr/config/camera_config/camera_config_pb2_grpc.py b/generated/cmvr/config/camera_config/camera_config_pb2_grpc.py new file mode 100644 index 0000000..80a33da --- /dev/null +++ b/generated/cmvr/config/camera_config/camera_config_pb2_grpc.py @@ -0,0 +1,24 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + + +GRPC_GENERATED_VERSION = '1.76.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in cmvr/config/camera_config/camera_config_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) diff --git a/generated/cmvr/config/dexhand_config/__init__.py b/generated/cmvr/config/dexhand_config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/generated/cmvr/config/dexhand_config/dexhand_config_pb2.py b/generated/cmvr/config/dexhand_config/dexhand_config_pb2.py new file mode 100644 index 0000000..35bf933 --- /dev/null +++ b/generated/cmvr/config/dexhand_config/dexhand_config_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: cmvr/config/dexhand_config/dexhand_config.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'cmvr/config/dexhand_config/dexhand_config.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n/cmvr/config/dexhand_config/dexhand_config.proto\x12\x0b\x63mvr.config\"M\n\x15RH56DFTPDexHandConfig\x12\n\n\x02id\x18\x01 \x01(\t\x12\n\n\x02ip\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\x05\x12\x0e\n\x06\x65nable\x18\x04 \x01(\x08\"N\n\rDexHandConfig\x12=\n\x11rh56dftp_dexhands\x18\x01 \x03(\x0b\x32\".cmvr.config.RH56DFTPDexHandConfigb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cmvr.config.dexhand_config.dexhand_config_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_RH56DFTPDEXHANDCONFIG']._serialized_start=64 + _globals['_RH56DFTPDEXHANDCONFIG']._serialized_end=141 + _globals['_DEXHANDCONFIG']._serialized_start=143 + _globals['_DEXHANDCONFIG']._serialized_end=221 +# @@protoc_insertion_point(module_scope) diff --git a/generated/cmvr/config/dexhand_config/dexhand_config_pb2_grpc.py b/generated/cmvr/config/dexhand_config/dexhand_config_pb2_grpc.py new file mode 100644 index 0000000..48d4f5e --- /dev/null +++ b/generated/cmvr/config/dexhand_config/dexhand_config_pb2_grpc.py @@ -0,0 +1,24 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + + +GRPC_GENERATED_VERSION = '1.76.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in cmvr/config/dexhand_config/dexhand_config_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) diff --git a/generated/cmvr/config/microphone_config/__init__.py b/generated/cmvr/config/microphone_config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/generated/cmvr/config/microphone_config/microphone_config_pb2.py b/generated/cmvr/config/microphone_config/microphone_config_pb2.py new file mode 100644 index 0000000..80620f7 --- /dev/null +++ b/generated/cmvr/config/microphone_config/microphone_config_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: cmvr/config/microphone_config/microphone_config.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'cmvr/config/microphone_config/microphone_config.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n5cmvr/config/microphone_config/microphone_config.proto\x12\x0b\x63mvr.config\"\x80\x01\n\x16\x46\x46MpegMicroPhoneConfig\x12\n\n\x02id\x18\x01 \x01(\t\x12\x10\n\x08\x63hannels\x18\x02 \x01(\x05\x12\x12\n\nsampleRate\x18\x03 \x01(\x05\x12\x0e\n\x06volume\x18\x04 \x01(\x05\x12\x0e\n\x06\x65nable\x18\x05 \x01(\x08\x12\x14\n\x0cinput_device\x18\x06 \x01(\t\"S\n\x10MicroPhoneConfig\x12?\n\x12\x66\x66mpeg_microphones\x18\x01 \x03(\x0b\x32#.cmvr.config.FFMpegMicroPhoneConfigb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cmvr.config.microphone_config.microphone_config_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_FFMPEGMICROPHONECONFIG']._serialized_start=71 + _globals['_FFMPEGMICROPHONECONFIG']._serialized_end=199 + _globals['_MICROPHONECONFIG']._serialized_start=201 + _globals['_MICROPHONECONFIG']._serialized_end=284 +# @@protoc_insertion_point(module_scope) diff --git a/generated/cmvr/config/microphone_config/microphone_config_pb2_grpc.py b/generated/cmvr/config/microphone_config/microphone_config_pb2_grpc.py new file mode 100644 index 0000000..a6a24d6 --- /dev/null +++ b/generated/cmvr/config/microphone_config/microphone_config_pb2_grpc.py @@ -0,0 +1,24 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + + +GRPC_GENERATED_VERSION = '1.76.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in cmvr/config/microphone_config/microphone_config_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) diff --git a/generated/cmvr/config/pinocchio_dls_ik_config_pb2.py b/generated/cmvr/config/pinocchio_dls_ik_config_pb2.py new file mode 100644 index 0000000..58a880d --- /dev/null +++ b/generated/cmvr/config/pinocchio_dls_ik_config_pb2.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: cmvr/config/pinocchio_dls_ik_config.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'cmvr/config/pinocchio_dls_ik_config.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n)cmvr/config/pinocchio_dls_ik_config.proto') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cmvr.config.pinocchio_dls_ik_config_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None +# @@protoc_insertion_point(module_scope) diff --git a/generated/cmvr/config/pinocchio_dls_ik_config_pb2_grpc.py b/generated/cmvr/config/pinocchio_dls_ik_config_pb2_grpc.py new file mode 100644 index 0000000..5c56c92 --- /dev/null +++ b/generated/cmvr/config/pinocchio_dls_ik_config_pb2_grpc.py @@ -0,0 +1,24 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + + +GRPC_GENERATED_VERSION = '1.76.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in cmvr/config/pinocchio_dls_ik_config_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) diff --git a/generated/cmvr/config/pinocchio_qp_ik_solver_config_pb2.py b/generated/cmvr/config/pinocchio_qp_ik_solver_config_pb2.py new file mode 100644 index 0000000..130cca9 --- /dev/null +++ b/generated/cmvr/config/pinocchio_qp_ik_solver_config_pb2.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: cmvr/config/pinocchio_qp_ik_solver_config.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'cmvr/config/pinocchio_qp_ik_solver_config.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n/cmvr/config/pinocchio_qp_ik_solver_config.proto\x12\x0b\x63mvr.config\"\xcd\x01\n\x13PinocchioQpIKConfig\x12\x11\n\turdf_path\x18\x01 \x01(\t\x12\x17\n\x0f\x62\x61se_frame_name\x18\x02 \x01(\t\x12\x19\n\x11\x66lange_frame_name\x18\x03 \x01(\t\x12\x16\n\x0etcp_frame_name\x18\x04 \x01(\t\x12\x0e\n\x06lambda\x18\x05 \x01(\x01\x12\x10\n\x08w_posrot\x18\x06 \x01(\x01\x12\x11\n\tmax_iters\x18\x07 \x01(\x05\x12\x0b\n\x03tol\x18\x08 \x01(\x01\x12\x15\n\rqp_time_limit\x18\t \x01(\x01\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cmvr.config.pinocchio_qp_ik_solver_config_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_PINOCCHIOQPIKCONFIG']._serialized_start=65 + _globals['_PINOCCHIOQPIKCONFIG']._serialized_end=270 +# @@protoc_insertion_point(module_scope) diff --git a/generated/cmvr/config/pinocchio_qp_ik_solver_config_pb2_grpc.py b/generated/cmvr/config/pinocchio_qp_ik_solver_config_pb2_grpc.py new file mode 100644 index 0000000..68c6d43 --- /dev/null +++ b/generated/cmvr/config/pinocchio_qp_ik_solver_config_pb2_grpc.py @@ -0,0 +1,24 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + + +GRPC_GENERATED_VERSION = '1.76.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in cmvr/config/pinocchio_qp_ik_solver_config_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) diff --git a/generated/cmvr/config/speaker_config/__init__.py b/generated/cmvr/config/speaker_config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/generated/cmvr/config/speaker_config/speaker_conifg_pb2.py b/generated/cmvr/config/speaker_config/speaker_conifg_pb2.py new file mode 100644 index 0000000..6eae730 --- /dev/null +++ b/generated/cmvr/config/speaker_config/speaker_conifg_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: cmvr/config/speaker_config/speaker_conifg.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'cmvr/config/speaker_config/speaker_conifg.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n/cmvr/config/speaker_config/speaker_conifg.proto\x12\x0b\x63mvr.config\"1\n\x13\x46\x46MpegSpeakerConfig\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0e\n\x06\x65nable\x18\x02 \x01(\x08\"J\n\rSpeakerConfig\x12\x39\n\x0f\x66\x66mpeg_speakers\x18\x01 \x03(\x0b\x32 .cmvr.config.FFMpegSpeakerConfigb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cmvr.config.speaker_config.speaker_conifg_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_FFMPEGSPEAKERCONFIG']._serialized_start=64 + _globals['_FFMPEGSPEAKERCONFIG']._serialized_end=113 + _globals['_SPEAKERCONFIG']._serialized_start=115 + _globals['_SPEAKERCONFIG']._serialized_end=189 +# @@protoc_insertion_point(module_scope) diff --git a/generated/cmvr/config/speaker_config/speaker_conifg_pb2_grpc.py b/generated/cmvr/config/speaker_config/speaker_conifg_pb2_grpc.py new file mode 100644 index 0000000..fcf45ff --- /dev/null +++ b/generated/cmvr/config/speaker_config/speaker_conifg_pb2_grpc.py @@ -0,0 +1,24 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + + +GRPC_GENERATED_VERSION = '1.76.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in cmvr/config/speaker_config/speaker_conifg_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) diff --git a/generated/cmvr/config/touch_screen_app_config/__init__.py b/generated/cmvr/config/touch_screen_app_config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/generated/cmvr/config/touch_screen_app_config/touch_screen_app_config_pb2.py b/generated/cmvr/config/touch_screen_app_config/touch_screen_app_config_pb2.py new file mode 100644 index 0000000..c543ace --- /dev/null +++ b/generated/cmvr/config/touch_screen_app_config/touch_screen_app_config_pb2.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: cmvr/config/touch_screen_app_config/touch_screen_app_config.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'cmvr/config/touch_screen_app_config/touch_screen_app_config.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\nAcmvr/config/touch_screen_app_config/touch_screen_app_config.proto\x12\x0b\x63mvr.config\"S\n\x0fTouchScreenVec3\x12\x0e\n\x01x\x18\x01 \x01(\x01H\x00\x88\x01\x01\x12\x0e\n\x01y\x18\x02 \x01(\x01H\x01\x88\x01\x01\x12\x0e\n\x01z\x18\x03 \x01(\x01H\x02\x88\x01\x01\x42\x04\n\x02_xB\x04\n\x02_yB\x04\n\x02_z\"\xa3\x01\n\x11TouchScreenTwist6\x12\x0f\n\x02vx\x18\x01 \x01(\x01H\x00\x88\x01\x01\x12\x0f\n\x02vy\x18\x02 \x01(\x01H\x01\x88\x01\x01\x12\x0f\n\x02vz\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12\x0f\n\x02wx\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x0f\n\x02wy\x18\x05 \x01(\x01H\x04\x88\x01\x01\x12\x0f\n\x02wz\x18\x06 \x01(\x01H\x05\x88\x01\x01\x42\x05\n\x03_vxB\x05\n\x03_vyB\x05\n\x03_vzB\x05\n\x03_wxB\x05\n\x03_wyB\x05\n\x03_wz\"\xff\x01\n\x13TouchScreenMatrix3d\x12\x10\n\x03m00\x18\x01 \x01(\x01H\x00\x88\x01\x01\x12\x10\n\x03m01\x18\x02 \x01(\x01H\x01\x88\x01\x01\x12\x10\n\x03m02\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12\x10\n\x03m10\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x10\n\x03m11\x18\x05 \x01(\x01H\x04\x88\x01\x01\x12\x10\n\x03m12\x18\x06 \x01(\x01H\x05\x88\x01\x01\x12\x10\n\x03m20\x18\x07 \x01(\x01H\x06\x88\x01\x01\x12\x10\n\x03m21\x18\x08 \x01(\x01H\x07\x88\x01\x01\x12\x10\n\x03m22\x18\t \x01(\x01H\x08\x88\x01\x01\x42\x06\n\x04_m00B\x06\n\x04_m01B\x06\n\x04_m02B\x06\n\x04_m10B\x06\n\x04_m11B\x06\n\x04_m12B\x06\n\x04_m20B\x06\n\x04_m21B\x06\n\x04_m22\"]\n\x19TouchScreenInitJointPoint\x12\x17\n\njoint_name\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03rad\x18\x02 \x01(\x01H\x01\x88\x01\x01\x42\r\n\x0b_joint_nameB\x06\n\x04_rad\"\xba\x15\n\x14TouchScreenAppConfig\x12\x15\n\x08robot_id\x18( \x01(\tH\x00\x88\x01\x01\x12\x17\n\ndexhand_id\x18) \x01(\tH\x01\x88\x01\x01\x12\x16\n\tcamera_id\x18* \x01(\tH\x02\x88\x01\x01\x12\"\n\x15move_to_init_position\x18+ \x01(\x08H\x03\x88\x01\x01\x12\x44\n\x14init_joint_positions\x18, \x03(\x0b\x32&.cmvr.config.TouchScreenInitJointPoint\x12\x1b\n\x0einit_movej_vel\x18- \x01(\x01H\x04\x88\x01\x01\x12\x1b\n\x0einit_movej_acc\x18. \x01(\x01H\x05\x88\x01\x01\x12/\n\"move_to_init_position_before_start\x18/ \x01(\x08H\x06\x88\x01\x01\x12\x16\n\turdf_path\x18\x01 \x01(\tH\x07\x88\x01\x01\x12\x16\n\tbase_link\x18\x02 \x01(\tH\x08\x88\x01\x01\x12\x18\n\x0b\x66lange_link\x18\x03 \x01(\tH\t\x88\x01\x01\x12\x18\n\x0b\x63\x61mera_link\x18\x04 \x01(\tH\n\x88\x01\x01\x12\x17\n\ntag_size_m\x18\x05 \x01(\x01H\x0b\x88\x01\x01\x12>\n\x0c\x64\x65pth_policy\x18\x06 \x01(\x0e\x32#.cmvr.config.TouchScreenDepthPolicyH\x0c\x88\x01\x01\x12K\n\x13target_point_method\x18\x07 \x01(\x0e\x32).cmvr.config.TouchScreenTargetPointMethodH\r\x88\x01\x01\x12<\n\x16hover_target_in_camera\x18\x08 \x01(\x0b\x32\x1c.cmvr.config.TouchScreenVec3\x12\x16\n\ttarget_rx\x18\t \x01(\x01H\x0e\x88\x01\x01\x12\x16\n\ttarget_ry\x18\n \x01(\x01H\x0f\x88\x01\x01\x12\x16\n\ttarget_rz\x18\x0b \x01(\x01H\x10\x88\x01\x01\x12\x18\n\x0bibvs_lambda\x18\x0c \x01(\x01H\x11\x88\x01\x01\x12\x14\n\x07ibvs_mu\x18\r \x01(\x01H\x12\x88\x01\x01\x12\x1a\n\ribvs_qdot_max\x18\x0e \x01(\x01H\x13\x88\x01\x01\x12\x32\n\nibvs_vmax6\x18\x0f \x01(\x0b\x32\x1e.cmvr.config.TouchScreenTwist6\x12)\n\x1c\x65nable_joint_limit_avoidance\x18\x10 \x01(\x08H\x14\x88\x01\x01\x12\'\n\x1ajoint_limit_avoidance_gain\x18\x11 \x01(\x01H\x15\x88\x01\x01\x12/\n\"joint_limit_avoidance_margin_ratio\x18\x12 \x01(\x01H\x16\x88\x01\x01\x12+\n\x1ejoint_limit_avoidance_max_push\x18\x13 \x01(\x01H\x17\x88\x01\x01\x12:\n\x10r_camera_to_visp\x18\x14 \x01(\x0b\x32 .cmvr.config.TouchScreenMatrix3d\x12:\n\x10r_camera_to_urdf\x18\x15 \x01(\x0b\x32 .cmvr.config.TouchScreenMatrix3d\x12\x1b\n\x13\x63ontrol_joint_names\x18\x16 \x03(\t\x12!\n\x14\x61lign_xy_threshold_m\x18\x17 \x01(\x01H\x18\x88\x01\x01\x12 \n\x13\x61lign_z_threshold_m\x18\x18 \x01(\x01H\x19\x88\x01\x01\x12$\n\x17\x61lign_rot_threshold_rad\x18\x19 \x01(\x01H\x1a\x88\x01\x01\x12 \n\x13\x61lign_stable_frames\x18\x1a \x01(\x05H\x1b\x88\x01\x01\x12\x1c\n\x0f\x61lign_timeout_s\x18\x1b \x01(\x01H\x1c\x88\x01\x01\x12&\n\x19pause_after_align_reached\x18\x1c \x01(\x08H\x1d\x88\x01\x01\x12\x38\n\x10touch_twist_base\x18\x1d \x01(\x0b\x32\x1e.cmvr.config.TouchScreenTwist6\x12\x1f\n\x12touch_acceleration\x18\x1e \x01(\x01H\x1e\x88\x01\x01\x12%\n\x18touch_forward_duration_s\x18\x1f \x01(\x01H\x1f\x88\x01\x01\x12\x19\n\x0c\x64well_time_s\x18 \x01(\x01H \x88\x01\x01\x12:\n\x12retract_twist_base\x18! \x01(\x0b\x32\x1e.cmvr.config.TouchScreenTwist6\x12!\n\x14retract_acceleration\x18\" \x01(\x01H!\x88\x01\x01\x12\x1f\n\x12retract_duration_s\x18# \x01(\x01H\"\x88\x01\x01\x12?\n\x0etactile_finger\x18$ \x01(\x0e\x32\".cmvr.config.TouchScreenFingerTypeH#\x88\x01\x01\x12\x42\n\x0etactile_region\x18% \x01(\x0e\x32%.cmvr.config.TouchScreenTactileRegionH$\x88\x01\x01\x12+\n\x1etactile_pressure_sum_threshold\x18& \x01(\x01H%\x88\x01\x01\x12,\n\x1ftactile_pressure_peak_threshold\x18\' \x01(\x01H&\x88\x01\x01\x42\x0b\n\t_robot_idB\r\n\x0b_dexhand_idB\x0c\n\n_camera_idB\x18\n\x16_move_to_init_positionB\x11\n\x0f_init_movej_velB\x11\n\x0f_init_movej_accB%\n#_move_to_init_position_before_startB\x0c\n\n_urdf_pathB\x0c\n\n_base_linkB\x0e\n\x0c_flange_linkB\x0e\n\x0c_camera_linkB\r\n\x0b_tag_size_mB\x0f\n\r_depth_policyB\x16\n\x14_target_point_methodB\x0c\n\n_target_rxB\x0c\n\n_target_ryB\x0c\n\n_target_rzB\x0e\n\x0c_ibvs_lambdaB\n\n\x08_ibvs_muB\x10\n\x0e_ibvs_qdot_maxB\x1f\n\x1d_enable_joint_limit_avoidanceB\x1d\n\x1b_joint_limit_avoidance_gainB%\n#_joint_limit_avoidance_margin_ratioB!\n\x1f_joint_limit_avoidance_max_pushB\x17\n\x15_align_xy_threshold_mB\x16\n\x14_align_z_threshold_mB\x1a\n\x18_align_rot_threshold_radB\x16\n\x14_align_stable_framesB\x12\n\x10_align_timeout_sB\x1c\n\x1a_pause_after_align_reachedB\x15\n\x13_touch_accelerationB\x1b\n\x19_touch_forward_duration_sB\x0f\n\r_dwell_time_sB\x17\n\x15_retract_accelerationB\x15\n\x13_retract_duration_sB\x11\n\x0f_tactile_fingerB\x11\n\x0f_tactile_regionB!\n\x1f_tactile_pressure_sum_thresholdB\"\n _tactile_pressure_peak_threshold*\x89\x01\n\x16TouchScreenDepthPolicy\x12\"\n\x1eTOUCH_SCREEN_DEPTH_POLICY_NONE\x10\x00\x12$\n TOUCH_SCREEN_DEPTH_POLICY_PREFER\x10\x01\x12%\n!TOUCH_SCREEN_DEPTH_POLICY_REQUIRE\x10\x02*\x80\x01\n\x1cTouchScreenTargetPointMethod\x12.\n*TOUCH_SCREEN_TARGET_POINT_METHOD_TAG_PLANE\x10\x00\x12\x30\n,TOUCH_SCREEN_TARGET_POINT_METHOD_DEPTH_IMAGE\x10\x01*\xcb\x01\n\x15TouchScreenFingerType\x12\"\n\x1eTOUCH_SCREEN_FINGER_TYPE_PINKY\x10\x00\x12!\n\x1dTOUCH_SCREEN_FINGER_TYPE_RING\x10\x01\x12#\n\x1fTOUCH_SCREEN_FINGER_TYPE_MIDDLE\x10\x02\x12\"\n\x1eTOUCH_SCREEN_FINGER_TYPE_INDEX\x10\x03\x12\"\n\x1eTOUCH_SCREEN_FINGER_TYPE_THUMB\x10\x04*\xea\x01\n\x18TouchScreenTactileRegion\x12#\n\x1fTOUCH_SCREEN_TACTILE_REGION_TIP\x10\x00\x12&\n\"TOUCH_SCREEN_TACTILE_REGION_FINGER\x10\x01\x12#\n\x1fTOUCH_SCREEN_TACTILE_REGION_PAD\x10\x02\x12.\n*TOUCH_SCREEN_TACTILE_REGION_TIP_AND_FINGER\x10\x03\x12,\n(TOUCH_SCREEN_TACTILE_REGION_THUMB_MIDDLE\x10\x04\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cmvr.config.touch_screen_app_config.touch_screen_app_config_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_TOUCHSCREENDEPTHPOLICY']._serialized_start=3436 + _globals['_TOUCHSCREENDEPTHPOLICY']._serialized_end=3573 + _globals['_TOUCHSCREENTARGETPOINTMETHOD']._serialized_start=3576 + _globals['_TOUCHSCREENTARGETPOINTMETHOD']._serialized_end=3704 + _globals['_TOUCHSCREENFINGERTYPE']._serialized_start=3707 + _globals['_TOUCHSCREENFINGERTYPE']._serialized_end=3910 + _globals['_TOUCHSCREENTACTILEREGION']._serialized_start=3913 + _globals['_TOUCHSCREENTACTILEREGION']._serialized_end=4147 + _globals['_TOUCHSCREENVEC3']._serialized_start=82 + _globals['_TOUCHSCREENVEC3']._serialized_end=165 + _globals['_TOUCHSCREENTWIST6']._serialized_start=168 + _globals['_TOUCHSCREENTWIST6']._serialized_end=331 + _globals['_TOUCHSCREENMATRIX3D']._serialized_start=334 + _globals['_TOUCHSCREENMATRIX3D']._serialized_end=589 + _globals['_TOUCHSCREENINITJOINTPOINT']._serialized_start=591 + _globals['_TOUCHSCREENINITJOINTPOINT']._serialized_end=684 + _globals['_TOUCHSCREENAPPCONFIG']._serialized_start=687 + _globals['_TOUCHSCREENAPPCONFIG']._serialized_end=3433 +# @@protoc_insertion_point(module_scope) diff --git a/generated/cmvr/config/touch_screen_app_config/touch_screen_app_config_pb2_grpc.py b/generated/cmvr/config/touch_screen_app_config/touch_screen_app_config_pb2_grpc.py new file mode 100644 index 0000000..cf0475d --- /dev/null +++ b/generated/cmvr/config/touch_screen_app_config/touch_screen_app_config_pb2_grpc.py @@ -0,0 +1,24 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + + +GRPC_GENERATED_VERSION = '1.76.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in cmvr/config/touch_screen_app_config/touch_screen_app_config_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) diff --git a/generated_apriltags/1.0x1.0cm/1.ai b/generated_apriltags/1.0x1.0cm/1.ai new file mode 100644 index 0000000000000000000000000000000000000000..9dcf321dbd8cfb7be99029e0200872aacd07ba82 GIT binary patch literal 1612 zcmZuxe{2(F7kqP3x^^9n)Rh&K1g5#Rx2~hR8+YGK zhpyUX0}><*Mus|m{Xql;h0KAIfEY~#wrpq!5zrAg1aSrfi&{1}_51o`>sIfd>pgwn z?|a|peV$k4ve`>$jgC+q{}CKJL(qT%qNkoHEd`{D^Yb#$BBUCSYnmH*Kw3G-1w}tF zn+aa<;Xq?bLPZExx*-R7po=HjMF9dP{@cYt$V&oX@h3sLB+={UAy|*@vDwl2dTZ*>dRA1}uUgLK)ChyaFq#h8GZE zil5=+eF3gq451ougx08Gbb1}D*PFDad0(gi7bl_QI2{C8&C6nw4CD<>*z%>B{m?P>ST?&gpCUij6qY4B>s z(URgfCf{_ARg~TRx9FO(z31ir?ova}jmoF?4-5?!t&y8nj16||er9x4AoFeQ78r3< z$YUqgW}o@)=bM!~H-Gs_V6U^6%lWWJKDS97I#^ux!B6GW zZ_C-Ma1JheZ`YIGC|%25J1Gyfb$Fk+x-YMl_)Iy$j;dRKSvb8dqJYYY2NRE;zhlYV zmLZ?tGcuk%vUJ0a?dr9m$1+}JZ}ITTjlOqVb41{~mS<_|KBRPi)VSr5_M!a3(-#&j zdb&AFec^WQ{ngp8s8(KHoR@#{*nx8M)S;}Z-1}Zadp5Esf2o|iLD5-A9QeC^s!QRV z%FOuw@NN(4>e6AQbT3a4tLMRRR(NpHlF^4pYx>1qmwP>5y>)J=AS!VeIV7)>#07-cjep613;Cfxpf%9IHMuG z7A=J~()>6!Esmy4DZN373j}#7w)P!Dtq5ZCL~E^D6tN6qE6yF*%p(#pu`=W&7^9We NvJ9b8RaDuD{{S0D1gQW3 literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.0x1.0cm/10.ai b/generated_apriltags/1.0x1.0cm/10.ai new file mode 100644 index 0000000000000000000000000000000000000000..2acad88935ab81c92742de72176fd8b167b744b6 GIT binary patch literal 1595 zcmZuxdrVtZ9F~oNULS)vHp3(wU=?uKdru#?4=1laOx*)07N;ADx9w>ulzZ{^pty`= zTNc?C*fVn*q9JoeP3IP)Ok9F-bG|^$9%0jIBI*|Qh?}|)9L>aYdRwTi_n+JQ_55Dn z`M#g3)@pwYs&$O&@J}FooPmG?lDnB%whXYff**x|h9FMBKK*hV0<1;ALQwL9@^S`= zUV6}&5Mb-TPu61rBcM})?2?FqR{32kV2orD=;u4o4 zt7C6V?Ck)JfUR#5yaCYL5MP6B?shtK)Tgo;PVsNnfOaT0thk3LOmVL?NkN_7Hw30W!zW*h+_?}Drw4~zFV%dw>xUaF z8uG4K4*V5zM2_67{&~JQ*xPwy^aJy|-}bnU`d#AX+m??%85zhMUougLcE8`fy}wtZ z-4|JW=H4gXnJ(PFbhD;dHuuaMlB+UX_6!s;I}o;xUM;u(*j0Bq0~{;3G*6?->&{=_ zcX;?<9=^7=ZA<3GUVQ8vzi#|&x^&{vhBSMwuCVX%o#9nm7Y;!9x#-T{5}x|9Gi^uL zl?PiY=ZyCJy`*9+oN-TKZpp-ZcaLWyX3ANCvUg2B{K$R3_O9RCxp&c~xjD}tswz4; ze13TO3)>j(Y=5?(I<$7uW(^P36x?=v0ghY)leS9Z#nWYV8#By0_hv)(L)SNM+QvWC z)zE#b|BuF3T+d$mw7_>d_w~^YAC2XN?<%@KH~YK1uUv1h{w-}V^SM2ttnW`jywwJN znO=Oxb@1znEL-}5Qt6bTruc$m?A-OE=@m{}$Q5|12Y&TOwk93aSU>h81}yyXEYF+ zlQC0DEN9aH4`v|5C&ijfWML#>9G_Z4t0y}t>6}rYdQQWo<};E>lVTxfNSF=FLLi9b t=$3bgJ_$tclWZ}kBvBbeH(GsQ9U>%BVnbMvag{{na7^a%g} literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.0x1.0cm/11.ai b/generated_apriltags/1.0x1.0cm/11.ai new file mode 100644 index 0000000000000000000000000000000000000000..409021b3a968e071660085d921249e17c069656a GIT binary patch literal 1621 zcmZWpZA@EL80KS)aaj~aC4M9xV;E)3-g|D_yA~0lWt{}(x^`qb0(#qfTFT13&Fuj@ z5I0DFhVl9h7Ze#Z=n3AE}W5XF_R<|tl6`pQ5 z7MTAoG}&_hqVO;C0RL3p=9;UQnnJI(1^>7_f1_9Z_1(pmZ(0Kvx^H*R^iFh+6%{7k z8Sj7bx0_wfJ01)TQHuwR-34=%AEcV^v}au3u*dc26Ze8|PIg1{^BvvSQj*Ym_rCsi z!zf*~?w2cl!)XVbzyHF0vNE_hJM+WD?wJM{0;ByIxhE*?@xcc3B7b^ zWMk?hd*A)&wERO7mqiU_JvHe(=G@aTsm2Ldb2X4HaU26H=`^->i;r3SVoD%%(6^k125G%eq!*Ckh{lEJ2`GpY1$^q9Oq!ekDk|BDHXS;#fKo}4VF1vSnt>T*BT5J` zsWGZW!ONG)VUz>5U^t!Lz#5D!Yhq0E-cSN|UO~<(9R%qR19DJtBYrReGyGu663D>^ z#o4rZeW-pw+I)y0i|CdG#AlT#d0;)DRgzH|0jP+)p9sG-(D|kG8I@_BnkHV@-8#XA zI-AP-#&rLr>?_GUd3n{qr(0iYJGg==SVNb!j=vpNy7rqlO^r=XUHEHz)3(VYkv7kv zJ->%MCH~Qpp1~^*H089=FMB@h|DfqqZYcQE#I2!I&ePA2zIrs&_wGkO-1&CXu=~-8 z-68)sk9;McJJXeZckA9?t9mZHvgl;h;)N^PE3auL^RsgPs6SvBTyLwBjduC%>xDTZUgqpFM@r$a z)%JJ6=QR%=Te4(Ps&mKv&4;h%H$Pi;qGP=C`X?i+ewz+H)7*bCGCdw4r{8%k6VHFF zSurI^Zkp>$acpuf422fa zd^v(9 I>joR~AId!V8~^|S literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.0x1.0cm/13.ai b/generated_apriltags/1.0x1.0cm/13.ai new file mode 100644 index 0000000000000000000000000000000000000000..665d46d6fd1fcde9435286b15526b086744ccea4 GIT binary patch literal 1604 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 A2><{9 literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.0x1.0cm/16.ai b/generated_apriltags/1.0x1.0cm/16.ai new file mode 100644 index 0000000000000000000000000000000000000000..2572c5604d127f1a7f9a16950af9a5faae0e019c GIT binary patch literal 1608 zcmZuxe@qi+7)DJQImysLjBIXx#xQ|l@2>a59%C46X`z7DmM|R=J$lztEO*`YgLH0^ zjjY?8OKglYThL7Fh%wvTqG40Epp0Y~Ci6#@h(timMnGe>7~R~|ZQs`)Q0VTT>plJ6 z_j}*xeV#YZZnmtZ^(>M1`8Dua4?zP8sEy4;NeLkBqDKw@140~ttbNTd1JWcy(WiPq zX(=Hq5axq&i8QDFekRICCMszz22 zAjERi$dXrFrv_0C_=LfzXIP%)cwR6FbG}dkc2PsYF**p+AqUh}&4u{E1WfaTDN7;; zUzA|e;`O2a0cr6eg3Pi@l@OoRvf_bFfR2%j+6X{R7F&qmf$sfP&HdD(cm6C6tp~To z&38{^o$I+1c_L$Kr8&f3Y&fIY-0cH*BjLW`isjEux!gt9Mt|!a|K{6)p)Y!0cRYLH zYwNG~8g@3qy`42WYJ9xo=ldJS$FDp*I@akuZY-UO1kWE? zV6XT5aqkW1t@4*9|1BI|7%F~wu(L#$IbzK|_~TDCh3v$odB@h6Y|4AtUAnZvrI*vo zPmi6-`R>BWmXCWb{W50V({c9rE$jUJQ+tad`4wbEuHInJo~+IgCf@9vU;V?#KIhsh zaCZCO++dx~%j8HI`bDH1tV`=_Tb&yLp@n~Ow%S_u=!4UQ$@X;J((Byg6TA2iRt!BF z8BNcTg7a4N*JXX!-R>gzuR6jjm*rd^R)j;Fni|RmU%hJU8ikq1ZVvBClY6D#>noQy z!#8wqXFgMqe@9yU>6Z(h%BY^GN`J9w=lPtC{=cN%-hD{My#*BEeYZ7uPqXG7ysSIIp^Uc_cbcujQ zI!lrowpB_(Y?c%dTP6j>0!&7-^z6VfmA zs)`w}fqZkZfimzELmO$HV<`3o%D9T6RuvGjbE*dNYeeYxsnF+b1Z|9-(=$b&2||CM zm?T@K|I(|P2L+*}A+@@Yi;|=Kk{dLNuI*?7@eBn?yaYAbO;CVl32OQQJjXE{aD!=# zMoi%cC<%Gv&-+~f literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.0x1.0cm/17.ai b/generated_apriltags/1.0x1.0cm/17.ai new file mode 100644 index 0000000000000000000000000000000000000000..c3a4aade12e19287388f4e119dd2bd3313a3be11 GIT binary patch literal 1598 zcmZuxZA?>F7NGqklJ>pi!(x7;?SS_+j4gj&dEf^1&e3l!RWyW4}d z2nO5-P8MetrO9-`IU^GmG@B4~*}{g|{Mwg+#W7|we<)11@MDZP=G-~`fI@fw+}@|> zeb4(o@AJGWM}h5mMysb(AB}_F^ArOhkgMyd4I2RM5NgE$&|$<0=!%eE1hiE^f>*8u zW-}#9ZgP-M2q+Aq&E-fyBG5;JY_fy^8~yDN5E2y$aO9Js9g6HK7ZIq$_Y~N0eldsu zZTI0j^J8yo>}>~CfG)2S+#abG7^30Z%Bt%{7Xo>a8|Pa8{_=_rfbtLbZs2D)BV!Vuk;O?CyxiO zez4}lfp72L`}gEXN7MHY3eUWl-q?32FO)mc|F+r`e(&k6b@)injX$%`nGfV}OSZmo zaHZz5@$viv#*$8PbeVdWxhXZ(S85Kw)t}uhr3~&^d1}e-{71 zw~p4|dCSt(6X3%2EvIr-mvhE0K6B#9+m^#SPvl;EbD!~5P2jOC;5%qPwl*bWBxCu2 zE~hy3@qV?Emi<+zB)EOJXRUKQQ&-npbM^GLp~|WK6|KLfk)npwZJejbuy^;dOM_|t zf4Z$lic(i-!#6+m77sO?yqI;fs^|5ITa$+j>CMemTc)gs`!|}VGkRW@D~R-CbBdWD zGCwkCJ;TP05m6n%DnV5oBUXs%VP3MtD{+xn7(kq7wO}k}xp`C3E&(uDT?qmsW=cv( zERqBgqa=YucJZsNawC>E1L$IpJAf?_agvi*FKYehHkdD)Noa<)V$~v#EEN#*YYI#{ zs53y8;TZ$RLj8KkuY+)1HWfW5D?n2!Aiq~eUQadH$!a+*n+xg?@&`;b?VI~cD=W1) z2(JovqYHZ{G0N|*0o8)50Z$;7Av=MWpd!{#5HNZQ&ON}uaV!UFz#PV4rpN=NIL1I7 z=42rT`8@3L#h4L$dl6>j{s-gnYFT)Wfes<6yP{Rgy7 B`Dg$D literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.0x1.0cm/18.ai b/generated_apriltags/1.0x1.0cm/18.ai new file mode 100644 index 0000000000000000000000000000000000000000..1c129316473ddecc3932b052d7b35652041e74a2 GIT binary patch literal 1599 zcmZuxdrVVT7*_%TZWb+z;1;rY-0XPDy|=e5g*Ya(6saOzi_VN0bK9P_w=4Iu+k>kx z7rH2WndlyBmcZg35sZ&WCT_w;bX(Yf1;LEq2oko*$S`4JiLzjt?VLWK(A|G-@2}_k z&iDPk-|thJ4aTi(ftpfYm;mRyDHbq5vU#X&+W>9mUC;+q7_k7l=1@HZw4O)2S8{=p z5(UI+)ZrkbBqG3tf17!PpezCn`J`yGEZMCPfm(cz!HDxiKLT`R z9loI)ZO=4Bilrh}p_&?hy>cFYeUV2K|>SqwSk zq9~h2!HfF`w9$(RGC;fJznML~*Qr6zw~V5EVUaof^p`(PVMwhH~-p~lrg*U*H1ftctu%h2^=+T?u8|z zy^~*V`06I7$mp*)w(+|?!P=I+6PgwLQ)h3j`@oP39K%P3Qm48u+S1nw%^QCCCU6VAZE94$-gKtFRnh$0 zGs!4Ff%;*-4|7V+G&t7tn9SS+s?g%LN!B_tk+ z1Bp}OK%%?o)q1G`>zf7iPQl^Bo(NgVLcACDes~=$m(9jBL+i0@ksyf%;{I$yv5HY? z8IIMlS`Ejj^B7$g!(`=9;d7DjRK3FqY`py?Jb_4toETpGGIUZ5z|&xs9-!4|I1O-uC5**Pkq3xTjAc}~ zt(6#~E5t@#g%#nbI86ILm=15Gm9Z?tVBS|^45P)PT!nF(_W|1T!gV0v93XKqxNQm_zUPd8WS(Tchl*;mIBlRE3 CH2{17 literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.0x1.0cm/19.ai b/generated_apriltags/1.0x1.0cm/19.ai new file mode 100644 index 0000000000000000000000000000000000000000..8be511f34c7f319d78436d41159c11864e5c191d GIT binary patch literal 1612 zcmZuxeNa?Y6elv&7dEMwlQDX&Sw2GdeeGLz3D;oPO%m8`7pEFy>h11jd9u96yuCOp zjS{FRR0A0n1x^h$4x;wqM5Z#;49+m)3^1w9W} zj3Vff#C#=yb08L+LCfp(Izg{Da>jWloPb?av2&OXigrN1Qmwi%KZJl8eh6hT1=A125G-HHeESqNn>DhF(sWL$0v$c?Z7J?!9&NZ|+GiV?UfMr+ zaQ`LYi;;w_J4=LRCH2>?q`-08m31vS-ODb&%HIxtQ1Icv{eSN5I#oOT>(trwp3%#{ zf7)T{vQ=K|_bxy4?VGzc-W_$_?g1@7Xcro=O^g{0V4@XO`tRBm*Z1Wg&bnH(`oWqTpEM4_->fTE{WWpmQoBhL?0@G%VggK-_H>?X z)F-bU*rAz7diR+%8>SvOd*rY`ZEvc-ZSwTsHz~bC{Kg}o@8Qjlr@1T7A8mi4xP4$j z`hndk+Oht`iBlJRmM_nty4KOo{Y_2dsix(k)!3J`Amd`)$3azUoYzYxp3{>RRhR0o#Q;`EF*6U)3j!~IGBAU& zm?`oAIf}6iABQmpBj#zY9b+Wr&&RU=2Q%XK&b4D17PoCK#xVMLj2G~dnrCOoj?aZ- z@J5<%C&byYj4`G*q>7Rcs-d-CAeSj1)K9$D9Ew85AhhC~QVql;5@P*GR8fdlmJ@hN KquFG$QvU%Emja&v literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.0x1.0cm/2.ai b/generated_apriltags/1.0x1.0cm/2.ai new file mode 100644 index 0000000000000000000000000000000000000000..3bfa606d2a64cacbed51f2a6b416856716c6723f GIT binary patch literal 1590 zcmZuxe@qi+7)FGcoyO_1Y+^R|5iJ!Nz5DKZ{ejL6T51>wr5y&NBwpKVDQoZW`V~8~ zfNX;V6M}QF&CEpHNQeQoW|Kv?;g31Y=4RBmxu9+=m~FU;31PT0XZC&l0bSw#x!%+F z{l52o-sgE$RAQ7xV!)bL|UZ*p%#*{ByfK^a%a*Pg&wuu2bs5mh{gn$`-2xUp+ zkc$#*TD(5oKcFo>OpsZ0$}Y@jnJ9TsEr2nSaT@`oh=QLA9X$DNsq`kZto1>FI&}8$ z<`Y||vc=mCR~MQmm$$s^@0ptBkGvmw?DB_CY{^*J#;v-n&9-x>mN^BziY2LuI`c}5z}Df#K{ka)dxu-HuA_aS@ihbGt{p|*&S^M%k4$I+pnA(cHQeak0W}@cfnyzS*JR!`r^K)SLBZE zc>VsUqpEe_#*yH;!>hvi_k7ofE8rQ)eNmAeX=!_@;e2}f ztxw)KI&gLUd)9FEx;-Q5k*+0+RV#9T4?Mb73`&K}1=5)MpH&aP5f@M_Tc|Sqc&EF1 zTx|_EXRR|nHQd=}xIfsk*wp9@UrN*eHTq6Q=B4k`_wHp)`4@xBNBW1VvXnGc#=w}S zbK%m-9iHYx)_rd;s>?dI>xa@4FO&1bp@~$q7JeV%f&1o+XAy?!=z?t6ILto%95FweyzEHWmp}%^SI~() zlbluWas!9pd=*b1enM^%FCj&AQw)GwikW$Uj^{ZZxWNntF;nCLQUZev#N+rs#u)T? zEu>;bykJr=BR>zO#~i1|88LM!7^B0ZOu;yQo_puPSO)K&lw1QhCl@mMq~4GsczvQ0 sUGOE6TL#g2;ss`tWg>&PhaH|W6{Mnd}wkD+K zBoL=Dn_(XcgD@6mI<^UseE=1sL9!3qoJ*Fe*^I^o7H8s;YIL)q)6DnnwNTspa=l-l z=Y5{{_xyfOp|i&J0@UiM!r7m|%;yvY43Jtosq%6_JNb6O4|Eu50QCOD-2$L3JmP&) zJ20CmLG+M7V^V@OfNEWcM*`3*Nj6DDfK`4wd4vR61RVLKXs0Z>T>=7)xK51?pDzRu zpzU3_W_A2+iNEdO0H9qRyvHlH1A~(OT77G$;6|V%cKAj+F!=&iq9`FhfQ-_C1$QHe z7%(YkI0TQEua*M12NIz(YFWKO&lwCRohjuDH{j%DoUG76(G7xM>XF@;A40%7KZLR* za!8>Bn>Mcx-yhI6A10_qa7!M{=RQGfM;!nvB;#xNku30CRN&;o=+(}POqMkgrQRN# zeNVV@)eP=^a&(}wu0T!A5B?q5eHPs<9nCwUTDFx2LQma>y?sj$`)`MLmV6ow+k47? zJQe)*%?GEu{$2A`SKNPNo2{bx8*^Xu^@5(hJNNI|Z@Ru5I5_-!zi)bRH5xj)3Wv8= zc1`~9*P7#e-h-(}Gc9!XZ%rEM&;3kCfdgsFO3q&&t9>H$vr4n5ekqu{sn}h&BRl8X z;=sq%*FV@5G53G>Sg)h(#IXg<*@5ZWiPJl)oGY@rBDZpBr1<^SCqr98s=Umc5yw2Q z%8Yny5!IcH!1r%WW~whe?VKCWuk0S|*IcwVj*L9N{X;OaWsQ4w=-0eH;nt25Ly^Oy z%vXn^T=b(|`zEf1b6m$)GTu=&PQLQ$>SoGtVd%K~x#=5QgX$Oa$8?qB!R5KhBBv6v zeR;yuR+rT@)qFXtIiuxP*f_O~oddso<_p~rO+&#w^J->XySXf2Z8@obJTISG_u(QY0< zthgl65rd^2NNke^67!^iM2d;h7O4kI9s+u=*W<@FiMgwR*il&r%7)lz8zx zr8M2O{+Cvg+i?Y zV-Pb%9v~(#$mlUA|Hl}k3Hv?;GePY4RIDTogE%BL4l+8txKeT%10H1x#&T)7jIkuW z7afz18M!nJ;=&s>kj#61f*f1^cCk$Yv3cU<)*wkl2C+rw^7aduL_(||@iL0h3U!=` LDlD|t+o=Blf5G|J literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.0x1.0cm/3.ai b/generated_apriltags/1.0x1.0cm/3.ai new file mode 100644 index 0000000000000000000000000000000000000000..6e53c0c54837911a375b3ea222d631f712951b60 GIT binary patch literal 1609 zcmZuxZA@EL7~T|&-Yf&Rg^z8nCnOzh*m}>s{h)=4mVPh--6|~E7RKbZy)9+s-g0|7 z3z6vNBugav!$}+?jzt(_sPiKX#Rx9MkKh!7F>|`OC5%6SM9qkE8{#?rfYN&Z+}@|> zeb4(o@AJHJhsCy=QE4dodpE(rFvS1}L}vqKGy>YedHEnf_T{QgQK4$~(LZC2`e6<`Vu>q4Hh$sjc7_GpJtKkI< z7@|4syxYf>ieX#>Il<~xYK=~#)#(hZA?XVj;NT=29HoPztN5T8l3bV{Lcj_?gt7#3 z$VG8BZ9YHlAJ8^GCdk6OL^tNMk{7(F9xzdoaT`G-@mwPn?i%d)Ztob(wEq1s_0sXV zvhBqcJF2dKc_QQD_`8=1O!9lJU3ng1tM~ZnCmVQK(GT20PAE9K+;Z!8+0fAah4RMm zP;o(D#;p0*`_rF1QG4sF5!u7Q;DeiU-qxwJ+xniL7#{g)LN+`1`N);&BTcPS);~KB zlyzTOn4ej)_h&D5FAvmR-ZFbcDgJeE@~F&?RKI7qeNv{kcP(`U^Sgb6`d<>zUeNXhW?{8`po(DY-r! zFYQjdnm+sLhML^BdW~nCoBv5ue$!mp`;@{oJetYE^X(?_`~i!pEhCfddZAlWY;Dh) ze7Y`gx&P|>H!|DmN=LIlp1$$e7oJlaK5E{tnklU4+x7MzLt~?x9_D>Ne|_)Lx_Q|f z*N6*ZBa8VVGDb7sG1VOhPlX84DQs zM8QJ#fWlJ5LRJUWjGobH)lgFa_4yFyE2-!?Q347(hXQ^P`F&1sOs&$Y)Vn}ELV;ir zO*gLmr4l7C4#Hc*9dcnCB}N6@9^m9$NAUz=8I%dUgeBfXLBMDzxbgsEx literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.0x1.0cm/4.ai b/generated_apriltags/1.0x1.0cm/4.ai new file mode 100644 index 0000000000000000000000000000000000000000..2b87322d9ca2cf030747054ef962ecd03c62244c GIT binary patch literal 1594 zcmZuxYfM{Z7;XUroH?N;fQgQMxFH>4&#kBJK}1R~1Z1|ORi=YvhxYV><(%&HV=21n z*i3e1ZW{2%n2SqXG+UNvByNK&Mz$Yz~JIA&JCS@UoTMGoj<4N>HEIl z`#taTyyZ@-{V7_fC(4gq0!L2~G@yXo(Mi(Dt?JE|{6As`#O z(3$m#wKMVjmPBOYi1S+D?MMMXO2F5!_sY2WBxuUySwu14u z-`!j{*;Vq-mVLGL?m+hJ&DQ6qzv>Q=#Imh@@b|z&w|#qzpUr=J;qtxLU+a=e11p9v z?;PR38{RNmva7HC>IbKne-Zw8{^Eg#IiI2RO3k6>;h`G^vv)f$Y`pZ&dB3Z1@{{VV zGgIFjyh&Oe(0^~&HC^GwnkiEY^wPyRiN8Oc% zlcqB@(_@F1hoxU5^@Yw;Wn~}MWmg1f zCYw3Fva*sH%WvAV#QZ|jpKZ^In*DvFgJ$=wLY3HguBy2Aftmh1p}059JDlgNym|MA zQrB5^-DvddZA$lz`bGjVUogdW)cWin5 zm!?fiemdX($V=Aw6K%WY0Y`h;{(`})5C82hn%%zlw7Ox1d<@GzF{6Y5Vza|T)>C}a z6fwnNY#nG%V%P==EiB3v`y?q4GYzooEM|ly?6znq(jx#GDJw->*hm=#u{|KN^h`Fc*+b(YV_$F9vn@+h5X+esGL0Ph5<7=(e48<5ImNwA_ zj-~WZQl<)us#rzD<75SBR|_y4l3^&=0XDKaPRCY(0EFR4HA!|Y{H2o>KT1Mt!rSdZ zzDdmrdwrlo@VtU1kf^XKg_o!z`UncpdV*SbfPv#!4*0+VMkA*11EeHIQ#A7O|1rvB zL?cSactm{~#&h?tCxh5L(GqKsWh{f(LURRQ6%mP;*a#F97^9VDSdJ(!w>j*@e_AQ)@Bjb+ literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.0x1.0cm/5.ai b/generated_apriltags/1.0x1.0cm/5.ai new file mode 100644 index 0000000000000000000000000000000000000000..2fade7e6e7fa11a3312fd97446a3f2b33da2738d GIT binary patch literal 1612 zcmZuxe@s(X6doGld=R!76EXhbm8=m^`+oJ6mN22Tw5wvQt($Xz?$N%MvhrT{dYN?r z5^748Et#Ug>c$d5iH4zuXkCVzW^*D#vmeoA3}?2*>B7twV5k{vVfXg6Q0U%2ukY(Q z_nz~e?>j1+$-IeHYY0`}kKn@rf(8_js%wd&B0$=Bj}QP1LhOLtx!*4U(#S*JCwai; z&4eJjaG)+Jp)3e34#-0RXp|(gBtpO{zim8(f-C|Ke-flkmYfa&f(mqx$&Ahyf)J3_ zI&^1g{B4ZCt)L2!jvC(O6+J+!q`$Pix>j&PP!KzOz8pHa0fQ(?FaT&uslbS;5kv&& zl{2;pE-zmy1yKz+g3+m2jaI{HwR%Rs=nEBK<7Jer&_R%PAt2SuPQ(u;V2&S5SrR$; zq6C{}uMhPPNV5+SWD=Z`3-MVlh#pu2XoY0dMgYnJUq=KxqaD}9E8yv}#|`OwOER+$ zyxaKv?vJXbmUd4)6W&sH;?d)`9m4$;cgs`s-H)GFRFEp)sGXImxp@Zq)NbAbGM@ptcZNF^n z>N~vrgSCxcym#Z2Cz~ue+F&?)C%5J2B@e=xYc>t#NB)`~4fB$3>Z_jK15fPw0~B|F zq3?P{I(^NEVYt6_MbXvv9E0Z9hovp!*&%-BM%vY5`>8#ZVUw5!F;)Sp5*Ri{TpjZQ#?DDd#y|t?L!$vU9N>L1MnLVxlsHKSx!ZW?_es#_tp=~NYEI2=1T_%)1BE16 zH}{uXl07I1Ee=<`6FDh4%kOf7YTo%Onn3)7yd++Nvfw5tKx+tU?g3hkV>#dka~O@7 z!VeG=7)`M$7^TxAo)*SYdffhEtl)nzJsR)AIGUo-*cM_GrA@_H4lSugak_%kS{Mp# zq{VSuN*qn;lX`r`-Fh(oQa4exx Jm2Ec@{{csa2{Zrz literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.0x1.0cm/6.ai b/generated_apriltags/1.0x1.0cm/6.ai new file mode 100644 index 0000000000000000000000000000000000000000..81111d491435a463915223b9cce8f59e148f0c85 GIT binary patch literal 1603 zcmZuyeN0z!-ObW~-}NbYoB$3%-~RN-e-FNiq%s1mhW4NH(HG zfTDPeEuzOKl*(a}1GV6Itxj*$8;r&xu4vgCNx&{B#5qm}!#c#E+^V<^G@CntbB;NPX9<2iLkDIq+yNm^;IF4OMP>zPNove)s&>{ik1a zJa%#Lo}d1xY;Shfn+}g>w&!$CM7*C2lo!urUcJiCw~hbVzjIe@%lGTQxc|3f^UkiC%X?SctF^P9OHFsxJsz&<+^-3#j*dOtvGdko z#;WWcC1cT2>)B9GiMcaN#rbzvUw)43+u=HwA#!&Yp9nS#ba$*~foCjN`;c=Kt;~8a zi<@4L)(jTr*!LW)jVg6=-?!hq4jyP&yJFuz1D4^CQb8g`xjMOD(Nw&hA3m1#yEAX) zXW4unT2pfJjP+M^ZOZ?k1@>Nid3I{#^rvOlCT6NTGAF*eGdsy1l_CtC|3tGAN{BU) zc3Y45q&i}wM=>YZp2Vmg5<{^#w^T66Niicp5awx^xSgzk{4D}*@yponbAgafYtZVR22B{Z1lO@_VBwQiR=mWC z>1gmSe{M!FbZ5rFICyv~vlRW(Rc!vYVFrLf)933kS&}T5hLLvqO+i zsSPWF&o3&mZC@@m${^NHvdtW_OvfO$*{XbPA|a6y8^nTwW3(bpPyQL|vaL4eKbqk8 Avj6}9 literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.0x1.0cm/7.ai b/generated_apriltags/1.0x1.0cm/7.ai new file mode 100644 index 0000000000000000000000000000000000000000..a08f7a4b7275803092af9748031635838cf6c17c GIT binary patch literal 1583 zcmZuye{2(F7-o_x-j*;BGB?3}1Ufc!y}Mp}w96#+$Lazfj$~@02Bw=X5fw6&Xabu9miT?WcB?J-&)q%u zzTfwL&-*;@>D^9ODQz$j`j5sz@8<*!C?NZG5as27baMew1xAE;0J*g(ECSNOK`tZ* zz=jQkCwl9Esng)h`X4vN5}CAnk?0!Djwa}bJ(1kCuAAl-`0*NPC-p?#b#w7wXH zfUIgjd)gCsN8(-uynw8&=Y*gX0IZh&a*c0?$V0F`zW97Obm{?Yk|aYF(3Dnz163nR z2(W5vY!-zeXP2X>1{`6u7#I_4GPA7JXia-V1-Ll{C2MpLq(@Zch{7X&FadM?V9HX+ z!5bynbOl4`{D5?Y5J66nmj%RUwI~H(J)kv`(J@r0h+G2^J$m+oP0}SQ_pND_I5zO_ zlCQowv$!oXRI(pYTd4E+-pttYiCbrNYx&HO7!5_}uoAF-QHF@^Kt7~R{Y+uvw zZ03MHLv9H!QS08Iybe;V*nl(9+cjrjUBa000_6ei)gOx)?dHW6p&J#k(pJ1T3(S3Vo zZs&QTYbbXc*nY)*U_rsBHy&TsRlNL@6$klC6N9gO5mRF7qo<(9-EV!a`_+Z`!}`%^ z@VI?TM|N>OdpUM8zfl~{%E>?UUAwLC=XE_`wXQSg;K~7hcTvvFJGy(1RiA9_z830y z-rWK97k{5F94q?!L3rcfJ{#BCcqjYIshV=E^+aP5s*g7ecb2hQl1_-r3}bb`o5Zm8 z69SmmC^kn@6*d}Rt2t~4OL%PFMI_Gw8p$a|QP@2h3t~@XfY=!sAl6xOYlj>`dZq!n zDJZDO199tku-7!NYxbWnn@%|l=|D1tL0NKQw--ChjFge37}`R!W`;67Ls`~R)Y=k4 zi<1>lyqSaHkPJgXA82F@W&`sqsE07DmXTz`+$V#q1W*#14MK!RW=YKo3x43^_}5Se z5-XIX@Df!-KS2T7L{M`VV9jR64E$gYqY+d10#XvADGCwwe~hxQ=&0$K1+^^=vzi`) zv8YAqah8mIDe56{j2X?Lv|NiRvlb(ThE95%F(Zzq*p#zDg$ss6B|h1!B)<&e{X~lo^){h(~)p%Gx`)ez@99 zTq*^#gs2EEm>JF3W^f>qvAJm$x{Qo66&HoY2o7~lNpw0H(?O>3`}zYa<^H+e)AxP9 z_j}&wd9zK$#$B{RMPv`ofzzK5G@yXg&_om#0@B0@ybCB1Vg}^i7n^xN8aT)~Bmoo^ z5xi){ftsj<5;rWZfgI$4DwJfDLcD`y;Y9>! zLp3UStDVzJZqx&gP-+y6O08nmYOPYc?h7?w;$)N@qJtpKyi018Er=gXz$!nOvM6%! zK@m2Mb_cpYAdL=0P%&?jtccGFUKC&>phG01Yq+4yb56p2?Ce|R;$@K1@%J%r>htke zzUQ;d*PUs*_w*DUHw|=6*Ng|-QI^<;lw>!C&e(Y~Y>*UR_Y=ChtW`0>i`Wo+T> zyS+1|_XcJcTPE*+_=kQzSv>#tOHVAF`S;ovT{FW%op@{6a-r?&+00*#-0Yt^GErC% z=lpE&;@o_Xr#g9MjJVya@5{SYkrD5??M=GA{XpsC8Fv@jr~8VGvz>2zwRsc2#s12m z_rmy>&pz_)r4v($ogV+W(emx1ZFgF1!qA^ZzMlk7#UznhHM${z@ir zG1+_18kd;4PZt>JZm+FM(-p|T=Wm}%zjj*i!vn=j)hW$Fc!b=m$B-fxS$ z>v`=kwnBJrVN(R>i3hJzY9mGost{xQL0tsH)(GokO~Tka5z**qfSqX2AuMdcHItDR z4$w$#QG&zfiYbUK69Z!7#DG|ak)sV#E0Q}6$a1^Yg)9N2DO zsTkTNu^2CdoZaGhQgl|c)dm_k%K4pl z+l!Lgpl3EFj;6FxaZu*$4qgr}{xZ=ff#5vRVlzt;mO*gI)!02eA`uhof}9M4w9-nJ LC9<cG%g31 literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.0x1.0cm/9.ai b/generated_apriltags/1.0x1.0cm/9.ai new file mode 100644 index 0000000000000000000000000000000000000000..81b27a2c19d650119675e180780177693ec515dc GIT binary patch literal 1629 zcmZWqaZD3e7}rb`Z)Ia_4Bf(fhM*;k-n(8~T4t)WwTJ>X6vH;u?b;rtQ0|)RQ2~t- z9WI&6BuqBh93!zoHW8yiwrMn=5u+Q9F%t#JoWel17?%Mv8elec$`u z@B96}CR4D;N?NH$t$mBR)^a2V>9T-tJEQbJ{ zR3n#lxEKRpiF&{hO09xcX;f;BMyJ#*ctZ_X7y%_qbP!}d>*Xs1JK_fuFv}07EQ}m{ zP>4;l%Z=_2NV6LeWMb{S1M!)|a!#=XP!h@L8eUOgnR24?*tt))lwN_c2cKlr=7FMm z_WJjF@JHvN*MR?UZ2aJc-7|wJjsGMbPNK}T1>E1UNuJxX}xLX-> zx-)C|(Jy-jeS2yj*lzFo>hb$X zwtRc5XZ7fSb@{*D4GMovbyvGx_cOB0BOr~ZM{slz_$ z#QvwZ&b^iNK}<_bhb-a6f~(m{=EXUyV8i$`>1SA3_j~%nv%?e5PL8aJTN0mF_*aqd z#Emt7{ycST|32f%tTnwk#n9S?F^uh_`>vuy%tdrJ17FgK8gUbDnmyb`3j_d3Xoe|4li;{ zz-al{nbKyEmc?A#RM=%mBNDaf;yDxE4RTY45-K&2rnHnsO+!^G)UJhaZ3-d9@dA+N zGNQ-Li*A<<_-KV%LB9@4MA75TAj$ICpA@{{L`i6qI4bPOTH#q9M=`K5_A)eqV1<-0 zUMdB)n1Fy%5pebcG-@@i2E||&qYzX01Go@IK{dM8d<^O!viU-cMjK@zri;KRh-sW3 zM?o6#J|BZngGRXkqty}hpjI2%ixQ$0wIElkiHM^pJhD)nC@?NJD+K-rvbka&1m=nM kTt3fZ83cBo)m6}P7E&cLhPV|z4v-|T}v3FA5@BCtEKUSZR)kX(w1^Zt}m=( zS>_mQxH0)ijLE_jLl&3JX_&GZL8GEGxcHGNE)gSB6NxC2n;OGl=;R&ee`m4Na$574r z$mHc)Ro^t{ZLO*~Xx$XNWB!lQi}vff{=BaBmjdw{J(WM7_-$(G&tDI`d0_X<+*tAG zhvBGa$Mvs|URb2Qk4u}}@)$FCuJYpFPZq@IF8y+K`19U>Z|P@d7N7c}sd};9-JLV< z9Ni}#d|9r2_spG3Pp{j5r0;5N)=#sYkM5mZD0Jkpl-IuhvBM*;_ZEKk?CIN{GjxiF zomzHwFJGIp6#VhakE!0bny#2-?@VvonH%Gmr=QoMjRl?#%k72Mnb~zO4xY`P4p_DK z-mNJbj|ao;cb(IWeDJ`hrtL9(ev|LmE9Y~IMqKY{go&Ywj#v9mmF4%lE@%oi_bi+o zcBo%`-3yw>=Q|pTUb5!CF?aKH>sRivq1^RUc8;;Zb!x-6_dHlMS9shSt?8a`vxxbg zK5v9ORC#2)^rPPo{c!(4Y|31=)mzn5uwkH<^H`4ES9eHq?eUO9E7)q zztx9rlpfXM4*;*=Yr+#qX3(Ya5>vzg1p&iRaQO#}JkRnV0G2TZGev$tN?{CSaa*e~ zXfk0-uffb@ylb&7|AU$FZd!egfkZB=F$j%#lxr}S&&+2sX4YbWcpa^YHE|i|7-&xG z4Jkq>A}WcU@00>ENX!%OG`B1h86@^weW+c;BobnyNKjCMR>r_XN~^8j>7f1t3KRDo literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.5x1.5cm/10.ai b/generated_apriltags/1.5x1.5cm/10.ai new file mode 100644 index 0000000000000000000000000000000000000000..f900677b2ac7a10bb49d373be1848619bc4639a3 GIT binary patch literal 1591 zcmZuxeNYr-9L5B7HZhy7z=rIdaR>~#efRcu??57tJI+9e19V0Pk=wg_IS!WHo4pr1 zor#jna{S0qqZ!90`_c*r`;Y;QA&JPrCev*A!#qH0Z=d^p zz0dnRzifxip3muxO!lcCK=)aO0}!aL7N)2Ounx(K0>D6!8o+Mc?ni*NN?4LrFDNc% zkm8|%=D38?AYNaKC5(VEl4MsE4EV@zhlDZG6d=$~hIMGFyB1+kPx{#Gq&^B_z?QX< zo+Z(@HTo_C4S=m}mOMVi3&cqJ<|v{Lj0s<40EeWJyXGeRPy*)op_IjuLmS1| zwEJXoe!$vgLXZu)RS)5_0x4eH47do%^t;m&XNw-k(A-InYU=z4p(Zt$Dn<0D7Sp{0&r_U`Y@yW(QROO~J^2?I zcgQm4D9=6p&X~^Azw^*UyS+}7HutUj{MtbVXZ{2*rzHs>iS$;p#d61$a7{+6nMzz zL30j-IjfmSoT`D`N(uXA70W&s*v9JxJ--GtW9$zUvTWPjUwT#Zk|eStJne2`n)ocg zrwOF*Latdzp2Yyk@=1*2lj1mNitB+j$tNQ%yy44~ pCKZJ5lWedWRi!crZ?jt84n#<##0IdW;V`Y7fj2PO*`-x>=3g;A^~V4J literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.5x1.5cm/11.ai b/generated_apriltags/1.5x1.5cm/11.ai new file mode 100644 index 0000000000000000000000000000000000000000..13afcca6329d966d8d56b1f3a55336ff19fce821 GIT binary patch literal 1618 zcmZWpeM}Q)7#AW=F4f^?MivwE?vF~Vqj%Tql@<}jmhvHJ*Ag>zm|U;dQY?4P^=0dH zvc&L_Z5=K_#AVD>HXW12Ejq|VLdF;i3L+sRaf5xts4OURWN}mX;oi3&v}^C5>;3wC z{+{ReD6M8oF0IiK%1>rNPd`Be3P_GxqM!hfR?aQ>ffgY)Kvuo#698%AAm^3bpshBsdfYV2K@aPyo7!lSL9CU?RV*9E5@_0v3M~q*a!9y8yvHl*epA@r3{cWLX`` zxjp(eMc-wh8j$uH&gl`|Kp$~`ahIc3;33Eh2alISCpJ(diW2k#nu=6lLe&T&0*sLu zm4eg5ZI=S525h0zYFOH+H?S0A)FpkP0<4^joFjA)q)qTk^)iq6!2~SvgDFcO2PcZL zY4Lbb|A4f35kY2wmz;>t3PE(k8bC)#Ms4_^EO2#1ptHAqN9{0`)%<6E)6P)odGF=1 zLU5<=t@Yr;qgff>Z#(qQmE3n9z8}nPQVdxh{TAHnvMkOw{B?iuyTWtQ!VhhG7Dc~q z=-2U<`wJaicZLneetd<`pE)cpT-b7by7IG6OJ>A{ht-z`XL?WmvEoJ@J8*k?eYdpv z@uAn5+am?nA8&lXH1*|exM;`-_HELvHnz-G1+MmNnY#JfFaC{fK)I}&FD1Lre4!qE zuO}<`!I-JTeelb3p6sr%!&@J5XR^=#Uf9v;YN#k`ep>bHHjqjpVJr1n`gICYd2?R zy#)4Ow$|82+TOV3D4$Eeq_BQG78-4PvRN^daid*vS~GpRRhzMHG;~w-dFD~=Ks(4@ z-5<2h9XN7CzIUQ)m3vZdyq0Eo(^QgG{U9yZxM?@|Abb&Ncpf&nlhTZ4URde|PB028DY0{1bPtP5iwK zf(sK^4$;{~g%O@99=?uYVqytzBF6TC>KKOQ5#5pa8e{#$7N>{?SdFG4ghgc-7aGZP zfJPgauyj~pDFLx;Qa~)86cDdsEVW6hM=MVQa)-z1M=}Yk%ZB9`IR}vo5igrgXofVQ zO@v4dW84aspo(DA$ z`uzDMS-13;Mv~pg37rpTJ&*L1=;d>|fP>=?pb11Hs1tYz$byTY0Iegar3dI)mSKSl zEMYWa3O_)MVKk*foctf73`V5;B+N)7y(eRNDHx4RlI>_piw;+kFQrGLOu`s8#g{VV zrPiXQQZWOYg3%~(JO`9Hk5`bx$G=Q;NgzB=blhx`gx4T^=xY~S!?e;` M)<`Io#k(xTe*uI9Bme*a literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.5x1.5cm/12.ai b/generated_apriltags/1.5x1.5cm/12.ai new file mode 100644 index 0000000000000000000000000000000000000000..a0cf410d509980f62c203f7126a492248b30240c GIT binary patch literal 1599 zcmZuxZA@EL7~U9c^$HFXRK6U?m<9%{_uSszQV0@COE<6!h0d_aklnTiC@c5Y+rtFZ zAM#^nO58G;1BbGWutkUQ2O~~cf5Ra=6F_jFKc{AK+lH0u!kQNd(9b z=Gcu~Zm~peB{k3!I;}?F^7RG*^7*=TU!(w=sF2_w9SmEAd~%E8B>YeUR`{WmMUX=; z3bSc(dr1F)wRi|YX5^GzgikAy8gL`vf+UkRd{{wZGt=5Td}30X0EwlK9zONP#?5JI zm;W+d9hpCykvMQP%iQ$*@L1hU`S0fSKx2N8LZx7x0ZL088|9$Cl+Y3EPj^BUs&(Zy-kQMq}zkYaOldama z@aSL5V#(IIF=oMfrt?V0{K?0LQjX7`Pku8ud%v%xICb!$Gv58=V8Vt!l5fRnmuA0x zwfD}Cxsw33m*LU>Rm)(xW*w^bm_{g4|YCH!f61w6K)+BY^bTOLYJr72$ zS(BA9>658NZT{GJTc((p)E}$Tz`85PmOIXLtBbvFEtMZG+0}Erc5druk57#BbaTZ6 zokrX2Gp7%1dbTTlDZS%tdY0|j-Oc2fb>9;gOJ?=EQtrNFpWC8RjeqjZwNHhgy<2wd zyzf6z9fRJ{&lo2wRoS0=5~g1kf7sA6#b;mbiHS@5{q()>*jI2DAIIpYjaMpFOvbyH za&NX@dam(6_I2}O&F#VRuLq{8AB_I}&%UR{z30DL*tk5@pMmDzU!>X(4JzbX9gr zIwcCEx(i=zl3R$rIl#W?cKL`W0#>S`-V1s^xDHm!<|3M5O+>cXElXzV{w#BW7Haj7 z=M0=);31y_4Ve&TZfAn$WCdjH7O~eOV~^Vb4)Gd6!{>rVjJ>`BmTg}7OCu`{B#7(^ zSBsOFCo;xf@;WS^L`7lx|G^AoBdv|)ASAr6#URv^QLe*yAv&Kyh^|Eo$r4%@Yv7~KanKOa z8&*WO2PuI~UnbSdATUp|$*N?T${?`S?Cy3%NTkI2u&Cewt(;Z|8MV5!!ovIqpdIth literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.5x1.5cm/13.ai b/generated_apriltags/1.5x1.5cm/13.ai new file mode 100644 index 0000000000000000000000000000000000000000..8f2bd9f17ab5ea5523753021f49d868cd6ec77dd GIT binary patch literal 1597 zcmZuxZA@EL7*>}kUbls~y3QzY6gyzJ-g|m`ON&6#PlU0oQWBRT8@KIkDVBTL?O|4& z12%E|Fa(u3HX$OB&95zCglT4!)EM_^!d4>;H^oGm0nC;VGMB;TIsJe_y?^dK&wbx> z-t)ZA^Q!C?+h(ZNQmQYnfiKQb5HLV;)=~upfVT5qApkTODF<}L>-7Sl%{=1$k{1*e zQG)0uf%=35YY-JX5RU|)jV9S75dk*(ZRZgZWD#&=Nzry$aybM9D)BiM8{S_CB0!ha z;WJIKw>kDM0abu@)bMVf=molH`u1(kTET@tUS#v7a$w>GjG`!^0Dw%i0yC~g5HVng z?y*&H`*@QS#5Irztwzm3gHF#etU)vGB0q$(1ainh zaW-u}KfXVpZGKFUMQ}-O%x9S(dQlC4QIhdB0!SA4Ix5&b)Ok@nz@%Dl-=msZ213q=3xcl6vRdc5|?3|rS`FXVG ze&O(wv(EIHrYGiGP1m1VSM`GOozoXjrj=8leSYic%f+8uzB6*X%WJHCz<#zvw3qD( zy!lndGh^-QHB~$Qe(={T?c;qt1v$qiOj}gL)Ax7%V1U!pX{NP2ENHGAZ~dkJyNreA z#w#y&wLDspduR9FiLA+#)K_|Up0~GsdXCqu9`alosC;g9efX{SXN&$)DEH@vos@3G zc>bjgrgzU;JNm2NPC4}32S*C|x zJ9XmE5{3KUhg9Z!ZOs#y4>o%QAF>h+w+p=)=NCrImUN2F8tW7ZwMA}FC ztc9AmFCwBNSS6^6W5fwDIV>rbSS79zBLu{FW+TR8j$85+?cxE%;!4mL@lw);#34x_ z@ktU$R2M(mEHz+tLqKowxdYe{5hIlo=S7_#9R^Ef!-UJwW-MCdlSB*gezqlF!)SC2 z3-wUPu?(Bb=(89mD~F24NixXZ%A3*qJPzXJN{nH2xRuM}7?w+p({stSXqe=4^=wicWb_Gl zLo)C43vy)6mxvw-MEZ&6Sh*w-8AN89!`CQa5(%*Z#LFl`E7Y)hN~N-Hvr+#6(r^5H literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.5x1.5cm/14.ai b/generated_apriltags/1.5x1.5cm/14.ai new file mode 100644 index 0000000000000000000000000000000000000000..b55a65614acbacff7d420f3c4abb29585bd36b14 GIT binary patch literal 1632 zcmZWqeM}Q)7{|@(c9P&S1~|#~qN!TlxO;oOmQw^uOZgCIrAv?*qDOlzr{#{_y|{v# zag}Tdi2(&R<_Cj0HX}~W!ZhkaXcnhq#NeWFFqVyf_Ccy_OVDgx*xu_06ng*M-S3{~ zd7tO^Jiq6(cB}1Wm~9}m=O;k_H39+(NHzP2++0A~IS;P@Jw_@3`D$Z54@e7#IIrXZ zd3gjcx>UoQIENx1Ds~_a@xTyqvPmKWbmZC2A;ilfVANNFw9Asy!6UF6pJTP*_`DAR zva}ALSs1-rqW4lz4M;~V=MqE@Fh<;W?WozuI}tF2gU_b};|nm0qJ$IxsYnJEoDDBx zfQ`i1&btJzQ1am%R11SXn}Mt`hoNZJu;>jZVCQ7)9HE0CD|kg}ke!$xm4G>ZRLbJW zQ4fl-X%oD-en8s1m>?_flw6q4a$fYHS^y&?<1!Q^^IRR_JM~59x8he|Wzpj|HG2w% zzFtO#+CCSr|F+!v`Ic*%q5^ee+7+EwI6K9%*aP04)y=A)@Yp_#$4ybIEUAKR<` z^eTqH!;yDqX41EqnuU=g)1Uf`p^0(N!At!o_wH%EJbN$n#>_t#?l*O>K6~ZHA6>-< zdj@a+JN@5DNd6L-tqgpqyL%PhX;1>LQ1BY;Ydc4@HVza&e>%m|(wO$KCTS|;yJa_A zru&aSBLbo<1RZCV2TvU6RfLRn`NpQ|vp>ATK05lMqis^tk$k;3tvR!Bzi<5fGr4B_ zq^!+PE6Be(*5yzBsmJ9TvlImxH*$Y}IH}<;&DOtrGW;v7Vt8zcX7w)fH#?_D->-(2r2)-U zXL4WO#%)_~rh4~kw8g!hEfZ*|kaYB^;JLSMU1Uew+nt2|%frWeM_=Q08`7#x}nj-&zdsE%Q(SfUd%Kh&z4 zVq;{6K-H$jjIpRF=VeDaIRNqe#f_jUw}gPIh!Q|mN(rDkn6aZRQUewM1mrfsrC`y7 zwN{~uGqM~a`(i$A7*`Bw!E=oSNwli#VS{zEp3)mB8sRoP7!#Vfk4n!m9 z;&}1Nyqll^G!WF>1&jB_FCfM+NHGZ*#j5RHXh*R$E`Kq`CSZ_)xV;PQ zAceD7gi(|+5u+KrxEA_SIc#DsdJ1o;#df9yI|$?JMKULNc{%(mC>7lj2=^1Ox(Z2B f#~{4+96}S1NmPkd5GSKBtx#{G39YthhmH6jJ*WnW literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.5x1.5cm/15.ai b/generated_apriltags/1.5x1.5cm/15.ai new file mode 100644 index 0000000000000000000000000000000000000000..89b2de8d1a18201294246490ce40dc5ae4abf2d6 GIT binary patch literal 1600 zcmZuxZERCj7`ALIcvmwPn6SyRvtglg*n3agTe>kP>(`uaQrj6ZWMOyhY1h%-+uWXE zX$;ELAqyLtff2(GT{r}L&AOgw&qsk!eDbe_(C}_wSj6;lyC?@E>VG*RD(nUlqX`; zBezd5Nnuh0wa{xdJS;aDc}`cZU-Cr?unRJAPSC-y4iu8wWEbIw5-`sXr7VRUnrMzq zo6k@B2dvFc2(loTIl08Eff+6ZA82>~X2Z1C`TaSW`qPQJQ)=j)?c8Rx5_ z13zn~74OcxZR#^PW@f%RRS_z@VeLCuQWLzH-+k-aUGez%m4EQ*;U72W^xwEX8$DKa z;+4R}_~eGqDnD-8e|vm)QP1AX($KIxJr$kUIr6VP7K_}uHPRkwTpiofmf1RSarVJ! z^9$6tP@;Gx4Xn$anb94K~g z(R8D0-c?^0YclsWeizFbJfr&K(RRFcIyW;i23omD*6X6h?cZT+Ai zOL?#N;o*qs`Mq05J@0*aCbR#o2L*~FUF!Vy?918thLZl=8dsq165GA}**C@xDj1~f zyeBvJ){SOWJX8KjQS8&aQfEUn%hb{J{<*VF;R^@M7gr$9ijF%zXw7d0=;~U<7iHm1 z_G#^JO2xU)%T8Qfr!)umMT*U{y&nl%>>oB|6u!CcF?giQa&&`kvZ&;&vQCXw-aPbe z$qT(;?2)MQO3q6^b@r&4*&ni}POW+(klWF)Y--uepz~K==<(C#PfjW+gZKpLsP&Ma zlSX{+D7FJM&S6vx$&FZ8TB?>L$W}u@wP&s-SW_=XTkMlW3zfgxQlaIv22KZ! z(7@|BT^VQG$Z;D>nS`Ar19iQCgMJD7ea&FEPQz<-n?MW3!B7Ru2Il|LNV1nWkz?U* za}o8VdIjAc&@8xikqIOtl&0_!mXU|y0Ms+w`~wU;uj7FS%wv!+MIRu}VUW|3aV*9- zqmdklC0IE}PR&xx@IRQ5aJ<+KavY&ty^|hi$0`Ym0Q|6E)x(4yH=JZ7nA(0Xr!h($Bv_h>OGAfmIn~nJo;EC|j literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.5x1.5cm/16.ai b/generated_apriltags/1.5x1.5cm/16.ai new file mode 100644 index 0000000000000000000000000000000000000000..c21ca96dfbf06ac73fd660e2cae053d775e538ae GIT binary patch literal 1606 zcmZuxeM}Q)7#9d`-nKv@ZbON@nIV<}z4z_)`azh3wv-TyRL~8=M6c}?%F126zOaHg z4SzV6#Vi@PpxYea=9Y{_!!Q|yj4}M+Q?qD-%h1FqEK5L%vN6oI?7e90rb8GFAr!FhdGZ}1$OPC zc)=+f>LVQT{kWhMbC?I3pp!)uFkph;HV$K65&$b7DcUB9j#3_j3X;ccA@O-X26SO9 z$!QF|O`&%oH~{F5vs(?1=URG?c;T;(00>M|yL6H~869f_a0D{2^Or#oKAb=ql zqlkC9Iiu(&HOLkkwTeXst)7L9L9^~e3b1h!aSqZ!(Ivc3tdkssADMs^eq_ob$dMC; z*|fMlCZj#V5a)H@eUhce<(f`jwu zV=c}9yzf4>F!1T#j1vn7KWJ!ePVs&(7&4h;~4*^+$raXd$q_-+A+Q9)trYvI6WKl&0 zl%)~{%7Tdkm)%n_h>yPZBFjew#`WC;dWKyWLpmW?7VLz~F-Vz(%m%af!&&Db8jAGtI$gldIo=^12t`Pb;KeWTE(!vqq2S5`v@FZ8zy($?M3|BvAcQdl z8L|@AVoJk#l1JLS#9OQvInq-kg2$<+^8wVjTi-5(S1Zk5cr-O%J7wTiSp!$3O03MO|f?cs0XB@nR5xE2WX<{@6V@k(1`~yilPMlfTp4on9wx5hyZ=G zMm6sexH2h#X220@m6E0P8XZe9dUetl8o2zQ0BQdB5?d%Us${ha>}1Rd9mx6=L)hK z77O{?9XHqX%v#rPI@dchM~b!gKAA69eDtc-*O$NHMmC&y^816m-_6wz9m`kCR;&`^En zvy+cr9w?f0{8LMLh&?rT;rHvST6!iom_`eS{`kDkI=H>y!M?+X8-+C6mC*~JH+$x2 z&c30B@YqS1chL6ys$Xx7JUxHW(z^N5;^@&}mNrLq;kaSot4|+)xsKa;#MhNJF?M$9 zg*!EF^VGenv|#2>nfY?Car($Q@U3C$WA3%VF;(6vO4fG- zk;xbWm$Ub0 z{rz6B@y+d_dHcfqOUm5JwzUUNKl?a+vDG>^ULrrlnvY#jO#P9I!#Av^G;v2niknC$2WTX#1Z80tr4+>8NCB}!Qb4S?cx#i? zj&w}}a+~1tBQHcOWXGP1dOo@aR>r0i4nvxdT%jO|X6*Wn<}E5prJ)#FM{8JyVm4E{ zB8n<1CZchY3^rDC(C3w)S7-tqjFMF{C7>BXpMMKUwk`jqlw=P|LOa6M?nI_Z%<{S1 zpow#~q8o@+C{ExdAoFg50<@Z-mLH&DS%w8}u#C}&Df|F2j?om2DE%)+>9lB@Bx8ES zT@t2e{|D0|nv&ztUP;C%O=@3;Mf)fzSI4H#qN339NsePu;%G{n&;!bx;N|7WhOZFa o5{TR<+F*7`!ZL_#Gl$U0BN8#Oe#pr%LMyFeRD?oNUSlEt1sA38s{jB1 literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.5x1.5cm/18.ai b/generated_apriltags/1.5x1.5cm/18.ai new file mode 100644 index 0000000000000000000000000000000000000000..ce852629b11e69eff4b9a65185eb4fae4b8928c5 GIT binary patch literal 1595 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 PRz}0=sO03#JR9{tMA8Up literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.5x1.5cm/3.ai b/generated_apriltags/1.5x1.5cm/3.ai new file mode 100644 index 0000000000000000000000000000000000000000..64f863b02ba0f57069a4190bb30a89aef0a885df GIT binary patch literal 1603 zcmZuyZ%h+s7@FQ?3!XN;F+Stq#7X!{M_$3{f2;u=;ReP%hIJrB~1Yq`pR%_P4#&t3~ERmhm)Kyg)rbM zT1d=&@w+{KuK=}x^9BS_R{X#kb3b^Xv03tAuqS%>YC0&ffKo+Ku?`T7Wnd@SND2W8 zV`o%KqAcuFLnH@k!JCX0RA{wXpt+D=_eK(M3mS2b(ZO&YNmtu6AK`}*u)+_eEP)&v zXqio?93=Gv&KV>GIV7Jd5Q7iVkDC?bgW523llnhrRT143uKo4z3^J+OKI8J zLx0%EB2W5uWL`hPRyFUuGF<=Y;&_{>u4j&IXC@X#doIua+gn^e^TUMR_1CUfPqgFIxv}x3 z&f(1W?w@&axofYN)%zMAwvPlN;=>&0jxQNv;&?N>aAcFbEu6h!5{1*n<|Dm6=Eq+S z_CE>y`rYl%z(B#$+|}9KsW0*dCwsde&pf?1U6cLKQJnHNGr;+uPcQX#Zn(C%aKGqv z?Hb-Aw%jk`YC!EWM)eRMiq*NLf{BA%DFRe}_ELhyb+@W4&L;pwMl4~|}JYtSSz=0a!;-K6J4x5b@qd6Z0FmBb0IId;olTp?D#EI++vCT&W zl<3teHi1ULcZhT#enMUXFCk58Vjw^~16M9!wOGs+&;(X6M3|x%P?j+SO@yem7_`|) zsq3&pNZPg@v;GfeBQ08MhagOfOQ0k>Xtt2uwAL5etjW2UAX!A~?f4`+1Z@emVNH;O uk`~?e6-tu|qWvV>%%iGw45FLOD|bkQL`tlV1r0}OMJAs7GYn-1oXmf%{P~Lj literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.5x1.5cm/4.ai b/generated_apriltags/1.5x1.5cm/4.ai new file mode 100644 index 0000000..7e27222 --- /dev/null +++ b/generated_apriltags/1.5x1.5cm/4.ai @@ -0,0 +1,72 @@ +%PDF-1.4 +%¬Ü «º +1 0 obj +<< /Pages 2 0 R /Type /Catalog >> +endobj +8 0 obj +<< /ExtGState 4 0 R /Font 3 0 R /Pattern 5 0 R +/ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /Shading 6 0 R +/XObject 7 0 R >> +endobj +11 0 obj +<< /Annots 10 0 R /Contents 9 0 R +/MediaBox [ 0 0 42.5196850394 42.5196850394 ] /Parent 2 0 R +/Resources 8 0 R /Type /Page >> +endobj +9 0 obj +<< /Filter /FlateDecode /Length 12 0 R >> +stream +xœµ”ËNÃ0E÷óþ×NœØYòº|@UF‚Jðû8 |oZgĨjdefŽf:Ý\ï?žwûÛí¥¹º“ n»£xs7ãÊ'tvðÓ˜×Oáäö¾7÷æÕl.¼y<šò(ïÊ÷³|·ó](ÞdÁ§¢®ÜÜ÷óIäAnÌ_܉B¹‹ÛÍ“é“íÆ},*8ãôÂ4Xç‡Ò,XÔ1¬²øÎÆa˜Ü¸( ÚM6Ž}\ûO?­s1۬ÅدBRA¸’د=LP’Á J†çÊÍV ¥‚OÞ/ÆKP§¤×n ¹P¼’a£¼!\–¨/QS +ý¢‡uv„Q‰’ ePr„;®Ì”l(ƒ’ccPY’ãû¥K…hÍ™¡RI{ËÍú…ì‚x-CÚF2lÏ”mA˱1¦L Àe‰ÖÍæ9Ôé"+®ô ”l(ƒ’c£ ü¯Èeq®kCM$¦óŸÈ‚øý± (É ’akFymž•’ (žÏ(¯˜TXChÂÄ~ìä ÜÈs* +endstream +endobj +12 0 obj +404 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /CA 0 /Type /ExtGState /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Count 1 /Kids [ 11 0 R ] /Type /Pages >> +endobj +13 0 obj +<< /CreationDate (D:20260318165303+08'00') +/Creator (Matplotlib v3.5.3, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.5.3) >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001013 00000 n +0000000869 00000 n +0000000890 00000 n +0000000950 00000 n +0000000971 00000 n +0000000992 00000 n +0000000065 00000 n +0000000350 00000 n +0000000849 00000 n +0000000208 00000 n +0000000829 00000 n +0000001073 00000 n +trailer +<< /Info 13 0 R /Root 1 0 R /Size 14 >> +startxref +1230 +%%EOF diff --git a/generated_apriltags/1.5x1.5cm/5.ai b/generated_apriltags/1.5x1.5cm/5.ai new file mode 100644 index 0000000000000000000000000000000000000000..b5ed8ec8c92125be8cedc7a0dbd154d13f7ca600 GIT binary patch literal 1608 zcmZuxe@q)?7-p=S=5)ABh$1e2Xe!id@4H@mv;+yIr4BZ-u1pyYaL4t4V!6ZX*R5MJ zoOO_GnM?+5=rI0BreqYu0s>i~}kP`;I&d8WKlM%9J!=f)-fP+_XaF`B?c8XrvrwEuILckn9gt8cN z$VE{$?QRe5AJBFWCdeiVvJ3OMPLyg;EnvbV<2JlV5&3$`-+AiLcXj9C%7R-rQ)Uti zz7W?|{=KBEr($|({>;4_-e1vu?xo(Qx4UeW$vLXDYZI+WUa{@s?43V%_Pjba{?6Hq z(Lv9)C*`DRzWjF6a8lzVKTXVN%7=1qO^$R9O%5En^rfa}>faB_f4e$5><#Q_D;&^g zC;G(6#_O_;p5Ah&YeJX&ZD;0XN!PLNJx|uaAHs&#llxoNyzXn%Z0 z3=i-fV|$Jy{{AW7pWs`yJh-(>6}azbZ7TQTo0^f{uRp6$%{bYk-rTy69)F;HJu6$EwJ6$*Y z#?7}L`tiYi+|=$vg7)LSc1=llYfAT{X_jEtJ=; z?NiPV^MlJ@a3~&C)7^8&nwhJE>9t#mo>c{#BxYG^S>A;kXO?ac?oL0PcX!w9$PW*^ zYiO5<2_h4V*da7YGIE2Sjanq6KZ0!r6;X_sBBG3W@eq~YN06u!VC=+J|`<6vzSK>9vOMuRbVTtGwRrXO>FAbMZz%Gi7YH(G9DqeUQPau*(6T^#N5vwT(7y||89>5umtPxa$ zIgG(fkq1aojDalXWFZDkChYb_m>FW1FUGk4!OVEcEIh|RLhV8fLJp5|5yl$hVxftP zuSE~zF_R$B`xkEn!JexXz?gU~$jN^{CGkwIv^mAH3^m_$OX7x4-T(aPuz MW=gFtSZ}BP1Hx7T!vFvP literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.5x1.5cm/6.ai b/generated_apriltags/1.5x1.5cm/6.ai new file mode 100644 index 0000000000000000000000000000000000000000..363dff9befbb7ccb54a50121e4c34cf0423e92fe GIT binary patch literal 1599 zcmZuxe@t6d6lReh@kusJ*a$(*9Z5$ATi?C!^>qc2QA&$!EHv$A)Mjz7?duOL@3Gel z6dZ0W3yB5>I+kJP919!C=48f16GYq;Gp7(`F}gWXVj>zh<4h)y4L7~FKcGnxOhaNO01X#kj%7H~J0ev*cs>m4d(cd-^V@Z{PKtCDQrYdfSgn^UXQ)wmnr7#9; zbv?PWBK9`N-qm0?U>$zZ6Oett5DkB})>S9DG1wAGzEloMY``ST3Jw7TqZOD*HIht# z;%JT?k|!WmC}C0qJ)!4x0xC8b1;`icSA3BIY@$knqjWH=T?#3Us+;gb30UNZQkFmt zy(rG6H4r5I1J)WO1XW6I#Y6b4kz^nC0~95hv=PFpB-S(GH{U-xUN;7_Ec5r6w)Rcm zzo%gr4xIl%cW>4DwnN&<^c&L`raqn6Rr*{umzwwcp03rQ`ZFWbZKFza_i^V-^PZBo z>T5S#AHDnT#}8b$FT>iFzsDZ?+I#ZJg<$swBM;BbdTzZ_cJacs=264=)TLHuW_Jrt zxp`~i-=C&CThN-hi_C+XQ(twxGW5c_iKZ(?SH`NF`!rhZe46%p zd77r`)koLPl(!$u%&~FVu0pjxb@+`QuHsCuDeLf>;JU7>9Yy|PwXx*9^yvBUh;QV}pvhATmHY zX+6@%oe_~8#hhSw9HU-{31UgJ)GTq8m=K`WGn)t&^W2iFSholekynDksGE`!Qja8o z)G0|I)m{8*v(iZPjR3nn;0X~=M66_|-ivxax(=4gMhVTZW+Gc0P~=MLer;t52RQ@e zkr5dL9`Z%dSO8(cMkabrQGs@ch#P_m4hCGHiPs4_eiQIx+z={Z+4{x5bc*UDL1b5W z8r{S^iBSz6FK~(O7s&)-88#;H5>_QI0|A)^Tzr5*5O@K2!6JqTQ}h9H97B*JZ7s*3 zaSJi>3apq!C1HmD!Hi@hEssSI65f|%5E{rRS75x5oX;pE*TO-vgjU2F`J{6Q8WVcM xsu&1LYGl(_%U%UU=1Df0T~VkEB3sQ7Xq5sA@R|Ss literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.5x1.5cm/7.ai b/generated_apriltags/1.5x1.5cm/7.ai new file mode 100644 index 0000000000000000000000000000000000000000..2e3e19161d31dd5c4b3237d1d3ffd50cafbc3c06 GIT binary patch literal 1581 zcmZuxe@q)?7@mvp~dPlsU)dHm|*FDO~TG>j$fI zMsWc%jJ1iA02zTeVjNpk<~HMM=7fojE*T3mo2yII7=JAOSomid4E6hZEp)B-&-I?Z z@Atmn^FGh3cG?{^w9Y`NkADx2ogrvI0m?Hd81W}TI%Ubz!b7kV)v-I!`Ft1x zvLS$KZcM#xsdod|1;~~*mh+1~U{cbzH@n+;4+Kvn4_|JF&b&aqC`vE{XiDk8hPvTJ z1Xz?ant0C7Zj{2P2OMG0>x{I;WHwTa#gO%d8gQ~QN>=C~NEaWHI%N;y2NSTw52h@G z99$^Pro%6w`vcM;AcE|?N8%8lTX@k2+W@VQjII%aGS3Ex@ZnSMG(Iy$t?s=QB1X*r72+n>LJU)9(> zC|sUCU-I&^{YT3>%jdTj=EcTFKK}IF!2UgjXQH!Lxqv!e!kN1d{W$cnDlyCTz1C6r z^C!bITN=p3!q*2{{f|XTM=$KJ|L{A_t3Tu|^sFkYiEGaPzIL*$cf5M$-JYlN!B23Zx_yk>Xr^wy{dD^MzftyxO>!^CtvhvU%Q*}C_DiOeesOLywc*jT8vvZ{@oAN6z+g1yE z-_XW`dF!VF1;3mwoI0VJFZkn1;dTJ~w%_ql&=gL@2X&)p-hOwPsR~p00 zPmO8_iaau$04=Jw$p~KqE0_=m~o#=RoX;91!~=2gE8%m$pfrNX;}L z8~t1eIUs2q7j~NBbjAG3ZPOW-A#F&c&@YL0?Da}}t)9}GD26uECL=>Jk5gt1MQOBz z5+})^vWbO3L4tzc4Z0YeQO8t+HVA{ET9OPb{iTy+A4)=-f$Q`jt7K*cIWKUto(?pD z)Ct-QUcxf(B`81}2x{p8CZmxt0xwv?Xv7qLfSAT;ib9m$jZtP3+9BDP8F80|Sq%S! znGj9cab`5~EQ~Vc)@6)n3uVQb4Y|GODYSC3-Bm0eqVn;q252&d-}ca z_rA~jJg=6w*&k=JO_cV{tKf|fDF#5GRMt>Cb^w|eJdz(6Fj4~OC!6XeKwAYQ_!JMw z$)P0KO%9sl0ty<@6D}km378^5c11?O82QZ$2uZ38Sn^5HysC&U34wBakIjzrON|K7 zMRoYj{OH>neHVcWK)b31w^#N6E)xFKp2`|YL||(;`Eof-d;@v1tRO#NV59;ou11nE zV2R`?mfT(;UuncOkP{|DHp^HzGYgFt)2c6A057OGI6?o4K@~ASgn%V}2xW2P zkc(n$+Pyy9KcMYCOpr|y6*uP7DajsG4VVbYxD7v2C83ULeD%HH7rW2E4F$LUrP|tS zGk0IlYAUQ)o02oQ<>h1cfMf2C8B`sg98c08*;2YM_+0l5=g0@&w~gLdxHVb({QR@r z$anqD&^_~V%i?F}HNvL(={x#?w{jL1E{f$@ba4WeUn=>wxboqlBE=Y1PX&b8ztQYk%v_5VJ15HFtbY?d-bB?GJA124kh3IrVr=hqdMRpg3jf+k7hbqu!wl z!*5@3c1|b#>FCVwn+|^RzTq`^^6Z+j#jam2C4c-v=wKjK8FcGx{7h*5&FQ?r`m*94 zPe$@==-{ws=g_R~06%kT?-A&bhF*7ib;AAa`iAu6w26l)!$^DYH7Tip6AHCG8KGyk&ucV)_azY* zL`N3&LwJ;A<|czF=8>=f304j&Vi<8nR2s|bCANv_C658bgw{NaMcudTFIp4;gM}8S zHR7v;gv2cgAn{BBNE8^m+Nv~Qr89uu<#qe9JHnBd;Rc z$R5zywi%#-gGR>8aI6s;AA{yh2s8CmRKe?$)bP$PlB*ODo+sXEC5l315Z-StZ?l9+B*glWprSCXjDdxe KR$H*gPW=aB#{%{M literal 0 HcmV?d00001 diff --git a/generated_apriltags/1.5x1.5cm/9.ai b/generated_apriltags/1.5x1.5cm/9.ai new file mode 100644 index 0000000..e0d451b --- /dev/null +++ b/generated_apriltags/1.5x1.5cm/9.ai @@ -0,0 +1,73 @@ +%PDF-1.4 +%¬Ü «º +1 0 obj +<< /Pages 2 0 R /Type /Catalog >> +endobj +8 0 obj +<< /ExtGState 4 0 R /Font 3 0 R /Pattern 5 0 R +/ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /Shading 6 0 R +/XObject 7 0 R >> +endobj +11 0 obj +<< /Annots 10 0 R /Contents 9 0 R +/MediaBox [ 0 0 42.5196850394 42.5196850394 ] /Parent 2 0 R +/Resources 8 0 R /Type /Page >> +endobj +9 0 obj +<< /Filter /FlateDecode /Length 12 0 R >> +stream +xœµ”ËjÃ0E÷ú +}"Ù²e/û€¬Û.ú!}„ªÐÚ߯܂îU2^2›ÑÁ3>hÆÚÜî¿^wûûíµ½y0¬vGìÁ|X_®Ø¹!Ìã4ø~Ž'«Ï½}´ïvsìóÑ–GyÿPîïro—µ¡|› bDoD}Yùßç‹1OæÎþWÁŸ(”µ1ÑuKÂlûÉucJ!ĈޘFçÃâ´Öu ë² KÃ0û±ù,h7»4ö©$vI¿œ÷iJ±ì†R&L!4‚ $ƒJ†0`C¹ $ƒ” Ï{”Íä–ô¾«É &xÑŸU–[I\(_ɰ* ÜI@RAº’ŸÐ¢¼²YDCÉš§Ø7=&¨³ƒd(ïQ’A%Ca ø'¡¯Vˆ“…v¡Î9È‚ˆ›Íª1Û Ör”†+Ë­«]¯eHG-Ê#Ê6¨ å(ŒR–‡°<‰uÎk”§‹(ÙP%GéÈ+CW!¹P¾’¡4Gymæ@Ɇ*(9 +£”å¡«°4ˆÄ”æôäÎ’ ^P2¤óŒ WºY)É €’áy²lXS¨ÃÄþì̈8Õ +endstream +endobj +12 0 obj +438 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /CA 0 /Type /ExtGState /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Count 1 /Kids [ 11 0 R ] /Type /Pages >> +endobj +13 0 obj +<< /CreationDate (D:20260318165303+08'00') +/Creator (Matplotlib v3.5.3, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.5.3) >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001047 00000 n +0000000903 00000 n +0000000924 00000 n +0000000984 00000 n +0000001005 00000 n +0000001026 00000 n +0000000065 00000 n +0000000350 00000 n +0000000883 00000 n +0000000208 00000 n +0000000863 00000 n +0000001107 00000 n +trailer +<< /Info 13 0 R /Root 1 0 R /Size 14 >> +startxref +1264 +%%EOF diff --git a/generated_apriltags/2.0x2.0cm/1.ai b/generated_apriltags/2.0x2.0cm/1.ai new file mode 100644 index 0000000000000000000000000000000000000000..a17cdd14406dedc96c360df8ecf7213c1de42b2c GIT binary patch literal 1608 zcmZuxeQXnD95#v)4(2d)3CI@Tv5bukdhhjmw|3x0*AF(?+@+l`oI|hITX(g)tJin1 z%}AUJ34X=+o#6gh;uMG|!wmjF9VR-fn~GqBF(d+;Mkp~xA|*3K-?!Ip-Rk{wy{GT* z<9&Y5^D7oyo(+uIN);de8GLYpVgLl1KSXWX1ZY7F%28m!h!@aJ-4PkkP7#S=EeNWr zC|M1VgSLzScNf~?Ln4xa)d=!vDgvzWTM!YFbro>rlcEJ(lYBA)Ex3-$gY(N>2+(yM zxMof2?M%JvKr5hq?P5StgMc@}n;ZQhSwf&9nS8DtCi4OgRn<@wFwp40iMx?i4A_kv z4RSycYqT!h13AI*X5MbGGc0T4Yzw|{1A?gIV1o{d_R3MMQOc0@c<2kb+?|-Ap6Ok)Blp61 z6^I{yanI)32MW!@y*Cr_!7rov)9ym&(5iwfrybk%k5Bxv=cIQ0m3Yh3iNH3?WPbh7 z#v97*Ti=YnIdawcQtKaP&*LrsgsZP#TRU;CuRxnh?4J6Y-5H;nxYWII<)QZ``(~b+ zZcJ22JHPF_edM{5!phiJf$b0HPUQD%er5l=V@+F+Ep9kJ+V5YsrAOU-&|kV@+1f?B zL(79dQZIj2-g@)sfx^pNjx+wL_6h%;>7DOKo4ji9g&L)(U^rjWtETx*Hnz96kq#zOx7;m5F1ild?9OY~-DPLbEj2Rj zSTwTA{-MzQ(-V11jwt(z?r@*)Z89Z0*mv>XSkL=Lcjb}zfOPO!ZUbG|v2J0+CYQ$Dq35i*9rV%> +endobj +8 0 obj +<< /ExtGState 4 0 R /Font 3 0 R /Pattern 5 0 R +/ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /Shading 6 0 R +/XObject 7 0 R >> +endobj +11 0 obj +<< /Annots 10 0 R /Contents 9 0 R +/MediaBox [ 0 0 56.6929133858 56.6929133858 ] /Parent 2 0 R +/Resources 8 0 R /Type /Page >> +endobj +9 0 obj +<< /Filter /FlateDecode /Length 12 0 R >> +stream +xœµ–MjÃ0F÷:…N H–%;Ëþ@Öm=@HBThíõ«¤Äó¤EÁ"ØŒñè1Ÿm¼ºß}¿ow›[}÷¤V²Ú•Ó{õ©mþ…hâº[;ïÇ0^­¾vúYèÕÓ¯GOùÿû|üäcsZ+\¯“’Zª¨Í+{>¿)õ¢ôì•B^+L<_ ƒ3¶ó±ëN*S-Õ´ƇÞ{w¢—„M eƒ¤Ü`¬Æ¡Ÿm+Ô¯Mƒõk.iØÆ÷±ÃL±2®‰ÒF:´r, !UæÚëÂг).i(}©(5Aaƒ%I:–3… :4r,•“væÜÀ϶*7 ’&\Ò°tƒ¥JzRÓF:´r,Ýa©’©PÚH‡VŽ… Re4 ‰Ãàb˜% ¸è›qêKE©© +thä(O$Ë™‚Â9‚â3Ím…JæHš°ÍÛ›Šåy ¥thåXBªÌ tª8EÀ6oF*Êì¨ +thäXÊ)U2… :4r,•*2øÖ½\„ ÁþüÔ/ÇNx  +endstream +endobj +12 0 obj +407 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /CA 0 /Type /ExtGState /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Count 1 /Kids [ 11 0 R ] /Type /Pages >> +endobj +13 0 obj +<< /CreationDate (D:20260318165303+08'00') +/Creator (Matplotlib v3.5.3, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.5.3) >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001016 00000 n +0000000872 00000 n +0000000893 00000 n +0000000953 00000 n +0000000974 00000 n +0000000995 00000 n +0000000065 00000 n +0000000350 00000 n +0000000852 00000 n +0000000208 00000 n +0000000832 00000 n +0000001076 00000 n +trailer +<< /Info 13 0 R /Root 1 0 R /Size 14 >> +startxref +1233 +%%EOF diff --git a/generated_apriltags/2.0x2.0cm/11.ai b/generated_apriltags/2.0x2.0cm/11.ai new file mode 100644 index 0000000..9293c73 --- /dev/null +++ b/generated_apriltags/2.0x2.0cm/11.ai @@ -0,0 +1,75 @@ +%PDF-1.4 +%¬Ü «º +1 0 obj +<< /Pages 2 0 R /Type /Catalog >> +endobj +8 0 obj +<< /ExtGState 4 0 R /Font 3 0 R /Pattern 5 0 R +/ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /Shading 6 0 R +/XObject 7 0 R >> +endobj +11 0 obj +<< /Annots 10 0 R /Contents 9 0 R +/MediaBox [ 0 0 56.6929133858 56.6929133858 ] /Parent 2 0 R +/Resources 8 0 R /Type /Page >> +endobj +9 0 obj +<< /Filter /FlateDecode /Length 12 0 R >> +stream +xœµ—KjÃ0@÷:…N H–%ÛË~ ë¶‹ ¤ŸÚ@{ý:)ñ<yQÈlFxô˜峺ß~¿o¶ë[{÷dV²ÚL°;óiýøJÙå¡BŒ}ê/V_[ûl?ìê&Ø×ƒoãûwãõ3^ëãÚày[ŒÄíAý¸ò§û›1/æÁþWÁ_(Œkc’˧l +Î717ÍQeŠ%Úƒ¶ÉÅÔÆŽôœ€PÅP6(&wJgÛ +ƒË}òq !á5 Cç¼ïú®)6Ë]rš) +¥dÐrlzÛܦnæ[çCêÚ~æ(”6’AËQòÒqaì&J‰µ+ÃTæt*(§ðª®LXYè)(lAÉQ:IÇzOAaƒ JŽ•FTl+T :M¨Ôi(â @Q(m$ƒ–c­Oe¡§Bi#´+â™æ¶ SéØi@o*Ö» +dPr:Öç6È äX›¥²0w °A%ÇÊ0•…¹*íÅ4*UŠõz ¥dÐr¬¡,Ô tŠXEÀ«þÚ™>ù¨X¯(lAɱR„² ƒÿ ç‡PD°??ó kC»h +endstream +endobj +12 0 obj +434 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /CA 0 /Type /ExtGState /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Count 1 /Kids [ 11 0 R ] /Type /Pages >> +endobj +13 0 obj +<< /CreationDate (D:20260318165303+08'00') +/Creator (Matplotlib v3.5.3, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.5.3) >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001043 00000 n +0000000899 00000 n +0000000920 00000 n +0000000980 00000 n +0000001001 00000 n +0000001022 00000 n +0000000065 00000 n +0000000350 00000 n +0000000879 00000 n +0000000208 00000 n +0000000859 00000 n +0000001103 00000 n +trailer +<< /Info 13 0 R /Root 1 0 R /Size 14 >> +startxref +1260 +%%EOF diff --git a/generated_apriltags/2.0x2.0cm/12.ai b/generated_apriltags/2.0x2.0cm/12.ai new file mode 100644 index 0000000..479d760 --- /dev/null +++ b/generated_apriltags/2.0x2.0cm/12.ai @@ -0,0 +1,72 @@ +%PDF-1.4 +%¬Ü «º +1 0 obj +<< /Pages 2 0 R /Type /Catalog >> +endobj +8 0 obj +<< /ExtGState 4 0 R /Font 3 0 R /Pattern 5 0 R +/ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /Shading 6 0 R +/XObject 7 0 R >> +endobj +11 0 obj +<< /Annots 10 0 R /Contents 9 0 R +/MediaBox [ 0 0 56.6929133858 56.6929133858 ] /Parent 2 0 R +/Resources 8 0 R /Type /Page >> +endobj +9 0 obj +<< /Filter /FlateDecode /Length 12 0 R >> +stream +xœµ–ËjÃ0E÷ú +}"Y/{ÙdÝvÑé#D…6Ðþ~•”ø^iQÈlF{t˜;YÝo¿ß7ÛÇõ­¾{R+¬6åôN}j[?1™4 “ó~ŒãÅêk«Ÿõ‡^Ý8ýzÐõT¯ßÕã§ëãZÑýº(Ô¨öDm]ÙÓùM©õ ÿ«`/êZ©hÒ鱃OÃpT™kT{¢!ƒ÷îHÏ Šbƒ¢\6Öæ1‡Å¶ ~2iŒÖOlÈðš†Ã`RÎ.Å…â0Rˆy¡Ê6è åˆ¾ìØ‰t¦lƒZʱTédJ4ëbã"i‚2I³b'SP²¡BŽèËŽíL‰’ urlU“væÔÀ/¶Å@)i†BI“b{2¨Ù¤é‰$Çv¦ lƒRŽ J'S¢óè8i‚×4Ä«„Q/2%ê äØú†•N¦DɆ:96‚*Ñ€¢¢¤Ê<Ó¬ØÎ”mp…”#~‡Ù±)(Û ƒ”c#(~5ò¶Dç§›“&(ófdÅö¼ˆ’ url ¡tdèÿøù&"±??õ ²²šB +endstream +endobj +12 0 obj +416 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /CA 0 /Type /ExtGState /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Count 1 /Kids [ 11 0 R ] /Type /Pages >> +endobj +13 0 obj +<< /CreationDate (D:20260318165303+08'00') +/Creator (Matplotlib v3.5.3, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.5.3) >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001025 00000 n +0000000881 00000 n +0000000902 00000 n +0000000962 00000 n +0000000983 00000 n +0000001004 00000 n +0000000065 00000 n +0000000350 00000 n +0000000861 00000 n +0000000208 00000 n +0000000841 00000 n +0000001085 00000 n +trailer +<< /Info 13 0 R /Root 1 0 R /Size 14 >> +startxref +1242 +%%EOF diff --git a/generated_apriltags/2.0x2.0cm/13.ai b/generated_apriltags/2.0x2.0cm/13.ai new file mode 100644 index 0000000000000000000000000000000000000000..99307208e00aeaa310070bed2faa84c31e6109c2 GIT binary patch literal 1602 zcmZuxeN0nV6d#!(+fz3Ga4~+dySR=IXWzYjZE2aU_Jay&wV{}qsnOT=wLI3o*Sub~ zVirvt&bSYffkLK4fiY|{LX3e;_aWJsBokv6b%=73gla)<&3jPmVbG3F!z=;R|syCl);<}lbs?y);ad@hOs zT^1sDS`%+u;#~%I0NP!}`gkD#^osk|&7Nw`i$PI5_-r~TwE&ACh&TceR5GxUY&d}c zMkR)m^YN@zjFKE=3!PrAH)@QCVGKG$+KD9KVkP3N&_U4^Ttuvqyo4W_fEj*d%2LRY zFG{lM;De-oKs$njAUo$3eT2`AoDjfO04XGsG9p;w*bo(M>1}dW_rryb$Hn_MwU#!i z-WZ%-cQv%ww61iowX*8j$%hjiKRsAJ@X@}KlS@pa_r2!9XieYLp6hpWi@ttz_vbEO zuj$CH$A4dbI?{Y@DhdYAXMoZr}KN{S;s zc`~*Q>_}p=91=4zJGQc3l7nJFKvtj4La>DFW`#w2S%Aodr3{%YsEmNJOfo=OEE%9Y zddaJ8VhtH_1n3RCFG6GyS5t*7KSln^Mwm?-r4&Qk$mC*P6zuW>$hQ}3phgcFWI%cy z1DRFOkOyI20j1c963BP5I2;slkoSOh7`0B#ybh``4o8YJ|3+frs_JO*)W>P>{k)RO0*;1V~H4nFr`~Iz|WlU6re1Fav4tTss6IY1>>3LVYI2=*VhHvoq*3bJ0Muhtln|8FmO7Qfk8z%Lh3r tzU<2czX;;}B+INq6y-69FE%&7n> +endobj +8 0 obj +<< /ExtGState 4 0 R /Font 3 0 R /Pattern 5 0 R +/ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /Shading 6 0 R +/XObject 7 0 R >> +endobj +11 0 obj +<< /Annots 10 0 R /Contents 9 0 R +/MediaBox [ 0 0 56.6929133858 56.6929133858 ] /Parent 2 0 R +/Resources 8 0 R /Type /Page >> +endobj +9 0 obj +<< /Filter /FlateDecode /Length 12 0 R >> +stream +xœµ—KjÃ0@÷:…N H–%ÛË~ ë¶‹ ¤ŸÚ@{ý:)ñ<yQÈlF{ôÐŒd²ºß~¿o¶ë[{÷dV2ÚL°;óiýøKÙå¡BŒ}ê/F_[ûl?ìê&Ø×ƒoãó»ñú¯õqlð¾-Fb‰ö ~ùÓý͘ó`ÿ«à/Ʊ1ÉåÓ 6盘›æ¨2ÅíAÛäbjc GzN@¨b(‚;%ˆ³i…ÆÁå>ù8Ð𚆡sÞw}×ΛÞÅ6·©›) +¥dÐrŒ­ó!um?s”Ùè(”6’AËQè¸Ðv¥<¡åXi¦²Ðw ÓÒ±u*MÅúz”+‹0n˜Æå® 9ͦ*[«HxMCìS(bgp½&J‰µÑíp¬×T(m$ƒ–c¥Pe¡¦ SÄJêTšŠ²vT… 2(9ÖêTj + +dPr¬Š'#§*>TšPç;MÅzõ„ÒFb-ÇZ‡ñÜ¡£PÚH-ÇZ/•…¾JÉ åXi¦²Ðw SÄn¼ê¹3­ëÕ… 2(9Ö:¬,Ô6È äX)TYÁÿšóK(4ØŸŸù#HÜÄ +endstream +endobj +12 0 obj +448 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /CA 0 /Type /ExtGState /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Count 1 /Kids [ 11 0 R ] /Type /Pages >> +endobj +13 0 obj +<< /CreationDate (D:20260318165303+08'00') +/Creator (Matplotlib v3.5.3, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.5.3) >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001057 00000 n +0000000913 00000 n +0000000934 00000 n +0000000994 00000 n +0000001015 00000 n +0000001036 00000 n +0000000065 00000 n +0000000350 00000 n +0000000893 00000 n +0000000208 00000 n +0000000873 00000 n +0000001117 00000 n +trailer +<< /Info 13 0 R /Root 1 0 R /Size 14 >> +startxref +1274 +%%EOF diff --git a/generated_apriltags/2.0x2.0cm/15.ai b/generated_apriltags/2.0x2.0cm/15.ai new file mode 100644 index 0000000000000000000000000000000000000000..855fef0a5c1ba86ef02b15f2cb616f71389f7292 GIT binary patch literal 1601 zcmZuxe{2(F7={J+$Hfs6M}U;+2V`uWuJ_%wz14D{OG*4FN>UcW-u zD4}C+is45bn2d-_HW^`yj_DK~S&TDg5kr^|H$m|aLRd>Vx$((Pd5ieKwAVP1ms3g zQ9+54hXnE|3APB@;6wrvfk92O%Mt>5^|wPnNK_cXs1{3_@qX_sp-$uxcs6Ufs**)bL}vx7cfbZj6#5cY6lkFjVNKD zR6V0w^!Nm`9Kk)12qUNCO4(9Iujh??+81uXAt*RmrGuht#gH6U+?XFiz$`z6vJ`Si zp(LAjUjW}9(Dnc($SS&J59YHYklyMRYf1h?#}d``686pV_RhzSdsVJxbR-n;3vmMe%yKS+TFSz%E#L_ z53kuW)LSFoq}Lv`mpuD>J73>FvUa+^;?mDwo|@=B)f>G(^6=x+lW%{!Xr|9x9||w< z-~8(9UrLTIi#q<@1ea0a>fG=V*)i_F_L6eDXYf*e-tM-OFF$zo%0uNP>kD0Dn%u!H znvV9BbywKw?x!qAFXI7H+?IA?I>?u4}t?2P;zC1=HB+!_FHXDmT!z=b(GdN%Q#|hd!u&v3%f_J5#{d zYOdU~`fP94nDKJ3`1)k@^Nj`06W-Cho|PKY;q%_zx=!~u3&azz?!DE#@Bz&f_-S%^ z)(Cy3c;fGUt=iRxnxAu=E?=Q0+#7D&f2*qVc+Sp)N?_5Zvd*lb=+)2Oe~vBl>7`?@x6$o}WRF}bBHcK_ZuJzR0*d7}Qrs1iPikC4pT zz;H=##Dzz&tzcUcBW_4YVotThE=ir37(mQtF<~s>x;bCbZUHb@U@00Se#$tIxFiE4 zUdaH7@{*-3au_R|0rZnTPYAmrZl+q|KGpr|LYQltNx2Md!NNs8S+WxU7h20$$Z}B6 z@C;|vL;a(WFMzN>OQ~_P0t%}I6b#5H;B$c;dYw_He++mL3WmyPx@q<=ovbwCB)ly= zVK=riC_7{cv{&{_0 z&pG$p?|k3UyGop|AfuVl_uT}i&N2ufxw~WDda!!ep)R&tu zV9SE!Onc((NW9Cy4#2v-g5*=`fHj_8RbJzlMGRhz6`ySfrC*>}QB)iT2*x{bkZxp! z0JeCIEwbbj>}nI~fks%YMyt(aLmbCj_<3KX0hgeWxVICrJ>VSme@%Ul1k^x$sx;h_e{#o>_WsCe+NBJuR93yU7Lomc`q=(esv z7tx^+&$1gP-Qi23CfFVO#?4Z;=iZU}Ztrlp*T$53-&heaUYL$fEb~S$_beVMU7A&W z)#b0&-_kAJ+oF?Pn)Eb2@(z<50Ii22S9)K+b;I5A`9ynu$8&`r{C;fz*HzP_RX^J+ zR=!3m=0#V0^5$p9znXd}$Y0m?^)KW{!y~ytzO_99HxF<-i>AA$%uimkUF|iDKVz6E zTBzS}GH1ZecJJQUbs(qVvbo^fyeCRYp)cAa*Lo)J=DVXmJ-h9oJ>u^%7fqiKd$NAn zombIz-tt~E%3gl4>$c&~PaB(4y-l|5{cmczqigJ&e;?g>adhi3o_ZiLu!I|8gQOET zn@mZE#N>U;msLw2-?!_hBd(# vkhR#xFH>q&5Su62XqBo;Wf0qLZeO!ZNTkGuv7q4?t;l5N8NI%=+{ye8n~(tA literal 0 HcmV?d00001 diff --git a/generated_apriltags/2.0x2.0cm/17.ai b/generated_apriltags/2.0x2.0cm/17.ai new file mode 100644 index 0000000000000000000000000000000000000000..c28ce96b3fd278e323e29cb0b444b201b98fd65f GIT binary patch literal 1595 zcmZuxVN4rk7#_yqa*K%ukSLmcnsgL|-d(SE^a!%hmM%b9Zj&#< zj6W2A5Kx>Or~@;SAs}i-%odo;&H1Annr#|&P82l@5yBRXKQ<#0{k~ocwe|koeb0UG z`@Qe;JTM?~DgY^nemM-x2x$Ohamfa(a`?SUecfkh8;stN>*{w;_Q%9;XLyd+3LQzefK!2xuR!-?|C5eUed z5IWPAc-s^28qf?#Pn+lqD1N}{;Z1IDyDULa7Eiue4xQRSm7=IH3}{NPz>cbs6$J2l zj(XV_5N&D%)qr=foQdPjJk2mx)|$3L1qh;sf^|9wvOx~3otlLB!31pZgDFcP2OpGV z(-{b&{sHL>B7z*Ur1}t_E?M!zHbCnnqc*}&lf@7b89F^s*Zw(`Tm9Sq?tP%{yV#vm zJ1$M%KCyk+bZ-w};&Czj5Qv z_|U?nI}~ei?Y#f?FSD!COYGVY%l`F|;{{_auSaM5uCB&b{IlHH%*E*Nv0fQ&IeqiX zcURTgbL01|w`_f3l)7@PXXkMK(o%2e#rF9(=PUzXWmz0IrQa8ZmqxdUdG;G%@(eq% z@=VXiq3*vvJ6XAZ*R}qRdi##v&Z+I|uIERZb4L3b#}}L0Z2PXf9nRV{{}BAzo1b;v z0*9y0tb9UwN)Bb`a^u3~y+=+;yhr%{gv}7M35Q2m+x)_x{kB_gS$=)z)rP^IMVIU< zc=)4S+7qjz<`8j?x zwU(;vpU)0fZ+qm{RrmTmgL@-GSoeubN;n{XdH9wsG?jEkTyhvY0Gg8+_CZ1nn|j4Y zNh+j@2H1A?DugBcw&^HR5&?~*m7*@}q>O~v9~mHaNd|~Dmpt08b|P)lfUFDn!pIMC z8#Q3x>Au%T!DiWXN;9M#NfriF#etn~bd;MZGeiP02|4EtYbr{J~Ne>&x$%ti>0Zul-{5w27Y^l@W=U*f10|7^jssGaO+sRJ)zTzsb4!4gdfE literal 0 HcmV?d00001 diff --git a/generated_apriltags/2.0x2.0cm/18.ai b/generated_apriltags/2.0x2.0cm/18.ai new file mode 100644 index 0000000..e9e6962 --- /dev/null +++ b/generated_apriltags/2.0x2.0cm/18.ai @@ -0,0 +1,74 @@ +%PDF-1.4 +%¬Ü «º +1 0 obj +<< /Pages 2 0 R /Type /Catalog >> +endobj +8 0 obj +<< /ExtGState 4 0 R /Font 3 0 R /Pattern 5 0 R +/ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /Shading 6 0 R +/XObject 7 0 R >> +endobj +11 0 obj +<< /Annots 10 0 R /Contents 9 0 R +/MediaBox [ 0 0 56.6929133858 56.6929133858 ] /Parent 2 0 R +/Resources 8 0 R /Type /Page >> +endobj +9 0 obj +<< /Filter /FlateDecode /Length 12 0 R >> +stream +xœµ–KjÃ0EçZ…V KÖÇö°È¸í  é'D…6Ðn¿NJüŽƒ<(Xé?Þ•Œ›ûÝ÷ûv÷¸¹ÕwOª‘Ùö¨œÞ«OmÇ_L& íà¼ïc5ûÚégý¡›§_z¼ÿß×ÏxmNs…çuV2–ÑÔŽ3{¾¿)õ¢ôì•Â8W*št~@GglëSÛžT¦±Œ !ƒ÷îD/«ÊY9gÎülY¡~0©Ö4$\ÓÐuÆÚ®ïÂLÑc]ìB?SJ©PËQêÒq!Ò‰ÒFƵ AeÕöƇb7[tj(“\µ‹…ôòB¿@aƒ +• Màyá²B¥Ÿè"ᚆ¥ð2÷û5QÚȸ–£œ:–3J©P˱TÆ«„Ë‚¶&uKq–4àªIOu©(½£"(lP¡’ci‡å…LAaƒ +• Aå…L…ÊIÖ9/T,÷K(m¤B-ÇBòB¿@§w$»Xç¼PQv Aaƒ +•e·Ó±œ)(lP¡’c!¨¼ ƒoÝËCìÏOý çxÎ +endstream +endobj +12 0 obj +412 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /CA 0 /Type /ExtGState /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Count 1 /Kids [ 11 0 R ] /Type /Pages >> +endobj +13 0 obj +<< /CreationDate (D:20260318165303+08'00') +/Creator (Matplotlib v3.5.3, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.5.3) >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001021 00000 n +0000000877 00000 n +0000000898 00000 n +0000000958 00000 n +0000000979 00000 n +0000001000 00000 n +0000000065 00000 n +0000000350 00000 n +0000000857 00000 n +0000000208 00000 n +0000000837 00000 n +0000001081 00000 n +trailer +<< /Info 13 0 R /Root 1 0 R /Size 14 >> +startxref +1238 +%%EOF diff --git a/generated_apriltags/2.0x2.0cm/19.ai b/generated_apriltags/2.0x2.0cm/19.ai new file mode 100644 index 0000000..a09015d --- /dev/null +++ b/generated_apriltags/2.0x2.0cm/19.ai @@ -0,0 +1,72 @@ +%PDF-1.4 +%¬Ü «º +1 0 obj +<< /Pages 2 0 R /Type /Catalog >> +endobj +8 0 obj +<< /ExtGState 4 0 R /Font 3 0 R /Pattern 5 0 R +/ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /Shading 6 0 R +/XObject 7 0 R >> +endobj +11 0 obj +<< /Annots 10 0 R /Contents 9 0 R +/MediaBox [ 0 0 56.6929133858 56.6929133858 ] /Parent 2 0 R +/Resources 8 0 R /Type /Page >> +endobj +9 0 obj +<< /Filter /FlateDecode /Length 12 0 R >> +stream +xœµ–ËjÃ0E÷ú +}"Y/{ÙdÝvÑé#D…6Ðþ~•”ø^iQÈl¤ƒ=:ÌŒä¬î·ßï›íãúVß=©f›ƒrz§>µ­¿˜Lš†Éy?ÆñböµÕÏúC¯nœ~=èz«ÏïêõS¯õq®è}]Æí‰Ú:³§û›R/êAÿWÁ^(Ô¹RÑ¤Ó ::cŸ†á¨21Ú Ñø¼wGzÀPÄ å²±69,–õ“Ic´~bC†×4“rv).‡ÑøBÌ EP¶A)GŒu1‡qáˆÕØ”mAÊìØi»™² žrl4SíFgNübY¢sê¸ Êt#+vú”l(‚c«N¥SS¢dC„…*½„Í#ª4ëîiìSRlç ”mAʱ‘Þ/¼,ѹ9‹¯iˆ+b5V$J6AȱÕ`¥SS¢dC„…*š‚"uTi†2'#+Ò EP¶ÁRŽ8ר±]SP¶A)ÇF¡J§¦DçWš ÌÉÈŠí|%Š äØHBéÈÐÿñóK”Db~ê<š@ +endstream +endobj +12 0 obj +424 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /CA 0 /Type /ExtGState /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Count 1 /Kids [ 11 0 R ] /Type /Pages >> +endobj +13 0 obj +<< /CreationDate (D:20260318165303+08'00') +/Creator (Matplotlib v3.5.3, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.5.3) >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001033 00000 n +0000000889 00000 n +0000000910 00000 n +0000000970 00000 n +0000000991 00000 n +0000001012 00000 n +0000000065 00000 n +0000000350 00000 n +0000000869 00000 n +0000000208 00000 n +0000000849 00000 n +0000001093 00000 n +trailer +<< /Info 13 0 R /Root 1 0 R /Size 14 >> +startxref +1250 +%%EOF diff --git a/generated_apriltags/2.0x2.0cm/2.ai b/generated_apriltags/2.0x2.0cm/2.ai new file mode 100644 index 0000000000000000000000000000000000000000..f97bb36f8bc90e114c392af448fbfc78ae72d878 GIT binary patch literal 1588 zcmZuxeQXnD7&kQ$azJ2lhCd|njdNM2Tko!SyB8swBE2pILZOMp;RBw)aw1nE*_zej|i8Qo)dqWoe40XSO`R<5a87CM>hIeL_Y+^bn@kL=-dV@k|e`8peeNi8>&W>5MWYs zG>Uu-!Ib5YgD=Xm z>5N2C|A2Hx5kYp*F9#5x4Wbl+VL+=Sqc-AD5rsA)@xtrR)VEAh>kcf$i5F{ToBtYK z`{CGc{VP^pDY18Q)7vKw)mKeSCN`a3$<1`Ji_iA{3>SV{xY6S$28!jfZ+S zuUPaB-15ytV*9VX-Tlwz-&?((U$(Uyo5qiN_k8hZ*W%5Kp`BOm{A5e#=%+&^F3%Ie z-4jLQ)B6`+-FMb~Vo(2x3#`R)^69G|ogJB)|7Er)d~oiA@5dj!P=8yparLgfR<6~r z9GmoQ9q5>Tx%>UK;e)Yy+dXGqqq{m%qeo6X*T1GVd`Wv>swZx~6!kn-u?o(fuh7iT zOkbPpD)w#HPF>d!+`3fn@t)+^`k|wmzuy~f*t%*MXW=z3X+sCf=VHmG!>7MKx9L>5 z=gbd9LmN7+cl}s7+9Q~cZ5U+Vsa$it$>K2Ws5y7Xy{uAv z_n*I|aSVhfOCB{neD~ns#XdEkHT6gEU2*Scl_&%E-Fk0dD^VN;){@%l-e0v}7Hhw% zNjeW(Q&Wdl-|T*?VW0K(8&{9-XdKy6g9V?NPDc9asp0Xm6qB_>T5A|<2HUb2Hb6!N z%R*cQe&m?ks8}Efe1g9fO(2t@ zJcpNrA_fTx&@4eMJ%BS97y}4`C5%Q);Ri@rjHd7?|BF#Phn$^{@yOA6n2G%#%!n3B z{y7s`O?eo_p;6{xjG-`=XA5i5Q-v6>FTiNZnA00ngh*6W((AoW3d$foPqe<=vW#Vr eUS*!hZV{1)iH$=+foWQ4z1~EWmO3^%iGKk}Yx$f2 literal 0 HcmV?d00001 diff --git a/generated_apriltags/2.0x2.0cm/20.ai b/generated_apriltags/2.0x2.0cm/20.ai new file mode 100644 index 0000000000000000000000000000000000000000..be696de599d6447e1114945d8b534d8d76ace0a8 GIT binary patch literal 1617 zcmZuxeM}o=7&p#MJcMAzl4V=u71B`H#@&0@D=lmcDD4IUYYIdRuyFLQaM0f2^@YI< zD!4_n7=-z;sVop6i?WHt3@3BL%tiKL;s`TpKuug&HpvJYF*6XO@7rskQ174X{rY_V zp6B-%oOzDjFwI06&iw?=UZfykfLc>e?b!opr%)^T0gDkApi5gDB|zH*BzVvT}GOY*Btnj7;&2w35VP!>iGNfct! zA$xKEfOdE>L3xr}6)~TMl2VK60MtpwZTOKU2|lW&XY}MZ^;elV`@y!qSZ zrSCrb$7OC^9EcVUT>bh%!_=)MOhV5~d*_-PZY|9Gb$fVT9QE2oZy%X{ zw{C0E(P+b4}+* z)l*TQ$k+Muvhy>kw~zJ5lyzyd8Rwc=!*TQC(YKQ}m(Gh6YR$(n+5CaT-W2CZJC)m6 zS^W30=cR&pZ(m1OQd`1g_d=$g<~`5Q>G7n3KXTI({q<5*>{CDX_AYJgtN5ams3ADI zpe_O8Vzq1-)(maKi;QGd$s_l` zn3usateN4U1)6z|;dU~XWQIvjp>#V{1I8i&HF{O#m1{sVm&T`YyFeX6js6Up_O1Mt zrfRj=311Jf$&KX{?$s!IK#kyTz!L~YND1SmMUy-f1E7gwRvuvHd5#Aju!2F%6nTIW z!XRV9H)SoxSge@5b(j_6Q?MRO{~yeX&%#EBOL}B2EEAd6 z!bjLa$b|KVG(q-CTHyBQD;^aD=812cOI67l1nyjkd_=+|5@P*G&`^L@$nqA-V6Y!_ GQ2zmt?)^Xj literal 0 HcmV?d00001 diff --git a/generated_apriltags/2.0x2.0cm/3.ai b/generated_apriltags/2.0x2.0cm/3.ai new file mode 100644 index 0000000000000000000000000000000000000000..651f93a64484d4dc2c100ff119312e1ea0ac8b5d GIT binary patch literal 1608 zcmZuxZA=_R7`6gU-PCFunkd9Leo#KWow>a|_7KUzaR--DyWA2nt*2S;mgC^q#l1n# zmXN4z8a1JsP!fq2FcnBYOi5{CkJd)}!!{LS0Bu8P(u%EAsr)bvG59redj|*S{@I;p z=Y8j$=Y5_xN33)`hfEeG=j1Fn5oHj7Kx=4bN=pGN$^j(|%mncOw(hMq1+Weo%ONcQ z%E}l;_0x?)Qh+mpt0XLA1z3zAm!@LC8Q-Fev7)Phr%Q$vbNc$=&RzP3-A2WUOPJY!-nBg z&+q@IL;BVE!u-Dl*9#9u>kE2ur@bn$Fh3<6t?S`WZLvEnWg8c7y!R>tGSRWL_p@^6 z91JS{cH8|kZfUgB0o1|M>s3!NO7wv3P1-yJdCfxI{U%fVYh zZ}*vdZb4ZeQLk-Tm%~?{7anXL<0I`)?ll`1It} z?!4>vjHjwhLz7oBjn5s!lUKU)y#DH}0dcbb^3n%+Ghzm<^YbaT|0*-KNxX9{d;6ag z4;W=lb#-+N+P~gdu*qgFKhm}NuO}~-rkAHJ{~=wxwZAu$njqe>xE*4hq%F6Yxr9Yx z`lHxh;7wrE6mey&ikG@3A(wIls0SV81dE$*)n2Sm28ie^Nov$qDG8}rQb1~%6p#uq zakN8gCqhSn-5K){y(*q=k3=My~kGLo~ zs?FaB8f0G!=|DU~eiAPcU1?+>Ko$nBJiyBH91j}73Wf+%^Z{xDLy#k!ti_-p5Vx>EoJ!btM%tnUH+INLL0i(@ur3EfiXI#J rZnaSZv3`<~=Fv1NgV=bJg8LLgA|*DAWgW+8MP`eQ$;ol&k}3lhzY?TFRYa!*K^r>9#-sRB6av!T zfzGs~@7DC)4%z|f^a*ai>;)#xeaj|SKoTLSNd;d>ht4jbUX~RY1~jE*U`5$TG6Fa) zMziGh3l=4ca=;d>NpIo|9L+Fh)|~T(5^xAAa@Ob|$QCKAL{t&+g9(`D2UC_s4n8Qu zChrfT`T@xY5kWRdRNRQqCQ0@}AD}goQ5j*VN(S0bZGQ)E>5P>8HhHf1sQS-a+b$%x9-3KpX5!rV*3C~k z9_yT&z3w^vQrFwN9{&9iLQ@$TE_hJ|lVBqD2u`v&3bgN`2?3#vCf(SOkrD;e687)Wj@ z3~XDm!Z-O;WBbIJz01DsySH?5dT~#2RRy>C`46W*K5p6n)2F7f(TjH!M!veKbf~Pf z=WD*o$L(ISYO;?=hj`<@Rf%`&4jd~u$bS~QA#%8}fz!RyRhWwM#fvdsiEb5S|hs*3GRn52UzS>ndw5Ro~8ZUZQ;g1&_+<5cw@{wszY;bn*=yIa0)uG0d zM!lQR9+$HE@t;p#Nv>I1JTvKideQqu(bw?Qr(2Ys|5WR6kByW$qlwhOVXO_bXE3aS z^e8M$E0#%SKI&e|MfD;lv?bgT5y>XoE9CY7mmqFO z9Y{y0%;F`gN*;m&w2`3ZFJNL>h6NrlkI{%Jd;vLw(G-n{`Y%SAYf!1V7>C-HgK_Nt zU^S>kxprLMxfEr}w_{kek#c;^Y> +endobj +8 0 obj +<< /ExtGState 4 0 R /Font 3 0 R /Pattern 5 0 R +/ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /Shading 6 0 R +/XObject 7 0 R >> +endobj +11 0 obj +<< /Annots 10 0 R /Contents 9 0 R +/MediaBox [ 0 0 56.6929133858 56.6929133858 ] /Parent 2 0 R +/Resources 8 0 R /Type /Page >> +endobj +9 0 obj +<< /Filter /FlateDecode /Length 12 0 R >> +stream +xœµ–MjÃ0F÷:…N K–%ÛËþ@Öm=@HBThíõ«¤ÄóäEÁ"$H{æ1ŸÜÜï¾ß·»ÇÍ­¾{Rì¶Gåô^}j›?!š8¶£ó~ÃÕîk§Ÿõ‡nnœ~=êü“¯ßçïOþnN{…ûuR²–ÕÔæ=ÿ¾)õ¢ôì•BÞ+L<ß ƒ3¶õ±mO*ÓZVÐ.:ï݉^ +V1”Iµ­‰}ïb˜µêG‡`ýHCÂ5 }g¬ }7Ì¥…ÒF*Ôr:.D:QÚȵ A%åzcm?ôݬ-è4:& X'i*–ç +T¨äXBž¢3ç~ÖVh;ßÅ.ôœ"ᚆxL¡(ݨ(”6R¡–c)'>Ó³L'J¹¢–#žH8–ÏPÚH…ZŽ…ÃÄgšmA§xyW=…–Ò… *Tr,å”2… *Tr,Åÿ¶*™#iÂ5 K,-d*”6²®åXÊ)-d*”6R¡–c!(¾‘±-è”9“¬3E*–ç +T¨äXBZÁûøå& ìÏOýš™Ü +endstream +endobj +12 0 obj +425 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /CA 0 /Type /ExtGState /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Count 1 /Kids [ 11 0 R ] /Type /Pages >> +endobj +13 0 obj +<< /CreationDate (D:20260318165303+08'00') +/Creator (Matplotlib v3.5.3, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.5.3) >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001034 00000 n +0000000890 00000 n +0000000911 00000 n +0000000971 00000 n +0000000992 00000 n +0000001013 00000 n +0000000065 00000 n +0000000350 00000 n +0000000870 00000 n +0000000208 00000 n +0000000850 00000 n +0000001094 00000 n +trailer +<< /Info 13 0 R /Root 1 0 R /Size 14 >> +startxref +1251 +%%EOF diff --git a/generated_apriltags/2.0x2.0cm/6.ai b/generated_apriltags/2.0x2.0cm/6.ai new file mode 100644 index 0000000000000000000000000000000000000000..9dbde59d7ecd86da04ddb26380bd5e270fc76cb1 GIT binary patch literal 1599 zcmZuxZ%k8H6j$aJ^R^l@bS&!dPJ$GfynFllN{h%=`Uh};T>&xi&wOnk?d#h2$m<2l z5Y8clYIa(9mC0%8NM)$65Q_XD^Td}mv`Uq z-gE9bzjJ;k+hup|M)^i2`;$rV@i2n`1d1of6cqv1CHTb%Fc8EI*qW9G5wJD^3n9f1 zii;Uh_R)*xBm+kjmsVo|i@>NEITaZLPTRW#j73!jJpE-@m#TQHMGOv+ckE8$UyNeF zmWRokC5f*s@ht~O09zdpe3I-3Ce8fdeos*JVo(rwz8nrpPQWV53XT8-wFqn^8c`;I zMe|W9`Xr%5iIN!T4c?S*vKTCgImWkVY~|BZ5^?2s6?1182SqehoJ|9z0@>x5hsE zTikxCal-L37;N9TdHj|6`P#!%LmMtSUbOXhY#5t?bxl`?x^LWSzWMw81Gn~G(f!a= zdS~wF@!&*p?zy5zV{NeKH_v_kPyfP&f`Y0qG8?ZA%+5~ybF=TB)OYOjzV}9qg=+@F z^>^pbx!J$0|JHqZRR8Ia`ThLA-k)o7 zmOh?b8^=etT^fJ4U4QbnaYyGvo!wEqSBq;X{lK0No^@>P1$Rc*wavaT`P_`*tDd#r zUT=1F%w}kQD$;|cPd#q$&akGpESBs}CM@e)U)Pp4q5vJ$@@*;+yfuyw(gXRJvB2E$&aS5ndW_+*LLT&9oclYb} zz4v{;@Avx(Jr3t{&}gO#FZ=+8E>jRNKxu5DN=pIl5dxA1Oc?P3df&k|3D9-{2_Yo_ z%E~B7_LD$9DPd;>RrrvABw*H)oQjM9tG{~$gd|l47V=5a9#s*25(0Jj9)}a3FGUcb zE5rEC^7zvpe^!EeK>L~ne^3qpPEW6`Zfuc61h&QwUo8hFH(--x1!({>dIfe|jU;2h zs-NMO{6V2yiQpPYgoQJ5R+AO7EN|gczHk8^LB+{B9Te@AG^Je?F+YTW6@Cb1N#u}= z5^OqyA>2Qpogqw+LlPA~=F=s~0n`kjPBLyoL#iZ%sYuVIGgU2LGr7n9)Tj@4%+xK9 zxcmEqKVErtXyM%XA8-ES zsCjDMclQ2Um4jEaXC9O-=MN3 zlj<9rJEECO`ybV!o*Bp9LFvt|nu4ECX4_jG1BG#MSvJKL8XIH}H!&VL1OobJoM?mgaZbNfGS zc;c^5woF}$^1)8iDd#r_#yXE=`*YQhIf`JbsD5OB;Y>-ZEq{H(QvDqJNY~<_!%zFZ zoGt3#@ONpi_}YML`WU;*%4HnA`%dkX^M={}H4{Dg`$E+@#kY23Y`Q*Ez=bFC^!IDM zi!Sk{felyt*Hdmo-tC8tU0jcC;`8aXrS$1_-!HwNaV5($@b>vz3;CXlhu4)K?|#Nr zU|QUMfAp~rvVQ&U#Gy5x8D4ysNIgE6xaec^B7Br~4DpBH% zgeYtf5U1H~7>iqM)kL%?0EpF;Bq(B@v;&DH(m-O2G?2(FakX7($8v^%t_u1!Y=M|_ zyu@m{)phr;mJO4dq3u|uD5%H|V)kOkHWOpw7#8x7v#<>NEW;NuOi>A?$0;f(b_=L2 zq@Yl+5gcHR79+a_G$YieZKLV%%3nrB4d5ia8vN}d_DXVAo4*M(3SujsK>UP~Bwiw_ z)I>1=nki=G2RMs`wSXqDf8jpOA*j#2F0?HvcpF0=P= z9TN7YMWiKpR51F1bZRuLi7`f*=4=L~hOl%-OvcD2iB20|z7PpzJIH>` zgrvCfMRSrtNf2(Tgd&uHDQ09-6$qHvw?l+b(iFhrBSAVe)m14$umi=h+K_)K2mxtt zKrxHsZz2A+gK9ul){1VQ;sspH{Mj;Ro#cX`AnJTR9Xh#yB1KW5AJ9}R0|8|tDG1$Yw1X&^Z)h5k__`w9s@q;N#A_oUb zuxazjsD41&WJHiva;a{_=T=Ga!dgJbNJeG&p(cq9MDXC3?|)Y}NM)8x&k(Jz=5Hrk z#?sv5feRVw*B`g;;m+UN)fxKh`LyQpJDKv4*ICQRUhBZ@`1EXG`sAt67t4+f$}LsT z+#da;e`@6cyK95x_M7JC&V>Ur+h2~{8oK`D;mablnvCA#IQNLa;*Ec z-AmRPPK;Z}mcFo==em#Phu)2ZEVpd0)vxNe^aifgytMhT$*$b;zMk~rcaB>+_jIq& z<+Ou|%lhoA>%Lm6Us2lWIeDXNh}WGBgg1^Y%{sC=$F5&?H`l@fZ^tjkewQk0(4V&V zmX~B|@)JGz6U(xvGo+55k>i8yH%DISTOQu8ooTMwWLYwiKFqa&-`ZOCJ$j`zE&6@A z_T#>)s`E$5Q#lU~S9L#p`w=XIc+cX3 zi1rdU-DG4FB8g5Q#&&?}1cqf1pN;wP#kxrhOc4#R3WXws#pO3IFw!Lg8ckZ#uwh}P zIK*;E0kLROKs1(soK4a7FXeA;wUF{FSd82VJjiWh<2 zYB5qqj$&vt&9Mx{JVlw;P}G_{B6d&JfPR|@8)X&BJ|_q;2G+pjgIWk1{T7mJnEPc= zH7_zkYs1~-LOM#eYIJ*mQ*_m%4#Yj=CGir}Bo9FW+C)%u58zmqVSxwCVKib2KR`)f zG{vBHEW{`?k5> +endobj +8 0 obj +<< /ExtGState 4 0 R /Font 3 0 R /Pattern 5 0 R +/ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /Shading 6 0 R +/XObject 7 0 R >> +endobj +11 0 obj +<< /Annots 10 0 R /Contents 9 0 R +/MediaBox [ 0 0 56.6929133858 56.6929133858 ] /Parent 2 0 R +/Resources 8 0 R /Type /Page >> +endobj +9 0 obj +<< /Filter /FlateDecode /Length 12 0 R >> +stream +xœµ—KjÃ0@÷:…N H–åϲȺí¢é'D…6Ð^¿NJ<ÏAZ2›ÑÃ=4ã¬î·ßï›íãúÖÞ=™•¬6ìÎ|Z?ýR纱CŒC.V_[ûl?ìê&Ø×ƒnÓó»éú™®õqmð¾ÍFb‰ö ~ZùÓý͘ó`ÿ«à/¦µ1Éu§l +Î7±kš£ÊK´m“‹©1é9¡Š¡lMè÷ýз‹m…ÆÑuCòq¤!á5 ›Æu}º´PŒ­ó!õí°PJÉ å(t¬”t¦´‘'´ …š*Ü)A\l :(+ xMÃRƒåJMAaƒ JŽâ@ÇrMAaƒ JŽ…BåÊIÜ .¶]›zVšPé¡X>/¡´‘ ZŽ…Cà÷ÂmAçQÃSÔ™ŒT”˜Š °A%ÇRƒåJõ@aƒ JŽ2{éXî;PØ ƒ’c¡™r¥ï„Ê8D7êLo*–;LbÚH¬åXú +r­ïfJÉ åXê¥\é;¡´‘ ZŽ…fÊ•¾#v# N7R±\=PØ ƒ’£L6:–k + +dPr,*Wdð¿æü + öçg~ŽzÜÖ +endstream +endobj +12 0 obj +442 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /CA 0 /Type /ExtGState /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Count 1 /Kids [ 11 0 R ] /Type /Pages >> +endobj +13 0 obj +<< /CreationDate (D:20260318165303+08'00') +/Creator (Matplotlib v3.5.3, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.5.3) >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001051 00000 n +0000000907 00000 n +0000000928 00000 n +0000000988 00000 n +0000001009 00000 n +0000001030 00000 n +0000000065 00000 n +0000000350 00000 n +0000000887 00000 n +0000000208 00000 n +0000000867 00000 n +0000001111 00000 n +trailer +<< /Info 13 0 R /Root 1 0 R /Size 14 >> +startxref +1268 +%%EOF diff --git a/generated_apriltags_svg_check/1.0x1.0cm/1.svg b/generated_apriltags_svg_check/1.0x1.0cm/1.svg new file mode 100644 index 0000000..88fcfd5 --- /dev/null +++ b/generated_apriltags_svg_check/1.0x1.0cm/1.svg @@ -0,0 +1,191 @@ + + + + + + + + 2026-03-18T17:39:49.905934 + image/svg+xml + + + Matplotlib v3.5.3, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/generated_apriltags_svg_check/1.5x1.5cm/1.svg b/generated_apriltags_svg_check/1.5x1.5cm/1.svg new file mode 100644 index 0000000..32dd73d --- /dev/null +++ b/generated_apriltags_svg_check/1.5x1.5cm/1.svg @@ -0,0 +1,191 @@ + + + + + + + + 2026-03-18T17:39:49.935930 + image/svg+xml + + + Matplotlib v3.5.3, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/protos/cmvr/api/biohead_command.proto b/protos/cmvr/api/biohead_command.proto index 98527ce..cc91e3e 100644 --- a/protos/cmvr/api/biohead_command.proto +++ b/protos/cmvr/api/biohead_command.proto @@ -174,4 +174,97 @@ message EmergencyStop { } } +message SpeakStart { + message Request { + CommandHeader.Request header = 1; // 通用命令头 + } + + message Feedback { + CommandHeader.Feedback header = 1; // 通用å馈头 + } +} + +message SpeakStop { + message Request { + CommandHeader.Request header = 1; // 通用命令头 + } + + message Feedback { + CommandHeader.Feedback header = 1; // 通用å馈头 + } +} + + + +// 表情控制 - 微笑 +message Happy { + message Request { + CommandHeader.Request header = 1; // 通用命令头 + } + + message Feedback { + CommandHeader.Feedback header = 1; // 通用å馈头 + } +} + + +// 表情控制 - 惊讶 +message Surprise { + message Request { + CommandHeader.Request header = 1; // 通用命令头 + } + + message Feedback { + CommandHeader.Feedback header = 1; // 通用å馈头 + } +} + + +//表情控制-疲惫 +message ExpressionTired { + message Request { + CommandHeader.Request header = 1; + } + + message Feedback { + CommandHeader.Feedback header = 1; + } +} + +//表情控制-愤怒 +message ExpressionAngry { + message Request { + CommandHeader.Request header = 1; + } + + message Feedback { + CommandHeader.Feedback header = 1; + } +} + + +//表情控制-悲伤 +message ExpressionSadness { + message Request { + CommandHeader.Request header = 1; + } + + message Feedback { + CommandHeader.Feedback header = 1; + } +} + +//表情控制-打哈欠 +message ExpressionYawn { + message Request { + CommandHeader.Request header = 1; + } + + message Feedback { + CommandHeader.Feedback header = 1; + } +} + + + diff --git a/protos/cmvr/api/biohead_service.proto b/protos/cmvr/api/biohead_service.proto index 7ca4d83..f2dda32 100644 --- a/protos/cmvr/api/biohead_service.proto +++ b/protos/cmvr/api/biohead_service.proto @@ -19,4 +19,29 @@ service BioHeadService { // ç´§æ€¥åœæ­¢ rpc EmergencyStop(EmergencyStop.Request) returns (EmergencyStop.Feedback){}; + + // å¼€å§‹è¯´è¯ + rpc SpeakStart(SpeakStart.Request) returns (SpeakStart.Feedback){}; + // åœæ­¢è¯´è¯ + rpc SpeakStop(SpeakStop.Request) returns (SpeakStop.Feedback){}; + + rpc Happy(Happy.Request) returns (Happy.Feedback){}; + + rpc Surprise(Surprise.Request) returns (Surprise.Feedback){}; + + rpc ExpressionTired(ExpressionTired.Request) returns (ExpressionTired.Feedback){}; + + rpc ExpressionAngry(ExpressionAngry.Request) returns (ExpressionAngry.Feedback){}; + + rpc ExpressionSadness(ExpressionSadness.Request) returns (ExpressionSadness.Feedback){}; + + rpc ExpressionYawn(ExpressionYawn.Request) returns (ExpressionYawn.Feedback){}; + + + + + + + + } \ No newline at end of file diff --git a/protos/cmvr/api/humanoid_robot_command.proto b/protos/cmvr/api/humanoid_robot_command.proto index 13273a1..75804fb 100644 --- a/protos/cmvr/api/humanoid_robot_command.proto +++ b/protos/cmvr/api/humanoid_robot_command.proto @@ -40,6 +40,17 @@ enum RobotJointIndexDirection{ ROTATE_Y = 9; // 绕Y轴旋转 ROTATE_Z = 10; // 绕Z轴旋转 } + + +// 4x4 齿¬¡å˜æ¢çŸ©é˜µ +message TransformMatrix4x4 { + double m00 = 1; double m01 = 2; double m02 = 3; double m03 = 4; + double m10 = 5; double m11 = 6; double m12 = 7; double m13 = 8; + double m20 = 9; double m21 = 10; double m22 = 11; double m23 = 12; + double m30 = 13; double m31 = 14; double m32 = 15; double m33 = 16; +} + + message MoveJ{ message Request{ CommandHeader.Request header = 1; @@ -157,4 +168,35 @@ message ServoJ{ CommandHeader.Feedback header= 1; } +} + + + +// 获å–当å‰ä½å§¿çš„4x4矩阵 +message GetPoseMatrix { + message Request { + CommandHeader.Request header = 1; + string base_link = 2; // åŸºåæ ‡ç³» + string ee_link = 3; // 末端连æ†åç§° + } + + message Response { + CommandHeader.Feedback header = 1; + TransformMatrix4x4 matrix = 2; // 4x4 ä½å§¿çŸ©é˜µ + } +} + +// æ ¹æ®å…³èŠ‚è§’åº¦è®¡ç®—æ­£è¿åЍ学ä½å§¿çŸ©é˜µ +message ComputeForwardKinematics { + message Request { + CommandHeader.Request header = 1; + string base_link = 2; // åŸºåæ ‡ç³» + string ee_link = 3; // 末端连æ†åç§° + repeated JointCmd joints = 4; // 关节角度 + } + + message Response { + CommandHeader.Feedback header = 1; + TransformMatrix4x4 matrix = 2; // 计算出的4x4ä½å§¿çŸ©é˜µ + } } \ No newline at end of file diff --git a/protos/cmvr/api/humanoid_robot_service.proto b/protos/cmvr/api/humanoid_robot_service.proto index a4d52f5..b046eb9 100644 --- a/protos/cmvr/api/humanoid_robot_service.proto +++ b/protos/cmvr/api/humanoid_robot_service.proto @@ -16,4 +16,7 @@ service HumanoidRobotService{ rpc getPose(GetPose.Request) returns (GetPose.Response); rpc calibrateZeroQ(CalibrateZeroQ.Request) returns (CalibrateZeroQ.Response); rpc servoJ(ServoJ.Request) returns (ServoJ.Response); + + rpc getPoseMatrix(GetPoseMatrix.Request) returns (GetPoseMatrix.Response); + rpc computeForwardKinematics(ComputeForwardKinematics.Request) returns (ComputeForwardKinematics.Response); } diff --git a/protos/cmvr/api/system_command.proto b/protos/cmvr/api/system_command.proto index ea6f21d..52b9922 100644 --- a/protos/cmvr/api/system_command.proto +++ b/protos/cmvr/api/system_command.proto @@ -59,3 +59,14 @@ message UpdateParamsCommand { CommandHeader.Feedback header = 1; } } + + +message StopAllCommand { + message Request { + CommandHeader.Request header = 1; + } + + message Feedback { + CommandHeader.Feedback header = 1; + } +} \ No newline at end of file diff --git a/protos/cmvr/api/system_service.proto b/protos/cmvr/api/system_service.proto index f96ea81..c8aee52 100644 --- a/protos/cmvr/api/system_service.proto +++ b/protos/cmvr/api/system_service.proto @@ -10,4 +10,6 @@ service SystemService { rpc GetSystemStatus(GetSystemStatusCommand.Request) returns (GetSystemStatusCommand.Feedback) {} rpc UpdateParams(UpdateParamsCommand.Request) returns (UpdateParamsCommand.Feedback) {} + + rpc StopAll(StopAllCommand.Request) returns (StopAllCommand.Feedback) {} } \ No newline at end of file diff --git a/protos/cmvr/config/camera_config/camera_config.proto b/protos/cmvr/config/camera_config/camera_config.proto new file mode 100644 index 0000000..602337f --- /dev/null +++ b/protos/cmvr/config/camera_config/camera_config.proto @@ -0,0 +1,60 @@ +// camera_config.proto - 所有é…置都在这里 +syntax = "proto3"; +package cmvr.config; + +// 枚举定义 +enum CameraMode { + CAMERA_MODE_PHOTO = 0; + CAMERA_MODE_VIDEO = 1; +} + +enum StreamMode { + STREAM_MODE_RGB = 0; + STREAM_MODE_RGBD = 1; + STREAM_MODE_DEPTH = 2; +} + +enum AlignMode { + ALIGN_MODE_COLOR = 0; + ALIGN_MODE_DEPTH = 1; +} + +// RealSenseæ‘„åƒå¤´ +message RealSenseCameraConfig { + string id = 1; + string serialNumber = 2; + int32 width = 3; + int32 height = 4; + int32 fps = 5; + string codec = 6; + CameraMode camera_mode = 7; + StreamMode stream_mode = 8; + AlignMode align_mode = 9; + int32 buffer_size = 10; + bool sync = 11; + bool enable = 12; + int32 encode_width = 13; + int32 encode_height = 14; +} + +// UVCæ‘„åƒå¤´ +message UVCCameraConfig { + string id = 1; + string usb = 2; // æˆ–è€…å« device_path + int32 width = 3; + int32 height = 4; + int32 fps = 5; + string codec = 6; + CameraMode camera_mode = 7; + StreamMode stream_mode = 8; + int32 buffer_size = 9; + bool enable = 10; + int32 encode_width = 11; + int32 encode_height = 12; +} + +// 主é…ç½®æ¶ˆæ¯ - ç»Ÿä¸€ç®¡ç†æ‰€æœ‰æ‘„åƒå¤´ +message CameraConfig { + repeated UVCCameraConfig uvc_cameras = 1; + repeated RealSenseCameraConfig realsense_cameras = 2; +} diff --git a/protos/cmvr/config/dexhand_config/dexhand_config.proto b/protos/cmvr/config/dexhand_config/dexhand_config.proto new file mode 100644 index 0000000..8502021 --- /dev/null +++ b/protos/cmvr/config/dexhand_config/dexhand_config.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package cmvr.config; + +message RH56DFTPDexHandConfig{ + string id = 1; + string ip = 2; + int32 port = 3; + bool enable = 4; +} + + + +message DexHandConfig{ + repeated RH56DFTPDexHandConfig rh56dftp_dexhands = 1; +} \ No newline at end of file diff --git a/protos/cmvr/config/microphone_config/microphone_config.proto b/protos/cmvr/config/microphone_config/microphone_config.proto new file mode 100644 index 0000000..be4451f --- /dev/null +++ b/protos/cmvr/config/microphone_config/microphone_config.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package cmvr.config; + +message FFMpegMicroPhoneConfig{ + string id = 1; + int32 channels = 2; + int32 sampleRate = 3; + int32 volume = 4; + bool enable = 5; + string input_device = 6; +} + +message MicroPhoneConfig{ + repeated FFMpegMicroPhoneConfig ffmpeg_microphones = 1; +} \ No newline at end of file diff --git a/protos/cmvr/config/pinocchio_dls_ik_config.proto b/protos/cmvr/config/pinocchio_dls_ik_config.proto new file mode 100644 index 0000000..e69de29 diff --git a/protos/cmvr/config/pinocchio_qp_ik_solver_config.proto b/protos/cmvr/config/pinocchio_qp_ik_solver_config.proto new file mode 100644 index 0000000..45cbc10 --- /dev/null +++ b/protos/cmvr/config/pinocchio_qp_ik_solver_config.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package cmvr.config; + +// åªç®¡ QP 这个 IK çš„å‚æ•° +message PinocchioQpIKConfig { + string urdf_path = 1; + string base_frame_name = 2; + string flange_frame_name = 3; + string tcp_frame_name = 4; + + double lambda = 5; + double w_posrot = 6; + int32 max_iters = 7; + double tol = 8; + double qp_time_limit = 9; +} diff --git a/protos/cmvr/config/speaker_config/speaker_conifg.proto b/protos/cmvr/config/speaker_config/speaker_conifg.proto new file mode 100644 index 0000000..7402b46 --- /dev/null +++ b/protos/cmvr/config/speaker_config/speaker_conifg.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; +package cmvr.config; + +message FFMpegSpeakerConfig{ + string id = 1; + bool enable = 2; +} + +message SpeakerConfig{ + repeated FFMpegSpeakerConfig ffmpeg_speakers = 1; +} \ No newline at end of file diff --git a/protos/cmvr/config/touch_screen_app_config/touch_screen_app_config.proto b/protos/cmvr/config/touch_screen_app_config/touch_screen_app_config.proto new file mode 100644 index 0000000..8b8bf17 --- /dev/null +++ b/protos/cmvr/config/touch_screen_app_config/touch_screen_app_config.proto @@ -0,0 +1,122 @@ +syntax = "proto3"; + +package cmvr.config; + +message TouchScreenVec3 { + optional double x = 1; + optional double y = 2; + optional double z = 3; +} + +message TouchScreenTwist6 { + optional double vx = 1; + optional double vy = 2; + optional double vz = 3; + optional double wx = 4; + optional double wy = 5; + optional double wz = 6; +} + +message TouchScreenMatrix3d { + optional double m00 = 1; + optional double m01 = 2; + optional double m02 = 3; + optional double m10 = 4; + optional double m11 = 5; + optional double m12 = 6; + optional double m20 = 7; + optional double m21 = 8; + optional double m22 = 9; +} + +message TouchScreenInitJointPoint { + optional string joint_name = 1; + optional double rad = 2; +} + +enum TouchScreenDepthPolicy { + TOUCH_SCREEN_DEPTH_POLICY_NONE = 0; + TOUCH_SCREEN_DEPTH_POLICY_PREFER = 1; + TOUCH_SCREEN_DEPTH_POLICY_REQUIRE = 2; +} + +enum TouchScreenTargetPointMethod { + TOUCH_SCREEN_TARGET_POINT_METHOD_TAG_PLANE = 0; + TOUCH_SCREEN_TARGET_POINT_METHOD_DEPTH_IMAGE = 1; +} + +enum TouchScreenFingerType { + TOUCH_SCREEN_FINGER_TYPE_PINKY = 0; + TOUCH_SCREEN_FINGER_TYPE_RING = 1; + TOUCH_SCREEN_FINGER_TYPE_MIDDLE = 2; + TOUCH_SCREEN_FINGER_TYPE_INDEX = 3; + TOUCH_SCREEN_FINGER_TYPE_THUMB = 4; +} + +enum TouchScreenTactileRegion { + TOUCH_SCREEN_TACTILE_REGION_TIP = 0; + TOUCH_SCREEN_TACTILE_REGION_FINGER = 1; + TOUCH_SCREEN_TACTILE_REGION_PAD = 2; + TOUCH_SCREEN_TACTILE_REGION_TIP_AND_FINGER = 3; + TOUCH_SCREEN_TACTILE_REGION_THUMB_MIDDLE = 4; +} + +message TouchScreenAppConfig { + optional string robot_id = 40; + optional string dexhand_id = 41; + optional string camera_id = 42; + optional bool move_to_init_position = 43; + repeated TouchScreenInitJointPoint init_joint_positions = 44; + optional double init_movej_vel = 45; + optional double init_movej_acc = 46; + optional bool move_to_init_position_before_start = 47; + + optional string urdf_path = 1; + optional string base_link = 2; + optional string flange_link = 3; + optional string camera_link = 4; + + optional double tag_size_m = 5; + optional TouchScreenDepthPolicy depth_policy = 6; + optional TouchScreenTargetPointMethod target_point_method = 7; + + TouchScreenVec3 hover_target_in_camera = 8; + optional double target_rx = 9; + optional double target_ry = 10; + optional double target_rz = 11; + + optional double ibvs_lambda = 12; + optional double ibvs_mu = 13; + optional double ibvs_qdot_max = 14; + TouchScreenTwist6 ibvs_vmax6 = 15; + optional bool enable_joint_limit_avoidance = 16; + optional double joint_limit_avoidance_gain = 17; + optional double joint_limit_avoidance_margin_ratio = 18; + optional double joint_limit_avoidance_max_push = 19; + + TouchScreenMatrix3d r_camera_to_visp = 20; + TouchScreenMatrix3d r_camera_to_urdf = 21; + + repeated string control_joint_names = 22; + + optional double align_xy_threshold_m = 23; + optional double align_z_threshold_m = 24; + optional double align_rot_threshold_rad = 25; + optional int32 align_stable_frames = 26; + optional double align_timeout_s = 27; + optional bool pause_after_align_reached = 28; + + TouchScreenTwist6 touch_twist_base = 29; + optional double touch_acceleration = 30; + optional double touch_forward_duration_s = 31; + + optional double dwell_time_s = 32; + TouchScreenTwist6 retract_twist_base = 33; + optional double retract_acceleration = 34; + optional double retract_duration_s = 35; + + optional TouchScreenFingerType tactile_finger = 36; + optional TouchScreenTactileRegion tactile_region = 37; + optional double tactile_pressure_sum_threshold = 38; + optional double tactile_pressure_peak_threshold = 39; +} diff --git a/requirements.txt b/requirements.txt index b09854e..11ec90c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,14 +2,14 @@ fastapi==0.124.4 grpcio==1.76.0 grpcio-tools==1.76.0 matplotlib==3.9.4 +numpy==1.26.4 protobuf==6.33.2 pydantic==2.12.5 PyQt5==5.15.11 +pyqtgraph>=0.13.7 requests==2.32.5 yourdfpy uvicorn==0.38.0 av>=10.0.0 -opencv-python>=4.0.0 - -numpy==1.26.4 +# Includes base cv2 plus cv2.aruco used by the apriltag scripts. opencv-contrib-python==4.10.0.84 diff --git a/scripts/__pycache__/generate_apriltag_single.cpython-39.pyc b/scripts/__pycache__/generate_apriltag_single.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a40b78da24a0db5347209de54ed882cbe0fb6cfe GIT binary patch literal 12548 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 AM*si- literal 6786 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 diff --git a/scripts/apriltags_border_20x15cm_A4.sync-conflict-20260318-013404-55JW2V4.pdf b/scripts/apriltags_border_20x15cm_A4.sync-conflict-20260318-013404-55JW2V4.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3c285f2fafae1790d0f1eba2af8aa9749c014f0b GIT binary patch literal 7312 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 str: + rounded = round(value) + if abs(value - rounded) < 1e-9: + return f"{value:.1f}" + return f"{value:.3f}".rstrip("0").rstrip(".") + + +def normalize_formats(values: List[str]) -> List[str]: + normalized = [] + seen = set() + for value in values: + fmt = value.lower().lstrip(".") + if fmt == "cdr": + raise SystemExit( + "Native .cdr export is not supported. Use .ai output and open/import it in CorelDRAW." + ) + if fmt not in SUPPORTED_FORMATS: + supported = ", ".join(SUPPORTED_FORMATS + ("cdr",)) + raise SystemExit(f"Unsupported format '{value}'. Supported: {supported}") + if fmt not in seen: + normalized.append(fmt) + seen.add(fmt) + return normalized + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Generate AprilTag ids 1-20 in multiple final sizes and separate folders" + ) + parser.add_argument("--id-start", type=int, default=1, help="First tag ID, inclusive") + parser.add_argument("--id-end", type=int, default=20, help="Last tag ID, inclusive") + parser.add_argument( + "--sizes-cm", + type=float, + nargs="+", + default=[2.0, 1.5, 1.0], + help="Square sizes in cm, one folder per size", + ) + parser.add_argument( + "--size-mode", + type=str, + default="canvas", + choices=("canvas", "marker"), + help=( + "How to interpret --sizes-cm: canvas means final output size including white border; " + "marker means black tag size excluding white border." + ), + ) + parser.add_argument( + "--family", + type=str, + default="tag36h11", + choices=sorted(APRILTAG_FAMILIES.keys()), + help="AprilTag family", + ) + parser.add_argument( + "--tag-white-ratio", + type=str, + default="1:4", + help="Total white border to marker ratio when --tag-white-border-cm is not set", + ) + parser.add_argument( + "--tag-white-border-cm", + type=float, + default=None, + help="Optional override: white margin on each side in cm", + ) + parser.add_argument( + "--margin-cm", + type=float, + default=0.0, + help="Extra white margin around the tag tile inside the final exported square", + ) + parser.add_argument("--dpi", type=int, default=300, help="Output DPI for raster export") + parser.add_argument( + "--formats", + nargs="+", + default=["svg"], + help="Output formats. Supported: ai, svg, pdf, png", + ) + parser.add_argument( + "--output-dir", + type=Path, + default=Path("generated_apriltags"), + help="Root folder where size subfolders will be created", + ) + return parser.parse_args() + + +def main() -> int: + args = parse_args() + + if args.id_start < 0: + raise SystemExit("id-start must be >= 0") + if args.id_end < args.id_start: + raise SystemExit("id-end must be >= id-start") + if not args.sizes_cm: + raise SystemExit("sizes-cm must contain at least one size") + + formats = normalize_formats(args.formats) + total_files = 0 + + for size_cm in args.sizes_cm: + size_label = format_cm_label(size_cm) + size_dir = args.output_dir / f"{size_label}x{size_label}cm" + size_dir.mkdir(parents=True, exist_ok=True) + + for tag_id in range(args.id_start, args.id_end + 1): + geometry, aruco_dict = build_tag_geometry( + tag_id=tag_id, + size_cm=size_cm, + size_mode=args.size_mode, + family=args.family, + tag_white_ratio_text=args.tag_white_ratio, + tag_white_border_cm=args.tag_white_border_cm, + margin_cm=args.margin_cm, + ) + + for fmt in formats: + output_path = size_dir / f"{tag_id}.{fmt}" + save_tag_artwork( + output_path=output_path, + geometry=geometry, + aruco_dict=aruco_dict, + dpi=args.dpi, + a4_landscape=False, + exact_size_pdf=True, + ) + total_files += 1 + + print( + f"Saved ids {args.id_start}-{args.id_end} at {size_label}x{size_label} cm -> {size_dir}" + ) + + print( + f"Completed batch export: ids={args.id_start}-{args.id_end}, " + f"sizes={len(args.sizes_cm)}, size_mode={args.size_mode}, " + f"formats={','.join(formats)}, files={total_files}" + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/generate_apriltag_single.py b/scripts/generate_apriltag_single.py index 8ce3bae..581f264 100644 --- a/scripts/generate_apriltag_single.py +++ b/scripts/generate_apriltag_single.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 -"""Generate a single AprilTag image with configurable id and physical size.""" +"""Generate single AprilTag artwork in raster or vector formats.""" import argparse +from dataclasses import dataclass import os from pathlib import Path import sys -from typing import Tuple +from typing import List, Optional, Sequence, Tuple try: import numpy as np @@ -27,6 +28,7 @@ try: import matplotlib matplotlib.use("Agg", force=True) + import matplotlib.patches as mpatches import matplotlib.pyplot as plt except ImportError as exc: raise SystemExit( @@ -43,6 +45,21 @@ APRILTAG_FAMILIES = { A4_WIDTH_CM = 21.0 A4_HEIGHT_CM = 29.7 +MARKER_BORDER_BITS = 1 +SUPPORTED_SUFFIXES = {".png", ".pdf", ".svg", ".ai"} + + +@dataclass(frozen=True) +class TagGeometry: + tag_id: int + family: str + marker_size_cm: float + tag_white_ratio: float + tag_white_border_cm: float + tag_tile_size_cm: float + margin_cm: float + canvas_size_cm: float + marker_modules: int def cm_to_px(cm: float, dpi: int) -> int: @@ -92,10 +109,190 @@ def make_marker(aruco_dict: "cv2.aruco_Dictionary", marker_id: int, size_px: int return marker marker = np.zeros((size_px, size_px), dtype=np.uint8) - cv2.aruco.drawMarker(aruco_dict, marker_id, size_px, marker, 1) + cv2.aruco.drawMarker(aruco_dict, marker_id, size_px, marker, MARKER_BORDER_BITS) return marker +def compute_tag_dimensions( + size_cm: float, + size_mode: str, + tag_white_ratio_text: str, + tag_white_border_cm: Optional[float], + margin_cm: float, +) -> Tuple[float, float, float, float, float]: + if size_cm <= 0: + raise SystemExit("size-cm/tag-size-cm must be > 0") + if margin_cm < 0: + raise SystemExit("margin-cm must be >= 0") + + if tag_white_border_cm is None: + try: + tag_white_ratio = parse_ratio_text(tag_white_ratio_text) + except ValueError as exc: + raise SystemExit(f"tag-white-ratio parse error: {exc}") from exc + + if size_mode == "marker": + marker_size_cm = size_cm + tag_white_border_cm = marker_size_cm * tag_white_ratio / 2.0 + tag_tile_size_cm = marker_size_cm + 2.0 * tag_white_border_cm + canvas_size_cm = tag_tile_size_cm + 2.0 * margin_cm + elif size_mode == "canvas": + canvas_size_cm = size_cm + tag_tile_size_cm = canvas_size_cm - 2.0 * margin_cm + if tag_tile_size_cm <= 0: + raise SystemExit("canvas size must be larger than 2 * margin-cm") + marker_size_cm = tag_tile_size_cm / (1.0 + tag_white_ratio) + if marker_size_cm <= 0: + raise SystemExit("canvas size is too small for the requested white border ratio") + tag_white_border_cm = marker_size_cm * tag_white_ratio / 2.0 + else: + raise SystemExit(f"Unsupported size mode '{size_mode}'") + else: + if tag_white_border_cm < 0: + raise SystemExit("tag-white-border-cm must be >= 0") + if size_mode == "marker": + marker_size_cm = size_cm + tag_tile_size_cm = marker_size_cm + 2.0 * tag_white_border_cm + canvas_size_cm = tag_tile_size_cm + 2.0 * margin_cm + elif size_mode == "canvas": + canvas_size_cm = size_cm + tag_tile_size_cm = canvas_size_cm - 2.0 * margin_cm + if tag_tile_size_cm <= 0: + raise SystemExit("canvas size must be larger than 2 * margin-cm") + marker_size_cm = tag_tile_size_cm - 2.0 * tag_white_border_cm + if marker_size_cm <= 0: + raise SystemExit( + "canvas size is too small after subtracting margin-cm and tag-white-border-cm" + ) + else: + raise SystemExit(f"Unsupported size mode '{size_mode}'") + tag_white_ratio = 2.0 * tag_white_border_cm / marker_size_cm + + return ( + marker_size_cm, + tag_white_ratio, + tag_white_border_cm, + tag_tile_size_cm, + canvas_size_cm, + ) + + +def build_tag_geometry( + tag_id: int, + size_cm: float, + size_mode: str, + family: str, + tag_white_ratio_text: str, + tag_white_border_cm: Optional[float], + margin_cm: float, +) -> Tuple[TagGeometry, "cv2.aruco_Dictionary"]: + if tag_id < 0: + raise SystemExit("id must be >= 0") + + aruco_dict, max_tags = resolve_aruco_dict(family) + if tag_id >= max_tags: + raise SystemExit( + f"Tag ID {tag_id} exceeds family max ({max_tags - 1}) for family {family}." + ) + + ( + marker_size_cm, + tag_white_ratio, + resolved_white_border_cm, + tag_tile_size_cm, + canvas_size_cm, + ) = compute_tag_dimensions( + size_cm=size_cm, + size_mode=size_mode, + tag_white_ratio_text=tag_white_ratio_text, + tag_white_border_cm=tag_white_border_cm, + margin_cm=margin_cm, + ) + + geometry = TagGeometry( + tag_id=tag_id, + family=family, + marker_size_cm=marker_size_cm, + tag_white_ratio=tag_white_ratio, + tag_white_border_cm=resolved_white_border_cm, + tag_tile_size_cm=tag_tile_size_cm, + margin_cm=margin_cm, + canvas_size_cm=canvas_size_cm, + marker_modules=aruco_dict.markerSize + 2 * MARKER_BORDER_BITS, + ) + return geometry, aruco_dict + + +def make_marker_module_grid( + aruco_dict: "cv2.aruco_Dictionary", + marker_id: int, + marker_modules: int, +) -> np.ndarray: + pixels_per_module = 16 + marker = make_marker(aruco_dict, marker_id, marker_modules * pixels_per_module) + return ( + marker.reshape(marker_modules, pixels_per_module, marker_modules, pixels_per_module) + .mean(axis=(1, 3)) + < 128 + ) + + +def build_vector_rects(geometry: TagGeometry, module_grid: np.ndarray) -> List[Tuple[float, float, float, float]]: + rects = [] + module_size_cm = geometry.marker_size_cm / float(geometry.marker_modules) + origin_cm = geometry.margin_cm + geometry.tag_white_border_cm + + for row_index in range(geometry.marker_modules): + start_col = None + for col_index in range(geometry.marker_modules + 1): + is_black = col_index < geometry.marker_modules and bool(module_grid[row_index, col_index]) + if is_black and start_col is None: + start_col = col_index + elif not is_black and start_col is not None: + rects.append( + ( + origin_cm + start_col * module_size_cm, + origin_cm + row_index * module_size_cm, + (col_index - start_col) * module_size_cm, + module_size_cm, + ) + ) + start_col = None + + return rects + + +def render_raster_canvas( + geometry: TagGeometry, + aruco_dict: "cv2.aruco_Dictionary", + dpi: int, +) -> np.ndarray: + canvas_px = cm_to_px(geometry.canvas_size_cm, dpi) + tag_tile_px = cm_to_px(geometry.tag_tile_size_cm, dpi) + marker_px = cm_to_px(geometry.marker_size_cm, dpi) + + if canvas_px <= 0 or tag_tile_px <= 0 or marker_px <= 0: + raise SystemExit("At current DPI, tag size is too small to draw") + if canvas_px < tag_tile_px: + raise SystemExit("Canvas size is smaller than the tag tile") + + canvas = np.full((canvas_px, canvas_px), 255, dtype=np.uint8) + + tile_x_px = cm_to_px(geometry.margin_cm, dpi) + tile_y_px = cm_to_px(geometry.margin_cm, dpi) + tile_x_px = min(max(tile_x_px, 0), canvas_px - tag_tile_px) + tile_y_px = min(max(tile_y_px, 0), canvas_px - tag_tile_px) + + marker_x_px = cm_to_px(geometry.margin_cm + geometry.tag_white_border_cm, dpi) + marker_y_px = cm_to_px(geometry.margin_cm + geometry.tag_white_border_cm, dpi) + marker_x_px = min(max(marker_x_px, tile_x_px), tile_x_px + tag_tile_px - marker_px) + marker_y_px = min(max(marker_y_px, tile_y_px), tile_y_px + tag_tile_px - marker_px) + + marker = make_marker(aruco_dict, geometry.tag_id, marker_px) + canvas[marker_y_px : marker_y_px + marker_px, marker_x_px : marker_x_px + marker_px] = marker + return canvas + + def save_printable_png(output_path: Path, canvas: np.ndarray, width_cm: float, height_cm: float, dpi: int) -> None: fig = plt.figure(figsize=(width_cm / 2.54, height_cm / 2.54), dpi=dpi, frameon=False) ax = fig.add_axes([0, 0, 1, 1]) @@ -105,14 +302,15 @@ def save_printable_png(output_path: Path, canvas: np.ndarray, width_cm: float, h plt.close(fig) -def save_a4_pdf( - output_path: Path, - canvas: np.ndarray, - width_cm: float, - height_cm: float, - dpi: int, +def resolve_page_dimensions( + canvas_width_cm: float, + canvas_height_cm: float, + center_on_a4: bool, landscape: bool, -) -> None: +) -> Tuple[float, float, float, float]: + if not center_on_a4: + return canvas_width_cm, canvas_height_cm, 0.0, 0.0 + if landscape: page_w_cm = A4_HEIGHT_CM page_h_cm = A4_WIDTH_CM @@ -120,34 +318,160 @@ def save_a4_pdf( page_w_cm = A4_WIDTH_CM page_h_cm = A4_HEIGHT_CM - if width_cm > page_w_cm + 1e-9 or height_cm > page_h_cm + 1e-9: + if canvas_width_cm > page_w_cm + 1e-9 or canvas_height_cm > page_h_cm + 1e-9: raise SystemExit( - f"Tag size {width_cm}x{height_cm} cm does not fit A4 " + f"Tag size {canvas_width_cm}x{canvas_height_cm} cm does not fit A4 " f"({'landscape' if landscape else 'portrait'}) {page_w_cm}x{page_h_cm} cm." ) + return ( + page_w_cm, + page_h_cm, + (page_w_cm - canvas_width_cm) / 2.0, + (page_h_cm - canvas_height_cm) / 2.0, + ) + + +def save_vector_art( + output_path: Path, + geometry: TagGeometry, + rects_cm: Sequence[Tuple[float, float, float, float]], + output_format: str, + dpi: int, + center_on_a4: bool, + landscape: bool, +) -> None: + page_w_cm, page_h_cm, offset_x_cm, offset_y_cm = resolve_page_dimensions( + geometry.canvas_size_cm, + geometry.canvas_size_cm, + center_on_a4=center_on_a4, + landscape=landscape, + ) + fig = plt.figure(figsize=(page_w_cm / 2.54, page_h_cm / 2.54), dpi=dpi, frameon=False) - x0 = (page_w_cm - width_cm) / 2.0 / page_w_cm - y0 = (page_h_cm - height_cm) / 2.0 / page_h_cm - w = width_cm / page_w_cm - h = height_cm / page_h_cm - ax = fig.add_axes([x0, y0, w, h]) - ax.imshow(canvas, cmap="gray", vmin=0, vmax=255, interpolation="nearest") + ax = fig.add_axes([0, 0, 1, 1]) + ax.set_xlim(0, page_w_cm) + ax.set_ylim(page_h_cm, 0) + ax.set_aspect("equal") ax.axis("off") - fig.savefig(output_path, format="pdf", dpi=dpi, pad_inches=0) + + ax.add_patch( + mpatches.Rectangle((0.0, 0.0), page_w_cm, page_h_cm, facecolor="white", edgecolor="none") + ) + + for x_cm, y_cm, width_cm, height_cm in rects_cm: + ax.add_patch( + mpatches.Rectangle( + (offset_x_cm + x_cm, offset_y_cm + y_cm), + width_cm, + height_cm, + facecolor="black", + edgecolor="none", + ) + ) + + fig.savefig(output_path, format=output_format, dpi=dpi, pad_inches=0) plt.close(fig) +def normalize_output_suffix(output_path: Path) -> str: + suffix = output_path.suffix.lower() + if suffix == ".cdr": + raise SystemExit( + "Native .cdr export is not supported in this script. " + "Use .ai or .svg output and open/import it in CorelDRAW." + ) + if suffix not in SUPPORTED_SUFFIXES: + supported = ", ".join(sorted(SUPPORTED_SUFFIXES | {".cdr"})) + raise SystemExit(f"Unsupported output suffix '{suffix}'. Supported: {supported}") + return suffix + + +def save_tag_artwork( + output_path: Path, + geometry: TagGeometry, + aruco_dict: "cv2.aruco_Dictionary", + dpi: int, + a4_landscape: bool, + exact_size_pdf: bool, +) -> None: + suffix = normalize_output_suffix(output_path) + output_path.parent.mkdir(parents=True, exist_ok=True) + + if suffix == ".png": + canvas = render_raster_canvas(geometry, aruco_dict, dpi) + save_printable_png(output_path, canvas, geometry.canvas_size_cm, geometry.canvas_size_cm, dpi) + return + + module_grid = make_marker_module_grid(aruco_dict, geometry.tag_id, geometry.marker_modules) + rects_cm = build_vector_rects(geometry, module_grid) + + if suffix == ".svg": + save_vector_art( + output_path, + geometry, + rects_cm, + output_format="svg", + dpi=dpi, + center_on_a4=False, + landscape=False, + ) + return + + if suffix == ".ai": + save_vector_art( + output_path, + geometry, + rects_cm, + output_format="pdf", + dpi=dpi, + center_on_a4=False, + landscape=False, + ) + return + + save_vector_art( + output_path, + geometry, + rects_cm, + output_format="pdf", + dpi=dpi, + center_on_a4=not exact_size_pdf, + landscape=a4_landscape, + ) + + +def describe_geometry(geometry: TagGeometry) -> str: + return ( + f"family={geometry.family}, id={geometry.tag_id}, marker={round(geometry.marker_size_cm, 6)}cm, " + f"tag_white_ratio={round(geometry.tag_white_ratio, 6)}, " + f"tag_white_border={round(geometry.tag_white_border_cm, 6)}cm, " + f"tag_tile={round(geometry.tag_tile_size_cm, 6)}cm, " + f"margin={round(geometry.margin_cm, 6)}cm, " + f"canvas={round(geometry.canvas_size_cm, 6)}cm" + ) + + def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser(description="Generate single AprilTag image") + parser = argparse.ArgumentParser(description="Generate single AprilTag artwork") parser.add_argument("--id", type=int, default=0, help="AprilTag ID (default: 0)") parser.add_argument( "--size-cm", "--tag-size-cm", - dest="tag_size_cm", + dest="size_cm", type=float, - default=15, - help="AprilTag marker side length in cm (excluding per-tag white border)", + default=15.0, + help=( + "Base size in cm. Use --size-mode marker to treat it as the black tag size, " + "or --size-mode canvas to treat it as the final exported square size." + ), + ) + parser.add_argument( + "--size-mode", + type=str, + default="marker", + choices=("marker", "canvas"), + help="Interpret --size-cm as marker size or final canvas size.", ) parser.add_argument( "--family", @@ -178,93 +502,48 @@ def parse_args() -> argparse.Namespace: parser.add_argument( "--a4-landscape", action="store_true", - help="When output is PDF, use A4 landscape instead of portrait", + help="When output is PDF and --exact-size-pdf is not set, use A4 landscape instead of portrait", + ) + parser.add_argument( + "--exact-size-pdf", + action="store_true", + help="For .pdf output, save an exact-size vector page instead of centering on A4", ) parser.add_argument( "--output", type=Path, default=Path("apriltag_id0_A4.pdf"), - help="Output path (.pdf for centered A4 page, .png for exact-size image)", + help=( + "Output path. Supported suffixes: .png, .pdf, .svg, .ai. " + ".ai is written as a PDF-based Illustrator-compatible vector file." + ), ) return parser.parse_args() def main() -> int: args = parse_args() + geometry, aruco_dict = build_tag_geometry( + tag_id=args.id, + size_cm=args.size_cm, + size_mode=args.size_mode, + family=args.family, + tag_white_ratio_text=args.tag_white_ratio, + tag_white_border_cm=args.tag_white_border_cm, + margin_cm=args.margin_cm, + ) - if args.id < 0: - raise SystemExit("id must be >= 0") - if args.tag_size_cm <= 0: - raise SystemExit("size-cm/tag-size-cm must be > 0") - if args.margin_cm < 0: - raise SystemExit("margin-cm must be >= 0") - - if args.tag_white_border_cm is None: - try: - tag_white_ratio = parse_ratio_text(args.tag_white_ratio) - except ValueError as exc: - raise SystemExit(f"tag-white-ratio parse error: {exc}") from exc - tag_white_border_cm = args.tag_size_cm * tag_white_ratio / 2.0 - else: - if args.tag_white_border_cm < 0: - raise SystemExit("tag-white-border-cm must be >= 0") - tag_white_border_cm = args.tag_white_border_cm - tag_white_ratio = 2.0 * tag_white_border_cm / args.tag_size_cm - - tag_tile_size_cm = args.tag_size_cm + 2.0 * tag_white_border_cm - canvas_size_cm = tag_tile_size_cm + 2.0 * args.margin_cm - - aruco_dict, max_tags = resolve_aruco_dict(args.family) - if args.id >= max_tags: - raise SystemExit( - f"Tag ID {args.id} exceeds family max ({max_tags - 1}) for family {args.family}." - ) - - canvas_px = cm_to_px(canvas_size_cm, args.dpi) - tag_tile_px = cm_to_px(tag_tile_size_cm, args.dpi) - marker_px = cm_to_px(args.tag_size_cm, args.dpi) - - if canvas_px <= 0 or tag_tile_px <= 0 or marker_px <= 0: - raise SystemExit("At current DPI, tag size is too small to draw") - if canvas_px < tag_tile_px: - raise SystemExit("Canvas size is smaller than the tag tile") - - canvas = np.full((canvas_px, canvas_px), 255, dtype=np.uint8) - - tile_x_px = cm_to_px(args.margin_cm, args.dpi) - tile_y_px = cm_to_px(args.margin_cm, args.dpi) - tile_x_px = min(max(tile_x_px, 0), canvas_px - tag_tile_px) - tile_y_px = min(max(tile_y_px, 0), canvas_px - tag_tile_px) - - marker_x_px = cm_to_px(args.margin_cm + tag_white_border_cm, args.dpi) - marker_y_px = cm_to_px(args.margin_cm + tag_white_border_cm, args.dpi) - marker_x_px = min(max(marker_x_px, tile_x_px), tile_x_px + tag_tile_px - marker_px) - marker_y_px = min(max(marker_y_px, tile_y_px), tile_y_px + tag_tile_px - marker_px) - - marker = make_marker(aruco_dict, args.id, marker_px) - canvas[marker_y_px : marker_y_px + marker_px, marker_x_px : marker_x_px + marker_px] = marker - - args.output.parent.mkdir(parents=True, exist_ok=True) - if args.output.suffix.lower() == ".pdf": - save_a4_pdf( - args.output, - canvas, - canvas_size_cm, - canvas_size_cm, - args.dpi, - landscape=args.a4_landscape, - ) - else: - save_printable_png(args.output, canvas, canvas_size_cm, canvas_size_cm, args.dpi) + save_tag_artwork( + output_path=args.output, + geometry=geometry, + aruco_dict=aruco_dict, + dpi=args.dpi, + a4_landscape=args.a4_landscape, + exact_size_pdf=args.exact_size_pdf, + ) print(f"Saved single tag image: {args.output}") - print( - f"family={args.family}, id={args.id}, marker={args.tag_size_cm}cm, " - f"tag_white_ratio={round(tag_white_ratio, 6)}, " - f"tag_white_border={round(tag_white_border_cm, 6)}cm, " - f"tag_tile={round(tag_tile_size_cm, 6)}cm, " - f"margin={args.margin_cm}cm, canvas={round(canvas_size_cm, 6)}cm" - ) + print(describe_geometry(geometry)) return 0 diff --git a/scripts/generate_apriltags_border.py b/scripts/generate_apriltags_border.py index d754c30..191a39f 100644 --- a/scripts/generate_apriltags_border.py +++ b/scripts/generate_apriltags_border.py @@ -43,9 +43,9 @@ APRILTAG_FAMILIES = { } -A4_WIDTH_CM = 29.7 -A4_HEIGHT_CM = 21.0 -# A4_HEIGHT_CM = 29.7 +A4_WIDTH_CM = 21.0 + +A4_HEIGHT_CM = 29.7 def cm_to_px(cm: float, dpi: int) -> int: @@ -174,8 +174,8 @@ def write_layout_csv(path: Path, rows: List[Tuple[int, float, float, int, int]]) def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description="Generate AprilTag border image") - parser.add_argument("--width-cm", type=float, default=26.0, help="Rectangle width in cm") - parser.add_argument("--height-cm", type=float, default=17.0, help="Rectangle height in cm") + parser.add_argument("--width-cm", type=float, default=20.0, help="Rectangle width in cm") + parser.add_argument("--height-cm", type=float, default=15.0, help="Rectangle height in cm") parser.add_argument( "--tag-size-cm", type=float, diff --git a/scripts/generate_apriltags_border.sync-conflict-20260318-013404-55JW2V4.py b/scripts/generate_apriltags_border.sync-conflict-20260318-013404-55JW2V4.py new file mode 100644 index 0000000..9514cfe --- /dev/null +++ b/scripts/generate_apriltags_border.sync-conflict-20260318-013404-55JW2V4.py @@ -0,0 +1,384 @@ +#!/usr/bin/env python3 +"""Generate an AprilTag border around a rectangle and save a printable image.""" + +import argparse +import csv +import math +import os +from pathlib import Path +import sys +from typing import List, Tuple + +try: + import numpy as np +except ImportError as exc: + raise SystemExit( + "Missing dependency 'numpy'. Install it with: pip install numpy" + ) from exc + +try: + import cv2 +except ImportError as exc: + raise SystemExit( + "Missing dependency 'opencv-contrib-python'. Install it with: pip install opencv-contrib-python" + ) from exc + +# Force non-GUI backend to avoid Qt/xcb plugin issues in headless environments. +os.environ.setdefault("MPLBACKEND", "Agg") +try: + import matplotlib + matplotlib.use("Agg", force=True) + import matplotlib.pyplot as plt +except ImportError as exc: + raise SystemExit( + "Missing dependency 'matplotlib'. Install it with: pip install matplotlib" + ) from exc + + +APRILTAG_FAMILIES = { + "tag16h5": ("DICT_APRILTAG_16h5", 30), + "tag25h9": ("DICT_APRILTAG_25h9", 35), + "tag36h10": ("DICT_APRILTAG_36h10", 2320), + "tag36h11": ("DICT_APRILTAG_36h11", 587), +} + + +A4_WIDTH_CM = 29.7 +A4_HEIGHT_CM = 21.0 +# A4_HEIGHT_CM = 29.7 + + +def cm_to_px(cm: float, dpi: int) -> int: + return int(round(cm / 2.54 * dpi)) + + +def parse_ratio_text(value: str) -> float: + text = value.strip() + if not text: + raise ValueError("empty ratio") + if ":" in text: + parts = text.split(":") + if len(parts) != 2: + raise ValueError(f"invalid ratio '{value}', expected like 1:4") + left = float(parts[0].strip()) + right = float(parts[1].strip()) + if left < 0 or right <= 0: + raise ValueError(f"invalid ratio '{value}', left must be >= 0 and right > 0") + return left / right + ratio = float(text) + if ratio < 0: + raise ValueError(f"invalid ratio '{value}', must be >= 0") + return ratio + + +def border_positions(total_cm: float, tag_cm: float, pitch_cm: float) -> List[float]: + if total_cm + 1e-9 < tag_cm: + raise ValueError("rectangle side is smaller than one tag") + + count = int(math.floor((total_cm - tag_cm) / pitch_cm)) + 1 + count = max(1, count) + used_cm = tag_cm + (count - 1) * pitch_cm + margin_cm = (total_cm - used_cm) / 2.0 + return [margin_cm + i * pitch_cm for i in range(count)] + + +def side_positions_excluding_corners(total_cm: float, tag_cm: float, pitch_cm: float) -> List[float]: + inner_cm = total_cm - 2.0 * tag_cm + if inner_cm + 1e-9 < tag_cm: + return [] + + count = int(math.floor((inner_cm - tag_cm) / pitch_cm)) + 1 + count = max(1, count) + used_cm = tag_cm + (count - 1) * pitch_cm + margin_cm = (inner_cm - used_cm) / 2.0 + start_cm = tag_cm + margin_cm + return [start_cm + i * pitch_cm for i in range(count)] + + +def resolve_aruco_dict(family: str) -> Tuple["cv2.aruco_Dictionary", int]: + if family not in APRILTAG_FAMILIES: + supported = ", ".join(sorted(APRILTAG_FAMILIES)) + raise ValueError(f"Unsupported family '{family}'. Supported: {supported}") + + dict_name, max_tags = APRILTAG_FAMILIES[family] + dict_id = getattr(cv2.aruco, dict_name, None) + if dict_id is None: + raise RuntimeError( + f"OpenCV build does not include {dict_name}. " + "Please install opencv-contrib-python." + ) + + return cv2.aruco.getPredefinedDictionary(dict_id), max_tags + + +def make_marker(aruco_dict: "cv2.aruco_Dictionary", marker_id: int, size_px: int) -> np.ndarray: + if hasattr(cv2.aruco, "generateImageMarker"): + marker = cv2.aruco.generateImageMarker(aruco_dict, marker_id, size_px) + if marker is not None: + return marker + + marker = np.zeros((size_px, size_px), dtype=np.uint8) + cv2.aruco.drawMarker(aruco_dict, marker_id, size_px, marker, 1) + return marker + + +def save_printable_png(output_path: Path, canvas: np.ndarray, width_cm: float, height_cm: float, dpi: int) -> None: + fig = plt.figure(figsize=(width_cm / 2.54, height_cm / 2.54), dpi=dpi, frameon=False) + ax = fig.add_axes([0, 0, 1, 1]) + ax.imshow(canvas, cmap="gray", vmin=0, vmax=255, interpolation="nearest") + ax.axis("off") + fig.savefig(output_path, dpi=dpi, pad_inches=0) + plt.close(fig) + + +def save_a4_pdf( + output_path: Path, + canvas: np.ndarray, + width_cm: float, + height_cm: float, + dpi: int, + landscape: bool, +) -> None: + if landscape: + page_w_cm = A4_HEIGHT_CM + page_h_cm = A4_WIDTH_CM + else: + page_w_cm = A4_WIDTH_CM + page_h_cm = A4_HEIGHT_CM + + if width_cm > page_w_cm + 1e-9 or height_cm > page_h_cm + 1e-9: + raise SystemExit( + f"Pattern size {width_cm}x{height_cm} cm does not fit A4 " + f"({'landscape' if landscape else 'portrait'}) {page_w_cm}x{page_h_cm} cm." + ) + + fig = plt.figure(figsize=(page_w_cm / 2.54, page_h_cm / 2.54), dpi=dpi, frameon=False) + x0 = (page_w_cm - width_cm) / 2.0 / page_w_cm + y0 = (page_h_cm - height_cm) / 2.0 / page_h_cm + w = width_cm / page_w_cm + h = height_cm / page_h_cm + ax = fig.add_axes([x0, y0, w, h]) + ax.imshow(canvas, cmap="gray", vmin=0, vmax=255, interpolation="nearest") + ax.axis("off") + fig.savefig(output_path, format="pdf", dpi=dpi, pad_inches=0) + plt.close(fig) + + +def write_layout_csv(path: Path, rows: List[Tuple[int, float, float, int, int]]) -> None: + with path.open("w", newline="", encoding="utf-8") as f: + writer = csv.writer(f) + writer.writerow(["tag_id", "x_cm", "y_cm", "x_px", "y_px"]) + for row in rows: + writer.writerow(row) + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Generate AprilTag border image") + parser.add_argument("--width-cm", type=float, default=27.0, help="Rectangle width in cm") + parser.add_argument("--height-cm", type=float, default=18.0, help="Rectangle height in cm") + parser.add_argument( + "--tag-size-cm", + type=float, + default=2.0, + help="AprilTag marker side length in cm (excluding per-tag white border)", + ) + parser.add_argument( + "--pitch-cm", + type=float, + default=None, + help="Distance between adjacent tag tile top-left positions in cm (default: auto = tag tile size)", + ) + parser.add_argument( + "--family", + type=str, + default="tag36h11", + choices=sorted(APRILTAG_FAMILIES.keys()), + help="AprilTag family", + ) + parser.add_argument("--start-id", type=int, default=0, help="First tag ID") + parser.add_argument( + "--wrap-ids", + action="store_true", + help="Wrap around if IDs exceed family max", + ) + parser.add_argument( + "--white-border-cm", + type=float, + default=0.0, + help="Keep this much white margin on all four sides (cm)", + ) + parser.add_argument( + "--tag-white-ratio", + type=str, + default="1:4", + help="Total white border to marker ratio (e.g. 1:4 means total white width is 1/4 of marker size)", + ) + parser.add_argument( + "--tag-white-border-cm", + type=float, + default=None, + help="Optional override: white margin on each side in cm; if omitted, auto-computed from --tag-white-ratio", + ) + parser.add_argument("--dpi", type=int, default=300, help="Output DPI") + parser.add_argument( + "--a4-landscape", + action="store_true", + help="When output is PDF, use A4 landscape instead of portrait", + ) + parser.add_argument( + "--output", + type=Path, + default=Path("apriltags_border_20x15cm_A4.pdf"), + help="Output path (.pdf for A4 page, .png for exact-size image)", + ) + parser.add_argument( + "--layout-csv", + type=Path, + default=None, + help="Optional CSV path to save tag placements", + ) + return parser.parse_args() + + +def main() -> int: + args = parse_args() + + if args.width_cm <= 0 or args.height_cm <= 0 or args.tag_size_cm <= 0: + raise SystemExit("width/height/tag-size must all be > 0") + if args.white_border_cm < 0: + raise SystemExit("white-border-cm must be >= 0") + + if args.tag_white_border_cm is None: + try: + tag_white_ratio = parse_ratio_text(args.tag_white_ratio) + except ValueError as exc: + raise SystemExit(f"tag-white-ratio parse error: {exc}") from exc + tag_white_border_cm = args.tag_size_cm * tag_white_ratio / 2.0 + else: + if args.tag_white_border_cm < 0: + raise SystemExit("tag-white-border-cm must be >= 0") + tag_white_border_cm = args.tag_white_border_cm + tag_white_ratio = 2.0 * tag_white_border_cm / args.tag_size_cm + + tag_tile_size_cm = args.tag_size_cm + 2.0 * tag_white_border_cm + pitch_cm = tag_tile_size_cm if args.pitch_cm is None else args.pitch_cm + if pitch_cm <= 0: + raise SystemExit("pitch-cm must be > 0") + if pitch_cm + 1e-9 < tag_tile_size_cm: + raise SystemExit( + f"pitch-cm ({pitch_cm}) is smaller than tag-size-cm + 2*tag-white-border-cm " + f"({tag_tile_size_cm}). Increase pitch-cm to keep white border around each tag." + ) + + inner_width_cm = args.width_cm - 2.0 * args.white_border_cm + inner_height_cm = args.height_cm - 2.0 * args.white_border_cm + + if inner_width_cm + 1e-9 < tag_tile_size_cm or inner_height_cm + 1e-9 < tag_tile_size_cm: + raise SystemExit( + "After white border is reserved, inner area must still fit at least one full tag tile in both dimensions" + ) + + aruco_dict, max_tags = resolve_aruco_dict(args.family) + + width_px = cm_to_px(args.width_cm, args.dpi) + height_px = cm_to_px(args.height_cm, args.dpi) + tag_tile_px = cm_to_px(tag_tile_size_cm, args.dpi) + marker_px = cm_to_px(args.tag_size_cm, args.dpi) + + if width_px < tag_tile_px or height_px < tag_tile_px: + raise SystemExit("At current DPI, tag tile is larger than the canvas") + if marker_px <= 0: + raise SystemExit("At current DPI, tag-size-cm leaves no drawable marker pixels") + + canvas = np.full((height_px, width_px), 255, dtype=np.uint8) + + top_x_local = border_positions(inner_width_cm, tag_tile_size_cm, pitch_cm) + side_y_local = side_positions_excluding_corners(inner_height_cm, tag_tile_size_cm, pitch_cm) + + placements_cm: List[Tuple[float, float]] = [] + + # Clockwise: top -> right -> bottom -> left + top_y = args.white_border_cm + left_x = args.white_border_cm + right_x = args.white_border_cm + inner_width_cm - tag_tile_size_cm + bottom_y = args.white_border_cm + inner_height_cm - tag_tile_size_cm + + for x_local in top_x_local: + placements_cm.append((args.white_border_cm + x_local, top_y)) + for y_local in side_y_local: + placements_cm.append((right_x, args.white_border_cm + y_local)) + for x_local in reversed(top_x_local): + placements_cm.append((args.white_border_cm + x_local, bottom_y)) + for y_local in reversed(side_y_local): + placements_cm.append((left_x, args.white_border_cm + y_local)) + + placements_csv: List[Tuple[int, float, float, int, int]] = [] + current_id = args.start_id + + for x_cm, y_cm in placements_cm: + if current_id >= max_tags: + if args.wrap_ids: + tag_id = current_id % max_tags + else: + raise SystemExit( + f"Tag ID {current_id} exceeds family max ({max_tags - 1}). " + "Use --wrap-ids or lower --start-id." + ) + else: + tag_id = current_id + + x_px = cm_to_px(x_cm, args.dpi) + y_px = cm_to_px(y_cm, args.dpi) + + # Clamp to keep full tag tile in canvas despite rounding. + x_px = min(max(x_px, 0), width_px - tag_tile_px) + y_px = min(max(y_px, 0), height_px - tag_tile_px) + + marker = make_marker(aruco_dict, tag_id, marker_px) + + marker_x_px = cm_to_px(x_cm + tag_white_border_cm, args.dpi) + marker_y_px = cm_to_px(y_cm + tag_white_border_cm, args.dpi) + + # Keep marker fully inside its tag tile despite rounding. + marker_x_px = min(max(marker_x_px, x_px), x_px + tag_tile_px - marker_px) + marker_y_px = min(max(marker_y_px, y_px), y_px + tag_tile_px - marker_px) + + canvas[marker_y_px : marker_y_px + marker_px, marker_x_px : marker_x_px + marker_px] = marker + placements_csv.append((tag_id, round(x_cm, 4), round(y_cm, 4), x_px, y_px)) + + current_id += 1 + + args.output.parent.mkdir(parents=True, exist_ok=True) + if args.output.suffix.lower() == ".pdf": + save_a4_pdf( + args.output, + canvas, + args.width_cm, + args.height_cm, + args.dpi, + landscape=args.a4_landscape, + ) + else: + save_printable_png(args.output, canvas, args.width_cm, args.height_cm, args.dpi) + + if args.layout_csv is not None: + args.layout_csv.parent.mkdir(parents=True, exist_ok=True) + write_layout_csv(args.layout_csv, placements_csv) + + print(f"Saved border image: {args.output}") + print( + f"family={args.family}, tags={len(placements_csv)}, " + f"size={args.width_cm}cm x {args.height_cm}cm, tag_marker={args.tag_size_cm}cm, " + f"pitch={round(pitch_cm, 6)}cm, white_border={args.white_border_cm}cm, " + f"tag_white_ratio={round(tag_white_ratio, 6)}, " + f"tag_white_border={round(tag_white_border_cm, 6)}cm, " + f"tag_tile={round(tag_tile_size_cm, 6)}cm, output={args.output.suffix.lower()}" + ) + if args.layout_csv is not None: + print(f"Saved layout CSV: {args.layout_csv}") + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/generated_apriltags.zip b/scripts/generated_apriltags.zip new file mode 100644 index 0000000000000000000000000000000000000000..76fd1f916ca2d110aaf797dbedcc6c8e7972f0b6 GIT binary patch literal 158359 zcmeFa2|Sc-`~Pp>3)!iR${uFMp6t7nwN&v%}h`I@89cr>h`YFR96Si&)Aq1OK=@>hosF&SEFgU9#uf-C2pmk%gh)t`AZ*Q$6Gb-dpkQa~ z4B=m!z;|5DC-ZP5~?h;qDCKQ?>z1koo#u_Um_Li2kPKz)(*Bxo+g@9T8x4>&xfU zL^#>GIGTd_!bN}hNguo>;6%Z*IYC6% z_m;M`wF5uFkh^YvagYyhyqc;ATMOq?5ZKohC|Fv9^M~*$Sc4Zu4q<9%hCrT%ld~hj z*oMSCKKokj>2fHA{OkgW*ZBv5h!?dIkl~z5-cm};EWA(7&&+bCnhkJWqw^6*wrIGI@iQetf3%$g7)SUp^K(G(t+|-d5<4a}-F>9lj zc8N;m!slEg*-rEA28mvFILQ?8nTvwD$2>NrYIytbZ+E<*1*YKHf7clB%Rt~5L` zd3LV2o@b8zdV5mB0{;2KVQk0?;p@Bh^^#b7i;-`;09<(EGFkg*Ah+p4^f$I4uavK! zj(@HvtEV$`c~5D3p!Js}f}+@{_r7 z==E!V{gUT9WNGFEE{wHnOA}mf;PdYSt|jErH_ipN>16n1!Dran&Jn_K*x1?L+RoY9 z(gfnl&nwC+upe^D+1cLd03V;tw}*J`94$CW_|zTk%v?;tC9!!gdoy#0iLt2_cxAsn z1DLt?^p!pN9C8QXP`D73A0`45hKs<3xS=BKP$)a+rp38CBFssk5SRc7^xGFi zNKlYp5MmDbwhabeDdZQ#c4He1%8#-QDk2JAr|;)MMUkif<2GS5+eE?J`~5r^6b9b5 z@3%prLa5vL1;KBZALfY&qpk%G1;5gMoF|Af4+a(8bZ*X$#+KFy$F*PkT9%#&@LECm zH0|t=Z!+>%8lSSQxgBKfOL(+;@=oCoQVUaR?3; z7IKjPaNn`FwFpu_qPUHO5qam@rmQ5V3BJ1`2NAeUVeLw_pakDUo#l@^YuTAQyMbTc zSerjg2uCOI0r=@hif?y#_bKos#P&)joUpKX_aX{5AiH9~58f>-W%(m=88Sw`|9qH`5Ob{Yq@)=5WFW^6A13G)#ry>n^Mz&s zN1nX)YP6Ts8ymW1pKu8o>QlI*<2pw^y>!{nM?zbsP%sg)eYWOV+~RWUqU~y4@liLm zCs*3XRFCcYC>uNJq`a6hF4IG9n|Ix7tmW~6dv&2lGcAtV_Rq`am(*tO8r(Onfw=lG zZT!j9|CIZZ;IgN=B0Wn9Ow6&MBf#EXTe9SNe%hQTUqO_(pH zEmV17hXsD5vXa7S37y*~*_%c{yoZrvI-O{c5<#+{DT$!GGP{$F?EUq#QC?AWzPL0- zDbjnN)x4=WYdb)pqJo>CU}Kvwpv51aPgE2YGJY9a1X0(57FqzF z{cv0uRCv?5{TpcE7eWgyd=6J%(*R&W4hnEL`sa^o7?k&aPA$G)sig~M3;s03lI920 zqAJN^R5n+1{Sy!6aXlxNG^jB&J4I-fF2?>&jlI(l13hGER29DO zwCxViC+~2I!w3ht+=)4X27!zHO>S>H3^C3tDB(_gB<9sIe;5U4)&QqfCpaR*ZtUEg zRu(6>62>JxQwS~8IXF6RRW1Hb3p0&)WzNmue@TU6A}GPJzCBHetLduf7gNP8-zr{u z0W+#F(T#I29E}PU!i*~E?}l-X0!%@UA5djWaQUMJmqWJY5Q>1=cwZ_b+{#`AvnC)F zaj8g&o9g#Mn+-}uj+%QD7Tt~1Fe8;f^wz$lC@k#pDz$S9i_`U*O*xQg6IL@+ywL15 zpJ%oN-EO>-U0DBRiR;3Nb05blhgxs(=1wkq<$Kp;#|{Sf-Kr0+I(PtUwDy5yS5Ks; z2KhoM$+EvpEO)=^bzFr}U*b1xRu(jjUq1RY$HElE0%99+un=UH7asVER`4Aq>Ufb- zh99^lxTt6xKBHQfGYM7$JyX4Uw3<>su|CM2Q;HCeox0T+l1 z;VD|@1qnE`O({ND7xyP^~B_@LI0kiiw96jaWIOYLzr*|5-A zN;-g!0`8}X%_XFJ8ImVe6mFb$Y4T(htOEXpMDw!C-hS6w%9I)zCLF37$j9^#!5k!w zIGO=R2^{&go}a>M$8~L?l38_vv>u_7R)1&DQo1lZm_uB2EaI_HPb5Jvr1ONX&qJY8O48hkQBMa3Q{`l>8|pIvqsZ;vBT{xYLgA>+LJoG2zMeZkmSc#a#yqFD1^e z%o#NlFD#|SV48fgguA5H);0GFMv4J53_R54=qlwK?9j_9liJc46p%ILh(LWrSrPxFYDdwJ)oak}K zOfGs@9hS0yT9D%ha)ED&EPu4fV*3mkDq0~qcRpxT#Pyn&(t}Rk1?-Cw6dBEXvP%sE zgJYTC`?+8;K_j=^9b+!Z9-HkPpRJp5@Uj|Dd>L#JcVWWSLc*q1;znK@xff#qEX3k* zX1Z4nv#U$r=x|w%R-t$6_X>V6O1_Nr#18qgCiNF~vFbDtX4mvv`>!sZD8h1?#1|J; z3h2cd&L+FZOCPL^BPVN+X~yBFeb}*T+KDcpS@k7-vSuZOV-}yIO$wh!vBN)^gOG%p z=AO^h8x&emxCkYi@sKwR|59Y{GH2g76!4`YobynRZYXEjZWergwG}}Zr zvB4VbKmog;iZZ_}wg{oF1udrtpv;3oMK+z=zu^=CU@t%uA?lexSZ_p#e&7`N|DIDe zLPTBCr*UCGY~h|;H9TfO;Y+4cIeNd)_(p84dz(Ip5kUrrOPSHfh&&TljM7~OmFHSs z%(Om78`5F5Zl1`P%W1M|i|CqOxp1hHv(OpSOGW#uHuR4-&8B?>xv$~X(wKa{`o+hE9?Nh zVYhTyAr%_U2|Vmj_}=nOIL3fg^Opok%gP1}qRcai>m}IRqxTYf?v0heOfPBAy0*&! zazTzC=!JiaUjAr$`N!yT^LwKU$-2?SdEme`I(+gng#$0D*`0X$>XuiAy>idJ?H(#* z&wkfF_8?y3F1gKcl?7En$t5qzxp${$@>?4gQiO$%lqR}}xAZU%P3VV>is6;zKHR6y!37n|cGSX-;Etm!VFaOCBk z?j_x)aK7*&A#C@wPr9XI?aeNk)Qi5P+x(XXR!apIpYKPyT)I*`uv(wgvUNcH{9My7 zLvbm2X&?1{mdcFXBW2CpdWfMOmur-R!L{;suUFy+Qu~&!cdA6915ir6kF;7@=V|i!{PA=yfDT*HW;H+$y2#R{@yqB zS0FL3W@TKbda(*!1MtfH?)#4nv`6L;6)~(t^>Pb5HWZu2jhDFG_EUTj-NKi@8(&c4 zI3glwuw=7jv5qCcKZ1fPKzczz6@V|BFyiaJzm6}cYeA1MDDwcmY&y4pg)f3=@x}D% z@^&g9u^`6}_`?6c#}}WCSdlad4iaDXF0S%rs)ZB1Zo5}Ny0=a*z-8C0R}Z;kV(D%l z!A-uFZ|;u*b4ehx@DtnB>7BDdA1-;OaJVPfB4NgPxb4F=TT={nlhgC*bB1>5Jq4ke z3nfBm0RdQRatdk29j%s|dB3!wmuYguIO`KX94jR;-k(_2m#RvM>6tRL5+5%+Upwd3 z^C%RPFK2Y6({?7Yu!+Yr)x#Ro$?clfn^6AJiGDd5THXcc7@5!;aI(Vg(B;D*%}2i-W`I_Z;|F39*dmrcm{_7WS#y6N7Or(-aYqu1KUtad0rw9V#@dbu%`T45 zcC9#^NgGdW4zMVUa9l{1$WCWYI{a~gcV@Xa@KSrLf#fiGalXUCbjq-f_^NtXlBac5 z{`-ZLOYdP}m!)hH+P}=kY4Qz8uj-T;a5U#@c7J%7+$wSXqn@j+_6;yasrZwYqt{MO z9xV0Ba9(w7fx(4*5^3R>xAPx0$lC-*9|Z}KvDA&|S?G~;5614+vcKWUA{iZ$&6#S; zE23{bAPhn+jx3V2Ql(MH_6B1&5o-oq+RjyX#8lx-z$l?Qo8c=fJ8D_`^PVi_m9)(S z&TM`=onmUCx6k0uQuH334YyO(PU*znM(j)4Ov)e=alu$jBRMaq$TzYNe&^9u4s}N3 zdMtlO1^E(O^(&lhPf27hr^nZvyrb&CgrpdR`kazq+K!agozKZsKZJ(3j`o6J7NDz> z3I1wKQ*n^H_W9w4Y3FKAW>Qx8SYmUQ%M(VKr&8X+c4p2>yK|@z*Rk#;@DN8In&&xr z|3Qip#q^!iEQN>zw|Q_RDN=VVaX%YrII@frA@KU-&xl44wna35HPHy6+GIsht-VlD zepD(E6-C(w?75pLVndMO-{eC1U7|tbxbks=C@B05(lsKV4OQNm*GD2iI4rkp)q}ou_I4OFSi5jJeiVh zeFZu%zjyS0_4w+$El&w8-e@((V3I+G!KUYCgV~dM=t^|X24mD=hIvDh-tuIUcc01Y zKOBU~ADpETb}n=7$@D8s0k2B#3f-0928@bK2~cE0Blxg(mJtzQ=9|*WS|1FGjNlgE z{K0(llhmWHd}FrGHcSEKwyj3QxjPW{NIg*(lcJYTII&7vc5$%qv8;G%yL#&#e&Ax`_$ z%Uigbb^(!ZDcDs=u_9q9gw#>UOe4z~ZC_h~{MpyHc4t|2EIQ3E?nysHiATdr zH))DJr5kjWLnz!f9Im%d`?@mxR-$ncR_1j(JHS8|%~ov6WU9$0t1~IpP7q&Z5egr^ zO%_ervi$&+%w^G+ofVOcfeg6RHS{#|O>**gjBvS?M0pJ}AJKAZX{X@(kukxdqAIa& zKZjZPz{T;(U2}bqMjNxXQ)MH%(sYbXl|(#vY*V+Bjna7Vr2T9CmyCpQOI>4BAD(~W z6Zvk>jmFDYjRjI$b`6lKvKcntD#o8CTIUyo`;V^fm2MW(A#LYr;_`m0u zi{JP~1rJ6o|N3&?Do+(_HM5*nZhb5%ezc@V05a!@1;mpiefZHyG7E_-Am`|1^yjn} z3&WWCg(k_h)2dDIZDnfDxzlIdFnM8Jx{jS~^n4w6iVlN_q0wqnk=iqHq-*ksdrbw} z@RdtfiilwB=NW|L1^cU)FsLm+-PygW=JO(}bfvd$oM${-ACuxD?#=C8J0sSQ>D-1U zbSFO9xavPzK9Y}KmZ^+*ALspL_JtmXLwH~J#yXm@7i(~j`FqUdl70x=ObMtm$nk?R zBeX>>e=@loLdKE;W)Juufeag;n0!QDT`+o+km%E1Ie(#72BnTF=H6A4H^a-?l$dva zGBw}-wrjkkC%3SwF1_T8=B|bu#r`P+uhY7GK$r=X*k1mz3J5btfH2e5C9>?Qo<*K_QNG-`jkf&A) zf;GqRE%~X}T)GJNV_0RbJPbD>{vcyoMO#}SbuOPoR)z5xeXrm$(0M{w>XtgX2&m26 zv02KGkw;~Fo014+`G)V^N!{CKE1G=#l!0`OL%YiDE@!fP@7sc~5G7`xb(Lwg!rvje zhLdB|j4~w?cL$N`z##&$Q(iBrHSBNP2sgZS5OMpeYMK9gkTE^YSr`8*JeVvSpUsKD zcPBGf46ASQvv-=_mpUR{DH(2di=M2uJ(18zd)~0ziD*TEfAO(asnJ}R(a-1w&cCI; z{MG6UKZ^Ah&27qs-MTbMdeae6;?jag%Yqo8#6#z-#qiU(oI=-ejA=dt5i_NI z%xBuu7W=h+BD{0}Tkmkj^w=r}?~P#6RQ%J0r&{=_59EF#ytuqo^hr20<q7oI9p*!l^E+%%thS?zezk8mEsXNj49s8gx2jz>EeO@(>ZN#rc@meY) zdQZHfc1KB88$-13tjAU8#k4Vsp-VoaBGUMbA0f}%RPOC(t>NmZ-jzZ>C>`aRvcECt zqUZ$)-ePIPfV_i0JHD zeU;C$QQ49Qrb+J`+G+m`S72MX@|TM(n{MgrcmYhWD7Su4^92o>2%_00f(BR6WG(%& z{Y4OUEogCtA7vh}zic|Ue}gOh=yAn4U;73lKo#Wp0at|o_qY=H4OfD3S+L~GL_VzO z2nolOVN-eC>kt+=`1b5`*kC+4$dq2j9brKu7Bo9eUw~C6O1@L$9?rz*$gS0#uypN7 z+!bxxcLdFotQBw(SL<}t$!+h_9V>Fa6V}Z-#nP}I``FH7FOQ*vHDV^0w|#rWa^E;q zUtZjf;oNE?-gAymOp38%u;MTu<_mtnhAURFw;nUHgrp~RN&;L#jvtUkWD8mT5@h+I zK;1u4qGPlGS4)(tq;apXMF{#Sf* zZCP0Lgc4EmlRI|dIUFTqDx+!BIYk@v6lYn+?<`SxRs#FoQ+w3HP4p_Yd;77~5U*n? z9A6q=gI`R$qv}LS;VQ{i!}aPZ9%p6$3|A`-f`-!4hr)R8oyP5va$FFzSAO-xz1is| zG}x9t)bF01Z*&e;@nl6ZOFV^7pE#lOejJkALlX(Y!oAsA`zR;syJVbeomH6dN^C$) zx+^su%%CP6QHQP1PnktvOJez}B^EeJrVd~hG%NxHk{@LfQFP`Q)J$ez+1YTOBU1v!3T7Lorwvjlx(mQ5K))m;>|$)Pz~ zF2Y3D^Fso6In~dDjH66u^cst}4#aPYi3ug6V z9re%_+@b9|+((TVQ0vM|)>!51AlqnhbGA_m$Tm{joNZ(>lMS+sKuzFy zZ}Cr__gg`>krp!BXy>MEBZ+U>M*i#BMz|QWjVv$RT(|1FvVJ6p5Mve}!Y;g%;IwA9 z{ghvi3?A`If}~_pB?dNAo0@xZOh@AC@YpYUQ}ceP!ugonXf5~fx%2tRcQ*rrg9mm> zP#G(TmJ{G{G@VawwG{-((-SfQp*T@dn(*h;jzy909w{Ms^i?hWjuN=Ha5be92XKEf z0Tpt6!j1RhyT?7vAiFmWy?~YZscLn5FJWC+{?`Z7vTIaC&--qB0Qu0yp=C)I3F+S! zOs4wr1d-?4g_!%GeC|2E4}@y)&sMEhIoXgYNP60L1-O+%2-NkB_B2S@TUi7_V$`36 zcTKiNgRG+tRhBu%x;2lkEnV2DpCQavS>3I&x|^dvqW_1=>P{<+Xzl>`f*d~}jOhO! zVb1-7Fwa4Sy0W96S4$r(OBrcMoygaB5v~?+V(_bscPgo#pP#{QlSSjB$WD+?LKP?!w{uz_^^W!ij`ILt{+TaC$LgZj6uRWn5wpCj4lg(VuAQen20% zgrS!2P~LE=SP}t!;Df~#jP>D=Mwe-WiHLd(HDcD`q^pN&ci>0{#)y+grU-_dY?rR( z0`^jV^pHioc-j;9_$7`7?PKKNvw;a*Msp_Bgn-n79N)JC*sAgJhgYY&fONJvkt98x zJOA*+3=5*m^);^C6s5n4P0GxI;Jui6;`*CAj^Qz$GZ|6iue_1ZT+%aJ-ZyrvOI%~` zt>vHx2VSMZ=LfIO*t7;@4C|kREig7eUQkEY(PfXao1d30@t(|%tK2uQ5xVc8oMJLt z*vt79g((^Gp;D3o8J4i1_#;{}sd}%o`D%DN8Df?ysDAxSjVfv>F7B>8J!*P zGdUWN(|XUP?8fdao>A-O%IDOb{@qcUAR#$6`Rcm@oV_R3O5XYhNx445!Fl_{H$CUI zShRZ9aW0=;ocSm+QdWA-k4`I%@{mKu_}I3e*cW#%)gZ7*;BaYH*|rbK$H&;!v(7Vc ziPCmNriqM~_>;XN?CUc>?rdp>>U=b_4**!Ln;XJ8TMvtoeM+c`v+*ajGX2ZCI7SIzxoU8S9e1VjS{ta9`1r zpGz#=qfa`s`tB4uVA2^hsz!r#+#Yim_B%8ckF#Sig*;QxPwk>I=o-ve^7gj3n9o>5 zpnt;VnW*>5-1!rP7VX6{UeV|SNNcs|E}yyZK9Y6e-k=A@S$F%oliTK(-#rs?V@Ds? zGMvt<7EZtbR4xgw36VXRfNbXV6z=U3P6EI3-M*MXB~v#sRue!9a{TZy+A8_~N0Z7X zE6WdYq0RMNKz2099!jC=fw{VsE_(Ki?H0~m7*lpR*qIpFbY}X7&GNjpjO3I9y@I5{ zO9{7y6U;7deN)5Aty;z2hGFBL)^|3NqqnamN6#O=M>n0knr6gDJ*3BJH>nLh#eCSA zS4{ZcE0AE6(oM#h&vGP^7K_7Fi3w>@@y?KW70Na%;n|GqdRT1D#^QZPG496hf6|~i zg5GaSAx$BpBc@qPHzJ-jQ|!*_2vn?TG`wNpJamQe~MM1x#p-KeO2z>gaR{J493 z2kG1pZA?g8%QYy{y1Q_nO*O7=jAq?ltDA827_!zJ@Z*}Ug??DX8Drp5v5{q&3GC`? z4qRP^vN$tyB?`S{gK7!`e51B8kz{)QOh|v8bxNbHsBh%{CoIrdUOX{(d82p%0y;*L zc|pds2lC3)dUp+~?PAp|$CN{40<6!!IJgVaT6Bm^Bdq535-s>u7xTI5oguOJ0RLjP zmZY117S_)whJVZ6@^{-?1W>~`B4{f2i=ycmAc`7N0%0LEGyzAYonHD>Me#R#Iew;b5j=0*?xjz*5}br$#^&hyUTxW`N%v<8 zJ8a?A7zey94Fx4F4a`Hsg(W$C25TwM*L4(=bH+ycmp33LlX}PiK+N%1R_hRR{}q^$ zvTnzX+>V48a*i68wK8%Cuiax(9XmV4L1cYc@&N5Jt*1P--YL!x!mEpyYSt>zHJDvn zEA!HL&?n8c0clCWo}M#qV+XMyP)u$ogkC5D?;_6!z%d?5YnAQ_peb>_47hqtnXnV? z7uX%(_zV(K)mehN__l0}paZ~N&;j7E#)<19cj)<(ApIiDDUG{D1Dw4awqs{pcQnnb zkx9lY!yob|(`AUdeK6L9Y95cX%0u<`E)MB}QVKZqMu@cC4IMe@0CEa=yj!w>n0yHB zD{-SetM|&%+bIH!WJ>8C^|ccen_nb*=%LNqFMKFJz{@BecC1M1TKlSgzKkA*_&HEe1*(|UWjy#3Rlm7-ib3W@A{ zv{h>nqJ?4HtiT^SAgGu)Y7884*fEwsWa>+G1UG=_FEK=3}C~0 z&%1=gS`7UbEfF}82LQ7m$M@W_RhjP3=9cwZx@g)hYJY3DXw65m%Qw_{;?(w8^AO?O z#fH|d74I`Pw!2U5E$j*^5`9(ZFiIC3s^4?Fg!!5M!^O5Z#V_VUI^748R132Q$wwDX zx4OQKXtvN&e)H&n?@&j5!Z@FtnzO}9a4#=KMn@or(+gS6Q4HBobTSo6nj zn$c|gK=_2ng17~dcD(jYzMi9S5S`qvRV=%94oXV(p=_c-+NWoYbSTA!LHhvJop>4+ zG8F0sEG&1WaM@46;JEONeOz4d8e)|YQt6{AGkVCbjG^W6(%O?0ScucF_tT!oABrL} zraf;js;2&0PE`7m!)~n#Y^iehx(nN27w&vu!OF<1;KG^2KIz9#*y}g$HGP&V@H0?> zZq;k~yFmrDJ{+*)Zi=vc1q%^TH1%50bV3wGt@{Q=Q&mXP8NaM#t*Sh7tFB zQrW68_h*v|vfHBk>{)z6DKh#C(Oya?a`mThZ+`W&%wEz$4D9HWXARqR3$`Yv1Ax@^Waw#OVzm?gQY%b>!}mj2uqY>$J>p z_i8=?ip(h{>&LnbE`F_{d!ta3!|8Usa+9xgp^|4skq}%b&r+>1z^{F7n#E{e=_^3A>5m%93br}9DI~LWOZBh%q zk?;23cyN_Cf!XW%^_O=o==r2#&q`-@aUeSu`I9m6RCC3AS?KULwzHn9zkUA#md+?d z@*1SN<-RQppQ%l{=HZnc2P>lXNDF)by3A*KU*pA>*cHhhgrLa3#xq5R`|y-g6Z>%V z1p6yL)sVJhBloF(7^P{T63Ll!#Vy=1p3r|)L_G?Eza56F+mzvuGpQLbSLPoe<2+SO7o)l=1=i<#SF!f?l6k2lU(Dx2Z3yBL^|fj(mmgpG zX|3EXAMTb9_y4L77t-yjgkEI%14_+ph%8+j#s61UYbS7h8~ql)N0+T4bALFxd~3JZ2ZBSj68k`>=w+(&2{fa?EM3%5b_|LboTzg7PaVc{8x=V$p4Q!W$6nWtVH zo-`bKsBE!_qKJZ3W6gKcN*rab6T^zkDl&Xd`uYB{Zs4(wP#hv&bv{tm7F&bA)%Qhs1*J(Ry|%jrEry+l-iSBIYw?i(MU6LGx&kEM%AN$fD-Wn0JrIsy?Q>d}u*S@%vKynz0V_D3a}U+r5#* zuH>P2(a`h>OezOx2%x{hL@n0f*r<6f3Zq%2_S^-NT!%IfZ;UC_%?_YJG#UEC0A87R zVt9GtCt3^ph$AP#?Xwe)F#}7Lf}I;5hzKFa_rS7MT<(tsmP5$?&|hs|m>!ZVRI_?0Ty=mKLb3Q5d3M<7qrT-C=sDIyC=;wmWgAcim0FJm$0 z3=&hvanL;38EyKkr|d+zpQE1>!Chxf^@pP3$ws)y2ZY<)EmLGPu8?vt2vz!}GPokD zvB^MV=n$alaNU6H#g8P5-u2YfmyK*b41a-8HT7I_t97K5`W0u^(*9ZIPPWfW_9|t8 z(#Gdpr%0dAs2%(XobZco^@9GpLqTX79g3jozl^q=zW_@3VWI($`MVO`vi=dRjnsfJ$L{;aQFS{W#a6?R=<`e=^b9d{ zv_4qStXZ-j{j-3z=cT(hCeWx2{A9vh>Dk#cap)J^JdyIzslmb?!zs_SOp%gb=2)%{ z)Sbo5C$@vcI7a}RAjkK7vQn!st>L@cmUg`_CfL6o=8s(@`X~8(I}Z%?tWE9 zP?N=%_zjztBC-RtUvsR4LTBK52WZO+4}9~VHykDEC`+lw5A=MR^W+fQ6YtLs<`(G_ z68;}7JlCt@TMO7QX^jl{HktF6f(j^sr1KB47Sf+EYV3!KLlAi%ll9UW`D++*n4caV zF0{U{*Ked0#35H-yrQ=Zv^-pHxnjQ~S3D$T;5v-&fx zX26n;e?apBhluplEqfM8k*cSe-8QNZpCkJa-3**m;UU)KmLbH6xX1Ugg-V`6TSTwg zZhqOR3(vsz4#n9}^BId&Z|pCgVLvS_2j6nC{N4D1T0|BUzD0v2Ff>jUVKm!NI~@Yc z?Iw))UCS*RR}ETxL3O+U%kBRoCyO9@CrgGN;u-p!=RcsX+(yoGm&X0|R?yw6H4`|` z_LARJ)|$?IAn=8s>HT`nv!NvVoagD2%TMV|8zMfZMvY=9kfPFdw_ktaeoIq&TH(j8 zREw&C!S?Jk!kChQvb}W9HfFf2m}Z_GKD-^H2y|4htDwbiNh>SkL}LMGZ=g`K&3gCF;^)*hUrKKxvTy3NwH{3 z+JLe!eqI8_*m_xtg7>7_Fwg zGT;cYGWF%wlChI@e6jd`IYkBrlAg<^MHeLwM+yOrMTz|GoxJ8smVtIH;)Sv(-V`6t zrx})m#g~w>i>E|kP93Y;T3Ij}Ja3w>$`Ws`@#O zdVC~mBqq8*q&oP3K+NNb2mu{Q)KW@&CU>9f`VkwKnzK*r4c>NV+Y>T6D(wp8y$aZ> z%+RP$xWW$+WMzI5I*X6+<@Q*^2I^OdKapbu zfQ*i?HM280O@hoZfgt~(3(?=$hE(2>n+_gC%I{w%f}7>WMm}j61U$y4?O~6A@X5P7 zD{46#JA;49N`py!rp6E$*m2Xo;0C&}?>|ondH|zt1h-JsjUbg}W8>FjsvBE?YliQT zrI`~%7lLe>140G*U;%b6w$5uu-&poq7~$*5@X0zNjGgTqAsmN||MFOo;HEU2udIQ# z?+`zl6rx}2I|PHG>38(Y+0SUwnb7hKYN`qsrX( za?GDqWDc49EX}=E!-b{#_;JglU33K3^T|xsGl=GAu+Z1`f4oozaAx0M0gwc%4g zgo{VL_E6|0M?JWw0?zm8(Hrw%TSas+710%d^9sU5g+v6Q{NS&%?|)kR^=|8ouyuBV zfW$+HCJCRkt*ssSY3&}i7ApRFuR=b&@jG7?VQb-h3WCgsM8=jCEUldpj^MjBxMRs7 zOzq4N>oI0#>(!4=DYg{p_?8DE;3Ym@`k(J!QL7)2RKkun|k#bZy+XHbf>09Ohfbdoo{Judy4YwC_EDCh|Bqnlaj$L?| z5yn#Ia~mrN?uWRb%*sFLCa6NWeCqwH3X-rVR*j-|COu)UcRv@^c3$RmreW6hmBwa~ z=^4a}j!z@%G?!ter>59bG+>Hy-WS^H9Bn!ni#7pl@n; z!hXgp>Nyn5Y$0>kZz-%lVtR3oLTA+XY(M? zGsJR9#!2bosH-%BQ9InkTHw4yKoR%z$@A}aUv+O0-7jt;6~at)RYgEsI(R~0C?wB_ zLt9}#z8?{j(BY|Dy*89|m z{DKg3$hU1UK!M1wfBGE)?$~tyUwaS=`YfWP8gHdXv&yR}koI?Eq-*0M8qb~AQF zU~L9vgrk$Coh?7FD6hbN$SG%Md#3|@d^X=mn%B zep}KN3kyeQ!w2))rd)~)gqoCAc?^T0+no6L@rh`&dZU@A;+^VApLv%oe`Wdys)q?~Hv#TQgC57do|37)UM9MlSXpCoEG2G{%9)F1b+ytYg0PDx3Yjiux8 z8Jpf6KR?iSd*|A=KdfusV;bl5y74*dudQ_L3GBKanU-tzQk7bqR(5FGV;X!2_FZ<#`@!c9P0BZt z#f-HHtcAg6965ftEVk%seBiSG?KSH zDK*T;uXBMLP#?q#d|k%4YoNn+EH!g>c`C0^&`quR^XYSJcH5_n_2&KG%y{qA94~4#Sn5?-yTf1P3e7Bk45)ah+WETj#wAi|Bi)7U zlAX{yuXynKjX_Y$HrdQw{QUkf5+lR<&o34$u&`|ply%VISp`MhCsQ@by~@l^`w2Or;U&gzcTyEBStIY6O30+j$6RSyJI z)$2)p1vTzKh<#JMm7%sD`OeE}Z%&nGU)x(0+RLF>Lv53H;o2<<&{V;ltNgmi0GayW zrBGk)CKD2&SJE#u=w+@|9W~}L@_XuBa`7&qn&cceW+41E~em5kG= zLDY&fqQa;sho+&yue*HElpjVbEAgYO86WL*l9aDuU}ZwnVmR8 z@ZLtSXrN{HT|2SWGw1jRDh{@-a?SDk=Loaqi{OXkaPw{#4ScKZUY91`_~GnFC$0zx zD>B;02cmtPC2?sKA-8H}qb-oJKDwK=UXlyOcjz8{k%*2r_Xc@ZR2&SIuT-Gdn(N3+ zdm8cEt%(1;le~JogsjqWJp-uauBoq*Mal zN%qCRNl>+3ikb90-;D@VTuY>{ADMGE~nY#v$5%R`MXDdm~!llwVBG@}A9 zvt-kX)#bA&={SMq^tgj{kBYY^%iae@+O)P~tcv;Hr2VATi|lfyQ7MT1ovEX-s~D z#-vY|OCh6tp#DzxK`HjW^QyH+$&b^%eyLn;Fc|;EH!Eqi1naEIuLxm)KT5R-^;-p9 zs0|0s=0zL-7USf!!w^H${`U@3&9*oEG6?`Z&aTV6ikgX{=Zlx>?`UstY+S9+beb^r z4A%ed(Q(H}-MQ?W8}p0r^qzsEvxJ1$`)%-Sal_C<*kz!59kKR#@O}UBZxFWC z5$lhJu-f$sLSJakViPbi6^+Q&6pY?vrnnu(A$NLjcB$c8m6xmxd|U|Xc1B<{dbaJK zJ`*6T?Z9puwGVZP5+U44uR32hXfbv{10TO0tUnlc>yvGfhj=U~Ak-5*T&Nd^P=)rG zy}zD@tFEIkHoMjlYoL^5O!Y=cknd>`?>U-T%;Hud@&`nqRWh3yimgz;fuaEVpu`LiFu%HDaBsZA0SUBEiZ_T zfl1vCkyh`f14(PUB6rU3^u4*016Z%tB(M|PpU89zDKWe|sx?4JMD*-lMcsYD4tqi# z#ZNs^Iyi`6p|{et8bn-~*4tj4G;YLlspsdaiN(QZ`k4Hon$N&Fk{^Fv2_qt5zLdAW z$up1Tlo3~h+>~zn1C_Gd^*WP@|7mruEKU|C- zc`V*Oz}D9KI6c*K`Nr;^>9)EvxYIfG249AVnp+cA#Y-8!nCu=oN3-NF)5S!kFGd%V zQ=73%ljPq0&&UQDujgN-##l(7264x5TXj`eC$C&>mbS~h+w@O<#O`do+;KDEAm^Q4 znM16l!>fkRMPb9kyJWT-!w&lBcKE*m^{$tFJR6l_&J$~M&pVv$Wgj3y_TYMB`i-2Y}w?_6me4Z$F~t)FPovlJ|)lP}P!=Un@BP=0^jzb^bg9^W^*{KvMU#TdR1aTVaK7Ur3@;rM?|t*=g*!8o_1!~^{dH0 zg)jIPeErpZNzruM5)nqtsYBc43$=?DXqAED8AI3Q>oqv(8UVH+q6`)`p@2cqh5t-srGI`Qb=T5C2qWUapz=@s21t8K?G=KZQf zgp9_9>bj~PCpr6UPaoTF!B^k4T}>sd#+LA;-&$%OIK@jB+&+7IJN_~ZcRO`hko4yM z26`lm~FrycZ@d$+xz%JzY0ll`meEiAi74=p5Le|cHT1mjS82-q%@FStz zr@bUsI+RAo>2hnnDqo_$d&ze?RLy)Jezg7?ew?Ng6ZW%wd0{-&U1;%bhsBw!v^XQf zu$+~aMYrjNj8@M?rn|WnpJNoyB$w1LjjzVR_y)>Wbjp%I0ZQ0w7pF3_#gPKo?eO~~ zVIEHu85785gFR?d@PYQ#NKj!W+&Y_1QfBLDIihuoaOAq{4JS)_Hc25*{kxqZWM!2-MnUn(IJPEvy` zuYDXiz=rtT2L*zW?aV+ZQ-bS|cons^5k9Npji}(a&Ultsp&$h5Rh=pNDk8#^1YYEG z^~S4{2vE7QS7sW-PKjww__0Jul>lqlOY&>1EjT};GXdc(I{T~XOc2GX4;=VtEMI8c z`)InsgK7yV)fa&Cf0IS)tF7#pGY-*M($LZwYPWijak%N+{tcZ8^P{D+YwvrUKLavD zjvwd@Q!|kNBAw}P(3ytHb14e>*Fkx!Jh`Ow6#H1;gbwb9>~s8-=`rI{FUm)>I5$k^ zQ;6A%{~e&sicTjF7u?xhA?aj029N(OY60eP)h?tl|Iuh+{iE6Z@?Uo|A;WKos>pl& z&$RSOX&JPB>%i%{uGYFc4}P+}l3EwS zm)%b&9DXM<2{I61h)&?@-_ytFnY8V9PxGLq!)K-?uWr#_=d`x4_o*w}B$pz8`GqMO zD#2pP;B&oXZnA_K)tZ__+N1!cA;%A>w$>5b=kSJ@QpC}{l2c1lz54|ZZ^XWvut?> z?-&J+G`KQHcsJV>6gn=7&D z)5YC?Sdz|6d|y)-tl;=jU=-V{Y4?Eib?S+5MyyC!3U07taS)4{81H37hXqv?3oqVz z{l-l4?5C`Qw7$;t3zT>?ymXVM*i%-4!0tXrz*6oab9)!7)OAJzvd=e(3q56ycuSR; zUwi%(@4o*MBX(3M2LoU}YZmDQ+o=W-wYl^t_QTS zN^Y_@Wv66^Lb7*Oc6J#lm6TDE6&aB|BQqmggzS=$S!9Rb=ORzatuNoZ|MTtnA3Bcf zNLRP(zHXA2^L@V06TlX9`~qA%=ZSn>Ye5VUEJ;E zQ_pi8pODmXou1G?O<5Lj%-Z&I<3o%V+4m;XIh=LU2oX|c7dkMO_@Q7`s<53bdU5f!>g0U>IW6Vj>$_0%qQk~bD6YT0b0^%X z1MEa9%31o$oK@$u0N6zj<1J1?q39FEBsK{Ly4dB1sZJyGA1mH79BP^iEHaN!33wvK z7_J9Pr>d&aRu&`}0eM|XWav2-(rFJ@E=4R-iDQp~PR10j3wz~Hy1%4%KhG;%sR7p; zZr{9&9-RZ*1IUaeRW~L?9!Z`3oUZSa71!dDlmbuRV@uU2f;t<;c*~CbRCKz zgk9Wlu?w@uqG^-m$ju5`{zdHiXE{>X41|b^2~$`sA3M%nq0cZlpm+cYZ!k{DN!f9H z5xDWq1(8l{)f_exM|#0H#DQ)pXz}uc_uJ8S>`dMjb(<|J-Q?74=-tjc8oc`wAyyWL z{wjSSsBk;ON%M}>63h4O(_&$1+o7i}Fz`$7D}G*=hQYulvn^W7wSC8Gn!{rH&Vp!L z1K648w{9CrdaHJ~plyrTz(g}^$)KDrD?f>L%RJ?0HzN7P<$9R;mS0+R{1~ZQCf#L@ zDVXl&1eS3a=CMg0#ZKGIe}%v>$Z^%Dsr5Q-8whT6oF=BvxYyRrY<8~yeh<{_d-}3J z8D$Ngb=Ks-uuFcoT6{3((3LE~d*?76l#aSyOnMnoHdw=wlh#?DaPV#LP-23EK-{+g zSJ#RBb96(PZlQ6oU^*%4TT`28ul4f!WPZ=L8nob6jM{Yv4yvIUM zVjoOI?9n=Z`?3>KR(je?{4QDjlP~+FkXGa^OE=Bd+k2?8gt9K<^7f52~LY7 zS*E;%@y)%E7r!!PH_^qm+4I+F%%#YBqO)vSg^B`w7lhpSyc<${i1l&;NcXZ}Uxl3Y`|itf0Bp`&n}_cw)5yu#DI z9pgwK{`KYTSHD&+Y=eY$ycusY>R&&1Df>0-q!~+opMUHiFi6~ zA9|Y1O%vQ*w&y?tlo!_>^>A9CDiyV#aLZ{OhDwrJL-}2{R zGHwBLu;QqFd`vd(=DdZ)EOGicP`jYx7sYF*J?F3Htsh~mRFavxXV!(W60e4_vP@D` z-E#BNWOvXlqZ~goG)AB?zEILTcD|cG&PQqCmgRh`K>BmR?%6rlsBGC%l$M2^id@Nq)kWt1X zm7scH88M(_rs#}a=~J*v29b*}m~0XyN|*W$SQUP&*iMxSq!xrW71&wR2q8A%4q zB|vOq&jBn zDm^ap=+=$oWmX0Gd)JNW%E~Z~8Q;6_&B7t%oxEE?{JiTcYrbD^%hN^9 zjV3X;GS@~k8OQ)z_u3GVx!Aej%3O8|vCEt6KyKER^Di>jx*f=3h`At{i=bUx#o^HW zop}7KNc>aYcat)j9fVu!vbvEvwybj{lDm_;^`T$wg*d=2==cS_c6xvQX7mb>gxp=L#f62f{}dN~6nH|oQe2pQ zPTyruUabFFT^s@Xo~mk1yP3(B1zVSn4iJO?_KS(*Y7BlKX&zYzM|9D&0#C4WEr`Ll zw(I>meD;B=y^9ivUNsHOak~;i0zCGZm#V6ly4Gq7r==pQ1F*}Ok^RQ}zUbU$+Dme9 z((o?FzTxZp-W(Fgf!tkqsg_-@v>uKqn^rZ+9`$if0Pe2FIx%3&VpVkO3NQt8NC^=6 z^%+Ivx(f`MiC2j{lq()hcBKLEE_!`Zrx{2IqXcr;NJPSYmEr{n53)X7a>8&aOgHtk zw?GMd1&m(U{z?+Bp%k!+uis@1FhUlUrcU?dBNgFc71C0#*ij=03TZeDH-DY@oCyixpeJ~O)P1cmMY9L~)HL|RxN`l@H{6<*&ZT0l zJkP`eE?)Z-8f5AB)rpkKQIm7yxOxbc$e#R3r^K@xZaAelb81V=9ecIJseSpuxN!)x zKJb3)?>LN&eTT#T(;Nn;eurn{$Pi5K)-lc6XmMk$4h!%45#QRl5o%6?e*K8w-^RFc z!}t<@<1)vF$ANL#9z9LB*5m&1}78DO12_kxaJw6D!Ne*Q%cllY;1t#FvW*b<#1 zYEK2yV%a%lsw#&eA`SHx8#Mx~g@q~9!Y%vlJEla9pQ{O$)?D0f5Pl}`+rOJ4@lv}* zQa8!FUAD>Hiy1TFqP|`6OKKM5)qw=}uI&vRC!5UWN5KB2UKN+i>t83lNM22^0|v2&c8oE zt1I^!mWqD*y@fSprqWD%(oD^?g>(FPXp@9~aHOR}j6k}yVE4mXJm(RUSMF85u|WhL~)<)<)5PkLCO=J|NN8yskaT~IT8BU>NktQVRD&NplY>*mm&$(5| zP>P29;A=^dmnJW%Tx+-p&1niNp1|YS*yM__AC(Oiy}wv5=`+#7>Tu_!vZKQCBw@-j zk}%UK5yo;6EGG>s)%p|K-dAr4FsfYhwtNwbwfcTM8oe2T*q*nEV|A4ciMnofaF(19 z@+%s0QixR=ikTzK-=L3$UG9QY=hzr`OMm}0zAn?dAZxYj&bq8szs~a76OXtX z)$AvoIQn9~xoYHV^yR8dqfH$p0(*=|LzT6Q-|`9h8$#xi+>W1xIX=-`c&f=Inz~3F zc~{J{C6A8^uX&F3dOB7!29gc#FfzzP9r8N=wAdxnV!t*BoVahh2vS`p@*Cp4BGf&@ zi~A&87_Pcl`U63086-=D${p=)=kZAk{w#NOs+mRe>VvP74Sb{uxG-2h#!BedXQ$)| zi9lRo&#HiL9yfY+l_`+T%d^L@Z~&9NVR5l$z>Q&}8a>FF1%mBs&a3Yth5?rs&1vdb zhSL|Q1u1;lMV>wumXq;sF)TR%!Y2HCoZrBfzGs0^UyV3nCDm~KxFIigq<>$8j&fw_ z8MIjTo3M&u{eED!hq{c)jwPX2u5=7V5Gn-9K2I*gqegACmywgrr(eyDtuv=edZ?%@ z*|T(Yv8{pod2U+M?>LNohr|AD4*RoSFZPW(77yTn^hfp9Ff>y4QvXhjGH^Fe5d|-5r3zpyOw~i|sGy9y+rIVhwkI-i0o__nvE| zBP&&sQ)4te5>6!8YFu^K&$zlGWp6d>K1=~buZB1f3+iih=xvLN4uI&@58b3=D)l3x z-IE?-ceg3*c=Z*V=eLk-S0&VQ^62(bVzWki4lW2wZp$KnqRJDywAA{Q(O(ae^w2O9u+5GmT0FjWcu-q*0LnH zxCxp*o46hVMUQ3|=RDSm9v{~T9Gx`mlCtWnnSAY^G_e$MLZk0pjg=cA$_m9HU~5ZbiW_jq(){&OMf1V zDH)`+4n^iQTY)`CTfj~i=&GcZs5LYPPkIDLGgvFB?}DuM1z@>R&%b3Nf!Op4h`^_g zX27gWR@amw3zw;cWy>>0YoZ>9+J!1CbLbOLVbm-kt|m=>@x>td0-Z*55M+vhlzz56 zQ2MBM%DyzJ_yyV?49H~v;+fw{to>$tjjStMk5JwEN)J2qhANfX+VBy?2&`4H3ka;8 z1Uvp~Cz*v=sZN}rVpg=jtVhKxcE$R%f;H50$Sf)h9>lDpMa2pN$k+8s10q;^-yatQ z-~KnA1mFKRo&GX1((4Yq!n1?!~OO zGMfKf-?&=YxLG;!-&*>}wgHqtoW#cX2VRzS(G05tL?Cv-ImfdB+Q44pMnK!L;frhI zy}$)8jt!3kaT4oZ8zSJv!2x&Bn4|Li*e3v8(DCyR{eMC1>iIS9T8mx1yhNu!HfzO^ zE`1K2YSyDHpCdnGrVHeH&3&ar+H#!4OcUnw3>hf&UU2*5^*PJPJV2S0(PGQ$3uI_Y zRI`12LH_Xtp~v%&73wm)oCRgKg?vB=>qzH$`0b)&>+zGJ+eI)Qy*40+z4^F3F%0C; z&xStTRv1MWhf|Fa8+(7LR zMwq}++tb`^n*>Zi5EimbUd9Ok3_5;>u$}DHzZ=42N}$;LNs=X$7MaJbDigjhF!nkL zojXW!%1z?)0uPmw`2~Y(uVOFhQtIjZ=<{`692X3=TWBABn)AXB&n!3cij^;SZzb(N z`4S(e^SNjG-hZ*CVt*5u`+Xf9IhFe%y$eqzJq;;p zf+FMz&Ja3Q)y#X2xLsisZ9BzgQb*)(>MLB8!Wu1wQ>b^dxu&0&sOi(8V~JmJR<97b z>swNVo<*XusPvKO$*>By*~Jtp*lVh5_qB{q*_(z2+{fJmBMYsAL3)>{Veu=Mk&ieX z6;|{Rcx;AUEIVC8|F+)sXXlO(P5$D&;#ohh`;J&V8+}{AXTL74VGYIL^mOyC_gQ^^ z+?KJ6W1VL8{e9tL7u$x%0iXT4*MaJBC8 zcHe2GgF21rLVor(A%-X8YWCELm=P^WKiZBhPGk@L2)dV2x0O0xhi10rR|q~9WuRxs z*$*V-k8kT{q5O)`(Yrw zh^E_*F9(RXLdW*RuX*f$=B=Fo)xVjy0@qC*#ZvVo)u)kFvedq!LX*evmC2)LQ2a@C z;uC1(pL*`$Tj}}LEG`W@XSsHCJ<6qRt1z)U3REwxi*uf}_%8b!d*x=nnQ)g4 z>YTloa@$AT-Nxkxb&XpdlF$LsE-Js3O2^ntk`>+p7W?O)TJWA0(vBY!?!sN%=jQEy zZ0;t~d`1Z|HrvG2`|oW;XnIxV)+p@CuJ z(|V-?XxY(}c@L>zx#v3fti;*t9HY5%_m~){;GUAW{`rub)+ z7pt7izEpc>GJy0AkvG?bN1)EJm^^nXCn7}{=jAL-&^(t5o?U0{s{nnE68vaIo@OAQOhxGR=NG8f zn}RxKPvl+>MkRErBZggPN(4Z?Ho8{#_!7!z`rTV&qQ#DR5Tt76OuSreZ?Qe^m!0Xm zFLBN4?%5gPyboMjil1z6X;J6F0pnY@IqM1!_3qvBzznJ@%F!!tJESvu=eDX3_i;_H zEz%~)&UqaD)w!Td{d2R-$_65Tw3?fC?=KO{4aEfECX8ULU+R0P>oB1o?JAch%J6~gX zmrBFDaCIfYp4!<1R*%uvozziWeeQ$`F&p47%{%y3rcZObonM;U-3?ZCoYCbAHI+Tu z`f@Hge@T11j78S|YY9c2e%SE^le?L@yQZgfho1`+B(>PRdWb$C!_F|80udLQa(Bo_o`DNr@EAg9VQz{7Ff@*(hm;lRie+377+k$i0K z`idGJ!npGGc1sRP&N-wa^k&d7#CX*l%}!4|P8$<>$cY99n?VQeE>Qd9y_=*@=$acY zsT>=zHoavyT27EJo5)jtVL~4Qlf8Eut{*exwY+^NlK22{dQDxp?sPCR;#(!F71|b_u$Rx(p z$MaAutkCPro!1TpvqaI<3j)V|Kc+&PU&ZgRYo}%VKaO2HE!$hbF8In99eMWWHlTVz z$Ir^wUy!4EdQJH%x>*7ROx(3tqN~H`@vASK_KQa$+;d`nx?x58dbzxS-ovy->~faj zhiV?retYP=O~*aRQMIUyD`0+n;gOUh<+kIwlyxr$Y||T7o(|$yXM*(k4O6zBc?HUV zfCHYjCf&0_p?d&-TYv}Rz`(>X#-(L{as^N6mRASu4;@{NeB~|}-tH6PSD`0vqu$Y= z%^bUje#Ym{9F5$ZxW3gK^kf9nMdn3WzI@O2+z~8g57YmUo*N>dG1Is>cA@ixTzUNB z%>=&sT(-G^5PVw-d6vb&y7QEN4vOZ_a#M%qq9zyxT`O9Y@@=agX%;-XspA9kQ`LjQ z7H}GfI^&kDoRMY5TO3nk*Sdd7k$+ zMxiSyd-(Z;j!HO9F)ylr5IcQlJ36rml{?6d*mdPXGDG78DXKKu(I!zs6T4d=vmS-I z#1ykXb5}_!`aI@Hi%&kXot6BF-|!VXFBkN;JBBVJZL0sKp^IgM0gHtPPVW!T#@-(m zUXFFi3~Q4`I0h`<^*ue?o-A%moq#`C-0(QC(YWrlAu?HHV}m^BxFhc9dW1)-@w zWG&F6jf*6#;k%@I-kSx_Dj*02-+^J4&#jJkAdhNfpq=2V-B#Oy?1n_^CZ(LcUHkKL z9%yZAQ84qJMSPqwp+Ycodso`E?R7NP_{7zCs&=N|V=X-b4e+*jyXKwx<(ch!6*4>9 zc&fT4@wePX2&rK&`94L z`tGPl5#!Plr}3ps);Kh2>%c|0xTRwi8xQ`-*sLEf-!-Y2kled}6i zitLfy@`l6YGe^|GTXeR_GpDRKr!nEl7*i!cV$kt3jqT*9{@pZoX~k3z4H*x;OVwL9 zWPG+dWON%l$EHrqwlBD$h+V+2tgU0LK;3R(VKn2VJqvk)?P;QsA}3{y#MT_@>Rygn z7a!XAx|h!BOqpA7n&01>Wiu)F61 zRnPcdglW7^ruPl+I3px4{NjFC{gb>@m6M{cAH}qC^B;{(bJvB9?>&287@0;?3ul%# zBI*1MKTliVh+5tXw;RDV&sf{3nl1gWAFFyE@2lpUq$hrwk*xACW+hiFm@?j$5A?!M zK6vfKO*ETY6YhOYw)=nBj2*Dqebr0RNF`i=%?tpWfkvSg*8=1#TaB6Mj!}n( zk58}sZ%Q0N+*b^blxl*MBVXzM&x_k$S$t9PP+#rq_TMj^r^kF-Po}m|?PCNOvhL8k zlCCm(@25+!2v0MR%jw$oc_WW4W(1i8&{2z~FWzsUDkvn;c zk(CL2#U*r5#lOIYtO3yM<;POIsFt;siMat4^XjHLw81XFZXq5-+Whklr_901I`{z% zJb+nQR{N5kjx`ndKLPsVkJWl;`yQN-)3UbKvoxau|9?@*V>2rlYUvu88Bjs1^w8g0 zn(HX&S%ZyuW^jXGKVR>PH5Kz2Q?M*A^8I(w@4ue`+wwnF@mCh;e|`bbeM8S@sR!>ls)p`f*v!lv{AneD zVEqRN`s=luk(YSyPolTg#+XiNs4kcYjtm`bZE-(f-aBL0JH@U(+}qk?e|%ru(>C{c zi;uE1+&b#j?m$N?klR;YrmG}F&`R;xT-)eTWTm2H2B!48(#P7kryb0MVzpsJxJS|K zE)g0uV&8drOl>+PhWb4xp6GC%c`jECad2&^qP&@bw}{bUkkO@MDe#fYhP~WVoK}MT zQ+S{9u1m@l#MW=L-(2N8qmW{B(WqT+LTWcL7l`~)t}1` z^pz*9ycwm#uK>be!6GJ2td=mwa(NvAwPCWL;3UpH;YY;3L$w`y{(syjwqwtSz8Qf3 zs?M0{n^Uc%kF5OPW@W8qX??{~Pam6=Z4-Nb=keDJMW{$f&;iM4>%Zq*HZ!;>CvzGP zdq4DD3-65hNqO-52y|cpaRJG$Q36`EQzj!2@fr2pSHxnL zOI*3O@3(3ri)Ry+-2(h1d~-8Z%k6Yb|BDaThWmU?aPxumU8ff5f;w#cfLz3G5`5Og zT^_V%j=yZr^&8jdzzgj!-s0zR!;P)}W0+u%=_8XJb6y~MeAY$_fqy68KZs0U(m&Xa z2ISRcT>Rhtdt1BDirMNS5|I`U_|k=r zU%uLRvS%n7U5<879$rCT!KQlINbZzCTr zuD_c`hr(n8bB=>?k_c zjB#K;?_ReL>@}vI68++jl55(7z5DEUJ-uUX*r&Wtevq_qTQ zmKSCW5Wh13@Bk)d?@G6LrN($Q(~{j3wE7domeSejNRs7Tg&1c6Fzru2(L3i6q>$K$ z_$PHv?ZfNN9-}_OWeg#Jj!-GLhTPL(J!Y)+!giYn5$1_NupE?HLtb%$|r@2xevO zF9Ks`4cKjCo%>ZWm?qe@+^pO+XZ5^k+T1f=mo}B{^ZEMts$8iY*kwhtlOQx`$G+oU z$7}k${l0@cH&C-1e_u{ zsAzIb5q}q-bZnCm>;taQS(xb&_duQJvU(9QKzSrfr#N=!Lr{NaxWcDaBcTxUee}(_ znAqgHAY~VmuXzBUn5zQFp}|j6S?t1-g)wN#D_H_ObBO3iOF8z?pWE#{#_=I^Q zC-ST>09a#3HaJc{o(<=~gc*+ek8L9-ZkaLHm!N;Y7r2bcxxujrCd}(z8zPL!1>gPW z?J#m75AY>){KA;5O#kP)CEXfh3S+P6mZ$TU1IjaW4~x?Z$lF}IT^woWI{I=3Rs4$j zB2tgzHdM-=zEeX|r@geR=5x-)U9-m(hKmp+2q2!aUgp_X`Yae2^R*G2_3(X=1wu`-%Of6PsN!X-(l)@+BOhieL8&7 zldFEoCBT1H9n)xqG%+2mACeze=`=6s=58;adY^=*-cRX1GbUb#x6gZ@w)>NdT+RKgUZt7gE^J1(n`KQZK0mr% zqA6d?ROd$=C$#A4;i_!ok`QC zRgg=Kz!&iutnwL^0>QvGgdjTZ9FQ-Y3Yjy}ONHo+u8W-U({^NweTQ)%isIAFBjvWy z;U@0*@;-OMC1@3%Ob+rGxwUY-iU-lc%xvg1>+Tj#F_$V8ufeVq@Bm&=Vlq~Yq5 zCsAaTCmIu4Yl;0$FMIkQcJX?b5<&-S4|n(1ISg~*oX9*m5EJZo6gH`^TRNS0WA#pO zBdm1?muKgW?mv_wC^&_an)*oOcU;N3v)23H=E{u)pP;8<0~>-u_>FAH%LV7$MyDZQ z&5e5ImbntniZNWSJ-ixLs8CgGi`pgQKV0dJ zA`Ir?#t#=Xcsud4$$U+m_gh>u!#pIy>>#Kcvz19t5v4^IuN)x6l8E!pi8q(uN%LdP#`xzhsnSF`2+Fb)Tf z%Sno|TW(^>Wc(k#=m=C@u_;(^=^i`Coi#o7Izcys|B>cQf8+f72dP!V`UkW}6kIdr z+uj*0Xen_Q*HpOmUiu^z#*g|uK>ZYe-im5w3NyHE(&O9DqPOL znPVhFuLI4)rJ16+$}U6m@N>9*7^NOPwRj&sW{UK-^$E;nM^nGQfBnq^Z}#5(bf!^> zeX+O^)zvuZHUd$JqqGWW7vHd0*DfTEx7~aqKn8q_qBN(^-1l*a25O?0gpY)r-cf}J z>2dAg_9j-NVj8^Qvc5zdUTNZ~l^J&I_p4bqeIQ029wDV4@~2RuGaW!38Tz6!k>{E_^6$6we+EJs5LRjGsRu{T_Y>MaJVwsTH&3C0=^rFE2~l z-F!&=FcK!aBA$c->9juLeq^5HV5|W)n32bRj72-Ault$bB?n8`eQ_>G!-@3~;cww` zXE6RBhs$-t51F-Pnas%iBxLo(z5Q0nigYkum zY41%$%gtKA{zbU_N3<-0c9z9fc9z-Eu~;ls8e~I_gfQ-Sy6!#`2 zMOr8YU4i7B^ZCoM9SAbus<*V%^d0TXI-<+~Nd!3%49Be<^sOw9Hk}-H>>8;2BX^f+ zTsN^agQ$+$%O2x6{zr@<{$O9{**PXEOK8JXz9|g~{i9##7oir^8(+<{B4k9+FOKF30yU z6PKm1zV2(p;X)q>ei+;z%T@{|=MPBNopHYK`Z}vgWo6P6oc9IwnR_woQUXCHi}NIw+nB4 zlR}PcLoZ!A?{KXcgUp}e*ew>bhbweh6HP*|iWx6{MOt)30j6zm`Q_?Ny7ref7L_`7)CkA+%IlwcYNP_$|*f(0eFNIfTXn8|9?9ca*48H|SMbJh0 z5*V6@S10XsiYmTE%a0}{VH~YtVRPt*#XDa8jypMa*0KNF+{w9aWqFM!;WVT1vTqb7 zd3iUSV`X7oCnT<356rCB@!$97*s`bz#|R3SJJ&a!eScn7)^)E95$@!Mzm82hia)jq zcWzdL_J6^htoSIbz>s-+cKLy{xR6LHrs=bTh18PIYxKde98Wykb%6jW3?WTZXCBq~ zIbeXgpbG@Hh+zQOE%N=M_F-sgm@c=nZ-#Dp2GPkj7{5bva;}$ms#J1|8WAV4^b?-; zr5?{_c!y09yx2ce@_}!x*6N5~>_~y+iI=x>n9~V zb8T2YNivTjX8vw%gZwfjH9fRJ9-sZn>Oj=$2Kh*XR-$QYPFQ}oU1RU``A+_Z>q@zY zRWf2^^@f;_hMeNQFz&^3F(LoxRDwWLf7!E^$Y(JQi}?oyr&>ds(PrXBE?U_jT^g#a z9^igL>L9m#j)fH4Mw-~>k-1#MrCuk?v8dFhiwCY=k9EfFYA5377J_M4kQ1k>ko$R2 zoNs5Fij)!Ma5*XJ(Zcann4H{d-+Avma(Xr~@-eEu3j9GZC66#HQ4(JnSxa4GiY$pj zR&T7wYx5%3R;RON)5GSf2l>?j3ZHbbiA};m8uj5=k=jrGY0?(_$(|E~R7M^mXWS3v z#e`RIwq0iIYjr3F1M+CY5->L>7Y?X3yvh`$^9D&T`iA$YJ*a#e1cUN@DEZ9Uj4j{p zc+eg4x7UzM;U`;thq>JAU?3m#1kcVF8p!hnp=3C86(-%sfcbIIPh??`Tyw*-2D5s8 zt*3r;a>>`g2p`PjPh%|I4KsHOzqUBnz?>`aNb`4Gxic*PvF`iZMPE8>C@MnNMh8SG zz}77ZU)I&9($>;321(-IQ?$V&MJ@HTtikH=Nszce1v|;d29k1E*jag4x!HKw{_LX9 z&aiw7yW{YOVEz!-&yyueU$=Q36CieR9pc? zgViQsI}?oV6z7YJg2!gf5#`k&g)Up~bRJQ@8pP$ykv)Qg9-W;;`4O)Wyx8r!$73&J zD3K{k5WCRD1I(QEhey6Xt^17$lf`(H;55)Oq2m`lbEmoPuja~?bswllB}C|6!h@G1 zsHtYTgUC>Ea0Fg@OVa&4b>l1-Kyh7ggBSn&L$aWRv8?X#;L+as<_`V_Z>3;V`?N^e z-Zo~v;25n5>f1(n%Fd=WX>%;`GdxFO!c;nt-n!Q?y*6e~xflsRO1VqwG+lTQ!2yj? z7t`9`5%44xh}N!;mFQ|$a7%6tqD_0_ z(`@OHguzp3RZeWm;~p5vB9*~zCo~Y<%K>t{8)IQDIH2whgXVMuR)zv|w3B$aHGDOxq_=DEuDxf{#e#r5FyOfl!?oP?bk^_Fe(!EeBR~aO$Cf5SHh?`K>5VlIk4}sa!!>>ZS5Rb zu~^Hj`Vp%Iq^`A-%)+cxKhnW|s}ioswI%-X=ub`PhtN^!-G=u<`bztWdIu{bo^q)+^oXxU!==JYgWvZ-C)HBO_*P7 ziB1r?r-wp2bPVmtN$HR*?>HIEEL%hP{OwA8NcDCaNwV^GQM9+PE9 z>ndu&edQ5?YzgUjvYwCH^6oBABdQ60&;1eoFvU7-)HlP7?<|Y>8aZL^D=+ijK)ajv z1BlrZML-}?BYV_TkX-k`AK2yx!g1pGeDj<@H=;Y~Wd<@cU5$6I79i-qO!OhORHN&^ zkYFaXxm;O%Am;0FfRxbj3s&yb^ZoT$Nh~&S!Sg(oWOMm+GG#^fXgChmCt7H?$E-j` z;+v2A379g}hs=MV>(1@qh0d7;+nMZ~w|276?!B9iecTlrp3266;sy%GI*c{d{GmIvUR8}9`! zO0sWw97rTu_u3FaNp>!{D5;*q%(=-{&t`3X{~}8M*y_OsC@Chqx-EXACz?nj;YFcG z;p(SlPiB`X%M)_{gp~+RdpF@4{puKOjoz_=PHW`UwAjLY0Jq2f6~bO_j^tcU$VN zn95jr4%nP$1IXCq!|s^>BjmD}M~VKCwTsYc1CwEe_E5#>l-u~>t1IItnbEQ0_f?{( z@EX*je=|{uaAAqN+f2Z5su@!9##4pMGYWAzk1+ z+g6bs82K0z1Y-U}m1XfBRc@I_Z9*h10y%n~osqSPgT0LW&Xz`AeV$Sv&j`ApFnH6m z66*|6GcU6u!;B=%G)e>lOXw+M?2hDODG%hyowcQcVmHC#^|BgM9lZ0lsa@6sa=95&>E!-l+|abe|M*2|XbS#I@uU|Spki4u`c1fUvl7Dp6RtE{;Yy6YC8dMs8E!m<1(%&rut8Qm z%s(}j^l~O2dDl#uE7GjsRq7lS+d zjS)2m&IL7oBCC5bfg5+T`48L}M800HV#>p5%M)&iHW%23A-w7Wtf}9aEZ%*L9w(6k zaqMFU5p@r+$Iv>ISL@qLWc;ZqxP}vX*3xp`pE6e>y3~{9gULcfH(pe;ys$Y}9=sw> zy;gJm3s>$mEB@77S+%ZNQRR0@L{aYieATx<{xbwW-_0LKq95#T=@283@x0)n)T8UK z?FGxeXisOnt(zB3GJEX8Kj;u&H$F6_0Csv32c6Z3%l3`lY~xDul;0VhbQf*W z>n$7<7=D(vbWFrv4M1?L#u1at6h@;v4J1rfFN^m#-nQ=cHzgFV+>JldYLpw4iWdq@ zjvNZ3rwn9}ziF#Hu^P%I-&JXS_?YcHzP|~~%~vO0=Ba|h6J62W9u(xB^e;VXhz~#G zD2X348c~op8NgY&TFO1NF6KOjh8$$xTWFw%=Pt%EZO-#YB)EuyYxAw<`U`|Sayw+B zXmp*8_VBSc2dTjA6%on%@U*n1s41-6YWb-MiJ0h<1G|+Tg;&3>@E&#AO+)St44dL2 zpDOB?zf4N{pVW4B86rJV{7C3`jJeZu{U0}3?(|#(5d9wtdN%2~wt79-_5zH+q2re^ z_-18_{~}}3u8qNk%|Id%nlQ%PvWD3$_B|6%i*imnX=19z<-ZLI$^nEa3*yv|BSe@c zu04;_K-8sOX0y8*Vy!D+p+(pELAIH(#S@{@<7`B-4%|Fd%b917(Df?{qZVyWm-)|6 z1aJVw5;~yT?jNlQJ297kHC(Q9ST?IprxH}W7=40?HMWv+sb1hMd7_}?%GqQrt~XvB z^AGQRw4UjmS#WXc=zNw-JCvOzc<&tMykhi{_8X(i?o;`97tf6wE7lE(+HjwL8LH^~ z_KVm2wni0u1`U^(WrVIS;B zgs&A}pNYan1{~|=Vg{6sH}z91D?6~tITUOz!iam3z8)_~pNFE%mZnOn;G{xV;Xw(9 z8C<7pyXIKMZ+h_eNckcfGCg`{TAe1LxRyFL1R=whfO1Gw^k<4H2!9jTi0= zy#Hjx<9xu7(DAc+@fQSJ9$UjmRx?y+2fX3)a)DIe`475uoad&l9Vaw2;R1F_N}=L9 zQ1pnPP$~1rg-Y%WW=EXdMgzpb1uW@skBx=4p2#G3B1KU2gmNuIu|pp3;%61U;6kbX z2kuUj8VtbAD(1UpSlyQXOt?>HIw}cBq+d;&hyr!saNMZjBXq7GNZF{PLx~)AIN? zv*d$yLgkaJ5j{#@n|3ils_eT^H_yVtxr-li@FSru@O${pgW<=^bgcxP3&x|%N1?=Y z@<*dSm7iYDH)Sr$bJ>55#L*HjDIGC>19VDrNT(dKHLe?PpK44THA>Wcndn`{!H1M# zsz0z!r%cb0fBn(^noO-SW$FxVfSyz&YW+hgaaJ_DCj%&0Vz@@`&mOZ|T}y5YG~6eB z@IKz*{fF|RwQiKXQ2dZAAJr*$57s7!PIuZ;s9YurM~vJ-r1IXbXEbWJGxT5<8I1LP zgDgf@!P%9)7ZL75#6zd#qFCwQ>@ziF=&P_%9@Ui~HjQ;# zxuyw@^=k@04A^9T_#szy0Il>`ylh@_$JsuIz0bqK-wo1+=F!7dbA5g%ShDX7#sAww z@r}{rz*xCqzzkMkHh>%pFE^ZX8*6=7S>V+AY#B7+4Cvv4=EmVUsOI@YLvc>{L-7wj z&-1eZWJ1T!f+gEuQ2$8t4{73qp*Wf_{+DHDO5P(!ZheXzLghO3PuP&gB1+)^yg@hc z#ck>I#6y?D3&!)iKU+or5xLUoLH3K~%BkGO#^upJvbzIBvMUa%wwB~s*|!!dXXX&? zy@7c1@w*dRml|hyZy_jt{KzKcAHR1Af;U7pEiYB2OdHUcW+vbdUo${TJIN(d@_*t&p zNlyQp5i{`2CGZor*h>Fn;01*lHqXcO3l^?7C29^$ z%wz;Jx#KgVV-{BN4^sUD{U#xj7M+O^tLKOo!pW_ROatL6IcivRT zrXpsILiOHH+bfFA%3Z!^D(#Fyy2~k1599svYKkN2#u0|1#&oBncZubgB-AM2*Q5x! zr!e)iaH^qYN5AKJr^m;%A;5UgJV3IZh*B-E^{IIQxRvm!p^y zF9*5gSf}m_x%3EZd7lmslDwE6abnX!a_|R+fZC4TV&u<2q!F&dr|JXseTTqO_{AwZ zub|{NRsKMIM5jK@6!YdaBt(KYkH)@v?GIDa72h8r_a?kz=GH0*NAG&t$Q)jUor@*c z-$G6@SBtcITa7+GPSM}dQaHc|_Zwxw2{d6KVJZ~7cYH+rJF;Zkkud+;WVx}B8*GHb zAx0KBawQj>bG#cQOaOT62yeR*CLB2xE?I7rI>AQxACfRR;7gd^#9#O40IGzJpLLDD zAU^%*KVWI*AxDT_v=}(<+Gk;6`axHdTuBWH?UM9#E$Dj?pM9}_ezgFh7|WSyR8Y-x z_v+n0R?Tz5igE^<>v=bVYy>UIsC^)}b53gQ>~tyXpLy4%C{Wa?gCM{GvgGuf>lja4 z91}xKKcfcmKtIZPTDBrl*!==hl|ccRn(YO?&flc&9E zNvPSBlwU~PRfNJ2b#y8o>g4$-79o1s_uzE3PJV^1O~Il=w+Us-Y9~(??;}mS{*w8L zGi%Z5Nw#6C3nm1%Hbf)C4S`9P%ZV4e`!0k{W7M@bR9rakD5qh&^leeU`j&mN>$%TL z*m~wtmUG{(N*B!??T_Y-lQLFWi%zF?po5~*@lFx(4LG;hjV3&==gID%rNrF7XDLRj zF}uTtIwt6eb>tI}Mvl&S+9JGHBnm8ucYu`iU6(HI+h;iJB(Bz8cORoE=n%>9B#I{< zJ&gDK^}@;GOp(>d^(+-v5i zGSNe1@MExIC-tMAd_#%D!X{S);p%-HOu8Uv9eNXulc}PoV62ZC4R3nbzy;|g?v>Er zp(M*rB>I0FCD+G>f5$wYjq&0@xZDtb&ce$H=Nz1Ea$Y#wfy@Y5^&WZ-?`^|TDT5dXeBDX-q9?kg=sGeX6 z{#JV`?dYDc&v-NlvM#7fzYzMxsq|z#*_$*3EsU#VPtc0rrReH9hTxA0zEeSsLGN79 zzL6vL3g0(>L5-+n=(T^Rjk7sVaxq0SO8}OHj-PpQCldWX!jn{zfi&xRQgIzm79Bbw zPT<(j8b;C4F`nO@m0Ml|O~dbuWS;K(s_uL#SdjV=PiEw#3s%Y7G2tCS>dC>ct|TJ?B3;Kwq)v2)yR&CbrI^anH(of8 zS?S~m8L#K(nG#AB1fv)ImkbvXNT}}L*1)D61G5MHI z@_~hJ5@;2irq4mgRPbwO0^=YD-%&heS|j?v0d#LC_!X%LSZ6F~@R5%Q)S;slXiSr$l_1Y~5_mXNoF}j@1VF3j1s~6nR|bzvg=4PR z??+bRzvD?1xc3d{&7oI>qKWphp|8G)R=Rv3lY~C#uo1XZNlXStU3!16)WkmJO1hOd zp{D@RME6n{2mN_L?IXL``n$W}TeL9ScNBN`O61U^y+Iwn157qf=x?D=M<}9jB`=ho|kt3U{}%8ih?Wx#}E z--HX8oNzSK^-zomU~;m;wOdBb8wR}uIwf@ctX2L6(dhJRT4fR5$`G9L(6awsq}f6p z)BQ>`n!{1eOHkzw@{tpXOUOM|Zl0^*H>=v&>S%ULBXFR!*{``RdH>W}c=cR+hQXtb zkWiN}l8$P`TYNyIBX=u);-476g3a&=+)rfep?xz!OEV;o&)Oh790nhGXm7^>*TJ%S z{`S4`iG|O9DA*@AyD>NaGalm7EMuLojBNUZD`MK92w2GiwLllocPaKGXp*E}b6aL3 z`06fO=OLvqGxWT=lYMJ*zO*7hqYwma2^~N4b>?fClq1H%`H=K&?Yxy{QnEHWyGENOx9znG*YZ&gE7_DIH$~dpvT&759u#1VCHPS!~i5H{|;5 zg~jsOPc~eq7w46~KUiV#!v_hxl}50mcydRL6oFL|<;Ohz;HuB^tL>@NOC6wr2h73hv@wApgpT|bTrTL108z@*@~l(EA#B+?v0bKUPp@P z(n{eDsW1^J+=!#5aH@$Q$~9?VWi%SK0r^KV(u$38f*WsVQ14*+~*@mJ(VRiX=5YGAI>gDN3{o zMJiHMLK!Vew9-&Xixi?{OO_OpysO9i^?u%S?>+B( z?mg!)UPS+YUx2Ike96vQRI2v)cVN9C1{*&`rJ6EpQF-KA@gm-OLu66;9ju7{bs0-z zAp=RHt1I7pTBztgF<3FJ^^TEkZ1k4g@~Sm=wFk;QZ%74m!L|9B3!a%e;zEiGSjbX0 z>02yh8Pz1duzg8W@%+VpontTEE4az$N{o2z#QL(bDzDRb?0xh33>O@EHr$(EExN6;t`05PsWvPJwp)*a{~;M^k6!3R|C0XbILnaQ^S>N{PN} zPAY<$5*k@o{#YCRyRIw$ZbJACur&JO>@+YTd{d_UvOXH|16`^ebByno?J95TeB-`0 zsqxV9%157!7Rc6GMzw64op@EWsB=o2R8s0Zy`+JU$`-YHTU0GLq#s_NXnSeB+36)_ zH&kZMj7>Oo+rw{^cbstRlAk=|%!`cXB|2>mSs+uh*YnRjY0uR{(ns1<*10)*&+#|E zvAdt?%a}RZC6Y;Lrd_d?BX*3qC-z)j)Gl@7tdZ5mg~G1f+i|IqU{bg&QaEmI~7qWLy`SwLaYNelpLNG?kiaU28++}BE zZjp?J>uvZwCHOrjw%OFtI$_w=qk}}uq$NwbmWF7=804KP6q>9Q(f>i~zTV)I%7oD4 z#f`9v><_kW9Ahdosad}zvu|{t*9E(L1yZCJhW$essj98{V{Gz2Jr!KJ=aS}H8a>NC zEoMpfxfIk^W0YyUKht1&A+FV9sq%lnvyNKNTv*G4(mPaY<-o8TlQohCZ4ao7{d&ziJdGP-vIlgM@Jbs2?wl#I(Ug1c+a}EC&N_}C z0xGo<4*MTgJv;n0|KHnHUb^wRJf$q7pI(Oy*Cw0awGHuVLOWfTJo{5Nli%0haA%zY zZjrN9VzysPw~}9{B%cF+U~KpE%9RmY)9MZDZ^kvP-}R5^zXWQWQQk$9k>(t zoh+{p)_%2(IdzZYQ&K;4Wlhm_OWi?kWXec4#2 zVdJY#u(1yKC%lbye&rYrGUS?vdsYv-QgH64*7(_m>AuShx41}O1ha7r2dLi6(o}HX z_req}ObORRzwNMJd#R?EkdutjZjZoTx{B##Mo)S#0cBwHi3#DmYQuFJXKZqjJ_qJ? zSK4m?HE7f?^U~t_Oxu~P6A=_Gj+cv+mn_%KoG34D9I^butEe39UvGM4O?}dP5ZDoX zwtT;iUv$*vocC0mtZ}li2Y(YBCORDyrK95}yzD30KG11T-0kG+Aq}_v(?qj&d)!!W zKU1gI%RYG;!E#l>K09PfHVUKPi&rG1Sy|mXp_Bnu&9sUhT+;CiUi5NLV49p^cdk;X z{I(mPK0Z`3c@#bLA1X_=A8);RueSfA1C&4BdV>+w|7)>I_P5?n?x8QUK~)KjtSrBS z_0T7LsVrT=T9wlciojd%5`jh2Y`vR*J1Jg#waBvkle+3#j=1lXE>6WpzFvE;@~gO* z(lGZ*_BWfN^Qh~w#HG9vH~qMe?HQX6JmIq%0mT;BeCFA+$gYBg=iSZTq=cn$KWgO3d|lhPGekbI1*Uz<9u|cY$e}W_WoPzeBuWOg7Xq zA~UzUO^JU@usHqOXxDO|Z!=G)?>AUwsNeB?h1YHsBkljb!gN+L9s3&?n}kM=PySd9 z{d=x3_o4C0sb~iHtjMZ@v=Mm4O1yXDVz77PdTNycUsgjedZrf@aY{p2 z;`QAPHu_x_H5bqPY`EP}T5-V=Fi%JS^{X+(k2fD&_2BKLx(`t)t?sVPS$AeE-PRWC zYt!+d>&||uxAxsh6-tr`XI1`MlPJX%xvP1`B|1KDm`S>@>)y~4J5+|%X$!=K#Bq0< z)gEl`x_3U*H7KWWjbV%X(UD-3@<7g-jUz6mn{Q8kJF>Omv^m&l=dai*`2nj>g|ALX z-!nFSSihj?ak8Zf2DaROIwR!Ai>@=fZn3$_*bHoQ)8BaKi>bLDpMMRSR+SepyR^UJ zIlBYi3!=^IB;yuMQHo371Ge%GGOTF7=4wzg@4BVX!>H|6;rl~kPaE|9H5Z0z1zYxj zP?LP>7B%Xc7lRGQ9pj=rjx`15X&GsXzcO_j7pZHy^y#cqRf|4|cqBJ1M>#HfbX7YgD%qK{WTez$_yZWQJh-h1lv#-ET z@6w`wDmm4DEI0Jup2?%$;~h45|IL^I!yBaRxo#wQ|J`HqIC%ff@@`tS=ezTN{h=kx zmgTI2nHoKERsla>_Xy%=FjIqlxuN)H>C@Ru&hKD(bgJaMDx2StSUP~?&~d-wyw$I# zmG6U+6D*JJADFtfn^V#BZ@;Q!`v$Y+a#w)oU9ehp`4gLg{C^g?yVdT+#pI1oE^#&7 zvI@>gZ1DRgWsY0Ziy>fV^lK&h{0@(HL|Izy-@tFd`lZHnu0H8~v(JaNPVT3zEpFv6 z_#X}ii$}Uvm6g3}Q!LtIrJw2oS~e!q^k}W&y@;P-Mu$;_JvXbyP8{J8NfF;`5Jp?sE6&~^(D5Z zpqPa=;7e>6M;_SM*eZ$+E1B`z)ACWgds|kdOMv*n&+>Z2hq+ryv^2>HayZZi67ge! zA~XIUtDKnvGD~9+fPEGgurtLz4Y1(8jsDl|)OiO70yOj?1mG&5Oo4)Z)hOfMwOC-v zrl$Inpi;Uel074|743)5s*GK5B|x=;XGkhSc;SE*me^sRMzG~P zU1hgPPb7g=s1;fbu}>o;n+Po9 ztrADJf~_8*06&#yB7`@;M=4)g{s$6>5yd`J0NzuTY&`3Rfb^k}2!)1}0EG}1lz|Y2 z?#Bv7j@V}^z+U0RiLJp?z)1v+L>N4SP=H@$F%fn&bs_}e8(3gxAM7&)Aj0Ylt2k+p zJ~R@+-;@%d5N2+;gb>o|u|l^Q_L&M0VSDo765b3265-YyLIJ)SWFj0q^9dpF1P-)! zv7SgmQ_AiS1L;E}5!B{W0u;h_tIG&sp@chXXEinK(+JCb4TX4pGl}54kWheQPfUdA z9Rhn{XMb4-3!F^nAfG7!TcLHvYYTCZJ~R@c-HsBV5K^ME5yHHsSYf0C_L&N>6&lX9 z%+&xV5j3*A(6nbN(A~mXgS+UU*KWh{b^PwIHp0~5jW)dVh&)75%gOi@z#id42%zpy zEHJm_Ao6K|)ZY$8S%BoBkpdMf2m!jh7&qZ3bj&?Vut2(*5NjLg$QG|13XVTCvJIBF zQ34c#!6RJa9qr|TP9t8)Mg5yl(5+EIKD?}IV(Vltv4qO(I4}+weC5{ z9vazAre{$C6vDaR0};Z~9azCz6Z^=Q;u!G&#_En)9q43}_l=2RBIt@b?XCjpLn9H=3n>8#LHa;2LP!u>j}DTx3-)OQqu(tT>VcC88hIk+ zJ|q&cguxE4 z#q2@)&`5-7&jDSheje4yrcvu1e={Z5W<{bjF2-Q z`!vFX2U=iC0*3>QL};iX6zGbjZciFY`E)84$Xa=Z6=BD3uQQ53`p`%On?_22LRffY z7ebIP#tO!Zu}>phJ{r4V21p+oiLm$$p+MIxCc>9d%7NWjz(hTh6+yVd{UQ4usj!_A zpb&m3*@Fa623i0b z*$PGi0?b<;`b3`jggJO>I6{bbzzCzIu}>rPQ8J#%K00eIL@3a8i)zR^u<0iKJ1)5UZ!p+2iZd-ueIZ{WPA#sX1p5$5S)tz{QF~{ zDF8dRSw>0Kb3yXZNP%=YLVzwWPUYd(9(%p8z~NiDtZlH;&SX$2$Q~Nm2Fhb80Se($ z3NG<<4q*je$;U*1ZQx@cFT(x^r8ADHK=*4u$L&BF^WqOl+s;amVVyj_4rC9FJVYmy z$@mmN%wTua1}kzffZw}6dRzw(;9$2*jygF0(8!Z0Ta_t5_i7r&yHUa#>o=m4bba$K zR)9_S=K8$=$wMOnA7xQfDv)=pOB5 zRwPPTbf_bFfbXe7rL}Xd*a$ zppgJ0&6xspe>RI7g%Xxtf#U}x?Pmq3cA1*X-U8kW$oLe%AZ=d+aBLSA$P~vu4dA4u z`!_>rTdbJ^bZ;h^6@wCXIFI9d6dYg$sOiiIc@2&}G_nOcmyq!(fWb2U2tZuMA2so@ zH-5;c0Sw+Ix-JAq9~#*Ll8#IPx-T0UeHbMyK4}w*KlJ_~RsicwcXQY;;(aU0_!L0c zv`r{seQPYxD2RO~z&}5QCvXKA`-d}&6;qerQS8CP?xUyhojD>sl81M= z60zwAzzmK7is#~wbI-`s?-3v7UZ(rkgsZ(c9B5?gPV;2u-s35}SV|uOY<#&H9sM-* zDXgtKE>*%o3$!dWQb2hvAwYMNeq1M%@zH*PNI-1&GFF0ldOxi?2eOAoc9RhsDFLc| z_oi8*ldLn9H^2M`K$BgDR2fl~ftjRgkBJF_B$c)zyE z1nEN~5jwU~0u;h@uLTG}a}!o59fo}xVX{SHtN}}42r0{L5Q3~RRv0rL`%DFRBH1^#cRPUep^*st zV+jSiURi7Jfe@~WZAAzNo!wbmL8@UuBRj&FL`r}{Pz|<42s&C=L18NPX@spx!w#~a zNMcEZ0$s7>E?$FDPFjKmvJb9iMUc%%j%G*jJwpjl2#c33K?okHSRtr&G4g2yWtWD5 zGeHYLBU@p`SwexXS@d?TLn%Km!U9*Wu4P5|kRn%i7F^2E$ZMhW5+y((=tel8lz9p= z^RZ7OT$4?F$NmVLolPjvRm=6$J}713PZ+^C!xndc-K>$k5pwcGa(zfB(3OiyV*pC|=V~nQaRm06#|Yk$JpZbFdJ&vN z(8v?1@+l=iA;|x}0;POaBNQQsN@Jg?0Pjdy$&(}0z;ytPtiAG|F%{@;p|9+8M6h`= zjxVKP#M%htX7|i5;l@{y@hN~Ue;6Quq#!KtKm_|V0J+6BQ(#CPAwZWGM{Wk9jPsJQ zKyK+~RswPtRi;3A6D2?)$R^mK*pz9VD6Av2%zEP4%Ea76*G}f1CUFfG6e#``^QP(Yg}O9Js{nqRb^SA zga?h@iQyX@wbIV-y*m0_gwF6an}aVSzr$*rx%=WgeIU(qowdbZ=&~ z-5MpF(1qiV*}RAqfSl0FjK6y#8J_}()Sim~3P~}R!|bC=C-}tI=Xwyy=SIF zSM9O^2@(b)Lmgi~h@ha5IdBpXqlv(l4hP)?i>!%jTw@(~qrNJ@?1WdCq&z z`@A2|t8`ebrLHMHUT(T2sx1lI=m%FXCv$N@eM$q zJQW3xlY?MAa%-_7DqIi(($tid0Ao56|k1?t>TD?}M*P9sAyqyc>1O;63yoBm>?oag@>>5c|Zl@}k0MV&v4 zk6-Ty-MvM<6FR%D?cN`s{`2j~jw>f>Tor?{*zIZO&ED3J2VVHqwkdKo>u71o8+E}Zu(!-0zftql{-NPW@fxXRMKsd2`^16$N1Ali;_2k*7va`NFyxWbU zx^!43-Ydw9^La3m6N)TaGWPJOYf#vAx!?WOTjz#L9~QoU{oww|1!4Qn=dcixRZB`D zUSeE!En`T@B;FbrTMt|*3@ats9kY7E@<_FenFg~uoK+B6WkyD%hXpihnY8A|TFQ73 zD=GuTs>}d!XQo^yw4abX`J&G&Az{R|Rfpt;`o9Hf342e)LZ<~oR-qn-f(VN2EDZPs z==Zt7>l(F5t$iLeLKu*ilVtPEky;QNR0Qb|1#gQ7Nh1ALz*`U8tY;(2Z1M$U8Gqg< z@D@ByiY&_+iqTUV+DIE1Bcm^&jD-|cs7fOj6uEkW0<@N(W){%vbQ&F~2QwIrGKCl5 zQy5KYGBC<$LV22NN11T=^D)E!U?x=Fxpp)~qq5D#C`zA+X>@41%(F8ZGPy7m8fo+G zbQyLuWlD<;imcDiiSe=T^lj!)t^ipl2)M|2q>(nhUI6hT$LE^_SrNw5N;5hQp;T7a HT8aMvH&6u7 literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/1.png b/scripts/generated_apriltags/1.0x1.0cm/1.png new file mode 100644 index 0000000000000000000000000000000000000000..77073d952b58c47f66198601a3744a2e8a2927e3 GIT binary patch literal 544 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 QoK}YASSlxH>kc#ZKP^516#xJL literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/11.png b/scripts/generated_apriltags/1.0x1.0cm/11.png new file mode 100644 index 0000000000000000000000000000000000000000..cbb8b6654ee4299b379404b698fdf17aea233250 GIT binary patch literal 522 zcmeAS@N?(olHy`uVBq!ia0vp^WgyJK1|(&drwalp%M#a!lHmNblJdl&R0iL~l7gK4 zlAO#Wg|gJ5;>`R!V?9ef6CH(&l9GaAD}DW3xB|WWqI7+^3HLI9hHw^mL>4nJ=qZCR zW5rT?pf!x;o-U3d6?5L+3FJCtAi{9aZO{LyuLV_-Ozxih6U}t`cjb|u&o<|q=N#Y9 ze?aPx8>h5t&Xf&-NDSNbdV^yT#X|RH-TXJ}hDPGzmR=z^$avMa9vURwD+CPE8G5gHXapmmHt1(kGY=DPr_>9rx3>}omdR{-OM!PC{xWt~$(69A*9)+zu1 literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/12.ai b/scripts/generated_apriltags/1.0x1.0cm/12.ai new file mode 100644 index 0000000000000000000000000000000000000000..d4834206e98a345d9bce0d8bc72ff9ed6b923b76 GIT binary patch literal 1603 zcmZuyeNYr-7!L`_6^k125G!7tVK@zLKW=Z`V~~RT5C+ie&T%s5u;q3+4)1nx@4cHd zhf+)u`mmv3FjHYNizNA@!J%|&h_sQ;sZl2^Os%GI8X?LunrxWf_dej@+Wlka{k_lo zKELPV_iUccY%Qd9ERolD6`c5(paBJx+6JPy7?7KGHcEgr2~hAVZa~@ux8w&5GO&Y^ z5<-$iY{(}ZuqrYH`iKGf)W+Hd$pyjM=+Y8`>m5*l5@4f%O@L5RWnjQdf^^giqDOWE zF1D*GE{6ocR^--fMO35^1Z0H|Q7DUjO|fqUa3;{9lLNrKF?&@)?4tBZyX02_std(~ z`6K_;lIRi2lo0Yrp_k!xdX{4i9A{*V^Hxrj6BH=A%@2&x)1|VkpcRdeCME}O$Fo@_ z$!=H==oka5$BW_zq}7X(VwPNrDB&>tP?ZE95qfK&^Go?ND$_DGO+3G+b%GCeHkJ2{ z>HbOCUy^z9@~VMPx4qbQa0OGahAwLze>d|1|yC3~<=iAN0u16>K1byE; z@|AM#Ojr8dZTo($>bdaBqLWpN7p`cpyr!AV&&v6u{s23;(*5|cR(s0sq87Nfp{>&S zuEmw(ubs}WT(jf&&0RPB89m{DyP7jJUo*uWmZ8O49BCT-!cl)}v>BQ2k-remznU0QBsoXdGMoP`414najq#s(g>+Di-5N-WA z_P*xFdkM}~dj84%y(5QLmZ#pgyzAnIh3kwPj)P&1cKPk=g*hW0=IkuAjd|Vxoj=q12Z+G+tddKKvP z)PkU1XVkF;pdP|Te-TOgX0~*S>RwHdHdPS=E~JFSQ;lLBs1;n>QD$Q|Aj`=09z{0e zDWWwOF%-j5dYY%#GCadQM)CO+mA^WHTu7Db2nx_FLCrjXGZ^#+PzPo(8f6MUK#pTH z#h^sZ#VDRfq0YmM6e`<%jQbyqM-r;#xLb(E@T~Tn6@gyTF4ve8QX E2jzeGLjV8( literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/12.png b/scripts/generated_apriltags/1.0x1.0cm/12.png new file mode 100644 index 0000000000000000000000000000000000000000..f334d99460e3a49dba725e767dfdb20512cbe740 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^WgyJK1|(&drwalp%M#a!lHmNblJdl&R0iL~l7gK4 zlAO#Wg|gJ5;>`R!V?9ef6CH(&l9GaAD}DW3xB|WWqI7+^3HLI9hHw^mL>4nJ=qZCR zW5rT?pf!xWo-U3d6?5L+byX#lbRJwv4d^_i~Qd=!UwPA@RYGh`KltGZZq1b5Nsk44pbXrw^zt_x^Ene)s#% z_xm2dFTcWKTg9q)D*x;?@a6@I1q_g?>!^|vKyPaDLO`1ZBzPnjpeqCy^Z^bVR04y6 zf})cc>S7#hl8AshWI#W&p}Gz_5GV>Ct&M1n9SH~mJ`7X{2tio{8gis)d#&Jfi!Pvz z{FNn#9U`z9hqc%+71)RXT~?1Nm?B?u!u? zx=<}(BMfYA56&OZHV-a}1v(@rBx(4N4262CvGe@Fp}LQlCDz%yRC7!B%W!bW0H%8P zZZ|%ew~RX1a%Z;i6q+nP_)yy7ep~V2!1H~Z>ss-9oN3Ub+O?5#ON<)I}N87 z*G~<&mR`8DxA}=bChG3IKk~)h<3&a5`??xdU;QEP)^Am9LEY0^j-ibulV4?=?oq#> z?^Din=ic6tIo)A8T6m*8H+@_1^;d@BPqxgQ-*4{u`iLQaWMKc5hIad zq-3bF-oE~;@vXzljh?)bwb{cdj|COk{!dSgH+TJ*?!7uTv2KxmlF8U|Gs_-m$!vY( z;c`XL&pxW0US>SJI(d7rHz_UqIpgJad#B<`m({#;JbA{nJFv7r&^&P8Cx@ESlmmah zx+&|y8!SvOS{``HeloW_^~fk2atd^8eD<>gir)6DcCHhqw&lFHg3U2r`q0v)9{*-| zKREQ|tjT|SJn5o;cWQs@nO)P9qp$TY`XKvW$9N(C&hwAYq;wkgeMDGXl4PJL7m(K@A&#PCQf@3SLY~%)P8M@vri59X( z6qaI+;k1mJ)v-mKj?)%0x&nqNP{xpJlwl3U0G6kixd&)98np)0fH{oCl_C!iqZrF@ zcrE-NV{|&)qBu;?;L^uq+66G(0x=lej5uFr0bjKSZpR$1K2^Pq1TgBJ=kl yS#W!x99s8w_cn-Y1?WmiB26Z%j4l&vBoJzHXs`K@Afr%OS&r9G`T5ojHtIipDF4d< literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/13.png b/scripts/generated_apriltags/1.0x1.0cm/13.png new file mode 100644 index 0000000000000000000000000000000000000000..47356521967d1374dffa9b3a92201fcd98679328 GIT binary patch literal 531 zcmeAS@N?(olHy`uVBq!ia0vp^WgyJK1|(&drwalp%M#a!lHmNblJdl&R0iL~l7gK4 zlAO#Wg|gJ5;>`R!V?9ef6CH(&l9GaAD}DW3xB|WWqI7+^3HLI9hHw^mL>4nJ=qZCR zW5rT?pf!vQo-U3d6?5L+b>w0)6ku>v{{3Hnz5fJ`i#Jy8;#NKz(^e|`z4ktT(Y*a~ z4W=!ANU;E53?18G>wp;XlQTE>}dY^BgIRvJ_TJzkzCt>@!5_RSs zf4tmB5SK~og6D1iTQaS__Wq_`EdBx+x8m);+j-yS?Y^vAwY)M1%};P&%6^Uc==u3J pcIT`MuG>{FC5;gx5Z(RD#CIAh@H>TVYXC+NgQu&X%Q~loCIF9~*QEde literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/14.ai b/scripts/generated_apriltags/1.0x1.0cm/14.ai new file mode 100644 index 0000000000000000000000000000000000000000..684b8a32de436da3418e01fc8902d34e5dc7ef85 GIT binary patch literal 1636 zcmZWqdr(wW7#AILykMf!*h41HPJ^-%dmp=dcb5!U9vcj=>n>%3pmW*1EQ`y%%iROB z!dQWVW0+E8;485Zn$W47Xd<#QN+*dP6Eoms4jH2ql7EzLZknC54_MfH|G0C0=R4o` z`@Zx2zMH^hnUZOhhDbPd8T5TZ(0~F`c@?p7BOr@+xCB6E@{o5*c0h8xU2p>i8JIz8 zDj|qAY^V=AV3I@#)P4i<`J(bF!3u#cu*~+rXDpD10?-5ij)zc?MWDq?g0xifHiu{j zY;aeWtQG-+Ehuc32~iO|5Rkdmh(bp2n;HD(g0e6=bYudUH)byjh+Tj_X%^g4t!zd4 zVE!n6fnam+8IlJ@q|nRgRcZ~Z(Xy<8F)Ub>p_-sXkvBIm1Wp@8Q9>&k9}O=K-VRkW zUl8rE63{^gCWjN{4@i>}6(visN;Uzf;fAunR}-G2{T;(qLsV?G*GKGZd~1*J&2TE1 z{;2hZ^qhFbh7TKWdy{%S*HYeixH;yWF~u{v>(B8**8A^G?=Sz=H1yqkbF{@i*e?6J zQu|NGA1|2lsczq!*wcDxY-`%|%CiGCzFXbXo1ebV9XMX^yfARp*WG%RKCnN%`q-tv zyhqIBAIAHe&uvW{9WY;+{Ge+r_3%__omhMVndF@P-6HRdQzkFs&bO(!V$F5m0AbX1 zUcNK6Y2~C2qgbVlx1S&_CT{quv* zJ@InXPs`0Mtq*P(DYTjzT4t7U>$d%{U3w_f)mX0>I=^Q00l02=oV#g$&rQOcq0Wd|#ISG^qti!h4I`H9wYMfdp?eC>8(J<95(!>5zQTM{=AhCdlE~f;Yj&e|^RvA>9CqX5IE_Vt^R?lv! zB-yScNKTe)wN@mR@KY{Z1t{mO+fikM9+0Kqy+abS@R&)=N?|C5rPQ>Z)-ig9O``Pc zDQdkkjGRXnDhLYD8iJaA0ISuiwV(pbVl=80et;OlXo^KjpN~<7NIS})Mg?7HXNbUP zibi8-z8y`e(e#*)Q51`=asj5+Msm>`A~;hF6%m8d>muxEnhEm`R!V?9ef6CH(&l9GaAD}DW3xB|WWqI7+^3HLI9hHw^mL>4nJ=qZCR zW5rT?pf!wro-U3d6?5L+am->i6ku@F{_#IPtZsq?Bk}$)dZtAYLzwvli_?f^%Zk*DpIa4+uGh)mC zelh#K@Oq*zngo*4uRp$bsn5SJzAwC{s}tE*aF=`CSn*n~XZ{`C_5Am$cRcm&)D*_B z32I<}{rVqUzFzo@!_mjIk4FFNI)B~=hjaGL+Q-;CS)W1XAO{C9x)?lN{an^LB{Ts5 DebnG+ literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/15.ai b/scripts/generated_apriltags/1.0x1.0cm/15.ai new file mode 100644 index 0000000000000000000000000000000000000000..380bc3b9c13d477d04b67c4ff7ad7e4c4a9d3be7 GIT binary patch literal 1604 zcmZuxeM}o=7{|b}?PzqwO>pY=PUUOZ-bb%T+YPkPcB_Lx+8RF!j-z+9h4zkJ59~;~ z0FfOjyZisGkL4y(l?A1I{|Lr-^>HNoRwLx=mK~(>*D=@f(BMl zT8i-khYYm|4rWo1fjVM$U9|F96g!>@-Caq$~>T>Ks77Nyd(e(I%2rZ&2k>W z1(?`hNp#wH88~2AqZv}+12VuZK1iV~_AQTnEuc1m4w;$&;*Ho#JY*N8k6U@a*djS$ zK8QbzU&(VGwoDAbhy;8otx8QZw1#1HlrHI13u^)sg}3>EF6wO%1QDJ{*VCn7 ze*Ex(mv(R6wtcMQ!D7*A?yml5c9&wxtPlJ;S>yQR@=w7D@vYE+Be*!MKj{lyox8F$ zJHDlK;QC|1rw`r;WnT!NIPmS(OPBX8Eu5Jjxib;|t*WGWreD27uw(I}CaxNQd`JlIW|Mr}*P@ca3#DLv>Z7AI;9_+onA^+vfv|e&}?Z&>!F;?Mu zVDA*@i;Fjgww!DF;UYI$sasH&wB`Jsm9a^c{ipMh`;K>d3Yc!8o2q%+5^AG2QbcivFxykLn*UJEFVx)-QG`R4oQd!94Q?szrxd*w`_FYB*=^!-1F=nF6ISje3FEHsP+5bIV<3X#^L zcGILTE|17aAj|=3;}}v%Y${fCgk%vP6$1%Y^tYlMyxahda3>2$I4TK4k2I8WAyQHb zh}4(@qKS-$j%q$Cb$pM9^Fu*IG-ZV{gLB^kHH4zab0HIw!OP+7%Ay1cDp|SNE6QF^ zJ!n;{bSnA<;FjfPzaGbZ%fD2jzssAru`u(Gimpk?HGk0=-ln8?a~ zvc!74ycAjYHcuN5YXxwtD555#Rfb!H1`$M>9NBArnU&;7SxJi4V7a-bDl_&U-`@5x literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/15.png b/scripts/generated_apriltags/1.0x1.0cm/15.png new file mode 100644 index 0000000000000000000000000000000000000000..b9dc22ce2b3d3f42866f1da336bf18c17f67b85c GIT binary patch literal 539 zcmeAS@N?(olHy`uVBq!ia0vp^WgyJK1|(&drwalp%M#a!lHmNblJdl&R0iL~l7gK4 zlAO#Wg|gJ5;>`R!V?9ef6CH(&l9GaAD}DW3xB|WWqI7+^3HLI9hHw^mL>4nJ=qZCR zW5rT?pf!vgo-U3d6?5L+ar9y_6mh-Sx8uL=an%#@F2UQ+&E#gCZeQ6JSN;C4;`7)0 z?Hi0+dWFoqZfGPfMq>P$_wQiT>oe0otUmecbrP}+lG?02qVvmcXWuZrb8Pwj`mE~m zje&>UIHgr{rffiEl->=ixm0y=c7`|>NwD(kxks1Z|LAx3lV8QDqd3f1VqrS-!?#rX z=7LGxSey)XnB0H=hLaoi|9f!uz3%%7u^4WFy3V#vyYBQi>!-N<_UdEw$CtTfv77$- v?L-S3s71#nKbl?l`TNNj99GoLs$&RUr_AtNDeVz3q8L0~{an^LB{Ts5_vGM{ literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/16.ai b/scripts/generated_apriltags/1.0x1.0cm/16.ai new file mode 100644 index 0000000000000000000000000000000000000000..e98e6fa6b5011eb9de276bbaf74658f3bc31e561 GIT binary patch literal 1608 zcmZuxdrTBZ7)K41tfXimMp|3P7%EV_ecax%ODP8LINo~R5vnDi%kA88cfn04R zE$OwjHjx->+MubPCC0S1jfPffgHn=WsMJTAh(th5OF(1V7;SCUwlnvDgX`=coB7T6 z&HTQ{@5{7VtZP{VPi2074Sd#1v480THOh&swaQ3iKmDmo;Mw>mz@2Uf@lFAaT;XT^-H|4iw+!15((#NL4kwV~J~Qa>#Kso0U*? zqej3+7+5`C96z9~UYryQbZQDDVfc^^yf8^CRO%iR;H=X&o1 zpGclwZ3ze$>(A)ruJ*yZ!Os4X(v{CmJ6*Zg#(wLY_~zTe;V=4Lw?BK~>ylsZ)$eSo zr9Y{fdbjY&fvLgWSFdbXcRO`#OnkiK=lh!`Cayd@I^N|uE*4G){pXJ?veml(xc7$R zR`E+y|K^M=4&=W))K!p}GFp;$@W-F3a`?$h3y$TP%hmVNx)T$ImS0XPK0SUa{ksb% zn?LTo^vih3o{qD}Z}I<+@9m{m%bW*9hI+Eit-H2G%N!pa{;_c_*8fV12GHVxGz zdW`8xvSA4g{WS^wZEG`vAh7r^Q+ahYfAqm=%3S_*&GPG}$0v3PAFLXFG&+`)uJ{+M z8mLMAu&3Qg314+|u3nLTeMFTGZEmbD8hZ6=dG{DfId*eoR|4!)ey=TC=IFeU_;$)O z*;#j#rJsJe=&9t&$%>>G8+V>h-_&yWaOc(|1+aT>Wq;si;L-m3*~14ecD|p|llZ(Z zsbj^;oR2a)4t;;a-oATuU)$1*;JEb4Id#jy%gf%{I^awG$7+0d(s5?ngFV}b2qG1W z$RSi-QgWU%MkNwz3xd^xx+q3e66uUtEfHx%+ogyFvl^UL4{a{OMzm7~EN+*W#wR+8 zyO5|U4kU_<14&m#pAPFjEO)xXqxi5WLb|eJso~yl#X2IdM`K}Qf}zd0f03qxtST9` zcs1npG=MgvK{D{Upb?=KUp`GY&m0*v-JMO*HeFL%omdXBUM-3XG|0~FIJ1!(ux03a zkEUA4D9N(qa||aiMpk6=IFS=xV8k^HvnD%+oL`47iUBN7F*6GYCX>+wTwn%cai+)u zY7}D`BhJZOj1fgVF6LnpgH=8s6aEL2@N}4K$1)_Xb1{Yya4F|uMpK+GBMR|cI3^ww z`8bSaq?p){E_=LC4~=|>X9vW&0<>MzNRi1nqsvs620}#+%{CvBbreb~%kdJGnOVHi GO8p1Nfcu01 literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/16.png b/scripts/generated_apriltags/1.0x1.0cm/16.png new file mode 100644 index 0000000000000000000000000000000000000000..a3c40b44a4d0b11db4821db0460076d4ca7aa57a GIT binary patch literal 534 zcmeAS@N?(olHy`uVBq!ia0vp^WgyJK1|(&drwalp%M#a!lHmNblJdl&R0iL~l7gK4 zlAO#Wg|gJ5;>`R!V?9ef6CH(&l9GaAD}DW3xB|WWqI7+^3HLI9hHw^mL>4nJ=qZCR zW5rT?pf!xmo-U3d6?5L+am-^@6ku=^{`g=2vAT*h=j^v@cSRq{I-4qHcrEI+>$&sW z`47k)a^sX%&6%Y^iduSa?bF`(dfnG||F0jx;v7zC)A?WK9Qbu0 zn05IY7hg2TB0K4hd&6AsFQpHvkM5p<%dT0=-`17CD?ZMCPus4w6T`(2Kdf-StF1TZ s`FbDhmVMT|ZaDuKcDJg&S^bjt;(Lu>e@^VH1;!DBr>mdKI;Vst05$^K0RR91 literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/17.ai b/scripts/generated_apriltags/1.0x1.0cm/17.ai new file mode 100644 index 0000000000000000000000000000000000000000..9c966840f84c135fdabb6ef98395ea3f9e7e12cd GIT binary patch literal 1598 zcmZuxdrVVT7wWaWy^X1smP!ReEo3vnZeH5k2ekLH+k+Ma z1MY#7#hFEEGF@=a$b<#WCInr!u+?n7_A;!tXy+Q~u$iM-N zMnVwX*pN+euu38XnwSB(t+KXGa6wQM-qGHz)o!;7p-Irw4#}WA?Iu*v09S4k08p$}SWS z=8xX55Zr#gSPG*TDfBU{TBFr#b$UI=aEo?MloJ#vx-A5_xVK3ZCA6clYie@vaWb3b zg6M@lKqnYj{Q(p|AguwE6pP@J+yV|G1Z9B_65*o*U03R&R8Gmv-$ZNs@ebkA6(hJa z*tus@>9fxjoNfPWW_^$QmZ59a(nr6ro)23)cCY#A$n5=@S@FTpXQR6+>!P3hV&huv z-D5v?f0qwiSIzF3ijLl`Kixguabujl-+XQQh3<_bcY0p>`IY`51_V~LCKjH6^;Feikb>12Hws$MBaOPs}Gs<-uP2>&Um0ew`A)Z2UjXDbB`Av z;L5v%(PjC&jLn&u!3tyKtpQb!m@%|t<*6mnWv?l2KeFP9Z_c%3e7So0`>mkg7O6PP zD;}PFx2sK|+^)Xx^}UC7Xi9T_*wi1%J-WGi?DI3%pNj0AX?lm+*Y`)xZ=G$wv!U(?^?%r4&&?dTs^&QxMpg9b=$95nAC87yUt&#-@E(RrJ<~be|pSEN;6le zBR4+{lnvLPyqJ5_+57s$t;s`rMN5lw%ar->z(&J#cJIqlHE#Vxof0C56%`j+OKXzC zh&2PoYJf9|VQnP(VO~L45XmMn(O_PA^O_;eCS*jqctE2jNojYip0o?GhSET+!!!{0 zV)E&@+T$W8%lz&Tl0r;P4kR$t@r_75`1NEgbV@L!8Fej`WT33zVM9QI0e>ynsZn!k zZ6WYM*bp+1WN_|CEy-RLLE2@>-RMFhNcC!Pdq6Gksz;ek+<+`&*ZU>Wf(MAwVqhpn zPibhDE@D_lzn)^(QPetB3c0W>cnAv6T7sHeK(Etjbif1VFdAhFFCZo{nqp8+7Ge}z zgoeaoj6>R9gmJq6!B{jC7TVDig@)8ZjH2|Yl#4KpF3p!>b?ICfDjj1rX&6nhDX~GB x_Xh+yHt4JT%>v35ARUr~i;RaDX%jsXh!r_D(L#`yVJxjQqv43Wypl>Q@gL73`Huhq literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/17.png b/scripts/generated_apriltags/1.0x1.0cm/17.png new file mode 100644 index 0000000000000000000000000000000000000000..a076e9cb74df3dd09c40b8befe80c717bfdcafa6 GIT binary patch literal 538 zcmeAS@N?(olHy`uVBq!ia0vp^WgyJK1|(&drwalp%M#a!lHmNblJdl&R0iL~l7gK4 zlAO#Wg|gJ5;>`R!V?9ef6CH(&l9GaAD}DW3xB|WWqI7+^3HLI9hHw^mL>4nJ=qZCR zW5rT?pf!x`o-U3d6?5L+aqMDNWNS!kH~QV4lJ2sk#Vx%;W?nZm5>Xjhzr*(IYJGPn?fBC2mj8!4u_}7?YdKfh z`KgZ=c$T!{bkiX>+3##e-Vj zsM+wfWb-v+0%krkSoX+>V>(c>sX+n5V39BhBd9|nOHfQU3`?Wu^Z|u>?jJYjcfRkO z-}m@^>MDbAJDaDa)aNI`xgLrI43MpEYR3*h*BowufG!n~;E|nxt`eNk3pi|04T_2= zDA|c2ALU?_B?L4<1NuOv)eUV3pnH){+3adEiO?<{O4bT14BjN?v=M* z*|}@W*6hpv`-?dp_TLNlrzLE>?mz>_H!hm{ZwKZd9O-SIoSf^;7+Y{R*X;dstmDeP z+0T;$Bi{>;1bkgf6Mr}ZANQtr96Zr8HTUo0y`{6`j^}dbD|<38UM*~K-yQsDUN(Q- znHV@2-|Vj%Hy?htqqX>f>l5$KSCfyqFK*~;(tLKOl|QRpXLz)vY3DPeZA-J6+j>Ut zOqHMToxWiDpyIOdkZ7EJr%ONc(~Z9lB&5x4{`J$YA6`}&vG^DFC8l3bz4fds?Tu@3fs&&y zC!M_1p3&)gyz->=`Tm;ene~-$^h1^Mqi~9Q{oI*7-W1iLr=J|m9=R5u)jxATdpr)N zrid*n;fXUhH@s)a2KJGo!^zX#7p$r4#g2nx9gJCkVqR9Ca=%5&YtHCTUpx5a%I^Y1y7>hGS7LXzs%WycX)fmI) zV3b0oUrvD#|#}jF_FUv4E@2fF}(c@CC!8E$q`*>X}7mmSGY>h9kiLqlDJ}Ne( x2%-ln!AWlsn;^~=psQt>6q!sh+9Ww-5G-`R!V?9ef6CH(&l9GaAD}DW3xB|WWqI7+^3HLI9hHw^mL>4nJ=qZCR zW5rT?pf!x0o-U3d6?5L+am->i6ku@F{_#IP?8IGx(+hgH1(%w0%-Y%0E?a!=r^55s z>*Wu~9dhH8R?V5RArOgS8(wd4?8axIx~-f3nr=jvK~lSQk9OSFs=HOU^s4TC`KxB; zbweX@aZ9fd8e`l0F8`Zz|NVHfnn!(aeQPI%0*ICskKc8bONqa!_221ihTTfp^17;1 zUhl4a-*fs17Dq#!cKbie5C8Jh>VGp6lCU@jWZSE6yB|%BeZ41P{hNe2YcZV+R4&Is1Tx?c6`^oZtD* z`F`Ky_i1cq%X(I;qcq2d!Lc(G3mBj{wo|#efG*ov0|8wiBGIF`0c{iA&Y z3d$~GXo$IBQDg-8kOBRY)v+Bq5y%d2H~G+Wb|fMQbYZ|IA_P?#2xLpq_Dazu$!?&J zoT`e`4iVUf-I^_!3iKgB7gl2m`H_D?`3>+F4ROG!WTXwx0jKI_Qz(!k)s{g;F< zMiRE}EEATMHD0@t0>`aa*7Vks($#_sk7<5qnCgGwA0jW zt-cm;FF*6`o4YpN9d+F94Gv9X@}0XkOxE=T*G%2Cx85+knEDZVA$RCM$!iC8 zXeN^0eP+#usRzy;IqXf_o9bgV0Vgk zERZ;H>Vn7oavY5Plwp@vwt=flT9S(2xRI)-#}-<-KdOQUGt>N!`MFoh zx^-EFZzb8vUwd-s*_tYMGibMc<^Sc+;QL=M>ifB9?cG%a*M~~(C(0{ZD~S*yRf|X> zRAN$g9hV)INvJglwhfd=F`|@6cg*UE$RpY^c`TUK;jDt_f;?kg(_18$7Yie#tx_yE-2b&$OT>FL7B(grx&ZeuQdE#pETS5Z zf;^G~?BKOVt!^EtM5xA_L(|nWM_NU7XHv9HRa~`BEQ#1#HLeQa5S>*xvym6DW$1ZH zk~NSkjb z#M!ZoF(x*oijoJap|Nk5>LAV)pi32n6q$@Px=^lAK&Z%}`Q}BUib82+If17%noU*< F^&c#t0>%IU literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/19.png b/scripts/generated_apriltags/1.0x1.0cm/19.png new file mode 100644 index 0000000000000000000000000000000000000000..707cb6a9cdbaf1957cf3ff3e7f0b4cf1e6a1e6ec GIT binary patch literal 530 zcmeAS@N?(olHy`uVBq!ia0vp^WgyJK1|(&drwalp%M#a!lHmNblJdl&R0iL~l7gK4 zlAO#Wg|gJ5;>`R!V?9ef6CH(&l9GaAD}DW3xB|WWqI7+^3HLI9hHw^mL>4nJ=qZCR zW5rT?pf!y3o-U3d6?5L+am+emAi!`i^uz!CYyLVU1Uf|Sj4d@kVNxvFa%@iVz7s!x zeq+4hd_yB~aZ9g|nHPewBKvEz;jwv>KU_TZ?fMZnPHEMgDH{S0p)#&~T350C>g^5M zyI=oopS^AM?vHby$0sJCn*}vJ>-V}J@AMVV%$|22r-9#MKVH7KMxy=C`c72$K+Sly zum4F^*Sms z8&kJrB+C#tl}JL-B@5Yr5@iO|@gbHdN`Ro40Xj3H4rL_E9Cjk{efzQ1c6WbV-rqgX z`+WU=kKAgult875kPqGh1E&cHC?Gf*i47Y7`O0fe93V}s#QFpeAg!#23ji7!*uch( z1joCup)SqABJdJm;s#`0wWE=9NT#*LD7lo z!TF>3n>m-4H3}gVkwP!6Q!*-zO0Ci8X?@129`ytjif#)4eZt$o^8z|S?3&&j{5#dn zN{;tP4FD!Huy}o_en47$s3~U7DY!UXMnDod)=z{Eo_x20f0J6)@gP7PI{SCqiLKKG z-0kM83(Zr@+h6wgP0y&0ydQh)@`q1s$z9p%__Lac9zONl@Xrsv9Glp3`-Pg-AN|`o zSbC+Oj{UZO+JB?t*dO(+aAxK8k7umkb=4kM^a`<^*U5{RP@@D%_y*8Mae;~a7m(#M*Yh|E<&0D~aEB;ya@EdLcLDL4g>Bl?ax(S6f(w4tY z|I|p&fbRZq`(jgzGjb_M``6ezxp|kqm+jq4n@TPQmyZrcYxBh%dG64-vS;DasU4oS zL)Lw7FKWy`w(Ez`(F2t=O_z_~`E+90GjBx-nWf>+&fb04y=VKWt|f-DYsImzZ!T*2 zN=aOp+_}5tOl@nlxNveO(?9A7M_yb%llS?vRc=iAx!l0PI$uSdP2PYk} zw5FKSDJV)&l$Kmb=t;@hD4H{(z#;E6sI}b)vXnAG$>X`R!V?9ef6CH(&l9GaAD}DW3xB|WWqI7+^3HLI9hHw^mL>4nJ=qZCR zW5rT?pf!x`o-U3d6?5L+am->i6ku>X{pSDvH8uyQbqeL~ewCx&l&05wVMg&ecFXhs z?HH;#q*ZgKYzRE$#))8*uKkzTv+=pgo-LdHnr=jugR9=UNBiE}Ew?v3UbeeX7@-#~ z4`v*H+ZA5ES$p@>!pnQ#tNUQs1XUfo{H}1^-RJB6)YTQF|6KPw6}y2~zD<4PslD?f zd-~12PPNlJdxgxrZfGPfZoy#mRX0DHqWWv)^GckmRy@A9;Kt9t4-Z+Ct7CUp{MQ{P z%;(-ef*dq(@0HFin>*!xViFe1fzJ7M@eA|PeVS}7hvk}p@xgTe~DWM4fi$UDQ literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/20.ai b/scripts/generated_apriltags/1.0x1.0cm/20.ai new file mode 100644 index 0000000000000000000000000000000000000000..72c03580310ea587bbdfeb3a6b665571d22c1f5e GIT binary patch literal 1618 zcmZWpZA@EL7+&Jga90-+fo@F4smh?)?EPqOKLE>T3(Z!kbQ%pG~e3tJ*8b$ z5{T27&9EPoK^O}&l{q1@AE071NcLl!bICF_o6)$y;!IqEMmHNe%{-?cP}_Td+??mW z?|Gm1<9SOQ_15QTjgBap{Rzx`PSAh?QfDtwSqaF4N4z{B>o~{>Qa2zSTsQ9n3^Hg2 zW;4NyZfs~wbFfMx1lqU(`ASn~FYkiDkT_a`u)zsA$OByhaBvXvvItmwBuHlu=k|!* zK%e|8OD-o5K?@37Z$(u2AOxhX4^gO1e(RE78)!?TLuV&|d1Ll6kJu&Xlg+$O^2;ui z59W{J+j+N#tCfN%B86VYsL|^5I##bYF{X@D8>$IP6y4?nri6EmC`#x=W7qWJ;NPig z9^l1p*aPS!1FJ_s`2*4_prX|CF3HW~G<;CzxjrIzYGL$B?*%H)5{(gW4$i*AU%p}n zcRx8cP~BLpBIXDGj_y4R@05=!k1Cd}<-y1kx9I-B(u2^gwcUnKV{0~l<&URFzJ2}v znZAG5h1#lnukEl_wSQv{#9l4-2X5cHYrEn6a^TSL?;&A&aWxh>wn`6gukM@t;jeZ3 zIpzJShcg{y{%@^n>Cb~yPq7_pDh%hYjWs+L`B|Y}RJ|}#xTVb1xGTTl>f*r1wb$O; z6E%mvd!*l9aq{?r`s~1T!^D~0HI5ZoRaIEM6fS#r_3_BIh(eiL5Vp^AirlE%8dcoR z34Z^^WUlJsla9IZqH6D8NPWT55)MDR^L-HBw$461^s6$!-`sU_D0*a+`s#3ujeWG| zz{J(H0_Ta9oVOG$lP|rzx|PtMA3EWBX8QW}5!LfWV@&n<$nxA|sUu;sb9utu)tJ{h z)qW|jJ*VU5nsI7}b`JdVnGm@ynubR9&8w(!&DM&brQ?+DQDqUa&KAwgT5S2PAGTkT z#+y8Aw&S&L3xk&{p8_|&*spkT`0Ee;DSWszJNC!Ll|#7$ceD={oK=CN=eJ-vBzu<> zM!eCu`8usOC6@SrV5|kSr7*0QTGSWzHk;a(wwaJs}>|3Xsi`gqw^P8)*}}BoJ?Me9QSDC&PGIX@)fsB_)<7 GEAbzkPWk=- literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/20.png b/scripts/generated_apriltags/1.0x1.0cm/20.png new file mode 100644 index 0000000000000000000000000000000000000000..771c1a06a849a97e469747b52d82e7726bf35707 GIT binary patch literal 536 zcmeAS@N?(olHy`uVBq!ia0vp^WgyJK1|(&drwalp%M#a!lHmNblJdl&R0iL~l7gK4 zlAO#Wg|gJ5;>`R!V?9ef6CH(&l9GaAD}DW3xB|WWqI7+^3HLI9hHw^mL>4nJ=qZCR zW5rT?pf!xGo-U3d6?5L+ar9$06ks?g@$`SZnLmTFPTK5M)k^Mbclzk8-}}98;^&Xo z*&pyFE^g@+GV{8jfyB7CTs|WEt|e!@)y;piP-KwQTHO`9H-shI!kFB)x23Gy3%94U z5F6c`%@T?Ja1zIeV-dy}>ih^pF@g*6BRGX%%$zQ63F8kSQ8VJ)hImdtptPR*M7EXyDyu;}5`Z=WRPhK2vIul!OVKqAywfAP zfj)XFOO6@=fm-~m*@~$M0R-ssCQP9u`Y(pYp~}NsKDasO4o;oj*7y9x@W@XSirKl(N3KjCX$ekQ{_H$Z)_rAR zerCzmpSjq*JWzjm%j^-A^w+`3qY4{R7aA^2Vw0ZV?y5d3e*N^ubxOycH08Me>)zYJ zcZxdtw^c5@cvtzj*Y-?7#!^O`Wy|3P)g8m~gV)A){sK>)oN#uu532gasiKz$j!m3N zm#?i~yvP5X7rOn(Vo7K_$kM-=a%%UTHq}_`9*f1-e{LA|oIR2K=;0qPGlv4NLFZTQgbb&L=`j zx{$~z2_%wC0!drOo{k7VqIbH&Q$!--zSILso@4$M9_iA>!fSq?7#hH!XfGxw< zdnD0JW{J{Vz(Q6JH4MiXSdP`_LoN@(JXIXIfGoHu2pBB|R~FFgbQ&FSffbCwnIa2_ zF^qv49M)P4avYX)0%j!TO~efUgBkH`T5HEZ(k^Q;2=%y>37AHgd>^Mz=E6ce&k}q& zZIT@Wjd8IdnfG`FIXv@gJZ%Eb6`-pni4>X4GrC-KNg!P0@Nn}XUPj@xGOP|#xw)2o GR_Z^~efv!S literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/3.png b/scripts/generated_apriltags/1.0x1.0cm/3.png new file mode 100644 index 0000000000000000000000000000000000000000..2fc87e80a3a36b04187a90470d50f28f16fcdd5d GIT binary patch literal 534 zcmeAS@N?(olHy`uVBq!ia0vp^WgyJK1|(&drwalp%M#a!lHmNblJdl&R0iL~l7gK4 zlAO#Wg|gJ5;>`R!V?9ef6CH(&l9GaAD}DW3xB|WWqI7+^3HLI9hHw^mL>4nJ=qZCR zW5rT?pf!xmo-U3d6?5L+am->-6mYm`_~q~V9KH#z1qV9+?b)6$V7GOp*zq~V`%e7) z^_qD@`-Z?nZk*DpIa81r=U=fW_t>gG4A;ILFN`J!SABZ-quFQq?`*M6ot~GpxTROf z%n+E)2%*7=xnk`E-61PDXBikQ>ubIIn?q!@=z~JIA&JCTO(+AXc_x^Ene)s#% z_xm2dugqbwJ;iEtRN1jh;OHre1q_fo15`~7pj&%F5YY7^5`(fA&<@cH!+^sEEuglR zf|8pU8j~DsvV?#(YCx~GcLtyf0YmI)ji3f65)lNt7~l{Qf{Fz6h0FI|f2~U@@vRuXymLhOZg|6N z@vgp(s~?RT@>a#jz(m2s`uV}lhiv_Msx{m9J=$8ypERDSnI1d5 zJUDHpc4SYz@qLf%s`}_mZi%73rbula>;EfDb?3#y?!u+lvp>!8=1#z)YsV)~wkw96 z#g=9Ns^lLk3JM%ELpe8!KHs#yt>Ecj!w`m_C6Sg^lubkNj#D_;cz=cBA9k>C~=-u*NsNL z-nNw8cI0}g>iCJXl^M6xFTbhHzSf=dOwX4;XZ+TCWKNT1yz`^_;EpYi|I)N+$xr9| zA9=|#f1-W2JYerA-Jdsj_2Iu;3um|QJ*{q7As-`SAMa6I0MUk$ChHg>A&TfA5NsXj zNMJ-4@mW|@4-rFRKuj!HRNbOp=z0@2qFo|j@qi?aI#EvAg+xDTAkknNNaipRI;QlP z!0G4w?l6`?R7ovZTzJ~Mv2sZCL@sPnGITwjSR^Z;Vzr1uK^X=8onWI@BWQG0z=u#M zTuswm3%@k7;$2124n=l%yRZO~y+Upe=oDSA;L65tz?RYL{jy{s%cH_l%`qI$Xjvm` z;EWvqBx5XRnDSLg+;qzFIxrB`J1BeL5G8xesS;njOm+lI)No`h!r3E_tW_ pRfuZ^=oVQfO(qMBZj?MSh&DO8&B91jP_(Qpr`1zsWmdb5`VVxb>=yt4 literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/4.png b/scripts/generated_apriltags/1.0x1.0cm/4.png new file mode 100644 index 0000000000000000000000000000000000000000..78c9ca169579f73f23fd4ec18bbb2817dab58a1b GIT binary patch literal 535 zcmeAS@N?(olHy`uVBq!ia0vp^WgyJK1|(&drwalp%M#a!lHmNblJdl&R0iL~l7gK4 zlAO#Wg|gJ5;>`R!V?9ef6CH(&l9GaAD}DW3xB|WWqI7+^3HLI9hHw^mL>4nJ=qZCR zW5rT?pf!vwo-U3d6?5L+bFVdQ&MBb@0NJ$Cp#T5? literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/5.ai b/scripts/generated_apriltags/1.0x1.0cm/5.ai new file mode 100644 index 0000000000000000000000000000000000000000..f52b0ea71cfa581e436816dad0132b8a02e66907 GIT binary patch literal 1612 zcmZuxdu&rx7wBKqR6O&_y`#1kf=d0lW4X%Ag-EuP9IyhdhQ=L=XbvE zoZt8OePWBjxQUd?G4a5U;KLz|1O(t54OnF*z<2HsFaX!n0`2EK0JqQ{CI~2KU4qGWD{tC0rE6pp#_2AS)f2$47Yh{myh)T zW#*LUoHj-Pb?{k(5mI5o0>I6UkV19luh0C=z@9^g%ntzZM(lY8vP;v)txS*$@lF^I z;t${7#<+ZRH5Z0267ZwcQkh&SS16SlO0#HZhdF_P!rOvCllIoJEC+Waa?MQ+I?iUZ zmSH`D7myhSMxP(X4{)O&CdI%wITwS%2nswyH)7%Lc-M9I3V6Eaag%7Twy5O5yUow< z{>VPPw0HWM=$6J4j~>5mWA3lGTO&SLS{GOuJMyu5>VC`Yzn5-TvE0KuN5&@n`>Zdn zy>;@>k!#|+$)2aK5JQsN( zBUJpj`P0H;QS0e(&++l0m}76@p0{3TS@mT3C-sSgqHnl$wY{U-nMlVk8+!&0FaKa| z^B3>kIOQq9wMUzDXYZ7?{k-Hsv}nzyM0xD5nXxF%`KQ0?>p$?su0KFk7f5{9&yu1w zqq>p7_7#;^J4$M!FiT>E>+=cd*D)Cuq8vrFm~_lCzu3V$d- zK8e9j>z*wgk_+wo4)NW~qO)pCClD2rOY3f)dF{fKwcGGnw5_`3bkmfz%P`n_z7=v@ z*K{w^TO{!&R>f*(wUA%rl<9ikKkmy_cx+5h3lA$eq5Mn{5q9nLEV*Xy7W?xX<;TPCOZk(Tl^{0ZL|>ZbAsRJ z0I$oW8mW9E@Crg8Sb^h>b4OB+_ee0@!gH>W6G|f2E8ubi2km?nW;SyJv`k&^<5&Y4 zCuN2TilCH)j8u~f0*`R!V?9ef6CH(&l9GaAD}DW3xB|WWqI7+^3HLI9hHw^mL>4nJ=qZCR zW5rT?pf!xmo-U3d6?5L+b>w0)6ku>v{`dd%N9HD%ja64moswuf!ouZApxtae)Y(d9R5ZLP)1AFn>YIs1EF z61q81)vtEMe!O~rjsf4!-DVpD54mwltL9AEfXev#WBKm;KU;0L>`vN-;ufeGS--ss zzx)3Ux3jm4xF7pf=Gqag#()hzephtsx#ewmW@X}Dede_QlZzMrPT wSe&YwbLErQ#huShXCOihIb2%yn%Xix`>Fj!FKmYrFpd~JUHx3vIVCg!0GV{z$N&HU literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/6.ai b/scripts/generated_apriltags/1.0x1.0cm/6.ai new file mode 100644 index 0000000000000000000000000000000000000000..dd303e9fa7359ce91d7e56a3ad7623d70997623f GIT binary patch literal 1603 zcmZuye@q)y9LHtz$2mh|rj-rj!`MKh^xeDu;JPh^($bBLv91s>DGaXdwG`VsydJe; z*dmz_b+b*j8MdJt3r3wHi8>t17{i&3ICWqM&Sf)mL$e4o#`q(SFnzB-piuArxV+E% zzW4one|)~DvRiG1NTZ`vA6^9?oT3n*f#_%H zf)5yyyOQXx;$`5(Zml*?)__l1|wr!wyMWD!GYr2Lco|fZ59L(uMjz!o*c5B z%4P*G_~a&lk_>GA0FEC(TL34;%DY7mPr?Yv63+#xNY9BkFAAU2in71&QoCNhxhZ$U zMblGLLFIhbev{&($tO>YG<3aoaGmSX1CRBBxwCB7Q03+qirYuTcQ1V1f955}F{`Vdv51M)c46idGSp4wQKBr+xVaTJ9pJakF*7T9=dU-@AVrf`d3Nt zt?zHn9d^K9%>UMozO9}da{Mqo)HrTBbgM2b)bwJL&BHkk$|Jrc#tuD~o9?N5B2v@2UmaE)9ebo>=k39)RXICK z#$u(`bK#y6bEi_l1a?e^b z5$_0sIYE62BLYb_#iEReDpH+dMqp8Xi^72xGd6;54j|kqX+2LQlyM;uQU*xGm;sW8 zOu0@-KB08D#qSAWJ;WvDz$(LS--;zfyr*J8X~94XZe3ZFK*3f{ZV8BT!0!TKt;VR) zJp-C#xh1q7!r;P|MwEPN3fd*n)9S`bNI%u$@d6j;ehz0gc>}hLU+)(ME14n%*7Xd{ z7-%hGQ4zy3hNo#ZpQiKGY2+dj@1i4^)DF>fhF=V2*wI=&9x6-E(ySqq3q#{cw%ksaVTWioEjC%= x`~hByPx~r=8;^4ZphFZ%k;x>3<$_lP@gm2^T1e(3Ii6O;=`R!V?9ef6CH(&l9GaAD}DW3xB|WWqI7+^3HLI9hHw^mL>4nJ=qZCR zW5rT?pf!w5o-U3d6?5L+4a_=hAi{8P#g~8KVR```qKm@f&i}d1G41>0=Kufz literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/7.ai b/scripts/generated_apriltags/1.0x1.0cm/7.ai new file mode 100644 index 0000000000000000000000000000000000000000..9eaf03c444fb2d8273e350078e60c0f04b33dbef GIT binary patch literal 1583 zcmZuyeQXnD7-y0y-j*;FGB?4!1Ufo&y^prH>oSS$y1K#0N=N+2Xz+S>?TX!9bN6<3 zEMp2qvN+J`A|ErdB?L&OEF&UOLfFvZNTw!gV7loNQ6WQ#Ca^hRiSOHwt+w3#ae065 z^S;mT`S?AjcR8FTw9!oHKbio2pA$5ofa2Xjl$Qf?Yja2fWCaiTpyCIli}yif+?@HGbMA%L-c2_-JZ!@OCnr zHInRy4S-HCa0Y@Xen2{dC@Bs}P(%rbp+QyR8;QsVXWri=U!-#1oY9D*ga0o3>YLL` z+r#6POJBdSxN~EPyJfCy>*)Q}zAf#`h=Mlnr9u-ehA+(RnhX|RJ^RP4_7_h7v~r|- zRcpbE+k3~x?s%r|ts1!`ZvDp7Xn=&U|=f?d*>oYX=-HJ*Q4h z{4=-z#@?q!+g3~*wtF7e)gO=k5*6Q@8NOG&S7@KE+Vht--Fn23x9FrW@?^(v=l2gB zd(1yh+}Ts&smjv5;ragC*REWD!hSg0cXONZv+it0mtWi?Mb^KyJMZ}A<+|G5cPCZ* z8huO6(dH%B*k2=AQ=@sekF-9t#Q09XIMy&!HC&Xp?~wl-Ar}7$2K$;^w|3@sog=!3 zbGL!*mt6-I6?}UA(dFHS6`!m;C|sNzdijf}8r2>?3Ei#%>$5$tEXEt9W0Aly`^&I^oe~ULf!Y)*Dlly3VJN7;V89ESOh&8G{0wM-Fr<}{WaIpnQBnQH z1nE*0F)X02Pdyb9eZb2LucFK*Za|i?>jR4Hz!SsZC}SvwrA#zOmogl~K234!C~94C z3b}|X`3MToW`df30Bf#J?3WyasHo`Q>t1y;UnqkoYL0?(xB>n?Qo$y=$ literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/7.png b/scripts/generated_apriltags/1.0x1.0cm/7.png new file mode 100644 index 0000000000000000000000000000000000000000..2c8221da7c3b12f7cf7a588f66899004d99e3a06 GIT binary patch literal 536 zcmeAS@N?(olHy`uVBq!ia0vp^WgyJK1|(&drwalp%M#a!lHmNblJdl&R0iL~l7gK4 zlAO#Wg|gJ5;>`R!V?9ef6CH(&l9GaAD}DW3xB|WWqI7+^3HLI9hHw^mL>4nJ=qZCR zW5rT?pf!xGo-U3d6?5L+am->i6k&DL{_!t7tWO|C;NRtyw{G*8+}b0#<=egAuIJ8g zmv69c=@l~bx}lM{7>Q9e|6jt|(&wH(E}#7M`iL8+v}(?j4S|PH8CO1r{dw}Yq;^B` z{JOc;sov#D$VMO;`s!EMpD(iou1$FReTpzvqkx9L&y0S3Hn+0=!~N~^|6db(i``b! zcdMFa3*RdWW%O6a;v}#md#gDO-k$3+O}S$K6uT*^IahY9t++H-)c+YC1wZ!Je6OC3 q-N9ErE_qRWKD_{km%m;8!p+~O@%Kb~^mkw^F?hQAxvXhVkb7TrLO>RCGG`ZgKvr`+bO8n#n1D`4 zK*53y*(e92D9At=G$8j?H8w-D4Dv(A61QAhD|0dgst{1k$ug7#pvFgntZm{fHh~A4 z@Lx$Z*FqWWM_~;{L!VZvIIE?;XZcut#aWqNa^_dm^bzL_$%MTOw)Bo z+U`9)y5rRYUDFL?Y%kw7;jWqN4{SZO=s((*)Z%*ie%pNJlLtRu`MpeCIQwqzOzFLW z*~OO0`yc*MG@mS-fBU5;md^Zp?TfCNVZL6twQRo7cJ*w=FGp_nPaT;kEQoV_Hh6Jv zzQ(~YWlfMf90>A+CMPenEGbno$@m#CL!(BG-mLz?BSm7ZKqzp zd2-9q5^w552jSC`cu>29y9O4Aoez~^tDO22lR_vHlc#G5)EJm>3}Udd+lOa0DmPq7pJ;Tw*Mk@3;P zJwF`pTzqC%k}0?7;mvOjkP9=ZB`JH^x`4R+kL$aH2H{l7p)TuR{QGTjcRjBi#!?9P zEi8&)lX2r!j5Z>a;Ba7UKd6sjSS#WASW^@hNMvaAG+0yOnuf??Ju)KA9H7zAM2$aI zQ_O`}Q!yacWekXCGZH$a{*dU&a+}44q!Cn?2?-7Y{%V=^bb_Yf6$cZB7mY|iO0IedZ)dy(QYNZ-j!74_h zO5q0x5sapkXhHlRqgX9!(K<{^qtdU(@;AV=8^oYdGuHW1npj_@8ts>Lc5Hqu7lzt^ zvpU9(rnFIZvc%c!Pzo;oTAK%=S^?4|inz&msgY%ZRRqB%2REEc<|H{-R+>?(iL9)W HDkJe9P-6$a literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/8.png b/scripts/generated_apriltags/1.0x1.0cm/8.png new file mode 100644 index 0000000000000000000000000000000000000000..5551e8c04069592abef4afb9903e026a6c346268 GIT binary patch literal 538 zcmeAS@N?(olHy`uVBq!ia0vp^WgyJK1|(&drwalp%M#a!lHmNblJdl&R0iL~l7gK4 zlAO#Wg|gJ5;>`R!V?9ef6CH(&l9GaAD}DW3xB|WWqI7+^3HLI9hHw^mL>4nJ=qZCR zW5rT?pf!x`o-U3d6?5L+4P3=!D8S&TeDD9Mk4%S@`4@Qxe_g-EB#qZ!5L*e%b; z+cQ*iNUP>d*${ZhjT6E68uj;pMDqE`AC^yjyS}qm$js}8M&jZYRK|*@u^(4my}Wky z?iY8Lryqaz{N~nb^Npwm!HwQp8(wp1UFpB%^);`3`P8r)2(+TlI=trA_xn}u?bvP2 z`t9{m7;w`+TpLg_WvqXHTw zI$ScANtkT1IYwfGY$8U3Y}05!BStqIVApLgdz9L{zv+5s+Esh=MNo)d#;>peT$E6&V2LjoAwvVi%xK=5ZdrLa?HE zFn{DfhqF0Y9bbt&LiEyVIipZ1lq!{m)+|~Tp`4&V(QO`}37pPwxp=gq@X_$(;O$U0 zvpJVtECE!Ifzjba@dMK6L`gAlR^G0^4?Rh*%>{)G-1YCZ z;E%4l*MRSEZ2ZusJ+ni}P5&ewPNE1UNuJxU|xQC88-IY1= z=$E}i-o3RCEVp-m_4s|bv;C6y@4(f!0D||iEni*}*5!I45>z3!+TED&3 zyLN2Qyz<|kM!B!1y1TjsvvZ?-l%n?a#+^+@8ns3zKjBf{qpDNAJb`F@>Ki#I(kAN)leoznYa~T$;TGHcmW~c7~Jmyr(TVJ2Ls~)abgnW%0QMe-(O9+*tSL z&(p^a?AITi9c+5%$Cvt>u`+^X3~Iz*bKD0Cg(9RIf3IMy5EO+lES+Gx%nJdu?P^$Gy=q}8gd;N27wtsZgbc?NGX1?sIXv2J!)!^7l15>72Qr=bUG}+%g8ly#p|F%6y2V5k}RLwlJkN+nIKI9 zZ>z8(jf9_a+lqmOwU(jG25&%?{_7pQ%Yf&L%#cn)S_K(OO{LLlT9pFT>mgj997e8E z;ED+dC1fxcpi}zM1NW-It>z1>E`W5;(x#Q5Dx$V literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.0x1.0cm/9.png b/scripts/generated_apriltags/1.0x1.0cm/9.png new file mode 100644 index 0000000000000000000000000000000000000000..bfc32f9b2aa55ed8bed8958910ec8fcf1a86742f GIT binary patch literal 530 zcmeAS@N?(olHy`uVBq!ia0vp^WgyJK1|(&drwalp%M#a!lHmNblJdl&R0iL~l7gK4 zlAO#Wg|gJ5;>`R!V?9ef6CH(&l9GaAD}DW3xB|WWqI7+^3HLI9hHw^mL>4nJ=qZCR zW5rT?pf!y3o-U3d6?5L+am->i6ku@Fe)E6-8XLtE5fPc8cg5Le?c_;1KBsu!iJw2e zF}zXCnX)19kQ=A8DuQuk^RI&v$>&x6WIp|soxHfESIErkhDIVPBkO0_p4+doZ`|Gf zdgss5ck6!UA`Cz^(sujZx^nB(I%#A(kd>U*-G2G@4GbH=cJ@`*?fRTzd4ERgSCy($ z#nCtoIpik$egC7m?%xY9N~hygb^LDE|KH6Y3s2bII)>_LxPz{Q{@0dcKf7smFIIoQ mvV8mha7`O_Nv|8>m-X9!DD%9~UA-0KX$lHbq9j5Hp~k*yU-0UIa?x+v*5w%dH{qsVXs2WE>53z!LZV%dT~62 zKYqU!`ht>0iQpF*{PCP#U`<9*U<@XH-L3)W1P6+53jq`%|9&!WVeZl|S4TeY`}dYU8eMwoi{|R34p&dsynSpRJovKW zc=ybm%TI6Ef29AaBjcypu1EJyE#}*D43x*Z|FOfPulMDD_U!4~p5waUVY}8ayH{{z zEeC%5@?)y+t>!Bx#S`soKXYT^^33x(v^me+X}&$*5}n=f;?UX58NWq)@7J5O!;_MQg|=JJnQ!Zkhf?Pi$k?)QZFLls9R zia+}O&=2wf{3xw|)AP!D~bXiIOGs5UVe#InVJ)jl}wb zV7oy>5+gE6w8pBKh&Gb_VrIdr2v_ApSDCR9?UewF`z59GiICDRByvgvi6qlN(w51m zYoqifnMYfVzQe-XT7|zHT zSdlH|L{2DX#6pHC)TNM%sL)R_faNJ>WdWlg7zE%4D;SG2MHY~g7|R%NSZgsx6tSe& zVJ1@E^;qfuU?x19*4nWQX_vJa!x(WX*I@=B{XWr{&V^&}JX_~0@@aM~V@in)sZuZm u)!59h3wA)9D?qyxg%p|0Gukfu6%Z?OY`BGyq@q|_Sx#Umt+slXjrtEJ`S(Ns literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/1.png b/scripts/generated_apriltags/1.5x1.5cm/1.png new file mode 100644 index 0000000000000000000000000000000000000000..1cfb0b387b09622ecafd2a69a1a423d9e571e791 GIT binary patch literal 746 zcmeAS@N?(olHy`uVBq!ia0vp^8$pCh%9Dc&{GCs z#)_r(Wef~V;hrvzAr*7p-o2Q6*nx-DQQPCcZhV&kZ?V2HD~H&I2h-)h%qc#{eW-5V zU*-p~N^1T`o^;Nz5uB`^^vT6%UdKsc!%9rS-*c-je6|Vhe>C4R|D90`KBegTk4=*e zUbb)FhTP1e*VVZXgz#I0qI-MZ&5dWTC?(G>zIf?~Dse6a8<2l@Q}W@U2g3W)vpfqg zDlGi7=-9&7lag+4x1Yz_-J_|8Ef_H^g9P~NX|lJ) z1h!j=>$;E>w%2s6m*qsr)Erv-{wv>fg0Tp*zW1-@z2~ey#E90h=hZ)k$p`fd?&dGh Q0j4_!Pgg&ebxsLQ0C2W9?*IS* literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/10.ai b/scripts/generated_apriltags/1.5x1.5cm/10.ai new file mode 100644 index 0000000000000000000000000000000000000000..042ee0b2f7215be34d498812309afecfd4d31e55 GIT binary patch literal 1591 zcmZuxZERCj7#^|#xiZ99-7phRLgtENy&vuE$C#6DU2&sxT?+}zDC=!|)^%&|ZEjDo zkT_)`O8Aff&V*pZFHlE@A1vE&nanv!F(QcB4>aNusr#W0!yF31c}_pJ+IsJgoAccF zocDR(^E_{{)nqPXRT`@J)DNKNEX4u_NUnCOq5{wxw+A7hs|6(ZB`=_@f)|DWhXZV& zvXX+LhXm@=Hkc(50d+ip-rV47hi(LPiDgX$t+yiqL7+(hRskU>i-0Ffing~29-rt1 z+T>qZa@!#S%{Z;ejH$o~0(5NvQ>aRQtCQba;7FsxW*0zs6ZSI1>=N{88w^Qd*^TQV z{Bizz=ClAHdgPYnJM`_q4+WpLKoJ0<@X<3~CV@9F40u+Gsmcx_hq zR>2Ntc2lr`g7pgjTB)%9HUHW#Cf$t_M|)O%>wJC6{nRJUK#ukNhbs?8`?WK7CwGs1 zWcmK`mv8R9*XV4%-8WpsoVZk$dtG4jR*1(vktcSpd4+PeT)YvzGnTVJEFZaAklV56 z@CQpR&i+G>mMrOX9myHVn<-sE-4E-w{dld+Q8<2b*#nCP-hE@*dHvO&pYO^?R#~yK zcjzId{our+y4_Wl?U1oNd*~3K-Zt9Ty=rLxPs;Td6oG*k zy5C-|82Tpi_~=o6Y^n8^z5BaLued1fC1bR)u&Vp~CZWu^>l_+>`Mt#rPyU7J4jZG^ zI_2qiq6*Kz&O;MnbCXus+`sPgbN~E0x%mBjxtnM{c|l3_$8VF|u!hm591&Lm!J2_1 zg%MLEg)na&;)Iksj4YV9+`L)nY9kJ!-2z~-I?{AbY?E~$F;5mq444HH5vEcnY@Tp8 zy}{=RVGqP@WW&zFQV(O>knE{i*tBBkYAjbI$v{~zprBtuexD0$Q>zRr%^J{#P%u3=asufx+M6Eoo6 zWnc#We=r?xQ)V2VC7Bqb&CaXl@gT|AtLL-1a15SanQ`i@IF`|+Ch%9Dc&{GCs z#)_r(Wef~Vex5FlAr*7p-aXiR*no%Opv8`V(bq&DEEjEAeMTgK<4}{n@UCl7uUQk{ z=gTw96Edt+Qu9CZq;rOi;AHirPcA<5I!+2>2;RHDjz9ML*4M#*W_&HsKdeH87MMBt zx6_WBT#hT;@bihq`q<3c@0P_6mhZ*sLU??I zZn=5h=EqmeHOuCvPd9y2H_xCGOGu#WfrXj$_M3&T*pBE4n`tgHBvIqW(<}3H3rKZp z@~zn-m(LxZ>xn(eusC$vUy=8b4Ev4}9ewL})%&c`DcF&*p&6Lm7(8A5T-G@yGywn- C4>5ND literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/11.ai b/scripts/generated_apriltags/1.5x1.5cm/11.ai new file mode 100644 index 0000000000000000000000000000000000000000..013d3de028e8ec4c9bd9c9f4c571cab7925f8b1e GIT binary patch literal 1618 zcmZWpeM}Q)7#AW=F4f^?MivwE?hmBZ(fjJb6|ssfQqvUNIH zV))3m4woR}GG;29j>+N{9b_UQV~hm_k&uzN!9HSC7L+-%xT*Va@7oXP+Pgn4@9&=H zeSXi!@5!)PtXZ_)KxBL}1G@VN8c;x~sv+|709o;>R|2G2fPzPH0n#S8Bp+aqfgPAk zgd{t#As^#lRb&X*kOBGPuBsYIgdit;Ebzm^awtFv7{Y)}fKXCpz~Li7mRAc7x9kGh zk-w@UmP-)qLt!meL`CvLKo-{`3fm)JbL3kLDr4x-i3wodn7t|?c47LYUGgb)s)+Kz z{89We$>A2ZD}EG_La%|*bF?v==P1@_h&xrHnxI6{Z9ZTOd*{otf=)DcjV%uT9j#`m zB)ecWpd$>dZV$>IkX8>WibWC?hlJDcK~)lJ34cdV+m4zcDy`|y+{T^3qVt~1qb6{t z_pSBd!=q^_-)}qg&y}oqAHE+*Z`2H0AN>~C>a;G*)&F&W;5*YfW&VfOJqxnWF!<|O z^Zohu&O1Z=u^(R%bEgl>^B1;UpDO$8)52+a{$b^%f$5%;f2_Ds%k|%$THmE?ethV4 z_V#ez^~W0@u#LSr8!qxGf!y5{t;LGhgTi-s?^a zd@yQmcOCrloIAa9^zhb4!kP5*znj`Sob{#oO;2l|{br(sI{xidjrh}K!&6_4y`vlP zCT9(m?_GQBqqlX$$>SyU?QF&H9?>d&owVFPmD=BNwr`@S>F4BylBSt$)Y{FdX)l5O zmu=Pd;np{9Rh7&pU((n<9u1DPKG~!hOu5mfIjx^M-NK}-8wuXjex7=i>2Cw+tNQ}B z*#k$8sP|5Eu5wMN4cC(RH_e4&An(v;litbIWr*&t$F%?p*bP z_tf6f!4-_wNECFb7gtUum#n;dcDCW)dp70NZc8Yi{kwx#HfVIq=bpHGZT#1vKrprsR7NqZm&lAPsS1lEIym_>SW@GXj!1JpG9pC*&}eXC zB!Cr_a3R)J0*JMl0OI+Kh7NClu<*$pZif%aBcw1p5*<=O9a0pI9?gZ0Nrp6|Nrs9F zv}FSHdKBn!SAmyVy-{y?9#li<_2rUe?cy)JqPlbhX;T$Porr`H>*aMgL6smLK$VT$ zfGk7TyA|1jmx|Vs%TP=<#nL>T!|)8ZiQ+RUDpMCj&aX;Nf&#RGpcWsH&2cOToL~{7 zQKj$$Dl6(H@3f}4yN8(Azn6%cB2Xv_Japu$jDX@)Zr K85sq;ti*pGFa%2g literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/11.png b/scripts/generated_apriltags/1.5x1.5cm/11.png new file mode 100644 index 0000000000000000000000000000000000000000..88549aea9e669a061f86d56f616f9662d09266aa GIT binary patch literal 728 zcmeAS@N?(olHy`uVBq!ia0vp^8$pCh%9Dc&{GCs z#)_r(Wef~VuAVNAAr*7p-gV46>>$A4sQu)B{5=~7^~a4qr$hN8#4eukJRJ9Y&gZ5L z^Z)N-sPQ`Tq;rOi;AHirPcA<5I!+23Rw}9aV+!sMW{TBytU*YdJ&#PcF9HAJ+8Toh9j*CbO-2YtbL%?+?23*T6%c;JdBz^z+IZ|!g zwq0f0-FEBA#Doac&ON2GWrMfv_1yT@&OeEiaQS}YY~S)v-;U4q#24-;K6CN8x9cq5 zZ0td5_o(SS#O*d_YAEYC%Z} zg9Ilv)WE_GCHu54QpGKwBat57V3)KeAx?Kaw2?>Nv|@TkJn^8Cnn<4o1}@(eZ)rQObaZ^Ao~a_F`3<&NOOw!$yw7LF8cYpuR_?cGldZXYYT7`eObHvW99!-@(<3kSzO__!xNKMN@rJprpFKV~+S|jG4t5!>v(KD9 zu<6pW@O&hP&$e5gWFBRPExb&Rs zK;CuJV*Tx*s;>v9>K=^!{m;Iq`Mu}ATG+TeJdlIt-(RG?A1PEs1)-|aN^3YxR2iXe zpjbUeiG1km(O9^cVAyifwpf&aVmFVy9ua%oc5q0oG$=I% zz=g5bSH!ZdD@RIEYTnMUR!MaF9Yh7OUS4Mtu=9?CB(sqlh-K({w9S8L>v0;gK wdyo{G^fq?~BDn%=ttiqW(<#PQ3QZyi6*)B0e3+MTD6O1I0~xuzqT0;-2fQcq^#A|> literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/12.png b/scripts/generated_apriltags/1.5x1.5cm/12.png new file mode 100644 index 0000000000000000000000000000000000000000..2a3512d8c0388a6599529710e0ca93461c205154 GIT binary patch literal 741 zcmeAS@N?(olHy`uVBq!ia0vp^8$pCh%9Dc&{GCs z#)_r(Wef~VL7py-Ar*7p-gV4lG7w;KlzI9;-t2meve-kjP<{uEthb$=*Y8!^E4+Dr zIi4X-HR+R!&%BP4!iJSfYW_!_bk495oUD!^xaa;#{-|up2}SBwWfQTg&uypkn{pwbzRRouk^A~a+fy|&V*^bS9rSb^1W>{if-MO zf4!)+gH*km%XP(djP4y*#S)BS8|C2<*csm#d0sZJ12 z1kp_lxr7T=Nkl*$F`&0^bJhwj1oER>OAwXV5sw6*i2^nrAwd>_mTW27UcUEr!QR_7;b`G2q94LM*0QAw@22qso4w9>h$sxz_Y?ce6 z7u5h5V_^09ar}U``f*asf=hA>B#Z!(1-_07b`N!46b~?|mfQEJrj~(F_27)@!kK^i zGE*;?C`xLxhK6^A%hZS6+bf-lmYFOMcjPh6?eOT`eT~6?%SI`1_$nT&kPn_o!mG!I(XuEHjWsp}FzOi(M^` zR_5K=y>}veG9~qu-ks-dZJ(aw+0{dyO9NHUt*#Hh_5N(}Uy8K-d0{7|8!?=JX@l|I zvzCtj>bFx4{q}fj=Z)_bbzdL&AnQh3`~3L+XAS6&#?7W+TFAC=$#T2VqA%*LUX|YYbbt4S9mmQ;4;SRr>TC550Ek#Gj=K z_rDJ*P50WGC(63aq1LHG^C#DBI+bGD)Uvl_;p&l0c=Wwq()qD6#WWD9DXFstvT*a zyN-%Is&RUY&mF)@h={2I>kBu00~QbQ9*+ek1Vfu}(;`U*Ia_&D@0XC@=LCDyD!oec zJg7maK2Si@b&E$TN%rPav`v=W4KA#K#9Q@l4{-9XS8--zFJQ~a^FB#5llhTjE?^l} z$EYC(^I4A7Aj2iZhGgF7 v7v#vCxBD6eoGUCh%9Dc&{GCs z#)_r(Wef~V0iG_7Ar*7p-aVMhY$(9ucroCg=4HkosU4fPuUy5za(Cgz{Rh|X{a)vA z<9hvn#)_#OCxs0wmDK!?Jn5WaBRE+->644kJWRoP%j+`MmcCm4!{GPP*fwu$O0ep4 z@wvA#^|G{N6Si|`KrYb=}U7?upU!vw0&rb`S^^gpI)P>y0k+iQuX^r1V%#kC-5ZX?{FS_VPZ} z#7v|4Bsp~l(2L#8o1kg8p}lt>u&^^fCilDN z-20u!cSh~7*z`E_|)cj;Zi|1n9DQOra?DwZ^_>peBJ1Ob&qXChTP%vy0NFD|tm~lwCL; z!XMwijdu%Nk>tlWGWgYNa}AI+nhlJO)h}Ar;GEz<@o5TRquv%#l<*1?*Tm$I?RYjT zc+rdM0E{uP3qBk_pzS`K6dUi7+&l?GK{C(PQ~r}*bbc#-1y&Y6dPB9laOmr0bhzzv z@!D_8ZNIOjj*XYzpU%8IJfdDjZ{X9}z}mFz*KRzVIvAcA9Lqm1-T$$@`cI#t4?Y-q zXJ#gIv#CWGIXwNT-x!`4_a3;|e`3$>)=RT@!>`Z$bN*g)_v$m3um90ia-e7M*1yyL zoq+T&!P%xBYibIq9_US=O2ecstNsp?3*)|=6iU9`_Xe(tF}i(^t&7i1I` zTp8;Mr2f?7_K#VMLk17>e19mV@h{buzk9L*EADJuMU^-zN^gZboW4t{^0XD*IYa&3 zIR)?c+-!aPxlBvLTg_@~oy>GRt)h$<1IJIVbXJ#kXj*}{rJOyyCT~fXX2-zf=7H?K zlf#vt*lxdFH7chE0#$1s47Uv`UAn?k7qryy*b>$1otAHQOws;d^+%QlRa2d*efb-< zZn=@}+oMvK^mZPZKud*`qfdm+zIpQkJKEmvqO@NgI^H|_DzB;d=;xxD@`LX^`Al_7 zPG{lZ&}?WV?OpZV$A~y$m5j+GQg+e`dc7$wok+V7Y!|4BV?;f%j+xgOkxaaeED+3V zb6!=n)q;&^7Y872qXa<^eI*@8RF(u1r6z%-TjN(p=RtG~&|3w!g2fWiTP2nt)uZE%>vgB@b zVVNX)HMl*XmUHdHnT?%*EhEPZl4v6%X1#5bmeCp+9W+Ce)~q#dWX$UrW}PO1oL}ZW z6a%21V&)!TG#GRS-~n?O#F-)w5aSqR3`rQnlJYLJV^|#ye=)`;VUU5iybJ9hgIO%X z7{-{4=?r+{Ec9i}Y%&)ugU8%rJ5!P!go*JYnG<}x9QhSEg=QY-3ec63M2bwt99<@Q aBoHZbWacS|lTjqCP;1grYIX5;JM}+a0|v1G literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/14.png b/scripts/generated_apriltags/1.5x1.5cm/14.png new file mode 100644 index 0000000000000000000000000000000000000000..07729f678d9322368c49d058e78b2f2da8bd4138 GIT binary patch literal 752 zcmeAS@N?(olHy`uVBq!ia0vp^8$pCh%9Dc&{GCs z#)_r(Wef~Vv7RoDAr*7p-VO9R;vnF9v2(|N-R;~G0xGK4dY|hks1&_iU-_-{oTWa; z?;o$l8+N<+%uN>C^(i|Ofr}xi`Pb%6dkF7+wX5yL3UKOkolZxM~@$_nv-ni;PKIifk~umSrx6$fZ3k#rEFj_TVa6 z++-V0BDe)zbn(YV$~1^0SwgbFm=ZRZ>6Ey+>DD&+0n8k$wj_sU&K%qPL$IW@p zd(Qj3AJ40@TWwE6jh;~r`~pr5GZ1hEI7o*QQP)KT(T_hgL zpZKpsZl7S5!o(v-KD}0BfF>hv;B+Sal3fGI2?>U3Es|7KO+!J_Kmt=#V0*YAlZCa(U2&y4(7n$>^v#$4oh#mQF# zlM_=LJ}dvYasQo(-32{+uSmlq^2~H(a_8v3_QAonySGQ%+Zt96?rBYLnY=Xj@QnJe z$h`-z=iE7FzESdf-Lq@l&pi44AvnDKrSQd5>mAZS=H(NwyqovPVHip4lt&K~x;JaO z(RJ^tuM0Kl`x?F*%o;kY`s49-ymlr#Jx8weI&!&(RK9vxyJCt1C~oGkY`25T_3ELoLqkFu z(RQHN4$zRms5;^uv7jL;i8#nsLa?B{1=X;YN@B#i1OQ2!BsDzMPs)Q-MJXUvVhTvR zG2uF<_n6Gt=Y8%F5kpi@4k9tq^Q}Zb)O#Wpm=p|aA$^M_8K~<79P~@r?`s0PbsCdK zzX>#B91NAQY+(LKBgx)khPBI*yVXTxkbEoX_JAhAwTomn{sOU#KJSx6D;*(fYnhhQ z8aW;0VX2na8cR5SBgbtlP9hhUk%!>`)HB@t0!D*DX8;~Bk3o_tx`3F#Ag3keSd4Kz zPn5j`GjU|pEX9ofgYhKCi|ruCk<=~5IL=5)xdhW0QvLGAR7}f}F}CC`uTQaq9G?^$ ymIa?5$Ch%9Dc&{GCs z#)_r(Wef~V&YmugAr*7p-o2R1uP`T(;`7HwQ3{F?hQAxvX)_q7$$>|F@_JGnne>_h9*W~SprIwjbXNB=kx)EdhQ=L=Xbty zzTfxweQ8#cIh#?cskELM@a+J_00<;|EtQ)K=>3ge0nkPs@gAuf&{n=$@BtPZl!85b zC_!`*LoVWiSrQRY1q|r25__%SKtLDV7Wh%24e>|->L6g{5fWq(XvmhLZ8f~pEmi|< z=v0;*HUWVO?ABz)R0KZ)bWt6qU$tN1P6*=^8tPEcD^V|c*l^dk;x&);cQw2 zu^QC?Cd9z(_TcydZT8@#m;{I96i668Bnx~U<^S|z`-C_G5(}0eZ)=W;*|B4A-Z(z6 z(4Ct2)rSgG?XHVM2WK)S(~{{7!TD>;E>+6Z<*M7#&>h`xSO5I3|48??>)OIcuENwS zr!rB);*E~Y(Cabx@PJG1{OmzzG{+M_$|pFI1j^-b4<~{`@*#Z=F9R6FK+tAQKlQcxnlS_>4~f2 z+->K*TUX7Aw>1BjZHk|DG4aXo=8rA*#+`2NyLLG#PNckwBY)pNdL!@n^wL&)=9cNH zyhQNBPfgh)ZL!aem$Rnffw#s=S{1h5$4`Ddz?Xe;VoTiMxi21iQcjGWJlWYibG+q+ z{Z_gC^?g-2hb_aKGZgxt&&Q_B8lOq>bhdnS*4xqBJfCrY^lsKGH&;F$mb%JMbz8DK z1w|j2n6j6>mM{4dWFOgC@v|u znq|TQ2{Z-4DnMlzBN_?y#hQ|cFv87}&ww=ru4#uh=3^t;!2onMbtM7)P%VIqQ|jktS}Bm+e;kGviUdE9o; zpi=6U>MT%$kk^+(({-yyN=dFxr)aAzIqMx*43W3IP8YEAj<<1ULoZ;oLf2QFc%daY8rP>9tS6I6P?9+c6O0`LG^?P>V~s0aIz7!bKa+g@sW*Ag7M9V=(D8 zA4uli9zhNae4D#Tz_|i+sU(pilVL^|i7p8QiX511KE%r?kXD9O>!`G}f)X?JKegTa AeECh%9Dc&{GCs z#)_r(Wef~Vex5FlAr*7p-gV4lG8AxilqvkLZEb!cP3Oh)5LpJ^7c0YSBC6m2ZQ3wD z-=4unHR+R!&%BP4!iJSfYW_!_bk495oUD!^xaa#y@u=*%Rhs{1{wtone0 zya(bXC6?+<2ZDi^Y3grT=dZRo9q0|-bDBf zrv2WI)Z<&89$I^TGnzopr0Km64p#T5? literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/17.ai b/scripts/generated_apriltags/1.5x1.5cm/17.ai new file mode 100644 index 0000000000000000000000000000000000000000..5fb9313b3e9105a6a349bccbb0ba9460695a8d72 GIT binary patch literal 1591 zcmZuxe@qis9A{<>cfdq(8-!#&wseTH-jBA|);ZSF(gp-+T9IW6;b^brpxiar2dRX} zQk^n2+gKoT)2SoaY=I1n2$RUBF;Qb){ z@B96J-skgG*vytUXr-D^49|k0NrDCxkeXVEty=-PV~>vqq=|!^SMmVT#(8)@U{HV^ zl$H{_=)!@zgbfx+gg_MuAm6EJYT=y_Xrs&W0JJ(F2YH~50yYjpUKW7{FA37o%((>7 z1K8MKS#mmf2zH^gW(z8X4?sXxw4oBpVqa72TLJ15%Ar#mz;)y5Wgb-*EuXaWeyLq{ zqJD7wD1SBY61XxcfHG3(RWnKrt!H%_N~KpPt?Cgcs8Mv9ALygIjiM-_6^*whl7oN8 z*{tG44{Qc>tOASRMg0TP;zgvGd8g#!aT|Up^IRJdIDW1#EM5jVz$b+#oBwH%$F-Zde!Rd%inM#8%{j={lVVv=IV!zWh?|H-tMi< z=&XCad-urH7gz4zpw6GYeRcSipN}nkGu&DF^-_O#_q8AD_I89%oN7?~rWyIrP<`jK zlaF2=D4KNqQ%iY>JvDdX_v@=#dL}oRMhl1j_`J?KxV_-PzQczbg*4lh(F>qAd**1) zzM+Qj*h!do(DwYQUvG^(J%7>Cy7|)L=+R)7Hix-z+%WLfr;oo}$L&1g>q?s#J3IBl zof@}!>Rwe^F!QI(d^y-SePkW@)-d%k_uAkXlXr@(tG=0Cdp%A2mf|$IX*@TbV1G1T zJJauYH{4e|ZOACRYOx6lN2rS|yS18sXxdS7aPZKdYX&lZ*_JJrclq0hgC zB-@sMDJ9ucOprENag`v>&N{&;d#L<*CAqSK>!OP2$3GWa( odBhbU?UIBq8BZ^=LUcCh%9Dc&{GCs z#)_r(Wef~V-kvUwAr*7p-aXjc>>%Lkc)sgT@I2lF?i23hui#=xe7Q3J{+hkt|2f>a zzdoMfoseOrlA8aKC!I5F1ShK}eRA=c*Ktx9L-5|$E6vxU{B66Y-!w0m)WeX+stF?4 zuWRjR`ZilY%;bIyPV=$oUl%-G#9voonXaae@4lS`U5I2tZ*-Z>R*?-275vlj`wB@< z=Zrmtr~86^e~X=z+g7vluqqLO0@I#N01YwF%!usBh)Z;7f zFPZVp?(`<+X97eAtMv9a7Nt84-q!j(62cQ}D0c37eP#dW=e9)0!RM8~*X95 literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/18.ai b/scripts/generated_apriltags/1.5x1.5cm/18.ai new file mode 100644 index 0000000000000000000000000000000000000000..bf81c1ef2c1f3167ce07d5d76c0f3c6a29323511 GIT binary patch literal 1595 zcmZuxdrVVT7&kyRa%V=J6f{mfGh^7mde7;*L|$#_XtW3|Q#MBCrT5Yn+FrOlB^?>l zkurxwH;p9AWQkkM!jx@97;a>lTW~}oe=O0VS(fP3y;LA^k|h}C&glcXcIWn91AXjT6~-?(F^2`9i2=17#)1?8T6$$zr-$Rc0XNXa zUnSAy6fkHcZY@?4iV((twfRX1rug3+|Jy)QQaC6*0U9@rUJ^)jvG7@k5EMg_i{wM& zC;sgM@8wKln0SQb)2NhMWYih7P;Jzt+?q&DNTS4A5Ex_Hm4SdrZiuQT7l%G4s<}%D zxUmPIcm!6jkK_+ntB(}LBDh3eplJlLByfHvJov?Nd+Q{W_5NMbTLX5E^R`R>RLuJG zODn4Pn{qvwbBjgi;9pN)>hC$YPu;~&ZC$Y4na_E3;o;=1`90T5KRL4ZVZXFAR5tc$ z`SlYw?!VWw`Rw_ZySF?TExS8E_2U@g~1*3@LbF;I2?sm)m(&c`=>C%?AWlh^lc#p5{NBD`dcXhekw?hsIQmwbVruHKqPC;Q z4na-Dp^Fuouql8XBu?IF)xXf2_P@F>}}_b<(+yH!045Xh&(6H3%kH5uF5z zHG-xDMu`z0hE@7dHY6sb5`k6PtrCVcR}vxC#Q{VnBx%+ubOGMQN=k+`laa-u1h(5b-0l;x&)W<-)k>pMQw%&9 zw+BmD*1z(k6eYKUVQVFk54i{flC|3T7SPPO+DK*N7KmijyjKiZ==RudDN#X{4yus> z=~V`mt_T_mAuLoR1s9ft76t;OVc^OEI;~c%1ub9&L!?slfItF65D~)u7lQ^pDRU}j zOw)x%?f+nUQuq{IqmgW!6b$OpeW6ZkfK}O r>Fp9otpMu~McQP#!B|_MMFi0%N7q>pa}ti06{*xZrl6o|x0U%9SFrMe literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/18.png b/scripts/generated_apriltags/1.5x1.5cm/18.png new file mode 100644 index 0000000000000000000000000000000000000000..fcd17f26a732ab50baa8a382e6ab85e400492472 GIT binary patch literal 741 zcmeAS@N?(olHy`uVBq!ia0vp^8$pCh%9Dc&{GCs z#)_r(Wef~VL7py-Ar*7p-gV479PLt7Nwbwe*Zj8lGZz$9xpk1Fkeja|8Y|Ey{~=6lr$@|yQlQRr6Z(h%)hfK z`FhZUB~K3t+g+E{!yhK79(_Db_VS!xJQGVVIFV%K`Ay2-cqYcKW9;sUjll?aR69^& zs4uq6CtA;^+Fre|lTh@*jJdZ>{;2-u@b$Kd*1{9o2K+XHJc z;JjIIF)9r!n`WEY*fJjwUFIXAO{Q}i3`7@YN@9mg6r->>x9Jwapy%`fg?jEEH|KZ0 zbH3mA_!(iwmf(8^&n%anM+W=YXZ6{iR2i7?mLO|d1*c1H z0bJ}U`qbM&@FmKC(4@zuoS;Ny5U zZIavqTLB$oV0F1s{D8E&QBq1JQE^H*3@=nA!9)0ty+5!+{tW1Q?;O+CHhwaBRm!gZ z&a4m3(Qy95UCllBhu#QQIdw(;t7GGh`|jNOe!jCO9G>|&@A3`zGqq1$ zpLqU*=?$UGFni|H+co^r?yu(dWW#+w{W)88q~70C_x5$wb9hAa9bebye=n_VY2PszbjYx*@)3SikPUTdjTU#@XJD-)!2hO9|4aGtQ|~7ll`@ z?e>gKR6hK2_i*ZSj&Hv%6FoM%R+}2|y#BykMWA5xkGqO{d-7Kq%yn6>aYLz<`dWVP zXy2Mk?_7~>mfmbV6S{VCxPQ-u`HBOX)9G3I6Sg$1;oZwAn(m@5lOuw46<_N2f`-wv zRi{J!h7;_CM^=Ys3eKFmr+4#jO#~C#9}pf|L&v<1fMfg_G#mBcz@NlRE7@piG$U`c~Z>LE)k$cPjLK%;g^Xnd@rqzAE@l0dA; zBoKFH+;vplTuv_%MMPKCNNT9}JCKgB_joLHLNH_r>R+g+Ab+O-+uaIuyP80! z(O@>PkAhYR+r3*!(zAGEP}G(Jg0!oOvqMC3NW9hVYz9q&_%zCF>;+^QdETYSrFfL& zmu_V!hNFx$Pj6v(hTBZ>c@&jbkU-9-O3efXXqKQB7vM}LqX{&FMT|z7!VAc8jHZkz zC(AL4=h3)Wfte|!@|76(KbRR!hvjxOh10qmqbLrQas_5ICEcZXE}08MC1X6Bgwd2a zAvUNAF1MscMt+T}OG3ECh%9Dc&{GCs z#)_r(Wef~VUY;(FAr*7p-aXiR$U(r>G0pX#W;OGJ6AXWkg)%Cfu6iXLA9uYpmY?JI z@2|`UcDwk@>o_TFSgEAuf8(48>Peq41;5|<>YTkc*8b?(nEZD}G1!z~)dv*Z zrfa?I=2y}7*lyADpDpzWT7_iJ<7u+H#n_jBs*Br+&v8gPz~1PMF7x@pc0_hf^S#a9 z#Q6_w#J$f~@`K()>G@R4k!<0cq=|*|zdshj9(?F71P65an@!2Ln386hCQRJeO_Doz zq#pO|X5M#L6@R25*?Rk*Nd2pH#u?{{*3!SLe!^7Ef_K)8Rluai;OXk;vd$@?2>?jz BGeiIY literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/2.ai b/scripts/generated_apriltags/1.5x1.5cm/2.ai new file mode 100644 index 0000000000000000000000000000000000000000..1fb14c7c4162fec0015d6f480da343bf8a44ce18 GIT binary patch literal 1585 zcmZuxeQXnD7#HH)Jf|TzGZjBxV#N}!q^{6%szrf7is1UOQymUt3;bd(LekkBPb43)HnqrF(F_`Mtt9XY_;Y3$Mt>g zdEV!Fe$VgsXdD)66|FN8n(6c4Xq=z{1!V7bqP7;0?p<9XARBndhvfhu9ehAkfgU+H zL0ugoN&*5O0>FC?kb_M!k-qO)SSy29q)G)Q&JgjiTFBU`j{VOOlLEG!D%L2Y=3@ zX&0pc3<5e+fi)CH{R7e(Mxa8~(@*1H-`)3DxaQwu=N7hXx;(J$0=L+E=FicapV+>8K7MxYIsW7$BSYfx;@OaY z&lS1l{;V8qfZFk<>;Q>O)<)-`Dh2`XZLNJ$YyRpo~i3YvSh(KLu=Wfr}QjkpgFo)&*_;bD6WE{D%R%|7ga<* zK>^xGP)iSB8OFc>KUlKS2r2vkDQlxC3d!{UHi}~r%=tF1AeJ&2Z((B*@OiN&4(*vd z8+D5u1_rH=JU`A@Sc{%2ybr;WA4^kgPH#}*Lt#-#?RHnFS43C=(kaV$$ars&&5~aR ZsUfG9ms{{VY|@5BHA literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/2.png b/scripts/generated_apriltags/1.5x1.5cm/2.png new file mode 100644 index 0000000000000000000000000000000000000000..c26d0a6c27ca14a4c6fe096b054baff34cb7d16e GIT binary patch literal 735 zcmeAS@N?(olHy`uVBq!ia0vp^8$pCh%9Dc&{GCs z#)_r(Wef~VKAtX)Ar*7p-aXiR$U(r>vFC*SQ$>cpc~`$lhqERmez~$;KR$cy*QO2M z>;E(C@ILaSbB2xJWc8#^EGAs=m@Y!tfbRC*=rW(^pooq$&q=Y*Jnv@V ztD?kP)oYulsp;Ve2CSAoo+i6{&X$;pL)CXl3K^h{8?$$AIK9%ltMo$kPEx%X!R7Pp uKM7&F=lsg~nhz{v2*fnpi=S8hW=izetl3~J`~aBP7(8A5T-G@yGywoKjX972 literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/20.ai b/scripts/generated_apriltags/1.5x1.5cm/20.ai new file mode 100644 index 0000000000000000000000000000000000000000..398c1ac4ff028cf7238713b8984bcb01400050c8 GIT binary patch literal 1615 zcmZWpdrVVT7}wzgZfHU>mSxT*hd-)yAotwfzGZHuP=cV;))<&8aOu6!veJvU2Ui?p zXa=}HrVAG5oKq@cnlOV=_JptmGPY^ZRTo(1a4ZpGWHCn6MFh8V`hY^u{p03*=X?CV z$M2I{a?G2VOcf>n@=wtHJ;eYB#ENPvD+|!O>T3l+n>fU|#VSBsxGKR5lvu$Ewr-^a z4^I?zF$QMQg8&;+po@ztss#rE+VC~khw^NQLjq8R1r`n=LGl1Kxl**v$?+~v70^Up zCDCCM5ZHsm=9n=Q!G{2yUxOiJNB*YBKOfj*;4twC5Zna4Bw%!5__S5?|rB5X~X!vd)l$7sRM>vHAilboVxW< z`srV92Cv!HjlY3WTIU9+Mtd3xoiZT7pZySoM( zzuvp9qI2y(ZTsensfGsw!S2>TX>-Tmzcl8wz*+UGtvG?6rHdui6no`tHUgnsAX zV0rtYY1fFrtbX}U$8E*P?$V@G#qV#dmFK0M%gB2(?e+B^v)NrIjg}q$G3U@Lz3aRD z6YpJDJTs!d_ir}&(wYKU-ojwZ7N?%-^iKvH#hpLquR6zmsQ7uha_aa&8L~*J!|6S% zFQ@X~9%u`s*p3yQzc4IIOSnK9;0=1;yRf}Z2MiVG&5HO>DK)SmCV^c&$q zq;U~egt|;RuVP`8DWT~gvOT~al@YQ;24s;^gdx$XF*0D0ii;$oO-8IlJ2=4Lsfn3? zLZ>)~gj8`tLd-ZJ8O~_vF#W@G|YZK7z4B=U6*%!*j6TD}rgaE`ZeWg{N2 zX2`uu^yH9@qR25Qp;7}`M#pHCI;AEZ>NY~SaZ?O9pCnXL5HKnVE*zjyt64Ru1Pd|- zSBf0qiOLwf0G7+3PLIpHR1fOoZJ}Np=MU=jxbRDD8OY!*v{VM6CO!tM#tUYdo=zLD zr-bqTbm}-g27_7jha}GB7NpSTx4HHUxK@C+iXv$;*=lsYr&0u=CWls>7jY5_m6cJd MHI!VQTV$sG2Vva^kN^Mx literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/20.png b/scripts/generated_apriltags/1.5x1.5cm/20.png new file mode 100644 index 0000000000000000000000000000000000000000..b3062566fa5a1ddc94533cf4d776eec6a504b08e GIT binary patch literal 732 zcmeAS@N?(olHy`uVBq!ia0vp^8$pCh%9Dc&{GCs z#)_r(Wef~Vo}Mm_Ar*7p-o4m+$U%VBQEX!UscJ)o^);85X)$mwzV^p9qWb;crVZcg z>lt=<9eL6@!$xqjdeSEspLrc8g$*l})ci38=LhpgWzVe=u}_P?lez;_9;+s>plo#6 zoJaS?-$h<{<9z>fi)+6(K6}v2xwj*={pq3neb+_xa5xN21H=oDr^))q>e+nVt5(p7 z(@U5-rMJI%AottRTX;G#K?2i%x^K3az;c_+iG^JxdhPg@qX)N~FFsXw!Rd%9wor#z zffhgccQ!RwuVL)&363EUZ3qLt&;0iJ=LcP)eYWS-KL%ZI%`*MPLUVvAjlt8^&t;uc GLK6Tz@iKJ) literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/3.ai b/scripts/generated_apriltags/1.5x1.5cm/3.ai new file mode 100644 index 0000000000000000000000000000000000000000..2b954354eefeec93456fc57f50722a150cd2675d GIT binary patch literal 1603 zcmZuyZA=tL7`B0eU5pY-Z7oe5YLLUi?d;y(v4@ld?>IsVdbuOTl=89M4tIygF4-N- zK~P(2)1>`jg8WkxL8XaaB0p-2CkaJkA_%pnMAB$$+CoEUjSWz%X=plgA8>Hp{jr&6 z-kJA#Kc07UopwhdXB3#+&_i%J%y0k#wYilkDFJLvXFCF{O~O(@^#ax@d65oyV&DR$ zr3_MJYA7ZgaHt9fricN1=wNdz@?cODU6u!Ng&Rv40U-)FC5(}#05e@OtlKBae#Hwc zv0Y8|xDf_*#I4;ysGuMQY-Jmvus`;-#lDrGK7kIG9027_*=vZfi_&LZNLM>F4~d8J zC;qFE?3ebdLE;gTSKy6i&T0|O&}0=htm;WlNTB349ay8M%M?W=D~=vbOb*?SXR`_^ zUhD&0jDf=+An^m%5gz1Nz(|-nzzol+nRz7%s z;{MZPZ@8X>xieFxdzO4ZRq1n6OXGc47XR%pX`K6EO7Hn=&&ww}aq7avpVx*!Syq zw>|+wg)0kR&F9U0mOnh*-}`9p$=%ty?0=5olsB0n*86OBWuSZ0*UL-yieGhaG2H%C zy7*d8MyQsb-8|-;+zy7n>B_a`T&NoV_V?=-Gj4X(LeGb%TMEA1(akzPH&p)G+Q`I6rbj>EVh;;Y+6k+W)a)#gq`KEUmV{iE(vAI)Y+# zpgxXKfyA0(O-57|F_10ez?%Hlgu~j(h!N|N07p6{q35ZDk}jk|N&=}ElR(;#ao17F zN0rXL>z8$+hlr$HL}jGyJBWm+_joK^LNKh2v@TXPV5pXGdqBkje=|5@GFpv70q|km zt{1ax+v=84)x7x(>(o@a!$Xvic&c4)0nL)%x04rw18ENBblNPP~sQ|d6KC07!*Yk>ITdT zN!d1HmjA&-Qlj;C9E3@J36x|9O=dDw)}MuB literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/3.png b/scripts/generated_apriltags/1.5x1.5cm/3.png new file mode 100644 index 0000000000000000000000000000000000000000..25bf9838cf4124f45a3ab3af04063a160c511cef GIT binary patch literal 745 zcmeAS@N?(olHy`uVBq!ia0vp^8$pCh%9Dc&{GCs z#)_r(Wef~VVV*9IAr*7p-aVMhY$(9uc(LH0X0YUsVv{Ytp{xo&T8`~A*|+ukJ$;Vf zKflN`_;=2*5uB`^^vT6%UdKsc!%8JJ|07Q@1>b+a!oGH^U#&=dn*E*B9oUp$)dv)m zjV}AJJu`o5gt%^D3_**KOj#FOHYX}*PL#u(Uvq-bZ*8x&_b18X-srMBTP$Y$iK)I} z?J}>R6OZ?hYy}5|^!7Ivske#}Z(Vw#riU|7&~*WW@4BA#k9kWwz43=8ipG0ePxob> zZq0lzu=8bg8Hui(u|ebq|8~3UEX2Fg`rG0k(f{Y#f8I%KEIteU&Nkmpt<2rrhy$4H O7(8A5T-G@yGywpQ@;M~{ literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/4.ai b/scripts/generated_apriltags/1.5x1.5cm/4.ai new file mode 100644 index 0000000..5d19c4c --- /dev/null +++ b/scripts/generated_apriltags/1.5x1.5cm/4.ai @@ -0,0 +1,71 @@ +%PDF-1.4 +%¬Ü «º +1 0 obj +<< /Type /Catalog /Pages 2 0 R >> +endobj +8 0 obj +<< /Font 3 0 R /XObject 7 0 R /ExtGState 4 0 R /Pattern 5 0 R +/Shading 6 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] >> +endobj +11 0 obj +<< /Type /Page /Parent 2 0 R /Resources 8 0 R +/MediaBox [ 0 0 42.5196850394 42.5196850394 ] /Contents 9 0 R +/Annots 10 0 R >> +endobj +9 0 obj +<< /Length 12 0 R /Filter /FlateDecode >> +stream +xœµ”ËNÃ0E÷óþ×NœØYòº|@UF‚Jðû8 |oZgĨjdefŽf:Ý\ï?žwûÛí¥¹º“ n»£xs7ãÊ'tvðÓ˜×Oáäö¾7÷æÕl.¼y<šò(ïÊ÷³|·ó](ÞdÁ§¢®ÜÜ÷óIäAnÌ_܉B¹‹ÛÍ“é“íÆ},*8ãôÂ4Xç‡Ò,XÔ1¬²øÎÆa˜Ü¸( ÚM6Ž}\ûO?­s1۬ÅدBRA¸’د=LP’Á J†çÊÍV ¥‚OÞ/ÆKP§¤×n ¹P¼’a£¼!\–¨/QS +ý¢‡uv„Q‰’ ePr„;®Ì”l(ƒ’ccPY’ãû¥K…hÍ™¡RI{ËÍú…ì‚x-CÚF2lÏ”mA˱1¦L Àe‰ÖÍæ9Ôé"+®ô ”l(ƒ’c£ ü¯Èeq®kCM$¦óŸÈ‚øý± (É ’akFymž•’ (žÏ(¯˜TXChÂÄ~ìä ÜÈs* +endstream +endobj +12 0 obj +404 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /Type /ExtGState /CA 0 /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Type /Pages /Kids [ 11 0 R ] /Count 1 >> +endobj +13 0 obj +<< /Creator (Matplotlib v3.9.4, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.9.4) +/CreationDate (D:20260318172825+08'00') >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001013 00000 n +0000000869 00000 n +0000000890 00000 n +0000000950 00000 n +0000000971 00000 n +0000000992 00000 n +0000000065 00000 n +0000000350 00000 n +0000000849 00000 n +0000000208 00000 n +0000000829 00000 n +0000001073 00000 n +trailer +<< /Size 14 /Root 1 0 R /Info 13 0 R >> +startxref +1230 +%%EOF diff --git a/scripts/generated_apriltags/1.5x1.5cm/4.png b/scripts/generated_apriltags/1.5x1.5cm/4.png new file mode 100644 index 0000000000000000000000000000000000000000..d9f6973bf2aa788b6b2f16c93ae34519a29186cc GIT binary patch literal 738 zcmeAS@N?(olHy`uVBq!ia0vp^8$pCh%9Dc&{GCs z#)_r(Wef~V{+=$5Ar*7p-o2RXWGKSwD6{ncG+)8tNOio%io&c z3?HU+oD?>!R8sRl@}zTyjo@VUq)#qB^DqVL{A({ny}si9@%)?i-8|;RXn~n?e46a# zIaNm=UORef-;3Xc!Z_`KX+XH9{OzXXLysEH7m%vy)@+f>=Qis_?c32wod3XfOK*Q; zv9)2xSDWQ;61^v@-ysktDDLfzF7t`t@`)?+u|Cg|ha(hGbil&Eyns>iYytZw#KUelF{r G5}E*g+&$v} literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/5.ai b/scripts/generated_apriltags/1.5x1.5cm/5.ai new file mode 100644 index 0000000000000000000000000000000000000000..e42bd8033ab20cd7c67b9a667769872bc850624b GIT binary patch literal 1608 zcmZuxZA@EL7-p=S=IU^n5Jg-(XezDM-jCj1T7rZ^se_HID^rG}xXbOK6wAGMd%JZ@ zhO-W`EtAQ>4IRdhWNOByPMM62IB*GJ%7|ZagCMfR5f^`KVd%0do9FZc3iaF{H|II; zd(QKIJg?erwLL@YSwek$9CV!_Xg~p}x{k=r1?0x<4G@rpyuy1WHz4i28~Ol)3>+XY zkAR{J8=B%AY?7z|L)d`aw7$9y3JS=MY>NWQb7cyzK)^--JFh5E7J(6O39_t~cX>oN z;G(CpB$Pn~Y(~#oZHNjCD1a=kM-&R8|HA0M7&znT(1`(H-k7}%5xWR|(gA&vUlve2 zm_K^I6uLZoffPV5Qs`qDy^%I^CL?7qvx|04loJ#vy3Gg75pRnqN@z!8*ZAb%<5)J= zLD8+$0y@gT=JBHV0crE1q*$RKxgZY1r^t}6CjytcKut5S1S8P_ITQ+%-P;_RJ2clNwGKJm`ktg#{QwkM^O z8NTv%(@09=BR@@sw3Wkox28rrho=UQT>4VmGyU%emA_pb8}S8qv=t9BImv!F)p%X9 zk~3TGbWQ40zwOjq7WEzL-t%S;{vm8=J-NSS&FlWgJ((ASeM$MN3nxI^fnRz%sz3QE zZLE}Q*?C;`+KFqIRvhimx~{WK+&}wpa9_Lb_THq>sq?7>GrjBJ6@P(xa%7P27~gXw z`S(xx{v`jZhwpC^>qNv+2?;L*0k75Nu#oLRbkV0Y%>{JXnmM}K(WUAA4sB8XNj zDu-}+amiW65R*u_EikqjIAa)ANwhQOwS=V+YZnU*<~2C49YBJ zxDcx;0mO<-0C88wo{s1~B6o7F$K^w!2wC8c@v(FQd#xZ$Ose z>phZa#iK-H&0#2pqYSi(&Sp%EQAe4wC@M=EM=l`48iE2eOHgwQa7LrS2x`C_Mx#vO z1;iLeQwEfig&1Wrp>eSYGgCIb`4nDO!fB-$)=a3? IMeA+Ef8Qnn>Hq)$ literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/5.png b/scripts/generated_apriltags/1.5x1.5cm/5.png new file mode 100644 index 0000000000000000000000000000000000000000..f7414aeb1cc087323628cdeb5128712704173f1c GIT binary patch literal 735 zcmeAS@N?(olHy`uVBq!ia0vp^8$pCh%9Dc&{GCs z#)_r(Wef~VKAtX)Ar*7p-aVM<>?p$GSpWD>a2)%C#bPG9;j9WO8TUL7+dZH2nKkkL z`+rOyv;-%sCw+49nb&br*sxMb&Hu=g&KWkCf`9M)TJXFwuU6#$naaZH4~2-+1vaWY z_vXiy*W#LYY+YbEz3%#po$yN&o(%l1Q!AoiLE(`YVC+e0#GLaE02&Mg@A@^DcbJi-Tsgl zXp^Us=&}n4IPkM33#KAO5uodWm_kkRZ%qE{Kw}ymCNluSo3NJz%q~Hnwh0ljS#sfc z2!H(kPQmTxYs4si5#mqHDm9Ew%W0rWr(U&d#5uu%;@cuXm+&@(LL%NV#5FxR4^q)L-#OuXfuXc;4W5XLY?U!Qy!uDgA_s-jg3SYldJzG$`Z5WKp`tug84)`0V?ve)T4PB2YiejoV z$&q!qxwp^gE6Is|+kI_h{mAaDPh{Vf7g1Xeh}o^e(`TKzYi{n7D-;Vd#dW<*Ui<2! z>u2?y2lEQ8Y=N^(8q6MfqmQjQ({IQ>ye_bz=W18EPbYQv3>7?XKtInt-?i_Fp;P~S z$(V~{gR_c{w$Ii?_5MS*OY|r9Keem>n};Z^)iAO5{=uU=USrMXvO%}vv+UBfe{S!~ z$!Fn-J2`W|Jj2L}UeJkG3mZi5W8ro>UMzo6u4DOP&ekbzDxR8h_10+Js07)~Z zo=(U;p>leM-yOkPh|9@_6^2{B8Ow*fo{EJ@3x+o0wnd@@6gzno4u~k=cY+p`Qm0gJ z0X~GnkxH5lE*&XF$-9}Nt&-?&c3~BydxhN|;N)E|;>;#*z?Sjr{bI;OCWyjR$wF2O zRSd^eupFx?hg>OyrJK{pMJ2&QL4d0Pmln`!G%5}7fF+EQWyhS9M(zBwt@LFe#CczdL8!&0T!pDLnfGy;OfD?MQ*4zlr^>KnAeR;!l6ZeW ukm8fx?r#@xt^jQlMN(uk#pt?_M+EUA$46QO@e+!sm0{J8lFQ8v7V1Cr((u6m literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/6.png b/scripts/generated_apriltags/1.5x1.5cm/6.png new file mode 100644 index 0000000000000000000000000000000000000000..253745ee7063a31a6bf21cf7e2254f8937dc6dc1 GIT binary patch literal 730 zcmeAS@N?(olHy`uVBq!ia0vp^8$pCh%9Dc&{GCs z#)_r(Wef~V?w&4=Ar*7p-aXiP*g?S6vA^)2=6SXUlP?_jHhDFZfRN3vZx?S>zZY-W z`~NrV13kgX>PeqmeCBnW6gI3>Qu9CZq;rN1reLj~ZN}QtSAlmdKI;Vst0CsFO A_y7O^ literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/7.ai b/scripts/generated_apriltags/1.5x1.5cm/7.ai new file mode 100644 index 0000000000000000000000000000000000000000..2260352f7bf6fbdf92698f934875f6d84724656d GIT binary patch literal 1581 zcmZuxdu&rx9LBL`?1~E8bR?T{i0Qh{t@qIfx3dmQOU*LonhueH18;k8yKc9=&F$H! zh|x(l!-%zsxNHl{7Fmo8q7DHgHHaI|MoAbM2BeaxF+PY7vVRC0Q_tyRE3N1LadW=& zJLmWMzMs-=wbjrX9icq&Jve@rpaBIWXFFjs0dm_*0RfN}4sxR80i>Pt2tmLg0|%(9 zBLqK>4cUwfHpvfxHeo>SXmYj-E(kUzw_8H+nP$jA0qBx|or6%2{Xmbm1lioi@m{|N z7*eOQ(%V&we9qfd7A zo5nuqRusHHd9*z;TJ*yDf|5&rPgh53$BW+F^rd#h{nM%FaNVBUvx|T2ykW9`b$s#Q ziQ1VcbLZFL2fNB0)Z%ZKwy%GC-!^GHdh+I-vALQ}ug0!Ef3EABp%*_NI_L_ApKjbe zII?j6<>DL1{;e9&_Iy?`cfF>}_O0sA-gUlO^y2-ZQ@#>j(d!EV5hX)tR`WL<9#SuA_yaogY=t&Xulg%&)j&StIT}q>csh zHca^nemPS(by6{3@W<8gx%>789VX25R9jMFw03I|NU}#w zkak((J6(wLnYRMG8#p;v2dZr91!S3c-Yfa7cwSUmYZ;0$P+FR$H!>`ve}ZCF6s1yU z1Q(J8H$eefM^H-(81#Cr9=O30Mx#pM1^j7@rYKa>dohYNpc#^lv8Z-gm{IpXm;se3 z+m1yo&%!7}u3xPljgKrlR+k$KL!p_KZO7!;(f9a(GUpWqIWgCpz54`ID?mCV2{#$f eEVAD3mO!G(i2)XboD36Xr5T1Hl*%nlHsU|+8SPX6 literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/7.png b/scripts/generated_apriltags/1.5x1.5cm/7.png new file mode 100644 index 0000000000000000000000000000000000000000..44c2c1e0a22af88321a6f784a2974648e600b47a GIT binary patch literal 735 zcmeAS@N?(olHy`uVBq!ia0vp^8$pCh%9Dc&{GCs z#)_r(Wef~VKAtX)Ar*7p-aVMhY{kC1Kl{{n)j!*_b9$l>5n5p8m~XpT zxO!!_K=5q++xIp%dg8YTrVH$(d%Mo|tvuTreRs=5!=ozLU526o=7;>dX~(zS77N~X z_m;)!d!P9x6X#2?S$j%n%LeD&ZMgAm9vTXdl-21jiEo8=Yhj6cMP4vamo{xPWVY8Ja~D%%cBYz&^R KelF{r5}E)ayENJW literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/8.ai b/scripts/generated_apriltags/1.5x1.5cm/8.ai new file mode 100644 index 0000000000000000000000000000000000000000..6f2185a9864ad758a46c8395e89628f8c4273145 GIT binary patch literal 1609 zcmZuxe@q)y9A7#XmxG$c=opI$Z<MHk5XFh@OmtO z14fyckcb4DxFy1XX|im(b^eIcA)<>e#5p%MjguKz1b@y+*2tU_=X?DDh5GJ~%lo|V z_r1^e$LFiEn=MaJc?_X?^BOq*AwdBML{}ZLV+SCgYHr{GSUd5D`LS(Dll-JpW@R2QF~H8sGA{{0hqnajtYzI^ z!2|U1Q%U5UybP+)vt|pT!Z*o)EUQNpisJv`_`eKPC()r&1Him7dx=NvV)RJ|9}pWQ z4#k7{qxZ{sx0fvvo6w68eGHwaql|ik4r-0es$Dh82?`Y5768VWccCDNXh&h!x>g`V%ZX&e3E`}VP$3%93yFGQZxkA62` z3*Q$JS{FaNpky~kX6|YR-!d&MTpGLjWkvX_oT_`jMqX-Z{o1$vr1Hv?>#xB-dJ0;r z?;O6D$qk+#SUefr={)M=Mt=J-II(cGk+W{8cxddWGmlRGyl<1^{5zn2js0BjGj`kF zKdT1W&E3W(+75)g?CO}N}p&xEOoz}?d-j-3}u`d-gw~QBCeDyE({KR zI%jRm-QRXAOs&gmE0~yAZ#iA?QQz>zk+(0} zx@HuATDyw+XF{L6PrnXNom*46*!|1pw2xm59}H%QLvFR%J{w+tYo;)`zOua6lbtpf zJ~*P>IXtI6V4pp`Y4T8Kmg0)hYqwumr~Y>QerwM^8m6cHhiUZ#X_-eW>DkK}%DF>p z34OS;?+CQ=!*AHUYWBenO=DJC=Hw#;J=)QComZ6T*-}++cKF%t5vB6)eg#%Rykv1b zMC*%d&d^LkBhkLVSQV&FU|1&c)>sx3)<&XV3MsHG!e#lO6e1&%V*!QwC8_hVkWwzh za!LWQBvU}#mWiih!jI{l+~su#kSe0Wav-gt=5Ito!mlS{p^}0ji%|p1q6F0CtlZ!i zWxv-2_GnSz^6@UW(3qr?-VixdNm^6mgO9JR{458WBW`935@}nU&;dS}9rw36-j3 HkA?UTf2{)@ literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/1.5x1.5cm/8.png b/scripts/generated_apriltags/1.5x1.5cm/8.png new file mode 100644 index 0000000000000000000000000000000000000000..963041f3ba631c88dbcf9d32a498a0966b1bba3e GIT binary patch literal 748 zcmeAS@N?(olHy`uVBq!ia0vp^8$pCh%9Dc&{GCs z#)_r(Wef~Vk)AG&Ar*7p-o2Q6*g$~Q(b(g^?qyRJrAN_K%NQIv#lAmfU%U5vo#T!3 z%i|g52^m%@sresy(mBIMaI$*RCl{Z29Vdk`1n<3H$-Z{0pKX`^X8CtUF*wy>(cGtN z?PvM*YiqFgfI> +endobj +8 0 obj +<< /Font 3 0 R /XObject 7 0 R /ExtGState 4 0 R /Pattern 5 0 R +/Shading 6 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] >> +endobj +11 0 obj +<< /Type /Page /Parent 2 0 R /Resources 8 0 R +/MediaBox [ 0 0 42.5196850394 42.5196850394 ] /Contents 9 0 R +/Annots 10 0 R >> +endobj +9 0 obj +<< /Length 12 0 R /Filter /FlateDecode >> +stream +xœµ”ËjÃ0E÷ú +}"Ù²e/û€¬Û.ú!}„ªÐÚ߯܂îU2^2›ÑÁ3>hÆÚÜî¿^wûûíµ½y0¬vGìÁ|X_®Ø¹!Ìã4ø~Ž'«Ï½}´ïvsìóÑ–GyÿPîïro—µ¡|› bDoD}Yùßç‹1OæÎþWÁŸ(”µ1ÑuKÂlûÉucJ!ĈޘFçÃâ´Öu ë² KÃ0û±ù,h7»4ö©$vI¿œ÷iJ±ì†R&L!4‚ $ƒJ†0`C¹ $ƒ” Ï{”Íä–ô¾«É &xÑŸU–[I\(_ɰ* ÜI@RAº’ŸÐ¢¼²YDCÉš§Ø7=&¨³ƒd(ïQ’A%Ca ø'¡¯Vˆ“…v¡Î9È‚ˆ›Íª1Û Ör”†+Ë­«]¯eHG-Ê#Ê6¨ å(ŒR–‡°<‰uÎk”§‹(ÙP%GéÈ+CW!¹P¾’¡4Gymæ@Ɇ*(9 +£”å¡«°4ˆÄ”æôäÎ’ ^P2¤óŒ WºY)É €’áy²lXS¨ÃÄþì̈8Õ +endstream +endobj +12 0 obj +438 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /Type /ExtGState /CA 0 /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Type /Pages /Kids [ 11 0 R ] /Count 1 >> +endobj +13 0 obj +<< /Creator (Matplotlib v3.9.4, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.9.4) +/CreationDate (D:20260318172825+08'00') >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001047 00000 n +0000000903 00000 n +0000000924 00000 n +0000000984 00000 n +0000001005 00000 n +0000001026 00000 n +0000000065 00000 n +0000000350 00000 n +0000000883 00000 n +0000000208 00000 n +0000000863 00000 n +0000001107 00000 n +trailer +<< /Size 14 /Root 1 0 R /Info 13 0 R >> +startxref +1264 +%%EOF diff --git a/scripts/generated_apriltags/1.5x1.5cm/9.png b/scripts/generated_apriltags/1.5x1.5cm/9.png new file mode 100644 index 0000000000000000000000000000000000000000..b7167377c1e5ffadb8259cc61b1f7d945317205d GIT binary patch literal 738 zcmeAS@N?(olHy`uVBq!ia0vp^8$pCh%9Dc&{GCs z#)_r(Wef~V{+=$5Ar*7p-o2Q6*g$~Q(b(g^uCXbL(xm9DWekVS#J)dGU%U7FKZhIF z>-RHOcpZ7tIm1S9vU<`d7oT|@Cxs0wmDKz(1@AA9%UE0bYH7vHzXj7D3K3L~Fr+-^ zrs3I@%PoAO*Z5Rh9hLg5VSCv+Kl+wAe4C>)T@SAfF`5Vt=3M%k-X{ zd0y$};d|$W^>73RR@XkBCc8YsVNbg^zF8^QI$I#HK+@x#Ov=Ery7(8A5T-G@yGywp5_BW6K literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/2.0x2.0cm/1.ai b/scripts/generated_apriltags/2.0x2.0cm/1.ai new file mode 100644 index 0000000000000000000000000000000000000000..c53dd0e731f3d7f7254ffb4a13958c4ccefefc57 GIT binary patch literal 1608 zcmZuxZERCj7&eL$F3e%*5|AyPv5bukdcWGc+kqQhKiFh*H|>PsEP8u;*WKFPTW`;x zn~^vd68wtsJHh=};uMG|!wi0)4ilZ#O+_%m7!rX^Ba|2;k&+pr=iIhiw|ef6oAaFa zocDP@p0`wRx;M~f3sHLbXYj!Zf(8`O{2^k~CP21yM`S=cL@0)}ARq-XC`SQ<3_PHw zhLF_&HsrG|xHT05)-WJjoBbhKf}kq3b;aNoFBG8+EGa+`A(V9$Sn-x1y&YmeQG zpX!?Al_6+DZk=vKMUFv0HgqBib?Lt&{cixiEIM><0GKysugi#CiazO)qgt0Pp?ENV zYJq&WCb>0!6Pyfh~30uBsZ^(fDe1a`15`n~ky> zgdKoRGjJAhrXbeyOJ}+Grene z`EB3rBhQ@_R>r>yY=1a^qOf1{EBoIaYuS2iant$He*dy9J?iFz{_+*e)-Ku|S|0q7 zc=@wR-_4^3iZ5I99Enf0Pq^<)?|eVn;!%Sy)F~xJ!-bMwGtG6e@xAqB53RmB@N4Um zQ0vzwc*cL`+3E)GQROk^g78=4eJ1Yrf$7$9h4P$Y3W(KE(BHV|j?awTwKuQ2+_sHd zk#9XvP*Jd}7ON{_xwvWc}g|@v$|3eja)BheKB??!0#0RdM#*($q^k7LBa3 zeJFJQ^hCjuBg(##J6z{`TTBLneHZVI_q?BCS830?b^6lqfcLk?`-L*4;w*c&;c)Ev zLFqxSFfq{gnpApFwBPe@{8WB%$53tUjis;J_nWW?(iKa~!6+{-xrJdf5;58WV{O2f z!LUlwoiV2+ER9UN*l94Q!8!Gi4m&a;B@xi5U9uV<>nP_ztfm|gD>4VfU72y6(tS$q z$56+)hw{wh+|p0-V*#T0uLQ#b}f%ynvd)Xo^KSnU7ICkH*CU%tj%V zFT}Y2!E9(c%(tT{oYwgmMRBN<3ozE2bC=?|TrLcii}98mjHYZ^u|Zu_!m@6Re6P|Y oqg(;fqiMLvc$|?9YP$xEA{((>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=dY&$hAr*7p-Z|KN$U(r>kzeWmly-(4KQ5V_4`JQlwz=xn?fB~VwG5o= zch@r%%u7&o*&v`4!6B&A!Xf6w!mZfUDsa%LgDBy9m*pd}ee0R(&Rwb6z)g}?Y!>yr z7YhF#KY!EeH?{MQwi53QY-R!d@@e;x)XTFEmYPl7zmse)g3X@r=S*hds!ylx+u4d* z{rrCV|7}e@GJ*@kohSJ_r~CHTT=(67o{MklZps`9j5UwC={L5$-+O!a> +endobj +8 0 obj +<< /Font 3 0 R /XObject 7 0 R /ExtGState 4 0 R /Pattern 5 0 R +/Shading 6 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] >> +endobj +11 0 obj +<< /Type /Page /Parent 2 0 R /Resources 8 0 R +/MediaBox [ 0 0 56.6929133858 56.6929133858 ] /Contents 9 0 R +/Annots 10 0 R >> +endobj +9 0 obj +<< /Length 12 0 R /Filter /FlateDecode >> +stream +xœµ–MjÃ0F÷:…N H–%;Ëþ@Öm=@HBThíõ«¤Äó¤EÁ"ØŒñè1Ÿm¼ºß}¿ow›[}÷¤V²Ú•Ó{õ©mþ…hâº[;ïÇ0^­¾vúYèÕÓ¯GOùÿû|üäcsZ+\¯“’Zª¨Í+{>¿)õ¢ôì•B^+L<_ ƒ3¶ó±ëN*S-Õ´ƇÞ{w¢—„M eƒ¤Ü`¬Æ¡Ÿm+Ô¯Mƒõk.iØÆ÷±ÃL±2®‰ÒF:´r, !UæÚëÂг).i(}©(5Aaƒ%I:–3… :4r,•“væÜÀ϶*7 ’&\Ò°tƒ¥JzRÓF:´r,Ýa©’©PÚH‡VŽ… Re4 ‰Ãàb˜% ¸è›qêKE©© +thä(O$Ë™‚Â9‚â3Ím…JæHš°ÍÛ›Šåy ¥thåXBªÌ tª8EÀ6oF*Êì¨ +thäXÊ)U2… :4r,•*2øÖ½\„ ÁþüÔ/ÇNx  +endstream +endobj +12 0 obj +407 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /Type /ExtGState /CA 0 /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Type /Pages /Kids [ 11 0 R ] /Count 1 >> +endobj +13 0 obj +<< /Creator (Matplotlib v3.9.4, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.9.4) +/CreationDate (D:20260318172825+08'00') >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001016 00000 n +0000000872 00000 n +0000000893 00000 n +0000000953 00000 n +0000000974 00000 n +0000000995 00000 n +0000000065 00000 n +0000000350 00000 n +0000000852 00000 n +0000000208 00000 n +0000000832 00000 n +0000001076 00000 n +trailer +<< /Size 14 /Root 1 0 R /Info 13 0 R >> +startxref +1233 +%%EOF diff --git a/scripts/generated_apriltags/2.0x2.0cm/10.png b/scripts/generated_apriltags/2.0x2.0cm/10.png new file mode 100644 index 0000000000000000000000000000000000000000..003679f15e464c1d94601a3d661dce75ce66031c GIT binary patch literal 950 zcmeAS@N?(olHy`uVBq!ia0vp^Z$OxX4M>(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=%APKcAr*7p-Z{wI>>$A6c&_N5=6vP{kIy$rhpQHFtlYXkvif~31LykP z_6$GfB`CUV5KxNX5Y%bm5OZSTR%~h&IOxXF{A;!@o8K_sc&pr-LquxAZN=l= zM@oZ#ZwlYY{Z~hi4CkPloHYH%!)4`D)yw-&-?uy3O1!U7je!KslXO4dM7!_*e}7M> z++{!`Px5#A_xoON-?Y5_n!04CeQ_cM(ExVvgg<&4@7}Ez-Clb-hP>E>`P-xJ*%q6} zKU-!7uX{>)r2dS3bn@> +endobj +8 0 obj +<< /Font 3 0 R /XObject 7 0 R /ExtGState 4 0 R /Pattern 5 0 R +/Shading 6 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] >> +endobj +11 0 obj +<< /Type /Page /Parent 2 0 R /Resources 8 0 R +/MediaBox [ 0 0 56.6929133858 56.6929133858 ] /Contents 9 0 R +/Annots 10 0 R >> +endobj +9 0 obj +<< /Length 12 0 R /Filter /FlateDecode >> +stream +xœµ—KjÃ0@÷:…N H–%ÛË~ ë¶‹ ¤ŸÚ@{ý:)ñ<yQÈlFxô˜峺ß~¿o¶ë[{÷dV²ÚL°;óiýøJÙå¡BŒ}ê/V_[ûl?ìê&Ø×ƒoãûwãõ3^ëãÚày[ŒÄíAý¸ò§û›1/æÁþWÁ_(Œkc’˧l +Î717ÍQeŠ%Úƒ¶ÉÅÔÆŽôœ€PÅP6(&wJgÛ +ƒË}òq !á5 Cç¼ïú®)6Ë]rš) +¥dÐrlzÛܦnæ[çCêÚ~æ(”6’AËQòÒqaì&J‰µ+ÃTæt*(§ðª®LXYè)(lAÉQ:IÇzOAaƒ JŽ•FTl+T :M¨Ôi(â @Q(m$ƒ–c­Oe¡§Bi#´+â™æ¶ SéØi@o*Ö» +dPr:Öç6È äX›¥²0w °A%ÇÊ0•…¹*íÅ4*UŠõz ¥dÐr¬¡,Ô tŠXEÀ«þÚ™>ù¨X¯(lAɱR„² ƒÿ ç‡PD°??ó kC»h +endstream +endobj +12 0 obj +434 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /Type /ExtGState /CA 0 /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Type /Pages /Kids [ 11 0 R ] /Count 1 >> +endobj +13 0 obj +<< /Creator (Matplotlib v3.9.4, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.9.4) +/CreationDate (D:20260318172825+08'00') >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001043 00000 n +0000000899 00000 n +0000000920 00000 n +0000000980 00000 n +0000001001 00000 n +0000001022 00000 n +0000000065 00000 n +0000000350 00000 n +0000000879 00000 n +0000000208 00000 n +0000000859 00000 n +0000001103 00000 n +trailer +<< /Size 14 /Root 1 0 R /Info 13 0 R >> +startxref +1260 +%%EOF diff --git a/scripts/generated_apriltags/2.0x2.0cm/11.png b/scripts/generated_apriltags/2.0x2.0cm/11.png new file mode 100644 index 0000000000000000000000000000000000000000..0f9ea8f6c0c7c8280144a38f66ce3ed4420839f5 GIT binary patch literal 956 zcmeAS@N?(olHy`uVBq!ia0vp^Z$OxX4M>(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=nw~C>Ar*7p-f`qQI;MWKBNYaYUBBOoY zF?Z{2lfx^2y-k1Pj~m2Ie{?bb;1pZ|Zq z?~frh#ISjLlDyD-x!LRZQ~mkZ7VC)G$<`G?fB)7LC6X1faFf&g zex8ha{Yw2#7dMe<0cfi)W`=Z~$LZ_u RECS{v22WQ%mvv4FO#sE+CiegU literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/2.0x2.0cm/12.ai b/scripts/generated_apriltags/2.0x2.0cm/12.ai new file mode 100644 index 0000000..e2a7590 --- /dev/null +++ b/scripts/generated_apriltags/2.0x2.0cm/12.ai @@ -0,0 +1,71 @@ +%PDF-1.4 +%¬Ü «º +1 0 obj +<< /Type /Catalog /Pages 2 0 R >> +endobj +8 0 obj +<< /Font 3 0 R /XObject 7 0 R /ExtGState 4 0 R /Pattern 5 0 R +/Shading 6 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] >> +endobj +11 0 obj +<< /Type /Page /Parent 2 0 R /Resources 8 0 R +/MediaBox [ 0 0 56.6929133858 56.6929133858 ] /Contents 9 0 R +/Annots 10 0 R >> +endobj +9 0 obj +<< /Length 12 0 R /Filter /FlateDecode >> +stream +xœµ–ËjÃ0E÷ú +}"Y/{ÙdÝvÑé#D…6Ðþ~•”ø^iQÈlF{t˜;YÝo¿ß7ÛÇõ­¾{R+¬6åôN}j[?1™4 “ó~ŒãÅêk«Ÿõ‡^Ý8ýzÐõT¯ßÕã§ëãZÑýº(Ô¨öDm]ÙÓùM©õ ÿ«`/êZ©hÒ鱃OÃpT™kT{¢!ƒ÷îHÏ Šbƒ¢\6Öæ1‡Å¶ ~2iŒÖOlÈðš†Ã`RÎ.Å…â0Rˆy¡Ê6è åˆ¾ìØ‰t¦lƒZʱTédJ4ëbã"i‚2I³b'SP²¡BŽèËŽíL‰’ urlU“væÔÀ/¶Å@)i†BI“b{2¨Ù¤é‰$Çv¦ lƒRŽ J'S¢óè8i‚×4Ä«„Q/2%ê äØú†•N¦DɆ:96‚*Ñ€¢¢¤Ê<Ó¬ØÎ”mp…”#~‡Ù±)(Û ƒ”c#(~5ò¶Dç§›“&(ófdÅö¼ˆ’ url ¡tdèÿøù&"±??õ ²²šB +endstream +endobj +12 0 obj +416 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /Type /ExtGState /CA 0 /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Type /Pages /Kids [ 11 0 R ] /Count 1 >> +endobj +13 0 obj +<< /Creator (Matplotlib v3.9.4, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.9.4) +/CreationDate (D:20260318172825+08'00') >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001025 00000 n +0000000881 00000 n +0000000902 00000 n +0000000962 00000 n +0000000983 00000 n +0000001004 00000 n +0000000065 00000 n +0000000350 00000 n +0000000861 00000 n +0000000208 00000 n +0000000841 00000 n +0000001085 00000 n +trailer +<< /Size 14 /Root 1 0 R /Info 13 0 R >> +startxref +1242 +%%EOF diff --git a/scripts/generated_apriltags/2.0x2.0cm/12.png b/scripts/generated_apriltags/2.0x2.0cm/12.png new file mode 100644 index 0000000000000000000000000000000000000000..a204de0a1dad4bdce62c3c52456c5c8d17f8a7b9 GIT binary patch literal 960 zcmeAS@N?(olHy`uVBq!ia0vp^Z$OxX4M>(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=x}GkMAr*7p-f`?=aujfN%%1T(-tsQr4#^2QwNizRd0cWkf2XHCV^jIF z=NEg!e_@>#4lySdZpEfnfrCyR35qTo1e78;1c?zo@%|h8Im_Gg8@6xWD);6PiMlcE zdcFHdY4q<+;kjn{|NfiJzSbW@q(d-`g8Al%o}vFn*&1DOa{UM~deVH$xiiE69|}J| zA#dM)&a3Wv#DouSXF&q_B!6fA>i)=l{`@5n(D8=LHkt&D-mZv8{b(p;Ww`E3{Fudidz?gHi} N22WQ%mvv4FO#oVsE*Jm+ literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/2.0x2.0cm/13.ai b/scripts/generated_apriltags/2.0x2.0cm/13.ai new file mode 100644 index 0000000000000000000000000000000000000000..fe231d8971328d7b66ce2d0f5087ef3454d763a1 GIT binary patch literal 1602 zcmZuxdu&rx7E%v3~US}1PClHp3}$Hwch*3z2|qn z^PS)K_a*a-*gGwc@5Uz$6nk2ej3PlVagrqMIjSgpkB@ek#%)Z?)AAFpI5^%Jy&WsA!eFJ~Xrb zihrqoeZ^i=P2JN|4<@^Qy1#Pp!+qr^mg&dtxeP;*aR2n4Yj+B@Uq8I_bB{Z&KeFr5 z- zj$iwz0Y3k*pPH%SwykK{Vc5CL5j=D7w6M9E!gUD|D)e?`WfSU|33b7=ezL8V$%MpTBU@LC@d+q3To5hNHhe&wu9O< zM)Z;Di8&P!QKTEi2*I5C=5#}wjo65GaRA~*$!K|^pd5!pM>!zTVh%|9F@1GX@kyc6 z8wGa=%ORntYAiC`^E=t}KindFlJM6+j$UGHvdw`R3y@fNI@_;Q9?gcSmAw#6tQl?-O8b%3Os8z5E z^{Wh9#4trA8RQ}o@1Ym~RTMM(0F7F$R09u~#URcUc|ahILF_miV^|jFX}%q!V{rJ{ znC5>l7MFLv9b_0>w)q&tXmT;78qbt0J64m+MZw@fmTjlXv4aeo5gU>?!N*I9Veb%j p^Eg+4t`(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=nw~C>Ar*7p-Z_|i*g(M5(b(g^t}!czK~dyI|9P1XY`yJ!|8D($Pha8T z^Dq1i|NJ)yC`E7x>a=i(Ik9jnHnj>Ibm~Y@bRkN3|7H1zY~Ok&yK`5nHgJ=w8^f-i z_d?;{<1NoG`#P0!!=99PF28xK_T95DT>rM~k>@{{3y++6d2r>~M`xbR-E*D_P6Gxt zPp7=h=6N!j`@MhGh83*1(@Ijf!`y`&OP_WhIhuP@eeZeUZBxBt$c|F5(H?crN~+R| zi`1^Tysp0Y{1jDOw=Mhpr+?pz5{XP(*!&1gppT80SHFMhf8crnWd^P9v%h&;;dj4^ S)lpzhV(@hJb6Mw<&;$T~fGza^ literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/2.0x2.0cm/14.ai b/scripts/generated_apriltags/2.0x2.0cm/14.ai new file mode 100644 index 0000000..3b74021 --- /dev/null +++ b/scripts/generated_apriltags/2.0x2.0cm/14.ai @@ -0,0 +1,73 @@ +%PDF-1.4 +%¬Ü «º +1 0 obj +<< /Type /Catalog /Pages 2 0 R >> +endobj +8 0 obj +<< /Font 3 0 R /XObject 7 0 R /ExtGState 4 0 R /Pattern 5 0 R +/Shading 6 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] >> +endobj +11 0 obj +<< /Type /Page /Parent 2 0 R /Resources 8 0 R +/MediaBox [ 0 0 56.6929133858 56.6929133858 ] /Contents 9 0 R +/Annots 10 0 R >> +endobj +9 0 obj +<< /Length 12 0 R /Filter /FlateDecode >> +stream +xœµ—KjÃ0@÷:…N H–%ÛË~ ë¶‹ ¤ŸÚ@{ý:)ñ<yQÈlF{ôÐŒd²ºß~¿o¶ë[{÷dV2ÚL°;óiýøKÙå¡BŒ}ê/F_[ûl?ìê&Ø×ƒoãó»ñú¯õqlð¾-Fb‰ö ~ùÓý͘ó`ÿ«à/Ʊ1ÉåÓ 6盘›æ¨2ÅíAÛäbjc GzN@¨b(‚;%ˆ³i…ÆÁå>ù8Ð𚆡sÞw}×ΛÞÅ6·©›) +¥dÐrŒ­ó!um?s”Ùè(”6’AËQè¸Ðv¥<¡åXi¦²Ðw ÓÒ±u*MÅúz”+‹0n˜Æå® 9ͦ*[«HxMCìS(bgp½&J‰µÑíp¬×T(m$ƒ–c¥Pe¡¦ SÄJêTšŠ²vT… 2(9ÖêTj + +dPr¬Š'#§*>TšPç;MÅzõ„ÒFb-ÇZ‡ñÜ¡£PÚH-ÇZ/•…¾JÉ åXi¦²Ðw SÄn¼ê¹3­ëÕ… 2(9Ö:¬,Ô6È äX)TYÁÿšóK(4ØŸŸù#HÜÄ +endstream +endobj +12 0 obj +448 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /Type /ExtGState /CA 0 /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Type /Pages /Kids [ 11 0 R ] /Count 1 >> +endobj +13 0 obj +<< /Creator (Matplotlib v3.9.4, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.9.4) +/CreationDate (D:20260318172825+08'00') >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001057 00000 n +0000000913 00000 n +0000000934 00000 n +0000000994 00000 n +0000001015 00000 n +0000001036 00000 n +0000000065 00000 n +0000000350 00000 n +0000000893 00000 n +0000000208 00000 n +0000000873 00000 n +0000001117 00000 n +trailer +<< /Size 14 /Root 1 0 R /Info 13 0 R >> +startxref +1274 +%%EOF diff --git a/scripts/generated_apriltags/2.0x2.0cm/14.png b/scripts/generated_apriltags/2.0x2.0cm/14.png new file mode 100644 index 0000000000000000000000000000000000000000..f9ecbe2d411ac441533e940719a5fd7128fe384c GIT binary patch literal 956 zcmeAS@N?(olHy`uVBq!ia0vp^Z$OxX4M>(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=nw~C>Ar*7p-ZA83G8ABO?D_G3y03?W(_mu#GF{T6`NWG4mx!tD7tJAP>SFXBu4l|@-OD>wJ-e-oSzxRUzSLUR%{kc z_!#@>)xX!8yH7p+uKEAFrXDeFz-AEKBa`l{&NJKFdpdIW!OJm3dJ2bu9(B*Q*tAw2 zZCUl*@^vnShC%F@Bro**k6}q1<%X&3_qN&Fz5ANizi*uHlRy8M%S~2%!o7a-{2ag8 zb8hWPdRFpF`s??Vg_OtbAH9ulZ@vHY?|Ko1W<54uUi}`J=I$3zX3+LN^{03B-@GcU ROata522WQ%mvv4FO#tjvE^z<= literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/2.0x2.0cm/15.ai b/scripts/generated_apriltags/2.0x2.0cm/15.ai new file mode 100644 index 0000000000000000000000000000000000000000..2bdfe27bb018dbb5c4c33d20b1d4a81da3649537 GIT binary patch literal 1601 zcmZuxYitx%6ov--V`2%3B|w(y1ycHu?mTvOcD7+F`@~{vH(ewu<2q3nnE3l~zhY?3T#AftN}|K220(Zd_A12eV)W^9s3{TEhvOmq zvHw!&4@x$r9(!c)XB6~;nKQEngJ=}z?W%B2aG>}#4VYuMt+K4(9ZRkzCx;v-vRMM< z8WaF*oPjGC!tn#z6~akzK%e4=Bn%CyP^zQq-yVGHeC-Ic#5w)P(!bK2%6I3Bz^@;@ zv1>#4u~qpWHr<=H58jA1q_7TZYzb9qcKC zx9GJ;U8d)LZxbu~hSyH@6fL~)PRUVmA=(>-vxGHZA1saGDpcJ&d9X+y4eRF^rhRoC9Ovf?T? z)%CQU-{r8kjPrk5bvkW%#olwee9o!618$5io}73cy%QMO@}ya4&Ochxr0*K(Uby|z z#f&R$ncnSrJ2*%Q7pxF~hc5(n>pOhkEPy9p+k3lV;X}Gf@YBTdv|;*e!T8_% zTJlyOYIxp##wT{@W!TCo*;(R6~n47j~o#HeUPe{WsHd+xh&vAEz&EYR*7Q z)R0Abw>{6@-?Z4ae_!XOjp_d!IIc8zHa)mMMh_Jod4Y6)yi{==MC(dw&9g#68_|9s z*fvm=z=%BJEio%1qKQPGSXnSDzFFDOb}KfbeG*`CpColW5m3s5L`Eqfkzxu++A-le zCis}n>8FBz4XYt4sB)|^-1HGFAmTj{3!4-SZO4s^6cyx_N+=vsP$=jHI}CcWo?iz7 z2!*vmny#BU(kp6B9!0xV#UJru9VFih`>TOh^1X;N8-D>?MxPHVvV+W!Tt^|tZ~|js zMb^ZLobho+%wd?Eyd-ks%dPp}c(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=nw~C>Ar*7p-m%U*>>$AExbETa_)1YL10D;>puMYhG1xmKZ`%It-ft$M zee>!VAN*(GR%~h&IOx=opy;weKq-PlP^X1M%!w%BPqu#>wqE<9{NSz6TK3(f>c+6^ z#1Fl~yI1zT(Nrs6zWo1RuDmqL&FqMMbXatI`On$h6!;V9Qp4h34}4zS`@L%RA2#8& z`*%|8Szve{nee0eox~}-#5Hr|^@tC3jBtZQUDEU)j^#OQ_czwY{I%Eqksm`+RAL%B zGx%Ov*A@$BtFx`-rVqHGZ-ne$wA{Tbyl>$V3JvQyKj+=vPyl7YkP$MFN&;m1iSkwsOKNf<$-$wGi?{%D|Mi*Fb%KJa&tI$>?^>pXvd z!5L-nZkBC2r{a_@I^KVH>Gu!dj$HPcuCaGFPJftptnarke|*sOYi#<)t&L*`w|6br zeKuSko22Egi15dn_q{{adjprxK7yxLsb+ew_)9` za~GDJzuNYwzje^*Y4})rcrW|NP{@91xUuS(c<3*ycxKJT#gE%hE(2A}9bKU=tYb#1 zR@`KDM=pz+V6*QZ=cMea`$rpkJj3N43svfQW0l`JgfGa(^sp% zty{jYRVO($X%Ra54wdT%ZHL2GdtbkO)7AR6>Fbtjz4_zM=1*7`>NlUt z8F0}(dtU24m=n6f6@HudR4I1!MMwC0Wb$5tEBe!OI}X{xz6fWXIw?l7e%X^((SE`7 zUJH}G@>2Jm{69Z!YMJ3S*}m_;*i>it*f;+^zWd_n_TxO!K(b&-HN>k+D$cQNN+I#C zAlOb&lfsBdl8rGdC8CT}w-hsAR)VwQq3y-kh!zFF;BHCldm^I@heS*nAQ5B+NSZSB zbVBwCwbNU?QV?q*E~`qcG~D_jEF4b%NM{Gn@2)E#vOJs!~EGNq$KY3t2NX zGCZ@MD?rK*x5lW9hmDfKFd7dbxKf=JL%Jgp4N@swU)TJE6!2e)zo1ONa4 literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/2.0x2.0cm/16.png b/scripts/generated_apriltags/2.0x2.0cm/16.png new file mode 100644 index 0000000000000000000000000000000000000000..6c779e7b767b2d5a8220b5fcbe3274439ecfceb7 GIT binary patch literal 949 zcmeAS@N?(olHy`uVBq!ia0vp^Z$OxX4M>(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=N}eu`Ar*7p-q|R4$U%U`(bn~!raj|>2Qo?X-m$2-_|Dy){eG{$!o$Mv z+zj)Dby_&YoLIOOn_2}9I&~x{x@-_oir^3=M)*YWFUIV(FWnEEpBcqpmPm?LY!*%U z7@KtN&T-p=TzP4`iFXAyqd;DXeRSBodHMEo^?K(*^NLyAWO)&8_{sBg{QO&I-P+Ut z|Jz&&&4L8(C+A0B4PR$I{jSef>t9T9=n3pT(w_f#T6t&X^Ft)YBixfvhZ!%wY`o;_ z%z*pPb@Yfy2Wa|H6Vs&os_WkWy7trV{!ty literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/2.0x2.0cm/17.ai b/scripts/generated_apriltags/2.0x2.0cm/17.ai new file mode 100644 index 0000000000000000000000000000000000000000..e2e2e1bc38f1b8924e54f069a6f712b9faec3413 GIT binary patch literal 1595 zcmZuxZ%kWN6d%Un@)i>fAW<~Cnsl@Xyg%)G?IXxSTe<*c?OL+nCd1d)M=7@N)z=H_ zYK$KgKM+uy8>j=bCPP5fjF>GjnVa)NH#FNc>YONQ79xZ#7(Z-ABzkZEKy7{R!+ZC4 z&$;J!&ON`QaawH^bftmNeDEWfJWJ4k0*a@TsICTN^Wl&LNQ(f4fZ_wBQ}9V)z#sz` zsHq_&*^3QL84fl@hCrV%AX^-sPDzBom|WIH;BGe*pacv_z$ri|sWM>kk|5n3g4Zwm z0GHZT711q0Z~%q1+7J~f0s&bcL=?=auO;=Z2dx=&=-dP_Z_Hkm5W6IO(j|qJE>%SN zVE!n6ljQXaW+j3mQs`y5N{(lETCX>;rmR&fstHOIofZas(z{lc6||zUYi4oqcDkB& zN%p}GK&Kek`~j3dAZ-Cu6ssgEUJ0iWhN>h4iOBHj!N$(dslvM7_V?@qjo-!ZoZ4|| z`u2(K+m=hMJ=`_<{GNutUwgS)+j4Ko^0QApw$R^qeC?;ZYu$IJXTCk;c>j$XcgBYo zCLO_en|1_{BggtAxaIWCFW+5N z8qSU1x8An(g;DCtvEH2{#Y;`s`%Q{$1AwLQR$(eO*)A*X_^8S_?+|o5vShI?Vg7ydBQlHUAL&+Ebi&-2g|X z&a8Ywxyujb7jomy%X^QU6nVGv`x9nO-0VCYW6kpme-4;$y=D0IomX8$y^D6qUh?pD zwS4htF;mn~GU5DU>Vf8imkMEYH{-uhrP#{mbg?J20cY%ps2iId&?SE#*M53#=0oS@ zN9zkdioRF6wQO)rbCmnTS`jxFL=H|@EPdnXe(= z|MS_Qx^0i#y6RZJXJ~I^825eZl2Qst+$g?f15Kq>k>~`B9RRIq3=1RG44d-7YDjlT zEe$qhw<#FXQj3g8Q2;dRkc?i((#bgx3n&M~BFq7CBc?+qrJhtc+35F%kq#14av^o0 zcJD&c!O_#X&>6{)7Sys(QGvEefT4f_1AY(a)>rbChH}sWVJKWhlEICwN=5bQ2-2x4 z-YyX-Ak!=4Z3iAfd=6DMbpo(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=x}GkMAr*7p-f`qQ6on=k?ez z|1aZ#?=0MkO|1e4ojMW}T{Z|PMQ{k}v~Y+y5heU-MqR>M)4wc#=B)ghAWozv+*U~5 zSAF*5?Jeb-bM2;aQ{XI!4NuBDmv66CFE3xe;bru_u(*T&-yb5&mv9GvGG2Z;c`c?2drylVkkIgV(^i8_2s+WJ=FYi}e}z`!}3F)q%N*!PC{xWt~$(69A> +endobj +8 0 obj +<< /Font 3 0 R /XObject 7 0 R /ExtGState 4 0 R /Pattern 5 0 R +/Shading 6 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] >> +endobj +11 0 obj +<< /Type /Page /Parent 2 0 R /Resources 8 0 R +/MediaBox [ 0 0 56.6929133858 56.6929133858 ] /Contents 9 0 R +/Annots 10 0 R >> +endobj +9 0 obj +<< /Length 12 0 R /Filter /FlateDecode >> +stream +xœµ–KjÃ0EçZ…V KÖÇö°È¸í  é'D…6Ðn¿NJüŽƒ<(Xé?Þ•Œ›ûÝ÷ûv÷¸¹ÕwOª‘Ùö¨œÞ«OmÇ_L& íà¼ïc5ûÚégý¡›§_z¼ÿß×ÏxmNs…çuV2–ÑÔŽ3{¾¿)õ¢ôì•Â8W*št~@GglëSÛžT¦±Œ !ƒ÷îD/«ÊY9gÎülY¡~0©Ö4$\ÓÐuÆÚ®ïÂLÑc]ìB?SJ©PËQêÒq!Ò‰ÒFƵ AeÕöƇb7[tj(“\µ‹…ôòB¿@aƒ +• Màyá²B¥Ÿè"ᚆ¥ð2÷û5QÚȸ–£œ:–3J©P˱TÆ«„Ë‚¶&uKq–4àªIOu©(½£"(lP¡’ci‡å…LAaƒ +• Aå…L…ÊIÖ9/T,÷K(m¤B-ÇBòB¿@§w$»Xç¼PQv Aaƒ +•e·Ó±œ)(lP¡’c!¨¼ ƒoÝËCìÏOý çxÎ +endstream +endobj +12 0 obj +412 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /Type /ExtGState /CA 0 /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Type /Pages /Kids [ 11 0 R ] /Count 1 >> +endobj +13 0 obj +<< /Creator (Matplotlib v3.9.4, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.9.4) +/CreationDate (D:20260318172825+08'00') >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001021 00000 n +0000000877 00000 n +0000000898 00000 n +0000000958 00000 n +0000000979 00000 n +0000001000 00000 n +0000000065 00000 n +0000000350 00000 n +0000000857 00000 n +0000000208 00000 n +0000000837 00000 n +0000001081 00000 n +trailer +<< /Size 14 /Root 1 0 R /Info 13 0 R >> +startxref +1238 +%%EOF diff --git a/scripts/generated_apriltags/2.0x2.0cm/18.png b/scripts/generated_apriltags/2.0x2.0cm/18.png new file mode 100644 index 0000000000000000000000000000000000000000..6acba4cf6281232d27e12d117711e6e2f8ad2682 GIT binary patch literal 960 zcmeAS@N?(olHy`uVBq!ia0vp^Z$OxX4M>(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=x}GkMAr*7p-f_%3#N3}VFTa#Lzv~mv z%Fb(4a2PP8pR^xYetd!Y@ipDf*Y;P=|6W9)3!7Re{Lw4C`DEF9QJ?+ix!%d`CNECm z;b^>Ez3TXxv`Vk77S2{@TZv2(aP1fg=#5Z(jr8_^|MyVp)5pfktKYx$KXAQ(vLM^u a$8I33`TpS9|G$B`iNVv=&t;ucLK6UyX)MD4 literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/2.0x2.0cm/19.ai b/scripts/generated_apriltags/2.0x2.0cm/19.ai new file mode 100644 index 0000000..36987f5 --- /dev/null +++ b/scripts/generated_apriltags/2.0x2.0cm/19.ai @@ -0,0 +1,71 @@ +%PDF-1.4 +%¬Ü «º +1 0 obj +<< /Type /Catalog /Pages 2 0 R >> +endobj +8 0 obj +<< /Font 3 0 R /XObject 7 0 R /ExtGState 4 0 R /Pattern 5 0 R +/Shading 6 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] >> +endobj +11 0 obj +<< /Type /Page /Parent 2 0 R /Resources 8 0 R +/MediaBox [ 0 0 56.6929133858 56.6929133858 ] /Contents 9 0 R +/Annots 10 0 R >> +endobj +9 0 obj +<< /Length 12 0 R /Filter /FlateDecode >> +stream +xœµ–ËjÃ0E÷ú +}"Y/{ÙdÝvÑé#D…6Ðþ~•”ø^iQÈl¤ƒ=:ÌŒä¬î·ßï›íãúVß=©f›ƒrz§>µ­¿˜Lš†Éy?ÆñböµÕÏúC¯nœ~=èz«ÏïêõS¯õq®è}]Æí‰Ú:³§û›R/êAÿWÁ^(Ô¹RÑ¤Ó ::cŸ†á¨21Ú Ñø¼wGzÀPÄ å²±69,–õ“Ic´~bC†×4“rv).‡ÑøBÌ EP¶A)GŒu1‡qáˆÕØ”mAÊìØi»™² žrl4SíFgNübY¢sê¸ Êt#+vú”l(‚c«N¥SS¢dC„…*½„Í#ª4ëîiìSRlç ”mAʱ‘Þ/¼,ѹ9‹¯iˆ+b5V$J6AȱÕ`¥SS¢dC„…*š‚"uTi†2'#+Ò EP¶ÁRŽ8ר±]SP¶A)ÇF¡J§¦DçWš ÌÉÈŠí|%Š äØHBéÈÐÿñóK”Db~ê<š@ +endstream +endobj +12 0 obj +424 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /Type /ExtGState /CA 0 /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Type /Pages /Kids [ 11 0 R ] /Count 1 >> +endobj +13 0 obj +<< /Creator (Matplotlib v3.9.4, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.9.4) +/CreationDate (D:20260318172825+08'00') >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001033 00000 n +0000000889 00000 n +0000000910 00000 n +0000000970 00000 n +0000000991 00000 n +0000001012 00000 n +0000000065 00000 n +0000000350 00000 n +0000000869 00000 n +0000000208 00000 n +0000000849 00000 n +0000001093 00000 n +trailer +<< /Size 14 /Root 1 0 R /Info 13 0 R >> +startxref +1250 +%%EOF diff --git a/scripts/generated_apriltags/2.0x2.0cm/19.png b/scripts/generated_apriltags/2.0x2.0cm/19.png new file mode 100644 index 0000000000000000000000000000000000000000..54ce8a465700765324cde7200fdc00a4e16574c5 GIT binary patch literal 954 zcmeAS@N?(olHy`uVBq!ia0vp^Z$OxX4M>(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=>Ygr+Ar*7p-f_%haui^3Wc&F)KCjzY=+n}JJ6&oL*rQkOyE=2`PDak} z{`L$%tPeVMBq+LU5KxNX5Y%bm5OZSTR%~h&AWHa8PW^*9oBwkDDBpIiIhG6qP_0X; zoxUS4JN{ej?6b4q-~Y$^bbEebBDrpZ89m{T-p6lcy6b=b-ann2c(1{E1umN@UVP(u$lu*PNeo?#ws!2iZF)b5Znh Z{U0it&q5rT4+8TMgQu&X%Q~loCIEG#E2{ti literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/2.0x2.0cm/2.ai b/scripts/generated_apriltags/2.0x2.0cm/2.ai new file mode 100644 index 0000000000000000000000000000000000000000..226779ed9d4d1736bbbd727dbd2a1efe6a0c7a27 GIT binary patch literal 1588 zcmZuxdrVtZ7&kR>%iWfZh}j=5@kAsPh2BSRZ*Ln!`XpG$y0&9ZWXo-P`+#z9b9->z zbi{3CEK3})5Tj8y%M!8&%n36x6mi2Rz6hIW5Z}(2B~HyUacnN0(+AYnbN{$Gzw@2% z{JzKUD|K4!m9&N>N(bk`sWE~E6p+0wL{$|aJv*Z!AT0tEB61LrP9Z490D}x%pt_n6 zB|kRgb1v9r2?A}(fP8GDw?*_ppiggYaaiky0u%w82Al$fq9Oqu-V&rcB>2Np5OA4O zMfSNx2%6BdRy(31#vvf<+7JbE=5NXT>p*i39l9_8%p0>;M8qylpLB^axn1$0crbtT zeuL-_3uZZvUZl{c<20OsG0<8qujBJ}%_t`*P~tQVyq z3;{aBz#fjE_yK8;prlwupX?WL7%`}bLK_i(>GkIvEtAx`{R=VT<(k>1zlPU-IQCor zij`MOtexESwuys|s)@q<8a*MbE%( z?@Tzl@A})_|7`xf)${okOS`^t{D^1w7k_pw-ntararN#`wsekuI#l9xKNZ+DQ8Yfi zZ}HW==Um5k_aDE=nrx?@z4p<$k*WD#W{W}x=05m-{Nam^I}#16ckVHBtv==Gq<8B; z$MlKr_t%CFL>-p<&b~%>btFd*pMIf#O>OA1`hjFm%y>ECexhO(oV`$?nxC1zKG#+3 z-KL(pp(40-$=+i2mkkABu)H zbeiw^v2wIWFdp47$i7p#=0>B*X4qbH{;vC3rTFkOJM@~5`? z2u+qeZg}+G!NE&?X@BPAAAxtpJ)cz~58QX_J$p!D<~wg*JGQ-HWJ?Wh_)K*&t)D6-E-y=IvvNqa0LGd?a~8vj$aKN7Zm9oev(k2~Ye+&sAr6CuR z73%YLq#68rHWoT37}A1z70L=wHwZ8qkzpk41y5=<1`S&QLJ&q{Mv`n>I?~8Wu$&;B zitKOqq1Mm674-*zSMar>%w{|w%ar@DELri`P+N@*#c-6C=4m~{GrET;zKo*E%5%uY z6)`|ifMyA5X#q~B)9OF~EMYXt6kb5eVl;(I`Cp9UIW!vbF&-&84>Pd;gXz&E$hR|~ z(UgZ#94ciVrqvbR<=H|m3{{BnOaVqy`kdIHB7`HNk{a*s@GcSM3Xm>Y#zn@Xi>#9Z aGDsCUHOFF5P+%&pG{YE((o)+-JMk}Nh55t) literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/2.0x2.0cm/2.png b/scripts/generated_apriltags/2.0x2.0cm/2.png new file mode 100644 index 0000000000000000000000000000000000000000..6d7fa0fdfe5721dcfbdaba29f39a16a71b21def3 GIT binary patch literal 958 zcmeAS@N?(olHy`uVBq!ia0vp^Z$OxX4M>(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=+MX_sAr*7p-Z9K$HWXlSy!he&exIjdk5V*EL%%j!3uNS&WxwAm?{Mh+ z?s|qF_6MCh5)@rF2q;Bx2k(X5GDM_uRdX|>0izt7QwF%>XE7&!!DzJ z-ZAD|^{cseUrygio{NypjD2MO?%li_Hru3Y#KlSS7qThfz?n4Pa_&5?xpO#e4IQKE zKJ7m8H22OUy?62Vp6gJ-yK`>td3gVH z@WRXQH#g-!K14w(fw=i+?4w89-uUkMA4y?CNvU~O^4=Dh=%gq!=;6hWEK1ijKe?|= R3O}SGyH~H3cFDSU7rb+k>`;*B1se zsNfdOVi4xXrm{eQEXpPlGn~u~GZ)#1i6hLY0X1=9*(4)q#LPg9zHdLEQ1AY@yuW+i z=lMM!zei&$wCsi%dPZ~ZCvf&60|5t=x_V~M9>A8jHX^_lh*P;z z5c;bsjxvNn6$x8tAyiNc2CUUfDCCE}1);AM)I`vM(FsuAl)Z`wyC8kmj{HiK>LB@0 z{v>`0l4LPoX(16g@(P9wgNZjmoz5s2*PUueO-Q1|n;)2hxAS~Hg`6P08d)6rJ6z3T z zXaCp@t&0P(;(@DQKWLb`wM3}*ytH?&x#8Br%wM;M=jGwr%frVf^jVLLdY$){Kd`fN zdH+6l<&gILnEl3VYkTRpl}VpIOnnCx4{Vq`ReS(GcDVoEqcP?u37{* zOP)Rvm$EJS(i!pfiHqfTM(l$>+q$*WwvB1~)!b{PgR$oANAhoWKQW}Kd1Dl5Ul`pz zdG|gizw(2A@t0Y9b?HuX&fdP-4O@=x7>hr9y12a~t~^1NI=(u*?Sf|R@y&IeAJtC9 zd?H^LDl5*4!G z;L{U}Z|rz$llIT?zT}q=(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=s-7;6Ar*7p-ZA7mBA?8Gs@TVDd2coin34SPEcC9&FqnZf1mpPQ%{^opJA8;37$#wE$7a; zX_J_B`MYH|Hw7UFH~*9KqdBKJepyrKFraxGy=y+a%lm6CQr^6LCuJcw;m?`OO0La+ z-l?BCuMbMNfVGJ{BV*#~S&?Xp9}w612@9hQY<2WKz3 zwuD4&)2Ip6gpx?KfT=9$!<3XJc4=+2KWtMW2GBNyCau^?mC6s(5QDFK_W=v*%#WG- z-E;2!&f`1zE}P>yq&G78CuhNl7=r)=YGVsiQ32Tcx7roJT4gK;RX<=|vR?@Uju^N> zWhJA8eAG}#b8x633=DAt_T}A;Es7U|vc%FJ!POF$u>y<Xd%GUCBsV1vM&(w z15C<1J)5FNwFzj)u+%f!dO$}HYW1P*#3B?n^-#eKNTT=$P*P+=4kPgUY_$ zcK?i18trme547gGJ@pHtt%J?h)}rd6hwSHqCN<0IyGNA!0f@5%ad=$6peb7tpA z-La{@uC3X5!{Fx{=V#CK7r9Qq%~W;Qy7+*9BD*tOS{D;PG=9TBd~U;~k{iC5Yjlut zch4T!em;7$CZ}yak1R@$Ow4Yz=N$*PFK?Fae)q)px1XLfKKRQ0HxGS$dh%*d;dOKN zQ`P$D~{%_CF^cNYI+< z?(Q5if4#AIlgL?)bZ`Fa$%_?P7Txk6(#2c*`*NrVk`+tJAzofuawBI*NhIDD6x$0t zDU7Nl*%_-^qS8pUiv@vI4X&z(wOWV~>y-f_?UL5`R7V*HQZ;3ORFN4V?aI{E3Ed~; z&h8BO!bB8tUAc+WNbh$L9nsrUv7oeISS#sYtZJZemyFwkDh>u3!5ap>s5h2^W{lgz z(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=YMw5RAr*7p-Z9K;2@qgC_;=d-`lp3CxsOs3q{CwwSh9S-ojY@e+vCTs z->eP$g>_mu#GF{T6`NWG4mx!tD7tJAP>SFXBu4ne_ixPSEN}NW^l#oO_vR3Zx-so~ zz59qwvHW)~v$gkk67LF3b0A)ceI#98yZ*-AyLHE&eZP4x#O~nTWN~u63bAC;d`r{T zpLgDG{`<~VkC+g`*hz5T+^v#q2@0Gjb{gg$S~(b`{}NJ=E4nE^}* zX@2r{TP=;t_a8cLb+G<5HAFPcNjmdKI;Vst05Xy*E&u=k literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/2.0x2.0cm/4.ai b/scripts/generated_apriltags/2.0x2.0cm/4.ai new file mode 100644 index 0000000000000000000000000000000000000000..fabe51de0d1bc2f5074327d78d3597041aea7fdb GIT binary patch literal 1593 zcmZuyeQXnD7)M~p&dsm6hz{h%Sofjp-L<{8w*cAN?r7r*%{mboi(aqSFWTO`z8&2Z zQIlyzNt8d9j1Spn$2O^AVqj>ZsA!x@1k{MSMogfXSVxQtGw7JU@3q}(>-CT8`+J}7 z=lMOSa#*-}TCF2gi6l7k9zg>NNUi{}b}b;=x;sTcnt8|vB`+WyyjP3>4H9St>(>)v z*o_5^c?z5qhCr(bkXtsn0-^wcA#-ew!3|ExLlNjQfP;rnl*53*M}lp^Fp1apUM^5k;2?pKKK)QdAaDJ~)2l z-!8iSyh(~7j}-bbdbOU_u(VcdWQ+x;4pbAAD6$p-Y(~2&9G1|D#;W0p|~*`~k@YQBf?SAh|`HMg+I-GKO1^C4p{qClV8p?eiyoP zU88lEX{B$;)YT*LPo`I1Y92IqzH-mZY2kwjcl6}-o(sX%=f`KRe4cQRv-=+3arx9= zf7pIV4c=vXdFQW3yAHMg9lWJ0TJc-@T;EaopSQMMNNqhdv+T^|xrwctpL9IdH8*?R zbNZ$3w|71K`y+%XwaC|IpRs>*?f%P$*|C=Gk4`mBUUCJ5vg)4N#!**IIakr}%g*yV zrV?+SHJ(UTufFl_+vi63Z%!nmYexEq;Oj$U`|( zC9Bf?MApO`_pM64+j!tu$wBV3_zls+)@D}qPIswd<%<{N!aAqp`+?{73aZgpUsx0p z>qn|=6?OL8c3XD{;q54sN`8Wclc{Cq6X0cx*XQ)#i{BDV^F)Wd1H? z)Du6Sypme8w0tJ*eR|RRWwF=r)Ms0ioqwgDxW_umnB#;p2N-Jy9XSlsB0CBTgh+u0G@6gRNyo$~GKlF@1Y!ynfp`pat}{f>@SA+d?~WiAC`4*S z+(N@1Ma040bGguY$&h9=uuzggjh%;`K?w%^F3_b_vufRgzz1Pxq=6(u^H*v~_SO=l zLzdi80nLBDR;SwoT)eOyRW@q@Nh;?3QrLobM~$UHLuvGsmNwD`jZwoqKpCqks=78W zxR@+@2nx_Tf|`GTo?)~M@PK)YMwP-32(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=+MX_sAr*7p-m%Ve3KU>FQr%6?zw0AmaUsPQ+mRO zukXYe=BY<;2k(X9CYePP;}WKphS%Dg!gaRW3F%g%kje^`t?CQa*cso zx@n(x%=WGNzfY%q{{64^>;K(Ex&v+irmJHgF>lYdoX$`)nH|tpbNK@v+hu1zbFFd95 VxbkKaH!v?Tc)I$ztaD0e0szyGFJ=G$ literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/2.0x2.0cm/5.ai b/scripts/generated_apriltags/2.0x2.0cm/5.ai new file mode 100644 index 0000000..ed47b86 --- /dev/null +++ b/scripts/generated_apriltags/2.0x2.0cm/5.ai @@ -0,0 +1,73 @@ +%PDF-1.4 +%¬Ü «º +1 0 obj +<< /Type /Catalog /Pages 2 0 R >> +endobj +8 0 obj +<< /Font 3 0 R /XObject 7 0 R /ExtGState 4 0 R /Pattern 5 0 R +/Shading 6 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] >> +endobj +11 0 obj +<< /Type /Page /Parent 2 0 R /Resources 8 0 R +/MediaBox [ 0 0 56.6929133858 56.6929133858 ] /Contents 9 0 R +/Annots 10 0 R >> +endobj +9 0 obj +<< /Length 12 0 R /Filter /FlateDecode >> +stream +xœµ–MjÃ0F÷:…N K–%ÛËþ@Öm=@HBThíõ«¤ÄóäEÁ"$H{æ1ŸÜÜï¾ß·»ÇÍ­¾{Rì¶Gåô^}j›?!š8¶£ó~ÃÕîk§Ÿõ‡nnœ~=êü“¯ßçïOþnN{…ûuR²–ÕÔæ=ÿ¾)õ¢ôì•BÞ+L<ß ƒ3¶õ±mO*ÓZVÐ.:ï݉^ +V1”Iµ­‰}ïb˜µêG‡`ýHCÂ5 }g¬ }7Ì¥…ÒF*Ôr:.D:QÚȵ A%åzcm?ôݬ-è4:& X'i*–ç +T¨äXBž¢3ç~ÖVh;ßÅ.ôœ"ᚆxL¡(ݨ(”6R¡–c)'>Ó³L'J¹¢–#žH8–ÏPÚH…ZŽ…ÃÄgšmA§xyW=…–Ò… *Tr,å”2… *Tr,Åÿ¶*™#iÂ5 K,-d*”6²®åXÊ)-d*”6R¡–c!(¾‘±-è”9“¬3E*–ç +T¨äXBZÁûøå& ìÏOýš™Ü +endstream +endobj +12 0 obj +425 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /Type /ExtGState /CA 0 /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Type /Pages /Kids [ 11 0 R ] /Count 1 >> +endobj +13 0 obj +<< /Creator (Matplotlib v3.9.4, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.9.4) +/CreationDate (D:20260318172825+08'00') >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001034 00000 n +0000000890 00000 n +0000000911 00000 n +0000000971 00000 n +0000000992 00000 n +0000001013 00000 n +0000000065 00000 n +0000000350 00000 n +0000000870 00000 n +0000000208 00000 n +0000000850 00000 n +0000001094 00000 n +trailer +<< /Size 14 /Root 1 0 R /Info 13 0 R >> +startxref +1251 +%%EOF diff --git a/scripts/generated_apriltags/2.0x2.0cm/5.png b/scripts/generated_apriltags/2.0x2.0cm/5.png new file mode 100644 index 0000000000000000000000000000000000000000..cca13969322eac70f0f6076779efdc1cde00cfda GIT binary patch literal 957 zcmeAS@N?(olHy`uVBq!ia0vp^Z$OxX4M>(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=TAnVBAr*7p-Z{v7$U%U`QC{i)lzxUE2aNne>s0x2ERV2N0L@-78&jH zKJ)YT*7R9-=FY2|FM8)pSt41^LN$Kjn`4>}Y*w2}lKk>YjG@keL`^`TlEiM26 literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/2.0x2.0cm/6.ai b/scripts/generated_apriltags/2.0x2.0cm/6.ai new file mode 100644 index 0000000000000000000000000000000000000000..d2f7a14e6e4e2afe39a9e72eacbda17687fead24 GIT binary patch literal 1599 zcmZuxe@q)y9N%ynbJ}GQCuW=MIl?Hz_1;~7T^XyE_6kg3wZK@iF?VgR?P2MW>jR3( z{^$%wO(tW3Wx5$M)+`$uLKca@=8T$<`ZMAZ{{Z49GKa>6e{jo2n7(%{P^fo*+`Z5H ze&73ie|*0AE{AhBqcc$XpG<*|M=1tCAbT3Ak`h4Iwl|A_whKrI$pJvSgn$?UEH-ch ztCbSNK4QqFIXLAo0(#YeKDgi0D0&evCzpH_mDL~tiNKHqTmnL(5(Y-Hr0ANU;FH1u zU`p*OvbRP=;1Isn;lxzLC<1gv6Q)p_`r1?93eb>7hsg|p@FwgP5wlCur`=*iZc)5A z9>O2rUnTk^p;V6I8zFv;CY{N`S{S{aGjeNI4LBz_P<&bhSd!kha9GAGhPb9Dhiorp zvr-HPP!KRF22Lr2;|H`egp=YBy|PavVMLH33Qbh>+|cRo8o!2{`1=p3W3j%^{uZ~N zY?*T(T#Sl@bOr1$#Gwj00SJ8*OFWz7$*Ww+;# z9BZ7k7M?A6INkx{zj^K%{|qdiH=C=!$ZfegG&eW-&y9h*(!kNr2i_Yq6t5d<^50oF z>!$y*{agR#5$&fV?r&z=W7R(%+UGErwdsn%| zeQGul*!OJHrQ_Xs*|m3l6q2+%HLjgfZJgPa_4w9HV5YpHFRwS(H5j8x-=A-FmOYwU zui}Yq7bo8B)SkFy*wOt!fOd}UnL_Y-qt_ECwfXQgo8-X7zV+_s|c|dpxV<3yeT8%-@jD@@gvtX|b%=AAP zhbPi%Uj{;)_thALCS1xjnBJIq9%szt!a_X7*7$Pz3_AvLX|W+ikV2xOPWl?@O%dk` l&~8~KMJ7{>t_b^OpcYvjX%QqSNKGrl8X%RQ&+m6q{{g3h^O67n literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/2.0x2.0cm/6.png b/scripts/generated_apriltags/2.0x2.0cm/6.png new file mode 100644 index 0000000000000000000000000000000000000000..466bef4da3527c0be649ca03dc38f577a3c3192e GIT binary patch literal 960 zcmeAS@N?(olHy`uVBq!ia0vp^Z$OxX4M>(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=x}GkMAr*7p-f{F|aTIYmnECNvxL%=dhu)^IXI)|z%xXJ+|7qztOMZoi zZ`a#1{IERe)RCa*vOz#8f3-9Vm%P@z z^mY67nT>x>QD_{*(LWVGZnAFM#Jkr1dcaO@;)5QCm!Fh(UcbHRb?%+5pZ;sUV_UzI zyoiOFKH<-qmsM?7t~~qxXMQz>W&x9wN8R)rdV1@RP#AJ&(*4d`zE*#bx`Q&`xgTeb YT&ek~!*cd^U~XdYboFyt=akR{0EdM*MF0Q* literal 0 HcmV?d00001 diff --git a/scripts/generated_apriltags/2.0x2.0cm/7.ai b/scripts/generated_apriltags/2.0x2.0cm/7.ai new file mode 100644 index 0000000000000000000000000000000000000000..8f47aa2a644b46944ecd2091e77bf7a141602ffd GIT binary patch literal 1582 zcmZuxe@q)y9CyhCJlDVsk)+J*%>{vM_I|a;b*XHnT^XYtT{{tjWw~CDQfPa4_m~~V z$!4}hlx=ZtW(GHk1Qt7GqD#z1LKbl|ghZGbSu&S}h?7J`TmovA8Q<#LBo3Vuli3^H(m z%1S~C`>-LOcEBcwAz3!BG}a#KxXBekZnBco-$JnrNCw)^7s-8b)^xcB?RKVE$;ws7vkkGKA@);}}v zKKtP9nxSi&nTM6jMX}+%FU0b1-ECOvbp2F+Zu|Sc483yopySeh$2eVG-8Xn~Qh9T8 zXEb+d|Kn=ZIb+>BB)!#LU;Oha^@|Cu3fF4;m+6S?@7&Yn96$M_eXe(p@lIiE#Qvzy zy|HKGRKcu4;~9Qq*QWcsr*lKW$>?z5*?x`xq%t=;{}p}nbid|?>v)gZ;rq1dslPti zI(0e92fCP3wr>uMbsf?83Y4He3h6dcId6IKOnIBRXnoU?XO4ZWd-2fWXWd`UmJV$A z+teq%K4_ml#_lq5IY;lk+wkVOe8EN(k}dW2_N)QW(}kvi_@z!2(D%g_#Dc zN?X+kX)z-sQWOAev0Ab&#G1(hvF@@!+dR$0QsKYyu zTCn$2EOc5hqy=>;loe3s5MX;yhQWXr9AI@uoqj85fv{cOMv|eGEuF0R%L&q{$i5B{ zHGTT2c3(5_3St||Z1M(V8NWUthpl*Elv%ei6vI(0&C?YO<8)d?`hhmZy=6C{i;) z0a{N`D-Yld2G#(Y!3svBOyLKFQy5KASs2B0C{O>jqj(;LpMe?m|ATR;yqR`9Dq99d zaoHGaK=UKRj@M^%VJI}TGVPcwJDTFsVuOkh2uezPu)71D63P`IU9yaejE5Fk6K(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=DxNNmAr*7p-Z{v7$U%U`vFC*SQ)Px0mVVpI%d{GeJ=Xp`yYX6-xx&M` zZ|n^7g>_mu#GF{T6`NWG4mx!tD7tJAP>SFXBu4l|@~_6n7R%;0%s1XD_vR3Zx-so~ zy!*(T`|IxReudI^mDr$G+V7dz)qVOy?%ci*Qpe?l7E??H)6AieFl51rkvR*7KVRl#Q}o{c*}B#DKD>8- z_ndpq`JMB-3cJO+mQiUbMek4G(*cSBG!R{N)cW-RI-462fM#Ci1qO) z+QGzSW&YK5U#(THDDU*1ywNqpDb9wX8^@OB9a&vqQ!cw(Xk>w}l8NkLT^snVt!3Y%S6Z{;|Ceh& z?sGcNABCq19vpUdKYZnGVYx2f^z9cNyZeCo-*4dC%D2wk`}-o?GTQwJkwLO&NkPOL zOq#A$vni3p2ZLZcKy?ZuI!VsPyqbtOQUhaRz`P3Q^#jc&Yy{mrVDP}CjXqIQ#(_jn z86eSQ21w>I6*{5(gy7*8zbA-g5m%N2iw#eI6IK$5o~ngOD+ZeJ1k0iXl-qc@Q4nRp z?*bu>ic@KeL9Hw|28|Fl%x$Se$yY={yCiy=+*lClUX30vaPjVX%xv-mY#Be^F9s~+ zGErKLYFe$MHH?ALs|{-QDcZ1xrq>juB^Q>Em!biqrRcc_=vY?60xy`u7|axTKp=%N zvHi1gFvkn+7@APG5Tj`wZsj6O!)C@c=rb`jjW4uCzJ^R+ zhUU_0lO^6SASr(7SNV4$%oTtRQ6x(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=I-V|$Ar*7p-Z_}d)!ew*uQzJ+?zw>8UwfV z_3k5A|6Ui~ed>0;{lY)rcQ(zwsYjB7;3gqCf6{%`d-tZ@y>rZVWB2a8Z>!sLp9+Z+ z??> +endobj +8 0 obj +<< /Font 3 0 R /XObject 7 0 R /ExtGState 4 0 R /Pattern 5 0 R +/Shading 6 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] >> +endobj +11 0 obj +<< /Type /Page /Parent 2 0 R /Resources 8 0 R +/MediaBox [ 0 0 56.6929133858 56.6929133858 ] /Contents 9 0 R +/Annots 10 0 R >> +endobj +9 0 obj +<< /Length 12 0 R /Filter /FlateDecode >> +stream +xœµ—KjÃ0@÷:…N H–åϲȺí¢é'D…6Ð^¿NJ<ÏAZ2›ÑÃ=4ã¬î·ßï›íãúÖÞ=™•¬6ìÎ|Z?ýR纱CŒC.V_[ûl?ìê&Ø×ƒnÓó»éú™®õqmð¾ÍFb‰ö ~ZùÓý͘ó`ÿ«à/¦µ1Éu§l +Î7±kš£ÊK´m“‹©1é9¡Š¡lMè÷ýз‹m…ÆÑuCòq¤!á5 ›Æu}º´PŒ­ó!õí°PJÉ å(t¬”t¦´‘'´ …š*Ü)A\l :(+ xMÃRƒåJMAaƒ JŽâ@ÇrMAaƒ JŽ…BåÊIÜ .¶]›zVšPé¡X>/¡´‘ ZŽ…Cà÷ÂmAçQÃSÔ™ŒT”˜Š °A%ÇRƒåJõ@aƒ JŽ2{éXî;PØ ƒ’c¡™r¥ï„Ê8D7êLo*–;LbÚH¬åXú +r­ïfJÉ åXê¥\é;¡´‘ ZŽ…fÊ•¾#v# N7R±\=PØ ƒ’£L6:–k + +dPr,*Wdð¿æü + öçg~ŽzÜÖ +endstream +endobj +12 0 obj +442 +endobj +10 0 obj +[ ] +endobj +3 0 obj +<< >> +endobj +4 0 obj +<< /A1 << /Type /ExtGState /CA 0 /ca 1 >> >> +endobj +5 0 obj +<< >> +endobj +6 0 obj +<< >> +endobj +7 0 obj +<< >> +endobj +2 0 obj +<< /Type /Pages /Kids [ 11 0 R ] /Count 1 >> +endobj +13 0 obj +<< /Creator (Matplotlib v3.9.4, https://matplotlib.org) +/Producer (Matplotlib pdf backend v3.9.4) +/CreationDate (D:20260318172825+08'00') >> +endobj +xref +0 14 +0000000000 65535 f +0000000016 00000 n +0000001051 00000 n +0000000907 00000 n +0000000928 00000 n +0000000988 00000 n +0000001009 00000 n +0000001030 00000 n +0000000065 00000 n +0000000350 00000 n +0000000887 00000 n +0000000208 00000 n +0000000867 00000 n +0000001111 00000 n +trailer +<< /Size 14 /Root 1 0 R /Info 13 0 R >> +startxref +1268 +%%EOF diff --git a/scripts/generated_apriltags/2.0x2.0cm/9.png b/scripts/generated_apriltags/2.0x2.0cm/9.png new file mode 100644 index 0000000000000000000000000000000000000000..cfee9db2ed21bc87d255a50cc21621402a90dfd5 GIT binary patch literal 959 zcmeAS@N?(olHy`uVBq!ia0vp^Z$OxX4M>(>_7VY7mL;wcCBgY=CFO}lsSLh}B?US8 zB{`W%3T3H9#hLke#(I`|COQfkB_##LR{Hw6a0Pn#Md|u-6YgaI4dE>Ch%9Dc&{GCs z#)_r(Weg0=I-V|$Ar*7p-Z_}da=i(IT0oNY46|0;&b2BAKbqg#b1_4x&f%x zP52u7XxG}>o4a$&^7ozJCR|f5PNYLn%|Z6f$@fC}?;nY6&whXCwtW$WPW(ADICw^l z5aq4{hVhg3Bk9jpRUPH{_3d}gzxGf0_a7Z1D-w|X{xkMb?$f(>UwzX%zmwF+L^cPQ zB8->kf9c}9^j7og+5ZWPzE0$(Ae}&rep23txG%Iqx<=mNX{SO>3pe#Pa bkMn<8r~G66sq`9PW@7Mk^>bP0l+XkK=^8f4 literal 0 HcmV?d00001 diff --git a/ui/__pycache__/__init__.cpython-39.pyc b/ui/__pycache__/__init__.cpython-39.pyc index 39c71599e068bace69221b39466a8eec171b64ad..d6bc60f6dc95fd945efc1fd6c90dfb922d0c736e 100644 GIT binary patch delta 21 acmeBV>}2Fk}2Fkx}gNB@sD<$9usv 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 literal 37393 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 XFc#Yx8ag#}X6O@v&VN=anlSzsfq{#D diff --git a/ui/__pycache__/camera_page.cpython-39.pyc b/ui/__pycache__/camera_page.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d72dbbe1a6004f7fe5948054776cc7e3cf2e6682 GIT binary patch literal 16282 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 Q=|nL({behSAOBDOU$4n#p#T5? literal 0 HcmV?d00001 diff --git a/ui/__pycache__/plot_page.cpython-39.pyc b/ui/__pycache__/plot_page.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..185db7ba6d95785271b14aeeb450125fdd3c94cf GIT binary patch literal 12406 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 literal 0 HcmV?d00001 diff --git a/ui/__pycache__/rgb_image_page.cpython-39.pyc b/ui/__pycache__/rgb_image_page.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b309d484aca4d9fc450f1a492121d33366a2494e GIT binary patch literal 20508 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 M!sLm`pE9ogAKC718vp None: @@ -223,7 +233,12 @@ class MainWindow(QtWidgets.QMainWindow): layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.center_tabs = QtWidgets.QTabWidget() - self.center_tabs.addTab(self._build_live_panel(), "Plot") + self.plot_page = PlotPage(self.joint_names, parent=self) + self.center_tabs.addTab(self.plot_page, "Plot") + self.camera_page = CameraPage(address_provider=self._grpc_address, parent=self) + self.center_tabs.addTab(self.camera_page, "Camera") + self.rgb_image_page = RGBImagePage(address_provider=self._grpc_address, parent=self) + self.center_tabs.addTab(self.rgb_image_page, "RGB Image") self.center_tabs.setCurrentIndex(0) self.center_tabs.currentChanged.connect(self._on_center_tab_changed) layout.addWidget(self.center_tabs) @@ -242,13 +257,6 @@ class MainWindow(QtWidgets.QMainWindow): status.setFeatures(QtWidgets.QDockWidget.AllDockWidgetFeatures) self.addDockWidget(QtCore.Qt.RightDockWidgetArea, status) - log_dock = QtWidgets.QDockWidget("Log") - log_dock.setWidget(self._build_log_panel()) - log_dock.setMinimumHeight(140) - log_dock.setMaximumHeight(220) - log_dock.setFeatures(QtWidgets.QDockWidget.AllDockWidgetFeatures) - self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, log_dock) - def _build_status_panel(self) -> QtWidgets.QWidget: panel = QtWidgets.QWidget() layout = QtWidgets.QVBoxLayout(panel) @@ -365,25 +373,6 @@ class MainWindow(QtWidgets.QMainWindow): return form - def _build_log(self) -> QtWidgets.QWidget: - self.log = QtWidgets.QTextEdit() - self.log.setReadOnly(True) - return self.log - - def _build_log_panel(self) -> QtWidgets.QWidget: - panel = QtWidgets.QWidget() - layout = QtWidgets.QVBoxLayout(panel) - header = QtWidgets.QHBoxLayout() - header.addWidget(QtWidgets.QLabel("Runtime logs")) - header.addStretch(1) - log_widget = self._build_log() - self.clear_log_btn = QtWidgets.QPushButton("Clear") - self.clear_log_btn.clicked.connect(log_widget.clear) - header.addWidget(self.clear_log_btn) - layout.addLayout(header) - layout.addWidget(log_widget) - return panel - def _build_pose_panel(self) -> QtWidgets.QGroupBox: group = QtWidgets.QGroupBox("GetPose") layout = QtWidgets.QVBoxLayout(group) @@ -418,38 +407,6 @@ class MainWindow(QtWidgets.QMainWindow): layout.addLayout(grid) return group - def _build_live_panel(self) -> QtWidgets.QWidget: - tab = QtWidgets.QWidget() - layout = QtWidgets.QVBoxLayout(tab) - - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(4) - - self.figure = Figure(figsize=(5, 4)) - self.canvas = FigureCanvas(self.figure) - self.ax_pos = self.figure.add_subplot(2, 1, 1) - self.ax_vel = self.figure.add_subplot(2, 1, 2) - self.ax_pos.set_title("Position (rad)") - self.ax_vel.set_title("Velocity (rad/s)") - self.ax_pos.grid(True, alpha=0.3) - self.ax_vel.grid(True, alpha=0.3) - self.pos_lines: Dict[str, object] = {} - self.vel_lines: Dict[str, object] = {} - - self.time_hist: Deque[float] = deque(maxlen=300) - self._last_plot_time: Optional[float] = None - self.pos_hist: Dict[str, Deque[float]] = {name: deque(maxlen=300) for name in self.joint_names} - self.vel_hist: Dict[str, Deque[float]] = {name: deque(maxlen=300) for name in self.joint_names} - self._plot_start = time.time() - - self.canvas.setSizePolicy( - QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding - ) - self.toolbar = NavigationToolbar(self.canvas, self) - layout.addWidget(self.toolbar) - layout.addWidget(self.canvas) - return tab - def _build_table(self) -> QtWidgets.QWidget: self.table = QtWidgets.QTableWidget(len(self.joint_names), 4) self.table.setHorizontalHeaderLabels( @@ -497,6 +454,9 @@ class MainWindow(QtWidgets.QMainWindow): if hasattr(self, "log") and self.log is not None: self.log.append(text.rstrip()) + def _grpc_address(self) -> str: + return f"{self.host_edit.text().strip()}:{self.port_edit.value()}" + def _base_args(self) -> list[str]: return [ "-m", @@ -561,8 +521,7 @@ class MainWindow(QtWidgets.QMainWindow): def _on_stop_pose(self) -> None: if self._joint_worker: self._append_log("[info] Stopping JointState...") - self._joint_worker.stop() - self._joint_worker = None + self._stop_jointstate_worker() def _on_torque_on(self) -> None: args = self._base_args() + ["torque_on"] @@ -588,67 +547,6 @@ class MainWindow(QtWidgets.QMainWindow): self._last_data = data self._render_joint_table_and_plot() - def _append_plot(self, name: str, pos: float, vel: float) -> None: - t = time.time() - self._plot_start - if self._last_plot_time is None or t > self._last_plot_time: - self.time_hist.append(t) - self._last_plot_time = t - self.pos_hist[name].append(pos) - self.vel_hist[name].append(vel) - - def _refresh_plot(self, selected: set) -> None: - for name in list(self.pos_lines.keys()): - if name not in selected: - self.pos_lines[name].remove() - self.vel_lines[name].remove() - del self.pos_lines[name] - del self.vel_lines[name] - - for name in selected: - if name not in self.pos_lines: - (pos_line,) = self.ax_pos.plot([], [], label=name) - (vel_line,) = self.ax_vel.plot([], [], label=name) - self.pos_lines[name] = pos_line - self.vel_lines[name] = vel_line - x_data = list(self.time_hist) - y_pos_full = list(self.pos_hist[name]) - y_vel_full = list(self.vel_hist[name]) - n_pos = min(len(x_data), len(y_pos_full)) - n_vel = min(len(x_data), len(y_vel_full)) - if n_pos: - self.pos_lines[name].set_data(x_data[-n_pos:], y_pos_full[-n_pos:]) - else: - self.pos_lines[name].set_data([], []) - if n_vel: - self.vel_lines[name].set_data(x_data[-n_vel:], y_vel_full[-n_vel:]) - else: - self.vel_lines[name].set_data([], []) - - if len(self.time_hist) > 1 and selected: - self.ax_pos.set_xlim(self.time_hist[0], self.time_hist[-1]) - self.ax_vel.set_xlim(self.time_hist[0], self.time_hist[-1]) - self.ax_pos.relim() - self.ax_pos.autoscale_view(scalex=False, scaley=True) - self.ax_vel.relim() - self.ax_vel.autoscale_view(scalex=False, scaley=True) - if selected: - self.ax_pos.legend(loc="upper right", fontsize=8, ncol=2) - self.ax_vel.legend(loc="upper right", fontsize=8, ncol=2) - self.canvas.draw_idle() - - def _reset_plot_data(self) -> None: - self.time_hist.clear() - for name in self.joint_names: - self.pos_hist[name].clear() - self.vel_hist[name].clear() - self._plot_start = time.time() - self._last_plot_time = None - for line in self.pos_lines.values(): - line.set_data([], []) - for line in self.vel_lines.values(): - line.set_data([], []) - self.canvas.draw_idle() - def _encode_joint_list(self, joint_list: list[dict]) -> str: return ";".join(f"{j['joint_name']}:{j['rad']}" for j in joint_list) @@ -728,8 +626,7 @@ class MainWindow(QtWidgets.QMainWindow): def _on_stop_get_pose(self) -> None: if self._pose_worker: self._append_log("[info] Stopping GetPose...") - self._pose_worker.stop() - self._pose_worker = None + self._stop_pose_worker() def _update_pose(self, data: Dict[str, float]) -> None: for key, value in data.items(): @@ -751,25 +648,57 @@ class MainWindow(QtWidgets.QMainWindow): if name in selected: selected_lines.append(f"{name}: {pos:.4f} rad, {vel:.4f} rad/s") - self._append_plot(name, pos, vel) if selected_lines: + preview = selected_lines[:STATUS_PREVIEW_JOINTS] + hidden_count = max(0, len(selected_lines) - STATUS_PREVIEW_JOINTS) + suffix = f" | +{hidden_count} more" if hidden_count else "" self.statusBar().showMessage( - f"Selected joints: {len(selected)} | " + " | ".join(selected_lines) + f"Selected joints: {len(selected)} | " + " | ".join(preview) + suffix ) else: self.statusBar().showMessage(f"Selected joints: {len(selected)}") - if self._is_plot_visible(): - self._refresh_plot(selected) + if self.plot_page is not None: + self.plot_page.update_data( + self._last_data, + selected, + refresh=self._is_plot_visible(), + ) return def _is_plot_visible(self) -> bool: return hasattr(self, "center_tabs") and self.center_tabs.currentIndex() == 0 def _on_center_tab_changed(self, index: int) -> None: - if index == 0: - self._render_joint_table_and_plot() + if index == 0 and self.plot_page is not None: + self.plot_page.refresh(self._plot_selected) + + def _stop_jointstate_worker(self) -> None: + worker = self._joint_worker + self._joint_worker = None + if worker: + worker.stop() + worker.wait(2000) + + def _stop_pose_worker(self) -> None: + worker = self._pose_worker + self._pose_worker = None + if worker: + worker.stop() + worker.wait(2000) + + def closeEvent(self, event: QtGui.QCloseEvent) -> None: + self._stop_jointstate_worker() + self._stop_pose_worker() + if self._pose_process and self._pose_process.state() != QtCore.QProcess.NotRunning: + self._pose_process.terminate() + self._pose_process.waitForFinished(1000) + if self.camera_page is not None: + self.camera_page.shutdown() + if self.rgb_image_page is not None: + self.rgb_image_page.shutdown() + super().closeEvent(event) def main() -> int: diff --git a/ui/camera_page.py b/ui/camera_page.py new file mode 100644 index 0000000..f5981ee --- /dev/null +++ b/ui/camera_page.py @@ -0,0 +1,605 @@ +import time +from datetime import datetime +from pathlib import Path +from typing import Callable, Optional + +from PyQt5 import QtCore, QtGui, QtWidgets + +try: + import av +except Exception as exc: + av = None + AV_IMPORT_ERROR = exc +else: + AV_IMPORT_ERROR = None + +try: + import cv2 +except Exception as exc: + cv2 = None + CV2_IMPORT_ERROR = exc +else: + CV2_IMPORT_ERROR = None + +from clients._path_setup import ensure_paths + +ensure_paths() + +from clients.camera_client import CameraClient + + +class CameraStreamThread(QtCore.QThread): + frame_received = QtCore.pyqtSignal(object, int, int) + status_update = QtCore.pyqtSignal(dict) + error_occurred = QtCore.pyqtSignal(str) + keyframe_received = QtCore.pyqtSignal() + + def __init__(self, client, device_id, codec="h264", wait_for_keyframe=True): + super().__init__() + self.client = client + self.device_id = device_id + self.codec_name = codec + self.wait_for_keyframe = wait_for_keyframe + self.streaming = False + self.decoder = None + self.decoder_error = None + self.frame_count = 0 + self.fps_counter = 0 + self.fps_time = time.time() + self.keyframe_count = 0 + self._init_decoder() + + def _init_decoder(self): + if av is None: + self.decoder = None + self.decoder_error = f"PyAV unavailable: {AV_IMPORT_ERROR}" + return + try: + self.decoder = av.CodecContext.create(self.codec_name, "r") + self.decoder.thread_count = 1 + if hasattr(self.decoder, "flags"): + try: + self.decoder.flags |= av.codec.context.AV_CODEC_FLAG_LOW_DELAY + except Exception: + pass + except Exception as exc: + self.decoder = None + self.decoder_error = f"Decoder init failed: {exc}" + + def _decode(self, frame_data): + if self.decoder is None or not frame_data: + return None + + packet = av.Packet(frame_data) + frames = self.decoder.decode(packet) + if not frames: + return None + + return frames[0].to_ndarray(format="rgb24") + + def _start_camera(self): + response = self.client.start_camera(self.device_id) + if not response.header.success: + self.error_occurred.emit(response.header.error_message or "StartCamera failed") + return False + return True + + def _stop_camera(self): + try: + self.client.stop_camera(self.device_id) + except Exception: + pass + + def run(self): + if self.decoder is None: + if self.decoder_error: + self.error_occurred.emit(self.decoder_error) + return + + try: + if not self._start_camera(): + return + + self.streaming = True + self.frame_count = 0 + self.fps_counter = 0 + self.fps_time = time.time() + self.keyframe_count = 0 + keyframe_seen = False + + responses = self.client.get_rgb_image_stream( + device_id=self.device_id, + stop_requested=lambda: not self.streaming, + ) + + for response in responses: + if not self.streaming: + break + + if not response.header.success: + raise RuntimeError(response.header.error_message or "GetRGBImageStream failed") + + frame_data = response.color_frame.data + is_keyframe = response.color_frame.is_key_frame + + if self.wait_for_keyframe and not keyframe_seen and not is_keyframe: + continue + + if is_keyframe and not keyframe_seen: + keyframe_seen = True + self.keyframe_count += 1 + self.keyframe_received.emit() + + image = self._decode(frame_data) + if image is None: + continue + + self.fps_counter += 1 + self.frame_count += 1 + + now = time.time() + if now - self.fps_time >= 1.0: + self.status_update.emit( + { + "fps": self.fps_counter, + "frames": self.frame_count, + "keyframes": self.keyframe_count, + } + ) + self.fps_counter = 0 + self.fps_time = now + + height, width = image.shape[:2] + self.frame_received.emit(image, width, height) + except Exception as exc: + self.error_occurred.emit(f"Stream error: {exc}") + finally: + self.streaming = False + self._stop_camera() + + def stop(self): + self.streaming = False + + +class CameraPage(QtWidgets.QWidget): + CODEC_OPTIONS = { + "H.264 (AVC)": "h264", + "H.265 (HEVC)": "hevc", + } + + def __init__( + self, + address_provider: Optional[Callable[[], str]] = None, + default_device_id: str = "right_hand_cam", + parent=None, + ): + super().__init__(parent) + self.address_provider = address_provider + self.connected = False + self.streaming = False + self.recording = False + self.stream_client = None + self.stream_thread = None + self.video_writer = None + self.current_frame = None + self.frame_width = 0 + self.frame_height = 0 + self.record_path = Path("./recordings") + self.record_path.mkdir(exist_ok=True) + + self._create_ui(default_device_id) + + self.display_timer = QtCore.QTimer(self) + self.display_timer.timeout.connect(self._update_display) + self.display_timer.start(30) + + def _create_ui(self, default_device_id: str): + layout = QtWidgets.QVBoxLayout(self) + + conn_group = QtWidgets.QGroupBox("Camera") + conn_layout = QtWidgets.QGridLayout(conn_group) + + conn_layout.addWidget(QtWidgets.QLabel("Device ID:"), 0, 0) + self.device_id = QtWidgets.QLineEdit(default_device_id) + conn_layout.addWidget(self.device_id, 0, 1) + + conn_layout.addWidget(QtWidgets.QLabel("Codec:"), 0, 2) + self.codec_combo = QtWidgets.QComboBox() + self.codec_combo.addItems(list(self.CODEC_OPTIONS)) + self.codec_combo.setCurrentText("H.264 (AVC)") + conn_layout.addWidget(self.codec_combo, 0, 3) + + self.wait_keyframe = QtWidgets.QCheckBox("Wait for first keyframe") + self.wait_keyframe.setChecked(True) + conn_layout.addWidget(self.wait_keyframe, 0, 4, 1, 2) + + if self.address_provider is None: + conn_layout.addWidget(QtWidgets.QLabel("Server IP:"), 1, 0) + self.server_ip = QtWidgets.QLineEdit("192.168.0.222") + conn_layout.addWidget(self.server_ip, 1, 1) + + conn_layout.addWidget(QtWidgets.QLabel("Port:"), 1, 2) + self.server_port = QtWidgets.QLineEdit("50052") + self.server_port.setMaximumWidth(100) + conn_layout.addWidget(self.server_port, 1, 3) + else: + conn_layout.addWidget(QtWidgets.QLabel("Server:"), 1, 0) + self.address_label = QtWidgets.QLabel("") + self.address_label.setStyleSheet("font-weight: bold;") + conn_layout.addWidget(self.address_label, 1, 1, 1, 3) + + conn_layout.addWidget(QtWidgets.QLabel("Timeout (s):"), 1, 4) + self.timeout_spin = QtWidgets.QSpinBox() + self.timeout_spin.setRange(1, 10) + self.timeout_spin.setValue(3) + conn_layout.addWidget(self.timeout_spin, 1, 5) + + conn_layout.addWidget(QtWidgets.QLabel("Record Dir:"), 2, 0) + self.record_dir = QtWidgets.QLineEdit(str(self.record_path)) + conn_layout.addWidget(self.record_dir, 2, 1, 1, 4) + + self.browse_btn = QtWidgets.QPushButton("Browse") + self.browse_btn.clicked.connect(self._browse_dir) + conn_layout.addWidget(self.browse_btn, 2, 5) + + button_layout = QtWidgets.QHBoxLayout() + self.connect_btn = QtWidgets.QPushButton("Connect") + self.connect_btn.clicked.connect(self._connect) + button_layout.addWidget(self.connect_btn) + + self.preview_btn = QtWidgets.QPushButton("Preview") + self.preview_btn.clicked.connect(self._toggle_preview) + self.preview_btn.setEnabled(False) + button_layout.addWidget(self.preview_btn) + + self.record_btn = QtWidgets.QPushButton("Record") + self.record_btn.clicked.connect(self._toggle_recording) + self.record_btn.setEnabled(False) + button_layout.addWidget(self.record_btn) + + self.disconnect_btn = QtWidgets.QPushButton("Disconnect") + self.disconnect_btn.clicked.connect(self._disconnect) + self.disconnect_btn.setEnabled(False) + button_layout.addWidget(self.disconnect_btn) + button_layout.addStretch() + conn_layout.addLayout(button_layout, 3, 0, 1, 6) + + layout.addWidget(conn_group) + + status_group = QtWidgets.QGroupBox("Stream Status") + status_layout = QtWidgets.QHBoxLayout(status_group) + + self.conn_status = QtWidgets.QLabel("Disconnected") + self.conn_status.setStyleSheet("color: red;") + status_layout.addWidget(self.conn_status) + + status_layout.addWidget(QtWidgets.QLabel("|")) + self.codec_status = QtWidgets.QLabel("Codec: H.264 (AVC)") + status_layout.addWidget(self.codec_status) + + status_layout.addWidget(QtWidgets.QLabel("|")) + self.fps_status = QtWidgets.QLabel("FPS: 0") + status_layout.addWidget(self.fps_status) + + status_layout.addWidget(QtWidgets.QLabel("|")) + self.frame_status = QtWidgets.QLabel("Frames: 0") + status_layout.addWidget(self.frame_status) + + status_layout.addWidget(QtWidgets.QLabel("|")) + self.resolution_status = QtWidgets.QLabel("Resolution: -") + status_layout.addWidget(self.resolution_status) + + status_layout.addWidget(QtWidgets.QLabel("|")) + self.keyframe_status = QtWidgets.QLabel("Keyframe: waiting") + self.keyframe_status.setStyleSheet("color: orange;") + status_layout.addWidget(self.keyframe_status) + + status_layout.addWidget(QtWidgets.QLabel("|")) + self.record_status = QtWidgets.QLabel("Record: idle") + self.record_status.setStyleSheet("color: gray;") + status_layout.addWidget(self.record_status) + status_layout.addStretch() + + layout.addWidget(status_group) + + video_group = QtWidgets.QGroupBox("Preview") + video_layout = QtWidgets.QVBoxLayout(video_group) + + self.video_label = QtWidgets.QLabel() + self.video_label.setAlignment(QtCore.Qt.AlignCenter) + self.video_label.setMinimumSize(640, 480) + self.video_label.setStyleSheet("background-color: black; border: 1px solid gray;") + video_layout.addWidget(self.video_label) + + layout.addWidget(video_group, 1) + + info_frame = QtWidgets.QFrame() + info_frame.setFrameStyle(QtWidgets.QFrame.StyledPanel | QtWidgets.QFrame.Sunken) + info_layout = QtWidgets.QHBoxLayout(info_frame) + info_label = QtWidgets.QLabel( + "Connect to the camera service, start preview, then optionally record decoded frames." + ) + info_label.setStyleSheet("color: gray; padding: 5px;") + info_layout.addWidget(info_label) + layout.addWidget(info_frame) + + self.status_bar = QtWidgets.QStatusBar() + self.status_bar.showMessage("Idle") + layout.addWidget(self.status_bar) + + def refresh_address_hint(self): + if self.address_provider is None or not hasattr(self, "address_label"): + return + try: + self.address_label.setText(self.address_provider()) + except Exception: + self.address_label.setText("(app settings unavailable)") + + def _browse_dir(self): + directory = QtWidgets.QFileDialog.getExistingDirectory( + self, "Select record directory", self.record_dir.text() + ) + if directory: + self.record_dir.setText(directory) + self.record_path = Path(directory) + self.record_path.mkdir(exist_ok=True) + + def _get_server_addr(self): + if self.address_provider is not None: + return self.address_provider() + return f"{self.server_ip.text().strip()}:{self.server_port.text().strip()}" + + def _get_codec(self): + return self.CODEC_OPTIONS[self.codec_combo.currentText()] + + def _connect(self): + if self.connected: + return + + self.refresh_address_hint() + self.status_bar.showMessage("Connecting to camera service...") + self.codec_status.setText(f"Codec: {self.codec_combo.currentText()}") + + try: + self.stream_client = CameraClient( + address=self._get_server_addr(), + timeout=self.timeout_spin.value(), + ) + except Exception as exc: + self.stream_client = None + self._on_error(f"Connect failed: {exc}") + return + + self.connected = True + self.conn_status.setText("Connected") + self.conn_status.setStyleSheet("color: green;") + self.connect_btn.setEnabled(False) + self.preview_btn.setEnabled(True) + self.disconnect_btn.setEnabled(True) + self.codec_combo.setEnabled(False) + self.wait_keyframe.setEnabled(False) + self.timeout_spin.setEnabled(False) + if self.address_provider is None: + self.server_ip.setEnabled(False) + self.server_port.setEnabled(False) + self.status_bar.showMessage("Connected to camera service", 3000) + + def _disconnect(self): + if self.streaming: + self._stop_preview() + + if self.recording: + self._stop_recording() + + if self.stream_client: + self.stream_client.close() + self.stream_client = None + + self.connected = False + self.stream_thread = None + self.conn_status.setText("Disconnected") + self.conn_status.setStyleSheet("color: red;") + self.connect_btn.setEnabled(True) + self.preview_btn.setEnabled(False) + self.preview_btn.setText("Preview") + self.record_btn.setEnabled(False) + self.record_btn.setText("Record") + self.disconnect_btn.setEnabled(False) + self.codec_combo.setEnabled(True) + self.wait_keyframe.setEnabled(True) + self.timeout_spin.setEnabled(True) + if self.address_provider is None: + self.server_ip.setEnabled(True) + self.server_port.setEnabled(True) + self._clear_preview() + self._reset_status_labels() + self.status_bar.showMessage("Disconnected", 3000) + + def _toggle_preview(self): + if self.streaming: + self._stop_preview() + else: + self._start_preview() + + def _start_preview(self): + if not self.connected or self.stream_client is None or self.streaming: + return + + self.stream_thread = CameraStreamThread( + client=self.stream_client, + device_id=self.device_id.text().strip(), + codec=self._get_codec(), + wait_for_keyframe=self.wait_keyframe.isChecked(), + ) + self.stream_thread.frame_received.connect(self._on_frame_received) + self.stream_thread.status_update.connect(self._on_status_update) + self.stream_thread.error_occurred.connect(self._on_error) + self.stream_thread.keyframe_received.connect(self._on_keyframe_received) + self.stream_thread.finished.connect(self._on_stream_finished) + + self.streaming = True + self.preview_btn.setText("Stop") + self.record_btn.setEnabled(True) + + if self.wait_keyframe.isChecked(): + self.keyframe_status.setText("Keyframe: waiting") + self.keyframe_status.setStyleSheet("color: orange;") + else: + self.keyframe_status.setText("Keyframe: not required") + self.keyframe_status.setStyleSheet("color: gray;") + + self.status_bar.showMessage("Starting preview...") + self.stream_thread.start() + + def _stop_preview(self): + if self.recording: + self._stop_recording() + + thread = self.stream_thread + if thread is not None: + thread.stop() + thread.wait(2000) + if self.stream_thread is thread: + self._on_stream_finished() + else: + self._on_stream_finished() + + def _on_stream_finished(self): + if self.recording: + self._stop_recording() + self.streaming = False + self.stream_thread = None + self.preview_btn.setText("Preview") + self.record_btn.setEnabled(False) + self.keyframe_status.setText("Keyframe: waiting") + self.keyframe_status.setStyleSheet("color: orange;") + self.status_bar.showMessage("Preview stopped", 3000) + + def _toggle_recording(self): + if self.recording: + self._stop_recording() + else: + self._start_recording() + + def _start_recording(self): + if not self.streaming: + QtWidgets.QMessageBox.warning(self, "Warning", "Start preview before recording.") + return + if cv2 is None: + QtWidgets.QMessageBox.critical(self, "Error", f"OpenCV unavailable: {CV2_IMPORT_ERROR}") + return + + self.record_path = Path(self.record_dir.text()) + self.record_path.mkdir(parents=True, exist_ok=True) + + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + codec_name = self.codec_combo.currentText().replace(" ", "_") + filename = self.record_path / f"rec_{self.device_id.text()}_{codec_name}_{timestamp}.mp4" + + if self.current_frame is not None: + height, width = self.current_frame.shape[:2] + else: + width, height = 640, 480 + + fourcc = cv2.VideoWriter_fourcc(*"mp4v") + self.video_writer = cv2.VideoWriter(str(filename), fourcc, 30.0, (width, height)) + + if not self.video_writer.isOpened(): + self.video_writer = None + QtWidgets.QMessageBox.critical(self, "Error", "Failed to create the output video file.") + return + + self.recording = True + self.record_btn.setText("Stop Recording") + self.record_status.setText("Record: active") + self.record_status.setStyleSheet("color: red;") + self.status_bar.showMessage(f"Recording to {filename.name}", 3000) + + def _stop_recording(self): + if self.video_writer: + self.video_writer.release() + self.video_writer = None + + self.recording = False + self.record_btn.setText("Record") + self.record_status.setText("Record: idle") + self.record_status.setStyleSheet("color: gray;") + self.status_bar.showMessage("Recording stopped", 3000) + + def _on_frame_received(self, frame, width, height): + self.current_frame = frame + self.frame_width = width + self.frame_height = height + + if self.recording and self.video_writer and cv2 is not None: + self.video_writer.write(cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)) + + def _update_display(self): + if self.current_frame is None: + return + + bytes_per_line = 3 * self.frame_width + qimage = QtGui.QImage( + self.current_frame.data, + self.frame_width, + self.frame_height, + bytes_per_line, + QtGui.QImage.Format_RGB888, + ) + + pixmap = QtGui.QPixmap.fromImage(qimage).scaled( + self.video_label.size(), + QtCore.Qt.KeepAspectRatio, + QtCore.Qt.SmoothTransformation, + ) + self.video_label.setPixmap(pixmap) + self.resolution_status.setText(f"Resolution: {self.frame_width}x{self.frame_height}") + + def _on_status_update(self, stats): + self.fps_status.setText(f"FPS: {stats['fps']}") + self.frame_status.setText(f"Frames: {stats['frames']}") + + def _on_keyframe_received(self): + if self.wait_keyframe.isChecked(): + self.keyframe_status.setText("Keyframe: received") + self.keyframe_status.setStyleSheet("color: green;") + + def _on_error(self, message): + QtWidgets.QMessageBox.critical(self, "Error", message) + self.status_bar.showMessage(message, 5000) + + def _clear_preview(self): + self.video_label.clear() + self.current_frame = None + self.frame_width = 0 + self.frame_height = 0 + + def _reset_status_labels(self): + self.fps_status.setText("FPS: 0") + self.frame_status.setText("Frames: 0") + self.resolution_status.setText("Resolution: -") + self.keyframe_status.setText("Keyframe: waiting") + self.keyframe_status.setStyleSheet("color: orange;") + self.record_status.setText("Record: idle") + self.record_status.setStyleSheet("color: gray;") + + def shutdown(self): + if self.streaming: + self._stop_preview() + if self.recording: + self._stop_recording() + if self.connected: + self._disconnect() + + +class CameraWindow(QtWidgets.QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle("Camera Stream Player") + self.resize(1000, 800) + self.camera_page = CameraPage(parent=self) + self.setCentralWidget(self.camera_page) + + def closeEvent(self, event): + self.camera_page.shutdown() + super().closeEvent(event) diff --git a/ui/plot_page.py b/ui/plot_page.py new file mode 100644 index 0000000..15f23ae --- /dev/null +++ b/ui/plot_page.py @@ -0,0 +1,396 @@ +from __future__ import annotations + +import time +from dataclasses import dataclass, field +from typing import Dict, Optional, Tuple + +import numpy as np +from PyQt5 import QtCore, QtWidgets + +try: + import pyqtgraph as pg +except Exception as exc: + pg = None + PYQTGRAPH_IMPORT_ERROR = exc +else: + PYQTGRAPH_IMPORT_ERROR = None + + +if pg is None: + InteractivePlotViewBox = None +else: + class InteractivePlotViewBox(pg.ViewBox): + def wheelEvent(self, ev, axis=None): + modifiers = QtWidgets.QApplication.keyboardModifiers() + target_axis = 0 if modifiers & QtCore.Qt.ControlModifier else 1 + super().wheelEvent(ev, axis=target_axis) + + +MAX_RENDER_POINTS = 600 +REFRESH_INTERVAL_MS = 80 +LEGEND_LIMIT = 8 +POSITION_Y_RANGE = (-3.141592653589793, 3.141592653589793) +VELOCITY_Y_RANGE = (-5.0, 5.0) +TIME_WINDOW_OPTIONS = ( + ("1 min", 60.0), + ("5 min", 300.0), + ("All", None), +) + + +@dataclass +class MultiResolutionSeries: + levels: list[list[tuple[float, float]]] = field(default_factory=lambda: [[]]) + carry: list[list[tuple[float, float]]] = field(default_factory=lambda: [[]]) + + def clear(self) -> None: + self.levels = [[]] + self.carry = [[]] + + def append(self, t_value: float, y_value: float) -> None: + self._append_level(0, (t_value, y_value)) + + def sample(self, max_points: int, min_time: Optional[float] = None) -> tuple[np.ndarray, np.ndarray]: + raw_points = self.levels[0] + if not raw_points: + return np.empty(0, dtype=float), np.empty(0, dtype=float) + + points = self._slice_points(raw_points, min_time) + if not points: + return np.empty(0, dtype=float), np.empty(0, dtype=float) + + for level_points in self.levels: + sliced_points = self._slice_points(level_points, min_time) + if not sliced_points: + continue + points = sliced_points + if len(sliced_points) <= max_points: + break + + if len(points) > max_points: + step = max(1, len(points) // max_points) + points = points[::step] + + sampled = list(points) + first_raw = self._slice_points(raw_points, min_time)[0] + last_raw = raw_points[-1] + if sampled[0][0] > first_raw[0]: + sampled.insert(0, first_raw) + if sampled[-1][0] < last_raw[0]: + sampled.append(last_raw) + + x_values = np.fromiter((point[0] for point in sampled), dtype=float, count=len(sampled)) + y_values = np.fromiter((point[1] for point in sampled), dtype=float, count=len(sampled)) + return x_values, y_values + + def _append_level(self, level: int, point: tuple[float, float]) -> None: + while len(self.levels) <= level: + self.levels.append([]) + self.carry.append([]) + + self.levels[level].append(point) + bucket = self.carry[level] + bucket.append(point) + if len(bucket) < 2: + return + + point_a, point_b = bucket + bucket.clear() + merged = ( + (point_a[0] + point_b[0]) * 0.5, + (point_a[1] + point_b[1]) * 0.5, + ) + self._append_level(level + 1, merged) + + def _slice_points( + self, + points: list[tuple[float, float]], + min_time: Optional[float], + ) -> list[tuple[float, float]]: + if not points: + return [] + if min_time is None or min_time <= points[0][0]: + return points + + left = 0 + right = len(points) + while left < right: + mid = (left + right) // 2 + if points[mid][0] < min_time: + left = mid + 1 + else: + right = mid + + start = max(0, left - 1) + return points[start:] + + +class PlotPage(QtWidgets.QWidget): + def __init__(self, joint_names: list[str], parent=None) -> None: + super().__init__(parent) + self.joint_names = joint_names + self.pos_lines: Dict[str, object] = {} + self.vel_lines: Dict[str, object] = {} + self.pos_hist: Dict[str, MultiResolutionSeries] = { + name: MultiResolutionSeries() for name in joint_names + } + self.vel_hist: Dict[str, MultiResolutionSeries] = { + name: MultiResolutionSeries() for name in joint_names + } + self._plot_start = time.time() + self._elapsed_time = 0.0 + self._last_plot_time = None + self._pending_selected: set[str] = set() + self._dirty = False + self._last_legend_key: tuple[str, ...] = () + self._color_map = {} + self._auto_follow_x = True + self._applying_x_range = False + self._build_ui() + self._refresh_timer = QtCore.QTimer(self) + self._refresh_timer.setSingleShot(True) + self._refresh_timer.setInterval(REFRESH_INTERVAL_MS) + self._refresh_timer.timeout.connect(self._flush_refresh) + + def _build_ui(self) -> None: + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(4) + + controls = QtWidgets.QHBoxLayout() + controls.addWidget(QtWidgets.QLabel("Time Window:")) + self.window_combo = QtWidgets.QComboBox() + for label, seconds in TIME_WINDOW_OPTIONS: + self.window_combo.addItem(label, seconds) + self.window_combo.setCurrentIndex(0) + self.window_combo.currentIndexChanged.connect(self._on_window_changed) + controls.addWidget(self.window_combo) + controls.addStretch(1) + self.clear_btn = QtWidgets.QPushButton("Clear Plot") + self.clear_btn.clicked.connect(self.reset_data) + controls.addWidget(self.clear_btn) + self.reset_view_btn = QtWidgets.QPushButton("Reset View") + self.reset_view_btn.clicked.connect(self._reset_view) + controls.addWidget(self.reset_view_btn) + layout.addLayout(controls) + + if pg is None: + message = QtWidgets.QLabel(f"pyqtgraph unavailable: {PYQTGRAPH_IMPORT_ERROR}") + message.setStyleSheet("color: red; padding: 12px;") + layout.addWidget(message) + return + + pg.setConfigOptions(antialias=False) + self.plot_splitter = QtWidgets.QSplitter(QtCore.Qt.Vertical) + self.plot_splitter.setChildrenCollapsible(False) + self.plot_splitter.setHandleWidth(8) + layout.addWidget(self.plot_splitter, 1) + + self.pos_plot_widget = pg.PlotWidget(viewBox=InteractivePlotViewBox()) + self.pos_plot = self.pos_plot_widget.getPlotItem() + self.pos_plot.setTitle("Position (rad)") + self.plot_splitter.addWidget(self.pos_plot_widget) + + self.vel_plot_widget = pg.PlotWidget(viewBox=InteractivePlotViewBox()) + self.vel_plot = self.vel_plot_widget.getPlotItem() + self.vel_plot.setTitle("Velocity (rad/s)") + self.plot_splitter.addWidget(self.vel_plot_widget) + self.plot_splitter.setStretchFactor(0, 1) + self.plot_splitter.setStretchFactor(1, 1) + + self.vel_plot.setXLink(self.pos_plot) + self.pos_plot.getViewBox().sigXRangeChanged.connect(self._on_plot_x_range_changed) + self.vel_plot.getViewBox().sigXRangeChanged.connect(self._on_plot_x_range_changed) + + self._configure_plot(self.pos_plot, "Position", "rad", POSITION_Y_RANGE, show_bottom=False) + self._configure_plot(self.vel_plot, "Velocity", "rad/s", VELOCITY_Y_RANGE, show_bottom=True) + + self.pos_legend = self.pos_plot.addLegend(offset=(-10, 10)) + self.vel_legend = self.vel_plot.addLegend(offset=(-10, 10)) + self.pos_legend.setVisible(False) + self.vel_legend.setVisible(False) + self._apply_time_window() + + def _configure_plot(self, plot, label: str, units: str, y_range: tuple[float, float], show_bottom: bool) -> None: + plot.showGrid(x=True, y=True, alpha=0.25) + plot.setLabel("left", label, units=units) + plot.setMouseEnabled(x=True, y=True) + plot.setMenuEnabled(False) + plot.setYRange(y_range[0], y_range[1], padding=0.0) + plot.getAxis("bottom").setStyle(showValues=show_bottom) + if show_bottom: + plot.setLabel("bottom", "Time", units="s") + + def update_data( + self, + data: Dict[str, Tuple[float, float]], + selected: set[str], + refresh: bool = True, + ) -> None: + sample_time = time.time() - self._plot_start + if self._last_plot_time is not None and sample_time <= self._last_plot_time: + sample_time = self._last_plot_time + 1e-6 + self._last_plot_time = sample_time + self._elapsed_time = max(self._elapsed_time, sample_time) + + for name in selected: + pos, vel = data.get(name, (0.0, 0.0)) + self.pos_hist[name].append(sample_time, pos) + self.vel_hist[name].append(sample_time, vel) + + if refresh: + self.refresh(selected) + + def refresh(self, selected: set[str]) -> None: + self._pending_selected = set(selected) + self._dirty = True + if not self._refresh_timer.isActive(): + self._refresh_timer.start() + + def reset_data(self) -> None: + for name in self.joint_names: + self.pos_hist[name].clear() + self.vel_hist[name].clear() + self._plot_start = time.time() + self._elapsed_time = 0.0 + self._last_plot_time = None + self._pending_selected.clear() + self._dirty = False + self._last_legend_key = () + self._auto_follow_x = True + + if pg is None: + return + + for line in self.pos_lines.values(): + line.setData([], []) + for line in self.vel_lines.values(): + line.setData([], []) + + self._apply_time_window() + self.pos_plot.setYRange(POSITION_Y_RANGE[0], POSITION_Y_RANGE[1], padding=0.0) + self.vel_plot.setYRange(VELOCITY_Y_RANGE[0], VELOCITY_Y_RANGE[1], padding=0.0) + self._sync_legends(set()) + + def _flush_refresh(self) -> None: + if not self._dirty: + return + self._dirty = False + self._refresh_plot(self._pending_selected) + + def _refresh_plot(self, selected: set[str]) -> None: + if pg is None: + return + + min_time = self._window_min_time() + + for name in list(self.pos_lines.keys()): + if name not in selected: + self.pos_plot.removeItem(self.pos_lines[name]) + self.vel_plot.removeItem(self.vel_lines[name]) + del self.pos_lines[name] + del self.vel_lines[name] + + for name in selected: + if name not in self.pos_lines: + color = self._color_for_joint(name) + self.pos_lines[name] = self._create_curve(self.pos_plot, color) + self.vel_lines[name] = self._create_curve(self.vel_plot, color) + + pos_x, pos_y = self.pos_hist[name].sample(MAX_RENDER_POINTS, min_time=min_time) + vel_x, vel_y = self.vel_hist[name].sample(MAX_RENDER_POINTS, min_time=min_time) + self.pos_lines[name].setData(pos_x, pos_y) + self.vel_lines[name].setData(vel_x, vel_y) + + self._apply_time_window() + + self._sync_legends(selected) + + def _create_curve(self, plot, color): + curve = plot.plot([], [], pen=pg.mkPen(color=color, width=1.6), antialias=False) + curve.setClipToView(True) + curve.setDownsampling(auto=True, method="peak") + return curve + + def _color_for_joint(self, name: str): + if name not in self._color_map: + index = len(self._color_map) + self._color_map[name] = pg.intColor(index, hues=max(8, len(self.joint_names))) + return self._color_map[name] + + def _sync_legends(self, selected: set[str]) -> None: + if pg is None: + return + + selection_key = tuple(sorted(selected)) + if selection_key == self._last_legend_key: + return + + self.pos_legend.clear() + self.vel_legend.clear() + + show_legend = bool(selected) and len(selected) <= LEGEND_LIMIT + self.pos_legend.setVisible(show_legend) + self.vel_legend.setVisible(show_legend) + if show_legend: + for name in selection_key: + pos_line = self.pos_lines.get(name) + vel_line = self.vel_lines.get(name) + if pos_line is not None: + self.pos_legend.addItem(pos_line, name) + if vel_line is not None: + self.vel_legend.addItem(vel_line, name) + self._last_legend_key = selection_key + + def _window_min_time(self) -> Optional[float]: + window_seconds = self.window_combo.currentData() if hasattr(self, "window_combo") else None + if window_seconds is None: + return None + return max(0.0, self._elapsed_time - float(window_seconds)) + + def _apply_time_window(self) -> None: + if pg is None or not hasattr(self, "window_combo"): + return + if not self._auto_follow_x: + return + + window_seconds = self.window_combo.currentData() + if window_seconds is None: + min_time = 0.0 + max_time = max(1.0, self._elapsed_time) + else: + duration = float(window_seconds) + min_time = max(0.0, self._elapsed_time - duration) + max_time = duration if self._elapsed_time < duration else self._elapsed_time + max_time = max(min_time + 1.0, max_time) + + self._set_x_range(min_time, max_time) + + def _set_x_range(self, min_time: float, max_time: float) -> None: + self._applying_x_range = True + try: + self.pos_plot.setXRange(min_time, max_time, padding=0.0) + self.vel_plot.setXRange(min_time, max_time, padding=0.0) + finally: + self._applying_x_range = False + + def _on_plot_x_range_changed(self, *_args) -> None: + if self._applying_x_range: + return + self._auto_follow_x = False + + def _reset_view(self) -> None: + if pg is None: + return + self._auto_follow_x = True + self._apply_time_window() + self.pos_plot.setYRange(POSITION_Y_RANGE[0], POSITION_Y_RANGE[1], padding=0.0) + self.vel_plot.setYRange(VELOCITY_Y_RANGE[0], VELOCITY_Y_RANGE[1], padding=0.0) + + def _on_window_changed(self, _index: int) -> None: + self._auto_follow_x = True + active = self._pending_selected or set(self.pos_lines.keys()) + if active: + self.refresh(active) + else: + self._apply_time_window() diff --git a/ui/rgb_image_page.py b/ui/rgb_image_page.py new file mode 100644 index 0000000..2f5e715 --- /dev/null +++ b/ui/rgb_image_page.py @@ -0,0 +1,710 @@ +import time +from pathlib import Path +from typing import Callable, Optional + +import grpc +from PyQt5 import QtCore, QtGui, QtWidgets + +try: + import av +except Exception as exc: + av = None + AV_IMPORT_ERROR = exc +else: + AV_IMPORT_ERROR = None + +from clients._path_setup import ensure_paths + +ensure_paths() + +from clients.camera_client import CameraClient +from clients.hlc_client import HlcClient +from cmvr.api import camera_command_pb2 as pb + +TOUCH_TIMEOUT_SECONDS = 30.0 + + +class ImagePreviewLabel(QtWidgets.QLabel): + uv_changed = QtCore.pyqtSignal(object) + + def __init__(self, parent=None): + super().__init__(parent) + self._image = None + self._selected_uv = None + + def set_image(self, image: QtGui.QImage): + self._image = image + self._selected_uv = None + self._refresh_pixmap() + + def clear_image(self): + self._image = None + self._selected_uv = None + self.clear() + self.uv_changed.emit(None) + + def resizeEvent(self, event): + super().resizeEvent(event) + if self._image is not None: + self._refresh_pixmap() + + def mousePressEvent(self, event): + super().mousePressEvent(event) + if event.button() != QtCore.Qt.LeftButton: + return + + uv = self._map_to_image_uv(event.pos()) + self._selected_uv = uv + self._refresh_pixmap() + self.uv_changed.emit(uv) + + def _map_to_image_uv(self, pos): + if self._image is None: + return None + + pixmap = self.pixmap() + if pixmap is None or pixmap.isNull(): + return None + + x_offset = (self.width() - pixmap.width()) / 2.0 + y_offset = (self.height() - pixmap.height()) / 2.0 + x = pos.x() - x_offset + y = pos.y() - y_offset + + if x < 0 or y < 0 or x >= pixmap.width() or y >= pixmap.height(): + return None + + u = int(x * self._image.width() / pixmap.width()) + v = int(y * self._image.height() / pixmap.height()) + u = max(0, min(u, self._image.width() - 1)) + v = max(0, min(v, self._image.height() - 1)) + return (u, v) + + def _refresh_pixmap(self): + if self._image is None: + return + pixmap = QtGui.QPixmap.fromImage(self._image).scaled( + self.size(), + QtCore.Qt.KeepAspectRatio, + QtCore.Qt.SmoothTransformation, + ) + if self._selected_uv is not None and not pixmap.isNull(): + self._draw_crosshair(pixmap, self._selected_uv) + self.setPixmap(pixmap) + + def _draw_crosshair(self, pixmap: QtGui.QPixmap, uv): + if self._image is None: + return + + u, v = uv + x = int((u + 0.5) * pixmap.width() / self._image.width()) + y = int((v + 0.5) * pixmap.height() / self._image.height()) + x = max(0, min(x, pixmap.width() - 1)) + y = max(0, min(y, pixmap.height() - 1)) + arm = max(4, min(pixmap.width(), pixmap.height()) // 48) + + painter = QtGui.QPainter(pixmap) + painter.setRenderHint(QtGui.QPainter.Antialiasing, False) + + outline_pen = QtGui.QPen(QtGui.QColor("white"), 2) + painter.setPen(outline_pen) + painter.drawLine(max(0, x - arm), y, min(pixmap.width() - 1, x + arm), y) + painter.drawLine(x, max(0, y - arm), x, min(pixmap.height() - 1, y + arm)) + + cross_pen = QtGui.QPen(QtGui.QColor("red"), 1) + painter.setPen(cross_pen) + painter.drawLine(max(0, x - arm), y, min(pixmap.width() - 1, x + arm), y) + painter.drawLine(x, max(0, y - arm), x, min(pixmap.height() - 1, y + arm)) + painter.end() + + +class ToggleSwitch(QtWidgets.QCheckBox): + def __init__(self, parent=None): + super().__init__(parent) + self.setCursor(QtCore.Qt.PointingHandCursor) + self.setFixedSize(56, 30) + + def sizeHint(self): + return QtCore.QSize(56, 30) + + def paintEvent(self, event): + del event + radius = (self.height() - 2) / 2.0 + track_rect = QtCore.QRectF(1, 1, self.width() - 2, self.height() - 2) + knob_diameter = track_rect.height() - 6 + knob_y = track_rect.top() + 3 + knob_x = track_rect.left() + 3 + if self.isChecked(): + knob_x = track_rect.right() - knob_diameter - 3 + + if self.isEnabled(): + track_color = QtGui.QColor("#2ca36a") if self.isChecked() else QtGui.QColor("#9aa4b2") + border_color = QtGui.QColor("#1c7a4c") if self.isChecked() else QtGui.QColor("#6c7685") + knob_color = QtGui.QColor("#ffffff") + else: + track_color = QtGui.QColor("#c7cdd6") + border_color = QtGui.QColor("#afb7c2") + knob_color = QtGui.QColor("#f5f6f8") + + painter = QtGui.QPainter(self) + painter.setRenderHint(QtGui.QPainter.Antialiasing, True) + painter.setPen(QtGui.QPen(border_color, 1)) + painter.setBrush(track_color) + painter.drawRoundedRect(track_rect, radius, radius) + + knob_rect = QtCore.QRectF(knob_x, knob_y, knob_diameter, knob_diameter) + painter.setPen(QtGui.QPen(QtGui.QColor(0, 0, 0, 25), 1)) + painter.setBrush(knob_color) + painter.drawEllipse(knob_rect) + + +class CameraRpcWorker(QtCore.QThread): + image_ready = QtCore.pyqtSignal(object, dict) + command_done = QtCore.pyqtSignal(str) + error_occurred = QtCore.pyqtSignal(str) + + def __init__( + self, + address: str, + timeout: float, + device_id: str, + action: str, + u: Optional[int] = None, + v: Optional[int] = None, + max_force: Optional[float] = None, + ): + super().__init__() + self.address = address + self.timeout = timeout + self.device_id = device_id + self.action = action + self.u = u + self.v = v + self.max_force = max_force + + def run(self): + client = None + try: + if self.action == "touch": + if self.u is None or self.v is None: + raise RuntimeError("No UV selected for touch") + client = HlcClient(address=self.address, timeout=self.timeout) + rpc_timeout = max(float(self.timeout), TOUCH_TIMEOUT_SECONDS) + max_force = -1.0 if self.max_force is None else float(self.max_force) + response = client.touch( + u=self.u, + v=self.v, + max_force=max_force, + timeout=rpc_timeout, + ) + if not response.header.success: + raise RuntimeError(response.header.error_message or "touch failed") + self.command_done.emit("touch") + return + + client = CameraClient(address=self.address, timeout=self.timeout) + + if self.action == "start": + start_resp = client.start_camera(self.device_id) + if not start_resp.header.success: + raise RuntimeError(start_resp.header.error_message or "StartCamera failed") + self.command_done.emit("start") + return + + if self.action == "stop": + stop_resp = client.stop_camera(self.device_id) + if hasattr(stop_resp, "header") and not stop_resp.header.success: + raise RuntimeError(stop_resp.header.error_message or "StopCamera failed") + self.command_done.emit("stop") + return + + response = client.get_rgb_image(self.device_id) + if not response.header.success: + raise RuntimeError(response.header.error_message or "GetRGBImage failed") + + image = self._decode_to_qimage(response.color_frame) + codec_value = (response.color_frame.codec or "").strip() + if codec_value.lower() in {"", "none", "raw"}: + codec_value = "" + metadata = { + "width": response.color_frame.width, + "height": response.color_frame.height, + "codec": codec_value, + "frame_type": pb.FrameData.FrameType.Name(response.color_frame.type), + "is_key_frame": response.color_frame.is_key_frame, + "data_size": len(response.color_frame.data), + } + self.image_ready.emit(image, metadata) + except grpc.RpcError as exc: + if exc.code() == grpc.StatusCode.DEADLINE_EXCEEDED: + timeout_value = max(float(self.timeout), TOUCH_TIMEOUT_SECONDS) if self.action == "touch" else self.timeout + self.error_occurred.emit(f"{self.action} timed out after {timeout_value:g}s") + else: + self.error_occurred.emit(f"{self.action} RPC failed: {exc.code().name}") + except Exception as exc: + self.error_occurred.emit(str(exc)) + finally: + if client is not None: + client.close() + + def _decode_to_qimage(self, frame) -> QtGui.QImage: + if not frame.data: + raise RuntimeError("Empty RGB image payload") + + raw_image = self._decode_raw_qimage(frame) + if raw_image is not None: + return raw_image + + file_image = QtGui.QImage.fromData(frame.data) + if not file_image.isNull(): + return file_image + + if frame.codec: + decoded_image = self._decode_with_av(frame) + if decoded_image is not None: + return decoded_image + + raise RuntimeError( + "Failed to decode RGB image payload: " + f"codec={frame.codec or '-'}, " + f"type={pb.FrameData.FrameType.Name(frame.type)}, " + f"width={frame.width}, height={frame.height}, bytes={len(frame.data)}" + ) + + def _decode_raw_qimage(self, frame) -> Optional[QtGui.QImage]: + if frame.width <= 0 or frame.height <= 0: + return None + + if frame.type == pb.FrameData.U8C3: + expected_size = frame.width * frame.height * 3 + if len(frame.data) != expected_size: + return None + # The current C++ service returns raw cv::Mat bytes, which are BGR for CV_8UC3. + image = QtGui.QImage( + frame.data, + frame.width, + frame.height, + 3 * frame.width, + QtGui.QImage.Format_RGB888, + ) + return image.rgbSwapped().copy() + + if frame.type == pb.FrameData.U8C1: + expected_size = frame.width * frame.height + if len(frame.data) != expected_size: + return None + return QtGui.QImage( + frame.data, + frame.width, + frame.height, + frame.width, + QtGui.QImage.Format_Grayscale8, + ).copy() + + return None + + def _decode_with_av(self, frame) -> Optional[QtGui.QImage]: + if av is None: + raise RuntimeError(f"PyAV unavailable: {AV_IMPORT_ERROR}") + + codec_names = self._codec_candidates(frame.codec) + packet = av.Packet(frame.data) + last_error = None + + for codec_name in codec_names: + try: + decoder = av.CodecContext.create(codec_name, "r") + frames = decoder.decode(packet) + if not frames: + continue + + image = frames[0].to_ndarray(format="rgb24") + return QtGui.QImage( + image.data, + image.shape[1], + image.shape[0], + 3 * image.shape[1], + QtGui.QImage.Format_RGB888, + ).copy() + except Exception as exc: + last_error = exc + + if last_error is not None: + raise RuntimeError( + f"PyAV decode failed for codec={frame.codec!r}: {last_error}" + ) + return None + + def _codec_candidates(self, codec: str) -> list[str]: + normalized = (codec or "").strip().lower() + if normalized in {"", "none", "raw"}: + return [] + aliases = { + "h265": ["hevc", "h265"], + "hevc": ["hevc", "h265"], + "h.265": ["hevc", "h265"], + "h264": ["h264"], + "avc": ["h264"], + "h.264": ["h264"], + "jpeg": ["mjpeg", "jpeg"], + "jpg": ["mjpeg", "jpeg"], + "mjpeg": ["mjpeg", "jpeg"], + } + return aliases.get(normalized, [normalized] if normalized else []) + + +class RGBImagePage(QtWidgets.QWidget): + def __init__( + self, + address_provider: Optional[Callable[[], str]] = None, + default_device_id: str = "right_hand_cam", + parent=None, + ): + super().__init__(parent) + self.address_provider = address_provider + self.worker: Optional[CameraRpcWorker] = None + self.pending_action = None + self.camera_started = False + self.current_image: Optional[QtGui.QImage] = None + self.current_uv = None + self._touch_started_at: Optional[float] = None + self.save_dir = Path("./captured_images") + self.save_dir.mkdir(exist_ok=True) + self._create_ui(default_device_id) + + def _create_ui(self, default_device_id: str): + layout = QtWidgets.QVBoxLayout(self) + + settings_group = QtWidgets.QGroupBox("RGB Image") + settings_layout = QtWidgets.QGridLayout(settings_group) + + settings_layout.addWidget(QtWidgets.QLabel("Device ID:"), 0, 0) + self.device_id = QtWidgets.QLineEdit(default_device_id) + settings_layout.addWidget(self.device_id, 0, 1) + + if self.address_provider is None: + settings_layout.addWidget(QtWidgets.QLabel("Server IP:"), 0, 2) + self.server_ip = QtWidgets.QLineEdit("192.168.0.222") + settings_layout.addWidget(self.server_ip, 0, 3) + + settings_layout.addWidget(QtWidgets.QLabel("Port:"), 0, 4) + self.server_port = QtWidgets.QLineEdit("50052") + self.server_port.setMaximumWidth(100) + settings_layout.addWidget(self.server_port, 0, 5) + else: + settings_layout.addWidget(QtWidgets.QLabel("Server:"), 0, 2) + self.address_label = QtWidgets.QLabel("") + self.address_label.setStyleSheet("font-weight: bold;") + settings_layout.addWidget(self.address_label, 0, 3, 1, 3) + + settings_layout.addWidget(QtWidgets.QLabel("Timeout (s):"), 1, 0) + self.timeout_spin = QtWidgets.QSpinBox() + self.timeout_spin.setRange(1, 10) + self.timeout_spin.setValue(10) + settings_layout.addWidget(self.timeout_spin, 1, 1) + + settings_layout.addWidget(QtWidgets.QLabel("Save Dir:"), 2, 0) + self.save_dir_edit = QtWidgets.QLineEdit(str(self.save_dir)) + settings_layout.addWidget(self.save_dir_edit, 2, 1, 1, 4) + + self.browse_btn = QtWidgets.QPushButton("Browse") + self.browse_btn.clicked.connect(self._browse_dir) + settings_layout.addWidget(self.browse_btn, 2, 5) + + button_layout = QtWidgets.QHBoxLayout() + button_layout.addWidget(QtWidgets.QLabel("Camera:")) + self.camera_switch = ToggleSwitch() + self.camera_switch.toggled.connect(self._on_camera_switch_toggled) + button_layout.addWidget(self.camera_switch) + button_layout.addSpacing(12) + + self.fetch_btn = QtWidgets.QPushButton("Get RGB Image") + self.fetch_btn.clicked.connect(self._fetch_image) + button_layout.addWidget(self.fetch_btn) + + self.save_btn = QtWidgets.QPushButton("Save Image") + self.save_btn.clicked.connect(self._save_image) + self.save_btn.setEnabled(False) + button_layout.addWidget(self.save_btn) + button_layout.addStretch() + + self.touch_btn = QtWidgets.QPushButton("Touch") + self.touch_btn.clicked.connect(self._touch_uv) + self.touch_btn.setEnabled(False) + button_layout.addWidget(self.touch_btn) + + self.uv_label = QtWidgets.QLabel("UV: -") + button_layout.addWidget(self.uv_label) + + button_layout.addWidget(QtWidgets.QLabel("max_force:")) + self.max_force_spin = QtWidgets.QDoubleSpinBox() + self.max_force_spin.setRange(-1.0, 1000.0) + self.max_force_spin.setDecimals(4) + self.max_force_spin.setSingleStep(0.0001) + self.max_force_spin.setValue(-1.0) + self.max_force_spin.setMaximumWidth(110) + button_layout.addWidget(self.max_force_spin) + settings_layout.addLayout(button_layout, 3, 0, 1, 6) + + layout.addWidget(settings_group) + + status_group = QtWidgets.QGroupBox("Image Status") + status_layout = QtWidgets.QHBoxLayout(status_group) + self.status_label = QtWidgets.QLabel("Idle") + self.status_label.setStyleSheet("color: gray;") + status_layout.addWidget(self.status_label) + + status_layout.addWidget(QtWidgets.QLabel("|")) + self.camera_state_label = QtWidgets.QLabel("Camera: unknown") + status_layout.addWidget(self.camera_state_label) + + status_layout.addWidget(QtWidgets.QLabel("|")) + self.size_label = QtWidgets.QLabel("Resolution: -") + status_layout.addWidget(self.size_label) + + status_layout.addWidget(QtWidgets.QLabel("|")) + self.codec_label = QtWidgets.QLabel("Codec: -") + status_layout.addWidget(self.codec_label) + + status_layout.addWidget(QtWidgets.QLabel("|")) + self.type_label = QtWidgets.QLabel("Type: -") + status_layout.addWidget(self.type_label) + + status_layout.addWidget(QtWidgets.QLabel("|")) + self.data_size_label = QtWidgets.QLabel("Bytes: -") + status_layout.addWidget(self.data_size_label) + status_layout.addStretch() + layout.addWidget(status_group) + + preview_group = QtWidgets.QGroupBox("RGB Image") + preview_layout = QtWidgets.QVBoxLayout(preview_group) + self.image_label = ImagePreviewLabel() + self.image_label.setAlignment(QtCore.Qt.AlignCenter) + self.image_label.setMinimumSize(640, 480) + self.image_label.setStyleSheet("background-color: black; border: 1px solid gray;") + self.image_label.uv_changed.connect(self._on_uv_changed) + preview_layout.addWidget(self.image_label) + layout.addWidget(preview_group, 1) + + info_frame = QtWidgets.QFrame() + info_frame.setFrameStyle(QtWidgets.QFrame.StyledPanel | QtWidgets.QFrame.Sunken) + info_layout = QtWidgets.QHBoxLayout(info_frame) + info_label = QtWidgets.QLabel("Fetch a single RGB image through CameraService.GetRGBImage.") + info_label.setStyleSheet("color: gray; padding: 5px;") + info_layout.addWidget(info_label) + layout.addWidget(info_frame) + + self.status_bar = QtWidgets.QStatusBar() + self.status_bar.showMessage("Ready") + layout.addWidget(self.status_bar) + + def refresh_address_hint(self): + if self.address_provider is None or not hasattr(self, "address_label"): + return + try: + self.address_label.setText(self.address_provider()) + except Exception: + self.address_label.setText("(app settings unavailable)") + + def _browse_dir(self): + directory = QtWidgets.QFileDialog.getExistingDirectory(self, "Select save directory", self.save_dir_edit.text()) + if directory: + self.save_dir_edit.setText(directory) + self.save_dir = Path(directory) + self.save_dir.mkdir(exist_ok=True) + + def _get_server_addr(self) -> str: + if self.address_provider is not None: + return self.address_provider() + return f"{self.server_ip.text().strip()}:{self.server_port.text().strip()}" + + def _fetch_image(self): + self._start_worker("get") + + def _on_camera_switch_toggled(self, checked: bool): + self._start_worker("start" if checked else "stop") + + def _touch_uv(self): + if self.current_uv is None: + QtWidgets.QMessageBox.warning(self, "Touch", "Please click the image to select UV first.") + return + self._start_worker("touch") + + def _start_touch_timer(self): + self._touch_started_at = time.perf_counter() + + def _finish_touch_timer(self) -> Optional[float]: + if self._touch_started_at is None: + return None + elapsed = time.perf_counter() - self._touch_started_at + self._touch_started_at = None + return elapsed + + def _format_touch_elapsed(self, elapsed: Optional[float]) -> str: + if elapsed is None: + return "" + return f" ({elapsed:.3f}s)" + + def _start_worker(self, action: str): + if self.worker is not None and self.worker.isRunning(): + return + + self.refresh_address_hint() + self.pending_action = action + self.camera_switch.setEnabled(False) + self.fetch_btn.setEnabled(False) + self.touch_btn.setEnabled(False) + self.save_btn.setEnabled(False) + + if action == "start": + self.status_label.setText("Starting") + self.status_label.setStyleSheet("color: orange;") + self.status_bar.showMessage("Calling StartCamera...") + elif action == "stop": + self.status_label.setText("Stopping") + self.status_label.setStyleSheet("color: orange;") + self.status_bar.showMessage("Calling StopCamera...") + elif action == "touch": + self._start_touch_timer() + self.status_label.setText("Touching") + self.status_label.setStyleSheet("color: orange;") + if self.current_uv is None: + self.status_bar.showMessage("No UV selected") + else: + self.status_bar.showMessage( + f"Calling touch(u={self.current_uv[0]}, v={self.current_uv[1]}, max_force={self.max_force_spin.value():.4f}, timeout={max(float(self.timeout_spin.value()), TOUCH_TIMEOUT_SECONDS):g}s)..." + ) + else: + self.status_label.setText("Fetching") + self.status_label.setStyleSheet("color: orange;") + self.status_bar.showMessage("Calling GetRGBImage...") + + u = None + v = None + max_force = None + if action == "touch" and self.current_uv is not None: + u, v = self.current_uv + max_force = float(self.max_force_spin.value()) + + self.worker = CameraRpcWorker( + address=self._get_server_addr(), + timeout=self.timeout_spin.value(), + device_id=self.device_id.text().strip(), + action=action, + u=u, + v=v, + max_force=max_force, + ) + self.worker.image_ready.connect(self._on_image_ready) + self.worker.command_done.connect(self._on_command_done) + self.worker.error_occurred.connect(self._on_error) + self.worker.finished.connect(self._on_worker_finished) + self.worker.start() + + def _on_image_ready(self, image: QtGui.QImage, metadata: dict): + self.current_image = image + self.current_uv = None + self.image_label.set_image(image) + self.uv_label.setText("UV: -") + self.status_label.setText("Fetched") + self.status_label.setStyleSheet("color: green;") + self.size_label.setText(f"Resolution: {metadata['width']}x{metadata['height']}") + self.codec_label.setText(f"Codec: {metadata['codec'] or '-'}") + self.type_label.setText(f"Type: {metadata['frame_type']}") + self.data_size_label.setText(f"Bytes: {metadata['data_size']}") + self.status_bar.showMessage("GetRGBImage completed", 3000) + self.save_btn.setEnabled(True) + + def _on_uv_changed(self, uv): + self.current_uv = uv + if self.worker is None: + self.touch_btn.setEnabled(uv is not None) + if uv is None: + self.uv_label.setText("UV: -") + return + u, v = uv + self.uv_label.setText(f"UV: ({u}, {v})") + + def _on_command_done(self, action: str): + if action == "start": + self.camera_started = True + self._set_camera_switch_checked(True) + self.camera_state_label.setText("Camera: started") + self.status_label.setText("Started") + self.status_label.setStyleSheet("color: green;") + self.status_bar.showMessage("StartCamera completed", 3000) + return + + if action == "stop": + self.camera_started = False + self._set_camera_switch_checked(False) + self.camera_state_label.setText("Camera: stopped") + self.status_label.setText("Stopped") + self.status_label.setStyleSheet("color: gray;") + self.status_bar.showMessage("StopCamera completed", 3000) + return + + if action == "touch": + elapsed = self._finish_touch_timer() + elapsed_text = self._format_touch_elapsed(elapsed) + self.status_label.setText(f"Touched{elapsed_text}") + self.status_label.setStyleSheet("color: green;") + if self.current_uv is None: + self.status_bar.showMessage(f"touch completed{elapsed_text}", 3000) + else: + self.status_bar.showMessage( + f"touch completed at UV ({self.current_uv[0]}, {self.current_uv[1]}){elapsed_text}", + 3000, + ) + + def _on_error(self, message: str): + if self.pending_action == "start": + self.camera_started = False + self._set_camera_switch_checked(False) + self.camera_state_label.setText("Camera: stopped") + elif self.pending_action == "stop": + self.camera_started = True + self._set_camera_switch_checked(True) + self.camera_state_label.setText("Camera: started") + if self.pending_action == "touch": + elapsed = self._finish_touch_timer() + elapsed_text = self._format_touch_elapsed(elapsed) + message = f"{message}{elapsed_text}" + self.status_label.setText(f"Touch Failed{elapsed_text}") + else: + self.status_label.setText("Error") + self.status_label.setStyleSheet("color: red;") + self.status_bar.showMessage(message, 5000) + QtWidgets.QMessageBox.critical(self, "RGB Image Error", message) + + def _on_worker_finished(self): + self.camera_switch.setEnabled(True) + self.fetch_btn.setEnabled(True) + self.touch_btn.setEnabled(self.current_uv is not None) + if self.current_image is not None: + self.save_btn.setEnabled(True) + self.pending_action = None + self.worker = None + + def _set_camera_switch_checked(self, checked: bool): + blocker = QtCore.QSignalBlocker(self.camera_switch) + self.camera_switch.setChecked(checked) + del blocker + + def _save_image(self): + if self.current_image is None: + return + + self.save_dir = Path(self.save_dir_edit.text()) + self.save_dir.mkdir(parents=True, exist_ok=True) + timestamp = QtCore.QDateTime.currentDateTime().toString("yyyyMMdd_HHmmss") + target = self.save_dir / f"rgb_{self.device_id.text().strip()}_{timestamp}.png" + if self.current_image.save(str(target)): + self.status_bar.showMessage(f"Saved to {target.name}", 3000) + else: + QtWidgets.QMessageBox.critical(self, "Save Error", "Failed to save image.") + + def shutdown(self): + worker = self.worker + if worker and worker.isRunning(): + worker.wait(2000)