
要点
- On 9 November 2026, Jay Carlson and Ansley Dunn published an NVIDIA Technical Blog tutorial on building a gravity-compensation loop with Holoscan 5, Pinocchio, and a Seeed Studio reBot Arm B601-DM.
- The loop reads joint state at 400 Hz, computes the torques that cancel gravity, and sends commands to the motors. The three operators run on one thread, with queues that keep only the newest sample.
- NVIDIA reports more than a million measured cycles per configuration, excluding the first ten seconds. Latency is counted from entry into the first operator to entry into the last, and does not include sending the motor command.
- With the control partition pinned and isolated, C++ p99.99 latency fell from 86.6 microseconds to 17.2. Python fell from 1,373.5 microseconds to 142.6. The C++ median was 7.1 microseconds pinned, against 93.0 for Python.
- The authors tested the walkthrough on Jetson Orin Nano, Jetson Thor, and x86 machines running Ubuntu 24.04. The figures are NVIDIA's measurements of NVIDIA's own runtime.
On 9 November 2026, NVIDIA's technical blog asked readers to picture guiding a robot arm by hand while its motors supply just enough torque to hold up its own weight. Jay Carlson and Ansley Dunn then show how to build that loop with Holoscan 5, an inverse-dynamics library called Pinocchio, and a Seeed Studio reBot Arm B601-DM. The demo is easy to watch. The reason to write about it is narrower. Gravity compensation, they argue, is a proof of life for two things at once: the physical model of the arm, and the software runtime that has to recompute torques hundreds of times a second without jitter. Their table is about that second question.
[1]What the tutorial builds
Holoscan 5 is NVIDIA's SDK for mixing low-latency control with heavier perception work in one programming model. This tutorial stays on the control path. It splits the job into three operators. JointStateSourceOp wakes every 2.5 milliseconds, which is 400 times a second, and reads the seven motors. PinocchioGravityOp runs when that state arrives, maps the six arm angles into the robot's URDF model, and asks Pinocchio for gravity torques through the Recursive Newton-Euler Algorithm. MotorCommandSinkOp sends each command onward through the motorbridge library, and rejects expired, out-of-order, or out-of-range commands. If no batch finishes for 100 milliseconds, a watchdog tries to disable the motors.
A URDF file is a standard description of a robot's links, joints, and masses. If those numbers are wrong, gravity compensation fails in a way you can feel. The arm sags, or it pushes upward as the pose changes. Position-control demos can hide the same bugs. That is why the authors treat this loop as a first check, even for projects that will not ship gravity compensation in the final product.
The hardware choice matters for the same reason. Many low-cost arms use highly geared hobby servos that are only suited to position control. The reBot Arm uses quasi-direct-drive actuators, which can produce and sense torque quickly enough for this kind of compliant motion. The walkthrough was tested on Jetson Orin Nano, Jetson Thor, and x86 Ubuntu 24.04 systems. A matching C++ implementation lives in the same repository.
[1]What the latency table measures
Holoscan lets the developer put the three operators in one partition, so their callbacks run one after another on a single runtime-owned thread. The connections between them use depth-one queues that drop the oldest sample. The point is to avoid handing work across threads and to avoid acting on stale state.
The authors profiled the unmodified application with NVIDIA Nsight Systems, using built-in markers, and compared pinned and unpinned runs in both C++ and Python. For the pinned runs they reserved physical cores with a temporary Linux cgroup and moved helper and profiler threads aside. They note that interrupts, timers, and some kernel threads can still land on those cores. Each configuration kept more than a million cycles. They discarded the first ten seconds and kept the outliers.
A p99.99 figure means 99.99 percent of cycles were faster than that number, so only about 1 in 10,000 was slower. The table they publish is:
HSDK C++ pinned: p50 7.1 microseconds, p99.99 17.2, p99.999 20.7.
HSDK C++ unpinned: 15.3, 86.6, 133.4.
HSDK Python pinned: 93.0, 142.6, 1,014.0.
HSDK Python unpinned: 113.8, 1,373.5, 3,134.1.
Pinning plus isolation cut p99.99 latency by about 5 times in C++ and about 9.6 times in Python. Latency here stops at the last operator. It does not include the time to transmit the motor command. Both language versions call Pinocchio's C++ solver. The extra Python cost, the authors say, comes from interpreter execution, object allocation, and coordination around the Global Interpreter Lock.
At 400 Hz you have 2,500 microseconds between ticks. Even the slowest Python tail they report, 3,134.1 microseconds, is about one extra tick. The pinned Python tail of 142.6 microseconds at p99.99 looks comfortable on that budget. The pinned C++ tail of 17.2 microseconds leaves far more room for perception or planning to share the machine.
[1]Why gravity compensation is the diagnostic
The authors are not arguing that a weightless arm is the product. They are arguing that a runtime which cannot hold a tight, repeatable control loop will not be a safe place to add cameras, learned policies, or a person's hand. Torque control lets the arm support its own weight and still yield when someone pushes it. That mixture is useful for teaching by demonstration and for working next to people. It also makes timing errors visible as instability rather than as a slightly late waypoint.
Once the basic loop works, the tutorial repository adds kinematics and impedance control as reusable operators, so a learned policy can request a pose while the arm stays compliant. Perception, they say, can come later through Holoscan's vision module and Isaac ROS. The sequence is deliberate: measure the control path before you bury it under larger graphs.
[1]The fair case, and what is missing
The fair case for a Python control path is that many robotics teams already live in Python, and Holoscan will compile the graph and show the plan before the motors are enabled. For perception pipelines that take milliseconds rather than microseconds, the authors themselves say that chasing small allocations on the CPU usually matters less.
The gaps are clear. These are NVIDIA's numbers on NVIDIA's SDK, on one arm, with a profiling setup that isolates cores more carefully than a typical shop floor process list. The published latency excludes command transmission, which is part of the real loop. The Python p99.999, even when pinned, is 1,014 microseconds, which is still a reminder that rare stalls exist. Independent groups have not yet posted their own million-cycle tables on the same arm and the same Holoscan 5 build.
[1]Who should care, and what to watch
Anyone writing a torque loop that a person might hold should care, because the difference between a pinned C++ tail of 17 microseconds and an unpinned Python tail of 1.4 milliseconds is the difference between a loop that waits for physics and a loop that waits for the interpreter. Teams that want to stay in Python should care too: pinning bought them about an order of magnitude on the far tail, but not parity with C++.
Over the next six months, watch whether users of the public tutorial publish their own Nsight summaries on Jetson Thor or Orin Nano, including the motor-command time that this table leaves out. Watch whether later Holoscan examples keep the same partition-and-pin pattern when cameras and learned policies join the graph. Watch whether the Python tail comes down without giving up the language.
[1]Close
The demo makes an arm feel light. The table says why that feeling is fragile. If other labs recover the fivefold and tenfold tail cuts from pinning, then core isolation will look like a required setting for this class of control, not an optional tuning trick. If their tails stay long once command transmission and other processes are included, then the weightless arm will have been a clean laboratory measurement, not yet a factory runtime.
[1]