|
|
|
|
|
|
If you did not arrive at this page from the simulation software page or the simulation
research page, you may want to return to it briefly to understand the context for this page. The current page provides more
information about scripting languages and the language for studying robotic or animal locomotor control that we developed.
|
|
|
|
|
|
|
|
Scripting and macro languages are computer programming languages designed facilitate the rapid construction and configuration
of applications. The term 'macro language' is usually reserved for custom-written, highly application-specific languages, like
those found in word processors or spreadsheets. For example, one might use the macro language in a spreadsheet program to
write an algorithm for computing credit risk based on past history. Macro languages provide a way for end-users to write
short algorithms or scripts that manipulate internal application-specific data, and can be found in most large applications.
Whereas macro languages are designed to be run by end-users from within an application, 'scripting' languages are typically
targeted at programmers, and are implemented as stand-alone interpreters. A common definition of a scripting language is a
language intended for rapidly building programs and utilities which make heavy use of external components. For example, if
you had a collection of graphics files that needed to be converted to some other format, you could use a scripting language
to write a short program that searched for all graphic files, and for each one, ran a stand-alone conversion utility on that file.
The distinction between traditional programming languages and some of the more general scripting languages can be hard to discern.
Some of the more general scripting languages, like Perl, provide a fairly complete and flexible programming environment, and some
of the more recent general-purpose programming languages, like Java, provide substantial facilities for easily integrating
external components.
In addition to scripting and macro languages, a growing body of tools is being developed to bring some of the functionality of
scripting languages to standard programming languages. RAD programs provide a higher level of abstraction to languages like C
or Pascal, allowing programmers to assemble third-party pre-built components into complete applications. JavaBeans, an
extension of the Java language, is aimed at providing a similar functionality through a different mechanism; while RAD
tools are specific applications designed to build programs out of component parts, JavaBeans is a formalism that makes
use of built-in language constructs and informal programming paradigms to establish a framework for classes to publish
their interfaces and interact with one another in a safe, reusable manner. JavaBeans provides a mechanism for compliant
Java classes to declare what properties they want to make visible to other classes, and a mechanism for establishing
connections between objects. The intention is to provide a framework whereby conforming objects (JavaBeans) are usable
as plug-and-play components that can be created, configured, and connected together by someone with minimal knowledge of
the underlying class code (the user-programmer should only need to understand the published interfaces).
|
| |
|
|
|
|
|
|
Config++ acts as a glue between component C++ class libraries. In the lingo of scripting languages, making an existing class library
accessible to the Config++ interpreter is called "wrapping" the class library. Wrapping a class involves writing an extra piece of code
that tells the Config++ interpreter how to create and access instances of the class. This represents the back-end of Config++ which is
invisible to the user of the class library.
Config++ differs from most scripting languages in that it imposes a more substantial framework for wrapping objects and variables,
including the specification of value restrictions and unit conversions, stressing the use of hierarchical objects consisting of component
parts and providing a wide variety of mechanisms for accessing class variables and functions. Like JavaBeans, Config++ requires some
extra work when building component libraries, in order that the user of the library need do less.
Once a class or class library has been "wrapped" and registered with the Config++ interpreter, it can easily be configured and connected
to other registered classes using the Config++ language. In this way, Config++ provides a common bridge for connecting heterogeneous
components. Because Config++ libraries are required to specify extended information concerning the valid parameterization of classes
(such as argument ranges, etc.), they are amenable to configuration via graphical drag-and-drop construction tools.
The design of Config++ has been motivated by three main goals. The first goal is to provide a generic configuration language that would
be intuitive and easy to use by application end-users who are not programmers, and still be powerful enough to support the manipulation
of complex, hierarchical data structures by application programmers.
The second goal is to provide a framework for decoupling the interface mechanism from the underlying code of C++ class libraries so
that class libraries can be replaced in a transparent fashion. Every Config++ library consists of two parts, the back-end, which
represents the underlying library code, and the front-end, which describes the interface for constructing and instantiating library
objects. For example, the front-end of the Config++ graphical user interface library consists of a description of the kinds of GUI
components that can be built, their names and parameters, and the manner in which they may be constructed and wired together. This
description does not depend on the underlying code that actually implements the GUI functionality. Hence, it represents an "interface"
as opposed to an "implementation." The actual code that implements the GUI functions is maintained separately; a "wrapper" provides
the bridge from the implementation to the interface. In this fashion, the wrapper acts like a translator between the Config++ language
and the underlying implementation. By writing a new wrapper, or translator, entire libraries may be replaced transparently.
The final goal of Config++ is to provide a collection of common, reusable, platform-independent tools for C++ programmers working with
highly configurable projects, such as simulations. To this end we have been working on a set of libraries, and wrappers for existing
C++ libraries, which provide support for platform independent GUI, data visualization, and file manipulation. From a programmer's
point of view, an advantage of the approach is that all interaction between different class libraries and between class libraries
and the Config++ interpreter is done through intermediate wrapper classes. This promotes encapsulation and portability, ensuring
that underlying class library code is usable independently of the Config++ system and of other Config++ components.
While Config++ is an attempt to provide for C++ a framework similar to JavaBeans, it has a slightly different focus. Where
JavaBeans is a formalism for constructing classes that will be easy for another programmer to reuse and wire together, typically
using a graphical draw-and-drop tool, Config++ is meant to be embedded inside an application, providing an interpreted language
for rapidly reconfiguring application projects. In addition, while JavaBeans is a tool for programmers, Config++ is designed to
be used by application end-users as well as hard-core application programmers.
|
| |
|
|
|
|
|
|
In order to explain the design principles underlying Config++, we describe a robotic simulation tool we have implemented using Config++.
Config++ plays two main roles in the simulation. First, Config++ is used as an embedded front-end to the simulation. It parses
user configuration files and builds/configures robots by making appropriate calls to the underlying robotic library classes. A
substantial amount of work is done while parsing the configuration files because the description of a robot is non-trivial.
These descriptions consist of hierarchical components like arms and legs, each of which may be made up of other components,
like joints, muscles, and sensors. Config++ allows us to use a simple, straightforward syntax for describing these hierarchical
structures, while behind the scenes it instantiates objects, connects them together, infers default settings, registers callbacks,
and so forth. Simple control-flow facilities allow users to write simple scripts to conduct multiple experiments, for example
running a series of trials while varying certain parameters.
Second, the robotic simulation classes were written completely independently of any user-interface code. Config++ was used
to describe generic, platform-independent GUI components and interface them with the robotics classes. Because the user-interface
and simulation code is completely decoupled, either could be replaced without modifying the other. In a similar way, Config++
was used to connect the robotic simulation to libraries for visualizing data and reading/writing data files. Config++ also
allows end-users to incorporate additional 3rd party libraries seamlessly with no modification of the underlying simulation
code. While still in the early stages of development, Config++ also provides facilities for building and connecting components
of a simulation project interactively in a drag-and-drop fashion, and for saving and loading projects.
While most of Config++'s syntax is borrowed from C/C++, it differs most obviously in its syntax for instantiating objects,
which resembles the hierarchical, object-oriented data description languages and file-formats used in 3d graphics programs
such as OpenInventor, VRML, and Povray. Almost everything in a Config++ "program" consists of object instantiations (creating
new objects from existing object classes):
Robot r2d2
{
height=1 meter;
color=blue;
Leg left
{
length=.1 meter;
mass=1 kilogram;
}
Leg right
{
length=.1 meter;
mass=2 kilograms;
}
}
The example above may seem unnecessarily verbose. However, Config++ code is intentionally verbose; it is meant to be
intuitive and easily readable. When parsing this example, Config++ might make C++ calls similar to those shown below:
Robot *r2d2=new Robot(1,BLUE);
Leg *left=new Leg(.1,1);
r2d2-attachleg(left);
Leg *right=new Leg(.1,2);
r2d2-attachleg(right);
Of course, while one might claim that the Config++ code version looks nicer, it's hardly a compelling
example. The main advantage of using the Config++ language, however, is not in describing single objects, but rather in wiring objects
together. For example, we might add the following Config++ code to our robot configuration file in order to give the user interactive
control over the height of the robot using a standard draggable slider control.
Window
{
SliderControl
{
range=[0,100];
Wire to r2d2.height;
}
}
We can also add the following Config++ code in order to give the user a way to display a plot of how the robot's battery power is
holding up over time:
Plot
{
Axis x
Wire from simulation.time;
Axis y
Wire from r2d2.batterypower;
}
In the example above, the underlying C++ code for instantiating the slider and graphical plot, and connecting them to the simulation
data would depend critically on the underlying library being used to provide the graphical user-interface. It would also depend on the
data structures being used to store and retrieve values from the simulation classes and could be quite involved.
Furthermore, without some form of intermediate macro/scripting language, if you wanted to set up multiple simulations with slightly
different configurations, each version of the simulation would have to be maintained separately and compiled independently before execution.
Because the need for multiple configurations in such programs is so common, nearly all large applications end up providing support for a
custom configuration file of some sort. One of the motivations for Config++ was to provide a robust configuration file which could be
used across a range of applications.
Applications rarely support the modification of things like the user-interface of the application, but because implementation specific
code is encapsulated within Config++ libraries, decoupling application specific code from things like the user-interface library produces
a highly-customizable application whose GUI components could be replaced without modifying the underlying application or Config++ code.
|
| |
|
|
|
|
|
|
We have implemented some basic libraries for use with the Config++ interpreter, described here:
GUI Library
A GUI toolkit is a perfect example of a collection of hierarchical objects that can be assembled and wired together in a variety of ways.
We have taken an existing GUI toolkit, FLTK, and provided Config++ wrappers that allow Config++ projects to provide a full-featured
user-interface, and methods for easily wiring user interface components to other components. One strategy of our approach to writing
the Config++ wrapper interface was to provide routines for guessing parameters and providing default actions. This demonstrates one
of the roles that the Config++ interpreter can play, providing a simpler, more intuitive interface to a set of existing classes.
FLTK provides the underlying user interface routines and runs on Windows and Unix platforms. One of the advantages of using
Config++ is that it would be straightforward to replace the underlying GUI toolkit (FLTK) with another. Because the Config++
interface specification for the GUI components would be unchanged, no applications written for Config++ would have to be changed.
Datafile Library
A set of classes and associated wrappers have been provide that support input and output to data files of various formats.
For example, a simulation application can easily be configured to save arbitrary information, at arbitrary intervals, to a
datafile in a variety of formats (such as Matlab). Data files are augmented with information about the quantities being saved in
order to make them easier to understand. Data files can also be automatically read by the Config++ interpreter, and routed to
arbitrary components.
OpenGL Graphic Object (GROB) Library
We have designed a set of classes and Config++ wrappers for providing access to a set of 3d primitives, as well as a basic
camera class for displaying and manipulating these primitives. Some basic rendering options are provided to support the rapid
design of simple applications, such as the switching between multiple detail levels. The 3d shape classes are implemented
using OpenGL, a graphics standard freely available on most platforms. The 3d classes are used by our robotics library, and
could easily be added to other applications.
OpenGL Plotting Library
This is a simple OpenGL-based plotting library for generating online xyplots, barplots, 3d scatterplots, etc. Like all
Config++ libraries, the intention is to provide a set of components that can receive data easily from any other Config++
components. Further, these components are highly-configurable while still supporting the extensive use of default and
best-guess values for rapid design. OpenGL has been used to insure platform independence.
Simulation/Robotic Library
We have written, and described in the document doc04_biobot.txt, a set of classes for conducting discrete-time simulations,
and for simulating the dynamics of multibody robotic mechanisms. These classes can be used independently from Config++,
but Config++ has been used extensively to provide a way of building and configuring robots. Robot structures are naturally
hierarchical, consisting of arms and legs that are themselves built from segments, and can be connected in various ways.
Config++ makes it easy to reconfigure and reuse components in the construction of robots and experiments.
|
| |
|
|