I was tired of ROS2, so I rewrote it in Rust

Github: HORUS

Every once in a while, I hear developers complain about how working with ROS2 frustrates them, or students who find the barriers to understanding ROS2 too high. Undoubtedly, ROS2 was originally designed in the lab for the prototyping phase.

However, being widely adopted has turned its design into a messy blob that is not only "thick" - and not in a good way - but also too tangled. Therefore, I came up with the design for HORUS -Hybrid Optimized Robotics Unified System.

At first, I was just experimenting to see whether building a system that respects both the versions and dependencies of low-level packages like drivers, and high-level packages like ML/robotics algorithms would be that hard - unlike in ROS2. Surprisingly, with current modern technology, we can do a lot to make this vision come true. ROS2 users are always looking to avoid "reinventing the wheel" by trying to pick whatever exists in that tangled web and sticking it into their project. This workflow was good, at least in the old days. But now we have AI and modern frameworks/packages, yet developers are still struggling to make them work with ROS2.

My vision is to create a robotics framework that is "thick" as a whole (we can't avoid this - the tools, drivers, etc. make it impossible to be slim and fit everyone's needs), but modular by choice. I mean that developers, similar to ROS2, can still integrate their packages, but in a way that won't require opening at least 10 tabs. The design makes parts swappable - it's like building with Lego blocks, but with easy integration. You don't like this IPC communication mechanism? Swap it with a different IPC framework by editing 1 configuration line, and it won't break. You don't like these environment settings or how upgrading a single package breaks everything else? HORUS solves this.

HORUS aims to provide developers an interface where they can focus on writing algorithms and logic, not on setting up their environments and solving configuration issues. HORUS is designed to be both hybrid and unified. For example:

You can run horus run app.py driver.rs, and Python and Rust will communicate with each other seamlessly, as they all use shared memory for IPC. If a package is missing, the HORUS manager detects it and auto-installs a version that fits your current dependencies.

By design, every HORUS project can be a virtual environment if you want it to be. You can use packages already installed on your machine and link them to your project, or install new ones exclusively in your project. This solves dependency hell and the "works on my machine" problem. We also have horus env freeze, which freezes your environment and provides you an ID that other developers can use with horus env restore - similar to requirements.txt in Python.

I know what you're thinking: don't we already have these features elsewhere? Why not just install them and integrate them into ROS2, like virtual environments or multiple compilers? That's where the unified part of HORUS comes in. Robotics as a whole should be cohesive for the most part. Continuously splitting things apart and then piecing them back together will just create another ROS2. Therefore, by original design, the ecosystem should easily adopt new components and make them native. HORUS has a registry - a database of sorts - that aims to grow by gaining more HORUS packages. This makes future development easier by making every HORUS project part of the ecosystem.

Given all that, a modern robotics framework should have better performance, be high-level, easily integrate with AI/ML, and be user-friendly - even for students breaking into the field of robotics. HORUS is trying to achieve all of this, but without growth in the ecosystem, HORUS would be just another forgotten project.

By the way, you can try it out with the documentation to see the vision and goals: HORUS Documentation

10 Likes

It's sort of refreshing to read a long introduction of a new library/package that isn't ai-slop-ified. No emojis or emdashes or overly emphatic language.

The framework looks good too, but what's the answer to the main advantage of ROS2 (and no doubt the reason people sludge through it): the network effects. Everything is on ROS2. maybe HORUS would be good for a robotics company building from scratch and wanting to finely control every part of it. But who really wants to do that? Is there any plans for something like wrapping ROS packages into the HORUS system?

1 Like

It will be just a matter of time, to re-export standard, common ROS2 packages into HORUS, it will be a better approach than wrapping it, so HORUS won't be too dependent. There will be ROS2 bridge system for HORUS in the future, which letting ROS2 developers to make their nodes, launch files, config files into HORUS with ease. For standardized packages, it is better to create new ones for HORUS marketplace/registry, which contain more modern approaches/frameworks to be used for modern robotics application, while current ROS2 projects can be bridged to HORUS in the future via ROS2 bridge feature.

How complex would be to integrate external Rust package for inverse kinematics? I obviously think should I try to port rs-opw-kinematics.

It will not be integrated strictly like you think, horus environment still use Cargo.toml and horus run still depends on Cargo(building compiler from scratch using rustc will get more trouble in the future for high level parts), but the Cargo.toml file will be hidden, it will be treated as artifacts(kind of lock structures, only for horus run to call cargo to compile). So you can just horus pkg install and horus manager will smart check for its name in crates.io to install inside its local env, or you can decide to have it installed as global, which is a dedicated env of horus in that machine, instead of each horus project like local. For crates.io, and pip packages, horus is wrapping those but the design is to have a dedicated place to put them together, as cache, so .py and .rs can communicate with ease. The packages that need to be integrated or build from scratch might be ros2 packages which are not published elsewhere other than ros2 ecosystem, community.

I only wish you success, but be careful with this. When I — a hardcore C++ programmer who has explored every corner of cmake — started working with ROS 1 back then, one major source of frustration was that although it kind of used cmake, it also introduced tools like catkin and later colcon. These were supposed to simplify things, but instead, they created extra problems when I tried to integrate my existing CUDA application that also relied on cmake.

On top of that, syntax highlighting, code completion, and debugging didn’t work properly in my IDE, which had no idea what this “catkin” was. Things have improved since then — CLion is now a good tool for ROS — but we risk repeating the same cycle.

Rust still isn’t well supported in ROS; the integration is complex, and there are many abandoned crates lying around. If we want to take that niche seriously, I’d build everything around cargo instead.

2 Likes

Thank you for the advice! Really appreciate it! I will take a notice, as I'm also working on C++ API and its build/compile system, and at this point, I wouldn't confidently say that C++ will be integrated, I will make sure all edge cases and tests be passing on UX perspectives, if HORUS gonna break just by adding layers, that beats the purpose of creating it. The only reason I think of adding C++ are for the old drivers packages that seems not to have other languages being provided by the vendors, other than C and C++. Nevertheless, I will make sure HORUS maintain its user-friendly characteristics, even if we have to trade.

At first glance, your project looks to have a similar motivation as
copper-rs. Although, copper seems to pursue more ambitious goals (e.g., ROS2/DDS interfacing).

I think it would help me (and possibly others) appreciate your effort even more, if you pinpoint the differences with Copper as you see them.

2 Likes

Well, our main goal is to facing ROS2 and exceed it as well. I can list the differences between Copper and HORUS:

  • API: copper using tasks and graph ticking, we kept the the transferable API structure from ROS2 for developers to felt easily welcomed, meaning nodes,topics, scheduler is treated like launch file.

  • Copper, is an SDK, meaning they will have more of tool kits, using Zenoh and Iceoryx2 as IPC, (Their default is single process only, which might not be ideal in lots of sophisticated robotics application or prototyping), and they are likely using existing infrastructure. HORUS is a framework, similar to ROS2 originally, and its kind of built from ground up, has its own dedicated structure of shared memory, IPC runtime/ multi-process system(Hub for MPMC, Link for 1P1C) if other tools fit in the design, we integrate them as hybrid. If not, we would rather build from scratch again.

  • We are obsessed with real time more, you can look at the benchmark, normally data types in an IPC system kept around under 100ns, and we are trying to pursue that for robotics messages data types. We are willing to trade WAN communicating for swam robots to pursue the shared memory of a single robot more, meaning we will likely keeping this structure of shared memory as main IPC, WAN communication will be a hybrid approach, meaning we will separate them as communication mechanism for any nodes developers want, not integrate fully like ROS2. This is to focus on dedicating to low-level shared memory for real time control. The goal is to keep this current nodes and pub/sub design and still be applicable for real-time critical robots(in these areas, ROS2 are just prototyping phases, not production)

  • Ecosystem Motivation: We aim to improve by gaining in ecosystem as well, meaning a dedicated marketplace, where all HORUS project unified, so this will prevent the scenarios of nowadays where ROS2 packages are everywhere and don't have their own discover channel other than the forum, community, discussion.

  • We would focus on developer experience more, means we are willing to trade everything to pursue that, if later on, any robots need parts that will break this principle, we would rather add as outer package to be used, but not integrated strictly in our system. You can look at the API, they are very idiomatic.

There are lots of more details or listings I can do to differentiate, there are features that Copper is definitely a better choice today, compared to us - we are alpha and under development. But sum up, Copper positioned themself as like set of tools to build applications, like game engine to games, while we aim to be everything of infrastructure and ecosystem(beside the OS of course) the games rely on to keep existing - system design, low-level runtime, linking packages, development tooling, deployment infrastructure, marketplace, multi-language.Our vision is become the standard system that can be extendable from low-level(hardware, driver, kernel) to high level(algorithms, AI/ML) that every robots stand on, which means outside the scope of just middleware.

3 Likes

Hi neos, HORUS is a very interesting robotics middleware. I’d like to ask: is there a fundamental difference between HORUS and dora-rs (GitHub - dora-rs/dora: DORA (Dataflow-Oriented Robotic Architecture) is middleware designed to streamline and simplify the creation of AI-based robotic applications. It offers low latency, composable, and distributed dataflow capabilities. Applications are modeled as directed graphs, also referred to as pipelines.)? I noticed that both of these middlewares utilize shared memory for communication.

The fundamental might be architectural difference. DORA uses apache arrow on top of shm, this is a good approach for AI/ML robotics, as apache arrows are very well supported in ML development. While HORUS is a comprehensive runtime for robotics, our high-levels looks more like ROS2 and the low-levels is customized, we dont use iceoryx2, zenoh but totally build from scratch, which make us tremendously faster and can adapt to all scenarios or messages as we push toward the hybrid approach. We have support on tensor and cuda for AI/ML applications as well, though not battle-tested. DORA will be a good case for sensors component or AI/ML robots that dont require strict real time or determinism in motor, actuator, which is our main performance. An easy comparison would be DORA is a high-level framework from starting point, where let you have performant latency by design, while HORUS is more comprehensively customizable, we can exposed your application(based on your choice) to low level like touching the kernel or high level like loading for AI model and let boths communicate.

Sounds great. I get the idea that you’ve intentionally avoided advanced frameworks to deliver optimal performance for specific use cases.

I think a ROS2 bridge is critical to getting initial wider adoption. There will always be some driver that only exists in ROS, and the next great algorithm will most likely be implemented by a university student using ROS. Not having to reinvent the wheel aligns well with the stated developer friendliness goal.

Currently, I'm also working on the bridge for ros2. So it is just a matter of time.

1 Like

For real real time applications like motor or actuator controls, you wouldn't be running this on Linux. What are your thoughts about either running on or integrating with MCU's?

There are lots of standard embedded frameworks and MCU firmwares that currently dominate, temporary approach might be adding another layer to communicate with them, like industry standard, or we might take the ROS solution, which is creating microROS or another version of horus library that can take the embedded path for runtime, or we would refactor horus to work on both, which will sacrifice lots of developing cases on linux or for undergrads to learn, ofc. Well, there are lots of approaches, but I will seek out a way to yield both performance and good DX, without trading the architectural cost of a distributed system.

As far as I understand, a bridge to ROS2 will be essential. ROS2 is simply too powerful in its current functionality for this. Even if you can write some nodes in Horus and link it to ROS2, basic functionalities are simply not available. A few simple examples:

  • Firmware linking (as with microros)
  • Navigation (as with nav2)
  • Hardware interface control (as with controll2)
  • Launch files
  • Support for multiple programming languages (ros2 supports C, C++ and Python as first-class citizens and Rust with rclrs as second-class citizens)

Horus looks promising. To be fair, all ROS2 competitors looked promising until they were given a certain amount of time. To be fair, it must also be said that robotics engineers are often not full-blooded programmers or computer scientists and therefore tend to fall back on programming languages such as Python or, when in doubt, simply take something that already exists and adapt it to their own application, and ros2 can simply deliver that. That's why it's popular. Or to put it another way: Ros2 is big because it's big.

Well, ros2 using DDS, a native bridge to ros2, meaning using LAN, Zenoh is a good candidate for this approach, but the trade off is performance, might be 5us-20us( Icant tell exactly), its not local until using the shm route of ros2, and then the maintainability will be a big burden, as bridging too much submodules of ros2. So there is a trade off here if want to reconnect the ecosystem of ros2, it might work well as a Frankenstein product, but beyound that, it will be very ugly, rather having pure ros2 or pure HORUS. The time for HORUS is mostly designing time, I can really tell why ROS2 is designed like the way it is, as currently, trying to scale up the breadth reach of HORUS, it is very compelling to push everything into a unified blob, so the development process will become easy, but will entangled for later use, hence broken every where like ROS2, if a single thing is broken. It's like if you want the endgoal of the product to have a good flow for every aspects it is being used, you have to visualize the internal architecture and connections between parts, that will somehow surviving in the long-term for development. That's why ROS is very hard to refactor, they would rather create ROS2 or may be ROS3, then just modify the current version, as everything are so entangled and vulnerable.

One of the main improvements of Ros2 over Ros1 is that it is not monolithic. A monolithic approach is cool when everyone develops exclusively for your system (such as Linux) or has to be compatible with it. This is not the case with ROS2. One of the core ideas of ros2 is that it is possible to swap core components at any time. Anyone can communicate with ROS2 via DDS. As long as you are in the same DDS domain as ROS2 and design the messages to behave like ROS2 messages, it works. There are also plenty of examples, even in ROS2, of programmes that deviate from the concepts of ROS2 but can still be recognised as subscribers or publishers within the ROS2 world. For example, you can write programmes in Python that use rclpy as library to implement publishers or subscribers, but do not rely on the Ros2 system clock for their loops. In most cases, the node cannot be started directly via ros2 run simple_ros_run package_name node_name and is only recognised at runtime. There are certain parts of ROS2 that function monolithically because they are not subject to the same restrictions as ROS2 itself. For example, launch files are not exclusively implementable through the official launch library. The command ros2 launch path/to/launchfile at it's core is only an advanced python3 path/to/lauchfile.py command. The behavior of launch files itself is replicable through simple shell scripts.

  • ros2 control is monolithic because it uses a standalone memory management concept.
  • ros2 nav2 is monolithic because everything is regulated via nav2 plugins.

For microros, there are now interfaces in Python, even if these are extremely experimental.

ROS is designed to be modular, this is for the purpose of robotics researches and prototypes. DDS is industry standard for autonomous exploration, but for most users, if they want to dive deep to optimize their applications, they have to dig a little deeper for a specific submodule or route in ROS, if not, they will stick to that gold path in ROS. I mean the architecture in ROS is understandable for a product, it is just not for developers' perspective. That is why I find it would need to take more time to visualize a flow that also is a good case for both developers and developments, means modular by choice, not just by design. ROS architecture is actually very well designed, if the goal is to check all requirements for designing a robot. Developers dont hate ROS, they hate working with ROS. I mean, it originally came out of the Stanford Lab, so the developer experience never get a box to check, hence yield only the practical requirements of "just make it work, polish later". That is why in HORUS, I design the local machine and LAN to be seperated, but users have to acknowledge both to work, most undergards dont actually need to learn DDS or QoS, until being thrown to industry, which makes it easier if they just need to know this common API workflow, and then explore later if they need more for their robots, not to bundle everything inside a golden API that will work for every things, this is like learning python basic and external libraries later for their jobs.