描述: 本教程阐述一种在python中使用C++类的方法。
                               关键词: C++, Python, 绑定 
                              教程级别: 高级 
                              本教程演示了一种在 Python 中使用带有 ROS 消息的 
                                C++ 类的方法。使用 Boost Python 库。难点在于将用纯 Python 编写的 
                                ROS 消息的 Python 对象转换为等效的 C++ 实例。此转换将通过序列化/反序列化完成。源文件可在 
                                https://github.com/galou/python_bindings_tutorial 
                                中找到。 
                              另一种解决方案是使用经典的ROS服务,用C++编写的服务器将是C++类的包装器,客户端C++或Python将调用该服务。这里提出的解决方案不会创建 
                                ROS 节点,前提是要包装的类不使用 ros::NodeHandle。 
                              另一种解决方案是为所有需要的 ROS 消息及其依赖项编写包装器。一些显然被弃用的软件包为这个任务的自动化提出了一些解决方案:genpybindings 
                                和 boost_ python_ros。 
                           1没有 NodeHandle 的类 
                                因为 roscpp 在调用 rospy.init_node 时没有初始化。ros::NodeHandle 
                                实例不能在 C++ 类中使用,而不会生成错误。如果 C++ 不使用 ros::NodeHandle,这不是问题。 
                            1.1创建包并编写 C++ 类 
                                创建一个包并创建 C++ 类,我们将要为其进行 Python 绑定。此类使用 ROS 消息作为参数和返回类型。 
                             
                            
                                  1 catkin_create_pkg python_bindings_tutorial rospy roscpp std_msgs
   2 cd python_bindings_tutorial/include/python_bindings_tutorial
   3 touch add_two_ints.h
   4 rosed python_bindings_tutorial add_two_ints.h
 
  |   
 
                              
                              
                                include/python_bindings_tutorial/add_two_ints.h 
                                的内容为: 
                             
                            
                                  1 #ifndef PYTHON_BINDINGS_TUTORIAL_ADD_TWO_INTS_H
   2 #define PYTHON_BINDINGS_TUTORIAL_ADD_TWO_INTS_H
   3 
   4 #include <std_msgs/Int64.h>
   5 
   6 namespace python_bindings_tutorial {
   7 
   8 class AddTwoInts
   9 {
  10   public:
  11     std_msgs::Int64 add(const std_msgs::Int64& a, const std_msgs::Int64& b);
  12 };
  13 
  14 } 
  15 
  16 #endif 
  17 
  |   
                                
                              
                                将类实现写入 。 
                            
                            
                               
roscd python_bindings_tutorial/src
touch add_two_ints.cpp
rosed python_bindings_tutorial add_two_ints.cpp 
  |   
  
                              
                                src/add_two_ints.cpp的内容将是: 
                             
                            
                                  1 #include <python_bindings_tutorial/add_two_ints.h>
   2 
   3 using namespace python_bindings_tutorial;
   4 
   5 std_msgs::Int64 AddTwoInts::add(const std_msgs::Int64& a, const std_msgs::Int64& b)
   6 {
   7   std_msgs::Int64 sum;
   8   sum.data = a.data + b.data;
   9   return sum;
  10 }
  |   
 
                                 1.2绑定,C++ 部分 
                                绑定通过两个包装类进行,一个在 C++ 中,一个在 Python 中。C++ 包装将序列化内容的输入转换为 
                                C++ 消息实例,并将 C++ 消息实例的输出转换为序列化内容。 
                             
                            
                               
   1 roscd python_bindings_tutorial/src
   2 touch add_two_ints_wrapper.cpp
   3 rosed python_bindings_tutorial add_two_ints_wrapper.cpp
 
  |   
 
                              
                                src/add_two_ints_wrapper.cpp的内容将是: 
                              
                            
                                  1 #include <boost/python.hpp>
   2 
   3 #include <string>
   4 
   5 #include <ros/serialization.h>
   6 #include <std_msgs/Int64.h>
   7 
   8 #include <python_bindings_tutorial/add_two_ints.h>
   9 
  10 
  11 
  12 template <typename M>
  13 M from_python(const std::string str_msg)
  14 {
  15   size_t serial_size = str_msg.size();
  16   boost::shared_array<uint8_t> buffer(new uint8_t[serial_size]);
  17   for (size_t i = 0; i < serial_size; ++i)
  18   {
  19     buffer[i] = str_msg[i];
  20   }
  21   ros::serialization::IStream stream(buffer.get(), serial_size);
  22   M msg;
  23   ros::serialization::Serializer<M>::read(stream, msg);
  24   return msg;
  25 }
  26 
  27 
  28 
  29 template <typename M>
  30 std::string to_python(const M& msg)
  31 {
  32   size_t serial_size = ros::serialization::serializationLength(msg);
  33   boost::shared_array<uint8_t> buffer(new uint8_t[serial_size]);
  34   ros::serialization::OStream stream(buffer.get(), serial_size);
  35   ros::serialization::serialize(stream, msg);
  36   std::string str_msg;
  37   str_msg.reserve(serial_size);
  38   for (size_t i = 0; i < serial_size; ++i)
  39   {
  40     str_msg.push_back(buffer[i]);
  41   }
  42   return str_msg;
  43 }
  44 
  45 class AddTwoIntsWrapper : public python_bindings_tutorial::AddTwoInts
  46 {
  47   public:
  48     AddTwoIntsWrapper() : AddTwoInts() {}
  49 
  50     std::string add(const std::string& str_a, const std::string& str_b)
  51     {
  52       std_msgs::Int64 a = from_python<std_msgs::Int64>(str_a);
  53       std_msgs::Int64 b = from_python<std_msgs::Int64>(str_b);
  54       std_msgs::Int64 sum = AddTwoInts::add(a, b);
  55 
  56       return to_python(sum);
  57     }
  58 };
  59 
  60 BOOST_PYTHON_MODULE(_add_two_ints_wrapper_cpp)
  61 {
  62   boost::python::class_<AddTwoIntsWrapper>("AddTwoIntsWrapper", boost::python::init<>())
  63     .def("add", &AddTwoIntsWrapper::add)
  64     ;
  65 }
  |   
                               No code_block found 行以动态库的形式创建 Python 模块。模块的名称必须是 CMakeLists.txt 
                                中导出的库的名称。 
                             1.3绑定,Python 部分 
                                Python 包装器将 Python 消息实例的输入转换为序列化内容,并将序列化内容的输出转换为 
                                Python 消息实例。从 Python 序列化内容 (str) 到 C++ 序列化内容 (std::string) 
                                的转换是在 Boost Python 库中构建的。 
                             
                            
                               roscd python_bindings_tutorial/src
mkdir python_bindings_tutorial
roscd python_bindings_tutorial/src/python_bindings_tutorial
touch _add_two_ints_wrapper_py.py
rosed python_bindings_tutorial _add_two_ints_wrapper_py.py 
  |   
                              src/python_bindings_tutorial/_add_two_ints_wrapper_py.py 的内容将是 
                              
                            
                                  1 from StringIO import StringIO
   2 
   3 import rospy
   4 from std_msgs.msg import Int64
   5 
   6 from python_bindings_tutorial._add_two_ints_wrapper_cpp import AddTwoIntsWrapper
   7 
   8 
   9 class AddTwoInts(object):
  10     def __init__(self):
  11         self._add_two_ints = AddTwoIntsWrapper()
  12 
  13     def _to_cpp(self, msg):
  14         """Return a serialized string from a ROS message
  15 
  16         Parameters
  17         ----------
  18         - msg: a ROS message instance.
  19         """
  20         buf = StringIO()
  21         msg.serialize(buf)
  22         return buf.getvalue()
  23 
  24     def _from_cpp(self, str_msg, cls):
  25         """Return a ROS message from a serialized string
  26 
  27         Parameters
  28         ----------
  29         - str_msg: str, serialized message
  30         - cls: ROS message class, e.g. sensor_msgs.msg.LaserScan.
  31         """
  32         msg = cls()
  33         return msg.deserialize(str_msg)
  34 
  35     def add(self, a, b):
  36         """Add two std_mgs/Int64 messages
  37 
  38         Return a std_msgs/Int64 instance.
  39 
  40         Parameters
  41         ----------
  42         - a: a std_msgs/Int64 instance.
  43         - b: a std_msgs/Int64 instance.
  44         """
  45         if not isinstance(a, Int64):
  46             rospy.ROSException('Argument 1 is not a std_msgs/Int64')
  47         if not isinstance(b, Int64):
  48             rospy.ROSException('Argument 2 is not a std_msgs/Int64')
  49         str_a = self._to_cpp(a)
  50         str_b = self._to_cpp(b)
  51         str_sum = self._add_two_ints.add(str_a, str_b)
  52         return self._from_cpp(str_sum, Int64)
  |   
   
                              以便能够将类导入为python_bindings_tutorial。AddTwoInts,我们导入 
                                __init__.py 中的符号。首先,我们创建文件: 
                             
                            
                                  1 roscd python_bindings_tutorial/src/python_bindings_tutorial
   2 touch __init__.py
   3 rosed python_bindings_tutorial __init__.py
 
  |   
   
                              src/python_bindings_tutorial/__init__.py的内容为: 
                             
                            
                                  1 from python_bindings_tutorial._add_two_ints_wrapper_py import AddTwoInts
 
  |   
   
                            1.4将所有东西粘合在一起 
                                编辑 CMakeLists.txt (rosed python_bindings_tutorial 
                                CmakeLists.txt) 如下所示: 
                             
                            
                               cmake_minimum_required(VERSION 2.8.3)
project(python_bindings_tutorial)
find_package(catkin REQUIRED COMPONENTS
  roscpp
  roscpp_serialization
  std_msgs
)
## Both Boost.python and Python libs are required.
find_package(Boost REQUIRED COMPONENTS python)
find_package(PythonLibs 2.7 REQUIRED)
## Uncomment this if the package has a setup.py. This macro ensures
## modules and global scripts declared therein get installed
## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
catkin_python_setup()
###################################
## catkin specific configuration ##
###################################
catkin_package(
        INCLUDE_DIRS include
        LIBRARIES add_two_ints _add_two_ints_wrapper_cpp
        CATKIN_DEPENDS roscpp
        #  DEPENDS system_lib
)
###########
## Build ##
###########
# include Boost and Python.
include_directories(
        include
        ${catkin_INCLUDE_DIRS}
        ${Boost_INCLUDE_DIRS}
        ${PYTHON_INCLUDE_DIRS}
        )
## Declare a cpp library
add_library(add_two_ints src/add_two_ints.cpp)
add_library(_add_two_ints_wrapper_cpp src/add_two_ints_wrapper.cpp)
## Specify libraries to link a library or executable target against
target_link_libraries(add_two_ints ${catkin_LIBRARIES})
target_link_libraries(_add_two_ints_wrapper_cpp add_two_ints ${catkin_LIBRARIES} ${Boost_LIBRARIES})
# Don't prepend wrapper library name with lib and add to Python libs.
set_target_properties(_add_two_ints_wrapper_cpp PROPERTIES
        PREFIX ""
        LIBRARY_OUTPUT_DIRECTORY ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION}
        )
  |   
   
                              
                                c++ 包装库应与 Python 模块同名。如果由于某种原因需要不同目标名称,则可以使用 set_target_properties(_add_two_ints_wrapper_cpp 
                                PROPERTIES OUTPUT_NAME correct_library_name) 指定库名称。 
                              生产线 
                             
 
                                用于导出 Python 模块,并与文件相关联 setup.py 
                             
                            
                                  1 roscd python_bindings_tutorial
   2 touch setup.py
 
  |   
                              
                              
                                setup.py 的内容将是: 
                             
                            
                                  1 
   2 
   3 from distutils.core import setup
   4 from catkin_pkg.python_setup import generate_distutils_setup
   5 
   6 
   7 setup_args = generate_distutils_setup(
   8     packages=['python_bindings_tutorial'],
   9     package_dir={'': 'src'})
  10 
  11 setup(**setup_args)
  |   
 
                              
                                然后,我们使用 catkin_make 构建包。 
                          1.5测试绑定 
                                现在,您可以在 Python 脚本或 Python shell 中使用绑定 
                             
                            
                               
   1 from std_msgs.msg import Int64
   2 from python_bindings_tutorial import AddTwoInts
   3 a = Int64(4)
   4 b = Int64(2)
   5 addtwoints = AddTwoInts()
   6 sum = addtwoints.add(a, b)
   7 sum
 
  |   
                              
                             
                                2具有 NodeHandle 的类 
                                如前所述,对 rospy.init_node 的 Python 调用不会初始化 roscpp。如果未初始化 
                                roscpp,则实例化 ros::NodeHandle 将导致致命错误。moveit_ros_planning_interface为此提供了解决方案。在任何使用包装类的 
                                Python 脚本中,在实例化 AddTwoInts 之前需要添加两行: 
                             
                            
                                  1 from moveit_ros_planning_interface._moveit_roscpp_initializer import roscpp_init
   2 roscpp_init('node_name', [])
  |   
                              
                              
                                这将创建一个 ROS 节点。因此,与经典的 ROS 服务服务器/客户端实现相比,这种方法的优势并不像不需要 
                                ros::NodeHandle 的情况那样明显。 
                            3带有 ROS 消息容器的类 
                                如果类使用 ROS 消息的容器,则必须添加额外的转换步骤。此步骤并非特定于 ROS,而是 Boost 
                                Python 库的一部分。 
                            3.1“std::vector<M>”作为返回类型 
                                在定义 C++ 包装类的文件中,添加以下行: 
                             
                            
                                  1 
   2 template<class T>
   3 struct vector_to_python
   4 {
   5   static PyObject* convert(const std::vector<T>& vec)
   6   {
   7     boost::python::list* l = new boost::python::list();
   8     for(std::size_t i = 0; i < vec.size(); i++)
   9       (*l).append(vec[i]);
  10 
  11     return l->ptr();
  12   }
  13 };
  14 
  15 class Wrapper : public WrappedClass
  16 {
  17 
  18 
  19 
  20     std::vector<std::string> wrapper_fun(const std::string str_msg)
  21     {
  22       
  23     }
  24 
  25 };
  26 
  27 BOOST_PYTHON_MODULE(module_wrapper_cpp)
  28 {
  29   boost::python::class_<Wrapper>("Wrapper", bp::init<>())
  30     .def("fun", &Wrapper::wrapper_fun);
  31 
  32   boost::python::to_python_converter<std::vector<std::string, std::allocator<std::string> >, vector_to_python<std::string> >();
  33 }
  |   
 
                              
                                3.2 'std::vector<M>' 作为参数类型 
                                参见 Boost.Python 文档。  |