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_cmakeandcolcon- 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:
migrate_ws/runtime/bin/mtranserver, which owns Bergamot WASM and theen_zh-Hansmodel.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:
- Received a successful response from
GET /health. - Completed one internal
en -> zh-Hanswarm-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
resultmapping - 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:
- Build the current machine's MTranServer executable into
migrate_ws/runtime/bin/mtranserver. - Place the
en_zh-Hansmodel inmigrate_ws/models/en_zh-Hans. - Run
colcon buildfrommigrate_ws. - Source
migrate_ws/install/setup.bash. - Start
translator.launch.py. - 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_ros2succeeds 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.