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