Installation

ROS Wiki

ROS work with Anaconda

在Anaconda虚拟python3环境中运行ROS包或用python import rospy时会出现:

ImportError: No module named rospkg

通过conda deactivate命令退出Anaconda环境之后,一切恢复正常。

原因:

  • ROS只支持python2.7的调用,对python3不支持。
  • 无法在Anaconda中导入rospy是由于环境中没有相应的包。

解决办法:

在Anaconda环境中安装相应的依赖包:

$ conda install setuptools
$ pip install -U rosdep rosinstall_generator wstool rosinstall six vcstools

结果:

(env-name) csy@UAV:~$ python
Python 3.6.12 |Anaconda, Inc.| (default, Sep  8 2020, 23:10:56) 
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 
>>> import rospy
>>> 

Basic Usage

rostopic

  • list
  • info
  • echo
  • pub
  • hz

rosmsg

rosmsg show std_msgs/String

Write a ROS Node with C++ or Python

#include

  • #include <ros/ros.h>: includes all the headers necessary to use the most common public pieces of the ROS system.
  • #include <std_msgs/String.h>: includes the std_msgs/String message.
  • Also, other message hearer files which are used in the topics should be included in the code.

Using Python:

import rospy
from std_msgs.msg import String

ros::init()

  • Initialize ROS node.
  • Specify the name of the node, node names must be unique in the running system.
  • needs to see argc and argv so that it can perform any ROS arguments and name remapping that were provided at the command line.
  • must call ros::init() before using any other part of the ROS system.
ros::init(argc, argv, "talker");

Using Python:

rospy.init_node('talker', anonymous=True)

ros::NodeHandle n

  • NodeHandle is the main access point to communications with the ROS system.
  • The first NodeHandle constructed will fully initialize the node.
  • The last NodeHandle destructed will close down the node.

ros::Publisher

  • Tell the master that we are going to publish a message.
  • Let the master tell any nodes listening that we are going to publish data on that topic.
  • NodeHandle::advertise: returns a ros::Publisher object.
    • contains a publish() method that lets you publish messages onto the topic which was created with.
    • when it goes out of scope, it will automatically unadvertise.
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);

chatter_pub.publish(msg);

The second argument is the size of publishing queue. In this case if we are publishing too quickly, it will buffer up a maximum of 1000 messages before beginning to throw away old ones.

Using Python:

pub = rospy.Publisher('chatter', String, queue_size=1000)

pub.publish(msg)

ros::Subscriber

  • subscribe to the topic with the master.
  • ROS will call the callback() function whenever a new message arrives.
  • NodeHandle::subscribe() renturns a ros::Subscriber object.
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
  ROS_INFO("I heard: [%s]", msg->data.c_str());
}

ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);

Using Python:

def callback(data):
  rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data)

rospy.Subscriber("chatter", String, callback)

ros::Rate

  • allows you to specify a frequency to loop at.
  • keep track of how long it has been since the last call to Rate::sleep().
ros::Rate loop_rate(10);

loop_rate.sleep();

In this case, the loop run at 10Hz.

Using Python:

rate = rospy.Rate(10)

rate.sleep()

ros::Duration

  • Duration is a period of time.
ros::Duration five_seconds(5.0);
ros::Duration two_hours = ros::Duration(60*60) + ros::Duration(60*60);

ros::Duration(3.0).sleep(); // sleep for 3 seconds

ros::Time

  • Time is a specific moment.
ros::Time begin_time = ros::Time::now(); // get the current time

ros::Time tomorrow = ros::Time::now() + ros::Duration(24*60*60);

ros::ok()

  • ros::ok() will return false if:
    • Ctrl+C.
    • kick off the network by another node with the same name.
    • ros::shutdown() has been called by another part of the application.
    • all ros::NodeHandle has been destroyed.
while (ros::ok())
{

}

Using Python:

while not rospy.is_shutdown():
  pass

ROS_INFO()

ROS_INFO("%s", msg.data.c_str());

Using Python:

hello_str = "hello world %s" % rospy.get_time()
rospy.loginfo(hello_str)

ros::spin() and ros::spinOnce()

  • 订阅消息回调处理函数,出现在ROS主函数中。
  • 基于ROS的消息发布、订阅机制,ROS会自动再后台接收订阅的消息,接收到的消息不会立即处理,必须等到回调处理函数执行时才被调用。
  • 如果程序中有消息订阅函数,则必须加上该回调处理函数,否则无法接收消息。
  • ros::spin(): enter a loop, pumping callbacks via subscribe topics as fast as possible.
    • 调用后不再返回,主程序不会再执行此函数之后的程序。
    • 一般不会出现在循环中,出现在主函数末尾。
    • will not use much CPU if there is nothing for it to do.
    • exit when ctrl+C is pressed or the node is shutdown by the master.
  • ros::spinOnce():
    • 调用后主程序还可以继续执行之后的程序。
    • 可以出现在循环中。
    • 需要注意消息池调用频率的协调性,避免消息丢失或延迟:
      • 消息池 \(\ge\) 消息接收频率 / 调用频率。

Using Python:

rospy.spin()

ros::Timer

  • 在某一频率下执行一次回调操作。
  • 可用于固定频率消息的发布,不受ros::spin()的影响。
  • 订阅服务中的回调函数队列中使用。
ros::Timer timer = n.createTimer(ros::Duration(0.1), timerCallback, bool oneshot = false);

void timerCallback(const ros::TimerEvent& event)
{

}
  • ros::Duration(0.1): 每0.1秒执行一次。
  • oneshot: 表示是否只执行一次。

Using Python:

rospy.Timer(rospy.Duration(0.1), my_callback, oneshot=False)

def my_callback(event):
  print('Timer called at ' + str(event.current_real))

References