/* * Copyright 2010 SRI International * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef OPEN_KARTO_KARTO_H #define OPEN_KARTO_KARTO_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_POCO #include #endif #include #include #define KARTO_Object(name) \ virtual const char* GetClassName() const { return #name; } \ virtual kt_objecttype GetObjectType() const { return ObjectType_##name; } typedef kt_int32u kt_objecttype; const kt_objecttype ObjectType_None = 0x00000000; const kt_objecttype ObjectType_Sensor = 0x00001000; const kt_objecttype ObjectType_SensorData = 0x00002000; const kt_objecttype ObjectType_CustomData = 0x00004000; const kt_objecttype ObjectType_Misc = 0x10000000; const kt_objecttype ObjectType_Drive = ObjectType_Sensor | 0x01; const kt_objecttype ObjectType_LaserRangeFinder = ObjectType_Sensor | 0x02; const kt_objecttype ObjectType_Camera = ObjectType_Sensor | 0x04; const kt_objecttype ObjectType_DrivePose = ObjectType_SensorData | 0x01; const kt_objecttype ObjectType_LaserRangeScan = ObjectType_SensorData | 0x02; const kt_objecttype ObjectType_LocalizedRangeScan = ObjectType_SensorData | 0x04; const kt_objecttype ObjectType_CameraImage = ObjectType_SensorData | 0x08; const kt_objecttype ObjectType_LocalizedRangeScanWithPoints = ObjectType_SensorData | 0x16; const kt_objecttype ObjectType_Header = ObjectType_Misc | 0x01; const kt_objecttype ObjectType_Parameters = ObjectType_Misc | 0x02; const kt_objecttype ObjectType_DatasetInfo = ObjectType_Misc | 0x04; const kt_objecttype ObjectType_Module = ObjectType_Misc | 0x08; namespace karto { /** * \defgroup OpenKarto OpenKarto Module */ /*@{*/ /** * Exception class. All exceptions thrown from Karto will inherit from this class or be of this class */ class KARTO_EXPORT Exception { public: /** * Constructor with exception message * @param rMessage exception message (default: "Karto Exception") * @param errorCode error code (default: 0) */ Exception(const std::string& rMessage = "Karto Exception", kt_int32s errorCode = 0) : m_Message(rMessage) , m_ErrorCode(errorCode) { } /** * Copy constructor */ Exception(const Exception& rException) : m_Message(rException.m_Message) , m_ErrorCode(rException.m_ErrorCode) { } /** * Destructor */ virtual ~Exception() { } public: /** * Assignment operator */ Exception& operator = (const Exception& rException) { m_Message = rException.m_Message; m_ErrorCode = rException.m_ErrorCode; return *this; } public: /** * Gets the exception message * @return error message as string */ const std::string& GetErrorMessage() const { return m_Message; } /** * Gets error code * @return error code */ kt_int32s GetErrorCode() { return m_ErrorCode; } public: /** * Write exception to output stream * @param rStream output stream * @param rException exception to write */ friend KARTO_EXPORT std::ostream& operator << (std::ostream& rStream, Exception& rException); private: std::string m_Message; kt_int32s m_ErrorCode; }; // class Exception //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Subclass this class to make a non-copyable class (copy * constructor and assignment operator are private) */ class KARTO_EXPORT NonCopyable { private: NonCopyable(const NonCopyable&); const NonCopyable& operator=(const NonCopyable&); protected: NonCopyable() { } virtual ~NonCopyable() { } }; // class NonCopyable //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Singleton class ensures only one instance of T is created */ template class Singleton { public: /** * Constructor */ Singleton() : m_pPointer(NULL) { } /** * Destructor */ virtual ~Singleton() { delete m_pPointer; } /** * Gets the singleton * @return singleton */ T* Get() { #ifdef USE_POCO Poco::FastMutex::ScopedLock lock(m_Mutex); #endif if (m_pPointer == NULL) { m_pPointer = new T; } return m_pPointer; } private: T* m_pPointer; #ifdef USE_POCO Poco::FastMutex m_Mutex; #endif private: Singleton(const Singleton&); const Singleton& operator=(const Singleton&); }; //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Functor */ class KARTO_EXPORT Functor { public: /** * Functor function */ virtual void operator() (kt_int32u) {}; }; // Functor //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// class AbstractParameter; /** * Type declaration of AbstractParameter vector */ typedef std::vector ParameterVector; /** * Parameter manager. */ class KARTO_EXPORT ParameterManager : public NonCopyable { public: /** * Default constructor */ ParameterManager() { } /** * Destructor */ virtual ~ParameterManager() { Clear(); } public: /** * Adds the parameter to this manager * @param pParameter */ void Add(AbstractParameter* pParameter); /** * Gets the parameter of the given name * @param rName * @return parameter of given name */ AbstractParameter* Get(const std::string& rName) { if (m_ParameterLookup.find(rName) != m_ParameterLookup.end()) { return m_ParameterLookup[rName]; } std::cout << "Unknown parameter: " << rName << std::endl; return NULL; } /** * Clears the manager of all parameters */ void Clear(); /** * Gets all parameters * @return vector of all parameters */ inline const ParameterVector& GetParameterVector() const { return m_Parameters; } public: /** * Gets the parameter with the given name * @param rName * @return parameter of given name */ AbstractParameter* operator() (const std::string& rName) { return Get(rName); } private: ParameterManager(const ParameterManager&); const ParameterManager& operator=(const ParameterManager&); private: ParameterVector m_Parameters; std::map m_ParameterLookup; }; // ParameterManager //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// // valid names // 'Test' -- no scope // '/Test' -- no scope will be parsed to 'Test' // '/scope/Test' - 'scope' scope and 'Test' name // '/scope/name/Test' - 'scope/name' scope and 'Test' name // class Name { public: /** * Constructor */ Name() { } /** * Constructor */ Name(const std::string& rName) { Parse(rName); } /** * Constructor */ Name(const Name& rOther) : m_Name(rOther.m_Name) , m_Scope(rOther.m_Scope) { } /** * Destructor */ virtual ~Name() { } public: /** * Gets the name of this name * @return name */ inline const std::string& GetName() const { return m_Name; } /** * Sets the name * @param rName name */ inline void SetName(const std::string& rName) { std::string::size_type pos = rName.find_last_of('/'); if (pos != 0 && pos != std::string::npos) { throw Exception("Name can't contain a scope!"); } m_Name = rName; } /** * Gets the scope of this name * @return scope */ inline const std::string& GetScope() const { return m_Scope; } /** * Sets the scope of this name * @param rScope scope */ inline void SetScope(const std::string& rScope) { m_Scope = rScope; } /** * Returns a string representation of this name * @return string representation of this name */ inline std::string ToString() const { if (m_Scope == "") { return m_Name; } else { std::string name; name.append("/"); name.append(m_Scope); name.append("/"); name.append(m_Name); return name; } } public: /** * Assignment operator. */ Name& operator = (const Name& rOther) { if (&rOther != this) { m_Name = rOther.m_Name; m_Scope = rOther.m_Scope; } return *this; } /** * Equality operator. */ kt_bool operator == (const Name& rOther) const { return (m_Name == rOther.m_Name) && (m_Scope == rOther.m_Scope); } /** * Inequality operator. */ kt_bool operator != (const Name& rOther) const { return !(*this == rOther); } /** * Less than operator. */ kt_bool operator < (const Name& rOther) const { return ToString() < rOther.ToString(); } /** * Write Name onto output stream * @param rStream output stream * @param rName to write */ friend inline std::ostream& operator << (std::ostream& rStream, const Name& rName) { rStream << rName.ToString(); return rStream; } private: /** * Parse the given string into a Name object * @param rName name */ void Parse(const std::string& rName) { std::string::size_type pos = rName.find_last_of('/'); if (pos == std::string::npos) { m_Name = rName; } else { m_Scope = rName.substr(0, pos); m_Name = rName.substr(pos+1, rName.size()); // remove '/' from m_Scope if first!! if (m_Scope.size() > 0 && m_Scope[0] == '/') { m_Scope = m_Scope.substr(1, m_Scope.size()); } } } /** * Validates the given string as a Name * @param rName name */ void Validate(const std::string& rName) { if (rName.empty()) { return; } char c = rName[0]; if (IsValidFirst(c)) { for (size_t i = 1; i < rName.size(); ++i) { c = rName[i]; if (!IsValid(c)) { throw Exception("Invalid character in name. Valid characters must be within the ranges A-Z, a-z, 0-9, '/', '_' and '-'."); } } } else { throw Exception("Invalid first character in name. Valid characters must be within the ranges A-Z, a-z, and '/'."); } } /** * Whether the character is valid as a first character (alphanumeric or /) * @param c character * @return true if the character is a valid first character */ inline kt_bool IsValidFirst(char c) { return (isalpha(c) || c == '/'); } /** * Whether the character is a valid character (alphanumeric, /, _, or -) * @param c character * @return true if the character is valid */ inline kt_bool IsValid(char c) { return (isalnum(c) || c == '/' || c == '_' || c == '-'); } private: std::string m_Name; std::string m_Scope; }; //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Abstract base class for Karto objects. */ class KARTO_EXPORT Object : public NonCopyable { public: /** * Default constructor */ Object(); /** * Constructs an object with the given name * @param rName */ Object(const Name& rName); /** * Default constructor */ virtual ~Object(); public: /** * Gets the name of this object * @return name */ inline const Name& GetName() const { return m_Name; } /** * Gets the class name of this object * @return class name */ virtual const char* GetClassName() const = 0; /** * Gets the type of this object * @return object type */ virtual kt_objecttype GetObjectType() const = 0; /** * Gets the parameter manager of this dataset * @return parameter manager */ virtual inline ParameterManager* GetParameterManager() { return m_pParameterManager; } /** * Gets the named parameter * @param rName name of parameter * @return parameter */ inline AbstractParameter* GetParameter(const std::string& rName) const { return m_pParameterManager->Get(rName); } /** * Sets the parameter with the given name with the given value * @param rName name * @param value value */ template inline void SetParameter(const std::string& rName, T value); /** * Gets all parameters * @return parameters */ inline const ParameterVector& GetParameters() const { return m_pParameterManager->GetParameterVector(); } private: Object(const Object&); const Object& operator=(const Object&); private: Name m_Name; ParameterManager* m_pParameterManager; }; /** * Type declaration of Object vector */ typedef std::vector ObjectVector; //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Whether the object is a sensor * @param pObject object * @return whether the object is a sensor */ inline kt_bool IsSensor(Object* pObject) { return (pObject->GetObjectType() & ObjectType_Sensor) == ObjectType_Sensor; } /** * Whether the object is sensor data * @param pObject object * @return whether the object is sensor data */ inline kt_bool IsSensorData(Object* pObject) { return (pObject->GetObjectType() & ObjectType_SensorData) == ObjectType_SensorData; } /** * Whether the object is a laser range finder * @param pObject object * @return whether the object is a laser range finder */ inline kt_bool IsLaserRangeFinder(Object* pObject) { return (pObject->GetObjectType() & ObjectType_LaserRangeFinder) == ObjectType_LaserRangeFinder; } /** * Whether the object is a localized range scan * @param pObject object * @return whether the object is a localized range scan */ inline kt_bool IsLocalizedRangeScan(Object* pObject) { return (pObject->GetObjectType() & ObjectType_LocalizedRangeScan) == ObjectType_LocalizedRangeScan; } /** * Whether the object is a localized range scan with points * @param pObject object * @return whether the object is a localized range scan with points */ inline kt_bool IsLocalizedRangeScanWithPoints(Object* pObject) { return (pObject->GetObjectType() & ObjectType_LocalizedRangeScanWithPoints) == ObjectType_LocalizedRangeScanWithPoints; } /** * Whether the object is a Parameters object * @param pObject object * @return whether the object is a Parameters object */ inline kt_bool IsParameters(Object* pObject) { return (pObject->GetObjectType() & ObjectType_Parameters) == ObjectType_Parameters; } /** * Whether the object is a DatasetInfo object * @param pObject object * @return whether the object is a DatasetInfo object */ inline kt_bool IsDatasetInfo(Object* pObject) { return (pObject->GetObjectType() & ObjectType_DatasetInfo) == ObjectType_DatasetInfo; } //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Abstract base class for Karto modules. */ class KARTO_EXPORT Module : public Object { public: // @cond EXCLUDE KARTO_Object(Module) // @endcond public: /** * Construct a Module * @param rName module name */ Module(const std::string& rName); /** * Destructor */ virtual ~Module(); public: /** * Reset the module */ virtual void Reset() = 0; /** * Process an Object */ virtual kt_bool Process(karto::Object*) { return false; } private: Module(const Module&); const Module& operator=(const Module&); }; //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Represents a size (width, height) in 2-dimensional real space. */ template class Size2 { public: /** * Default constructor */ Size2() : m_Width(0) , m_Height(0) { } /** * Constructor initializing point location * @param width * @param height */ Size2(T width, T height) : m_Width(width) , m_Height(height) { } /** * Copy constructor * @param rOther */ Size2(const Size2& rOther) : m_Width(rOther.m_Width) , m_Height(rOther.m_Height) { } public: /** * Gets the width * @return the width */ inline const T GetWidth() const { return m_Width; } /** * Sets the width * @param width */ inline void SetWidth(T width) { m_Width = width; } /** * Gets the height * @return the height */ inline const T GetHeight() const { return m_Height; } /** * Sets the height * @param height */ inline void SetHeight(T height) { m_Height = height; } /** * Assignment operator */ inline Size2& operator = (const Size2& rOther) { m_Width = rOther.m_Width; m_Height = rOther.m_Height; return(*this); } /** * Equality operator */ inline kt_bool operator == (const Size2& rOther) const { return (m_Width == rOther.m_Width && m_Height == rOther.m_Height); } /** * Inequality operator */ inline kt_bool operator != (const Size2& rOther) const { return (m_Width != rOther.m_Width || m_Height != rOther.m_Height); } /** * Write Size2 onto output stream * @param rStream output stream * @param rSize to write */ friend inline std::ostream& operator << (std::ostream& rStream, const Size2& rSize) { rStream << "(" << rSize.m_Width << ", " << rSize.m_Height << ")"; return rStream; } private: T m_Width; T m_Height; }; // Size2 //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Represents a vector (x, y) in 2-dimensional real space. */ template class Vector2 { public: /** * Default constructor */ Vector2() { m_Values[0] = 0; m_Values[1] = 0; } /** * Constructor initializing vector location * @param x * @param y */ Vector2(T x, T y) { m_Values[0] = x; m_Values[1] = y; } public: /** * Gets the x-coordinate of this vector2 * @return the x-coordinate of the vector2 */ inline const T& GetX() const { return m_Values[0]; } /** * Sets the x-coordinate of this vector2 * @param x the x-coordinate of the vector2 */ inline void SetX(const T& x) { m_Values[0] = x; } /** * Gets the y-coordinate of this vector2 * @return the y-coordinate of the vector2 */ inline const T& GetY() const { return m_Values[1]; } /** * Sets the y-coordinate of this vector2 * @param y the y-coordinate of the vector2 */ inline void SetY(const T& y) { m_Values[1] = y; } /** * Floor point operator * @param rOther */ inline void MakeFloor(const Vector2& rOther) { if ( rOther.m_Values[0] < m_Values[0] ) m_Values[0] = rOther.m_Values[0]; if ( rOther.m_Values[1] < m_Values[1] ) m_Values[1] = rOther.m_Values[1]; } /** * Ceiling point operator * @param rOther */ inline void MakeCeil(const Vector2& rOther) { if ( rOther.m_Values[0] > m_Values[0] ) m_Values[0] = rOther.m_Values[0]; if ( rOther.m_Values[1] > m_Values[1] ) m_Values[1] = rOther.m_Values[1]; } /** * Returns the square of the length of the vector * @return square of the length of the vector */ inline kt_double SquaredLength() const { return math::Square(m_Values[0]) + math::Square(m_Values[1]); } /** * Returns the length of the vector (x and y). * @return length of the vector */ inline kt_double Length() const { return sqrt(SquaredLength()); } /** * Returns the square distance to the given vector * @returns square distance to the given vector */ inline kt_double SquaredDistance(const Vector2& rOther) const { return (*this - rOther).SquaredLength(); } /** * Gets the distance to the other vector2 * @param rOther * @return distance to other vector2 */ inline kt_double Distance(const Vector2& rOther) const { return sqrt(SquaredDistance(rOther)); } public: /** * In place Vector2 addition. */ inline void operator += (const Vector2& rOther) { m_Values[0] += rOther.m_Values[0]; m_Values[1] += rOther.m_Values[1]; } /** * In place Vector2 subtraction. */ inline void operator -= (const Vector2& rOther) { m_Values[0] -= rOther.m_Values[0]; m_Values[1] -= rOther.m_Values[1]; } /** * Addition operator * @param rOther * @return vector resulting from adding this vector with the given vector */ inline const Vector2 operator + (const Vector2& rOther) const { return Vector2(m_Values[0] + rOther.m_Values[0], m_Values[1] + rOther.m_Values[1]); } /** * Subtraction operator * @param rOther * @return vector resulting from subtracting this vector from the given vector */ inline const Vector2 operator - (const Vector2& rOther) const { return Vector2(m_Values[0] - rOther.m_Values[0], m_Values[1] - rOther.m_Values[1]); } /** * In place scalar division operator * @param scalar */ inline void operator /= (T scalar) { m_Values[0] /= scalar; m_Values[1] /= scalar; } /** * Divides a Vector2 * @param scalar * @return scalar product */ inline const Vector2 operator / (T scalar) const { return Vector2(m_Values[0] / scalar, m_Values[1] / scalar); } /** * Computes the dot product between the two vectors * @param rOther * @return dot product */ inline kt_double operator * (const Vector2& rOther) const { return m_Values[0] * rOther.m_Values[0] + m_Values[1] * rOther.m_Values[1]; } /** * Scales the vector by the given scalar * @param scalar */ inline const Vector2 operator * (T scalar) const { return Vector2(m_Values[0] * scalar, m_Values[1] * scalar); } /** * Subtract the vector by the given scalar * @param scalar */ inline const Vector2 operator - (T scalar) const { return Vector2(m_Values[0] - scalar, m_Values[1] - scalar); } /** * In place scalar multiplication operator * @param scalar */ inline void operator *= (T scalar) { m_Values[0] *= scalar; m_Values[1] *= scalar; } /** * Equality operator returns true if the corresponding x, y values of each Vector2 are the same values. * @param rOther */ inline kt_bool operator == (const Vector2& rOther) const { return (m_Values[0] == rOther.m_Values[0] && m_Values[1] == rOther.m_Values[1]); } /** * Inequality operator returns true if any of the corresponding x, y values of each Vector2 not the same. * @param rOther */ inline kt_bool operator != (const Vector2& rOther) const { return (m_Values[0] != rOther.m_Values[0] || m_Values[1] != rOther.m_Values[1]); } /** * Less than operator * @param rOther * @return true if left vector is less than right vector */ inline kt_bool operator < (const Vector2& rOther) const { if (m_Values[0] < rOther.m_Values[0]) return true; else if (m_Values[0] > rOther.m_Values[0]) return false; else return (m_Values[1] < rOther.m_Values[1]); } /** * Write Vector2 onto output stream * @param rStream output stream * @param rVector to write */ friend inline std::ostream& operator << (std::ostream& rStream, const Vector2& rVector) { rStream << rVector.GetX() << " " << rVector.GetY(); return rStream; } /** * Read Vector2 from input stream * @param rStream input stream */ friend inline std::istream& operator >> (std::istream& rStream, const Vector2& /*rVector*/) { // Implement me!! TODO(lucbettaieb): What the what? Do I need to implement this? return rStream; } private: T m_Values[2]; }; // Vector2 /** * Type declaration of Vector2 vector */ typedef std::vector< Vector2 > PointVectorDouble; //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Represents a vector (x, y, z) in 3-dimensional real space. */ template class Vector3 { public: /** * Default constructor */ Vector3() { m_Values[0] = 0; m_Values[1] = 0; m_Values[2] = 0; } /** * Constructor initializing point location * @param x * @param y * @param z */ Vector3(T x, T y, T z) { m_Values[0] = x; m_Values[1] = y; m_Values[2] = z; } /** * Copy constructor * @param rOther */ Vector3(const Vector3& rOther) { m_Values[0] = rOther.m_Values[0]; m_Values[1] = rOther.m_Values[1]; m_Values[2] = rOther.m_Values[2]; } public: /** * Gets the x-component of this vector * @return x-component */ inline const T& GetX() const { return m_Values[0]; } /** * Sets the x-component of this vector * @param x */ inline void SetX(const T& x) { m_Values[0] = x; } /** * Gets the y-component of this vector * @return y-component */ inline const T& GetY() const { return m_Values[1]; } /** * Sets the y-component of this vector * @param y */ inline void SetY(const T& y) { m_Values[1] = y; } /** * Gets the z-component of this vector * @return z-component */ inline const T& GetZ() const { return m_Values[2]; } /** * Sets the z-component of this vector * @param z */ inline void SetZ(const T& z) { m_Values[2] = z; } /** * Floor vector operator * @param rOther Vector3d */ inline void MakeFloor(const Vector3& rOther) { if (rOther.m_Values[0] < m_Values[0]) m_Values[0] = rOther.m_Values[0]; if (rOther.m_Values[1] < m_Values[1]) m_Values[1] = rOther.m_Values[1]; if (rOther.m_Values[2] < m_Values[2]) m_Values[2] = rOther.m_Values[2]; } /** * Ceiling vector operator * @param rOther Vector3d */ inline void MakeCeil(const Vector3& rOther) { if (rOther.m_Values[0] > m_Values[0]) m_Values[0] = rOther.m_Values[0]; if (rOther.m_Values[1] > m_Values[1]) m_Values[1] = rOther.m_Values[1]; if (rOther.m_Values[2] > m_Values[2]) m_Values[2] = rOther.m_Values[2]; } /** * Returns the square of the length of the vector * @return square of the length of the vector */ inline kt_double SquaredLength() const { return math::Square(m_Values[0]) + math::Square(m_Values[1]) + math::Square(m_Values[2]); } /** * Returns the length of the vector. * @return Length of the vector */ inline kt_double Length() const { return sqrt(SquaredLength()); } /** * Returns a string representation of this vector * @return string representation of this vector */ inline std::string ToString() const { std::stringstream converter; converter.precision(std::numeric_limits::digits10); converter << GetX() << " " << GetY() << " " << GetZ(); return converter.str(); } public: /** * Assignment operator */ inline Vector3& operator = (const Vector3& rOther) { m_Values[0] = rOther.m_Values[0]; m_Values[1] = rOther.m_Values[1]; m_Values[2] = rOther.m_Values[2]; return *this; } /** * Binary vector add. * @param rOther * @return vector sum */ inline const Vector3 operator + (const Vector3& rOther) const { return Vector3(m_Values[0] + rOther.m_Values[0], m_Values[1] + rOther.m_Values[1], m_Values[2] + rOther.m_Values[2]); } /** * Binary vector add. * @param scalar * @return sum */ inline const Vector3 operator + (kt_double scalar) const { return Vector3(m_Values[0] + scalar, m_Values[1] + scalar, m_Values[2] + scalar); } /** * Binary vector subtract. * @param rOther * @return vector difference */ inline const Vector3 operator - (const Vector3& rOther) const { return Vector3(m_Values[0] - rOther.m_Values[0], m_Values[1] - rOther.m_Values[1], m_Values[2] - rOther.m_Values[2]); } /** * Binary vector subtract. * @param scalar * @return difference */ inline const Vector3 operator - (kt_double scalar) const { return Vector3(m_Values[0] - scalar, m_Values[1] - scalar, m_Values[2] - scalar); } /** * Scales the vector by the given scalar * @param scalar */ inline const Vector3 operator * (T scalar) const { return Vector3(m_Values[0] * scalar, m_Values[1] * scalar, m_Values[2] * scalar); } /** * Equality operator returns true if the corresponding x, y, z values of each Vector3 are the same values. * @param rOther */ inline kt_bool operator == (const Vector3& rOther) const { return (m_Values[0] == rOther.m_Values[0] && m_Values[1] == rOther.m_Values[1] && m_Values[2] == rOther.m_Values[2]); } /** * Inequality operator returns true if any of the corresponding x, y, z values of each Vector3 not the same. * @param rOther */ inline kt_bool operator != (const Vector3& rOther) const { return (m_Values[0] != rOther.m_Values[0] || m_Values[1] != rOther.m_Values[1] || m_Values[2] != rOther.m_Values[2]); } /** * Write Vector3 onto output stream * @param rStream output stream * @param rVector to write */ friend inline std::ostream& operator << (std::ostream& rStream, const Vector3& rVector) { rStream << rVector.ToString(); return rStream; } /** * Read Vector3 from input stream * @param rStream input stream */ friend inline std::istream& operator >> (std::istream& rStream, const Vector3& /*rVector*/) { // Implement me!! return rStream; } private: T m_Values[3]; }; // Vector3 //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Defines an orientation as a quaternion rotation using the positive Z axis as the zero reference. *
* Q = w + ix + jy + kz
* w = c_1 * c_2 * c_3 - s_1 * s_2 * s_3
* x = s_1 * s_2 * c_3 + c_1 * c_2 * s_3
* y = s_1 * c_2 * c_3 + c_1 * s_2 * s_3
* z = c_1 * s_2 * c_3 - s_1 * c_2 * s_3
* where
* c_1 = cos(theta/2)
* c_2 = cos(phi/2)
* c_3 = cos(psi/2)
* s_1 = sin(theta/2)
* s_2 = sin(phi/2)
* s_3 = sin(psi/2)
* and
* theta is the angle of rotation about the Y axis measured from the Z axis.
* phi is the angle of rotation about the Z axis measured from the X axis.
* psi is the angle of rotation about the X axis measured from the Y axis.
* (All angles are right-handed.) */ class Quaternion { public: /** * Create a quaternion with default (x=0, y=0, z=0, w=1) values */ inline Quaternion() { m_Values[0] = 0.0; m_Values[1] = 0.0; m_Values[2] = 0.0; m_Values[3] = 1.0; } /** * Create a quaternion using x, y, z, w values. * @param x * @param y * @param z * @param w */ inline Quaternion(kt_double x, kt_double y, kt_double z, kt_double w) { m_Values[0] = x; m_Values[1] = y; m_Values[2] = z; m_Values[3] = w; } /** * Copy constructor */ inline Quaternion(const Quaternion& rQuaternion) { m_Values[0] = rQuaternion.m_Values[0]; m_Values[1] = rQuaternion.m_Values[1]; m_Values[2] = rQuaternion.m_Values[2]; m_Values[3] = rQuaternion.m_Values[3]; } public: /** * Returns the X-value * @return Return the X-value of the quaternion */ inline kt_double GetX() const { return m_Values[0]; } /** * Sets the X-value * @param x X-value of the quaternion */ inline void SetX(kt_double x) { m_Values[0] = x; } /** * Returns the Y-value * @return Return the Y-value of the quaternion */ inline kt_double GetY() const { return m_Values[1]; } /** * Sets the Y-value * @param y Y-value of the quaternion */ inline void SetY(kt_double y) { m_Values[1] = y; } /** * Returns the Z-value * @return Return the Z-value of the quaternion */ inline kt_double GetZ() const { return m_Values[2]; } /** * Sets the Z-value * @param z Z-value of the quaternion */ inline void SetZ(kt_double z) { m_Values[2] = z; } /** * Returns the W-value * @return Return the W-value of the quaternion */ inline kt_double GetW() const { return m_Values[3]; } /** * Sets the W-value * @param w W-value of the quaternion */ inline void SetW(kt_double w) { m_Values[3] = w; } /** * Converts this quaternion into Euler angles * Source: http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm * @param rYaw * @param rPitch * @param rRoll */ void ToEulerAngles(kt_double& rYaw, kt_double& rPitch, kt_double& rRoll) const { kt_double test = m_Values[0] * m_Values[1] + m_Values[2] * m_Values[3]; if (test > 0.499) { // singularity at north pole rYaw = 2 * atan2(m_Values[0], m_Values[3]);; rPitch = KT_PI_2; rRoll = 0; } else if (test < -0.499) { // singularity at south pole rYaw = -2 * atan2(m_Values[0], m_Values[3]); rPitch = -KT_PI_2; rRoll = 0; } else { kt_double sqx = m_Values[0] * m_Values[0]; kt_double sqy = m_Values[1] * m_Values[1]; kt_double sqz = m_Values[2] * m_Values[2]; rYaw = atan2(2 * m_Values[1] * m_Values[3] - 2 * m_Values[0] * m_Values[2], 1 - 2 * sqy - 2 * sqz); rPitch = asin(2 * test); rRoll = atan2(2 * m_Values[0] * m_Values[3] - 2 * m_Values[1] * m_Values[2], 1 - 2 * sqx - 2 * sqz); } } /** * Set x,y,z,w values of the quaternion based on Euler angles. * Source: http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm * @param yaw * @param pitch * @param roll */ void FromEulerAngles(kt_double yaw, kt_double pitch, kt_double roll) { kt_double angle; angle = yaw * 0.5; kt_double cYaw = cos(angle); kt_double sYaw = sin(angle); angle = pitch * 0.5; kt_double cPitch = cos(angle); kt_double sPitch = sin(angle); angle = roll * 0.5; kt_double cRoll = cos(angle); kt_double sRoll = sin(angle); m_Values[0] = sYaw * sPitch * cRoll + cYaw * cPitch * sRoll; m_Values[1] = sYaw * cPitch * cRoll + cYaw * sPitch * sRoll; m_Values[2] = cYaw * sPitch * cRoll - sYaw * cPitch * sRoll; m_Values[3] = cYaw * cPitch * cRoll - sYaw * sPitch * sRoll; } /** * Assignment operator * @param rQuaternion */ inline Quaternion& operator = (const Quaternion& rQuaternion) { m_Values[0] = rQuaternion.m_Values[0]; m_Values[1] = rQuaternion.m_Values[1]; m_Values[2] = rQuaternion.m_Values[2]; m_Values[3] = rQuaternion.m_Values[3]; return(*this); } /** * Equality operator returns true if the corresponding x, y, z, w values of each quaternion are the same values. * @param rOther */ inline kt_bool operator == (const Quaternion& rOther) const { return (m_Values[0] == rOther.m_Values[0] && m_Values[1] == rOther.m_Values[1] && m_Values[2] == rOther.m_Values[2] && m_Values[3] == rOther.m_Values[3]); } /** * Inequality operator returns true if any of the corresponding x, y, z, w values of each quaternion not the same. * @param rOther */ inline kt_bool operator != (const Quaternion& rOther) const { return (m_Values[0] != rOther.m_Values[0] || m_Values[1] != rOther.m_Values[1] || m_Values[2] != rOther.m_Values[2] || m_Values[3] != rOther.m_Values[3]); } /** * Write this quaternion onto output stream * @param rStream output stream * @param rQuaternion */ friend inline std::ostream& operator << (std::ostream& rStream, const Quaternion& rQuaternion) { rStream << rQuaternion.m_Values[0] << " " << rQuaternion.m_Values[1] << " " << rQuaternion.m_Values[2] << " " << rQuaternion.m_Values[3]; return rStream; } private: kt_double m_Values[4]; }; //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Stores x, y, width and height that represents the location and size of a rectangle * (x, y) is at bottom left in mapper! */ template class Rectangle2 { public: /** * Default constructor */ Rectangle2() { } /** * Constructor initializing rectangle parameters * @param x x-coordinate of left edge of rectangle * @param y y-coordinate of bottom edge of rectangle * @param width width of rectangle * @param height height of rectangle */ Rectangle2(T x, T y, T width, T height) : m_Position(x, y) , m_Size(width, height) { } /** * Constructor initializing rectangle parameters * @param rPosition (x,y)-coordinate of rectangle * @param rSize Size of the rectangle */ Rectangle2(const Vector2& rPosition, const Size2& rSize) : m_Position(rPosition) , m_Size(rSize) { } /** * Copy constructor */ Rectangle2(const Rectangle2& rOther) : m_Position(rOther.m_Position) , m_Size(rOther.m_Size) { } public: /** * Gets the x-coordinate of the left edge of this rectangle * @return the x-coordinate of the left edge of this rectangle */ inline T GetX() const { return m_Position.GetX(); } /** * Sets the x-coordinate of the left edge of this rectangle * @param x the x-coordinate of the left edge of this rectangle */ inline void SetX(T x) { m_Position.SetX(x); } /** * Gets the y-coordinate of the bottom edge of this rectangle * @return the y-coordinate of the bottom edge of this rectangle */ inline T GetY() const { return m_Position.GetY(); } /** * Sets the y-coordinate of the bottom edge of this rectangle * @param y the y-coordinate of the bottom edge of this rectangle */ inline void SetY(T y) { m_Position.SetY(y); } /** * Gets the width of this rectangle * @return the width of this rectangle */ inline T GetWidth() const { return m_Size.GetWidth(); } /** * Sets the width of this rectangle * @param width the width of this rectangle */ inline void SetWidth(T width) { m_Size.SetWidth(width); } /** * Gets the height of this rectangle * @return the height of this rectangle */ inline T GetHeight() const { return m_Size.GetHeight(); } /** * Sets the height of this rectangle * @param height the height of this rectangle */ inline void SetHeight(T height) { m_Size.SetHeight(height); } /** * Gets the position of this rectangle * @return the position of this rectangle */ inline const Vector2& GetPosition() const { return m_Position; } /** * Sets the position of this rectangle * @param rX x * @param rY y */ inline void SetPosition(const T& rX, const T& rY) { m_Position = Vector2(rX, rY); } /** * Sets the position of this rectangle * @param rPosition position */ inline void SetPosition(const Vector2& rPosition) { m_Position = rPosition; } /** * Gets the size of this rectangle * @return the size of this rectangle */ inline const Size2& GetSize() const { return m_Size; } /** * Sets the size of this rectangle * @param rSize size */ inline void SetSize(const Size2& rSize) { m_Size = rSize; } /** * Gets the center of this rectangle * @return the center of this rectangle */ inline const Vector2 GetCenter() const { return Vector2(m_Position.GetX() + m_Size.GetWidth() * 0.5, m_Position.GetY() + m_Size.GetHeight() * 0.5); } public: /** * Assignment operator */ Rectangle2& operator = (const Rectangle2& rOther) { m_Position = rOther.m_Position; m_Size = rOther.m_Size; return *this; } /** * Equality operator */ inline kt_bool operator == (const Rectangle2& rOther) const { return (m_Position == rOther.m_Position && m_Size == rOther.m_Size); } /** * Inequality operator */ inline kt_bool operator != (const Rectangle2& rOther) const { return (m_Position != rOther.m_Position || m_Size != rOther.m_Size); } private: Vector2 m_Position; Size2 m_Size; }; // Rectangle2 //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// class Pose3; /** * Defines a position (x, y) in 2-dimensional space and heading. */ class Pose2 { public: /** * Default Constructor */ Pose2() : m_Heading(0.0) { } /** * Constructor initializing pose parameters * @param rPosition position * @param heading heading **/ Pose2(const Vector2& rPosition, kt_double heading) : m_Position(rPosition) , m_Heading(heading) { } /** * Constructor initializing pose parameters * @param x x-coordinate * @param y y-coordinate * @param heading heading **/ Pose2(kt_double x, kt_double y, kt_double heading) : m_Position(x, y) , m_Heading(heading) { } /** * Constructs a Pose2 object from a Pose3. */ Pose2(const Pose3& rPose); /** * Copy constructor */ Pose2(const Pose2& rOther) : m_Position(rOther.m_Position) , m_Heading(rOther.m_Heading) { } public: /** * Returns the x-coordinate * @return the x-coordinate of the pose */ inline kt_double GetX() const { return m_Position.GetX(); } /** * Sets the x-coordinate * @param x the x-coordinate of the pose */ inline void SetX(kt_double x) { m_Position.SetX(x); } /** * Returns the y-coordinate * @return the y-coordinate of the pose */ inline kt_double GetY() const { return m_Position.GetY(); } /** * Sets the y-coordinate * @param y the y-coordinate of the pose */ inline void SetY(kt_double y) { m_Position.SetY(y); } /** * Returns the position * @return the position of the pose */ inline const Vector2& GetPosition() const { return m_Position; } /** * Sets the position * @param rPosition of the pose */ inline void SetPosition(const Vector2& rPosition) { m_Position = rPosition; } /** * Returns the heading of the pose (in radians) * @return the heading of the pose */ inline kt_double GetHeading() const { return m_Heading; } /** * Sets the heading * @param heading of the pose */ inline void SetHeading(kt_double heading) { m_Heading = heading; } /** * Return the squared distance between two Pose2 * @return squared distance */ inline kt_double SquaredDistance(const Pose2& rOther) const { return m_Position.SquaredDistance(rOther.m_Position); } public: /** * Assignment operator */ inline Pose2& operator = (const Pose2& rOther) { m_Position = rOther.m_Position; m_Heading = rOther.m_Heading; return *this; } /** * Equality operator */ inline kt_bool operator == (const Pose2& rOther) const { return (m_Position == rOther.m_Position && m_Heading == rOther.m_Heading); } /** * Inequality operator */ inline kt_bool operator != (const Pose2& rOther) const { return (m_Position != rOther.m_Position || m_Heading != rOther.m_Heading); } /** * In place Pose2 add. */ inline void operator += (const Pose2& rOther) { m_Position += rOther.m_Position; m_Heading = math::NormalizeAngle(m_Heading + rOther.m_Heading); } /** * Binary Pose2 add * @param rOther * @return Pose2 sum */ inline Pose2 operator + (const Pose2& rOther) const { return Pose2(m_Position + rOther.m_Position, math::NormalizeAngle(m_Heading + rOther.m_Heading)); } /** * Binary Pose2 subtract * @param rOther * @return Pose2 difference */ inline Pose2 operator - (const Pose2& rOther) const { return Pose2(m_Position - rOther.m_Position, math::NormalizeAngle(m_Heading - rOther.m_Heading)); } /** * Read pose from input stream * @param rStream input stream */ friend inline std::istream& operator >> (std::istream& rStream, const Pose2& /*rPose*/) { // Implement me!! return rStream; } /** * Write this pose onto output stream * @param rStream output stream * @param rPose to read */ friend inline std::ostream& operator << (std::ostream& rStream, const Pose2& rPose) { rStream << rPose.m_Position.GetX() << " " << rPose.m_Position.GetY() << " " << rPose.m_Heading; return rStream; } private: Vector2 m_Position; kt_double m_Heading; }; // Pose2 /** * Type declaration of Pose2 vector */ typedef std::vector< Pose2 > Pose2Vector; //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Defines a position and orientation in 3-dimensional space. * Karto uses a right-handed coordinate system with X, Y as the 2-D ground plane and X is forward and Y is left. * Values in Vector3 used to define position must have units of meters. * The value of angle when defining orientation in two dimensions must be in units of radians. * The definition of orientation in three dimensions uses quaternions. */ class Pose3 { public: /** * Default constructor */ Pose3() { } /** * Create a new Pose3 object from the given position. * @param rPosition position vector in three space. */ Pose3(const Vector3& rPosition) : m_Position(rPosition) { } /** * Create a new Pose3 object from the given position and orientation. * @param rPosition position vector in three space. * @param rOrientation quaternion orientation in three space. */ Pose3(const Vector3& rPosition, const karto::Quaternion& rOrientation) : m_Position(rPosition) , m_Orientation(rOrientation) { } /** * Copy constructor */ Pose3(const Pose3& rOther) : m_Position(rOther.m_Position) , m_Orientation(rOther.m_Orientation) { } /** * Constructs a Pose3 object from a Pose2. */ Pose3(const Pose2& rPose) { m_Position = Vector3(rPose.GetX(), rPose.GetY(), 0.0); m_Orientation.FromEulerAngles(rPose.GetHeading(), 0.0, 0.0); } public: /** * Get the position of the pose as a 3D vector as const. Values have units of meters. * @return 3-dimensional position vector as const */ inline const Vector3& GetPosition() const { return m_Position; } /** * Set the position of the pose as a 3D vector. Values have units of meters. * @return 3-dimensional position vector */ inline void SetPosition(const Vector3& rPosition) { m_Position = rPosition; } /** * Get the orientation quaternion of the pose as const. * @return orientation quaternion as const */ inline const Quaternion& GetOrientation() const { return m_Orientation; } /** * Get the orientation quaternion of the pose. * @return orientation quaternion */ inline void SetOrientation(const Quaternion& rOrientation) { m_Orientation = rOrientation; } /** * Returns a string representation of this pose * @return string representation of this pose */ inline std::string ToString() { std::stringstream converter; converter.precision(std::numeric_limits::digits10); converter << GetPosition() << " " << GetOrientation(); return converter.str(); } public: /** * Assignment operator */ inline Pose3& operator = (const Pose3& rOther) { m_Position = rOther.m_Position; m_Orientation = rOther.m_Orientation; return *this; } /** * Equality operator */ inline kt_bool operator == (const Pose3& rOther) const { return (m_Position == rOther.m_Position && m_Orientation == rOther.m_Orientation); } /** * Inequality operator */ inline kt_bool operator != (const Pose3& rOther) const { return (m_Position != rOther.m_Position || m_Orientation != rOther.m_Orientation); } /** * Write Pose3 onto output stream * @param rStream output stream * @param rPose to write */ friend inline std::ostream& operator << (std::ostream& rStream, const Pose3& rPose) { rStream << rPose.GetPosition() << ", " << rPose.GetOrientation(); return rStream; } /** * Read Pose3 from input stream * @param rStream input stream */ friend inline std::istream& operator >> (std::istream& rStream, const Pose3& /*rPose*/) { // Implement me!! return rStream; } private: Vector3 m_Position; Quaternion m_Orientation; }; // Pose3 //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Defines a Matrix 3 x 3 class. */ class Matrix3 { public: /** * Default constructor */ Matrix3() { Clear(); } /** * Copy constructor */ inline Matrix3(const Matrix3& rOther) { memcpy(m_Matrix, rOther.m_Matrix, 9*sizeof(kt_double)); } public: /** * Sets this matrix to identity matrix */ void SetToIdentity() { memset(m_Matrix, 0, 9*sizeof(kt_double)); for (kt_int32s i = 0; i < 3; i++) { m_Matrix[i][i] = 1.0; } } /** * Sets this matrix to zero matrix */ void Clear() { memset(m_Matrix, 0, 9*sizeof(kt_double)); } /** * Sets this matrix to be the rotation matrix of rotation around given axis * @param x x-coordinate of axis * @param y y-coordinate of axis * @param z z-coordinate of axis * @param radians amount of rotation */ void FromAxisAngle(kt_double x, kt_double y, kt_double z, const kt_double radians) { kt_double cosRadians = cos(radians); kt_double sinRadians = sin(radians); kt_double oneMinusCos = 1.0 - cosRadians; kt_double xx = x * x; kt_double yy = y * y; kt_double zz = z * z; kt_double xyMCos = x * y * oneMinusCos; kt_double xzMCos = x * z * oneMinusCos; kt_double yzMCos = y * z * oneMinusCos; kt_double xSin = x * sinRadians; kt_double ySin = y * sinRadians; kt_double zSin = z * sinRadians; m_Matrix[0][0] = xx * oneMinusCos + cosRadians; m_Matrix[0][1] = xyMCos - zSin; m_Matrix[0][2] = xzMCos + ySin; m_Matrix[1][0] = xyMCos + zSin; m_Matrix[1][1] = yy * oneMinusCos + cosRadians; m_Matrix[1][2] = yzMCos - xSin; m_Matrix[2][0] = xzMCos - ySin; m_Matrix[2][1] = yzMCos + xSin; m_Matrix[2][2] = zz * oneMinusCos + cosRadians; } /** * Returns transposed version of this matrix * @return transposed matrix */ Matrix3 Transpose() const { Matrix3 transpose; for (kt_int32u row = 0; row < 3; row++) { for (kt_int32u col = 0; col < 3; col++) { transpose.m_Matrix[row][col] = m_Matrix[col][row]; } } return transpose; } /** * Returns the inverse of the matrix */ Matrix3 Inverse() const { Matrix3 kInverse = *this; kt_bool haveInverse = InverseFast(kInverse, 1e-14); if (haveInverse == false) { assert(false); } return kInverse; } /** * Internal helper method for inverse matrix calculation * This code is lifted from the OgreMatrix3 class!! */ kt_bool InverseFast(Matrix3& rkInverse, kt_double fTolerance = KT_TOLERANCE) const { // Invert a 3x3 using cofactors. This is about 8 times faster than // the Numerical Recipes code which uses Gaussian elimination. rkInverse.m_Matrix[0][0] = m_Matrix[1][1]*m_Matrix[2][2] - m_Matrix[1][2]*m_Matrix[2][1]; rkInverse.m_Matrix[0][1] = m_Matrix[0][2]*m_Matrix[2][1] - m_Matrix[0][1]*m_Matrix[2][2]; rkInverse.m_Matrix[0][2] = m_Matrix[0][1]*m_Matrix[1][2] - m_Matrix[0][2]*m_Matrix[1][1]; rkInverse.m_Matrix[1][0] = m_Matrix[1][2]*m_Matrix[2][0] - m_Matrix[1][0]*m_Matrix[2][2]; rkInverse.m_Matrix[1][1] = m_Matrix[0][0]*m_Matrix[2][2] - m_Matrix[0][2]*m_Matrix[2][0]; rkInverse.m_Matrix[1][2] = m_Matrix[0][2]*m_Matrix[1][0] - m_Matrix[0][0]*m_Matrix[1][2]; rkInverse.m_Matrix[2][0] = m_Matrix[1][0]*m_Matrix[2][1] - m_Matrix[1][1]*m_Matrix[2][0]; rkInverse.m_Matrix[2][1] = m_Matrix[0][1]*m_Matrix[2][0] - m_Matrix[0][0]*m_Matrix[2][1]; rkInverse.m_Matrix[2][2] = m_Matrix[0][0]*m_Matrix[1][1] - m_Matrix[0][1]*m_Matrix[1][0]; kt_double fDet = m_Matrix[0][0]*rkInverse.m_Matrix[0][0] + m_Matrix[0][1]*rkInverse.m_Matrix[1][0] + m_Matrix[0][2]*rkInverse.m_Matrix[2][0]; if (fabs(fDet) <= fTolerance) { return false; } kt_double fInvDet = 1.0/fDet; for (size_t row = 0; row < 3; row++) { for (size_t col = 0; col < 3; col++) { rkInverse.m_Matrix[row][col] *= fInvDet; } } return true; } /** * Returns a string representation of this matrix * @return string representation of this matrix */ inline std::string ToString() const { std::stringstream converter; converter.precision(std::numeric_limits::digits10); for (int row = 0; row < 3; row++) { for (int col = 0; col < 3; col++) { converter << m_Matrix[row][col] << " "; } } return converter.str(); } public: /** * Assignment operator */ inline Matrix3& operator = (const Matrix3& rOther) { memcpy(m_Matrix, rOther.m_Matrix, 9*sizeof(kt_double)); return *this; } /** * Matrix element access, allows use of construct mat(r, c) * @param row * @param column * @return reference to mat(r,c) */ inline kt_double& operator()(kt_int32u row, kt_int32u column) { return m_Matrix[row][column]; } /** * Read-only matrix element access, allows use of construct mat(r, c) * @param row * @param column * @return mat(r,c) */ inline kt_double operator()(kt_int32u row, kt_int32u column) const { return m_Matrix[row][column]; } /** * Binary Matrix3 multiplication. * @param rOther * @return Matrix3 product */ Matrix3 operator * (const Matrix3& rOther) const { Matrix3 product; for (size_t row = 0; row < 3; row++) { for (size_t col = 0; col < 3; col++) { product.m_Matrix[row][col] = m_Matrix[row][0]*rOther.m_Matrix[0][col] + m_Matrix[row][1]*rOther.m_Matrix[1][col] + m_Matrix[row][2]*rOther.m_Matrix[2][col]; } } return product; } /** * Matrix3 and Pose2 multiplication - matrix * pose [3x3 * 3x1 = 3x1] * @param rPose2 * @return Pose2 product */ inline Pose2 operator * (const Pose2& rPose2) const { Pose2 pose2; pose2.SetX(m_Matrix[0][0] * rPose2.GetX() + m_Matrix[0][1] * rPose2.GetY() + m_Matrix[0][2] * rPose2.GetHeading()); pose2.SetY(m_Matrix[1][0] * rPose2.GetX() + m_Matrix[1][1] * rPose2.GetY() + m_Matrix[1][2] * rPose2.GetHeading()); pose2.SetHeading(m_Matrix[2][0] * rPose2.GetX() + m_Matrix[2][1] * rPose2.GetY() + m_Matrix[2][2] * rPose2.GetHeading()); return pose2; } /** * In place Matrix3 add. * @param rkMatrix */ inline void operator += (const Matrix3& rkMatrix) { for (kt_int32u row = 0; row < 3; row++) { for (kt_int32u col = 0; col < 3; col++) { m_Matrix[row][col] += rkMatrix.m_Matrix[row][col]; } } } /** * Write Matrix3 onto output stream * @param rStream output stream * @param rMatrix to write */ friend inline std::ostream& operator << (std::ostream& rStream, const Matrix3& rMatrix) { rStream << rMatrix.ToString(); return rStream; } private: kt_double m_Matrix[3][3]; }; // Matrix3 //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Defines a general Matrix class. */ class Matrix { public: /** * Constructs a matrix of size rows x columns */ Matrix(kt_int32u rows, kt_int32u columns) : m_Rows(rows) , m_Columns(columns) , m_pData(NULL) { Allocate(); Clear(); } /** * Destructor */ virtual ~Matrix() { delete [] m_pData; } public: /** * Set all entries to 0 */ void Clear() { if (m_pData != NULL) { memset(m_pData, 0, sizeof(kt_double) * m_Rows * m_Columns); } } /** * Gets the number of rows of the matrix * @return nubmer of rows */ inline kt_int32u GetRows() const { return m_Rows; } /** * Gets the number of columns of the matrix * @return nubmer of columns */ inline kt_int32u GetColumns() const { return m_Columns; } /** * Returns a reference to the entry at (row,column) * @param row * @param column * @return reference to entry at (row,column) */ inline kt_double& operator()(kt_int32u row, kt_int32u column) { RangeCheck(row, column); return m_pData[row + column * m_Rows]; } /** * Returns a const reference to the entry at (row,column) * @param row * @param column * @return const reference to entry at (row,column) */ inline const kt_double& operator()(kt_int32u row, kt_int32u column) const { RangeCheck(row, column); return m_pData[row + column * m_Rows]; } private: /** * Allocate space for the matrix */ void Allocate() { try { if (m_pData != NULL) { delete[] m_pData; } m_pData = new kt_double[m_Rows * m_Columns]; } catch (const std::bad_alloc& ex) { throw Exception("Matrix allocation error"); } if (m_pData == NULL) { throw Exception("Matrix allocation error"); } } /** * Checks if (row,column) is a valid entry into the matrix * @param row * @param column */ inline void RangeCheck(kt_int32u row, kt_int32u column) const { if (math::IsUpTo(row, m_Rows) == false) { throw Exception("Matrix - RangeCheck ERROR!!!!"); } if (math::IsUpTo(column, m_Columns) == false) { throw Exception("Matrix - RangeCheck ERROR!!!!"); } } private: kt_int32u m_Rows; kt_int32u m_Columns; kt_double* m_pData; }; // Matrix //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Defines a bounding box in 2-dimensional real space. */ class BoundingBox2 { public: /* * Default constructor */ BoundingBox2() : m_Minimum(999999999999999999.99999, 999999999999999999.99999) , m_Maximum(-999999999999999999.99999, -999999999999999999.99999) { } public: /** * Get bounding box minimum */ inline const Vector2& GetMinimum() const { return m_Minimum; } /** * Set bounding box minimum */ inline void SetMinimum(const Vector2& mMinimum) { m_Minimum = mMinimum; } /** * Get bounding box maximum */ inline const Vector2& GetMaximum() const { return m_Maximum; } /** * Set bounding box maximum */ inline void SetMaximum(const Vector2& rMaximum) { m_Maximum = rMaximum; } /** * Get the size of the bounding box */ inline Size2 GetSize() const { Vector2 size = m_Maximum - m_Minimum; return Size2(size.GetX(), size.GetY()); } /** * Add vector to bounding box */ inline void Add(const Vector2& rPoint) { m_Minimum.MakeFloor(rPoint); m_Maximum.MakeCeil(rPoint); } /** * Add other bounding box to bounding box */ inline void Add(const BoundingBox2& rBoundingBox) { Add(rBoundingBox.GetMinimum()); Add(rBoundingBox.GetMaximum()); } /** * Whether the given point is in the bounds of this box * @param rPoint * @return in bounds? */ inline kt_bool IsInBounds(const Vector2& rPoint) const { return (math::InRange(rPoint.GetX(), m_Minimum.GetX(), m_Maximum.GetX()) && math::InRange(rPoint.GetY(), m_Minimum.GetY(), m_Maximum.GetY())); } private: Vector2 m_Minimum; Vector2 m_Maximum; }; // BoundingBox2 //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Implementation of a Pose2 transform */ class Transform { public: /** * Constructs a transformation from the origin to the given pose * @param rPose pose */ Transform(const Pose2& rPose) { SetTransform(Pose2(), rPose); } /** * Constructs a transformation from the first pose to the second pose * @param rPose1 first pose * @param rPose2 second pose */ Transform(const Pose2& rPose1, const Pose2& rPose2) { SetTransform(rPose1, rPose2); } public: /** * Transforms the pose according to this transform * @param rSourcePose pose to transform from * @return transformed pose */ inline Pose2 TransformPose(const Pose2& rSourcePose) { Pose2 newPosition = m_Transform + m_Rotation * rSourcePose; kt_double angle = math::NormalizeAngle(rSourcePose.GetHeading() + m_Transform.GetHeading()); return Pose2(newPosition.GetPosition(), angle); } /** * Inverse transformation of the pose according to this transform * @param rSourcePose pose to transform from * @return transformed pose */ inline Pose2 InverseTransformPose(const Pose2& rSourcePose) { Pose2 newPosition = m_InverseRotation * (rSourcePose - m_Transform); kt_double angle = math::NormalizeAngle(rSourcePose.GetHeading() - m_Transform.GetHeading()); // components of transform return Pose2(newPosition.GetPosition(), angle); } private: /** * Sets this to be the transformation from the first pose to the second pose * @param rPose1 first pose * @param rPose2 second pose */ void SetTransform(const Pose2& rPose1, const Pose2& rPose2) { if (rPose1 == rPose2) { m_Rotation.SetToIdentity(); m_InverseRotation.SetToIdentity(); m_Transform = Pose2(); return; } // heading transformation m_Rotation.FromAxisAngle(0, 0, 1, rPose2.GetHeading() - rPose1.GetHeading()); m_InverseRotation.FromAxisAngle(0, 0, 1, rPose1.GetHeading() - rPose2.GetHeading()); // position transformation Pose2 newPosition; if (rPose1.GetX() != 0.0 || rPose1.GetY() != 0.0) { newPosition = rPose2 - m_Rotation * rPose1; } else { newPosition = rPose2; } m_Transform = Pose2(newPosition.GetPosition(), rPose2.GetHeading() - rPose1.GetHeading()); } private: // pose transformation Pose2 m_Transform; Matrix3 m_Rotation; Matrix3 m_InverseRotation; }; // Transform //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Enumerated type for valid LaserRangeFinder types */ typedef enum { LaserRangeFinder_Custom = 0, LaserRangeFinder_Sick_LMS100 = 1, LaserRangeFinder_Sick_LMS200 = 2, LaserRangeFinder_Sick_LMS291 = 3, LaserRangeFinder_Hokuyo_UTM_30LX = 4, LaserRangeFinder_Hokuyo_URG_04LX = 5 } LaserRangeFinderType; //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Abstract base class for Parameters */ class AbstractParameter { public: /** * Constructs a parameter with the given name * @param rName * @param pParameterManger */ AbstractParameter(const std::string& rName, ParameterManager* pParameterManger = NULL) : m_Name(rName) { // if parameter manager is provided add myself to it! if (pParameterManger != NULL) { pParameterManger->Add(this); } } /** * Constructs a parameter with the given name and description * @param rName * @param rDescription * @param pParameterManger */ AbstractParameter(const std::string& rName, const std::string& rDescription, ParameterManager* pParameterManger = NULL) : m_Name(rName) , m_Description(rDescription) { // if parameter manager is provided add myself to it! if (pParameterManger != NULL) { pParameterManger->Add(this); } } /** * Destructor */ virtual ~AbstractParameter() { } public: /** * Gets the name of this object * @return name */ inline const std::string& GetName() const { return m_Name; } /** * Returns the parameter description * @return parameter description */ inline const std::string& GetDescription() const { return m_Description; } /** * Get parameter value as string. * @return value as string */ virtual const std::string GetValueAsString() const = 0; /** * Set parameter value from string. * @param rStringValue value as string */ virtual void SetValueFromString(const std::string& rStringValue) = 0; /** * Clones the parameter * @return clone */ virtual AbstractParameter* Clone() = 0; public: /** * Write this parameter onto output stream * @param rStream output stream * @param rParameter */ friend std::ostream& operator << (std::ostream& rStream, const AbstractParameter& rParameter) { rStream.precision(6); rStream.flags(std::ios::fixed); rStream << rParameter.GetName() << " = " << rParameter.GetValueAsString(); return rStream; } private: std::string m_Name; std::string m_Description; }; // AbstractParameter //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Parameter class */ template class Parameter : public AbstractParameter { public: /** * Parameter with given name and value * @param rName * @param value * @param pParameterManger */ Parameter(const std::string& rName, T value, ParameterManager* pParameterManger = NULL) : AbstractParameter(rName, pParameterManger) , m_Value(value) { } /** * Parameter with given name, description and value * @param rName * @param rDescription * @param value * @param pParameterManger */ Parameter(const std::string& rName, const std::string& rDescription, T value, ParameterManager* pParameterManger = NULL) : AbstractParameter(rName, rDescription, pParameterManger) , m_Value(value) { } /** * Destructor */ virtual ~Parameter() { } public: /** * Gets value of parameter * @return parameter value */ inline const T& GetValue() const { return m_Value; } /** * Sets value of parameter * @param rValue */ inline void SetValue(const T& rValue) { m_Value = rValue; } /** * Gets value of parameter as string * @return string version of value */ virtual const std::string GetValueAsString() const { std::stringstream converter; converter << m_Value; return converter.str(); } /** * Sets value of parameter from string * @param rStringValue */ virtual void SetValueFromString(const std::string& rStringValue) { std::stringstream converter; converter.str(rStringValue); converter >> m_Value; } /** * Clone this parameter * @return clone of this parameter */ virtual Parameter* Clone() { return new Parameter(GetName(), GetDescription(), GetValue()); } public: /** * Assignment operator */ Parameter& operator = (const Parameter& rOther) { m_Value = rOther.m_Value; return *this; } /** * Sets the value of this parameter to given value */ T operator = (T value) { m_Value = value; return m_Value; } protected: /** * Parameter value */ T m_Value; }; // Parameter template<> inline void Parameter::SetValueFromString(const std::string& rStringValue) { int precision = std::numeric_limits::digits10; std::stringstream converter; converter.precision(precision); converter.str(rStringValue); m_Value = 0.0; converter >> m_Value; } template<> inline const std::string Parameter::GetValueAsString() const { std::stringstream converter; converter.precision(std::numeric_limits::digits10); converter << m_Value; return converter.str(); } template<> inline void Parameter::SetValueFromString(const std::string& rStringValue) { if (rStringValue == "true" || rStringValue == "TRUE") { m_Value = true; } else { m_Value = false; } } template<> inline const std::string Parameter::GetValueAsString() const { if (m_Value == true) { return "true"; } return "false"; } //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Parameter enum class */ class ParameterEnum : public Parameter { typedef std::map EnumMap; public: /** * Construct a Parameter object with name and value * @param rName parameter name * @param value of parameter * @param pParameterManger */ ParameterEnum(const std::string& rName, kt_int32s value, ParameterManager* pParameterManger = NULL) : Parameter(rName, value, pParameterManger) { } /** * Destructor */ virtual ~ParameterEnum() { } public: /** * Return a clone of this instance * @return clone */ virtual Parameter* Clone() { ParameterEnum* pEnum = new ParameterEnum(GetName(), GetValue()); pEnum->m_EnumDefines = m_EnumDefines; return pEnum; } /** * Set parameter value from string. * @param rStringValue value as string */ virtual void SetValueFromString(const std::string& rStringValue) { if (m_EnumDefines.find(rStringValue) != m_EnumDefines.end()) { m_Value = m_EnumDefines[rStringValue]; } else { std::string validValues; const_forEach(EnumMap, &m_EnumDefines) { validValues += iter->first + ", "; } throw Exception("Unable to set enum: " + rStringValue + ". Valid values are: " + validValues); } } /** * Get parameter value as string. * @return value as string */ virtual const std::string GetValueAsString() const { const_forEach(EnumMap, &m_EnumDefines) { if (iter->second == m_Value) { return iter->first; } } throw Exception("Unable to lookup enum"); } /** * Defines the enum with the given name as having the given value * @param value * @param rName */ void DefineEnumValue(kt_int32s value, const std::string& rName) { if (m_EnumDefines.find(rName) == m_EnumDefines.end()) { m_EnumDefines[rName] = value; } else { std::cerr << "Overriding enum value: " << m_EnumDefines[rName] << " with " << value << std::endl; m_EnumDefines[rName] = value; assert(false); } } public: /** * Assignment operator */ ParameterEnum& operator = (const ParameterEnum& rOther) { SetValue(rOther.GetValue()); return *this; } /** * Assignment operator */ kt_int32s operator = (kt_int32s value) { SetValue(value); return m_Value; } private: EnumMap m_EnumDefines; }; // ParameterEnum //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Set of parameters */ class Parameters : public Object { public: // @cond EXCLUDE KARTO_Object(Parameters) // @endcond public: /** * Parameters * @param rName */ Parameters(const std::string& rName) : Object(rName) { } /** * Destructor */ virtual ~Parameters() { } private: Parameters(const Parameters&); const Parameters& operator=(const Parameters&); }; // Parameters //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// class SensorData; /** * Abstract Sensor base class */ class KARTO_EXPORT Sensor : public Object { public: // @cond EXCLUDE KARTO_Object(Sensor) // @endcond protected: /** * Construct a Sensor * @param rName sensor name */ Sensor(const Name& rName); public: /** * Destructor */ virtual ~Sensor(); public: /** * Gets this range finder sensor's offset * @return offset pose */ inline const Pose2& GetOffsetPose() const { return m_pOffsetPose->GetValue(); } /** * Sets this range finder sensor's offset * @param rPose */ inline void SetOffsetPose(const Pose2& rPose) { m_pOffsetPose->SetValue(rPose); } /** * Validates sensor * @return true if valid */ virtual kt_bool Validate() = 0; /** * Validates sensor data * @param pSensorData sensor data * @return true if valid */ virtual kt_bool Validate(SensorData* pSensorData) = 0; private: /** * Restrict the copy constructor */ Sensor(const Sensor&); /** * Restrict the assignment operator */ const Sensor& operator=(const Sensor&); private: /** * Sensor offset pose */ Parameter* m_pOffsetPose; }; // Sensor /** * Type declaration of Sensor vector */ typedef std::vector SensorVector; //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Type declaration of map */ typedef std::map SensorManagerMap; /** * Manages sensors */ class KARTO_EXPORT SensorManager { public: /** * Constructor */ SensorManager() { } /** * Destructor */ virtual ~SensorManager() { } public: /** * Get singleton instance of SensorManager */ static SensorManager* GetInstance(); public: /** * Registers a sensor by it's name. The Sensor name must be unique, if not sensor is not registered * unless override is set to true * @param pSensor sensor to register * @param override * @return true if sensor is registered with SensorManager, false if Sensor name is not unique */ void RegisterSensor(Sensor* pSensor, kt_bool override = false) { Validate(pSensor); if ((m_Sensors.find(pSensor->GetName()) != m_Sensors.end()) && !override) { throw Exception("Cannot register sensor: already registered: [" + pSensor->GetName().ToString() + "] (Consider setting 'override' to true)"); } std::cout << "Registering sensor: [" << pSensor->GetName().ToString() << "]" << std::endl; m_Sensors[pSensor->GetName()] = pSensor; } /** * Unregisters the given sensor * @param pSensor sensor to unregister */ void UnregisterSensor(Sensor* pSensor) { Validate(pSensor); if (m_Sensors.find(pSensor->GetName()) != m_Sensors.end()) { std::cout << "Unregistering sensor: " << pSensor->GetName().ToString() << std::endl; m_Sensors.erase(pSensor->GetName()); } else { throw Exception("Cannot unregister sensor: not registered: [" + pSensor->GetName().ToString() + "]"); } } /** * Gets the sensor with the given name * @param rName name of sensor * @return sensor */ Sensor* GetSensorByName(const Name& rName) { if (m_Sensors.find(rName) != m_Sensors.end()) { return m_Sensors[rName]; } throw Exception("Sensor not registered: [" + rName.ToString() + "] (Did you add the sensor to the Dataset?)"); } /** * Gets the sensor with the given name * @param rName name of sensor * @return sensor */ template T* GetSensorByName(const Name& rName) { Sensor* pSensor = GetSensorByName(rName); return dynamic_cast(pSensor); } /** * Gets all registered sensors * @return vector of all registered sensors */ SensorVector GetAllSensors() { SensorVector sensors; forEach(SensorManagerMap, &m_Sensors) { sensors.push_back(iter->second); } return sensors; } protected: /** * Checks that given sensor is not NULL and has non-empty name * @param pSensor sensor to validate */ static void Validate(Sensor* pSensor) { if (pSensor == NULL) { throw Exception("Invalid sensor: NULL"); } else if (pSensor->GetName().ToString() == "") { throw Exception("Invalid sensor: nameless"); } } protected: /** * Sensor map */ SensorManagerMap m_Sensors; }; //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Sensor that provides pose information relative to world coordinates. * * The user can set the offset pose of the drive sensor. If no value is provided by the user the default is no offset, * i.e, the sensor is initially at the world origin, oriented along the positive z axis. */ class Drive : public Sensor { public: // @cond EXCLUDE KARTO_Object(Drive) // @endcond public: /** * Constructs a Drive object */ Drive(const std::string& rName) : Sensor(rName) { } /** * Destructor */ virtual ~Drive() { } public: virtual kt_bool Validate() { return true; } virtual kt_bool Validate(SensorData* pSensorData) { if (pSensorData == NULL) { return false; } return true; } private: Drive(const Drive&); const Drive& operator=(const Drive&); }; // class Drive //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// class LocalizedRangeScan; class CoordinateConverter; /** * The LaserRangeFinder defines a laser sensor that provides the pose offset position of a localized range scan relative to the robot. * The user can set an offset pose for the sensor relative to the robot coordinate system. If no value is provided * by the user, the sensor is set to be at the origin of the robot coordinate system. * The LaserRangeFinder contains parameters for physical laser sensor used by the mapper for scan matching * Also contains information about the maximum range of the sensor and provides a threshold * for limiting the range of readings. * The optimal value for the range threshold depends on the angular resolution of the scan and * the desired map resolution. RangeThreshold should be set as large as possible while still * providing "solid" coverage between consecutive range readings. The diagram below illustrates * the relationship between map resolution and the range threshold. */ class KARTO_EXPORT LaserRangeFinder : public Sensor { public: // @cond EXCLUDE KARTO_Object(LaserRangeFinder) // @endcond public: /** * Destructor */ virtual ~LaserRangeFinder() { } public: /** * Gets this range finder sensor's minimum range * @return minimum range */ inline kt_double GetMinimumRange() const { return m_pMinimumRange->GetValue(); } /** * Sets this range finder sensor's minimum range * @param minimumRange */ inline void SetMinimumRange(kt_double minimumRange) { m_pMinimumRange->SetValue(minimumRange); SetRangeThreshold(GetRangeThreshold()); } /** * Gets this range finder sensor's maximum range * @return maximum range */ inline kt_double GetMaximumRange() const { return m_pMaximumRange->GetValue(); } /** * Sets this range finder sensor's maximum range * @param maximumRange */ inline void SetMaximumRange(kt_double maximumRange) { m_pMaximumRange->SetValue(maximumRange); SetRangeThreshold(GetRangeThreshold()); } /** * Gets the range threshold * @return range threshold */ inline kt_double GetRangeThreshold() const { return m_pRangeThreshold->GetValue(); } /** * Sets the range threshold * @param rangeThreshold */ inline void SetRangeThreshold(kt_double rangeThreshold) { // make sure rangeThreshold is within laser range finder range m_pRangeThreshold->SetValue(math::Clip(rangeThreshold, GetMinimumRange(), GetMaximumRange())); if (math::DoubleEqual(GetRangeThreshold(), rangeThreshold) == false) { std::cout << "Info: clipped range threshold to be within minimum and maximum range!" << std::endl; } } /** * Gets this range finder sensor's minimum angle * @return minimum angle */ inline kt_double GetMinimumAngle() const { return m_pMinimumAngle->GetValue(); } /** * Sets this range finder sensor's minimum angle * @param minimumAngle */ inline void SetMinimumAngle(kt_double minimumAngle) { m_pMinimumAngle->SetValue(minimumAngle); Update(); } /** * Gets this range finder sensor's maximum angle * @return maximum angle */ inline kt_double GetMaximumAngle() const { return m_pMaximumAngle->GetValue(); } /** * Sets this range finder sensor's maximum angle * @param maximumAngle */ inline void SetMaximumAngle(kt_double maximumAngle) { m_pMaximumAngle->SetValue(maximumAngle); Update(); } /** * Gets this range finder sensor's angular resolution * @return angular resolution */ inline kt_double GetAngularResolution() const { return m_pAngularResolution->GetValue(); } /** * Sets this range finder sensor's angular resolution * @param angularResolution */ inline void SetAngularResolution(kt_double angularResolution) { if (m_pType->GetValue() == LaserRangeFinder_Custom) { m_pAngularResolution->SetValue(angularResolution); } else if (m_pType->GetValue() == LaserRangeFinder_Sick_LMS100) { if (math::DoubleEqual(angularResolution, math::DegreesToRadians(0.25))) { m_pAngularResolution->SetValue(math::DegreesToRadians(0.25)); } else if (math::DoubleEqual(angularResolution, math::DegreesToRadians(0.50))) { m_pAngularResolution->SetValue(math::DegreesToRadians(0.50)); } else { std::stringstream stream; stream << "Invalid resolution for Sick LMS100: "; stream << angularResolution; throw Exception(stream.str()); } } else if (m_pType->GetValue() == LaserRangeFinder_Sick_LMS200 || m_pType->GetValue() == LaserRangeFinder_Sick_LMS291) { if (math::DoubleEqual(angularResolution, math::DegreesToRadians(0.25))) { m_pAngularResolution->SetValue(math::DegreesToRadians(0.25)); } else if (math::DoubleEqual(angularResolution, math::DegreesToRadians(0.50))) { m_pAngularResolution->SetValue(math::DegreesToRadians(0.50)); } else if (math::DoubleEqual(angularResolution, math::DegreesToRadians(1.00))) { m_pAngularResolution->SetValue(math::DegreesToRadians(1.00)); } else { std::stringstream stream; stream << "Invalid resolution for Sick LMS291: "; stream << angularResolution; throw Exception(stream.str()); } } else if (m_pType->GetValue() == LaserRangeFinder_Hokuyo_UTM_30LX) { if (math::DoubleEqual(angularResolution, math::DegreesToRadians(0.25))) { m_pAngularResolution->SetValue(math::DegreesToRadians(0.25)); } else { std::stringstream stream; stream << "Invalid resolution for Hokuyo_UTM_30LX: "; stream << angularResolution; throw Exception(stream.str()); } } else if (m_pType->GetValue() == LaserRangeFinder_Hokuyo_URG_04LX) { if (math::DoubleEqual(angularResolution, math::DegreesToRadians(0.352))) { m_pAngularResolution->SetValue(math::DegreesToRadians(0.352)); } else { std::stringstream stream; stream << "Invalid resolution for Hokuyo_URG_04LX: "; stream << angularResolution; throw Exception(stream.str()); } } else { throw Exception("Can't set angular resolution, please create a LaserRangeFinder of type Custom"); } Update(); } /** * Return Laser type */ inline kt_int32s GetType() { return m_pType->GetValue(); } /** * Gets the number of range readings each localized range scan must contain to be a valid scan. * @return number of range readings */ inline kt_int32u GetNumberOfRangeReadings() const { return m_NumberOfRangeReadings; } virtual kt_bool Validate() { Update(); if (math::InRange(GetRangeThreshold(), GetMinimumRange(), GetMaximumRange()) == false) { std::cout << "Please set range threshold to a value between [" << GetMinimumRange() << ";" << GetMaximumRange() << "]" << std::endl; return false; } return true; } virtual kt_bool Validate(SensorData* pSensorData); /** * Get point readings (potentially scale readings if given coordinate converter is not null) * @param pLocalizedRangeScan * @param pCoordinateConverter * @param ignoreThresholdPoints * @param flipY */ const PointVectorDouble GetPointReadings(LocalizedRangeScan* pLocalizedRangeScan, CoordinateConverter* pCoordinateConverter, kt_bool ignoreThresholdPoints = true, kt_bool flipY = false) const; public: /** * Create a laser range finder of the given type and ID * @param type * @param rName name of sensor - if no name is specified default name will be assigned * @return laser range finder */ static LaserRangeFinder* CreateLaserRangeFinder(LaserRangeFinderType type, const Name& rName) { LaserRangeFinder* pLrf = NULL; switch (type) { // see http://www.hizook.com/files/publications/SICK_LMS100.pdf // set range threshold to 18m case LaserRangeFinder_Sick_LMS100: { pLrf = new LaserRangeFinder((rName.GetName() != "") ? rName : Name("Sick LMS 100")); // Sensing range is 18 meters (at 10% reflectivity, max range of 20 meters), with an error of about 20mm pLrf->m_pMinimumRange->SetValue(0.0); pLrf->m_pMaximumRange->SetValue(20.0); // 270 degree range, 50 Hz pLrf->m_pMinimumAngle->SetValue(math::DegreesToRadians(-135)); pLrf->m_pMaximumAngle->SetValue(math::DegreesToRadians(135)); // 0.25 degree angular resolution pLrf->m_pAngularResolution->SetValue(math::DegreesToRadians(0.25)); pLrf->m_NumberOfRangeReadings = 1081; } break; // see http://www.hizook.com/files/publications/SICK_LMS200-291_Tech_Info.pdf // set range threshold to 10m case LaserRangeFinder_Sick_LMS200: { pLrf = new LaserRangeFinder((rName.GetName() != "") ? rName : Name("Sick LMS 200")); // Sensing range is 80 meters pLrf->m_pMinimumRange->SetValue(0.0); pLrf->m_pMaximumRange->SetValue(80.0); // 180 degree range, 75 Hz pLrf->m_pMinimumAngle->SetValue(math::DegreesToRadians(-90)); pLrf->m_pMaximumAngle->SetValue(math::DegreesToRadians(90)); // 0.5 degree angular resolution pLrf->m_pAngularResolution->SetValue(math::DegreesToRadians(0.5)); pLrf->m_NumberOfRangeReadings = 361; } break; // see http://www.hizook.com/files/publications/SICK_LMS200-291_Tech_Info.pdf // set range threshold to 30m case LaserRangeFinder_Sick_LMS291: { pLrf = new LaserRangeFinder((rName.GetName() != "") ? rName : Name("Sick LMS 291")); // Sensing range is 80 meters pLrf->m_pMinimumRange->SetValue(0.0); pLrf->m_pMaximumRange->SetValue(80.0); // 180 degree range, 75 Hz pLrf->m_pMinimumAngle->SetValue(math::DegreesToRadians(-90)); pLrf->m_pMaximumAngle->SetValue(math::DegreesToRadians(90)); // 0.5 degree angular resolution pLrf->m_pAngularResolution->SetValue(math::DegreesToRadians(0.5)); pLrf->m_NumberOfRangeReadings = 361; } break; // see http://www.hizook.com/files/publications/Hokuyo_UTM_LaserRangeFinder_LIDAR.pdf // set range threshold to 30m case LaserRangeFinder_Hokuyo_UTM_30LX: { pLrf = new LaserRangeFinder((rName.GetName() != "") ? rName : Name("Hokuyo UTM-30LX")); // Sensing range is 30 meters pLrf->m_pMinimumRange->SetValue(0.1); pLrf->m_pMaximumRange->SetValue(30.0); // 270 degree range, 40 Hz pLrf->m_pMinimumAngle->SetValue(math::DegreesToRadians(-135)); pLrf->m_pMaximumAngle->SetValue(math::DegreesToRadians(135)); // 0.25 degree angular resolution pLrf->m_pAngularResolution->SetValue(math::DegreesToRadians(0.25)); pLrf->m_NumberOfRangeReadings = 1081; } break; // see http://www.hizook.com/files/publications/HokuyoURG_Datasheet.pdf // set range threshold to 3.5m case LaserRangeFinder_Hokuyo_URG_04LX: { pLrf = new LaserRangeFinder((rName.GetName() != "") ? rName : Name("Hokuyo URG-04LX")); // Sensing range is 4 meters. It has detection problems when // scanning absorptive surfaces (such as black trimming). pLrf->m_pMinimumRange->SetValue(0.02); pLrf->m_pMaximumRange->SetValue(4.0); // 240 degree range, 10 Hz pLrf->m_pMinimumAngle->SetValue(math::DegreesToRadians(-120)); pLrf->m_pMaximumAngle->SetValue(math::DegreesToRadians(120)); // 0.352 degree angular resolution pLrf->m_pAngularResolution->SetValue(math::DegreesToRadians(0.352)); pLrf->m_NumberOfRangeReadings = 751; } break; case LaserRangeFinder_Custom: { pLrf = new LaserRangeFinder((rName.GetName() != "") ? rName : Name("User-Defined LaserRangeFinder")); // Sensing range is 80 meters. pLrf->m_pMinimumRange->SetValue(0.0); pLrf->m_pMaximumRange->SetValue(80.0); // 180 degree range pLrf->m_pMinimumAngle->SetValue(math::DegreesToRadians(-90)); pLrf->m_pMaximumAngle->SetValue(math::DegreesToRadians(90)); // 1.0 degree angular resolution pLrf->m_pAngularResolution->SetValue(math::DegreesToRadians(1.0)); pLrf->m_NumberOfRangeReadings = 181; } break; } if (pLrf != NULL) { pLrf->m_pType->SetValue(type); Pose2 defaultOffset; pLrf->SetOffsetPose(defaultOffset); } return pLrf; } private: /** * Constructs a LaserRangeFinder object with given ID */ LaserRangeFinder(const Name& rName) : Sensor(rName) , m_NumberOfRangeReadings(0) { m_pMinimumRange = new Parameter("MinimumRange", 0.0, GetParameterManager()); m_pMaximumRange = new Parameter("MaximumRange", 80.0, GetParameterManager()); m_pMinimumAngle = new Parameter("MinimumAngle", -KT_PI_2, GetParameterManager()); m_pMaximumAngle = new Parameter("MaximumAngle", KT_PI_2, GetParameterManager()); m_pAngularResolution = new Parameter("AngularResolution", math::DegreesToRadians(1), GetParameterManager()); m_pRangeThreshold = new Parameter("RangeThreshold", 12.0, GetParameterManager()); m_pType = new ParameterEnum("Type", LaserRangeFinder_Custom, GetParameterManager()); m_pType->DefineEnumValue(LaserRangeFinder_Custom, "Custom"); m_pType->DefineEnumValue(LaserRangeFinder_Sick_LMS100, "Sick_LMS100"); m_pType->DefineEnumValue(LaserRangeFinder_Sick_LMS200, "Sick_LMS200"); m_pType->DefineEnumValue(LaserRangeFinder_Sick_LMS291, "Sick_LMS291"); m_pType->DefineEnumValue(LaserRangeFinder_Hokuyo_UTM_30LX, "Hokuyo_UTM_30LX"); m_pType->DefineEnumValue(LaserRangeFinder_Hokuyo_URG_04LX, "Hokuyo_URG_04LX"); } /** * Set the number of range readings based on the minimum and * maximum angles of the sensor and the angular resolution */ void Update() { m_NumberOfRangeReadings = static_cast(math::Round((GetMaximumAngle() - GetMinimumAngle()) / GetAngularResolution()) + 1); } private: LaserRangeFinder(const LaserRangeFinder&); const LaserRangeFinder& operator=(const LaserRangeFinder&); private: // sensor m_Parameters Parameter* m_pMinimumAngle; Parameter* m_pMaximumAngle; Parameter* m_pAngularResolution; Parameter* m_pMinimumRange; Parameter* m_pMaximumRange; Parameter* m_pRangeThreshold; ParameterEnum* m_pType; kt_int32u m_NumberOfRangeReadings; // static std::string LaserRangeFinderTypeNames[6]; }; // LaserRangeFinder //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Enumerated type for valid grid cell states */ typedef enum { GridStates_Unknown = 0, GridStates_Occupied = 100, GridStates_Free = 255 } GridStates; //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * The CoordinateConverter class is used to convert coordinates between world and grid coordinates * In world coordinates 1.0 = 1 meter where 1 in grid coordinates = 1 pixel! * Default scale for coordinate converter is 20 that converters to 1 pixel = 0.05 meter */ class CoordinateConverter { public: /** * Default constructor */ CoordinateConverter() : m_Scale(20.0) { } public: /** * Scales the value * @param value * @return scaled value */ inline kt_double Transform(kt_double value) { return value * m_Scale; } /** * Converts the point from world coordinates to grid coordinates * @param rWorld world coordinate * @param flipY * @return grid coordinate */ inline Vector2 WorldToGrid(const Vector2& rWorld, kt_bool flipY = false) const { kt_double gridX = (rWorld.GetX() - m_Offset.GetX()) * m_Scale; kt_double gridY = 0.0; if (flipY == false) { gridY = (rWorld.GetY() - m_Offset.GetY()) * m_Scale; } else { gridY = (m_Size.GetHeight() / m_Scale - rWorld.GetY() + m_Offset.GetY()) * m_Scale; } return Vector2(static_cast(math::Round(gridX)), static_cast(math::Round(gridY))); } /** * Converts the point from grid coordinates to world coordinates * @param rGrid world coordinate * @param flipY * @return world coordinate */ inline Vector2 GridToWorld(const Vector2& rGrid, kt_bool flipY = false) const { kt_double worldX = m_Offset.GetX() + rGrid.GetX() / m_Scale; kt_double worldY = 0.0; if (flipY == false) { worldY = m_Offset.GetY() + rGrid.GetY() / m_Scale; } else { worldY = m_Offset.GetY() + (m_Size.GetHeight() - rGrid.GetY()) / m_Scale; } return Vector2(worldX, worldY); } /** * Gets the scale * @return scale */ inline kt_double GetScale() const { return m_Scale; } /** * Sets the scale * @param scale */ inline void SetScale(kt_double scale) { m_Scale = scale; } /** * Gets the offset * @return offset */ inline const Vector2& GetOffset() const { return m_Offset; } /** * Sets the offset * @param rOffset */ inline void SetOffset(const Vector2& rOffset) { m_Offset = rOffset; } /** * Sets the size * @param rSize */ inline void SetSize(const Size2& rSize) { m_Size = rSize; } /** * Gets the size * @return size */ inline const Size2& GetSize() const { return m_Size; } /** * Gets the resolution * @return resolution */ inline kt_double GetResolution() const { return 1.0 / m_Scale; } /** * Sets the resolution * @param resolution */ inline void SetResolution(kt_double resolution) { m_Scale = 1.0 / resolution; } /** * Gets the bounding box * @return bounding box */ inline BoundingBox2 GetBoundingBox() const { BoundingBox2 box; kt_double minX = GetOffset().GetX(); kt_double minY = GetOffset().GetY(); kt_double maxX = minX + GetSize().GetWidth() * GetResolution(); kt_double maxY = minY + GetSize().GetHeight() * GetResolution(); box.SetMinimum(GetOffset()); box.SetMaximum(Vector2(maxX, maxY)); return box; } private: Size2 m_Size; kt_double m_Scale; Vector2 m_Offset; }; // CoordinateConverter //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Defines a grid class */ template class Grid { public: /** * Creates a grid of given size and resolution * @param width * @param height * @param resolution * @return grid pointer */ static Grid* CreateGrid(kt_int32s width, kt_int32s height, kt_double resolution) { Grid* pGrid = new Grid(width, height); pGrid->GetCoordinateConverter()->SetScale(1.0 / resolution); return pGrid; } /** * Destructor */ virtual ~Grid() { delete [] m_pData; delete m_pCoordinateConverter; } public: /** * Clear out the grid data */ void Clear() { memset(m_pData, 0, GetDataSize() * sizeof(T)); } /** * Returns a clone of this grid * @return grid clone */ Grid* Clone() { Grid* pGrid = CreateGrid(GetWidth(), GetHeight(), GetResolution()); pGrid->GetCoordinateConverter()->SetOffset(GetCoordinateConverter()->GetOffset()); memcpy(pGrid->GetDataPointer(), GetDataPointer(), GetDataSize()); return pGrid; } /** * Resizes the grid (deletes all old data) * @param width * @param height */ virtual void Resize(kt_int32s width, kt_int32s height) { m_Width = width; m_Height = height; m_WidthStep = math::AlignValue(width, 8); if (m_pData != NULL) { delete[] m_pData; m_pData = NULL; } try { m_pData = new T[GetDataSize()]; if (m_pCoordinateConverter == NULL) { m_pCoordinateConverter = new CoordinateConverter(); } m_pCoordinateConverter->SetSize(Size2(width, height)); } catch(...) { m_pData = NULL; m_Width = 0; m_Height = 0; m_WidthStep = 0; } Clear(); } /** * Checks whether the given coordinates are valid grid indices * @param rGrid */ inline kt_bool IsValidGridIndex(const Vector2& rGrid) const { return (math::IsUpTo(rGrid.GetX(), m_Width) && math::IsUpTo(rGrid.GetY(), m_Height)); } /** * Gets the index into the data pointer of the given grid coordinate * @param rGrid * @param boundaryCheck default value is true * @return grid index */ virtual kt_int32s GridIndex(const Vector2& rGrid, kt_bool boundaryCheck = true) const { if (boundaryCheck == true) { if (IsValidGridIndex(rGrid) == false) { std::stringstream error; error << "Index " << rGrid << " out of range. Index must be between [0; " << m_Width << ") and [0; " << m_Height << ")"; throw Exception(error.str()); } } kt_int32s index = rGrid.GetX() + (rGrid.GetY() * m_WidthStep); if (boundaryCheck == true) { assert(math::IsUpTo(index, GetDataSize())); } return index; } /** * Gets the grid coordinate from an index * @param index * @return grid coordinate */ Vector2 IndexToGrid(kt_int32s index) const { Vector2 grid; grid.SetY(index / m_WidthStep); grid.SetX(index - grid.GetY() * m_WidthStep); return grid; } /** * Converts the point from world coordinates to grid coordinates * @param rWorld world coordinate * @param flipY * @return grid coordinate */ inline Vector2 WorldToGrid(const Vector2& rWorld, kt_bool flipY = false) const { return GetCoordinateConverter()->WorldToGrid(rWorld, flipY); } /** * Converts the point from grid coordinates to world coordinates * @param rGrid world coordinate * @param flipY * @return world coordinate */ inline Vector2 GridToWorld(const Vector2& rGrid, kt_bool flipY = false) const { return GetCoordinateConverter()->GridToWorld(rGrid, flipY); } /** * Gets pointer to data at given grid coordinate * @param rGrid grid coordinate * @return grid point */ T* GetDataPointer(const Vector2& rGrid) { kt_int32s index = GridIndex(rGrid, true); return m_pData + index; } /** * Gets pointer to data at given grid coordinate * @param rGrid grid coordinate * @return grid point */ T* GetDataPointer(const Vector2& rGrid) const { kt_int32s index = GridIndex(rGrid, true); return m_pData + index; } /** * Gets the width of the grid * @return width of the grid */ inline kt_int32s GetWidth() const { return m_Width; }; /** * Gets the height of the grid * @return height of the grid */ inline kt_int32s GetHeight() const { return m_Height; }; /** * Get the size as a Size2 * @return size of the grid */ inline const Size2 GetSize() const { return Size2(m_Width, m_Height); } /** * Gets the width step in bytes * @return width step */ inline kt_int32s GetWidthStep() const { return m_WidthStep; } /** * Gets the grid data pointer * @return data pointer */ inline T* GetDataPointer() { return m_pData; } /** * Gets const grid data pointer * @return data pointer */ inline T* GetDataPointer() const { return m_pData; } /** * Gets the allocated grid size in bytes * @return data size */ inline kt_int32s GetDataSize() const { return m_WidthStep * m_Height; } /** * Get value at given grid coordinate * @param rGrid grid coordinate * @return value */ inline T GetValue(const Vector2& rGrid) const { kt_int32s index = GridIndex(rGrid); return m_pData[index]; } /** * Gets the coordinate converter for this grid * @return coordinate converter */ inline CoordinateConverter* GetCoordinateConverter() const { return m_pCoordinateConverter; } /** * Gets the resolution * @return resolution */ inline kt_double GetResolution() const { return GetCoordinateConverter()->GetResolution(); } /** * Gets the grids bounding box * @return bounding box */ inline BoundingBox2 GetBoundingBox() const { return GetCoordinateConverter()->GetBoundingBox(); } /** * Increments all the grid cells from (x0, y0) to (x1, y1); * if applicable, apply f to each cell traced * @param x0 * @param y0 * @param x1 * @param y1 * @param f */ void TraceLine(kt_int32s x0, kt_int32s y0, kt_int32s x1, kt_int32s y1, Functor* f = NULL) { kt_bool steep = abs(y1 - y0) > abs(x1 - x0); if (steep) { std::swap(x0, y0); std::swap(x1, y1); } if (x0 > x1) { std::swap(x0, x1); std::swap(y0, y1); } kt_int32s deltaX = x1 - x0; kt_int32s deltaY = abs(y1 - y0); kt_int32s error = 0; kt_int32s ystep; kt_int32s y = y0; if (y0 < y1) { ystep = 1; } else { ystep = -1; } kt_int32s pointX; kt_int32s pointY; for (kt_int32s x = x0; x <= x1; x++) { if (steep) { pointX = y; pointY = x; } else { pointX = x; pointY = y; } error += deltaY; if (2 * error >= deltaX) { y += ystep; error -= deltaX; } Vector2 gridIndex(pointX, pointY); if (IsValidGridIndex(gridIndex)) { kt_int32s index = GridIndex(gridIndex, false); T* pGridPointer = GetDataPointer(); pGridPointer[index]++; if (f != NULL) { (*f)(index); } } } } protected: /** * Constructs grid of given size * @param width * @param height */ Grid(kt_int32s width, kt_int32s height) : m_pData(NULL) , m_pCoordinateConverter(NULL) { Resize(width, height); } private: kt_int32s m_Width; // width of grid kt_int32s m_Height; // height of grid kt_int32s m_WidthStep; // 8 bit aligned width of grid T* m_pData; // grid data // coordinate converter to convert between world coordinates and grid coordinates CoordinateConverter* m_pCoordinateConverter; }; // Grid //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * For making custom data */ class CustomData : public Object { public: // @cond EXCLUDE KARTO_Object(CustomData) // @endcond public: /** * Constructor */ CustomData() : Object() { } /** * Destructor */ virtual ~CustomData() { } public: /** * Write out this custom data as a string * @return string representation of this data object */ virtual const std::string Write() const = 0; /** * Read in this custom data from a string * @param rValue string representation of this data object */ virtual void Read(const std::string& rValue) = 0; private: CustomData(const CustomData&); const CustomData& operator=(const CustomData&); }; /** * Type declaration of CustomData vector */ typedef std::vector CustomDataVector; //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * SensorData is a base class for all sensor data */ class KARTO_EXPORT SensorData : public Object { public: // @cond EXCLUDE KARTO_Object(SensorData) // @endcond public: /** * Destructor */ virtual ~SensorData(); public: /** * Gets sensor data id * @return sensor id */ inline kt_int32s GetStateId() const { return m_StateId; } /** * Sets sensor data id * @param stateId id */ inline void SetStateId(kt_int32s stateId) { m_StateId = stateId; } /** * Gets sensor unique id * @return unique id */ inline kt_int32s GetUniqueId() const { return m_UniqueId; } /** * Sets sensor unique id * @param uniqueId */ inline void SetUniqueId(kt_int32u uniqueId) { m_UniqueId = uniqueId; } /** * Gets sensor data time * @return time */ inline kt_double GetTime() const { return m_Time; } /** * Sets sensor data time * @param time */ inline void SetTime(kt_double time) { m_Time = time; } /** * Get the sensor that created this sensor data * @return sensor */ inline const Name& GetSensorName() const { return m_SensorName; } /** * Add a CustomData object to sensor data * @param pCustomData */ inline void AddCustomData(CustomData* pCustomData) { m_CustomData.push_back(pCustomData); } /** * Get all custom data objects assigned to sensor data * @return CustomDataVector& */ inline const CustomDataVector& GetCustomData() const { return m_CustomData; } protected: /** * Construct a SensorData object with a sensor name */ SensorData(const Name& rSensorName); private: /** * Restrict the copy constructor */ SensorData(const SensorData&); /** * Restrict the assignment operator */ const SensorData& operator=(const SensorData&); private: /** * ID unique to individual sensor */ kt_int32s m_StateId; /** * ID unique across all sensor data */ kt_int32s m_UniqueId; /** * Sensor that created this sensor data */ Name m_SensorName; /** * Time the sensor data was created */ kt_double m_Time; CustomDataVector m_CustomData; }; //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Type declaration of range readings vector */ typedef std::vector RangeReadingsVector; /** * LaserRangeScan representing the range readings from a laser range finder sensor. */ class LaserRangeScan : public SensorData { public: // @cond EXCLUDE KARTO_Object(LaserRangeScan) // @endcond public: /** * Constructs a scan from the given sensor with the given readings * @param rSensorName */ LaserRangeScan(const Name& rSensorName) : SensorData(rSensorName) , m_pRangeReadings(NULL) , m_NumberOfRangeReadings(0) { } /** * Constructs a scan from the given sensor with the given readings * @param rSensorName * @param rRangeReadings */ LaserRangeScan(const Name& rSensorName, const RangeReadingsVector& rRangeReadings) : SensorData(rSensorName) , m_pRangeReadings(NULL) , m_NumberOfRangeReadings(0) { assert(rSensorName.ToString() != ""); SetRangeReadings(rRangeReadings); } /** * Destructor */ virtual ~LaserRangeScan() { delete [] m_pRangeReadings; } public: /** * Gets the range readings of this scan * @return range readings of this scan */ inline kt_double* GetRangeReadings() const { return m_pRangeReadings; } inline RangeReadingsVector GetRangeReadingsVector() const { return RangeReadingsVector(m_pRangeReadings, m_pRangeReadings + m_NumberOfRangeReadings); } /** * Sets the range readings for this scan * @param rRangeReadings */ inline void SetRangeReadings(const RangeReadingsVector& rRangeReadings) { // ignore for now! XXXMAE BUGUBUG 05/21/2010 << TODO(lucbettaieb): What the heck is this?? // if (rRangeReadings.size() != GetNumberOfRangeReadings()) // { // std::stringstream error; // error << "Given number of readings (" << rRangeReadings.size() // << ") does not match expected number of range finder (" << GetNumberOfRangeReadings() << ")"; // throw Exception(error.str()); // } if (!rRangeReadings.empty()) { if (rRangeReadings.size() != m_NumberOfRangeReadings) { // delete old readings delete [] m_pRangeReadings; // store size of array! m_NumberOfRangeReadings = static_cast(rRangeReadings.size()); // allocate range readings m_pRangeReadings = new kt_double[m_NumberOfRangeReadings]; } // copy readings kt_int32u index = 0; const_forEach(RangeReadingsVector, &rRangeReadings) { m_pRangeReadings[index++] = *iter; } } else { delete [] m_pRangeReadings; m_pRangeReadings = NULL; } } /** * Gets the laser range finder sensor that generated this scan * @return laser range finder sensor of this scan */ inline LaserRangeFinder* GetLaserRangeFinder() const { return SensorManager::GetInstance()->GetSensorByName(GetSensorName()); } /** * Gets the number of range readings * @return number of range readings */ inline kt_int32u GetNumberOfRangeReadings() const { return m_NumberOfRangeReadings; } private: LaserRangeScan(const LaserRangeScan&); const LaserRangeScan& operator=(const LaserRangeScan&); private: kt_double* m_pRangeReadings; kt_int32u m_NumberOfRangeReadings; }; // LaserRangeScan //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * DrivePose representing the pose value of a drive sensor. */ class DrivePose : public SensorData { public: // @cond EXCLUDE KARTO_Object(DrivePose) // @endcond public: /** * Constructs a pose of the given drive sensor * @param rSensorName */ DrivePose(const Name& rSensorName) : SensorData(rSensorName) { } /** * Destructor */ virtual ~DrivePose() { } public: /** * Gets the odometric pose of this scan * @return odometric pose of this scan */ inline const Pose3& GetOdometricPose() const { return m_OdometricPose; } /** * Sets the odometric pose of this scan * @param rPose */ inline void SetOdometricPose(const Pose3& rPose) { m_OdometricPose = rPose; } private: DrivePose(const DrivePose&); const DrivePose& operator=(const DrivePose&); private: /** * Odometric pose of robot */ Pose3 m_OdometricPose; }; // class DrivePose //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * The LocalizedRangeScan contains range data from a single sweep of a laser range finder sensor * in a two-dimensional space and position information. The odometer position is the position * reported by the robot when the range data was recorded. The corrected position is the position * calculated by the mapper (or localizer) * LocalizedRangeScan包含二维空间中激光测距仪传感器单次扫描的距离数据和位置信息。 * 里程表位置是记录里程数据时机器人报告的位置。校正后的位置是由映射器(或定位器)计算的位置 */ class LocalizedRangeScan : public LaserRangeScan { public: // @cond EXCLUDE KARTO_Object(LocalizedRangeScan) // @endcond public: /** * Constructs a range scan from the given range finder with the given readings */ LocalizedRangeScan(const Name& rSensorName, const RangeReadingsVector& rReadings) : LaserRangeScan(rSensorName, rReadings) , m_IsDirty(true) { } /** * Destructor */ virtual ~LocalizedRangeScan() { } private: mutable boost::shared_mutex m_Lock; public: /** * Gets the odometric pose of this scan * @return odometric pose of this scan */ inline const Pose2& GetOdometricPose() const { return m_OdometricPose; } /** * Sets the odometric pose of this scan * @param rPose */ inline void SetOdometricPose(const Pose2& rPose) { m_OdometricPose = rPose; } /** * Gets the (possibly corrected) robot pose at which this scan was taken. The corrected robot pose of the scan * is usually set by an external module such as a localization or mapping module when it is determined * that the original pose was incorrect. The external module will set the correct pose based on * additional sensor data and any context information it has. If the pose has not been corrected, * a call to this method returns the same pose as GetOdometricPose(). * @return corrected pose */ inline const Pose2& GetCorrectedPose() const { return m_CorrectedPose; } /** * Moves the scan by moving the robot pose to the given location. * @param rPose new pose of the robot of this scan */ inline void SetCorrectedPose(const Pose2& rPose) { m_CorrectedPose = rPose; m_IsDirty = true; } /** * Gets barycenter of point readings */ inline const Pose2& GetBarycenterPose() const { boost::shared_lock lock(m_Lock); if (m_IsDirty) { // throw away constness and do an update! lock.unlock(); boost::unique_lock uniqueLock(m_Lock); const_cast(this)->Update(); } return m_BarycenterPose; } /** * Gets barycenter if the given parameter is true, otherwise returns the scanner pose * @param useBarycenter * @return barycenter if parameter is true, otherwise scanner pose */ inline Pose2 GetReferencePose(kt_bool useBarycenter) const { boost::shared_lock lock(m_Lock); if (m_IsDirty) { // throw away constness and do an update! lock.unlock(); boost::unique_lock uniqueLock(m_Lock); const_cast(this)->Update(); } return useBarycenter ? GetBarycenterPose() : GetSensorPose(); } /** * Computes the position of the sensor * @return scan pose */ inline Pose2 GetSensorPose() const { return GetSensorAt(m_CorrectedPose); } /** * Computes the robot pose given the corrected scan pose * @param rScanPose pose of the sensor */ void SetSensorPose(const Pose2& rScanPose) { Pose2 deviceOffsetPose2 = GetLaserRangeFinder()->GetOffsetPose(); kt_double offsetLength = deviceOffsetPose2.GetPosition().Length(); kt_double offsetHeading = deviceOffsetPose2.GetHeading(); kt_double angleoffset = atan2(deviceOffsetPose2.GetY(), deviceOffsetPose2.GetX()); kt_double correctedHeading = math::NormalizeAngle(rScanPose.GetHeading()); Pose2 worldSensorOffset = Pose2(offsetLength * cos(correctedHeading + angleoffset - offsetHeading), offsetLength * sin(correctedHeading + angleoffset - offsetHeading), offsetHeading); m_CorrectedPose = rScanPose - worldSensorOffset; Update(); } /** * Computes the position of the sensor if the robot were at the given pose * @param rPose * @return sensor pose */ inline Pose2 GetSensorAt(const Pose2& rPose) const { return Transform(rPose).TransformPose(GetLaserRangeFinder()->GetOffsetPose()); } /** * Gets the bounding box of this scan * @return bounding box of this scan */ inline const BoundingBox2& GetBoundingBox() const { boost::shared_lock lock(m_Lock); if (m_IsDirty) { // throw away constness and do an update! lock.unlock(); boost::unique_lock uniqueLock(m_Lock); const_cast(this)->Update(); } return m_BoundingBox; } /** * Get point readings in local coordinates */ inline const PointVectorDouble& GetPointReadings(kt_bool wantFiltered = false) const { boost::shared_lock lock(m_Lock); if (m_IsDirty) { // throw away constness and do an update! lock.unlock(); boost::unique_lock uniqueLock(m_Lock); const_cast(this)->Update(); } if (wantFiltered == true) { return m_PointReadings; } else { return m_UnfilteredPointReadings; } } private: /** * Compute point readings based on range readings * Only range readings within [minimum range; range threshold] are returned */ virtual void Update() { LaserRangeFinder* pLaserRangeFinder = GetLaserRangeFinder(); if (pLaserRangeFinder != NULL) { m_PointReadings.clear(); m_UnfilteredPointReadings.clear(); kt_double rangeThreshold = pLaserRangeFinder->GetRangeThreshold(); kt_double minimumAngle = pLaserRangeFinder->GetMinimumAngle(); kt_double angularResolution = pLaserRangeFinder->GetAngularResolution(); Pose2 scanPose = GetSensorPose(); // compute point readings Vector2 rangePointsSum; kt_int32u beamNum = 0; for (kt_int32u i = 0; i < pLaserRangeFinder->GetNumberOfRangeReadings(); i++, beamNum++) { kt_double rangeReading = GetRangeReadings()[i]; if (!math::InRange(rangeReading, pLaserRangeFinder->GetMinimumRange(), rangeThreshold)) { kt_double angle = scanPose.GetHeading() + minimumAngle + beamNum * angularResolution; Vector2 point; point.SetX(scanPose.GetX() + (rangeReading * cos(angle))); point.SetY(scanPose.GetY() + (rangeReading * sin(angle))); m_UnfilteredPointReadings.push_back(point); continue; } kt_double angle = scanPose.GetHeading() + minimumAngle + beamNum * angularResolution; Vector2 point; point.SetX(scanPose.GetX() + (rangeReading * cos(angle))); point.SetY(scanPose.GetY() + (rangeReading * sin(angle))); m_PointReadings.push_back(point); m_UnfilteredPointReadings.push_back(point); rangePointsSum += point; } // compute barycenter kt_double nPoints = static_cast(m_PointReadings.size()); if (nPoints != 0.0) { Vector2 averagePosition = Vector2(rangePointsSum / nPoints); m_BarycenterPose = Pose2(averagePosition, 0.0); } else { m_BarycenterPose = scanPose; } // calculate bounding box of scan m_BoundingBox = BoundingBox2(); m_BoundingBox.Add(scanPose.GetPosition()); forEach(PointVectorDouble, &m_PointReadings) { m_BoundingBox.Add(*iter); } } m_IsDirty = false; } private: LocalizedRangeScan(const LocalizedRangeScan&); const LocalizedRangeScan& operator=(const LocalizedRangeScan&); private: /** * Odometric pose of robot */ Pose2 m_OdometricPose; /** * Corrected pose of robot calculated by mapper (or localizer) */ Pose2 m_CorrectedPose; protected: /** * Average of all the point readings */ Pose2 m_BarycenterPose; /** * Vector of point readings */ PointVectorDouble m_PointReadings; /** * Vector of unfiltered point readings */ PointVectorDouble m_UnfilteredPointReadings; /** * Bounding box of localized range scan */ BoundingBox2 m_BoundingBox; /** * Internal flag used to update point readings, barycenter and bounding box */ kt_bool m_IsDirty; }; // LocalizedRangeScan /** * Type declaration of LocalizedRangeScan vector */ typedef std::vector LocalizedRangeScanVector; //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * The LocalizedRangeScanWithPoints is an extension of the LocalizedRangeScan with precomputed points. */ class LocalizedRangeScanWithPoints : public LocalizedRangeScan { public: // @cond EXCLUDE KARTO_Object(LocalizedRangeScanWithPoints) // @endcond public: /** * Constructs a range scan from the given range finder with the given readings. Precomptued points should be * in the robot frame. */ LocalizedRangeScanWithPoints(const Name& rSensorName, const RangeReadingsVector& rReadings, const PointVectorDouble& rPoints) : LocalizedRangeScan(rSensorName, rReadings), m_Points(rPoints) { } /** * Destructor */ virtual ~LocalizedRangeScanWithPoints() { } private: /** * Update the points based on the latest sensor pose. */ void Update() { m_PointReadings.clear(); m_UnfilteredPointReadings.clear(); Pose2 scanPose = GetSensorPose(); Pose2 robotPose = GetCorrectedPose(); // update point readings Vector2 rangePointsSum; for (kt_int32u i = 0; i < m_Points.size(); i++) { // check if this has a NaN if (!std::isfinite(m_Points[i].GetX()) || !std::isfinite(m_Points[i].GetY())) { Vector2 point(m_Points[i].GetX(), m_Points[i].GetY()); m_UnfilteredPointReadings.push_back(point); continue; } // transform into world coords Pose2 pointPose(m_Points[i].GetX(), m_Points[i].GetY(), 0); Pose2 result = Transform(robotPose).TransformPose(pointPose); Vector2 point(result.GetX(), result.GetY()); m_PointReadings.push_back(point); m_UnfilteredPointReadings.push_back(point); rangePointsSum += point; } // compute barycenter kt_double nPoints = static_cast(m_PointReadings.size()); if (nPoints != 0.0) { Vector2 averagePosition = Vector2(rangePointsSum / nPoints); m_BarycenterPose = Pose2(averagePosition, 0.0); } else { m_BarycenterPose = scanPose; } // calculate bounding box of scan m_BoundingBox = BoundingBox2(); m_BoundingBox.Add(scanPose.GetPosition()); forEach(PointVectorDouble, &m_PointReadings) { m_BoundingBox.Add(*iter); } m_IsDirty = false; } private: LocalizedRangeScanWithPoints(const LocalizedRangeScanWithPoints&); const LocalizedRangeScanWithPoints& operator=(const LocalizedRangeScanWithPoints&); private: const PointVectorDouble m_Points; }; // LocalizedRangeScanWithPoints //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// class OccupancyGrid; class KARTO_EXPORT CellUpdater : public Functor { public: CellUpdater(OccupancyGrid* pGrid) : m_pOccupancyGrid(pGrid) { } /** * Destructor */ virtual ~CellUpdater() {} /** * Updates the cell at the given index based on the grid's hits and pass counters * @param index */ virtual void operator() (kt_int32u index); private: OccupancyGrid* m_pOccupancyGrid; }; // CellUpdater /** * Occupancy grid definition. See GridStates for possible grid values. */ class OccupancyGrid : public Grid { friend class CellUpdater; friend class IncrementalOccupancyGrid; public: /** * Constructs an occupancy grid of given size * @param width * @param height * @param rOffset * @param resolution */ OccupancyGrid(kt_int32s width, kt_int32s height, const Vector2& rOffset, kt_double resolution) : Grid(width, height) , m_pCellPassCnt(Grid::CreateGrid(0, 0, resolution)) , m_pCellHitsCnt(Grid::CreateGrid(0, 0, resolution)) , m_pCellUpdater(NULL) { m_pCellUpdater = new CellUpdater(this); if (karto::math::DoubleEqual(resolution, 0.0)) { throw Exception("Resolution cannot be 0"); } m_pMinPassThrough = new Parameter("MinPassThrough", 2); m_pOccupancyThreshold = new Parameter("OccupancyThreshold", 0.1); GetCoordinateConverter()->SetScale(1.0 / resolution); GetCoordinateConverter()->SetOffset(rOffset); } /** * Destructor */ virtual ~OccupancyGrid() { delete m_pCellUpdater; delete m_pCellPassCnt; delete m_pCellHitsCnt; delete m_pMinPassThrough; delete m_pOccupancyThreshold; } public: /** * Create an occupancy grid from the given scans using the given resolution * @param rScans * @param resolution */ static OccupancyGrid* CreateFromScans(const LocalizedRangeScanVector& rScans, kt_double resolution) { if (rScans.empty()) { return NULL; } kt_int32s width, height; Vector2 offset; ComputeDimensions(rScans, resolution, width, height, offset); OccupancyGrid* pOccupancyGrid = new OccupancyGrid(width, height, offset, resolution); pOccupancyGrid->CreateFromScans(rScans); return pOccupancyGrid; } /** * Make a clone * @return occupancy grid clone */ OccupancyGrid* Clone() const { OccupancyGrid* pOccupancyGrid = new OccupancyGrid(GetWidth(), GetHeight(), GetCoordinateConverter()->GetOffset(), 1.0 / GetCoordinateConverter()->GetScale()); memcpy(pOccupancyGrid->GetDataPointer(), GetDataPointer(), GetDataSize()); pOccupancyGrid->GetCoordinateConverter()->SetSize(GetCoordinateConverter()->GetSize()); pOccupancyGrid->m_pCellPassCnt = m_pCellPassCnt->Clone(); pOccupancyGrid->m_pCellHitsCnt = m_pCellHitsCnt->Clone(); return pOccupancyGrid; } /** * Check if grid point is free * @param rPose * @return whether the cell at the given point is free space */ virtual kt_bool IsFree(const Vector2& rPose) const { kt_int8u* pOffsets = reinterpret_cast(GetDataPointer(rPose)); if (*pOffsets == GridStates_Free) { return true; } return false; } /** * Casts a ray from the given point (up to the given max range) * and returns the distance to the closest obstacle * @param rPose2 * @param maxRange * @return distance to closest obstacle */ virtual kt_double RayCast(const Pose2& rPose2, kt_double maxRange) const { double scale = GetCoordinateConverter()->GetScale(); kt_double x = rPose2.GetX(); kt_double y = rPose2.GetY(); kt_double theta = rPose2.GetHeading(); kt_double sinTheta = sin(theta); kt_double cosTheta = cos(theta); kt_double xStop = x + maxRange * cosTheta; kt_double xSteps = 1 + fabs(xStop - x) * scale; kt_double yStop = y + maxRange * sinTheta; kt_double ySteps = 1 + fabs(yStop - y) * scale; kt_double steps = math::Maximum(xSteps, ySteps); kt_double delta = maxRange / steps; kt_double distance = delta; for (kt_int32u i = 1; i < steps; i++) { kt_double x1 = x + distance * cosTheta; kt_double y1 = y + distance * sinTheta; Vector2 gridIndex = WorldToGrid(Vector2(x1, y1)); if (IsValidGridIndex(gridIndex) && IsFree(gridIndex)) { distance = (i + 1) * delta; } else { break; } } return (distance < maxRange)? distance : maxRange; } /** * Sets the minimum number of beams that must pass through a cell before it * will be considered to be occupied or unoccupied. * This prevents stray beams from messing up the map. */ void SetMinPassThrough(kt_int32u count) { m_pMinPassThrough->SetValue(count); } /** * Sets the minimum ratio of beams hitting cell to beams passing through * cell for cell to be marked as occupied. */ void SetOccupancyThreshold(kt_double thresh) { m_pOccupancyThreshold->SetValue(thresh); } protected: /** * Get cell hit grid * @return Grid* */ virtual Grid* GetCellHitsCounts() { return m_pCellHitsCnt; } /** * Get cell pass grid * @return Grid* */ virtual Grid* GetCellPassCounts() { return m_pCellPassCnt; } protected: /** * Calculate grid dimensions from localized range scans * @param rScans * @param resolution * @param rWidth * @param rHeight * @param rOffset */ static void ComputeDimensions(const LocalizedRangeScanVector& rScans, kt_double resolution, kt_int32s& rWidth, kt_int32s& rHeight, Vector2& rOffset) { BoundingBox2 boundingBox; const_forEach(LocalizedRangeScanVector, &rScans) { boundingBox.Add((*iter)->GetBoundingBox()); } kt_double scale = 1.0 / resolution; Size2 size = boundingBox.GetSize(); rWidth = static_cast(math::Round(size.GetWidth() * scale)); rHeight = static_cast(math::Round(size.GetHeight() * scale)); rOffset = boundingBox.GetMinimum(); } /** * Create grid using scans * @param rScans */ virtual void CreateFromScans(const LocalizedRangeScanVector& rScans) { m_pCellPassCnt->Resize(GetWidth(), GetHeight()); m_pCellPassCnt->GetCoordinateConverter()->SetOffset(GetCoordinateConverter()->GetOffset()); m_pCellHitsCnt->Resize(GetWidth(), GetHeight()); m_pCellHitsCnt->GetCoordinateConverter()->SetOffset(GetCoordinateConverter()->GetOffset()); const_forEach(LocalizedRangeScanVector, &rScans) { LocalizedRangeScan* pScan = *iter; AddScan(pScan); } Update(); } /** * Adds the scan's information to this grid's counters (optionally * update the grid's cells' occupancy status) * @param pScan * @param doUpdate whether to update the grid's cell's occupancy status * @return returns false if an endpoint fell off the grid, otherwise true */ virtual kt_bool AddScan(LocalizedRangeScan* pScan, kt_bool doUpdate = false) { kt_double rangeThreshold = pScan->GetLaserRangeFinder()->GetRangeThreshold(); kt_double maxRange = pScan->GetLaserRangeFinder()->GetMaximumRange(); kt_double minRange = pScan->GetLaserRangeFinder()->GetMinimumRange(); Vector2 scanPosition = pScan->GetSensorPose().GetPosition(); // get scan point readings const PointVectorDouble& rPointReadings = pScan->GetPointReadings(false); kt_bool isAllInMap = true; // draw lines from scan position to all point readings int pointIndex = 0; const_forEachAs(PointVectorDouble, &rPointReadings, pointsIter) { Vector2 point = *pointsIter; kt_double rangeReading = pScan->GetRangeReadings()[pointIndex]; kt_bool isEndPointValid = rangeReading < (rangeThreshold - KT_TOLERANCE); if (rangeReading <= minRange || rangeReading >= maxRange || std::isnan(rangeReading)) { // ignore these readings pointIndex++; continue; } else if (rangeReading >= rangeThreshold) { // trace up to range reading kt_double ratio = rangeThreshold / rangeReading; kt_double dx = point.GetX() - scanPosition.GetX(); kt_double dy = point.GetY() - scanPosition.GetY(); point.SetX(scanPosition.GetX() + ratio * dx); point.SetY(scanPosition.GetY() + ratio * dy); } kt_bool isInMap = RayTrace(scanPosition, point, isEndPointValid, doUpdate); if (!isInMap) { isAllInMap = false; } pointIndex++; } return isAllInMap; } /** * Traces a beam from the start position to the end position marking * the bookkeeping arrays accordingly. * @param rWorldFrom start position of beam * @param rWorldTo end position of beam * @param isEndPointValid is the reading within the range threshold? * @param doUpdate whether to update the cells' occupancy status immediately * @return returns false if an endpoint fell off the grid, otherwise true */ virtual kt_bool RayTrace(const Vector2& rWorldFrom, const Vector2& rWorldTo, kt_bool isEndPointValid, kt_bool doUpdate = false) { assert(m_pCellPassCnt != NULL && m_pCellHitsCnt != NULL); Vector2 gridFrom = m_pCellPassCnt->WorldToGrid(rWorldFrom); Vector2 gridTo = m_pCellPassCnt->WorldToGrid(rWorldTo); CellUpdater* pCellUpdater = doUpdate ? m_pCellUpdater : NULL; m_pCellPassCnt->TraceLine(gridFrom.GetX(), gridFrom.GetY(), gridTo.GetX(), gridTo.GetY(), pCellUpdater); // for the end point if (isEndPointValid) { if (m_pCellPassCnt->IsValidGridIndex(gridTo)) { kt_int32s index = m_pCellPassCnt->GridIndex(gridTo, false); kt_int32u* pCellPassCntPtr = m_pCellPassCnt->GetDataPointer(); kt_int32u* pCellHitCntPtr = m_pCellHitsCnt->GetDataPointer(); // increment cell pass through and hit count pCellPassCntPtr[index]++; pCellHitCntPtr[index]++; if (doUpdate) { (*m_pCellUpdater)(index); } } } return m_pCellPassCnt->IsValidGridIndex(gridTo); } /** * Updates a single cell's value based on the given counters * @param pCell * @param cellPassCnt * @param cellHitCnt */ virtual void UpdateCell(kt_int8u* pCell, kt_int32u cellPassCnt, kt_int32u cellHitCnt) { if (cellPassCnt > m_pMinPassThrough->GetValue()) { kt_double hitRatio = static_cast(cellHitCnt) / static_cast(cellPassCnt); if (hitRatio > m_pOccupancyThreshold->GetValue()) { *pCell = GridStates_Occupied; } else { *pCell = GridStates_Free; } } } /** * Update the grid based on the values in m_pCellHitsCnt and m_pCellPassCnt */ virtual void Update() { assert(m_pCellPassCnt != NULL && m_pCellHitsCnt != NULL); // clear grid Clear(); // set occupancy status of cells kt_int8u* pDataPtr = GetDataPointer(); kt_int32u* pCellPassCntPtr = m_pCellPassCnt->GetDataPointer(); kt_int32u* pCellHitCntPtr = m_pCellHitsCnt->GetDataPointer(); kt_int32u nBytes = GetDataSize(); for (kt_int32u i = 0; i < nBytes; i++, pDataPtr++, pCellPassCntPtr++, pCellHitCntPtr++) { UpdateCell(pDataPtr, *pCellPassCntPtr, *pCellHitCntPtr); } } /** * Resizes the grid (deletes all old data) * @param width * @param height */ virtual void Resize(kt_int32s width, kt_int32s height) { Grid::Resize(width, height); m_pCellPassCnt->Resize(width, height); m_pCellHitsCnt->Resize(width, height); } protected: /** * Counters of number of times a beam passed through a cell */ Grid* m_pCellPassCnt; /** * Counters of number of times a beam ended at a cell */ Grid* m_pCellHitsCnt; private: /** * Restrict the copy constructor */ OccupancyGrid(const OccupancyGrid&); /** * Restrict the assignment operator */ const OccupancyGrid& operator=(const OccupancyGrid&); private: CellUpdater* m_pCellUpdater; //////////////////////////////////////////////////////////// // NOTE: These two values are dependent on the resolution. If the resolution is too small, // then not many beams will hit the cell! // Number of beams that must pass through a cell before it will be considered to be occupied // or unoccupied. This prevents stray beams from messing up the map. Parameter* m_pMinPassThrough; // Minimum ratio of beams hitting cell to beams passing through cell for cell to be marked as occupied Parameter* m_pOccupancyThreshold; }; // OccupancyGrid //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Dataset info * Contains title, author and other information about the dataset */ class DatasetInfo : public Object { public: // @cond EXCLUDE KARTO_Object(DatasetInfo) // @endcond public: DatasetInfo() : Object() { m_pTitle = new Parameter("Title", "", GetParameterManager()); m_pAuthor = new Parameter("Author", "", GetParameterManager()); m_pDescription = new Parameter("Description", "", GetParameterManager()); m_pCopyright = new Parameter("Copyright", "", GetParameterManager()); } virtual ~DatasetInfo() { } public: /** * Dataset title */ const std::string& GetTitle() const { return m_pTitle->GetValue(); } /** * Dataset author(s) */ const std::string& GetAuthor() const { return m_pAuthor->GetValue(); } /** * Dataset description */ const std::string& GetDescription() const { return m_pDescription->GetValue(); } /** * Dataset copyrights */ const std::string& GetCopyright() const { return m_pCopyright->GetValue(); } private: DatasetInfo(const DatasetInfo&); const DatasetInfo& operator=(const DatasetInfo&); private: Parameter* m_pTitle; Parameter* m_pAuthor; Parameter* m_pDescription; Parameter* m_pCopyright; }; // class DatasetInfo //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Karto dataset. Stores LaserRangeFinders and LocalizedRangeScans and manages memory of allocated LaserRangeFinders * and LocalizedRangeScans */ class Dataset { public: /** * Default constructor */ Dataset() : m_pDatasetInfo(NULL) { } /** * Destructor */ virtual ~Dataset() { Clear(); } public: /** * Adds object to this dataset * @param pObject */ void Add(Object* pObject) { if (pObject != NULL) { if (dynamic_cast(pObject)) { Sensor* pSensor = dynamic_cast(pObject); if (pSensor != NULL) { m_SensorNameLookup[pSensor->GetName()] = pSensor; karto::SensorManager::GetInstance()->RegisterSensor(pSensor); } m_Objects.push_back(pObject); } else if (dynamic_cast(pObject)) { SensorData* pSensorData = dynamic_cast(pObject); m_Objects.push_back(pSensorData); } else if (dynamic_cast(pObject)) { m_pDatasetInfo = dynamic_cast(pObject); } else { m_Objects.push_back(pObject); } } } /** * Get sensor states * @return sensor state */ inline const ObjectVector& GetObjects() const { return m_Objects; } /** * Get dataset info * @return dataset info */ inline DatasetInfo* GetDatasetInfo() { return m_pDatasetInfo; } /** * Delete all stored data */ virtual void Clear() { for (std::map::iterator iter = m_SensorNameLookup.begin(); iter != m_SensorNameLookup.end(); ++iter) { karto::SensorManager::GetInstance()->UnregisterSensor(iter->second); } forEach(ObjectVector, &m_Objects) { delete *iter; } m_Objects.clear(); if (m_pDatasetInfo != NULL) { delete m_pDatasetInfo; m_pDatasetInfo = NULL; } } private: std::map m_SensorNameLookup; ObjectVector m_Objects; DatasetInfo* m_pDatasetInfo; }; // Dataset //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * An array that can be resized as long as the size * does not exceed the initial capacity */ class LookupArray { public: /** * Constructs lookup array */ LookupArray() : m_pArray(NULL) , m_Capacity(0) , m_Size(0) { } /** * Destructor */ virtual ~LookupArray() { assert(m_pArray != NULL); delete[] m_pArray; m_pArray = NULL; } public: /** * Clear array */ void Clear() { memset(m_pArray, 0, sizeof(kt_int32s) * m_Capacity); } /** * Gets size of array * @return array size */ kt_int32u GetSize() const { return m_Size; } /** * Sets size of array (resize if not big enough) * @param size */ void SetSize(kt_int32u size) { assert(size != 0); if (size > m_Capacity) { if (m_pArray != NULL) { delete [] m_pArray; } m_Capacity = size; m_pArray = new kt_int32s[m_Capacity]; } m_Size = size; } /** * Gets reference to value at given index * @param index * @return reference to value at index */ inline kt_int32s& operator [] (kt_int32u index) { assert(index < m_Size); return m_pArray[index]; } /** * Gets value at given index * @param index * @return value at index */ inline kt_int32s operator [] (kt_int32u index) const { assert(index < m_Size); return m_pArray[index]; } /** * Gets array pointer * @return array pointer */ inline kt_int32s* GetArrayPointer() { return m_pArray; } /** * Gets array pointer * @return array pointer */ inline kt_int32s* GetArrayPointer() const { return m_pArray; } private: kt_int32s* m_pArray; kt_int32u m_Capacity; kt_int32u m_Size; }; // LookupArray //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// /** * Create lookup tables for point readings at varying angles in grid. * For each angle, grid indexes are calculated for each range reading. * This is to speed up finding best angle/position for a localized range scan * * Used heavily in mapper and localizer. * * In the localizer, this is a huge speed up for calculating possible position. For each particle, * a probability is calculated. The range scan is the same, but all grid indexes at all possible angles are * calculated. So when calculating the particle probability at a specific angle, the index table is used * to look up probability in probability grid! * */ template class GridIndexLookup { public: /** * Construct a GridIndexLookup with a grid * @param pGrid */ GridIndexLookup(Grid* pGrid) : m_pGrid(pGrid) , m_Capacity(0) , m_Size(0) , m_ppLookupArray(NULL) { } /** * Destructor */ virtual ~GridIndexLookup() { DestroyArrays(); } public: /** * Gets the lookup array for a particular angle index * @param index * @return lookup array */ const LookupArray* GetLookupArray(kt_int32u index) const { assert(math::IsUpTo(index, m_Size)); return m_ppLookupArray[index]; } /** * Get angles * @return std::vector& angles */ const std::vector& GetAngles() const { return m_Angles; } /** * Compute lookup table of the points of the given scan for the given angular space * @param pScan the scan * @param angleCenter * @param angleOffset computes lookup arrays for the angles within this offset around angleStart * @param angleResolution how fine a granularity to compute lookup arrays in the angular space */ void ComputeOffsets(LocalizedRangeScan* pScan, kt_double angleCenter, kt_double angleOffset, kt_double angleResolution) { assert(angleOffset != 0.0); assert(angleResolution != 0.0); kt_int32u nAngles = static_cast(math::Round(angleOffset * 2.0 / angleResolution) + 1); SetSize(nAngles); ////////////////////////////////////////////////////// // convert points into local coordinates of scan pose const PointVectorDouble& rPointReadings = pScan->GetPointReadings(); // compute transform to scan pose Transform transform(pScan->GetSensorPose()); Pose2Vector localPoints; const_forEach(PointVectorDouble, &rPointReadings) { // do inverse transform to get points in local coordinates Pose2 vec = transform.InverseTransformPose(Pose2(*iter, 0.0)); localPoints.push_back(vec); } ////////////////////////////////////////////////////// // create lookup array for different angles kt_double angle = 0.0; kt_double startAngle = angleCenter - angleOffset; for (kt_int32u angleIndex = 0; angleIndex < nAngles; angleIndex++) { angle = startAngle + angleIndex * angleResolution; ComputeOffsets(angleIndex, angle, localPoints, pScan); } // assert(math::DoubleEqual(angle, angleCenter + angleOffset)); } private: /** * Compute lookup value of points for given angle * @param angleIndex * @param angle * @param rLocalPoints */ void ComputeOffsets(kt_int32u angleIndex, kt_double angle, const Pose2Vector& rLocalPoints, LocalizedRangeScan* pScan) { m_ppLookupArray[angleIndex]->SetSize(static_cast(rLocalPoints.size())); m_Angles.at(angleIndex) = angle; // set up point array by computing relative offsets to points readings // when rotated by given angle const Vector2& rGridOffset = m_pGrid->GetCoordinateConverter()->GetOffset(); kt_double cosine = cos(angle); kt_double sine = sin(angle); kt_int32u readingIndex = 0; kt_int32s* pAngleIndexPointer = m_ppLookupArray[angleIndex]->GetArrayPointer(); kt_double maxRange = pScan->GetLaserRangeFinder()->GetMaximumRange(); const_forEach(Pose2Vector, &rLocalPoints) { const Vector2& rPosition = iter->GetPosition(); if (std::isnan(pScan->GetRangeReadings()[readingIndex]) || std::isinf(pScan->GetRangeReadings()[readingIndex])) { pAngleIndexPointer[readingIndex] = INVALID_SCAN; readingIndex++; continue; } // counterclockwise rotation and that rotation is about the origin (0, 0). Vector2 offset; offset.SetX(cosine * rPosition.GetX() - sine * rPosition.GetY()); offset.SetY(sine * rPosition.GetX() + cosine * rPosition.GetY()); // have to compensate for the grid offset when getting the grid index Vector2 gridPoint = m_pGrid->WorldToGrid(offset + rGridOffset); // use base GridIndex to ignore ROI kt_int32s lookupIndex = m_pGrid->Grid::GridIndex(gridPoint, false); pAngleIndexPointer[readingIndex] = lookupIndex; readingIndex++; } assert(readingIndex == rLocalPoints.size()); } /** * Sets size of lookup table (resize if not big enough) * @param size */ void SetSize(kt_int32u size) { assert(size != 0); if (size > m_Capacity) { if (m_ppLookupArray != NULL) { DestroyArrays(); } m_Capacity = size; m_ppLookupArray = new LookupArray*[m_Capacity]; for (kt_int32u i = 0; i < m_Capacity; i++) { m_ppLookupArray[i] = new LookupArray(); } } m_Size = size; m_Angles.resize(size); } /** * Delete the arrays */ void DestroyArrays() { for (kt_int32u i = 0; i < m_Capacity; i++) { delete m_ppLookupArray[i]; } delete[] m_ppLookupArray; m_ppLookupArray = NULL; } private: Grid* m_pGrid; kt_int32u m_Capacity; kt_int32u m_Size; LookupArray **m_ppLookupArray; // for sanity check std::vector m_Angles; }; // class GridIndexLookup //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// inline Pose2::Pose2(const Pose3& rPose) : m_Position(rPose.GetPosition().GetX(), rPose.GetPosition().GetY()) { kt_double t1, t2; // calculates heading from orientation rPose.GetOrientation().ToEulerAngles(m_Heading, t1, t2); } //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// // @cond EXCLUDE template inline void Object::SetParameter(const std::string& rName, T value) { AbstractParameter* pParameter = GetParameter(rName); if (pParameter != NULL) { std::stringstream stream; stream << value; pParameter->SetValueFromString(stream.str()); } else { throw Exception("Parameter does not exist: " + rName); } } template<> inline void Object::SetParameter(const std::string& rName, kt_bool value) { AbstractParameter* pParameter = GetParameter(rName); if (pParameter != NULL) { pParameter->SetValueFromString(value ? "true" : "false"); } else { throw Exception("Parameter does not exist: " + rName); } } // @endcond /*@}*/ } // namespace karto #endif // OPEN_KARTO_KARTO_H