u014587147的專欄
已經學ROS快兩個月了,一開始對ROS 名稱空間,引數,引數伺服器,重對映沒認真看,後來發現很重要,它是學習ROS程式碼的基礎。我們都知道ros以topic通訊,但是隻靠topic通訊是遠遠不夠的,於是使用客服端伺服器、actionlib、引數伺服器來補充,這幾種都是通訊的機制。但還不夠,為了方便又加入了名稱空間,重對映。只有清楚了這些,其實才算對ros有了初步瞭解,下面的幾個文章我將分別介紹。
在介紹之前,我先抱怨一下,對於只學過C語言的我,沒有寫過什麼專案,然後自己學習ROS,簡直痛苦。想找幾個資料,英文的根本看不懂,四級都沒過,找中文的基本都是直譯過來的,而且大多數人的理解,或多或少的都有點錯誤,當然,我的理解應該有點錯誤的,希望明白之人能指出,感激不盡。
在C++中的名稱空間,using namespace std,std就是名空間,這裡的名稱空間跟它差不多,但是比它更多,一層巢狀一層,用的特別靈活。先舉個簡單的例子,然後細說。
我們先執行一個小烏龜:
rosrun turtlesim turtlesim_node
turtlesim是一個包名,turtlesim_node是執行檔名,可以與節點名相同,也不可以不同。這裡的節點名是 /turtlesim。說明可能初始化的時候是這樣的,為什麼是可能,因為重對映可以改變:
ros::init(argc, argv, "turtlesim");
節點名前面加個“/”,這意味這個節點是全域性空間的。另外說下,節點是有型別的,節點的型別就是節點所在包名和可執行檔名的加。一般的定義節點控制代碼:
ros::NodeHandle n;
NodeHandle是一個類,然後我們例項化的一個物件n.這種情況預設n這個節點是全域性名稱空間下的。你可以認為"/"這個斜槓就是全域性名空間。現在我們來改變這個節點的名空間,通過重對映,我們先用重對映,以後再講。
你會發現,這個節點的名變為/my/turtlesim,此時,節點所在的空間發生了變化,我們把這個節點從全域性名空間放進了一個/my的名空間下。於是節點內的所有引數,服務,話題也都放在了/my的名稱空間下。
你會發現,它所釋出的和訂閱的話題,服務都在這個名空間,這意味著它現在只會訂閱和釋出這個名空間下的話題。
這個時候執行rosrun turtlesim turtle_teleop_key ,是不會控制小烏龜的,因為它只會釋出/turtel1/cmd_vel話題,不會發布/my/turtel1/cmd_vel。
這個時候你可能懂一點了,節點,話題,服務,包括引數都是有名空間的,這個名空間約束著是它們的範圍。這個時候你可能會想,那不在一個名空間節點是不是就無法通訊了,顯然這種想法是錯誤的,這違背了我們通訊的初衷,我們通過重對映和引數伺服器,很容易讓不同名稱空間的節點都能通訊。好吧,先簡單的通過重對映使他們通訊,具體下一篇文再講,這一篇主要記錄名稱空間。
看到沒,重對映不僅可以把名稱空間改變,還可以將釋出的話題給改變(還可改變引數的空間),這樣/my空間下的烏龜就可以移動了。當然你也可以直接將這個節點的名空間改為/my,這樣他們在同一個名稱空間,當然可以直接通訊了。
上面說的是全域性名稱空間,還有一個相對名稱空間,/turtle1/cmd_vel就是相對名稱空間,它在/my的名稱空間下,釋出/turtle1/cmd_vel話題,解析器會把它解析成/my/turtle1/cmd_vel話題。釋出的/turtle1/cmd_vel話題相對/my空間下是/turtle1/cmd_vel話題,對於全域性它是/my/turtle1/cmd_vel話題。
接下來我們看看節點控制代碼的建構函式,也可以跳過:
namespace ros
{
class NodeHandleBackingCollection;
/**
* \brief roscpp's interface for creating subscribers, publishers, etc.
*
* This class is used for writing nodes. It provides a RAII interface
* to this process' node, in that when the first NodeHandle is
* created, it instantiates everything necessary for this node, and
* when the last NodeHandle goes out of scope it shuts down the node.
*
* NodeHandle uses reference counting internally, and copying a
* NodeHandle is very lightweight.
*
* You must call one of the ros::init functions prior to instantiating
* this class.
*
* The most widely used methods are:
* - Setup:
* - ros::init()
* - Publish / subscribe messaging:
* - advertise()
* - subscribe()
* - RPC services:
* - advertiseService()
* - serviceClient()
* - ros::service::call()
* - Parameters:
* - getParam()
* - setParam()
*/
class ROSCPP_DECL NodeHandle
{
public:
/**
* \brief Constructor
*
* When a NodeHandle is constructed, it checks to see if the global
* node state has already been started. If so, it increments a
* global reference count. If not, it starts the node with
* ros::start() and sets the reference count to 1.
*
* \param ns Namespace for this NodeHandle. This acts in addition to any namespace assigned to this ROS node.
* eg. If the node's namespace is "/a" and the namespace passed in here is "b", all
* topics/services/parameters will be prefixed with "/a/b/"
* \param remappings Remappings for this NodeHandle.
* \throws InvalidNameException if the namespace is not a valid graph resource name
*/
NodeHandle(const std::string& ns = std::string(), const M_string& remappings = M_string());
NodeHandle(const NodeHandle& rhs);
NodeHandle(const NodeHandle& parent, const std::string& ns);
NodeHandle(const NodeHandle& parent, const std::string& ns, const M_string& remappings);
~NodeHandle();
}
是不是發現他有多個建構函式,
ros::init(argc, argv, "my_node_name");
ros::NodeHandle nh("/my_node_handle_namespace");
我們建立節點控制代碼nh時,指明瞭它的名稱空間,所以nh的名稱空間為/my_node_handle_namespace,
所以這個節點裡的所有的引數,話題前面都應該有/my_node_handle_namespace
最後一種名稱空間是私有名稱空間,私有名稱,以一個波浪字元(~)開始。和相對名稱一樣,私有名稱並不能完全確定它們自身所在的名稱空間,而是需要ROS 客戶端庫將這個名稱解析為一個全域性名稱。與相對名稱的主要差別在於,私有名稱不是用當前預設名稱空間,而是用的它們節點名稱作為名稱空間。
ros::init(argc, argv, "my_node_name");
ros::NodeHandle nh1("~");
ros::NodeHandle nh2("~foo");
ros::Subscriber sub1 = nh1.subscribe("my_private_topic", ...);
ros::Subscriber sub2 = nh2.subscribe("my_private_topic", ...);sub1訂閱的話題是my_node_name/my_private_topic
sub2訂閱的話題是my_node_name/foo/my_private_topic
到這裡,我相信大家對名稱空間似懂非懂,這個東西到底是幹什麼的。如果你懂上面說的東西,接下來,看看引數的使用,似乎就懂名稱空間到底是幹什麼的了。