feat: add all code
This commit is contained in:
17
.vscode/c_cpp_properties.json
vendored
Normal file
17
.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"/opt/ros/humble/include/**"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/bin/gcc",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "gnu++17",
|
||||
"intelliSenseMode": "linux-gcc-x64"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
70
chapt10/chapt10_ws/.vscode/settings.json
vendored
Normal file
70
chapt10/chapt10_ws/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"array": "cpp",
|
||||
"cctype": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"atomic": "cpp",
|
||||
"bit": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"bitset": "cpp",
|
||||
"chrono": "cpp",
|
||||
"compare": "cpp",
|
||||
"complex": "cpp",
|
||||
"concepts": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"deque": "cpp",
|
||||
"forward_list": "cpp",
|
||||
"list": "cpp",
|
||||
"map": "cpp",
|
||||
"set": "cpp",
|
||||
"string": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"vector": "cpp",
|
||||
"exception": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"functional": "cpp",
|
||||
"iterator": "cpp",
|
||||
"memory": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"numeric": "cpp",
|
||||
"optional": "cpp",
|
||||
"random": "cpp",
|
||||
"ratio": "cpp",
|
||||
"string_view": "cpp",
|
||||
"system_error": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"fstream": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"mutex": "cpp",
|
||||
"new": "cpp",
|
||||
"numbers": "cpp",
|
||||
"ostream": "cpp",
|
||||
"semaphore": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"thread": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"typeindex": "cpp",
|
||||
"typeinfo": "cpp"
|
||||
}
|
||||
}
|
||||
2729
chapt10/chapt10_ws/pub5_sub5_2M.txt
Normal file
2729
chapt10/chapt10_ws/pub5_sub5_2M.txt
Normal file
File diff suppressed because it is too large
Load Diff
24
chapt10/chapt10_ws/shm.xml
Normal file
24
chapt10/chapt10_ws/shm.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<profiles xmlns="http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles">
|
||||
|
||||
<data_writer profile_name="default publisher profile" is_default_profile="true">
|
||||
<qos>
|
||||
<publishMode>
|
||||
<kind>SYNCHRONOUS</kind>
|
||||
</publishMode>
|
||||
<data_sharing>
|
||||
<kind>AUTOMATIC</kind>
|
||||
</data_sharing>
|
||||
</qos>
|
||||
<historyMemoryPolicy>DYNAMIC</historyMemoryPolicy>
|
||||
</data_writer>
|
||||
|
||||
<data_reader profile_name="default subscription profile" is_default_profile="true">
|
||||
<qos>
|
||||
<data_sharing>
|
||||
<kind>AUTOMATIC</kind>
|
||||
</data_sharing>
|
||||
</qos>
|
||||
<historyMemoryPolicy>DYNAMIC</historyMemoryPolicy>
|
||||
</data_reader>
|
||||
</profiles>
|
||||
56
chapt10/chapt10_ws/src/learn_compose/CMakeLists.txt
Normal file
56
chapt10/chapt10_ws/src/learn_compose/CMakeLists.txt
Normal file
@@ -0,0 +1,56 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(learn_compose)
|
||||
|
||||
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)
|
||||
# uncomment the following section in order to fill in
|
||||
# further dependencies manually.
|
||||
# find_package(<dependency> REQUIRED)
|
||||
|
||||
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()
|
||||
|
||||
find_package(rclcpp REQUIRED)
|
||||
find_package(std_msgs REQUIRED)
|
||||
find_package(rclcpp_components REQUIRED)
|
||||
include_directories(include)
|
||||
add_executable(intra_process_pubsub
|
||||
src/intra_process_pubsub.cpp
|
||||
src/talker.cpp
|
||||
src/listener.cpp
|
||||
)
|
||||
ament_target_dependencies(intra_process_pubsub std_msgs rclcpp rclcpp_components)
|
||||
|
||||
install(TARGETS intra_process_pubsub
|
||||
DESTINATION lib/${PROJECT_NAME})
|
||||
|
||||
|
||||
find_package(rclcpp_components REQUIRED)
|
||||
add_library(talker_component SHARED src/talker.cpp)
|
||||
ament_target_dependencies(talker_component "std_msgs" "rclcpp" "rclcpp_components")
|
||||
rclcpp_components_register_nodes(talker_component "learn_compose::Talker")
|
||||
|
||||
add_library(listener_component SHARED src/listener.cpp)
|
||||
ament_target_dependencies(listener_component "std_msgs" "rclcpp" "rclcpp_components")
|
||||
rclcpp_components_register_nodes(listener_component "learn_compose::Listener")
|
||||
|
||||
install(TARGETS talker_component listener_component
|
||||
ARCHIVE DESTINATION lib
|
||||
LIBRARY DESTINATION lib
|
||||
RUNTIME DESTINATION bin
|
||||
)
|
||||
|
||||
ament_package()
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef LEARN_COMPOSE__LISTENER_COMPONENT_HPP_
|
||||
#define LEARN_COMPOSE__LISTENER_COMPONENT_HPP_
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "std_msgs/msg/int32.hpp"
|
||||
|
||||
namespace learn_compose {
|
||||
|
||||
class Listener : public rclcpp::Node {
|
||||
public:
|
||||
explicit Listener(const rclcpp::NodeOptions &options);
|
||||
|
||||
private:
|
||||
rclcpp::Subscription<std_msgs::msg::Int32>::SharedPtr sub_;
|
||||
};
|
||||
|
||||
} // namespace learn_compose
|
||||
|
||||
#endif // LEARN_COMPOSE__LISTENER_COMPONENT_HPP_
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef LEARN_COMPOSE__TALKER_COMPONENT_HPP_
|
||||
#define LEARN_COMPOSE__TALKER_COMPONENT_HPP_
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "std_msgs/msg/int32.hpp"
|
||||
|
||||
namespace learn_compose {
|
||||
|
||||
class Talker : public rclcpp::Node {
|
||||
public:
|
||||
explicit Talker(const rclcpp::NodeOptions &options);
|
||||
|
||||
private:
|
||||
int32_t count_;
|
||||
rclcpp::Publisher<std_msgs::msg::Int32>::SharedPtr pub_;
|
||||
rclcpp::TimerBase::SharedPtr timer_;
|
||||
};
|
||||
|
||||
} // namespace learn_compose
|
||||
|
||||
#endif // LEARN_COMPOSE__TALKER_COMPONENT_HPP_
|
||||
18
chapt10/chapt10_ws/src/learn_compose/package.xml
Normal file
18
chapt10/chapt10_ws/src/learn_compose/package.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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>learn_compose</name>
|
||||
<version>0.0.0</version>
|
||||
<description>TODO: Package description</description>
|
||||
<maintainer email="87068644+fishros@users.noreply.github.com">fishros</maintainer>
|
||||
<license>TODO: License declaration</license>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "learn_compose/listener.hpp"
|
||||
#include "learn_compose/talker.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
|
||||
rclcpp::NodeOptions options; // 创建节点选项
|
||||
options.use_intra_process_comms(true); // 使用进程内通信
|
||||
auto talker = std::make_shared<learn_compose::Talker>(options);
|
||||
auto listener = std::make_shared<learn_compose::Listener>(options);
|
||||
|
||||
executor.add_node(talker);
|
||||
executor.add_node(listener);
|
||||
executor.spin();
|
||||
|
||||
rclcpp::shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
20
chapt10/chapt10_ws/src/learn_compose/src/listener.cpp
Normal file
20
chapt10/chapt10_ws/src/learn_compose/src/listener.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "learn_compose/listener.hpp"
|
||||
#include <chrono>
|
||||
|
||||
namespace learn_compose {
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
Listener::Listener(const rclcpp::NodeOptions &options)
|
||||
: Node("listener", options) {
|
||||
sub_ = this->create_subscription<std_msgs::msg::Int32>(
|
||||
"count", 10, [&](const std_msgs::msg::Int32::UniquePtr msg) {
|
||||
RCLCPP_INFO(this->get_logger(), "收到数据:%d(0x%lX)", msg->data,
|
||||
reinterpret_cast<std::uintptr_t>(msg.get()));
|
||||
});
|
||||
}
|
||||
} // namespace learn_compose
|
||||
|
||||
|
||||
#include "rclcpp_components/register_node_macro.hpp"
|
||||
RCLCPP_COMPONENTS_REGISTER_NODE(learn_compose::Listener)
|
||||
24
chapt10/chapt10_ws/src/learn_compose/src/talker.cpp
Normal file
24
chapt10/chapt10_ws/src/learn_compose/src/talker.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include <chrono>
|
||||
#include "learn_compose/talker.hpp"
|
||||
|
||||
namespace learn_compose {
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
Talker::Talker(const rclcpp::NodeOptions &options) : Node("talker", options) {
|
||||
pub_ = this->create_publisher<std_msgs::msg::Int32>("count", 10);
|
||||
auto callback = [&]() -> void {
|
||||
std_msgs::msg::Int32::UniquePtr msg(new std_msgs::msg::Int32());
|
||||
msg->data = count_++;
|
||||
RCLCPP_INFO(this->get_logger(), "发布数据:%d(0x%lX)", msg->data,
|
||||
reinterpret_cast<std::uintptr_t>(msg.get()));
|
||||
pub_->publish(std::move(msg));
|
||||
};
|
||||
timer_ = this->create_wall_timer(1s, callback);
|
||||
}
|
||||
} // namespace learn_compose
|
||||
|
||||
|
||||
|
||||
#include "rclcpp_components/register_node_macro.hpp"
|
||||
RCLCPP_COMPONENTS_REGISTER_NODE(learn_compose::Talker)
|
||||
36
chapt10/chapt10_ws/src/learn_dds_cpp/CMakeLists.txt
Normal file
36
chapt10/chapt10_ws/src/learn_dds_cpp/CMakeLists.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(learn_dds_cpp)
|
||||
|
||||
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_msgs REQUIRED)
|
||||
|
||||
|
||||
add_executable(shm_pub src/shm_pub.cpp)
|
||||
ament_target_dependencies(shm_pub
|
||||
rclcpp std_msgs
|
||||
)
|
||||
|
||||
install(TARGETS shm_pub
|
||||
DESTINATION lib/${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()
|
||||
21
chapt10/chapt10_ws/src/learn_dds_cpp/package.xml
Normal file
21
chapt10/chapt10_ws/src/learn_dds_cpp/package.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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>learn_dds_cpp</name>
|
||||
<version>0.0.0</version>
|
||||
<description>TODO: Package description</description>
|
||||
<maintainer email="87068644+fishros@users.noreply.github.com">fishros</maintainer>
|
||||
<license>TODO: License declaration</license>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<depend>rclcpp</depend>
|
||||
<depend>std_msgs</depend>
|
||||
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
||||
30
chapt10/chapt10_ws/src/learn_dds_cpp/src/shm_pub.cpp
Normal file
30
chapt10/chapt10_ws/src/learn_dds_cpp/src/shm_pub.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "rclcpp/loaned_message.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "std_msgs/msg/int32.hpp"
|
||||
|
||||
class LoanedMessagePublisher : public rclcpp::Node {
|
||||
public:
|
||||
LoanedMessagePublisher() : Node("loaned_message_publisher") {
|
||||
publisher_ =
|
||||
this->create_publisher<std_msgs::msg::Int32>("loaned_int_topic", 10);
|
||||
timer_ = this->create_wall_timer(std::chrono::seconds(1), [&]() {
|
||||
auto message = publisher_->borrow_loaned_message(); // 1.租借消息
|
||||
message.get().data = count_++; // 2.放入数据
|
||||
RCLCPP_INFO(this->get_logger(), "发布数据:%d", message.get().data);
|
||||
publisher_->publish(std::move(message)); // 3.发布数据
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
rclcpp::Publisher<std_msgs::msg::Int32>::SharedPtr publisher_;
|
||||
rclcpp::TimerBase::SharedPtr timer_;
|
||||
int32_t count_{0};
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
rclcpp::init(argc, argv);
|
||||
auto node = std::make_shared<LoanedMessagePublisher>();
|
||||
rclcpp::spin(node);
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
36
chapt10/chapt10_ws/src/learn_executor_cpp/CMakeLists.txt
Normal file
36
chapt10/chapt10_ws/src/learn_executor_cpp/CMakeLists.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(learn_executor_cpp)
|
||||
|
||||
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)
|
||||
# uncomment the following section in order to fill in
|
||||
# further dependencies manually.
|
||||
# find_package(<dependency> REQUIRED)
|
||||
|
||||
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()
|
||||
|
||||
find_package(rclcpp REQUIRED)
|
||||
find_package(example_interfaces REQUIRED)
|
||||
find_package(std_msgs REQUIRED)
|
||||
|
||||
add_executable(learn_executor src/learn_executor.cpp)
|
||||
ament_target_dependencies(learn_executor rclcpp example_interfaces std_msgs)
|
||||
|
||||
install(TARGETS learn_executor
|
||||
DESTINATION lib/${PROJECT_NAME})
|
||||
|
||||
ament_package()
|
||||
18
chapt10/chapt10_ws/src/learn_executor_cpp/package.xml
Normal file
18
chapt10/chapt10_ws/src/learn_executor_cpp/package.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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>learn_executor_cpp</name>
|
||||
<version>0.0.0</version>
|
||||
<description>TODO: Package description</description>
|
||||
<maintainer email="87068644+fishros@users.noreply.github.com">fishros</maintainer>
|
||||
<license>TODO: License declaration</license>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "example_interfaces/srv/add_two_ints.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "std_msgs/msg/string.hpp"
|
||||
#include <sstream>
|
||||
|
||||
class LearnExecutorNode : public rclcpp::Node {
|
||||
public:
|
||||
LearnExecutorNode() : Node("learn_executor") {
|
||||
publisher_ =
|
||||
this->create_publisher<std_msgs::msg::String>("string_topic", 10);
|
||||
timer_ = this->create_wall_timer(
|
||||
std::chrono::seconds(1),
|
||||
std::bind(&LearnExecutorNode::timer_callback, this));
|
||||
service_callback_group_ = this->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive); // 互斥回调组
|
||||
service_ = this->create_service<example_interfaces::srv::AddTwoInts>(
|
||||
"add_two_ints",
|
||||
std::bind(&LearnExecutorNode::add_two_ints_callback, this,
|
||||
std::placeholders::_1, std::placeholders::_2),
|
||||
rmw_qos_profile_services_default, service_callback_group_);
|
||||
}
|
||||
|
||||
private:
|
||||
void timer_callback() {
|
||||
auto msg = std_msgs::msg::String();
|
||||
msg.data = "话题发布:" + thread_info();
|
||||
RCLCPP_INFO(this->get_logger(), msg.data.c_str());
|
||||
publisher_->publish(msg);
|
||||
}
|
||||
|
||||
std::string thread_info() {
|
||||
std::ostringstream thread_str;
|
||||
thread_str << "线程ID:" << std::this_thread::get_id();
|
||||
return thread_str.str();
|
||||
}
|
||||
|
||||
void add_two_ints_callback(
|
||||
const std::shared_ptr<example_interfaces::srv::AddTwoInts::Request>
|
||||
request,
|
||||
std::shared_ptr<example_interfaces::srv::AddTwoInts::Response> response) {
|
||||
RCLCPP_INFO(this->get_logger(), "服务开始处理:%s", thread_info().c_str());
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
response->sum = request->a + request->b;
|
||||
RCLCPP_INFO(this->get_logger(), "服务处理完成:%s", thread_info().c_str());
|
||||
}
|
||||
|
||||
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
|
||||
rclcpp::TimerBase::SharedPtr timer_;
|
||||
rclcpp::Service<example_interfaces::srv::AddTwoInts>::SharedPtr service_;
|
||||
rclcpp::CallbackGroup::SharedPtr service_callback_group_;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
rclcpp::init(argc, argv);
|
||||
auto node = std::make_shared<LearnExecutorNode>();
|
||||
// auto executor = rclcpp::executors::SingleThreadedExecutor();
|
||||
auto executor = rclcpp::executors::MultiThreadedExecutor();
|
||||
executor.add_node(node);
|
||||
executor.spin();
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import rclpy
|
||||
from rclpy.node import Node
|
||||
from rclpy.executors import MultiThreadedExecutor, SingleThreadedExecutor
|
||||
from rclpy.callback_groups import MutuallyExclusiveCallbackGroup, ReentrantCallbackGroup
|
||||
from std_msgs.msg import String
|
||||
from example_interfaces.srv import AddTwoInts
|
||||
import threading
|
||||
import time
|
||||
|
||||
|
||||
class LearnExecutorNode(Node):
|
||||
def __init__(self):
|
||||
super().__init__('learn_executor')
|
||||
self.publisher = self.create_publisher(String, 'string_topic', 10)
|
||||
self.timer = self.create_timer(1.0, self.timer_callback)
|
||||
my_callback_group = ReentrantCallbackGroup()
|
||||
self.service = self.create_service(
|
||||
AddTwoInts, 'add_two_ints', self.add_two_ints_callback,
|
||||
callback_group=my_callback_group)
|
||||
# self.service = self.create_service(
|
||||
# AddTwoInts, 'add_two_ints', self.add_two_ints_callback)
|
||||
|
||||
def timer_callback(self):
|
||||
msg = String()
|
||||
msg.data = f'话题发布,线程ID:{threading.get_ident()} 线程总数:{threading.active_count()}'
|
||||
self.get_logger().info(msg.data)
|
||||
self.publisher.publish(msg)
|
||||
|
||||
def add_two_ints_callback(self, request: AddTwoInts.Request, response: AddTwoInts.Response):
|
||||
self.get_logger().info(f'处理服务,线程ID:{threading.get_ident()}')
|
||||
time.sleep(10) # 模拟处理延时
|
||||
response.sum = request.a + request.b
|
||||
self.get_logger().info(f'处理完成,线程ID:{threading.get_ident()}')
|
||||
return response
|
||||
|
||||
|
||||
def main(args=None):
|
||||
rclpy.init(args=args)
|
||||
node = LearnExecutorNode()
|
||||
# executor = SingleThreadedExecutor()
|
||||
executor = MultiThreadedExecutor()
|
||||
executor.add_node(node)
|
||||
executor.spin()
|
||||
rclpy.shutdown()
|
||||
18
chapt10/chapt10_ws/src/learn_executor_py/package.xml
Normal file
18
chapt10/chapt10_ws/src/learn_executor_py/package.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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>learn_executor_py</name>
|
||||
<version>0.0.0</version>
|
||||
<description>TODO: Package description</description>
|
||||
<maintainer email="87068644+fishros@users.noreply.github.com">fishros</maintainer>
|
||||
<license>TODO: License declaration</license>
|
||||
|
||||
<test_depend>ament_copyright</test_depend>
|
||||
<test_depend>ament_flake8</test_depend>
|
||||
<test_depend>ament_pep257</test_depend>
|
||||
<test_depend>python3-pytest</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_python</build_type>
|
||||
</export>
|
||||
</package>
|
||||
4
chapt10/chapt10_ws/src/learn_executor_py/setup.cfg
Normal file
4
chapt10/chapt10_ws/src/learn_executor_py/setup.cfg
Normal file
@@ -0,0 +1,4 @@
|
||||
[develop]
|
||||
script_dir=$base/lib/learn_executor_py
|
||||
[install]
|
||||
install_scripts=$base/lib/learn_executor_py
|
||||
26
chapt10/chapt10_ws/src/learn_executor_py/setup.py
Normal file
26
chapt10/chapt10_ws/src/learn_executor_py/setup.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
package_name = 'learn_executor_py'
|
||||
|
||||
setup(
|
||||
name=package_name,
|
||||
version='0.0.0',
|
||||
packages=find_packages(exclude=['test']),
|
||||
data_files=[
|
||||
('share/ament_index/resource_index/packages',
|
||||
['resource/' + package_name]),
|
||||
('share/' + package_name, ['package.xml']),
|
||||
],
|
||||
install_requires=['setuptools'],
|
||||
zip_safe=True,
|
||||
maintainer='fishros',
|
||||
maintainer_email='87068644+fishros@users.noreply.github.com',
|
||||
description='TODO: Package description',
|
||||
license='TODO: License declaration',
|
||||
tests_require=['pytest'],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'learn_executor=learn_executor_py.learn_executor:main'
|
||||
],
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
# Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ament_copyright.main import main
|
||||
import pytest
|
||||
|
||||
|
||||
# Remove the `skip` decorator once the source file(s) have a copyright header
|
||||
@pytest.mark.skip(reason='No copyright header has been placed in the generated source file.')
|
||||
@pytest.mark.copyright
|
||||
@pytest.mark.linter
|
||||
def test_copyright():
|
||||
rc = main(argv=['.', 'test'])
|
||||
assert rc == 0, 'Found errors'
|
||||
25
chapt10/chapt10_ws/src/learn_executor_py/test/test_flake8.py
Normal file
25
chapt10/chapt10_ws/src/learn_executor_py/test/test_flake8.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# Copyright 2017 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ament_flake8.main import main_with_errors
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.flake8
|
||||
@pytest.mark.linter
|
||||
def test_flake8():
|
||||
rc, errors = main_with_errors(argv=[])
|
||||
assert rc == 0, \
|
||||
'Found %d code style errors / warnings:\n' % len(errors) + \
|
||||
'\n'.join(errors)
|
||||
23
chapt10/chapt10_ws/src/learn_executor_py/test/test_pep257.py
Normal file
23
chapt10/chapt10_ws/src/learn_executor_py/test/test_pep257.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ament_pep257.main import main
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.linter
|
||||
@pytest.mark.pep257
|
||||
def test_pep257():
|
||||
rc = main(argv=['.', 'test'])
|
||||
assert rc == 0, 'Found code style errors / warnings'
|
||||
@@ -0,0 +1,33 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(learn_lifecyclenode_cpp)
|
||||
|
||||
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(rclcpp_lifecycle REQUIRED)
|
||||
|
||||
add_executable(learn_lifecyclenode src/learn_lifecyclenode.cpp)
|
||||
ament_target_dependencies(learn_lifecyclenode
|
||||
rclcpp rclcpp_lifecycle
|
||||
)
|
||||
|
||||
install(TARGETS learn_lifecyclenode
|
||||
DESTINATION lib/${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()
|
||||
21
chapt10/chapt10_ws/src/learn_lifecyclenode_cpp/package.xml
Normal file
21
chapt10/chapt10_ws/src/learn_lifecyclenode_cpp/package.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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>learn_lifecyclenode_cpp</name>
|
||||
<version>0.0.0</version>
|
||||
<description>TODO: Package description</description>
|
||||
<maintainer email="87068644+fishros@users.noreply.github.com">fishros</maintainer>
|
||||
<license>TODO: License declaration</license>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<depend>rclcpp</depend>
|
||||
<depend>rclcpp_lifecycle</depend>
|
||||
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "rclcpp_lifecycle/lifecycle_node.hpp"
|
||||
|
||||
using CallbackReturn =
|
||||
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn;
|
||||
|
||||
class LearnLifeCycleNode : public rclcpp_lifecycle::LifecycleNode {
|
||||
public:
|
||||
LearnLifeCycleNode()
|
||||
: rclcpp_lifecycle::LifecycleNode("lifecyclenode") {
|
||||
timer_period_ = 1.0;
|
||||
timer_ = nullptr;
|
||||
RCLCPP_INFO(get_logger(), "%s: 已创建", get_name());
|
||||
}
|
||||
|
||||
CallbackReturn on_configure(const rclcpp_lifecycle::State &state) override {
|
||||
(void)state;
|
||||
timer_period_ = 1.0;
|
||||
RCLCPP_INFO(get_logger(), "on_configure():配置周期 timer_period");
|
||||
return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::
|
||||
CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
CallbackReturn on_activate(const rclcpp_lifecycle::State &state) override {
|
||||
(void)state;
|
||||
timer_ = create_wall_timer(
|
||||
std::chrono::seconds(static_cast<int>(timer_period_)),
|
||||
[this]() { RCLCPP_INFO(get_logger(), "定时器打印进行中..."); });
|
||||
RCLCPP_INFO(get_logger(), "on_activate():处理激活指令,创建定时器");
|
||||
return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::
|
||||
CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
CallbackReturn on_deactivate(const rclcpp_lifecycle::State &state) override {
|
||||
(void)state;
|
||||
timer_.reset();
|
||||
RCLCPP_INFO(get_logger(), "on_deactivate():处理失活指令,停止定时器");
|
||||
return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::
|
||||
CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
CallbackReturn on_shutdown(const rclcpp_lifecycle::State &state) override {
|
||||
(void)state;
|
||||
timer_.reset();
|
||||
RCLCPP_INFO(get_logger(), "on_shutdown():处理关闭指令");
|
||||
return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::
|
||||
CallbackReturn::SUCCESS;
|
||||
}
|
||||
|
||||
private:
|
||||
rclcpp::TimerBase::SharedPtr timer_;
|
||||
double timer_period_;
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
rclcpp::init(argc, argv);
|
||||
auto node = std::make_shared<LearnLifeCycleNode>();
|
||||
rclcpp::spin(node->get_node_base_interface());
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import rclpy
|
||||
from rclpy.lifecycle import LifecycleNode, TransitionCallbackReturn
|
||||
|
||||
class LearnLifeCycleNode(LifecycleNode):
|
||||
def __init__(self):
|
||||
super().__init__('lifecyclenode')
|
||||
self.timer_period = 0
|
||||
self.timer_ = None
|
||||
self.get_logger().info(f'{self.get_name()}:已创建')
|
||||
|
||||
def timer_callback(self):
|
||||
self.get_logger().info('定时器打印进行中...')
|
||||
|
||||
def on_configure(self, state):
|
||||
self.timer_period = 1.0 # 设置定时器周期
|
||||
self.get_logger().info('on_configure():配置周期 timer_period')
|
||||
return TransitionCallbackReturn.SUCCESS
|
||||
|
||||
def on_activate(self, state):
|
||||
self.timer_ = self.create_timer(self.timer_period, self.timer_callback)
|
||||
self.get_logger().info('on_activate():处理激活指令,创建定时器')
|
||||
return TransitionCallbackReturn.SUCCESS
|
||||
|
||||
def on_deactivate(self, state):
|
||||
self.destroy_timer(self.timer_) # 销毁定时器
|
||||
self.get_logger().info('on_deactivate():处理失活指令停止定时器')
|
||||
return TransitionCallbackReturn.SUCCESS
|
||||
|
||||
def on_cleanup(self, state):
|
||||
self.timer_ = None
|
||||
self.timer_period = 0
|
||||
self.get_logger().info('on_cleanup():处理清理指令')
|
||||
return TransitionCallbackReturn.SUCCESS
|
||||
|
||||
def on_shutdown(self, state):
|
||||
# 定时器未销毁则销毁
|
||||
if self.timer_: self.destroy_timer(self.timer_)
|
||||
self.get_logger().info('on_shutdown():处理关闭指令')
|
||||
return TransitionCallbackReturn.SUCCESS
|
||||
|
||||
def on_error(self, state):
|
||||
# 直接调用父类处理
|
||||
return super().on_error(state)
|
||||
|
||||
def main():
|
||||
rclpy.init()
|
||||
node = LearnLifeCycleNode()
|
||||
rclpy.spin(node)
|
||||
rclpy.shutdown()
|
||||
18
chapt10/chapt10_ws/src/learn_lifecyclenode_py/package.xml
Normal file
18
chapt10/chapt10_ws/src/learn_lifecyclenode_py/package.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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>learn_lifecyclenode_py</name>
|
||||
<version>0.0.0</version>
|
||||
<description>TODO: Package description</description>
|
||||
<maintainer email="87068644+fishros@users.noreply.github.com">fishros</maintainer>
|
||||
<license>TODO: License declaration</license>
|
||||
|
||||
<test_depend>ament_copyright</test_depend>
|
||||
<test_depend>ament_flake8</test_depend>
|
||||
<test_depend>ament_pep257</test_depend>
|
||||
<test_depend>python3-pytest</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_python</build_type>
|
||||
</export>
|
||||
</package>
|
||||
4
chapt10/chapt10_ws/src/learn_lifecyclenode_py/setup.cfg
Normal file
4
chapt10/chapt10_ws/src/learn_lifecyclenode_py/setup.cfg
Normal file
@@ -0,0 +1,4 @@
|
||||
[develop]
|
||||
script_dir=$base/lib/learn_lifecyclenode_py
|
||||
[install]
|
||||
install_scripts=$base/lib/learn_lifecyclenode_py
|
||||
26
chapt10/chapt10_ws/src/learn_lifecyclenode_py/setup.py
Normal file
26
chapt10/chapt10_ws/src/learn_lifecyclenode_py/setup.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
package_name = 'learn_lifecyclenode_py'
|
||||
|
||||
setup(
|
||||
name=package_name,
|
||||
version='0.0.0',
|
||||
packages=find_packages(exclude=['test']),
|
||||
data_files=[
|
||||
('share/ament_index/resource_index/packages',
|
||||
['resource/' + package_name]),
|
||||
('share/' + package_name, ['package.xml']),
|
||||
],
|
||||
install_requires=['setuptools'],
|
||||
zip_safe=True,
|
||||
maintainer='fishros',
|
||||
maintainer_email='87068644+fishros@users.noreply.github.com',
|
||||
description='TODO: Package description',
|
||||
license='TODO: License declaration',
|
||||
tests_require=['pytest'],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'learn_lifecyclenode=learn_lifecyclenode_py.learn_lifecyclenode:main'
|
||||
],
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
# Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ament_copyright.main import main
|
||||
import pytest
|
||||
|
||||
|
||||
# Remove the `skip` decorator once the source file(s) have a copyright header
|
||||
@pytest.mark.skip(reason='No copyright header has been placed in the generated source file.')
|
||||
@pytest.mark.copyright
|
||||
@pytest.mark.linter
|
||||
def test_copyright():
|
||||
rc = main(argv=['.', 'test'])
|
||||
assert rc == 0, 'Found errors'
|
||||
@@ -0,0 +1,25 @@
|
||||
# Copyright 2017 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ament_flake8.main import main_with_errors
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.flake8
|
||||
@pytest.mark.linter
|
||||
def test_flake8():
|
||||
rc, errors = main_with_errors(argv=[])
|
||||
assert rc == 0, \
|
||||
'Found %d code style errors / warnings:\n' % len(errors) + \
|
||||
'\n'.join(errors)
|
||||
@@ -0,0 +1,23 @@
|
||||
# Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ament_pep257.main import main
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.linter
|
||||
@pytest.mark.pep257
|
||||
def test_pep257():
|
||||
rc = main(argv=['.', 'test'])
|
||||
assert rc == 0, 'Found code style errors / warnings'
|
||||
@@ -0,0 +1,35 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(learn_message_filter_cpp)
|
||||
|
||||
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(message_filters REQUIRED)
|
||||
find_package(nav_msgs REQUIRED)
|
||||
find_package(sensor_msgs REQUIRED)
|
||||
|
||||
add_executable(timesync_test src/timesync_test.cpp)
|
||||
ament_target_dependencies(timesync_test
|
||||
rclcpp message_filters nav_msgs sensor_msgs
|
||||
)
|
||||
|
||||
install(TARGETS timesync_test
|
||||
DESTINATION lib/${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()
|
||||
23
chapt10/chapt10_ws/src/learn_message_filter_cpp/package.xml
Normal file
23
chapt10/chapt10_ws/src/learn_message_filter_cpp/package.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?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>learn_message_filter_cpp</name>
|
||||
<version>0.0.0</version>
|
||||
<description>TODO: Package description</description>
|
||||
<maintainer email="87068644+fishros@users.noreply.github.com">fishros</maintainer>
|
||||
<license>TODO: License declaration</license>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<depend>rclcpp</depend>
|
||||
<depend>message_filters</depend>
|
||||
<depend>nav_msgs</depend>
|
||||
<depend>sensor_msgs</depend>
|
||||
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
||||
@@ -0,0 +1,70 @@
|
||||
#include "message_filters/subscriber.h"
|
||||
#include "message_filters/sync_policies/approximate_time.h"
|
||||
#include "message_filters/sync_policies/exact_time.h"
|
||||
#include "message_filters/sync_policies/latest_time.h"
|
||||
#include "message_filters/time_synchronizer.h"
|
||||
#include "nav_msgs/msg/odometry.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "sensor_msgs/msg/imu.hpp"
|
||||
|
||||
using Imu = sensor_msgs::msg::Imu;
|
||||
using Odometry = nav_msgs::msg::Odometry;
|
||||
using namespace message_filters;
|
||||
|
||||
// 同步策略:严格时间对齐策略
|
||||
// using MySyncPolicy = sync_policies::ExactTime<Imu, Odometry>;
|
||||
// 同步策略:大约时间对齐策略
|
||||
// using MySyncPolicy = sync_policies::ApproximateTime<Imu, Odometry>;
|
||||
// 同步策略:最新时间对齐策略
|
||||
using MySyncPolicy = sync_policies::LatestTime<Imu, Odometry>;
|
||||
|
||||
|
||||
class TimeSyncTestNode : public rclcpp::Node {
|
||||
public:
|
||||
TimeSyncTestNode() : Node("sync_node") {
|
||||
// 1.订阅 imu 话题并注册回调并打印时间戳
|
||||
imu_sub_ = std::make_shared<Subscriber<Imu>>(this, "imu");
|
||||
imu_sub_->registerCallback<Imu::SharedPtr>(
|
||||
[&](const Imu::SharedPtr &imu_msg) {
|
||||
RCLCPP_INFO(get_logger(), "imu(%u,%u)", imu_msg->header.stamp.sec,
|
||||
imu_msg->header.stamp.nanosec);
|
||||
});
|
||||
// 2.订阅 odom 话题并注册回调函数打印时间戳
|
||||
odom_sub_ = std::make_shared<Subscriber<Odometry>>(this, "odom");
|
||||
odom_sub_->registerCallback<Odometry::SharedPtr>(
|
||||
[&](const Odometry::SharedPtr &odom_msg) {
|
||||
RCLCPP_INFO(get_logger(), "odom(%u,%u)", odom_msg->header.stamp.sec,
|
||||
odom_msg->header.stamp.nanosec);
|
||||
});
|
||||
// 3.创建对应策略的同步器同步两个话题,并注册回调函数打印数据
|
||||
// synchronizer_ = std::make_shared<Synchronizer<MySyncPolicy>>(
|
||||
// MySyncPolicy(10), *imu_sub_, *odom_sub_);
|
||||
|
||||
// 3.创建对应策略的同步器同步两个话题,并注册回调函数打印数据
|
||||
synchronizer_ = std::make_shared<Synchronizer<MySyncPolicy>>(
|
||||
MySyncPolicy(), *imu_sub_, *odom_sub_);
|
||||
synchronizer_->registerCallback(
|
||||
std::bind(&TimeSyncTestNode::result_callback, this,
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
private:
|
||||
void result_callback(const Imu::ConstSharedPtr imu_msg,
|
||||
const Odometry::ConstSharedPtr odom_msg) {
|
||||
RCLCPP_INFO(get_logger(), "imu(%u,%u),odom(%u,%u))",
|
||||
imu_msg->header.stamp.sec, imu_msg->header.stamp.nanosec,
|
||||
odom_msg->header.stamp.sec, odom_msg->header.stamp.nanosec);
|
||||
}
|
||||
|
||||
std::shared_ptr<Subscriber<Imu>> imu_sub_;
|
||||
std::shared_ptr<Subscriber<Odometry>> odom_sub_;
|
||||
std::shared_ptr<Synchronizer<MySyncPolicy>> synchronizer_;
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
rclcpp::init(argc, argv);
|
||||
auto node = std::make_shared<TimeSyncTestNode>();
|
||||
rclcpp::spin(node);
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import rclpy
|
||||
from rclpy.node import Node
|
||||
from sensor_msgs.msg import Imu
|
||||
from nav_msgs.msg import Odometry
|
||||
from message_filters import Subscriber, ApproximateTimeSynchronizer
|
||||
|
||||
class TimeSyncTestNode(Node):
|
||||
def __init__(self):
|
||||
super().__init__('sync_node')
|
||||
# 1. 订阅 imu 话题并注册回调并打印时间戳
|
||||
self.imu_sub = Subscriber(self, Imu, 'imu')
|
||||
self.imu_sub.registerCallback(self.imu_callback)
|
||||
# 2. 订阅 odom 话题并注册回调函数打印时间戳
|
||||
self.odom_sub = Subscriber(self, Odometry, 'odom')
|
||||
self.odom_sub.registerCallback(self.odom_callback)
|
||||
# 3. 创建对应策略的同步器同步两个话题,并注册回调函数打印数据
|
||||
self.synchronizer = ApproximateTimeSynchronizer(
|
||||
[self.imu_sub, self.odom_sub], 10,
|
||||
slop=0.01, # slop 表示时间窗口单位为秒
|
||||
)
|
||||
self.synchronizer.registerCallback(self.result_callback)
|
||||
|
||||
def imu_callback(self, imu_msg):
|
||||
self.get_logger().info(
|
||||
f'imu({imu_msg.header.stamp.sec},{imu_msg.header.stamp.nanosec})')
|
||||
|
||||
def odom_callback(self, odom_msg):
|
||||
self.get_logger().info(
|
||||
f'odom({odom_msg.header.stamp.sec},{odom_msg.header.stamp.nanosec})')
|
||||
|
||||
def result_callback(self, imu_msg, odom_msg):
|
||||
self.get_logger().info(
|
||||
f'imu({imu_msg.header.stamp.sec},{imu_msg.header.stamp.nanosec}),odom({odom_msg.header.stamp.sec},{odom_msg.header.stamp.nanosec})')
|
||||
|
||||
|
||||
def main(args=None):
|
||||
rclpy.init(args=args)
|
||||
node = TimeSyncTestNode()
|
||||
rclpy.spin(node)
|
||||
rclpy.shutdown()
|
||||
18
chapt10/chapt10_ws/src/learn_message_filter_py/package.xml
Normal file
18
chapt10/chapt10_ws/src/learn_message_filter_py/package.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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>learn_message_filter_py</name>
|
||||
<version>0.0.0</version>
|
||||
<description>TODO: Package description</description>
|
||||
<maintainer email="87068644+fishros@users.noreply.github.com">fishros</maintainer>
|
||||
<license>TODO: License declaration</license>
|
||||
|
||||
<test_depend>ament_copyright</test_depend>
|
||||
<test_depend>ament_flake8</test_depend>
|
||||
<test_depend>ament_pep257</test_depend>
|
||||
<test_depend>python3-pytest</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_python</build_type>
|
||||
</export>
|
||||
</package>
|
||||
4
chapt10/chapt10_ws/src/learn_message_filter_py/setup.cfg
Normal file
4
chapt10/chapt10_ws/src/learn_message_filter_py/setup.cfg
Normal file
@@ -0,0 +1,4 @@
|
||||
[develop]
|
||||
script_dir=$base/lib/learn_message_filter_py
|
||||
[install]
|
||||
install_scripts=$base/lib/learn_message_filter_py
|
||||
26
chapt10/chapt10_ws/src/learn_message_filter_py/setup.py
Normal file
26
chapt10/chapt10_ws/src/learn_message_filter_py/setup.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
package_name = 'learn_message_filter_py'
|
||||
|
||||
setup(
|
||||
name=package_name,
|
||||
version='0.0.0',
|
||||
packages=find_packages(exclude=['test']),
|
||||
data_files=[
|
||||
('share/ament_index/resource_index/packages',
|
||||
['resource/' + package_name]),
|
||||
('share/' + package_name, ['package.xml']),
|
||||
],
|
||||
install_requires=['setuptools'],
|
||||
zip_safe=True,
|
||||
maintainer='fishros',
|
||||
maintainer_email='87068644+fishros@users.noreply.github.com',
|
||||
description='TODO: Package description',
|
||||
license='TODO: License declaration',
|
||||
tests_require=['pytest'],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'timesync_test=learn_message_filter_py.timesync_test:main'
|
||||
],
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
# Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ament_copyright.main import main
|
||||
import pytest
|
||||
|
||||
|
||||
# Remove the `skip` decorator once the source file(s) have a copyright header
|
||||
@pytest.mark.skip(reason='No copyright header has been placed in the generated source file.')
|
||||
@pytest.mark.copyright
|
||||
@pytest.mark.linter
|
||||
def test_copyright():
|
||||
rc = main(argv=['.', 'test'])
|
||||
assert rc == 0, 'Found errors'
|
||||
@@ -0,0 +1,25 @@
|
||||
# Copyright 2017 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ament_flake8.main import main_with_errors
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.flake8
|
||||
@pytest.mark.linter
|
||||
def test_flake8():
|
||||
rc, errors = main_with_errors(argv=[])
|
||||
assert rc == 0, \
|
||||
'Found %d code style errors / warnings:\n' % len(errors) + \
|
||||
'\n'.join(errors)
|
||||
@@ -0,0 +1,23 @@
|
||||
# Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ament_pep257.main import main
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.linter
|
||||
@pytest.mark.pep257
|
||||
def test_pep257():
|
||||
rc = main(argv=['.', 'test'])
|
||||
assert rc == 0, 'Found code style errors / warnings'
|
||||
37
chapt10/chapt10_ws/src/learn_qos_cpp/CMakeLists.txt
Normal file
37
chapt10/chapt10_ws/src/learn_qos_cpp/CMakeLists.txt
Normal file
@@ -0,0 +1,37 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(learn_qos_cpp)
|
||||
|
||||
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)
|
||||
# uncomment the following section in order to fill in
|
||||
# further dependencies manually.
|
||||
# find_package(<dependency> REQUIRED)
|
||||
|
||||
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()
|
||||
|
||||
find_package(rclcpp REQUIRED)
|
||||
find_package(nav_msgs REQUIRED)
|
||||
|
||||
add_executable(reliability_test src/reliability_test.cpp)
|
||||
ament_target_dependencies(reliability_test
|
||||
rclcpp nav_msgs
|
||||
)
|
||||
|
||||
install(TARGETS reliability_test
|
||||
DESTINATION lib/${PROJECT_NAME})
|
||||
|
||||
ament_package()
|
||||
18
chapt10/chapt10_ws/src/learn_qos_cpp/package.xml
Normal file
18
chapt10/chapt10_ws/src/learn_qos_cpp/package.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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>learn_qos_cpp</name>
|
||||
<version>0.0.0</version>
|
||||
<description>TODO: Package description</description>
|
||||
<maintainer email="87068644+fishros@users.noreply.github.com">fishros</maintainer>
|
||||
<license>TODO: License declaration</license>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
||||
@@ -0,0 +1,56 @@
|
||||
#include <nav_msgs/msg/odometry.hpp>
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
|
||||
class OdomPublisherSubscriber : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
OdomPublisherSubscriber() : Node("odom_publisher_subscriber")
|
||||
{
|
||||
rclcpp::QoS qos_profile(10); // 队列深度为10
|
||||
qos_profile.reliability(RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT); // 可靠性策略
|
||||
qos_profile.durability(RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL); // 持久性策略
|
||||
qos_profile.history(RMW_QOS_POLICY_HISTORY_KEEP_LAST); // 历史记录策略
|
||||
qos_profile.deadline(rclcpp::Duration(1, 0)); // 截止时间为1秒
|
||||
|
||||
odom_publisher_ = this->create_publisher<nav_msgs::msg::Odometry>(
|
||||
"odom", qos_profile);
|
||||
|
||||
// // 创建发布者并设置QoS为sensor
|
||||
// odom_publisher_ = this->create_publisher<nav_msgs::msg::Odometry>(
|
||||
// "odom", rclcpp::SensorDataQoS());
|
||||
|
||||
// 创建订阅者(默认QoS配置)队列深度设置为 5
|
||||
// odom_subscription_ = this->create_subscription<nav_msgs::msg::Odometry>(
|
||||
// "odom", 5,
|
||||
// [this](const nav_msgs::msg::Odometry::SharedPtr msg) {
|
||||
// (void)msg;
|
||||
// RCLCPP_INFO(this->get_logger(), "收到里程计消息");
|
||||
// });
|
||||
|
||||
odom_subscription_ = this->create_subscription<nav_msgs::msg::Odometry>(
|
||||
"odom", rclcpp::SensorDataQoS(),
|
||||
[this](const nav_msgs::msg::Odometry::SharedPtr msg)
|
||||
{
|
||||
(void)msg;
|
||||
RCLCPP_INFO(this->get_logger(), "收到里程计消息");
|
||||
});
|
||||
|
||||
// 创建一个1秒的定时器,并指定回调函数
|
||||
timer_ = this->create_wall_timer(std::chrono::seconds(1), [this]()
|
||||
{ odom_publisher_->publish(nav_msgs::msg::Odometry()); });
|
||||
}
|
||||
|
||||
private:
|
||||
rclcpp::Publisher<nav_msgs::msg::Odometry>::SharedPtr odom_publisher_;
|
||||
rclcpp::Subscription<nav_msgs::msg::Odometry>::SharedPtr odom_subscription_;
|
||||
rclcpp::TimerBase::SharedPtr timer_;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
auto odom_node = std::make_shared<OdomPublisherSubscriber>();
|
||||
rclcpp::spin(odom_node);
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import rclpy
|
||||
from rclpy.node import Node
|
||||
from nav_msgs.msg import Odometry
|
||||
from rclpy import qos
|
||||
from rclpy.duration import Duration
|
||||
|
||||
|
||||
|
||||
class OdomPublisherSubscriber(Node):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__('odom_publisher_subscriber')
|
||||
# 创建发布者并设置QoS为sensor
|
||||
qos_profile = qos.QoSProfile(
|
||||
depth=10, # 队列深度
|
||||
reliability=qos.ReliabilityPolicy.BEST_EFFORT, # 可靠性
|
||||
durability=qos.DurabilityPolicy.TRANSIENT_LOCAL, # 持久性
|
||||
history=qos.HistoryPolicy.KEEP_LAST, # 历史记录策略
|
||||
deadline=Duration(seconds=1.0, nanoseconds=0),
|
||||
)
|
||||
# 在订阅和发布中使用
|
||||
self.odom_publisher = self.create_publisher(Odometry, 'odom', qos_profile)
|
||||
# 创建订阅者(默认QoS配置)队列深度设置为 5
|
||||
self.odom_subscriber = self.create_subscription(
|
||||
Odometry, 'odom', self.odom_callback,qos.qos_profile_sensor_data)
|
||||
# 创建一个1秒的定时器,并指定回调函数
|
||||
self.timer = self.create_timer(1.0, self.timer_callback)
|
||||
|
||||
def odom_callback(self, msg):
|
||||
self.get_logger().info('收到里程计消息')
|
||||
|
||||
def timer_callback(self):
|
||||
odom_msg = Odometry() # 创建一个Odometry消息
|
||||
self.odom_publisher.publish(odom_msg) # 发布消息
|
||||
|
||||
|
||||
def main(args=None):
|
||||
rclpy.init(args=args)
|
||||
odom_node = OdomPublisherSubscriber()
|
||||
rclpy.spin(odom_node)
|
||||
rclpy.shutdown()
|
||||
18
chapt10/chapt10_ws/src/learn_qos_py/package.xml
Normal file
18
chapt10/chapt10_ws/src/learn_qos_py/package.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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>learn_qos_py</name>
|
||||
<version>0.0.0</version>
|
||||
<description>TODO: Package description</description>
|
||||
<maintainer email="87068644+fishros@users.noreply.github.com">fishros</maintainer>
|
||||
<license>TODO: License declaration</license>
|
||||
|
||||
<test_depend>ament_copyright</test_depend>
|
||||
<test_depend>ament_flake8</test_depend>
|
||||
<test_depend>ament_pep257</test_depend>
|
||||
<test_depend>python3-pytest</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_python</build_type>
|
||||
</export>
|
||||
</package>
|
||||
4
chapt10/chapt10_ws/src/learn_qos_py/setup.cfg
Normal file
4
chapt10/chapt10_ws/src/learn_qos_py/setup.cfg
Normal file
@@ -0,0 +1,4 @@
|
||||
[develop]
|
||||
script_dir=$base/lib/learn_qos_py
|
||||
[install]
|
||||
install_scripts=$base/lib/learn_qos_py
|
||||
26
chapt10/chapt10_ws/src/learn_qos_py/setup.py
Normal file
26
chapt10/chapt10_ws/src/learn_qos_py/setup.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
package_name = 'learn_qos_py'
|
||||
|
||||
setup(
|
||||
name=package_name,
|
||||
version='0.0.0',
|
||||
packages=find_packages(exclude=['test']),
|
||||
data_files=[
|
||||
('share/ament_index/resource_index/packages',
|
||||
['resource/' + package_name]),
|
||||
('share/' + package_name, ['package.xml']),
|
||||
],
|
||||
install_requires=['setuptools'],
|
||||
zip_safe=True,
|
||||
maintainer='fishros',
|
||||
maintainer_email='87068644+fishros@users.noreply.github.com',
|
||||
description='TODO: Package description',
|
||||
license='TODO: License declaration',
|
||||
tests_require=['pytest'],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'reliability_test=learn_qos_py.reliability_test:main'
|
||||
],
|
||||
},
|
||||
)
|
||||
25
chapt10/chapt10_ws/src/learn_qos_py/test/test_copyright.py
Normal file
25
chapt10/chapt10_ws/src/learn_qos_py/test/test_copyright.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ament_copyright.main import main
|
||||
import pytest
|
||||
|
||||
|
||||
# Remove the `skip` decorator once the source file(s) have a copyright header
|
||||
@pytest.mark.skip(reason='No copyright header has been placed in the generated source file.')
|
||||
@pytest.mark.copyright
|
||||
@pytest.mark.linter
|
||||
def test_copyright():
|
||||
rc = main(argv=['.', 'test'])
|
||||
assert rc == 0, 'Found errors'
|
||||
25
chapt10/chapt10_ws/src/learn_qos_py/test/test_flake8.py
Normal file
25
chapt10/chapt10_ws/src/learn_qos_py/test/test_flake8.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# Copyright 2017 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ament_flake8.main import main_with_errors
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.flake8
|
||||
@pytest.mark.linter
|
||||
def test_flake8():
|
||||
rc, errors = main_with_errors(argv=[])
|
||||
assert rc == 0, \
|
||||
'Found %d code style errors / warnings:\n' % len(errors) + \
|
||||
'\n'.join(errors)
|
||||
23
chapt10/chapt10_ws/src/learn_qos_py/test/test_pep257.py
Normal file
23
chapt10/chapt10_ws/src/learn_qos_py/test/test_pep257.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ament_pep257.main import main
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.linter
|
||||
@pytest.mark.pep257
|
||||
def test_pep257():
|
||||
rc = main(argv=['.', 'test'])
|
||||
assert rc == 0, 'Found code style errors / warnings'
|
||||
23
chapt10/chapt10_ws/topic_sub_limit.xml
Normal file
23
chapt10/chapt10_ws/topic_sub_limit.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<profiles xmlns="http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles">
|
||||
|
||||
<!-- 默认发布者配置 -->
|
||||
<publisher profile_name="default_publisher" is_default_profile="true">
|
||||
<historyMemoryPolicy>DYNAMIC</historyMemoryPolicy>
|
||||
</publisher>
|
||||
|
||||
<!-- 默认订阅者配置 -->
|
||||
<subscriber profile_name="default_subscriber" is_default_profile="true">
|
||||
<historyMemoryPolicy>DYNAMIC</historyMemoryPolicy>
|
||||
</subscriber>
|
||||
|
||||
<!-- 话题 chatter 的发布者配置 -->
|
||||
<publisher profile_name="/chatter">
|
||||
<historyMemoryPolicy>DYNAMIC</historyMemoryPolicy>
|
||||
<matchedSubscribersAllocation>
|
||||
<initial>0</initial>
|
||||
<maximum>1</maximum>
|
||||
<increment>1</increment>
|
||||
</matchedSubscribersAllocation>
|
||||
</publisher>
|
||||
</profiles>
|
||||
32
chapt10/rosbag2_message_filter/metadata.yaml
Normal file
32
chapt10/rosbag2_message_filter/metadata.yaml
Normal file
@@ -0,0 +1,32 @@
|
||||
rosbag2_bagfile_information:
|
||||
version: 5
|
||||
storage_identifier: sqlite3
|
||||
duration:
|
||||
nanoseconds: 221085265896
|
||||
starting_time:
|
||||
nanoseconds_since_epoch: 1697985428618130944
|
||||
message_count: 33156
|
||||
topics_with_message_count:
|
||||
- topic_metadata:
|
||||
name: /imu
|
||||
type: sensor_msgs/msg/Imu
|
||||
serialization_format: cdr
|
||||
offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false"
|
||||
message_count: 22104
|
||||
- topic_metadata:
|
||||
name: /odom
|
||||
type: nav_msgs/msg/Odometry
|
||||
serialization_format: cdr
|
||||
offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 1\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false"
|
||||
message_count: 11052
|
||||
compression_format: ""
|
||||
compression_mode: ""
|
||||
relative_file_paths:
|
||||
- rosbag2_message_filter.db3
|
||||
files:
|
||||
- path: rosbag2_message_filter.db3
|
||||
starting_time:
|
||||
nanoseconds_since_epoch: 1697985428618130944
|
||||
duration:
|
||||
nanoseconds: 221085265896
|
||||
message_count: 33156
|
||||
BIN
chapt10/rosbag2_message_filter/rosbag2_message_filter.db3
Normal file
BIN
chapt10/rosbag2_message_filter/rosbag2_message_filter.db3
Normal file
Binary file not shown.
83
chapt7/chapt7_ws/README.md
Normal file
83
chapt7/chapt7_ws/README.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# 基于 ROS 2 和 Navigation 2 自动巡检机器人
|
||||
|
||||
## 1.项目介绍
|
||||
|
||||
本项目基于 ROS 2 和 Navigation 2 设计了一个自动巡检机器人仿真功能。
|
||||
|
||||
该巡检机器人要能够在不同的目标点之间进行循环移动,每到达一个目标点后首先通过语音播放到达的目标点信息,接着通过摄像头采集一张实时的图像并保存到本地。
|
||||
|
||||
各功能包功能如下:
|
||||
- fishbot_description 机器人描述文件,包含仿真相关配置
|
||||
- fishbot_navigation2 机器人导航配置文件
|
||||
- fishbot_application 机器人导航应用 Python 代码
|
||||
- fishbot_application_cpp 机器人导航应用 C++ 代码
|
||||
- autopatrol_interfaces 自动巡检相关接口
|
||||
- autopatrol_robot 自动巡检实现功能包
|
||||
|
||||
## 2.使用方法
|
||||
|
||||
本项目开发平台信息如下:
|
||||
|
||||
- 系统版本: Ubunt22.04
|
||||
- ROS 版本:ROS 2 Humble
|
||||
|
||||
### 2.1安装
|
||||
|
||||
本项目建图采用 slam-toolbox,导航采用 Navigation 2 ,仿真采用 Gazebo,运动控制采用 ros2-control 实现,构建之前请先安装依赖,指令如下:
|
||||
|
||||
1. 安装 SLAM 和 Navigation 2
|
||||
|
||||
```
|
||||
sudo apt install ros-$ROS_DISTRO-nav2-bringup ros-$ROS_DISTRO-slam-toolbox
|
||||
```
|
||||
|
||||
2. 安装仿真相关功能包
|
||||
|
||||
```
|
||||
sudo apt install ros-$ROS_DISTRO-robot-state-publisher ros-$ROS_DISTRO-joint-state-publisher ros-$ROS_DISTRO-gazebo-ros-pkgs ros-$ROS_DISTRO-ros2-controllers ros-$ROS_DISTRO-xacro
|
||||
```
|
||||
|
||||
3. 安装语音合成和图像相关功能包
|
||||
|
||||
```
|
||||
sudo apt install python3-pip -y
|
||||
sudo apt install espeak-ng -y
|
||||
sudo pip3 install espeakng
|
||||
sudo apt install ros-$ROS_DISTRO-tf-transformations
|
||||
sudo pip3 install transforms3d
|
||||
```
|
||||
|
||||
### 2.2运行
|
||||
|
||||
安装完成依赖后,可以使用 colcon 工具进行构建和运行。
|
||||
|
||||
构建功能包
|
||||
|
||||
```
|
||||
colcon build
|
||||
```
|
||||
|
||||
运行仿真
|
||||
|
||||
```
|
||||
source install/setup.bash
|
||||
ros2 launch fishbot_description gazebo.launch.py
|
||||
```
|
||||
|
||||
运行导航
|
||||
|
||||
```
|
||||
source install/setup.bash
|
||||
ros2 launch fishbot_navigation2 navigation2.launch.py
|
||||
```
|
||||
|
||||
运行自动巡检
|
||||
|
||||
```
|
||||
source install/setup.bash
|
||||
ros2 launch autopatrol_robot autopatrol.launch.py
|
||||
```
|
||||
|
||||
## 3.作者
|
||||
|
||||
- [fishros](https://github.com/fishros)
|
||||
28
chapt7/chapt7_ws/src/autopatrol_interfaces/CMakeLists.txt
Normal file
28
chapt7/chapt7_ws/src/autopatrol_interfaces/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(autopatrol_interfaces)
|
||||
|
||||
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)
|
||||
# uncomment the following section in order to fill in
|
||||
# further dependencies manually.
|
||||
# find_package(<dependency> REQUIRED)
|
||||
find_package(rosidl_default_generators REQUIRED)
|
||||
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()
|
||||
rosidl_generate_interfaces(${PROJECT_NAME}
|
||||
"srv/SpeachText.srv"
|
||||
)
|
||||
ament_package()
|
||||
18
chapt7/chapt7_ws/src/autopatrol_interfaces/package.xml
Normal file
18
chapt7/chapt7_ws/src/autopatrol_interfaces/package.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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>autopatrol_interfaces</name>
|
||||
<version>0.0.0</version>
|
||||
<description>TODO: Package description</description>
|
||||
<maintainer email="87068644+fishros@users.noreply.github.com">fishros</maintainer>
|
||||
<license>TODO: License declaration</license>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
<member_of_group>rosidl_interface_packages</member_of_group>
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
||||
@@ -0,0 +1,3 @@
|
||||
string text # 合成文字
|
||||
---
|
||||
bool result # 合成结果
|
||||
@@ -0,0 +1,173 @@
|
||||
import rclpy
|
||||
from geometry_msgs.msg import PoseStamped, Pose
|
||||
from nav2_simple_commander.robot_navigator import BasicNavigator, TaskResult
|
||||
from tf2_ros import TransformListener, Buffer
|
||||
from tf_transformations import euler_from_quaternion, quaternion_from_euler
|
||||
from rclpy.duration import Duration
|
||||
# 添加服务接口
|
||||
from autopatrol_interfaces.srv import SpeachText
|
||||
from sensor_msgs.msg import Image
|
||||
from cv_bridge import CvBridge
|
||||
import cv2
|
||||
|
||||
class PatrolNode(BasicNavigator):
|
||||
def __init__(self, node_name='patrol_node'):
|
||||
super().__init__(node_name)
|
||||
# 导航相关定义
|
||||
self.declare_parameter('initial_point', [0.0, 0.0, 0.0])
|
||||
self.declare_parameter('target_points', [0.0, 0.0, 0.0, 1.0, 1.0, 1.57])
|
||||
self.initial_point_ = self.get_parameter('initial_point').value
|
||||
self.target_points_ = self.get_parameter('target_points').value
|
||||
# 实时位置获取 TF 相关定义
|
||||
self.buffer_ = Buffer()
|
||||
self.listener_ = TransformListener(self.buffer_, self)
|
||||
self.speach_client_ = self.create_client(SpeachText, 'speech_text')
|
||||
|
||||
# 订阅与保存图像相关定义
|
||||
self.declare_parameter('image_save_path', '')
|
||||
self.image_save_path = self.get_parameter('image_save_path').value
|
||||
self.bridge = CvBridge()
|
||||
self.latest_image = None
|
||||
self.subscription_image = self.create_subscription(
|
||||
Image, '/camera_sensor/image_raw', self.image_callback, 10)
|
||||
|
||||
def image_callback(self, msg):
|
||||
"""
|
||||
将最新的消息放到 latest_image 中
|
||||
"""
|
||||
self.latest_image = msg
|
||||
|
||||
def record_image(self):
|
||||
"""
|
||||
记录图像
|
||||
"""
|
||||
if self.latest_image is not None:
|
||||
pose = self.get_current_pose()
|
||||
cv_image = self.bridge.imgmsg_to_cv2(self.latest_image)
|
||||
cv2.imwrite(f'{self.image_save_path}image_{pose.translation.x:3.2f}_{pose.translation.y:3.2f}.png', cv_image)
|
||||
|
||||
|
||||
def speach_text(self, text):
|
||||
"""
|
||||
调用服务播放语音
|
||||
"""
|
||||
while not self.speach_client_.wait_for_service(timeout_sec=1.0):
|
||||
self.get_logger().info('语合成服务未上线,等待中。。。')
|
||||
|
||||
request = SpeachText.Request()
|
||||
request.text = text
|
||||
future = self.speach_client_.call_async(request)
|
||||
rclpy.spin_until_future_complete(self, future)
|
||||
if future.result() is not None:
|
||||
result = future.result().result
|
||||
if result:
|
||||
self.get_logger().info(f'语音合成成功:{text}')
|
||||
else:
|
||||
self.get_logger().warn(f'语音合成失败:{text}')
|
||||
else:
|
||||
self.get_logger().warn('语音合成服务请求失败')
|
||||
|
||||
def get_pose_by_xyyaw(self, x, y, yaw):
|
||||
"""
|
||||
通过 x,y,yaw 合成 PoseStamped
|
||||
"""
|
||||
pose = PoseStamped()
|
||||
pose.header.frame_id = 'map'
|
||||
pose.pose.position.x = x
|
||||
pose.pose.position.y = y
|
||||
rotation_quat = quaternion_from_euler(0, 0, yaw)
|
||||
pose.pose.orientation.x = rotation_quat[0]
|
||||
pose.pose.orientation.y = rotation_quat[1]
|
||||
pose.pose.orientation.z = rotation_quat[2]
|
||||
pose.pose.orientation.w = rotation_quat[3]
|
||||
return pose
|
||||
|
||||
def init_robot_pose(self):
|
||||
"""
|
||||
初始化机器人位姿
|
||||
"""
|
||||
# 从参数获取初始化点
|
||||
self.initial_point_ = self.get_parameter('initial_point').value
|
||||
# 合成位姿并进行初始化
|
||||
self.setInitialPose(self.get_pose_by_xyyaw(
|
||||
self.initial_point_[0], self.initial_point_[1], self.initial_point_[2]))
|
||||
# 等待直到导航激活
|
||||
self.waitUntilNav2Active()
|
||||
|
||||
def get_target_points(self):
|
||||
"""
|
||||
通过参数值获取目标点集合
|
||||
"""
|
||||
points = []
|
||||
self.target_points_ = self.get_parameter('target_points').value
|
||||
for index in range(int(len(self.target_points_)/3)):
|
||||
x = self.target_points_[index*3]
|
||||
y = self.target_points_[index*3+1]
|
||||
yaw = self.target_points_[index*3+2]
|
||||
points.append([x, y, yaw])
|
||||
self.get_logger().info(f'获取到目标点: {index}->({x},{y},{yaw})')
|
||||
return points
|
||||
|
||||
def nav_to_pose(self, target_pose):
|
||||
"""
|
||||
导航到指定位姿
|
||||
"""
|
||||
self.waitUntilNav2Active()
|
||||
result = self.goToPose(target_pose)
|
||||
while not self.isTaskComplete():
|
||||
feedback = self.getFeedback()
|
||||
if feedback:
|
||||
self.get_logger().info(f'预计: {Duration.from_msg(feedback.estimated_time_remaining).nanoseconds / 1e9} s 后到达')
|
||||
# 最终结果判断
|
||||
result = self.getResult()
|
||||
if result == TaskResult.SUCCEEDED:
|
||||
self.get_logger().info('导航结果:成功')
|
||||
elif result == TaskResult.CANCELED:
|
||||
self.get_logger().warn('导航结果:被取消')
|
||||
elif result == TaskResult.FAILED:
|
||||
self.get_logger().error('导航结果:失败')
|
||||
else:
|
||||
self.get_logger().error('导航结果:返回状态无效')
|
||||
|
||||
def get_current_pose(self):
|
||||
"""
|
||||
通过TF获取当前位姿
|
||||
"""
|
||||
while rclpy.ok():
|
||||
try:
|
||||
tf = self.buffer_.lookup_transform(
|
||||
'map', 'base_footprint', rclpy.time.Time(seconds=0), rclpy.time.Duration(seconds=1))
|
||||
transform = tf.transform
|
||||
rotation_euler = euler_from_quaternion([
|
||||
transform.rotation.x,
|
||||
transform.rotation.y,
|
||||
transform.rotation.z,
|
||||
transform.rotation.w
|
||||
])
|
||||
self.get_logger().info(
|
||||
f'平移:{transform.translation},旋转四元数:{transform.rotation}:旋转欧拉角:{rotation_euler}')
|
||||
return transform
|
||||
except Exception as e:
|
||||
self.get_logger().warn(f'不能够获取坐标变换,原因: {str(e)}')
|
||||
|
||||
def main():
|
||||
rclpy.init()
|
||||
patrol = PatrolNode()
|
||||
patrol.speach_text(text='正在初始化位置')
|
||||
patrol.init_robot_pose()
|
||||
patrol.speach_text(text='位置初始化完成')
|
||||
|
||||
while rclpy.ok():
|
||||
for point in patrol.get_target_points():
|
||||
x, y, yaw = point[0], point[1], point[2]
|
||||
# 导航到目标点
|
||||
target_pose = patrol.get_pose_by_xyyaw(x, y, yaw)
|
||||
patrol.speach_text(text=f'准备前往目标点{x},{y}')
|
||||
patrol.nav_to_pose(target_pose)
|
||||
patrol.speach_text(text=f"已到达目标点{x},{y},准备记录图像")
|
||||
patrol.record_image()
|
||||
patrol.speach_text(text=f"图像记录完成")
|
||||
rclpy.shutdown()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,26 @@
|
||||
import rclpy
|
||||
from rclpy.node import Node
|
||||
from autopatrol_interfaces.srv import SpeachText
|
||||
import espeakng
|
||||
|
||||
class Speaker(Node):
|
||||
def __init__(self, node_name):
|
||||
super().__init__(node_name)
|
||||
self.speech_service = self.create_service(
|
||||
SpeachText, 'speech_text', self.speak_text_callback)
|
||||
self.speaker = espeakng.Speaker()
|
||||
self.speaker.voice = 'zh'
|
||||
|
||||
def speak_text_callback(self, request, response):
|
||||
self.get_logger().info('正在朗读 %s' % request.text)
|
||||
self.speaker.say(request.text)
|
||||
self.speaker.wait()
|
||||
response.result = True
|
||||
return response
|
||||
|
||||
|
||||
def main(args=None):
|
||||
rclpy.init(args=args)
|
||||
node = Speaker('speaker')
|
||||
rclpy.spin(node)
|
||||
rclpy.shutdown()
|
||||
@@ -0,0 +1,10 @@
|
||||
patrol_node:
|
||||
ros__parameters:
|
||||
initial_point: [0.0, 0.0, 0.0]
|
||||
target_points: [
|
||||
0.0,0.0,0.0,
|
||||
1.0,2.0,3.14,
|
||||
-4.5,1.5,1.57,
|
||||
-8.0,-5.0,1.57,
|
||||
1.0,-5.0,3.14,
|
||||
]
|
||||
@@ -0,0 +1,28 @@
|
||||
import os
|
||||
import launch
|
||||
import launch_ros
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
# 获取与拼接默认路径
|
||||
autopatrol_robot_dir = get_package_share_directory(
|
||||
'autopatrol_robot')
|
||||
patrol_config_path = os.path.join(
|
||||
autopatrol_robot_dir, 'config', 'patrol_config.yaml')
|
||||
|
||||
action_node_turtle_control = launch_ros.actions.Node(
|
||||
package='autopatrol_robot',
|
||||
executable='patrol_node',
|
||||
parameters=[patrol_config_path]
|
||||
)
|
||||
action_node_patrol_client = launch_ros.actions.Node(
|
||||
package='autopatrol_robot',
|
||||
executable='speaker',
|
||||
)
|
||||
|
||||
return launch.LaunchDescription([
|
||||
action_node_turtle_control,
|
||||
action_node_patrol_client,
|
||||
])
|
||||
18
chapt7/chapt7_ws/src/autopatrol_robot/package.xml
Normal file
18
chapt7/chapt7_ws/src/autopatrol_robot/package.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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>autopatrol_robot</name>
|
||||
<version>0.0.0</version>
|
||||
<description>TODO: Package description</description>
|
||||
<maintainer email="87068644+fishros@users.noreply.github.com">fishros</maintainer>
|
||||
<license>TODO: License declaration</license>
|
||||
|
||||
<test_depend>ament_copyright</test_depend>
|
||||
<test_depend>ament_flake8</test_depend>
|
||||
<test_depend>ament_pep257</test_depend>
|
||||
<test_depend>python3-pytest</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_python</build_type>
|
||||
</export>
|
||||
</package>
|
||||
4
chapt7/chapt7_ws/src/autopatrol_robot/setup.cfg
Normal file
4
chapt7/chapt7_ws/src/autopatrol_robot/setup.cfg
Normal file
@@ -0,0 +1,4 @@
|
||||
[develop]
|
||||
script_dir=$base/lib/autopatrol_robot
|
||||
[install]
|
||||
install_scripts=$base/lib/autopatrol_robot
|
||||
29
chapt7/chapt7_ws/src/autopatrol_robot/setup.py
Normal file
29
chapt7/chapt7_ws/src/autopatrol_robot/setup.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from setuptools import find_packages, setup
|
||||
from glob import glob
|
||||
package_name = 'autopatrol_robot'
|
||||
|
||||
setup(
|
||||
name=package_name,
|
||||
version='0.0.0',
|
||||
packages=find_packages(exclude=['test']),
|
||||
data_files=[
|
||||
('share/ament_index/resource_index/packages',
|
||||
['resource/' + package_name]),
|
||||
('share/' + package_name, ['package.xml']),
|
||||
('share/' + package_name+'/config', ['config/patrol_config.yaml']),
|
||||
('share/' + package_name+'/launch', glob('launch/*.launch.py')),
|
||||
],
|
||||
install_requires=['setuptools'],
|
||||
zip_safe=True,
|
||||
maintainer='fishros',
|
||||
maintainer_email='87068644+fishros@users.noreply.github.com',
|
||||
description='TODO: Package description',
|
||||
license='TODO: License declaration',
|
||||
tests_require=['pytest'],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'patrol_node=autopatrol_robot.patrol_node:main',
|
||||
'speaker=autopatrol_robot.speaker:main',
|
||||
],
|
||||
},
|
||||
)
|
||||
25
chapt7/chapt7_ws/src/autopatrol_robot/test/test_copyright.py
Normal file
25
chapt7/chapt7_ws/src/autopatrol_robot/test/test_copyright.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ament_copyright.main import main
|
||||
import pytest
|
||||
|
||||
|
||||
# Remove the `skip` decorator once the source file(s) have a copyright header
|
||||
@pytest.mark.skip(reason='No copyright header has been placed in the generated source file.')
|
||||
@pytest.mark.copyright
|
||||
@pytest.mark.linter
|
||||
def test_copyright():
|
||||
rc = main(argv=['.', 'test'])
|
||||
assert rc == 0, 'Found errors'
|
||||
25
chapt7/chapt7_ws/src/autopatrol_robot/test/test_flake8.py
Normal file
25
chapt7/chapt7_ws/src/autopatrol_robot/test/test_flake8.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# Copyright 2017 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ament_flake8.main import main_with_errors
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.flake8
|
||||
@pytest.mark.linter
|
||||
def test_flake8():
|
||||
rc, errors = main_with_errors(argv=[])
|
||||
assert rc == 0, \
|
||||
'Found %d code style errors / warnings:\n' % len(errors) + \
|
||||
'\n'.join(errors)
|
||||
23
chapt7/chapt7_ws/src/autopatrol_robot/test/test_pep257.py
Normal file
23
chapt7/chapt7_ws/src/autopatrol_robot/test/test_pep257.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ament_pep257.main import main
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.linter
|
||||
@pytest.mark.pep257
|
||||
def test_pep257():
|
||||
rc = main(argv=['.', 'test'])
|
||||
assert rc == 0, 'Found code style errors / warnings'
|
||||
@@ -0,0 +1,40 @@
|
||||
import rclpy
|
||||
from rclpy.node import Node
|
||||
from tf2_ros import TransformListener, Buffer
|
||||
from tf_transformations import euler_from_quaternion
|
||||
|
||||
|
||||
class TFListener(Node):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__('tf2_listener')
|
||||
self.buffer = Buffer()
|
||||
self.listener = TransformListener(self.buffer, self)
|
||||
self.timer = self.create_timer(1, self.get_transform)
|
||||
|
||||
def get_transform(self):
|
||||
try:
|
||||
tf = self.buffer.lookup_transform(
|
||||
'map', 'base_footprint', rclpy.time.Time(seconds=0), rclpy.time.Duration(seconds=1))
|
||||
transform = tf.transform
|
||||
rotation_euler = euler_from_quaternion([
|
||||
transform.rotation.x,
|
||||
transform.rotation.y,
|
||||
transform.rotation.z,
|
||||
transform.rotation.w
|
||||
])
|
||||
self.get_logger().info(
|
||||
f'平移:{transform.translation},旋转四元数:{transform.rotation}:旋转欧拉角:{rotation_euler}')
|
||||
except Exception as e:
|
||||
self.get_logger().warn(f'不能够获取坐标变换,原因: {str(e)}')
|
||||
|
||||
|
||||
def main():
|
||||
rclpy.init()
|
||||
node = TFListener()
|
||||
rclpy.spin(node)
|
||||
rclpy.shutdown()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,21 @@
|
||||
from geometry_msgs.msg import PoseStamped
|
||||
from nav2_simple_commander.robot_navigator import BasicNavigator
|
||||
import rclpy
|
||||
|
||||
|
||||
def main():
|
||||
rclpy.init()
|
||||
navigator = BasicNavigator()
|
||||
initial_pose = PoseStamped()
|
||||
initial_pose.header.frame_id = 'map'
|
||||
initial_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
initial_pose.pose.position.x = 0.0
|
||||
initial_pose.pose.position.y = 0.0
|
||||
initial_pose.pose.orientation.w = 1.0
|
||||
navigator.setInitialPose(initial_pose)
|
||||
navigator.waitUntilNav2Active()
|
||||
rclpy.spin(navigator)
|
||||
rclpy.shutdown()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,40 @@
|
||||
from geometry_msgs.msg import PoseStamped
|
||||
from nav2_simple_commander.robot_navigator import BasicNavigator, TaskResult
|
||||
import rclpy
|
||||
from rclpy.duration import Duration
|
||||
|
||||
|
||||
def main():
|
||||
rclpy.init()
|
||||
navigator = BasicNavigator()
|
||||
# 等待导航启动完成
|
||||
navigator.waitUntilNav2Active()
|
||||
# 设置目标点坐标
|
||||
goal_pose = PoseStamped()
|
||||
goal_pose.header.frame_id = 'map'
|
||||
goal_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
goal_pose.pose.position.x = 1.0
|
||||
goal_pose.pose.position.y = 1.0
|
||||
goal_pose.pose.orientation.w = 1.0
|
||||
# 发送目标接收反馈结果
|
||||
navigator.goToPose(goal_pose)
|
||||
while not navigator.isTaskComplete():
|
||||
feedback = navigator.getFeedback()
|
||||
navigator.get_logger().info(
|
||||
f'预计: {Duration.from_msg(feedback.estimated_time_remaining).nanoseconds / 1e9} s 后到达')
|
||||
# 超时自动取消
|
||||
if Duration.from_msg(feedback.navigation_time) > Duration(seconds=600.0):
|
||||
navigator.cancelTask()
|
||||
# 最终结果判断
|
||||
result = navigator.getResult()
|
||||
if result == TaskResult.SUCCEEDED:
|
||||
navigator.get_logger().info('导航结果:成功')
|
||||
elif result == TaskResult.CANCELED:
|
||||
navigator.get_logger().warn('导航结果:被取消')
|
||||
elif result == TaskResult.FAILED:
|
||||
navigator.get_logger().error('导航结果:失败')
|
||||
else:
|
||||
navigator.get_logger().error('导航结果:返回状态无效')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,52 @@
|
||||
from geometry_msgs.msg import PoseStamped
|
||||
from nav2_simple_commander.robot_navigator import BasicNavigator, TaskResult
|
||||
import rclpy
|
||||
from rclpy.duration import Duration
|
||||
|
||||
def main():
|
||||
rclpy.init()
|
||||
navigator = BasicNavigator()
|
||||
navigator.waitUntilNav2Active()
|
||||
# 创建点集
|
||||
goal_poses = []
|
||||
goal_pose1 = PoseStamped()
|
||||
goal_pose1.header.frame_id = 'map'
|
||||
goal_pose1.header.stamp = navigator.get_clock().now().to_msg()
|
||||
goal_pose1.pose.position.x = 0.0
|
||||
goal_pose1.pose.position.y = 0.0
|
||||
goal_pose1.pose.orientation.w = 1.0
|
||||
goal_poses.append(goal_pose1)
|
||||
goal_pose2 = PoseStamped()
|
||||
goal_pose2.header.frame_id = 'map'
|
||||
goal_pose2.header.stamp = navigator.get_clock().now().to_msg()
|
||||
goal_pose2.pose.position.x = 2.0
|
||||
goal_pose2.pose.position.y = 0.0
|
||||
goal_pose2.pose.orientation.w = 1.0
|
||||
goal_poses.append(goal_pose2)
|
||||
goal_pose3 = PoseStamped()
|
||||
goal_pose3.header.frame_id = 'map'
|
||||
goal_pose3.header.stamp = navigator.get_clock().now().to_msg()
|
||||
goal_pose3.pose.position.x = 2.0
|
||||
goal_pose3.pose.position.y = 2.0
|
||||
goal_pose3.pose.orientation.w = 1.0
|
||||
goal_poses.append(goal_pose3)
|
||||
# 调用路点导航服务
|
||||
navigator.followWaypoints(goal_poses)
|
||||
# 判断结束及获取反馈
|
||||
while not navigator.isTaskComplete():
|
||||
feedback = navigator.getFeedback()
|
||||
navigator.get_logger().info(
|
||||
f'当前目标编号:{feedback.current_waypoint}')
|
||||
# 最终结果判断
|
||||
result = navigator.getResult()
|
||||
if result == TaskResult.SUCCEEDED:
|
||||
navigator.get_logger().info('导航结果:成功')
|
||||
elif result == TaskResult.CANCELED:
|
||||
navigator.get_logger().warn('导航结果:被取消')
|
||||
elif result == TaskResult.FAILED:
|
||||
navigator.get_logger().error('导航结果:失败')
|
||||
else:
|
||||
navigator.get_logger().error('导航结果:返回状态无效')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
18
chapt7/chapt7_ws/src/fishbot_application/package.xml
Normal file
18
chapt7/chapt7_ws/src/fishbot_application/package.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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>fishbot_application</name>
|
||||
<version>0.0.0</version>
|
||||
<description>TODO: Package description</description>
|
||||
<maintainer email="87068644+fishros@users.noreply.github.com">fishros</maintainer>
|
||||
<license>TODO: License declaration</license>
|
||||
|
||||
<test_depend>ament_copyright</test_depend>
|
||||
<test_depend>ament_flake8</test_depend>
|
||||
<test_depend>ament_pep257</test_depend>
|
||||
<test_depend>python3-pytest</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_python</build_type>
|
||||
</export>
|
||||
</package>
|
||||
4
chapt7/chapt7_ws/src/fishbot_application/setup.cfg
Normal file
4
chapt7/chapt7_ws/src/fishbot_application/setup.cfg
Normal file
@@ -0,0 +1,4 @@
|
||||
[develop]
|
||||
script_dir=$base/lib/fishbot_application
|
||||
[install]
|
||||
install_scripts=$base/lib/fishbot_application
|
||||
26
chapt7/chapt7_ws/src/fishbot_application/setup.py
Normal file
26
chapt7/chapt7_ws/src/fishbot_application/setup.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
package_name = 'fishbot_application'
|
||||
|
||||
setup(
|
||||
name=package_name,
|
||||
version='0.0.0',
|
||||
packages=find_packages(exclude=['test']),
|
||||
data_files=[
|
||||
('share/ament_index/resource_index/packages',
|
||||
['resource/' + package_name]),
|
||||
('share/' + package_name, ['package.xml']),
|
||||
],
|
||||
install_requires=['setuptools'],
|
||||
zip_safe=True,
|
||||
maintainer='fishros',
|
||||
maintainer_email='87068644+fishros@users.noreply.github.com',
|
||||
description='TODO: Package description',
|
||||
license='TODO: License declaration',
|
||||
tests_require=['pytest'],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'init_robot_pose=fishbot_application.init_robot_pose:main',
|
||||
],
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
# Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ament_copyright.main import main
|
||||
import pytest
|
||||
|
||||
|
||||
# Remove the `skip` decorator once the source file(s) have a copyright header
|
||||
@pytest.mark.skip(reason='No copyright header has been placed in the generated source file.')
|
||||
@pytest.mark.copyright
|
||||
@pytest.mark.linter
|
||||
def test_copyright():
|
||||
rc = main(argv=['.', 'test'])
|
||||
assert rc == 0, 'Found errors'
|
||||
25
chapt7/chapt7_ws/src/fishbot_application/test/test_flake8.py
Normal file
25
chapt7/chapt7_ws/src/fishbot_application/test/test_flake8.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# Copyright 2017 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ament_flake8.main import main_with_errors
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.flake8
|
||||
@pytest.mark.linter
|
||||
def test_flake8():
|
||||
rc, errors = main_with_errors(argv=[])
|
||||
assert rc == 0, \
|
||||
'Found %d code style errors / warnings:\n' % len(errors) + \
|
||||
'\n'.join(errors)
|
||||
23
chapt7/chapt7_ws/src/fishbot_application/test/test_pep257.py
Normal file
23
chapt7/chapt7_ws/src/fishbot_application/test/test_pep257.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ament_pep257.main import main
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.linter
|
||||
@pytest.mark.pep257
|
||||
def test_pep257():
|
||||
rc = main(argv=['.', 'test'])
|
||||
assert rc == 0, 'Found code style errors / warnings'
|
||||
36
chapt7/chapt7_ws/src/fishbot_application_cpp/CMakeLists.txt
Normal file
36
chapt7/chapt7_ws/src/fishbot_application_cpp/CMakeLists.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(fishbot_application_cpp)
|
||||
|
||||
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)
|
||||
# uncomment the following section in order to fill in
|
||||
# further dependencies manually.
|
||||
find_package(rclcpp REQUIRED)
|
||||
find_package(nav2_msgs REQUIRED)
|
||||
find_package(rclcpp_action REQUIRED)
|
||||
|
||||
add_executable(nav2pose src/nav2pose.cpp)
|
||||
ament_target_dependencies(nav2pose rclcpp nav2_msgs rclcpp_action)
|
||||
|
||||
install(TARGETS
|
||||
nav2pose
|
||||
DESTINATION lib/${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()
|
||||
18
chapt7/chapt7_ws/src/fishbot_application_cpp/package.xml
Normal file
18
chapt7/chapt7_ws/src/fishbot_application_cpp/package.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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>fishbot_application_cpp</name>
|
||||
<version>0.0.0</version>
|
||||
<description>TODO: Package description</description>
|
||||
<maintainer email="fish@fishros.com">fishros</maintainer>
|
||||
<license>TODO: License declaration</license>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
||||
@@ -0,0 +1,71 @@
|
||||
#include <memory>
|
||||
|
||||
#include "nav2_msgs/action/navigate_to_pose.hpp" // 导入导航动作消息的头文件
|
||||
#include "rclcpp/rclcpp.hpp" // 导入ROS 2的C++客户端库
|
||||
#include "rclcpp_action/rclcpp_action.hpp" // 导入ROS 2的C++ Action客户端库
|
||||
|
||||
using NavigationAction = nav2_msgs::action::NavigateToPose; // 定义导航动作类型为NavigateToPose
|
||||
|
||||
class NavToPoseClient : public rclcpp::Node {
|
||||
public:
|
||||
using NavigationActionClient = rclcpp_action::Client<NavigationAction>; // 定义导航动作客户端类型
|
||||
using NavigationActionGoalHandle =
|
||||
rclcpp_action::ClientGoalHandle<NavigationAction>; // 定义导航动作目标句柄类型
|
||||
|
||||
NavToPoseClient() : Node("nav_to_pose_client") {
|
||||
// 创建导航动作客户端
|
||||
action_client_ = rclcpp_action::create_client<NavigationAction>(
|
||||
this, "navigate_to_pose");
|
||||
}
|
||||
|
||||
void sendGoal() {
|
||||
// 等待导航动作服务器上线,等待时间为5秒
|
||||
while (!action_client_->wait_for_action_server(std::chrono::seconds(5))) {
|
||||
RCLCPP_INFO(get_logger(), "等待Action服务上线。");
|
||||
}
|
||||
// 设置导航目标点
|
||||
auto goal_msg = NavigationAction::Goal();
|
||||
goal_msg.pose.header.frame_id = "map"; // 设置目标点的坐标系为地图坐标系
|
||||
goal_msg.pose.pose.position.x = 2.0f; // 设置目标点的x坐标为2.0
|
||||
goal_msg.pose.pose.position.y = 2.0f; // 设置目标点的y坐标为2.0
|
||||
|
||||
auto send_goal_options =
|
||||
rclcpp_action::Client<NavigationAction>::SendGoalOptions();
|
||||
// 设置请求目标结果回调函数
|
||||
send_goal_options.goal_response_callback =
|
||||
[this](NavigationActionGoalHandle::SharedPtr goal_handle) {
|
||||
if (goal_handle) {
|
||||
RCLCPP_INFO(get_logger(), "目标点已被服务器接收");
|
||||
}
|
||||
};
|
||||
// 设置移动过程反馈回调函数
|
||||
send_goal_options.feedback_callback =
|
||||
[this](
|
||||
NavigationActionGoalHandle::SharedPtr goal_handle,
|
||||
const std::shared_ptr<const NavigationAction::Feedback> feedback) {
|
||||
(void)goal_handle; // 假装调用,避免 warning: unused
|
||||
RCLCPP_INFO(this->get_logger(), "反馈剩余距离:%f",
|
||||
feedback->distance_remaining);
|
||||
};
|
||||
// 设置执行结果回调函数
|
||||
send_goal_options.result_callback =
|
||||
[this](const NavigationActionGoalHandle::WrappedResult& result) {
|
||||
if (result.code == rclcpp_action::ResultCode::SUCCEEDED) {
|
||||
RCLCPP_INFO(this->get_logger(), "处理成功!");
|
||||
}
|
||||
};
|
||||
// 发送导航目标点
|
||||
action_client_->async_send_goal(goal_msg, send_goal_options);
|
||||
}
|
||||
|
||||
NavigationActionClient::SharedPtr action_client_;
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
rclcpp::init(argc, argv);
|
||||
auto node = std::make_shared<NavToPoseClient>();
|
||||
node->sendGoal();
|
||||
rclcpp::spin(node);
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
32
chapt7/chapt7_ws/src/fishbot_description/CMakeLists.txt
Normal file
32
chapt7/chapt7_ws/src/fishbot_description/CMakeLists.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(fishbot_description)
|
||||
|
||||
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)
|
||||
# uncomment the following section in order to fill in
|
||||
# further dependencies manually.
|
||||
# find_package(<dependency> REQUIRED)
|
||||
|
||||
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()
|
||||
|
||||
|
||||
install(DIRECTORY launch urdf config world
|
||||
DESTINATION share/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
|
||||
ament_package()
|
||||
202
chapt7/chapt7_ws/src/fishbot_description/LICENSE
Normal file
202
chapt7/chapt7_ws/src/fishbot_description/LICENSE
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,52 @@
|
||||
controller_manager:
|
||||
ros__parameters:
|
||||
update_rate: 100 # Hz
|
||||
use_sim_time: true
|
||||
fishbot_joint_state_broadcaster:
|
||||
type: joint_state_broadcaster/JointStateBroadcaster
|
||||
use_sim_time: true
|
||||
fishbot_effort_controller:
|
||||
type: effort_controllers/JointGroupEffortController
|
||||
fishbot_diff_drive_controller:
|
||||
type: diff_drive_controller/DiffDriveController
|
||||
|
||||
fishbot_effort_controller:
|
||||
ros__parameters:
|
||||
joints:
|
||||
- left_wheel_joint
|
||||
- right_wheel_joint
|
||||
command_interfaces:
|
||||
- effort
|
||||
state_interfaces:
|
||||
- position
|
||||
- velocity
|
||||
- effort
|
||||
|
||||
|
||||
|
||||
fishbot_diff_drive_controller:
|
||||
ros__parameters:
|
||||
left_wheel_names: ["left_wheel_joint"]
|
||||
right_wheel_names: ["right_wheel_joint"]
|
||||
|
||||
wheel_separation: 0.17
|
||||
#wheels_per_side: 1 # actually 2, but both are controlled by 1 signal
|
||||
wheel_radius: 0.032
|
||||
|
||||
wheel_separation_multiplier: 1.0
|
||||
left_wheel_radius_multiplier: 1.0
|
||||
right_wheel_radius_multiplier: 1.0
|
||||
|
||||
publish_rate: 50.0
|
||||
odom_frame_id: odom
|
||||
base_frame_id: base_footprint
|
||||
pose_covariance_diagonal : [0.001, 0.001, 0.0, 0.0, 0.0, 0.01]
|
||||
twist_covariance_diagonal: [0.001, 0.0, 0.0, 0.0, 0.0, 0.01]
|
||||
|
||||
open_loop: true
|
||||
enable_odom_tf: true
|
||||
|
||||
cmd_vel_timeout: 0.5
|
||||
#publish_limited_velocity: true
|
||||
use_stamped_vel: false
|
||||
#velocity_rolling_window_size: 10
|
||||
@@ -0,0 +1,170 @@
|
||||
Panels:
|
||||
- Class: rviz_common/Displays
|
||||
Help Height: 78
|
||||
Name: Displays
|
||||
Property Tree Widget:
|
||||
Expanded:
|
||||
- /Global Options1
|
||||
- /Status1
|
||||
- /RobotModel1
|
||||
Splitter Ratio: 0.5
|
||||
Tree Height: 480
|
||||
- Class: rviz_common/Selection
|
||||
Name: Selection
|
||||
- Class: rviz_common/Tool Properties
|
||||
Expanded:
|
||||
- /2D Goal Pose1
|
||||
- /Publish Point1
|
||||
Name: Tool Properties
|
||||
Splitter Ratio: 0.5886790156364441
|
||||
- Class: rviz_common/Views
|
||||
Expanded:
|
||||
- /Current View1
|
||||
Name: Views
|
||||
Splitter Ratio: 0.5
|
||||
- Class: rviz_common/Time
|
||||
Experimental: false
|
||||
Name: Time
|
||||
SyncMode: 0
|
||||
SyncSource: ""
|
||||
Visualization Manager:
|
||||
Class: ""
|
||||
Displays:
|
||||
- Alpha: 0.5
|
||||
Cell Size: 1
|
||||
Class: rviz_default_plugins/Grid
|
||||
Color: 160; 160; 164
|
||||
Enabled: true
|
||||
Line Style:
|
||||
Line Width: 0.029999999329447746
|
||||
Value: Lines
|
||||
Name: Grid
|
||||
Normal Cell Count: 0
|
||||
Offset:
|
||||
X: 0
|
||||
Y: 0
|
||||
Z: 0
|
||||
Plane: XY
|
||||
Plane Cell Count: 10
|
||||
Reference Frame: <Fixed Frame>
|
||||
Value: true
|
||||
- Alpha: 1
|
||||
Class: rviz_default_plugins/RobotModel
|
||||
Collision Enabled: false
|
||||
Description File: ""
|
||||
Description Source: Topic
|
||||
Description Topic:
|
||||
Depth: 5
|
||||
Durability Policy: Volatile
|
||||
History Policy: Keep Last
|
||||
Reliability Policy: Reliable
|
||||
Value: /robot_description
|
||||
Enabled: true
|
||||
Links:
|
||||
All Links Enabled: true
|
||||
Expand Joint Details: false
|
||||
Expand Link Details: false
|
||||
Expand Tree: false
|
||||
Link Tree Style: Links in Alphabetic Order
|
||||
base_link:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
imu_link:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
Mass Properties:
|
||||
Inertia: false
|
||||
Mass: false
|
||||
Name: RobotModel
|
||||
TF Prefix: ""
|
||||
Update Interval: 0
|
||||
Value: true
|
||||
Visual Enabled: true
|
||||
Enabled: true
|
||||
Global Options:
|
||||
Background Color: 48; 48; 48
|
||||
Fixed Frame: base_link
|
||||
Frame Rate: 30
|
||||
Name: root
|
||||
Tools:
|
||||
- Class: rviz_default_plugins/Interact
|
||||
Hide Inactive Objects: true
|
||||
- Class: rviz_default_plugins/MoveCamera
|
||||
- Class: rviz_default_plugins/Select
|
||||
- Class: rviz_default_plugins/FocusCamera
|
||||
- Class: rviz_default_plugins/Measure
|
||||
Line color: 128; 128; 0
|
||||
- Class: rviz_default_plugins/SetInitialPose
|
||||
Covariance x: 0.25
|
||||
Covariance y: 0.25
|
||||
Covariance yaw: 0.06853891909122467
|
||||
Topic:
|
||||
Depth: 5
|
||||
Durability Policy: Volatile
|
||||
History Policy: Keep Last
|
||||
Reliability Policy: Reliable
|
||||
Value: /initialpose
|
||||
- Class: rviz_default_plugins/SetGoal
|
||||
Topic:
|
||||
Depth: 5
|
||||
Durability Policy: Volatile
|
||||
History Policy: Keep Last
|
||||
Reliability Policy: Reliable
|
||||
Value: /goal_pose
|
||||
- Class: rviz_default_plugins/PublishPoint
|
||||
Single click: true
|
||||
Topic:
|
||||
Depth: 5
|
||||
Durability Policy: Volatile
|
||||
History Policy: Keep Last
|
||||
Reliability Policy: Reliable
|
||||
Value: /clicked_point
|
||||
Transformation:
|
||||
Current:
|
||||
Class: rviz_default_plugins/TF
|
||||
Value: true
|
||||
Views:
|
||||
Current:
|
||||
Class: rviz_default_plugins/Orbit
|
||||
Distance: 0.4101419746875763
|
||||
Enable Stereo Rendering:
|
||||
Stereo Eye Separation: 0.05999999865889549
|
||||
Stereo Focal Distance: 1
|
||||
Swap Stereo Eyes: false
|
||||
Value: false
|
||||
Focal Point:
|
||||
X: 0
|
||||
Y: 0
|
||||
Z: 0
|
||||
Focal Shape Fixed Size: true
|
||||
Focal Shape Size: 0.05000000074505806
|
||||
Invert Z Axis: false
|
||||
Name: Current View
|
||||
Near Clip Distance: 0.009999999776482582
|
||||
Pitch: 0.785398006439209
|
||||
Target Frame: <Fixed Frame>
|
||||
Value: Orbit (rviz)
|
||||
Yaw: 0.785398006439209
|
||||
Saved: ~
|
||||
Window Geometry:
|
||||
Displays:
|
||||
collapsed: false
|
||||
Height: 777
|
||||
Hide Left Dock: false
|
||||
Hide Right Dock: false
|
||||
QMainWindow State: 000000ff00000000fd0000000400000000000001560000026bfc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d0000026b000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f0000026bfc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d0000026b000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e100000197000000030000054a0000003efc0100000002fb0000000800540069006d006501000000000000054a000002fb00fffffffb0000000800540069006d00650100000000000004500000000000000000000002d90000026b00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
|
||||
Selection:
|
||||
collapsed: false
|
||||
Time:
|
||||
collapsed: false
|
||||
Tool Properties:
|
||||
collapsed: false
|
||||
Views:
|
||||
collapsed: false
|
||||
Width: 1354
|
||||
X: 70
|
||||
Y: 27
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user