阿里雲官方映象站:ROS2源
developer.aliyun.com/mirror/?utm_c...
建立工作空間,即編寫程式碼的位置mkdir -p dev_ws/src
進入 dev_ws/src 路徑下:
先建立依賴包 tutorial_interfaces:
ros2 pkg create --build-type ament_cmake tutorial_interfaces
進入 dev_ws/src/tutorial_interfaces ,然後建立msg和srv目錄存放.msg檔案和.srv檔案:
mkdir msg
mkdir srv
進入dev_ws/src/tutorial_interface/msg目錄,新建 Num.msg檔案:
int64 num
進入 dev_ws/src/tutorial_interface/srv目錄,新建 AddThreeInts.srv檔案:
int64 a
int64 b
int64 c
---
int64 sum
然後編輯CMakeLists.txt檔案:
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/Num.msg"
"srv/AddThreeInts.srv"
)
繼續編輯package.xml:
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
最後回到工作空間路徑下編譯構建 tutorial_interfaces 包:
colcon build --packages-select tutorial_interfaces
以上操作步驟詳細連結:
Creating custom ROS 2 msg and srv files — ROS 2 Documentation: Galactic documentation
然後在工作空間的src路徑下建立自己的包 service,並指定依賴的包:
ros2 pkg create --build-type ament_cmake service --dependencies rclcpp tutorial_interfaces
在 dev_ws/src/service/src目錄下新建service.cpp
#include "rclcpp/rclcpp.hpp"
#include "tutorial_interfaces/srv/add_three_ints.hpp" // CHANGE
#include <memory>
void add(const std::shared_ptr<tutorial_interfaces::srv::AddThreeInts::Request> request, // CHANGE
std::shared_ptr<tutorial_interfaces::srv::AddThreeInts::Response> response) // CHANGE
{
response->sum = request->a + request->b + request->c; // CHANGE
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %ld" " b: %ld" " c: %ld", // CHANGE
request->a, request->b, request->c); // CHANGE
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%ld]", (long int)response->sum);
}
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_three_ints_server"); // CHANGE
rclcpp::Service<tutorial_interfaces::srv::AddThreeInts>::SharedPtr service = // CHANGE
node->create_service<tutorial_interfaces::srv::AddThreeInts>("add_three_ints", &add); // CHANGE
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Ready to add three ints."); // CHANGE
rclcpp::spin(node);
rclcpp::shutdown();
}
進入工作空間的src路徑下,執行下面命令生成待測模組client:
ros2 pkg create --build-type ament_cmake client --dependencies rclcpp tutorial_interfaces
進入client路徑下,分別新建src,test目錄,include目錄預設已經存在。
include目錄下檔案:client.h和params.h
// client.h
#ifndef CLIENT_H
#define CLIENT_H
class ClientHandler
{
public:
ClientHandler();
~ClientHandler();
bool sendParams(int argc, char **argv);
};
#endif
// params.h
#ifndef PARAMS_H
#define PARAMS_H
extern int my_argc;
extern char** my_argv;
#endif
src目錄下檔案:client.cpp和main.cpp
// client.cpp
#include "rclcpp/rclcpp.hpp"
#include "tutorial_interfaces/srv/add_three_ints.hpp"
#include "../include/client.h"
#include <chrono>
#include <cstdlib>
#include <memory>
#include<vector>
using namespace std;
using namespace std::chrono_literals;
// 建構函式
ClientHandler::ClientHandler(){
}
// 解構函式
ClientHandler::~ClientHandler(){
}
// 普通函式——傳送引數
bool ClientHandler::sendParams(int argc, char **argv)
{
rclcpp::init(argc, argv);
if (argc != 4) {
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "usage: add_three_ints_client X Y Z");
return false;
}
std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_three_ints_client");
rclcpp::Client<tutorial_interfaces::srv::AddThreeInts>::SharedPtr client =
node->create_client<tutorial_interfaces::srv::AddThreeInts>("add_three_ints");
auto request = std::make_shared<tutorial_interfaces::srv::AddThreeInts::Request>();
request->a = atoll(argv[1]);
request->b = atoll(argv[2]);
request->c = atoll(argv[3]);
while (!client->wait_for_service(1s)) {
if (!rclcpp::ok()) {
RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting.");
return false;
}
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again...");
}
auto result = client->async_send_request(request);
// Wait for the result.
if (rclcpp::spin_until_future_complete(node, result) ==
rclcpp::FutureReturnCode::SUCCESS)
{
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Sum: %ld", result.get()->sum);
} else {
RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Failed to call service add_three_ints");
}
rclcpp::shutdown();
return true;
}
// main.cpp
#include "../include/client.h"
int main(int argc, char **argv){
// 注意這裡: C++ 編譯器把不帶引數的建構函式優先認為是一個函式宣告
ClientHandler client{};
client.sendParams(argc, argv);
}
test目錄下檔案:clientTest.cpp和main.cpp
// clientTest.cpp
#include "gtest/gtest.h"
#include "../include/client.h"
#include "../include/params.h"
TEST(ClientHandler, sendParams)
{
// 測試的時候的互動方式也不能改變,既然client實際的效果是在命令列輸入引數,
// 那這裡也是這樣的效果
ClientHandler client{};
EXPECT_EQ(true, client.sendParams(my_argc, my_argv));
}
// main.cpp
#include <gtest/gtest.h>
// #include <gmock/gmock.h>
int my_argc;
char** my_argv;
int main(int argc, char** argv) {
// ::testing::InitGoogleMock(&argc, argv);
// 注意這裡使用的是Gtest,不是Gmock
::testing::InitGoogleTest(&argc, argv);
// Runs all tests using Google Test.
my_argc = argc;
my_argv = argv;
return RUN_ALL_TESTS();
}
CMakeLists.txt檔案:
cmake_minimum_required(VERSION 3.8)
project(client)
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(tutorial_interfaces REQUIRED)
set(SRC
src/client.cpp
src/main.cpp
)
add_executable(client
${SRC}
)
ament_target_dependencies(client
rclcpp tutorial_interfaces)
# 5. 新增當前專案中的標頭檔案 注意有順序的要求,不能亂
target_include_directories(client
PRIVATE
${PROJECT_SOURCE_DIR}/include
)
# 如果是測試程式碼
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# 加入gtest包
find_package(ament_cmake_gtest REQUIRED)
# the following line skips the linter which checks for copyrights
# uncomment the line when a copyright and license is not present in all source files
# set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# uncomment the line when this package is not in a git repo
# set(ament_cmake_cpplint_FOUND TRUE)
set(TEST
test/main.cpp
test/clientTest.cpp
)
# 生成加入gtest的test執行檔案。${PROJECT_NAME}_test為自定義的test執行檔名稱;test/demo_test.cpp為test原始碼路徑
# 注意這裡導包的時候,不再需要將 .h 檔案匯入進來,因為在 client.cpp中已經匯入了我們需要使用到的.h檔案
# 另外,注意這裡不能匯入開發程式碼中的 main.cpp,因為已經有了一個測試的main.cpp
ament_add_gtest(${PROJECT_NAME}_test ${TEST} src/client.cpp)
# 務必注意這裡需要新增的依賴包
ament_target_dependencies(${PROJECT_NAME}_test rclcpp tutorial_interfaces)
install(TARGETS
${PROJECT_NAME}_test
# 將生成的test執行檔案安裝到DESTINATION後的路徑下
DESTINATION lib/${PROJECT_NAME})
ament_lint_auto_find_test_dependencies()
endif()
install(TARGETS
client
DESTINATION lib/${PROJECT_NAME})
# 設定編譯構建型別為 除錯 模式
set(CMAKE_BUILD_TYPE Debug)
# 生成覆蓋率檔案
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage")
ament_package()
package.xml檔案:
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>client</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="zhi@todo.todo">zhi</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<depend>tutorial_interfaces</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
然後回到工作空間執行編譯構建命令:
colcon build --packages-select client
進入到 dev_ws/build/client路徑下,找到client_test可執行檔案即為生成的測試檔案
在另外一個終端啟動service包:
ros2 run cpp_srvcli server
執行
./client_test 23 3 4
執行測試檔案即可。
生成覆蓋率的指令碼:
#!/usr/bin/bash
echo "begin gen coverage file ..."
lcov --no-external --capture --initial --directory . --output-file /home/zhi/ros2-gtest-gmock/info/ros2_base.info
cd /home/zhi/ros2-gtest-gmock/build/client;./client_test 45 56 56;cd /home/zhi/ros2-gtest-gmock
# 注意下面的=號兩側是不可以有空格的,這是個大坑
current_path=$(pwd)
echo "當前目錄是:" $current_path
lcov --no-external --capture --directory . --output-file /home/zhi/ros2-gtest-gmock/info/ros2.info
lcov --add-tracefile /home/zhi/ros2-gtest-gmock/info/ros2_base.info --add-tracefile /home/zhi/ros2-gtest-gmock/info/ros2.info --output-file /home/zhi/ros2-gtest-gmock/info/ros2_coverage.info
mkdir -p coverage && genhtml /home/zhi/ros2-gtest-gmock/info/ros2_coverage.info --output-directory coverage
程式封裝改寫:
如果不知道返回型別,直接斷點下去看下型別:
本文轉自:www.cnblogs.com/huaibin/p/15423963...
本作品採用《CC 協議》,轉載必須註明作者和本文連結