Keypoints
- ROS Server
// to excute turtle_command service, service data type is std_srvs::Trigger
#include <ros/ros.h>
#include <std_srvs/Trigger.h>
#include <geometry_msgs/Twist.h>
ros::Publisher turtle_vel_pub;
bool pubCommand = false;
// arg: req, res. req here is empty, res contains two variables: bool success and string message.
bool commandCallback(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &res)
{
pubCommand = !pubCommand; // initial pubCommand is false, when receive the service request, change pubCommand state.
res.success = true;
res.message = "change turtle command state!";
ROS_INFO("send turtle velocity command[%s]", pubCommand == true ? "true" : "false");
return true;
}
int main(int argc, char **argv)
{
// init node
ros::init(argc, argv, "turtle_command_server");
ros::NodeHandle n;
// create service server: command_service, register callback function: commandCallback. Define the service name: /turtle_command.
// When receive the service "/turtle_command" request, call the callback function.
ros::ServiceServer command_service = n.advertiseService("/turtle_command", commandCallback); //.advertiseService() is a function of NodeHandle class, is to create a service server.
// create publisher: turtle_vel_pub, define the topic name: /turtle1/cmd_vel, define the message type: geometry_msgs::Twist, queue size: 10
turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10);
ROS_INFO("ready to receive turtle command");
ros::Rate loop_rate(10); // set the loop rate: 10Hz
while (ros::ok())
{
ros::spinOnce(); // to check the callback function queue. If there is a callback function, call it. If not, continue the while loop.
if (pubCommand) // if pubCommand is true, publish the velocity command
{
geometry_msgs::Twist vel_msg;
vel_msg.linear.x = 0.5;
vel_msg.angular.z = 0.2;
turtle_vel_pub.publish(vel_msg);
}
loop_rate.sleep(); // sleep to keep the loop rate: 10Hz
}
return 0;
}
add_executable(turtle_command_server src/turtle_command_server.cpp) ## to compile the cpp file into executable file: turtle_command_server. src is the folder name, turtle_command_server.cpp is the cpp file name.
target_link_libraries(turtle_command_server ${catkin_LIBRARIES}) ## to link the executable file with catkin_LIBRARIES
also, uncomment the following line in CMakeLists.txt:
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES learning_service
CATKIN_DEPENDS geometry_msgs roscpp rospy turtlesim ## here is to add CATKIN_DEPENDS geometry_msgs roscpp rospy turtlesim
# DEPENDS system_lib
)
source devel/setup.bash
roscore
rosrun turtlesim turtlesim_node
rosrun learning_service turtle_command_server
rosservice call /turtle_command "{}"
// to call the service, here is to call the service “/turtle_command”, “{}” is the request data, here is empty. #!/usr/bin/env python
# -*- coding: utf-8 -*-
import rospy
from geometry_msgs.msg import Twist
import thread, time
from std_srvs.srv import Trigger, TriggerResponse
# global variable
pubCommand = False
turtle_vel_pub = rospy.Publisher('/turtle1/cmd_vel', Twist, queue_size=10)
def command_thread():
while True:
if pubCommand:
twist = Twist()
twist.linear.x = 0.5
twist.angular.z = 0.2
turtle_vel_pub.publish(twist)
time.sleep(0.1)
def commandCallback(req):
global pubCommand # to get the global variable
pubCommand = not pubCommand
rospy.loginfo("Change turtle command state to " + str(pubCommand))
# return TriggerResponse(success, message). TriggerResponse is a class, success and message are the parameters of the class
return TriggerResponse(True, "Change turtle command state to " + str(pubCommand))
def turtle_command_server():
# init a node
rospy.init_node('turtle_command_server')
# rospy.Service is a class, turtle_command is the name of the service, Trigger is the type of the service, commandCallback is the callback function
s = rospy.Service('turtle_command', Trigger, commandCallback)
print("Waiting for command")
# thread is a module, start_new_thread is a function to start multi thread (Python is usually one thread), command_thread is the thread function
# There is no ros::spinOnce() in python, so we need to use thread to publish the command
thread.start_new_thread(command_thread, ())
# spin() simply keeps python from exiting until this node is stopped
rospy.spin()
if __name__ == "__main__":
turtle_command_server()
Then run the following commands:
chmod 777 scripts/turtle_command_server.py
python scripts/turtle_command_server.py or rosrun learning_service turtle_command_server.py