This tutorial shows the generation of a new mode for the RiSE platform.

RiSE Hello World Tutorial

RHexLib terminology

Supervisor
the code that runs on the robot's CPU. It handles all the behaviors of the robot while provide a remote interface.
Operator
the code that runs on a remote computer that provides the user with a GUI interface to the robot.
Modes
the remote interface on the supervisor.
Modules
tasks that are scheduled and executed by the module manager. These are the heart of the behavior code:

Further documentation can be found at:
Mode supervisor: ~/RoboDevel/Libraries/RHexLib/doc/html/a00351.html
Module manager: ~/RoboDevel/Libraries/RHexLib/doc/html/a00352.html
Hardware interface: ~/RoboDevel/Libraries/RHexLib/doc/html/a00349.html

The task of this tutorial is to create a “Hello World” mode, which will wave the robot's front right leg.

Creating the mode interface

The mode interface is defined in a text file:
~/RoboDevel/RiSE/RobotCode/Behaviors/modes/HelloWorldMode.mod
HelloWorldMode.mod

This file defines the interface between the Operator and the Supervisor via the blackboard. Most have the following format:

mode = ModeName ModePrefix;

enum Status {
   ModePrefix_SUCCESS,  ModePrefix_INV_CMD,  ModePrefix_INV_PARAM
};

struct commands {
  ...
};

struct params {
  int param_set;
  ...  
};

struct state {
  Status status;       
  ...
};

For this example, we will use the following:

ModeName = HelloWorldMode
ModePrefix = HWM
commands:
   int wave;
no parameters
state:
   int iswaving;

Then, we use modeBuilder to generate the .hh and .acc files. Execute the following commands:

cd ~/RoboDevel/RiSE/RobotCode/Behaviors/modes
~/RoboDevel/Libraries/RHexLib/tools/modeBuilder -h HelloWorldMode.mod
~/RoboDevel/Libraries/RHexLib/tools/modeBuilder -a HelloWorldMode.mod
mv HelloWorldModeStructs.hh ../include/
mv HelloWorldMode.acc ../../../Demo/Operator/

Writing the module code

The module code consists of a header and source file:
~/RoboDevel/RiSE/RobotCode/Behaviors/modules/HelloWorldModule.cc
~/RoboDevel/RiSE/RobotCode/Behaviors/include/modules/HelloWorldModule.hh
HelloWorldModule.cc HelloWorldModule.hh

Highlights from the header code

Module classes are derived rom the class Module:

class HelloWorldModule: public Module {

More information about the Module class can be found at:
~/RoboDevel/Libraries/RHexLib/doc/html/a00183.html

They must provide implementations for these five methods:

  void init( void );
  void uninit( void ) {};
  void activate( void );
  void deactivate( void );
  void update( void );

Since there is nothing to do during deinitialization, uninit is defined in the header file.

The header file also declares and defines public methods accessed by the mode. Since these methods are defined directly in the header, they should be short and brief. At most, they should raise a flag and pass any important parameters.

Pseudo code:

  wave( ) {
    Set private flags indicating an incoming wave command
  };

  iswaving( ) {
    Return boolean indicating current wave state
  };

The code would not build if the header only declared a function prototype (even if the function is defined in the source code):

  void wave();
Returns the following error during make:
/home/rise/RoboDevel/RiSE/lib/librisemodes.a(HelloWorldMode.o)(.text+0x281): In function `HelloWorldMode::updateDatabase()':
/home/rise/RoboDevel/RiSE/RobotCode/Behaviors/modes/HelloWorldMode.cc:98: undefined reference to `HelloWorldModule::wave()'

Highlights from the source code

Each module is responsible for providing gains to the motor hardware. The gains that are used in the Hello World module are taken from the RiSE Stand module and are intended for use in on the physical RiSE platform, not the simulator (To Do: change so that the gains are read from an .rc file)

The init and uninit methods are called when the robot's supervisor starts and ends. The activate and deactive methods are called when the mode grabs the module (i.e. when the operator switches to “Hello World” mode.) Thus, these are good times to gain and release control of the motor hardware.

Psuedo-code:

  HelloWorldModule::activate( ) {
    Iterate through each hip:
      Set each hip's target position to the current position
      Enable hip
  }

  HelloWorldModule::deactivate( ) {
    Iterate through each hip:
      Disable hip
  }

The LinearProfiler class provides a means to quickly produce a linear trajectory, the following is taken from the header file:

   // The LinearProfiler class implements a piecewise linear function of
   // time. Its parameters are a, b, start and stop. The function
   // getVal() returns a if time<start and b if time>start and the point
   // on the line from (start,a) to (stop,b) corresponding to the current
   // time otherwise. Used for trajectory generation.

LinearProfiler::setup(double start, double stop, double a, double b);
LinearProfiler::getVal(double now, float *posout, float *velout );

Using this class, a quick piecewise linear trajectory can be generated. The wave trajectory is hard-coded and is comprised of the following steps:

0: Move to up position
1: Wave from up to down
2: Wave from down to up
3: Wave from up to down
4: Wave from down to up
5: End of wave, stop motor

Write code for mode

The mode code also consists of a header and source file:
~/RoboDevel/RiSE/RobotCode/Behaviors/modes/HelloWorldMode.cc
~/RoboDevel/RiSE/RobotCode/Behaviors/include/modes/HelloWorldMode.hh
HelloWorldMode.cc HelloWorldMode.hh

Highlights from the header code

The header will declare an empty class such that the HelloWorldMode can have a class pointer to HelloWorldModule without exposing the module to all the other programs that include HelloWorldMode.hh

class HelloWorldModule;

The modes are derived from the RHexMode class:

HelloWorldMode( void ): RHexMode( "helloworldmode" ) {};

More information about the RHexMode can be found at:
~/RoboDevel/Libraries/RHexLib/doc/html/a00203.html

The mode must provide implementations for the following members:

  bool entryPostureCheck( void ) { return true; };
  void initialize( void );
  void start( void );
  void stop( void );
  bool pause( void ) { return false; };
  void resume( void ) {};
  void kill( void ) {};
  void updateDatabase( void );
  void setParamValue( param_id_t index, double val ) {};
  double getParamValue( param_id_t index ) { return 0.0; };

For simplicity, we assume that the robot is always in a good configuration to enter our mode, so entryPostureCheck always returns true. We also assume that the mode cannot be paused, and therefore pause returns false and resume does nothing. Also, we have no parameters to set for this mode, so setParamValue does nothing and the getParamValue returns zero for all indices. Also, if kill is ever called, it's followed by a stop call, so we really don't need to do anything for it.

Highlights from the source code

The initialization routine will register the blackboard variables. These constants are defined in the HelloWorldModeStructs.hh and were auto-generated by the ModeBuilder.

    commands_id =
    registerParam( HWM_COMMANDS_ENTRY_NAME, HWM_COMMANDS_ENTRY_DESC,
                   &cmd_init);
    ...

Upon the starting the module, the mode will grab the HelloWorld module, which will declare to anyother programs that HelloWorld is in use:

  MMGrabModule(hwm, NULL);

Then the mode will initialize the current commands:

  cmd_init.wave = 0;
  setParam( cur_commands_id, & cmd_init );

The command indicates an incoming command from the operator. The cur_command is the current state of the mode. Therefore, if command and cur_command are different, this indicates that the operator has issued a command. The mode should inform the module, and then update cur_command to indicate that the command has been handled:

  if (commands.wave != cur_commands.wave) {
    switch (commands.wave) {
    ...
    case 1:
      ...
      hwm->wave();
      break;
    }      
    cur_commands.wave = commands.wave;
    command_reset = true;
  }
  ...
  if ( command_reset )
    setParam( cur_commands_id, &cur_commands);

Update Makefiles

Open ~/RoboDevel/RiSE/RobotCode/Behaviors/modules/Makefile
Add HelloWorldModule.cc to the end of the SOURCES list. (Use a backslash to continue lines.)

Open ~/RoboDevel/RiSE/RobotCode/Behaviors/modes/Makefile
Add HelloWorldMode to the end of the MODES list.

Update RiSEModes.cfg

Open ~/RoboDevel/RiSE/Demo/Operator/RiSEModes.cfg and append the following code to the end of the file:

  { string modename = "helloworldmode";
    string label = "Hello World";
    int    button = 12;
    string title = "Hello World";
    string accessor = "HelloWorldMode.acc";
  }

The button parameter indicates the joystick button combo needed to enter the mode:

Shoulder buttons
R1 = 0 R2 = 6 R1 + R2 = 12
Primary buttons
X = 3 Y = 4 Z = 5
A = 0 B = 1 C = 2

So, “button = 12” cooresponds to R1 + R2 + A

Update supervisor.cc

Open ~/RoboDevel/RiSE/Demo/Supervisor/supervisor.cc

Follow the layout set by the other modules and add referernces to the HelloWorld module based on the examples of the other modules:

First include the HelloWorldModule and HelloWorldMode header files:

#include "modules/HelloWorldModule.hh"
#include "modes/HelloWorldMode.hh"

Add constants to define the period, offset and order of the HelloWorldModule:

#define HELLOWORLD_PERIOD 1
#define HELLOWORLD_OFFSET 0
#define HELLOWORLD_ORDER (BEHAVIORAL_CONTROLLERS + 4000)

The period indicates how often the module should be called and the offset indicates the phase. Period = 1 tells the supervisor to run the module at every cycle. The offset does not matter since the period is one. (If you had two modules that you wanted to alternate, so that only one ran during any given cycle, then you would set Period = 2 for both, and one would have Offset = 0 and the other have Offset = 1.)

Then add the code to intialize the module:

    addModule( new HelloWorldModule, HELLOWORLD_PERIOD,
               HELLOWORLD_OFFSET, HELLOWORLD_ORDER );

Finally add the code to intialize the mode:

    addMode( new HelloWorldMode );

Place these snipets of code at the end of their respective groups.

Build the project

From the ~/RoboDevel directory, build the project:

make rise

Creating the GUI

Run the operator without any arguments:

cd ~/RoboDevel/RiSE/Operator
./start.sh

Add a new panel:

Then, click on Panels -> Panel Edit
Select “Hello World” and then click “Edit Template”

Create the “Wave” button

Hold-Right Click the panel, select “Add Widget” and release.
Select “Button” and click OK.
Resize and position the button.
Double Click the button to open an editor window
Under the GUI tab, change the label to “Wave – A”
Under the Widget tab, change value to “1” and click the “On Change” button
Click the Joystick button and under Button select Buttons -> A.
Under the Accessor tab, click browse, select “command.wave” and click OK
click “Dismiss”

Create the wave indicator

Hold-Right Click the panel, select “Add Widget” and release.
Select “Light Button” and click OK.
Resize and position the button.
Double Click the button to open an editor window
Under the GUI tab, change the label to blank
Under the Widget tab, change value to “1”
Under the Accessor tab, click browse, select “state.iswaving” and click OK
click “Dismiss”

Click “Save Template” and save as ~/RoboDevel/RiSE/Operator/RiSEPanel.cfg

Congratulations, you have added a new mode to the RiSE platform.

-- SalomonTrujillo - 19 Jul 2005

 
This site is powered by the TWiki collaboration platformCopyright &© by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback