Files
yiliao2026/src/mtran/docs/superpowers/specs/2026-07-29-mtran-ros2-service-design.md

10 KiB

MTranServer ROS2 English-to-Chinese Service Design

1. Goal

Build a ROS2 Humble C++ package that exposes one synchronous service for translating a single English sentence into Simplified Chinese. The package runs on Ubuntu 22.04, is built with the current machine's native toolchain, and starts a persistent MTranServer sidecar through ROS2 launch.

All new source, documentation, runtime staging files, and build outputs for this migration live under ./migrate_ws.

2. Scope

The package provides exactly one operation:

English string in a ROS2 service request
  -> MTranServer en-to-zh-Hans translation
  -> Chinese string in the ROS2 service response

The following features are deliberately excluded:

  • Topic subscription or publication
  • Batch translation
  • Runtime language selection
  • Automatic language detection
  • HTML translation
  • Long-text splitting
  • Web UI integration
  • Native C++ Bergamot integration

3. Target Environment

  • ROS2 Humble
  • Ubuntu 22.04
  • C++17
  • ament_cmake and colcon
  • libcurl for HTTP
  • nlohmann/json for JSON
  • Bun only for producing the MTranServer sidecar executable

The same ROS2 source is compiled natively on x64 or ARM64. The current implementation and smoke test use the current machine's native MTranServer artifact. ARM64 MTranServer build and target-device deployment commands are documentation-only in this iteration.

4. Workspace Layout

migrate_ws/
├── docs/
│   └── superpowers/
│       ├── specs/
│       └── plans/
├── models/
│   └── en_zh-Hans/
├── runtime/
│   └── bin/
│       └── mtranserver
├── src/
│   └── mtran_ros2/
│       ├── CMakeLists.txt
│       ├── package.xml
│       ├── config/
│       │   └── translator.yaml
│       ├── include/mtran_ros2/
│       │   ├── http_translation_client.hpp
│       │   └── translator_node.hpp
│       ├── launch/
│       │   └── translator.launch.py
│       ├── scripts/
│       │   └── build_mtranserver.sh
│       ├── src/
│       │   ├── http_translation_client.cpp
│       │   ├── main.cpp
│       │   └── translator_node.cpp
│       ├── srv/
│       │   └── TranslateEnglishToChinese.srv
│       └── test/
│           ├── test_http_translation_client.cpp
│           └── test_translator_node.cpp
└── README.md

Generated build/, install/, log/, the local MTranServer executable, and translation model files are not committed.

5. Architecture

translator.launch.py starts two long-lived processes:

  1. migrate_ws/runtime/bin/mtranserver, which owns Bergamot WASM and the en_zh-Hans model.
  2. mtran_bridge_node, which exposes the ROS2 service and converts service requests into local HTTP requests.

The C++ bridge communicates only with http://127.0.0.1:8989. It does not expose MTranServer's language, HTML, batch, or authentication features through ROS2.

The MTranServer executable remains a separate Bun build product. CMake builds only the ROS2 C++ package. The build script stages a native MTranServer binary at migrate_ws/runtime/bin/mtranserver; it does not create an architecture-specific ROS2 source tree.

6. ROS2 Interface

The service type is mtran_ros2/srv/TranslateEnglishToChinese:

string text
---
bool success
string translation
string error

The service name is fixed:

/translate_en_to_zh

Successful response:

success: true
translation: "..."
error: ""

Failed response:

success: false
translation: ""
error: "ERROR_CODE"

7. Request Processing

The node uses rclcpp::executors::SingleThreadedExecutor. It performs at most one translation at a time and does not create an application-level request queue or worker pool.

For every valid request, the bridge sends:

{
  "from": "en",
  "to": "zh-Hans",
  "text": "request text",
  "html": false
}

to POST /translate. A successful upstream response must be HTTP 200 with this shape:

{
  "result": "translated text"
}

The bridge returns the result only after the HTTP request completes. It does not retry a translation request.

8. Input Constraints

Input validation occurs before HTTP communication:

  • Empty or whitespace-only input returns INVALID_INPUT.
  • Input longer than 512 characters returns INPUT_TOO_LONG.
  • Valid input is treated as plain English text.

The 512-character limit keeps the existing MTranServer call on its direct fixed-language path and avoids its long-text language-detection and segmentation path.

9. Startup and Model Residency

Launch supplies these MTranServer settings:

MT_HOST=127.0.0.1
MT_PORT=8989
MT_ENABLE_UI=false
MT_OFFLINE=true
MT_CHECK_UPDATE=false
MT_MODEL_DIR=<migrate_ws/models>
MT_WORKER_IDLE_TIMEOUT=86400
MT_CACHE_SIZE=100

The bridge does not advertise /translate_en_to_zh until it has:

  1. Received a successful response from GET /health.
  2. Completed one internal en -> zh-Hans warm-up translation.

The existing MTranServer treats an idle timeout of zero as an immediate timeout, so this migration does not set it to zero and does not modify code outside migrate_ws. Instead, the bridge sends one internal warm-up translation every 43,200 seconds. Normal client requests also reset MTranServer's idle timer. The 86,400-second timeout and 43,200-second keep-warm interval keep the model resident without repeatedly starting the process.

Launch terminates both processes together. If MTranServer exits unexpectedly, launch respawns it. During the outage, service calls fail with NOT_READY. After health recovers, the bridge warms the model before returning to ready state.

10. Configuration

translator.yaml defines:

mtran_bridge_node:
  ros__parameters:
    server_url: "http://127.0.0.1:8989"
    request_timeout_ms: 30000
    startup_timeout_ms: 60000
    health_retry_ms: 500
    keep_warm_interval_s: 43200
    max_input_characters: 512

Language direction and HTML mode are compile-time behavior, not ROS parameters.

Launch arguments allow overriding the MTranServer executable path, model directory, offline records/configuration directory, parameter file, host, and port. Defaults point to the corresponding paths under migrate_ws for local development.

11. Error Semantics

The service returns these exact error codes:

Code Meaning
INVALID_INPUT Input is empty or only whitespace.
INPUT_TOO_LONG Input exceeds 512 characters.
NOT_READY MTranServer is starting, unavailable, or being rewarmed.
TIMEOUT The translation request exceeded 30 seconds.
UPSTREAM_ERROR MTranServer returned a non-200 HTTP response or transport error.
INVALID_RESPONSE The upstream body is not JSON containing a string result.

Errors are logged without logging complete request text. Failed requests always return an empty translation. The original English input is never substituted as a successful result.

12. Component Boundaries

HttpTranslationClient

Owns libcurl setup, HTTP calls, timeout handling, request JSON serialization, and response JSON parsing. It exposes health checking and fixed-direction translation methods without any ROS2 dependency.

TranslatorNode

Owns ROS parameters, readiness state, input validation, service creation, startup warm-up, periodic keep-warm, and mapping client failures to service responses. It depends on the translation client through an interface so tests can inject deterministic results.

main.cpp

Initializes ROS2, creates TranslatorNode, and spins it in a SingleThreadedExecutor.

translator.launch.py

Resolves launch arguments, starts the MTranServer sidecar with the fixed environment, enables respawn, and starts the bridge node.

13. Testing

Automated tests cover:

  • Empty and whitespace-only request rejection
  • 513-character request rejection
  • Boundary acceptance at 512 characters
  • Correct fixed HTTP request payload
  • Successful HTTP response parsing
  • Non-200 response mapping
  • Timeout mapping
  • Invalid JSON and missing/non-string result mapping
  • Service success and failure response fields
  • Startup readiness transition after health and warm-up
  • Recovery transition after a simulated sidecar outage
  • Keep-warm scheduling using short test-only intervals

HTTP client tests use a local mock HTTP endpoint and require no model or network. Node tests inject a fake translation client.

A manual smoke test uses the native staged MTranServer binary and the local en_zh-Hans model:

ros2 service call /translate_en_to_zh \
  mtran_ros2/srv/TranslateEnglishToChinese \
  "{text: 'The robot has completed the inspection.'}"

The expected response has success: true, a non-empty Chinese translation, and an empty error.

14. Build and Deployment

The local workflow is:

  1. Build the current machine's MTranServer executable into migrate_ws/runtime/bin/mtranserver.
  2. Place the en_zh-Hans model in migrate_ws/models/en_zh-Hans.
  3. Run colcon build from migrate_ws.
  4. Source migrate_ws/install/setup.bash.
  5. Start translator.launch.py.
  6. Run the service smoke test.

The README also documents ARM64 deployment. On ARM64, the same C++ source is built with the target's native colcon; the sidecar is built with Bun's bun-linux-arm64 target or built natively on the ARM64 device.

15. Acceptance Criteria

  • Every new migration file is under migrate_ws.
  • colcon build --packages-select mtran_ros2 succeeds on ROS2 Humble and Ubuntu 22.04 when declared dependencies are installed.
  • Launch starts both the native MTranServer sidecar and the C++ bridge.
  • The ROS2 service appears only after health checking and model warm-up succeed.
  • A valid English sentence of at most 512 characters returns Simplified Chinese in the service response.
  • Empty, oversized, unavailable, timeout, upstream, and malformed-response cases return the specified error code and an empty translation.
  • Translation work is serialized on one executor thread.
  • The model remains warm during idle operation through the keep-warm mechanism.
  • Runtime translation works without network access after the executable and model are staged.
  • README includes local native build instructions and separate ARM64 build/deployment instructions.