Introduction to ROS 21 lecture notes (summary for personal use)

Keywords: C++

sketch

After a week's reading Mr. Gu Yue's introduction to ROS 21, I clarified how ROS workspace and various function packages communicate with each other, as well as how to build their own function package and write internal code. This paper skips the environment construction problems such as Linux command line and ROS installation, and focuses on recording how to write ROS programs.

Create workspaces and feature packs

Create a project folder in home, create a src folder in the project folder, then enter the src folder for workspace initialization and function package creation, and finally compile in the workspace folder. Finally, you need to set environment variables. The specific command line code is as follows:

mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_init_workspace
catkin_pkg_create pkg_name depend1 depend2  ....
cd ..
catkin_make
source ~/catkin_ws/devel/setup.bash
echo "source /WORKSPACE/devel/setup.bash">>~/.bashrc //Add to system environment variable

All subsequent code writing will be carried out in this workspace and function package.

Topic

A topic is a channel through which publishers and subscribers transmit Messages to each other. It is a one-way communication. A topic can include multiple msg objects. You need to master the writing methods of Messages, publishers and subscribers. Publisher s and subscribers can be written in C + + or python. C + + needs to be modified in the CMakelist file to make the compilation successful; Python does not need to compile, but needs to change the execution permission. The following code is written in C + +, and the following is an overall summary.

Messages

Create the MSG folder in the function package and create the. MSG file in it. The file name is the object name. For example, asoul.msg creates the new object asoul. Then you need to configure dependency and Compilation Rules in CMakelist.txt and package.xml files in the function package.

Each message is a strict data structure. It supports standard data types (integer, floating point, Boolean, etc.), nested structures and arrays (similar to the structure struct of C language). It can also be independently defined by developers according to requirements—— ROS robot development practice

MESSAGE.msg

string name
uint8 age
............ 

CMakelist.txt

find_package(..... message_generation)

add_message_files(FILES MESSAGE.msg)
generate_messages(DEPENDENCIES std_msgs)

catkin_package(... message_runtime)

package.xml

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

Publisher (publisher)

You need to create a. cpp file under the src file of the function package. The file name should be the same as the release name for subsequent use. Unlike message, publishers only need to configure the CMakelist.txt file.
PUBLISHER.cpp

#include <ros/ros.h>
#include <package_ name/msgs_ Name. H > / / publish a message under a function pack

int main(int argc,char *argv){
	// Node initialization
	ros::init(argc,argv,"node_name");
	//Create node handle
	ros::NodeHandle n;
	//Create a Publisher object named / topic_name, where you can select an existing topic. The message type is the class defined by msgs, and the queue length is 10
	ros::Publisher publisher_name = n.advertise<msgs Type of>("/topic_name",10);
	//Set cycle frequency, 10ms
	ros::Rate loop_rate(10);
	while(ros::ok()){
		//Initialize the message type under msgs, create a new msg object and assign a value to the object variable
		msgs::asoul  idol;
		idol.name = "";
		idol.age = "";
		//Release news
		publisher_name.publish(idol);
		ROS_INFO()	//Log content
		//Delay according to cycle frequency
		loop_rate.sleep();
	}
	return 0;
}

CMakelist.txt

add_executable(PUBLISHER src/PUBLISHER.cpp)
target_link_libraries(PUBLISHER ${catkin_LIBRARIES})

Subscriber

Like the publisher, you need to create a. cpp file in the src folder of the function package and configure the CMakelist.txt file.
SUBSCRIBER.cpp

#include <ros/ros.h>
#include <package_ name/msgs_ name.h> 	// Subscribe to a message under a feature pack

// After receiving the subscribed message type, it will enter the callback function
void callback(const package::msg_name::ConstPtr& msg){
	//The task under the callback function cannot be too complex, otherwise the publisher's real-time data will be lost when processing data
	ROS_INFO();
}

int main(int argc,char *argv){
	// Node initialization
	ros::init(argc,argv,"node_name");
	//Create node handle
	ros::NodeHandle n;
	//Create a Subscriber object and subscribe to / topic_name's topic, register callback function
	ros::Subscriber subscriber_name = n.subscribe("/topic_name",10,callback);
	//Loop waiting callback function
	ros::spin();
	return 0;
}

CMakelist.txt

add_executable(SUBSCRIBER src/SUBSCRIBER.cpp)
target_link_libraries(SUBSCRIBER ${catkin_LIBRARIES})

Service

Different from topic, service is a way of synchronous communication, which allows the client to send a request to the server, and the server to respond after processing. Similar to the topic, you also need to master the writing methods of service data, client and server. After writing, you also need to configure files.

Service data (srv)

Create an srv folder in the function package and create a. srv file in it. The srv file includes two data fields: request and response. The data type is similar to the message msg, but you need to use --- to split the data field. You also need to configure dependency and Compilation Rules in package.xml and CMakelist.txt files in the function package.
Test.srv

int64 id
...............	//The data type of the request sent by the client
---
String name
...............	//Data type of response sent by the server

CMakelist.txt

find_package(..... message_generation)

add_message_files(FILES Test.srv)
generate_messages(DEPENDENCIES std_msgs)

catkin_package(... message_runtime)

package.xml

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

client

You also need to create a. cpp file in the src folder under the function package,
CLIENT.cpp

#include <ros/ros.h>
#include <package_ name/srv_ Name. H > / / publish a service data under a function package

int main(int argc, char** argv){
	//Initialize ROS node
	ros::init(argc, argv, "Node_NAME");
	//Create node handle
	ros::NodeHandle n;
	//Wait for the SERVER to start, create a client and connect
	ros::service::waitForService("/SERVER_NAME");
	ros::ServiceClient client = n.serviceClient<package_name::srv_name>("/SERVER_NAME");
	
	//Initialize the request data, that is, assign a value to the srv request data
	package_name::srv_name srv;
	srv.request.a = 2;
	.....
	
	//Request service call 	 call 	 waitForService 	 Spins are blocking functions. If there is no feedback, the subsequent code will not run
	client.call(srv);

	//If you get feedback from the server, you can run to see if srv.response is called successfully
	ROS_INFO("%s",srv.response.name.c_str());
	
	return 0;
}

CMakelist.txt

add_executable(CLIENT src/CLIENT.cpp)
target_link_libraries(CLIENT ${catkin_LIBRARIES})

Server

Gu Yue joined the publisher on the server of lecture 21 to realize the little turtle's circle movement. Services and topics can be written to a file at the same time, so as to realize the functions we want. As it is a summary note, only how to accept the request sent by the client and how to send the response are written here. The specific code is as follows.
SERVER.cpp

#include <ros/ros.h>
#include <package_ name/srv_ Name. H > / / publish a service data under a function package

//Callback function, input parameter req, output parameter res
void callback(package_name::srv_name::Request &req, package_name::srv_name::Response &res){
	//Similar to the subscriber's callback function, the difference is that there are two data entry functions, which can obtain the request data for query or calculation, and set the feedback data
	ROS_INFO("%d", req.id);
	res.name = "JR";
	.......
}

int main(int argc, char **argv){
	//ROS node initialization
	ros::init(argc, argv, "Node_Name");
	//Create node handle
	ros::NodeHandle n;
	//Create a file named / Server_name's server, register callback function
	ros::ServiceServer server = n.advertiseService("/Server_name", callback);
	//Loop waiting callback function
	ROS_INFO("");
	ros::spin();   
	//If the queue is queried in the while loop, you need to use the ros::spinOnce() method to make the while run normally
	return 0;
}

CMakelist.txt

add_executable(SERVER src/SERVER.cpp)
target_link_libraries(SERVER ${catkin_LIBRARIES})

Coordinate management system (TF)

In the process of robot design and robot application, the position and attitude of different components will be involved, which requires the introduction of the concept of coordinate system and coordinate transformation. TF function package is a function package that allows users to track multiple coordinate systems over time. It uses a tree data structure to buffer and maintain the coordinate transformation relationship between multiple coordinate systems according to time, which can help us complete the coordinate transformation of points, vectors, etc. between coordinate systems at any time. To use the TF function pack, you need to write two parts: broadcast and Listener.

Broadcast

Teacher Gu Yue used the little turtle example, which is easier to understand. Here, we only write how to broadcast the coordinate system and coordinate transformation. Similarly, you need to create a. cpp file under the src of the function package and configure the Compilation Rules for CMakelist.txt in the function package.
Broadcaster.cpp

#include <ros/ros.h>
#include <tf/transform_broadcaster.h>
#include <package_ name/pose_ msg.h> 	// Here you need to add the coordinate data to be broadcast

//msg in the callback function may contain x,y,z and r,p,y. if it exists, msg - > is used for assignment
void poseCallback(const package_name::pose_msgConstPtr& msg){
	//Create tf's broadcaster
	static tf::TransformBroadcaster br;

	//Initialize tf data, that is, create coordinate transformation values
	tf::Transform transform;
	transform.setOrigin(tf::Vector3(msg->x,msg->y,msg->z));	//Set coordinate transformation translation
	tf::Quaternion q;	//Here, RPY is used to set the coordinate transformation rotation, that is, xyz three-axis rotation, and quaternion can also be used directly
	q.setRPY(roll, pitch, yaw);
	transform.setRotation(q)	//transform.setRotation(tf::Quaternion( , , )); Quaternion method
	//Broadcast tf data between two coordinate systems
	br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "frame1", "frame2"));
}

int main(int argc, char** argv){
	//Initialize ROS node
	ros::init(argc, argv, "node_name");
	//Create node handle
	ros::NodeHandle n;
	//Subscribe to pose topics with coordinate information
	ros::Subscriber sub = n.subscribe("",10.&poseCallback);
	//Loop waiting callback function
	ros::spin();
	
	return 0;
}

CMakelist.txt

add_executable(Broadcaster src/Broadcaster.cpp)
target_link_libraries(Broadcaster ${catkin_LIBRARIES})

Listener

The listener can be used to monitor and calculate tf data. In the same way, it only writes the code related to monitoring, and does not expand the specific application.
Listener.cpp

#include <ros/ros.h>
#include <tf/transform_listener.h>

int main(int argc, char **argv){
	//Initialize ROS node
	ros::init(argc, argv, "node_name");
	//Create node handle
	ros::NodeHandle n;
	//Obtain the coordinate transformation relationship between frame1 and frame2
	tf::TransformListener listener;
	while(ros.ok()){
		tf::StampedTransform transform;
		try{
			listener.waitForTransform("/frame1", "/frame2", ros::Time(0), ros::Duration(3.0));	//
			listener.lookupTransform("/frame1", "/frame2", ros::Time(0),transform);
		}catch{
			ROS_ERROR("%s",ex.what());
			ros::Duration(1.0).sleep();
			continue;
		}
		//Operation using data in transform
		float angular = atan2(transform.getOrigin().x(),transform.getOrigin().y());
		float linear = sqrt(pow(transform.getOrigin().x(),2))+
											pow(transform.getOrigin().y(),2));
		ROS_INFO(angular, linear);
	}

	return 0;
}

CMakelist.txt

add_executable(Listener src/Listener.cpp)
target_link_libraries(Listener ${catkin_LIBRARIES})

Use of startup file (. launch)

The startup file can start multiple nodes at the same time and automatically start the ROS Master node manager, and can realize the configuration of each node, providing great convenience for multi node operation. Write an example here to simply list the basic elements, such as node, launch, param, arg, etc. The internal attribute function of each element needs to refer to Mr. Gu Yue's ROS robot development practice or reference launch file parsing
Test.launch

<launch>
	
	<---param For parameters in the global dictionary, arg by launch Parameters used under file--->
	<param name="param_name"	value=""/>
	<arg name="argument_name" value="">
	
	<node pkg="package_name" type="node_file_name" name="node_name">
		<param name="argTest" value="&(arg argument_name)"/>
		<rosparam file="FILE_PATH/test.yaml" command=load/>
	</node>
	
	<node pkg="package_name" type="node_file_name" name="node_name" args="argument" output="screen"/>
	
</launch>

epilogue

So far, I have understood some superficial aspects of ROS programming and still need to be further practiced in application. The above is not the whole content of lecture 21. The installation of Ubuntu and ROS is also introduced in detail in teacher Gu Yue's lecture 21. In addition, there are the applications of visualization tools such as Qt, Rivz and Gezabo. Thank you very much for teacher Gu Yue's courses and books, which make the learning road of ROS relatively smooth.
Fear what truth is infinite, there is further joy.

Posted by lorddemos90 on Wed, 20 Oct 2021 10:26:30 -0700