1. 程式人生 > >u014587147的專欄

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

到這裡,我相信大家對名稱空間似懂非懂,這個東西到底是幹什麼的。如果你懂上面說的東西,接下來,看看引數的使用,似乎就懂名稱空間到底是幹什麼的了。