trialone 0.4.7__py3-none-any.whl
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.
- trialone/__init__.py +0 -0
- trialone/adapters/__init__.py +9 -0
- trialone/adapters/gen_ground_truth.py +97 -0
- trialone/adapters/gen_map.py +99 -0
- trialone/adapters/instrument_world.py +127 -0
- trialone/adapters/isaac/__init__.py +0 -0
- trialone/adapters/isaac/boot_proof.py +110 -0
- trialone/adapters/isaac/gen_ground_truth_isaac.py +59 -0
- trialone/adapters/isaac/gt_publisher.py +137 -0
- trialone/adapters/isaac/sim_main.py +173 -0
- trialone/adapters/llm/custom_local_llm_adapter.py +1 -0
- trialone/adapters/llm/openai_adapter.py +1 -0
- trialone/adapters/machine_learning/anomaly_detection_model.py +1 -0
- trialone/agency/__init__.py +26 -0
- trialone/agency/assist.py +267 -0
- trialone/agency/build.py +384 -0
- trialone/agency/causal.py +192 -0
- trialone/agency/code_patch.py +96 -0
- trialone/agency/interpret.py +169 -0
- trialone/agency/llm_policy.py +281 -0
- trialone/agency/llm_proxy_cache.py +57 -0
- trialone/agency/memory/long_term_episodic_memory.py +1 -0
- trialone/agency/memory/short_term_working_memory.py +1 -0
- trialone/agency/memory/swarm_state_graph.py +1 -0
- trialone/agency/neuro_symbolic/__init__.py +2 -0
- trialone/agency/neuro_symbolic/neuro_symbolic_orchestrator.py +1 -0
- trialone/agency/param_server.py +119 -0
- trialone/agency/populate_tools.py +65 -0
- trialone/agency/routing/intent_classifier.py +11 -0
- trialone/agency/routing/load_balancer.py +10 -0
- trialone/agency/routing/resilience/llm_fallback_router.py +1 -0
- trialone/agency/strategy/__init__.py +1 -0
- trialone/agency/strategy/hierarchical_tool_planner.py +1 -0
- trialone/agency/strategy/hypothesis_evaluator.py +1 -0
- trialone/agency/subagents/code_patch_synthesizer_subagent.py +25 -0
- trialone/agency/subagents/fleet_supervisor_subagent.py +22 -0
- trialone/agency/subagents/master_swarm_orchestrator.py +16 -0
- trialone/agency/subagents/parameter_optimization_subagent.py +23 -0
- trialone/agency/subagents/patch_generator.py +26 -0
- trialone/agency/subagents/regression_test_author_subagent.py +20 -0
- trialone/agency/subagents/telemetry_filtering_subagent.py +19 -0
- trialone/agency/subagents/triage_brain.py +14 -0
- trialone/agency/subagents/triage_subagent.py +45 -0
- trialone/agency/subagents/verification_subagent.py +24 -0
- trialone/agency/swarm/__init__.py +1 -0
- trialone/agency/swarm/high_bandwidth_tensor_comm.py +1 -0
- trialone/agency/tool_registry.py +129 -0
- trialone/agency/tools/behavior_tree_state_tool.py +105 -0
- trialone/agency/tools/binary_payload_decoder_tool.py +31 -0
- trialone/agency/tools/bms_cell_voltage_tool.py +103 -0
- trialone/agency/tools/causal_dag_falsifier_tool.py +29 -0
- trialone/agency/tools/ci_cd_pytest_suite_tool.py +29 -0
- trialone/agency/tools/confidence_evidence_scorer_tool.py +29 -0
- trialone/agency/tools/contact_sensor_state_tool.py +138 -0
- trialone/agency/tools/costmap_voxel_clearing_tool.py +96 -0
- trialone/agency/tools/counterfactual_generator_tool.py +29 -0
- trialone/agency/tools/cpp_source_ast_inspector_tool.py +29 -0
- trialone/agency/tools/cto_guarantee_certificate_tool.py +29 -0
- trialone/agency/tools/degradation_trend_inspector_tool.py +29 -0
- trialone/agency/tools/docker_env_inspector_tool.py +29 -0
- trialone/agency/tools/docker_sim_container_tool.py +29 -0
- trialone/agency/tools/force_torque_wrench_tool.py +57 -0
- trialone/agency/tools/git_diff_inspector_tool.py +29 -0
- trialone/agency/tools/github_pr_builder_tool.py +29 -0
- trialone/agency/tools/graph_math_tools.py +55 -0
- trialone/agency/tools/hardware/bom_diff_analyzer.py +18 -0
- trialone/agency/tools/hardware/camera_intrinsics_calibration_tool.py +1 -0
- trialone/agency/tools/hardware/chrony_ntp_drift_analyzer_tool.py +1 -0
- trialone/agency/tools/hardware/dynamic_system_tracer.py +18 -0
- trialone/agency/tools/hardware/ebpf_kernel_probe.py +26 -0
- trialone/agency/tools/hardware/hardware_spec_sheet_analyzer.py +17 -0
- trialone/agency/tools/hardware/kernel_memory_prober.py +11 -0
- trialone/agency/tools/hardware/p1_bms_cell_voltage_tool.py +1 -0
- trialone/agency/tools/hardware/p1_canopen_sdo_reader_tool.py +1 -0
- trialone/agency/tools/hardware/p1_linux_dmesg_search_tool.py +1 -0
- trialone/agency/tools/hardware/p1_nvml_gpu_thermal_tool.py +1 -0
- trialone/agency/tools/hardware/p1_power_board_sag_tool.py +1 -0
- trialone/agency/tools/hardware/p1_serial_modbus_rtu_tool.py +1 -0
- trialone/agency/tools/hardware/p1_syslog_kernel_oops_tool.py +1 -0
- trialone/agency/tools/hardware/p1_usb_power_autosuspend_tool.py +1 -0
- trialone/agency/tools/hardware/power_brownout_transient_tool.py +1 -0
- trialone/agency/tools/hardware/rtos_scheduler_trace_tool.py +1 -0
- trialone/agency/tools/hardware/system_resource_allocator.py +16 -0
- trialone/agency/tools/hardware/systemd_journal_tool.py +13 -0
- trialone/agency/tools/hardware_action_card_tool.py +29 -0
- trialone/agency/tools/hil_tools.py +46 -0
- trialone/agency/tools/incident_fingerprint_hasher_tool.py +29 -0
- trialone/agency/tools/kinematic_wheel_slip_tool.py +29 -0
- trialone/agency/tools/knowledge_graph_query_tool.py +29 -0
- trialone/agency/tools/linux_dmesg_search_tool.py +85 -0
- trialone/agency/tools/mcap_compression_check_tool.py +29 -0
- trialone/agency/tools/mcap_inspector_tool.py +32 -0
- trialone/agency/tools/mcap_slice_tool.py +70 -0
- trialone/agency/tools/mcap_topic_frequency_tool.py +124 -0
- trialone/agency/tools/message_timestamp_drift_tool.py +58 -0
- trialone/agency/tools/micro_ros_entity_tool.py +104 -0
- trialone/agency/tools/network/external_api_mqtt_traffic_tool.py +1 -0
- trialone/agency/tools/network/headless_mcap_replayer.py +19 -0
- trialone/agency/tools/network/mcap_slice_tool.py +12 -0
- trialone/agency/tools/network/p2_binary_payload_decoder_tool.py +1 -0
- trialone/agency/tools/network/p2_mcap_compression_check_tool.py +1 -0
- trialone/agency/tools/network/p2_mcap_topic_frequency_tool.py +1 -0
- trialone/agency/tools/network/p2_message_timestamp_drift_tool.py +1 -0
- trialone/agency/tools/network/p2_micro_ros_entity_tool.py +1 -0
- trialone/agency/tools/network/p2_protobuf_schema_inspector_tool.py +1 -0
- trialone/agency/tools/network/p2_rosbag2_metadata_reader_tool.py +1 -0
- trialone/agency/tools/network/p2_zenoh_session_stats_tool.py +1 -0
- trialone/agency/tools/network/ros2_dynamic_reconfigure_tool.py +13 -0
- trialone/agency/tools/network/ros2_network_topology_mapper.py +16 -0
- trialone/agency/tools/network/wireless_roaming_rssi_tool.py +1 -0
- trialone/agency/tools/network/wireshark_pcap_analyzer_tool.py +1 -0
- trialone/agency/tools/nvml_gpu_thermal_tool.py +67 -0
- trialone/agency/tools/pareto_constraint_solver_tool.py +29 -0
- trialone/agency/tools/physics/p3_behavior_tree_state_tool.py +57 -0
- trialone/agency/tools/physics/p3_contact_sensor_state_tool.py +1 -0
- trialone/agency/tools/physics/p3_costmap_voxel_clearing_tool.py +46 -0
- trialone/agency/tools/physics/p3_force_torque_wrench_tool.py +1 -0
- trialone/agency/tools/physics/p3_kinematic_wheel_slip_tool.py +61 -0
- trialone/agency/tools/physics/p3_pointcloud_glare_noise_tool.py +58 -0
- trialone/agency/tools/physics/p3_tf2_transform_drift_tool.py +55 -0
- trialone/agency/tools/physics/p3_visual_odometry_tracking_tool.py +52 -0
- trialone/agency/tools/physics/sim2real_calibrator_tool.py +22 -0
- trialone/agency/tools/physics/sim_experiment_tool.py +12 -0
- trialone/agency/tools/physics/urdf_kinematics_tool.py +11 -0
- trialone/agency/tools/pointcloud_glare_noise_tool.py +29 -0
- trialone/agency/tools/power_board_sag_tool.py +29 -0
- trialone/agency/tools/protobuf_schema_inspector_tool.py +29 -0
- trialone/agency/tools/pytorch_retrain_script_tool.py +29 -0
- trialone/agency/tools/ros2_dynamic_reconfigure_tool.py +105 -0
- trialone/agency/tools/ros2_params_yaml_tool.py +29 -0
- trialone/agency/tools/rosbag2_metadata_reader_tool.py +67 -0
- trialone/agency/tools/serial_modbus_rtu_tool.py +71 -0
- trialone/agency/tools/severity_ranker_calculator_tool.py +29 -0
- trialone/agency/tools/sim_experiment_runner_tool.py +29 -0
- trialone/agency/tools/simulation/digital_twin_physics_engine.py +18 -0
- trialone/agency/tools/simulation/synthetic_scenario_generator.py +18 -0
- trialone/agency/tools/software/anti_hallucination/ast_syntax_checker.py +1 -0
- trialone/agency/tools/software/anti_hallucination/hallucination_score_calculator.py +1 -0
- trialone/agency/tools/software/anti_hallucination/z3_logical_solver.py +1 -0
- trialone/agency/tools/software/binary_decompiler_ghidra_bridge.py +12 -0
- trialone/agency/tools/software/code_patcher.py +14 -0
- trialone/agency/tools/software/distributed_swarm_temporal_analyzer.py +18 -0
- trialone/agency/tools/software/docker_container_tool.py +13 -0
- trialone/agency/tools/software/git_blame_tool.py +12 -0
- trialone/agency/tools/software/git_history_blame_analyzer.py +16 -0
- trialone/agency/tools/software/p5_cpp_source_ast_inspector_tool.py +59 -0
- trialone/agency/tools/software/p5_docker_env_inspector_tool.py +56 -0
- trialone/agency/tools/software/p5_git_diff_inspector_tool.py +46 -0
- trialone/agency/tools/software/p5_mcap_slice_tool.py +1 -0
- trialone/agency/tools/software/p5_ros2_dynamic_reconfigure_tool.py +1 -0
- trialone/agency/tools/software/p5_ros2_params_yaml_tool.py +44 -0
- trialone/agency/tools/software/p5_systemd_service_status_tool.py +58 -0
- trialone/agency/tools/software/p5_urdf_tf_tree_tool.py +63 -0
- trialone/agency/tools/software/plc_memory_map_decoder.py +18 -0
- trialone/agency/tools/software/proprietary_language_transpiler.py +19 -0
- trialone/agency/tools/software/timeline_alignment_tool.py +8 -0
- trialone/agency/tools/software/yaml_param_tool.py +14 -0
- trialone/agency/tools/solvers/deduplication_query_tool.py +13 -0
- trialone/agency/tools/solvers/fix_proposal_evaluator_tool.py +14 -0
- trialone/agency/tools/solvers/formal_cxx_verifier_tool.py +17 -0
- trialone/agency/tools/solvers/p4_causal_dag_falsifier_tool.py +62 -0
- trialone/agency/tools/solvers/p4_confidence_evidence_scorer_tool.py +62 -0
- trialone/agency/tools/solvers/p4_counterfactual_generator_tool.py +48 -0
- trialone/agency/tools/solvers/p4_degradation_trend_inspector_tool.py +54 -0
- trialone/agency/tools/solvers/p4_incident_fingerprint_hasher.py +48 -0
- trialone/agency/tools/solvers/p4_knowledge_graph_query_tool.py +55 -0
- trialone/agency/tools/solvers/p4_pareto_constraint_solver_tool.py +57 -0
- trialone/agency/tools/solvers/p4_severity_ranker_calculator_tool.py +48 -0
- trialone/agency/tools/syslog_kernel_oops_tool.py +59 -0
- trialone/agency/tools/systemd_service_status_tool.py +29 -0
- trialone/agency/tools/tf2_transform_drift_tool.py +29 -0
- trialone/agency/tools/urdf_tf_tree_tool.py +100 -0
- trialone/agency/tools/usb_power_autosuspend_tool.py +82 -0
- trialone/agency/tools/verification/p6_ci_cd_pytest_suite_tool.py +92 -0
- trialone/agency/tools/verification/p6_cto_guarantee_certificate_tool.py +120 -0
- trialone/agency/tools/verification/p6_docker_sim_container_tool.py +68 -0
- trialone/agency/tools/verification/p6_github_pr_builder_tool.py +54 -0
- trialone/agency/tools/verification/p6_hardware_action_card_tool.py +65 -0
- trialone/agency/tools/verification/p6_pytorch_retrain_script_tool.py +60 -0
- trialone/agency/tools/verification/p6_sim_experiment_runner_tool.py +77 -0
- trialone/agency/tools/verification/p6_vscode_editor_push_tool.py +58 -0
- trialone/agency/tools/visual_odometry_tracking_tool.py +118 -0
- trialone/agency/tools/vscode_editor_push_tool.py +29 -0
- trialone/agency/tools/zenoh_session_stats_tool.py +40 -0
- trialone/agency/vscode_link.py +404 -0
- trialone/anzeige.py +1019 -0
- trialone/api/__init__.py +6 -0
- trialone/api/agency_routes.py +16 -0
- trialone/api/campaigns.py +82 -0
- trialone/api/connect.py +71 -0
- trialone/api/execution.py +101 -0
- trialone/api/findings.py +95 -0
- trialone/api/fixes.py +16 -0
- trialone/api/github_integration.py +16 -0
- trialone/api/health.py +16 -0
- trialone/api/news.py +16 -0
- trialone/api/runs.py +16 -0
- trialone/api/scenarios.py +66 -0
- trialone/cli.py +2097 -0
- trialone/cloud/api_gateway.py +17 -0
- trialone/cloud/api_gateway_ingress.py +15 -0
- trialone/cloud/browser_websocket_gateway.py +11 -0
- trialone/cloud/canary_deployment_manager.py +20 -0
- trialone/cloud/compliance/audit_log_generator.py +1 -0
- trialone/cloud/compliance/data_retention_scrubber.py +1 -0
- trialone/cloud/data_anonymization_pipeline.py +15 -0
- trialone/cloud/database_migrations/migration_runner.py +1 -0
- trialone/cloud/disaster_recovery/database_snapshot_manager.py +1 -0
- trialone/cloud/disaster_recovery/multi_region_failover.py +1 -0
- trialone/cloud/docker_container_orchestrator.py +8 -0
- trialone/cloud/edge_to_cloud_vpn_tunnel.py +11 -0
- trialone/cloud/federated_learning_coordinator.py +15 -0
- trialone/cloud/finops/api_rate_limiter.py +1 -0
- trialone/cloud/finops/context_window_trimmer.py +1 -0
- trialone/cloud/finops/llm_cost_circuit_breaker.py +1 -0
- trialone/cloud/finops/s3_glacier_lifecycle_manager.py +1 -0
- trialone/cloud/finops/spot_instance_orchestrator.py +1 -0
- trialone/cloud/fleet_analytics/global_error_trend_analyzer.py +1 -0
- trialone/cloud/fleet_analytics/predictive_maintenance_model.py +1 -0
- trialone/cloud/fleet_cohort_matrix.py +13 -0
- trialone/cloud/hybrid_storage_router.py +20 -0
- trialone/cloud/mcap_diagnostics_api.py +14 -0
- trialone/cloud/multi_tenancy/rbac_permission_engine.py +15 -0
- trialone/cloud/multi_tenancy/tenant_isolation_middleware.py +15 -0
- trialone/cloud/network/fleet_raft_consensus.py +27 -0
- trialone/cloud/network/websocket_event_bus.py +11 -0
- trialone/cloud/on_premise_k8s_deployer.py +18 -0
- trialone/cloud/payload_encryption_aes.py +12 -0
- trialone/cloud/security/gvisor_sandbox_jail.py +1 -0
- trialone/cloud/security/llm_prompt_injection_firewall.py +1 -0
- trialone/cloud/security/secrets_vault_manager.py +1 -0
- trialone/cloud/simulation_compute_cluster.py +20 -0
- trialone/cloud/storage_router.py +14 -0
- trialone/cloud/web_api_server.py +36 -0
- trialone/cloud/zero_trust_auth_gateway.py +19 -0
- trialone/config/backend_dependency_registry.py +1 -0
- trialone/config/cloud_event_dispatcher.py +1 -0
- trialone/config/dynamic/feature_flag_manager.py +1 -0
- trialone/config/llm_interface.py +1676 -0
- trialone/config/semantic_llm_cache.py +1 -0
- trialone/config/settings.py +83 -0
- trialone/dataset.py +170 -0
- trialone/delivery/__init__.py +30 -0
- trialone/delivery/audit_report.py +223 -0
- trialone/delivery/ci_cd/enterprise_ci_cd_integration.py +18 -0
- trialone/delivery/ci_cd/github_exporter.py +14 -0
- trialone/delivery/hitl_gate/approval_state_machine.py +1 -0
- trialone/delivery/hitl_gate/notification_dispatcher.py +1 -0
- trialone/delivery/hitl_gate/patch_quarantine_storage.py +1 -0
- trialone/delivery/immediate_alert_dispatcher.py +19 -0
- trialone/delivery/llm_handoff_markdown_generator.py +153 -0
- trialone/delivery/notifications/slack_notifier.py +178 -0
- trialone/delivery/ota/fleet_ota_deployer.py +17 -0
- trialone/delivery/proof_of_value/comprehensive_audit_report.py +1 -0
- trialone/delivery/proof_of_value/instant_aha_moment_webhook.py +1 -0
- trialone/delivery/proof_of_value/weekly_roi_report_generator.py +1 -0
- trialone/delivery/report_generator.py +126 -0
- trialone/demo_log.py +75 -0
- trialone/diagnose/__init__.py +6 -0
- trialone/diagnose/abdeckung.py +433 -0
- trialone/diagnose/action_plan.py +190 -0
- trialone/diagnose/bauplan.py +647 -0
- trialone/diagnose/bauplan_abgleich.py +193 -0
- trialone/diagnose/beobachter.py +156 -0
- trialone/diagnose/brain.py +95 -0
- trialone/diagnose/channel_prediction.py +187 -0
- trialone/diagnose/coupling.py +136 -0
- trialone/diagnose/crash_window.py +781 -0
- trialone/diagnose/engine.py +5495 -0
- trialone/diagnose/episodes.py +558 -0
- trialone/diagnose/erklaerte_grenzen.py +239 -0
- trialone/diagnose/experiment.py +91 -0
- trialone/diagnose/explain.py +1645 -0
- trialone/diagnose/explain_design.py +204 -0
- trialone/diagnose/explain_moat.py +136 -0
- trialone/diagnose/explain_search.py +847 -0
- trialone/diagnose/explain_text.py +1583 -0
- trialone/diagnose/explain_tools.py +1151 -0
- trialone/diagnose/faults.py +140 -0
- trialone/diagnose/faultspace.py +221 -0
- trialone/diagnose/feature_advisor.py +179 -0
- trialone/diagnose/fix_corpus.py +170 -0
- trialone/diagnose/fleet.py +216 -0
- trialone/diagnose/followup.py +163 -0
- trialone/diagnose/fortschritt.py +592 -0
- trialone/diagnose/geschwister.py +258 -0
- trialone/diagnose/history.py +632 -0
- trialone/diagnose/hypothesis_loop.py +270 -0
- trialone/diagnose/instrumente.py +976 -0
- trialone/diagnose/log_text.py +116 -0
- trialone/diagnose/parameter_prediction.py +152 -0
- trialone/diagnose/passt_die_reparatur.py +123 -0
- trialone/diagnose/physics.py +847 -0
- trialone/diagnose/place.py +284 -0
- trialone/diagnose/quell_vorhersage.py +257 -0
- trialone/diagnose/randlage.py +119 -0
- trialone/diagnose/recipe.py +779 -0
- trialone/diagnose/remediation.py +587 -0
- trialone/diagnose/robot_model.py +1406 -0
- trialone/diagnose/safe_expr.py +127 -0
- trialone/diagnose/selfcheck.py +524 -0
- trialone/diagnose/specialists.py +297 -0
- trialone/diagnose/startsequenz.py +161 -0
- trialone/diagnose/surrogate.py +136 -0
- trialone/diagnose/temporal.py +313 -0
- trialone/diagnose/textform.py +441 -0
- trialone/diagnose/vorgeschichte.py +153 -0
- trialone/diagnose/was_fehlt_ist_der_befund.py +152 -0
- trialone/diagnose/werkzeugspur.py +839 -0
- trialone/diagnose/zeitachse.py +256 -0
- trialone/diagnose/zustellung.py +119 -0
- trialone/diagnostics/__init__.py +30 -0
- trialone/diagnostics/abrupt_end.py +188 -0
- trialone/diagnostics/analyze.py +112 -0
- trialone/diagnostics/anomaly_heat_map_generator.py +1 -0
- trialone/diagnostics/bayesian_causality_solver.py +1 -0
- trialone/diagnostics/calibrate.py +151 -0
- trialone/diagnostics/calibration.py +144 -0
- trialone/diagnostics/consistency.py +181 -0
- trialone/diagnostics/engine/__init__.py +1 -0
- trialone/diagnostics/engine/ambiguity_resolver_active_probe.py +1 -0
- trialone/diagnostics/engine/causal_elimination_dag.py +21 -0
- trialone/diagnostics/engine/causal_hypotheses.py +24 -0
- trialone/diagnostics/engine/counterfactual_generator.py +27 -0
- trialone/diagnostics/engine/cross_layer_differential.py +21 -0
- trialone/diagnostics/engine/cross_modal_reasoning_engine.py +1 -0
- trialone/diagnostics/engine/graph_traversal_optimizer.py +1 -0
- trialone/diagnostics/engine/memory_leak_slope_detector.py +17 -0
- trialone/diagnostics/engine/pareto_safety_roof.py +19 -0
- trialone/diagnostics/engine/peer_handshake_manager.py +10 -0
- trialone/diagnostics/engine/robotics_knowledge_graph.py +12 -0
- trialone/diagnostics/engine/robotics_knowledge_graph_builder.py +296 -0
- trialone/diagnostics/engine/robotics_knowledge_graph_memory.py +13 -0
- trialone/diagnostics/engine/software_stack_inspector.py +22 -0
- trialone/diagnostics/engine/universal_topic_canonicalizer.py +30 -0
- trialone/diagnostics/fault_judge.py +170 -0
- trialone/diagnostics/incident.py +148 -0
- trialone/diagnostics/judge_tools.py +146 -0
- trialone/diagnostics/monotonic.py +403 -0
- trialone/diagnostics/multimodal_sensor_fusion_engine.py +1 -0
- trialone/diagnostics/predictive/__init__.py +1 -0
- trialone/diagnostics/predictive/predictive_hardware_failure_engine.py +1 -0
- trialone/diagnostics/robust_anomaly.py +453 -0
- trialone/diagnostics/supervised.py +125 -0
- trialone/diagnostics/value_anomaly.py +439 -0
- trialone/execution/__init__.py +29 -0
- trialone/execution/agentic_coder_bridge/__init__.py +3 -0
- trialone/execution/agentic_coder_bridge/aider_cli_adapter.py +1 -0
- trialone/execution/agentic_coder_bridge/claude_code_cli_adapter.py +210 -0
- trialone/execution/agentic_coder_bridge/human_engineer_handoff.py +1 -0
- trialone/execution/agentic_coder_bridge/universal_handoff_router.py +94 -0
- trialone/execution/agentic_coder_bridge/waddle_embodied_ai_adapter.py +1 -0
- trialone/execution/autonomous_repl_loop.py +1 -0
- trialone/execution/build_management/cmake_dependency_resolver.py +1 -0
- trialone/execution/build_management/ros_package_xml_updater.py +1 -0
- trialone/execution/build_system_adapter.py +170 -0
- trialone/execution/compiler_error_parser.py +163 -0
- trialone/execution/fault_injector.py +175 -0
- trialone/execution/lifecycle_modes/__init__.py +1 -0
- trialone/execution/lifecycle_modes/active_diagnostic_mode.py +1 -0
- trialone/execution/lifecycle_modes/dead_log_analysis_mode.py +246 -0
- trialone/execution/lifecycle_modes/digital_twin_calibrator.py +19 -0
- trialone/execution/lifecycle_modes/digital_twin_physics_validator.py +1 -0
- trialone/execution/lifecycle_modes/fault_injection_synthesizer.py +13 -0
- trialone/execution/lifecycle_modes/headless_sim_runner.py +15 -0
- trialone/execution/lifecycle_modes/hil_testbench_mode.py +271 -0
- trialone/execution/lifecycle_modes/live_shadow_mode.py +1 -0
- trialone/execution/lifecycle_modes/map_elites_grid_engine.py +21 -0
- trialone/execution/lifecycle_modes/pure_simulation_mode.py +1 -0
- trialone/execution/lifecycle_modes/sim_scenario_builder.py +19 -0
- trialone/execution/lifecycle_modes/synthetic_crash_hallucinator.py +1 -0
- trialone/execution/lifecycle_modes/urdf_cad_analyzer.py +1 -0
- trialone/execution/progressive_disclosure_engine.py +12 -0
- trialone/execution/ros_process_manager.py +159 -0
- trialone/execution/semantic_phantom_injector.py +78 -0
- trialone/fortschritt.py +128 -0
- trialone/handoff/__init__.py +7 -0
- trialone/handoff/agent.py +68 -0
- trialone/handoff/agent_tools.py +157 -0
- trialone/handoff/fix_agent.py +69 -0
- trialone/handoff/fix_tournament.py +51 -0
- trialone/handoff/live_tools.py +110 -0
- trialone/hub.py +316 -0
- trialone/ingestion/__init__.py +21 -0
- trialone/ingestion/ardupilot_devid.py +315 -0
- trialone/ingestion/ardupilot_internal_error.py +140 -0
- trialone/ingestion/ardupilot_log.py +283 -0
- trialone/ingestion/ardupilot_loop.py +237 -0
- trialone/ingestion/auto_onboard.py +383 -0
- trialone/ingestion/auto_schema_inferencer.py +16 -0
- trialone/ingestion/bag_signals.py +1050 -0
- trialone/ingestion/bag_sqlite.py +208 -0
- trialone/ingestion/can_codec.py +191 -0
- trialone/ingestion/can_frames.py +433 -0
- trialone/ingestion/can_signals.py +107 -0
- trialone/ingestion/collection.py +245 -0
- trialone/ingestion/crash_log.py +374 -0
- trialone/ingestion/csv_signals.py +49 -0
- trialone/ingestion/dbc_codec.py +208 -0
- trialone/ingestion/deklaration.py +239 -0
- trialone/ingestion/detect.py +387 -0
- trialone/ingestion/front_door/__init__.py +4 -0
- trialone/ingestion/front_door/corrupted_mcap_healer.py +1 -0
- trialone/ingestion/front_door/data_completeness_checker.py +1 -0
- trialone/ingestion/front_door/ros_bag_recorder.py +108 -0
- trialone/ingestion/gdb_backtrace.py +881 -0
- trialone/ingestion/h5_signals.py +160 -0
- trialone/ingestion/introspect.py +1080 -0
- trialone/ingestion/kernel_oops.py +357 -0
- trialone/ingestion/kommentierte_konfiguration.py +167 -0
- trialone/ingestion/language_sandbox_manager.py +12 -0
- trialone/ingestion/live/__init__.py +54 -0
- trialone/ingestion/live/hardware/__init__.py +96 -0
- trialone/ingestion/live/hardware/adapter.py +378 -0
- trialone/ingestion/live/hardware/can_adapter.py +181 -0
- trialone/ingestion/live/hardware/config.py +329 -0
- trialone/ingestion/live/hardware/fanin.py +393 -0
- trialone/ingestion/live/hardware/registry.py +84 -0
- trialone/ingestion/live/hardware/replay.py +165 -0
- trialone/ingestion/live/hardware/ros2_adapter.py +152 -0
- trialone/ingestion/live/ringbuffer.py +248 -0
- trialone/ingestion/live/ros2.py +225 -0
- trialone/ingestion/live/source.py +418 -0
- trialone/ingestion/live/trigger.py +425 -0
- trialone/ingestion/local_repo_ast_indexer.py +1 -0
- trialone/ingestion/lsp_language_server_client.py +1 -0
- trialone/ingestion/nuttx_top.py +240 -0
- trialone/ingestion/omni_channel/__init__.py +1 -0
- trialone/ingestion/omni_channel/omni_data_aggregator.py +1 -0
- trialone/ingestion/omni_channel/pdf_spec_sheet_parser.py +1 -0
- trialone/ingestion/onboard.py +711 -0
- trialone/ingestion/parsers/json_parser.py +10 -0
- trialone/ingestion/pcap_signals.py +300 -0
- trialone/ingestion/profile_builder.py +195 -0
- trialone/ingestion/px4_perf.py +382 -0
- trialone/ingestion/robot_description.py +184 -0
- trialone/ingestion/ros_console.py +506 -0
- trialone/ingestion/ros_params.py +198 -0
- trialone/ingestion/ros_version_bridge_adapter.py +11 -0
- trialone/ingestion/rtps_signals.py +508 -0
- trialone/ingestion/signal_surface.py +99 -0
- trialone/ingestion/source_index.py +355 -0
- trialone/ingestion/transformers/lidar_pointcloud_transformer.py +1 -0
- trialone/ingestion/transformers/video_stream_transformer.py +1 -0
- trialone/ingestion/ulog_signals.py +112 -0
- trialone/ingestion/validators/schema_validator.py +9 -0
- trialone/injectors/__init__.py +12 -0
- trialone/injectors/cmd_inject.py +117 -0
- trialone/injectors/delay_relay.py +81 -0
- trialone/injectors/drop_topic.py +102 -0
- trialone/injectors/phantom_scan.py +149 -0
- trialone/injectors/transform_relay.py +222 -0
- trialone/kernel_blackboard/__init__.py +14 -0
- trialone/kernel_blackboard/central_state_blackboard.py +249 -0
- trialone/kernel_blackboard/ontology_graph_database.py +1 -0
- trialone/kernel_blackboard/self_model.py +182 -0
- trialone/konto.py +153 -0
- trialone/legacy/__init__.py +8 -0
- trialone/legacy/ab.py +151 -0
- trialone/legacy/campaign.py +266 -0
- trialone/legacy/chain_evolution.py +522 -0
- trialone/legacy/discover.py +64 -0
- trialone/legacy/field_wire.py +133 -0
- trialone/legacy/genetic.py +391 -0
- trialone/legacy/hunt.py +218 -0
- trialone/legacy/nav2_steps.py +513 -0
- trialone/legacy/runner.py +898 -0
- trialone/legacy/search.py +114 -0
- trialone/legacy/sim_wait.py +77 -0
- trialone/mlops/continuous_training_pipeline.py +11 -0
- trialone/mlops/custom_model_adapter.py +16 -0
- trialone/mlops/semantic_embedding_engine.py +11 -0
- trialone/mlops/telemetry_feature_store.py +11 -0
- trialone/mlops/vector_database_connector.py +10 -0
- trialone/onboard/__init__.py +9 -0
- trialone/onboard/analyze.py +2 -0
- trialone/onboard/assist.py +2 -0
- trialone/onboard/auto_onboard.py +2 -0
- trialone/onboard/calibrate.py +2 -0
- trialone/onboard/crash_daemon.py +86 -0
- trialone/onboard/incident.py +2 -0
- trialone/onboard/interpret.py +2 -0
- trialone/onboard/introspect.py +2 -0
- trialone/onboard/introspect_split_mocks/hardware_limit_introspector_mock.py +1 -0
- trialone/onboard/introspect_split_mocks/node_graph_introspector_mock.py +1 -0
- trialone/onboard/introspect_split_mocks/topic_bandwidth_introspector_mock.py +1 -0
- trialone/onboard/onboard.py +2 -0
- trialone/onboard/self_model.py +2 -0
- trialone/onboard/signal_surface.py +2 -0
- trialone/oracle/__init__.py +3 -0
- trialone/oracle/checks.py +2 -0
- trialone/oracle/consistency.py +2 -0
- trialone/oracle/dynamics.py +2 -0
- trialone/oracle/oracle_node.py +7 -0
- trialone/oracle/shadow_node.py +7 -0
- trialone/oracle/telemetry.py +2 -0
- trialone/orchestrator/__init__.py +1 -0
- trialone/orchestrator/code_fix_agent.py +2 -0
- trialone/orchestrator/environment_auto_discovery.py +1 -0
- trialone/orchestrator/fix/__init__.py +1 -0
- trialone/orchestrator/fix/code_fix_agent.py +218 -0
- trialone/orchestrator/fix/fix_run.py +128 -0
- trialone/orchestrator/fix_run.py +2 -0
- trialone/orchestrator/master_execution_state_machine.py +866 -0
- trialone/plugins/enterprise/bmw_custom_erp_sync_plugin.py +1 -0
- trialone/proprietary_physics_engine/__init__.py +1 -0
- trialone/proprietary_physics_engine/kinematic_singularity_predictor.py +1 -0
- trialone/proprietary_physics_engine/stochastic_resonance_analyzer.py +1 -0
- trialone/proprietary_signal_processing/__init__.py +1 -0
- trialone/proxy/__init__.py +0 -0
- trialone/proxy/kunden.py +95 -0
- trialone/proxy/server.py +240 -0
- trialone/repo.py +71 -0
- trialone/schluessel.py +465 -0
- trialone/server.py +684 -0
- trialone/shell.py +2365 -0
- trialone/store/__init__.py +52 -0
- trialone/store/index.py +367 -0
- trialone/store/machinestore.py +473 -0
- trialone/store/retention.py +243 -0
- trialone/store/schema.py +514 -0
- trialone/triage/__init__.py +19 -0
- trialone/triage/maintenance_ticket_generator.py +195 -0
- trialone/triage/ontology_detectors/__init__.py +1 -0
- trialone/triage/ontology_detectors/calibration_drift_monitor.py +123 -0
- trialone/triage/ontology_detectors/environmental_interference_detector.py +173 -0
- trialone/triage/ontology_detectors/firmware_mismatch_checker.py +152 -0
- trialone/triage/root_cause_pinpoint_router.py +222 -0
- trialone/uebergabe.py +138 -0
- trialone/verification/__init__.py +24 -0
- trialone/verification/checks.py +374 -0
- trialone/verification/confidence_and_severity_filter.py +123 -0
- trialone/verification/consistency.py +99 -0
- trialone/verification/counterfactual.py +218 -0
- trialone/verification/dynamics.py +273 -0
- trialone/verification/evaluate.py +114 -0
- trialone/verification/fix_validation_pipeline/__init__.py +3 -0
- trialone/verification/fix_validation_pipeline/ai_unit_test_generator.py +144 -0
- trialone/verification/fix_validation_pipeline/historical_regression_runner.py +1 -0
- trialone/verification/fix_validation_pipeline/quarantine_gate.py +175 -0
- trialone/verification/formal_safety_verifier.py +1 -0
- trialone/verification/ground_truth_bridge.py +83 -0
- trialone/verification/objective.py +192 -0
- trialone/verification/oracle_evaluator.py +143 -0
- trialone/verification/oracle_node.py +690 -0
- trialone/verification/reliability.py +80 -0
- trialone/verification/reproducibility.py +114 -0
- trialone/verification/runspec.py +523 -0
- trialone/verification/shadow_node.py +311 -0
- trialone/verification/telemetry.py +155 -0
- trialone/verify_backends/__init__.py +7 -0
- trialone/verify_backends/criu_backend.py +323 -0
- trialone/verify_backends/field.py +480 -0
- trialone/verify_backends/multiverse.py +295 -0
- trialone/verify_backends/nav2_backend.py +171 -0
- trialone/watch.py +1001 -0
- trialone-0.4.7.dist-info/METADATA +354 -0
- trialone-0.4.7.dist-info/RECORD +562 -0
- trialone-0.4.7.dist-info/WHEEL +5 -0
- trialone-0.4.7.dist-info/entry_points.txt +2 -0
- trialone-0.4.7.dist-info/licenses/LICENSE +50 -0
- trialone-0.4.7.dist-info/top_level.txt +1 -0
trialone/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Simulator adapters.
|
|
2
|
+
|
|
3
|
+
Sim-agnostic interface so the core does not care which simulator runs the robot:
|
|
4
|
+
instrument the world and publish the SAME /gt/* ground-truth contract regardless
|
|
5
|
+
of backend. Gazebo Harmonic is the proven path (every recorded run). The isaac/
|
|
6
|
+
subpackage implements the same contract for NVIDIA Isaac; its message SHAPE is
|
|
7
|
+
contract-tested without a GPU (tests/test_isaac_gt_contract), but booting Isaac on
|
|
8
|
+
a GPU box is not yet run (see adapters/isaac/boot_proof.py). See docs/architecture.md.
|
|
9
|
+
"""
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""TrialONE Sim Adapter (step 1) — generate the ground-truth ros_gz bridge for a world.
|
|
3
|
+
|
|
4
|
+
The Oracle needs, from ANY world: the robot's true pose + real collisions. Hand-writing
|
|
5
|
+
the ros_gz bridge for that is the painful part of onboarding a new world. This reads a
|
|
6
|
+
Gazebo world SDF and emits the ground_truth_bridge.yaml automatically:
|
|
7
|
+
|
|
8
|
+
- /world/<name>/dynamic_pose/info -> /gt/tf_dynamic (true model poses)
|
|
9
|
+
- each <sensor type="contact"> "<x>_contact" -> /gt/contact/<x> (real collisions)
|
|
10
|
+
- each <sensor type="camera"> -> its <topic> (for the Recorder)
|
|
11
|
+
|
|
12
|
+
Convention (docs/contracts/sim-adapter.md): name your world (not "default"), no <actor>
|
|
13
|
+
elements, add contact sensors named "<x>_contact" where collisions should be caught
|
|
14
|
+
(or one on your robot's base), and an overhead camera if you want video.
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
python3 src/trialone/adapters/gen_ground_truth.py worlds/walled_room.sdf
|
|
18
|
+
python3 src/trialone/adapters/gen_ground_truth.py my_world.sdf --out victim/config/ground_truth_bridge.yaml
|
|
19
|
+
"""
|
|
20
|
+
import argparse
|
|
21
|
+
import sys
|
|
22
|
+
import xml.etree.ElementTree as ET
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _entry(ros, gz, ros_type, gz_type, comment=None):
|
|
26
|
+
head = f"# {comment}\n" if comment else ""
|
|
27
|
+
return (f"{head}- ros_topic_name: \"{ros}\"\n"
|
|
28
|
+
f" gz_topic_name: \"{gz}\"\n"
|
|
29
|
+
f" ros_type_name: \"{ros_type}\"\n"
|
|
30
|
+
f" gz_type_name: \"{gz_type}\"\n"
|
|
31
|
+
f" direction: GZ_TO_ROS\n"
|
|
32
|
+
f" lazy: false")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def generate(world_sdf_path):
|
|
36
|
+
root = ET.parse(world_sdf_path).getroot()
|
|
37
|
+
world = root.find("world")
|
|
38
|
+
if world is None:
|
|
39
|
+
raise ValueError("no <world> element found")
|
|
40
|
+
wname = world.get("name")
|
|
41
|
+
if not wname or wname == "default":
|
|
42
|
+
raise ValueError(f"world must be named (not 'default') for dynamic_pose/info: got {wname!r}")
|
|
43
|
+
|
|
44
|
+
blocks = [
|
|
45
|
+
"# AUTO-GENERATED by src/trialone/adapters/gen_ground_truth.py -- do not hand-edit.",
|
|
46
|
+
"# The ground truth the Oracle judges from (true pose + real collisions), bridged to ROS.",
|
|
47
|
+
"",
|
|
48
|
+
"# True pose of every non-static model (SceneBroadcaster). Robot found by proximity.",
|
|
49
|
+
_entry(f"/gt/tf_dynamic", f"/world/{wname}/dynamic_pose/info",
|
|
50
|
+
"tf2_msgs/msg/TFMessage", "gz.msgs.Pose_V"),
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
contacts, cameras = [], []
|
|
54
|
+
for model in world.findall("model"):
|
|
55
|
+
mname = model.get("name")
|
|
56
|
+
for link in model.findall("link"):
|
|
57
|
+
lname = link.get("name")
|
|
58
|
+
for sensor in link.findall("sensor"):
|
|
59
|
+
stype, sname = sensor.get("type"), sensor.get("name")
|
|
60
|
+
if stype == "contact":
|
|
61
|
+
label = sname[:-8] if sname.endswith("_contact") else sname
|
|
62
|
+
gz = f"/world/{wname}/model/{mname}/link/{lname}/sensor/{sname}/contact"
|
|
63
|
+
contacts.append(_entry(f"/gt/contact/{label}", gz,
|
|
64
|
+
"ros_gz_interfaces/msg/Contacts", "gz.msgs.Contacts"))
|
|
65
|
+
elif stype == "camera":
|
|
66
|
+
topic = sensor.findtext("topic") or f"/{sname}"
|
|
67
|
+
cameras.append(_entry(topic, topic,
|
|
68
|
+
"sensor_msgs/msg/Image", "gz.msgs.Image"))
|
|
69
|
+
|
|
70
|
+
if contacts:
|
|
71
|
+
blocks.append("\n# Real collisions (one entry per contact sensor in the world).")
|
|
72
|
+
blocks.extend(contacts)
|
|
73
|
+
if cameras:
|
|
74
|
+
blocks.append("\n# Camera(s) for the Recorder.")
|
|
75
|
+
blocks.extend(cameras)
|
|
76
|
+
return "\n".join(blocks) + "\n", len(contacts), len(cameras)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def main():
|
|
80
|
+
parser = argparse.ArgumentParser(description="Generate the ground-truth ros_gz bridge from a world SDF.")
|
|
81
|
+
parser.add_argument("world", help="path to the Gazebo world .sdf")
|
|
82
|
+
parser.add_argument("--out", default=None, help="write here instead of stdout")
|
|
83
|
+
args = parser.parse_args()
|
|
84
|
+
|
|
85
|
+
text, n_contacts, n_cams = generate(args.world)
|
|
86
|
+
if args.out:
|
|
87
|
+
with open(args.out, "w", encoding="utf-8") as f:
|
|
88
|
+
f.write(text)
|
|
89
|
+
print(f"wrote {args.out}: dynamic_pose + {n_contacts} contact(s) + {n_cams} camera(s)",
|
|
90
|
+
file=sys.stderr)
|
|
91
|
+
else:
|
|
92
|
+
sys.stdout.write(text)
|
|
93
|
+
return 0
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if __name__ == "__main__":
|
|
97
|
+
sys.exit(main())
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""TrialONE Sim Adapter (step 3) — generate a Nav2 occupancy map from a world SDF.
|
|
3
|
+
|
|
4
|
+
Nav2/AMCL need a static occupancy map. Hand-drawing one per world is painful and must
|
|
5
|
+
stay in sync with the SDF. This rasterizes the world's static BOX obstacles into a .pgm
|
|
6
|
+
+ .yaml, and can EXCLUDE models by name — e.g. the unmapped crash target, so the global
|
|
7
|
+
planner doesn't know about it (the whole point of the spoof/drop demo).
|
|
8
|
+
|
|
9
|
+
Assumes axis-aligned boxes (yaw ~ 0), which covers walls + box obstacles.
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
python3 src/trialone/adapters/gen_map.py worlds/corridor.sdf --out victim/maps/corridor --exclude crash_box
|
|
13
|
+
-> writes victim/maps/corridor.pgm + victim/maps/corridor.yaml
|
|
14
|
+
"""
|
|
15
|
+
import argparse
|
|
16
|
+
import os
|
|
17
|
+
import sys
|
|
18
|
+
import xml.etree.ElementTree as ET
|
|
19
|
+
|
|
20
|
+
RES = 0.05 # m / pixel
|
|
21
|
+
MARGIN = 0.5 # m of free space padded around the obstacle bounds
|
|
22
|
+
FREE, OCC = 254, 0
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _boxes(world, exclude):
|
|
26
|
+
"""Yield (cx, cy, sx, sy) for each static, axis-aligned box obstacle to map."""
|
|
27
|
+
for model in world.findall("model"):
|
|
28
|
+
name = (model.get("name") or "").lower()
|
|
29
|
+
if name in ("ground_plane", "ground") or name in exclude:
|
|
30
|
+
continue
|
|
31
|
+
for link in model.findall("link"):
|
|
32
|
+
lp = (link.findtext("pose") or "0 0 0 0 0 0").split()
|
|
33
|
+
lx, ly = float(lp[0]), float(lp[1])
|
|
34
|
+
for col in link.findall("collision"):
|
|
35
|
+
box = col.find("geometry/box/size")
|
|
36
|
+
if box is None:
|
|
37
|
+
continue
|
|
38
|
+
sx, sy = (float(v) for v in box.text.split()[:2])
|
|
39
|
+
# collision can have its own offset pose too
|
|
40
|
+
cp = (col.findtext("pose") or "0 0 0 0 0 0").split()
|
|
41
|
+
yield (lx + float(cp[0]), ly + float(cp[1]), sx, sy)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def generate(world_sdf_path, exclude):
|
|
45
|
+
world = ET.parse(world_sdf_path).getroot().find("world")
|
|
46
|
+
if world is None:
|
|
47
|
+
raise ValueError("no <world> element")
|
|
48
|
+
boxes = list(_boxes(world, {e.lower() for e in exclude}))
|
|
49
|
+
if not boxes:
|
|
50
|
+
raise ValueError("no static box obstacles found to map")
|
|
51
|
+
|
|
52
|
+
xmin = min(cx - sx / 2 for cx, cy, sx, sy in boxes) - MARGIN
|
|
53
|
+
xmax = max(cx + sx / 2 for cx, cy, sx, sy in boxes) + MARGIN
|
|
54
|
+
ymin = min(cy - sy / 2 for cx, cy, sx, sy in boxes) - MARGIN
|
|
55
|
+
ymax = max(cy + sy / 2 for cx, cy, sx, sy in boxes) + MARGIN
|
|
56
|
+
W = int(round((xmax - xmin) / RES))
|
|
57
|
+
H = int(round((ymax - ymin) / RES))
|
|
58
|
+
|
|
59
|
+
px = bytearray([FREE]) * (W * H)
|
|
60
|
+
for row in range(H):
|
|
61
|
+
wy = ymin + (H - 1 - row + 0.5) * RES # image row 0 = top -> flip y
|
|
62
|
+
for col in range(W):
|
|
63
|
+
wx = xmin + (col + 0.5) * RES
|
|
64
|
+
for cx, cy, sx, sy in boxes:
|
|
65
|
+
if abs(wx - cx) <= sx / 2 and abs(wy - cy) <= sy / 2:
|
|
66
|
+
px[row * W + col] = OCC
|
|
67
|
+
break
|
|
68
|
+
return px, W, H, (xmin, ymin)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def main():
|
|
72
|
+
parser = argparse.ArgumentParser(description="Generate a Nav2 occupancy map from a world SDF.")
|
|
73
|
+
parser.add_argument("world")
|
|
74
|
+
parser.add_argument("--out", required=True, help="output path stem (writes .pgm + .yaml)")
|
|
75
|
+
parser.add_argument("--exclude", nargs="*", default=[],
|
|
76
|
+
help="model names to leave OUT of the map (e.g. the unmapped crash target)")
|
|
77
|
+
args = parser.parse_args()
|
|
78
|
+
|
|
79
|
+
px, W, H, (ox, oy) = generate(args.world, args.exclude)
|
|
80
|
+
pgm = args.out + ".pgm"
|
|
81
|
+
yaml_path = args.out + ".yaml"
|
|
82
|
+
with open(pgm, "wb") as f:
|
|
83
|
+
f.write(f"P5\n{W} {H}\n255\n".encode("ascii"))
|
|
84
|
+
f.write(bytes(px))
|
|
85
|
+
with open(yaml_path, "w", encoding="utf-8") as f:
|
|
86
|
+
f.write(
|
|
87
|
+
f"image: {os.path.basename(pgm)}\n"
|
|
88
|
+
f"mode: trinary\nresolution: {RES}\n"
|
|
89
|
+
f"origin: [{ox:.3f}, {oy:.3f}, 0.0]\n"
|
|
90
|
+
f"negate: 0\noccupied_thresh: 0.65\nfree_thresh: 0.25\n"
|
|
91
|
+
)
|
|
92
|
+
occ = sum(1 for b in px if b == OCC)
|
|
93
|
+
print(f"wrote {pgm} ({W}x{H}, {occ} occupied px) + {yaml_path} (origin {ox:.2f},{oy:.2f})",
|
|
94
|
+
file=sys.stderr)
|
|
95
|
+
return 0
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if __name__ == "__main__":
|
|
99
|
+
sys.exit(main())
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""TrialONE Sim Adapter (step 2) — instrument a bare world for TrialONE.
|
|
3
|
+
|
|
4
|
+
Takes a plain Gazebo world SDF and adds everything the Oracle + Recorder need, so a
|
|
5
|
+
user doesn't hand-edit SDF:
|
|
6
|
+
- the required world systems (Physics, UserCommands, SceneBroadcaster, Sensors,
|
|
7
|
+
Imu, Contact) if missing,
|
|
8
|
+
- a contact sensor on every static obstacle link (named "<link>_contact") so
|
|
9
|
+
collisions are detected,
|
|
10
|
+
- an overhead camera (if none) for the Recorder.
|
|
11
|
+
|
|
12
|
+
Then run gen_ground_truth.py on the OUTPUT to get the ros_gz bridge.
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
python3 src/trialone/adapters/instrument_world.py bare.sdf --out instrumented.sdf
|
|
16
|
+
"""
|
|
17
|
+
import argparse
|
|
18
|
+
import sys
|
|
19
|
+
import xml.etree.ElementTree as ET
|
|
20
|
+
|
|
21
|
+
# Required world-system plugins: (filename, name, optional (child_tag, child_text)).
|
|
22
|
+
PLUGINS = [
|
|
23
|
+
("gz-sim-physics-system", "gz::sim::systems::Physics", None),
|
|
24
|
+
("gz-sim-user-commands-system", "gz::sim::systems::UserCommands", None),
|
|
25
|
+
("gz-sim-scene-broadcaster-system", "gz::sim::systems::SceneBroadcaster", None),
|
|
26
|
+
("gz-sim-sensors-system", "gz::sim::systems::Sensors", ("render_engine", "ogre2")),
|
|
27
|
+
("gz-sim-imu-system", "gz::sim::systems::Imu", None),
|
|
28
|
+
("gz-sim-contact-system", "gz::sim::systems::Contact", None),
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _is_ground(model):
|
|
33
|
+
"""Skip the ground plane: huge, and a contact sensor on it is useless noise."""
|
|
34
|
+
if (model.get("name") or "").lower() in ("ground_plane", "ground"):
|
|
35
|
+
return True
|
|
36
|
+
return model.find(".//collision/geometry/plane") is not None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def instrument(world_sdf_path, cam_xyz):
|
|
40
|
+
tree = ET.parse(world_sdf_path)
|
|
41
|
+
root = tree.getroot()
|
|
42
|
+
world = root.find("world")
|
|
43
|
+
if world is None:
|
|
44
|
+
raise ValueError("no <world> element found")
|
|
45
|
+
if not world.get("name") or world.get("name") == "default":
|
|
46
|
+
world.set("name", "trialone_world") # dynamic_pose/info needs a real name
|
|
47
|
+
|
|
48
|
+
added = {"plugins": 0, "contacts": 0, "camera": 0}
|
|
49
|
+
|
|
50
|
+
# 1) World systems (add missing, by plugin name).
|
|
51
|
+
have = {p.get("name") for p in world.findall("plugin")}
|
|
52
|
+
for i, (filename, name, child) in enumerate(PLUGINS):
|
|
53
|
+
if name in have:
|
|
54
|
+
continue
|
|
55
|
+
p = ET.Element("plugin", {"filename": filename, "name": name})
|
|
56
|
+
if child:
|
|
57
|
+
ET.SubElement(p, child[0]).text = child[1]
|
|
58
|
+
world.insert(i, p)
|
|
59
|
+
added["plugins"] += 1
|
|
60
|
+
|
|
61
|
+
# 2) Contact sensor on every static obstacle link that has a named collision.
|
|
62
|
+
for model in world.findall("model"):
|
|
63
|
+
if _is_ground(model):
|
|
64
|
+
continue
|
|
65
|
+
for link in model.findall("link"):
|
|
66
|
+
if link.find("sensor[@type='contact']") is not None:
|
|
67
|
+
continue # already instrumented
|
|
68
|
+
col = link.find("collision[@name]")
|
|
69
|
+
if col is None:
|
|
70
|
+
continue
|
|
71
|
+
lname = link.get("name") or "link"
|
|
72
|
+
sensor = ET.SubElement(link, "sensor",
|
|
73
|
+
{"name": f"{lname}_contact", "type": "contact"})
|
|
74
|
+
contact = ET.SubElement(sensor, "contact")
|
|
75
|
+
ET.SubElement(contact, "collision").text = col.get("name")
|
|
76
|
+
ET.SubElement(sensor, "update_rate").text = "30"
|
|
77
|
+
added["contacts"] += 1
|
|
78
|
+
|
|
79
|
+
# 3) Overhead camera if none exists anywhere.
|
|
80
|
+
if world.find(".//sensor[@type='camera']") is None:
|
|
81
|
+
cx, cy, cz = cam_xyz
|
|
82
|
+
m = ET.SubElement(world, "model", {"name": "overhead_cam"})
|
|
83
|
+
ET.SubElement(m, "static").text = "true"
|
|
84
|
+
ET.SubElement(m, "pose").text = f"{cx} {cy} {cz} 0 1.5708 0"
|
|
85
|
+
link = ET.SubElement(m, "link", {"name": "link"})
|
|
86
|
+
s = ET.SubElement(link, "sensor", {"name": "overhead_camera", "type": "camera"})
|
|
87
|
+
ET.SubElement(s, "always_on").text = "1"
|
|
88
|
+
ET.SubElement(s, "update_rate").text = "12"
|
|
89
|
+
ET.SubElement(s, "topic").text = "/overhead/image"
|
|
90
|
+
cam = ET.SubElement(s, "camera")
|
|
91
|
+
ET.SubElement(cam, "horizontal_fov").text = "1.2"
|
|
92
|
+
img = ET.SubElement(cam, "image")
|
|
93
|
+
ET.SubElement(img, "width").text = "960"
|
|
94
|
+
ET.SubElement(img, "height").text = "540"
|
|
95
|
+
ET.SubElement(img, "format").text = "R8G8B8"
|
|
96
|
+
clip = ET.SubElement(cam, "clip")
|
|
97
|
+
ET.SubElement(clip, "near").text = "0.1"
|
|
98
|
+
ET.SubElement(clip, "far").text = "50"
|
|
99
|
+
added["camera"] = 1
|
|
100
|
+
|
|
101
|
+
ET.indent(tree, space=" ")
|
|
102
|
+
return tree, added
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def main():
|
|
106
|
+
parser = argparse.ArgumentParser(description="Instrument a bare world SDF for TrialONE.")
|
|
107
|
+
parser.add_argument("world", help="path to the bare Gazebo world .sdf")
|
|
108
|
+
parser.add_argument("--out", default=None, help="write here instead of stdout")
|
|
109
|
+
parser.add_argument("--cam-x", type=float, default=0.0)
|
|
110
|
+
parser.add_argument("--cam-y", type=float, default=0.0)
|
|
111
|
+
parser.add_argument("--cam-z", type=float, default=7.0, help="overhead camera height (m)")
|
|
112
|
+
args = parser.parse_args()
|
|
113
|
+
|
|
114
|
+
tree, added = instrument(args.world, (args.cam_x, args.cam_y, args.cam_z))
|
|
115
|
+
out = args.out or "-"
|
|
116
|
+
if args.out:
|
|
117
|
+
tree.write(args.out, encoding="unicode", xml_declaration=True)
|
|
118
|
+
else:
|
|
119
|
+
sys.stdout.write(ET.tostring(tree.getroot(), encoding="unicode"))
|
|
120
|
+
print(f"instrumented -> {out}: +{added['plugins']} systems, "
|
|
121
|
+
f"+{added['contacts']} contact sensors, +{added['camera']} camera",
|
|
122
|
+
file=sys.stderr)
|
|
123
|
+
return 0
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
if __name__ == "__main__":
|
|
127
|
+
sys.exit(main())
|
|
File without changes
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TrialONE Isaac adapter — BOOT PROOF smoke test.
|
|
3
|
+
|
|
4
|
+
The one thing our Isaac path has never had: a committed, runnable proof that the
|
|
5
|
+
stack actually boots on a real GPU box. Everything in ``sim_main.py`` is "needs the
|
|
6
|
+
GPU to validate, deferred". This script closes that gap: run it on the Ubuntu + RTX
|
|
7
|
+
box and it proves — in stages, so a failure localises — that the SAME boot path
|
|
8
|
+
``sim_main.py`` uses is alive end-to-end.
|
|
9
|
+
|
|
10
|
+
Run (Isaac's python, NOT system python — same as sim_main.py):
|
|
11
|
+
${ISAAC_SIM}/python.sh -m trialone.adapters.isaac.boot_proof
|
|
12
|
+
# or: ${ISAAC_SIM}/python.sh src/trialone/adapters/isaac/boot_proof.py
|
|
13
|
+
|
|
14
|
+
Exit 0 + "BOOT PROOF: SUCCESS" on the last line = the box can run the Isaac backend.
|
|
15
|
+
Any non-zero exit + the last "[stage] ..." line tells you exactly where it died.
|
|
16
|
+
|
|
17
|
+
What it proves (each is a distinct failure the deferred path could hide):
|
|
18
|
+
1. SimulationApp boots headless -> GPU + Isaac install OK
|
|
19
|
+
2. isaacsim.ros2.bridge extension loads -> ROS 2 bringup will work
|
|
20
|
+
3. World + default ground plane builds + world.reset() -> scene graph OK
|
|
21
|
+
4. world.step() advances physics AND world.current_time -> the sim clock ticks
|
|
22
|
+
(our GroundTruthPublisher publishes /clock off current_time — a frozen clock is
|
|
23
|
+
a silent killer: topics look "up" but never advance)
|
|
24
|
+
|
|
25
|
+
Design notes stolen (as mechanism, not code) from robocurve/inspect-robots (MIT),
|
|
26
|
+
plugins/inspect-robots-isaacsim/scripts/boot_proof.py — two hard-won Isaac lessons:
|
|
27
|
+
* Print the verdict + flush BEFORE app.close(): Isaac's close() does a hard
|
|
28
|
+
os._exit that swallows buffered stdout, so a verdict printed after close()
|
|
29
|
+
silently vanishes and the run looks like it hung.
|
|
30
|
+
* Time the boot: a "success" that took 400 s tells you the box is thermal/paging
|
|
31
|
+
limited even when it technically worked.
|
|
32
|
+
"""
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
import sys
|
|
36
|
+
import time
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _stage(msg: str) -> None:
|
|
40
|
+
# flush every stage line so the last line before a crash is always visible
|
|
41
|
+
print(f"[stage] {msg}", flush=True)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def main() -> int:
|
|
45
|
+
t0 = time.perf_counter()
|
|
46
|
+
|
|
47
|
+
# 1. SimulationApp — MUST be created before any other isaac/omni import
|
|
48
|
+
# (identical dict to sim_main.py so we prove the exact same boot).
|
|
49
|
+
_stage("launching SimulationApp (headless, RayTracedLighting)...")
|
|
50
|
+
from isaacsim import SimulationApp
|
|
51
|
+
|
|
52
|
+
app = SimulationApp({"headless": True, "renderer": "RayTracedLighting"})
|
|
53
|
+
t_app = time.perf_counter() - t0
|
|
54
|
+
running = getattr(app, "is_running", lambda: None)()
|
|
55
|
+
_stage(f"SimulationApp live in {t_app:.1f}s is_running={running}")
|
|
56
|
+
|
|
57
|
+
# 2. ROS 2 bridge extension — the bringup script depends on this loading
|
|
58
|
+
_stage("enabling isaacsim.ros2.bridge extension...")
|
|
59
|
+
from isaacsim.core.utils.extensions import enable_extension
|
|
60
|
+
|
|
61
|
+
enable_extension("isaacsim.ros2.bridge")
|
|
62
|
+
app.update()
|
|
63
|
+
_stage("ros2.bridge enabled")
|
|
64
|
+
|
|
65
|
+
# 3. World + ground plane + reset (the scene graph sim_main.py builds)
|
|
66
|
+
_stage("building World + default ground plane...")
|
|
67
|
+
from isaacsim.core.api import World
|
|
68
|
+
|
|
69
|
+
world = World(stage_units_in_meters=1.0)
|
|
70
|
+
world.scene.add_default_ground_plane()
|
|
71
|
+
world.reset()
|
|
72
|
+
_stage("World reset OK")
|
|
73
|
+
|
|
74
|
+
# 4. Step physics and prove the sim CLOCK actually advances (not just that
|
|
75
|
+
# step() returns). current_time frozen -> /clock frozen -> every downstream
|
|
76
|
+
# topic looks alive but time never moves. This is the check that matters.
|
|
77
|
+
_stage("stepping physics x60, watching world.current_time...")
|
|
78
|
+
t_before = float(world.current_time)
|
|
79
|
+
for _ in range(60):
|
|
80
|
+
world.step(render=False)
|
|
81
|
+
t_after = float(world.current_time)
|
|
82
|
+
dt = float(world.get_physics_dt())
|
|
83
|
+
advanced = t_after - t_before
|
|
84
|
+
_stage(f"current_time {t_before:.3f} -> {t_after:.3f} (+{advanced:.3f}s, physics_dt={dt:.4f})")
|
|
85
|
+
|
|
86
|
+
clock_ok = advanced > 0.0
|
|
87
|
+
total = time.perf_counter() - t0
|
|
88
|
+
|
|
89
|
+
# Verdict BEFORE close(): app.close() hard-exits and eats buffered stdout.
|
|
90
|
+
if clock_ok:
|
|
91
|
+
print(
|
|
92
|
+
f"BOOT PROOF: SUCCESS (boot {t_app:.1f}s, full stack {total:.1f}s, "
|
|
93
|
+
f"sim clock advanced +{advanced:.3f}s over 60 steps)",
|
|
94
|
+
flush=True,
|
|
95
|
+
)
|
|
96
|
+
rc = 0
|
|
97
|
+
else:
|
|
98
|
+
print(
|
|
99
|
+
"BOOT PROOF: FAILURE (SimulationApp booted but the sim clock did NOT "
|
|
100
|
+
"advance — /clock would be frozen; check physics_dt / world.play state)",
|
|
101
|
+
flush=True,
|
|
102
|
+
)
|
|
103
|
+
rc = 1
|
|
104
|
+
|
|
105
|
+
app.close() # may hard-exit here; the verdict above is already flushed
|
|
106
|
+
return rc
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
if __name__ == "__main__":
|
|
110
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TrialONE Isaac adapter — ground-truth config from a world manifest.
|
|
3
|
+
|
|
4
|
+
The USD analog of ``gen_ground_truth.py``. Instead of parsing a world SDF for contact
|
|
5
|
+
sensors + the dynamic-pose source, read a tiny hand-written sidecar manifest (JSON next
|
|
6
|
+
to the ``.usd``) that names: the robot's dynamic prim(s), the contact-link prim(s) +
|
|
7
|
+
labels, and the overhead camera. Generic USD introspection (auto-inject the hooks) is a
|
|
8
|
+
later increment — exactly mirroring the ``[planned]`` auto-inject status on the Gazebo
|
|
9
|
+
side, so we don't repeat that scope creep here.
|
|
10
|
+
|
|
11
|
+
Manifest shape (see ``worlds/walled_room.isaac.json``)::
|
|
12
|
+
|
|
13
|
+
{
|
|
14
|
+
"world": "worlds/walled_room.usd", # optional; ground plane if absent
|
|
15
|
+
"robot_usd": ".../nova_carter.usd",
|
|
16
|
+
"robot_prim": "/World/robot",
|
|
17
|
+
"dynamic_prims": { "robot": "/World/robot" }, # model_name -> prim path
|
|
18
|
+
"contacts": [ { "label": "base", "prim": "/World/robot/chassis_link" } ],
|
|
19
|
+
"camera": { "prim": "/World/overhead_cam", "topic": "/overhead/image",
|
|
20
|
+
"resolution": [640, 480], "position": [0, 0, 8] },
|
|
21
|
+
"cmd_vel": { "topic": "/cmd_vel", "wheel_radius": 0.14, "wheel_base": 0.41 }
|
|
22
|
+
}
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import json
|
|
27
|
+
from typing import Any, Dict
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def load_manifest(path: str) -> Dict[str, Any]:
|
|
31
|
+
"""Read a world manifest JSON."""
|
|
32
|
+
with open(path) as f:
|
|
33
|
+
return json.load(f)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def to_gt_config(manifest: Dict[str, Any]) -> Dict[str, Any]:
|
|
37
|
+
"""Normalise a manifest into the config ``sim_main`` / ``GroundTruthPublisher`` consume."""
|
|
38
|
+
cfg = {
|
|
39
|
+
"world": manifest.get("world"),
|
|
40
|
+
"robot_usd": manifest.get("robot_usd"),
|
|
41
|
+
"robot_prim": manifest.get("robot_prim", "/World/robot"),
|
|
42
|
+
"dynamic_prims": dict(manifest.get("dynamic_prims", {})),
|
|
43
|
+
"contacts": list(manifest.get("contacts", [])),
|
|
44
|
+
"camera": manifest.get("camera"),
|
|
45
|
+
"cmd_vel": manifest.get("cmd_vel"),
|
|
46
|
+
}
|
|
47
|
+
# A robot with no explicit dynamic_prims still needs its base tracked for /gt/tf_dynamic.
|
|
48
|
+
if not cfg["dynamic_prims"]:
|
|
49
|
+
cfg["dynamic_prims"] = {"robot": cfg["robot_prim"]}
|
|
50
|
+
# Validate the pieces the contract depends on.
|
|
51
|
+
for c in cfg["contacts"]:
|
|
52
|
+
if "label" not in c or "prim" not in c:
|
|
53
|
+
raise ValueError(f"contact spec needs 'label' and 'prim': {c}")
|
|
54
|
+
return cfg
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__": # quick CLI: print the normalised config for a manifest
|
|
58
|
+
import sys
|
|
59
|
+
print(json.dumps(to_gt_config(load_manifest(sys.argv[1])), indent=2))
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TrialONE Isaac adapter — ground-truth publisher (the contract-critical piece).
|
|
3
|
+
|
|
4
|
+
Publishes the SAME ground-truth ROS 2 topics the Gazebo ``ros_gz`` bridge does, so the
|
|
5
|
+
Oracle, injectors and recorder work UNCHANGED across backends (see
|
|
6
|
+
``docs/contracts/sim-adapter.md``). Only the backend differs:
|
|
7
|
+
|
|
8
|
+
/gt/tf_dynamic tf2_msgs/TFMessage true world pose of every dynamic model
|
|
9
|
+
/gt/contact/<label> ros_gz_interfaces/Contacts non-empty ``.contacts`` == a real collision
|
|
10
|
+
/clock rosgraph_msgs/Clock sim time (so the rest of the stack syncs)
|
|
11
|
+
|
|
12
|
+
State is read from *pluggable sources* so this node can be driven by Isaac (reading the
|
|
13
|
+
USD stage + a ContactSensor) in production AND by a fake source in a CPU-only contract
|
|
14
|
+
test (no Isaac, no GPU) — the test asserts the real Oracle accepts these exact messages.
|
|
15
|
+
That keeps the riskiest unknown (message shape) verifiable off the GPU box.
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
from typing import Callable, Dict, List, Optional, Tuple
|
|
21
|
+
|
|
22
|
+
import rclpy
|
|
23
|
+
from rclpy.node import Node
|
|
24
|
+
from tf2_msgs.msg import TFMessage
|
|
25
|
+
from geometry_msgs.msg import TransformStamped
|
|
26
|
+
from rosgraph_msgs.msg import Clock
|
|
27
|
+
from ros_gz_interfaces.msg import Contacts, Contact
|
|
28
|
+
|
|
29
|
+
# pose_source() -> {model_name: (x, y, z[, qx, qy, qz, qw])} in the world frame, metres.
|
|
30
|
+
Pose = Tuple[float, ...]
|
|
31
|
+
PoseSource = Callable[[], Dict[str, Pose]]
|
|
32
|
+
# contact_source() -> True iff that link is currently in contact with anything.
|
|
33
|
+
ContactSource = Callable[[], bool]
|
|
34
|
+
# clock_source() -> sim time in seconds (float), or None to use the node's wall clock.
|
|
35
|
+
ClockSource = Optional[Callable[[], float]]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class ContactSpec:
|
|
40
|
+
"""One ``/gt/contact/<label>`` sensor: a label and a predicate that is True on contact."""
|
|
41
|
+
label: str
|
|
42
|
+
source: ContactSource
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class GroundTruthPublisher(Node):
|
|
46
|
+
"""Republishes Isaac true-state on the Gazebo-compatible ``/gt/*`` topics.
|
|
47
|
+
|
|
48
|
+
Drive it by calling :meth:`tick` once per simulation step (after ``world.step``).
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
pose_source: PoseSource,
|
|
54
|
+
contact_specs: List[ContactSpec],
|
|
55
|
+
clock_source: ClockSource = None,
|
|
56
|
+
world_frame: str = "world",
|
|
57
|
+
node_name: str = "trialone_gt_publisher",
|
|
58
|
+
) -> None:
|
|
59
|
+
super().__init__(node_name)
|
|
60
|
+
self._pose_source = pose_source
|
|
61
|
+
self._contacts = contact_specs
|
|
62
|
+
self._clock_source = clock_source
|
|
63
|
+
self._world_frame = world_frame
|
|
64
|
+
|
|
65
|
+
self._tf_pub = self.create_publisher(TFMessage, "/gt/tf_dynamic", 10)
|
|
66
|
+
self._clock_pub = self.create_publisher(Clock, "/clock", 10)
|
|
67
|
+
self._contact_pubs = {
|
|
68
|
+
cs.label: self.create_publisher(Contacts, f"/gt/contact/{cs.label}", 10)
|
|
69
|
+
for cs in contact_specs
|
|
70
|
+
}
|
|
71
|
+
self.get_logger().info(
|
|
72
|
+
"GT publisher up: /gt/tf_dynamic, /clock, "
|
|
73
|
+
+ str([f"/gt/contact/{cs.label}" for cs in contact_specs])
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# --- time -------------------------------------------------------------
|
|
77
|
+
def _stamp(self):
|
|
78
|
+
"""ROS time stamp; from the sim clock if available so all stamps share sim time."""
|
|
79
|
+
if self._clock_source is not None:
|
|
80
|
+
sec = float(self._clock_source())
|
|
81
|
+
stamp = rclpy.time.Time(seconds=sec).to_msg()
|
|
82
|
+
return stamp
|
|
83
|
+
return self.get_clock().now().to_msg()
|
|
84
|
+
|
|
85
|
+
def publish_clock(self) -> None:
|
|
86
|
+
if self._clock_source is None:
|
|
87
|
+
return
|
|
88
|
+
msg = Clock()
|
|
89
|
+
msg.clock = self._stamp()
|
|
90
|
+
self._clock_pub.publish(msg)
|
|
91
|
+
|
|
92
|
+
# --- ground truth -----------------------------------------------------
|
|
93
|
+
def publish_tf_dynamic(self) -> None:
|
|
94
|
+
"""One ``TFMessage`` holding the world pose of every dynamic model.
|
|
95
|
+
|
|
96
|
+
Matches the Gazebo ``Pose_V`` -> ``TFMessage`` bridge shape: the Oracle
|
|
97
|
+
proximity-picks the robot from ``msg.transforms`` by translation (dropping
|
|
98
|
+
entries with |x|,|y| > 6 or |z| > 2), so the only hard requirement is that the
|
|
99
|
+
robot's transform carries its true world (x, y, z) in metres.
|
|
100
|
+
"""
|
|
101
|
+
poses = self._pose_source()
|
|
102
|
+
msg = TFMessage()
|
|
103
|
+
stamp = self._stamp()
|
|
104
|
+
for name, p in poses.items():
|
|
105
|
+
ts = TransformStamped()
|
|
106
|
+
ts.header.stamp = stamp
|
|
107
|
+
ts.header.frame_id = self._world_frame
|
|
108
|
+
ts.child_frame_id = name
|
|
109
|
+
ts.transform.translation.x = float(p[0])
|
|
110
|
+
ts.transform.translation.y = float(p[1])
|
|
111
|
+
ts.transform.translation.z = float(p[2])
|
|
112
|
+
if len(p) >= 7:
|
|
113
|
+
ts.transform.rotation.x = float(p[3])
|
|
114
|
+
ts.transform.rotation.y = float(p[4])
|
|
115
|
+
ts.transform.rotation.z = float(p[5])
|
|
116
|
+
ts.transform.rotation.w = float(p[6])
|
|
117
|
+
else:
|
|
118
|
+
ts.transform.rotation.w = 1.0
|
|
119
|
+
msg.transforms.append(ts)
|
|
120
|
+
self._tf_pub.publish(msg)
|
|
121
|
+
|
|
122
|
+
def publish_contacts(self) -> None:
|
|
123
|
+
"""Per sensor: publish ``Contacts`` whose ``.contacts`` is non-empty iff in contact."""
|
|
124
|
+
stamp = self._stamp()
|
|
125
|
+
for cs in self._contacts:
|
|
126
|
+
msg = Contacts()
|
|
127
|
+
msg.header.stamp = stamp
|
|
128
|
+
msg.header.frame_id = self._world_frame
|
|
129
|
+
if cs.source():
|
|
130
|
+
msg.contacts.append(Contact()) # one entry -> Oracle reads len>0 == collision
|
|
131
|
+
self._contact_pubs[cs.label].publish(msg)
|
|
132
|
+
|
|
133
|
+
def tick(self) -> None:
|
|
134
|
+
"""Publish all ground-truth topics for the current step (call after ``world.step``)."""
|
|
135
|
+
self.publish_clock()
|
|
136
|
+
self.publish_tf_dynamic()
|
|
137
|
+
self.publish_contacts()
|