Permalink
Browse files

Two changes: (1) refactored sensors data passing, so vectors of data …

…are used instead of CSV strings. Now, the data manager is responsible for converting vectors of strings into whatever else is needed (e.g. a single CSV string.) Also, (2) working sensor for spring cable actuators. Now, tgDataLogger2 should be almost functionally equivalent to tgDataLogger! (Still don't have markers though.)
  • Loading branch information...
apsabelhaus committed Jan 8, 2017
1 parent d6060b2 commit 067d97b9bde94b8e11d452b477b6652e7e18c311
View
@@ -331,6 +331,24 @@ inline tgTags operator+(tgTags lhs, const tgTags& rhs)
return lhs;
}
// Overload the + operator for strings too. tgTags should be able to be added
// to strings, with a string as the return result.
inline std::string operator+(tgTags tags, std::string str)
{
// Use the stringstream overloaded operator.
std::stringstream os;
os << tags << str;
return os.str();
}
// Similarly, have a + function with the operands flipped.
inline std::string operator+(std::string str, tgTags tags)
{
// Use the stringstream overloaded operator.
std::stringstream os;
os << str << tags;
return os.str();
}
/**
* Represent tags as a YAML list
* Note: this function has no dependencies on external libraries
@@ -35,6 +35,7 @@
#include "core/tgWorld.h"
#include "sensors/tgDataLogger2.h"
#include "sensors/tgRodSensorInfo.h"
#include "sensors/tgSpringCableActuatorSensorInfo.h"
// Bullet Physics
#include "LinearMath/btVector3.h"
// The C++ Standard Library
@@ -93,8 +94,11 @@ int main(int argc, char** argv)
// Create sensor infos for all the types of sensors that the data logger
// will create.
tgRodSensorInfo* myRodSensorInfo = new tgRodSensorInfo();
tgSpringCableActuatorSensorInfo* mySCASensorInfo =
new tgSpringCableActuatorSensorInfo();
// Attach the sensor infos to the data logger
myDataLogger->addSensorInfo(myRodSensorInfo);
myDataLogger->addSensorInfo(mySCASensorInfo);
// Next, attach it to the simulation
simulation.addDataManager(myDataLogger);
// and everything else should happen automatically.
View
@@ -5,14 +5,19 @@ link_directories(${LIB_DIR})
link_libraries(${PROJECT_NAME} util core tgOpenGLSupport)
add_library( ${PROJECT_NAME} SHARED
tgDataLogger.cpp
tgDataObserver.cpp
# Older software
tgDataLogger.cpp
tgDataObserver.cpp
# For the new sensors
tgDataManager.cpp
tgDataLogger2.cpp
tgSensor.cpp
tgRodSensor.cpp
tgSensorInfo.cpp
tgRodSensorInfo.cpp
# For the new sensors
tgDataManager.cpp
tgDataLogger2.cpp
tgSensor.cpp
tgRodSensor.cpp
tgSpringCableActuatorSensor.cpp
tgSensorInfo.cpp
tgRodSensorInfo.cpp
tgSpringCableActuatorSensorInfo.cpp
)
@@ -142,15 +142,18 @@ void tgDataLogger2::setup()
tgOutput << "time,";
// Iterate. For each sensor, output its header.
// The prefix here is the sensor number, which we choose to be the index in
// Prepend each label with the sensor number, which we choose to be the index in
// the vector of sensors. NOTE that this means the sensors vector CANNOT
// BE CHANGED, otherwise the data will not be aligned properly.
for (std::size_t i=0; i < m_sensors.size(); i++) {
// First, convert i to a string.
std::stringstream iAsString;
iAsString << i;
// Append header for sensor i.
tgOutput << m_sensors[i]->getSensorDataHeading( iAsString.str() );
// Get the vector of sensor data headings from this sensor
std::vector<std::string> headings = m_sensors[i]->getSensorDataHeadings();
// Iterate and output each heading
for (std::size_t j=0; j < headings.size(); j++) {
// Prepend with the sensor number and an underscore.
// Also, end with a comma, since this is a comma-separated-value log file.
tgOutput << i << "_" << headings[j] << ",";
}
}
// End with a new line.
tgOutput << std::endl;
@@ -197,7 +200,13 @@ void tgDataLogger2::step(double dt)
tgOutput << m_totalTime << ",";
// Collect the data and output it to the file!
for (size_t i=0; i < m_sensors.size(); i++) {
tgOutput << m_sensors[i]->getSensorData();
// Get the vector of sensor data from this sensor
std::vector<std::string> sensordata = m_sensors[i]->getSensorData();
// Iterate and output each data sample
for (std::size_t j=0; j < sensordata.size(); j++) {
// Include a comma, since this is a comma-separated-value log file.
tgOutput << sensordata[j] << ",";
}
}
tgOutput << std::endl;
// Close the output, to be re-opened next step.
View
@@ -37,6 +37,7 @@
#include <sstream>
#include <stdexcept>
#include <cassert>
#include <string> // for std::to_string(float)
// Includes from Bullet Physics:
#include "LinearMath/btVector3.h"
@@ -69,60 +70,55 @@ tgRodSensor::~tgRodSensor()
* Also, the previous tgDataObserver recorded the mass of the rod too,
* so do that here for consistency. (even though mass doesn't change with time.)
*/
std::string tgRodSensor::getSensorDataHeading(std::string prefix) {
std::vector<std::string> tgRodSensor::getSensorDataHeadings() {
// Note that this class has access to the parent's pointer, m_pSens.
// Let's cast that to a pointer to a tgRod right now.
// Here, "m_pRod" stands for "my pointer to a tgRod."
tgRod* m_pRod = tgCast::cast<tgSenseable, tgRod>(m_pSens);
// Check: if the cast failed, this will return 0.
// In that case, this tgRodSensor does not point to a tgRod!!!
assert( m_pRod != 0);
// The list to which we'll append all the sensor headings:
std::vector<std::string> headings;
// Create a string stream to which we'll append all the
// information.
std::stringstream heading;
// Pull out the tags for this rod, so we only have to call the accessor once.
//std::deque<std::string> m_tags = m_pRod->getTags();
tgTags m_tags = m_pRod->getTags();
// Copied from tgSensor.h:
/**
* A sensor heading should have:
* (a) a series of comma-separated values in a row, (b) each "column" of
* the CSV is prepended with "prefix" and then a "_", (c) then has
* the type of sensor, then an open parenthesis "(" and the tags
* Headings should have the following form:
* The type of sensor, then an open parenthesis "(" and the tags
* of the specific tgSenseable object, then a ")." and a label for the
* specific field that will be output in that row.
* For example, if sensor 4 (the prefix) will be sensing a rod
* with tags "t4 t5", its label for the X position might be "4_rod(t4 t5).X"
* For example, if sensor will be sensing a rod
* with tags "t4 t5", its label for the X position might be "rod(t4 t5).X"
*/
// The string 'prefix' will be added to each column. Usually, this would
// be for something like a numbering system that a data manager would
// implement.
// Needs an underscore to separate it from the rest of the heading.
prefix = prefix + "_rod(";
// The string 'prefix' will be added to each heading.
std::string prefix = "rod(";
// Note that the orientation is a btVector3 object of Euler angles,
// which I believe are overloaded as strings...
// Also, the XYZ positions are of the center of mass.
// TO-DO: check which euler angles are which!!!
heading << prefix << m_tags << ").X,"
<< prefix << m_tags << ").Y,"
<< prefix << m_tags << ").Z,"
<< prefix << m_tags << ").Euler1,"
<< prefix << m_tags << ").Euler2,"
<< prefix << m_tags << ").Euler3,"
<< prefix << m_tags << ").mass,";
headings.push_back( prefix + m_tags + ").X" );
headings.push_back( prefix + m_tags + ").Y" );
headings.push_back( prefix + m_tags + ").Z" );
headings.push_back( prefix + m_tags + ").Euler1" );
headings.push_back( prefix + m_tags + ").Euler2" );
headings.push_back( prefix + m_tags + ").Euler3" );
headings.push_back( prefix + m_tags + ").mass" );
// Return the string version of this string stream.
return heading.str();
return headings;
}
/**
* The method that collects the actual data from this tgRod.
*/
std::string tgRodSensor::getSensorData() {
std::vector<std::string> tgRodSensor::getSensorData() {
// Similar to getSensorDataHeading, cast the a pointer to a tgRod right now.
tgRod* m_pRod = tgCast::cast<tgSenseable, tgRod>(m_pSens);
// Check: if the cast failed, this will return 0.
@@ -132,19 +128,59 @@ std::string tgRodSensor::getSensorData() {
btVector3 com = m_pRod->centerOfMass();
btVector3 orient = m_pRod->orientation();
// Note that the 'orientation' method also returns a btVector3.
// The list of sensor data that will be returned:
std::vector<std::string> sensordata;
/**
* The original version of this section of code, which
* output one string, looked like:
* std::stringstream sensordata;
* sensordata << com[0] << ","
* << com[1] << ","
* << com[2] << ","
* << orient[0] << ","
* << orient[1] << ","
* << orient[2] << ","
* << m_pRod->mass() << ",";
*/
// The floats (btScalars?) need to be converted to strings
// via a stringstream.
std::stringstream ss;
// com[0]
ss << com[0];
sensordata.push_back( ss.str() );
// Reset the stream.
ss.str("");
// com[1]
ss << com[1];
sensordata.push_back( ss.str() );
ss.str("");
// com[2]
ss << com[2];
sensordata.push_back( ss.str() );
ss.str("");
// orient[0]
ss << orient[0];
sensordata.push_back( ss.str() );
ss.str("");
// orient[1]
ss << orient[1];
sensordata.push_back( ss.str() );
ss.str("");
// orient[2]
ss << orient[2];
sensordata.push_back( ss.str() );
ss.str("");
// mass
ss << m_pRod->mass();
sensordata.push_back( ss.str() );
ss.str("");
// Similar to the heading, create a string steam
// of all the data to be returned.
std::stringstream sensordata;
sensordata << com[0] << ","
<< com[1] << ","
<< com[2] << ","
<< orient[0] << ","
<< orient[1] << ","
<< orient[2] << ","
<< m_pRod->mass() << ",";
// Again, must return a string, not a stringstream.
return sensordata.str();
return sensordata;
}
//end.
@@ -54,8 +54,8 @@ class tgRodSensor : public tgSensor
/**
* Similarly, this class will implement the two data colleciton methods.
*/
virtual std::string getSensorDataHeading(std::string prefix);
virtual std::string getSensorData();
virtual std::vector<std::string> getSensorDataHeadings();
virtual std::vector<std::string> getSensorData();
};
View
@@ -32,6 +32,7 @@ class tgSenseable;
// From the C++ standard library:
#include <iostream> //for strings
#include <vector> // for returning lists of strings
/**
* This class defines methods for use with sensors.
@@ -40,7 +41,7 @@ class tgSenseable;
* Note that we make everything pure virtual here to force re-definition.
* Sensing data from objects occurs in two places in the NTRTsim workflow.
* First, when setting up the simulation, a heading for the data is given.
* This describes the data that will be returned, in a comma-separated-value form.
* This describes the data that will be returned.
* Second, when the simulation is running, the data itself can be taken.
* Note that it's up to the caller (e.g., a tgDataLogger2) to match up the headings
* with the data.
@@ -67,27 +68,26 @@ class tgSensor
/**
* Create a descriptive heading for all the data that this class can return.
* This will be a CSV string, with the number of columns the same as the
* number of columns output by the getData function below.
* @param[in] prefix a string to pre-pend to all columns of the
* sensor data heading.
* @return a string with the heading. A heading should have the following:
* (a) a series of comma-separated values in a row, (b) each "column" of
* the CSV is prepended with "prefix" and then a "_", (c) then has
* the type of sensor, then an open parenthesis "(" and the tags
* This will be a vector of strings, with each string being a heading.
* Headings should have the following form:
* The type of sensor, then an open parenthesis "(" and the tags
* of the specific tgSenseable object, then a ")." and a label for the
* specific field that will be output in that row.
* For example, if sensor 4 (the prefix) will be sensing a rod
* with tags "t4 t5", its label for the X position might be "4_rod(t4 t5).X"
* For example, if sensor will be sensing a rod
* with tags "t4 t5", its label for the X position might be "rod(t4 t5).X"
* @return a list of strings, each being a descriptive heading for a
* specific piece of data that will be returned.
*/
virtual std::string getSensorDataHeading(std::string prefix = "") = 0;
virtual std::vector<std::string> getSensorDataHeadings() = 0;
/**
* Return the data from this class itself.
* Note that this MUST be the same number of CSV columns as is returned by
* Note that this MUST have the same number of elements as is returned by
* the getDataHeading function.
* @return a list of strings, each being a piece of sensor data,
* in the same order as the headings.
*/
virtual std::string getSensorData() = 0;
virtual std::vector<std::string> getSensorData() = 0;
// TO-DO: should any of this be const?
@@ -103,4 +103,4 @@ class tgSensor
};
#endif //TG_SENSEABLE_H
#endif //TG_SENSOR_H
Oops, something went wrong.

0 comments on commit 067d97b

Please sign in to comment.