In this usage, a software architecture is directly analogous to the blueprint of a house. Unfortunately, robotic system development is not house building.
A tendency in this approach is to overreach, i.e., to assume that "my architecture is the architecture for robotics" when in fact it turns out that "my architecture is the approach for the type of robots I want to build".
This reflects a tension in any of these "grand theory" approaches to robot architectures between specificity and efficacy. The more general a grand theory is, the less useful it tends to be in building any particular robot. In general, the narrower the class of tasks that the architecture addresses, the more useful the architecture is within that domain.
Inevitably, algorithm developers find they must move their code from one grand architecture to another for any of a variety of reasons ranging from the inadequacy of the grand architecture for a specific task or simply the funding agent demands it. The toolkits used by any grand architecture are designed to be used pervasively under the assumption that no other architecture exists, and thus it can be somewhat difficult to move just an algorithm from one grand architecture to another.
So far, this has not panned out in robotics. Robotic modules and architectures are in serious flux as research is continually done on them, and the range of tasks that robots are given, from bumbling about in beige corridors at 1 mph to ripping along cross country trails at 50 mph, is so wide that it has been extremely difficult to come up with a standard set of components, infrastructure, or data flow that is widely applicable or accepted. Thus robotic systems have tended to be one-off productions, or, at best, productions where code reuse is limited to within the group doing the work. As the field of robotics matures, the hope is that we will have a stable, tested set of components and architectures (not just one grand architecture) that will span the useful set of robotic tasks and allow some amount of code reuse between people developing real robots in the field, but this hope is currently far from reality.
The problem in most robotic system development efforts is that in order to develop a module, most module developers are forced to learn about a particular system architecture that is specific to the current system being built. If they ignore this, it becomes very difficult for them to get the results they need. In addition, the system integrators typically have to learn a large amount about the individual modules in order to help the module developers integrate their modules into a coherent system. Usually, the system integrators have to take a version of the module and modify it for the current system needs. This blurring of roles locks the system into a particular architectural approach, as changing the system architecture means retraining everyone and rewriting everything involved in the system. Thus, mistakes made early in a project tend to be essentially uncorrectable later in the project developement without a huge investment of resources.
The core of the ModUtils package is to recognize this division of labor and first and foremost formalize the relationship between module developers and system integrators. We give module developers a common framework for module developement and utilities to support the standalone development of algorithms within modules, such as standard mechanisms for logging and replaying data, accessing parameters, and dealing with time when playing back data. The module developers view the rest of the system through what we call "reconfigurable interface." These reconfigurable interfaces are simply abstract C++ classes which cover adapters that can integrate the module into a particular system architecture. Behind the reconfigurable interfaces we provide communications toolkits that let system integrators connect the various modules together through their interfaces within a particular infrastructure. The goal is to allow system integrators to change almost anything about the integrated architecture without that change being visible to the module developers. Thus, we allow module developers to work on what they are experts in: algorithm development, while allowing system integrators to work on what they are experts in: building systems.
For example, we can look an an obstacle detection module. This module takes input from three laser scanners, so it uses three LaserScanner reconfigurable interfaces and needs to know the pose of the vehicle, so it uses a VehicleState reconfigurable interface. It needs to know what parameters to use for its algorithm, so it reads them from a ConfigurationSource. It creates a map of the obstacles and outputs through a CostMap reconfigurable interface. The ObstacleDetector algorithm cannot tell what is on the other side of these interfaces, it is almost completely isolated from the architecture in which it resides.
The reconfigurable interfaces for an obstacle detection module
The vast majority of the time in this module's life cycle will be spent with the module developer running in a "stand-alone" architecture. In this "architecture" we only run one process: the obstacle detector. The parameters are read in from a file, and the various data sources are read in from files using reconfigurable interfaces implemented with the underlying ModUtils data and parameter reading tools. The CostMap output can be a dummy, in which case the ObstacleDetector module is usually configured to run with a private development GUI, or, in some cases, the CostMap can cover another GUI which shows the output before we do the system integration. No other architectureal support processes need to be run, thus vastly simplifying the debugging of this one algorithm.
The module developer architecture
As the module matures, the system integrator will start moving into the larger, integrated architecture. To do this, the integrator will first reconfigure the module's configuration source to read its parameters from a central database via TCP/IP sockets instead of reading from a local file, and then will reconfigure the other data sources and outputs to use the shared memory based communications to talk with the rest of the system. The integrator does not have to change, or even recompile or relink, the obstacle detector module in order to do this. When bugs are found in the integrated system and isolated to this module, the system integrator can reconfigure the necessary interfaces to log data to disk, and can then hand off the data to the module developer to test and fix with in the predictable, canned data-based "stand-alone" architecture.
The integrated architecture
Reconfigurable interfaces are not rocket science. Actually, good module developers have always done something equivalent in order to protect themselves from the realities of system integration. The problem is that different module developers use different methods with differing degrees of rigor, some use
#ifdef's to reconfigure at compile time, some use conditional statements to reconfigure at run time, and some use libraries with API's to reconfigure at link time. Some reconfigure based on the command line, some based on a configuration file (of some arbitrary syntax). In a large system, it can get very difficult for a system integrator to keep track of exactly how to configure each module. Reconfigurable interfaces simply represent a consistent, run-time changeable, means of isolating the modules from the architectures in which they reside.
In essense, a reconfigurable interface is an abstract API represented by an abstract C++ class definition. Then there is a suite of interface instances available at runtime. The particular interface instance is chosen and parameterized by a specification string. Once an interface is chosen and instantiated, the user simply invokes the top level, abstract methods without regard for the underlying implementation, i.e., whether the methods resolve to looking up data in files, fetching data across a network, or talking directly to a physical sensor or actuator.
The hope is to allow module developers the freedom to achieve algorithmic breakthroughs while giving system integrators the freedom to change the system as the real requirements of the task and capabilities of the modules become evident. Robot systems need to be in a constant prototype/evaluate cycle, and the ModUtils toolkit is there to support development and change in both algorithm and architecture while providing a consistent framework and nexus of communication for both algorithm and system developers.