rosotacom-dev 2.2__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.
- rosotacom/__init__.py +32 -0
- rosotacom/__main__.py +8 -0
- rosotacom/anonymize.py +360 -0
- rosotacom/bag_ground_truth.py +395 -0
- rosotacom/benched_set.py +492 -0
- rosotacom/benchmark.py +1842 -0
- rosotacom/bundle_check.py +517 -0
- rosotacom/cli.py +8637 -0
- rosotacom/cli_benchmark.py +8211 -0
- rosotacom/deployment.py +196 -0
- rosotacom/ffmpeg_packet.py +123 -0
- rosotacom/forensics.py +1172 -0
- rosotacom/geomap.py +848 -0
- rosotacom/network_profiles.py +563 -0
- rosotacom/network_shaper.py +127 -0
- rosotacom/plots.py +609 -0
- rosotacom/py.typed +0 -0
- rosotacom/resources/benched-set.yaml +317 -0
- rosotacom/resources/examples/.gitignore +1 -0
- rosotacom/resources/examples/README.md +163 -0
- rosotacom/resources/examples/__init__.py +1 -0
- rosotacom/resources/examples/bags/8_drop_reference/metadata.yaml +17 -0
- rosotacom/resources/examples/deployment.example.yaml +14 -0
- rosotacom/resources/examples/profiles.yaml +85 -0
- rosotacom/resources/examples/ros2docker.json +33 -0
- rosotacom/resources/examples/rosotacom.yaml +12 -0
- rosotacom/resources/examples/scenarios/2_native_chatter/scenario-definition.yaml +10 -0
- rosotacom/resources/examples/scripts/1_heartbeat/run_machine_a.sh +9 -0
- rosotacom/resources/examples/scripts/1_heartbeat/run_machine_b.sh +9 -0
- rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.ros2docker.json +21 -0
- rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_communication.sh +9 -0
- rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_external.py +15 -0
- rosotacom/resources/examples/scripts/2_native_chatter/machine_a/wait_for_echo_topic.sh +10 -0
- rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.ros2docker.json +19 -0
- rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_communication.sh +9 -0
- rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_external.py +15 -0
- rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external.ros2docker.json +12 -0
- rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external_session.yaml +9 -0
- rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/foxglove.json +89 -0
- rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_communication.sh +9 -0
- rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_external.py +15 -0
- rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
- rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/metadata.yaml +44 -0
- rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/external.ros2docker.json +13 -0
- rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_communication.sh +9 -0
- rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_external.py +15 -0
- rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external.ros2docker.json +12 -0
- rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external_session.yaml +9 -0
- rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/foxglove.json +89 -0
- rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_communication.sh +9 -0
- rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_external.py +15 -0
- rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
- rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/metadata.yaml +44 -0
- rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/external.ros2docker.json +13 -0
- rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_communication.sh +9 -0
- rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_external.py +15 -0
- rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external.ros2docker.json +20 -0
- rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external_session.yaml +22 -0
- rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_communication.sh +9 -0
- rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_external.py +15 -0
- rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external.ros2docker.json +20 -0
- rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external_session.yaml +22 -0
- rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_communication.sh +9 -0
- rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_external.py +15 -0
- rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external.ros2docker.json +18 -0
- rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external_session.yaml +22 -0
- rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_communication.sh +9 -0
- rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_external.py +15 -0
- rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external.ros2docker.json +18 -0
- rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external_session.yaml +22 -0
- rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_communication.sh +9 -0
- rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_external.py +15 -0
- rosotacom/resources/examples/sessions/10_restamp/session-definition.yaml +39 -0
- rosotacom/resources/examples/sessions/11_trickle/session-definition.yaml +42 -0
- rosotacom/resources/examples/sessions/12_content_integrity/session-definition.yaml +33 -0
- rosotacom/resources/examples/sessions/13_link_latency/session-definition.yaml +42 -0
- rosotacom/resources/examples/sessions/14_remote_assist_anonymized/session-definition.yaml +211 -0
- rosotacom/resources/examples/sessions/15_remote_assist_anonymized_costmap/session-definition.yaml +59 -0
- rosotacom/resources/examples/sessions/16_remote_assist_anonymized_camera/session-definition.yaml +61 -0
- rosotacom/resources/examples/sessions/17_synthetic_camera_quality/session-definition.yaml +57 -0
- rosotacom/resources/examples/sessions/1_heartbeat/session-definition.yaml +26 -0
- rosotacom/resources/examples/sessions/1_heartbeat_status/session-definition.yaml +37 -0
- rosotacom/resources/examples/sessions/2_native_chatter/session-definition.yaml +34 -0
- rosotacom/resources/examples/sessions/3_comp_occ_grid/session-definition.yaml +43 -0
- rosotacom/resources/examples/sessions/4_comp_occ_grid_zen/session-definition.yaml +43 -0
- rosotacom/resources/examples/sessions/5_sized_payload/session-definition.yaml +39 -0
- rosotacom/resources/examples/sessions/6_sized_payload_zen/session-definition.yaml +50 -0
- rosotacom/resources/examples/sessions/7_latched_static/session-definition.yaml +49 -0
- rosotacom/resources/examples/sessions/8_drop/session-definition.yaml +37 -0
- rosotacom/resources/examples/sessions/9_throttle/session-definition.yaml +37 -0
- rosotacom/resources/examples/sessions/bench_1_1_capacity/session-definition.yaml +42 -0
- rosotacom/resources/examples/sessions/bench_1_2_load_sweep/session-definition.yaml +51 -0
- rosotacom/resources/examples/sessions/bench_1_3_ramp/session-definition.yaml +41 -0
- rosotacom/resources/examples/sessions/bench_1_4_recovery/session-definition.yaml +47 -0
- rosotacom/resources/examples/sessions/bench_1_5_camera/session-definition.yaml +45 -0
- rosotacom/resources/ros2docker.json.example +33 -0
- rosotacom/resources/ws/.vscode/c_cpp_properties.json +20 -0
- rosotacom/resources/ws/.vscode/settings.json +8 -0
- rosotacom/resources/ws/ota_configs/archive/fastdds_easy_mode.xml.template +32 -0
- rosotacom/resources/ws/ota_configs/archive/fastdds_initial_peer_list.xml.template +21 -0
- rosotacom/resources/ws/ota_configs/archive/fastdds_v2.xml.template +17 -0
- rosotacom/resources/ws/ota_configs/archive/fastdds_v3.xml.template +48 -0
- rosotacom/resources/ws/ota_configs/archive/fastdds_v4.xml.template +95 -0
- rosotacom/resources/ws/ota_configs/archive/fastdds_v5.xml.template +64 -0
- rosotacom/resources/ws/ota_configs/create_zenoh_json5.py +56 -0
- rosotacom/resources/ws/ota_configs/cyclonedds_minimal.xml.template +9 -0
- rosotacom/resources/ws/ota_configs/cyclonedds_tuned.xml.template +33 -0
- rosotacom/resources/ws/ota_configs/fastdds_unicast.xml.template +71 -0
- rosotacom/resources/ws/ota_configs/get_ota_xml.py +214 -0
- rosotacom/resources/ws/ota_configs/utils.py +74 -0
- rosotacom/resources/ws/ota_configs/zenoh.json5.template +32 -0
- rosotacom/resources/ws/ros2src/.gitkeep +0 -0
- rosotacom/resources/ws/ros2src/com_cpp/CMakeLists.txt +67 -0
- rosotacom/resources/ws/ros2src/com_cpp/COLCON_IGNORE +0 -0
- rosotacom/resources/ws/ros2src/com_cpp/LICENSE.txt +25 -0
- rosotacom/resources/ws/ros2src/com_cpp/package.xml +37 -0
- rosotacom/resources/ws/ros2src/com_cpp/src/hz_monitor.cpp +192 -0
- rosotacom/resources/ws/ros2src/com_cpp/src/universal_compressor.cpp +283 -0
- rosotacom/resources/ws/ros2src/com_cpp/src/universal_decompressor.cpp +306 -0
- rosotacom/resources/ws/ros2src/com_msgs/CMakeLists.txt +22 -0
- rosotacom/resources/ws/ros2src/com_msgs/LICENSE.txt +25 -0
- rosotacom/resources/ws/ros2src/com_msgs/msg/CompressedData.msg +3 -0
- rosotacom/resources/ws/ros2src/com_msgs/msg/EchoHeartbeat.msg +10 -0
- rosotacom/resources/ws/ros2src/com_msgs/msg/OtaStamped.msg +4 -0
- rosotacom/resources/ws/ros2src/com_msgs/msg/SizedPayload.msg +4 -0
- rosotacom/resources/ws/ros2src/com_msgs/package.xml +25 -0
- rosotacom/resources/ws/ros2src/com_py/LICENSE.txt +25 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/__init__.py +0 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/base_bridge_relay.py +106 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/bridge_in.py +76 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/bridge_out.py +70 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/heartbeat_echo.py +221 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/hz_monitor.py +178 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/image_bandwidth_reducer.py +218 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/image_pixel_capper.py +370 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/lanelet_saver.py +80 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/latch_relay.py +205 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/legacy/costmap_remapper.py +73 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/legacy/marker_remapper.py +93 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/legacy/speed_test.py +107 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/legacy/tf_remapper.py +156 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/legacy/traffic_monitor.py +91 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/legacy/wifi_network_analysis_1.py +213 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/link_bytes.py +177 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/link_trace.py +186 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/local_global_frame_bridge.py +465 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/localization_map_split_reduce_saver.py +258 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/msg_transform.py +277 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/pair_management.py +87 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/pub_sub_pair.py +145 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/qos.py +254 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/relay_in.py +78 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/relay_out.py +75 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/restamp.py +191 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/sized_publisher.py +244 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency.py +64 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency_core.py +51 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/status_overview.py +476 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/status_overview_core.py +989 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/tf_map_to_cart_saver.py +99 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/topic_monitor.py +327 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/topic_resolution.py +171 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/trickle.py +183 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/universal_compressor.py +653 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/universal_decompressor.py +372 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_unwrapper.py +191 -0
- rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_wrapper.py +154 -0
- rosotacom/resources/ws/ros2src/com_py/package.xml +34 -0
- rosotacom/resources/ws/ros2src/com_py/resource/com_py +0 -0
- rosotacom/resources/ws/ros2src/com_py/setup.cfg +6 -0
- rosotacom/resources/ws/ros2src/com_py/setup.py +55 -0
- rosotacom/resources/ws/ros2src/com_py/test/test_copyright.py +25 -0
- rosotacom/resources/ws/ros2src/com_py/test/test_flake8.py +25 -0
- rosotacom/resources/ws/ros2src/com_py/test/test_pep257.py +23 -0
- rosotacom/resources/ws/session/content/base/session_plugin_base.yaml +633 -0
- rosotacom/resources/ws/session/content/network/up_connectivity.py +364 -0
- rosotacom/resources/ws/session/content/network/wifi_network_analysis_2.py +215 -0
- rosotacom/resources/ws/session/creation/anonymize_bag.py +156 -0
- rosotacom/resources/ws/session/creation/catmux_log_setup.sh +65 -0
- rosotacom/resources/ws/session/creation/create_session_yaml.py +145 -0
- rosotacom/resources/ws/session/creation/generate_session_files.py +3217 -0
- rosotacom/resources/ws/session/creation/run_session.py +447 -0
- rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py +54 -0
- rosotacom/resources/ws/session/creation/strip_ansi.py +42 -0
- rosotacom/status_eval.py +475 -0
- rosotacom/stream_stats.py +650 -0
- rosotacom/trace_profiles.py +561 -0
- rosotacom/transit.py +249 -0
- rosotacom/video_quality.py +906 -0
- rosotacom_dev-2.2.dist-info/METADATA +874 -0
- rosotacom_dev-2.2.dist-info/RECORD +195 -0
- rosotacom_dev-2.2.dist-info/WHEEL +5 -0
- rosotacom_dev-2.2.dist-info/entry_points.txt +4 -0
- rosotacom_dev-2.2.dist-info/licenses/LICENSE.txt +25 -0
- rosotacom_dev-2.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,874 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rosotacom-dev
|
|
3
|
+
Version: 2.2
|
|
4
|
+
Summary: Host CLI for ROS OTA communication sessions.
|
|
5
|
+
Author: develNor
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/develNor/rosotacom
|
|
8
|
+
Project-URL: Repository, https://github.com/develNor/rosotacom
|
|
9
|
+
Project-URL: Issues, https://github.com/develNor/rosotacom/issues
|
|
10
|
+
Keywords: ros2,docker,teleoperation,communication,cli
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering
|
|
22
|
+
Classifier: Topic :: System :: Networking
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE.txt
|
|
26
|
+
Requires-Dist: argcomplete<4,>=3.6
|
|
27
|
+
Requires-Dist: mcap<2,>=1.4
|
|
28
|
+
Requires-Dist: Pillow<13,>=10
|
|
29
|
+
Requires-Dist: PyYAML>=6
|
|
30
|
+
Requires-Dist: ros2docker<0.2,>=0.1.4
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
33
|
+
Requires-Dist: check-wheel-contents>=0.6; extra == "dev"
|
|
34
|
+
Requires-Dist: mypy>=1.14; extra == "dev"
|
|
35
|
+
Requires-Dist: pre-commit>=4.0; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest>=8.3; extra == "dev"
|
|
37
|
+
Requires-Dist: pytest-cov>=6.0; extra == "dev"
|
|
38
|
+
Requires-Dist: ruff==0.15.17; extra == "dev"
|
|
39
|
+
Requires-Dist: shellcheck-py>=0.10.0; extra == "dev"
|
|
40
|
+
Requires-Dist: twine>=6.0; extra == "dev"
|
|
41
|
+
Requires-Dist: types-PyYAML>=6.0; extra == "dev"
|
|
42
|
+
Provides-Extra: plots
|
|
43
|
+
Requires-Dist: matplotlib>=3.8; extra == "plots"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
# ROS Communication DevContainer
|
|
47
|
+
|
|
48
|
+
[](https://github.com/develNor/ros_communication_devcontainer/actions/workflows/pr-merge-gate.yml)
|
|
49
|
+
[](https://codecov.io/gh/develNor/ros_communication_devcontainer)
|
|
50
|
+
[](https://pypi.org/project/rosotacom/)
|
|
51
|
+
[](https://pypi.org/project/rosotacom/)
|
|
52
|
+
[](https://github.com/develNor/ros_communication_devcontainer/blob/HEAD/LICENSE.txt)
|
|
53
|
+
[](https://github.com/develNor/ros_communication_devcontainer/actions/workflows/nightly-e2e.yml)
|
|
54
|
+
|
|
55
|
+
The ROS Communication DevContainer is a Docker-based solution designed to streamline the bidirectional synchronization of ROS2 topics between two Linux machines. It provides built-in compression and routing capabilities for over-the-air (OTA) data transfer: selected topics are remapped into an OTA namespace and transmitted either via direct DDS (CycloneDDS) or through a Zenoh router. When desired, the session can also place local application nodes and OTA-facing bridge nodes into separate ROS 2 domain IDs and automatically generate a standard ROS 2 `domain_bridge` configuration for the `/com/...` boundary. This project aligns with the publication *“Scalable Remote Operation for Autonomous Vehicles: Integration of Cooperative Perception and Open Source Communication.”*
|
|
56
|
+
|
|
57
|
+
<details>
|
|
58
|
+
<summary>Key Features</summary>
|
|
59
|
+
|
|
60
|
+
- **Minimal Dependencies**: Only Docker is needed to get started, simplifying the setup process.
|
|
61
|
+
- **Isolation**: Operates in a separate Docker container, ensuring minimal impact on existing ROS setups.
|
|
62
|
+
- **Centralized Configuration Management**: All configurations are stored and managed in this repository.
|
|
63
|
+
- **Compression**: Built-in compression capabilities for efficient data transfer.
|
|
64
|
+
- **QoS Configuration**: Flexible Quality of Service settings for optimized communication.
|
|
65
|
+
|
|
66
|
+
</details>
|
|
67
|
+
|
|
68
|
+
## Getting Started
|
|
69
|
+
|
|
70
|
+
### Prerequisites
|
|
71
|
+
|
|
72
|
+
- Docker installed on all machines
|
|
73
|
+
- Git for configuration management
|
|
74
|
+
- Python 3 with virtual environment support (`python3-venv` on Debian/Ubuntu;
|
|
75
|
+
for versioned Python packages this may be named like `python3.14-venv`)
|
|
76
|
+
- `ros2docker` v0.1.4 or newer. The local installer below installs the pinned
|
|
77
|
+
supported range into this checkout's virtual environment.
|
|
78
|
+
- `tmux` for the optional `rosotacom scenario` orchestration commands
|
|
79
|
+
- Machines connected to the same network (VPN or local WLAN)
|
|
80
|
+
|
|
81
|
+
### Convenience CLI: `rosotacom`
|
|
82
|
+
|
|
83
|
+
This repository's main entrypoint for starting ROS communication sessions is
|
|
84
|
+
the checkout-local `rosotacom` command. Each checkout owns its own `.venv`,
|
|
85
|
+
so multiple rosotacom versions can coexist without global symlink drift.
|
|
86
|
+
|
|
87
|
+
#### Install
|
|
88
|
+
|
|
89
|
+
Use a released version, or develop from a checkout — both with off-the-shelf
|
|
90
|
+
tooling, nothing rosotacom-specific:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# released version (recommended for users)
|
|
94
|
+
pipx install rosotacom-dev # isolated, on PATH everywhere
|
|
95
|
+
|
|
96
|
+
<!-- The distribution is rosotacom-dev; the import name and the CLI stay
|
|
97
|
+
`rosotacom`. This fork publishes under its own name so that `rosotacom`
|
|
98
|
+
on PyPI stays reserved for the FZI upstream. -->
|
|
99
|
+
# or: pip install --user rosotacom
|
|
100
|
+
|
|
101
|
+
# from a source checkout (developers)
|
|
102
|
+
cd /path/to/ros_communication_devcontainer
|
|
103
|
+
./install.sh # builds a checkout-local .venv
|
|
104
|
+
source .venv/bin/activate # use it in this terminal
|
|
105
|
+
# ./install.sh --global-symlink # optional: put it on PATH via ~/.local/bin
|
|
106
|
+
|
|
107
|
+
rosotacom --version
|
|
108
|
+
python -m rosotacom --version
|
|
109
|
+
rosotacom doctor
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
For a source checkout, that single `source .venv/bin/activate` command also
|
|
113
|
+
enables completion. Nothing needs to be added to your shell configuration.
|
|
114
|
+
|
|
115
|
+
For a pipx/global installation, add one line once to your shell startup file:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# zsh: add this line to ~/.zshrc
|
|
119
|
+
eval "$(rosotacom completion zsh)"
|
|
120
|
+
|
|
121
|
+
# bash: add this line to ~/.bashrc
|
|
122
|
+
eval "$(rosotacom completion bash)"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Every new terminal then loads completion automatically; you do not run the
|
|
126
|
+
command manually in each terminal. After reloading the shell,
|
|
127
|
+
`rosotacom <TAB><TAB>` lists commands,
|
|
128
|
+
`rosotacom smoke <TAB><TAB>` lists sessions from the active project, and a
|
|
129
|
+
prefix such as `rosotacom smoke 1<TAB>` expands to `1_heartbeat`. The same
|
|
130
|
+
session completion is available for `start`, `stop`, `status`, `test`, and the
|
|
131
|
+
probe commands; absolute and relative session-directory paths still complete
|
|
132
|
+
normally. `--identity <TAB><TAB>` lists the peer identities from the selected
|
|
133
|
+
session or scenario. `--peer <TAB><TAB>` completes logical peer keys, and
|
|
134
|
+
`--peer a=<TAB><TAB>` completes named hosts from the active deployment file.
|
|
135
|
+
`--peer-address <TAB><TAB>` completes logical peer keys for raw overrides.
|
|
136
|
+
Scenario `attach` and `stop` complete only active scenarios and identities.
|
|
137
|
+
Running `rosotacom completion` without a shell argument infers bash or zsh from
|
|
138
|
+
`$SHELL`.
|
|
139
|
+
|
|
140
|
+
Completion is not pinned to the version that registered it: each Tab press runs
|
|
141
|
+
the `rosotacom` currently selected by `PATH`. Activating another checkout's
|
|
142
|
+
virtual environment therefore switches both the command and its completion,
|
|
143
|
+
and `deactivate` switches back without a reset step. A pipx-suffixed executable
|
|
144
|
+
can be registered independently with, for example,
|
|
145
|
+
`eval "$(rosotacom@2.2.0 completion zsh)"`.
|
|
146
|
+
|
|
147
|
+
**Multiple versions / try-without-disturbing.** Because each checkout owns its
|
|
148
|
+
own `.venv` and `./install.sh` never touches your PATH on its own, you can keep a
|
|
149
|
+
pinned/production version in use while smoke-testing another in one terminal:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
cd ~/checkouts/rosotacom-newest # a second checkout (or git worktree)
|
|
153
|
+
./install.sh # builds .venv here; your other rosotacom is untouched
|
|
154
|
+
source .venv/bin/activate # this terminal now runs the new commit
|
|
155
|
+
rosotacom smoke # built-in example, zero config
|
|
156
|
+
deactivate # back to your usual rosotacom
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
For coexisting *released* versions, `pipx install rosotacom-dev==2.2.0 --suffix=@2.2.0`
|
|
160
|
+
gives you a `rosotacom@2.2.0` alongside the default — standard pipx, no bespoke
|
|
161
|
+
version manager.
|
|
162
|
+
|
|
163
|
+
### Basic Setup
|
|
164
|
+
|
|
165
|
+
`rosotacom` uses five layers of configuration/runtime state:
|
|
166
|
+
|
|
167
|
+
- A **project setup** file (`rosotacom.yaml`) points to host-local resources such as `ros2docker.json`, static `sessions/`, ignored `session-instances/`, and an optional deployment file.
|
|
168
|
+
- A **session config** defines the communication behavior for one run: logical peers, topics, QoS, processing, and transport choices.
|
|
169
|
+
- An optional **scenario config** composes one communication session with identity-specific local application containers.
|
|
170
|
+
- An optional **deployment config** maps named physical hosts to reachable addresses and optional SSH targets.
|
|
171
|
+
- A **session instance** stores one concrete run: generated config, catmux pane logs, smoke debug output, and future rosbags.
|
|
172
|
+
|
|
173
|
+
`rosotacom` resolves the active `rosotacom.yaml` by scope (first wins), using the
|
|
174
|
+
`shell` / `local` / `global` vocabulary from `pyenv`:
|
|
175
|
+
|
|
176
|
+
1. `--project <path>` (alias of `--rosotacom-config`) on the command
|
|
177
|
+
2. **shell** — `ROSOTACOM_CONFIG` in the environment (needs `eval`, since only
|
|
178
|
+
your shell can change its own environment)
|
|
179
|
+
3. **local** — the nearest `rosotacom.yaml` discovered upward from the cwd
|
|
180
|
+
4. **global** — a machine-wide default (`rosotacom config set project ... --global`)
|
|
181
|
+
5. a built-in example project, so a fresh install runs with zero setup
|
|
182
|
+
|
|
183
|
+
The built-in example is used **in place** (read-only) and writes nothing to
|
|
184
|
+
`$HOME`: its generated session output goes to a per-user tmpfs dir under
|
|
185
|
+
`$XDG_RUNTIME_DIR` (printed on `start`/`smoke`), so it is ephemeral. For
|
|
186
|
+
persistent, editable projects, create one with `rosotacom examples create`.
|
|
187
|
+
|
|
188
|
+
So the simplest workflow is just to be in a project directory (the **local**
|
|
189
|
+
scope — no command needed, just have the file):
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
rosotacom examples create ./rosotacom_examples
|
|
193
|
+
cd ./rosotacom_examples
|
|
194
|
+
rosotacom doctor # picks up ./rosotacom.yaml automatically
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Inspect or pin the selection with `rosotacom config`:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
rosotacom config show # every scope + the winner
|
|
201
|
+
rosotacom config set project ./rosotacom.yaml --global # machine-wide default
|
|
202
|
+
eval "$(rosotacom config set project ./rosotacom.yaml --shell)" # this terminal only
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The copied packaged example project uses this layout:
|
|
206
|
+
|
|
207
|
+
```text
|
|
208
|
+
rosotacom.yaml
|
|
209
|
+
ros2docker.json
|
|
210
|
+
deployment.example.yaml
|
|
211
|
+
sessions/
|
|
212
|
+
scenarios/
|
|
213
|
+
session-instances/
|
|
214
|
+
scripts/
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
`session_configs_dir` and `scenario_configs_dir` are ordered YAML lists. Put
|
|
218
|
+
project-local directories first and shared/example directories later when one
|
|
219
|
+
project should discover both.
|
|
220
|
+
|
|
221
|
+
See the [example project README](src/rosotacom/resources/examples/README.md)
|
|
222
|
+
for the copyable example layout, the
|
|
223
|
+
[session configuration reference](session-configuration.md) for the session
|
|
224
|
+
schema, the [deployment configuration reference](deployment-configuration.md) for
|
|
225
|
+
the machine-specific schema and peer-binding precedence,
|
|
226
|
+
[terminology.md](terminology.md) for the project vocabulary, and
|
|
227
|
+
[CONCURRENCY.md](CONCURRENCY.md) for what can run in parallel and how
|
|
228
|
+
conflicting runs are detected and aborted.
|
|
229
|
+
|
|
230
|
+
The normal examples intentionally contain no machine addresses. `rosotacom
|
|
231
|
+
smoke` injects isolated Docker-network addresses, while manual and OTA runs
|
|
232
|
+
select physical hosts explicitly.
|
|
233
|
+
|
|
234
|
+
Write or edit session configs under `sessions/<name>/`:
|
|
235
|
+
|
|
236
|
+
- `session-definition.yaml` for a self-contained session
|
|
237
|
+
- `session-parametrization.yaml` for a template plus parameters
|
|
238
|
+
|
|
239
|
+
Run `rosotacom` on each peer with the same active setup but a different identity:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# on peer "a"
|
|
243
|
+
rosotacom start 1_heartbeat --identity a \
|
|
244
|
+
--peer-address a=10.0.0.10 --peer-address b=10.0.0.11
|
|
245
|
+
|
|
246
|
+
# on peer "b"
|
|
247
|
+
rosotacom start 1_heartbeat --identity b \
|
|
248
|
+
--peer-address a=10.0.0.10 --peer-address b=10.0.0.11
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
`rosotacom` reads the static session input and creates generated files under `session-instances/<date>/<session>_<timestamp>_<id>/config/`, including per-peer plugin/session specs, topic lists, optional QoS, and optional `domain_bridge.yaml`. Catmux pane output is logged under the same instance in `logs/<peer>/catmux/`.
|
|
252
|
+
|
|
253
|
+
### Complete use cases with scenarios
|
|
254
|
+
|
|
255
|
+
A session intentionally describes only the communication contract. When a use
|
|
256
|
+
case also needs a local ROS application, rosbag, or ROS command, define a
|
|
257
|
+
scenario above it:
|
|
258
|
+
|
|
259
|
+
```yaml
|
|
260
|
+
schema_version: 1
|
|
261
|
+
session: 2_native_chatter
|
|
262
|
+
|
|
263
|
+
applications:
|
|
264
|
+
a:
|
|
265
|
+
- name: native_application
|
|
266
|
+
ros2docker_config: ../../scripts/2_native_chatter/machine_a/external.ros2docker.json
|
|
267
|
+
b:
|
|
268
|
+
- name: native_application
|
|
269
|
+
ros2docker_config: ../../scripts/2_native_chatter/machine_b/external.ros2docker.json
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Start one identity's communication and local application together:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
rosotacom scenario start 2_native_chatter --identity a
|
|
276
|
+
rosotacom scenario list
|
|
277
|
+
rosotacom scenario attach
|
|
278
|
+
rosotacom scenario stop
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
`scenario list` shows both configured scenarios and currently active
|
|
282
|
+
scenario/identity pairs. `attach` and `stop` infer omitted values when exactly
|
|
283
|
+
one active choice exists; otherwise their completions and error messages show
|
|
284
|
+
the eligible active choices.
|
|
285
|
+
|
|
286
|
+
The outer view uses an isolated host tmux server with one full window for
|
|
287
|
+
communication and one full window per local application. Its prefix remains
|
|
288
|
+
`Ctrl-b`; switch windows with `Ctrl-b n`/`Ctrl-b p`, and send the inner
|
|
289
|
+
container-side catmux prefix with `Ctrl-b Ctrl-b`. Detaching from the outer
|
|
290
|
+
tmux does not stop the use case. `scenario stop` owns cleanup of the application
|
|
291
|
+
containers, communication container, and outer tmux session.
|
|
292
|
+
|
|
293
|
+
### Safe Replay Testing with Anonymized Rosbags
|
|
294
|
+
|
|
295
|
+
To test scenarios involving proprietary data safely, first record the processed
|
|
296
|
+
outbound handoff topics that the session pipeline actually forwards into the OTA
|
|
297
|
+
path. `rosotacom anonymize` consumes that processed trace bag and the matching
|
|
298
|
+
session/scenario definition. It:
|
|
299
|
+
|
|
300
|
+
1. Resolves the session pipeline and selects the outbound handoff topics that feed
|
|
301
|
+
`/com/out/...` and `/ota/...`.
|
|
302
|
+
2. Fails closed if the input bag only contains raw source topics and is missing
|
|
303
|
+
the processed handoff topics.
|
|
304
|
+
3. Renames the handoff topics to generic names like `/topic1`, `/topic2`, etc.
|
|
305
|
+
4. Replaces message contents with mock payloads while preserving message shape,
|
|
306
|
+
timestamps, payload lengths, bag QoS metadata, and playback QoS overrides.
|
|
307
|
+
For ffmpeg camera packets it additionally preserves the keyframe flag and
|
|
308
|
+
`pts`, keeping the GOP burst structure analyzable — see
|
|
309
|
+
[docs/ffmpeg-keyframes.md](docs/ffmpeg-keyframes.md).
|
|
310
|
+
5. Generates a self-contained `rosotacom` project containing the anonymized bag,
|
|
311
|
+
processed-topic session definition, replay scenario, QoS overrides, and an
|
|
312
|
+
anonymization manifest.
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
rosotacom anonymize /path/to/processed_handoff_bag \
|
|
316
|
+
-s 3_comp_occ_grid \
|
|
317
|
+
-o ./anonymized_project
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Usage Examples
|
|
321
|
+
|
|
322
|
+
Create the example project first (cwd discovery wires it automatically once you
|
|
323
|
+
`cd` in):
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
rosotacom examples create ./rosotacom_examples
|
|
327
|
+
cd ./rosotacom_examples
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Run the local heartbeat smoke test:
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
rosotacom smoke
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
To smoke-test one specific configured session locally, pass its name:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
rosotacom smoke 1_heartbeat
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
The smoke test verifies both directions through the communication path: it waits
|
|
343
|
+
for `/com/in/a/heartbeat_a` and `/heartbeat_a` in peer `b`, plus
|
|
344
|
+
`/com/in/b/heartbeat_b` and `/heartbeat_b` in peer `a`. For each checked topic it
|
|
345
|
+
also reports a `SMOKE_METRIC` line with the received rate (`hz`) and end-to-end
|
|
346
|
+
latency (`delay_s`) so rate and latency regressions are visible. It prints the
|
|
347
|
+
`session-instances/...` artifact path so failures (and the per-peer
|
|
348
|
+
`logs/<peer>/catmux/...` pane output) can be inspected after the containers stop.
|
|
349
|
+
|
|
350
|
+
For local debugging, run the same end-to-end idea interactively:
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
rosotacom smoke 2_native_chatter --interactive
|
|
354
|
+
rosotacom smoke --interactive --list
|
|
355
|
+
rosotacom smoke 2_native_chatter --interactive --stop
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Interactive smoke starts all peers on one host, opens an isolated outer tmux
|
|
359
|
+
session, and keeps full windows for each peer's communication container. If the
|
|
360
|
+
target is a scenario, it also opens one window per local application container;
|
|
361
|
+
each application shares its peer communication container's isolated network
|
|
362
|
+
namespace so local ROS discovery behaves like colocated processes. A
|
|
363
|
+
`verification` window runs the local checks/status view while you inspect the
|
|
364
|
+
system. Its upper pane keeps the verification log; its lower pane watches live
|
|
365
|
+
status. The outer prefix is `Ctrl-b`; use `Ctrl-b n`/`Ctrl-b p` for windows and
|
|
366
|
+
`Ctrl-b Ctrl-b` for the inner catmux session.
|
|
367
|
+
|
|
368
|
+
`rosotacom smoke TARGET --interactive` accepts either a session or a scenario.
|
|
369
|
+
When both names exist, interactive smoke treats `TARGET` as a scenario unless
|
|
370
|
+
you pass `--target-type session`. Non-interactive `rosotacom smoke TARGET`
|
|
371
|
+
keeps its existing session-only behavior.
|
|
372
|
+
|
|
373
|
+
Local smoke runs of different targets are parallel-safe: every run gets
|
|
374
|
+
instance-scoped container names and its own isolated Docker network. Starting a
|
|
375
|
+
run that cannot coexist with an active one (same smoke target, same session
|
|
376
|
+
identity, any benchmark) aborts immediately with the conflicting containers and
|
|
377
|
+
the stop command. `rosotacom ps` shows what is active in this workspace;
|
|
378
|
+
[CONCURRENCY.md](CONCURRENCY.md) documents the boundaries.
|
|
379
|
+
|
|
380
|
+
For a real two-host OTA check with no deployment file, provide both addresses
|
|
381
|
+
and only the SSH target needed to reach the remote peer:
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
rosotacom ota-smoke 2_native_chatter \
|
|
385
|
+
--peer-address a=10.0.0.10 \
|
|
386
|
+
--peer-address b=10.0.0.11 \
|
|
387
|
+
--peer-ssh b=robot-b
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
For reusable host names, reference one deployment file from `rosotacom.yaml`:
|
|
391
|
+
|
|
392
|
+
```yaml
|
|
393
|
+
# rosotacom.yaml
|
|
394
|
+
deployment: deployment.yaml
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
```yaml
|
|
398
|
+
# deployment.yaml
|
|
399
|
+
hosts:
|
|
400
|
+
workstation:
|
|
401
|
+
address: 10.0.0.10
|
|
402
|
+
ssh: null
|
|
403
|
+
robot:
|
|
404
|
+
address: 10.0.0.11
|
|
405
|
+
ssh: robot-b
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
```bash
|
|
409
|
+
rosotacom ota-smoke 2_native_chatter \
|
|
410
|
+
--peer a=workstation \
|
|
411
|
+
--peer b=robot
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
`ota-smoke` accepts sessions and scenarios. It automatically stages and
|
|
415
|
+
installs the currently selected rosotacom version, stages the active project,
|
|
416
|
+
runs delivery and isolation checks, collects artifacts, stops the run, and
|
|
417
|
+
removes the remote workdir. Add `--interactive` for a local control tmux with
|
|
418
|
+
one attachable communication/catmux window per peer with a live status pane below
|
|
419
|
+
each one. Scenario targets also get one native application-container window per
|
|
420
|
+
scenario app. Stop an interactive run with `rosotacom ota-smoke TARGET
|
|
421
|
+
--interactive --stop`; pass the same peer/deployment arguments if the local tmux
|
|
422
|
+
metadata is not available. Use `--keep-running` to leave components up,
|
|
423
|
+
`--keep-workdir` to retain staged files, or `--reuse` to reuse an existing
|
|
424
|
+
installation.
|
|
425
|
+
|
|
426
|
+
### Live status / debugging overview
|
|
427
|
+
|
|
428
|
+
Enable a continuously-updated, per-topic pipeline overview by setting
|
|
429
|
+
`shared.use_status_overview: true` in the session definition (see the
|
|
430
|
+
ready-made `1_heartbeat_status` example). For every configured topic it tracks
|
|
431
|
+
where the topic currently is in the communication pipeline (furthest stage
|
|
432
|
+
reached and the first stage that is missing/broken), plus last-message age, Hz,
|
|
433
|
+
mean size, latency, exact wrapped-topic sequence loss/reordering, and echo-derived
|
|
434
|
+
RTT/clock offset.
|
|
435
|
+
|
|
436
|
+
The running session writes, under
|
|
437
|
+
`session-instances/.../logs/<peer>/status/`:
|
|
438
|
+
|
|
439
|
+
- `status.json` — machine-readable snapshot (source of truth) for tools/agents,
|
|
440
|
+
refreshed on a short interval and on every state transition,
|
|
441
|
+
- `status.txt` — a human-rendered table, and
|
|
442
|
+
- `events.jsonl` — state transitions plus per-`(topic, seq)` transit records for
|
|
443
|
+
wrapped topics (delivered/lost/reordered, section latency, size, inter-arrival,
|
|
444
|
+
and jitter).
|
|
445
|
+
|
|
446
|
+
For fixed-interval network-condition samples alongside the same session, enable
|
|
447
|
+
the [link trace recorder](docs/link-trace.md). It writes
|
|
448
|
+
`link_trace.jsonl` under the same status directory with `/proc/net/dev` counter
|
|
449
|
+
deltas, echo-heartbeat RTT/loss provenance, and an optional modem-metrics hook.
|
|
450
|
+
|
|
451
|
+
Convert a recorded link trace into a profiles-file entry when you want to replay
|
|
452
|
+
the same drive conditions in a repeatable benchmark:
|
|
453
|
+
|
|
454
|
+
```bash
|
|
455
|
+
rosotacom profile from-trace session-instances/.../logs/a/status/link_trace.jsonl \
|
|
456
|
+
--mode timeline --name drive_replay --out generated-profiles.yaml
|
|
457
|
+
rosotacom benchmark recovery --profiles-file generated-profiles.yaml --profile drive_replay
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
Timeline mode emits piecewise-constant RFC 0004 segments, turns long sample gaps
|
|
461
|
+
into `outage: reconnect`, and turns sustained 100% probe loss into
|
|
462
|
+
`outage: catchup`. Static mode distills one profile with median valid rate, p90
|
|
463
|
+
one-way delay, delay-spread jitter, and mean loss; constrain it with
|
|
464
|
+
`--window START:END` when only part of a drive should calibrate the profile. RTT
|
|
465
|
+
is mapped to symmetric one-way delay. Passive `/proc/net/dev` throughput is only
|
|
466
|
+
a lower-bound observation, so `rate` is emitted only when a sample is marked as
|
|
467
|
+
saturated/probed/capacity-like; otherwise the converter omits rate instead of
|
|
468
|
+
fabricating capacity. The generated YAML carries a provenance comment with the
|
|
469
|
+
source path, SHA-256, parameters, and these caveats.
|
|
470
|
+
|
|
471
|
+
Render an offline route map when a run also has GPS or pose samples:
|
|
472
|
+
|
|
473
|
+
```bash
|
|
474
|
+
rosotacom geomap --bag session-instances/.../rosbags/a/native --gps-topic /fix \
|
|
475
|
+
--trace session-instances/.../logs/a/status/link_trace.jsonl \
|
|
476
|
+
--metric observed_tx_kbps \
|
|
477
|
+
--out-csv artifacts/geo-link-quality.csv \
|
|
478
|
+
--out-html artifacts/geo-link-quality.html
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
`geomap` writes georeferenced CSV samples, an HTML report, and a sibling
|
|
482
|
+
`.route.png` route image. The timestamp offset between trace/event time and
|
|
483
|
+
bag/GPS time is explicit; see [docs/geo-link-map.md](docs/geo-link-map.md) for
|
|
484
|
+
the alignment model, CSV fixture workflow, and supported metrics.
|
|
485
|
+
|
|
486
|
+
Read it from the host with the `status` command:
|
|
487
|
+
|
|
488
|
+
```bash
|
|
489
|
+
rosotacom status 1_heartbeat_status # human-readable table
|
|
490
|
+
rosotacom status 1_heartbeat_status --json # machine-readable, for tools/agents
|
|
491
|
+
rosotacom status 1_heartbeat_status --watch # live refresh
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
Join one or more peer timelines after a run:
|
|
495
|
+
|
|
496
|
+
```bash
|
|
497
|
+
rosotacom metrics session-instances/.../logs/a/status/events.jsonl \
|
|
498
|
+
session-instances/.../logs/b/status/events.jsonl
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
Use `--records` for joined per-message rows instead of the loss and p50/p95
|
|
502
|
+
summary. Wrapped-topic corrected latency requires `shared.use_heartbeat: true`;
|
|
503
|
+
`status.json.clock_sync` reports the selected minimum-RTT sample, peer offset,
|
|
504
|
+
sample age, and symmetric-path assumption. Uncorrected OTA delay remains a
|
|
505
|
+
separate field.
|
|
506
|
+
|
|
507
|
+
Compare how a stream changes across recorded stages — for example native
|
|
508
|
+
pre-processing, processed handoff, and post-OTA transit rows:
|
|
509
|
+
|
|
510
|
+
```bash
|
|
511
|
+
rosotacom stream-stats \
|
|
512
|
+
--bag pre=logs/b/metrics/stages_20260702_172900:/sensors/camera/front_medium/resized/image_rect_color \
|
|
513
|
+
--bag handoff=logs/b/metrics/stages_20260702_172900:/sensors/camera/front_medium/resized/image_rect_color/compressed/restamped/drop1of2/ffmpeg \
|
|
514
|
+
--events post=logs/b/status/events.jsonl:/sensors/camera/front_medium/resized/image_rect_color \
|
|
515
|
+
--out stream-stats
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
The command emits a side-by-side Markdown table plus `stream-stats.json`, with
|
|
519
|
+
per-stage size distributions, observed rate, interval regularity, and FFMPEG
|
|
520
|
+
GOP/keyframe shape when available. MCAP bag sources use the Python `mcap`
|
|
521
|
+
reader; other rosbag2 storage backends require `rosbag2_py`. `events`
|
|
522
|
+
sources work from the recorded RFC 0003 transit rows. See
|
|
523
|
+
[docs/ffmpeg-keyframes.md](docs/ffmpeg-keyframes.md) for the GOP methodology
|
|
524
|
+
and example camera interpretation.
|
|
525
|
+
|
|
526
|
+
Turn a whole recorded instance into an explanation of *where* and *how*
|
|
527
|
+
delivery degraded — loss bursts, latency excursions, and rate collapses with
|
|
528
|
+
exact boundaries, joined with link-trace/profile/keyframe context:
|
|
529
|
+
|
|
530
|
+
```bash
|
|
531
|
+
rosotacom report session-instances/<day>/<instance>
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
It writes `report/report.json`, `report/report.md`, and per-stream figures with
|
|
535
|
+
the events marked (needs the `[plots]` extra). See
|
|
536
|
+
[docs/forensics-report.md](docs/forensics-report.md) for the detection
|
|
537
|
+
semantics and the correlation-not-causation caveat.
|
|
538
|
+
|
|
539
|
+
For opt-in local per-step latency, record generated local stage topics:
|
|
540
|
+
|
|
541
|
+
```yaml
|
|
542
|
+
shared:
|
|
543
|
+
use_status_overview: true
|
|
544
|
+
metric_backbone:
|
|
545
|
+
record_stages: true
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
The MCAP is written under `logs/<peer>/metrics/`. Analyze an ordered pipeline
|
|
549
|
+
with `ros2 run com_py stage_latency BAG TOPIC...`.
|
|
550
|
+
|
|
551
|
+
For decoded camera stages, compute offline image quality with PSNR/SSIM:
|
|
552
|
+
|
|
553
|
+
```bash
|
|
554
|
+
rosotacom videoquality \
|
|
555
|
+
session-instances/.../logs/b/metrics/stages_... \
|
|
556
|
+
session-instances/.../logs/a/metrics/stages_... \
|
|
557
|
+
--ref-topic /camera/image \
|
|
558
|
+
--degraded-topic /camera/image/ffmpeg/raw \
|
|
559
|
+
--out videoquality.json \
|
|
560
|
+
--plot videoquality.png \
|
|
561
|
+
--min-mean-psnr 30 \
|
|
562
|
+
--min-mean-ssim 0.90 \
|
|
563
|
+
--max-loss-pct 0
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
Use the outbound pre-transport image topic as the reference (`native` when no
|
|
567
|
+
preprocessing runs; otherwise the generated topic immediately before
|
|
568
|
+
`transport`, such as a restamped/drop output) and the receiver-side decoded
|
|
569
|
+
`native_in` stage as the degraded stream. The reference topic may be decoded
|
|
570
|
+
`sensor_msgs/msg/Image` or JPEG/PNG `sensor_msgs/msg/CompressedImage`; the
|
|
571
|
+
receiver-side ffmpeg comparison topic should be the decoded `/raw` reverse
|
|
572
|
+
transport output. Lost frames are reported as delivery loss and are never
|
|
573
|
+
averaged into PSNR/SSIM. Encoded `ffmpeg_image_transport_msgs/msg/FFMPEGPacket`
|
|
574
|
+
topics carry packet bytes, not pixels, so select the decoded `/raw` topic when
|
|
575
|
+
computing image quality. The same command also accepts JSON frame manifests for
|
|
576
|
+
host-only checks:
|
|
577
|
+
|
|
578
|
+
```bash
|
|
579
|
+
rosotacom videoquality --make-synthetic /tmp/rosotacom-quality
|
|
580
|
+
rosotacom videoquality \
|
|
581
|
+
/tmp/rosotacom-quality/reference-frames.json \
|
|
582
|
+
/tmp/rosotacom-quality/degraded-frames.json \
|
|
583
|
+
--min-mean-psnr 20 --max-loss-pct 0
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
After a recording run, validate that a session instance still contains the
|
|
587
|
+
artifacts needed for later analysis:
|
|
588
|
+
|
|
589
|
+
```bash
|
|
590
|
+
rosotacom bundle check session-instances/.../drive_run --peer a --peer b \
|
|
591
|
+
--file config/resolved-session.yaml \
|
|
592
|
+
--bag rosbags/a/native
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
`bundle check` validates each expected peer's `status.json` and `events.jsonl`,
|
|
596
|
+
requires transit rows in the event streams, and validates expected rosbag2 bags
|
|
597
|
+
from their `metadata.yaml` message counts. Extra expected artifacts can be passed
|
|
598
|
+
as repeated `--file`, `--optional-file`, `--bag`, and `--optional-bag` flags, or
|
|
599
|
+
via a generic YAML manifest:
|
|
600
|
+
|
|
601
|
+
```yaml
|
|
602
|
+
schema_version: 1
|
|
603
|
+
peers: [a, b]
|
|
604
|
+
required_files:
|
|
605
|
+
- config/resolved-session.yaml
|
|
606
|
+
required_bags:
|
|
607
|
+
- path: rosbags/a/native
|
|
608
|
+
label: native bag
|
|
609
|
+
optional_files:
|
|
610
|
+
- traces/link.jsonl
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
Phase 1 samples local-domain stages directly. OTA stages are graph-only and
|
|
614
|
+
their activity is inferred from adjacent local flow, so the status overview
|
|
615
|
+
never creates an additional OTA payload subscription. Combine both peers' files
|
|
616
|
+
for the full end-to-end picture; cross-peer confirmation is reserved for a later
|
|
617
|
+
phase.
|
|
618
|
+
|
|
619
|
+
Run the CI single-machine smoke matrix locally:
|
|
620
|
+
|
|
621
|
+
```bash
|
|
622
|
+
just test-e2e-smoke
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
Run the heartbeat example manually:
|
|
626
|
+
|
|
627
|
+
```bash
|
|
628
|
+
./scripts/1_heartbeat/run_machine_a.sh
|
|
629
|
+
./scripts/1_heartbeat/run_machine_b.sh
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
Example 2 is the pilot for one-command scenario orchestration:
|
|
633
|
+
|
|
634
|
+
```bash
|
|
635
|
+
rosotacom scenario start 2_native_chatter --identity a
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
The existing `run_external.py` and `run_communication.sh` helpers remain
|
|
639
|
+
available as a compatibility fallback. Examples 3–6 continue to use those
|
|
640
|
+
helpers for now.
|
|
641
|
+
|
|
642
|
+
The `sessions/` directory contains curated built-in session definitions:
|
|
643
|
+
|
|
644
|
+
- `1_heartbeat`: minimal heartbeat exchange
|
|
645
|
+
- `1_heartbeat_status`: heartbeat exchange with the live status overview enabled
|
|
646
|
+
- `2_native_chatter`: bridge `/chatter` from `machine_b` to `machine_a`
|
|
647
|
+
- `3_comp_occ_grid`: compressed occupancy grid over DDS
|
|
648
|
+
- `4_comp_occ_grid_zen`: compressed occupancy grid through Zenoh
|
|
649
|
+
- `5_sized_payload`: sized payload test over DDS
|
|
650
|
+
- `6_sized_payload_zen`: sized payload test through Zenoh
|
|
651
|
+
- `14_remote_assist_anonymized`: generic processed replay shape for the
|
|
652
|
+
remote-assist OTA contract; run it with
|
|
653
|
+
`rosotacom smoke 14_remote_assist_anonymized`
|
|
654
|
+
- `15_remote_assist_anonymized_costmap`: single-stream cut of example 14 —
|
|
655
|
+
only `/topic5`, the anonymized compressed occupancy-grid stream; run it with
|
|
656
|
+
`rosotacom smoke 15_remote_assist_anonymized_costmap`
|
|
657
|
+
- `16_remote_assist_anonymized_camera`: single-stream cut of example 14 —
|
|
658
|
+
only `/topic9`, the anonymized ffmpeg camera stream; run it with
|
|
659
|
+
`rosotacom smoke 16_remote_assist_anonymized_camera`
|
|
660
|
+
- `17_synthetic_camera_quality`: synthetic raw camera stream encoded through
|
|
661
|
+
ffmpeg and reverse-republished as decoded `/raw` frames for offline
|
|
662
|
+
`rosotacom videoquality` checks
|
|
663
|
+
|
|
664
|
+
Transport-combination coverage lives under `tests/sessions/rmw_matrix` and is
|
|
665
|
+
generated from `tests/sessions/generate_rmw_matrix.py`.
|
|
666
|
+
|
|
667
|
+
`rosotacom benchmark` uses the packaged `bench_1_*` sessions for local or OTA
|
|
668
|
+
benchmark probes. Benchmark sessions default to Cyclone DDS; pass `--rmw
|
|
669
|
+
fastdds`, `--rmw cyclone`, or another supported session RMW value to pin a run.
|
|
670
|
+
Each run writes a self-contained `result.json` under its benchmark artifact
|
|
671
|
+
directory with the selected RMW, configured load and offered bandwidth, profile
|
|
672
|
+
shaping context, thresholds, verdict, per-topic loss/latency/jitter metrics, and
|
|
673
|
+
the runner fingerprint. Deterministic runs gate against the committed two-sided
|
|
674
|
+
performance bands in `budgets.jsonl` via `rosotacom benchmark compare`, and
|
|
675
|
+
bands move only through `rosotacom benchmark ratchet` — see
|
|
676
|
+
[docs/performance-bands.md](docs/performance-bands.md) for the band schema,
|
|
677
|
+
verdicts, and the ratchet workflow (RFC 0007).
|
|
678
|
+
For live benchmark probes, `--duration` is the shaped publish window; after that
|
|
679
|
+
rosotacom stops synthetic publishers and waits `--drain-s` seconds before
|
|
680
|
+
tearing down shaping, so delayed in-flight messages are not miscounted as loss.
|
|
681
|
+
Cyclone DDS benchmark sessions keep the default tuned OTA
|
|
682
|
+
`SPDPInterval=30s`. On tight rate-limited profiles, this periodic discovery
|
|
683
|
+
metatraffic can share the shaped link with payload traffic and appear as regular
|
|
684
|
+
p99/max latency spikes. rosotacom records a `cyclonedds_spdp` diagnostic in
|
|
685
|
+
`result.json` and prints a warning when the probe duration and offered/shaped
|
|
686
|
+
bandwidth make that effect plausible. Keep the default for end-to-end DDS
|
|
687
|
+
behavior. For payload-only characterization where discovery bursts would mask the
|
|
688
|
+
question under test, pass `--cyclone-spdp-interval 150s` or another longer
|
|
689
|
+
positive `ms`/`s` duration. Making SPDP too frequent improves stale-peer and
|
|
690
|
+
reconnect detection cadence but contaminates tight-link latency more often;
|
|
691
|
+
making it too lax quiets short probes but hides discovery/liveliness overhead and
|
|
692
|
+
delays detection of changed peer state. The living record for this quirk is the
|
|
693
|
+
[SPDP discovery-burst finding](docs/findings/cyclone-spdp-discovery-bursts.md).
|
|
694
|
+
Use `benchmark probe` when you want a fixed payload/rate under one profile
|
|
695
|
+
instead of a breakpoint search. It writes the normal `result.json` plus
|
|
696
|
+
`time-bins.jsonl` with per-second loss, delivered Hz, payload bandwidth, and
|
|
697
|
+
p50/p95 latency/jitter bins; by default it also renders `probe-timeseries.png`
|
|
698
|
+
when the optional plotting dependency is installed:
|
|
699
|
+
|
|
700
|
+
```bash
|
|
701
|
+
rosotacom benchmark probe --profile cellular-4g-typical --size 18000 --rate-hz 20 --duration 20 --repeats 1
|
|
702
|
+
rosotacom benchmark probe --profile cellular-4g-typical --size-pattern 1x20KB+1x0KB --rate-hz 10 --duration 20 --repeats 1
|
|
703
|
+
rosotacom benchmark probe --profile cellular-4g-typical --size 18000 --rate-hz 20 --duration 20 --cyclone-spdp-interval 150s
|
|
704
|
+
rosotacom benchmark plot time-bins.jsonl --type probe
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
Use `benchmark requirements` when you want rosotacom to search for a tight
|
|
708
|
+
network profile for a stream and quality target. The driver starts from ideal
|
|
709
|
+
conditions, uses a geometric boundary search for bandwidth, linear boundary
|
|
710
|
+
searches for latency-like axes, and finishes with an extra refinement pass around
|
|
711
|
+
the final candidate. By default jitter and loss are searched as a coupled
|
|
712
|
+
practical tradeoff curve; pass `--loss-coupling independent` if you want network
|
|
713
|
+
loss treated as its own axis. For an exact `--max-loss 0` target, rosotacom
|
|
714
|
+
automatically keeps shaped network loss at `0%` and searches jitter separately,
|
|
715
|
+
so the reproducible command encodes the same policy used for the measurement. If
|
|
716
|
+
the zero-loss boundary is noisy, repeat each candidate and require a pass count;
|
|
717
|
+
for example, `--probe-repeats 10 --probe-min-passes 9 --bad-lossy-count 10`
|
|
718
|
+
defines a good case as at least nine clean repeats and records bad-case neighbors
|
|
719
|
+
where all ten repeats lose messages. Add `--netem-seed <n>` when you want the
|
|
720
|
+
generated jitter/loss draw to be replayable across repeated boundary searches.
|
|
721
|
+
For local Docker benchmarks, rosotacom copies the host `tc` binary into seeded
|
|
722
|
+
benchmark containers when the container distro `tc` is too old for
|
|
723
|
+
`netem seed`; the host `tc` must support `seed SEED`. For a practical good-case
|
|
724
|
+
reference, `--jitter-guard-ratio 0.10` records the tight jitter boundary but
|
|
725
|
+
uses 10% less jitter for later axes and the final profile;
|
|
726
|
+
`--bandwidth-guard-ratio 0.10` does the symmetric thing for bandwidth by using
|
|
727
|
+
10% more bandwidth after finding the lower bound. The chosen profile, target
|
|
728
|
+
slack, repeat statistics, all probes, and the nearby failing neighbors are written to
|
|
729
|
+
`result.json`, `requirements.jsonl`, and `generated-profiles.yaml`:
|
|
730
|
+
|
|
731
|
+
```bash
|
|
732
|
+
rosotacom benchmark requirements --rate-hz 20 --size 18000 --qos-reliability best_effort --qos-depth 1 --max-loss 5 --max-latency-ms 250
|
|
733
|
+
rosotacom benchmark requirements --rate-hz 20 --size 18000 --qos-reliability best_effort --qos-depth 1 --max-loss 0 --max-latency-ms 250
|
|
734
|
+
rosotacom benchmark requirements --rate-hz 20 --size 18000 --qos-reliability best_effort --qos-depth 1 --max-loss 0 --max-latency-ms 250 --latency-base-ms 30 --downlink-mode lan --probe-repeats 10 --probe-min-passes 9 --bad-lossy-count 10 --jitter-guard-ratio 0.10 --bandwidth-guard-ratio 0.10 --netem-seed 424242
|
|
735
|
+
rosotacom benchmark requirements --rate-hz 20 --size 18000 --qos-reliability best_effort --qos-depth 1 --max-loss 0 --max-latency-ms 250 --latency-base-ms 30 --downlink-mode lan --axes jitter,bandwidth --bandwidth-high-factor 8 --bandwidth-low-factor 1 --jitter-high-ms 40 --search-iterations 7 --final-refine-iterations 0 --search-rounds 1 --min-duration 20 --min-messages 100 --probe-repeats 1 --bandwidth-probe-repeats 1 --netem-seed 424242 --jitter-guard-ratio 0.20 --bandwidth-guard-ratio 0.20
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
Use `benchmark loss-boundaries` when the question is specifically “where does
|
|
739
|
+
the first ROS 2 loss appear?” It searches bandwidth and jitter as discrete axes:
|
|
740
|
+
bandwidth reports the lowest loss-free rate and the adjacent lower bad rate at
|
|
741
|
+
`--bandwidth-step`; jitter reports the highest fully clean value, the first
|
|
742
|
+
non-clean neighbor, the lowest fully lossy value, and the mixed zone between
|
|
743
|
+
them at `--jitter-step-ms`. Seedless runs are the best empirical estimate for a
|
|
744
|
+
host/link, a single `--netem-seed` is useful for replay/debugging one random
|
|
745
|
+
jitter sequence, and `--netem-seeds 101,102,...` is the reproducible compromise
|
|
746
|
+
when you want several replayable jitter draws instead of overfitting one seed:
|
|
747
|
+
|
|
748
|
+
```bash
|
|
749
|
+
rosotacom benchmark loss-boundaries --rate-hz 20 --size 18000 --qos-reliability best_effort --qos-depth 1 --max-latency-ms 250 --latency-base-ms 30 --downlink-mode lan --bandwidth-low 2.8mbit --bandwidth-high 4mbit --bandwidth-step 0.1mbit --jitter-low-ms 0 --jitter-high-ms 40 --jitter-step-ms 1 --min-duration 20 --min-messages 100 --probe-repeats 10
|
|
750
|
+
rosotacom benchmark loss-boundaries --rate-hz 20 --size 18000 --qos-reliability best_effort --qos-depth 1 --max-latency-ms 250 --latency-base-ms 30 --downlink-mode lan --bandwidth-low 2.8mbit --bandwidth-high 4mbit --bandwidth-step 0.1mbit --jitter-low-ms 0 --jitter-high-ms 40 --jitter-step-ms 1 --min-duration 20 --min-messages 100 --probe-repeats 1 --netem-seeds 101,102,103,104,105,106,107,108,109,110
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
Both `requirements` and `loss-boundaries` also accept a **bag replay as the
|
|
754
|
+
load** instead of the synthetic `sized_publisher` stream: pass `--target
|
|
755
|
+
<session>` (a two-peer replay session such as `15_remote_assist_anonymized_costmap`)
|
|
756
|
+
and, optionally, `--bag <metadata.yaml>` for per-topic completeness ground truth.
|
|
757
|
+
A probe point is then one full replay run under the candidate profile, and the
|
|
758
|
+
oracle judges the **whole contract** — every carried topic must clear the
|
|
759
|
+
loss/latency bound and the `--oracle-min-completeness` floor — aggregated to a
|
|
760
|
+
run verdict that names any failing topics. This answers "which network could
|
|
761
|
+
carry *this bag*?" rather than "…this synthetic stream?". Because a probe now
|
|
762
|
+
costs a full bag loop, keep the iteration budget small. See
|
|
763
|
+
[docs/benchmark-bag-as-load.md](docs/benchmark-bag-as-load.md) for the oracle,
|
|
764
|
+
the local-vs-OTA execution split, and cost guidance:
|
|
765
|
+
|
|
766
|
+
```bash
|
|
767
|
+
rosotacom benchmark loss-boundaries --target 15_remote_assist_anonymized_costmap --target-type session --axes bandwidth --bandwidth-low 0.5mbit --bandwidth-high 2mbit --bandwidth-step 0.5mbit --rate-hz 10 --min-duration 20 --min-messages 1 --probe-repeats 1 --good-clean-count 1 --bad-lossy-count 1 --max-latency-ms 1000
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
Use `benchmark ab` when the question is a **tuning** one: "does candidate config
|
|
771
|
+
B beat baseline config A on the same load and profile?" Each `--baseline` and
|
|
772
|
+
`--candidate label=…` is a whole session config (a directory or a
|
|
773
|
+
`session-definition.yaml`) that shares the synthetic `a_to_b` load and differs
|
|
774
|
+
only in the pipeline knobs under test (QoS reliability/depth/lifespan,
|
|
775
|
+
compression, ffmpeg gop/bitrate/crf, … — knobs that keep the delivered topic
|
|
776
|
+
name, unlike `throttle_hz`/`drop` which republish to a renamed topic; see the
|
|
777
|
+
doc). The driver runs them interleaved with a rotating start over `--repeats`,
|
|
778
|
+
then classifies every candidate against the baseline per topic and metric as
|
|
779
|
+
**IMPROVED / WITHIN / REGRESSED** — the same verdict language the regression gate
|
|
780
|
+
uses — with a two-sided tolerance band (`--rel-tolerance`, `--abs-tolerance`).
|
|
781
|
+
The overall verdict fails if any watched metric regressed; the repeat spread
|
|
782
|
+
(min/median/max) travels with every cell so small-N effects are read honestly.
|
|
783
|
+
It writes `result.json`, a markdown table (`ab.md`), the per-config YAML diffs,
|
|
784
|
+
and `ab.jsonl`. See [docs/benchmark-ab.md](docs/benchmark-ab.md) for the config
|
|
785
|
+
model, interleaving, and the statistical-power note:
|
|
786
|
+
|
|
787
|
+
```bash
|
|
788
|
+
rosotacom benchmark ab --profile cellular-4g-degraded --baseline configs/gop30 --candidate gop15=configs/gop15 --size 18000 --rate-hz 20 --duration 20 --repeats 3
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
Use `rosotacom ota-benchmark` for the same probes over deployment peers, without
|
|
792
|
+
having to name a target:
|
|
793
|
+
|
|
794
|
+
```bash
|
|
795
|
+
rosotacom ota-benchmark probe --profile cellular-4g-degraded --size 18000 --rate-hz 20 --duration 20 --repeats 1 --peer a=seat_tks --peer b=majestic_tks
|
|
796
|
+
rosotacom ota-benchmark capacity --profile cellular-4g-degraded --knob size --low 1 --high 1 --max-loss 30 --max-latency-ms 1000 --duration 10 --repeats 1 --peer a=seat_tks --peer b=majestic_tks
|
|
797
|
+
rosotacom ota-benchmark requirements --rate-hz 20 --size 18000 --qos-reliability best_effort --qos-depth 1 --max-loss 5 --max-latency-ms 250 --peer a=seat_tks --peer b=majestic_tks
|
|
798
|
+
rosotacom ota-benchmark loss-boundaries --rate-hz 20 --size 18000 --qos-reliability best_effort --qos-depth 1 --max-latency-ms 250 --latency-base-ms 30 --downlink-mode lan --bandwidth-low 2.8mbit --bandwidth-high 4mbit --bandwidth-step 0.1mbit --jitter-high-ms 40 --jitter-step-ms 1 --probe-repeats 10 --peer a=seat_tks --peer b=majestic_tks
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
OTA benchmarks default to the benchmark session for the selected genre. Pass
|
|
802
|
+
`--target` and `--target-type` only when you deliberately want to benchmark a
|
|
803
|
+
project-specific session or scenario instead. Add `--interactive` to open a tmux
|
|
804
|
+
operator view with a high-level run window, one fullscreen attachable catmux
|
|
805
|
+
window per local peer, a network window split into qdisc status and tc/netem
|
|
806
|
+
command logs, and a results window that prints the final result once. Shaped OTA
|
|
807
|
+
benchmark profiles need sudo on the remote peers for the generated `tc`/`ip`
|
|
808
|
+
commands. The default `--sudo-mode passwordless` checks `sudo -n` and is the right
|
|
809
|
+
mode for unattended runs. For attended operator runs, pass `--sudo-mode askpass`
|
|
810
|
+
to prompt locally once per peer and feed the password to remote `sudo -S` over SSH
|
|
811
|
+
stdin without printing or storing it.
|
|
812
|
+
|
|
813
|
+
## Development
|
|
814
|
+
|
|
815
|
+
For contributor setup, local checks, PR workflow, CI, and releases, see
|
|
816
|
+
[CONTRIBUTING.md](CONTRIBUTING.md). CI behavior is summarized in
|
|
817
|
+
[docs/ci.md](docs/ci.md), releases in [docs/release.md](docs/release.md), and
|
|
818
|
+
issue-driven work tracking in [docs/work-items.md](docs/work-items.md).
|
|
819
|
+
|
|
820
|
+
The test and measurement architecture is recorded in the
|
|
821
|
+
[design RFCs](docs/rfcs/README.md), with measured public effects in the
|
|
822
|
+
[findings ledger](docs/findings/README.md); see [docs/testing.md](docs/testing.md)
|
|
823
|
+
for the test taxonomy. The diagnosis-first quality workflow is described in
|
|
824
|
+
[docs/quality-model.md](docs/quality-model.md), with owner-facing entrypoints in
|
|
825
|
+
[docs/owner-runbook.md](docs/owner-runbook.md). Reusable maintainer procedures
|
|
826
|
+
live in [docs/playbooks/README.md](docs/playbooks/README.md). Quality rules live
|
|
827
|
+
in [DEVELOPMENT_PRINCIPLES.md](DEVELOPMENT_PRINCIPLES.md).
|
|
828
|
+
|
|
829
|
+
## Choosing the Transport Layer: CycloneDDS or Zenoh
|
|
830
|
+
|
|
831
|
+
- **Use CycloneDDS** when all machines share the **same `ROS_DOMAIN_ID`**.
|
|
832
|
+
This is the simplest and most direct configuration.
|
|
833
|
+
|
|
834
|
+
- **Use Zenoh** when peers cannot rely on one shared DDS domain, or when you want to split local application nodes and OTA-facing bridge nodes into different ROS 2 domains on each peer.
|
|
835
|
+
In that split-domain setup this repository generates a standard ROS 2 `domain_bridge` for `/com/...` topics locally, while Zenoh carries the `/ota/...` traffic between peers.
|
|
836
|
+
|
|
837
|
+
## Position in the OTA Communication Landscape
|
|
838
|
+
|
|
839
|
+
This repository fits into a broader set of ROS-based OTA communication approaches:
|
|
840
|
+
|
|
841
|
+
- **Direct ROS 2 DDS Communication**
|
|
842
|
+
Native DDS (CycloneDDS, Fast DDS), often with custom configuration for constrained or long-range links.
|
|
843
|
+
The examples in this repository use CycloneDDS to illustrate this approach.
|
|
844
|
+
|
|
845
|
+
- **ROS 2 over Router-like Backbones**
|
|
846
|
+
Some RMW have their own DDS Routers such as [eProsima/DDS-Router](https://github.com/eProsima/DDS-Router).
|
|
847
|
+
Example 4 uses Zenoh to act as a lightweight router layer.
|
|
848
|
+
|
|
849
|
+
- **MQTT-based Approaches**
|
|
850
|
+
Common in cloud/IoT scenarios. Example:
|
|
851
|
+
[ika-rwth-aachen/mqtt_client](https://github.com/ika-rwth-aachen/mqtt_client)
|
|
852
|
+
|
|
853
|
+
- **Custom TCP/UDP Teleoperation Stacks**
|
|
854
|
+
Some frameworks implement their manual tcp/udp transportion layers. Example:
|
|
855
|
+
[TUMFTM/teleoperated_driving](https://github.com/TUMFTM/teleoperated_driving)
|
|
856
|
+
|
|
857
|
+
## How to Cite
|
|
858
|
+
|
|
859
|
+
If you wish to cite the ROS Communication DevContainer in your work, please use the following citation:
|
|
860
|
+
|
|
861
|
+
```latex
|
|
862
|
+
@InProceedings{gontscharow_scalable,
|
|
863
|
+
author = {Gontscharow, Martin and Doll Jens and Schotschneider, Albert and Bogdoll, Daniel and Orf Stefan and Jestram Johannes and Zofka, Marc and Z\"{o}llner, J. Marius},
|
|
864
|
+
title = {{Scalable Remote Operation for Autonomous Vehicles: Integration of Cooperative Perception and Open Source Communication}},
|
|
865
|
+
booktitle = {2024 IEEE Intelligent Vehicles Symposium (IV)},
|
|
866
|
+
year = {2024}
|
|
867
|
+
}
|
|
868
|
+
```
|
|
869
|
+
## Acknowledgements
|
|
870
|
+
The research leading to these results was conducted within
|
|
871
|
+
the project ÖV-LeitmotiF-KI and was funded by the German
|
|
872
|
+
Federal Ministry for Digital and Transport (BMDV), grant number 45AVF3004A-G.
|
|
873
|
+
Responsibility for the information and views set out in this
|
|
874
|
+
publication lies entirely with the authors.
|