注釋表明Stream是個(gè)基類(lèi),輸入輸出流IStream和OStream都繼承自它。
Stream的成員變量data_是個(gè)指針,指向序列化的字節流開(kāi)始的位置,它的類(lèi)型是uint8_t。
在Ubuntu系統中,uint8_t的定義是typedef unsigned char uint8_t;
所以uint8_t就是一個(gè)字節,可以用size_of()函數檢驗。data_指向的空間就是保存字節流的。
輸出流類(lèi)OStream用來(lái)序列化一個(gè)對象,它引用了serialize函數,如下。
struct OStream : public Stream
{
static const StreamType stream_type = stream_types::Output;
OStream(uint8_t* data, uint32_t count) : Stream(data, count) {}
/* Serialize an item to this output stream*/
template<typename T>
ROS_FORCE_INLINE void next(const T& t)
{
serialize(*this, t);
}
template<typename T>
ROS_FORCE_INLINE OStream& operator<<(const T& t)
{
serialize(*this, t);
return *this;
}
};
輸入流類(lèi)IStream用來(lái)反序列化一個(gè)字節流,它引用了deserialize函數,如下。
struct ROSCPP_SERIALIZATION_DECL IStream : public Stream
{
static const StreamType stream_type = stream_types::Input;
IStream(uint8_t* data, uint32_t count) : Stream(data, count) {}
/* Deserialize an item from this input stream */
template<typename T>
ROS_FORCE_INLINE void next(T& t)
{
deserialize(*this, t);
}
template<typename T>
ROS_FORCE_INLINE IStream& operator>>(T& t)
{
deserialize(*this, t);
return *this;
}
};
自然,serialize函數和deserialize函數就是改變數據形式的地方,它們的定義在比較靠前的地方。它們都接收兩個(gè)模板,都是內聯(lián)函數,然后里面沒(méi)什么東西,只是又調用了Serializer類(lèi)的成員函數write和read。所以,serialize和deserialize函數就是個(gè)二道販子。
// Serialize an object. Stream here should normally be a ros::serialization::OStream
template<typename T, typename Stream>
inline void serialize(Stream& stream, const T& t)
{
Serializer
所以,我們來(lái)分析Serializer類(lèi),如下。我們發(fā)現,write和read函數又調用了類(lèi)型里的serialize函數和deserialize函數。
頭別暈,這里的serialize和deserialize函數跟上面的同名函數不是一回事。
注釋中說(shuō):“Specializing the Serializer class is the only thing you need to do to get the ROS serialization system to work with a type”(要想讓ROS的序列化功能適用于其它的某個(gè)類(lèi)型,你唯一需要做的就是特化這個(gè)Serializer類(lèi))。
這就涉及到的另一個(gè)知識點(diǎn)——模板特化(template specialization)。
template<typename T> struct Serializer
{
// Write an object to the stream. Normally the stream passed in here will be a ros::serialization::OStream
template<typename Stream>
inline static void write(Stream& stream, typename boost::call_traits
{
t.serialize(stream.getData(), 0);
}
// Read an object from the stream. Normally the stream passed in here will be a ros::serialization::IStream
template<typename Stream>
inline static void read(Stream& stream, typename boost::call_traits
{
t.deserialize(stream.getData());
}
// Determine the serialized length of an object.
inline static uint32_t serializedLength(typename boost::call_traits
{
return t.serializationLength();
}
};
接著(zhù)又定義了一個(gè)帶參數的宏函數ROS_CREATE_SIMPLE_SERIALIZER(Type),然后把這個(gè)宏作用到了ROS中的10種基本數據類(lèi)型,分別是:uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t, float, double。
說(shuō)明這10種數據類(lèi)型的處理方式都是類(lèi)似的??吹竭@里大家應該明白了,write和read函數都使用了memcpy函數進(jìn)行數據的移動(dòng)。
注意宏定義中的template<>語(yǔ)句,這正是模板特化的標志,關(guān)鍵詞template后面跟一對尖括號。
關(guān)于模板特化可以看這里。
#define ROS_CREATE_SIMPLE_SERIALIZER(Type) \\
template<> struct Serializer \\
{ \\
template
對于其它類(lèi)型的數據,例如bool、std::string、std::vector、ros::Time、ros::Duration、boost::array等等,它們各自的處理方式有細微的不同,所以不再用上面的宏函數,而是用模板特化的方式每種單獨定義,這也是為什么serialization.h這個(gè)文件這么冗長(cháng)。
對于int、double這種單個(gè)元素的數據,直接用上面特化的Serializer類(lèi)中的memcpy函數實(shí)現序列化。
對于vector、array這種多個(gè)元素的數據類(lèi)型怎么辦呢?方法是分成幾種情況,對于固定長(cháng)度簡(jiǎn)單類(lèi)型的(fixed-size simple types),還是用各自特化的Serializer類(lèi)中的memcpy函數實(shí)現,沒(méi)啥太大區別。
對于固定但是類(lèi)型不簡(jiǎn)單的(fixed-size non-simple types)或者既不固定也不簡(jiǎn)單的(non-fixed-size, non-simple types)或者固定但是不簡(jiǎn)單的(fixed-size, non-simple types),用for循環(huán)遍歷,一個(gè)元素一個(gè)元素的單獨處理。
那怎么判斷一個(gè)數據是不是固定是不是簡(jiǎn)單呢?這是在roscpp_traits文件夾中的message_traits.h完成的。
其中采用了萃取Type Traits,這是相對高級一點(diǎn)的編程技巧了,筆者也不太懂。
對序列化的介紹暫時(shí)就到這里了,有一些細節還沒(méi)講,等筆者看懂了再補。
2、消息訂閱發(fā)布
2.1ROS的本質(zhì)
如果問(wèn)ROS的本質(zhì)是什么,或者用一句話(huà)概括ROS的核心功能。那么,筆者認為ROS就是個(gè)通信庫,讓不同的程序節點(diǎn)能夠相互對話(huà)。
很多文章和書(shū)籍在介紹ROS是什么的時(shí)候,經(jīng)常使用“ROS是一個(gè)通信框架”這種描述。
但是筆者認為這種描述并不是太合適?!翱蚣堋笔莻€(gè)對初學(xué)者非常不友好的抽象詞匯,用一個(gè)更抽象難懂的概念去解釋一個(gè)本來(lái)就不清楚的概念,對初學(xué)者起不到任何幫助。
而且筆者嚴重懷疑絕大多數作者能對機器人的本質(zhì)或者軟件框架能有什么太深的理解,他們的見(jiàn)解不會(huì )比你我深刻多少。
既然提到本質(zhì),那我們就深入到最基本的問(wèn)題。
在接觸無(wú)窮的細節之前,我們不妨先做一個(gè)哲學(xué)層面的思考。
那就是,為什么ROS要解決通信問(wèn)題?
機器人涉及的東西千千萬(wàn)萬(wàn),機械、電子、軟件、人工智能無(wú)所不包,為什么底層的設計是一套用來(lái)通信的程序而不是別的東西。
到目前為止,我還沒(méi)有看到有人討論過(guò)這個(gè)問(wèn)題。這要回到機器人或者智能的本質(zhì)。
當我們在談?wù)摍C器人的時(shí)候,最首要的問(wèn)題不是硬件設計,而是對信息的處理。一個(gè)機器人需要哪些信息,信息從何而來(lái),如何傳遞,又被誰(shuí)使用,這些才是最重要的問(wèn)題。
人類(lèi)飛不鳥(niǎo),游不過(guò)魚(yú),跑不過(guò)馬,力不如牛,為什么卻自稱(chēng)萬(wàn)物之靈呢。
因為人有大腦,而且人類(lèi)大腦處理的信息更多更復雜。
拋開(kāi)物質(zhì),從信息的角度看,人與動(dòng)物、與機器人存在很多相似的地方。
機器人由許多功能模塊組成,它們之間需要協(xié)作才能形成一個(gè)有用的整體,機器人與機器人之間也需要協(xié)作才能形成一個(gè)有用的系統,要協(xié)作就離不開(kāi)通信。
需要什么樣的信息以及信息從何而來(lái)不是ROS首先關(guān)心的,因為這取決于機器人的應用場(chǎng)景。
因此,ROS首先要解決的是通信的問(wèn)題,即如何建立通信、用什么方式通信、通信的格式是什么等等一系列具體問(wèn)題。
帶著(zhù)這些問(wèn)題,我們看看ROS是如何設計的。
2.2客戶(hù)端庫
實(shí)現通信的代碼在ros_comm包中,如下。
其中clients文件夾一共有127個(gè)文件,看來(lái)是最大的包了。
現在我們來(lái)到了ROS最核心的地帶。
-
機器人
+關(guān)注
關(guān)注
207文章
27329瀏覽量
202163 -
操作系統
+關(guān)注
關(guān)注
37文章
6378瀏覽量
122212 -
ROS
+關(guān)注
關(guān)注
1文章
273瀏覽量
16783
發(fā)布評論請先 登錄
相關(guān)推薦
評論