該節內容主要來自於官方文檔的兩個小節:
1.使用rosed來編輯
2.創建ros消息的服務
先來看rosed:
- rosed
rosed命令是rosbash的一部分,使用rosed可以直接編輯包中的一個文件,而無需鍵入文件所在的全路徑,用法如下:
$ rosed [package_name] [filename]
例如我們想編輯roscpp包下的Logger.msg,則輸入如下命令:
$ rosed roscpp Logger.msg
則會使用vim打開Logger.msg進入編輯狀態。
同時,rosed支持使用tab鍵實現自動完成功能,用法如下:$ rosed [package_name] <tab><tab>
例如:
$ rosed roscpp <tab><tab>
注意:兩個tab和包名之間有空格。
會列出roscpp包中的所有的文件:Empty.srv roscpp.cmake genmsg_cpp.py roscppConfig.cmake gensrv_cpp.py roscppConfig-version.cmake GetLoggers.srv roscpp-msg-extras.cmake Logger.msg roscpp-msg-paths.cmake msg_gen.py SetLoggerLevel.srv package.xml
並且,rosed還可以指定編輯器,默認的采用vim編輯器,可以通過設置~/.bashrc文件中的環境變量來指定編輯器:
例如輸入如下命令將編輯器設置為nano編輯器。export EDITOR='nano -w'
也可以使用如下命令設置為gedit:
export EDITOR='gedit -w'
- msg和srv文件介紹
msg文件每行包含一個類型和一個變量名:
其中,變量類型可以為如下類型:
- int8, int16, int32, int64 (以及uint)
- float32, float64
- string
- time, duration
- 其他消息類型
- 變長度以及定長度的數組[]
Header header string child_frame_id geometry_msgs/PoseWithCovariance pose geometry_msgs/TwistWithCovariance twist
srv文件定義與msg類似,也是由不同行組成,每行包含一個變量類型和一個變量名,不過srv包含請求(request)和響應(response)兩個部分,該兩部分用一行三短杠---分隔開。例如:
int64 A int64 B --- int64 Sum
在上面的例子中:A和B是請求,Sum是響應。
- 使用msg
- 創建msg
比如使用如下命令可以在之前的示例包里創建一個新msg:
$ roscd beginner_tutorials $ mkdir msg $ echo "int64 num" > msg/Num.msg
該msg只有一行,當然也可以創建一個更復雜的msg
string first_name string last_name uint8 age uint32 score
然后,為了確保該msg文件能被轉化為其他語言的代碼,需要檢查package.xml中如下兩行存在且未被注釋掉:(查看之后發現默認是注釋掉的)
<build_depend>message_generation</build_depend> <exec_depend>message_runtime</exec_depend>
在編譯期間,我們需要啟用message_generation,在運行期間,我們需要啟用message_runtime。
然后使用你最喜愛的編輯器打開CMakeLists.txt(可以使用rosed)
然后在find_package中添加message_generation的條目:## Find catkin macros and libraries ## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) ## is used, also find other catkin packages find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs message_generation )
同時,確保在運行時依賴中也添加了該消息的依賴:
catkin_package( # INCLUDE_DIRS include # LIBRARIES begginner_tutorials # CATKIN_DEPENDS roscpp rospy std_msgs # DEPENDS system_lib CATKIN_DEPENS message_runtime )
找到添加消息文件的代碼段:
# add_message_files( # FILES # Message1.msg # Message2.msg # )
取消注釋並添加該消息文件名:
add_message_files( FILES Num.msg )
確保generate_message被調用:
generate_messages( DEPENDENCIES std_msgs )
- 查看msg的信息
定義信息之后,可以用rosmsg命令來查看消息的信息:
$ rosmsg show [message type]
例如,對於上述定義的信息,用如下命令:
$ rosmsg show beginner_tutorials/Num
可以看到返回為:
int64 num
也可以不寫全路徑,如果你不記得該消息在哪個包下面,可以這樣寫:
$ rosmsg show Num
返回為:
[beginner_tutorials/Num]: int64 num
- 創建msg
- 使用srv
- 創建srv
類似於msg,我們先創建一個srv的文件夾:
$ roscd beginner_tutorials $ mkdir srv
可以在其中手動寫一個srv文件,也可以將其他地方的srv文件復制過來,在此處介紹一個ros下面復制文件的命令,roscp,該命令用法如下:
$ roscp [package_name] [file_to_copy_path] [copy_path]
然后我們將rospy_tutorials包中的srv文件復制過來:
$ roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv
同時在package.xml中啟用message_generation和message_runtime(該步與msg相同)。
然后在find_package的調用中增加message_generation(與msg中相同)。
不同的是在add_service_files的調用中要添加與服務對應的條目。add_service_files( FILES AddTwoInts.srv )
- 查看srv的信息
與rosmsg類似,ros也提供了rossrv來查看srv服務相關的信息:
例如:
$ rossrv show beginner_tutorials/AddTwoInts
返回:
int64 a int64 b --- int64 sum
也可以不加路徑:
$ rossrv show AddTwoInts [beginner_tutorials/AddTwoInts]: int64 a int64 b --- int64 sum [rospy_tutorials/AddTwoInts]: int64 a int64 b --- int64 sum
- 創建srv
- 通用步驟(個人感覺應該該節改名為編譯步驟)
如果進行完了如上步驟,然后就可以編譯這些msg和srv,取消CMakeLists.txt中以下幾行的注釋:
generate_messages( DEPENDENCIES std_msgs )
然后運行catkin_make進行編譯:
# In your catkin workspace $ roscd beginner_tutorials $ cd ../.. $ catkin_make install $ cd -
則會將msg和srv文件編譯成其他語言的代碼。例如,對於msg,C++的頭文件會包含在~/catkin_ws/devel/include/beginner_tutorials/中;
python的源文件會包含在~/catkin_ws/devel/lib/python2.7/dist-packages/beginner_tutorials/msg中;
~/catkin_ws/devel/share/common-lisp/ros/beginner_tutorials/msg/中。
srv文件對於C++會和msg文件生成的源文件在統一文件夾中,而python和lisp的srv文件生成的文件會在單獨的srv文件夾中,該文件夾與msg文件夾在同一個目錄下。
看一個C++的msg頭文件:// Generated by gencpp from file begginner_tutorials/Num.msg // DO NOT EDIT! #ifndef BEGGINNER_TUTORIALS_MESSAGE_NUM_H #define BEGGINNER_TUTORIALS_MESSAGE_NUM_H #include <string> #include <vector> #include <map> #include <ros/types.h> #include <ros/serialization.h> #include <ros/builtin_message_traits.h> #include <ros/message_operations.h> namespace begginner_tutorials { template <class ContainerAllocator> struct Num_ { typedef Num_<ContainerAllocator> Type; Num_() : num(0) { } Num_(const ContainerAllocator& _alloc) : num(0) { (void)_alloc; } typedef int64_t _num_type; _num_type num; typedef boost::shared_ptr< ::begginner_tutorials::Num_<ContainerAllocator> > Ptr; typedef boost::shared_ptr< ::begginner_tutorials::Num_<ContainerAllocator> const> ConstPtr; }; // struct Num_ typedef ::begginner_tutorials::Num_<std::allocator<void> > Num; typedef boost::shared_ptr< ::begginner_tutorials::Num > NumPtr; typedef boost::shared_ptr< ::begginner_tutorials::Num const> NumConstPtr; // constants requiring out of line definition template<typename ContainerAllocator> std::ostream& operator<<(std::ostream& s, const ::begginner_tutorials::Num_<ContainerAllocator> & v) { ros::message_operations::Printer< ::begginner_tutorials::Num_<ContainerAllocator> >::stream(s, "", v); return s; } } // namespace begginner_tutorials namespace ros { namespace message_traits { // BOOLTRAITS {'IsFixedSize': True, 'IsMessage': True, 'HasHeader': False} // {'std_msgs': ['/opt/ros/kinetic/share/std_msgs/cmake/../msg'], 'begginner_tutorials': ['/home/shao/catkin_ws/src/begginner_tutorials/msg']} // !!!!!!!!!!! ['__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_parsed_fields', 'constants', 'fields', 'full_name', 'has_header', 'header_present', 'names', 'package', 'parsed_fields', 'short_name', 'text', 'types'] template <class ContainerAllocator> struct IsFixedSize< ::begginner_tutorials::Num_<ContainerAllocator> > : TrueType { }; template <class ContainerAllocator> struct IsFixedSize< ::begginner_tutorials::Num_<ContainerAllocator> const> : TrueType { }; template <class ContainerAllocator> struct IsMessage< ::begginner_tutorials::Num_<ContainerAllocator> > : TrueType { }; template <class ContainerAllocator> struct IsMessage< ::begginner_tutorials::Num_<ContainerAllocator> const> : TrueType { }; template <class ContainerAllocator> struct HasHeader< ::begginner_tutorials::Num_<ContainerAllocator> > : FalseType { }; template <class ContainerAllocator> struct HasHeader< ::begginner_tutorials::Num_<ContainerAllocator> const> : FalseType { }; template<class ContainerAllocator> struct MD5Sum< ::begginner_tutorials::Num_<ContainerAllocator> > { static const char* value() { return "57d3c40ec3ac3754af76a83e6e73127a"; } static const char* value(const ::begginner_tutorials::Num_<ContainerAllocator>&) { return value(); } static const uint64_t static_value1 = 0x57d3c40ec3ac3754ULL; static const uint64_t static_value2 = 0xaf76a83e6e73127aULL; }; template<class ContainerAllocator> struct DataType< ::begginner_tutorials::Num_<ContainerAllocator> > { static const char* value() { return "begginner_tutorials/Num"; } static const char* value(const ::begginner_tutorials::Num_<ContainerAllocator>&) { return value(); } }; template<class ContainerAllocator> struct Definition< ::begginner_tutorials::Num_<ContainerAllocator> > { static const char* value() { return "int64 num\n\ "; } static const char* value(const ::begginner_tutorials::Num_<ContainerAllocator>&) { return value(); } }; } // namespace message_traits } // namespace ros namespace ros { namespace serialization { template<class ContainerAllocator> struct Serializer< ::begginner_tutorials::Num_<ContainerAllocator> > { template<typename Stream, typename T> inline static void allInOne(Stream& stream, T m) { stream.next(m.num); } ROS_DECLARE_ALLINONE_SERIALIZER }; // struct Num_ } // namespace serialization } // namespace ros namespace ros { namespace message_operations { template<class ContainerAllocator> struct Printer< ::begginner_tutorials::Num_<ContainerAllocator> > { template<typename Stream> static void stream(Stream& s, const std::string& indent, const ::begginner_tutorials::Num_<ContainerAllocator>& v) { s << indent << "num: "; Printer<int64_t>::stream(s, indent + " ", v.num); } }; } // namespace message_operations } // namespace ros #endif // BEGGINNER_TUTORIALS_MESSAGE_NUM_H
還是挺復雜的。