15-494/694 Cognitive Robotics: Coding Assignment 2

In this assignment you are going to add some more new functionality to Tekkotsu.

Part I: Understanding Button Transitions

Button events don't use a special event class; they are generic events (instances of EventBase) with EventBase::buttonEGID as the generator id.

Tekkotsu's Event Router dispatches events to subscribing behaviors based on three criteria that are applied in this order: (1) the generator ID; (2) the source ID; and (3) the type ID. For button events, depressing a button generates an event whose type id is EventBase::activateETID, while releasing the button generates an event whose type ID is EventBase::deactivateETID.

A call to addListener(...) can specify anywhere from zero to all three of these ID values, but a higher number filter can only be applied if the lower number filters have a value specified. In other words, you can subscribe to button events without specifying a particular source ID, but in that case you cannot filter on the type ID.

Most of the time people are interested in responding to button press events, not to button release events. So in the Tekkotsu statemachine shorthand notation, the transition =B(PlayButOffset)=> gets macroexpanded to >==EventTrans(EventBase::buttonEGID, RobotInfo::PlayButOffset, EventBase::activateETID)==>.

It is natural to expect that =B=> will fire only for button press events, but since the source ID is unspecified there is no simple way to filter for the type ID being EventBase::activateETID. Thus, as long as =B=> macroexpands to an EventTrans, it cannot produce the behavior that people expect.

Your assignment is to extend the EventTrans class to handle the special case where the user specifies a generator ID and a type ID but no source ID. To do this, you will need a new constructor with type signature:

EventTrans(const std::string& name,
           StateNode* destination,
           EventBase::EventGeneratorID_t gid,
           EventBase::EventTypeID_t tid)
Ordinarily, EvenTrans uses addListener to subscribe to exactly the events it needs. However, in this new special case, if we don't care about source ID but do care about type ID, EventTrans must subscribe to all events with the specified generator ID, and then check inside its doEvent() method to see if the type ID matches.

What you should do:

  1. Copy Tekkotsu/Behaviors/Transitions/EventTrans.h to your ~/project directory and rename EventTrans to MyEventTrans.

  2. Add the new constructor as described above. You will need to set some kind of boolean flag to indicate that we are filtering on ETID at runtime rather than relying on addListener to set up the filtering for us.

  3. Modify the logic of doEvent() to handle this new case correctly.

  4. Test your modification using the following sample code:
    #include "MyEventTrans.h"
    
    $setupmachine{
      startnode: SpeechNode("Press a button") >==MyEventTrans(EventBase::buttonEGID, EventBase::activateETID)==>
        SoundNode("ping.wav") =C=> startnode
    }
    
I will take the best solution submitted and use it as the new implementation of EventTrans. I will also modify the state parser's macroexpansion of =B=> to use your extension of EventTrans.

Part II: Converting Between Reference Frames

As discussed in class, a DualCoding::Point object contains both x/y/z coordinates and a reference frame type, which is one of camcentric, egocentric (local space), allocentric (world space), or unspecified.

Sometimes it is necessary to convert a point from one reference frame to another. Here we consider the problem of converting between egocentric and allocentric reference frames. To make this conversion we have to know the agent's location and orientation (heading). Tekkotsu only considers orientation in the x-y plane, and therefore uses an AngTwoPi to encode heading information.

Your task is to implement the following functions:

Point inLocalFrame(const Point& pt, const AgentData& a);
Point inWorldFrame(const Point& pt, const AgentData& a);
If a point is already in the desired reference frame it should simply be returned. If a point's reference frame is unspecified, it should be assumed to be in the desired reference frame, and a new point should be returned with that reference frame. But if a point is in the local frame and we need it to be in the world frame, or vice versa, you will need to perform a coordinate transformation based on the agent's location and heading.

What you should do:

  1. Examine the implementation of Point, which you can find in Tekkotsu/DualCoding/Point.{h,cc}.

  2. Write definitions for inLocalFrame and inWorldFrame.

  3. Write code to test your functions by converting some points and printing the results. You can use theAgent, the built in shape tied to the robot, as your AgentData instance. You can use fmat::rotationZ() to rotate a coordinate vector around the Z axis.

  4. When your behavior starts up, local coordinates and world coordinates will be the same. If you drive the robot around so that it's no longer at the origin and/or has a nonzero heading, then local and world coordinates will no longer be identical, and your program's output should change accordingly.

Hand In

This assignment should be done individually, not in teams.

When you've completed the assignment, email your source files to Dave Touretzky.

Your solution is due on Wednesday, Feb. 10, 2016.


Back to the Cognitive Robotics home page