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,635 @@
1
+ """
2
+ PersonTracker - RealSense + MediaPipe Person Detection
3
+
4
+ Uses pyrealsense2 for depth + color frames and MediaPipe Pose
5
+ for fast person detection with 3D position estimation.
6
+ """
7
+
8
+ import time
9
+ import numpy as np
10
+ from dataclasses import dataclass, field
11
+ from typing import Optional
12
+ import threading
13
+
14
+ try:
15
+ import pyrealsense2 as rs
16
+ REALSENSE_AVAILABLE = True
17
+ except ImportError:
18
+ REALSENSE_AVAILABLE = False
19
+ print("[WARN] pyrealsense2 not available - using mock camera")
20
+
21
+ try:
22
+ import mediapipe as mp
23
+ # Check if using new Tasks API (0.10.x+) or legacy Solutions API
24
+ if hasattr(mp, 'solutions'):
25
+ MEDIAPIPE_API = 'solutions'
26
+ elif hasattr(mp, 'tasks'):
27
+ MEDIAPIPE_API = 'tasks'
28
+ else:
29
+ MEDIAPIPE_API = None
30
+ MEDIAPIPE_AVAILABLE = MEDIAPIPE_API is not None
31
+ if not MEDIAPIPE_AVAILABLE:
32
+ print("[WARN] mediapipe installed but no compatible API found")
33
+ except ImportError:
34
+ MEDIAPIPE_AVAILABLE = False
35
+ MEDIAPIPE_API = None
36
+ print("[WARN] mediapipe not available - using mock detection")
37
+
38
+ try:
39
+ import cv2
40
+ CV2_AVAILABLE = True
41
+ except ImportError:
42
+ CV2_AVAILABLE = False
43
+
44
+
45
+ @dataclass
46
+ class DetectedPerson:
47
+ """Represents a detected person with 3D position."""
48
+ id: int
49
+ x: float # meters, positive = right of camera
50
+ y: float # meters, positive = down
51
+ z: float # meters, depth/distance from camera
52
+ confidence: float # 0.0 to 1.0
53
+ bbox: tuple[int, int, int, int] # x, y, width, height in pixels
54
+ landmarks: Optional[list] = None # MediaPipe pose landmarks
55
+ last_seen: float = field(default_factory=time.time)
56
+
57
+ @property
58
+ def distance(self) -> float:
59
+ """Euclidean distance from camera."""
60
+ return np.sqrt(self.x**2 + self.y**2 + self.z**2)
61
+
62
+ def __repr__(self):
63
+ return f"Person #{self.id}: x={self.x:.2f}m, y={self.y:.2f}m, z={self.z:.2f}m (conf={self.confidence:.2f})"
64
+
65
+
66
+ class PersonTracker:
67
+ """
68
+ Tracks people using RealSense depth camera and MediaPipe Pose.
69
+
70
+ Provides real-time detection at ~30 Hz with 3D position estimation.
71
+ """
72
+
73
+ def __init__(self, use_camera: bool = True):
74
+ self.use_camera = use_camera and REALSENSE_AVAILABLE
75
+ self.running = False
76
+ self.lock = threading.Lock()
77
+
78
+ # Detected persons (thread-safe access via lock)
79
+ self._persons: list[DetectedPerson] = []
80
+ self._latest_color_frame: Optional[np.ndarray] = None
81
+ self._latest_depth_frame: Optional[np.ndarray] = None
82
+
83
+ # Tracking state
84
+ self._next_person_id = 1
85
+ self._tracking_history: dict[int, DetectedPerson] = {}
86
+
87
+ # Camera intrinsics (will be set from RealSense)
88
+ self.fx = 600.0 # focal length x (pixels)
89
+ self.fy = 600.0 # focal length y (pixels)
90
+ self.cx = 320.0 # principal point x
91
+ self.cy = 240.0 # principal point y
92
+ self.depth_scale = 0.001 # depth units to meters
93
+
94
+ # RealSense pipeline
95
+ self.pipeline = None
96
+ self.align = None
97
+
98
+ # MediaPipe
99
+ self.mp_pose = None
100
+ self.pose = None
101
+
102
+ if self.use_camera:
103
+ self._init_realsense()
104
+
105
+ if MEDIAPIPE_AVAILABLE:
106
+ self._init_mediapipe()
107
+
108
+ def _init_realsense(self):
109
+ """Initialize RealSense camera pipeline."""
110
+ try:
111
+ self.pipeline = rs.pipeline()
112
+ config = rs.config()
113
+
114
+ # Configure streams
115
+ config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
116
+ config.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 30)
117
+
118
+ # Start pipeline
119
+ profile = self.pipeline.start(config)
120
+
121
+ # Get depth scale
122
+ depth_sensor = profile.get_device().first_depth_sensor()
123
+ self.depth_scale = depth_sensor.get_depth_scale()
124
+
125
+ # Get camera intrinsics
126
+ depth_stream = profile.get_stream(rs.stream.depth)
127
+ intrinsics = depth_stream.as_video_stream_profile().get_intrinsics()
128
+ self.fx = intrinsics.fx
129
+ self.fy = intrinsics.fy
130
+ self.cx = intrinsics.ppx
131
+ self.cy = intrinsics.ppy
132
+
133
+ # Align depth to color
134
+ self.align = rs.align(rs.stream.color)
135
+
136
+ print(f"[INFO] RealSense initialized: {intrinsics.width}x{intrinsics.height}")
137
+ print(f"[INFO] Depth scale: {self.depth_scale}")
138
+
139
+ except Exception as e:
140
+ print(f"[ERROR] Failed to initialize RealSense: {e}")
141
+ self.use_camera = False
142
+ self.pipeline = None
143
+
144
+ def _init_mediapipe(self):
145
+ """Initialize MediaPipe Pose."""
146
+ if MEDIAPIPE_API == 'solutions':
147
+ # Legacy API (mediapipe < 0.10)
148
+ self.mp_pose = mp.solutions.pose
149
+ self.pose = self.mp_pose.Pose(
150
+ static_image_mode=False,
151
+ model_complexity=1, # 0=lite, 1=full, 2=heavy
152
+ enable_segmentation=False,
153
+ min_detection_confidence=0.5,
154
+ min_tracking_confidence=0.5
155
+ )
156
+ print("[INFO] MediaPipe Pose initialized (solutions API)")
157
+ elif MEDIAPIPE_API == 'tasks':
158
+ # New Tasks API (mediapipe >= 0.10)
159
+ from mediapipe.tasks import python as mp_python
160
+ from mediapipe.tasks.python import vision as mp_vision
161
+
162
+ # For Tasks API, we need to download the model
163
+ # We'll use a simpler approach with pose landmarker
164
+ try:
165
+ base_options = mp_python.BaseOptions(
166
+ model_asset_path=self._get_pose_model_path()
167
+ )
168
+ options = mp_vision.PoseLandmarkerOptions(
169
+ base_options=base_options,
170
+ running_mode=mp_vision.RunningMode.VIDEO,
171
+ num_poses=5,
172
+ min_pose_detection_confidence=0.5,
173
+ min_tracking_confidence=0.5
174
+ )
175
+ self.pose = mp_vision.PoseLandmarker.create_from_options(options)
176
+ self.mp_pose = None # Not used in Tasks API
177
+ self._mp_vision = mp_vision
178
+ print("[INFO] MediaPipe Pose initialized (tasks API)")
179
+ except Exception as e:
180
+ print(f"[WARN] Failed to init MediaPipe Tasks: {e}")
181
+ print("[INFO] Falling back to simple detection")
182
+ self.pose = None
183
+ self.mp_pose = None
184
+ else:
185
+ self.pose = None
186
+ self.mp_pose = None
187
+
188
+ def _get_pose_model_path(self) -> str:
189
+ """Get or download the pose landmarker model."""
190
+ import os
191
+ import urllib.request
192
+
193
+ model_dir = os.path.expanduser("~/.cache/mediapipe")
194
+ os.makedirs(model_dir, exist_ok=True)
195
+ model_path = os.path.join(model_dir, "pose_landmarker_lite.task")
196
+
197
+ if not os.path.exists(model_path):
198
+ print("[INFO] Downloading MediaPipe pose model...")
199
+ url = "https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/pose_landmarker_lite.task"
200
+ urllib.request.urlretrieve(url, model_path)
201
+ print("[INFO] Model downloaded")
202
+
203
+ return model_path
204
+
205
+ def start(self):
206
+ """Start the tracking loop in a background thread."""
207
+ if self.running:
208
+ return
209
+
210
+ self.running = True
211
+ self._thread = threading.Thread(target=self._tracking_loop, daemon=True)
212
+ self._thread.start()
213
+ print("[INFO] PersonTracker started")
214
+
215
+ def stop(self):
216
+ """Stop the tracking loop."""
217
+ self.running = False
218
+ if hasattr(self, '_thread'):
219
+ self._thread.join(timeout=1.0)
220
+
221
+ if self.pipeline:
222
+ self.pipeline.stop()
223
+
224
+ if self.pose:
225
+ self.pose.close()
226
+
227
+ print("[INFO] PersonTracker stopped")
228
+
229
+ def _tracking_loop(self):
230
+ """Main tracking loop running at ~30 Hz."""
231
+ while self.running:
232
+ try:
233
+ if self.use_camera and self.pipeline:
234
+ self._process_camera_frame()
235
+ else:
236
+ self._generate_mock_data()
237
+
238
+ time.sleep(1/30) # Target 30 Hz
239
+
240
+ except Exception as e:
241
+ print(f"[ERROR] Tracking loop error: {e}")
242
+ time.sleep(0.1)
243
+
244
+ def _process_camera_frame(self):
245
+ """Process a frame from the RealSense camera."""
246
+ frames = self.pipeline.wait_for_frames()
247
+
248
+ # Align depth to color
249
+ aligned_frames = self.align.process(frames)
250
+ depth_frame = aligned_frames.get_depth_frame()
251
+ color_frame = aligned_frames.get_color_frame()
252
+
253
+ if not depth_frame or not color_frame:
254
+ return
255
+
256
+ # Convert to numpy
257
+ depth_image = np.asanyarray(depth_frame.get_data())
258
+ color_image = np.asanyarray(color_frame.get_data())
259
+
260
+ # Store latest frames
261
+ with self.lock:
262
+ self._latest_color_frame = color_image.copy()
263
+ self._latest_depth_frame = depth_image.copy()
264
+
265
+ # Detect people
266
+ persons = self._detect_persons(color_image, depth_image)
267
+
268
+ # Update tracked persons with ID continuity
269
+ self._update_tracking(persons)
270
+
271
+ def _detect_persons(self, color_image: np.ndarray, depth_image: np.ndarray) -> list[DetectedPerson]:
272
+ """Detect persons in the frame using MediaPipe."""
273
+ if not MEDIAPIPE_AVAILABLE or self.pose is None:
274
+ # Fall back to simple detection if no MediaPipe
275
+ return self._simple_detect_persons(color_image, depth_image)
276
+
277
+ h, w = color_image.shape[:2]
278
+
279
+ if MEDIAPIPE_API == 'solutions':
280
+ return self._detect_persons_solutions(color_image, depth_image)
281
+ elif MEDIAPIPE_API == 'tasks':
282
+ return self._detect_persons_tasks(color_image, depth_image)
283
+ else:
284
+ return self._simple_detect_persons(color_image, depth_image)
285
+
286
+ def _detect_persons_solutions(self, color_image: np.ndarray, depth_image: np.ndarray) -> list[DetectedPerson]:
287
+ """Detect using legacy Solutions API."""
288
+ # Convert BGR to RGB for MediaPipe
289
+ rgb_image = cv2.cvtColor(color_image, cv2.COLOR_BGR2RGB) if CV2_AVAILABLE else color_image
290
+
291
+ # Process with MediaPipe
292
+ results = self.pose.process(rgb_image)
293
+
294
+ if not results.pose_landmarks:
295
+ return []
296
+
297
+ persons = []
298
+ h, w = color_image.shape[:2]
299
+
300
+ # Get landmarks
301
+ landmarks = results.pose_landmarks.landmark
302
+
303
+ # Calculate bounding box from pose landmarks
304
+ x_coords = [lm.x * w for lm in landmarks if lm.visibility > 0.5]
305
+ y_coords = [lm.y * h for lm in landmarks if lm.visibility > 0.5]
306
+
307
+ if not x_coords or not y_coords:
308
+ return []
309
+
310
+ # Bounding box with padding
311
+ padding = 20
312
+ x_min = max(0, int(min(x_coords)) - padding)
313
+ y_min = max(0, int(min(y_coords)) - padding)
314
+ x_max = min(w, int(max(x_coords)) + padding)
315
+ y_max = min(h, int(max(y_coords)) + padding)
316
+
317
+ bbox = (x_min, y_min, x_max - x_min, y_max - y_min)
318
+
319
+ # Get center point (use hip center for more stable depth)
320
+ left_hip = landmarks[self.mp_pose.PoseLandmark.LEFT_HIP]
321
+ right_hip = landmarks[self.mp_pose.PoseLandmark.RIGHT_HIP]
322
+
323
+ center_x = int((left_hip.x + right_hip.x) / 2 * w)
324
+ center_y = int((left_hip.y + right_hip.y) / 2 * h)
325
+
326
+ # Clamp to image bounds
327
+ center_x = max(0, min(w - 1, center_x))
328
+ center_y = max(0, min(h - 1, center_y))
329
+
330
+ # Get depth at center (average over small region for stability)
331
+ depth_region = depth_image[
332
+ max(0, center_y - 5):min(h, center_y + 5),
333
+ max(0, center_x - 5):min(w, center_x + 5)
334
+ ]
335
+
336
+ # Filter out zero/invalid depths
337
+ valid_depths = depth_region[depth_region > 0]
338
+ if len(valid_depths) == 0:
339
+ return []
340
+
341
+ depth_value = np.median(valid_depths) * self.depth_scale # Convert to meters
342
+
343
+ # Convert pixel to 3D coordinates
344
+ x_3d = (center_x - self.cx) * depth_value / self.fx
345
+ y_3d = (center_y - self.cy) * depth_value / self.fy
346
+ z_3d = depth_value
347
+
348
+ # Calculate confidence from landmark visibility
349
+ visibility_sum = sum(lm.visibility for lm in landmarks)
350
+ confidence = visibility_sum / len(landmarks)
351
+
352
+ person = DetectedPerson(
353
+ id=0, # Will be assigned in _update_tracking
354
+ x=x_3d,
355
+ y=y_3d,
356
+ z=z_3d,
357
+ confidence=confidence,
358
+ bbox=bbox,
359
+ landmarks=[(lm.x, lm.y, lm.z, lm.visibility) for lm in landmarks]
360
+ )
361
+
362
+ persons.append(person)
363
+
364
+ return persons
365
+
366
+ def _detect_persons_tasks(self, color_image: np.ndarray, depth_image: np.ndarray) -> list[DetectedPerson]:
367
+ """Detect using new Tasks API (MediaPipe 0.10+)."""
368
+ h, w = color_image.shape[:2]
369
+
370
+ # Convert to RGB
371
+ rgb_image = cv2.cvtColor(color_image, cv2.COLOR_BGR2RGB) if CV2_AVAILABLE else color_image
372
+
373
+ # Create MediaPipe Image
374
+ mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=rgb_image)
375
+
376
+ # Get timestamp in milliseconds
377
+ timestamp_ms = int(time.time() * 1000)
378
+
379
+ # Detect poses
380
+ try:
381
+ results = self.pose.detect_for_video(mp_image, timestamp_ms)
382
+ except Exception as e:
383
+ print(f"[WARN] Pose detection failed: {e}")
384
+ return []
385
+
386
+ if not results.pose_landmarks:
387
+ return []
388
+
389
+ persons = []
390
+
391
+ # Process each detected pose
392
+ for pose_idx, pose_landmarks in enumerate(results.pose_landmarks):
393
+ landmarks = pose_landmarks
394
+
395
+ # Calculate bounding box
396
+ x_coords = [lm.x * w for lm in landmarks if lm.visibility > 0.5]
397
+ y_coords = [lm.y * h for lm in landmarks if lm.visibility > 0.5]
398
+
399
+ if not x_coords or not y_coords:
400
+ continue
401
+
402
+ padding = 20
403
+ x_min = max(0, int(min(x_coords)) - padding)
404
+ y_min = max(0, int(min(y_coords)) - padding)
405
+ x_max = min(w, int(max(x_coords)) + padding)
406
+ y_max = min(h, int(max(y_coords)) + padding)
407
+
408
+ bbox = (x_min, y_min, x_max - x_min, y_max - y_min)
409
+
410
+ # Get center point (use hip landmarks - indices 23 and 24 in Tasks API)
411
+ left_hip = landmarks[23] if len(landmarks) > 23 else landmarks[0]
412
+ right_hip = landmarks[24] if len(landmarks) > 24 else landmarks[0]
413
+
414
+ center_x = int((left_hip.x + right_hip.x) / 2 * w)
415
+ center_y = int((left_hip.y + right_hip.y) / 2 * h)
416
+
417
+ center_x = max(0, min(w - 1, center_x))
418
+ center_y = max(0, min(h - 1, center_y))
419
+
420
+ # Get depth
421
+ depth_region = depth_image[
422
+ max(0, center_y - 5):min(h, center_y + 5),
423
+ max(0, center_x - 5):min(w, center_x + 5)
424
+ ]
425
+
426
+ valid_depths = depth_region[depth_region > 0]
427
+ if len(valid_depths) == 0:
428
+ continue
429
+
430
+ depth_value = np.median(valid_depths) * self.depth_scale
431
+
432
+ x_3d = (center_x - self.cx) * depth_value / self.fx
433
+ y_3d = (center_y - self.cy) * depth_value / self.fy
434
+ z_3d = depth_value
435
+
436
+ visibility_sum = sum(lm.visibility for lm in landmarks)
437
+ confidence = visibility_sum / len(landmarks)
438
+
439
+ person = DetectedPerson(
440
+ id=0,
441
+ x=x_3d,
442
+ y=y_3d,
443
+ z=z_3d,
444
+ confidence=confidence,
445
+ bbox=bbox,
446
+ landmarks=[(lm.x, lm.y, lm.z, lm.visibility) for lm in landmarks]
447
+ )
448
+
449
+ persons.append(person)
450
+
451
+ return persons
452
+
453
+ def _simple_detect_persons(self, color_image: np.ndarray, depth_image: np.ndarray) -> list[DetectedPerson]:
454
+ """Simple depth-based person detection fallback."""
455
+ h, w = color_image.shape[:2]
456
+
457
+ # Simple approach: find the largest depth blob in the center region
458
+ # This is a basic fallback when MediaPipe is not available
459
+
460
+ # Focus on center 60% of the image
461
+ margin_x = int(w * 0.2)
462
+ margin_y = int(h * 0.1)
463
+
464
+ center_depth = depth_image[margin_y:h-margin_y, margin_x:w-margin_x]
465
+
466
+ # Find valid depth values between 0.5m and 4m
467
+ min_depth = int(0.5 / self.depth_scale)
468
+ max_depth = int(4.0 / self.depth_scale)
469
+
470
+ valid_mask = (center_depth > min_depth) & (center_depth < max_depth)
471
+
472
+ if not np.any(valid_mask):
473
+ return []
474
+
475
+ # Find the center of the valid region
476
+ y_indices, x_indices = np.where(valid_mask)
477
+
478
+ if len(x_indices) == 0:
479
+ return []
480
+
481
+ center_x = int(np.median(x_indices)) + margin_x
482
+ center_y = int(np.median(y_indices)) + margin_y
483
+
484
+ # Get depth at that point
485
+ depth_value = depth_image[center_y, center_x] * self.depth_scale
486
+
487
+ if depth_value <= 0:
488
+ return []
489
+
490
+ # Convert to 3D
491
+ x_3d = (center_x - self.cx) * depth_value / self.fx
492
+ y_3d = (center_y - self.cy) * depth_value / self.fy
493
+ z_3d = depth_value
494
+
495
+ # Create a rough bounding box
496
+ bbox = (margin_x, margin_y, w - 2*margin_x, h - 2*margin_y)
497
+
498
+ person = DetectedPerson(
499
+ id=0,
500
+ x=x_3d,
501
+ y=y_3d,
502
+ z=z_3d,
503
+ confidence=0.5, # Low confidence for simple detection
504
+ bbox=bbox,
505
+ landmarks=None
506
+ )
507
+
508
+ return [person]
509
+
510
+ def _update_tracking(self, new_persons: list[DetectedPerson]):
511
+ """Update tracking with ID continuity based on position."""
512
+ current_time = time.time()
513
+
514
+ # Match new detections to existing tracked persons
515
+ for person in new_persons:
516
+ best_match_id = None
517
+ best_match_dist = float('inf')
518
+
519
+ # Find closest existing person
520
+ for tracked_id, tracked in self._tracking_history.items():
521
+ # Only match if seen recently (within 500ms)
522
+ if current_time - tracked.last_seen > 0.5:
523
+ continue
524
+
525
+ # Calculate 3D distance
526
+ dist = np.sqrt(
527
+ (person.x - tracked.x)**2 +
528
+ (person.y - tracked.y)**2 +
529
+ (person.z - tracked.z)**2
530
+ )
531
+
532
+ # Match if within 0.5m (person couldn't have moved further in one frame)
533
+ if dist < 0.5 and dist < best_match_dist:
534
+ best_match_dist = dist
535
+ best_match_id = tracked_id
536
+
537
+ if best_match_id is not None:
538
+ person.id = best_match_id
539
+ else:
540
+ person.id = self._next_person_id
541
+ self._next_person_id += 1
542
+
543
+ person.last_seen = current_time
544
+ self._tracking_history[person.id] = person
545
+
546
+ # Clean up old tracks
547
+ stale_ids = [
548
+ pid for pid, p in self._tracking_history.items()
549
+ if current_time - p.last_seen > 2.0 # Remove after 2 seconds
550
+ ]
551
+ for pid in stale_ids:
552
+ del self._tracking_history[pid]
553
+
554
+ # Update current persons list
555
+ with self.lock:
556
+ self._persons = new_persons.copy()
557
+
558
+ def _generate_mock_data(self):
559
+ """Generate mock person data for testing without camera."""
560
+ t = time.time()
561
+
562
+ # Simulate a person moving back and forth
563
+ x = 0.3 * np.sin(t * 0.5) # Side to side
564
+ z = 1.5 + 0.5 * np.sin(t * 0.3) # Forward/back
565
+
566
+ mock_person = DetectedPerson(
567
+ id=1,
568
+ x=x,
569
+ y=0.0,
570
+ z=z,
571
+ confidence=0.9,
572
+ bbox=(200, 100, 200, 300),
573
+ last_seen=t
574
+ )
575
+
576
+ with self.lock:
577
+ self._persons = [mock_person]
578
+
579
+ # Generate mock color frame
580
+ if CV2_AVAILABLE:
581
+ frame = np.zeros((480, 640, 3), dtype=np.uint8)
582
+ # Draw a simple rectangle for the person
583
+ cv2.rectangle(frame, (200, 100), (400, 400), (0, 255, 0), 2)
584
+ cv2.putText(frame, f"Person #1 z={z:.2f}m", (200, 90),
585
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
586
+ self._latest_color_frame = frame
587
+
588
+ @property
589
+ def persons(self) -> list[DetectedPerson]:
590
+ """Get current list of detected persons (thread-safe)."""
591
+ with self.lock:
592
+ return self._persons.copy()
593
+
594
+ @property
595
+ def latest_frame(self) -> Optional[np.ndarray]:
596
+ """Get latest color frame (thread-safe)."""
597
+ with self.lock:
598
+ if self._latest_color_frame is not None:
599
+ return self._latest_color_frame.copy()
600
+ return None
601
+
602
+ def get_annotated_frame(self) -> Optional[np.ndarray]:
603
+ """Get color frame with detection annotations."""
604
+ frame = self.latest_frame
605
+ if frame is None or not CV2_AVAILABLE:
606
+ return frame
607
+
608
+ persons = self.persons
609
+
610
+ for person in persons:
611
+ x, y, w, h = person.bbox
612
+
613
+ # Draw bounding box
614
+ cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
615
+
616
+ # Draw label
617
+ label = f"#{person.id} z={person.z:.2f}m"
618
+ cv2.putText(frame, label, (x, y - 10),
619
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
620
+
621
+ return frame
622
+
623
+ def get_person_by_id(self, person_id: int) -> Optional[DetectedPerson]:
624
+ """Get a specific person by ID."""
625
+ for person in self.persons:
626
+ if person.id == person_id:
627
+ return person
628
+ return None
629
+
630
+ def get_closest_person(self) -> Optional[DetectedPerson]:
631
+ """Get the closest detected person."""
632
+ persons = self.persons
633
+ if not persons:
634
+ return None
635
+ return min(persons, key=lambda p: p.distance)
@@ -0,0 +1,22 @@
1
+ <?xml version="1.0"?>
2
+ <?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
3
+ <package format="3">
4
+ <name>agenticros_follow_me</name>
5
+ <version>0.0.1</version>
6
+ <description>Follow Me mission: person tracking and follower control, exposes ROS2 services for AgenticROS</description>
7
+ <maintainer email="team@plaipin.com">PlaiPin</maintainer>
8
+ <license>Apache-2.0</license>
9
+
10
+ <depend>rclpy</depend>
11
+ <depend>std_msgs</depend>
12
+ <depend>geometry_msgs</depend>
13
+ <depend>agenticros_msgs</depend>
14
+
15
+ <test_depend>ament_copyright</test_depend>
16
+ <test_depend>ament_flake8</test_depend>
17
+ <test_depend>ament_pep257</test_depend>
18
+
19
+ <export>
20
+ <build_type>ament_python</build_type>
21
+ </export>
22
+ </package>
@@ -0,0 +1,25 @@
1
+ from setuptools import setup
2
+
3
+ package_name = "agenticros_follow_me"
4
+
5
+ setup(
6
+ name=package_name,
7
+ version="0.0.1",
8
+ packages=[package_name],
9
+ data_files=[
10
+ ("share/ament_index/resource_index/packages", ["resource/" + package_name]),
11
+ ("share/" + package_name, ["package.xml"]),
12
+ ],
13
+ install_requires=["setuptools"],
14
+ zip_safe=True,
15
+ maintainer="PlaiPin",
16
+ maintainer_email="team@plaipin.com",
17
+ description="Follow Me mission: person tracking and follower control for AgenticROS",
18
+ license="Apache-2.0",
19
+ tests_require=["pytest"],
20
+ entry_points={
21
+ "console_scripts": [
22
+ "follow_me_node = agenticros_follow_me.follow_me_node:main",
23
+ ],
24
+ },
25
+ )