Adding Behavior Functionality

This document will walk you through the creation of your first interactive Behavior. Behaviors are high level programs which direct the robot to complete some task, comparable to an application on a PC.

Prerequisites

  1. You should have already completed the "Writing your First Behavior" guide, which will set up the minimal behavior shell which is common to all Tekkotsu behaviors.

  2. If you're new to C++, or just rusty, there is a C++ review available.

Adding Functionality

This behavior will light up all of the LEDs whenever a button is pressed.

If your robot does not have LEDs or buttons, you won't be able to run this tutorial as-is, but follow along and try using a different MotionCommand subclass or EventGenerator instead.

  1. First, we need to know whenever a button is pressed. If your robot does not have buttons, try using textmsgEGID below instead of buttonEGID. (If so, you can generate a TextMsgEvent by typing ''!msg your message here' in the ControllerGUI “Send Input” box.)

    SampleBehavior.h
    #ifndef INCLUDED_SampleBehavior_h_
    #define INCLUDED_SampleBehavior_h_
    #include "Behaviors/BehaviorBase.h"

    class SampleBehavior : public BehaviorBase {
    public:
    SampleBehavior() : BehaviorBase("SampleBehavior") {}

    virtual void DoStart() {
    // subscribe to all button events
    erouter->addListener(this, EventBase::buttonEGID);
    }

    // Subscribed events will be sent here:
    virtual void processEvent(const EventBase& event) {
    std::cout << "Received: " << event.getDescription() << std::endl;
    }
    };
    #endif // INCLUDED_SampleBehavior_h_

    You can try running the behavior with these modifications—you should see a description of each event printed to the console (the terminal where you launched Tekkotsu, or port 59000 on the Aibo) as you push buttons.

    You will actually see at least two two events for each button press, one “activate” event when the button is depressed, and a “deactivate” event when it is released. Pressure sensitive buttons may also see “status” events as the pressure reading is updated.

  2. Now we need a way to control the LEDs.  We do this with an LED MotionCommand (specifically, LedMC), which provides some handy functions for LED special effects. For instance, instead of the set command below, you may wish to try cycle, or if you are using textmsgEGID instead of buttonEGID, then use flash.

    SampleBehavior.h
    #ifndef INCLUDED_SampleBehavior_h_
    #define INCLUDED_SampleBehavior_h_
    #include "Behaviors/BehaviorBase.h"
    #include "Events/EventRouter.h"
    #include "Motion/MotionPtr.h"
    #include "Motion/LedMC.h"

    class SampleBehavior : public BehaviorBase {
    protected:
    // This is a smart pointer which creates the MC and serializes access:
    MotionPtr<LedMC> leds;

    public:
    SampleBehavior() : BehaviorBase("SampleBehavior"),
    leds() // initializer for leds
    // see MotionPtr docs for more constructor usage

    {}

    virtual void DoStart() {
    // subscribe to all button events
    erouter->addListener(this, EventBase::buttonEGID);
    // handoff to MotionManager to make it 'active'
    addMotion(leds);
    }

    // Subscribed events will be sent here:
    virtual void processEvent(const EventBase& event) {
    std::cout << "Received: " << event.getDescription() << std::endl;
    // set brightness of all LEDs to magnitude of event
    leds->set(AllLEDMask, event.getMagnitude());
    }
    };
    #endif // INCLUDED_SampleBehavior_h_

    The MotionPtr class will automatically create the specified type of motion command when it is first used. In this case, since we used an empty constructor, that will be at the addMotion() call. The motion will stay alive as long as the behavior is active, and will be automatically removed by the BehaviorBase superclass on exit. If you want to remove it before then, you can call removeMotion(leds).

    The motion command is stored in a shared memory region, which is why your behavior must use a pointer instead of directly referencing the motion command structure. The shared memory storage is needed so motion commands can be accessed by both behaviors in the Main process and by the MotionManager which polls for joint angles in the Motion process. Accesses via MotionPtr will also ensure mutual exclusion, so that the MotionManager does not poll joint angles while you are in the middle of modifying a motion command.

    The set command will control the brightness of the LEDs specified by the bitmask in the first argument. You can find the symbolic names for these bitmasks in your robot's RobotInfo header file. For example, the Chiara is described by Shared/ChiaraInfo.h.

    Each motion command will have its own variety of methods, depending on the task it intends to perform. For example, the PostureMC has setOutputCmd(offset, value), which can assign values to any “output” (joint, LED, or other) on the robot. The available outputs are also listed in the Info header file.

  3. Just for style, let's check the event we've been sent is really a button event. You would need something like this if you subscribe to more than one kind of event:

    SampleBehavior.h
    virtual void processEvent(const EventBase& event) {
    // to be more general, let's check that it's the right event first:
    if(event.getGeneratorID()==EventBase::buttonEGID) {
    std::cout << "Received: " << event.getDescription() << std::endl;
    // set brightness of all LEDs to magnitude of event
    leds->set(AllLEDMask, event.getMagnitude());
    } else {
    // should never happen
    cout << "Bad Event:" << event.getName() << endl;
    }
    }
  4. All done! Now you have done everything you need to with SampleBehavior. It should look like this. When you're running, don't forget to turn off the emergency stop mode, otherwise this will override what your behavior is trying to do.

Further exercises

  1. Explore some of the other events, such as timer events (timerEGID), or when a brightly colored object is seen (visObjEGID). You can get current sensor values via WorldState (globally instantiated as state), which is you can check for new readings whenever sensorEGID events are posted.

  2. Trigger other MotionCommands.  See the MotionCommand documentation for a list of subclasses, including walking, tail wagging, keyframing (MotionSequence), and more.

  3. Complex behaviors can become unwieldy. Try breaking them into specific actions and link them as a state machine. See tutorial here, and a special shorthand notation to make this even easier.

  4. Write your own MotionCommand!