agenticros 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (330) hide show
  1. package/LICENSE +192 -0
  2. package/README.md +90 -4
  3. package/dist/commands/config.d.ts +20 -0
  4. package/dist/commands/config.d.ts.map +1 -0
  5. package/dist/commands/config.js +179 -0
  6. package/dist/commands/config.js.map +1 -0
  7. package/dist/commands/doctor.d.ts +33 -0
  8. package/dist/commands/doctor.d.ts.map +1 -0
  9. package/dist/commands/doctor.js +232 -0
  10. package/dist/commands/doctor.js.map +1 -0
  11. package/dist/commands/down.d.ts +15 -0
  12. package/dist/commands/down.d.ts.map +1 -0
  13. package/dist/commands/down.js +91 -0
  14. package/dist/commands/down.js.map +1 -0
  15. package/dist/commands/init.d.ts +21 -0
  16. package/dist/commands/init.d.ts.map +1 -0
  17. package/dist/commands/init.js +259 -0
  18. package/dist/commands/init.js.map +1 -0
  19. package/dist/commands/logs.d.ts +18 -0
  20. package/dist/commands/logs.d.ts.map +1 -0
  21. package/dist/commands/logs.js +67 -0
  22. package/dist/commands/logs.js.map +1 -0
  23. package/dist/commands/status.d.ts +12 -0
  24. package/dist/commands/status.d.ts.map +1 -0
  25. package/dist/commands/status.js +56 -0
  26. package/dist/commands/status.js.map +1 -0
  27. package/dist/commands/up.d.ts +20 -0
  28. package/dist/commands/up.d.ts.map +1 -0
  29. package/dist/commands/up.js +70 -0
  30. package/dist/commands/up.js.map +1 -0
  31. package/dist/index.d.ts +12 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +107 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/menu.d.ts +9 -0
  36. package/dist/menu.d.ts.map +1 -0
  37. package/dist/menu.js +96 -0
  38. package/dist/menu.js.map +1 -0
  39. package/dist/runners/real-robot.d.ts +15 -0
  40. package/dist/runners/real-robot.d.ts.map +1 -0
  41. package/dist/runners/real-robot.js +46 -0
  42. package/dist/runners/real-robot.js.map +1 -0
  43. package/dist/runners/sim.d.ts +19 -0
  44. package/dist/runners/sim.d.ts.map +1 -0
  45. package/dist/runners/sim.js +53 -0
  46. package/dist/runners/sim.js.map +1 -0
  47. package/dist/util/env.d.ts +24 -0
  48. package/dist/util/env.d.ts.map +1 -0
  49. package/dist/util/env.js +53 -0
  50. package/dist/util/env.js.map +1 -0
  51. package/dist/util/logger.d.ts +24 -0
  52. package/dist/util/logger.d.ts.map +1 -0
  53. package/dist/util/logger.js +62 -0
  54. package/dist/util/logger.js.map +1 -0
  55. package/dist/util/paths.d.ts +57 -0
  56. package/dist/util/paths.d.ts.map +1 -0
  57. package/dist/util/paths.js +132 -0
  58. package/dist/util/paths.js.map +1 -0
  59. package/dist/util/pidfile.d.ts +16 -0
  60. package/dist/util/pidfile.d.ts.map +1 -0
  61. package/dist/util/pidfile.js +63 -0
  62. package/dist/util/pidfile.js.map +1 -0
  63. package/dist/util/state.d.ts +26 -0
  64. package/dist/util/state.d.ts.map +1 -0
  65. package/dist/util/state.js +55 -0
  66. package/dist/util/state.js.map +1 -0
  67. package/package.json +60 -1
  68. package/runtime/BUNDLE.json +11 -0
  69. package/runtime/LICENSE +192 -0
  70. package/runtime/README.md +273 -0
  71. package/runtime/docs/architecture.md +366 -0
  72. package/runtime/docs/cli.md +140 -0
  73. package/runtime/docs/memory.md +292 -0
  74. package/runtime/docs/robot-setup.md +347 -0
  75. package/runtime/package.json +28 -0
  76. package/runtime/packages/agenticros/agenticros-agenticros-0.0.1.tgz +0 -0
  77. package/runtime/packages/agenticros/openclaw.plugin.json +451 -0
  78. package/runtime/packages/agenticros/package.json +41 -0
  79. package/runtime/packages/agenticros/src/camera-snapshot-cache.ts +59 -0
  80. package/runtime/packages/agenticros/src/camera-snapshot-routes.ts +44 -0
  81. package/runtime/packages/agenticros/src/commands/estop.ts +41 -0
  82. package/runtime/packages/agenticros/src/commands/transport.ts +195 -0
  83. package/runtime/packages/agenticros/src/config-file.ts +136 -0
  84. package/runtime/packages/agenticros/src/config-page.ts +498 -0
  85. package/runtime/packages/agenticros/src/context/robot-context.ts +373 -0
  86. package/runtime/packages/agenticros/src/depth.ts +313 -0
  87. package/runtime/packages/agenticros/src/describer.ts +157 -0
  88. package/runtime/packages/agenticros/src/image-binary-trim.ts +16 -0
  89. package/runtime/packages/agenticros/src/index.ts +85 -0
  90. package/runtime/packages/agenticros/src/landing-page.ts +38 -0
  91. package/runtime/packages/agenticros/src/memory.ts +44 -0
  92. package/runtime/packages/agenticros/src/plugin-api.ts +173 -0
  93. package/runtime/packages/agenticros/src/plugin-image-base64.ts +69 -0
  94. package/runtime/packages/agenticros/src/preflight.ts +110 -0
  95. package/runtime/packages/agenticros/src/routes.ts +328 -0
  96. package/runtime/packages/agenticros/src/safety/validator.ts +43 -0
  97. package/runtime/packages/agenticros/src/service.ts +359 -0
  98. package/runtime/packages/agenticros/src/skill-api.ts +65 -0
  99. package/runtime/packages/agenticros/src/skill-loader.ts +146 -0
  100. package/runtime/packages/agenticros/src/teleop/page.ts +498 -0
  101. package/runtime/packages/agenticros/src/teleop/routes.ts +650 -0
  102. package/runtime/packages/agenticros/src/tools/index.ts +26 -0
  103. package/runtime/packages/agenticros/src/tools/ros2-action.ts +50 -0
  104. package/runtime/packages/agenticros/src/tools/ros2-camera.ts +221 -0
  105. package/runtime/packages/agenticros/src/tools/ros2-depth-distance.ts +58 -0
  106. package/runtime/packages/agenticros/src/tools/ros2-introspect.ts +62 -0
  107. package/runtime/packages/agenticros/src/tools/ros2-memory.ts +158 -0
  108. package/runtime/packages/agenticros/src/tools/ros2-param.ts +87 -0
  109. package/runtime/packages/agenticros/src/tools/ros2-publish.ts +52 -0
  110. package/runtime/packages/agenticros/src/tools/ros2-service.ts +46 -0
  111. package/runtime/packages/agenticros/src/tools/ros2-subscribe.ts +71 -0
  112. package/runtime/packages/agenticros/tsconfig.json +9 -0
  113. package/runtime/packages/agenticros-claude-code/README.md +260 -0
  114. package/runtime/packages/agenticros-claude-code/config.example.json +9 -0
  115. package/runtime/packages/agenticros-claude-code/dist/config.d.ts +8 -0
  116. package/runtime/packages/agenticros-claude-code/dist/config.d.ts.map +1 -0
  117. package/runtime/packages/agenticros-claude-code/dist/config.js +93 -0
  118. package/runtime/packages/agenticros-claude-code/dist/config.js.map +1 -0
  119. package/runtime/packages/agenticros-claude-code/dist/depth.d.ts +20 -0
  120. package/runtime/packages/agenticros-claude-code/dist/depth.d.ts.map +1 -0
  121. package/runtime/packages/agenticros-claude-code/dist/depth.js +126 -0
  122. package/runtime/packages/agenticros-claude-code/dist/depth.js.map +1 -0
  123. package/runtime/packages/agenticros-claude-code/dist/find-object/coco-classes.d.ts +6 -0
  124. package/runtime/packages/agenticros-claude-code/dist/find-object/coco-classes.d.ts.map +1 -0
  125. package/runtime/packages/agenticros-claude-code/dist/find-object/coco-classes.js +36 -0
  126. package/runtime/packages/agenticros-claude-code/dist/find-object/coco-classes.js.map +1 -0
  127. package/runtime/packages/agenticros-claude-code/dist/find-object/find-object.d.ts +33 -0
  128. package/runtime/packages/agenticros-claude-code/dist/find-object/find-object.d.ts.map +1 -0
  129. package/runtime/packages/agenticros-claude-code/dist/find-object/find-object.js +134 -0
  130. package/runtime/packages/agenticros-claude-code/dist/find-object/find-object.js.map +1 -0
  131. package/runtime/packages/agenticros-claude-code/dist/follow-me/controller.d.ts +43 -0
  132. package/runtime/packages/agenticros-claude-code/dist/follow-me/controller.d.ts.map +1 -0
  133. package/runtime/packages/agenticros-claude-code/dist/follow-me/controller.js +73 -0
  134. package/runtime/packages/agenticros-claude-code/dist/follow-me/controller.js.map +1 -0
  135. package/runtime/packages/agenticros-claude-code/dist/follow-me/detector.d.ts +58 -0
  136. package/runtime/packages/agenticros-claude-code/dist/follow-me/detector.d.ts.map +1 -0
  137. package/runtime/packages/agenticros-claude-code/dist/follow-me/detector.js +251 -0
  138. package/runtime/packages/agenticros-claude-code/dist/follow-me/detector.js.map +1 -0
  139. package/runtime/packages/agenticros-claude-code/dist/follow-me/loop.d.ts +61 -0
  140. package/runtime/packages/agenticros-claude-code/dist/follow-me/loop.d.ts.map +1 -0
  141. package/runtime/packages/agenticros-claude-code/dist/follow-me/loop.js +268 -0
  142. package/runtime/packages/agenticros-claude-code/dist/follow-me/loop.js.map +1 -0
  143. package/runtime/packages/agenticros-claude-code/dist/index.d.ts +3 -0
  144. package/runtime/packages/agenticros-claude-code/dist/index.d.ts.map +1 -0
  145. package/runtime/packages/agenticros-claude-code/dist/index.js +111 -0
  146. package/runtime/packages/agenticros-claude-code/dist/index.js.map +1 -0
  147. package/runtime/packages/agenticros-claude-code/dist/memory.d.ts +17 -0
  148. package/runtime/packages/agenticros-claude-code/dist/memory.d.ts.map +1 -0
  149. package/runtime/packages/agenticros-claude-code/dist/memory.js +44 -0
  150. package/runtime/packages/agenticros-claude-code/dist/memory.js.map +1 -0
  151. package/runtime/packages/agenticros-claude-code/dist/safety.d.ts +10 -0
  152. package/runtime/packages/agenticros-claude-code/dist/safety.d.ts.map +1 -0
  153. package/runtime/packages/agenticros-claude-code/dist/safety.js +34 -0
  154. package/runtime/packages/agenticros-claude-code/dist/safety.js.map +1 -0
  155. package/runtime/packages/agenticros-claude-code/dist/tools.d.ts +36 -0
  156. package/runtime/packages/agenticros-claude-code/dist/tools.d.ts.map +1 -0
  157. package/runtime/packages/agenticros-claude-code/dist/tools.js +777 -0
  158. package/runtime/packages/agenticros-claude-code/dist/tools.js.map +1 -0
  159. package/runtime/packages/agenticros-claude-code/dist/transport.d.ts +17 -0
  160. package/runtime/packages/agenticros-claude-code/dist/transport.d.ts.map +1 -0
  161. package/runtime/packages/agenticros-claude-code/dist/transport.js +46 -0
  162. package/runtime/packages/agenticros-claude-code/dist/transport.js.map +1 -0
  163. package/runtime/packages/agenticros-claude-code/dist/zero-shot/detector.d.ts +42 -0
  164. package/runtime/packages/agenticros-claude-code/dist/zero-shot/detector.d.ts.map +1 -0
  165. package/runtime/packages/agenticros-claude-code/dist/zero-shot/detector.js +114 -0
  166. package/runtime/packages/agenticros-claude-code/dist/zero-shot/detector.js.map +1 -0
  167. package/runtime/packages/agenticros-claude-code/package.json +29 -0
  168. package/runtime/packages/agenticros-claude-code/src/config.ts +96 -0
  169. package/runtime/packages/agenticros-claude-code/src/depth.ts +173 -0
  170. package/runtime/packages/agenticros-claude-code/src/find-object/coco-classes.ts +38 -0
  171. package/runtime/packages/agenticros-claude-code/src/find-object/find-object.ts +190 -0
  172. package/runtime/packages/agenticros-claude-code/src/follow-me/controller.ts +109 -0
  173. package/runtime/packages/agenticros-claude-code/src/follow-me/depth-loop.ts +420 -0
  174. package/runtime/packages/agenticros-claude-code/src/follow-me/detector.ts +303 -0
  175. package/runtime/packages/agenticros-claude-code/src/follow-me/loop.ts +330 -0
  176. package/runtime/packages/agenticros-claude-code/src/index.ts +125 -0
  177. package/runtime/packages/agenticros-claude-code/src/memory.ts +51 -0
  178. package/runtime/packages/agenticros-claude-code/src/safety.ts +44 -0
  179. package/runtime/packages/agenticros-claude-code/src/tools.ts +891 -0
  180. package/runtime/packages/agenticros-claude-code/src/transport.ts +58 -0
  181. package/runtime/packages/agenticros-claude-code/src/zero-shot/detector.ts +169 -0
  182. package/runtime/packages/agenticros-claude-code/tsconfig.json +9 -0
  183. package/runtime/packages/agenticros-claude-code/yolo-debug.mjs +106 -0
  184. package/runtime/packages/agenticros-gemini/README.md +139 -0
  185. package/runtime/packages/agenticros-gemini/package.json +28 -0
  186. package/runtime/packages/agenticros-gemini/scripts/smoke-api.mjs +42 -0
  187. package/runtime/packages/agenticros-gemini/src/chat.ts +139 -0
  188. package/runtime/packages/agenticros-gemini/src/config.ts +92 -0
  189. package/runtime/packages/agenticros-gemini/src/depth.ts +173 -0
  190. package/runtime/packages/agenticros-gemini/src/index.ts +58 -0
  191. package/runtime/packages/agenticros-gemini/src/memory.ts +32 -0
  192. package/runtime/packages/agenticros-gemini/src/safety.ts +44 -0
  193. package/runtime/packages/agenticros-gemini/src/tools.ts +516 -0
  194. package/runtime/packages/agenticros-gemini/src/transport.ts +58 -0
  195. package/runtime/packages/agenticros-gemini/tsconfig.json +8 -0
  196. package/runtime/packages/core/package.json +47 -0
  197. package/runtime/packages/core/src/banner.ts +32 -0
  198. package/runtime/packages/core/src/cmd-vel-twist.ts +31 -0
  199. package/runtime/packages/core/src/config.ts +279 -0
  200. package/runtime/packages/core/src/index.ts +54 -0
  201. package/runtime/packages/core/src/memory/__tests__/factory.test.ts +70 -0
  202. package/runtime/packages/core/src/memory/__tests__/local-provider.test.ts +195 -0
  203. package/runtime/packages/core/src/memory/__tests__/mem0-provider.test.ts +192 -0
  204. package/runtime/packages/core/src/memory/__tests__/smart-defaults.test.ts +46 -0
  205. package/runtime/packages/core/src/memory/factory.ts +63 -0
  206. package/runtime/packages/core/src/memory/index.ts +10 -0
  207. package/runtime/packages/core/src/memory/local/provider.ts +229 -0
  208. package/runtime/packages/core/src/memory/mem0/provider.ts +379 -0
  209. package/runtime/packages/core/src/memory/types.ts +96 -0
  210. package/runtime/packages/core/src/topic-utils.ts +95 -0
  211. package/runtime/packages/core/src/transport/factory.ts +47 -0
  212. package/runtime/packages/core/src/transport/local/conversion.ts +333 -0
  213. package/runtime/packages/core/src/transport/local/entities.ts +129 -0
  214. package/runtime/packages/core/src/transport/local/transport.ts +406 -0
  215. package/runtime/packages/core/src/transport/rosbridge/actions.ts +81 -0
  216. package/runtime/packages/core/src/transport/rosbridge/adapter.ts +157 -0
  217. package/runtime/packages/core/src/transport/rosbridge/client.ts +438 -0
  218. package/runtime/packages/core/src/transport/rosbridge/services.ts +41 -0
  219. package/runtime/packages/core/src/transport/rosbridge/topics.ts +60 -0
  220. package/runtime/packages/core/src/transport/rosbridge/types.ts +118 -0
  221. package/runtime/packages/core/src/transport/transport.ts +77 -0
  222. package/runtime/packages/core/src/transport/types.ts +137 -0
  223. package/runtime/packages/core/src/transport/webrtc/signaling-client.ts +196 -0
  224. package/runtime/packages/core/src/transport/webrtc/signaling-types.ts +130 -0
  225. package/runtime/packages/core/src/transport/webrtc/transport.ts +516 -0
  226. package/runtime/packages/core/src/transport/zenoh/adapter.ts +357 -0
  227. package/runtime/packages/core/src/transport/zenoh/cdr.ts +183 -0
  228. package/runtime/packages/core/src/transport/zenoh/keys.ts +51 -0
  229. package/runtime/packages/core/tsconfig.json +9 -0
  230. package/runtime/packages/ros-camera/package.json +30 -0
  231. package/runtime/packages/ros-camera/src/index.ts +13 -0
  232. package/runtime/packages/ros-camera/src/snapshot.ts +372 -0
  233. package/runtime/packages/ros-camera/tsconfig.json +9 -0
  234. package/runtime/pnpm-lock.yaml +5260 -0
  235. package/runtime/pnpm-workspace.yaml +2 -0
  236. package/runtime/ros2_ws/src/agenticros_agent/agenticros_agent/__init__.py +0 -0
  237. package/runtime/ros2_ws/src/agenticros_agent/agenticros_agent/agent_node.py +561 -0
  238. package/runtime/ros2_ws/src/agenticros_agent/package.xml +25 -0
  239. package/runtime/ros2_ws/src/agenticros_agent/resource/agenticros_agent +0 -0
  240. package/runtime/ros2_ws/src/agenticros_agent/setup.cfg +4 -0
  241. package/runtime/ros2_ws/src/agenticros_agent/setup.py +25 -0
  242. package/runtime/ros2_ws/src/agenticros_bringup/README.md +128 -0
  243. package/runtime/ros2_ws/src/agenticros_bringup/agenticros_bringup/__init__.py +1 -0
  244. package/runtime/ros2_ws/src/agenticros_bringup/agenticros_bringup/cmd_vel_relay.py +33 -0
  245. package/runtime/ros2_ws/src/agenticros_bringup/launch/cmd_vel_bridge.launch.py +58 -0
  246. package/runtime/ros2_ws/src/agenticros_bringup/launch/gazebo_turtlebot3.launch.py +69 -0
  247. package/runtime/ros2_ws/src/agenticros_bringup/launch/mode_a_gazebo.launch.py +55 -0
  248. package/runtime/ros2_ws/src/agenticros_bringup/launch/mode_a_gazebo_rviz.launch.py +48 -0
  249. package/runtime/ros2_ws/src/agenticros_bringup/launch/realsense_rosbridge.launch.py +154 -0
  250. package/runtime/ros2_ws/src/agenticros_bringup/launch/rosbridge_gazebo.launch.py +54 -0
  251. package/runtime/ros2_ws/src/agenticros_bringup/launch/rviz.launch.py +38 -0
  252. package/runtime/ros2_ws/src/agenticros_bringup/launch/turtlebot3_gazebo_rviz.launch.py +42 -0
  253. package/runtime/ros2_ws/src/agenticros_bringup/package.xml +24 -0
  254. package/runtime/ros2_ws/src/agenticros_bringup/resource/agenticros_bringup +0 -0
  255. package/runtime/ros2_ws/src/agenticros_bringup/rviz/turtlebot3_agenticros.rviz +174 -0
  256. package/runtime/ros2_ws/src/agenticros_bringup/setup.cfg +4 -0
  257. package/runtime/ros2_ws/src/agenticros_bringup/setup.py +28 -0
  258. package/runtime/ros2_ws/src/agenticros_discovery/agenticros_discovery/__init__.py +0 -0
  259. package/runtime/ros2_ws/src/agenticros_discovery/agenticros_discovery/discovery_node.py +172 -0
  260. package/runtime/ros2_ws/src/agenticros_discovery/package.xml +22 -0
  261. package/runtime/ros2_ws/src/agenticros_discovery/resource/agenticros_discovery +0 -0
  262. package/runtime/ros2_ws/src/agenticros_discovery/setup.cfg +5 -0
  263. package/runtime/ros2_ws/src/agenticros_discovery/setup.py +25 -0
  264. package/runtime/ros2_ws/src/agenticros_follow_me/README.md +66 -0
  265. package/runtime/ros2_ws/src/agenticros_follow_me/agenticros_follow_me/__init__.py +1 -0
  266. package/runtime/ros2_ws/src/agenticros_follow_me/agenticros_follow_me/__main__.py +5 -0
  267. package/runtime/ros2_ws/src/agenticros_follow_me/agenticros_follow_me/follow_me_node.py +278 -0
  268. package/runtime/ros2_ws/src/agenticros_follow_me/agenticros_follow_me/follower_controller.py +631 -0
  269. package/runtime/ros2_ws/src/agenticros_follow_me/agenticros_follow_me/person_tracker.py +635 -0
  270. package/runtime/ros2_ws/src/agenticros_follow_me/package.xml +22 -0
  271. package/runtime/ros2_ws/src/agenticros_follow_me/resource/agenticros_follow_me +0 -0
  272. package/runtime/ros2_ws/src/agenticros_follow_me/setup.py +25 -0
  273. package/runtime/ros2_ws/src/agenticros_msgs/CMakeLists.txt +26 -0
  274. package/runtime/ros2_ws/src/agenticros_msgs/msg/CapabilityManifest.msg +9 -0
  275. package/runtime/ros2_ws/src/agenticros_msgs/package.xml +22 -0
  276. package/runtime/ros2_ws/src/agenticros_msgs/srv/FollowMeGetStatus.srv +11 -0
  277. package/runtime/ros2_ws/src/agenticros_msgs/srv/FollowMeSetDistance.srv +4 -0
  278. package/runtime/ros2_ws/src/agenticros_msgs/srv/FollowMeSetTarget.srv +6 -0
  279. package/runtime/ros2_ws/src/agenticros_msgs/srv/FollowMeStart.srv +5 -0
  280. package/runtime/ros2_ws/src/agenticros_msgs/srv/FollowMeStop.srv +3 -0
  281. package/runtime/ros2_ws/src/agenticros_msgs/srv/GetCapabilities.srv +5 -0
  282. package/runtime/ros2_ws/src/agenticros_sim/CMakeLists.txt +24 -0
  283. package/runtime/ros2_ws/src/agenticros_sim/README.md +120 -0
  284. package/runtime/ros2_ws/src/agenticros_sim/config/agenticros-sim.config.json +28 -0
  285. package/runtime/ros2_ws/src/agenticros_sim/config/amr_bridge.yaml +111 -0
  286. package/runtime/ros2_ws/src/agenticros_sim/config/amr_view.rviz +172 -0
  287. package/runtime/ros2_ws/src/agenticros_sim/env-hooks/gz_resource_path.dsv.in +3 -0
  288. package/runtime/ros2_ws/src/agenticros_sim/env-hooks/gz_resource_path.sh.in +7 -0
  289. package/runtime/ros2_ws/src/agenticros_sim/launch/sim_amr.launch.py +159 -0
  290. package/runtime/ros2_ws/src/agenticros_sim/models/agenticros_amr/model.config +17 -0
  291. package/runtime/ros2_ws/src/agenticros_sim/models/agenticros_amr/model.sdf +244 -0
  292. package/runtime/ros2_ws/src/agenticros_sim/package.xml +27 -0
  293. package/runtime/ros2_ws/src/agenticros_sim/worlds/agenticros_indoor.sdf +183 -0
  294. package/runtime/scripts/activate_workspace.sh +285 -0
  295. package/runtime/scripts/agenticros-describer.policy.yaml +96 -0
  296. package/runtime/scripts/agenticros-proxy.cjs +99 -0
  297. package/runtime/scripts/agenticros-rosbridge.policy.yaml +62 -0
  298. package/runtime/scripts/check-cli-tarball-size.mjs +42 -0
  299. package/runtime/scripts/configure_agenticros.sh +200 -0
  300. package/runtime/scripts/configure_for_sim.sh +64 -0
  301. package/runtime/scripts/fix-openclaw-control-ui-path.sh +20 -0
  302. package/runtime/scripts/install_cli.sh +94 -0
  303. package/runtime/scripts/install_rosbridge_from_source.sh +67 -0
  304. package/runtime/scripts/lib/agenticros-banner.sh +28 -0
  305. package/runtime/scripts/onboard_robot.sh +75 -0
  306. package/runtime/scripts/openai.policy.yaml +77 -0
  307. package/runtime/scripts/openclaw-dashboard-url.cjs +49 -0
  308. package/runtime/scripts/pack-runtime.mjs +245 -0
  309. package/runtime/scripts/run_demo_native.sh +43 -0
  310. package/runtime/scripts/run_nemoclaw_host_stack.sh +91 -0
  311. package/runtime/scripts/run_robot_rosbridge.sh +36 -0
  312. package/runtime/scripts/sandbox_rosbridge_relay.py +137 -0
  313. package/runtime/scripts/setup-openclaw-local.cjs +75 -0
  314. package/runtime/scripts/setup_gateway_plugin.sh +329 -0
  315. package/runtime/scripts/setup_robot.sh +113 -0
  316. package/runtime/scripts/setup_workspace.sh +484 -0
  317. package/runtime/scripts/sim/run_sim.sh +146 -0
  318. package/runtime/scripts/smoke_test_nemoclaw.sh +123 -0
  319. package/runtime/scripts/start_demo.sh +55 -0
  320. package/runtime/scripts/sync-skill-tools.mjs +335 -0
  321. package/runtime/scripts/test-follow-me-sim.mjs +135 -0
  322. package/runtime/scripts/test-mcp-e2e.mjs +184 -0
  323. package/runtime/scripts/test-rclnodejs.mts +129 -0
  324. package/runtime/scripts/use-openclaw-2026.2.26.sh +19 -0
  325. package/runtime/scripts/use-openclaw-2026.3.11.sh +19 -0
  326. package/runtime/scripts/zenoh-bridge-ros2dds-robot.json5 +30 -0
  327. package/runtime/scripts/zenohd-agenticros.json5 +11 -0
  328. package/runtime/scripts/zenohd-rosclaw.json5 +11 -0
  329. package/runtime/tsconfig.base.json +19 -0
  330. package/index.js +0 -6
@@ -0,0 +1,631 @@
1
+ """
2
+ FollowerController - Robot Follow Control Logic
3
+
4
+ Computes Twist commands (linear_x, angular_z) for a differential drive robot
5
+ to follow a target person at a specified distance.
6
+
7
+ Also supports manual teleoperation commands:
8
+ - Move forward/backward for distance or time
9
+ - Turn left/right by angle
10
+ - Raw velocity commands
11
+ """
12
+
13
+ import time
14
+ import threading
15
+ from dataclasses import dataclass, field
16
+ from typing import Optional, Callable
17
+ from enum import Enum
18
+ import math
19
+
20
+ from .person_tracker import DetectedPerson
21
+
22
+
23
+ class ControlMode(Enum):
24
+ """Current control mode."""
25
+ IDLE = "idle"
26
+ FOLLOW = "follow"
27
+ MANUAL = "manual"
28
+
29
+
30
+ @dataclass
31
+ class ManualCommand:
32
+ """A manual movement command."""
33
+ command_type: str # "move", "turn", "velocity"
34
+ linear_vel: float = 0.0 # m/s
35
+ angular_vel: float = 0.0 # rad/s
36
+ distance: Optional[float] = None # meters (for move)
37
+ angle: Optional[float] = None # radians (for turn)
38
+ duration: Optional[float] = None # seconds
39
+ start_time: float = 0.0
40
+ completed: bool = False
41
+
42
+
43
+ @dataclass
44
+ class Twist:
45
+ """ROS-compatible Twist message structure."""
46
+ linear_x: float = 0.0 # Forward/backward velocity (m/s)
47
+ linear_y: float = 0.0 # Lateral velocity (m/s) - usually 0 for diff drive
48
+ linear_z: float = 0.0 # Vertical velocity (m/s) - usually 0
49
+ angular_x: float = 0.0 # Roll rate (rad/s) - usually 0
50
+ angular_y: float = 0.0 # Pitch rate (rad/s) - usually 0
51
+ angular_z: float = 0.0 # Yaw rate (rad/s) - turning
52
+
53
+ def __repr__(self):
54
+ return f"Twist(linear_x={self.linear_x:.3f} m/s, angular_z={self.angular_z:.3f} rad/s)"
55
+
56
+ def to_dict(self) -> dict:
57
+ """Convert to dictionary for JSON serialization."""
58
+ return {
59
+ 'linear': {'x': self.linear_x, 'y': self.linear_y, 'z': self.linear_z},
60
+ 'angular': {'x': self.angular_x, 'y': self.angular_y, 'z': self.angular_z}
61
+ }
62
+
63
+ def is_zero(self) -> bool:
64
+ """Check if this is a zero/stop command."""
65
+ return abs(self.linear_x) < 0.001 and abs(self.angular_z) < 0.001
66
+
67
+
68
+ @dataclass
69
+ class ControllerConfig:
70
+ """Configuration for the follower controller."""
71
+ # Target distance
72
+ target_distance: float = 1.0 # meters
73
+
74
+ # Velocity limits
75
+ max_linear_vel: float = 0.5 # m/s
76
+ max_angular_vel: float = 1.0 # rad/s
77
+
78
+ # Proportional gains
79
+ Kp_distance: float = 0.5 # Gain for distance error
80
+ Kp_angular: float = 1.5 # Gain for angular error
81
+
82
+ # Deadzones (ignore small errors)
83
+ distance_deadzone: float = 0.05 # 5cm
84
+ angular_deadzone: float = 0.05 # ~3 degrees
85
+
86
+ # Smoothing (exponential moving average)
87
+ smoothing_factor: float = 0.3 # 0 = no smoothing, 1 = instant response
88
+
89
+ # Watchdog
90
+ watchdog_timeout: float = 0.5 # seconds without detection before stopping
91
+
92
+
93
+ class FollowerController:
94
+ """
95
+ Computes velocity commands to follow a target person.
96
+
97
+ Uses proportional control with smoothing, deadzones, and safety limits.
98
+ Also supports manual teleoperation commands.
99
+ """
100
+
101
+ def __init__(self, config: Optional[ControllerConfig] = None):
102
+ self.config = config or ControllerConfig()
103
+
104
+ # State
105
+ self.enabled = False
106
+ self.mode = ControlMode.IDLE
107
+ self.target_person_id: Optional[int] = None
108
+ self.target_description: Optional[str] = None
109
+
110
+ # Manual control state
111
+ self._manual_command: Optional[ManualCommand] = None
112
+ self._manual_start_position = 0.0 # Estimated position for distance tracking
113
+ self._manual_start_angle = 0.0 # Estimated angle for turn tracking
114
+ self._command_queue: list[ManualCommand] = []
115
+ self._queue_lock = threading.Lock()
116
+
117
+ # Smoothing state
118
+ self._smoothed_linear = 0.0
119
+ self._smoothed_angular = 0.0
120
+
121
+ # Watchdog
122
+ self._last_detection_time = 0.0
123
+
124
+ # Last computed twist
125
+ self._last_twist = Twist()
126
+
127
+ # Odometry estimation (simple integration)
128
+ self._estimated_distance = 0.0
129
+ self._estimated_angle = 0.0
130
+ self._last_update_time = time.time()
131
+
132
+ # Statistics
133
+ self._update_count = 0
134
+ self._start_time = time.time()
135
+
136
+ def start(self, target_description: Optional[str] = None):
137
+ """Start following mode."""
138
+ self.enabled = True
139
+ self.mode = ControlMode.FOLLOW
140
+ self.target_description = target_description
141
+ self._last_detection_time = time.time()
142
+ self._cancel_manual_commands()
143
+ print(f"[CONTROLLER] Started following" +
144
+ (f" target: '{target_description}'" if target_description else ""))
145
+
146
+ def stop(self):
147
+ """Stop all movement and zero velocities."""
148
+ self.enabled = False
149
+ self.mode = ControlMode.IDLE
150
+ self._smoothed_linear = 0.0
151
+ self._smoothed_angular = 0.0
152
+ self._last_twist = Twist()
153
+ self._cancel_manual_commands()
154
+ print("[CONTROLLER] Stopped")
155
+
156
+ # ==========================================
157
+ # Manual Teleoperation Commands
158
+ # ==========================================
159
+
160
+ def move(self, distance: float, velocity: Optional[float] = None) -> dict:
161
+ """
162
+ Move forward (positive) or backward (negative) by a distance.
163
+
164
+ Args:
165
+ distance: Distance in meters (positive=forward, negative=backward)
166
+ velocity: Optional velocity in m/s (default: 0.3 m/s)
167
+
168
+ Returns:
169
+ Status dict
170
+ """
171
+ vel = velocity if velocity is not None else 0.3
172
+ vel = min(abs(vel), self.config.max_linear_vel)
173
+ if distance < 0:
174
+ vel = -vel
175
+
176
+ duration = abs(distance / vel) if vel != 0 else 0
177
+
178
+ cmd = ManualCommand(
179
+ command_type="move",
180
+ linear_vel=vel,
181
+ angular_vel=0.0,
182
+ distance=abs(distance),
183
+ duration=duration,
184
+ start_time=time.time()
185
+ )
186
+
187
+ self._start_manual_command(cmd)
188
+
189
+ return {
190
+ 'status': 'ok',
191
+ 'command': 'move',
192
+ 'distance': distance,
193
+ 'velocity': vel,
194
+ 'estimated_duration': duration
195
+ }
196
+
197
+ def turn(self, angle_degrees: float, angular_velocity: Optional[float] = None) -> dict:
198
+ """
199
+ Turn left (positive) or right (negative) by an angle.
200
+
201
+ Args:
202
+ angle_degrees: Angle in degrees (positive=left/CCW, negative=right/CW)
203
+ angular_velocity: Optional angular velocity in rad/s (default: 0.5 rad/s)
204
+
205
+ Returns:
206
+ Status dict
207
+ """
208
+ angle_rad = math.radians(angle_degrees)
209
+ vel = angular_velocity if angular_velocity is not None else 0.5
210
+ vel = min(abs(vel), self.config.max_angular_vel)
211
+ if angle_degrees < 0:
212
+ vel = -vel
213
+
214
+ duration = abs(angle_rad / vel) if vel != 0 else 0
215
+
216
+ cmd = ManualCommand(
217
+ command_type="turn",
218
+ linear_vel=0.0,
219
+ angular_vel=vel,
220
+ angle=abs(angle_rad),
221
+ duration=duration,
222
+ start_time=time.time()
223
+ )
224
+
225
+ self._start_manual_command(cmd)
226
+
227
+ return {
228
+ 'status': 'ok',
229
+ 'command': 'turn',
230
+ 'angle_degrees': angle_degrees,
231
+ 'angular_velocity': vel,
232
+ 'estimated_duration': duration
233
+ }
234
+
235
+ def move_for_time(self, duration: float, velocity: float = 0.3) -> dict:
236
+ """
237
+ Move forward/backward for a specified duration.
238
+
239
+ Args:
240
+ duration: Time in seconds
241
+ velocity: Velocity in m/s (positive=forward, negative=backward)
242
+
243
+ Returns:
244
+ Status dict
245
+ """
246
+ vel = max(-self.config.max_linear_vel,
247
+ min(self.config.max_linear_vel, velocity))
248
+
249
+ cmd = ManualCommand(
250
+ command_type="move",
251
+ linear_vel=vel,
252
+ angular_vel=0.0,
253
+ duration=duration,
254
+ start_time=time.time()
255
+ )
256
+
257
+ self._start_manual_command(cmd)
258
+
259
+ return {
260
+ 'status': 'ok',
261
+ 'command': 'move_for_time',
262
+ 'duration': duration,
263
+ 'velocity': vel
264
+ }
265
+
266
+ def set_velocity(self, linear: float = 0.0, angular: float = 0.0,
267
+ duration: Optional[float] = None) -> dict:
268
+ """
269
+ Set raw velocity command.
270
+
271
+ Args:
272
+ linear: Linear velocity in m/s
273
+ angular: Angular velocity in rad/s
274
+ duration: Optional duration in seconds (None = until next command)
275
+
276
+ Returns:
277
+ Status dict
278
+ """
279
+ linear = max(-self.config.max_linear_vel,
280
+ min(self.config.max_linear_vel, linear))
281
+ angular = max(-self.config.max_angular_vel,
282
+ min(self.config.max_angular_vel, angular))
283
+
284
+ cmd = ManualCommand(
285
+ command_type="velocity",
286
+ linear_vel=linear,
287
+ angular_vel=angular,
288
+ duration=duration,
289
+ start_time=time.time()
290
+ )
291
+
292
+ self._start_manual_command(cmd)
293
+
294
+ return {
295
+ 'status': 'ok',
296
+ 'command': 'velocity',
297
+ 'linear': linear,
298
+ 'angular': angular,
299
+ 'duration': duration
300
+ }
301
+
302
+ def queue_command(self, cmd_type: str, **kwargs) -> dict:
303
+ """Add a command to the queue to execute after current command."""
304
+ with self._queue_lock:
305
+ if cmd_type == "move":
306
+ distance = kwargs.get('distance', 1.0)
307
+ velocity = kwargs.get('velocity', 0.3)
308
+ vel = min(abs(velocity), self.config.max_linear_vel)
309
+ if distance < 0:
310
+ vel = -vel
311
+ duration = abs(distance / vel) if vel != 0 else 0
312
+ cmd = ManualCommand(
313
+ command_type="move",
314
+ linear_vel=vel,
315
+ distance=abs(distance),
316
+ duration=duration
317
+ )
318
+ elif cmd_type == "turn":
319
+ angle = kwargs.get('angle', 90)
320
+ angle_rad = math.radians(angle)
321
+ vel = kwargs.get('angular_velocity', 0.5)
322
+ vel = min(abs(vel), self.config.max_angular_vel)
323
+ if angle < 0:
324
+ vel = -vel
325
+ duration = abs(angle_rad / vel) if vel != 0 else 0
326
+ cmd = ManualCommand(
327
+ command_type="turn",
328
+ angular_vel=vel,
329
+ angle=abs(angle_rad),
330
+ duration=duration
331
+ )
332
+ elif cmd_type == "wait":
333
+ duration = kwargs.get('duration', 1.0)
334
+ cmd = ManualCommand(
335
+ command_type="velocity",
336
+ linear_vel=0,
337
+ angular_vel=0,
338
+ duration=duration
339
+ )
340
+ else:
341
+ return {'status': 'error', 'message': f'Unknown command type: {cmd_type}'}
342
+
343
+ self._command_queue.append(cmd)
344
+
345
+ return {
346
+ 'status': 'ok',
347
+ 'message': f'Command queued (queue length: {len(self._command_queue)})',
348
+ 'queue_length': len(self._command_queue)
349
+ }
350
+
351
+ def execute_sequence(self, commands: list[dict]) -> dict:
352
+ """
353
+ Execute a sequence of movement commands.
354
+
355
+ Args:
356
+ commands: List of command dicts, e.g.:
357
+ [
358
+ {"type": "move", "distance": 1.0},
359
+ {"type": "turn", "angle": -90},
360
+ {"type": "move", "distance": 2.0},
361
+ {"type": "wait", "duration": 1.0}
362
+ ]
363
+
364
+ Returns:
365
+ Status dict
366
+ """
367
+ self._cancel_manual_commands()
368
+
369
+ for cmd_dict in commands:
370
+ cmd_type = cmd_dict.get('type', cmd_dict.get('command'))
371
+ self.queue_command(cmd_type, **cmd_dict)
372
+
373
+ # Start first command
374
+ self._start_next_queued_command()
375
+
376
+ return {
377
+ 'status': 'ok',
378
+ 'message': f'Executing sequence of {len(commands)} commands',
379
+ 'commands': len(commands)
380
+ }
381
+
382
+ def _start_manual_command(self, cmd: ManualCommand):
383
+ """Start executing a manual command."""
384
+ self.mode = ControlMode.MANUAL
385
+ self.enabled = True
386
+ self._manual_command = cmd
387
+ self._manual_command.start_time = time.time()
388
+ self._manual_start_position = self._estimated_distance
389
+ self._manual_start_angle = self._estimated_angle
390
+ print(f"[CONTROLLER] Manual {cmd.command_type}: "
391
+ f"linear={cmd.linear_vel:.2f}m/s, angular={cmd.angular_vel:.2f}rad/s"
392
+ + (f", duration={cmd.duration:.1f}s" if cmd.duration else ""))
393
+
394
+ def _cancel_manual_commands(self):
395
+ """Cancel current manual command and clear queue."""
396
+ self._manual_command = None
397
+ with self._queue_lock:
398
+ self._command_queue.clear()
399
+
400
+ def _start_next_queued_command(self):
401
+ """Start the next command in the queue."""
402
+ with self._queue_lock:
403
+ if self._command_queue:
404
+ cmd = self._command_queue.pop(0)
405
+ self._start_manual_command(cmd)
406
+ else:
407
+ # Queue empty, stop
408
+ self._manual_command = None
409
+ self.mode = ControlMode.IDLE
410
+ self.enabled = False
411
+ print("[CONTROLLER] Command sequence complete")
412
+
413
+ def _update_manual_command(self) -> Twist:
414
+ """Update manual command execution and return twist."""
415
+ if not self._manual_command:
416
+ return Twist()
417
+
418
+ cmd = self._manual_command
419
+ elapsed = time.time() - cmd.start_time
420
+
421
+ # Check completion conditions
422
+ completed = False
423
+
424
+ if cmd.duration is not None and elapsed >= cmd.duration:
425
+ completed = True
426
+
427
+ if cmd.command_type == "move" and cmd.distance is not None:
428
+ traveled = abs(self._estimated_distance - self._manual_start_position)
429
+ if traveled >= cmd.distance:
430
+ completed = True
431
+
432
+ if cmd.command_type == "turn" and cmd.angle is not None:
433
+ turned = abs(self._estimated_angle - self._manual_start_angle)
434
+ if turned >= cmd.angle:
435
+ completed = True
436
+
437
+ if completed:
438
+ cmd.completed = True
439
+ print(f"[CONTROLLER] Manual command completed")
440
+ self._start_next_queued_command()
441
+ if self._manual_command is None:
442
+ return Twist()
443
+ cmd = self._manual_command
444
+
445
+ return Twist(linear_x=cmd.linear_vel, angular_z=cmd.angular_vel)
446
+
447
+ def get_manual_status(self) -> dict:
448
+ """Get status of manual control."""
449
+ cmd = self._manual_command
450
+ if not cmd:
451
+ return {
452
+ 'active': False,
453
+ 'queue_length': len(self._command_queue)
454
+ }
455
+
456
+ elapsed = time.time() - cmd.start_time
457
+ remaining = (cmd.duration - elapsed) if cmd.duration else None
458
+
459
+ return {
460
+ 'active': True,
461
+ 'command_type': cmd.command_type,
462
+ 'linear_vel': cmd.linear_vel,
463
+ 'angular_vel': cmd.angular_vel,
464
+ 'elapsed': elapsed,
465
+ 'remaining': remaining,
466
+ 'queue_length': len(self._command_queue)
467
+ }
468
+
469
+ def set_target_distance(self, distance: float):
470
+ """Set the target follow distance in meters."""
471
+ self.config.target_distance = max(0.3, min(5.0, distance)) # Clamp 0.3-5m
472
+ print(f"[CONTROLLER] Target distance set to {self.config.target_distance:.2f}m")
473
+
474
+ def set_target_person(self, person_id: int):
475
+ """Lock onto a specific person ID."""
476
+ self.target_person_id = person_id
477
+ print(f"[CONTROLLER] Locked onto Person #{person_id}")
478
+
479
+ def clear_target_person(self):
480
+ """Clear target lock, follow closest person."""
481
+ self.target_person_id = None
482
+ self.target_description = None
483
+ print("[CONTROLLER] Target cleared, following closest person")
484
+
485
+ def update(self, target_person: Optional[DetectedPerson]) -> Twist:
486
+ """
487
+ Compute velocity command based on mode and target.
488
+
489
+ Args:
490
+ target_person: The person to follow, or None if not detected
491
+
492
+ Returns:
493
+ Twist command
494
+ """
495
+ self._update_count += 1
496
+ current_time = time.time()
497
+ dt = current_time - self._last_update_time
498
+ self._last_update_time = current_time
499
+
500
+ # Update odometry estimation from last twist
501
+ if dt > 0 and dt < 1.0: # Sanity check
502
+ self._estimated_distance += self._last_twist.linear_x * dt
503
+ self._estimated_angle += self._last_twist.angular_z * dt
504
+
505
+ # If disabled, return zero
506
+ if not self.enabled:
507
+ return Twist()
508
+
509
+ # Handle manual control mode
510
+ if self.mode == ControlMode.MANUAL:
511
+ twist = self._update_manual_command()
512
+ self._last_twist = twist
513
+
514
+ # Print status periodically
515
+ if self._update_count % 30 == 0:
516
+ cmd = self._manual_command
517
+ if cmd:
518
+ print(f"[MANUAL] {cmd.command_type}: "
519
+ f"linear={twist.linear_x:.2f}m/s, angular={twist.angular_z:.2f}rad/s")
520
+
521
+ return twist
522
+
523
+ # Follow mode - need a target person
524
+ if self.mode != ControlMode.FOLLOW:
525
+ return Twist()
526
+
527
+ # Watchdog check
528
+ if target_person is None:
529
+ if current_time - self._last_detection_time > self.config.watchdog_timeout:
530
+ # Watchdog triggered - stop
531
+ if self._update_count % 30 == 0: # Print every ~1 second at 30Hz
532
+ print("[CONTROLLER] Watchdog: No person detected, stopping")
533
+ self._smoothed_linear = 0.0
534
+ self._smoothed_angular = 0.0
535
+ self._last_twist = Twist()
536
+ return Twist()
537
+ else:
538
+ # Brief dropout, maintain last command
539
+ return self._last_twist
540
+
541
+ self._last_detection_time = current_time
542
+
543
+ # Calculate errors
544
+ distance_error = target_person.z - self.config.target_distance
545
+ angular_error = -math.atan2(target_person.x, target_person.z) # Negative because x>0 means person is to the right
546
+
547
+ # Apply deadzones
548
+ if abs(distance_error) < self.config.distance_deadzone:
549
+ distance_error = 0.0
550
+ if abs(angular_error) < self.config.angular_deadzone:
551
+ angular_error = 0.0
552
+
553
+ # Compute raw velocities (proportional control)
554
+ linear_cmd = self.config.Kp_distance * distance_error
555
+ angular_cmd = self.config.Kp_angular * angular_error
556
+
557
+ # Clamp to limits
558
+ linear_cmd = max(-self.config.max_linear_vel,
559
+ min(self.config.max_linear_vel, linear_cmd))
560
+ angular_cmd = max(-self.config.max_angular_vel,
561
+ min(self.config.max_angular_vel, angular_cmd))
562
+
563
+ # Apply smoothing (exponential moving average)
564
+ alpha = self.config.smoothing_factor
565
+ self._smoothed_linear = alpha * linear_cmd + (1 - alpha) * self._smoothed_linear
566
+ self._smoothed_angular = alpha * angular_cmd + (1 - alpha) * self._smoothed_angular
567
+
568
+ # Create twist
569
+ twist = Twist(
570
+ linear_x=self._smoothed_linear,
571
+ angular_z=self._smoothed_angular
572
+ )
573
+
574
+ self._last_twist = twist
575
+
576
+ # Print status periodically
577
+ if self._update_count % 10 == 0: # Every ~0.3s at 30Hz
578
+ self._print_status(target_person, twist, distance_error, angular_error)
579
+
580
+ return twist
581
+
582
+ def _print_status(self, person: DetectedPerson, twist: Twist,
583
+ dist_err: float, ang_err: float):
584
+ """Print formatted status to console."""
585
+ print(f"[DETECTION] Person #{person.id}: "
586
+ f"distance={person.z:.2f}m, x={person.x:.2f}m "
587
+ f"(conf={person.confidence:.0%})")
588
+
589
+ print(f"[TARGET] Following Person #{person.id} "
590
+ f"(target: {self.config.target_distance:.1f}m, "
591
+ f"error: {dist_err:+.2f}m)")
592
+
593
+ print(f"[TWIST] linear_x={twist.linear_x:+.3f} m/s, "
594
+ f"angular_z={twist.angular_z:+.3f} rad/s")
595
+ print() # Blank line for readability
596
+
597
+ def get_status(self) -> dict:
598
+ """Get current controller status."""
599
+ status = {
600
+ 'enabled': self.enabled,
601
+ 'mode': self.mode.value,
602
+ 'target_distance': self.config.target_distance,
603
+ 'target_person_id': self.target_person_id,
604
+ 'target_description': self.target_description,
605
+ 'last_twist': self._last_twist.to_dict(),
606
+ 'max_linear_vel': self.config.max_linear_vel,
607
+ 'max_angular_vel': self.config.max_angular_vel,
608
+ 'watchdog_timeout': self.config.watchdog_timeout,
609
+ 'update_count': self._update_count,
610
+ 'uptime': time.time() - self._start_time
611
+ }
612
+
613
+ # Add manual control status if active
614
+ if self.mode == ControlMode.MANUAL:
615
+ status['manual'] = self.get_manual_status()
616
+
617
+ return status
618
+
619
+ @staticmethod
620
+ def print_twist(twist: Twist):
621
+ """Print a twist command in ROS-compatible format."""
622
+ print("---")
623
+ print("linear:")
624
+ print(f" x: {twist.linear_x:.6f}")
625
+ print(f" y: {twist.linear_y:.6f}")
626
+ print(f" z: {twist.linear_z:.6f}")
627
+ print("angular:")
628
+ print(f" x: {twist.angular_x:.6f}")
629
+ print(f" y: {twist.angular_y:.6f}")
630
+ print(f" z: {twist.angular_z:.6f}")
631
+ print("---")