MCA2 Puma Tutorial Chapter 5

From Mca2
Jump to navigationJump to search

GO BACK HOME

Simulating a complete Puma robot

A main feature of MCA is the possibility to combine individual modules to groups. These groups then act like any other individual module and can also be integrated into other more complex groups. In this chapter we will learn how to integrate our six joint simulation modules into one group, which then simulates a complete puma robot.

Create a group

The script newModule creates empty but useful module definitions. There is an equivalent script for groups, too: new_group.py Just type:

  new_group.py -C projects/mcap_puma PumaJoints

in your MCA main directory.

In the new header file gPumaJoints.h ("g" for "group") the necessary modules have to be included:

  //----------------------------------------------------------------------
  // Project Includes - include with ""
  //----------------------------------------------------------------------
  #include "mJointSimulation.h"

Additionally the IO definitions have to be added. We use a controller input and a sensor output for each included joint module:

    /*!
    Anonymous enumeration type which contains the indices of the
    controller inputs.
    */
    DESCR( static, gPumaJoints, m_ci_description, 4, Natural, cDATA_VECTOR_END_MARKER );
    enum {
        eCI_WAIST,
        eCI_SHOULDER,
        eCI_ELBOW,
        eCI_WRIST_ROT,
        eCI_WRIST_BEND,
        eCI_FLANGE,
        eCI_DIMENSION /*!< Endmarker and Dimension */
    };

    /*!
      Anonymous enumeration type which contains the indices of the
      sensor outputs.
     */
    DESCR( static, gPumaJoints, m_so_description, 4, Natural, cDATA_VECTOR_END_MARKER );
    enum {
        eSO_WAIST,
        eSO_SHOULDER,
        eSO_ELBOW,
        eSO_WRIST_ROT,
        eSO_WRIST_BEND,
        eSO_FLANGE,
        eSO_DIMENSION /*!< Endmarker and Dimension */
    };
 
 //gPumaJoints.h

The functions Control and Sense of groups are already implemented. They call the equivalent functions of the integrated modules in an iterative way. You as user only have to create modules or groups within the constructor and if necessary release used heap memory in the destructor. All modules that are integrated into a group are deleted automatically within the destructor of tModule .

In our case we have to create six instances of the module mJointSimulation in the constructor of gPumaJoints.cpp. Then the IOs of these modules have to be connected to the IOs of our group. AddEdge... commands are used to do this.

gPumaJoints::gPumaJoints( tParent *parent, tDescription name, bool fixit )
        : tGroup( parent, name,
                  eSI_DIMENSION, eSO_DIMENSION, eCI_DIMENSION, eCO_DIMENSION,
                  si_description, so_description, ci_description, co_description )
{
    // create module
    mJointSimulation *waist = new mJointSimulation( this, "Waist" );
    // Add Edges
    AddEdgeDown( this, waist, 1, eCI_WAIST, mJointSimulation::eCI_DESIRED_JOINT_ANGLE );
    AddEdgeUp( waist, this, 1, mJointSimulation::eSO_CURRENT_JOINT_ANGLE, eSO_WAIST );

    mJointSimulation *shoulder = new mJointSimulation( this, "Shoulder" );
    AddEdgeDown( this, shoulder, 1, eCI_SHOULDER, mJointSimulation::eCI_DESIRED_JOINT_ANGLE );
    AddEdgeUp( shoulder, this, 1, mJointSimulation::eSO_CURRENT_JOINT_ANGLE, eSO_SHOULDER );

    mJointSimulation *elbow = new mJointSimulation( this, "Elbow" );
    AddEdgeDown( this, elbow, 1, eCI_ELBOW, mJointSimulation::eCI_DESIRED_JOINT_ANGLE );
    AddEdgeUp( elbow, this, 1, mJointSimulation::eSO_CURRENT_JOINT_ANGLE, eSO_ELBOW );

    mJointSimulation *wrist_rot = new mJointSimulation( this, "Wrist Rot" );
    AddEdgeDown( this, wrist_rot, 1, eCI_WRIST_ROT, mJointSimulation::eCI_DESIRED_JOINT_ANGLE );
    AddEdgeUp( wrist_rot, this, 1, mJointSimulation::eSO_CURRENT_JOINT_ANGLE, eSO_WRIST_ROT );

    mJointSimulation *wrist_bend = new mJointSimulation( this, "Wrist Bend" );
    AddEdgeDown( this, wrist_bend, 1, eCI_WRIST_BEND, mJointSimulation::eCI_DESIRED_JOINT_ANGLE );
    AddEdgeUp( wrist_bend, this, 1, mJointSimulation::eSO_CURRENT_JOINT_ANGLE, eSO_WRIST_BEND );

    mJointSimulation *flange = new mJointSimulation( this, "Flange" );
    AddEdgeDown( this, flange, 1, eCI_FLANGE, mJointSimulation::eCI_DESIRED_JOINT_ANGLE );
    AddEdgeUp( flange, this, 1, mJointSimulation::eSO_CURRENT_JOINT_ANGLE, eSO_FLANGE );

    if ( fixit )
        FixIt();
}

In the above example we use one form of the AddEdge... functions. We define the from and to modules and the number of values that are to be copied. As our modules only have one value on each side, we connect only one. The last two parameters specify the index of the inputs and outputs to be connected.

Tip: Always take care to set the correct number of edges to be connected in the AddEdge... function. Otherwise either data is not transferred or a segfault might occur.

Configuration files

We would need 24 parameters to configure our new program. This is confusing, unclear and very uncomfortable. At this point we learn about the possibility to use configuration files in MCA. We will not use the bellow described Attribute tree but an xml file which was described in the calculator Tutorial. This time our xml file will not define the edges as those will be configured in group gPumaJoints.cpp. But it will give parameters to configure the program. For this we will write a xml file. To understand how an xml file works you could look at the following xml file example:

'''Example xml'''
<?xml version="1.0" encoding="UTF-8"?>
<Config>
 
<camera>
    <simulate>1</simulate>
    <cam_type>2</cam_type>
    <left_pic>data/5_markers_left.bmp</left_pic>
    <right_pic>data/5_markers_right.bmp</right_pic>
    <marker_detection>
      <near_clipping>1.0</near_clipping>
      <far_clipping>1000.0</far_clipping>
    </marker_detection>
  </camera>
</Config>

and then write it yourself with the information from the AttributeTree seen bellow. In the end it should look like this:

<?xml version="1.0" encoding="UTF-8"?>
<Config>
 
<mcap_puma>
    <joints>
    	<Waist>
    		<min>-160</min>
    		<max>160</max>
    		<v>64</v>
       	</Waist>
       	<Shoulder>
    		<min>-255</min>
    		<max>45</max>
    		<v>60</v>
       	</Shoulder>
       	<Elbow>
    		<min>-45</min>
    		<max>225</max>
    		<v>54</v>
       	</Elbow>
       	<Wrist_Rot>
    		<min>-110</min>
    		<max>170</max>
    		<v>60</v>
       	</Wrist_Rot>
       	<Wrist_Bend>
    		<min>-100</min>
    		<max>100</max>
    		<v>40</v>
       	</Wrist_Bend>
       	<Flange>
    		<min>-226</min>
    		<max>226</max>
    		<v>54</v>
       	</Flange>
	</joints>    
</mcap_puma>
</Config>

Warning xml files do not like spacing. Example: Write Wrist_Rot instead of Wrist Rot, otherwise the xml file will not be opened.

If you use the XML config file skip the section with the Attribute Tree and go on with section "Work with config files".

Configuration via Attribute Tree

Configuration files use tAttributeTree to read, store (and save) parameter attributes. An attribute tree is organised like the directory structure of a file system: Every entry has an attribute name and an attribute content. The content may be empty. Moreover every entry may have subentries. When saved to file, subentries are included into { } brackets, or their names include the parent entry name separated by a point:

  {
    example_entry{
      sub_entry: entry content
   }example_entry
   example_entry_2: entry 2 content
   example_entry_2.sub_entry: hello world 
  }

Such configuration files can be easily be created manually with every editor you like.

In our example, we need an AttributeTree file that contains all necessary parameters for our 6 joint simulation modules. Of course there are many possibilities to define them. Here's one (save this as etc/puma.AttributeTree):

  puma{
     joints{
         Waist{
             min:-160
             max:160
             v:64
         }Waist
         Shoulder{
             min:-255
             max:45
             v:60
         }Shoulder
         Elbow{
             min:-45
             max:225
             v:54
         }Elbow
         Wrist_Rot{
             min:-110
             max:170
             v:60
         }Wrist_Rot
         Wrist_Bend{
             min:-100
             max:100
             v:40
         }Wrist_Bend
         Flange{
             min:-266
             max:266
             v:54
         }Flange
     }joints
 }puma

If a program is started with parameter --configfile=etc/puma.AttributeTree, the part reads the file and stores all attributes in an internal structure. As all modules can access the part via their parents (top level parent is always a part) they are all able to ask for attributes. The following example shows how to access and use attributes from a global configuration file.

Work with config files

Resume working here if you chose the xml version

Add this to the constructor of the file gPumaJoints.cpp.

 //----------------------------------------------------------------------
 // class gPumaJoints
 //----------------------------------------------------------------------
 gPumaJoints::gPumaJoints( tParent *parent, tDescription name, bool fixit )
         : tGroup( parent, name,
                   eSI_DIMENSION, eSO_DIMENSION, eCI_DIMENSION, eCO_DIMENSION,
                   si_description, so_description, ci_description, co_description )
 {
     float waist_min, waist_max, waist_v;
     float shoulder_min, shoulder_max, shoulder_v;
     float elbow_min, elbow_max, elbow_v;
     float wrist_rot_min, wrist_rot_max, wrist_rot_v;
     float wrist_bend_min, wrist_bend_max, wrist_bend_v;
     float flange_min, flange_max, flange_v;
 
     tAttributesStruct attributes[] = { tAttributeFloat( "puma.joints.Waist.min", waist_min ),
                                        tAttributeFloat( "puma.joints.Waist.max", waist_max ),
                                        tAttributeFloat( "puma.joints.Waist.v", waist_v ),
                                        tAttributeFloat( "puma.joints.Shoulder.min", shoulder_min ),
                                        tAttributeFloat( "puma.joints.Shoulder.max", shoulder_max ),
                                        tAttributeFloat( "puma.joints.Shoulder.v", shoulder_v ),
                                        tAttributeFloat( "puma.joints.Elbow.min", elbow_min ),
                                        tAttributeFloat( "puma.joints.Elbow.max", elbow_max ),
                                        tAttributeFloat( "puma.joints.Elbow.v", elbow_v ),
                                        tAttributeFloat( "puma.joints.Wrist_Rot.min", wrist_rot_min ),
                                        tAttributeFloat( "puma.joints.Wrist_Rot.max", wrist_rot_max ),
                                        tAttributeFloat( "puma.joints.Wrist_Rot.v", wrist_rot_v ),
                                        tAttributeFloat( "puma.joints.Wrist_Bend.min", wrist_bend_min ),
                                        tAttributeFloat( "puma.joints.Wrist_Bend.max", wrist_bend_max ),
                                        tAttributeFloat( "puma.joints.Wrist_Bend.v", wrist_bend_v ),
                                        tAttributeFloat( "puma.joints.Flange.min", flange_min ),
                                        tAttributeFloat( "puma.joints.Flange.max", flange_max ),
                                        tAttributeFloat( "puma.joints.Flange.v", flange_v ),
                                        tAttributeFieldEnd()
                                      };
 
     if ( !GetAttributes( attributes, 0, true ) )
     {
         ERRORMSG( "%s could not be initialized because of missing attributes!\n", Description() );
         SetStatus( eERROR );
         return ;
     }
 
     // create module
     mJointSimulation *waist = new mJointSimulation( this, "Waist" );
     // Add Edges
     AddEdgeDown( this, waist, 1, eCI_WAIST, mJointSimulation::eCI_DESIRED_JOINT_ANGLE );
     AddEdgeUp( waist, this, 1, mJointSimulation::eSO_CURRENT_JOINT_ANGLE, eSO_WAIST );
     waist->SetParameters( mJointSimulation::ePAR_DIMENSION,
                           mJointSimulation::ePAR_MIN_ANGLE, Deg2Rad( waist_min ),
                           mJointSimulation::ePAR_MAX_ANGLE, Deg2Rad( waist_max ),
                           mJointSimulation::ePAR_ANGULAR_VELOCITY, Deg2Rad( waist_v ) );
 
     mJointSimulation *shoulder = new mJointSimulation( this, "Shoulder" );
     AddEdgeDown( this, shoulder, 1, eCI_SHOULDER, mJointSimulation::eCI_DESIRED_JOINT_ANGLE );
     AddEdgeUp( shoulder, this, 1, mJointSimulation::eSO_CURRENT_JOINT_ANGLE, eSO_SHOULDER );
     shoulder->SetParameters( mJointSimulation::ePAR_DIMENSION,
                              mJointSimulation::ePAR_MIN_ANGLE, Deg2Rad( shoulder_min ),
                              mJointSimulation::ePAR_MAX_ANGLE, Deg2Rad( shoulder_max ),
                              mJointSimulation::ePAR_ANGULAR_VELOCITY, Deg2Rad( shoulder_v ) );
 
     mJointSimulation *elbow = new mJointSimulation( this, "Elbow" );
     AddEdgeDown( this, elbow, 1, eCI_ELBOW, mJointSimulation::eCI_DESIRED_JOINT_ANGLE );
     AddEdgeUp( elbow, this, 1, mJointSimulation::eSO_CURRENT_JOINT_ANGLE, eSO_ELBOW );
     elbow->SetParameters( mJointSimulation::ePAR_DIMENSION,
                           mJointSimulation::ePAR_MIN_ANGLE, Deg2Rad( elbow_min ),
                           mJointSimulation::ePAR_MAX_ANGLE, Deg2Rad( elbow_max ),
                           mJointSimulation::ePAR_ANGULAR_VELOCITY, Deg2Rad( elbow_v ) );
 
     mJointSimulation *wrist_rot = new mJointSimulation( this, "Wrist Rot" );
     AddEdgeDown( this, wrist_rot, 1, eCI_WRIST_ROT, mJointSimulation::eCI_DESIRED_JOINT_ANGLE );
     AddEdgeUp( wrist_rot, this, 1, mJointSimulation::eSO_CURRENT_JOINT_ANGLE, eSO_WRIST_ROT );
     wrist_rot->SetParameters( mJointSimulation::ePAR_DIMENSION,
                               mJointSimulation::ePAR_MIN_ANGLE, Deg2Rad( wrist_rot_min ),
                               mJointSimulation::ePAR_MAX_ANGLE, Deg2Rad( wrist_rot_max ),
                               mJointSimulation::ePAR_ANGULAR_VELOCITY, Deg2Rad( wrist_rot_v ) );
 
     mJointSimulation *wrist_bend = new mJointSimulation( this, "Wrist Bend" );
     AddEdgeDown( this, wrist_bend, 1, eCI_WRIST_BEND, mJointSimulation::eCI_DESIRED_JOINT_ANGLE );
     AddEdgeUp( wrist_bend, this, 1, mJointSimulation::eSO_CURRENT_JOINT_ANGLE, eSO_WRIST_BEND );
     wrist_bend->SetParameters( mJointSimulation::ePAR_DIMENSION,
                                mJointSimulation::ePAR_MIN_ANGLE, Deg2Rad( wrist_bend_min ),
                                mJointSimulation::ePAR_MAX_ANGLE, Deg2Rad( wrist_bend_max ),
                                mJointSimulation::ePAR_ANGULAR_VELOCITY, Deg2Rad( wrist_bend_v ) );
 
 
     mJointSimulation *flange = new mJointSimulation( this, "Flange" );
     AddEdgeDown( this, flange, 1, eCI_FLANGE, mJointSimulation::eCI_DESIRED_JOINT_ANGLE );
     AddEdgeUp( flange, this, 1, mJointSimulation::eSO_CURRENT_JOINT_ANGLE, eSO_FLANGE );
     flange->SetParameters( mJointSimulation::ePAR_DIMENSION,
                            mJointSimulation::ePAR_MIN_ANGLE, Deg2Rad( flange_min ),
                            mJointSimulation::ePAR_MAX_ANGLE, Deg2Rad( flange_max ),
                            mJointSimulation::ePAR_ANGULAR_VELOCITY, Deg2Rad( flange_v ) );
 
     if ( fixit )
         FixIt();
}
 
 // gPumaJoints.cpp

Here you inserted a function Deg2Rad () which will not be defined if the following package is not included on top of gPumaJoints.cpp.:

  #include "mcal_math/OwnMath.h"

Change CMakeLists.txt

There are several other possibilities in order to get attributes from the part.

Of course we need another main program, where our group gPumaJoints is used as top-level module instead of mJointSimulation. We also no longer need the prompt parameters, as we read the values from the configuration file.

So make a new part using the script

new_part.py -C projects/mcap_puma PumaJoints

and edit the generated file src/pPumaJoints.cpp so that a group gPumaJoints is instantiated.

The entry in our CMakeLists.txt file must then be:

INCLUDE (MCADescriptionBuilder)

ICMAKER_SET("mcap_puma" IDE_FOLDER ${MCAP_PUMA_IDE_FOLDER})

ICMAKER_ADD_SOURCES(
  pJointSimulation.cpp
  pPumaJoints.cpp
  mJointSimulation.cpp
  gPumaJoints.cpp
  )

ICMAKER_ADD_HEADERS(
  mJointSimulation.h
  gPumaJoints.h
  )

MCA_GENERATE_DESCRIPTIONS()

ICMAKER_LOCAL_CPPDEFINES(-DCALCULATOR_EXPORT_SYMBOLS)
ICMAKER_GLOBAL_CPPDEFINES(-D_IC_BUILDER_MCA2_CALCULATOR_)
ICMAKER_INCLUDE_DIRECTORIES(
  ${MCAL_KERNEL_INCLUDE_DIRS}
  ${MCAL_GENERAL_INCLUDE_DIRS}
  ${MCAP_PUMA_INCLUDE_DIRS}
  )

ICMAKER_INTERNAL_DEPENDENCIES(
  icl_core
  icl_core_config
  icl_core_logging
  mca2_kernel
  mca2_general
  mca2_math
  )

ICMAKER_BUILD_PROGRAM()

// projects/mcap_puma/src/CMakeLists.txt

After compilation, you can start the new program by running

puma_joints --configfile=projects/puma/etc/puma.AttributeTree

and have a closer look with MCAbrowser to it. When controlling the robot with MCAGUI, you have to reconnect the IOs, as they now have individual descriptions on program side.

< Back=== Forward>