# [ROS Tutorial Notes](http://wiki.ros.org/ROS/Tutorials) ###### tags: `Learning Notes` [TOC] ## Prerequisites - [ ] Download Ubuntu [Virtual Box(for both Mac and Windows)](https://) || [Dual System(for Windows)](https://) :::info :cactus: **Option:** 1. VirtualBox(free, support: Windows, MacOS except M1): https://www.virtualbox.org/wiki/Downloads 2. UTM(free): https://github.com/utmapp/UTM/releases 3. Parallels VM($$, support Mac M1): https://www.parallels.com/products/desktop/ ::: - [ ] Check out Ubuntu version - [ ] [Choose ROS Version corresponding Ubuntu Version and Install ROS](http://wiki.ros.org/ROS/Installation) (ex: Ubuntu 18.04 -> ROS Melodic Morenia) ## Preapration in ROS ### Step 1: Source you .bash file and Install ros tutorial :::info **Source file** $ source /opt/ros/<distro>/setup.bash **# put corrsponding ROS version to replace <distro>** ex: Ubuntu 18.04 -> $ source /opt/ros/melodic/setup.bash **Install ROS tutorial** $ sudo apt-get install ros-<distro>-ros-tutorials **# put corrsponding ROS version to replace <distro>** :bulb: **Source the generated setup file before using ros command** $ . ~/catkin_ws/devel/setup.bash ::: ### Step 2: Being Familiar with baisc command > **Create catkin workspace** :::info $ mkdir -p ~/catkin_ws/src #make directory $ cd ~/catkin_ws/ # change directory $ catkin_make #make catkin ::: * **tab -> hot key to find file while changing path** * **cat -> dispay content in the .testfile** * **rospack -> ros + pack** * **roscd -> ros + cd** * **rosls -> ros + ls** # ls = list ### Step 3: Create own catkin package and Build package > **Create catkin package** :::info $ catkin_create_pkg beginner_tutorials std_msgs rospy roscpp **#catkin_create_pkg beginner_tutorials + depend1 + depend2 +depend3** **#roscpp is c++ library, rospy is python library** ::: After creating own package, we can see package.xml and CMakeLists.txt at /catkin_ws/src/begineer_tutorial. In package.xml, we can see some info of our package. > **Bulid catkin package** :::info $ catkin_make ::: ### Catkin_make_isolated 這個是方便使用在有很多個ros pkg的時候,因為有時候catkin_make把全部一起compile,如果有一些重複的節點出現,會有error,所以用這個執行更好。 :warning: 如果想要使用Catkin_make_isolated的話,創見的pkg要記得使用catkin_create_pkg要不然系統會找不到這個資源包。 :::info catkin_create_pkg your-pkg-name std_msgs rospy roscpp ::: :bulb: 後面的dependency自己選擇都可以。 ## Beginning of ROS ### Overview of Graph concepts * **Nodes**: A node is an executable that uses ROS to communicate with other nodes. * **Messages**: ROS data type used when subscribing or publishing to a topic. * **Topics**: Nodes can publish messages to a topic as well as subscribe to a topic to receive messages. * **Master**: Name service for ROS (i.e. helps nodes find each other) * **rosout**: ROS equivalent of stdout/stderr * **roscore**: Master + rosout + parameter server (parameter server will be introduced later) ### Roscore :::info $ roscore **# matser +rosout +parameter server** ::: You will see: ... logging to /home/leokim10073/.ros/log/bb4ab0b4-7900-11ec-8061-18c04d9f4621/roslaunch-leokim1007 3-X570-AORUS-ELITE-WIFI-9897.log Checking log directory for disk usage. This may take a while. Press Ctrl-C to interrupt Done checking log file disk usage. Usage is <1GB. started roslaunch server http://leokim10073-X570-AORUS-ELITE-WIFI:35307/ ros_comm version 1.14.12 SUMMARY ======== PARAMETERS * /rosdistro: melodic * /rosversion: 1.14.12 NODES auto-starting new master process[master]: started with pid [9907] ROS_MASTER_URI=http://leokim10073-X570-AORUS-ELITE-WIFI:11311/ setting /run_id to bb4ab0b4-7900-11ec-8061-18c04d9f4621 process[rosout-1]: started with pid [9918] started core service [/rosout] If you meet some probelm, you can visit [ROS roscore](https://http://wiki.ros.org/ROS/Tutorials/UnderstandingNodes). ### Rosnode > ***Node** : Node is a unit in ROS and we could use it to communicate with other nodes.* :::info $ rosnode list **# list active nodes** $ rosnode info /rosout **# display specified node info** ::: ``` #include <rclcpp/rclcpp.hpp> int main(int argc, char * argv[]) { rclcpp::init(argc, argv); // Create a loop that will keep the program in execution rclcpp::spin(std::make_shared<SimpleSubscriber>()); rclcpp::shutdown(); return 0; } ``` **rclcpp::spin**- 在调用后不会再返回,也就是你的主程序到这儿就不往下执行了 ### Rosrun :::info $ rosrun [package_name] [node_name] **#Runs a node from given package** [**Example**](https://wiki.ros.org/ROS/Tutorials/UnderstandingNodes) ::: ### ROS Topics and Messages :bulb: *Please done following website.* [Topics and Message](https://wiki.ros.org/ROS/Tutorials/UnderstandingTopics) > ***Topics** : Nodes can publish messages to a topic as well as subscribe to a topic to receive messages.* * **rostopic echo:** echo the infromation of things we add behind command * **rostopic type :** show topic message * **rostopic pub:** rostopic publishes > ***Messages** : ROS data type used when subscribing or publishing to a topic.* * **rosmsg show:** show msg type, then we can use it to publish topic. ### ROS Services and Parameters :bulb: *Please done following website. [Services and Parameters](http://wiki.ros.org/ROS/Tutorials/UnderstandingServicesParams)* > **Serivce** * **rosservice list:** all commands that rosservice can do * **rosparam type:** find the type of service * **rosparam call:** call the service (run service) >**Parameter** * **rosparam list:** list all parameter * **rosparam set:** set new parameter or change parameter value * **rosparam get:** get the value of parameter :link: *https://ithelp.ithome.com.tw/articles/10209175* ### [ROS Launch File](https://charlyhuangrostutorial.wordpress.com/2015/08/12/20/) :bulb: *Launch file is used to record every commands in one file for final target. If we do not have this process, we may need to open a lot of files at the same time to collect data or exceute trivial works for completing final target. But after we add those trivial works in launch file, it could be avoided.* :::info roslaunch [package] [filename.launch] $ mkdir launch $ cd launch ::: ### Rosed :::info $ rosed [package_name] [filename] **# Similar to vim** ::: ### Alternatvie method :::info $ gedit **# This command can gererate a file that you can edit it** ::: ### Msg and Srv :bulb: *Please done following website. [Msg and Srv](http://wiki.ros.org/ROS/Tutorials/CreatingMsgAndSrv)* >***Msg** : We can define new type to .msgfile in msg folder* :::info $ mkdir msg $ echo "int64 num" > msg/Num.msg **#point int64 type to msg/Num.msg** ::: >***Srv** : Almost the same with msg, except they contain two parts: a request and a response* :::info $ mkdir srv $ roscp [package_name] [file_to_copy_path] [copy_path] $ roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv **#copy srv** ::: ### Topic vs Service :spiral_note_pad: https://reurl.cc/Xj19Vg * **Topic:** Publisher and Subscriber * **Service:** Service and Client Topic 发布一个消息后,就直接去执行后面的程序;而Service 调用一个服务,会一直等待结果。 ![](https://i.imgur.com/LhIBcqm.png) ### Publisher and Subscriber(C++) :bulb: *Please done following website. [Publisher and Subscriber(C++)](http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber%28c%2B%2B%29)* :::info $ mkdir -p src **#Create a src directory in the beginner_tutorials package directory.** ::: src will contain any source files for package >*[**Publisher** :](https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp) create a talker.cpp file in src directory* ``` #include "ros/ros.h" #include "std_msgs/String.h" #include <sstream> int main(int argc, char **argv){ ros::init(argc, argv, "talker"); ros:: NodeHandle n; ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter",1000); ros::Rate loop_rate(10); int count = 0; while(ros::ok()){ std_msgs::String msg; std::stringstream ss; ss << "hello world " << count; msg.data = ss.str(); std::cout << msg.data.c_str() << std::endl; chatter_pub.publish(msg); ros::spinOnce(); loop_rate.sleep(); ++count; } return 0; } ``` >*[**Subscriber** :](https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/listener/listener.cpp) create a listener.cpp file in src directory* ``` #include "ros/ros.h" #include "std_msgs/String.h" void chatterCallback(const std_msgs::String::ConstPtr& msg){ std::cout << "I heard: " << msg->data.c_str() << std::endl; } int main(int argc, char **argv){ ros::init(argc, argv, "linster"); ros::NodeHandle n; ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback); ros::spin(); return 0; } ``` >**Buliding nodes:** Add the following lines to the bottom of **CMakeLists.txt** ``` 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) ``` Run catkin_make in your catkin workspace: :::info $ cd ~/catkin_ws $ catkin_make ::: Before Running Pubulisher and Subscriber make sure: :::info $ roscore $ source ./devel/setup.bash **#In catkin_ws/** ::: >**Running Publisher:** :::info $ rosrun beginner_tutorials talker ::: You will get something similar to: ``` hello world 77 hello world 78 hello world 79 hello world 80 hello world 81 hello world 82 ``` >**Running Subscriber:** :::info $ rosrun beginner_tutorials listener ::: You will get something similar to: ``` heard hello world 77 heard hello world 78 heard hello world 79 heard hello world 80 heard hello world 81 heard hello world 82 ``` ### Publisher and Subscriber(Python) :bulb: *Please done following website. [Publisher and Subscriber(python)](http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber%28python%29)* :::info $ roscd beginner_tutorials **#Change directory into the beginner_tutorials package** $ mkdir scripts **#Create a 'scripts' folder to store our Python code** $ cd scripts ::: >**Pubilsher Node:** In the beginner_tutorials/scripts, we create a talker.py file. The following is the code: ``` import rospy from std_msgs.msg import String def talker(): pub = rospy.Publisher('chatter', String, queue_size=10) rospy.init_node('talker', anonymous=True) rate = rospy.Rate(10) # 10hz while not rospy.is_shutdown(): hello_str = "hello world %s" % rospy.get_time() rospy.loginfo(hello_str) pub.publish(hello_str) rate.sleep() if __name__ == '__main__': try: talker() except rospy.ROSInterruptException: pass ``` >**Subscriber Node:** In the beginner_tutorials/scripts, we create a listener.py file. The following is the code: ``` import rospy from std_msgs.msg import String def callback(data): rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data) def listener(): rospy.init_node('listener', anonymous=True) rospy.Subscriber("chatter", String, callback) rospy.spin() if __name__ == '__main__': listener() ``` > **Making our Python file executable**:warning: :::info First, you need to go to beginner_tutorials/scripts $ chmod +x talker.py $ chmod +x listener.py ::: >**Buliding nodes:** Add the following lines to the middle of **CMakeLists.txt**, you can check this:{%youtube yqYvMEYJoTk%} ``` catkin_install_python(PROGRAMS scripts/talker.py scripts/listener.py DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} ) ``` Run catkin_make in your catkin workspace: :::info $ cd ~/catkin_ws $ catkin_make ::: Before Running Pubulisher and Subscriber make sure: :::info $ roscore $ source ./devel/setup.bash **#In catkin_ws/** ::: >**Running Publisher:** :::info $ rosrun beginner_tutorials talker.py ::: You will get something similar to: ``` hello world 77 hello world 78 hello world 79 hello world 80 hello world 81 hello world 82 ``` >**Running Subscriber:** :::info $ rosrun beginner_tutorials listener.py ::: You will get something similar to: ``` heard hello world 77 heard hello world 78 heard hello world 79 heard hello world 80 heard hello world 81 heard hello world 82 ``` ### Service and Client (C++) :bulb: *Please done following website. [Service and Client(C++)](http://wiki.ros.org/ROS/Tutorials/WritingServiceClient%28c%2B%2B%29)* >***Service node:** In the beginner_tutorials package, create an add_two_ints_server.cpp file in src.* ``` #include "ros/ros.h" #include "beginner_tutorials/AddTwoInts.h" using namespace ros; 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 reaponse: [%ld]", (long int)res.sum); return true; } int main(int argc, char **argv){ init(argc, argv, "add_two_ints_server"); NodeHandle n; ServiceServer service = n.advertiseService("add_two_ints", add); ROS_INFO("Ready to add two ints."); spin(); return 0; } ``` >***Client node:** Same as server node, create an add_two_ints_client.cpp file in src.* ``` #include "ros/ros.h" #include "beginner_tutorials/AddTwoInts.h" #include <cstdlib> using namespace ros; int main(int argc, char **argv){ init(argc, argv, "add_two_ints_client"); if(argc!=3){ ROS_INFO("usage: add_two_ints_client X Y"); return 1; } NodeHandle n; ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints"); beginner_tutorials::AddTwoInts srv; srv.request.a = atoll(argv[1]); //char to long long int srv.request.b = atoll(argv[2]); 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; } ``` > **Buliding node:** Add the following lines to the bottom of **CMakeLists.txt** ``` 27 add_executable(add_two_ints_server src/add_two_ints_server.cpp) 28 target_link_libraries(add_two_ints_server ${catkin_LIBRARIES}) 29 add_dependencies(add_two_ints_server beginner_tutorials_gencpp) 30 31 add_executable(add_two_ints_client src/add_two_ints_client.cpp) 32 target_link_libraries(add_two_ints_client ${catkin_LIBRARIES}) 33 add_dependencies(add_two_ints_client beginner_tutorials_gencpp) ``` Run catkin_make in your catkin workspace: :::info $ cd ~/catkin_ws $ catkin_make ::: > **Running the Server:** :::info $ rosrun beginner_tutorials add_two_ints_server ::: You will get something similar to: ``` Ready to add two ints. ``` > **Running the Client:** :::info $ rosrun beginner_tutorials add_two_ints_client 1 3 ::: You will get something similar to: ``` #In the client's shell Sum: 4 ``` ``` #In the server's shell request: x=1, y=3 sending back response: [4] ``` ### Service and Client (Python) :bulb: *Please done following website. [Service and Client(python)](http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber%28python%29)* :::info $ roscd beginner_tutorials **#Change directory into the beginner_tutorials package** $ cd scripts ::: >**Service Node:** In the beginner_tutorials/scripts, we create a add_two_ints_server.py file. The following is the code. ``` from __future__ import print_function from beginner_tutorials.srv import AddTwoInts,AddTwoIntsResponse import rospy def handle_add_two_ints(req): print("Returning [%s + %s = %s]"%(req.a, req.b, (req.a + req.b))) return AddTwoIntsResponse(req.a + req.b) def add_two_ints_server(): rospy.init_node('add_two_ints_server') s = rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints) print("Ready to add two ints.") rospy.spin() if __name__ == "__main__": add_two_ints_server() ``` >**Client Node:** In the beginner_tutorials/scripts, we create a add_two_ints_client.py file. The following is the code. ``` from __future__ import print_function import sys import rospy from beginner_tutorials.srv import * def add_two_ints_client(x, y): rospy.wait_for_service('add_two_ints') try: add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts) resp1 = add_two_ints(x, y) return resp1.sum except rospy.ServiceException as e: print("Service call failed: %s"%e) def usage(): return "%s [x y]"%sys.argv[0] if __name__ == "__main__": if len(sys.argv) == 3: x = int(sys.argv[1]) y = int(sys.argv[2]) else: print(usage()) sys.exit(1) print("Requesting %s+%s"%(x, y)) print("%s + %s = %s"%(x, y, add_two_ints_client(x, y))) ``` > **Making our Python file executable** :::info First, you need to go to beginner_tutorials/scripts $ chmod +x add_two_ints_server.py $ chmod +x add_two_ints_client.py ::: >**Buliding nodes:** Add the following lines to the middle of **CMakeLists.txt**, you can check this:{%youtube o0difVe6GOw%} ``` catkin_install_python(PROGRAMS scripts/add_two_ints_server.py scripts/add_two_ints_client.py DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} ) ``` Run catkin_make in your catkin workspace: :::info $ cd ~/catkin_ws $ catkin_make ::: > **Running the Server:** :::info $ rosrun beginner_tutorials add_two_ints_server.py ::: You will get something similar to: ``` Ready to add two ints. ``` > **Running the Client:** :::info $ rosrun beginner_tutorials add_two_ints_client.py 1 3 ::: You will get something similar to: ``` #In the client's shell Requesting 1+3 1 + 3 = 4 ``` ### Recording and playing back data :bulb: *Please done following website. [Recording and playing back data](http://wiki.ros.org/ROS/Tutorials/Recording%20and%20playing%20back%20data)* Examine the full list of topics that are currently being published in the running system: :::info $ rostopic list -v ::: You will get something similar to: ``` Published topics: * /turtle1/color_sensor [turtlesim/Color] 1 publisher * /turtle1/cmd_vel [geometry_msgs/Twist] 1 publisher * /rosout [rosgraph_msgs/Log] 2 publishers * /rosout_agg [rosgraph_msgs/Log] 1 publisher * /turtle1/pose [turtlesim/Pose] 1 publisher Subscribed topics: * /turtle1/cmd_vel [geometry_msgs/Twist] 1 subscriber * /rosout [rosgraph_msgs/Log] 1 subscriber ``` >**Record** Record the published data: :::info $ mkdir ~/bagfiles $ cd ~/bagfiles $ rosbag record -a ::: >**Examining and playing the bag file** Get the information of the bag file: :::info rosbag info <your bagfile> ::: Replay the record in the bag file: :::info rosbag play <your bagfile> ::: ## Other tutorial video https://www.bilibili.com/s/video/BV1AZ4y1F7xQ