ROS学习记录(二)

1、创建ROS消息和ROS服务

消息(msg): msg文件就是一个描述ROS中所使用消息类型的简单文本。它们会被用来生成不同语言的源代码。
服务(srv): 一个srv文件描述一项服务。它包含两个部分:请求和响应。
msg文件存放在package的msg目录下,srv文件则存放在srv目录下。
msg文件实际上就是每行声明一个数据类型和变量名。可以使用的数据类型如下:
int8, int16, int32, int64 (plus uint*)
float32, float64
string
time, duration
other msg files
variable-length array[] and fixed-length array[C]
在ROS中有一个特殊的数据类型:Header,它含有时间戳和坐标系信息。(PS:上面的数据类型中没用Header,也没有kill等等的,感觉把header理解为结构体或者类还更好点?.)

1.1、使用msg

先创建一个msg:
在这里插入图片描述
想直接用roscd 进入的,但是没想到找不到。。
在这里插入图片描述
吐了,吐了,每次重启电脑,想用roscd,就要source setup.bash一次。
永久设置source setup.bash方法:添加环境setup.bash到bashrc
gedit ~/.bashrc(打开文件)
在文件最后加上source ~/catkin_ws/devel/setup.bash并保存。
然后运行source ~/.bashrc使之生效。

重启看看,还用要source setup.bash后,roscd才找得到文件吗?
在这里插入图片描述
事实证明是有效果的,这个.bashrc文件是ubuntu系统运行的时候要把上面的命令都运行一遍,我们把source setup.bash加在上面,相当于ubuntu启动就把这条命令运行了一遍。
接下来接着创建msg:
mkdir msg(创建文件夹)
echo “int64 num” > msg/Num.msg(输入一条语句到Num.msg里面)
查看package.xml, 确保它包含一下两条语句,没有的话就加上去。

  <build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>

在这里插入图片描述
打开CMakeLists.txt文件,
增加:
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs message_generation)
在catkin_package里面增加CATKIN_DEPENDS message_runtime
增加或者把add_message_files那段被#注释的修改成
add_message_files(
FILES
Num.msg
)
增加generate_messages()(这段别加,加了后面就是这段出错,因为后面会加上一个generate_messages())
(PS:CMakeLists.txt文件里面都有模板这样的,照着改就行了)
在这里插入图片描述
使用rosmsg show beginner_tutorials/Num检查ROS是否能够识消息。
在这里插入图片描述

1.2使用srv

创建一个srv:
roscd beginner_tutorials(进入文件夹)
mkdir srv(创建文件夹)
roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv(复制一个服务文件)
同样打开CMakeLists.txt文件
在find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation)这段添加message_generation
增加
add_service_files(
FILES
AddTwoInts.srv
)
在这里插入图片描述
输入rossrv show beginner_tutorials/AddTwoInts检查ROS是否能够识该服务。
在这里插入图片描述
继续打开CMakeLists.txt文件
添加下面这段或者把下面这段前面的#号去除
generate_messages(
DEPENDENCIES
std_msgs
)
在这里插入图片描述
回到catkin_ws目录下运行catkin_make
在这里插入图片描述
报错,CMakeLists.txt文件有问题,generate_messages重复,在文件的222行。
把generate_messages删除,运行成功。
在这里插入图片描述

2、编写消息发布器和订阅器

切换到beginner_tutorials路径下
mkdir -p ~/catkin_ws/src/beginner_tutorials/src(创建文件夹,然而实际上是有的因为以前创建过,虽然我也忘了啥时候创建的)
创建src/talker.cpp 文件
本来准备用rosed来创建的,但是不知道怎么弄。还是直接code吧。
安装个中文插件。
在这里插入图片描述
复制粘贴下面内容:

#include "ros/ros.h"
#include "std_msgs/String.h" 
#include <sstream> 
   /**
    * This tutorial demonstrates simple sending of messages over the ROS system.
    */
   //这是一个回调函数,当接收到 chatter 话题的时候就会被调用。
   //消息是以 boost shared_ptr 指针的形式传输,意味着可以存储它而又不需要复制数据。
   void chatterCallback(const std_msgs::String::ConstPtr& msg){
     ROS_INFO("I heard: [%s]", msg->data.c_str());
   }
   int main(int argc, char **argv)
   {
     /**
      * The ros::init() function needs to see argc and argv so that it can perform
      * any ROS arguments and name remapping that were provided at the command line. For programmatic
      * remappings you can use a different version of init() which takes remappings
      * directly, but for most command-line programs, passing argc and argv is the easiest
      * way to do it.  The third argument to init() is the name of the node.
      *
      * You must call one of the versions of ros::init() before using any other
      * part of the ROS system.
      */
     //初始化一个叫talker的节点
     ros::init(argc, argv, "talker");
   
     /**
      * NodeHandle is the main access point to communications with the ROS system.
      * The first NodeHandle constructed will fully initialize this node, and the last
      * NodeHandle destructed will close down the node.
      */
     //为节点创建handle,第一个初始化节点,第二个清除节点所用的资源
     ros::NodeHandle n;
   
     /**
      * The advertise() function is how you tell ROS that you want to
      * publish on a given topic name. This invokes a call to the ROS
      * master node, which keeps a registry of who is publishing and who
      * is subscribing. After this advertise() call is made, the master
      * node will notify anyone who is trying to subscribe to this topic name,
      * and they will in turn negotiate a peer-to-peer connection with this
      * node.  advertise() returns a Publisher object which allows you to
      * publish messages on that topic through a call to publish().  Once
      * all copies of the returned Publisher object are destroyed, the topic
      * will be automatically unadvertised.
      *
      * The second parameter to advertise() is the size of the message queue
      * used for publishing messages.  If messages are published more quickly
      * than we can send them, the number here specifies how many messages to
      * buffer up before throwing some away.
      */
     //发送一个信息到名为chatter的话题上,这个信息类型是std_msgs::String,推送的队列大小是1000
     ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);

     //循环发送,发送频率10HZ
     ros::Rate loop_rate(10);
   
     /**
      * A count of how many messages we have sent. This is used to create
      * a unique string for each message.
      */
     int count = 0;
     //有4种情况ros::ok()返回false,1、触发中断。2、被另一个同名节点踢出ROS网络
     //3、ros::shutdown() 被程序的另一部分调用。4、节点中的所有 ros::NodeHandles 都已经被销毁。
     while (ros::ok())
     {
       /**
        * This is a message object. You stuff it with data, and then publish it.
      */
       //声明一个msg,并把字符串赋值给msg中的data
       std_msgs::String msg;
   
       std::stringstream ss;
       ss << "hello world " << count;
       msg.data = ss.str();
       
       //打印信息,代替cout跟print
       ROS_INFO("%s", msg.data.c_str());
   
       /**
        * The publish() function is how you send messages. The parameter
        * is the message object. The type of this object must agree with the type
        * given as a template parameter to the advertise<>() call, as was done
        * in the constructor above.
       */

      //发布消息
      chatter_pub.publish(msg);

      //在这个例子中并不是一定要调用 ros::spinOnce(),因为我们不接受回调。
      //然而,如果你的程序里包含其他回调函数,最好在这里加上 ros::spinOnce()这一语句,
      //否则你的回调函数就永远也不会被调用了。
      ros::spinOnce();
  
      //延迟一段时间
      loop_rate.sleep();
      ++count;
    }

    return 0;
  }

头文件报错,是因为没搭建ROS+VSCODE的环境,那就搭建个ROS+VSCODE的环境
在这里插入图片描述

2.1、搭建ROS+Vscode开发环境

安装插件:
安装c/c++插件(输出C++就能看到)
安装ROS插件(输入ROS第一个就是)
接下来可选:
安装txt插件(输入txt第一个)
安装msg插件(输入msg第一个)
修改配置任务:
快捷键shift+ctrl+b,选择catkin_make:build,
修改成:

{
	"version": "2.0.0",
	"tasks": [
		{
			"type": "shell",
			"command": "catkin_make",
			"args": [],
			"group": {"kind":"build","isDefault":true},
			"label": "catkin_make: build",
			"presentation":{
				"reveal": "always"
			},
			"problemMatcher": "$msCompile"
		}
	]
}

现在,在头文件那就没报错了。
在这里插入图片描述
接下来是订阅器
在src文件下新建一个listener.cpp
复制粘贴以下代码

#include "ros/ros.h"
#include "std_msgs/String.h"

/**
 * This tutorial demonstrates simple receipt of messages over the ROS system.
 */
//回调函数
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
  ROS_INFO("I heard: [%s]", msg->data.c_str());
}

int main(int argc, char **argv)
{
  /**
   * The ros::init() function needs to see argc and argv so that it can perform
   * any ROS arguments and name remapping that were provided at the command line.
   * For programmatic remappings you can use a different version of init() which takes
   * remappings directly, but for most command-line programs, passing argc and argv is
   * the easiest way to do it.  The third argument to init() is the name of the node.
   *
   * You must call one of the versions of ros::init() before using any other
   * part of the ROS system.
   */
  //初始化一个较listener的节点
  ros::init(argc, argv, "listener");

  /**
   * NodeHandle is the main access point to communications with the ROS system.
   * The first NodeHandle constructed will fully initialize this node, and the last
   * NodeHandle destructed will close down the node.
   */
  //为节点创建handle
  ros::NodeHandle n;

  /**
   * The subscribe() call is how you tell ROS that you want to receive messages
   * on a given topic.  This invokes a call to the ROS
   * master node, which keeps a registry of who is publishing and who
   * is subscribing.  Messages are passed to a callback function, here
   * called chatterCallback.  subscribe() returns a Subscriber object that you
   * must hold on to until you want to unsubscribe.  When all copies of the Subscriber
   * object go out of scope, this callback will automatically be unsubscribed from
   * this topic.
   *
   * The second parameter to the subscribe() function is the size of the message
   * queue.  If messages are arriving faster than they are being processed, this
   * is the number of messages that will be buffered up before beginning to throw
   * away the oldest ones.
   */
  //订阅 chatter 话题上的消息。当有消息发布到这个话题时,
  //ROS 就会调用 chatterCallback() 函数。第二个参数是队列大小1000
  ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);

  /**
   * ros::spin() will enter a loop, pumping callbacks.  With this version, all
   * callbacks will be called from within this thread (the main one).  ros::spin()
   * will exit when Ctrl-C is pressed, or the node is shutdown by the master.
   */
  //ros::spin() 进入自循环,可以尽可能快的调用消息回调函数。
  //如果没有消息到达,它不会占用很多 CPU,所以不用担心。一旦 ros::ok() 返回 false,
  //ros::spin() 就会立刻跳出自循环。
  ros::spin();

  return 0;
}

打开CMakeLists.txt文件,在文件末尾加上:

include_directories(include ${catkin_INCLUDE_DIRS})

add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker beginner_tutorials_generate_messages_cpp)

add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(listener beginner_tutorials_generate_messages_cpp)

在这里插入图片描述
返回 cd ~/carkin_ws目录下,运行catkin_make

3、测试发布器和订阅器

在roscore运行的情况下,新建一个终端
cd ~/catkin_ws(进入文件夹)
rosrun beginner_tutorials talker(运行beginner_tutorials里面的talker节点)
一直在不断刷新信息。
在这里插入图片描述
再新建一个终端,rosrun beginner_tutorials listener启动订阅器。
在这里插入图片描述
用rqt_graph看下,就是刚才我们新建的节点talker,节点listener跟话题chatter.
在这里插入图片描述

4、编写简单的服务器和客户端

4.1编写服务器端节点

操作与编写发布器、订阅器差不多。
roscd beginner_tutorials(进入文件夹)
在src文件中创建add_two_ints_server.cpp文件
复制粘贴以下代码

#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h"
/*
这个函数提供两个int值求和的服务,int值从request里面获取,
而返回数据装入response内,这些数据类型都定义在srv文件内部,
函数返回一个boolean值。
*/
bool add(beginner_tutorials::AddTwoInts::Request  &req,
    beginner_tutorials::AddTwoInts::Response &res){
        res.sum = req.a + req.b;
        ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
        ROS_INFO("sending back response: [%ld]", (long int)res.sum);
        return true;
    }
int main(int argc, char **argv){
    ros::init(argc, argv, "add_two_ints_server");
    ros::NodeHandle n;
    //建立发布service
    ros::ServiceServer service = n.advertiseService("add_two_ints", add);
    ROS_INFO("Ready to add two ints.");
    ros::spin();
    return 0;
}

4.2编写客户端节点

在src文件中创建add_two_ints_client.cpp文件
复制粘贴以下代码

#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h"
#include <cstdlib>

int main(int argc, char **argv){
    ros::init(argc, argv, "add_two_ints_client");
    if (argc != 3){
         ROS_INFO("usage: add_two_ints_client X Y");
         return 1;
    }
    ros::NodeHandle n;
    //为add_two_ints service创建一个client。ros::ServiceClient 对象待会用来调用service。
    ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints");
    /*
    实例化一个由ROS编译系统自动生成的service类,并给其request成员赋值。
    一个service类包含两个成员request和response。
    同时也包括两个类定义Request和Response
    */
    beginner_tutorials::AddTwoInts srv;
    srv.request.a = atoll(argv[1]);
    srv.request.b = atoll(argv[2]);
    /*
    这段代码是在调用service。由于service的调用是模态过程
    (调用的时候占用进程阻止其他代码的执行),一旦调用完成,将返回调用结果。
    如果service调用成功,call()函数将返回true,srv.response里面的值将是合法的值。
    如果调用失败,call()函数将返回false,srv.response里面的值将是非法的
    */
    if (client.call(srv)){
        ROS_INFO("Sum: %ld", (long int)srv.response.sum);
    }
    else{
        ROS_ERROR("Failed to call service add_two_ints");
        return 1;
    }
    return 0;
}

打开CMakeLists.txt文件,在文件末尾加上:

add_executable(add_two_ints_server src/add_two_ints_server.cpp)
target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})
add_dependencies(add_two_ints_server beginner_tutorials_gencpp)

add_executable(add_two_ints_client src/add_two_ints_client.cpp)
target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})
add_dependencies(add_two_ints_client beginner_tutorials_gencpp)

进入catkin_ws目录,运行catkin_make
在这里插入图片描述

5、测试服务器和客户端

运行Service:
rosrun beginner_tutorials add_two_ints_server
在这里插入图片描述
新建一个终端,运行客户端:
rosrun beginner_tutorials add_two_ints_client 1 3
在这里插入图片描述
客户端发送请求,1,3,服务器端接受到后进行相加,回调给客户端。

6、录制与回放数据

6.1、录制数据

启动乌龟窗口节点:rosrun turtlesim turtlesim_node
启动键盘控制节点:rosrun turtlesim turtle_teleop_key
新建一个文件夹:mkdir ~/bagfiles
进入这个新建的文件夹: cd ~/bagfiles
开始录制:rosbag record -a(想结束录制就ctrl+c直接中断)
在这里插入图片描述

6.2、回放数据

先ls下看看刚才生成的数据名字。
在这里插入图片描述
加载数据:rosbag info 数据名字(用tab键比较简便)
在这里插入图片描述
回放数据:rosbag play 数据名字
在这里插入图片描述
可以用rosbag play -r 2 数据名字来改变消息发布速率。

6.3、录制数据子集

当运行一个复杂的系统时很多话题被发布,有些话题会发布大量数据。在这种系统中,要想把所有话题都录制保存到硬盘上的单个bag文件中是不切实际的。rosbag record命令支持只录制某些特别指定的话题到单个bag文件中,这样就允许用户只录制他们感兴趣的话题。
先看下有哪些话题rostopic list
在这里插入图片描述
运行rosbag record -O subset /turtle1/cmd_vel /turtle1/pose.
表示把数据保存在subset中,只保存/turtle1/cmd_vel和/turtle1/pose这两个话题的数据。

在这里插入图片描述

7、手动创建ROS package

在catkin_ws目录下mkdir -p src/foobar建立文件夹。
进入文件夹:cd src/foobar
添加配置文件:gedit package.xml
复制粘贴以下语句:

<package>
  <name>foobar</name>
  <version>1.2.4</version>
  <description>
  This package provides foo capability.
  </description>
  <maintainer email="foobar@foo.bar.willowgarage.com">PR-foobar</maintainer>
  <license>BSD</license>

  <buildtool_depend>catkin</buildtool_depend>

  <build_depend>roscpp</build_depend>
  <build_depend>std_msgs</build_depend>

  <run_depend>roscpp</run_depend>
  <run_depend>std_msgs</run_depend>
</package>

检测以下ros是否能找到这个包:rospack find foobar
在这里插入图片描述
建立CMakeLists.txt文件:gedit CMakeLists.txt
复制粘贴以下数据:

cmake_minimum_required(VERSION 2.8.3)
project(foobar)
find_package(catkin REQUIRED roscpp std_msgs)
catkin_package()

在catkin_ws目录下编译一下
在这里插入图片描述
可以看到增加了两个包,一个是刚才我们弄的,一个是之前我们做测试弄的。
在这里插入图片描述

8、管理系统依赖项

ROS packages有时会需要操作系统提供一些外部函数库,这些函数库就是所谓的“系统依赖项”。在一些情况下,这些依赖项并没有被系统默认安装,因此,ROS提供了一个工具rosdep来下载并安装所需系统依赖项。
进入turtlesim目录:roscd turtlesim
就能用rosdep 安装包。

9、ROS在多机器人上的使用

假如说我们希望在两台机器上分别运行talker / listener, 主机名分别为 marvin 和 hal.登陆主机名为marvin的机器。
一台机器输入:ssh hal
然后启动ROS:roscore
新建一个终端输入:
ssh hal
export ROS_MASTER_URI=http://hal:11311
rosrun rospy_tutorials listener.py
运行listener节点。
在另一台机器上输入:ssh marvin
启动ros:roscore
同样新建一个终端输入:
ssh marvin
export ROS_MASTER_URI=http://hal:11311
rosrun rospy_tutorials talker.py
就能看到机器hal上的listener正在接收来自marvin机器上talker发布的消息。(PS:可惜目前手上没两台都装了ubuntu的机器,不能测试下)。

10、在python中使用C++类

我还没运行下python的例子呢。刚好刚才新建的foobar包是空包,就用这个包来做实验。

10.1写发布器与订阅器并测试(Python)

进入目录:roscd foobar
创建文件夹:mkdir scripts
进入文件夹:cd scripts
下载代码:wget https://raw.github.com/ros/ros_tutorials/kinetic-devel/rospy_tutorials/001_talker_listener/talker.py
在CMakeLists.txt文件中增加如下信息:

catkin_install_python(PROGRAMS scripts/talker.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

在这里插入图片描述
继续进入scripts目录下载代码:wget https://raw.github.com/ros/ros_tutorials/kinetic-devel/rospy_tutorials/001_talker_listener/listener.py
继续打开CMakeLists.txt
增加如下信息:

catkin_install_python(PROGRAMS scripts/talker.py scripts/listener.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

在这里插入图片描述
进入目录catkin:cd ~/catkin_ws
运行:catkin_make

测试:rosrun foobar talker.py
报错。
在这里插入图片描述
缩进问题,改下就行,写的时候没注意到。
在这里插入图片描述
接着新建终端运行rosrun foobar listener.py
在这里插入图片描述

10.2python中使用c++类

建立头文件
进入放包的目录:cd ~/catkin_ws/src
创建包并依赖roscpp,rospy,std_msgs:catkin_create_pkg python_bindings_tutorial rospy roscpp std_msgs
进入include目录:cd python_bindings_tutorial/include
打开vscode:code
把code的文件夹选在当前的文件夹。
新建add_two_ints.h
输入以下语句:

#ifndef PYTHON_BINDINGS_TUTORIAL_ADD_TWO_INTS_H
#define PYTHON_BINDINGS_TUTORIAL_ADD_TWO_INTS_H
#include <std_msgs/Int64.h>
namespace python_bindings_tutorial {
class AddTwoInts{
    public:
    std_msgs::Int64 add(const std_msgs::Int64& a, const std_msgs::Int64& b);
};
}
#endif // PYTHON_BINDINGS_TUTORIAL_ADD_TWO_INTS_H   

把code的文件下选到~/catkin_ws/src/python_bindings_tutorial/src
新建文件add_two_ints.cpp
输入以下语句:

#include <python_bindings_tutorial/add_two_ints.h>
using namespace python_bindings_tutorial;
std_msgs::Int64 AddTwoInts::add(const std_msgs::Int64& a, const std_msgs::Int64& b){
    std_msgs::Int64 sum;
    sum.data = a.data + b.data;
    return sum;
}

建立C++部分,c++包装器将输入从序列化的内容转换为c++消息实例,并将c++消息实例的输出转换为序列化的内容。
继续在这个目录下建立文件add_two_ints_wrapper.cpp
输入以下语句:

#include <boost/python.hpp>
#include <string>

#include <ros/serialization.h>
#include <std_msgs/Int64.h>

#include <python_bindings_tutorial/add_two_ints.h>

/* Read a ROS message from a serialized string.
*/
template <typename M>
M from_python(const std::string str_msg)
{
size_t serial_size = str_msg.size();
boost::shared_array<uint8_t> buffer(new uint8_t[serial_size]);
for (size_t i = 0; i < serial_size; ++i)
{
  buffer[i] = str_msg[i];
}
  ros::serialization::IStream stream(buffer.get(), serial_size);
  M msg;
  ros::serialization::Serializer<M>::read(stream, msg);
  return msg;
}

/* Write a ROS message into a serialized string.
*/
template <typename M>
std::string to_python(const M& msg)
{
  size_t serial_size = ros::serialization::serializationLength(msg);
  boost::shared_array<uint8_t> buffer(new uint8_t[serial_size]);
  ros::serialization::OStream stream(buffer.get(), serial_size);
  ros::serialization::serialize(stream, msg);
  std::string str_msg;
  str_msg.reserve(serial_size);
  for (size_t i = 0; i < serial_size; ++i)
  {
    str_msg.push_back(buffer[i]);
  }
  return str_msg;
}

class AddTwoIntsWrapper : public python_bindings_tutorial::AddTwoInts
{
  public:
    AddTwoIntsWrapper() : AddTwoInts() {}

    std::string add(const std::string& str_a, const std::string& str_b)
    {
      std_msgs::Int64 a = from_python<std_msgs::Int64>(str_a);
      std_msgs::Int64 b = from_python<std_msgs::Int64>(str_b);
      std_msgs::Int64 sum = AddTwoInts::add(a, b);

      return to_python(sum);
    }
};

BOOST_PYTHON_MODULE(_add_two_ints_wrapper_cpp)
{
  boost::python::class_<AddTwoIntsWrapper>("AddTwoIntsWrapper", boost::python::init<>())
    .def("add", &AddTwoIntsWrapper::add)
    ;
}

建立python部分,Python包装器将来自Python消息实例的输入转换为序列化的内容,并将来自序列化内容的输出转换为Python消息实例。从Python串行化内容(str)到c++串行化内容(std::string)的转换构建在Boost Python库中。
进入文件夹:roscd python_bindings_tutorial/src
建立文件夹:mkdir python_bindings_tutorial(把python文件放在这个文件夹下)
进入文件夹:cd python_bindings_tutorial
新建文件_add_two_ints_wrapper_py.py,并输入以下内容:

from StringIO import StringIO

import rospy
from std_msgs.msg import Int64

from python_bindings_tutorial._add_two_ints_wrapper_cpp import AddTwoIntsWrapper


class AddTwoInts(object):
    def __init__(self):
        self._add_two_ints = AddTwoIntsWrapper()

    def _to_cpp(self, msg):
        """Return a serialized string from a ROS message

        Parameters
        ----------
        - msg: a ROS message instance.
        """
        buf = StringIO()
        msg.serialize(buf)
        return buf.getvalue()

    def _from_cpp(self, str_msg, cls):
        """Return a ROS message from a serialized string

        Parameters
        ----------
        - str_msg: str, serialized message
        - cls: ROS message class, e.g. sensor_msgs.msg.LaserScan.
        """
        msg = cls()
        return msg.deserialize(str_msg)

    def add(self, a, b):
        """Add two std_mgs/Int64 messages

        Return a std_msgs/Int64 instance.

        Parameters
        ----------
        - a: a std_msgs/Int64 instance.
        - b: a std_msgs/Int64 instance.
        """
        if not isinstance(a, Int64):
            rospy.ROSException('Argument 1 is not a std_msgs/Int64')
        if not isinstance(b, Int64):
            rospy.ROSException('Argument 2 is not a std_msgs/Int64')
        str_a = self._to_cpp(a)
        str_b = self._to_cpp(b)
        str_sum = self._add_two_ints.add(str_a, str_b)
        return self._from_cpp(str_sum, Int64)

创建文件__init__.py
输入以下语句:

from python_bindings_tutorial._add_two_ints_wrapper_py import AddTwoInts

进入CMakeLists.txt.复制粘贴以下内容:

cmake_minimum_required(VERSION 3.0.2)
project(python_bindings_tutorial)

## Compile as C++11, supported in ROS Kinetic and newer
# add_compile_options(-std=c++11)

## 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
  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}
        )

进入python_bindings_tutorial目录:roscd python_bindings_tutorial
新建setup.py文件
输入以下语句:

# ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD

from distutils.core import setup
from catkin_pkg.python_setup import generate_distutils_setup

# fetch values from package.xml
setup_args = generate_distutils_setup(
    packages=['python_bindings_tutorial'],
    package_dir={'': 'src'})

setup(**setup_args)

进入catkin_ws目录:cd ~/catkin_ws
运行catkin_make
在这里插入图片描述
测试下,运行python3
输入:from std_msgs.msg import Int64
输入:from python_bindings_tutorial import AddTwoInts
报错:
在这里插入图片描述
找到路径cd ~/catkin_ws/src/python_bindings_tutorial/src/python_bindings_tutorial下的文件_add_two_ints_wrapper_py.py
把from StringIO import StringIO改成from io import StringIO
然后回到cd ~/catkin_ws目录,运行catkin_make
再接着输入:
python3
from std_msgs.msg import Int64
from python_bindings_tutorial import AddTwoInts
a = Int64(4)
b = Int64(2)
addtwoints = AddTwoInts()
sum = addtwoints.add(a, b)
接着报错,吐了,吐了
在这里插入图片描述
仔细的看了下,查找了下资料,是因为python3中把StringIO写在io里面了,目前不知道怎么解决,希望有大佬能教下。
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值