Developing Well-Designed Packages for Robot Operating System (ROS), Part I
Posted on 04:21, April 25th, 2010 by Billy McCafferty
In previous posts, I discussed general patterns of message-based systems along with basic guidance on developing components for such systems. While these posts may have provided basic information about their respective topics, there’s a clear difference between theory and implementation. (The former is easier to fake.) Accordingly, it’s time to get our hands dirty and provide concrete guidance on developing packages for Robot Operating System (ROS) using proven messaging-system and other developmental design patterns.
This post is the first in a number of posts which walks the reader through nearly every facet of developing a package for ROS. Primary interests of this series will focus on:
A ROS package is a complete application which happens to send and receive messages via messaging middleware (e.g., ROS) to help it complete its job. This isn’t too different from an application which simply retrieves and saves data to a database. Accordingly, the development of the package deserves the same design considerations that a stand-alone application would should receive. This series of blogs will walk the reader through the development of a package, taking into such considerations, appropriately. This will be a six part series include:
At the end of this series, the reader should feel comfortable with the concepts behind designing and developing an extensible and maintainable ROS package adhering to proven design patterns and solid coding practices.
A Ladar Reporting Package
Before delving straight into implementation, it’s important to establish the context of the project to assist in guiding architectural and developmental decisions. Accordingly, the package that will be developed will be a ladar reporting package. The requirements of the package are simple enough:
Our first stop will be in examining the overall architecture of the package in order to conceptualize how the end product will be layered and developed.
Part I: Planning the Package Architecture
When developing any application, it’s important to strike the appropriate balance between extensibility and maintainability. We’ve all seen as many “over-architected” solutions as those that did not receive a single thought toward design. Such applications usually meet their demise in the middle of the night when a wearied developer throws up their hands in debugging exhaustion before starting The (infamous) Rewrite. Accordingly, when designing a package, “just enough” prefactoring should take place to architect a solution that will smoothly evolve to meet the package’s requirements without over-complicating the solution. For the task at hand, when architecting a ROS package, there are a number of minimal architectural guidelines which should be adhered to which will lead to a extensible messaging component while being maintainable and easy to understand; a few guiding principles are as follows:
At first glance, this may appear to be a lot more than just “minimal prefactoring.” But after more than a dozen years of professional development, and many painful lessons along the way, I can assure you that this is a solid approach to architecting your package. With that said, I certainly encourage you and your team to discuss what architectural approach is most appropriate for the task at hand – and the capabilities of the team – before agreeing upon a particular approach.
The best way to further discuss how the architectural guidelines will be implemented in the package, is to review a UML package diagram expressing all of the layers and the direction of dependencies, accordingly:
Keep in mind that this is not a diagram for the overall message-based system; this represents how each individual ROS package is architected. Accordingly, in the above UML package diagram, each box represents a separate library encapsulating a distinct functional concern. The arrows represent the direction of dependencies; e.g., application_services depends on core. Let us now review each layer in more detail, beginning from the bottom up.
As described previously, the domain layer encapsulates the core capabilities of the package. For instance, if this were a package providing SLAM capabilities, then this layer would contain the associated algorithms and logic. In addition to domain logic, the core library contains interfaces for services which depend on external resources, such as message endpoints and (if applicable) data repositories. Having interfaces in the core library allow both core and the application services layers to communicate with services while remaining loosely coupled to the implementation details of those services.
As described previously, the application services layer acts as the “task manager” of the application, delegating execution responsibilities to the domain layer and services. Note that the application services layer, similar to core, has no direct dependency on the service implementation classes, e.g., the message endpoints. It only has knowledge of the service interfaces for remaining loosely coupled and for facilitating unit testing.
Arguably, in smaller packages and applications, an application services may not add much value and simply complicate an otherwise simpler design. With that said, care should be taken at the beginning of a project to fully consider the pros and cons of including an application services layer before deciding to discard its use, accordingly.
If the package were to retrieve and save data to a database or other external data source, data repositories would encapsulate the details of communicating with the external data source. Note that the data repositories implement the repository interfaces defined in core.
This library contains the concrete implementations of the message endpoints. The message endpoints are the only classes which actually know how to publish, subscribe, and otherwise communicate with the ROS messaging middleware. Note that the message endpoints implement the endpoint interfaces defined in core.
This layer contains the user interface for interacting with the package, if needed.
Finally, this library contains all of the unit tests for testing all layers of the ROS package.
This is our package architecture in a nutshell. In the next post, we’ll begin developing each layer from Core on up to the UI, starting with the overall package structure itself.
© 2011-2014 Codai, Inc. All Rights Reserved