diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
new file mode 100644
index 0000000..208c88a
--- /dev/null
+++ b/.vscode/c_cpp_properties.json
@@ -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
+}
\ No newline at end of file
diff --git a/chapt10/chapt10_ws/.vscode/settings.json b/chapt10/chapt10_ws/.vscode/settings.json
new file mode 100644
index 0000000..f6e40f6
--- /dev/null
+++ b/chapt10/chapt10_ws/.vscode/settings.json
@@ -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"
+ }
+}
\ No newline at end of file
diff --git a/chapt10/chapt10_ws/pub5_sub5_2M.txt b/chapt10/chapt10_ws/pub5_sub5_2M.txt
new file mode 100644
index 0000000..b5ddf61
--- /dev/null
+++ b/chapt10/chapt10_ws/pub5_sub5_2M.txt
@@ -0,0 +1,2729 @@
+1
+1
+0
+1
+1
+1
+1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+2
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+1
+1
+1
+1
+1
+1
+0
+0
+1
+1
+1
+1
+0
+0
+0
+0
+0
+0
+1
+0
+1
+1
+0
+1
+1
+0
+1
+1
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+1
+0
+0
+1
+1
+0
+1
+1
+0
+1
+1
+0
+1
+1
+0
+1
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+0
+1
+1
+1
+1
+1
+0
+1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+2
+1
+1
+1
+1
+1
+1
+0
+0
+1
+0
+0
+1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+1
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+1
+1
+1
+1
+1
+1
+1
+1
+0
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+0
+1
+1
+1
+1
+2
+1
+1
+1
+1
+1
+1
+1
+1
+1
+2
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+2
+4
+1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+1
+1
+1
+1
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+1
+0
+0
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+0
+1
+1
+1
+1
+1
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+1
+1
+1
+0
+1
+1
+1
+1
+1
+1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+1
+0
+0
+1
+0
+1
+0
+1
+1
+0
+1
+1
+1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+1
+1
+1
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+1
+0
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+0
+1
+1
+1
+1
+1
+1
+0
+1
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+1
+1
+1
+1
+0
+2
+1
+1
+0
+0
+0
+0
+2
+1
+1
+2
+0
+1
+2
+2
+1
+1
+0
+0
+0
+0
+1
+0
+0
+1
+0
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+1
+1
+1
+2
+1
+1
+1
+2
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+1
+0
+1
+2
+2
+1
+1
+1
+4
+1
+1
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+2
+1
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+2
+0
+0
+1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+2
+1
+0
+1
+0
+1
+2
+1
+3
+2
+0
+1
+2
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+0
+0
+0
+0
+0
+0
+0
+1
+2
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+1
+0
+0
+0
+0
+3
+0
+0
+0
+0
+1
+1
+0
+1
+0
+1
+0
+1
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+1
+1
+0
+0
+0
+1
+0
+1
+1
+1
+1
+0
+1
+0
+1
+0
+1
+1
+0
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+2
+1
+1
+0
+1
+2
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+1
+1
+0
+0
+0
+1
+0
+1
+0
+1
+0
+0
+0
+0
+1
+0
+0
+0
+0
+1
+0
+0
+0
+1
+0
+0
+0
+0
+1
+0
+0
+1
+0
+0
+1
+0
+0
+0
+2
+0
+1
+0
+0
+0
+0
+1
+0
+0
+0
+0
+1
+0
+0
+0
+1
+1
+1
+1
+2
+0
+1
+1
+1
+1
+1
+1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+1
+1
+1
+1
+1
+1
+0
+2
+1
+1
+1
+1
+1
+1
+1
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+1
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+1
+0
+1
+0
+0
+0
+0
+2
+0
+0
+1
+2
+2
+1
+1
+1
+1
+1
+0
+2
+1
+1
+3
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+2
+2
+2
+2
+2
+2
+0
+1
+1
+1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+4
+4
+3
+4
+3
+4
+4
+3
+4
+4
+2
+3
+3
+2
+2
+2
+4
+3
+3
+3
+2
+3
+3
+3
+3
+6
+3
+3
+3
+3
+3
+3
+3
+3
+2
+2
+2
+3
+2
+2
+2
+3
+2
+3
+3
+3
+3
+3
+3
+3
+3
+3
+3
+3
+3
+7
+3
+3
+3
+3
+6
+5
+3
+3
+3
+3
+3
+4
+7
+4
+3
+2
+1
+0
+0
+0
+1
+0
+0
+1
+0
+0
+0
+0
+0
+1
+1
+1
+0
+1
+1
+0
+1
+0
+1
+2
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+1
+1
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+1
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+1
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+1
+0
+0
+0
+1
+1
+1
+0
+0
+0
+1
+1
+3
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+1
+1
+1
+0
+0
+0
+4
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+2
+1
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+0
+1
+0
+0
+0
+0
+0
+0
+0
+1
+1
+1
+0
+0
+0
+0
+0
+1
+0
+1
+0
+0
+1
+1
+1
+0
+1
+0
+0
+1
+1
+0
+1
+0
+1
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+0
+0
+0
+1
+0
+0
+1
+1
+0
+1
+1
+0
+0
+1
+0
+1
+1
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+1
+1
+1
+0
+1
+1
+1
+1
+0
+1
+1
+1
+1
+0
+0
+0
+0
+1
+0
+0
+0
+1
+1
+1
+1
+0
+1
+0
+0
+1
+1
+1
+1
+1
+1
+0
+1
+1
+0
+0
+1
+0
+0
+0
+0
+1
+1
+1
+1
+2
+1
+1
+1
+2
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+1
+1
+1
+2
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+0
+0
+1
+1
+1
+0
+1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+2
+1
+3
+1
+1
+1
+0
+1
+1
+0
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+1
+0
+1
+1
+1
+1
+0
+1
+1
+0
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+1
+1
+209040809
+209042537
+209044355
+209046146
+209047966
+209049870
+209051509
+209053381
+209055177
+209057057
+209058904
+209060530
+209062708
+209064646
+209066060
+209067873
+209069610
+209071567
+209073352
+209075115
+209076943
+209078563
+209080340
+209082169
+209083978
+209085769
+209087618
+209089358
+209091181
+209093167
+209094746
+209096553
+209098503
+209100175
+209101971
+209103762
+209105507
+209107480
+209109191
+209110954
+209112744
+209114566
+209116367
+209118230
+209119920
+209121728
+209123542
+209125439
+209127226
+209128934
+209130723
+209132548
+209134331
+209136191
+209138133
+209139839
+209141588
+209143467
+209145355
+209147337
+209148844
+209150629
+209152451
+209154772
+209157003
+209158232
+209159775
+209161503
+209163232
+209165000
+209166746
+209168669
+209170360
+209172165
+209174419
+209176084
+209177772
+209179583
+209181314
+209183096
+209184750
+209186605
+209188377
+209190236
+209192288
+209193862
+209195583
+209197384
+209199249
+209201068
+209202889
+209204973
+209206450
+209208230
+209210148
+209212206
+209213581
+209215457
+209217219
+209219546
+209220809
+209223324
+209224605
+209226197
+209228072
+209229782
+209231623
+209233579
+209235360
+209237238
+209239295
+209240829
+209242395
+209244244
+209246472
+209248763
+209250356
+73
+57
+51
+59
+102
+58
+65
+65
+55
+58
+58
+67
+63
+68
+61
+88
+71
+63
+73
+59
+60
+92
+55
+64
+62
+61
+76
+62
+79
+78
+72
+92
+82
+63
+65
+61
+62
+65
+65
+54
+104
+74
+69
+52
+58
+57
+92
+67
+74
+63
+65
+53
+58
+56
+64
+91
+103
+107
+73
+68
+56
+81
+69
+88
+75
+62
+55
+46
+53
+57
+65
+60
+54
+66
+56
+47
+47
+47
+47
+53
+53
+51
+59
+66
+76
+46
+84
+76
+191
+50
+48
+46
+51
+60
+53
+78
+56
+48
+56
+50
+61
+76
+76
+53
+45
+56
+56
+72
+50
+52
+61
+58
+54
+59
+86
+68
+69
+58
+54
+50
+58
+96
+159
+48
+53
+63
+59
+64
+54
+59
+53
+47
+56
+56
+66
+47
+52
+48
+59
+47
+52
+53
+46
+45
+85
+71
+75
+55
+67
+85
+86
+73
+59
+58
+56
+57
+54
+68
+119
+102
+146
+66
+111
+61
+60
+69
+58
+57
+89
+56
+60
+63
+60
+59
+52
+64
+128
+104
+57
+56
+83
+55
+46
+30
+37
+28
+25
+39
+44
+33
+28
diff --git a/chapt10/chapt10_ws/shm.xml b/chapt10/chapt10_ws/shm.xml
new file mode 100644
index 0000000..b9e53be
--- /dev/null
+++ b/chapt10/chapt10_ws/shm.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+ SYNCHRONOUS
+
+
+ AUTOMATIC
+
+
+ DYNAMIC
+
+
+
+
+
+ AUTOMATIC
+
+
+ DYNAMIC
+
+
\ No newline at end of file
diff --git a/chapt10/chapt10_ws/src/learn_compose/CMakeLists.txt b/chapt10/chapt10_ws/src/learn_compose/CMakeLists.txt
new file mode 100644
index 0000000..293534a
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_compose/CMakeLists.txt
@@ -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( 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()
diff --git a/chapt10/chapt10_ws/src/learn_compose/include/learn_compose/listener.hpp b/chapt10/chapt10_ws/src/learn_compose/include/learn_compose/listener.hpp
new file mode 100644
index 0000000..8692f60
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_compose/include/learn_compose/listener.hpp
@@ -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::SharedPtr sub_;
+};
+
+} // namespace learn_compose
+
+#endif // LEARN_COMPOSE__LISTENER_COMPONENT_HPP_
\ No newline at end of file
diff --git a/chapt10/chapt10_ws/src/learn_compose/include/learn_compose/talker.hpp b/chapt10/chapt10_ws/src/learn_compose/include/learn_compose/talker.hpp
new file mode 100644
index 0000000..60b3239
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_compose/include/learn_compose/talker.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::SharedPtr pub_;
+ rclcpp::TimerBase::SharedPtr timer_;
+};
+
+} // namespace learn_compose
+
+#endif // LEARN_COMPOSE__TALKER_COMPONENT_HPP_
\ No newline at end of file
diff --git a/chapt10/chapt10_ws/src/learn_compose/package.xml b/chapt10/chapt10_ws/src/learn_compose/package.xml
new file mode 100644
index 0000000..f97a616
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_compose/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ learn_compose
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_cmake
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+
diff --git a/chapt10/chapt10_ws/src/learn_compose/src/intra_process_pubsub.cpp b/chapt10/chapt10_ws/src/learn_compose/src/intra_process_pubsub.cpp
new file mode 100644
index 0000000..af30db8
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_compose/src/intra_process_pubsub.cpp
@@ -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(options);
+ auto listener = std::make_shared(options);
+
+ executor.add_node(talker);
+ executor.add_node(listener);
+ executor.spin();
+
+ rclcpp::shutdown();
+
+ return 0;
+}
\ No newline at end of file
diff --git a/chapt10/chapt10_ws/src/learn_compose/src/listener.cpp b/chapt10/chapt10_ws/src/learn_compose/src/listener.cpp
new file mode 100644
index 0000000..fde1ef5
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_compose/src/listener.cpp
@@ -0,0 +1,20 @@
+#include "learn_compose/listener.hpp"
+#include
+
+namespace learn_compose {
+
+using namespace std::chrono_literals;
+
+Listener::Listener(const rclcpp::NodeOptions &options)
+ : Node("listener", options) {
+ sub_ = this->create_subscription(
+ "count", 10, [&](const std_msgs::msg::Int32::UniquePtr msg) {
+ RCLCPP_INFO(this->get_logger(), "收到数据:%d(0x%lX)", msg->data,
+ reinterpret_cast(msg.get()));
+ });
+}
+} // namespace learn_compose
+
+
+#include "rclcpp_components/register_node_macro.hpp"
+RCLCPP_COMPONENTS_REGISTER_NODE(learn_compose::Listener)
\ No newline at end of file
diff --git a/chapt10/chapt10_ws/src/learn_compose/src/talker.cpp b/chapt10/chapt10_ws/src/learn_compose/src/talker.cpp
new file mode 100644
index 0000000..1596472
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_compose/src/talker.cpp
@@ -0,0 +1,24 @@
+#include
+#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("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(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)
\ No newline at end of file
diff --git a/chapt10/chapt10_ws/src/learn_dds_cpp/CMakeLists.txt b/chapt10/chapt10_ws/src/learn_dds_cpp/CMakeLists.txt
new file mode 100644
index 0000000..adb278d
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_dds_cpp/CMakeLists.txt
@@ -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()
diff --git a/chapt10/chapt10_ws/src/learn_dds_cpp/package.xml b/chapt10/chapt10_ws/src/learn_dds_cpp/package.xml
new file mode 100644
index 0000000..cae164e
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_dds_cpp/package.xml
@@ -0,0 +1,21 @@
+
+
+
+ learn_dds_cpp
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_cmake
+
+ rclcpp
+ std_msgs
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+
diff --git a/chapt10/chapt10_ws/src/learn_dds_cpp/src/shm_pub.cpp b/chapt10/chapt10_ws/src/learn_dds_cpp/src/shm_pub.cpp
new file mode 100644
index 0000000..424d4b2
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_dds_cpp/src/shm_pub.cpp
@@ -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("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::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();
+ rclcpp::spin(node);
+ rclcpp::shutdown();
+ return 0;
+}
\ No newline at end of file
diff --git a/chapt10/chapt10_ws/src/learn_executor_cpp/CMakeLists.txt b/chapt10/chapt10_ws/src/learn_executor_cpp/CMakeLists.txt
new file mode 100644
index 0000000..2448b7f
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_executor_cpp/CMakeLists.txt
@@ -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( 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()
diff --git a/chapt10/chapt10_ws/src/learn_executor_cpp/package.xml b/chapt10/chapt10_ws/src/learn_executor_cpp/package.xml
new file mode 100644
index 0000000..e2a5db1
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_executor_cpp/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ learn_executor_cpp
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_cmake
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+
diff --git a/chapt10/chapt10_ws/src/learn_executor_cpp/src/learn_executor.cpp b/chapt10/chapt10_ws/src/learn_executor_cpp/src/learn_executor.cpp
new file mode 100644
index 0000000..7883da9
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_executor_cpp/src/learn_executor.cpp
@@ -0,0 +1,62 @@
+#include "example_interfaces/srv/add_two_ints.hpp"
+#include "rclcpp/rclcpp.hpp"
+#include "std_msgs/msg/string.hpp"
+#include
+
+class LearnExecutorNode : public rclcpp::Node {
+public:
+ LearnExecutorNode() : Node("learn_executor") {
+ publisher_ =
+ this->create_publisher("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(
+ "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
+ request,
+ std::shared_ptr 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::SharedPtr publisher_;
+ rclcpp::TimerBase::SharedPtr timer_;
+ rclcpp::Service::SharedPtr service_;
+ rclcpp::CallbackGroup::SharedPtr service_callback_group_;
+};
+
+int main(int argc, char *argv[]) {
+ rclcpp::init(argc, argv);
+ auto node = std::make_shared();
+// auto executor = rclcpp::executors::SingleThreadedExecutor();
+ auto executor = rclcpp::executors::MultiThreadedExecutor();
+ executor.add_node(node);
+ executor.spin();
+ rclcpp::shutdown();
+ return 0;
+}
\ No newline at end of file
diff --git a/chapt10/chapt10_ws/src/learn_executor_py/learn_executor_py/__init__.py b/chapt10/chapt10_ws/src/learn_executor_py/learn_executor_py/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/chapt10/chapt10_ws/src/learn_executor_py/learn_executor_py/learn_executor.py b/chapt10/chapt10_ws/src/learn_executor_py/learn_executor_py/learn_executor.py
new file mode 100644
index 0000000..2b903bf
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_executor_py/learn_executor_py/learn_executor.py
@@ -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()
\ No newline at end of file
diff --git a/chapt10/chapt10_ws/src/learn_executor_py/package.xml b/chapt10/chapt10_ws/src/learn_executor_py/package.xml
new file mode 100644
index 0000000..fc2e166
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_executor_py/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ learn_executor_py
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_copyright
+ ament_flake8
+ ament_pep257
+ python3-pytest
+
+
+ ament_python
+
+
diff --git a/chapt10/chapt10_ws/src/learn_executor_py/resource/learn_executor_py b/chapt10/chapt10_ws/src/learn_executor_py/resource/learn_executor_py
new file mode 100644
index 0000000..e69de29
diff --git a/chapt10/chapt10_ws/src/learn_executor_py/setup.cfg b/chapt10/chapt10_ws/src/learn_executor_py/setup.cfg
new file mode 100644
index 0000000..3c8ff06
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_executor_py/setup.cfg
@@ -0,0 +1,4 @@
+[develop]
+script_dir=$base/lib/learn_executor_py
+[install]
+install_scripts=$base/lib/learn_executor_py
diff --git a/chapt10/chapt10_ws/src/learn_executor_py/setup.py b/chapt10/chapt10_ws/src/learn_executor_py/setup.py
new file mode 100644
index 0000000..97702ea
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_executor_py/setup.py
@@ -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'
+ ],
+ },
+)
diff --git a/chapt10/chapt10_ws/src/learn_executor_py/test/test_copyright.py b/chapt10/chapt10_ws/src/learn_executor_py/test/test_copyright.py
new file mode 100644
index 0000000..97a3919
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_executor_py/test/test_copyright.py
@@ -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'
diff --git a/chapt10/chapt10_ws/src/learn_executor_py/test/test_flake8.py b/chapt10/chapt10_ws/src/learn_executor_py/test/test_flake8.py
new file mode 100644
index 0000000..27ee107
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_executor_py/test/test_flake8.py
@@ -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)
diff --git a/chapt10/chapt10_ws/src/learn_executor_py/test/test_pep257.py b/chapt10/chapt10_ws/src/learn_executor_py/test/test_pep257.py
new file mode 100644
index 0000000..b234a38
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_executor_py/test/test_pep257.py
@@ -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'
diff --git a/chapt10/chapt10_ws/src/learn_lifecyclenode_cpp/CMakeLists.txt b/chapt10/chapt10_ws/src/learn_lifecyclenode_cpp/CMakeLists.txt
new file mode 100644
index 0000000..b7d6530
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_lifecyclenode_cpp/CMakeLists.txt
@@ -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()
diff --git a/chapt10/chapt10_ws/src/learn_lifecyclenode_cpp/package.xml b/chapt10/chapt10_ws/src/learn_lifecyclenode_cpp/package.xml
new file mode 100644
index 0000000..7b39c6c
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_lifecyclenode_cpp/package.xml
@@ -0,0 +1,21 @@
+
+
+
+ learn_lifecyclenode_cpp
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_cmake
+
+ rclcpp
+ rclcpp_lifecycle
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+
diff --git a/chapt10/chapt10_ws/src/learn_lifecyclenode_cpp/src/learn_lifecyclenode.cpp b/chapt10/chapt10_ws/src/learn_lifecyclenode_cpp/src/learn_lifecyclenode.cpp
new file mode 100644
index 0000000..59f2c2f
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_lifecyclenode_cpp/src/learn_lifecyclenode.cpp
@@ -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(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();
+ rclcpp::spin(node->get_node_base_interface());
+ rclcpp::shutdown();
+ return 0;
+}
\ No newline at end of file
diff --git a/chapt10/chapt10_ws/src/learn_lifecyclenode_py/learn_lifecyclenode_py/__init__.py b/chapt10/chapt10_ws/src/learn_lifecyclenode_py/learn_lifecyclenode_py/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/chapt10/chapt10_ws/src/learn_lifecyclenode_py/learn_lifecyclenode_py/learn_lifecyclenode.py b/chapt10/chapt10_ws/src/learn_lifecyclenode_py/learn_lifecyclenode_py/learn_lifecyclenode.py
new file mode 100644
index 0000000..dde44c6
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_lifecyclenode_py/learn_lifecyclenode_py/learn_lifecyclenode.py
@@ -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()
\ No newline at end of file
diff --git a/chapt10/chapt10_ws/src/learn_lifecyclenode_py/package.xml b/chapt10/chapt10_ws/src/learn_lifecyclenode_py/package.xml
new file mode 100644
index 0000000..d2d15a4
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_lifecyclenode_py/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ learn_lifecyclenode_py
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_copyright
+ ament_flake8
+ ament_pep257
+ python3-pytest
+
+
+ ament_python
+
+
diff --git a/chapt10/chapt10_ws/src/learn_lifecyclenode_py/resource/learn_lifecyclenode_py b/chapt10/chapt10_ws/src/learn_lifecyclenode_py/resource/learn_lifecyclenode_py
new file mode 100644
index 0000000..e69de29
diff --git a/chapt10/chapt10_ws/src/learn_lifecyclenode_py/setup.cfg b/chapt10/chapt10_ws/src/learn_lifecyclenode_py/setup.cfg
new file mode 100644
index 0000000..fc53b44
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_lifecyclenode_py/setup.cfg
@@ -0,0 +1,4 @@
+[develop]
+script_dir=$base/lib/learn_lifecyclenode_py
+[install]
+install_scripts=$base/lib/learn_lifecyclenode_py
diff --git a/chapt10/chapt10_ws/src/learn_lifecyclenode_py/setup.py b/chapt10/chapt10_ws/src/learn_lifecyclenode_py/setup.py
new file mode 100644
index 0000000..a6b35ca
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_lifecyclenode_py/setup.py
@@ -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'
+ ],
+ },
+)
diff --git a/chapt10/chapt10_ws/src/learn_lifecyclenode_py/test/test_copyright.py b/chapt10/chapt10_ws/src/learn_lifecyclenode_py/test/test_copyright.py
new file mode 100644
index 0000000..97a3919
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_lifecyclenode_py/test/test_copyright.py
@@ -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'
diff --git a/chapt10/chapt10_ws/src/learn_lifecyclenode_py/test/test_flake8.py b/chapt10/chapt10_ws/src/learn_lifecyclenode_py/test/test_flake8.py
new file mode 100644
index 0000000..27ee107
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_lifecyclenode_py/test/test_flake8.py
@@ -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)
diff --git a/chapt10/chapt10_ws/src/learn_lifecyclenode_py/test/test_pep257.py b/chapt10/chapt10_ws/src/learn_lifecyclenode_py/test/test_pep257.py
new file mode 100644
index 0000000..b234a38
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_lifecyclenode_py/test/test_pep257.py
@@ -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'
diff --git a/chapt10/chapt10_ws/src/learn_message_filter_cpp/CMakeLists.txt b/chapt10/chapt10_ws/src/learn_message_filter_cpp/CMakeLists.txt
new file mode 100644
index 0000000..0b92616
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_message_filter_cpp/CMakeLists.txt
@@ -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()
diff --git a/chapt10/chapt10_ws/src/learn_message_filter_cpp/package.xml b/chapt10/chapt10_ws/src/learn_message_filter_cpp/package.xml
new file mode 100644
index 0000000..e6df4b0
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_message_filter_cpp/package.xml
@@ -0,0 +1,23 @@
+
+
+
+ learn_message_filter_cpp
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_cmake
+
+ rclcpp
+ message_filters
+ nav_msgs
+ sensor_msgs
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+
diff --git a/chapt10/chapt10_ws/src/learn_message_filter_cpp/src/timesync_test.cpp b/chapt10/chapt10_ws/src/learn_message_filter_cpp/src/timesync_test.cpp
new file mode 100644
index 0000000..8e0c9a8
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_message_filter_cpp/src/timesync_test.cpp
@@ -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;
+// 同步策略:大约时间对齐策略
+// using MySyncPolicy = sync_policies::ApproximateTime;
+// 同步策略:最新时间对齐策略
+using MySyncPolicy = sync_policies::LatestTime;
+
+
+class TimeSyncTestNode : public rclcpp::Node {
+public:
+ TimeSyncTestNode() : Node("sync_node") {
+ // 1.订阅 imu 话题并注册回调并打印时间戳
+ imu_sub_ = std::make_shared>(this, "imu");
+ imu_sub_->registerCallback(
+ [&](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>(this, "odom");
+ odom_sub_->registerCallback(
+ [&](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>(
+ // MySyncPolicy(10), *imu_sub_, *odom_sub_);
+
+ // 3.创建对应策略的同步器同步两个话题,并注册回调函数打印数据
+ synchronizer_ = std::make_shared>(
+ 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> imu_sub_;
+ std::shared_ptr> odom_sub_;
+ std::shared_ptr> synchronizer_;
+};
+
+int main(int argc, char **argv) {
+ rclcpp::init(argc, argv);
+ auto node = std::make_shared();
+ rclcpp::spin(node);
+ rclcpp::shutdown();
+ return 0;
+}
\ No newline at end of file
diff --git a/chapt10/chapt10_ws/src/learn_message_filter_py/learn_message_filter_py/__init__.py b/chapt10/chapt10_ws/src/learn_message_filter_py/learn_message_filter_py/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/chapt10/chapt10_ws/src/learn_message_filter_py/learn_message_filter_py/timesync_test.py b/chapt10/chapt10_ws/src/learn_message_filter_py/learn_message_filter_py/timesync_test.py
new file mode 100644
index 0000000..0d197cc
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_message_filter_py/learn_message_filter_py/timesync_test.py
@@ -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()
\ No newline at end of file
diff --git a/chapt10/chapt10_ws/src/learn_message_filter_py/package.xml b/chapt10/chapt10_ws/src/learn_message_filter_py/package.xml
new file mode 100644
index 0000000..3c35501
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_message_filter_py/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ learn_message_filter_py
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_copyright
+ ament_flake8
+ ament_pep257
+ python3-pytest
+
+
+ ament_python
+
+
diff --git a/chapt10/chapt10_ws/src/learn_message_filter_py/resource/learn_message_filter_py b/chapt10/chapt10_ws/src/learn_message_filter_py/resource/learn_message_filter_py
new file mode 100644
index 0000000..e69de29
diff --git a/chapt10/chapt10_ws/src/learn_message_filter_py/setup.cfg b/chapt10/chapt10_ws/src/learn_message_filter_py/setup.cfg
new file mode 100644
index 0000000..66a92c6
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_message_filter_py/setup.cfg
@@ -0,0 +1,4 @@
+[develop]
+script_dir=$base/lib/learn_message_filter_py
+[install]
+install_scripts=$base/lib/learn_message_filter_py
diff --git a/chapt10/chapt10_ws/src/learn_message_filter_py/setup.py b/chapt10/chapt10_ws/src/learn_message_filter_py/setup.py
new file mode 100644
index 0000000..8b4e350
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_message_filter_py/setup.py
@@ -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'
+ ],
+ },
+)
diff --git a/chapt10/chapt10_ws/src/learn_message_filter_py/test/test_copyright.py b/chapt10/chapt10_ws/src/learn_message_filter_py/test/test_copyright.py
new file mode 100644
index 0000000..97a3919
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_message_filter_py/test/test_copyright.py
@@ -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'
diff --git a/chapt10/chapt10_ws/src/learn_message_filter_py/test/test_flake8.py b/chapt10/chapt10_ws/src/learn_message_filter_py/test/test_flake8.py
new file mode 100644
index 0000000..27ee107
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_message_filter_py/test/test_flake8.py
@@ -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)
diff --git a/chapt10/chapt10_ws/src/learn_message_filter_py/test/test_pep257.py b/chapt10/chapt10_ws/src/learn_message_filter_py/test/test_pep257.py
new file mode 100644
index 0000000..b234a38
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_message_filter_py/test/test_pep257.py
@@ -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'
diff --git a/chapt10/chapt10_ws/src/learn_qos_cpp/CMakeLists.txt b/chapt10/chapt10_ws/src/learn_qos_cpp/CMakeLists.txt
new file mode 100644
index 0000000..700b9e2
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_qos_cpp/CMakeLists.txt
@@ -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( 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()
diff --git a/chapt10/chapt10_ws/src/learn_qos_cpp/package.xml b/chapt10/chapt10_ws/src/learn_qos_cpp/package.xml
new file mode 100644
index 0000000..60c3fa0
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_qos_cpp/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ learn_qos_cpp
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_cmake
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+
diff --git a/chapt10/chapt10_ws/src/learn_qos_cpp/src/reliability_test.cpp b/chapt10/chapt10_ws/src/learn_qos_cpp/src/reliability_test.cpp
new file mode 100644
index 0000000..b89f21b
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_qos_cpp/src/reliability_test.cpp
@@ -0,0 +1,56 @@
+#include
+#include
+
+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(
+ "odom", qos_profile);
+
+ // // 创建发布者并设置QoS为sensor
+ // odom_publisher_ = this->create_publisher(
+ // "odom", rclcpp::SensorDataQoS());
+
+ // 创建订阅者(默认QoS配置)队列深度设置为 5
+ // odom_subscription_ = this->create_subscription(
+ // "odom", 5,
+ // [this](const nav_msgs::msg::Odometry::SharedPtr msg) {
+ // (void)msg;
+ // RCLCPP_INFO(this->get_logger(), "收到里程计消息");
+ // });
+
+ odom_subscription_ = this->create_subscription(
+ "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::SharedPtr odom_publisher_;
+ rclcpp::Subscription::SharedPtr odom_subscription_;
+ rclcpp::TimerBase::SharedPtr timer_;
+};
+
+int main(int argc, char *argv[])
+{
+ rclcpp::init(argc, argv);
+ auto odom_node = std::make_shared();
+ rclcpp::spin(odom_node);
+ rclcpp::shutdown();
+ return 0;
+}
\ No newline at end of file
diff --git a/chapt10/chapt10_ws/src/learn_qos_py/learn_qos_py/__init__.py b/chapt10/chapt10_ws/src/learn_qos_py/learn_qos_py/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/chapt10/chapt10_ws/src/learn_qos_py/learn_qos_py/reliability_test.py b/chapt10/chapt10_ws/src/learn_qos_py/learn_qos_py/reliability_test.py
new file mode 100644
index 0000000..888daa6
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_qos_py/learn_qos_py/reliability_test.py
@@ -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()
\ No newline at end of file
diff --git a/chapt10/chapt10_ws/src/learn_qos_py/package.xml b/chapt10/chapt10_ws/src/learn_qos_py/package.xml
new file mode 100644
index 0000000..fe21c03
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_qos_py/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ learn_qos_py
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_copyright
+ ament_flake8
+ ament_pep257
+ python3-pytest
+
+
+ ament_python
+
+
diff --git a/chapt10/chapt10_ws/src/learn_qos_py/resource/learn_qos_py b/chapt10/chapt10_ws/src/learn_qos_py/resource/learn_qos_py
new file mode 100644
index 0000000..e69de29
diff --git a/chapt10/chapt10_ws/src/learn_qos_py/setup.cfg b/chapt10/chapt10_ws/src/learn_qos_py/setup.cfg
new file mode 100644
index 0000000..6bb3d28
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_qos_py/setup.cfg
@@ -0,0 +1,4 @@
+[develop]
+script_dir=$base/lib/learn_qos_py
+[install]
+install_scripts=$base/lib/learn_qos_py
diff --git a/chapt10/chapt10_ws/src/learn_qos_py/setup.py b/chapt10/chapt10_ws/src/learn_qos_py/setup.py
new file mode 100644
index 0000000..bb3aa60
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_qos_py/setup.py
@@ -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'
+ ],
+ },
+)
diff --git a/chapt10/chapt10_ws/src/learn_qos_py/test/test_copyright.py b/chapt10/chapt10_ws/src/learn_qos_py/test/test_copyright.py
new file mode 100644
index 0000000..97a3919
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_qos_py/test/test_copyright.py
@@ -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'
diff --git a/chapt10/chapt10_ws/src/learn_qos_py/test/test_flake8.py b/chapt10/chapt10_ws/src/learn_qos_py/test/test_flake8.py
new file mode 100644
index 0000000..27ee107
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_qos_py/test/test_flake8.py
@@ -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)
diff --git a/chapt10/chapt10_ws/src/learn_qos_py/test/test_pep257.py b/chapt10/chapt10_ws/src/learn_qos_py/test/test_pep257.py
new file mode 100644
index 0000000..b234a38
--- /dev/null
+++ b/chapt10/chapt10_ws/src/learn_qos_py/test/test_pep257.py
@@ -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'
diff --git a/chapt10/chapt10_ws/topic_sub_limit.xml b/chapt10/chapt10_ws/topic_sub_limit.xml
new file mode 100644
index 0000000..a19be44
--- /dev/null
+++ b/chapt10/chapt10_ws/topic_sub_limit.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+ DYNAMIC
+
+
+
+
+ DYNAMIC
+
+
+
+
+ DYNAMIC
+
+ 0
+ 1
+ 1
+
+
+
\ No newline at end of file
diff --git a/chapt10/rosbag2_message_filter/metadata.yaml b/chapt10/rosbag2_message_filter/metadata.yaml
new file mode 100644
index 0000000..3bb95c9
--- /dev/null
+++ b/chapt10/rosbag2_message_filter/metadata.yaml
@@ -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
\ No newline at end of file
diff --git a/chapt10/rosbag2_message_filter/rosbag2_message_filter.db3 b/chapt10/rosbag2_message_filter/rosbag2_message_filter.db3
new file mode 100644
index 0000000..7629fa8
Binary files /dev/null and b/chapt10/rosbag2_message_filter/rosbag2_message_filter.db3 differ
diff --git a/chapt7/chapt7_ws/README.md b/chapt7/chapt7_ws/README.md
new file mode 100644
index 0000000..ec1ddee
--- /dev/null
+++ b/chapt7/chapt7_ws/README.md
@@ -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)
\ No newline at end of file
diff --git a/chapt7/chapt7_ws/src/autopatrol_interfaces/CMakeLists.txt b/chapt7/chapt7_ws/src/autopatrol_interfaces/CMakeLists.txt
new file mode 100644
index 0000000..b2ed6b4
--- /dev/null
+++ b/chapt7/chapt7_ws/src/autopatrol_interfaces/CMakeLists.txt
@@ -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( 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()
diff --git a/chapt7/chapt7_ws/src/autopatrol_interfaces/package.xml b/chapt7/chapt7_ws/src/autopatrol_interfaces/package.xml
new file mode 100644
index 0000000..ccdf647
--- /dev/null
+++ b/chapt7/chapt7_ws/src/autopatrol_interfaces/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ autopatrol_interfaces
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_cmake
+
+ ament_lint_auto
+ ament_lint_common
+ rosidl_interface_packages
+
+ ament_cmake
+
+
diff --git a/chapt7/chapt7_ws/src/autopatrol_interfaces/srv/SpeachText.srv b/chapt7/chapt7_ws/src/autopatrol_interfaces/srv/SpeachText.srv
new file mode 100644
index 0000000..440fa28
--- /dev/null
+++ b/chapt7/chapt7_ws/src/autopatrol_interfaces/srv/SpeachText.srv
@@ -0,0 +1,3 @@
+string text # 合成文字
+---
+bool result # 合成结果
\ No newline at end of file
diff --git a/chapt7/chapt7_ws/src/autopatrol_robot/autopatrol_robot/__init__.py b/chapt7/chapt7_ws/src/autopatrol_robot/autopatrol_robot/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/chapt7/chapt7_ws/src/autopatrol_robot/autopatrol_robot/patrol_node.py b/chapt7/chapt7_ws/src/autopatrol_robot/autopatrol_robot/patrol_node.py
new file mode 100644
index 0000000..e1e3f1c
--- /dev/null
+++ b/chapt7/chapt7_ws/src/autopatrol_robot/autopatrol_robot/patrol_node.py
@@ -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()
\ No newline at end of file
diff --git a/chapt7/chapt7_ws/src/autopatrol_robot/autopatrol_robot/speaker.py b/chapt7/chapt7_ws/src/autopatrol_robot/autopatrol_robot/speaker.py
new file mode 100644
index 0000000..32e7269
--- /dev/null
+++ b/chapt7/chapt7_ws/src/autopatrol_robot/autopatrol_robot/speaker.py
@@ -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()
\ No newline at end of file
diff --git a/chapt7/chapt7_ws/src/autopatrol_robot/config/patrol_config.yaml b/chapt7/chapt7_ws/src/autopatrol_robot/config/patrol_config.yaml
new file mode 100644
index 0000000..cfef5b3
--- /dev/null
+++ b/chapt7/chapt7_ws/src/autopatrol_robot/config/patrol_config.yaml
@@ -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,
+ ]
\ No newline at end of file
diff --git a/chapt7/chapt7_ws/src/autopatrol_robot/launch/autopatrol.launch.py b/chapt7/chapt7_ws/src/autopatrol_robot/launch/autopatrol.launch.py
new file mode 100644
index 0000000..819362b
--- /dev/null
+++ b/chapt7/chapt7_ws/src/autopatrol_robot/launch/autopatrol.launch.py
@@ -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,
+ ])
\ No newline at end of file
diff --git a/chapt7/chapt7_ws/src/autopatrol_robot/package.xml b/chapt7/chapt7_ws/src/autopatrol_robot/package.xml
new file mode 100644
index 0000000..aa36510
--- /dev/null
+++ b/chapt7/chapt7_ws/src/autopatrol_robot/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ autopatrol_robot
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_copyright
+ ament_flake8
+ ament_pep257
+ python3-pytest
+
+
+ ament_python
+
+
diff --git a/chapt7/chapt7_ws/src/autopatrol_robot/resource/autopatrol_robot b/chapt7/chapt7_ws/src/autopatrol_robot/resource/autopatrol_robot
new file mode 100644
index 0000000..e69de29
diff --git a/chapt7/chapt7_ws/src/autopatrol_robot/setup.cfg b/chapt7/chapt7_ws/src/autopatrol_robot/setup.cfg
new file mode 100644
index 0000000..60e9700
--- /dev/null
+++ b/chapt7/chapt7_ws/src/autopatrol_robot/setup.cfg
@@ -0,0 +1,4 @@
+[develop]
+script_dir=$base/lib/autopatrol_robot
+[install]
+install_scripts=$base/lib/autopatrol_robot
diff --git a/chapt7/chapt7_ws/src/autopatrol_robot/setup.py b/chapt7/chapt7_ws/src/autopatrol_robot/setup.py
new file mode 100644
index 0000000..8b889e3
--- /dev/null
+++ b/chapt7/chapt7_ws/src/autopatrol_robot/setup.py
@@ -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',
+ ],
+ },
+)
diff --git a/chapt7/chapt7_ws/src/autopatrol_robot/test/test_copyright.py b/chapt7/chapt7_ws/src/autopatrol_robot/test/test_copyright.py
new file mode 100644
index 0000000..97a3919
--- /dev/null
+++ b/chapt7/chapt7_ws/src/autopatrol_robot/test/test_copyright.py
@@ -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'
diff --git a/chapt7/chapt7_ws/src/autopatrol_robot/test/test_flake8.py b/chapt7/chapt7_ws/src/autopatrol_robot/test/test_flake8.py
new file mode 100644
index 0000000..27ee107
--- /dev/null
+++ b/chapt7/chapt7_ws/src/autopatrol_robot/test/test_flake8.py
@@ -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)
diff --git a/chapt7/chapt7_ws/src/autopatrol_robot/test/test_pep257.py b/chapt7/chapt7_ws/src/autopatrol_robot/test/test_pep257.py
new file mode 100644
index 0000000..b234a38
--- /dev/null
+++ b/chapt7/chapt7_ws/src/autopatrol_robot/test/test_pep257.py
@@ -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'
diff --git a/chapt7/chapt7_ws/src/fishbot_application/fishbot_application/__init__.py b/chapt7/chapt7_ws/src/fishbot_application/fishbot_application/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/chapt7/chapt7_ws/src/fishbot_application/fishbot_application/get_robot_pose.py b/chapt7/chapt7_ws/src/fishbot_application/fishbot_application/get_robot_pose.py
new file mode 100644
index 0000000..fddcd8e
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_application/fishbot_application/get_robot_pose.py
@@ -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()
diff --git a/chapt7/chapt7_ws/src/fishbot_application/fishbot_application/init_robot_pose.py b/chapt7/chapt7_ws/src/fishbot_application/fishbot_application/init_robot_pose.py
new file mode 100644
index 0000000..8ed9029
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_application/fishbot_application/init_robot_pose.py
@@ -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()
diff --git a/chapt7/chapt7_ws/src/fishbot_application/fishbot_application/nav_to_pose.py b/chapt7/chapt7_ws/src/fishbot_application/fishbot_application/nav_to_pose.py
new file mode 100644
index 0000000..45ff081
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_application/fishbot_application/nav_to_pose.py
@@ -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()
\ No newline at end of file
diff --git a/chapt7/chapt7_ws/src/fishbot_application/fishbot_application/waypoint_follower.py b/chapt7/chapt7_ws/src/fishbot_application/fishbot_application/waypoint_follower.py
new file mode 100644
index 0000000..07e31f9
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_application/fishbot_application/waypoint_follower.py
@@ -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()
diff --git a/chapt7/chapt7_ws/src/fishbot_application/package.xml b/chapt7/chapt7_ws/src/fishbot_application/package.xml
new file mode 100644
index 0000000..8074680
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_application/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ fishbot_application
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_copyright
+ ament_flake8
+ ament_pep257
+ python3-pytest
+
+
+ ament_python
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_application/resource/fishbot_application b/chapt7/chapt7_ws/src/fishbot_application/resource/fishbot_application
new file mode 100644
index 0000000..e69de29
diff --git a/chapt7/chapt7_ws/src/fishbot_application/setup.cfg b/chapt7/chapt7_ws/src/fishbot_application/setup.cfg
new file mode 100644
index 0000000..b995d8e
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_application/setup.cfg
@@ -0,0 +1,4 @@
+[develop]
+script_dir=$base/lib/fishbot_application
+[install]
+install_scripts=$base/lib/fishbot_application
diff --git a/chapt7/chapt7_ws/src/fishbot_application/setup.py b/chapt7/chapt7_ws/src/fishbot_application/setup.py
new file mode 100644
index 0000000..98ebc51
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_application/setup.py
@@ -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',
+ ],
+ },
+)
diff --git a/chapt7/chapt7_ws/src/fishbot_application/test/test_copyright.py b/chapt7/chapt7_ws/src/fishbot_application/test/test_copyright.py
new file mode 100644
index 0000000..97a3919
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_application/test/test_copyright.py
@@ -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'
diff --git a/chapt7/chapt7_ws/src/fishbot_application/test/test_flake8.py b/chapt7/chapt7_ws/src/fishbot_application/test/test_flake8.py
new file mode 100644
index 0000000..27ee107
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_application/test/test_flake8.py
@@ -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)
diff --git a/chapt7/chapt7_ws/src/fishbot_application/test/test_pep257.py b/chapt7/chapt7_ws/src/fishbot_application/test/test_pep257.py
new file mode 100644
index 0000000..b234a38
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_application/test/test_pep257.py
@@ -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'
diff --git a/chapt7/chapt7_ws/src/fishbot_application_cpp/CMakeLists.txt b/chapt7/chapt7_ws/src/fishbot_application_cpp/CMakeLists.txt
new file mode 100644
index 0000000..f6075f8
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_application_cpp/CMakeLists.txt
@@ -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()
diff --git a/chapt7/chapt7_ws/src/fishbot_application_cpp/package.xml b/chapt7/chapt7_ws/src/fishbot_application_cpp/package.xml
new file mode 100644
index 0000000..efe88a5
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_application_cpp/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ fishbot_application_cpp
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_cmake
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_application_cpp/src/nav2pose.cpp b/chapt7/chapt7_ws/src/fishbot_application_cpp/src/nav2pose.cpp
new file mode 100644
index 0000000..a8e3383
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_application_cpp/src/nav2pose.cpp
@@ -0,0 +1,71 @@
+#include
+
+#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; // 定义导航动作客户端类型
+ using NavigationActionGoalHandle =
+ rclcpp_action::ClientGoalHandle; // 定义导航动作目标句柄类型
+
+ NavToPoseClient() : Node("nav_to_pose_client") {
+ // 创建导航动作客户端
+ action_client_ = rclcpp_action::create_client(
+ 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::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 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();
+ node->sendGoal();
+ rclcpp::spin(node);
+ rclcpp::shutdown();
+ return 0;
+}
diff --git a/chapt7/chapt7_ws/src/fishbot_description/CMakeLists.txt b/chapt7/chapt7_ws/src/fishbot_description/CMakeLists.txt
new file mode 100644
index 0000000..2a82fdf
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/CMakeLists.txt
@@ -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( 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()
diff --git a/chapt7/chapt7_ws/src/fishbot_description/LICENSE b/chapt7/chapt7_ws/src/fishbot_description/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/LICENSE
@@ -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.
diff --git a/chapt7/chapt7_ws/src/fishbot_description/config/fishbot_ros2_controller.yaml b/chapt7/chapt7_ws/src/fishbot_description/config/fishbot_ros2_controller.yaml
new file mode 100644
index 0000000..8a60aeb
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/config/fishbot_ros2_controller.yaml
@@ -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
\ No newline at end of file
diff --git a/chapt7/chapt7_ws/src/fishbot_description/config/rviz/dispaly_model.rviz b/chapt7/chapt7_ws/src/fishbot_description/config/rviz/dispaly_model.rviz
new file mode 100644
index 0000000..d624b06
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/config/rviz/dispaly_model.rviz
@@ -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:
+ 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:
+ 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
diff --git a/chapt7/chapt7_ws/src/fishbot_description/launch/display_robot.launch.py b/chapt7/chapt7_ws/src/fishbot_description/launch/display_robot.launch.py
new file mode 100644
index 0000000..3db487d
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/launch/display_robot.launch.py
@@ -0,0 +1,42 @@
+import launch
+import launch_ros
+from ament_index_python.packages import get_package_share_directory
+
+
+def generate_launch_description():
+ # 获取默认路径
+ urdf_tutorial_path = get_package_share_directory('fishbot_description')
+ default_model_path = urdf_tutorial_path + '/urdf/first_robot.urdf'
+ default_rviz_config_path = urdf_tutorial_path + '/config/rviz/display_model.rviz'
+ # 为 Launch 声明参数
+ action_declare_arg_mode_path = launch.actions.DeclareLaunchArgument(
+ name='model', default_value=str(default_model_path),
+ description='URDF 的绝对路径')
+ # 获取文件内容生成新的参数
+ robot_description = launch_ros.parameter_descriptions.ParameterValue(
+ launch.substitutions.Command(
+ ['xacro ', launch.substitutions.LaunchConfiguration('model')]),
+ value_type=str)
+ # 状态发布节点
+ robot_state_publisher_node = launch_ros.actions.Node(
+ package='robot_state_publisher',
+ executable='robot_state_publisher',
+ parameters=[{'robot_description': robot_description}]
+ )
+ # 关节状态发布节点
+ joint_state_publisher_node = launch_ros.actions.Node(
+ package='joint_state_publisher',
+ executable='joint_state_publisher',
+ )
+ # RViz 节点
+ rviz_node = launch_ros.actions.Node(
+ package='rviz2',
+ executable='rviz2',
+ arguments=['-d', default_rviz_config_path]
+ )
+ return launch.LaunchDescription([
+ action_declare_arg_mode_path,
+ joint_state_publisher_node,
+ robot_state_publisher_node,
+ rviz_node
+ ])
diff --git a/chapt7/chapt7_ws/src/fishbot_description/launch/gazebo_sim.launch.py b/chapt7/chapt7_ws/src/fishbot_description/launch/gazebo_sim.launch.py
new file mode 100644
index 0000000..16050ca
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/launch/gazebo_sim.launch.py
@@ -0,0 +1,75 @@
+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():
+ # 获取默认路径
+ robot_name_in_model = "fishbot"
+ urdf_tutorial_path = get_package_share_directory('fishbot_description')
+ default_model_path = urdf_tutorial_path + '/urdf/fishbot/fishbot.urdf.xacro'
+ default_world_path = urdf_tutorial_path + '/world/custom_room.world'
+ # 为 Launch 声明参数
+ action_declare_arg_mode_path = launch.actions.DeclareLaunchArgument(
+ name='model', default_value=str(default_model_path),
+ description='URDF 的绝对路径')
+ # 获取文件内容生成新的参数
+ robot_description = launch_ros.parameter_descriptions.ParameterValue(
+ launch.substitutions.Command(
+ ['xacro ', launch.substitutions.LaunchConfiguration('model')]),
+ value_type=str)
+
+ robot_state_publisher_node = launch_ros.actions.Node(
+ package='robot_state_publisher',
+ executable='robot_state_publisher',
+ parameters=[{'robot_description': robot_description}]
+ )
+
+ # 通过 IncludeLaunchDescription 包含另外一个 launch 文件
+ launch_gazebo = launch.actions.IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([get_package_share_directory(
+ 'gazebo_ros'), '/launch', '/gazebo.launch.py']),
+ # 传递参数
+ launch_arguments=[('world', default_world_path),('verbose','true')]
+ )
+ # 请求 Gazebo 加载机器人
+ spawn_entity_node = launch_ros.actions.Node(
+ package='gazebo_ros',
+ executable='spawn_entity.py',
+ arguments=['-topic', '/robot_description',
+ '-entity', robot_name_in_model, ])
+
+ # 加载并激活 fishbot_joint_state_broadcaster 控制器
+ load_joint_state_controller = launch.actions.ExecuteProcess(
+ cmd=['ros2', 'control', 'load_controller', '--set-state', 'active',
+ 'fishbot_joint_state_broadcaster'],
+ output='screen'
+ )
+
+ # 加载并激活 fishbot_effort_controller 控制器
+ load_fishbot_effort_controller = launch.actions.ExecuteProcess(
+ cmd=['ros2', 'control', 'load_controller', '--set-state', 'active','fishbot_effort_controller'],
+ output='screen')
+
+ load_fishbot_diff_drive_controller = launch.actions.ExecuteProcess(
+ cmd=['ros2', 'control', 'load_controller', '--set-state', 'active','fishbot_diff_drive_controller'],
+ output='screen')
+
+ return launch.LaunchDescription([
+ action_declare_arg_mode_path,
+ robot_state_publisher_node,
+ launch_gazebo,
+ spawn_entity_node,
+ # 事件动作,当加载机器人结束后执行
+ launch.actions.RegisterEventHandler(
+ event_handler=launch.event_handlers.OnProcessExit(
+ target_action=spawn_entity_node,
+ on_exit=[load_joint_state_controller],)
+ ),
+ # 事件动作,load_fishbot_diff_drive_controller
+ launch.actions.RegisterEventHandler(
+ event_handler=launch.event_handlers.OnProcessExit(
+ target_action=load_joint_state_controller,
+ on_exit=[load_fishbot_diff_drive_controller],)
+ ),
+ ])
diff --git a/chapt7/chapt7_ws/src/fishbot_description/package.xml b/chapt7/chapt7_ws/src/fishbot_description/package.xml
new file mode 100644
index 0000000..575ade0
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ fishbot_description
+ 0.0.0
+ TODO: Package description
+ mzebra
+ Apache-2.0
+
+ ament_cmake
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_description/urdf/first_robot.gv b/chapt7/chapt7_ws/src/fishbot_description/urdf/first_robot.gv
new file mode 100644
index 0000000..6d190c9
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/urdf/first_robot.gv
@@ -0,0 +1,8 @@
+digraph G {
+node [shape=box];
+"base_link" [label="base_link"];
+"imu_link" [label="imu_link"];
+node [shape=ellipse, color=blue, fontcolor=blue];
+"base_link" -> "imu_joint" [label="xyz: 0 0 0.03 \nrpy: 0 -0 0"]
+"imu_joint" -> "imu_link"
+}
diff --git a/chapt7/chapt7_ws/src/fishbot_description/urdf/first_robot.pdf b/chapt7/chapt7_ws/src/fishbot_description/urdf/first_robot.pdf
new file mode 100644
index 0000000..a0a0955
Binary files /dev/null and b/chapt7/chapt7_ws/src/fishbot_description/urdf/first_robot.pdf differ
diff --git a/chapt7/chapt7_ws/src/fishbot_description/urdf/first_robot.urdf b/chapt7/chapt7_ws/src/fishbot_description/urdf/first_robot.urdf
new file mode 100644
index 0000000..5f706eb
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/urdf/first_robot.urdf
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_description/urdf/first_robot.urdf.xacro b/chapt7/chapt7_ws/src/fishbot_description/urdf/first_robot.urdf.xacro
new file mode 100644
index 0000000..3a36fe7
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/urdf/first_robot.urdf.xacro
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/actuator/caster.urdf.xacro b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/actuator/caster.urdf.xacro
new file mode 100644
index 0000000..e654233
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/actuator/caster.urdf.xacro
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/actuator/wheel.urdf.xacro b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/actuator/wheel.urdf.xacro
new file mode 100644
index 0000000..6927798
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/actuator/wheel.urdf.xacro
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/base.urdf.xacro b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/base.urdf.xacro
new file mode 100644
index 0000000..9f14ebc
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/base.urdf.xacro
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/common_inertia.xacro b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/common_inertia.xacro
new file mode 100644
index 0000000..8486102
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/common_inertia.xacro
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/fishbot.ros2_control.xacro b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/fishbot.ros2_control.xacro
new file mode 100644
index 0000000..157b5ba
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/fishbot.ros2_control.xacro
@@ -0,0 +1,45 @@
+
+
+
+
+
+ gazebo_ros2_control/GazeboSystem
+
+
+
+ -1
+ 1
+
+
+ -0.1
+ 0.1
+
+
+
+
+
+
+
+ -1
+ 1
+
+
+ -0.1
+ 0.1
+
+
+
+
+
+
+
+
+ $(find fishbot_description)/config/fishbot_ros2_controller.yaml
+
+ /fishbot_diff_drive_controller/cmd_vel_unstamped:=/cmd_vel
+ /fishbot_diff_drive_controller/odom:=/odom
+
+
+
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/fishbot.urdf.xacro b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/fishbot.urdf.xacro
new file mode 100644
index 0000000..77c980b
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/fishbot.urdf.xacro
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/plugins/gazebo_control_plugin.xacro b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/plugins/gazebo_control_plugin.xacro
new file mode 100644
index 0000000..46d898a
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/plugins/gazebo_control_plugin.xacro
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+ /
+ cmd_vel:=cmd_vel
+ odom:=odom
+
+ 30
+
+ left_wheel_joint
+ right_wheel_joint
+
+ 0.2
+ 0.064
+
+ 20
+ 1.0
+
+ true
+ true
+ true
+
+ odom
+ base_footprint
+
+
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/plugins/gazebo_sensor_plugin.xacro b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/plugins/gazebo_sensor_plugin.xacro
new file mode 100644
index 0000000..c69cbb4
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/plugins/gazebo_sensor_plugin.xacro
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+ /
+ ~/out:=scan
+
+ sensor_msgs/LaserScan
+ laser_link
+
+ true
+ true
+ 5
+ 0 0 0 0 0 0
+
+
+
+
+
+ 360
+ 1.000000
+ 0.000000
+ 6.280000
+
+
+
+
+ 0.120000
+ 8.0
+ 0.015000
+
+
+
+ gaussian
+ 0.0
+ 0.01
+
+
+
+
+
+
+
+
+
+
+
+ /
+ ~/out:=imu
+
+ false
+
+ 100
+ true
+
+
+
+
+
+ 0.0
+ 2e-4
+ 0.0000075
+ 0.0000008
+
+
+
+
+ 0.0
+ 2e-4
+ 0.0000075
+ 0.0000008
+
+
+
+
+ 0.0
+ 2e-4
+ 0.0000075
+ 0.0000008
+
+
+
+
+
+
+ 0.0
+ 1.7e-2
+ 0.1
+ 0.001
+
+
+
+
+ 0.0
+ 1.7e-2
+ 0.1
+ 0.001
+
+
+
+
+ 0.0
+ 1.7e-2
+ 0.1
+ 0.001
+
+
+
+
+
+
+
+
+
+
+
+
+
+ camera_optical_link
+
+ true
+ 10
+
+ 1.5009831567
+
+ 800
+ 600
+ R8G8B8
+
+
+ 0.0
+ 0.0
+ 0.0
+ 0.0
+ 0.0
+ 0.5 0.5
+
+
+
+
+
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/sensor/camera.urdf.xacro b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/sensor/camera.urdf.xacro
new file mode 100644
index 0000000..567a95f
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/sensor/camera.urdf.xacro
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/sensor/imu.urdf.xacro b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/sensor/imu.urdf.xacro
new file mode 100644
index 0000000..2d0b45d
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/sensor/imu.urdf.xacro
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/sensor/laser.urdf.xacro b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/sensor/laser.urdf.xacro
new file mode 100644
index 0000000..c9cabb3
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/urdf/fishbot/sensor/laser.urdf.xacro
@@ -0,0 +1,73 @@
+
+
+
+
+
+ Gazebo/Black
+
+
+ Gazebo/Black
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_description/world/custom_room.world b/chapt7/chapt7_ws/src/fishbot_description/world/custom_room.world
new file mode 100644
index 0000000..a341114
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/world/custom_room.world
@@ -0,0 +1,2745 @@
+
+
+
+ 1
+ 0 0 10 0 -0 0
+ 0.8 0.8 0.8 1
+ 0.2 0.2 0.2 1
+
+ 1000
+ 0.9
+ 0.01
+ 0.001
+
+ -0.5 0.1 -0.9
+
+ 0
+ 0
+ 0
+
+
+
+ 1
+
+
+
+
+ 0 0 1
+ 100 100
+
+
+
+
+
+ 100
+ 50
+
+
+
+
+
+
+
+
+
+
+ 10
+
+
+ 0
+
+
+ 0 0 1
+ 100 100
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+ 0 0 -9.8
+ 6e-06 2.3e-05 -4.2e-05
+
+
+ 0.001
+ 1
+ 1000
+
+
+ 0.4 0.4 0.4 1
+ 0.7 0.7 0.7 1
+ 1
+
+
+
+
+ EARTH_WGS84
+ 0
+ 0
+ 0
+ 0
+
+
+ 1435 337000000
+ 1463 259419335
+ 1684421669 225276651
+ 1435337
+
+ 7.91236 4.68506 0 0 -0 0
+ 1 1 1
+
+ 7.91236 4.68506 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ -4.31975 4.20897 0 0 -0 0
+ 1 1 1
+
+ -4.31975 4.20897 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ -5.63889 4.25649 0 0 -0 0
+ 1 1 1
+
+ -5.63889 4.25649 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ -0.201253 -3.92041 0 0 -0 0
+ 1 1 1
+
+ -0.201253 -3.92041 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ -0.211824 -5.1227 0 0 -0 0
+ 1 1 1
+
+ -0.211824 -5.1227 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ -8.8359 -3.13197 0 0 -0 0
+ 1 1 1
+
+ -8.8359 -3.13197 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ -7.80202 -3.07286 0 0 -0 0
+ 1 1 1
+
+ -7.80202 -3.07286 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ -4.89593 2.8545 0 0 -0 0
+ 1 1 1
+
+ -4.89593 2.8545 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ 3.54156 1.36869 0 0 -0 0
+ 1 1 1
+
+ 3.54156 1.36869 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ 4.46016 1.36814 0 0 -0 0
+ 1 1 1
+
+ 4.46016 1.36814 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ 0 0 0 0 -0 0
+ 1 1 1
+
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ -0.972666 -0.903877 0 0 -0 0
+ 1 1 1
+
+ -0.958745 4.60567 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+ 8.46625 -0.944332 0 0 0 -1.5708
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+ -0.958745 -6.49434 0 0 -0 3.14159
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+ -10.3837 -0.944332 0 0 -0 1.5708
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+ -2.09036 2.38658 0 0 0 -1.5708
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+ -5.39036 0.086579 0 0 -0 3.14159
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+ -0.580882 -4.77187 0 0 -0 1.5708
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+ 3.09411 -3.14872 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+ -7.36159 -2.71357 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+ -4.31159 -4.01357 0 0 0 -1.5708
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ -0.958121 2.77839 0.5 0 -0 0
+ 1 1 1
+
+ -0.958121 2.77839 0.5 0 -0 0
+ 0 0 0 0 -0 0
+ -0.004709 -9.78112 9.78158 0.712677 -0.009414 -4.3e-05
+ -0.004709 -9.78112 9.78158 0 -0 0
+
+
+
+ -0.961048 0.717233 0.499993 -3e-06 -4e-06 -0
+ 1 1 1
+
+ -0.961048 0.717233 0.499993 -3e-06 -4e-06 -0
+ 0 0 0 0 -0 0
+ 0 0 -9.8 0 -0 0
+ 0 0 -9.8 0 -0 0
+
+
+
+ 0 0 10 0 -0 0
+
+
+
+ -0.972666 -0.903877 0 0 -0 0
+
+
+
+
+ 19 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 19 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 0.013921 5.50955 0 0 -0 0
+ 0
+ 0
+ 0
+
+
+
+
+
+ 11.25 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 11.25 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 9.43892 -0.040455 0 0 -0 -1.5708
+ 0
+ 0
+ 0
+
+
+
+
+
+ 19 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 19 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 0.013921 -5.59046 0 0 -0 3.14159
+ 0
+ 0
+ 0
+
+
+
+
+
+ 11.25 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 11.25 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -9.41108 -0.040455 0 0 -0 1.5708
+ 0
+ 0
+ 0
+
+
+
+
+
+ 4.75 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 4.75 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -1.11769 3.29046 0 0 -0 -1.5708
+ 0
+ 0
+ 0
+
+
+
+
+
+ 6.75 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 6.75 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -4.41769 0.990456 0 0 -0 3.14159
+ 0
+ 0
+ 0
+
+
+
+
+
+ 3.47722 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 3.47722 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 0.391784 -3.86799 0 0 -0 1.5708
+ 0
+ 0
+ 0
+
+
+
+
+
+ 7.5 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 7.5 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 4.06678 -2.24484 0 0 -0 0
+ 0
+ 0
+ 0
+
+
+
+
+
+ 6.25 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 6.25 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -6.38892 -1.80969 0 0 -0 0
+ 0
+ 0
+ 0
+
+
+
+
+
+ 2.75 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 2.75 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -3.33892 -3.10969 0 0 -0 -1.5708
+ 0
+ 0
+ 0
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 0 0.005 0.6 0 -0 0
+
+
+ 0.9 0.01 1.2
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0.005 0.6 0 -0 0
+
+
+ 0.9 0.01 1.2
+
+
+
+
+
+
+
+ 0.45 -0.195 0.6 0 -0 0
+
+
+ 0.02 0.4 1.2
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.45 -0.195 0.6 0 -0 0
+
+
+ 0.02 0.4 1.2
+
+
+
+
+
+
+
+ -0.45 -0.195 0.6 0 -0 0
+
+
+ 0.02 0.4 1.2
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -0.45 -0.195 0.6 0 -0 0
+
+
+ 0.02 0.4 1.2
+
+
+
+
+
+
+
+ 0 -0.195 0.03 0 -0 0
+
+
+ 0.88 0.4 0.06
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.195 0.03 0 -0 0
+
+
+ 0.88 0.4 0.06
+
+
+
+
+
+
+
+ 0 -0.195 1.19 0 -0 0
+
+
+ 0.88 0.4 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.195 1.19 0 -0 0
+
+
+ 0.88 0.4 0.02
+
+
+
+
+
+
+
+ 0 -0.195 0.43 0 -0 0
+
+
+ 0.88 0.4 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.195 0.43 0 -0 0
+
+
+ 0.88 0.4 0.02
+
+
+
+
+
+
+
+ 0 -0.195 0.8 0 -0 0
+
+
+ 0.88 0.4 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.195 0.8 0 -0 0
+
+
+ 0.88 0.4 0.02
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+ 7.91236 4.68506 0 0 -0 0
+
+
+ 1
+
+
+ 0 0 -1 0 -0 0
+
+ 2.05
+ 0
+ 0
+ 2.05
+ 0
+ 2.05
+
+ 25
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+ -4.31975 4.20897 0 0 -0 0
+
+
+ 1
+
+
+ 0 0 -1 0 -0 0
+
+ 2.05
+ 0
+ 0
+ 2.05
+ 0
+ 2.05
+
+ 25
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+ -5.63889 4.25649 0 0 -0 0
+
+
+ 1
+
+
+ 0 0 -1 0 -0 0
+
+ 2.05
+ 0
+ 0
+ 2.05
+ 0
+ 2.05
+
+ 25
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+ -0.201253 -3.92041 0 0 -0 0
+
+
+ 1
+
+
+ 0 0 -1 0 -0 0
+
+ 2.05
+ 0
+ 0
+ 2.05
+ 0
+ 2.05
+
+ 25
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+ -0.211824 -5.1227 0 0 -0 0
+
+
+ 1
+
+
+ 0 0 -1 0 -0 0
+
+ 2.05
+ 0
+ 0
+ 2.05
+ 0
+ 2.05
+
+ 25
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+ -8.8359 -3.13197 0 0 -0 0
+
+
+ 1
+
+
+ 0 0 -1 0 -0 0
+
+ 2.05
+ 0
+ 0
+ 2.05
+ 0
+ 2.05
+
+ 25
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+ -7.80202 -3.07286 0 0 -0 0
+
+
+ 1
+
+
+ 0 0 0.755 0 -0 0
+
+
+ 0.913 0.913 0.04
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.37 0 -0 0
+
+
+ 0.042 0.042 0.74
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.02 0 -0 0
+
+
+ 0.56 0.56 0.04
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ model://cafe_table/meshes/cafe_table.dae
+
+
+
+ 0
+ 0
+ 0
+
+ -4.89593 2.8545 0 0 -0 0
+
+
+ 1
+
+
+ 0 0 0.755 0 -0 0
+
+
+ 0.913 0.913 0.04
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.37 0 -0 0
+
+
+ 0.042 0.042 0.74
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.02 0 -0 0
+
+
+ 0.56 0.56 0.04
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ model://cafe_table/meshes/cafe_table.dae
+
+
+
+ 0
+ 0
+ 0
+
+ 3.54156 1.36869 0 0 -0 0
+
+
+ 1
+
+
+ 0 0 0.755 0 -0 0
+
+
+ 0.913 0.913 0.04
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.37 0 -0 0
+
+
+ 0.042 0.042 0.74
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.02 0 -0 0
+
+
+ 0.56 0.56 0.04
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ model://cafe_table/meshes/cafe_table.dae
+
+
+
+ 0
+ 0
+ 0
+
+ 4.46016 1.36814 0 0 -0 0
+
+
+ -0.961046 0.717231 0.5 0 -0 0
+
+
+ 1
+
+ 0.145833
+ 0
+ 0
+ 0.145833
+ 0
+ 0.125
+
+ 0 0 0 0 -0 0
+
+
+
+
+ 0.5
+ 1
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.5
+ 1
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+ -0.958121 2.77839 0.5 0 -0 0
+
+
+ 1
+
+ 0.166667
+ 0
+ 0
+ 0.166667
+ 0
+ 0.166667
+
+ 0 0 0 0 -0 0
+
+
+
+
+ 1 1 1
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1 1 1
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+ -0.170626 -11.744 24.2897 0 1.1458 1.5962
+ orbit
+ perspective
+
+
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_description/world/room/model.config b/chapt7/chapt7_ws/src/fishbot_description/world/room/model.config
new file mode 100644
index 0000000..6222143
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/world/room/model.config
@@ -0,0 +1,11 @@
+
+
+ room
+ 1.0
+ model.sdf
+
+
+
+
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_description/world/room/model.sdf b/chapt7/chapt7_ws/src/fishbot_description/world/room/model.sdf
new file mode 100644
index 0000000..d499393
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_description/world/room/model.sdf
@@ -0,0 +1,297 @@
+
+
+
+ -0.972666 -0.903877 0 0 -0 0
+
+
+
+
+ 19 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 19 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 0.013921 5.50955 0 0 -0 0
+
+
+
+
+
+ 11.25 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 11.25 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 9.43892 -0.040455 0 0 -0 -1.5708
+
+
+
+
+
+ 19 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 19 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 0.013921 -5.59046 0 0 -0 3.14159
+
+
+
+
+
+ 11.25 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 11.25 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -9.41108 -0.040455 0 0 -0 1.5708
+
+
+
+
+
+ 4.75 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 4.75 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -1.11769 3.29046 0 0 -0 -1.5708
+
+
+
+
+
+ 6.75 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 6.75 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -4.41769 0.990456 0 0 -0 3.14159
+
+
+
+
+
+ 3.47722 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 3.47722 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 0.391784 -3.86799 0 0 -0 1.5708
+
+
+
+
+
+ 7.5 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 7.5 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 4.06678 -2.24484 0 0 -0 0
+
+
+
+
+
+ 6.25 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 6.25 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -6.38892 -1.80969 0 0 -0 0
+
+
+
+
+
+ 2.75 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 2.75 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -3.33892 -3.10969 0 0 -0 -1.5708
+
+ 1
+
+
diff --git a/chapt7/chapt7_ws/src/fishbot_navigation2/CMakeLists.txt b/chapt7/chapt7_ws/src/fishbot_navigation2/CMakeLists.txt
new file mode 100644
index 0000000..c185e3e
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_navigation2/CMakeLists.txt
@@ -0,0 +1,31 @@
+cmake_minimum_required(VERSION 3.8)
+project(fishbot_navigation2)
+
+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( 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 maps config
+ DESTINATION share/${PROJECT_NAME}
+)
+
+
+ament_package()
diff --git a/chapt7/chapt7_ws/src/fishbot_navigation2/config/nav2_params.yaml b/chapt7/chapt7_ws/src/fishbot_navigation2/config/nav2_params.yaml
new file mode 100644
index 0000000..5f4b08f
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_navigation2/config/nav2_params.yaml
@@ -0,0 +1,349 @@
+amcl:
+ ros__parameters:
+ use_sim_time: True
+ alpha1: 0.2
+ alpha2: 0.2
+ alpha3: 0.2
+ alpha4: 0.2
+ alpha5: 0.2
+ base_frame_id: "base_footprint"
+ beam_skip_distance: 0.5
+ beam_skip_error_threshold: 0.9
+ beam_skip_threshold: 0.3
+ do_beamskip: false
+ global_frame_id: "map"
+ lambda_short: 0.1
+ laser_likelihood_max_dist: 2.0
+ laser_max_range: 100.0
+ laser_min_range: -1.0
+ laser_model_type: "likelihood_field"
+ max_beams: 60
+ max_particles: 2000
+ min_particles: 500
+ odom_frame_id: "odom"
+ pf_err: 0.05
+ pf_z: 0.99
+ recovery_alpha_fast: 0.0
+ recovery_alpha_slow: 0.0
+ resample_interval: 1
+ robot_model_type: "nav2_amcl::DifferentialMotionModel"
+ save_pose_rate: 0.5
+ sigma_hit: 0.2
+ tf_broadcast: true
+ transform_tolerance: 1.0
+ update_min_a: 0.2
+ update_min_d: 0.25
+ z_hit: 0.5
+ z_max: 0.05
+ z_rand: 0.5
+ z_short: 0.05
+ scan_topic: scan
+
+bt_navigator:
+ ros__parameters:
+ use_sim_time: True
+ global_frame: map
+ robot_base_frame: base_link
+ odom_topic: /odom
+ bt_loop_duration: 10
+ default_server_timeout: 20
+ # 'default_nav_through_poses_bt_xml' and 'default_nav_to_pose_bt_xml' are use defaults:
+ # nav2_bt_navigator/navigate_to_pose_w_replanning_and_recovery.xml
+ # nav2_bt_navigator/navigate_through_poses_w_replanning_and_recovery.xml
+ # They can be set here or via a RewrittenYaml remap from a parent launch file to Nav2.
+ plugin_lib_names:
+ - nav2_compute_path_to_pose_action_bt_node
+ - nav2_compute_path_through_poses_action_bt_node
+ - nav2_smooth_path_action_bt_node
+ - nav2_follow_path_action_bt_node
+ - nav2_spin_action_bt_node
+ - nav2_wait_action_bt_node
+ - nav2_assisted_teleop_action_bt_node
+ - nav2_back_up_action_bt_node
+ - nav2_drive_on_heading_bt_node
+ - nav2_clear_costmap_service_bt_node
+ - nav2_is_stuck_condition_bt_node
+ - nav2_goal_reached_condition_bt_node
+ - nav2_goal_updated_condition_bt_node
+ - nav2_globally_updated_goal_condition_bt_node
+ - nav2_is_path_valid_condition_bt_node
+ - nav2_initial_pose_received_condition_bt_node
+ - nav2_reinitialize_global_localization_service_bt_node
+ - nav2_rate_controller_bt_node
+ - nav2_distance_controller_bt_node
+ - nav2_speed_controller_bt_node
+ - nav2_truncate_path_action_bt_node
+ - nav2_truncate_path_local_action_bt_node
+ - nav2_goal_updater_node_bt_node
+ - nav2_recovery_node_bt_node
+ - nav2_pipeline_sequence_bt_node
+ - nav2_round_robin_node_bt_node
+ - nav2_transform_available_condition_bt_node
+ - nav2_time_expired_condition_bt_node
+ - nav2_path_expiring_timer_condition
+ - nav2_distance_traveled_condition_bt_node
+ - nav2_single_trigger_bt_node
+ - nav2_goal_updated_controller_bt_node
+ - nav2_is_battery_low_condition_bt_node
+ - nav2_navigate_through_poses_action_bt_node
+ - nav2_navigate_to_pose_action_bt_node
+ - nav2_remove_passed_goals_action_bt_node
+ - nav2_planner_selector_bt_node
+ - nav2_controller_selector_bt_node
+ - nav2_goal_checker_selector_bt_node
+ - nav2_controller_cancel_bt_node
+ - nav2_path_longer_on_approach_bt_node
+ - nav2_wait_cancel_bt_node
+ - nav2_spin_cancel_bt_node
+ - nav2_back_up_cancel_bt_node
+ - nav2_assisted_teleop_cancel_bt_node
+ - nav2_drive_on_heading_cancel_bt_node
+ - nav2_is_battery_charging_condition_bt_node
+
+bt_navigator_navigate_through_poses_rclcpp_node:
+ ros__parameters:
+ use_sim_time: True
+
+bt_navigator_navigate_to_pose_rclcpp_node:
+ ros__parameters:
+ use_sim_time: True
+
+controller_server:
+ ros__parameters:
+ use_sim_time: True
+ controller_frequency: 20.0
+ min_x_velocity_threshold: 0.001
+ min_y_velocity_threshold: 0.5
+ min_theta_velocity_threshold: 0.001
+ failure_tolerance: 0.3
+ progress_checker_plugin: "progress_checker"
+ goal_checker_plugins: ["general_goal_checker"] # "precise_goal_checker"
+ controller_plugins: ["FollowPath"]
+
+ # Progress checker parameters
+ progress_checker:
+ plugin: "nav2_controller::SimpleProgressChecker"
+ required_movement_radius: 0.5
+ movement_time_allowance: 10.0
+ # Goal checker parameters
+ #precise_goal_checker:
+ # plugin: "nav2_controller::SimpleGoalChecker"
+ # xy_goal_tolerance: 0.25
+ # yaw_goal_tolerance: 0.25
+ # stateful: True
+ general_goal_checker:
+ stateful: True
+ plugin: "nav2_controller::SimpleGoalChecker"
+ xy_goal_tolerance: 0.25
+ yaw_goal_tolerance: 0.25
+ # DWB parameters
+ FollowPath:
+ plugin: "dwb_core::DWBLocalPlanner"
+ debug_trajectory_details: True
+ min_vel_x: 0.0
+ min_vel_y: 0.0
+ max_vel_x: 0.26
+ max_vel_y: 0.0
+ max_vel_theta: 1.0
+ min_speed_xy: 0.0
+ max_speed_xy: 0.26
+ min_speed_theta: 0.0
+ # Add high threshold velocity for turtlebot 3 issue.
+ # https://github.com/ROBOTIS-GIT/turtlebot3_simulations/issues/75
+ acc_lim_x: 2.5
+ acc_lim_y: 0.0
+ acc_lim_theta: 3.2
+ decel_lim_x: -2.5
+ decel_lim_y: 0.0
+ decel_lim_theta: -3.2
+ vx_samples: 20
+ vy_samples: 5
+ vtheta_samples: 20
+ sim_time: 1.7
+ linear_granularity: 0.05
+ angular_granularity: 0.025
+ transform_tolerance: 0.2
+ xy_goal_tolerance: 0.25
+ trans_stopped_velocity: 0.25
+ short_circuit_trajectory_evaluation: True
+ stateful: True
+ critics: ["RotateToGoal", "Oscillation", "BaseObstacle", "GoalAlign", "PathAlign", "PathDist", "GoalDist"]
+ BaseObstacle.scale: 0.02
+ PathAlign.scale: 32.0
+ PathAlign.forward_point_distance: 0.1
+ GoalAlign.scale: 24.0
+ GoalAlign.forward_point_distance: 0.1
+ PathDist.scale: 32.0
+ GoalDist.scale: 24.0
+ RotateToGoal.scale: 32.0
+ RotateToGoal.slowing_factor: 5.0
+ RotateToGoal.lookahead_time: -1.0
+
+local_costmap:
+ local_costmap:
+ ros__parameters:
+ update_frequency: 5.0
+ publish_frequency: 2.0
+ global_frame: odom
+ robot_base_frame: base_link
+ use_sim_time: True
+ rolling_window: true
+ width: 3
+ height: 3
+ resolution: 0.05
+ robot_radius: 0.22
+ plugins: ["voxel_layer", "inflation_layer"]
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ voxel_layer:
+ plugin: "nav2_costmap_2d::VoxelLayer"
+ enabled: True
+ publish_voxel_map: True
+ origin_z: 0.0
+ z_resolution: 0.05
+ z_voxels: 16
+ max_obstacle_height: 2.0
+ mark_threshold: 0
+ observation_sources: scan
+ scan:
+ topic: /scan
+ max_obstacle_height: 2.0
+ clearing: True
+ marking: True
+ data_type: "LaserScan"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ static_layer:
+ plugin: "nav2_costmap_2d::StaticLayer"
+ map_subscribe_transient_local: True
+ always_send_full_costmap: True
+
+global_costmap:
+ global_costmap:
+ ros__parameters:
+ update_frequency: 1.0
+ publish_frequency: 1.0
+ global_frame: map
+ robot_base_frame: base_link
+ use_sim_time: True
+ robot_radius: 0.22
+ resolution: 0.05
+ track_unknown_space: true
+ plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
+ obstacle_layer:
+ plugin: "nav2_costmap_2d::ObstacleLayer"
+ enabled: True
+ observation_sources: scan
+ scan:
+ topic: /scan
+ max_obstacle_height: 2.0
+ clearing: True
+ marking: True
+ data_type: "LaserScan"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ static_layer:
+ plugin: "nav2_costmap_2d::StaticLayer"
+ map_subscribe_transient_local: True
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ always_send_full_costmap: True
+
+map_server:
+ ros__parameters:
+ use_sim_time: True
+ # Overridden in launch by the "map" launch configuration or provided default value.
+ # To use in yaml, remove the default "map" value in the tb3_simulation_launch.py file & provide full path to map below.
+ yaml_filename: ""
+
+map_saver:
+ ros__parameters:
+ use_sim_time: True
+ save_map_timeout: 5.0
+ free_thresh_default: 0.25
+ occupied_thresh_default: 0.65
+ map_subscribe_transient_local: True
+
+planner_server:
+ ros__parameters:
+ expected_planner_frequency: 20.0
+ use_sim_time: True
+ planner_plugins: ["GridBased"]
+ GridBased:
+ plugin: "nav2_navfn_planner/NavfnPlanner"
+ tolerance: 0.5
+ use_astar: false
+ allow_unknown: true
+
+smoother_server:
+ ros__parameters:
+ use_sim_time: True
+ smoother_plugins: ["simple_smoother"]
+ simple_smoother:
+ plugin: "nav2_smoother::SimpleSmoother"
+ tolerance: 1.0e-10
+ max_its: 1000
+ do_refinement: True
+
+behavior_server:
+ ros__parameters:
+ costmap_topic: local_costmap/costmap_raw
+ footprint_topic: local_costmap/published_footprint
+ cycle_frequency: 10.0
+ behavior_plugins: ["spin", "backup", "drive_on_heading", "assisted_teleop", "wait"]
+ spin:
+ plugin: "nav2_behaviors/Spin"
+ backup:
+ plugin: "nav2_behaviors/BackUp"
+ drive_on_heading:
+ plugin: "nav2_behaviors/DriveOnHeading"
+ wait:
+ plugin: "nav2_behaviors/Wait"
+ assisted_teleop:
+ plugin: "nav2_behaviors/AssistedTeleop"
+ global_frame: odom
+ robot_base_frame: base_link
+ transform_tolerance: 0.1
+ use_sim_time: true
+ simulate_ahead_time: 2.0
+ max_rotational_vel: 1.0
+ min_rotational_vel: 0.4
+ rotational_acc_lim: 3.2
+
+robot_state_publisher:
+ ros__parameters:
+ use_sim_time: True
+
+waypoint_follower:
+ ros__parameters:
+ use_sim_time: True
+ loop_rate: 20
+ stop_on_failure: false
+ waypoint_task_executor_plugin: "wait_at_waypoint"
+ wait_at_waypoint:
+ plugin: "nav2_waypoint_follower::WaitAtWaypoint"
+ enabled: True
+ waypoint_pause_duration: 200
+
+velocity_smoother:
+ ros__parameters:
+ use_sim_time: True
+ smoothing_frequency: 20.0
+ scale_velocities: False
+ feedback: "OPEN_LOOP"
+ max_velocity: [0.26, 0.0, 1.0]
+ min_velocity: [-0.26, 0.0, -1.0]
+ max_accel: [2.5, 0.0, 3.2]
+ max_decel: [-2.5, 0.0, -3.2]
+ odom_topic: "odom"
+ odom_duration: 0.1
+ deadband_velocity: [0.0, 0.0, 0.0]
+ velocity_timeout: 1.0
diff --git a/chapt7/chapt7_ws/src/fishbot_navigation2/launch/navigation2.launch.py b/chapt7/chapt7_ws/src/fishbot_navigation2/launch/navigation2.launch.py
new file mode 100644
index 0000000..a55c3d5
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_navigation2/launch/navigation2.launch.py
@@ -0,0 +1,49 @@
+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():
+ # 获取与拼接默认路径
+ fishbot_navigation2_dir = get_package_share_directory(
+ 'fishbot_navigation2')
+ nav2_bringup_dir = get_package_share_directory('nav2_bringup')
+ rviz_config_dir = os.path.join(
+ nav2_bringup_dir, 'rviz', 'nav2_default_view.rviz')
+
+ # 创建 Launch 配置
+ use_sim_time = launch.substitutions.LaunchConfiguration(
+ 'use_sim_time', default='true')
+ map_yaml_path = launch.substitutions.LaunchConfiguration(
+ 'map', default=os.path.join(fishbot_navigation2_dir, 'maps', 'room.yaml'))
+ nav2_param_path = launch.substitutions.LaunchConfiguration(
+ 'params_file', default=os.path.join(fishbot_navigation2_dir, 'config', 'nav2_params.yaml'))
+
+ return launch.LaunchDescription([
+ # 声明新的 Launch 参数
+ launch.actions.DeclareLaunchArgument('use_sim_time', default_value=use_sim_time,
+ description='Use simulation (Gazebo) clock if true'),
+ launch.actions.DeclareLaunchArgument('map', default_value=map_yaml_path,
+ description='Full path to map file to load'),
+ launch.actions.DeclareLaunchArgument('params_file', default_value=nav2_param_path,
+ description='Full path to param file to load'),
+
+ launch.actions.IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ [nav2_bringup_dir, '/launch', '/bringup_launch.py']),
+ # 使用 Launch 参数替换原有参数
+ launch_arguments={
+ 'map': map_yaml_path,
+ 'use_sim_time': use_sim_time,
+ 'params_file': nav2_param_path}.items(),
+ ),
+ launch_ros.actions.Node(
+ package='rviz2',
+ executable='rviz2',
+ name='rviz2',
+ arguments=['-d', rviz_config_dir],
+ parameters=[{'use_sim_time': use_sim_time}],
+ output='screen'),
+ ])
\ No newline at end of file
diff --git a/chapt7/chapt7_ws/src/fishbot_navigation2/maps/room.pgm b/chapt7/chapt7_ws/src/fishbot_navigation2/maps/room.pgm
new file mode 100644
index 0000000..6bdfd22
Binary files /dev/null and b/chapt7/chapt7_ws/src/fishbot_navigation2/maps/room.pgm differ
diff --git a/chapt7/chapt7_ws/src/fishbot_navigation2/maps/room.yaml b/chapt7/chapt7_ws/src/fishbot_navigation2/maps/room.yaml
new file mode 100644
index 0000000..d0cbed8
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_navigation2/maps/room.yaml
@@ -0,0 +1,7 @@
+image: room.pgm
+mode: trinary
+resolution: 0.05
+origin: [-10.4, -6.53, 0]
+negate: 0
+occupied_thresh: 0.65
+free_thresh: 0.25
\ No newline at end of file
diff --git a/chapt7/chapt7_ws/src/fishbot_navigation2/package.xml b/chapt7/chapt7_ws/src/fishbot_navigation2/package.xml
new file mode 100644
index 0000000..19065ec
--- /dev/null
+++ b/chapt7/chapt7_ws/src/fishbot_navigation2/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ fishbot_navigation2
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_cmake
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+
diff --git a/chapt8/chapt8_ws/src/autopatrol_interfaces/CMakeLists.txt b/chapt8/chapt8_ws/src/autopatrol_interfaces/CMakeLists.txt
new file mode 100644
index 0000000..b2ed6b4
--- /dev/null
+++ b/chapt8/chapt8_ws/src/autopatrol_interfaces/CMakeLists.txt
@@ -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( 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()
diff --git a/chapt8/chapt8_ws/src/autopatrol_interfaces/package.xml b/chapt8/chapt8_ws/src/autopatrol_interfaces/package.xml
new file mode 100644
index 0000000..ccdf647
--- /dev/null
+++ b/chapt8/chapt8_ws/src/autopatrol_interfaces/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ autopatrol_interfaces
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_cmake
+
+ ament_lint_auto
+ ament_lint_common
+ rosidl_interface_packages
+
+ ament_cmake
+
+
diff --git a/chapt8/chapt8_ws/src/autopatrol_interfaces/srv/SpeachText.srv b/chapt8/chapt8_ws/src/autopatrol_interfaces/srv/SpeachText.srv
new file mode 100644
index 0000000..440fa28
--- /dev/null
+++ b/chapt8/chapt8_ws/src/autopatrol_interfaces/srv/SpeachText.srv
@@ -0,0 +1,3 @@
+string text # 合成文字
+---
+bool result # 合成结果
\ No newline at end of file
diff --git a/chapt8/chapt8_ws/src/autopatrol_robot/autopatrol_robot/__init__.py b/chapt8/chapt8_ws/src/autopatrol_robot/autopatrol_robot/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/chapt8/chapt8_ws/src/autopatrol_robot/autopatrol_robot/patrol_node.py b/chapt8/chapt8_ws/src/autopatrol_robot/autopatrol_robot/patrol_node.py
new file mode 100644
index 0000000..e1e3f1c
--- /dev/null
+++ b/chapt8/chapt8_ws/src/autopatrol_robot/autopatrol_robot/patrol_node.py
@@ -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()
\ No newline at end of file
diff --git a/chapt8/chapt8_ws/src/autopatrol_robot/autopatrol_robot/speaker.py b/chapt8/chapt8_ws/src/autopatrol_robot/autopatrol_robot/speaker.py
new file mode 100644
index 0000000..32e7269
--- /dev/null
+++ b/chapt8/chapt8_ws/src/autopatrol_robot/autopatrol_robot/speaker.py
@@ -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()
\ No newline at end of file
diff --git a/chapt8/chapt8_ws/src/autopatrol_robot/config/patrol_config.yaml b/chapt8/chapt8_ws/src/autopatrol_robot/config/patrol_config.yaml
new file mode 100644
index 0000000..cfef5b3
--- /dev/null
+++ b/chapt8/chapt8_ws/src/autopatrol_robot/config/patrol_config.yaml
@@ -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,
+ ]
\ No newline at end of file
diff --git a/chapt8/chapt8_ws/src/autopatrol_robot/launch/autopatrol.launch.py b/chapt8/chapt8_ws/src/autopatrol_robot/launch/autopatrol.launch.py
new file mode 100644
index 0000000..819362b
--- /dev/null
+++ b/chapt8/chapt8_ws/src/autopatrol_robot/launch/autopatrol.launch.py
@@ -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,
+ ])
\ No newline at end of file
diff --git a/chapt8/chapt8_ws/src/autopatrol_robot/package.xml b/chapt8/chapt8_ws/src/autopatrol_robot/package.xml
new file mode 100644
index 0000000..aa36510
--- /dev/null
+++ b/chapt8/chapt8_ws/src/autopatrol_robot/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ autopatrol_robot
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_copyright
+ ament_flake8
+ ament_pep257
+ python3-pytest
+
+
+ ament_python
+
+
diff --git a/chapt8/chapt8_ws/src/autopatrol_robot/resource/autopatrol_robot b/chapt8/chapt8_ws/src/autopatrol_robot/resource/autopatrol_robot
new file mode 100644
index 0000000..e69de29
diff --git a/chapt8/chapt8_ws/src/autopatrol_robot/setup.cfg b/chapt8/chapt8_ws/src/autopatrol_robot/setup.cfg
new file mode 100644
index 0000000..60e9700
--- /dev/null
+++ b/chapt8/chapt8_ws/src/autopatrol_robot/setup.cfg
@@ -0,0 +1,4 @@
+[develop]
+script_dir=$base/lib/autopatrol_robot
+[install]
+install_scripts=$base/lib/autopatrol_robot
diff --git a/chapt8/chapt8_ws/src/autopatrol_robot/setup.py b/chapt8/chapt8_ws/src/autopatrol_robot/setup.py
new file mode 100644
index 0000000..8b889e3
--- /dev/null
+++ b/chapt8/chapt8_ws/src/autopatrol_robot/setup.py
@@ -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',
+ ],
+ },
+)
diff --git a/chapt8/chapt8_ws/src/autopatrol_robot/test/test_copyright.py b/chapt8/chapt8_ws/src/autopatrol_robot/test/test_copyright.py
new file mode 100644
index 0000000..97a3919
--- /dev/null
+++ b/chapt8/chapt8_ws/src/autopatrol_robot/test/test_copyright.py
@@ -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'
diff --git a/chapt8/chapt8_ws/src/autopatrol_robot/test/test_flake8.py b/chapt8/chapt8_ws/src/autopatrol_robot/test/test_flake8.py
new file mode 100644
index 0000000..27ee107
--- /dev/null
+++ b/chapt8/chapt8_ws/src/autopatrol_robot/test/test_flake8.py
@@ -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)
diff --git a/chapt8/chapt8_ws/src/autopatrol_robot/test/test_pep257.py b/chapt8/chapt8_ws/src/autopatrol_robot/test/test_pep257.py
new file mode 100644
index 0000000..b234a38
--- /dev/null
+++ b/chapt8/chapt8_ws/src/autopatrol_robot/test/test_pep257.py
@@ -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'
diff --git a/chapt8/chapt8_ws/src/fishbot_application/fishbot_application/__init__.py b/chapt8/chapt8_ws/src/fishbot_application/fishbot_application/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/chapt8/chapt8_ws/src/fishbot_application/fishbot_application/get_robot_pose.py b/chapt8/chapt8_ws/src/fishbot_application/fishbot_application/get_robot_pose.py
new file mode 100644
index 0000000..fddcd8e
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_application/fishbot_application/get_robot_pose.py
@@ -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()
diff --git a/chapt8/chapt8_ws/src/fishbot_application/fishbot_application/init_robot_pose.py b/chapt8/chapt8_ws/src/fishbot_application/fishbot_application/init_robot_pose.py
new file mode 100644
index 0000000..8ed9029
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_application/fishbot_application/init_robot_pose.py
@@ -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()
diff --git a/chapt8/chapt8_ws/src/fishbot_application/fishbot_application/nav_to_pose.py b/chapt8/chapt8_ws/src/fishbot_application/fishbot_application/nav_to_pose.py
new file mode 100644
index 0000000..45ff081
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_application/fishbot_application/nav_to_pose.py
@@ -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()
\ No newline at end of file
diff --git a/chapt8/chapt8_ws/src/fishbot_application/fishbot_application/waypoint_follower.py b/chapt8/chapt8_ws/src/fishbot_application/fishbot_application/waypoint_follower.py
new file mode 100644
index 0000000..07e31f9
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_application/fishbot_application/waypoint_follower.py
@@ -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()
diff --git a/chapt8/chapt8_ws/src/fishbot_application/package.xml b/chapt8/chapt8_ws/src/fishbot_application/package.xml
new file mode 100644
index 0000000..8074680
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_application/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ fishbot_application
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_copyright
+ ament_flake8
+ ament_pep257
+ python3-pytest
+
+
+ ament_python
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_application/resource/fishbot_application b/chapt8/chapt8_ws/src/fishbot_application/resource/fishbot_application
new file mode 100644
index 0000000..e69de29
diff --git a/chapt8/chapt8_ws/src/fishbot_application/setup.cfg b/chapt8/chapt8_ws/src/fishbot_application/setup.cfg
new file mode 100644
index 0000000..b995d8e
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_application/setup.cfg
@@ -0,0 +1,4 @@
+[develop]
+script_dir=$base/lib/fishbot_application
+[install]
+install_scripts=$base/lib/fishbot_application
diff --git a/chapt8/chapt8_ws/src/fishbot_application/setup.py b/chapt8/chapt8_ws/src/fishbot_application/setup.py
new file mode 100644
index 0000000..98ebc51
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_application/setup.py
@@ -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',
+ ],
+ },
+)
diff --git a/chapt8/chapt8_ws/src/fishbot_application/test/test_copyright.py b/chapt8/chapt8_ws/src/fishbot_application/test/test_copyright.py
new file mode 100644
index 0000000..97a3919
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_application/test/test_copyright.py
@@ -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'
diff --git a/chapt8/chapt8_ws/src/fishbot_application/test/test_flake8.py b/chapt8/chapt8_ws/src/fishbot_application/test/test_flake8.py
new file mode 100644
index 0000000..27ee107
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_application/test/test_flake8.py
@@ -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)
diff --git a/chapt8/chapt8_ws/src/fishbot_application/test/test_pep257.py b/chapt8/chapt8_ws/src/fishbot_application/test/test_pep257.py
new file mode 100644
index 0000000..b234a38
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_application/test/test_pep257.py
@@ -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'
diff --git a/chapt8/chapt8_ws/src/fishbot_application_cpp/CMakeLists.txt b/chapt8/chapt8_ws/src/fishbot_application_cpp/CMakeLists.txt
new file mode 100644
index 0000000..f6075f8
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_application_cpp/CMakeLists.txt
@@ -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()
diff --git a/chapt8/chapt8_ws/src/fishbot_application_cpp/package.xml b/chapt8/chapt8_ws/src/fishbot_application_cpp/package.xml
new file mode 100644
index 0000000..efe88a5
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_application_cpp/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ fishbot_application_cpp
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_cmake
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_application_cpp/src/nav2pose.cpp b/chapt8/chapt8_ws/src/fishbot_application_cpp/src/nav2pose.cpp
new file mode 100644
index 0000000..a8e3383
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_application_cpp/src/nav2pose.cpp
@@ -0,0 +1,71 @@
+#include
+
+#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; // 定义导航动作客户端类型
+ using NavigationActionGoalHandle =
+ rclcpp_action::ClientGoalHandle; // 定义导航动作目标句柄类型
+
+ NavToPoseClient() : Node("nav_to_pose_client") {
+ // 创建导航动作客户端
+ action_client_ = rclcpp_action::create_client(
+ 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::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 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();
+ node->sendGoal();
+ rclcpp::spin(node);
+ rclcpp::shutdown();
+ return 0;
+}
diff --git a/chapt8/chapt8_ws/src/fishbot_description/CMakeLists.txt b/chapt8/chapt8_ws/src/fishbot_description/CMakeLists.txt
new file mode 100644
index 0000000..2a82fdf
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/CMakeLists.txt
@@ -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( 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()
diff --git a/chapt8/chapt8_ws/src/fishbot_description/LICENSE b/chapt8/chapt8_ws/src/fishbot_description/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/LICENSE
@@ -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.
diff --git a/chapt8/chapt8_ws/src/fishbot_description/config/fishbot_ros2_controller.yaml b/chapt8/chapt8_ws/src/fishbot_description/config/fishbot_ros2_controller.yaml
new file mode 100644
index 0000000..8a60aeb
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/config/fishbot_ros2_controller.yaml
@@ -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
\ No newline at end of file
diff --git a/chapt8/chapt8_ws/src/fishbot_description/config/rviz/dispaly_model.rviz b/chapt8/chapt8_ws/src/fishbot_description/config/rviz/dispaly_model.rviz
new file mode 100644
index 0000000..d624b06
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/config/rviz/dispaly_model.rviz
@@ -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:
+ 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:
+ 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
diff --git a/chapt8/chapt8_ws/src/fishbot_description/launch/display_robot.launch.py b/chapt8/chapt8_ws/src/fishbot_description/launch/display_robot.launch.py
new file mode 100644
index 0000000..3db487d
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/launch/display_robot.launch.py
@@ -0,0 +1,42 @@
+import launch
+import launch_ros
+from ament_index_python.packages import get_package_share_directory
+
+
+def generate_launch_description():
+ # 获取默认路径
+ urdf_tutorial_path = get_package_share_directory('fishbot_description')
+ default_model_path = urdf_tutorial_path + '/urdf/first_robot.urdf'
+ default_rviz_config_path = urdf_tutorial_path + '/config/rviz/display_model.rviz'
+ # 为 Launch 声明参数
+ action_declare_arg_mode_path = launch.actions.DeclareLaunchArgument(
+ name='model', default_value=str(default_model_path),
+ description='URDF 的绝对路径')
+ # 获取文件内容生成新的参数
+ robot_description = launch_ros.parameter_descriptions.ParameterValue(
+ launch.substitutions.Command(
+ ['xacro ', launch.substitutions.LaunchConfiguration('model')]),
+ value_type=str)
+ # 状态发布节点
+ robot_state_publisher_node = launch_ros.actions.Node(
+ package='robot_state_publisher',
+ executable='robot_state_publisher',
+ parameters=[{'robot_description': robot_description}]
+ )
+ # 关节状态发布节点
+ joint_state_publisher_node = launch_ros.actions.Node(
+ package='joint_state_publisher',
+ executable='joint_state_publisher',
+ )
+ # RViz 节点
+ rviz_node = launch_ros.actions.Node(
+ package='rviz2',
+ executable='rviz2',
+ arguments=['-d', default_rviz_config_path]
+ )
+ return launch.LaunchDescription([
+ action_declare_arg_mode_path,
+ joint_state_publisher_node,
+ robot_state_publisher_node,
+ rviz_node
+ ])
diff --git a/chapt8/chapt8_ws/src/fishbot_description/launch/gazebo_sim.launch.py b/chapt8/chapt8_ws/src/fishbot_description/launch/gazebo_sim.launch.py
new file mode 100644
index 0000000..16050ca
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/launch/gazebo_sim.launch.py
@@ -0,0 +1,75 @@
+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():
+ # 获取默认路径
+ robot_name_in_model = "fishbot"
+ urdf_tutorial_path = get_package_share_directory('fishbot_description')
+ default_model_path = urdf_tutorial_path + '/urdf/fishbot/fishbot.urdf.xacro'
+ default_world_path = urdf_tutorial_path + '/world/custom_room.world'
+ # 为 Launch 声明参数
+ action_declare_arg_mode_path = launch.actions.DeclareLaunchArgument(
+ name='model', default_value=str(default_model_path),
+ description='URDF 的绝对路径')
+ # 获取文件内容生成新的参数
+ robot_description = launch_ros.parameter_descriptions.ParameterValue(
+ launch.substitutions.Command(
+ ['xacro ', launch.substitutions.LaunchConfiguration('model')]),
+ value_type=str)
+
+ robot_state_publisher_node = launch_ros.actions.Node(
+ package='robot_state_publisher',
+ executable='robot_state_publisher',
+ parameters=[{'robot_description': robot_description}]
+ )
+
+ # 通过 IncludeLaunchDescription 包含另外一个 launch 文件
+ launch_gazebo = launch.actions.IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([get_package_share_directory(
+ 'gazebo_ros'), '/launch', '/gazebo.launch.py']),
+ # 传递参数
+ launch_arguments=[('world', default_world_path),('verbose','true')]
+ )
+ # 请求 Gazebo 加载机器人
+ spawn_entity_node = launch_ros.actions.Node(
+ package='gazebo_ros',
+ executable='spawn_entity.py',
+ arguments=['-topic', '/robot_description',
+ '-entity', robot_name_in_model, ])
+
+ # 加载并激活 fishbot_joint_state_broadcaster 控制器
+ load_joint_state_controller = launch.actions.ExecuteProcess(
+ cmd=['ros2', 'control', 'load_controller', '--set-state', 'active',
+ 'fishbot_joint_state_broadcaster'],
+ output='screen'
+ )
+
+ # 加载并激活 fishbot_effort_controller 控制器
+ load_fishbot_effort_controller = launch.actions.ExecuteProcess(
+ cmd=['ros2', 'control', 'load_controller', '--set-state', 'active','fishbot_effort_controller'],
+ output='screen')
+
+ load_fishbot_diff_drive_controller = launch.actions.ExecuteProcess(
+ cmd=['ros2', 'control', 'load_controller', '--set-state', 'active','fishbot_diff_drive_controller'],
+ output='screen')
+
+ return launch.LaunchDescription([
+ action_declare_arg_mode_path,
+ robot_state_publisher_node,
+ launch_gazebo,
+ spawn_entity_node,
+ # 事件动作,当加载机器人结束后执行
+ launch.actions.RegisterEventHandler(
+ event_handler=launch.event_handlers.OnProcessExit(
+ target_action=spawn_entity_node,
+ on_exit=[load_joint_state_controller],)
+ ),
+ # 事件动作,load_fishbot_diff_drive_controller
+ launch.actions.RegisterEventHandler(
+ event_handler=launch.event_handlers.OnProcessExit(
+ target_action=load_joint_state_controller,
+ on_exit=[load_fishbot_diff_drive_controller],)
+ ),
+ ])
diff --git a/chapt8/chapt8_ws/src/fishbot_description/package.xml b/chapt8/chapt8_ws/src/fishbot_description/package.xml
new file mode 100644
index 0000000..575ade0
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ fishbot_description
+ 0.0.0
+ TODO: Package description
+ mzebra
+ Apache-2.0
+
+ ament_cmake
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_description/urdf/first_robot.gv b/chapt8/chapt8_ws/src/fishbot_description/urdf/first_robot.gv
new file mode 100644
index 0000000..6d190c9
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/urdf/first_robot.gv
@@ -0,0 +1,8 @@
+digraph G {
+node [shape=box];
+"base_link" [label="base_link"];
+"imu_link" [label="imu_link"];
+node [shape=ellipse, color=blue, fontcolor=blue];
+"base_link" -> "imu_joint" [label="xyz: 0 0 0.03 \nrpy: 0 -0 0"]
+"imu_joint" -> "imu_link"
+}
diff --git a/chapt8/chapt8_ws/src/fishbot_description/urdf/first_robot.pdf b/chapt8/chapt8_ws/src/fishbot_description/urdf/first_robot.pdf
new file mode 100644
index 0000000..a0a0955
Binary files /dev/null and b/chapt8/chapt8_ws/src/fishbot_description/urdf/first_robot.pdf differ
diff --git a/chapt8/chapt8_ws/src/fishbot_description/urdf/first_robot.urdf b/chapt8/chapt8_ws/src/fishbot_description/urdf/first_robot.urdf
new file mode 100644
index 0000000..5f706eb
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/urdf/first_robot.urdf
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_description/urdf/first_robot.urdf.xacro b/chapt8/chapt8_ws/src/fishbot_description/urdf/first_robot.urdf.xacro
new file mode 100644
index 0000000..3a36fe7
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/urdf/first_robot.urdf.xacro
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/actuator/caster.urdf.xacro b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/actuator/caster.urdf.xacro
new file mode 100644
index 0000000..e654233
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/actuator/caster.urdf.xacro
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/actuator/wheel.urdf.xacro b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/actuator/wheel.urdf.xacro
new file mode 100644
index 0000000..6927798
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/actuator/wheel.urdf.xacro
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/base.urdf.xacro b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/base.urdf.xacro
new file mode 100644
index 0000000..9f14ebc
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/base.urdf.xacro
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/common_inertia.xacro b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/common_inertia.xacro
new file mode 100644
index 0000000..8486102
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/common_inertia.xacro
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/fishbot.ros2_control.xacro b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/fishbot.ros2_control.xacro
new file mode 100644
index 0000000..157b5ba
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/fishbot.ros2_control.xacro
@@ -0,0 +1,45 @@
+
+
+
+
+
+ gazebo_ros2_control/GazeboSystem
+
+
+
+ -1
+ 1
+
+
+ -0.1
+ 0.1
+
+
+
+
+
+
+
+ -1
+ 1
+
+
+ -0.1
+ 0.1
+
+
+
+
+
+
+
+
+ $(find fishbot_description)/config/fishbot_ros2_controller.yaml
+
+ /fishbot_diff_drive_controller/cmd_vel_unstamped:=/cmd_vel
+ /fishbot_diff_drive_controller/odom:=/odom
+
+
+
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/fishbot.urdf.xacro b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/fishbot.urdf.xacro
new file mode 100644
index 0000000..77c980b
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/fishbot.urdf.xacro
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/plugins/gazebo_control_plugin.xacro b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/plugins/gazebo_control_plugin.xacro
new file mode 100644
index 0000000..46d898a
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/plugins/gazebo_control_plugin.xacro
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+ /
+ cmd_vel:=cmd_vel
+ odom:=odom
+
+ 30
+
+ left_wheel_joint
+ right_wheel_joint
+
+ 0.2
+ 0.064
+
+ 20
+ 1.0
+
+ true
+ true
+ true
+
+ odom
+ base_footprint
+
+
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/plugins/gazebo_sensor_plugin.xacro b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/plugins/gazebo_sensor_plugin.xacro
new file mode 100644
index 0000000..c69cbb4
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/plugins/gazebo_sensor_plugin.xacro
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+ /
+ ~/out:=scan
+
+ sensor_msgs/LaserScan
+ laser_link
+
+ true
+ true
+ 5
+ 0 0 0 0 0 0
+
+
+
+
+
+ 360
+ 1.000000
+ 0.000000
+ 6.280000
+
+
+
+
+ 0.120000
+ 8.0
+ 0.015000
+
+
+
+ gaussian
+ 0.0
+ 0.01
+
+
+
+
+
+
+
+
+
+
+
+ /
+ ~/out:=imu
+
+ false
+
+ 100
+ true
+
+
+
+
+
+ 0.0
+ 2e-4
+ 0.0000075
+ 0.0000008
+
+
+
+
+ 0.0
+ 2e-4
+ 0.0000075
+ 0.0000008
+
+
+
+
+ 0.0
+ 2e-4
+ 0.0000075
+ 0.0000008
+
+
+
+
+
+
+ 0.0
+ 1.7e-2
+ 0.1
+ 0.001
+
+
+
+
+ 0.0
+ 1.7e-2
+ 0.1
+ 0.001
+
+
+
+
+ 0.0
+ 1.7e-2
+ 0.1
+ 0.001
+
+
+
+
+
+
+
+
+
+
+
+
+
+ camera_optical_link
+
+ true
+ 10
+
+ 1.5009831567
+
+ 800
+ 600
+ R8G8B8
+
+
+ 0.0
+ 0.0
+ 0.0
+ 0.0
+ 0.0
+ 0.5 0.5
+
+
+
+
+
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/sensor/camera.urdf.xacro b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/sensor/camera.urdf.xacro
new file mode 100644
index 0000000..567a95f
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/sensor/camera.urdf.xacro
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/sensor/imu.urdf.xacro b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/sensor/imu.urdf.xacro
new file mode 100644
index 0000000..2d0b45d
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/sensor/imu.urdf.xacro
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/sensor/laser.urdf.xacro b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/sensor/laser.urdf.xacro
new file mode 100644
index 0000000..c9cabb3
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/urdf/fishbot/sensor/laser.urdf.xacro
@@ -0,0 +1,73 @@
+
+
+
+
+
+ Gazebo/Black
+
+
+ Gazebo/Black
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_description/world/custom_room.world b/chapt8/chapt8_ws/src/fishbot_description/world/custom_room.world
new file mode 100644
index 0000000..a341114
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/world/custom_room.world
@@ -0,0 +1,2745 @@
+
+
+
+ 1
+ 0 0 10 0 -0 0
+ 0.8 0.8 0.8 1
+ 0.2 0.2 0.2 1
+
+ 1000
+ 0.9
+ 0.01
+ 0.001
+
+ -0.5 0.1 -0.9
+
+ 0
+ 0
+ 0
+
+
+
+ 1
+
+
+
+
+ 0 0 1
+ 100 100
+
+
+
+
+
+ 100
+ 50
+
+
+
+
+
+
+
+
+
+
+ 10
+
+
+ 0
+
+
+ 0 0 1
+ 100 100
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+ 0 0 -9.8
+ 6e-06 2.3e-05 -4.2e-05
+
+
+ 0.001
+ 1
+ 1000
+
+
+ 0.4 0.4 0.4 1
+ 0.7 0.7 0.7 1
+ 1
+
+
+
+
+ EARTH_WGS84
+ 0
+ 0
+ 0
+ 0
+
+
+ 1435 337000000
+ 1463 259419335
+ 1684421669 225276651
+ 1435337
+
+ 7.91236 4.68506 0 0 -0 0
+ 1 1 1
+
+ 7.91236 4.68506 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ -4.31975 4.20897 0 0 -0 0
+ 1 1 1
+
+ -4.31975 4.20897 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ -5.63889 4.25649 0 0 -0 0
+ 1 1 1
+
+ -5.63889 4.25649 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ -0.201253 -3.92041 0 0 -0 0
+ 1 1 1
+
+ -0.201253 -3.92041 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ -0.211824 -5.1227 0 0 -0 0
+ 1 1 1
+
+ -0.211824 -5.1227 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ -8.8359 -3.13197 0 0 -0 0
+ 1 1 1
+
+ -8.8359 -3.13197 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ -7.80202 -3.07286 0 0 -0 0
+ 1 1 1
+
+ -7.80202 -3.07286 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ -4.89593 2.8545 0 0 -0 0
+ 1 1 1
+
+ -4.89593 2.8545 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ 3.54156 1.36869 0 0 -0 0
+ 1 1 1
+
+ 3.54156 1.36869 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ 4.46016 1.36814 0 0 -0 0
+ 1 1 1
+
+ 4.46016 1.36814 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ 0 0 0 0 -0 0
+ 1 1 1
+
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ -0.972666 -0.903877 0 0 -0 0
+ 1 1 1
+
+ -0.958745 4.60567 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+ 8.46625 -0.944332 0 0 0 -1.5708
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+ -0.958745 -6.49434 0 0 -0 3.14159
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+ -10.3837 -0.944332 0 0 -0 1.5708
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+ -2.09036 2.38658 0 0 0 -1.5708
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+ -5.39036 0.086579 0 0 -0 3.14159
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+ -0.580882 -4.77187 0 0 -0 1.5708
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+ 3.09411 -3.14872 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+ -7.36159 -2.71357 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+ -4.31159 -4.01357 0 0 0 -1.5708
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+ 0 0 0 0 -0 0
+
+
+
+ -0.958121 2.77839 0.5 0 -0 0
+ 1 1 1
+
+ -0.958121 2.77839 0.5 0 -0 0
+ 0 0 0 0 -0 0
+ -0.004709 -9.78112 9.78158 0.712677 -0.009414 -4.3e-05
+ -0.004709 -9.78112 9.78158 0 -0 0
+
+
+
+ -0.961048 0.717233 0.499993 -3e-06 -4e-06 -0
+ 1 1 1
+
+ -0.961048 0.717233 0.499993 -3e-06 -4e-06 -0
+ 0 0 0 0 -0 0
+ 0 0 -9.8 0 -0 0
+ 0 0 -9.8 0 -0 0
+
+
+
+ 0 0 10 0 -0 0
+
+
+
+ -0.972666 -0.903877 0 0 -0 0
+
+
+
+
+ 19 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 19 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 0.013921 5.50955 0 0 -0 0
+ 0
+ 0
+ 0
+
+
+
+
+
+ 11.25 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 11.25 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 9.43892 -0.040455 0 0 -0 -1.5708
+ 0
+ 0
+ 0
+
+
+
+
+
+ 19 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 19 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 0.013921 -5.59046 0 0 -0 3.14159
+ 0
+ 0
+ 0
+
+
+
+
+
+ 11.25 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 11.25 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -9.41108 -0.040455 0 0 -0 1.5708
+ 0
+ 0
+ 0
+
+
+
+
+
+ 4.75 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 4.75 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -1.11769 3.29046 0 0 -0 -1.5708
+ 0
+ 0
+ 0
+
+
+
+
+
+ 6.75 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 6.75 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -4.41769 0.990456 0 0 -0 3.14159
+ 0
+ 0
+ 0
+
+
+
+
+
+ 3.47722 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 3.47722 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 0.391784 -3.86799 0 0 -0 1.5708
+ 0
+ 0
+ 0
+
+
+
+
+
+ 7.5 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 7.5 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 4.06678 -2.24484 0 0 -0 0
+ 0
+ 0
+ 0
+
+
+
+
+
+ 6.25 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 6.25 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -6.38892 -1.80969 0 0 -0 0
+ 0
+ 0
+ 0
+
+
+
+
+
+ 2.75 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 2.75 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -3.33892 -3.10969 0 0 -0 -1.5708
+ 0
+ 0
+ 0
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 0 0.005 0.6 0 -0 0
+
+
+ 0.9 0.01 1.2
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0.005 0.6 0 -0 0
+
+
+ 0.9 0.01 1.2
+
+
+
+
+
+
+
+ 0.45 -0.195 0.6 0 -0 0
+
+
+ 0.02 0.4 1.2
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.45 -0.195 0.6 0 -0 0
+
+
+ 0.02 0.4 1.2
+
+
+
+
+
+
+
+ -0.45 -0.195 0.6 0 -0 0
+
+
+ 0.02 0.4 1.2
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -0.45 -0.195 0.6 0 -0 0
+
+
+ 0.02 0.4 1.2
+
+
+
+
+
+
+
+ 0 -0.195 0.03 0 -0 0
+
+
+ 0.88 0.4 0.06
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.195 0.03 0 -0 0
+
+
+ 0.88 0.4 0.06
+
+
+
+
+
+
+
+ 0 -0.195 1.19 0 -0 0
+
+
+ 0.88 0.4 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.195 1.19 0 -0 0
+
+
+ 0.88 0.4 0.02
+
+
+
+
+
+
+
+ 0 -0.195 0.43 0 -0 0
+
+
+ 0.88 0.4 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.195 0.43 0 -0 0
+
+
+ 0.88 0.4 0.02
+
+
+
+
+
+
+
+ 0 -0.195 0.8 0 -0 0
+
+
+ 0.88 0.4 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.195 0.8 0 -0 0
+
+
+ 0.88 0.4 0.02
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+ 7.91236 4.68506 0 0 -0 0
+
+
+ 1
+
+
+ 0 0 -1 0 -0 0
+
+ 2.05
+ 0
+ 0
+ 2.05
+ 0
+ 2.05
+
+ 25
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+ -4.31975 4.20897 0 0 -0 0
+
+
+ 1
+
+
+ 0 0 -1 0 -0 0
+
+ 2.05
+ 0
+ 0
+ 2.05
+ 0
+ 2.05
+
+ 25
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+ -5.63889 4.25649 0 0 -0 0
+
+
+ 1
+
+
+ 0 0 -1 0 -0 0
+
+ 2.05
+ 0
+ 0
+ 2.05
+ 0
+ 2.05
+
+ 25
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+ -0.201253 -3.92041 0 0 -0 0
+
+
+ 1
+
+
+ 0 0 -1 0 -0 0
+
+ 2.05
+ 0
+ 0
+ 2.05
+ 0
+ 2.05
+
+ 25
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+ -0.211824 -5.1227 0 0 -0 0
+
+
+ 1
+
+
+ 0 0 -1 0 -0 0
+
+ 2.05
+ 0
+ 0
+ 2.05
+ 0
+ 2.05
+
+ 25
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+ -8.8359 -3.13197 0 0 -0 0
+
+
+ 1
+
+
+ 0 0 -1 0 -0 0
+
+ 2.05
+ 0
+ 0
+ 2.05
+ 0
+ 2.05
+
+ 25
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.235 0 0.51 0 -0 0
+
+
+ 0.02 0.45 1.02
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.51 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 -0.235 0.51 0 -0 0
+
+
+ 0.45 0.02 1.02
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 1.01 0 -0 0
+
+
+ 0.45 0.45 0.02
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+ -7.80202 -3.07286 0 0 -0 0
+
+
+ 1
+
+
+ 0 0 0.755 0 -0 0
+
+
+ 0.913 0.913 0.04
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.37 0 -0 0
+
+
+ 0.042 0.042 0.74
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.02 0 -0 0
+
+
+ 0.56 0.56 0.04
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ model://cafe_table/meshes/cafe_table.dae
+
+
+
+ 0
+ 0
+ 0
+
+ -4.89593 2.8545 0 0 -0 0
+
+
+ 1
+
+
+ 0 0 0.755 0 -0 0
+
+
+ 0.913 0.913 0.04
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.37 0 -0 0
+
+
+ 0.042 0.042 0.74
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.02 0 -0 0
+
+
+ 0.56 0.56 0.04
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ model://cafe_table/meshes/cafe_table.dae
+
+
+
+ 0
+ 0
+ 0
+
+ 3.54156 1.36869 0 0 -0 0
+
+
+ 1
+
+
+ 0 0 0.755 0 -0 0
+
+
+ 0.913 0.913 0.04
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.37 0 -0 0
+
+
+ 0.042 0.042 0.74
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0 0 0.02 0 -0 0
+
+
+ 0.56 0.56 0.04
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ model://cafe_table/meshes/cafe_table.dae
+
+
+
+ 0
+ 0
+ 0
+
+ 4.46016 1.36814 0 0 -0 0
+
+
+ -0.961046 0.717231 0.5 0 -0 0
+
+
+ 1
+
+ 0.145833
+ 0
+ 0
+ 0.145833
+ 0
+ 0.125
+
+ 0 0 0 0 -0 0
+
+
+
+
+ 0.5
+ 1
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.5
+ 1
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+ -0.958121 2.77839 0.5 0 -0 0
+
+
+ 1
+
+ 0.166667
+ 0
+ 0
+ 0.166667
+ 0
+ 0.166667
+
+ 0 0 0 0 -0 0
+
+
+
+
+ 1 1 1
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1 1 1
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+ -0.170626 -11.744 24.2897 0 1.1458 1.5962
+ orbit
+ perspective
+
+
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_description/world/room/model.config b/chapt8/chapt8_ws/src/fishbot_description/world/room/model.config
new file mode 100644
index 0000000..6222143
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/world/room/model.config
@@ -0,0 +1,11 @@
+
+
+ room
+ 1.0
+ model.sdf
+
+
+
+
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_description/world/room/model.sdf b/chapt8/chapt8_ws/src/fishbot_description/world/room/model.sdf
new file mode 100644
index 0000000..d499393
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_description/world/room/model.sdf
@@ -0,0 +1,297 @@
+
+
+
+ -0.972666 -0.903877 0 0 -0 0
+
+
+
+
+ 19 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 19 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 0.013921 5.50955 0 0 -0 0
+
+
+
+
+
+ 11.25 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 11.25 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 9.43892 -0.040455 0 0 -0 -1.5708
+
+
+
+
+
+ 19 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 19 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 0.013921 -5.59046 0 0 -0 3.14159
+
+
+
+
+
+ 11.25 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 11.25 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -9.41108 -0.040455 0 0 -0 1.5708
+
+
+
+
+
+ 4.75 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 4.75 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -1.11769 3.29046 0 0 -0 -1.5708
+
+
+
+
+
+ 6.75 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 6.75 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -4.41769 0.990456 0 0 -0 3.14159
+
+
+
+
+
+ 3.47722 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 3.47722 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 0.391784 -3.86799 0 0 -0 1.5708
+
+
+
+
+
+ 7.5 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 7.5 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ 4.06678 -2.24484 0 0 -0 0
+
+
+
+
+
+ 6.25 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 6.25 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -6.38892 -1.80969 0 0 -0 0
+
+
+
+
+
+ 2.75 0.15 2.5
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 0 0 1.25 0 -0 0
+
+
+ 2.75 0.15 2.5
+
+
+
+
+ 1 1 1 1
+
+
+ 0
+
+
+ -3.33892 -3.10969 0 0 -0 -1.5708
+
+ 1
+
+
diff --git a/chapt8/chapt8_ws/src/fishbot_navigation2/CMakeLists.txt b/chapt8/chapt8_ws/src/fishbot_navigation2/CMakeLists.txt
new file mode 100644
index 0000000..c185e3e
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_navigation2/CMakeLists.txt
@@ -0,0 +1,31 @@
+cmake_minimum_required(VERSION 3.8)
+project(fishbot_navigation2)
+
+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( 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 maps config
+ DESTINATION share/${PROJECT_NAME}
+)
+
+
+ament_package()
diff --git a/chapt8/chapt8_ws/src/fishbot_navigation2/config/nav2_params.yaml b/chapt8/chapt8_ws/src/fishbot_navigation2/config/nav2_params.yaml
new file mode 100644
index 0000000..a15be1e
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_navigation2/config/nav2_params.yaml
@@ -0,0 +1,317 @@
+amcl:
+ ros__parameters:
+ use_sim_time: True
+ alpha1: 0.2
+ alpha2: 0.2
+ alpha3: 0.2
+ alpha4: 0.2
+ alpha5: 0.2
+ base_frame_id: "base_footprint"
+ beam_skip_distance: 0.5
+ beam_skip_error_threshold: 0.9
+ beam_skip_threshold: 0.3
+ do_beamskip: false
+ global_frame_id: "map"
+ lambda_short: 0.1
+ laser_likelihood_max_dist: 2.0
+ laser_max_range: 100.0
+ laser_min_range: -1.0
+ laser_model_type: "likelihood_field"
+ max_beams: 60
+ max_particles: 2000
+ min_particles: 500
+ odom_frame_id: "odom"
+ pf_err: 0.05
+ pf_z: 0.99
+ recovery_alpha_fast: 0.0
+ recovery_alpha_slow: 0.0
+ resample_interval: 1
+ robot_model_type: "nav2_amcl::DifferentialMotionModel"
+ save_pose_rate: 0.5
+ sigma_hit: 0.2
+ tf_broadcast: true
+ transform_tolerance: 1.0
+ update_min_a: 0.2
+ update_min_d: 0.25
+ z_hit: 0.5
+ z_max: 0.05
+ z_rand: 0.5
+ z_short: 0.05
+ scan_topic: scan
+
+bt_navigator:
+ ros__parameters:
+ use_sim_time: True
+ global_frame: map
+ robot_base_frame: base_link
+ odom_topic: /odom
+ bt_loop_duration: 10
+ default_server_timeout: 20
+ # 'default_nav_through_poses_bt_xml' and 'default_nav_to_pose_bt_xml' are use defaults:
+ # nav2_bt_navigator/navigate_to_pose_w_replanning_and_recovery.xml
+ # nav2_bt_navigator/navigate_through_poses_w_replanning_and_recovery.xml
+ # They can be set here or via a RewrittenYaml remap from a parent launch file to Nav2.
+ plugin_lib_names:
+ - nav2_compute_path_to_pose_action_bt_node
+ - nav2_compute_path_through_poses_action_bt_node
+ - nav2_smooth_path_action_bt_node
+ - nav2_follow_path_action_bt_node
+ - nav2_spin_action_bt_node
+ - nav2_wait_action_bt_node
+ - nav2_assisted_teleop_action_bt_node
+ - nav2_back_up_action_bt_node
+ - nav2_drive_on_heading_bt_node
+ - nav2_clear_costmap_service_bt_node
+ - nav2_is_stuck_condition_bt_node
+ - nav2_goal_reached_condition_bt_node
+ - nav2_goal_updated_condition_bt_node
+ - nav2_globally_updated_goal_condition_bt_node
+ - nav2_is_path_valid_condition_bt_node
+ - nav2_initial_pose_received_condition_bt_node
+ - nav2_reinitialize_global_localization_service_bt_node
+ - nav2_rate_controller_bt_node
+ - nav2_distance_controller_bt_node
+ - nav2_speed_controller_bt_node
+ - nav2_truncate_path_action_bt_node
+ - nav2_truncate_path_local_action_bt_node
+ - nav2_goal_updater_node_bt_node
+ - nav2_recovery_node_bt_node
+ - nav2_pipeline_sequence_bt_node
+ - nav2_round_robin_node_bt_node
+ - nav2_transform_available_condition_bt_node
+ - nav2_time_expired_condition_bt_node
+ - nav2_path_expiring_timer_condition
+ - nav2_distance_traveled_condition_bt_node
+ - nav2_single_trigger_bt_node
+ - nav2_goal_updated_controller_bt_node
+ - nav2_is_battery_low_condition_bt_node
+ - nav2_navigate_through_poses_action_bt_node
+ - nav2_navigate_to_pose_action_bt_node
+ - nav2_remove_passed_goals_action_bt_node
+ - nav2_planner_selector_bt_node
+ - nav2_controller_selector_bt_node
+ - nav2_goal_checker_selector_bt_node
+ - nav2_controller_cancel_bt_node
+ - nav2_path_longer_on_approach_bt_node
+ - nav2_wait_cancel_bt_node
+ - nav2_spin_cancel_bt_node
+ - nav2_back_up_cancel_bt_node
+ - nav2_assisted_teleop_cancel_bt_node
+ - nav2_drive_on_heading_cancel_bt_node
+ - nav2_is_battery_charging_condition_bt_node
+
+bt_navigator_navigate_through_poses_rclcpp_node:
+ ros__parameters:
+ use_sim_time: True
+
+bt_navigator_navigate_to_pose_rclcpp_node:
+ ros__parameters:
+ use_sim_time: True
+
+controller_server:
+ ros__parameters:
+ use_sim_time: True
+ controller_frequency: 20.0
+ min_x_velocity_threshold: 0.001
+ min_y_velocity_threshold: 0.5
+ min_theta_velocity_threshold: 0.001
+ failure_tolerance: 0.3
+ progress_checker_plugin: "progress_checker"
+ goal_checker_plugins: ["general_goal_checker"] # "precise_goal_checker"
+ controller_plugins: ["FollowPath"]
+
+ # Progress checker parameters
+ progress_checker:
+ plugin: "nav2_controller::SimpleProgressChecker"
+ required_movement_radius: 0.5
+ movement_time_allowance: 10.0
+ # Goal checker parameters
+ #precise_goal_checker:
+ # plugin: "nav2_controller::SimpleGoalChecker"
+ # xy_goal_tolerance: 0.25
+ # yaw_goal_tolerance: 0.25
+ # stateful: True
+ general_goal_checker:
+ stateful: True
+ plugin: "nav2_controller::SimpleGoalChecker"
+ xy_goal_tolerance: 0.25
+ yaw_goal_tolerance: 0.25
+ # DWB parameters
+ FollowPath:
+ plugin: "nav2_custom_controller::CustomController"
+ max_linear_speed: 0.1
+ max_angular_speed: 1.0
+
+local_costmap:
+ local_costmap:
+ ros__parameters:
+ update_frequency: 5.0
+ publish_frequency: 2.0
+ global_frame: odom
+ robot_base_frame: base_link
+ use_sim_time: True
+ rolling_window: true
+ width: 3
+ height: 3
+ resolution: 0.05
+ robot_radius: 0.22
+ plugins: ["voxel_layer", "inflation_layer"]
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ voxel_layer:
+ plugin: "nav2_costmap_2d::VoxelLayer"
+ enabled: True
+ publish_voxel_map: True
+ origin_z: 0.0
+ z_resolution: 0.05
+ z_voxels: 16
+ max_obstacle_height: 2.0
+ mark_threshold: 0
+ observation_sources: scan
+ scan:
+ topic: /scan
+ max_obstacle_height: 2.0
+ clearing: True
+ marking: True
+ data_type: "LaserScan"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ static_layer:
+ plugin: "nav2_costmap_2d::StaticLayer"
+ map_subscribe_transient_local: True
+ always_send_full_costmap: True
+
+global_costmap:
+ global_costmap:
+ ros__parameters:
+ update_frequency: 1.0
+ publish_frequency: 1.0
+ global_frame: map
+ robot_base_frame: base_link
+ use_sim_time: True
+ robot_radius: 0.22
+ resolution: 0.05
+ track_unknown_space: true
+ plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
+ obstacle_layer:
+ plugin: "nav2_costmap_2d::ObstacleLayer"
+ enabled: True
+ observation_sources: scan
+ scan:
+ topic: /scan
+ max_obstacle_height: 2.0
+ clearing: True
+ marking: True
+ data_type: "LaserScan"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ static_layer:
+ plugin: "nav2_costmap_2d::StaticLayer"
+ map_subscribe_transient_local: True
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ always_send_full_costmap: True
+
+map_server:
+ ros__parameters:
+ use_sim_time: True
+ # Overridden in launch by the "map" launch configuration or provided default value.
+ # To use in yaml, remove the default "map" value in the tb3_simulation_launch.py file & provide full path to map below.
+ yaml_filename: ""
+
+map_saver:
+ ros__parameters:
+ use_sim_time: True
+ save_map_timeout: 5.0
+ free_thresh_default: 0.25
+ occupied_thresh_default: 0.65
+ map_subscribe_transient_local: True
+
+planner_server:
+ ros__parameters:
+ expected_planner_frequency: 20.0
+ use_sim_time: True
+ planner_plugins: ["GridBased"]
+ # GridBased:
+ # plugin: "nav2_navfn_planner/NavfnPlanner"
+ # tolerance: 0.5
+ # use_astar: false
+ # allow_unknown: true
+ # plugins: ["GridBased"]
+ # use_sim_time: True
+ GridBased:
+ plugin: "nav2_custom_planner/CustomPlanner"
+ interpolation_resolution: 0.1
+
+smoother_server:
+ ros__parameters:
+ use_sim_time: True
+ smoother_plugins: ["simple_smoother"]
+ simple_smoother:
+ plugin: "nav2_smoother::SimpleSmoother"
+ tolerance: 1.0e-10
+ max_its: 1000
+ do_refinement: True
+
+behavior_server:
+ ros__parameters:
+ costmap_topic: local_costmap/costmap_raw
+ footprint_topic: local_costmap/published_footprint
+ cycle_frequency: 10.0
+ behavior_plugins: ["spin", "backup", "drive_on_heading", "assisted_teleop", "wait"]
+ spin:
+ plugin: "nav2_behaviors/Spin"
+ backup:
+ plugin: "nav2_behaviors/BackUp"
+ drive_on_heading:
+ plugin: "nav2_behaviors/DriveOnHeading"
+ wait:
+ plugin: "nav2_behaviors/Wait"
+ assisted_teleop:
+ plugin: "nav2_behaviors/AssistedTeleop"
+ global_frame: odom
+ robot_base_frame: base_link
+ transform_tolerance: 0.1
+ use_sim_time: true
+ simulate_ahead_time: 2.0
+ max_rotational_vel: 1.0
+ min_rotational_vel: 0.4
+ rotational_acc_lim: 3.2
+
+robot_state_publisher:
+ ros__parameters:
+ use_sim_time: True
+
+waypoint_follower:
+ ros__parameters:
+ use_sim_time: True
+ loop_rate: 20
+ stop_on_failure: false
+ waypoint_task_executor_plugin: "wait_at_waypoint"
+ wait_at_waypoint:
+ plugin: "nav2_waypoint_follower::WaitAtWaypoint"
+ enabled: True
+ waypoint_pause_duration: 200
+
+velocity_smoother:
+ ros__parameters:
+ use_sim_time: True
+ smoothing_frequency: 20.0
+ scale_velocities: False
+ feedback: "OPEN_LOOP"
+ max_velocity: [0.26, 0.0, 1.0]
+ min_velocity: [-0.26, 0.0, -1.0]
+ max_accel: [2.5, 0.0, 3.2]
+ max_decel: [-2.5, 0.0, -3.2]
+ odom_topic: "odom"
+ odom_duration: 0.1
+ deadband_velocity: [0.0, 0.0, 0.0]
+ velocity_timeout: 1.0
diff --git a/chapt8/chapt8_ws/src/fishbot_navigation2/launch/navigation2.launch.py b/chapt8/chapt8_ws/src/fishbot_navigation2/launch/navigation2.launch.py
new file mode 100644
index 0000000..a55c3d5
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_navigation2/launch/navigation2.launch.py
@@ -0,0 +1,49 @@
+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():
+ # 获取与拼接默认路径
+ fishbot_navigation2_dir = get_package_share_directory(
+ 'fishbot_navigation2')
+ nav2_bringup_dir = get_package_share_directory('nav2_bringup')
+ rviz_config_dir = os.path.join(
+ nav2_bringup_dir, 'rviz', 'nav2_default_view.rviz')
+
+ # 创建 Launch 配置
+ use_sim_time = launch.substitutions.LaunchConfiguration(
+ 'use_sim_time', default='true')
+ map_yaml_path = launch.substitutions.LaunchConfiguration(
+ 'map', default=os.path.join(fishbot_navigation2_dir, 'maps', 'room.yaml'))
+ nav2_param_path = launch.substitutions.LaunchConfiguration(
+ 'params_file', default=os.path.join(fishbot_navigation2_dir, 'config', 'nav2_params.yaml'))
+
+ return launch.LaunchDescription([
+ # 声明新的 Launch 参数
+ launch.actions.DeclareLaunchArgument('use_sim_time', default_value=use_sim_time,
+ description='Use simulation (Gazebo) clock if true'),
+ launch.actions.DeclareLaunchArgument('map', default_value=map_yaml_path,
+ description='Full path to map file to load'),
+ launch.actions.DeclareLaunchArgument('params_file', default_value=nav2_param_path,
+ description='Full path to param file to load'),
+
+ launch.actions.IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ [nav2_bringup_dir, '/launch', '/bringup_launch.py']),
+ # 使用 Launch 参数替换原有参数
+ launch_arguments={
+ 'map': map_yaml_path,
+ 'use_sim_time': use_sim_time,
+ 'params_file': nav2_param_path}.items(),
+ ),
+ launch_ros.actions.Node(
+ package='rviz2',
+ executable='rviz2',
+ name='rviz2',
+ arguments=['-d', rviz_config_dir],
+ parameters=[{'use_sim_time': use_sim_time}],
+ output='screen'),
+ ])
\ No newline at end of file
diff --git a/chapt8/chapt8_ws/src/fishbot_navigation2/maps/room.pgm b/chapt8/chapt8_ws/src/fishbot_navigation2/maps/room.pgm
new file mode 100644
index 0000000..6bdfd22
Binary files /dev/null and b/chapt8/chapt8_ws/src/fishbot_navigation2/maps/room.pgm differ
diff --git a/chapt8/chapt8_ws/src/fishbot_navigation2/maps/room.yaml b/chapt8/chapt8_ws/src/fishbot_navigation2/maps/room.yaml
new file mode 100644
index 0000000..d0cbed8
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_navigation2/maps/room.yaml
@@ -0,0 +1,7 @@
+image: room.pgm
+mode: trinary
+resolution: 0.05
+origin: [-10.4, -6.53, 0]
+negate: 0
+occupied_thresh: 0.65
+free_thresh: 0.25
\ No newline at end of file
diff --git a/chapt8/chapt8_ws/src/fishbot_navigation2/package.xml b/chapt8/chapt8_ws/src/fishbot_navigation2/package.xml
new file mode 100644
index 0000000..19065ec
--- /dev/null
+++ b/chapt8/chapt8_ws/src/fishbot_navigation2/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ fishbot_navigation2
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_cmake
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+
diff --git a/chapt8/chapt8_ws/src/nav2_custom_controller/CMakeLists.txt b/chapt8/chapt8_ws/src/nav2_custom_controller/CMakeLists.txt
new file mode 100644
index 0000000..380a549
--- /dev/null
+++ b/chapt8/chapt8_ws/src/nav2_custom_controller/CMakeLists.txt
@@ -0,0 +1,45 @@
+cmake_minimum_required(VERSION 3.8)
+project(nav2_custom_controller)
+
+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(nav2_core REQUIRED)
+find_package(pluginlib 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()
+
+# 包含头文件目录
+include_directories(include)
+# 定义库名称
+set(library_name ${PROJECT_NAME}_plugin)
+# 创建共享库
+add_library(${library_name} SHARED src/custom_controller.cpp)
+# 指定库的依赖关系
+ament_target_dependencies(${library_name} nav2_core pluginlib)
+# 安装库文件到指定目录
+install(TARGETS ${library_name}
+ ARCHIVE DESTINATION lib
+ LIBRARY DESTINATION lib
+ RUNTIME DESTINATION lib/${PROJECT_NAME}
+)
+# 安装头文件到指定目录
+install(DIRECTORY include/
+ DESTINATION include/)
+# 导出插件描述文件
+pluginlib_export_plugin_description_file(nav2_core nav2_custom_controller.xml)
+
+ament_package()
diff --git a/chapt8/chapt8_ws/src/nav2_custom_controller/include/nav2_custom_controller/custom_controller.hpp b/chapt8/chapt8_ws/src/nav2_custom_controller/include/nav2_custom_controller/custom_controller.hpp
new file mode 100644
index 0000000..599cb2d
--- /dev/null
+++ b/chapt8/chapt8_ws/src/nav2_custom_controller/include/nav2_custom_controller/custom_controller.hpp
@@ -0,0 +1,62 @@
+#ifndef NAV2_CUSTOM_CONTROLLER__NAV2_CUSTOM_CONTROLLER_HPP_
+#define NAV2_CUSTOM_CONTROLLER__NAV2_CUSTOM_CONTROLLER_HPP_
+
+#include
+#include
+#include
+
+#include "nav2_core/controller.hpp"
+#include "rclcpp/rclcpp.hpp"
+#include "nav2_util/robot_utils.hpp"
+
+
+namespace nav2_custom_controller {
+
+class CustomController : public nav2_core::Controller {
+public:
+ CustomController() = default;
+ ~CustomController() override = default;
+ void configure(
+ const rclcpp_lifecycle::LifecycleNode::WeakPtr &parent, std::string name,
+ std::shared_ptr tf,
+ std::shared_ptr costmap_ros) override;
+ void cleanup() override;
+ void activate() override;
+ void deactivate() override;
+ geometry_msgs::msg::TwistStamped
+ computeVelocityCommands(const geometry_msgs::msg::PoseStamped &pose,
+ const geometry_msgs::msg::Twist &velocity,
+ nav2_core::GoalChecker * goal_checker) override;
+ void setPlan(const nav_msgs::msg::Path &path) override;
+ void setSpeedLimit(const double &speed_limit,
+ const bool &percentage) override;
+
+protected:
+ // 存储插件名称
+ std::string plugin_name_;
+ // 存储坐标变换缓存指针,可用于查询坐标关系
+ std::shared_ptr tf_;
+ // 存储代价地图
+ std::shared_ptr costmap_ros_;
+ // 存储节点指针
+ nav2_util::LifecycleNode::SharedPtr node_;
+ // 存储全局代价地图
+ nav2_costmap_2d::Costmap2D *costmap_;
+ // 存储 setPlan 提供的全局路径
+ nav_msgs::msg::Path global_plan_;
+ // 参数:最大线速度角速度
+ double max_angular_speed_;
+ double max_linear_speed_;
+
+ // 获取路径中距离当前点最近的点
+ geometry_msgs::msg::PoseStamped
+ getNearestTargetPose(const geometry_msgs::msg::PoseStamped ¤t_pose);
+ // 计算目标点方向和当前位置的角度差
+ double
+ calculateAngleDifference(const geometry_msgs::msg::PoseStamped ¤t_pose,
+ const geometry_msgs::msg::PoseStamped &target_pose);
+};
+
+} // namespace nav2_custom_controller
+
+#endif // NAV2_CUSTOM_CONTROLLER__NAV2_CUSTOM_CONTROLLER_HPP_
\ No newline at end of file
diff --git a/chapt8/chapt8_ws/src/nav2_custom_controller/nav2_custom_controller.xml b/chapt8/chapt8_ws/src/nav2_custom_controller/nav2_custom_controller.xml
new file mode 100644
index 0000000..06e97ef
--- /dev/null
+++ b/chapt8/chapt8_ws/src/nav2_custom_controller/nav2_custom_controller.xml
@@ -0,0 +1,9 @@
+
+
+
+
+ 自定义导航控制器
+
+
+
+
\ No newline at end of file
diff --git a/chapt8/chapt8_ws/src/nav2_custom_controller/package.xml b/chapt8/chapt8_ws/src/nav2_custom_controller/package.xml
new file mode 100644
index 0000000..84534e0
--- /dev/null
+++ b/chapt8/chapt8_ws/src/nav2_custom_controller/package.xml
@@ -0,0 +1,22 @@
+
+
+
+ nav2_custom_controller
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_cmake
+
+ nav2_core
+ pluginlib
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+
+
diff --git a/chapt8/chapt8_ws/src/nav2_custom_controller/src/custom_controller.cpp b/chapt8/chapt8_ws/src/nav2_custom_controller/src/custom_controller.cpp
new file mode 100644
index 0000000..541ea61
--- /dev/null
+++ b/chapt8/chapt8_ws/src/nav2_custom_controller/src/custom_controller.cpp
@@ -0,0 +1,142 @@
+#include "nav2_custom_controller/custom_controller.hpp"
+#include "nav2_core/exceptions.hpp"
+#include "nav2_util/geometry_utils.hpp"
+#include "nav2_util/node_utils.hpp"
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace nav2_custom_controller {
+void CustomController::configure(
+ const rclcpp_lifecycle::LifecycleNode::WeakPtr &parent, std::string name,
+ std::shared_ptr tf,
+ std::shared_ptr costmap_ros) {
+ node_ = parent.lock();
+ costmap_ros_ = costmap_ros;
+ tf_ = tf;
+ plugin_name_ = name;
+
+ // 声明并获取参数,设置最大线速度和最大角速度
+ nav2_util::declare_parameter_if_not_declared(
+ node_, plugin_name_ + ".max_linear_speed", rclcpp::ParameterValue(0.1));
+ node_->get_parameter(plugin_name_ + ".max_linear_speed", max_linear_speed_);
+ nav2_util::declare_parameter_if_not_declared(
+ node_, plugin_name_ + ".max_angular_speed", rclcpp::ParameterValue(1.0));
+ node_->get_parameter(plugin_name_ + ".max_angular_speed", max_angular_speed_);
+}
+
+void CustomController::cleanup() {
+ RCLCPP_INFO(node_->get_logger(),
+ "清理控制器:%s 类型为 nav2_custom_controller::CustomController",
+ plugin_name_.c_str());
+}
+
+void CustomController::activate() {
+ RCLCPP_INFO(node_->get_logger(),
+ "激活控制器:%s 类型为 nav2_custom_controller::CustomController",
+ plugin_name_.c_str());
+}
+
+void CustomController::deactivate() {
+ RCLCPP_INFO(node_->get_logger(),
+ "停用控制器:%s 类型为 nav2_custom_controller::CustomController",
+ plugin_name_.c_str());
+}
+
+geometry_msgs::msg::TwistStamped CustomController::computeVelocityCommands(
+ const geometry_msgs::msg::PoseStamped &pose,
+ const geometry_msgs::msg::Twist &, nav2_core::GoalChecker *) {
+ // 1. 检查路径是否为空
+ if (global_plan_.poses.empty()) {
+ throw nav2_core::PlannerException("收到长度为零的路径");
+ }
+
+ // 2.将机器人当前姿态转换到全局计划坐标系中
+ geometry_msgs::msg::PoseStamped pose_in_globalframe;
+ if (!nav2_util::transformPoseInTargetFrame(
+ pose, pose_in_globalframe, *tf_, global_plan_.header.frame_id, 0.1)) {
+ throw nav2_core::PlannerException("无法将机器人姿态转换为全局计划的坐标系");
+ }
+
+ // 3.获取最近的目标点和计算角度差
+ auto target_pose = getNearestTargetPose(pose_in_globalframe);
+ auto angle_diff = calculateAngleDifference(pose_in_globalframe, target_pose);
+
+ // 4.根据角度差计算线速度和角速度
+ geometry_msgs::msg::TwistStamped cmd_vel;
+ cmd_vel.header.frame_id = pose_in_globalframe.header.frame_id;
+ cmd_vel.header.stamp = node_->get_clock()->now();
+ // 根据角度差计算速度,角度差大于 0.3 则原地旋转,否则直行
+ if (fabs(angle_diff) > M_PI/10.0) {
+ cmd_vel.twist.linear.x = .0;
+ cmd_vel.twist.angular.z = fabs(angle_diff) / angle_diff * max_angular_speed_;
+ } else {
+ cmd_vel.twist.linear.x = max_linear_speed_;
+ cmd_vel.twist.angular.z = .0;
+ }
+ RCLCPP_INFO(node_->get_logger(), "控制器:%s 发送速度(%f,%f)",
+ plugin_name_.c_str(), cmd_vel.twist.linear.x,
+ cmd_vel.twist.angular.z);
+ return cmd_vel;
+}
+
+void CustomController::setSpeedLimit(const double &speed_limit,
+ const bool &percentage) {
+ (void)percentage;
+ (void)speed_limit;
+}
+
+void CustomController::setPlan(const nav_msgs::msg::Path &path) {
+ global_plan_ = path;
+}
+
+geometry_msgs::msg::PoseStamped CustomController::getNearestTargetPose(
+ const geometry_msgs::msg::PoseStamped ¤t_pose) {
+ // 1.遍历路径获取路径中距离当前点最近的索引,存储到 nearest_pose_index
+ using nav2_util::geometry_utils::euclidean_distance;
+ int nearest_pose_index = 0;
+ double min_dist = euclidean_distance(current_pose, global_plan_.poses.at(0));
+ for (unsigned int i = 1; i < global_plan_.poses.size(); i++) {
+ double dist = euclidean_distance(current_pose, global_plan_.poses.at(i));
+ if (dist < min_dist) {
+ nearest_pose_index = i;
+ min_dist = dist;
+ }
+ }
+ // 2.从路径中擦除头部到最近点的路径
+ global_plan_.poses.erase(std::begin(global_plan_.poses),
+ std::begin(global_plan_.poses) + nearest_pose_index);
+ // 3.如果只有一个点则直接,否则返回最近点的下一个点
+ if (global_plan_.poses.size() == 1) {
+ return global_plan_.poses.at(0);
+ }
+ return global_plan_.poses.at(1);
+ return current_pose;
+}
+
+double CustomController::calculateAngleDifference(
+ const geometry_msgs::msg::PoseStamped ¤t_pose,
+ const geometry_msgs::msg::PoseStamped &target_pose) {
+ // 计算当前姿态与目标姿态之间的角度差
+ // 1. 获取当前角度
+ float current_robot_yaw = tf2::getYaw(current_pose.pose.orientation);
+ // 2.获取目标点朝向
+ float target_angle =
+ std::atan2(target_pose.pose.position.y - current_pose.pose.position.y,
+ target_pose.pose.position.x - current_pose.pose.position.x);
+ // 3.计算角度差,并转换到 -M_PI 到 M_PI 之间
+ double angle_diff = target_angle - current_robot_yaw;
+ if (angle_diff < -M_PI) {
+ angle_diff += 2.0 * M_PI;
+ } else if (angle_diff > M_PI) {
+ angle_diff -= 2.0 * M_PI;
+ }
+ return angle_diff;
+}
+} // namespace nav2_custom_controller
+
+#include "pluginlib/class_list_macros.hpp"
+PLUGINLIB_EXPORT_CLASS(nav2_custom_controller::CustomController,nav2_core::Controller)
\ No newline at end of file
diff --git a/chapt8/chapt8_ws/src/nav2_custom_planner/CMakeLists.txt b/chapt8/chapt8_ws/src/nav2_custom_planner/CMakeLists.txt
new file mode 100644
index 0000000..867e987
--- /dev/null
+++ b/chapt8/chapt8_ws/src/nav2_custom_planner/CMakeLists.txt
@@ -0,0 +1,45 @@
+cmake_minimum_required(VERSION 3.8)
+project(nav2_custom_planner)
+
+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(nav2_core REQUIRED)
+find_package(pluginlib 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()
+
+# 包含头文件目录
+include_directories(include)
+# 定义库名称
+set(library_name ${PROJECT_NAME}_plugin)
+# 创建共享库
+add_library(${library_name} SHARED src/nav2_custom_planner.cpp)
+# 指定库的依赖关系
+ament_target_dependencies(${library_name} nav2_core pluginlib)
+# 安装库文件到指定目录
+install(TARGETS ${library_name}
+ ARCHIVE DESTINATION lib
+ LIBRARY DESTINATION lib
+ RUNTIME DESTINATION lib/${PROJECT_NAME}
+)
+# 安装头文件到指定目录
+install(DIRECTORY include/
+ DESTINATION include/ )
+# 导出插件描述文件
+pluginlib_export_plugin_description_file(nav2_core custom_planner_plugin.xml)
+
+ament_package()
diff --git a/chapt8/chapt8_ws/src/nav2_custom_planner/custom_planner_plugin.xml b/chapt8/chapt8_ws/src/nav2_custom_planner/custom_planner_plugin.xml
new file mode 100644
index 0000000..57621e6
--- /dev/null
+++ b/chapt8/chapt8_ws/src/nav2_custom_planner/custom_planner_plugin.xml
@@ -0,0 +1,5 @@
+
+
+ 是一个自定义示例插件,用于生成自定义路径。
+
+
\ No newline at end of file
diff --git a/chapt8/chapt8_ws/src/nav2_custom_planner/include/nav2_custom_planner/nav2_custom_planner.hpp b/chapt8/chapt8_ws/src/nav2_custom_planner/include/nav2_custom_planner/nav2_custom_planner.hpp
new file mode 100644
index 0000000..c92ea49
--- /dev/null
+++ b/chapt8/chapt8_ws/src/nav2_custom_planner/include/nav2_custom_planner/nav2_custom_planner.hpp
@@ -0,0 +1,51 @@
+#ifndef NAV2_CUSTOM_PLANNER__NAV2_CUSTOM_PLANNER_HPP_
+#define NAV2_CUSTOM_PLANNER__NAV2_CUSTOM_PLANNER_HPP_
+#include
+#include
+#include "geometry_msgs/msg/point.hpp"
+#include "geometry_msgs/msg/pose_stamped.hpp"
+#include "rclcpp/rclcpp.hpp"
+#include "nav2_core/global_planner.hpp"
+#include "nav2_costmap_2d/costmap_2d_ros.hpp"
+#include "nav2_util/lifecycle_node.hpp"
+#include "nav2_util/robot_utils.hpp"
+#include "nav_msgs/msg/path.hpp"
+
+namespace nav2_custom_planner {
+// 自定义导航规划器类
+class CustomPlanner : public nav2_core::GlobalPlanner {
+public:
+ CustomPlanner() = default;
+ ~CustomPlanner() = default;
+ // 插件配置方法
+ void configure(
+ const rclcpp_lifecycle::LifecycleNode::WeakPtr &parent, std::string name,
+ std::shared_ptr tf,
+ std::shared_ptr costmap_ros) override;
+ // 插件清理方法
+ void cleanup() override;
+ // 插件激活方法
+ void activate() override;
+ // 插件停用方法
+ void deactivate() override;
+ // 为给定的起始和目标位姿创建路径的方法
+ nav_msgs::msg::Path
+ createPlan(const geometry_msgs::msg::PoseStamped &start,
+ const geometry_msgs::msg::PoseStamped &goal) override;
+
+private:
+ // 坐标变换缓存指针,可用于查询坐标关系
+ std::shared_ptr tf_;
+ // 节点指针
+ nav2_util::LifecycleNode::SharedPtr node_;
+ // 全局代价地图
+ nav2_costmap_2d::Costmap2D *costmap_;
+ // 全局代价地图的坐标系
+ std::string global_frame_, name_;
+ // 插值分辨率
+ double interpolation_resolution_;
+};
+
+} // namespace nav2_custom_planner
+
+#endif // NAV2_CUSTOM_PLANNER__NAV2_CUSTOM_PLANNER_HPP_
\ No newline at end of file
diff --git a/chapt8/chapt8_ws/src/nav2_custom_planner/package.xml b/chapt8/chapt8_ws/src/nav2_custom_planner/package.xml
new file mode 100644
index 0000000..dbb4261
--- /dev/null
+++ b/chapt8/chapt8_ws/src/nav2_custom_planner/package.xml
@@ -0,0 +1,22 @@
+
+
+
+ nav2_custom_planner
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_cmake
+
+ nav2_core
+ pluginlib
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+
+
diff --git a/chapt8/chapt8_ws/src/nav2_custom_planner/src/nav2_custom_planner.cpp b/chapt8/chapt8_ws/src/nav2_custom_planner/src/nav2_custom_planner.cpp
new file mode 100644
index 0000000..8872a57
--- /dev/null
+++ b/chapt8/chapt8_ws/src/nav2_custom_planner/src/nav2_custom_planner.cpp
@@ -0,0 +1,126 @@
+#include "nav2_util/node_utils.hpp"
+#include
+#include
+#include
+
+#include "nav2_core/exceptions.hpp"
+#include "nav2_custom_planner/nav2_custom_planner.hpp"
+
+namespace nav2_custom_planner
+{
+
+ void CustomPlanner::configure(
+ const rclcpp_lifecycle::LifecycleNode::WeakPtr &parent, std::string name,
+ std::shared_ptr tf,
+ std::shared_ptr costmap_ros)
+ {
+ tf_ = tf;
+ node_ = parent.lock();
+ name_ = name;
+ costmap_ = costmap_ros->getCostmap();
+ global_frame_ = costmap_ros->getGlobalFrameID();
+ // 参数初始化
+ nav2_util::declare_parameter_if_not_declared(
+ node_, name_ + ".interpolation_resolution", rclcpp::ParameterValue(0.1));
+ node_->get_parameter(name_ + ".interpolation_resolution",
+ interpolation_resolution_);
+ }
+
+ void CustomPlanner::cleanup()
+ {
+ RCLCPP_INFO(node_->get_logger(), "正在清理类型为 CustomPlanner 的插件 %s",
+ name_.c_str());
+ }
+
+ void CustomPlanner::activate()
+ {
+ RCLCPP_INFO(node_->get_logger(), "正在激活类型为 CustomPlanner 的插件 %s",
+ name_.c_str());
+ }
+
+ void CustomPlanner::deactivate()
+ {
+ RCLCPP_INFO(node_->get_logger(), "正在停用类型为 CustomPlanner 的插件 %s",
+ name_.c_str());
+ }
+
+ nav_msgs::msg::Path
+ CustomPlanner::createPlan(const geometry_msgs::msg::PoseStamped &start,
+ const geometry_msgs::msg::PoseStamped &goal)
+ {
+ // 1.声明并初始化 global_path
+ nav_msgs::msg::Path global_path;
+ global_path.poses.clear();
+ global_path.header.stamp = node_->now();
+ global_path.header.frame_id = global_frame_;
+
+ // 2.检查目标和起始状态是否在全局坐标系中
+ if (start.header.frame_id != global_frame_)
+ {
+ RCLCPP_ERROR(node_->get_logger(), "规划器仅接受来自 %s 坐标系的起始位置",
+ global_frame_.c_str());
+ return global_path;
+ }
+
+ if (goal.header.frame_id != global_frame_)
+ {
+ RCLCPP_INFO(node_->get_logger(), "规划器仅接受来自 %s 坐标系的目标位置",
+ global_frame_.c_str());
+ return global_path;
+ }
+
+ // 3.计算当前插值分辨率 interpolation_resolution_ 下的循环次数和步进值
+ int total_number_of_loop =
+ std::hypot(goal.pose.position.x - start.pose.position.x,
+ goal.pose.position.y - start.pose.position.y) /
+ interpolation_resolution_;
+ double x_increment =
+ (goal.pose.position.x - start.pose.position.x) / total_number_of_loop;
+ double y_increment =
+ (goal.pose.position.y - start.pose.position.y) / total_number_of_loop;
+
+ // 4. 生成路径
+ for (int i = 0; i < total_number_of_loop; ++i)
+ {
+ geometry_msgs::msg::PoseStamped pose; // 生成一个点
+ pose.pose.position.x = start.pose.position.x + x_increment * i;
+ pose.pose.position.y = start.pose.position.y + y_increment * i;
+ pose.pose.position.z = 0.0;
+ pose.header.stamp = node_->now();
+ pose.header.frame_id = global_frame_;
+ // 将该点放到路径中
+ global_path.poses.push_back(pose);
+ }
+
+ // 5.使用 costmap 检查该条路径是否经过障碍物
+ for (geometry_msgs::msg::PoseStamped pose : global_path.poses)
+ {
+ unsigned int mx, my; // 将点的坐标转换为栅格坐标
+ if (costmap_->worldToMap(pose.pose.position.x, pose.pose.position.y, mx, my))
+ {
+ unsigned char cost = costmap_->getCost(mx, my); // 获取对应栅格的代价值
+ // 如果存在致命障碍物则抛出异常
+ if (cost == nav2_costmap_2d::LETHAL_OBSTACLE)
+ {
+ RCLCPP_WARN(node_->get_logger(),"在(%f,%f)检测到致命障碍物,规划失败。",
+ pose.pose.position.x, pose.pose.position.y);
+ throw nav2_core::PlannerException(
+ "无法创建目标规划: " + std::to_string(goal.pose.position.x) + "," +
+ std::to_string(goal.pose.position.y));
+ }
+ }
+ }
+
+ // 6.收尾,将目标点作为路径的最后一个点并返回路径
+ geometry_msgs::msg::PoseStamped goal_pose = goal;
+ goal_pose.header.stamp = node_->now();
+ goal_pose.header.frame_id = global_frame_;
+ global_path.poses.push_back(goal_pose);
+ return global_path;
+ }
+
+} // namespace nav2_custom_planner
+
+#include "pluginlib/class_list_macros.hpp"
+PLUGINLIB_EXPORT_CLASS(nav2_custom_planner::CustomPlanner,
+ nav2_core::GlobalPlanner)
\ No newline at end of file
diff --git a/chapt8/learn_pluginlib/src/motion_control_system/CMakeLists.txt b/chapt8/learn_pluginlib/src/motion_control_system/CMakeLists.txt
new file mode 100644
index 0000000..2581b7a
--- /dev/null
+++ b/chapt8/learn_pluginlib/src/motion_control_system/CMakeLists.txt
@@ -0,0 +1,45 @@
+cmake_minimum_required(VERSION 3.8)
+project(motion_control_system)
+
+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(pluginlib 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()
+
+include_directories(include)
+# ================添加库文件=====================
+add_library(spin_motion_controller SHARED src/spin_motion_controller.cpp)
+ament_target_dependencies(spin_motion_controller pluginlib )
+
+add_executable(test_plugin src/test_plugin.cpp)
+ament_target_dependencies(test_plugin pluginlib)
+install(TARGETS test_plugin
+ DESTINATION lib/${PROJECT_NAME}
+)
+
+install(TARGETS spin_motion_controller
+ ARCHIVE DESTINATION lib
+ LIBRARY DESTINATION lib
+ RUNTIME DESTINATION bin
+)
+install(DIRECTORY include/
+ DESTINATION include/
+)
+# 导出插件描述文件
+pluginlib_export_plugin_description_file(motion_control_system spin_motion_plugins.xml)
+ament_package()
diff --git a/chapt8/learn_pluginlib/src/motion_control_system/LICENSE b/chapt8/learn_pluginlib/src/motion_control_system/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/chapt8/learn_pluginlib/src/motion_control_system/LICENSE
@@ -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.
diff --git a/chapt8/learn_pluginlib/src/motion_control_system/include/motion_control_system/motion_control_interface.hpp b/chapt8/learn_pluginlib/src/motion_control_system/include/motion_control_system/motion_control_interface.hpp
new file mode 100644
index 0000000..97d4000
--- /dev/null
+++ b/chapt8/learn_pluginlib/src/motion_control_system/include/motion_control_system/motion_control_interface.hpp
@@ -0,0 +1,15 @@
+#ifndef MOTION_CONTROL_INTERFACE_HPP
+#define MOTION_CONTROL_INTERFACE_HPP
+
+namespace motion_control_system {
+
+class MotionController {
+public:
+ virtual void start() = 0;
+ virtual void stop() = 0;
+ virtual ~MotionController() {}
+};
+
+} // namespace motion_control_system
+
+#endif // MOTION_CONTROL_INTERFACE_HPP
\ No newline at end of file
diff --git a/chapt8/learn_pluginlib/src/motion_control_system/include/motion_control_system/spin_motion_controller.hpp b/chapt8/learn_pluginlib/src/motion_control_system/include/motion_control_system/spin_motion_controller.hpp
new file mode 100644
index 0000000..028b707
--- /dev/null
+++ b/chapt8/learn_pluginlib/src/motion_control_system/include/motion_control_system/spin_motion_controller.hpp
@@ -0,0 +1,17 @@
+#ifndef SPIN_MOTION_CONTROLLER_HPP
+#define SPIN_MOTION_CONTROLLER_HPP
+
+#include "motion_control_system/motion_control_interface.hpp"
+
+namespace motion_control_system
+{
+ class SpinMotionController : public MotionController
+ {
+ public:
+ void start() override;
+ void stop() override;
+ };
+
+} // namespace motion_control_system
+
+#endif // SPIN_MOTION_CONTROLLER_HPP
\ No newline at end of file
diff --git a/chapt8/learn_pluginlib/src/motion_control_system/package.xml b/chapt8/learn_pluginlib/src/motion_control_system/package.xml
new file mode 100644
index 0000000..a9d3c4b
--- /dev/null
+++ b/chapt8/learn_pluginlib/src/motion_control_system/package.xml
@@ -0,0 +1,20 @@
+
+
+
+ motion_control_system
+ 0.0.0
+ TODO: Package description
+ fishros
+ Apache-2.0
+
+ ament_cmake
+
+ pluginlib
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+
diff --git a/chapt8/learn_pluginlib/src/motion_control_system/spin_motion_plugins.xml b/chapt8/learn_pluginlib/src/motion_control_system/spin_motion_plugins.xml
new file mode 100644
index 0000000..5eff615
--- /dev/null
+++ b/chapt8/learn_pluginlib/src/motion_control_system/spin_motion_plugins.xml
@@ -0,0 +1,5 @@
+
+
+ Spin Motion Controller
+
+
\ No newline at end of file
diff --git a/chapt8/learn_pluginlib/src/motion_control_system/src/spin_motion_controller.cpp b/chapt8/learn_pluginlib/src/motion_control_system/src/spin_motion_controller.cpp
new file mode 100644
index 0000000..0af1c7d
--- /dev/null
+++ b/chapt8/learn_pluginlib/src/motion_control_system/src/spin_motion_controller.cpp
@@ -0,0 +1,17 @@
+#include
+#include "motion_control_system/spin_motion_controller.hpp"
+namespace motion_control_system
+{
+ void SpinMotionController::start()
+ {
+ // 实现旋转运动控制逻辑
+ std::cout << "SpinMotionController::start" << std::endl;
+ }
+ void SpinMotionController::stop()
+ {
+ // 停止运动控制
+ std::cout << "SpinMotionController::stop" << std::endl;
+ }
+} // namespace motion_control_system
+#include "pluginlib/class_list_macros.hpp"
+PLUGINLIB_EXPORT_CLASS(motion_control_system::SpinMotionController, motion_control_system::MotionController)
\ No newline at end of file
diff --git a/chapt8/learn_pluginlib/src/motion_control_system/src/test_plugin.cpp b/chapt8/learn_pluginlib/src/motion_control_system/src/test_plugin.cpp
new file mode 100644
index 0000000..29c9e0f
--- /dev/null
+++ b/chapt8/learn_pluginlib/src/motion_control_system/src/test_plugin.cpp
@@ -0,0 +1,20 @@
+#include "motion_control_system/motion_control_interface.hpp"
+#include
+
+int main(int argc, char **argv) {
+ // 判断参数数量是否合法
+ if (argc != 2)
+ return 0;
+ // 通过命令行参数,选择要加载的插件,argv[0]是可执行文件名,argv[1]表示参数名
+ std::string controller_name = argv[1];
+ // 1.通过功能包名称和基类名称创建控制器加载器
+ pluginlib::ClassLoader
+ controller_loader("motion_control_system",
+ "motion_control_system::MotionController");
+ // 2.使用加载器加载指定名称的插件,返回的是指定插件类的对象的指针
+ auto controller = controller_loader.createSharedInstance(controller_name);
+ // 3.调用插件的方法
+ controller->start();
+ controller->stop();
+ return 0;
+}
\ No newline at end of file
diff --git a/chapt9/fishbot_ws/src/fishbot_bringup/CMakeLists.txt b/chapt9/fishbot_ws/src/fishbot_bringup/CMakeLists.txt
new file mode 100644
index 0000000..a3efba1
--- /dev/null
+++ b/chapt9/fishbot_ws/src/fishbot_bringup/CMakeLists.txt
@@ -0,0 +1,41 @@
+cmake_minimum_required(VERSION 3.8)
+project(fishbot_bringup)
+
+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( 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(tf2 REQUIRED)
+find_package(tf2_ros REQUIRED)
+find_package(geometry_msgs REQUIRED)
+find_package(nav_msgs REQUIRED)
+
+add_executable(odom2tf src/odom2tf.cpp)
+ament_target_dependencies(odom2tf
+ rclcpp tf2 nav_msgs geometry_msgs tf2_ros
+)
+install(TARGETS odom2tf
+ DESTINATION lib/${PROJECT_NAME})
+install(DIRECTORY
+ launch
+ DESTINATION share/${PROJECT_NAME}
+)
+ament_package()
diff --git a/chapt9/fishbot_ws/src/fishbot_bringup/launch/bringup.launch.py b/chapt9/fishbot_ws/src/fishbot_bringup/launch/bringup.launch.py
new file mode 100644
index 0000000..71fffdd
--- /dev/null
+++ b/chapt9/fishbot_ws/src/fishbot_bringup/launch/bringup.launch.py
@@ -0,0 +1,50 @@
+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():
+ fishbot_bringup_dir = get_package_share_directory(
+ 'fishbot_bringup')
+ ydlidar_ros2_dir = get_package_share_directory(
+ 'ydlidar')
+
+ urdf2tf = launch.actions.IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ [fishbot_bringup_dir, '/launch', '/urdf2tf.launch.py']),
+ )
+
+ odom2tf = launch_ros.actions.Node(
+ package='fishbot_bringup',
+ executable='odom2tf',
+ output='screen'
+ )
+
+ microros_agent = launch_ros.actions.Node(
+ package='micro_ros_agent',
+ executable='micro_ros_agent',
+ arguments=['udp4','--port','8888'],
+ output='screen'
+ )
+
+ ros_serail2wifi = launch_ros.actions.Node(
+ package='ros_serail2wifi',
+ executable='tcp_server',
+ parameters=[{'serial_port': '/tmp/tty_laser'}],
+ output='screen'
+ )
+
+ ydlidar = launch.actions.IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ [ydlidar_ros2_dir, '/launch', '/ydlidar_launch.py']),
+ )
+
+ # 使用 TimerAction 启动后 5 秒执行 ydlidar 节点
+ ydlidar_delay = launch.actions.TimerAction(period=5.0, actions=[ydlidar])
+ return launch.LaunchDescription([
+ urdf2tf,
+ odom2tf,
+ microros_agent,
+ ros_serail2wifi,
+ ydlidar_delay
+ ])
\ No newline at end of file
diff --git a/chapt9/fishbot_ws/src/fishbot_bringup/launch/urdf2tf.launch.py b/chapt9/fishbot_ws/src/fishbot_bringup/launch/urdf2tf.launch.py
new file mode 100644
index 0000000..3c1e76e
--- /dev/null
+++ b/chapt9/fishbot_ws/src/fishbot_bringup/launch/urdf2tf.launch.py
@@ -0,0 +1,33 @@
+import launch
+import launch_ros
+from ament_index_python.packages import get_package_share_directory
+
+
+def generate_launch_description():
+ # 获取默认路径
+ urdf_tutorial_path = get_package_share_directory('fishbot_description')
+ fishbot_model_path = urdf_tutorial_path + '/urdf/fishbot.urdf'
+ # 为 Launch 声明参数
+ action_declare_arg_mode_path = launch.actions.DeclareLaunchArgument(
+ name='model', default_value=str(fishbot_model_path),
+ description='URDF 的绝对路径')
+ # 获取文件内容生成新的参数
+ robot_description = launch_ros.parameter_descriptions.ParameterValue(
+ launch.substitutions.Command(
+ ['cat ', launch.substitutions.LaunchConfiguration('model')]),
+ value_type=str)
+ # 状态发布节点
+ robot_state_publisher_node = launch_ros.actions.Node(
+ package='robot_state_publisher',
+ executable='robot_state_publisher',
+ parameters=[{'robot_description': robot_description}]
+ )
+ # 关节状态发布节点
+ joint_state_publisher_node = launch_ros.actions.Node(
+ package='joint_state_publisher',
+ executable='joint_state_publisher',
+ )
+ return launch.LaunchDescription([
+ action_declare_arg_mode_path, joint_state_publisher_node,
+ robot_state_publisher_node,
+ ])
\ No newline at end of file
diff --git a/chapt9/fishbot_ws/src/fishbot_bringup/package.xml b/chapt9/fishbot_ws/src/fishbot_bringup/package.xml
new file mode 100644
index 0000000..8f7d5d7
--- /dev/null
+++ b/chapt9/fishbot_ws/src/fishbot_bringup/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ fishbot_bringup
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_cmake
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+
diff --git a/chapt9/fishbot_ws/src/fishbot_bringup/src/odom2tf.cpp b/chapt9/fishbot_ws/src/fishbot_bringup/src/odom2tf.cpp
new file mode 100644
index 0000000..310eac6
--- /dev/null
+++ b/chapt9/fishbot_ws/src/fishbot_bringup/src/odom2tf.cpp
@@ -0,0 +1,44 @@
+#include
+#include
+#include
+#include
+#include
+
+class OdomTopic2TF : public rclcpp::Node {
+public:
+ OdomTopic2TF(std::string name) : Node(name) {
+ // 创建 odom 话题订阅者,使用传感器数据的 Qos
+ odom_subscribe_ = this->create_subscription(
+ "odom", rclcpp::SensorDataQoS(),
+ std::bind(&OdomTopic2TF::odom_callback_, this, std::placeholders::_1));
+ // 创建一个tf2_ros::TransformBroadcaster用于广播坐标变换
+ tf_broadcaster_ = std::make_unique(this);
+ }
+
+private:
+ rclcpp::Subscription::SharedPtr odom_subscribe_;
+ std::unique_ptr tf_broadcaster_;
+ // 回调函数,处理接收到的odom消息,并发布tf
+ void odom_callback_(const nav_msgs::msg::Odometry::SharedPtr msg) {
+ geometry_msgs::msg::TransformStamped transform;
+ transform.header = msg->header; // 使用消息的时间戳和框架ID
+ transform.child_frame_id = msg->child_frame_id;
+ transform.transform.translation.x = msg->pose.pose.position.x;
+ transform.transform.translation.y = msg->pose.pose.position.y;
+ transform.transform.translation.z = msg->pose.pose.position.z;
+ transform.transform.rotation.x = msg->pose.pose.orientation.x;
+ transform.transform.rotation.y = msg->pose.pose.orientation.y;
+ transform.transform.rotation.z = msg->pose.pose.orientation.z;
+ transform.transform.rotation.w = msg->pose.pose.orientation.w;
+ // 广播坐标变换信息
+ tf_broadcaster_->sendTransform(transform);
+ };
+};
+
+int main(int argc, char **argv) {
+ rclcpp::init(argc, argv);
+ auto node = std::make_shared("odom2tf");
+ rclcpp::spin(node);
+ rclcpp::shutdown();
+ return 0;
+}
\ No newline at end of file
diff --git a/chapt9/fishbot_ws/src/fishbot_description/CMakeLists.txt b/chapt9/fishbot_ws/src/fishbot_description/CMakeLists.txt
new file mode 100644
index 0000000..80f85e1
--- /dev/null
+++ b/chapt9/fishbot_ws/src/fishbot_description/CMakeLists.txt
@@ -0,0 +1,31 @@
+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( 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
+ urdf
+ DESTINATION share/${PROJECT_NAME}
+)
+
+ament_package()
diff --git a/chapt9/fishbot_ws/src/fishbot_description/package.xml b/chapt9/fishbot_ws/src/fishbot_description/package.xml
new file mode 100644
index 0000000..15178d3
--- /dev/null
+++ b/chapt9/fishbot_ws/src/fishbot_description/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ fishbot_description
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_cmake
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+
diff --git a/chapt9/fishbot_ws/src/fishbot_description/urdf/fishbot.urdf b/chapt9/fishbot_ws/src/fishbot_description/urdf/fishbot.urdf
new file mode 100644
index 0000000..9ebe729
--- /dev/null
+++ b/chapt9/fishbot_ws/src/fishbot_description/urdf/fishbot.urdf
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/chapt9/fishbot_ws/src/fishbot_navigation2/CMakeLists.txt b/chapt9/fishbot_ws/src/fishbot_navigation2/CMakeLists.txt
new file mode 100644
index 0000000..2310073
--- /dev/null
+++ b/chapt9/fishbot_ws/src/fishbot_navigation2/CMakeLists.txt
@@ -0,0 +1,33 @@
+cmake_minimum_required(VERSION 3.8)
+project(fishbot_navigation2)
+
+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( 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
+ config
+ maps
+ DESTINATION share/${PROJECT_NAME}
+)
+
+ament_package()
diff --git a/chapt9/fishbot_ws/src/fishbot_navigation2/config/nav2_params.yaml b/chapt9/fishbot_ws/src/fishbot_navigation2/config/nav2_params.yaml
new file mode 100644
index 0000000..5f4b08f
--- /dev/null
+++ b/chapt9/fishbot_ws/src/fishbot_navigation2/config/nav2_params.yaml
@@ -0,0 +1,349 @@
+amcl:
+ ros__parameters:
+ use_sim_time: True
+ alpha1: 0.2
+ alpha2: 0.2
+ alpha3: 0.2
+ alpha4: 0.2
+ alpha5: 0.2
+ base_frame_id: "base_footprint"
+ beam_skip_distance: 0.5
+ beam_skip_error_threshold: 0.9
+ beam_skip_threshold: 0.3
+ do_beamskip: false
+ global_frame_id: "map"
+ lambda_short: 0.1
+ laser_likelihood_max_dist: 2.0
+ laser_max_range: 100.0
+ laser_min_range: -1.0
+ laser_model_type: "likelihood_field"
+ max_beams: 60
+ max_particles: 2000
+ min_particles: 500
+ odom_frame_id: "odom"
+ pf_err: 0.05
+ pf_z: 0.99
+ recovery_alpha_fast: 0.0
+ recovery_alpha_slow: 0.0
+ resample_interval: 1
+ robot_model_type: "nav2_amcl::DifferentialMotionModel"
+ save_pose_rate: 0.5
+ sigma_hit: 0.2
+ tf_broadcast: true
+ transform_tolerance: 1.0
+ update_min_a: 0.2
+ update_min_d: 0.25
+ z_hit: 0.5
+ z_max: 0.05
+ z_rand: 0.5
+ z_short: 0.05
+ scan_topic: scan
+
+bt_navigator:
+ ros__parameters:
+ use_sim_time: True
+ global_frame: map
+ robot_base_frame: base_link
+ odom_topic: /odom
+ bt_loop_duration: 10
+ default_server_timeout: 20
+ # 'default_nav_through_poses_bt_xml' and 'default_nav_to_pose_bt_xml' are use defaults:
+ # nav2_bt_navigator/navigate_to_pose_w_replanning_and_recovery.xml
+ # nav2_bt_navigator/navigate_through_poses_w_replanning_and_recovery.xml
+ # They can be set here or via a RewrittenYaml remap from a parent launch file to Nav2.
+ plugin_lib_names:
+ - nav2_compute_path_to_pose_action_bt_node
+ - nav2_compute_path_through_poses_action_bt_node
+ - nav2_smooth_path_action_bt_node
+ - nav2_follow_path_action_bt_node
+ - nav2_spin_action_bt_node
+ - nav2_wait_action_bt_node
+ - nav2_assisted_teleop_action_bt_node
+ - nav2_back_up_action_bt_node
+ - nav2_drive_on_heading_bt_node
+ - nav2_clear_costmap_service_bt_node
+ - nav2_is_stuck_condition_bt_node
+ - nav2_goal_reached_condition_bt_node
+ - nav2_goal_updated_condition_bt_node
+ - nav2_globally_updated_goal_condition_bt_node
+ - nav2_is_path_valid_condition_bt_node
+ - nav2_initial_pose_received_condition_bt_node
+ - nav2_reinitialize_global_localization_service_bt_node
+ - nav2_rate_controller_bt_node
+ - nav2_distance_controller_bt_node
+ - nav2_speed_controller_bt_node
+ - nav2_truncate_path_action_bt_node
+ - nav2_truncate_path_local_action_bt_node
+ - nav2_goal_updater_node_bt_node
+ - nav2_recovery_node_bt_node
+ - nav2_pipeline_sequence_bt_node
+ - nav2_round_robin_node_bt_node
+ - nav2_transform_available_condition_bt_node
+ - nav2_time_expired_condition_bt_node
+ - nav2_path_expiring_timer_condition
+ - nav2_distance_traveled_condition_bt_node
+ - nav2_single_trigger_bt_node
+ - nav2_goal_updated_controller_bt_node
+ - nav2_is_battery_low_condition_bt_node
+ - nav2_navigate_through_poses_action_bt_node
+ - nav2_navigate_to_pose_action_bt_node
+ - nav2_remove_passed_goals_action_bt_node
+ - nav2_planner_selector_bt_node
+ - nav2_controller_selector_bt_node
+ - nav2_goal_checker_selector_bt_node
+ - nav2_controller_cancel_bt_node
+ - nav2_path_longer_on_approach_bt_node
+ - nav2_wait_cancel_bt_node
+ - nav2_spin_cancel_bt_node
+ - nav2_back_up_cancel_bt_node
+ - nav2_assisted_teleop_cancel_bt_node
+ - nav2_drive_on_heading_cancel_bt_node
+ - nav2_is_battery_charging_condition_bt_node
+
+bt_navigator_navigate_through_poses_rclcpp_node:
+ ros__parameters:
+ use_sim_time: True
+
+bt_navigator_navigate_to_pose_rclcpp_node:
+ ros__parameters:
+ use_sim_time: True
+
+controller_server:
+ ros__parameters:
+ use_sim_time: True
+ controller_frequency: 20.0
+ min_x_velocity_threshold: 0.001
+ min_y_velocity_threshold: 0.5
+ min_theta_velocity_threshold: 0.001
+ failure_tolerance: 0.3
+ progress_checker_plugin: "progress_checker"
+ goal_checker_plugins: ["general_goal_checker"] # "precise_goal_checker"
+ controller_plugins: ["FollowPath"]
+
+ # Progress checker parameters
+ progress_checker:
+ plugin: "nav2_controller::SimpleProgressChecker"
+ required_movement_radius: 0.5
+ movement_time_allowance: 10.0
+ # Goal checker parameters
+ #precise_goal_checker:
+ # plugin: "nav2_controller::SimpleGoalChecker"
+ # xy_goal_tolerance: 0.25
+ # yaw_goal_tolerance: 0.25
+ # stateful: True
+ general_goal_checker:
+ stateful: True
+ plugin: "nav2_controller::SimpleGoalChecker"
+ xy_goal_tolerance: 0.25
+ yaw_goal_tolerance: 0.25
+ # DWB parameters
+ FollowPath:
+ plugin: "dwb_core::DWBLocalPlanner"
+ debug_trajectory_details: True
+ min_vel_x: 0.0
+ min_vel_y: 0.0
+ max_vel_x: 0.26
+ max_vel_y: 0.0
+ max_vel_theta: 1.0
+ min_speed_xy: 0.0
+ max_speed_xy: 0.26
+ min_speed_theta: 0.0
+ # Add high threshold velocity for turtlebot 3 issue.
+ # https://github.com/ROBOTIS-GIT/turtlebot3_simulations/issues/75
+ acc_lim_x: 2.5
+ acc_lim_y: 0.0
+ acc_lim_theta: 3.2
+ decel_lim_x: -2.5
+ decel_lim_y: 0.0
+ decel_lim_theta: -3.2
+ vx_samples: 20
+ vy_samples: 5
+ vtheta_samples: 20
+ sim_time: 1.7
+ linear_granularity: 0.05
+ angular_granularity: 0.025
+ transform_tolerance: 0.2
+ xy_goal_tolerance: 0.25
+ trans_stopped_velocity: 0.25
+ short_circuit_trajectory_evaluation: True
+ stateful: True
+ critics: ["RotateToGoal", "Oscillation", "BaseObstacle", "GoalAlign", "PathAlign", "PathDist", "GoalDist"]
+ BaseObstacle.scale: 0.02
+ PathAlign.scale: 32.0
+ PathAlign.forward_point_distance: 0.1
+ GoalAlign.scale: 24.0
+ GoalAlign.forward_point_distance: 0.1
+ PathDist.scale: 32.0
+ GoalDist.scale: 24.0
+ RotateToGoal.scale: 32.0
+ RotateToGoal.slowing_factor: 5.0
+ RotateToGoal.lookahead_time: -1.0
+
+local_costmap:
+ local_costmap:
+ ros__parameters:
+ update_frequency: 5.0
+ publish_frequency: 2.0
+ global_frame: odom
+ robot_base_frame: base_link
+ use_sim_time: True
+ rolling_window: true
+ width: 3
+ height: 3
+ resolution: 0.05
+ robot_radius: 0.22
+ plugins: ["voxel_layer", "inflation_layer"]
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ voxel_layer:
+ plugin: "nav2_costmap_2d::VoxelLayer"
+ enabled: True
+ publish_voxel_map: True
+ origin_z: 0.0
+ z_resolution: 0.05
+ z_voxels: 16
+ max_obstacle_height: 2.0
+ mark_threshold: 0
+ observation_sources: scan
+ scan:
+ topic: /scan
+ max_obstacle_height: 2.0
+ clearing: True
+ marking: True
+ data_type: "LaserScan"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ static_layer:
+ plugin: "nav2_costmap_2d::StaticLayer"
+ map_subscribe_transient_local: True
+ always_send_full_costmap: True
+
+global_costmap:
+ global_costmap:
+ ros__parameters:
+ update_frequency: 1.0
+ publish_frequency: 1.0
+ global_frame: map
+ robot_base_frame: base_link
+ use_sim_time: True
+ robot_radius: 0.22
+ resolution: 0.05
+ track_unknown_space: true
+ plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
+ obstacle_layer:
+ plugin: "nav2_costmap_2d::ObstacleLayer"
+ enabled: True
+ observation_sources: scan
+ scan:
+ topic: /scan
+ max_obstacle_height: 2.0
+ clearing: True
+ marking: True
+ data_type: "LaserScan"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ static_layer:
+ plugin: "nav2_costmap_2d::StaticLayer"
+ map_subscribe_transient_local: True
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ always_send_full_costmap: True
+
+map_server:
+ ros__parameters:
+ use_sim_time: True
+ # Overridden in launch by the "map" launch configuration or provided default value.
+ # To use in yaml, remove the default "map" value in the tb3_simulation_launch.py file & provide full path to map below.
+ yaml_filename: ""
+
+map_saver:
+ ros__parameters:
+ use_sim_time: True
+ save_map_timeout: 5.0
+ free_thresh_default: 0.25
+ occupied_thresh_default: 0.65
+ map_subscribe_transient_local: True
+
+planner_server:
+ ros__parameters:
+ expected_planner_frequency: 20.0
+ use_sim_time: True
+ planner_plugins: ["GridBased"]
+ GridBased:
+ plugin: "nav2_navfn_planner/NavfnPlanner"
+ tolerance: 0.5
+ use_astar: false
+ allow_unknown: true
+
+smoother_server:
+ ros__parameters:
+ use_sim_time: True
+ smoother_plugins: ["simple_smoother"]
+ simple_smoother:
+ plugin: "nav2_smoother::SimpleSmoother"
+ tolerance: 1.0e-10
+ max_its: 1000
+ do_refinement: True
+
+behavior_server:
+ ros__parameters:
+ costmap_topic: local_costmap/costmap_raw
+ footprint_topic: local_costmap/published_footprint
+ cycle_frequency: 10.0
+ behavior_plugins: ["spin", "backup", "drive_on_heading", "assisted_teleop", "wait"]
+ spin:
+ plugin: "nav2_behaviors/Spin"
+ backup:
+ plugin: "nav2_behaviors/BackUp"
+ drive_on_heading:
+ plugin: "nav2_behaviors/DriveOnHeading"
+ wait:
+ plugin: "nav2_behaviors/Wait"
+ assisted_teleop:
+ plugin: "nav2_behaviors/AssistedTeleop"
+ global_frame: odom
+ robot_base_frame: base_link
+ transform_tolerance: 0.1
+ use_sim_time: true
+ simulate_ahead_time: 2.0
+ max_rotational_vel: 1.0
+ min_rotational_vel: 0.4
+ rotational_acc_lim: 3.2
+
+robot_state_publisher:
+ ros__parameters:
+ use_sim_time: True
+
+waypoint_follower:
+ ros__parameters:
+ use_sim_time: True
+ loop_rate: 20
+ stop_on_failure: false
+ waypoint_task_executor_plugin: "wait_at_waypoint"
+ wait_at_waypoint:
+ plugin: "nav2_waypoint_follower::WaitAtWaypoint"
+ enabled: True
+ waypoint_pause_duration: 200
+
+velocity_smoother:
+ ros__parameters:
+ use_sim_time: True
+ smoothing_frequency: 20.0
+ scale_velocities: False
+ feedback: "OPEN_LOOP"
+ max_velocity: [0.26, 0.0, 1.0]
+ min_velocity: [-0.26, 0.0, -1.0]
+ max_accel: [2.5, 0.0, 3.2]
+ max_decel: [-2.5, 0.0, -3.2]
+ odom_topic: "odom"
+ odom_duration: 0.1
+ deadband_velocity: [0.0, 0.0, 0.0]
+ velocity_timeout: 1.0
diff --git a/chapt9/fishbot_ws/src/fishbot_navigation2/launch/navigation2.launch.py b/chapt9/fishbot_ws/src/fishbot_navigation2/launch/navigation2.launch.py
new file mode 100644
index 0000000..a55c3d5
--- /dev/null
+++ b/chapt9/fishbot_ws/src/fishbot_navigation2/launch/navigation2.launch.py
@@ -0,0 +1,49 @@
+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():
+ # 获取与拼接默认路径
+ fishbot_navigation2_dir = get_package_share_directory(
+ 'fishbot_navigation2')
+ nav2_bringup_dir = get_package_share_directory('nav2_bringup')
+ rviz_config_dir = os.path.join(
+ nav2_bringup_dir, 'rviz', 'nav2_default_view.rviz')
+
+ # 创建 Launch 配置
+ use_sim_time = launch.substitutions.LaunchConfiguration(
+ 'use_sim_time', default='true')
+ map_yaml_path = launch.substitutions.LaunchConfiguration(
+ 'map', default=os.path.join(fishbot_navigation2_dir, 'maps', 'room.yaml'))
+ nav2_param_path = launch.substitutions.LaunchConfiguration(
+ 'params_file', default=os.path.join(fishbot_navigation2_dir, 'config', 'nav2_params.yaml'))
+
+ return launch.LaunchDescription([
+ # 声明新的 Launch 参数
+ launch.actions.DeclareLaunchArgument('use_sim_time', default_value=use_sim_time,
+ description='Use simulation (Gazebo) clock if true'),
+ launch.actions.DeclareLaunchArgument('map', default_value=map_yaml_path,
+ description='Full path to map file to load'),
+ launch.actions.DeclareLaunchArgument('params_file', default_value=nav2_param_path,
+ description='Full path to param file to load'),
+
+ launch.actions.IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ [nav2_bringup_dir, '/launch', '/bringup_launch.py']),
+ # 使用 Launch 参数替换原有参数
+ launch_arguments={
+ 'map': map_yaml_path,
+ 'use_sim_time': use_sim_time,
+ 'params_file': nav2_param_path}.items(),
+ ),
+ launch_ros.actions.Node(
+ package='rviz2',
+ executable='rviz2',
+ name='rviz2',
+ arguments=['-d', rviz_config_dir],
+ parameters=[{'use_sim_time': use_sim_time}],
+ output='screen'),
+ ])
\ No newline at end of file
diff --git a/chapt9/fishbot_ws/src/fishbot_navigation2/maps/room.pgm b/chapt9/fishbot_ws/src/fishbot_navigation2/maps/room.pgm
new file mode 100644
index 0000000..4af93a9
Binary files /dev/null and b/chapt9/fishbot_ws/src/fishbot_navigation2/maps/room.pgm differ
diff --git a/chapt9/fishbot_ws/src/fishbot_navigation2/maps/room.yaml b/chapt9/fishbot_ws/src/fishbot_navigation2/maps/room.yaml
new file mode 100644
index 0000000..cad4a2a
--- /dev/null
+++ b/chapt9/fishbot_ws/src/fishbot_navigation2/maps/room.yaml
@@ -0,0 +1,7 @@
+image: room.pgm
+mode: trinary
+resolution: 0.05
+origin: [-3.9, -1.82, 0]
+negate: 0
+occupied_thresh: 0.65
+free_thresh: 0.25
\ No newline at end of file
diff --git a/chapt9/fishbot_ws/src/fishbot_navigation2/package.xml b/chapt9/fishbot_ws/src/fishbot_navigation2/package.xml
new file mode 100644
index 0000000..19065ec
--- /dev/null
+++ b/chapt9/fishbot_ws/src/fishbot_navigation2/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ fishbot_navigation2
+ 0.0.0
+ TODO: Package description
+ fishros
+ TODO: License declaration
+
+ ament_cmake
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+