본 포스트는 ROS1에 대한 전반적인 내용 및 ROS2 Python에 대한 지식이 있다는 가정 하에 작성되었으며, The Construct Youtube 영상을 참고하였습니다.
또한, 시뮬레이션 환경은 turtlebot3 waffle_pi를 사용합니다 : https://github.com/ROBOTIS-GIT/turtlebot3_simulations
1. Service Client
기본적인 내용은 python과 같으니, 다 생략하고 바로 예제로 넘어갑니다.
1. 패키지 만들기
$ ros2 pkg create client_cpp_pkg --build-type ament_cmake --dependencies rclcpp std_srvs
2. cpp 코드 작성
service_client.cpp
#include "rclcpp/rclcpp.hpp"
#include "std_srvs/srv/empty.hpp"
#include <chrono>
#include <cstdlib>
#include <memory>
using namespace std::chrono_literals;
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("service_client");
rclcpp::Client<std_srvs::srv::Empty>::SharedPtr client =
node->create_client<std_srvs::srv::Empty>("reset_world");
auto request = std::make_shared<std_srvs::srv::Empty::Request>();
while (!client->wait_for_service(1s)) {
if (!rclcpp::ok()) {
RCLCPP_ERROR(rclcpp::get_logger("rclcpp"),
"Interrupted while waiting for the service. Exiting.");
return 0;
}
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again...");
}
auto result_future = client->async_send_request(request);
if (rclcpp::spin_until_future_complete(node, result_future) ==
rclcpp::FutureReturnCode::SUCCESS)
{
auto result = result_future.get();
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "The world has been reset");
} else {
RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Failed to call service /reset_world");
}
rclcpp::shutdown();
return 0;
}
3. launch 파일 만들기
service_client.launch.py
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='client_cpp_pkg',
executable='service_client_node',
output='screen'),
])
4. CMakeLists.txt 수정
cmake_minimum_required(VERSION 3.8)
project(client_cpp_pkg)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_srvs REQUIRED)
add_executable(service_client_node src/service_client.cpp)
ament_target_dependencies(service_client_node rclcpp std_srvs)
install(TARGETS
service_client_node
DESTINATION lib/${PROJECT_NAME}
)
# Install launch files.
install(DIRECTORY
launch
DESTINATION share/${PROJECT_NAME}/
)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# comment the line when a copyright and license is added to all source files
set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# comment the line when this package is in a git repo and when
# a copyright and license is added to all source files
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
5. 빌드 후, 실행
$ ros2 launch client_pkg service_client_launch_file.launch.py
launch 파일을 실행하면 시뮬레이션이 초기화되는 것을 볼 수 있습니다
2. Spinning Node에서 service call 하기
이전 예제의 line 29~31을 보면 다음과 같습니다.
그런데 node가 이미 디폴트로 spin 상태로 선언되어있을 수 있습니다. 이런 경우에 대한 예제를 보겠습니다.
1. cpp파일 생성
service_client_2.cpp
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/timer.hpp"
#include "std_srvs/srv/empty.hpp"
#include <chrono>
#include <cstdlib>
#include <future>
#include <memory>
using namespace std::chrono_literals;
class ServiceClient : public rclcpp::Node {
private:
rclcpp::Client<std_srvs::srv::Empty>::SharedPtr client_;
rclcpp::TimerBase::SharedPtr timer_;
bool service_done_ = false;
void timer_callback() {
while (!client_->wait_for_service(1s)) {
if (!rclcpp::ok()) {
RCLCPP_ERROR(
this->get_logger(),
"Client interrupted while waiting for service. Terminating...");
return;
}
RCLCPP_INFO(this->get_logger(),
"Service Unavailable. Waiting for Service...");
}
auto request = std::make_shared<std_srvs::srv::Empty::Request>();
service_done_ = false;
auto result_future = client_->async_send_request(
request, std::bind(&ServiceClient::response_callback, this,
std::placeholders::_1));
}
void
response_callback(rclcpp::Client<std_srvs::srv::Empty>::SharedFuture future) {
auto status = future.wait_for(1s);
if (status == std::future_status::ready) {
RCLCPP_INFO(this->get_logger(), "Result: success");
service_done_ = true;
} else {
RCLCPP_INFO(this->get_logger(), "Service In-Progress...");
}
}
public:
ServiceClient() : Node("service_client") {
client_ = this->create_client<std_srvs::srv::Empty>("reset_world");
timer_ = this->create_wall_timer(
1s, std::bind(&ServiceClient::timer_callback, this));
}
bool is_service_done() const { return this->service_done_; }
};
int main(int argc, char *argv[]) {
rclcpp::init(argc, argv);
auto service_client = std::make_shared<ServiceClient>();
while (!service_client->is_service_done()) {
rclcpp::spin_some(service_client);
}
rclcpp::shutdown();
return 0;
}
2. launch 파일 생성, CMakeLists.txt 파일 수정 후 실행해봅니다.
결과는 이전과 같습니다. 이전 코드와 달라진 부분은 아래와 같습니다.
3. Service Server 만들기
1. 패키지 생성 후, 코드 작성
$ ros2 pkg create server_cpp_pkg --build-type ament_cmake --dependencies rclcpp geometry_msgs std_srvs
service_server.cpp
#include "rclcpp/rclcpp.hpp"
#include "std_srvs/srv/set_bool.hpp"
#include "geometry_msgs/msg/twist.hpp"
#include <memory>
using SetBool = std_srvs::srv::SetBool;
using std::placeholders::_1;
using std::placeholders::_2;
class ServerNode : public rclcpp::Node
{
public:
ServerNode()
: Node("service_moving")
{
srv_ = create_service<SetBool>("moving_right", std::bind(&ServerNode::moving_callback, this, _1, _2));
publisher_ = this->create_publisher<geometry_msgs::msg::Twist>("cmd_vel", 10);
}
private:
rclcpp::Service<SetBool>::SharedPtr srv_;
rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr publisher_;
void moving_callback(
const std::shared_ptr<SetBool::Request> request,
const std::shared_ptr<SetBool::Response> response)
{
auto message = geometry_msgs::msg::Twist();
if (request->data == true)
{
message.linear.x = 0.2;
message.angular.z = -0.2;
publisher_->publish(message);
response->success = true;
response->message = "Turning to the right!";
}
if (request->data == false)
{
message.linear.x = 0.0;
message.angular.z = 0.0;
publisher_->publish(message);
response->success = false;
response->message = "stop!";
}
}
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<ServerNode>());
rclcpp::shutdown();
return 0;
}
2. launch 파일 작성
service_server.launch.py
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='server_cpp_pkg',
executable='service_server_node',
output='screen'),
])
3. CMakeLists.txt 수정
CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(server_cpp_pkg)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(std_srvs REQUIRED)
add_executable(service_server_node src/service_server.cpp)
ament_target_dependencies(service_server_node rclcpp geometry_msgs std_srvs)
install(TARGETS
service_server_node
DESTINATION lib/${PROJECT_NAME}
)
install(DIRECTORY
launch
DESTINATION share/${PROJECT_NAME}/
)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# comment the line when a copyright and license is added to all source files
set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# comment the line when this package is in a git repo and when
# a copyright and license is added to all source files
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
4. 빌드 및 실행
$ ros2 service call /moving_right std_srvs/srv/SetBool data:\ true\
$ ros2 service call /moving_right std_srvs/srv/SetBool data:\ false\
Custom srvice만드는 법은 python과 동일하므로 생략하겠습니다.
'Study > [ROS2] ROS2 Basic' 카테고리의 다른 글
ROS2 with C++ - Node composition (0) | 2023.07.30 |
---|---|
ROS2 with C++ - Action (0) | 2023.07.30 |
ROS2 with C++ - Executors and Callbacks (0) | 2023.07.30 |
ROS2 with C++ - Topic (0) | 2023.07.28 |
ROS2 with python - Actions (0) | 2023.07.28 |