devagent-physical-engine 0.12.4__tar.gz → 0.13.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {devagent_physical_engine-0.12.4/src/devagent_physical_engine.egg-info → devagent_physical_engine-0.13.0}/PKG-INFO +22 -3
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/README.md +22 -3
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/pyproject.toml +1 -1
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/__init__.py +17 -1
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/cli.py +117 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/physical_motion.py +14 -1
- devagent_physical_engine-0.13.0/src/devagent_physical_engine/pose_math.py +134 -0
- devagent_physical_engine-0.13.0/src/devagent_physical_engine/project.py +601 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/reference_workcell.py +151 -39
- devagent_physical_engine-0.13.0/src/devagent_physical_engine/ros2/joint_state_probe.py +139 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/joint_state_recorder.py +35 -9
- devagent_physical_engine-0.13.0/src/devagent_physical_engine/ros2/moveit_joint_plan_probe.py +580 -0
- devagent_physical_engine-0.13.0/src/devagent_physical_engine/ros2/moveit_task_planner.py +279 -0
- devagent_physical_engine-0.13.0/src/devagent_physical_engine/ros2/task_adapter.py +160 -0
- devagent_physical_engine-0.13.0/src/devagent_physical_engine/ros2/task_visualization.py +602 -0
- devagent_physical_engine-0.13.0/src/devagent_physical_engine/ros2/trajectory.py +376 -0
- devagent_physical_engine-0.13.0/src/devagent_physical_engine/ros2/trajectory_action_probe.py +352 -0
- devagent_physical_engine-0.13.0/src/devagent_physical_engine/ros2/trajectory_tracking.py +201 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/workpiece_follow.py +79 -5
- devagent_physical_engine-0.13.0/src/devagent_physical_engine/ros2/workpiece_gazebo_follower.py +275 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/setup_profile.py +5 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0/src/devagent_physical_engine.egg-info}/PKG-INFO +22 -3
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine.egg-info/SOURCES.txt +12 -0
- devagent_physical_engine-0.13.0/tests/test_joint_state_probe_v0125.py +54 -0
- devagent_physical_engine-0.13.0/tests/test_moveit_planning_diagnostics_v0125.py +104 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_physical_motion_v09.py +5 -3
- devagent_physical_engine-0.13.0/tests/test_pose_math_v0125.py +42 -0
- devagent_physical_engine-0.13.0/tests/test_project_spine_v013.py +300 -0
- devagent_physical_engine-0.13.0/tests/test_reference_workcell_ros_frames_v0125.py +41 -0
- devagent_physical_engine-0.13.0/tests/test_ros_trajectory_v09.py +300 -0
- devagent_physical_engine-0.13.0/tests/test_trajectory_action_probe_v0125.py +107 -0
- devagent_physical_engine-0.13.0/tests/test_trajectory_tracking_v0125.py +142 -0
- devagent_physical_engine-0.12.4/src/devagent_physical_engine/ros2/moveit_joint_plan_probe.py +0 -159
- devagent_physical_engine-0.12.4/src/devagent_physical_engine/ros2/moveit_task_planner.py +0 -139
- devagent_physical_engine-0.12.4/src/devagent_physical_engine/ros2/task_adapter.py +0 -52
- devagent_physical_engine-0.12.4/src/devagent_physical_engine/ros2/task_visualization.py +0 -325
- devagent_physical_engine-0.12.4/src/devagent_physical_engine/ros2/trajectory.py +0 -213
- devagent_physical_engine-0.12.4/src/devagent_physical_engine/ros2/workpiece_gazebo_follower.py +0 -218
- devagent_physical_engine-0.12.4/tests/test_ros_trajectory_v09.py +0 -116
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/NOTICE +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/setup.cfg +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/agent/__init__.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/agent/compiler.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/agent/contracts.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/agent/coordinator.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/agent/critic.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/agent/evidence.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/agent/interpreter.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/agent/planner.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/agent/recovery.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/agent/routing.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/agent/runtime.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/agent/semantic.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/agent/structured.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ai_cli.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/continuous_clearance.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/doctor.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/engineering_agent.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/engineering_request.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/execution.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/models.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/operating_envelope.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/optimization/__init__.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/optimization/benchmark.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/optimization/candidates.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/optimization/contracts.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/optimization/evaluator.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/optimization/evidence.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/optimization/experience.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/optimization/measured.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/optimization/optimizer.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/optimization/orchestrator.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/physical_campaign.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/physical_evidence.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/planning.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/preexecution_contract.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/provider_adapters/__init__.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/provider_adapters/anthropic.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/provider_adapters/common.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/provider_adapters/factory.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/provider_adapters/gemini.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/provider_adapters/openai.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/provider_qualification.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/providers.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/qualification.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/qualification_cli.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/qualification_harness.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/robot_platform.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/robots.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/__init__.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/acceptance.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/commands.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/continuous_clearance_probe.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/doctor.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/fk_pose.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/fk_probe.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/frame_alignment.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/gazebo_cli.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/gazebo_pose.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/gazebo_world.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/measured_motion.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/moveit_scene.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/preexecution.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/qualification.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/qualification_v10.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/qualification_v11.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/qualification_v12.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/scene_probe.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/state_validity_probe.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/tf_probe.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/tool_scene.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/tool_scene_probe.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/ur5e.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/ur5e_adapter.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/ur5e_v10_adapter.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/workpiece_scene.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/ros2/workpiece_scene_probe.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/sim_real_qualification.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/simulation.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/simulation_platform.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/tool_collision.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/trajectory_qualification.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/twin.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/twin_builder.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/twin_materialization.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/verification.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine/workpiece_lifecycle.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine.egg-info/dependency_links.txt +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine.egg-info/entry_points.txt +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine.egg-info/requires.txt +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/src/devagent_physical_engine.egg-info/top_level.txt +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_agent_core.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_agent_semantic_hardening.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_ai_cli.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_ai_cli_v07.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_continuous_clearance_v011.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_core.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_engineering_request_v07.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_gazebo_cli_v0122.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_interpreter_profiles_v08.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_live_provider_qualification.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_measured_motion_v10.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_moveit_scene_v0123.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_natural_language_agent_v07.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_openai_strict_schema_v0113.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_optimization_v05.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_physical_campaign_v09.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_physical_evidence_v09.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_planning_fallback_v012.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_preexecution_contract_v10.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_provider_and_qualification.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_qualification_cli_lifecycle_v06.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_qualification_harness_v06.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_readme_pypi.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_real_provider_adapters.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_reference_workcell_v012.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_robot_platform_v08.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_ros2_production_receipts_v011.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_ros2_simulation.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_setup_profile.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_sim_real_qualification_v011.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_simulation_platform_v08.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_tool_collision_v011.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_tool_scene_v0124.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_trajectory_qualification_v09.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_twin_materialization_v10.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_twin_v08.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_ur5e_adapter_scopes_v09.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_ur5e_v10_adapter_scopes.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_v052_stabilization.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_v08_catalog_mapping_regression.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.13.0}/tests/test_workpiece_lifecycle_v011.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devagent-physical-engine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.0
|
|
4
4
|
Summary: Verification-first agentic engineering, simulation, and evidence runtime for robotics and industrial automation.
|
|
5
5
|
Author: Tom Ha
|
|
6
6
|
License: All Rights Reserved
|
|
@@ -143,6 +143,24 @@ While the command runs, stage-by-stage progress is printed to `stderr` and the f
|
|
|
143
143
|
|
|
144
144
|
Use `--quiet` when a script or CI job needs JSON without terminal progress.
|
|
145
145
|
|
|
146
|
+
## Commercial project spine
|
|
147
|
+
|
|
148
|
+
v0.13 adds durable customer project, site, and workcell identity around the existing verification engine. Project manifests are fingerprinted, Twin builds can be persisted as immutable revisions, and project evidence is stored locally in SQLite with content hashes.
|
|
149
|
+
|
|
150
|
+
Create and inspect a project:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
devagent-physical project init warehouse-cnc-04 \
|
|
154
|
+
--name "Warehouse CNC Loading Cell 04" \
|
|
155
|
+
--site atl-01 \
|
|
156
|
+
--workcell cnc-04
|
|
157
|
+
|
|
158
|
+
devagent-physical project show warehouse-cnc-04
|
|
159
|
+
devagent-physical project revisions warehouse-cnc-04
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The default store is `~/.devagent/projects.db`. This project spine is the durable boundary for upcoming customer Twin import, requirement traceability, physical test campaigns, regression analysis, and FAT reporting. It does not by itself claim customer geometry import, physical qualification, site qualification, or real execution.
|
|
163
|
+
|
|
146
164
|
## Task-specific visual simulation
|
|
147
165
|
|
|
148
166
|
For the supported v0.12 UR5e `load` scope, `--visualize` continues from the verified high-level task into a physical simulation path:
|
|
@@ -408,6 +426,7 @@ A green `main` release is tied to the exact CI-tested commit. Release artifacts
|
|
|
408
426
|
|
|
409
427
|
Detailed engineering documentation lives in the repository:
|
|
410
428
|
|
|
429
|
+
- [Commercial project spine](https://github.com/tomha85/devagent-physical-engine/blob/main/docs/COMMERCIAL_PROJECT_SPINE.md)
|
|
411
430
|
- [Production readiness](https://github.com/tomha85/devagent-physical-engine/blob/main/docs/PRODUCTION_READINESS.md)
|
|
412
431
|
- [Architecture](https://github.com/tomha85/devagent-physical-engine/blob/main/docs/ARCHITECTURE.md)
|
|
413
432
|
- [Agent Core](https://github.com/tomha85/devagent-physical-engine/blob/main/docs/AGENT_CORE.md)
|
|
@@ -424,9 +443,9 @@ Detailed engineering documentation lives in the repository:
|
|
|
424
443
|
|
|
425
444
|
## Project status
|
|
426
445
|
|
|
427
|
-
**v0.
|
|
446
|
+
**v0.13.0 — Beta software / commercial project spine + evidence-gated task-specific physical simulation.**
|
|
428
447
|
|
|
429
|
-
The software and release pipeline are production-oriented, but physical readiness is scope-specific and evidence-driven. The UR5e task-specific reference-workcell path must pass the executable v0.12 ROS/Gazebo/MoveIt campaign on the target workstation before that scope is considered simulation-qualified there. Real robot execution remains locked. Functional-safety certification and site qualification are not claimed.
|
|
448
|
+
The software and release pipeline are production-oriented, but physical readiness is scope-specific and evidence-driven. v0.13 adds durable project/site/workcell identity, immutable Twin revisions, and content-addressed local evidence; it does not claim arbitrary customer Twin import or customer-site qualification. The UR5e task-specific reference-workcell path must pass the executable v0.12 ROS/Gazebo/MoveIt campaign on the target workstation before that scope is considered simulation-qualified there. Real robot execution remains locked. Functional-safety certification and site qualification are not claimed.
|
|
430
449
|
|
|
431
450
|
## Ownership
|
|
432
451
|
|
|
@@ -102,6 +102,24 @@ While the command runs, stage-by-stage progress is printed to `stderr` and the f
|
|
|
102
102
|
|
|
103
103
|
Use `--quiet` when a script or CI job needs JSON without terminal progress.
|
|
104
104
|
|
|
105
|
+
## Commercial project spine
|
|
106
|
+
|
|
107
|
+
v0.13 adds durable customer project, site, and workcell identity around the existing verification engine. Project manifests are fingerprinted, Twin builds can be persisted as immutable revisions, and project evidence is stored locally in SQLite with content hashes.
|
|
108
|
+
|
|
109
|
+
Create and inspect a project:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
devagent-physical project init warehouse-cnc-04 \
|
|
113
|
+
--name "Warehouse CNC Loading Cell 04" \
|
|
114
|
+
--site atl-01 \
|
|
115
|
+
--workcell cnc-04
|
|
116
|
+
|
|
117
|
+
devagent-physical project show warehouse-cnc-04
|
|
118
|
+
devagent-physical project revisions warehouse-cnc-04
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The default store is `~/.devagent/projects.db`. This project spine is the durable boundary for upcoming customer Twin import, requirement traceability, physical test campaigns, regression analysis, and FAT reporting. It does not by itself claim customer geometry import, physical qualification, site qualification, or real execution.
|
|
122
|
+
|
|
105
123
|
## Task-specific visual simulation
|
|
106
124
|
|
|
107
125
|
For the supported v0.12 UR5e `load` scope, `--visualize` continues from the verified high-level task into a physical simulation path:
|
|
@@ -367,6 +385,7 @@ A green `main` release is tied to the exact CI-tested commit. Release artifacts
|
|
|
367
385
|
|
|
368
386
|
Detailed engineering documentation lives in the repository:
|
|
369
387
|
|
|
388
|
+
- [Commercial project spine](https://github.com/tomha85/devagent-physical-engine/blob/main/docs/COMMERCIAL_PROJECT_SPINE.md)
|
|
370
389
|
- [Production readiness](https://github.com/tomha85/devagent-physical-engine/blob/main/docs/PRODUCTION_READINESS.md)
|
|
371
390
|
- [Architecture](https://github.com/tomha85/devagent-physical-engine/blob/main/docs/ARCHITECTURE.md)
|
|
372
391
|
- [Agent Core](https://github.com/tomha85/devagent-physical-engine/blob/main/docs/AGENT_CORE.md)
|
|
@@ -383,9 +402,9 @@ Detailed engineering documentation lives in the repository:
|
|
|
383
402
|
|
|
384
403
|
## Project status
|
|
385
404
|
|
|
386
|
-
**v0.
|
|
405
|
+
**v0.13.0 — Beta software / commercial project spine + evidence-gated task-specific physical simulation.**
|
|
387
406
|
|
|
388
|
-
The software and release pipeline are production-oriented, but physical readiness is scope-specific and evidence-driven. The UR5e task-specific reference-workcell path must pass the executable v0.12 ROS/Gazebo/MoveIt campaign on the target workstation before that scope is considered simulation-qualified there. Real robot execution remains locked. Functional-safety certification and site qualification are not claimed.
|
|
407
|
+
The software and release pipeline are production-oriented, but physical readiness is scope-specific and evidence-driven. v0.13 adds durable project/site/workcell identity, immutable Twin revisions, and content-addressed local evidence; it does not claim arbitrary customer Twin import or customer-site qualification. The UR5e task-specific reference-workcell path must pass the executable v0.12 ROS/Gazebo/MoveIt campaign on the target workstation before that scope is considered simulation-qualified there. Real robot execution remains locked. Functional-safety certification and site qualification are not claimed.
|
|
389
408
|
|
|
390
409
|
## Ownership
|
|
391
410
|
|
|
@@ -393,4 +412,4 @@ DevAgent Smart Physical Engine
|
|
|
393
412
|
Copyright © 2026 Tom Ha
|
|
394
413
|
Original creator: Tom Ha
|
|
395
414
|
Original project: https://github.com/tomha85/devagent-physical-engine
|
|
396
|
-
All rights reserved.
|
|
415
|
+
All rights reserved.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "devagent-physical-engine"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.13.0"
|
|
8
8
|
description = "Verification-first agentic engineering, simulation, and evidence runtime for robotics and industrial automation."
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -10,6 +10,15 @@ from .models import (
|
|
|
10
10
|
WorldState,
|
|
11
11
|
)
|
|
12
12
|
from .physical_motion import JointTrajectoryPoint, MotionExecutionMetrics, PhysicalMotionPlan
|
|
13
|
+
from .project import (
|
|
14
|
+
EvidenceArtifactRecord,
|
|
15
|
+
ProjectError,
|
|
16
|
+
ProjectManifest,
|
|
17
|
+
ProjectService,
|
|
18
|
+
ProjectTwinBuildOutcome,
|
|
19
|
+
SQLiteProjectRepository,
|
|
20
|
+
TwinRevisionRecord,
|
|
21
|
+
)
|
|
13
22
|
from .robot_platform import (
|
|
14
23
|
BUILTIN_ROBOT_PROFILES,
|
|
15
24
|
QualificationState,
|
|
@@ -30,6 +39,13 @@ __all__ = [
|
|
|
30
39
|
"JointTrajectoryPoint",
|
|
31
40
|
"MotionExecutionMetrics",
|
|
32
41
|
"PhysicalMotionPlan",
|
|
42
|
+
"ProjectError",
|
|
43
|
+
"ProjectManifest",
|
|
44
|
+
"ProjectService",
|
|
45
|
+
"ProjectTwinBuildOutcome",
|
|
46
|
+
"SQLiteProjectRepository",
|
|
47
|
+
"TwinRevisionRecord",
|
|
48
|
+
"EvidenceArtifactRecord",
|
|
33
49
|
"BUILTIN_ROBOT_PROFILES",
|
|
34
50
|
"QualificationState",
|
|
35
51
|
"RobotProfile",
|
|
@@ -41,4 +57,4 @@ __all__ = [
|
|
|
41
57
|
"TwinValidator",
|
|
42
58
|
]
|
|
43
59
|
|
|
44
|
-
__version__ = "0.
|
|
60
|
+
__version__ = "0.13.0"
|
|
@@ -24,6 +24,34 @@ def _parser() -> argparse.ArgumentParser:
|
|
|
24
24
|
sub.add_parser("qualify", help="Run deterministic cross-vendor smoke qualification")
|
|
25
25
|
add_qualification_parsers(sub)
|
|
26
26
|
|
|
27
|
+
project = sub.add_parser(
|
|
28
|
+
"project",
|
|
29
|
+
help="Manage durable customer project/site/workcell context and Twin revisions",
|
|
30
|
+
)
|
|
31
|
+
project.add_argument(
|
|
32
|
+
"--db",
|
|
33
|
+
default="~/.devagent/projects.db",
|
|
34
|
+
help="SQLite project/evidence database path",
|
|
35
|
+
)
|
|
36
|
+
project_sub = project.add_subparsers(dest="project_command", required=True)
|
|
37
|
+
|
|
38
|
+
project_init = project_sub.add_parser("init", help="Create a new customer project")
|
|
39
|
+
project_init.add_argument("project_id")
|
|
40
|
+
project_init.add_argument("--name", required=True)
|
|
41
|
+
project_init.add_argument("--site", required=True)
|
|
42
|
+
project_init.add_argument("--workcell", required=True)
|
|
43
|
+
|
|
44
|
+
project_sub.add_parser("list", help="List known customer projects")
|
|
45
|
+
|
|
46
|
+
project_show = project_sub.add_parser("show", help="Show one project and latest Twin revision")
|
|
47
|
+
project_show.add_argument("project_id")
|
|
48
|
+
|
|
49
|
+
project_revisions = project_sub.add_parser(
|
|
50
|
+
"revisions",
|
|
51
|
+
help="List immutable Twin revisions for a project",
|
|
52
|
+
)
|
|
53
|
+
project_revisions.add_argument("project_id")
|
|
54
|
+
|
|
27
55
|
setup = sub.add_parser(
|
|
28
56
|
"setup",
|
|
29
57
|
help="Configure an explicit system profile; pip installation never changes OS packages",
|
|
@@ -92,6 +120,92 @@ def _parser() -> argparse.ArgumentParser:
|
|
|
92
120
|
return parser
|
|
93
121
|
|
|
94
122
|
|
|
123
|
+
def _project_command(args: argparse.Namespace) -> int:
|
|
124
|
+
from .project import ProjectError, ProjectManifest, SQLiteProjectRepository
|
|
125
|
+
|
|
126
|
+
repository = SQLiteProjectRepository(args.db)
|
|
127
|
+
try:
|
|
128
|
+
if args.project_command == "init":
|
|
129
|
+
manifest = repository.create_project(
|
|
130
|
+
ProjectManifest(
|
|
131
|
+
project_id=args.project_id,
|
|
132
|
+
name=args.name,
|
|
133
|
+
site_id=args.site,
|
|
134
|
+
workcell_id=args.workcell,
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
print(
|
|
138
|
+
json.dumps(
|
|
139
|
+
{
|
|
140
|
+
**manifest.to_dict(),
|
|
141
|
+
"manifest_hash": manifest.fingerprint,
|
|
142
|
+
"database": str(repository.path),
|
|
143
|
+
},
|
|
144
|
+
indent=2,
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
return 0
|
|
148
|
+
|
|
149
|
+
if args.project_command == "list":
|
|
150
|
+
projects = repository.list_projects()
|
|
151
|
+
print(
|
|
152
|
+
json.dumps(
|
|
153
|
+
{
|
|
154
|
+
"database": str(repository.path),
|
|
155
|
+
"projects": [
|
|
156
|
+
{**item.to_dict(), "manifest_hash": item.fingerprint}
|
|
157
|
+
for item in projects
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
indent=2,
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
return 0
|
|
164
|
+
|
|
165
|
+
if args.project_command == "show":
|
|
166
|
+
project = repository.get_project(args.project_id)
|
|
167
|
+
latest = repository.latest_twin_revision(project.project_id)
|
|
168
|
+
print(
|
|
169
|
+
json.dumps(
|
|
170
|
+
{
|
|
171
|
+
"project": {**project.to_dict(), "manifest_hash": project.fingerprint},
|
|
172
|
+
"latest_twin_revision": None if latest is None else latest.to_dict(),
|
|
173
|
+
"database": str(repository.path),
|
|
174
|
+
},
|
|
175
|
+
indent=2,
|
|
176
|
+
)
|
|
177
|
+
)
|
|
178
|
+
return 0
|
|
179
|
+
|
|
180
|
+
if args.project_command == "revisions":
|
|
181
|
+
project = repository.get_project(args.project_id)
|
|
182
|
+
revisions = repository.list_twin_revisions(project.project_id)
|
|
183
|
+
print(
|
|
184
|
+
json.dumps(
|
|
185
|
+
{
|
|
186
|
+
"project_id": project.project_id,
|
|
187
|
+
"revisions": [item.to_dict() for item in revisions],
|
|
188
|
+
"database": str(repository.path),
|
|
189
|
+
},
|
|
190
|
+
indent=2,
|
|
191
|
+
)
|
|
192
|
+
)
|
|
193
|
+
return 0
|
|
194
|
+
except ProjectError as exc:
|
|
195
|
+
print(
|
|
196
|
+
json.dumps(
|
|
197
|
+
{
|
|
198
|
+
"passed": False,
|
|
199
|
+
"failure_code": str(exc),
|
|
200
|
+
"database": str(repository.path),
|
|
201
|
+
},
|
|
202
|
+
indent=2,
|
|
203
|
+
)
|
|
204
|
+
)
|
|
205
|
+
return 10
|
|
206
|
+
return 2
|
|
207
|
+
|
|
208
|
+
|
|
95
209
|
def _setup_profile(args: argparse.Namespace) -> int:
|
|
96
210
|
from .ros2 import RosDoctor, SubprocessRosRunner
|
|
97
211
|
from .setup_profile import (
|
|
@@ -367,6 +481,9 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
367
481
|
if qualification_code is not None:
|
|
368
482
|
return qualification_code
|
|
369
483
|
|
|
484
|
+
if args.command == "project":
|
|
485
|
+
return _project_command(args)
|
|
486
|
+
|
|
370
487
|
if args.command == "setup":
|
|
371
488
|
return _setup_profile(args)
|
|
372
489
|
|
|
@@ -160,6 +160,12 @@ class MotionExecutionMetrics:
|
|
|
160
160
|
min_clearance_m: float | None = None
|
|
161
161
|
final_tcp_error_m: float | None = None
|
|
162
162
|
energy_proxy: float | None = None
|
|
163
|
+
joint_state_sample_rate_hz: float | None = None
|
|
164
|
+
max_sample_gap_s: float | None = None
|
|
165
|
+
trajectory_duration_error_s: float | None = None
|
|
166
|
+
tracking_sample_count: int | None = None
|
|
167
|
+
tracking_scope: str = "endpoint_only"
|
|
168
|
+
tracking_interpolation: str = "unspecified"
|
|
163
169
|
planner_retries: int | None = None
|
|
164
170
|
trajectory_retries: int | None = None
|
|
165
171
|
failure_codes: tuple[str, ...] = ()
|
|
@@ -168,6 +174,10 @@ class MotionExecutionMetrics:
|
|
|
168
174
|
def __post_init__(self) -> None:
|
|
169
175
|
if not self.metrics_origin.strip():
|
|
170
176
|
raise PhysicalMotionError("metrics_origin_required")
|
|
177
|
+
if not self.tracking_scope.strip():
|
|
178
|
+
raise PhysicalMotionError("tracking_scope_required")
|
|
179
|
+
if not self.tracking_interpolation.strip():
|
|
180
|
+
raise PhysicalMotionError("tracking_interpolation_required")
|
|
171
181
|
for name in (
|
|
172
182
|
"observed_duration_s",
|
|
173
183
|
"planned_duration_s",
|
|
@@ -178,6 +188,9 @@ class MotionExecutionMetrics:
|
|
|
178
188
|
"min_clearance_m",
|
|
179
189
|
"final_tcp_error_m",
|
|
180
190
|
"energy_proxy",
|
|
191
|
+
"joint_state_sample_rate_hz",
|
|
192
|
+
"max_sample_gap_s",
|
|
193
|
+
"trajectory_duration_error_s",
|
|
181
194
|
):
|
|
182
195
|
value = getattr(self, name)
|
|
183
196
|
if value is None:
|
|
@@ -185,7 +198,7 @@ class MotionExecutionMetrics:
|
|
|
185
198
|
numeric = float(value)
|
|
186
199
|
if not isfinite(numeric) or numeric < 0:
|
|
187
200
|
raise PhysicalMotionError(f"metric_invalid:{name}")
|
|
188
|
-
for name in ("planner_retries", "trajectory_retries"):
|
|
201
|
+
for name in ("tracking_sample_count", "planner_retries", "trajectory_retries"):
|
|
189
202
|
value = getattr(self, name)
|
|
190
203
|
if value is not None and value < 0:
|
|
191
204
|
raise PhysicalMotionError(f"metric_invalid:{name}")
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from math import acos, isfinite, sqrt
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PoseMathError(ValueError):
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _finite(values: tuple[float, ...], *, code: str) -> tuple[float, ...]:
|
|
12
|
+
converted = tuple(float(value) for value in values)
|
|
13
|
+
if any(not isfinite(value) for value in converted):
|
|
14
|
+
raise PoseMathError(code)
|
|
15
|
+
return converted
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def normalize_quaternion(
|
|
19
|
+
quaternion_xyzw: tuple[float, float, float, float],
|
|
20
|
+
) -> tuple[float, float, float, float]:
|
|
21
|
+
values = _finite(tuple(quaternion_xyzw), code="quaternion_non_finite")
|
|
22
|
+
if len(values) != 4:
|
|
23
|
+
raise PoseMathError("quaternion_size_invalid")
|
|
24
|
+
norm = sqrt(sum(value * value for value in values))
|
|
25
|
+
if norm <= 1e-12:
|
|
26
|
+
raise PoseMathError("quaternion_zero")
|
|
27
|
+
normalized = tuple(value / norm for value in values)
|
|
28
|
+
# q and -q encode the same rotation. Keep one deterministic representation.
|
|
29
|
+
for component in (normalized[3], normalized[0], normalized[1], normalized[2]):
|
|
30
|
+
if abs(component) <= 1e-15:
|
|
31
|
+
continue
|
|
32
|
+
return tuple(-value for value in normalized) if component < 0.0 else normalized
|
|
33
|
+
raise PoseMathError("quaternion_zero")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def quaternion_multiply(
|
|
37
|
+
first: tuple[float, float, float, float],
|
|
38
|
+
second: tuple[float, float, float, float],
|
|
39
|
+
) -> tuple[float, float, float, float]:
|
|
40
|
+
ax, ay, az, aw = normalize_quaternion(first)
|
|
41
|
+
bx, by, bz, bw = normalize_quaternion(second)
|
|
42
|
+
return normalize_quaternion(
|
|
43
|
+
(
|
|
44
|
+
aw * bx + ax * bw + ay * bz - az * by,
|
|
45
|
+
aw * by - ax * bz + ay * bw + az * bx,
|
|
46
|
+
aw * bz + ax * by - ay * bx + az * bw,
|
|
47
|
+
aw * bw - ax * bx - ay * by - az * bz,
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def quaternion_conjugate(
|
|
53
|
+
quaternion_xyzw: tuple[float, float, float, float],
|
|
54
|
+
) -> tuple[float, float, float, float]:
|
|
55
|
+
x, y, z, w = normalize_quaternion(quaternion_xyzw)
|
|
56
|
+
return (-x, -y, -z, w)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def rotate_vector(
|
|
60
|
+
quaternion_xyzw: tuple[float, float, float, float],
|
|
61
|
+
vector_xyz: tuple[float, float, float],
|
|
62
|
+
) -> tuple[float, float, float]:
|
|
63
|
+
qx, qy, qz, qw = normalize_quaternion(quaternion_xyzw)
|
|
64
|
+
vx, vy, vz = _finite(tuple(vector_xyz), code="vector_non_finite")
|
|
65
|
+
if len((vx, vy, vz)) != 3:
|
|
66
|
+
raise PoseMathError("vector_size_invalid")
|
|
67
|
+
# Unit-quaternion vector rotation without constructing temporary quaternions.
|
|
68
|
+
tx = 2.0 * (qy * vz - qz * vy)
|
|
69
|
+
ty = 2.0 * (qz * vx - qx * vz)
|
|
70
|
+
tz = 2.0 * (qx * vy - qy * vx)
|
|
71
|
+
return (
|
|
72
|
+
vx + qw * tx + (qy * tz - qz * ty),
|
|
73
|
+
vy + qw * ty + (qz * tx - qx * tz),
|
|
74
|
+
vz + qw * tz + (qx * ty - qy * tx),
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass(frozen=True, slots=True)
|
|
79
|
+
class RigidPose:
|
|
80
|
+
position_m: tuple[float, float, float]
|
|
81
|
+
quaternion_xyzw: tuple[float, float, float, float]
|
|
82
|
+
|
|
83
|
+
def __post_init__(self) -> None:
|
|
84
|
+
position = _finite(tuple(self.position_m), code="pose_position_non_finite")
|
|
85
|
+
if len(position) != 3:
|
|
86
|
+
raise PoseMathError("pose_position_size_invalid")
|
|
87
|
+
object.__setattr__(self, "position_m", position)
|
|
88
|
+
object.__setattr__(
|
|
89
|
+
self,
|
|
90
|
+
"quaternion_xyzw",
|
|
91
|
+
normalize_quaternion(tuple(self.quaternion_xyzw)),
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
@classmethod
|
|
95
|
+
def identity(cls) -> "RigidPose":
|
|
96
|
+
return cls((0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 1.0))
|
|
97
|
+
|
|
98
|
+
def compose(self, child: "RigidPose") -> "RigidPose":
|
|
99
|
+
offset = rotate_vector(self.quaternion_xyzw, child.position_m)
|
|
100
|
+
return RigidPose(
|
|
101
|
+
tuple(self.position_m[index] + offset[index] for index in range(3)),
|
|
102
|
+
quaternion_multiply(self.quaternion_xyzw, child.quaternion_xyzw),
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def inverse(self) -> "RigidPose":
|
|
106
|
+
inverse_q = quaternion_conjugate(self.quaternion_xyzw)
|
|
107
|
+
inverse_position = rotate_vector(
|
|
108
|
+
inverse_q,
|
|
109
|
+
tuple(-value for value in self.position_m),
|
|
110
|
+
)
|
|
111
|
+
return RigidPose(inverse_position, inverse_q)
|
|
112
|
+
|
|
113
|
+
def relative_to(self, parent: "RigidPose") -> "RigidPose":
|
|
114
|
+
return parent.inverse().compose(self)
|
|
115
|
+
|
|
116
|
+
def position_error_m(self, other: "RigidPose") -> float:
|
|
117
|
+
return sqrt(
|
|
118
|
+
sum(
|
|
119
|
+
(self.position_m[index] - other.position_m[index]) ** 2
|
|
120
|
+
for index in range(3)
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
def orientation_error_rad(self, other: "RigidPose") -> float:
|
|
125
|
+
first = normalize_quaternion(self.quaternion_xyzw)
|
|
126
|
+
second = normalize_quaternion(other.quaternion_xyzw)
|
|
127
|
+
dot = abs(sum(a * b for a, b in zip(first, second)))
|
|
128
|
+
return 2.0 * acos(max(-1.0, min(1.0, dot)))
|
|
129
|
+
|
|
130
|
+
def to_dict(self) -> dict[str, list[float]]:
|
|
131
|
+
return {
|
|
132
|
+
"position_m": list(self.position_m),
|
|
133
|
+
"quaternion_xyzw": list(self.quaternion_xyzw),
|
|
134
|
+
}
|