Difference between revisions of "Architecture Tutorial: Retrieve laser scans"

From The BABEL Development Site
(Auxiliary code)
(Calling getObservations)
 
(6 intermediate revisions by 2 users not shown)
Line 4: Line 4:
  
 
The interface of this module is described in [[Architecture:Specification_of_BABEL_Interfaces#Common_Sensory_Interface| this section]] and this tutorial explains how to write a client module to query the latest laser scans from ''BS_RangeSensors''.
 
The interface of this module is described in [[Architecture:Specification_of_BABEL_Interfaces#Common_Sensory_Interface| this section]] and this tutorial explains how to write a client module to query the latest laser scans from ''BS_RangeSensors''.
 +
 +
 +
'''Notice that this tutorial only covers the specific case of writing the codification of the client module in the C/C++ language.'''
 +
 +
 +
<br>
  
 
== Steps ==
 
== Steps ==
Line 9: Line 15:
 
=== Calling ''getObservations'' ===
 
=== Calling ''getObservations'' ===
  
At the point in your module where you need to request the laser scans, this service from BS_RangeSensors must be invoked:
+
At the point in your module where you need to request the laser scans, the service "getObservations" from "BS_RangeSensors" must be invoked. This is the definition of that service, just to know how it looks like:
 +
 
 +
<center>
 +
[[Image:tut-scans_service.jpg]]
 +
</center>
 +
 
 +
 
 +
To call that service, '''you must use the aracne-atom "request-synchronous&blocking"'''. It can be inserted in your code from the BABEL_MD menu.
 +
A dialog will appear asking you which module and service you want to call. Write this information by hand or by using the button "From ICE..." as in the image:
 +
 
 +
<center>
 +
[[Image:tut-scans_wizard1.jpg]]
 +
</center>
 +
 
 +
Accept and a wizard will appear asking you for the arguments of the service. Fill it out as in the figure:
 +
 
 +
<center>
 +
[[Image:tut-scans_wizard2.jpg]]
 +
</center>
 +
 
 +
 
 +
Now, this aracne atom must have some preparatory C++ code '''before''' it, and some C++ code '''after''' it to process the results.
 +
 
 +
You can use the following fragments of code, self-explained in its comments.
 +
'''Pay attention to''':
 +
* The label "YOUR CODE HERE" for the place where to process the laser scan.
 +
* The point "ARACNE_ATOM_HERE" at which the aracne atom "request-synchronous&blocking" must be inserted.
  
<cpp>
 
::getObservations(
 
out SeqOfBytes SF,
 
out boolean error
 
)
 
</cpp>
 
  
For doing so, use the following fragment of code, explained in the comments.
 
Pay attention to the label "YOUR CODE HERE" for the place where to process the laser scan.
 
  
Also, note that the macro #JMS-INCLUDE()#, if not inserted through the BABEL menu will NOT automatically add the dependency to your module, so it is recommended to either add the dependency on "BS_RangeSensors::getObservations" manually (first BABEL_MD tab, "Deps" button) or to insert the JMS macro from the menu and fill the parameters by hand (see the correct order of the parameters in the code below).
 
  
  
Line 35: Line 58:
 
bool err_var;                              // BABEL communication error?
 
bool err_var;                              // BABEL communication error?
  
#JMS-INCLUDE(INC_request-synchronous&blocking,"getObservations","BS_RangeSensors","getObservations","REMOTE_BS_RangeSensors_var","SF,sensorError","err_var","0","INF","","","")#
+
/**** ARACNE_ATOM_HERE  ****/
  
// Error? Propagate it:
+
// Communications error? Propagate it:
 
if (err_var)
 
if (err_var)
 
{
 
{
std::cerr << "[OnNewRangeData] *** ERROR querying the module *** " << std::endl;
+
// communication error
 
}
 
}
 +
else
 +
{
 +
// service requested ok
  
 +
CObservation2DRangeScanPtr laserScan; // A smart-pointer to the laser scan (read below)
  
CObservation2DRangeScanPtr laserScan; // A smart-pointer to the laser scan (read below)
+
if (!sensorError)
 
 
if (!err_var && !sensorError)
 
{
 
try
 
 
{
 
{
// De-serialize the object:
+
try
mrpt::utils::CSerializablePtr obj = SeqOfBytes2MRPTObject(SF);
+
{
 +
// De-serialize the object:
 +
mrpt::utils::CSerializablePtr obj = SeqOfBytes2MRPTObject(SF);
  
// Assure it's a valid observation:
+
// Assure it's a valid observation (if not, the "catch" block will take control):
ASSERT_(obj)
+
ASSERT_(obj)
ASSERT_( IS_CLASS(obj,CSensoryFrame) )
+
ASSERT_( IS_CLASS(obj,CSensoryFrame) )
  
CSensoryFramePtr theSF = CSensoryFramePtr(obj);
+
CSensoryFramePtr theSF = CSensoryFramePtr(obj);
  
laserScan = theSF->getObservationByClass<CObservation2DRangeScan>(); // Get the first scan, if any.
+
laserScan = theSF->getObservationByClass<CObservation2DRangeScan>(); // Get the first scan, if any.
}
+
}
catch(std::exception &e)
+
catch(std::exception &e)
{
+
{
std::cerr << "Exception parsing binary object: " << std::endl << e.what() << std::endl;
+
// Process ERROR, message in e.what()
 +
}
 
}
 
}
}
 
  
// Free memory of the Sequence
+
// Free memory of the Sequence
if (!err_var)
 
{
 
 
delete SF;
 
delete SF;
 
SF = NULL;
 
SF = NULL;
}
 
  
  
// Now, we have the latest scan laser in the variable "laserScan", which  
+
// Now, we have the latest scan laser in the variable "laserScan", which  
//  is a smart pointer to a mrpt::slam::CObservation2DRangeScan
+
//  is a smart pointer to a mrpt::slam::CObservation2DRangeScan
// -------------------------------------------------------------------------
+
// -------------------------------------------------------------------------
if (laserScan)
+
if (laserScan)
{
+
{
// ******* YOUR CODE HERE **************
+
// ******* YOUR CODE HERE **************
// Access to the object data members to get the scan data.
+
// Access to the object data members to get the scan data.
// Refer to: http://babel.isa.uma.es/mrpt/reference/svn/classmrpt_1_1slam_1_1_c_observation2_d_range_scan.html
+
// Refer to: http://babel.isa.uma.es/mrpt/reference/svn/classmrpt_1_1slam_1_1_c_observation2_d_range_scan.html
// Example:
+
// Examples:
cout << "Scan size: " << laserScan->scan.size() << endl;
+
//  Scan size: laserScan->scan.size()
cout << "Scan point[0]: " << laserScan->scan[0] << endl;
+
//  Scan point[0]: laserScan->scan[0]
cout << "Scan point[0] is valid: " << laserScan->validRange[0]!=0 ? "Yes" : "No" << endl;
+
//  Scan point[0] is valid: laserScan->validRange[0]!=0
}
+
}
else
+
else
{
+
{
// For some reason (comms error, hardware error, etc...) there is no laser scan.
+
// For some reason (comms error, hardware error, etc...) there is no laser scan.
 +
}
 
}
 
}
 
 
</cpp>
 
</cpp>
  

Latest revision as of 17:58, 23 July 2009

Overview

Following the Robotic Architecture proposed here, laser scans from either a simulated or a real robot should be accessible from a single module, named BS_RangeSensors.

The interface of this module is described in this section and this tutorial explains how to write a client module to query the latest laser scans from BS_RangeSensors.


Notice that this tutorial only covers the specific case of writing the codification of the client module in the C/C++ language.



Steps

Calling getObservations

At the point in your module where you need to request the laser scans, the service "getObservations" from "BS_RangeSensors" must be invoked. This is the definition of that service, just to know how it looks like:

Tut-scans service.jpg


To call that service, you must use the aracne-atom "request-synchronous&blocking". It can be inserted in your code from the BABEL_MD menu. A dialog will appear asking you which module and service you want to call. Write this information by hand or by using the button "From ICE..." as in the image:

Tut-scans wizard1.jpg

Accept and a wizard will appear asking you for the arguments of the service. Fill it out as in the figure:

Tut-scans wizard2.jpg


Now, this aracne atom must have some preparatory C++ code before it, and some C++ code after it to process the results.

You can use the following fragments of code, self-explained in its comments. Pay attention to:

  • The label "YOUR CODE HERE" for the place where to process the laser scan.
  • The point "ARACNE_ATOM_HERE" at which the aracne atom "request-synchronous&blocking" must be inserted.



<cpp> using namespace mrpt; using namespace mrpt::utils; using namespace mrpt::slam;

// Retrieve the latest laser scan // ----------------------------------------------------- BABEL::BS_RangeSensors::SeqOfBytes *SF; // The sequence of bytes sent by the server module BABEL::Boolean sensorError; // Sensor error? bool err_var; // BABEL communication error?

/**** ARACNE_ATOM_HERE ****/

// Communications error? Propagate it: if (err_var) { // communication error } else { // service requested ok

CObservation2DRangeScanPtr laserScan; // A smart-pointer to the laser scan (read below)

if (!sensorError) { try { // De-serialize the object: mrpt::utils::CSerializablePtr obj = SeqOfBytes2MRPTObject(SF);

// Assure it's a valid observation (if not, the "catch" block will take control): ASSERT_(obj) ASSERT_( IS_CLASS(obj,CSensoryFrame) )

CSensoryFramePtr theSF = CSensoryFramePtr(obj);

laserScan = theSF->getObservationByClass<CObservation2DRangeScan>(); // Get the first scan, if any. } catch(std::exception &e) { // Process ERROR, message in e.what() } }

// Free memory of the Sequence delete SF; SF = NULL;


// Now, we have the latest scan laser in the variable "laserScan", which // is a smart pointer to a mrpt::slam::CObservation2DRangeScan // ------------------------------------------------------------------------- if (laserScan) { // ******* YOUR CODE HERE ************** // Access to the object data members to get the scan data. // Refer to: http://babel.isa.uma.es/mrpt/reference/svn/classmrpt_1_1slam_1_1_c_observation2_d_range_scan.html // Examples: // Scan size: laserScan->scan.size() // Scan point[0]: laserScan->scan[0] // Scan point[0] is valid: laserScan->validRange[0]!=0 } else { // For some reason (comms error, hardware error, etc...) there is no laser scan. } } </cpp>

Auxiliary code

Add the following auxiliary function to your module "Auxiliary logic" section, since this function is used in the code above:

<cpp> // SeqOfBytes2MRPTObject: Convert a BABEL sequence of bytes into a MRPT object. // IMPORTANT: "T of seqofbytes" must be a POINTER to a BABEL sequence, not a reference to it. template <typename T> mrpt::utils::CSerializablePtr SeqOfBytes2MRPTObject(const T &seqofbytes) { if (seqofbytes->length()==0) return mrpt::utils::CSerializablePtr(); mrpt::utils::CMemoryStream memBlock;

	memBlock.assignMemoryNotOwn(&(*seqofbytes)[0], seqofbytes->length() );

return memBlock.ReadObject(); } </cpp>

Non-deportabilities of the module

If your module didn't use MRPT before adding these code fragments, it must be inserted by:

  • Selecting the tab "Non-deportabilities" in BABEL_MD.
  • Select "Execution".
  • Add "mrpt-core" from the list below.
  • Save the module.

You'll need MRPT installed as explained here.