MCA2 Puma Tutorial Chapter 4

From Mca2
Jump to: navigation, search

GO BACK HOME


Contents

Using Parameters

A part is the interface to the external environment for modules and groups. This also includes command line parameter input. There are many standard parameters in order to configure the program behaviour, but the programmer is also able to define application specific parameters. The standard command line parameters are explained in section 3.3 of the programmers guide .

In this chapter we learn how to use command line parameters inside a part (section 4.1 and 4.2). These parameters are then forwarded to the module which is executed inside our part (section 4.3). In section 4.4 it is explained what the default method parameters of the module constructor mean.

Note that it is important to distinguish between the different types of parameters: First of all we have the method parameters as usually used in C++. Additionally MCA provides internal part parameters and internal module parameters which can be used to adjust the behaviour to a specific configuration.

Define program parameters

Program parameters can be easily defined using the tPartParameter struct. We also recommend to use enumerations as for all array definitions in MCA. The following example defines parameters for setting name, min and max angle values and angular velocity for our example program pJointSimulation.cpp. Add the following lines just before void initPartDescription() in the file pJointSimulation.cpp:

enum {
  eOPT_NAME,
  eOPT_MIN_ANGLE,
  eOPT_MAX_ANGLE,
  eOPT_ANGULAR_VELOCITY,
  eOPT_DIMENSION
};

tPartParameter parameter[eOPT_DIMENSION+1] = {
 {"name:", "set the module name", "/joint_simulation2/name"},
 {"min:", "set the minimum joint angle", "/joint_simulation2/min"  },
 {"max:", "set the maximum joint angle", "/joint_simulation2/max"  },
 {"v:", "set the angular velocity", "/joint_simulation2/v"       },
 {0,0,0}
};

// pJointSimulation.cpp

Our part uses these parameter definitions if we add an additional entry into the function initPartDescription():

void initPartDescription()
{
  SetProgramDescription("\n This Program simulates a single joint.\n");
  SetProgramParameter(parameter);
}

Save the changes and compile everything.

Check and use program parameters

We defined the parameters and our program will accept parameter settings on startup. (Just call joint_simulation2 --name=hello and the program will accept it.)

But now we want to check whether the user set a parameter on startup or not. And of course we want to read the specified values and use it. A part has two simple functions that provide this functionality: ParamSet(index) returns true if the user specified a value for the parameter with given index. ParamOpt returns a pointer to a character (string) that contains the specified values. The following example shows how to check and read the parameters within the startup function of our main program pJointSimulation2.cpp:

int startup(tPart* part)
{

  // Create the module
  mJointSimulation *joint = new mJointSimulation(part);

  float min = 0;
  float max = 2 * M_PI;
  float v = 0.1;
  icl_core::tString name = "nothing";

  if (icl_core::config::Get<icl_core::tString>("/mcap_puma/name", name))
  {
    LOGGING_INFO_C(icl_core::logging::Default, pJointSimulation, "Name set to '" << name << "'" << icl_core::logging::Endl);
  }

  if (icl_core::config::Get<float>("/mcap_puma/min", min))
  {
    LOGGING_INFO_C(icl_core::logging::Default, pJointSimulation, "Min joint angle set to '" << min << "'" << icl_core::logging::Endl);
  }

  if (icl_core::config::Get<float>("/mcap_puma/max", max))
  {
    LOGGING_INFO_C(icl_core::logging::Default, pJointSimulation, "Max joint angle set to '" << max << "'" << icl_core::logging::Endl);
  }

  if (icl_core::config::Get<float>("/mcap_puma/v", v))
  {
    LOGGING_INFO_C(icl_core::logging::Default, pJointSimulation, "Angular velocity set to '" << v << "'" << icl_core::logging::Endl);
  }

  // Here we can use the parameters
  // when creating mJointSimulation

  // Execute as usual
  return part->Execute(tTime().FromMSec(30));
}

// pJointSimulation.cpp

Setting module parameters

In the previous section we checked and read program parameters. As already mentioned in the comment there, we have to create our module. But this time we want to use the new parameters. How to use the name (given by the command line parameter) is explained in this section, we just have to use it as parameter of our constructor.

The internal parameters of a module can bet set using the function SetParameters. As it is defined using ellipses, it first needs the number of values the user wants to set. If you want to set all of them, you can use the enum value ePAR_DIMENSION. Then you have to specify pairs of indices and values for each parameter that is to be set. Again, we recommend to use the enumeration values as index. The values' variables have to be of the same kind as the corresponding parameters are defined (bool,int,float,string). Here's how we do it for our example (pJointSimulation.cpp):

// Create the module
mJointSimulation *joint = new mJointSimulation(part, name);
// Set module parameter values
joint->SetParameters(mJointSimulation::ePAR_DIMENSION,
                     mJointSimulation::ePAR_MIN_ANGLE, min,
                     mJointSimulation::ePAR_MAX_ANGLE, max,
                     mJointSimulation::ePAR_ANGULAR_VELOCITY, v);

// pJointSimulation.cpp

The order of the startup is crucial here. Ofcourse the code has to have the definition first, then it has to read in the parameters last it should assign the new parameters. The order explicitly should look like this:

int startup(tPart* part)
{
  float min = 0;
  float max = 2 * M_PI;
  float v = 0.1;
  icl_core::tString name = "nothing";

  if (icl_core::config::Get<icl_core::tString>("/mcap_puma/name", name))
  {
    LOGGING_INFO_C(icl_core::logging::Default, pJointSimulation, "Name set to '" << name << "'" <<icl_core::logging::Endl);
  }

  if (icl_core::config::Get<float>("/mcap_puma/min", min))
  {
    LOGGING_INFO_C(icl_core::logging::Default, pJointSimulation, "Min joint angle set to '" << min << "'" << icl_core::logging::Endl);
  }

  if (icl_core::config::Get<float>("/mcap_puma/max", max))
  {
    LOGGING_INFO_C(icl_core::logging::Default, pJointSimulation, "Max joint angle set to '" << max << "'" << icl_core::logging::Endl);
  }

  if (icl_core::config::Get<float>("/mcap_puma/v", v))
  {
    LOGGING_INFO_C(icl_core::logging::Default, pJointSimulation, "Angular velocity set to '" << v << "'" << icl_core::logging::Endl);
  }

  // Create the module
    mJointSimulation *joint = new mJointSimulation(part, name);

  // Here we can use the parameters
  // when creating mJointSimulation
  // Set module parameter values
      joint->SetParameters(mJointSimulation::ePAR_DIMENSION,
                           mJointSimulation::ePAR_MIN_ANGLE, min,
                           mJointSimulation::ePAR_MAX_ANGLE, max,
                           mJointSimulation::ePAR_ANGULAR_VELOCITY, v);
  // Execute as usual
  return part->Execute(tTime().FromMSec(30));
}

// pJointSimulation.cpp

After compilation the mcap_puma example now accepts our new parameters (start the following in a shell):

mcap_puma --name=waist --min=0 --max=5.585 --v=1.117 &
mcap_puma --name=shoulder --min=0 --max=5.236 --v=1.047 &
mcap_puma --name=elbow --min=0 --max=4.712 --v=0.942 &
mcap_puma --name=wrist_rot --min=0 --max=5.236 --v=1.047 &
mcap_puma --name=wrist_bend --min=0 --max=3.491 --v=0.698 &
mcap_puma --name=flange --min=0 --max=4.712 --v=0.942 &

Start MCAbrowser and look at the differences: Every module has an individual name and the internal parameters are individually set to the specified values.

It is getting better but it is not good enough: The same problems remain: From mcagui's point of view the IOs did not change and it is difficult to distinguish between them. Moreover we have to start all parts individually and write the parameters again and again. To make things easier MCA offers mechanisms to arrange several modules in one group which is then executed in only one part.

Tip: Another advantage of executing only one part is that there is a fixed synchronisation mechanism inside a part which is not the case between parts.

The usage of groups is explained in section 5.

Defaults vs. individuals

Before we move on to combining our modules in a group we want to learn something about the constructor of our modules. Probably you already asked yourself about the standard parameters of modules, which are at least:

tParent *parent
Every module has a parent. Parents can be modules , groups, task containers or parts. Only the top level module has a part as parent as in our examples. Next examples will show how groups act as parent.
tDescription *name
The MCAbrowser is able to show all modules, groups and parts graphically. The user distinguishes between different modules by their names. If a module is integrated at least twice, it is better to name them in different ways.
name is an optional parameter. If no name or 0 is given the name of the class is used, as we did it in all our examples so far.
bool fixit
This parameter is also optional and its default is true. Another value (false) is only necessary if someone wants to derive from a module or group. Then he/she usually expands or changes the IO descriptions. The standard constructors call the FixIt function if fixit is true, which creates the IOs and tells the parent of the module, that the module itself is ready considering its IOs.

If your module needs additional constructor parameters, we recommend to include them between parent and name. Moreover we recommend not to define constructor parameters in order to define default values for internal module parameters. There is another common way to define these values. This possibility is shown in the next section.


< Back=== Forward>

Personal tools