devagent-physical-engine 0.12.4__tar.gz → 0.12.5__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.12.5}/PKG-INFO +2 -2
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/README.md +1 -1
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/pyproject.toml +1 -1
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/__init__.py +1 -1
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/physical_motion.py +14 -1
- devagent_physical_engine-0.12.5/src/devagent_physical_engine/pose_math.py +134 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/reference_workcell.py +151 -39
- devagent_physical_engine-0.12.5/src/devagent_physical_engine/ros2/joint_state_probe.py +139 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/joint_state_recorder.py +35 -9
- devagent_physical_engine-0.12.5/src/devagent_physical_engine/ros2/moveit_joint_plan_probe.py +580 -0
- devagent_physical_engine-0.12.5/src/devagent_physical_engine/ros2/moveit_task_planner.py +279 -0
- devagent_physical_engine-0.12.5/src/devagent_physical_engine/ros2/task_adapter.py +160 -0
- devagent_physical_engine-0.12.5/src/devagent_physical_engine/ros2/task_visualization.py +602 -0
- devagent_physical_engine-0.12.5/src/devagent_physical_engine/ros2/trajectory.py +376 -0
- devagent_physical_engine-0.12.5/src/devagent_physical_engine/ros2/trajectory_action_probe.py +352 -0
- devagent_physical_engine-0.12.5/src/devagent_physical_engine/ros2/trajectory_tracking.py +201 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/workpiece_follow.py +79 -5
- devagent_physical_engine-0.12.5/src/devagent_physical_engine/ros2/workpiece_gazebo_follower.py +275 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/setup_profile.py +5 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5/src/devagent_physical_engine.egg-info}/PKG-INFO +2 -2
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine.egg-info/SOURCES.txt +10 -0
- devagent_physical_engine-0.12.5/tests/test_joint_state_probe_v0125.py +54 -0
- devagent_physical_engine-0.12.5/tests/test_moveit_planning_diagnostics_v0125.py +104 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_physical_motion_v09.py +5 -3
- devagent_physical_engine-0.12.5/tests/test_pose_math_v0125.py +42 -0
- devagent_physical_engine-0.12.5/tests/test_reference_workcell_ros_frames_v0125.py +41 -0
- devagent_physical_engine-0.12.5/tests/test_ros_trajectory_v09.py +300 -0
- devagent_physical_engine-0.12.5/tests/test_trajectory_action_probe_v0125.py +107 -0
- devagent_physical_engine-0.12.5/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.12.5}/NOTICE +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/setup.cfg +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/agent/__init__.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/agent/compiler.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/agent/contracts.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/agent/coordinator.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/agent/critic.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/agent/evidence.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/agent/interpreter.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/agent/planner.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/agent/recovery.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/agent/routing.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/agent/runtime.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/agent/semantic.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/agent/structured.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ai_cli.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/cli.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/continuous_clearance.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/doctor.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/engineering_agent.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/engineering_request.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/execution.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/models.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/operating_envelope.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/optimization/__init__.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/optimization/benchmark.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/optimization/candidates.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/optimization/contracts.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/optimization/evaluator.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/optimization/evidence.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/optimization/experience.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/optimization/measured.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/optimization/optimizer.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/optimization/orchestrator.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/physical_campaign.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/physical_evidence.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/planning.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/preexecution_contract.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/provider_adapters/__init__.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/provider_adapters/anthropic.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/provider_adapters/common.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/provider_adapters/factory.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/provider_adapters/gemini.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/provider_adapters/openai.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/provider_qualification.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/providers.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/qualification.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/qualification_cli.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/qualification_harness.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/robot_platform.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/robots.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/__init__.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/acceptance.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/commands.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/continuous_clearance_probe.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/doctor.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/fk_pose.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/fk_probe.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/frame_alignment.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/gazebo_cli.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/gazebo_pose.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/gazebo_world.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/measured_motion.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/moveit_scene.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/preexecution.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/qualification.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/qualification_v10.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/qualification_v11.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/qualification_v12.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/scene_probe.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/state_validity_probe.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/tf_probe.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/tool_scene.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/tool_scene_probe.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/ur5e.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/ur5e_adapter.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/ur5e_v10_adapter.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/workpiece_scene.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/ros2/workpiece_scene_probe.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/sim_real_qualification.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/simulation.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/simulation_platform.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/tool_collision.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/trajectory_qualification.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/twin.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/twin_builder.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/twin_materialization.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/verification.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine/workpiece_lifecycle.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine.egg-info/dependency_links.txt +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine.egg-info/entry_points.txt +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine.egg-info/requires.txt +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/src/devagent_physical_engine.egg-info/top_level.txt +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_agent_core.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_agent_semantic_hardening.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_ai_cli.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_ai_cli_v07.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_continuous_clearance_v011.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_core.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_engineering_request_v07.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_gazebo_cli_v0122.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_interpreter_profiles_v08.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_live_provider_qualification.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_measured_motion_v10.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_moveit_scene_v0123.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_natural_language_agent_v07.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_openai_strict_schema_v0113.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_optimization_v05.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_physical_campaign_v09.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_physical_evidence_v09.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_planning_fallback_v012.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_preexecution_contract_v10.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_provider_and_qualification.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_qualification_cli_lifecycle_v06.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_qualification_harness_v06.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_readme_pypi.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_real_provider_adapters.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_reference_workcell_v012.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_robot_platform_v08.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_ros2_production_receipts_v011.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_ros2_simulation.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_setup_profile.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_sim_real_qualification_v011.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_simulation_platform_v08.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_tool_collision_v011.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_tool_scene_v0124.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_trajectory_qualification_v09.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_twin_materialization_v10.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_twin_v08.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_ur5e_adapter_scopes_v09.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_ur5e_v10_adapter_scopes.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_v052_stabilization.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/tests/test_v08_catalog_mapping_regression.py +0 -0
- {devagent_physical_engine-0.12.4 → devagent_physical_engine-0.12.5}/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.12.
|
|
3
|
+
Version: 0.12.5
|
|
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
|
|
@@ -424,7 +424,7 @@ Detailed engineering documentation lives in the repository:
|
|
|
424
424
|
|
|
425
425
|
## Project status
|
|
426
426
|
|
|
427
|
-
**v0.12.
|
|
427
|
+
**v0.12.5 — Beta software / evidence-gated task-specific physical simulation.**
|
|
428
428
|
|
|
429
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.
|
|
430
430
|
|
|
@@ -383,7 +383,7 @@ Detailed engineering documentation lives in the repository:
|
|
|
383
383
|
|
|
384
384
|
## Project status
|
|
385
385
|
|
|
386
|
-
**v0.12.
|
|
386
|
+
**v0.12.5 — Beta software / evidence-gated task-specific physical simulation.**
|
|
387
387
|
|
|
388
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.
|
|
389
389
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "devagent-physical-engine"
|
|
7
|
-
version = "0.12.
|
|
7
|
+
version = "0.12.5"
|
|
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"
|
|
@@ -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
|
+
}
|
|
@@ -34,11 +34,21 @@ _PLATFORM_DIMS_M = (0.36, 0.36, 0.18)
|
|
|
34
34
|
_TOOL_DIMS_M = (0.04, 0.04, 0.03)
|
|
35
35
|
_TOOL_COLLISION_CENTER_LOCAL_M = (0.0, 0.0, 0.015)
|
|
36
36
|
_WORKPIECE_CENTER_FROM_TOOL_LOCAL_M = (0.0, 0.0, 0.07)
|
|
37
|
-
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
37
|
+
# tool0 and flange are fixed UR end-effector frames without collision geometry;
|
|
38
|
+
# wrist_3_link owns the physical wrist collision mesh at the mounting interface.
|
|
39
|
+
# The packaged reference gripper starts at that interface, so those three links are
|
|
40
|
+
# the only robot links it may touch. All other robot/environment collisions remain
|
|
41
|
+
# authoritative in MoveIt.
|
|
42
|
+
_REFERENCE_TOOL_TOUCH_LINKS = ("tool0", "flange", "wrist_3_link")
|
|
43
|
+
|
|
44
|
+
# Universal Robots published UR5e nominal DH dimensions in metres. Standard UR DH
|
|
45
|
+
# kinematics are expressed in the controller ``base`` convention. The ROS 2 Jazzy
|
|
46
|
+
# UR description defines ``base_link`` as REP-103 and ``base`` at the same origin
|
|
47
|
+
# rotated by pi around Z. DevAgent's canonical Twin and MoveIt FK both use
|
|
48
|
+
# ``base_link``, so the reference FK applies that documented fixed transform before
|
|
49
|
+
# the DH chain. Runtime MoveIt FK still has authority and must agree before a BOX is
|
|
50
|
+
# attached, so this template cannot silently override the robot model installed on
|
|
51
|
+
# the workstation.
|
|
42
52
|
_UR5E_DH_A = (0.0, -0.425, -0.3922, 0.0, 0.0, 0.0)
|
|
43
53
|
_UR5E_DH_D = (0.1625, 0.0, 0.0, 0.1333, 0.0997, 0.0996)
|
|
44
54
|
_UR5E_DH_ALPHA = (pi / 2, 0.0, 0.0, pi / 2, -pi / 2, 0.0)
|
|
@@ -58,46 +68,88 @@ class LoadPhaseTarget:
|
|
|
58
68
|
synchronize_workpiece_pose: bool = False
|
|
59
69
|
|
|
60
70
|
|
|
61
|
-
def _matmul(
|
|
71
|
+
def _matmul(
|
|
72
|
+
first: tuple[tuple[float, ...], ...],
|
|
73
|
+
second: tuple[tuple[float, ...], ...],
|
|
74
|
+
):
|
|
62
75
|
return tuple(
|
|
63
|
-
tuple(
|
|
76
|
+
tuple(
|
|
77
|
+
sum(first[row][k] * second[k][col] for k in range(4))
|
|
78
|
+
for col in range(4)
|
|
79
|
+
)
|
|
64
80
|
for row in range(4)
|
|
65
81
|
)
|
|
66
82
|
|
|
67
83
|
|
|
68
|
-
def _rotation_to_quaternion(
|
|
84
|
+
def _rotation_to_quaternion(
|
|
85
|
+
rotation: tuple[tuple[float, ...], ...],
|
|
86
|
+
) -> tuple[float, float, float, float]:
|
|
69
87
|
r00, r01, r02 = rotation[0]
|
|
70
88
|
r10, r11, r12 = rotation[1]
|
|
71
89
|
r20, r21, r22 = rotation[2]
|
|
72
90
|
trace = r00 + r11 + r22
|
|
73
91
|
if trace > 0.0:
|
|
74
92
|
s = sqrt(trace + 1.0) * 2.0
|
|
75
|
-
q = (
|
|
93
|
+
q = (
|
|
94
|
+
(r21 - r12) / s,
|
|
95
|
+
(r02 - r20) / s,
|
|
96
|
+
(r10 - r01) / s,
|
|
97
|
+
0.25 * s,
|
|
98
|
+
)
|
|
76
99
|
elif r00 > r11 and r00 > r22:
|
|
77
100
|
s = sqrt(1.0 + r00 - r11 - r22) * 2.0
|
|
78
|
-
q = (
|
|
101
|
+
q = (
|
|
102
|
+
0.25 * s,
|
|
103
|
+
(r01 + r10) / s,
|
|
104
|
+
(r02 + r20) / s,
|
|
105
|
+
(r21 - r12) / s,
|
|
106
|
+
)
|
|
79
107
|
elif r11 > r22:
|
|
80
108
|
s = sqrt(1.0 + r11 - r00 - r22) * 2.0
|
|
81
|
-
q = (
|
|
109
|
+
q = (
|
|
110
|
+
(r01 + r10) / s,
|
|
111
|
+
0.25 * s,
|
|
112
|
+
(r12 + r21) / s,
|
|
113
|
+
(r02 - r20) / s,
|
|
114
|
+
)
|
|
82
115
|
else:
|
|
83
116
|
s = sqrt(1.0 + r22 - r00 - r11) * 2.0
|
|
84
|
-
q = (
|
|
117
|
+
q = (
|
|
118
|
+
(r02 + r20) / s,
|
|
119
|
+
(r12 + r21) / s,
|
|
120
|
+
0.25 * s,
|
|
121
|
+
(r10 - r01) / s,
|
|
122
|
+
)
|
|
85
123
|
norm = sqrt(sum(value * value for value in q))
|
|
86
124
|
normalized = tuple(value / norm for value in q)
|
|
87
|
-
return
|
|
125
|
+
return (
|
|
126
|
+
tuple(-value for value in normalized)
|
|
127
|
+
if normalized[3] < 0.0
|
|
128
|
+
else normalized
|
|
129
|
+
)
|
|
88
130
|
|
|
89
131
|
|
|
90
132
|
def ur5e_reference_fk(joints_rad: Iterable[float]) -> ReferenceToolPose:
|
|
91
133
|
joints = tuple(float(value) for value in joints_rad)
|
|
92
134
|
if len(joints) != 6:
|
|
93
135
|
raise ValueError("ur5e_reference_fk_requires_six_joints")
|
|
136
|
+
|
|
137
|
+
# base_link_T_base: same origin, 180 deg around Z. This is the fixed transform
|
|
138
|
+
# documented by Universal Robots for the ROS ``base_link`` -> controller ``base``
|
|
139
|
+
# convention. Multiplying it before the standard DH chain expresses tool0 in
|
|
140
|
+
# ``base_link`` exactly like MoveIt / robot_state_publisher.
|
|
94
141
|
transform: tuple[tuple[float, ...], ...] = (
|
|
95
|
-
(1.0, 0.0, 0.0, 0.0),
|
|
96
|
-
(0.0, 1.0, 0.0, 0.0),
|
|
142
|
+
(-1.0, 0.0, 0.0, 0.0),
|
|
143
|
+
(0.0, -1.0, 0.0, 0.0),
|
|
97
144
|
(0.0, 0.0, 1.0, 0.0),
|
|
98
145
|
(0.0, 0.0, 0.0, 1.0),
|
|
99
146
|
)
|
|
100
|
-
for theta, a, d, alpha in zip(
|
|
147
|
+
for theta, a, d, alpha in zip(
|
|
148
|
+
joints,
|
|
149
|
+
_UR5E_DH_A,
|
|
150
|
+
_UR5E_DH_D,
|
|
151
|
+
_UR5E_DH_ALPHA,
|
|
152
|
+
):
|
|
101
153
|
ct, st = cos(theta), sin(theta)
|
|
102
154
|
ca, sa = cos(alpha), sin(alpha)
|
|
103
155
|
local = (
|
|
@@ -107,17 +159,22 @@ def ur5e_reference_fk(joints_rad: Iterable[float]) -> ReferenceToolPose:
|
|
|
107
159
|
(0.0, 0.0, 0.0, 1.0),
|
|
108
160
|
)
|
|
109
161
|
transform = _matmul(transform, local)
|
|
110
|
-
rotation = tuple(
|
|
162
|
+
rotation = tuple(
|
|
163
|
+
tuple(transform[row][col] for col in range(3))
|
|
164
|
+
for row in range(3)
|
|
165
|
+
)
|
|
111
166
|
return ReferenceToolPose(
|
|
112
167
|
position_m=tuple(float(transform[row][3]) for row in range(3)),
|
|
113
168
|
quaternion_xyzw=_rotation_to_quaternion(rotation),
|
|
114
169
|
)
|
|
115
170
|
|
|
116
171
|
|
|
117
|
-
def _rotate(
|
|
172
|
+
def _rotate(
|
|
173
|
+
quaternion: tuple[float, float, float, float],
|
|
174
|
+
vector: tuple[float, float, float],
|
|
175
|
+
):
|
|
118
176
|
x, y, z, w = quaternion
|
|
119
177
|
vx, vy, vz = vector
|
|
120
|
-
# Optimized unit-quaternion vector rotation.
|
|
121
178
|
tx = 2.0 * (y * vz - z * vy)
|
|
122
179
|
ty = 2.0 * (z * vx - x * vz)
|
|
123
180
|
tz = 2.0 * (x * vy - y * vx)
|
|
@@ -128,12 +185,19 @@ def _rotate(quaternion: tuple[float, float, float, float], vector: tuple[float,
|
|
|
128
185
|
)
|
|
129
186
|
|
|
130
187
|
|
|
131
|
-
def _translated_tool_pose(
|
|
188
|
+
def _translated_tool_pose(
|
|
189
|
+
tool: ReferenceToolPose,
|
|
190
|
+
local_offset_m: tuple[float, float, float],
|
|
191
|
+
) -> tuple[float, float, float]:
|
|
132
192
|
offset = _rotate(tool.quaternion_xyzw, local_offset_m)
|
|
133
193
|
return tuple(tool.position_m[index] + offset[index] for index in range(3))
|
|
134
194
|
|
|
135
195
|
|
|
136
|
-
def _box(
|
|
196
|
+
def _box(
|
|
197
|
+
dimensions_m: tuple[float, float, float],
|
|
198
|
+
*,
|
|
199
|
+
origin: EvidenceOrigin = EvidenceOrigin.DEFAULT,
|
|
200
|
+
) -> GeometrySpec:
|
|
137
201
|
return GeometrySpec(
|
|
138
202
|
GeometryKind.BOX,
|
|
139
203
|
origin,
|
|
@@ -156,7 +220,9 @@ def ur5e_load_phase_targets() -> tuple[LoadPhaseTarget, ...]:
|
|
|
156
220
|
)
|
|
157
221
|
|
|
158
222
|
|
|
159
|
-
def ur5e_reference_load_twin(
|
|
223
|
+
def ur5e_reference_load_twin(
|
|
224
|
+
request: ValidatedEngineeringRequest,
|
|
225
|
+
) -> TwinSpec:
|
|
160
226
|
if request.robot_key != "ur5e":
|
|
161
227
|
raise ValueError("reference_load_workcell_requires_ur5e")
|
|
162
228
|
if request.operation != "load":
|
|
@@ -166,13 +232,25 @@ def ur5e_reference_load_twin(request: ValidatedEngineeringRequest) -> TwinSpec:
|
|
|
166
232
|
|
|
167
233
|
pick_tool = ur5e_reference_fk(UR5E_PICK)
|
|
168
234
|
place_tool = ur5e_reference_fk(UR5E_PLACE)
|
|
169
|
-
source_box_center = _translated_tool_pose(
|
|
170
|
-
|
|
235
|
+
source_box_center = _translated_tool_pose(
|
|
236
|
+
pick_tool,
|
|
237
|
+
_WORKPIECE_CENTER_FROM_TOOL_LOCAL_M,
|
|
238
|
+
)
|
|
239
|
+
destination_box_center = _translated_tool_pose(
|
|
240
|
+
place_tool,
|
|
241
|
+
_WORKPIECE_CENTER_FROM_TOOL_LOCAL_M,
|
|
242
|
+
)
|
|
171
243
|
|
|
172
244
|
workpiece_bottom_z = source_box_center[2] - _WORKPIECE_DIMS_M[2] / 2.0
|
|
173
|
-
source_platform_center_z =
|
|
174
|
-
|
|
175
|
-
|
|
245
|
+
source_platform_center_z = (
|
|
246
|
+
workpiece_bottom_z - _PLATFORM_DIMS_M[2] / 2.0
|
|
247
|
+
)
|
|
248
|
+
destination_bottom_z = (
|
|
249
|
+
destination_box_center[2] - _WORKPIECE_DIMS_M[2] / 2.0
|
|
250
|
+
)
|
|
251
|
+
destination_platform_center_z = (
|
|
252
|
+
destination_bottom_z - _PLATFORM_DIMS_M[2] / 2.0
|
|
253
|
+
)
|
|
176
254
|
|
|
177
255
|
source = TwinEntity(
|
|
178
256
|
request.source,
|
|
@@ -185,7 +263,10 @@ def ur5e_reference_load_twin(request: ValidatedEngineeringRequest) -> TwinSpec:
|
|
|
185
263
|
source_ref=REFERENCE_SOURCE_REF,
|
|
186
264
|
),
|
|
187
265
|
_box(_PLATFORM_DIMS_M),
|
|
188
|
-
metadata={
|
|
266
|
+
metadata={
|
|
267
|
+
"reference_role": "source",
|
|
268
|
+
"template": REFERENCE_WORKCELL_ID,
|
|
269
|
+
},
|
|
189
270
|
)
|
|
190
271
|
destination = TwinEntity(
|
|
191
272
|
request.destination,
|
|
@@ -198,7 +279,10 @@ def ur5e_reference_load_twin(request: ValidatedEngineeringRequest) -> TwinSpec:
|
|
|
198
279
|
source_ref=REFERENCE_SOURCE_REF,
|
|
199
280
|
),
|
|
200
281
|
_box(_PLATFORM_DIMS_M),
|
|
201
|
-
metadata={
|
|
282
|
+
metadata={
|
|
283
|
+
"reference_role": "destination",
|
|
284
|
+
"template": REFERENCE_WORKCELL_ID,
|
|
285
|
+
},
|
|
202
286
|
)
|
|
203
287
|
workpiece = TwinEntity(
|
|
204
288
|
request.object_id,
|
|
@@ -211,14 +295,29 @@ def ur5e_reference_load_twin(request: ValidatedEngineeringRequest) -> TwinSpec:
|
|
|
211
295
|
_box(_WORKPIECE_DIMS_M),
|
|
212
296
|
physics=PhysicsSpec(
|
|
213
297
|
mass_kg=EvidenceValue(
|
|
214
|
-
float(request.payload_kg)
|
|
215
|
-
|
|
298
|
+
float(request.payload_kg)
|
|
299
|
+
if request.payload_kg is not None
|
|
300
|
+
else 1.0,
|
|
301
|
+
EvidenceOrigin.USER_DECLARED
|
|
302
|
+
if request.payload_kg is not None
|
|
303
|
+
else EvidenceOrigin.DEFAULT,
|
|
304
|
+
source_ref=REFERENCE_SOURCE_REF,
|
|
305
|
+
),
|
|
306
|
+
friction_coefficient=EvidenceValue(
|
|
307
|
+
0.6,
|
|
308
|
+
EvidenceOrigin.DEFAULT,
|
|
309
|
+
source_ref=REFERENCE_SOURCE_REF,
|
|
310
|
+
),
|
|
311
|
+
restitution=EvidenceValue(
|
|
312
|
+
0.0,
|
|
313
|
+
EvidenceOrigin.DEFAULT,
|
|
216
314
|
source_ref=REFERENCE_SOURCE_REF,
|
|
217
315
|
),
|
|
218
|
-
friction_coefficient=EvidenceValue(0.6, EvidenceOrigin.DEFAULT, source_ref=REFERENCE_SOURCE_REF),
|
|
219
|
-
restitution=EvidenceValue(0.0, EvidenceOrigin.DEFAULT, source_ref=REFERENCE_SOURCE_REF),
|
|
220
316
|
),
|
|
221
|
-
metadata={
|
|
317
|
+
metadata={
|
|
318
|
+
"reference_role": "workpiece",
|
|
319
|
+
"template": REFERENCE_WORKCELL_ID,
|
|
320
|
+
},
|
|
222
321
|
)
|
|
223
322
|
return TwinSpec(
|
|
224
323
|
twin_id=f"TWIN-{request.request_id}-{REFERENCE_WORKCELL_ID}",
|
|
@@ -244,18 +343,31 @@ def ur5e_reference_load_twin(request: ValidatedEngineeringRequest) -> TwinSpec:
|
|
|
244
343
|
source_ref=REFERENCE_SOURCE_REF,
|
|
245
344
|
),
|
|
246
345
|
geometry=_box(_TOOL_DIMS_M, origin=EvidenceOrigin.DEFAULT),
|
|
247
|
-
max_payload_kg=EvidenceValue(
|
|
346
|
+
max_payload_kg=EvidenceValue(
|
|
347
|
+
5.0,
|
|
348
|
+
EvidenceOrigin.ROBOT_PROFILE,
|
|
349
|
+
source_ref=REFERENCE_SOURCE_REF,
|
|
350
|
+
),
|
|
248
351
|
),
|
|
249
352
|
controller=ControllerModel(
|
|
250
|
-
speed_scale_pct=EvidenceValue(
|
|
353
|
+
speed_scale_pct=EvidenceValue(
|
|
354
|
+
30.0,
|
|
355
|
+
EvidenceOrigin.DEFAULT,
|
|
356
|
+
source_ref=REFERENCE_SOURCE_REF,
|
|
357
|
+
)
|
|
251
358
|
),
|
|
252
359
|
metadata={
|
|
253
360
|
"template": REFERENCE_WORKCELL_ID,
|
|
254
|
-
"template_scope":
|
|
361
|
+
"template_scope": (
|
|
362
|
+
"packaged_reference_simulation_not_customer_site_geometry"
|
|
363
|
+
),
|
|
255
364
|
"request_id": request.request_id,
|
|
365
|
+
"kinematic_reference_frame": "base_link",
|
|
256
366
|
"expected_pick_tcp_position_m": list(pick_tool.position_m),
|
|
257
367
|
"expected_place_tcp_position_m": list(place_tool.position_m),
|
|
258
|
-
"workpiece_tool_offset_local_m": list(
|
|
368
|
+
"workpiece_tool_offset_local_m": list(
|
|
369
|
+
_WORKPIECE_CENTER_FROM_TOOL_LOCAL_M
|
|
370
|
+
),
|
|
259
371
|
"physical_qualification": False,
|
|
260
372
|
"site_qualification": False,
|
|
261
373
|
},
|
|
@@ -267,5 +379,5 @@ def ur5e_reference_tool_binding() -> ToolCollisionBinding:
|
|
|
267
379
|
mount_link="tool0",
|
|
268
380
|
position_m=_TOOL_COLLISION_CENTER_LOCAL_M,
|
|
269
381
|
quaternion_xyzw=(0.0, 0.0, 0.0, 1.0),
|
|
270
|
-
touch_links=
|
|
382
|
+
touch_links=_REFERENCE_TOOL_TOUCH_LINKS,
|
|
271
383
|
)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
from math import isfinite
|
|
6
|
+
import os
|
|
7
|
+
from time import monotonic
|
|
8
|
+
from typing import Any, Sequence
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _validated_payload(
|
|
12
|
+
names: Sequence[str],
|
|
13
|
+
positions: Sequence[float],
|
|
14
|
+
required_joint_names: Sequence[str],
|
|
15
|
+
) -> tuple[dict[str, Any] | None, str]:
|
|
16
|
+
canonical_names = tuple(str(name) for name in names)
|
|
17
|
+
try:
|
|
18
|
+
canonical_positions = tuple(float(value) for value in positions)
|
|
19
|
+
except (TypeError, ValueError):
|
|
20
|
+
return None, "joint_state_values_invalid"
|
|
21
|
+
|
|
22
|
+
if not canonical_names or len(canonical_names) != len(canonical_positions):
|
|
23
|
+
return None, "joint_state_shape_invalid"
|
|
24
|
+
if any(not name.strip() for name in canonical_names):
|
|
25
|
+
return None, "joint_state_empty_name"
|
|
26
|
+
if len(set(canonical_names)) != len(canonical_names):
|
|
27
|
+
return None, "joint_state_duplicate_name"
|
|
28
|
+
if any(not isfinite(value) for value in canonical_positions):
|
|
29
|
+
return None, "joint_state_non_finite"
|
|
30
|
+
|
|
31
|
+
required = tuple(str(name).strip() for name in required_joint_names)
|
|
32
|
+
if any(not name for name in required) or len(set(required)) != len(required):
|
|
33
|
+
return None, "joint_state_required_names_invalid"
|
|
34
|
+
missing = tuple(sorted(set(required) - set(canonical_names)))
|
|
35
|
+
if missing:
|
|
36
|
+
return None, "joint_state_missing:" + ",".join(missing)
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
{
|
|
40
|
+
"success": True,
|
|
41
|
+
"code": "joint_state_snapshot_verified",
|
|
42
|
+
"name": list(canonical_names),
|
|
43
|
+
"position": list(canonical_positions),
|
|
44
|
+
},
|
|
45
|
+
"joint_state_snapshot_verified",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def capture_joint_state(
|
|
50
|
+
*,
|
|
51
|
+
topic: str,
|
|
52
|
+
timeout_s: float,
|
|
53
|
+
required_joint_names: Sequence[str],
|
|
54
|
+
) -> dict[str, Any]:
|
|
55
|
+
if timeout_s <= 0:
|
|
56
|
+
raise ValueError("joint_state_timeout_must_be_positive")
|
|
57
|
+
if not topic.startswith("/") or any(character.isspace() for character in topic):
|
|
58
|
+
raise ValueError("joint_state_topic_invalid")
|
|
59
|
+
|
|
60
|
+
import rclpy
|
|
61
|
+
from rclpy.qos import qos_profile_sensor_data
|
|
62
|
+
from sensor_msgs.msg import JointState
|
|
63
|
+
|
|
64
|
+
rclpy.init(args=None)
|
|
65
|
+
node = rclpy.create_node(f"devagent_joint_state_probe_{os.getpid()}")
|
|
66
|
+
result: dict[str, Any] | None = None
|
|
67
|
+
received_samples = 0
|
|
68
|
+
last_invalid_code = "joint_state_timeout"
|
|
69
|
+
|
|
70
|
+
def receive(message: JointState) -> None:
|
|
71
|
+
nonlocal result, received_samples, last_invalid_code
|
|
72
|
+
received_samples += 1
|
|
73
|
+
payload, code = _validated_payload(
|
|
74
|
+
message.name,
|
|
75
|
+
message.position,
|
|
76
|
+
required_joint_names,
|
|
77
|
+
)
|
|
78
|
+
if payload is not None:
|
|
79
|
+
result = payload
|
|
80
|
+
else:
|
|
81
|
+
last_invalid_code = code
|
|
82
|
+
|
|
83
|
+
subscription = node.create_subscription(
|
|
84
|
+
JointState,
|
|
85
|
+
topic,
|
|
86
|
+
receive,
|
|
87
|
+
qos_profile_sensor_data,
|
|
88
|
+
)
|
|
89
|
+
deadline = monotonic() + float(timeout_s)
|
|
90
|
+
try:
|
|
91
|
+
while rclpy.ok() and result is None:
|
|
92
|
+
remaining = deadline - monotonic()
|
|
93
|
+
if remaining <= 0:
|
|
94
|
+
break
|
|
95
|
+
rclpy.spin_once(node, timeout_sec=min(0.2, remaining))
|
|
96
|
+
|
|
97
|
+
if result is not None:
|
|
98
|
+
return {
|
|
99
|
+
**result,
|
|
100
|
+
"received_samples": received_samples,
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
"success": False,
|
|
104
|
+
"code": last_invalid_code,
|
|
105
|
+
"received_samples": received_samples,
|
|
106
|
+
}
|
|
107
|
+
finally:
|
|
108
|
+
node.destroy_subscription(subscription)
|
|
109
|
+
node.destroy_node()
|
|
110
|
+
if rclpy.ok():
|
|
111
|
+
rclpy.shutdown()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def main() -> int:
|
|
115
|
+
parser = argparse.ArgumentParser()
|
|
116
|
+
parser.add_argument("--topic", default="/joint_states")
|
|
117
|
+
parser.add_argument("--timeout", type=float, default=8.0)
|
|
118
|
+
parser.add_argument("--required-joint", action="append", default=[])
|
|
119
|
+
args = parser.parse_args()
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
result = capture_joint_state(
|
|
123
|
+
topic=str(args.topic),
|
|
124
|
+
timeout_s=float(args.timeout),
|
|
125
|
+
required_joint_names=tuple(str(value) for value in args.required_joint),
|
|
126
|
+
)
|
|
127
|
+
except Exception as exc:
|
|
128
|
+
result = {
|
|
129
|
+
"success": False,
|
|
130
|
+
"code": f"joint_state_probe_error:{type(exc).__name__}",
|
|
131
|
+
"received_samples": 0,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
print(json.dumps(result, sort_keys=True, separators=(",", ":"), allow_nan=False))
|
|
135
|
+
return 0 if result.get("success") else 2
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
if __name__ == "__main__":
|
|
139
|
+
raise SystemExit(main())
|