Skip to content

Slow ATOM firmware — 50 Hz is the maximum rate for reading joint angles #53

@ferrolho

Description

@ferrolho

Hi! I have a myCobot 280 (for Arduino) and I am using an FT232 to control the robot directly from my laptop.

I was benchmarking the function get_angles() and was surprised to see that it takes at least 20 ms for it to return.

I updated the ATOM firmware from v6.5 to v7.2 and that did make a small difference: the mean time has decreased by about 3 ms.

I spent some time digging further and implementing my own serial implementation of the protocol to understand if this could be an issue with the pymycobot serial implementation, but my investigation seems to suggest that the delay is not due to some issue with the Python implementation. Instead, it seems that the delay has to do with the processing happening on the ATOM — I measured the elapsed time between writing the raw frame request (for getting the joint angles) and reading the raw bytes containing the response frame; this confirmed that the delay is coming from the ATOM firmware implementation.

Is it possible to optimise the implementation of the ATOM firmware?

20 ms feels quite slow, and I don't think we're hitting the bottleneck of the serial communication for the 1M baudrate. 20 ms for getting the joint angles means that, right now, a simple for loop to read only the joint positions of the robot (not even sending any other commands for controlling the arm) would be limited to a max frequency of about 50 Hz...

Thanks in advance!

Benchmark Results for get_angles()

ATOM v6.5

  • Samples: 100
  • Mean: 23.54 ± 1.82 ms
  • Range: 21.33 - 27.26 ms
Raw timings (ms): ['23.59', '26.56', '25.96', '22.09', '21.83', '26.55', '21.51', '21.83', '25.88', '24.95', '24.74', '24.87', '25.52', '26.69', '21.86', '22.06', '22.83', '22.63', '25.22', '21.52', '22.05', '24.54', '24.94', '26.53', '22.02', '22.51', '25.97', '21.87', '24.12', '23.79', '27.26', '22.00', '21.82', '26.55', '21.88', '21.96', '24.03', '22.91', '23.13', '24.05', '21.81', '21.88', '25.49', '21.89', '25.46', '22.10', '21.74', '26.21', '22.06', '24.56', '22.48', '21.48', '25.83', '23.15', '22.18', '23.36', '27.06', '21.33', '22.06', '22.42', '25.02', '21.94', '23.17', '22.85', '23.44', '22.07', '22.25', '22.31', '23.15', '26.47', '21.64', '22.05', '23.93', '21.82', '21.98', '22.44', '24.31', '21.64', '21.83', '21.83', '22.03', '25.45', '26.68', '24.36', '23.44', '21.96', '22.19', '21.89', '26.18', '22.09', '21.38', '25.66', '26.96', '24.29', '25.60', '22.93', '27.00', '21.55', '26.55', '22.72']

ATOM v7.2

  • Samples: 100
  • Mean: 20.62 ± 0.60 ms
  • Range: 19.81 - 24.30 ms
Raw timings (ms): ['20.81', '20.99', '20.94', '21.33', '21.16', '20.50', '21.19', '20.78', '20.08', '20.03', '20.10', '20.04', '20.29', '20.26', '20.56', '20.65', '19.98', '20.07', '20.27', '20.14', '20.16', '19.90', '20.03', '20.06', '20.18', '20.15', '19.92', '20.57', '19.88', '20.38', '21.52', '20.95', '20.69', '21.06', '21.48', '21.55', '20.73', '21.11', '21.32', '21.25', '21.53', '21.40', '21.08', '20.69', '21.52', '20.58', '20.51', '20.79', '20.43', '21.80', '20.26', '21.09', '20.86', '21.13', '20.26', '20.99', '21.41', '20.88', '24.30', '20.41', '20.83', '20.56', '20.55', '20.09', '21.23', '20.71', '20.96', '21.00', '20.21', '20.81', '19.97', '20.47', '20.06', '20.47', '20.58', '20.54', '20.33', '20.08', '20.83', '20.02', '20.98', '20.52', '20.63', '20.17', '20.49', '19.93', '20.33', '20.54', '19.96', '20.34', '19.95', '20.12', '19.81', '20.09', '20.13', '20.45', '20.69', '20.36', '20.61', '20.54']

Source code used for benchmarking the function

import statistics
import time

from pymycobot import MyCobot280

mycobot = MyCobot280("/dev/tty.usbserial-B00033ZX", baudrate=1000000)

def benchmark_get_angles(mycobot, n_samples=10, warmup=3):
    """Run timing benchmark for get_angles() with statistics."""
    timings = []

    # Warmup phase (discard initial unstable measurements)
    for _ in range(warmup):
        mycobot.get_angles()
        time.sleep(0.1)  # Short pause between calls

    # Timed samples
    for _ in range(n_samples):
        start = time.perf_counter()
        angles = mycobot.get_angles()  # Actual measurement we care about
        elapsed = time.perf_counter() - start
        timings.append(elapsed * 1000)  # Convert to milliseconds
        time.sleep(0.05)  # Prevent serial buffer overflow

    # Calculate statistics
    mean = statistics.mean(timings)
    stdev = statistics.stdev(timings) if n_samples > 1 else 0

    # GitHub-friendly output
    print(f"## MyCobot get_angles() Benchmark Results")
    print(f"- Samples: {n_samples}")
    print(f"- Mean: {mean:.2f} ± {stdev:.2f} ms")
    print(f"- Range: {min(timings):.2f} - {max(timings):.2f} ms\n")
    print("Raw timings (ms):", [f"{t:.2f}" for t in timings])


benchmark_get_angles(mycobot, n_samples=100)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions