본문 바로가기

Study_ROS

Tutorial of ROS

728x90
반응형

- Goal : Learn how to create and execute publisher and subscriber nodes using C++ and Python

 

A node in ROS is just a program (e.g. typically a piece of source code made in C++ or Python) that does some computation. ROS nodes reside inside ROS packages. ROS packages reside inside the src folder of your catkin workspace (i.e. catkin_ws).

 

 

1) How to Create ROS Workspace

  • We need to create a workspace before start writing code in ROS, which is a set of directories (or folders) where we store releated pieces of ROS code
  • The official name for workspaces in ROS is catkin workspaces
  • All the ROS packages that we create need to reside inside a catkin workspace
  • The name of catkin workspace is typically called catkin_ws

 

- Set Directions

 

$ mkdir -p ~/catkin_ws/src
$ cd ~/catkin_ws/
$ catkin_make
  • Open a new terminal and type the following commands to create and build at catkin workspace
    • mkdir : command to make directory (Make Directory)

 

$ dir
  • We can see the things in folder using dir command
  • There are 3 folders inside of this directory

 

$ source devel/setup.bash
  • We need to source the setup.bash file
  • This file sets the path of the workspace so that the packages and code inside the workspace can be found
  • source 명령어는 스크립트 파일을 수정한 후에 수정된 값을 바로 적용하기 위해 사용하는 명령어
source [환경 설정 파일명]

 

$ echo $ROS_PACKAGE_PATH
  • Check the workspace is properly overlayed by the setup script

 

 

$ gedit ~/.bashrc
  • Open a new terminal and open .bashrc text file and add the following line end of the .bashrc file

$ source ~/.bashrc
  • By adding that line to .bashrc file, we don`t need to source the setup.bash file every time we open a new terminal

 

 

 

2) How to Create ROS Package

  • Software in ROS is organized into packages. Each package might contain a mixture of code (e.g. ROS nodes), data, libraries, images, documentation, etc. Every program you write in ROS will need to be inside a package.
  • ROS packages promote software reuse. The goal of a ROS package is to be large enough to provide a specific useful functionality, but not so large and complicated that nobody wants to reuse it for their own project
  • ROS Packages are organized as follows :
    • launch folder: Contains launch files
    • src folder: Contains the source code (C++, Python)
    • CMakeLists.txt: List of cmake rules for compilation
    • package.xml: Package information and dependencies

 

$ roscreate-pkg [package_name]
  • Create a new package in the current directory
$ catkin_create_pkg <package_name> [depend1] [depend2] [depend3]
  • Syntax for creating a ROS Package
  • We can specify dependencies of the package

 

$ cd ~/catkin_ws/src
  • Open new terminal and move to the source directory of the workspace we created
$ catkin_create_pkg hello_world std_msgs rospy roscpp
  • Create a ROS package named 'hello_world' inside the src folder of the catkin workspace(i.e catkin_ws)
  • This package will have 3 dependencies (i.e. libraries the package depends on in order for the code inside the package to run properly): roscpp (the ROS library for C++), rospy (the ROS library for Python), and std_msgs (common data types that have been predefined in ROS … “standard messages”).

 

  • We can see the things inside of the directory
$ roscd hello_world
$ ls
  • Move to the package and type ls to see what`s in it. There are 4 files
  • CMakeLists.txt: This text file contains the commands to compile the programs that you write in ROS. It also has the commands to convert your source code and other files into an executable (i.e. the code that your computer can run).
  • include: Contains package header files. You might remember when we wrote header files in the C++ tutorial…well this folder is where your header files would be stored.
  • src: This folder will contain the C++ source code. If you are doing a project in Python, you can create a new folder named scripts that will contain Python code. To create this new folder type following command
$ mkdir scripts
  • package.xml: This is an Extensible Markup Language (XML) file.
    • XML file is a text file written in a language (called XML) that is easy to read by both humans and computers. An XML file does not do anything other than store information in a structured way.
    • Our package.xml file contains information about the hello_world package. You can see what is inside it by typing following command
$ gedit package.xml

 

 

 

 $ roscd <package_name>
  • If we want to go straight to a package without typing the full path, ROS has a command to enalbe to do that

 

- Compiling a Package in ROS : when we create a new package, we need to complie it in order for it to be able to run

 

$ cd ~/catkin_ws/
$ catkin_make
  • This command will compile all the stuff inside the src folder of the package
  • You must be inside your catkin_ws directory in order for this command be run
  • catkin_make는 cmake의 ROS 버전이라고 볼 수 있다
$ mkdir build
$ cd build
$ cmake ../
$ make
  • catkin_make command includes above`s processes

 

$ catkin_make --only-pkg-with-deps
  • If you just want to compile specific packages (in cases where your project is really big), you type this command
$ catkin_make --only-pkg-with-deps hello_world
  • Example of compile a package named hello_world

 

 

 

 

3)  Example of Making ROS Launch File

 

Goal : Get two pieces of ROS SW (called nodes) to talk each other

Remind

  • Node:
  • Message : One way node communicate
  • Topic : Like middle man between publishers and subcribers
  • Publisher : Node that send data
  • Subscriber : Node that receive data
  • Master : Node that keeps track of which nodes are publisher and which nodes are subcriber nodes. Without Master, nodes would not be able to communicate with each other
  • The communication is anonymous, so nodes do not know what nodes they are sending data to/receiving data from

 

- Example : Youtube

  • YouTubers (publisher nodes) publish videos (messages) to a channel (topic), and you (subscriber node) can subscribe to that channel (topic) so that you receive all the videos (messages) on that channel (topic). YouTube (ROS Master) keeps track of who is a publisher and who is a subscriber. One thing to keep in mind is that (in contrast to YouTube) in ROS there can be multiple publishers to the same topic, and publishers and subscribers don’t know each other.
  • In the robotic world, we need different parts of a robot to send data to each other so that the robot can sense the world, process what it sees, and then act on the world. For example, suppose we are working on a humanoid robot. We might have one small program (publisher node) responsible for reading sensor data from a camera mounted on the head of the robot. That program might send that data (i.e. message) via a topic called “vision scan” to another program (subscriber node) which is responsible for controlling the joint in the robot’s arm. Note that messages in ROS can be a variety of data types, including strings, floats, integers, etc.

 

 

Our example`s diagram

 

 

$ roscore
  • Launch ROS
$ rosrun roscpp_tutorials talker

  • rosrun [package_name] [node_name]
  • Start talker node and it starts printing hello world

 

$ rostopic list
  • We acan se a list of current active topics

  • The topic of the hello world messages is /chatter

 

$ rosrun roscpp_tutorials listener
  • Launch listener node
  • It will subscribe to the /chatter topic so that it can receive the hello world messages published by talker

 

 

  • We can launch multiple nodes using roslaunch
$ roslaunch roscpp_tutorials talker_listener.launch

  • To stop ros, type Ctrl + C

 

 

 

 

4) How to Create a Publisher / Subscriber / Executable Node in ROS Using C++ and Python

 

1. Create pulisher node

  • Publisher node publishes messages (i.e. data values) to a particular topic

 

$ gedit
  • Open geditor and insert following C++ code
  • And save the file as 'talker.cpp' in catkin_ws/src/hello_world/src folder
  • I added the explains of the code using remark ( '//' )
#include "ros/ros.h"	// ros/ros.h is a convenience include that includes all the
// necessary headers to use the most common public pieces of the ROS system

#include "std_msgs/String.h"	// Include std_msgs/String message, which resides 
// in the std_msgs package
// This header generated automatically from the String.msg file in that package 

#include <sstream>

/**
 * This tutorial demonstrates simple sending of messages over the ROS system.
 */
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.
   */
  ros::init(argc, argv, "talker");	// Initialize ROS to allows ROS to do name remapping
  // through the command line, and we specify the name of our node
  // Node`s name must ne unique in a running system

  /**
   * 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.
   */
  ros::NodeHandle n;	// Create a handle to this process` node
  // The first NodeHandle created will actually do initialization of the node
  // And the last one destructed will cleanup any resources the node was using

  /**
   * 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.
   */
  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
// Tell the master that we are going to be publishing a message of type std_msgs/String on the topic chatter
// It lets the master tell any nodes listening on chatter that we are going to publish data on that topic
// The second argument is the size of our 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

// NodeHandle::advertise() returns a ros::Publisher object, which serves two purposes: 1) it contains a publish() 
// method that lets you publish messages onto the topic it was created with, and 2) when it goes out of scope, 
// it will automatically unadvertise.

  ros::Rate loop_rate(10);	// The ros::Rate object allows us to specify a frequency that we would like to loop at
  // It will keep track of how long it has been since the last call to Rage::sleep(), 
  // and sleep for the correct amount of time
  // In this case we tell it we want to run at 10Hz

  /**
   * A count of how many messages we have sent. This is used to create
   * a unique string for each message.
   */
  int count = 0;
  while (ros::ok())	
  // 75 ~ 76
  // In default, roscpp will install a SIGINT handler which provides
  // Ctrl-C handling which will cause ros::ok() to return false if that happens
  
  // ros::ok() will return false if:
  // a SIGINT is received (Ctrl-C)
  // we have been kicked off the network by another node with the same name
  // ros::shutdown() has been called by another part of the application.
  // all ros::NodeHandles have been destroyed 
  // Once ros::ok() returns false, all ROS calls will fail
  // In short, its function is error detecting
  
  {
    /**
     * This is a message object. You stuff it with data, and then publish it.
     */
    std_msgs::String msg;

    std::stringstream ss;
    ss << "hello world " << count;
    msg.data = ss.str();
    // 93 ~ 96
    // We broadcast a message on ROS using a message-adapted class, generally generated from a msg file
    // More complicated datatypes are possible, but for now we`re going to use the standard
    // String message, which has one member : 'data'
    
    ROS_INFO("%s", msg.data.c_str());	// ROS_INFO and friends are our replacement for printf/cout

    /**
     * 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);	// Now we actually broadcast the message to anyone who is connected

    ros::spinOnce();	// Calling ros::spinOnce() here is not necessart for this simple program
    // Because we are not receiving any callbacks. However, if you were to add a subscription into
    // this application, and did not have ros::spinOnce() here, your callbacks would never get called
    // SO add if for good measure

    loop_rate.sleep();	// Now we use the ros::Rate object for the time remaining to let us hit our 10Hz publish rate
    // Here`s the condensed version of what`s going on:
    // Initialize the ROS system
    // Advertise that we are going to be publishing std_msgs/String messages on the chatter topic to the master
    // Loop while pulishing messgaes to chatter 10 times a second
    // Now we need to write a node to receive the messages
    
    ++count;
  }


  return 0;
}

 

 

- msg (Add explains about msg in http://wiki.ros.org/msg)

- rosconsole ( Add explains about rosconsole in http://wiki.ros.org/rosconsole)

 

 

2. Create Subscriber node

  • Subscriber node will subscribes to the data (i.e. 'hello world' message) published bt the talker
  • Subscriber node subscribes to messages (i.e. data values) on a particular topic

 

$ gedit

 

  • Open geditor and insert following C++ code
  • And save the file as 'listener.cpp' in catkin_ws/src/hello_world/src folder
  • I added the explains of the code using remark ( '//' )

 

#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());
}
// 7~10
// This is callback function that will get called when a new message has arrived on the chatter topic
// The message is passed in a boost shared_ptr, which means we can store it off if we want
// without worrying about it getting deleted underneate we, and without copying the underlying data



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.
   */
  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.
   */
  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.
   */
  ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
  // Subscribe to the chatter topic with the master. ROS will call the chatterCallback() function
  // whenever a new message arrives
  // The 2nd argument is the queue size, in case we are not albe to process messages fast enough
  // In this case, if the queue reaches 1000 messages, we will start throwing away old messages as new ones arrive
  // NodeHandle::subscribe() returns a ros::Subscriber object, that you must hold on to until you want to unsubscribe. 
  // When the Subscriber object is destructed, it will automatically unsubscribe from the chatter topic. 
  // There are versions of the NodeHandle::subscribe() function which allow you to specify a class member function, or 
  // even anything callable by a Boost.Function object


  /**
   * 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();	// ros::spin() enters a loop, calling message callbacks as fast as possible
  // Don't worry though, if there's nothing for it to do it won't use much CPU. ros::spin() will exit once ros::ok() returns 
  // false, which means ros::shutdown() has been called, either by the default Ctrl-C handler, the master telling us to shutdown, 
  // or it being called manually.
  // There are other ways of pumping callbacks, but we won't worry about those here. The roscpp_tutorials package has some demo 
  // applications which demonstrate this. The roscpp overview also contains more information.
  // Again, here's a condensed version of what's going on:
  // Initialize the ROS system
  // Subscribe to the chatter topic
  // Spin, waiting for messages to arrive
  // When a message arrives, the chatterCallback() function is called 

  return 0;
}

 

 

3. Create the Executable Nodes

  • Now we create two nodes but we need to convert that source code written in C++ into an executable that our machine can understand

 

$ cd catkin_ws/src/hello_world/
$ dir
  • Navigate to catkin_ws/src/hello_world/ folder and type dir to see the lists of files in that directory
  • The result must be like below
CMakeLists.txt	include  package.xml  src

 

$ gedit CMakeLists.txt
  • Open the CMakeLists.txt file and add following lines to the very bottom

 

 

 

 

 

 

Delelt that code

 

 

 

 

 

 

 

 

 


References :

https://automaticaddison.com/create-a-hello-world-project-in-ros/

 

Create a Hello World Project in ROS – Automatic Addison

In this section, we will develop our first ROS application. We are not going to do anything fancy here…just a basic “Hello World” project to get our feet wet with ROS. But before we get started on that, let’s cover some basic ROS terminology on a h

automaticaddison.com

 

http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber%28c%2B%2B%29#roscpp_tutorials.2BAC8-Tutorials.2BAC8-WritingPublisherSubscriber.The_Code_Explained

 

ROS/Tutorials/WritingPublisherSubscriber(c++) - ROS Wiki

Writing the Publisher Node "Node" is the ROS term for an executable that is connected to the ROS network. Here we'll create a publisher ("talker") node which will continually broadcast a message. Change directory into the beginner_tutorials package, you cr

wiki.ros.org

 

 

728x90
반응형