The AY-Robots try page: three ways to start without owning a robot, including renting a GPU for policy inference
GR00T N1.7Remote InferenceCloud GPULeRobotSO-100Latency

Run GR00T Inference Without a Local GPU

AY-Robots ResearchAugust 23, 202619 min read

Your robot machine has no GPU. Put the GR00T policy server on a rented cloud GPU, stream action chunks to the arm, and learn exactly what the network costs you.

A Raspberry Pi is enough to drive an SO-100 over a serial bus and pull frames off two USB cameras. It is not enough to run a three billion parameter vision-language-action model: NVIDIA's README puts GR00T N1.7 inference at one GPU with 16 GB or more of VRAM. To see what your fine-tuned GR00T N1.7 checkpoint does on the arm without buying a card, put the policy on a rented cloud GPU, keep the robot loop on the machine with the USB ports, and send observations and action chunks over the network.

It works, it is not free, and the price is not evenly spread across tasks. Below: NVIDIA's own policy server, lerobot's async stack, the arithmetic that says in advance whether your uplink is fast enough, and the platform route. All of it checked against the Isaac-GR00T main branch (N1.7 GA) and lerobot 0.6.1 on 23 August 2026.

What you need to know

  • GR00T N1.7, GR00T N1.5 and Pi0.5 are roughly 3 B parameter models. None fit on a robot controller without a discrete GPU.
  • Isaac-GR00T and lerobot both ship a client-server split. You do not write the transport.
  • Observations dominate the wire cost, not actions: two uncompressed 640x480 RGB frames are 1,843,200 bytes, about 14.7 Mbit per call, and neither stack compresses them.
  • AY-Robots lists 20 to 485 ms per action step by model. Internet round trips land on top of that.
  • Remote inference suits slow pick-and-place, not fast reactive motion. A longer execution horizon buys time and costs observation freshness.
  • Neither server is safe on a public IP as shipped, and lerobot's carries an unpatched RCE. Tunnel it.

Why the policy will not fit on the robot machine

Two of the five policies AY-Robots can train run on a workstation card, three do not. The inference latency column below is per action step, and that is the number which competes with your network round trip.

PolicyParamsInference per action stepGPU tier for trainingMin episodesDataset format
GR00T N1.7~3 B, ~40 M trained during fine-tuning152 msA100 80 GB or H100 80 GB50LeRobot v2.0 or v2.1
GR00T N1.5~3 B165 msA100 80 GB or H100 80 GB50LeRobot v2.0 or v2.1
Pi0.5~3 B, PaliGemma backbone485 msA100 80 GB or H100 80 GB50LeRobot v3.0
SmolVLA~450 M245 msRTX 4090 or any 24 GB card30LeRobot v3.0
ACT~80 M20 msRTX 4090 or any 24 GB card50LeRobot v3.0
The AY-Robots policies page comparing the five trainable policies by parameters, GPU tier, inference latency and minimum episodes
The same five rows on /policies. The latency column decides whether a policy survives a network hop.

Read that as a decision, not trivia. ACT at 20 ms per step runs on the robot machine and you never think about it again. Pi0.5 at 485 ms has spent a third of a second before a packet leaves your building. The policy comparison and GR00T N1.7 vs Pi0.5 add the accuracy side.

ACT has no base model

GR00T N1.7, GR00T N1.5 and Pi0.5 start from a vendor checkpoint (nvidia/GR00T-N1.7-3B, nvidia/GR00T-N1.5-3B, lerobot/pi05_base). ACT does not exist until you train it on your own task, so there is nothing to serve remotely until a training job has run. See ACT on SO-100.

The two client-server stacks that already exist

Isaac-GR00T ships a ZeroMQ request-reply server; lerobot ships a gRPC server built around asynchronous inference. Both accept a GR00T checkpoint. lerobot's supported policy list in async_inference/constants.py is act, smolvla, diffusion, tdmpc, vqbet, pi0, pi05 and groot; its robot list is so100_follower, so101_follower, bi_so_follower and omx_follower.

Isaac-GR00T PolicyServerlerobot async inference
Entry pointgr00t/eval/run_gr00t_server.pypython -m lerobot.async_inference.policy_server
TransportZeroMQ REQ/REPgRPC, add_insecure_port / insecure_channel
Serializationmsgpack + msgpack_numpy, allow_pickle=False enforcedpickle.dumps / pickle.loads, marked # nosec
Default port55558080
Default bind0.0.0.0, all interfaceslocalhost
Authapi_token supported by the class, not passed by the CLInone
Client timeout15000 ms (PolicyClient timeout_ms)2 s observation queue timeout
Execution modelsynchronous: block, then execute the chunkasynchronous: execute while the next chunk computes

The serialization row matters more than it looks. GR00T's MsgSerializer refuses object-dtype ndarray payloads in both directions, because msgpack_numpy would otherwise hand them to pickle. lerobot pickles instead: policy_server.py calls pickle.loads on request data, robot_client.py pickles the observation it sends. Defensible on a trusted LAN, indefensible once the port is reachable from the internet.

Route A: NVIDIA's own GR00T policy server

This is the path NVIDIA documents for SO-100 and SO-101 hardware, and the one to use if your checkpoint came out of examples/finetune.sh with --embodiment-tag NEW_EMBODIMENT. The steps add what the upstream README leaves out: getting the port to the robot without exposing it to everyone else.

  1. 1
    Install GR00T on the rented GPU box

    Submodules are required, and git-lfs must exist before the clone or the parquet files in demo_data arrive as pointers. flash-attn and TensorRT come with the default install. The trap on a fresh pod image: torchcodec 0.8.0 is the only supported video backend and loads FFmpeg 4 to 7 only. Ubuntu 25.10 and 26.04 ship FFmpeg 8, so GR00T fails with Could not load libtorchcodec. Install an FFmpeg below 8 and put its libraries on LD_LIBRARY_PATH.

    bash
    sudo apt install git-lfs && git lfs install
    curl -LsSf https://astral.sh/uv/install.sh | sh
    sudo apt-get update && sudo apt-get install -y ffmpeg
    
    git clone --recurse-submodules https://github.com/NVIDIA/Isaac-GR00T
    cd Isaac-GR00T
    uv sync --python 3.12
    uv run python -c "import gr00t; print('GR00T installed successfully')"
  2. 2
    Authenticate against the gated backbone

    Every GR00T N1.7 checkpoint, including your own fine-tune, loads the gated nvidia/Cosmos-Reason2-2B on first use. Request access on the model page and log in on the pod, or loading fails with a GatedRepoError.

    bash
    uv run huggingface-cli login
    # or:  export HF_TOKEN=<your_token>
  3. 3
    Start the policy server

    Point --model-path at your checkpoint directory; on that path the server ignores --modality-config-path, which is read only on the replay path. Omit --model-path and pass --dataset-path plus --execution-horizon instead for a ReplayPolicy that replays recorded actions, the cheapest way to prove the wiring works.

    bash
    uv run python gr00t/eval/run_gr00t_server.py \
      --model-path /workspace/so100_finetune/checkpoint-10000 \
      --embodiment-tag NEW_EMBODIMENT \
      --device cuda:0 \
      --host 127.0.0.1 --port 5555
  4. 4
    Tunnel port 5555 to the robot machine

    Bind to loopback, as above, and carry the port over SSH or a WireGuard-style mesh. That supplies the encryption and authentication the ZeroMQ socket does not, for about a millisecond.

    bash
    # on the robot machine
    ssh -N -L 5555:127.0.0.1:5555 root@<pod-host> -p <pod-ssh-port>
    
    # sanity check that something answers
    nc -vz 127.0.0.1 5555
  5. 5
    Run the robot client next to the servos

    The client needs its own uv environment: it wants lerobot's robot drivers, not the training stack. eval_so100.py imports so100_follower, so101_follower and koch_follower, so pass the --robot.type matching your arm (the upstream README uses so101_follower). Camera keys must match training: the adapter reads exactly front and wrist, and swapping them shows the policy the wrong view.

    bash
    cd gr00t/eval/real_robot/SO100
    uv sync
    uv pip install --no-deps -e ../../../../
    
    uv run --no-sync python eval_so100.py \
      --robot.type=so100_follower \
      --robot.port=/dev/ttyACM0 \
      --robot.id=orange_follower \
      --robot.cameras="{ front: {type: opencv, index_or_path: 6, width: 640, height: 480, fps: 30}, wrist: {type: opencv, index_or_path: 2, width: 640, height: 480, fps: 30}}" \
      --policy_host=127.0.0.1 \
      --policy_port=5555 \
      --lang_instruction="pick up the red block and put it in the bin"
Two defaults that will bite you

run_gr00t_server.py defaults to --host 0.0.0.0, binding every interface: on a pod with a public IP that is an open inference endpoint. And the PolicyServer class accepts an api_token and validates it per request, but run_gr00t_server.py never passes one, so the CLI server is unauthenticated whatever you configure. Bind to 127.0.0.1 and tunnel. A ZMQError: Address already in use means port 5555 is taken; pass --port.

Route B: lerobot async inference

lerobot solves a different problem. Instead of blocking the robot while the model thinks, the client keeps stepping through the queue it already has while the server computes the next chunk. This is action chunking taken further, the asynchronous stack introduced with SmolVLA. It works with a GR00T checkpoint too.

bash
# GPU machine
pip install -e ".[async]"
python -m lerobot.async_inference.policy_server \
     --host=127.0.0.1 \
     --port=8080

# robot machine, after tunnelling 8080
python -m lerobot.async_inference.robot_client \
    --server_address=127.0.0.1:8080 \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM0 \
    --robot.id=follower_so100 \
    --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}, wrist: {type: opencv, index_or_path: 1, width: 640, height: 480, fps: 30}}" \
    --task="pick up the red block and put it in the bin" \
    --policy_type=groot \
    --pretrained_name_or_path=<user>/my_groot_finetune \
    --policy_device=cuda \
    --actions_per_chunk=50 \
    --chunk_size_threshold=0.5 \
    --debug_visualize_queue_size=True
Policy server on the GPU, robot client on the machine with the USB ports

The server starts empty: it does not know which policy it serves until the client's first handshake tells it, which is convenient on a rented pod. The two knobs that decide whether the arm moves smoothly are actions_per_chunk and chunk_size_threshold (the lerobot docs call the second one g, after the SmolVLA paper), and the documented values and the shipped values do not agree.

ParameterValue in lerobot 0.6.1 codeWhat it doesNote
actions_per_chunkno default, requiredActions returned per callDocs table lists 50; the dataclass field has no default, so the CLI demands a value
chunk_size_threshold0.5Queue fill ratio at or below which the client sends a fresh observationDocs table says 0.7; the code and the docs' own example say 0.5
fps30Client control rate, sets environment_dt = 1/fpsLower it if the queue keeps draining
inference_latency1/30 s (33.3 ms)Target inference latency on the serverA target, not a measurement
obs_queue_timeout2 sHow long the server waits on the observation queueA slow uplink shows up here first
aggregate_fn_nameweighted_averageHow overlapping chunk regions are blended0.3 old + 0.7 new; latest_only, average and conservative also ship. The registry is AGGREGATE_FUNCTIONS in configs.py, not robot_client.py as the docs claim
The lerobot policy server has an unpatched RCE

CVE-2026-25874 is unauthenticated remote code execution in lerobot's async inference pipeline: pickle.loads() on data received over an unauthenticated gRPC channel without TLS, reachable through the SendPolicyInstructions, SendObservations and GetActions calls. CWE-502, CVSS 3.1 base score 9.8 from NVD, 4.0 base score 9.3 from the assigning CNA. The record lists LeRobot through 0.5.1 as affected and names both the policy server and the robot client, so the machine next to your arm is in scope. Upgrading is not the fix: the record cites upstream issue 3047 and the patch, PR 3048, which swaps pickle for safetensors plus JSON, and on 23 August 2026 both are still open. policy_server.py on main still calls pickle.loads on request data while serve() binds with add_insecure_port. Bind to loopback and never port-forward 8080.

The arithmetic that decides whether your link is fast enough

People skip this and then spend a day on a policy that freezes mid-motion. It takes two minutes and it is almost always decisive.

The commented observation dict in NVIDIA's eval_so100.py says what goes on the wire: two arrays of shape (480, 640, 3) in uint8, six joint floats, a language string. That is 921,600 bytes per frame, 1,843,200 bytes for two cameras, about 14.7 Mbit, and neither stack JPEG-compresses it. The chunk coming back is a few dozen steps of 6 floats. Your upload decides everything, not your download.

Upload bandwidthTime to push one observation (14.7 Mbit)Verdict for a 30 FPS arm
10 Mbit/s, typical home upload~1.47 sUnusable. The arm stops between every chunk.
25 Mbit/s~0.59 sSlow pick-and-place only, with a long execution horizon.
50 Mbit/s~0.29 sWorkable for deliberate tasks.
100 Mbit/s~0.15 sFine for pick-and-place, visible on fast motion.
1 Gbit/s fibre or datacentre~0.015 sThe model becomes the bottleneck instead.

The budget you have to fit inside

The GR00T SO-100 client is synchronous: it calls policy.get_action(obs), executes the first action_horizon steps of the chunk at 30 FPS, then calls again. Chunk size and horizon are different numbers: NVIDIA's deployment guide recommends an action chunk size of 16, at least 32 when combined with real-time chunking, while eval_so100.py ships an execution horizon of 8. Eight steps at 30 FPS is 267 ms of motion per call, and everything else has to fit inside that.

text
observation upload   14.7 Mbit / 100 Mbit/s   = 147 ms
network round trip                            =  30 ms
model inference (AY-Robots figure, N1.7)      = 152 ms
action chunk return + deserialize             =  ~2 ms
                                                -------
total per call                                  331 ms

budget at action_horizon = 8   ->  267 ms   FAIL, arm pauses ~64 ms per chunk
budget at action_horizon = 16  ->  533 ms   fits, with headroom
budget at action_horizon = 32  -> 1067 ms   fits, observations now ~1 s stale
Worked example: 100 Mbit/s uplink, 30 ms round trip, GR00T N1.7

Raising the horizon is the blunt fix and not a free one: the arm acts on an observation that is now old. The principled fix is real-time chunking, which computes the next chunk while the current one runs, freezes the actions guaranteed to execute and inpaints the rest; the RTC paper reports it as robust to inference delay with no retraining. Check where that stands first. NVIDIA marks RTC experimental, a low-level model primitive reachable through action_head.get_action(..., options={"rtc_overlap_steps": ..., "rtc_frozen_steps": ...}), not wired into Gr00tPolicy or the server-client path, where options is unused, with no tests and no example. Over a policy server you get asynchronous execution, not RTC.

What the model costs before the network does

NVIDIA benchmarks GR00T N1.7 end to end at 4 denoising steps with one camera. On an H100 80GB HBM3: 85.8 ms (11.7 Hz) in PyTorch eager, 48.6 ms (20.6 Hz) with torch.compile, 27.9 ms (35.9 Hz) with the TensorRT full pipeline. An L40 in eager mode takes 128.3 ms (7.8 Hz). NVIDIA calls 10 Hz the recommended minimum for typical manipulation, and below 10 Hz suitable only for slow, non-reactive tasks. Those are replanning rates: a 10 Hz policy can still drive a 30 FPS arm through action chunking. A second camera moves you the wrong way.

Measure it before you trust it

Every number above is a prediction. Four commands turn it into a measurement, worth running before committing a pod-hour to a task that was never going to work.

  1. 1
    Get the raw round trip

    Against the pod, not a CDN. Watch the deviation as closely as the mean: jitter makes an arm stutter, not average latency.

    bash
    ping -c 50 <pod-host>
    # the mdev column is the number that predicts stutter
  2. 2
    Measure the uplink you have, not the one you pay for

    Residential upload is usually a fraction of download, and it is the number in the bandwidth table above.

    bash
    # on the pod
    iperf3 -s
    
    # on the robot machine, -R omitted so this measures upload
    iperf3 -c <pod-host> -t 30
  3. 3
    Read the client's own latency log

    The lerobot robot client logs server-to-client latency and deserialization time for every chunk. On route B you need no external tooling.

    text
    Received action chunk for step #240 | Latest action: #232 |
      Incoming actions: 240:289 |
      Network latency (server->client): 187.44ms |
      Deserialization time: 3.10ms
  4. 4
    Watch the action queue drain

    Pass --debug_visualize_queue_size=True and the client plots queue size at runtime. If it repeatedly hits zero you are out of budget: lower fps, raise actions_per_chunk, or raise chunk_size_threshold so observations go out more often.

    bash
    python -m lerobot.async_inference.robot_client \
        ... \
        --debug_visualize_queue_size=True

What remote inference is actually good for

Policy on a rented GPU, arm on your desk
Advantages
  • You can evaluate a 3 B parameter policy on real hardware without owning a card that costs more than the arm.
  • The GPU is rented per hour, so a failed checkpoint costs a couple of dollars.
  • The robot side stays small: lerobot drivers, two cameras, a serial port, and you swap checkpoints without touching it.
Trade-offs
  • Uncompressed observations dominate the wire cost, and residential upload is the binding constraint.
  • Jitter hurts more than latency: a link averaging 40 ms with spikes to 300 ms stutters where a steady 120 ms link does not.
  • Fast reactive tasks do not survive the round trip at any horizon.
  • Both servers ship unauthenticated in CLI form, so the tunnelling work is yours.
  • A dropped connection mid-chunk leaves the arm holding a stale action. Add your own watchdog robot-side.
TaskWorks over the public internet?Why
Pick a static object, place it in a binYesNothing moves between observation and action.
Stack blocks at a deliberate paceYes, at action_horizon 16 or moreErrors accumulate slowly enough to fix on the next chunk.
Open a drawer, insert an objectUsuallyContact-rich but slow. Watch for stop-and-go at contact.
Follow a moving objectNoThe policy acts on an observation 300 ms to 1 s old.
Catch, balance, or recover from a slipNoThe correction window is shorter than one round trip.
A 30 Hz synchronous closed loopNoThe budget is 33 ms end to end. Even a LAN struggles.

If a remote run stutters at the same point in every episode, the network is probably not the cause. A policy hesitating at the same joint angle every time is usually a data problem; see the failure-mode pages, in particular a policy that only works in one setup and loss falls but the policy does nothing.

Doing it yourself vs doing it on AY-Robots

  1. Rent a GPU on a spot market and wait for enough VRAM at a price you like.
  2. Install CUDA, uv, an ffmpeg torchcodec accepts, and the GR00T stack with submodules.
  3. Request access to the gated nvidia/Cosmos-Reason2-2B backbone and put a token on the pod.
  4. Pull your checkpoint onto the pod.
  5. Start the server on loopback, then build an SSH tunnel from the robot machine.
  6. Install a second environment on the robot machine for the client and drivers.
  7. Match camera keys, joint names and the language instruction to what the checkpoint saw.
  8. Watch the pod. A forgotten A100 running overnight costs more than the experiment.
The idle pod is the real cost

The GPU bill does not stop when the robot stops. Most money lost on remote inference goes to a server that stayed up after everyone walked away. Set an alarm, or automate teardown.

The AY-Robots MCP server page listing platform operations exposed as tools to AI agents
The MCP page: provisioning and inference operations exposed as tools an agent can call.

What a remote inference session costs

Two numbers matter: the hourly rate of the card, and how long you leave it running. The first is published; the second surprises people.

CardRunpod community cloudRunpod secure cloudReasonable for
A100 PCIe 80 GB1.19 USD/h1.39 USD/hGR00T N1.7, GR00T N1.5, Pi0.5
A100 SXM 80 GB1.39 USD/h1.59 USD/hThe same, a little faster
H100 PCIe 80 GB1.99 USD/h2.89 USD/hFastest tier; NVIDIA's 11.7 Hz eager figure is for an H100 80GB HBM3
L40S 48 GB0.79 USD/h0.99 USD/hInference only, above the 16 GB floor
RTX 4090 24 GB0.34 USD/h0.74 USD/hSmolVLA, ACT

Those rates were read from Runpod's pricing page on 23 August 2026, and spot markets move. AY-Robots quotes a whole run instead: 3 to 6 hours at 1.20 to 2.00 USD per hour on the A100 or H100 tier, about 4 to 12 USD for a GR00T or Pi0.5 run; 2 to 5 hours at 0.30 to 0.60 USD per hour on the 24 GB tier, 1 to 3 USD for SmolVLA or ACT. An inference session beats a training run on cost only if you stop it, which is what the idle watchdog is for. See the billing docs and the pricing page.

The AY-Robots cost table showing which GPU each policy needs, typical run time and price, and episodes before a policy is useful
The cost table on /try: which card each model needs and what a run typically costs.

If you would rather not have the network in the loop

Remote inference solves a hardware problem and creates a latency problem. Sometimes the better answer is a policy that fits the hardware you have.

  • ACT, roughly 80 M parameters and 20 ms per action step, 50 episodes minimum, any 24 GB card. On a repetitive single-task setup it frequently beats a remote 3 B model, because it never waits for a packet.
  • SmolVLA, roughly 450 M parameters and 245 ms per action step, 30 episodes minimum. It keeps the language conditioning ACT lacks, and the lerobot docs put it at about 2 GB at inference time against roughly 14 GB for PI0.
  • ACT vs GR00T N1.7 for the accuracy half of the trade.

There is also a middle path: train in the cloud, evaluate locally. Fine-tuning needs the 80 GB card and does not care about latency, so training GR00T N1.7 on an SO-100 remotely is uncontroversial. Only the evaluation loop has a real-time constraint; the training docs and model-and-arm matrix cover that half.

No arm on the desk yet?

Drive a real SO-100 in the browser with no signup, compare the five trainable policies with their real latency numbers, or rent a GPU and train one. Three ways to start, none needing hardware you do not own.

Try it without hardware

Frequently asked questions

Can I run GR00T N1.7 on a Raspberry Pi if the GPU is remote?

Yes, that is what the client-server split is for. The Pi runs the lerobot drivers, reads two cameras and a serial bus, and sends observations to the policy server; it never loads the model. The constraint moves from VRAM to upload bandwidth: two uncompressed 640x480 RGB frames are 1,843,200 bytes per call, and neither stack compresses them.

How much latency does the network actually add?

Round-trip time plus observation transfer time. Transfer time is 14.7 Mbit divided by your upload bandwidth: roughly 147 ms on a 100 Mbit/s link, 1.47 s on a 10 Mbit/s link. Both land on top of the model's own inference time, which AY-Robots lists as 152 ms for GR00T N1.7 and 485 ms for Pi0.5. Measure with ping and iperf3 against the pod, not a speed-test server.

Is remote inference good enough for a real task?

For slow, deliberate pick-and-place, yes. For anything reactive, no. NVIDIA's deployment guide puts the synchronous single-step requirement at roughly 33 ms end to end at 30 FPS, and notes that capture, network, inference and post-processing routinely exceed that with no internet involved.

Which port do the servers use and is it safe to open it?

Isaac-GR00T's PolicyServer defaults to port 5555 over ZeroMQ and binds 0.0.0.0 in its CLI. lerobot's defaults to port 8080 over gRPC and binds localhost. Neither is safe to expose: the GR00T class supports an api_token but run_gr00t_server.py never passes one, and lerobot pickles data over an insecure gRPC channel, which is CVE-2026-25874. Bind to loopback and use an SSH tunnel.

Does upgrading lerobot fix CVE-2026-25874?

Not as of 23 August 2026. The CVE record lists LeRobot through 0.5.1 as affected and PyPI ships 0.6.1, but the pull request that would remove pickle from the async pipeline is still open, and policy_server.py on main still calls pickle.loads on request data. Treat network isolation as the mitigation, not a version bump, and assume the robot-side client is in scope too.

Can I use lerobot's async client with a GR00T checkpoint?

Yes. lerobot 0.6.1 lists groot in SUPPORTED_POLICIES alongside act, smolvla, diffusion, tdmpc, vqbet, pi0 and pi05, and both so100_follower and so101_follower are in SUPPORTED_ROBOTS. Pass --policy_type=groot and point --pretrained_name_or_path at your checkpoint. You get asynchronous execution, which the GR00T SO-100 example does not implement, at the cost of the pickle transport.

The short version

Remote inference for a 3 B policy is a solved engineering problem with an unsolved physics problem attached. The engineering is two commands and an SSH tunnel. The physics is that a 1.8 MB observation has to reach a GPU in another country and come back before the arm runs out of actions. Do the arithmetic before renting anything, pick a task that tolerates a stale observation, and raise the execution horizon rather than hoping the link improves.

If you have not recorded a dataset yet, record your first dataset and the SO-100 setup guide come first, and the LeRobot dataset format entry explains what the recorder writes. Background is in vision-language-action models and the flow-matching policy work; the arena entry links every benchmark number to a source.

Sources

Sources

Ready for high-quality robotics data?

AY-Robots connects your robots to skilled operators worldwide.

Get Started